Blob Blame History Raw
From 2fa90caf4ad38541615446b80dbeaccd0d0e6a6f Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 28 Oct 2020 13:40:03 +0100
Subject: [PATCH] Kerberos: add default_domain and udp_preference_limit

When joining an Active Directory domain realmd will set the
default_domain and udp_preference_limit in the Kerberos configuration to
avoid errors and make Kerberos handling in the AD domain more easy.

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1791016
---
 doc/manual/realmd.conf.xml      |  69 +++++++++++++++++++
 service/Makefile.am             |   2 +
 service/realm-kerberos-config.c | 116 ++++++++++++++++++++++++++++++++
 service/realm-kerberos-config.h |  35 ++++++++++
 service/realm-samba.c           |  12 ++++
 service/realm-sssd-ad.c         |  12 ++++
 service/realmd-debian.conf      |   1 +
 service/realmd-defaults.conf    |   1 +
 service/realmd-redhat.conf      |   1 +
 service/realmd-suse.conf        |   1 +
 10 files changed, 250 insertions(+)
 create mode 100644 service/realm-kerberos-config.c
 create mode 100644 service/realm-kerberos-config.h

diff --git a/doc/manual/realmd.conf.xml b/doc/manual/realmd.conf.xml
index 9062252..97d2e8d 100644
--- a/doc/manual/realmd.conf.xml
+++ b/doc/manual/realmd.conf.xml
@@ -304,6 +304,75 @@ DOMAIN\user:*:13445:13446:Name:/home/DOMAIN/user:/bin/bash
 
 </refsect1>
 
+<refsect1 id="realmd-conf-paths">
+	<title>paths</title>
+
+	<para>These options should go in an <option>[paths]</option>
+	section of the <filename>/etc/realmd.conf</filename> file. Only
+	specify the settings you wish to override.</para>
+
+	<variablelist>
+	<varlistentry>
+	<term><option>krb5.conf</option></term>
+	<listitem>
+		<para>Path to the Kerberos configuration file, typically
+		<filename>/etc/krb5.conf</filename>. It can also be the path of
+		a file included by <filename>/etc/krb5.conf</filename>, e.g.
+		<filename>/etc/krb5.conf.d/realmd_settings</filename>, if the
+		file does not exist if will be created.</para>
+
+		<informalexample>
+<programlisting language="js">
+[paths]
+krb5.conf = /etc/krb5.conf.d/realmd_settings
+
+</programlisting>
+		</informalexample>
+
+		<para>When joining an Active Directory domain
+		<command>realmd</command> will set the
+		<option>default_realm</option> and
+		<option>udp_preference_limit</option> options in the Kerberos
+		configuration:</para>
+
+		<informalexample>
+<programlisting language="js">
+default_realm = DOMAIN.EXAMPLE.COM
+udp_preference_limit = 0
+
+</programlisting>
+		</informalexample>
+
+		<para>The <option>default_realm</option> option is e.g. needed
+		when trying to resolve enterprise principals and makes it more
+		convenient to request Kerberos tickets for users of the default
+		realm. Instead of specifying the whole principal just
+		<command>kinit username</command> can be used.</para>
+
+		<para>With <option>udp_preference_limit = 0</option> always TCP
+		will be used to send Kerberos request to domain controller. This
+		is useful in Active Directory environments because Kerberos will
+		typically switch to TCP after initially starting with UDP
+		because AD Kerberos tickets are often larger than UDP can handle.
+		Using TCP by default will avoid those extra UDP round trips.
+		Additionally it helps to avoid issues with password changes when
+		the DC does not reply soon enough and the client will send a
+		second UDP request. The DC might reply with a reply error to the
+		second request although the original password change request was
+		successful and the client will no know if the request was
+		successful or not. When using TCP this cannot happen because the
+		client will never send a second request but waits on the
+		connection until the server replies.</para>
+
+		<para>Please note that <command>realmd</command> will not remove
+		those options while leaving the domain since they are useful in
+		general. When joining a new domain <command>realmd</command>
+		will of course overwrite <option>default_realm</option>.</para>
+	</listitem>
+	</varlistentry>
+	</variablelist>
+</refsect1>
+
 <refsect1 id="realmd-conf-specific-settings">
 	<title>Realm specific settings</title>
 	<para>These options should go in an section with the same name
diff --git a/service/Makefile.am b/service/Makefile.am
index 88ee780..031cd1d 100644
--- a/service/Makefile.am
+++ b/service/Makefile.am
@@ -57,6 +57,8 @@ realmd_SOURCES = \
 	service/realm-invocation.h \
 	service/realm-kerberos.c \
 	service/realm-kerberos.h \
+	service/realm-kerberos-config.c \
+	service/realm-kerberos-config.h \
 	service/realm-kerberos-membership.c \
 	service/realm-kerberos-membership.h \
 	service/realm-kerberos-provider.c \
diff --git a/service/realm-kerberos-config.c b/service/realm-kerberos-config.c
new file mode 100644
index 0000000..447a452
--- /dev/null
+++ b/service/realm-kerberos-config.c
@@ -0,0 +1,116 @@
+/* realmd -- Realm configuration service
+ *
+ * Copyright 2020 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Sumit Bose <sbose@redhat.com>
+ */
+
+#include "config.h"
+
+#include "realm-ini-config.h"
+#include "realm-kerberos-config.h"
+#include "realm-settings.h"
+
+#include <string.h>
+
+RealmIniConfig *
+realm_kerberos_config_new_with_flags (RealmIniFlags flags,
+                                      GError **error)
+{
+	RealmIniConfig *config;
+	const gchar *filename;
+	GError *err = NULL;
+
+	config = realm_ini_config_new (REALM_INI_LINE_CONTINUATIONS | flags);
+
+	filename = realm_settings_path ("krb5.conf");
+
+	realm_ini_config_read_file (config, filename, &err);
+
+	if (err != NULL) {
+		/* If the caller wants errors, then don't return an invalid samba config */
+		if (error) {
+			g_propagate_error (error, err);
+			g_object_unref (config);
+			config = NULL;
+
+		/* If the caller doesn't care, then warn but continue */
+		} else {
+			g_warning ("Couldn't load config file: %s: %s", filename,
+			           err->message);
+			g_error_free (err);
+		}
+	}
+
+	return config;
+}
+
+RealmIniConfig *
+realm_kerberos_config_new (GError **error)
+{
+	return realm_kerberos_config_new_with_flags (REALM_INI_NONE, error);
+}
+
+gboolean
+configure_krb5_conf_for_domain (const gchar *realm, GError **error )
+{
+	RealmIniConfig *config;
+	gboolean res;
+	GFile *gfile;
+	GFileInfo *file_info = NULL;
+
+	config = realm_kerberos_config_new (error);
+	if (config == NULL) {
+		return FALSE;
+	}
+
+	/* When writing to a file glib will replace the original file with a
+	 * new one. To make sure permissions and other attributes like e.g.
+	 * SELinux labels stay the same this information is saved before the
+	 * change and applied to the new file afterwards. */
+	gfile = g_file_new_for_path (realm_ini_config_get_filename (config));
+	file_info = g_file_query_info (gfile, "*", 0, NULL, error);
+	g_object_unref (gfile);
+	if (*error != NULL) {
+		g_warning ("Couldn't load file attributes, "
+		           "will continue without: %s: %s",
+		           realm_ini_config_get_filename (config),
+		           (*error)->message);
+		g_clear_error (error);
+	}
+
+	if (!realm_ini_config_begin_change (config, error)) {
+		g_object_unref (config);
+		return FALSE;
+	}
+
+	realm_ini_config_set (config, "libdefaults",
+	                              "default_realm", realm,
+	                              "udp_preference_limit", "0",
+	                               NULL);
+
+	res = realm_ini_config_finish_change (config, error);
+
+	if (file_info != NULL) {
+		gfile = g_file_new_for_path (realm_ini_config_get_filename (config));
+		if (!g_file_set_attributes_from_info (gfile, file_info,
+		                                      0, NULL, error)) {
+			g_warning ("Couldn't set file attributes: %s: %s",
+			           realm_ini_config_get_filename (config),
+			           (*error)->message);
+		}
+		g_object_unref (file_info);
+		g_object_unref (gfile);
+	}
+
+	g_object_unref (config);
+
+	return res;
+}
diff --git a/service/realm-kerberos-config.h b/service/realm-kerberos-config.h
new file mode 100644
index 0000000..791aa98
--- /dev/null
+++ b/service/realm-kerberos-config.h
@@ -0,0 +1,35 @@
+/* realmd -- Realm configuration service
+ *
+ * Copyright 2020 Red Hat Inc
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * See the included COPYING file for more information.
+ *
+ * Author: Sumit Bose <sbose@redhat.com>
+ */
+
+#include "config.h"
+
+#ifndef __REALM_KERBEROS_CONFIG_H__
+#define __REALM_KERBEROS_CONFIG_H__
+
+#include <gio/gio.h>
+
+#include "realm-ini-config.h"
+
+
+RealmIniConfig *    realm_kerberos_config_new                      (GError **error);
+
+RealmIniConfig *    realm_kerberos_config_new_with_flags           (RealmIniFlags flags,
+                                                                    GError **error);
+
+gboolean            configure_krb5_conf_for_domain                 (const gchar *realm,
+                                                                    GError **error );
+
+G_END_DECLS
+
+#endif /* __REALM_KERBEROS_CONFIG_H__ */
diff --git a/service/realm-samba.c b/service/realm-samba.c
index fe33600..e7b80a0 100644
--- a/service/realm-samba.c
+++ b/service/realm-samba.c
@@ -21,6 +21,7 @@
 #include "realm-disco.h"
 #include "realm-errors.h"
 #include "realm-kerberos.h"
+#include "realm-kerberos-config.h"
 #include "realm-kerberos-membership.h"
 #include "realm-options.h"
 #include "realm-packages.h"
@@ -210,6 +211,17 @@ on_join_do_winbind (GObject *source,
 		                         NULL);
 	}
 
+	if (error == NULL) {
+		configure_krb5_conf_for_domain (enroll->disco->kerberos_realm, &error);
+		if (error != NULL) {
+			realm_diagnostics_error (enroll->invocation, error,
+			                         "Failed to update Kerberos "
+			                         "configuration, not fatal, "
+			                         "please check manually");
+			g_clear_error (&error);
+		}
+	}
+
 	if (error == NULL) {
 		name = realm_kerberos_get_name (REALM_KERBEROS (self));
 		realm_samba_winbind_configure_async (self->config, name, enroll->options,
diff --git a/service/realm-sssd-ad.c b/service/realm-sssd-ad.c
index de7ce30..6b2f9f8 100644
--- a/service/realm-sssd-ad.c
+++ b/service/realm-sssd-ad.c
@@ -19,6 +19,7 @@
 #include "realm-dbus-constants.h"
 #include "realm-diagnostics.h"
 #include "realm-errors.h"
+#include "realm-kerberos-config.h"
 #include "realm-kerberos-membership.h"
 #include "realm-options.h"
 #include "realm-packages.h"
@@ -256,6 +257,17 @@ on_join_do_sssd (GObject *source,
 		                           join->options, join->use_adcli, &error);
 	}
 
+	if (error == NULL) {
+		configure_krb5_conf_for_domain (join->disco->kerberos_realm, &error);
+		if (error != NULL) {
+			realm_diagnostics_error (join->invocation, error,
+			                         "Failed to update Kerberos "
+			                         "configuration, not fatal, "
+			                         "please check manually");
+			g_clear_error (&error);
+		}
+	}
+
 	if (error == NULL) {
 		realm_service_enable_and_restart ("sssd", join->invocation,
 		                                  on_sssd_enable_nss, g_object_ref (task));
diff --git a/service/realmd-debian.conf b/service/realmd-debian.conf
index 3e93d60..6cfdcef 100644
--- a/service/realmd-debian.conf
+++ b/service/realmd-debian.conf
@@ -1,6 +1,7 @@
 # Distro specific overrides for debian
 [paths]
 smb.conf = /etc/samba/smb.conf
+krb5.conf = /etc/krb5.conf
 
 #
 # Normally in these packages sections we can specify a file
diff --git a/service/realmd-defaults.conf b/service/realmd-defaults.conf
index 6d7ccf8..ac4b436 100644
--- a/service/realmd-defaults.conf
+++ b/service/realmd-defaults.conf
@@ -11,6 +11,7 @@ sssd.conf = /etc/sssd/sssd.conf
 adcli = /usr/sbin/adcli
 ipa-client-install = /usr/sbin/ipa-client-install
 pam_winbind.conf = /etc/security/pam_winbind.conf
+krb5.conf = /etc/krb5.conf
 
 [active-directory]
 default-client = sssd
diff --git a/service/realmd-redhat.conf b/service/realmd-redhat.conf
index e39fad5..46e61b1 100644
--- a/service/realmd-redhat.conf
+++ b/service/realmd-redhat.conf
@@ -1,6 +1,7 @@
 # Distro specific overrides for redhat
 [paths]
 smb.conf = /etc/samba/smb.conf
+krb5.conf = /etc/krb5.conf
 
 [samba-packages]
 samba-common-tools = /usr/bin/net
diff --git a/service/realmd-suse.conf b/service/realmd-suse.conf
index 052b4dc..3165efa 100644
--- a/service/realmd-suse.conf
+++ b/service/realmd-suse.conf
@@ -1,6 +1,7 @@
 # Distro specific overrides for SuSE
 [paths]
 smb.conf = /etc/samba/smb.conf
+krb5.conf = /etc/krb5.conf
 
 [samba-packages]
 samba-client = /usr/bin/net
-- 
2.26.2