Blame SOURCES/0055-krb5_locator-always-use-port-88-for-master-KDC.patch

b6526f
From 941a381d7a7be2af73e41bd584aca4d22675d765 Mon Sep 17 00:00:00 2001
b6526f
From: Sumit Bose <sbose@redhat.com>
b6526f
Date: Fri, 15 Feb 2019 16:54:19 +0100
b6526f
Subject: [PATCH] krb5_locator: always use port 88 for master KDC
b6526f
b6526f
If the kpasswdinfo file exists and the found IP address includes a port
b6526f
number as well the master KDC lookup will use this port number which is
b6526f
most probably wrong. Better use the default port 88 always for master
b6526f
KDC lookups.
b6526f
b6526f
This patch also updates the man page for the locator plugin which was
b6526f
quite outdated.
b6526f
b6526f
Related to https://pagure.io/SSSD/sssd/issue/3958
b6526f
b6526f
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
b6526f
(cherry picked from commit 05350abdf2ab98770ca296b9485578218644a2a7)
b6526f
---
b6526f
 src/krb5_plugin/sssd_krb5_locator_plugin.c    |  43 +++--
b6526f
 src/man/sssd_krb5_locator_plugin.8.xml        |  76 +++++----
b6526f
 .../cmocka/test_sssd_krb5_locator_plugin.c    | 156 ++++++++++++++++++
b6526f
 3 files changed, 233 insertions(+), 42 deletions(-)
b6526f
b6526f
diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c
b6526f
index 952d487c2..fc5a4235d 100644
b6526f
--- a/src/krb5_plugin/sssd_krb5_locator_plugin.c
b6526f
+++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c
b6526f
@@ -80,6 +80,7 @@ struct sssd_ctx {
b6526f
     struct addr_port *kpasswd_addr;
b6526f
     bool debug;
b6526f
     bool disabled;
b6526f
+    bool kpasswdinfo_used;
b6526f
 };
b6526f
 
b6526f
 void plugin_debug_fn(const char *format, ...)
b6526f
@@ -411,6 +412,7 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context,
b6526f
         ctx->disabled = true;
b6526f
         PLUGIN_DEBUG(("SSSD KRB5 locator plugin is disabled.\n"));
b6526f
     }
b6526f
+    ctx->kpasswdinfo_used = false;
b6526f
 
b6526f
     *private_data = ctx;
b6526f
 
b6526f
@@ -451,6 +453,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
b6526f
     struct addr_port *addr = NULL;
b6526f
     char port_str[PORT_STR_SIZE];
b6526f
     size_t c;
b6526f
+    bool force_port = false;
b6526f
 
b6526f
     if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE;
b6526f
     ctx = (struct sssd_ctx *) private_data;
b6526f
@@ -478,20 +481,24 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
b6526f
             return KRB5_PLUGIN_NO_HANDLE;
b6526f
         }
b6526f
 
b6526f
-        if (svc == locate_service_kadmin || svc == locate_service_kpasswd ||
b6526f
-            svc == locate_service_master_kdc) {
b6526f
-            ret = get_krb5info(realm, ctx, locate_service_kpasswd);
b6526f
+    }
b6526f
+
b6526f
+    if (ctx->kpasswd_addr == NULL
b6526f
+            && (svc == locate_service_kadmin || svc == locate_service_kpasswd ||
b6526f
+                svc == locate_service_master_kdc)) {
b6526f
+        ret = get_krb5info(realm, ctx, locate_service_kpasswd);
b6526f
+        if (ret != EOK) {
b6526f
+            PLUGIN_DEBUG(("reading kpasswd address failed, "
b6526f
+                          "using kdc address.\n"));
b6526f
+            free_addr_port_list(&(ctx->kpasswd_addr));
b6526f
+            ret = copy_addr_port_list(ctx->kdc_addr, true,
b6526f
+                                      &(ctx->kpasswd_addr));
b6526f
             if (ret != EOK) {
b6526f
-                PLUGIN_DEBUG(("reading kpasswd address failed, "
b6526f
-                              "using kdc address.\n"));
b6526f
-                free_addr_port_list(&(ctx->kpasswd_addr));
b6526f
-                ret = copy_addr_port_list(ctx->kdc_addr, true,
b6526f
-                                          &(ctx->kpasswd_addr));
b6526f
-                if (ret != EOK) {
b6526f
-                    PLUGIN_DEBUG(("copying address list failed.\n"));
b6526f
-                    return KRB5_PLUGIN_NO_HANDLE;
b6526f
-                }
b6526f
+                PLUGIN_DEBUG(("copying address list failed.\n"));
b6526f
+                return KRB5_PLUGIN_NO_HANDLE;
b6526f
             }
b6526f
+        } else {
b6526f
+            ctx->kpasswdinfo_used = true;
b6526f
         }
b6526f
     }
b6526f
 
b6526f
@@ -507,6 +514,12 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
b6526f
         case locate_service_master_kdc:
b6526f
             addr = ctx->kpasswd_addr;
b6526f
             default_port = DEFAULT_KERBEROS_PORT;
b6526f
+            if (ctx->kpasswdinfo_used) {
b6526f
+                /* Use default port if the addresses from the kpasswdinfo
b6526f
+                 * files are used because the port numbers from the file will
b6526f
+                 * most probably not be suitable. */
b6526f
+                force_port = true;
b6526f
+            }
b6526f
             break;
b6526f
         case locate_service_kadmin:
b6526f
             addr = ctx->kpasswd_addr;
b6526f
@@ -539,11 +552,13 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data,
b6526f
             return KRB5_PLUGIN_NO_HANDLE;
b6526f
     }
b6526f
 
b6526f
-    if (strcmp(realm, ctx->sssd_realm) != 0)
b6526f
+    if (strcmp(realm, ctx->sssd_realm) != 0 || addr == NULL) {
b6526f
         return KRB5_PLUGIN_NO_HANDLE;
b6526f
+    }
b6526f
 
b6526f
     for (c = 0; addr[c].addr != NULL; c++) {
b6526f
-        port = (addr[c].port == 0 ? default_port : addr[c].port);
b6526f
+        port = ((addr[c].port == 0 || force_port) ? default_port
b6526f
+                                                  : addr[c].port);
b6526f
         memset(port_str, 0, PORT_STR_SIZE);
b6526f
         ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port);
b6526f
         if (ret < 0 || ret >= (PORT_STR_SIZE-1)) {
b6526f
diff --git a/src/man/sssd_krb5_locator_plugin.8.xml b/src/man/sssd_krb5_locator_plugin.8.xml
b6526f
index d28546012..d77f59d6a 100644
b6526f
--- a/src/man/sssd_krb5_locator_plugin.8.xml
b6526f
+++ b/src/man/sssd_krb5_locator_plugin.8.xml
b6526f
@@ -20,40 +20,60 @@
b6526f
         <title>DESCRIPTION</title>
b6526f
         <para>
b6526f
             The Kerberos locator plugin
b6526f
-            <command>sssd_krb5_locator_plugin</command> is used by the Kerberos
b6526f
-            provider of
b6526f
-            <citerefentry>
b6526f
-                <refentrytitle>sssd</refentrytitle>
b6526f
-                <manvolnum>8</manvolnum>
b6526f
-            </citerefentry>
b6526f
-            to tell the Kerberos libraries what Realm and which KDC to use.
b6526f
-            Typically this is done in
b6526f
+            <command>sssd_krb5_locator_plugin</command> is used by libkrb5 to
b6526f
+            find KDCs for a given Kerberos realm. SSSD provides such a plugin to
b6526f
+            guide all Kerberos clients on a system to a single KDC. In general
b6526f
+            it should not matter to which KDC a client process is talking to.
b6526f
+            But there are cases, e.g. after a password change, where not all
b6526f
+            KDCs are in the same state because the new data has to be replicated
b6526f
+            first. To avoid unexpected authentication failures and maybe even
b6526f
+            account lockings it would be good to talk to a single KDC as long as
b6526f
+            possible.
b6526f
+        </para>
b6526f
+        <para>
b6526f
+            libkrb5 will search the locator plugin in the libkrb5 sub-directory
b6526f
+            of the Kerberos plugin directory, see plugin_base_dir in
b6526f
             <citerefentry>
b6526f
                 <refentrytitle>krb5.conf</refentrytitle>
b6526f
                 <manvolnum>5</manvolnum>
b6526f
             </citerefentry>
b6526f
-            which is always read by the Kerberos libraries. To simplify the
b6526f
-            configuration the Realm and the KDC can be defined in
b6526f
-            <citerefentry>
b6526f
-                <refentrytitle>sssd.conf</refentrytitle>
b6526f
-                <manvolnum>5</manvolnum>
b6526f
-            </citerefentry>
b6526f
-            as described in
b6526f
-            <citerefentry>
b6526f
-                <refentrytitle>sssd-krb5</refentrytitle>
b6526f
-                <manvolnum>5</manvolnum>
b6526f
-            </citerefentry>
b6526f
+            for details. The plugin can only be disabled by removing the plugin
b6526f
+            file. There is no option in the Kerberos configuration to disable
b6526f
+            it. But the SSSD_KRB5_LOCATOR_DISABLE environment variable can be
b6526f
+            used to disable the plugin for individual commands. Alternatively
b6526f
+            the SSSD option krb5_use_kdcinfo=False can be used to not generate
b6526f
+            the data needed by the plugin. With this the plugin is still
b6526f
+            called but will provide no data to the caller so that libkrb5 can
b6526f
+            fall back to other methods defined in krb5.conf.
b6526f
         </para>
b6526f
         <para>
b6526f
-            <citerefentry>
b6526f
-                <refentrytitle>sssd</refentrytitle>
b6526f
-                <manvolnum>8</manvolnum>
b6526f
-            </citerefentry>
b6526f
-            puts the Realm and the name or IP address of the KDC into the
b6526f
-            environment variables SSSD_KRB5_REALM and SSSD_KRB5_KDC respectively.
b6526f
-            When <command>sssd_krb5_locator_plugin</command> is called by the
b6526f
-            kerberos libraries it reads and evaluates these variables and returns
b6526f
-            them to the libraries.
b6526f
+            The plugin reads the information about the KDCs of a given realm
b6526f
+            from a file called <filename>kdcinfo.REALM</filename>. The file
b6526f
+            should contain one or more IP addresses either in dotted-decimal
b6526f
+            IPv4 notation or the hexadecimal IPv6 notation. An optional port
b6526f
+            number can be added to the end separated with a colon, the IPv6
b6526f
+            address has to be enclosed in squared brackets in this case as
b6526f
+            usual. Valid entries are:
b6526f
+            <itemizedlist>
b6526f
+                <listitem><para>1.2.3.4</para></listitem>
b6526f
+                <listitem><para>5.6.7.8:99</para></listitem>
b6526f
+                <listitem><para>2001:db8:85a3::8a2e:370:7334</para></listitem>
b6526f
+                <listitem><para>[2001:db8:85a3::8a2e:370:7334]:321</para></listitem>
b6526f
+            </itemizedlist>
b6526f
+            SSSD's krb5 auth-provider which is used by the IPA and AD providers
b6526f
+            as well adds the address of the current KDC or domain controller
b6526f
+            SSSD is using to this file.
b6526f
+        </para>
b6526f
+        <para>
b6526f
+            In environments with read-only and read-write KDCs where clients are
b6526f
+            expected to use the read-only instances for the general operations
b6526f
+            and only the read-write KDC for config changes like password changes
b6526f
+            a <filename>kpasswdinfo.REALM</filename> is used as well to identify
b6526f
+            read-write KDCs. If this file exists for the given realm the content
b6526f
+            will be used by the plugin to reply to requests for a kpasswd or
b6526f
+            kadmin server or for the MIT Kerberos specific master KDC. If the
b6526f
+            address contains a port number the default KDC port 88 will be used
b6526f
+            for the latter.
b6526f
         </para>
b6526f
     </refsect1>
b6526f
 
b6526f
diff --git a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c
b6526f
index 3e7d00632..1b6838345 100644
b6526f
--- a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c
b6526f
+++ b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c
b6526f
@@ -44,6 +44,9 @@
b6526f
 #define TEST_IP_1_WITH_SERVICE TEST_IP_1":"TEST_SERVICE_1
b6526f
 #define TEST_IPV6_1_WITH_SERVICE TEST_IPV6_1":"TEST_SERVICE_2
b6526f
 
b6526f
+#define TEST_IP_1_WITH_SERVICE_2 TEST_IP_1":"TEST_SERVICE_2
b6526f
+#define TEST_IPV6_1_WITH_SERVICE_1 TEST_IPV6_1":"TEST_SERVICE_1
b6526f
+
b6526f
 struct test_state {
b6526f
     void *dummy;
b6526f
 };
b6526f
@@ -61,6 +64,7 @@ static int setup(void **state)
b6526f
     *state = (void *)ts;
b6526f
 
b6526f
     unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
b6526f
+    unlink(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM);
b6526f
     rmdir(TEST_PUBCONF_PATH);
b6526f
 
b6526f
     return 0;
b6526f
@@ -574,7 +578,157 @@ void test_service(void **state)
b6526f
 
b6526f
     k5_free_serverlist(&list);
b6526f
 
b6526f
+    /* locate_service_master_kdc should get the default port 88 if kpasswdinfo
b6526f
+     * does not exists. */
b6526f
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM,
b6526f
+                                    SOCK_DGRAM, AF_INET, module_callback,
b6526f
+                                    &cbdata);
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+    assert_int_equal(list.nservers, 1);
b6526f
+    assert_non_null(list.servers);
b6526f
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
b6526f
+                      list.servers[0].addrlen,
b6526f
+                      host, sizeof(host), service, sizeof(service),
b6526f
+                      NI_NUMERICHOST|NI_NUMERICSERV);
b6526f
+    assert_int_equal(ret, 0);
b6526f
+    assert_string_equal(TEST_IP_1, host);
b6526f
+    assert_string_equal("88", service);
b6526f
+
b6526f
+    k5_free_serverlist(&list);
b6526f
+
b6526f
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM,
b6526f
+                                    SOCK_DGRAM, AF_INET6, module_callback,
b6526f
+                                    &cbdata);
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+    assert_int_equal(list.nservers, 1);
b6526f
+    assert_non_null(list.servers);
b6526f
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
b6526f
+                      list.servers[0].addrlen,
b6526f
+                      host, sizeof(host), service, sizeof(service),
b6526f
+                      NI_NUMERICHOST|NI_NUMERICSERV);
b6526f
+    assert_int_equal(ret, 0);
b6526f
+    assert_string_equal(TEST_IPV6_1_PURE, host);
b6526f
+    assert_string_equal("88", service);
b6526f
+
b6526f
+    k5_free_serverlist(&list);
b6526f
+
b6526f
+    unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
b6526f
+    rmdir(TEST_PUBCONF_PATH);
b6526f
+    sssd_krb5_locator_close(priv);
b6526f
+
b6526f
+    krb5_free_context(ctx);
b6526f
+}
b6526f
+
b6526f
+void test_kpasswd_and_master_kdc(void **state)
b6526f
+{
b6526f
+    krb5_context ctx;
b6526f
+    krb5_error_code kerr;
b6526f
+    void *priv;
b6526f
+    int fd;
b6526f
+    struct serverlist list = SERVERLIST_INIT;
b6526f
+    struct module_callback_data cbdata = { 0 };
b6526f
+    ssize_t s;
b6526f
+    int ret;
b6526f
+    char host[NI_MAXHOST];
b6526f
+    char service[NI_MAXSERV];
b6526f
+
b6526f
+    cbdata.list = &list;
b6526f
+
b6526f
+    kerr = krb5_init_context (&ctx;;
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+
b6526f
+    kerr = sssd_krb5_locator_init(ctx, &priv;;
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+
b6526f
+    mkdir(TEST_PUBCONF_PATH, 0777);
b6526f
+    fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777);
b6526f
+    assert_int_not_equal(fd, -1);
b6526f
+    s = write(fd, TEST_IP_1_WITH_SERVICE, sizeof(TEST_IP_1_WITH_SERVICE));
b6526f
+    assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE));
b6526f
+    s = write(fd, "\n", 1);
b6526f
+    assert_int_equal(s, 1);
b6526f
+    s = write(fd, TEST_IPV6_1_WITH_SERVICE, sizeof(TEST_IPV6_1_WITH_SERVICE));
b6526f
+    assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE));
b6526f
+    close(fd);
b6526f
+    fd = open(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM, O_CREAT|O_RDWR, 0777);
b6526f
+    assert_int_not_equal(fd, -1);
b6526f
+    s = write(fd, TEST_IP_1_WITH_SERVICE_2, sizeof(TEST_IP_1_WITH_SERVICE_2));
b6526f
+    assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE_2));
b6526f
+    s = write(fd, "\n", 1);
b6526f
+    assert_int_equal(s, 1);
b6526f
+    s = write(fd, TEST_IPV6_1_WITH_SERVICE_1,
b6526f
+              sizeof(TEST_IPV6_1_WITH_SERVICE_1));
b6526f
+    assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE_1));
b6526f
+    close(fd);
b6526f
+
b6526f
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kpasswd, TEST_REALM,
b6526f
+                                    SOCK_DGRAM, AF_INET, module_callback,
b6526f
+                                    &cbdata);
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+    assert_int_equal(list.nservers, 1);
b6526f
+    assert_non_null(list.servers);
b6526f
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
b6526f
+                      list.servers[0].addrlen,
b6526f
+                      host, sizeof(host), service, sizeof(service),
b6526f
+                      NI_NUMERICHOST|NI_NUMERICSERV);
b6526f
+    assert_int_equal(ret, 0);
b6526f
+    assert_string_equal(TEST_IP_1, host);
b6526f
+    assert_string_equal(TEST_SERVICE_2, service);
b6526f
+
b6526f
+    k5_free_serverlist(&list);
b6526f
+
b6526f
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_kpasswd , TEST_REALM,
b6526f
+                                    SOCK_DGRAM, AF_INET6, module_callback,
b6526f
+                                    &cbdata);
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+
b6526f
+    assert_int_equal(list.nservers, 1);
b6526f
+    assert_non_null(list.servers);
b6526f
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
b6526f
+                      list.servers[0].addrlen,
b6526f
+                      host, sizeof(host), service, sizeof(service),
b6526f
+                      NI_NUMERICHOST|NI_NUMERICSERV);
b6526f
+    assert_int_equal(ret, 0);
b6526f
+    assert_string_equal(TEST_IPV6_1_PURE, host);
b6526f
+    assert_string_equal(TEST_SERVICE_1, service);
b6526f
+
b6526f
+    k5_free_serverlist(&list);
b6526f
+
b6526f
+    /* locate_service_master_kdc should use the default KDC port 88 and not
b6526f
+     * the one set in the kpasswdinfo file. */
b6526f
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM,
b6526f
+                                    SOCK_DGRAM, AF_INET, module_callback,
b6526f
+                                    &cbdata);
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+    assert_int_equal(list.nservers, 1);
b6526f
+    assert_non_null(list.servers);
b6526f
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
b6526f
+                      list.servers[0].addrlen,
b6526f
+                      host, sizeof(host), service, sizeof(service),
b6526f
+                      NI_NUMERICHOST|NI_NUMERICSERV);
b6526f
+    assert_int_equal(ret, 0);
b6526f
+    assert_string_equal(TEST_IP_1, host);
b6526f
+    assert_string_equal("88", service);
b6526f
+
b6526f
+    k5_free_serverlist(&list);
b6526f
 
b6526f
+    kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM,
b6526f
+                                    SOCK_DGRAM, AF_INET6, module_callback,
b6526f
+                                    &cbdata);
b6526f
+    assert_int_equal(kerr, 0);
b6526f
+    assert_int_equal(list.nservers, 1);
b6526f
+    assert_non_null(list.servers);
b6526f
+    ret = getnameinfo((struct sockaddr *) &list.servers[0].addr,
b6526f
+                      list.servers[0].addrlen,
b6526f
+                      host, sizeof(host), service, sizeof(service),
b6526f
+                      NI_NUMERICHOST|NI_NUMERICSERV);
b6526f
+    assert_int_equal(ret, 0);
b6526f
+    assert_string_equal(TEST_IPV6_1_PURE, host);
b6526f
+    assert_string_equal("88", service);
b6526f
+
b6526f
+    k5_free_serverlist(&list);
b6526f
+
b6526f
+    unlink(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM);
b6526f
     unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM);
b6526f
     rmdir(TEST_PUBCONF_PATH);
b6526f
     sssd_krb5_locator_close(priv);
b6526f
@@ -606,6 +760,8 @@ int main(int argc, const char *argv[])
b6526f
                                         setup, teardown),
b6526f
         cmocka_unit_test_setup_teardown(test_service,
b6526f
                                         setup, teardown),
b6526f
+        cmocka_unit_test_setup_teardown(test_kpasswd_and_master_kdc,
b6526f
+                                        setup, teardown),
b6526f
     };
b6526f
 
b6526f
     /* Set debug level to invalid value so we can decide if -d 0 was used. */
b6526f
-- 
b6526f
2.19.1
b6526f