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