Blob Blame History Raw
From f4b1a7e7b80ce68e57912edcd48c39ea62c73e43 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sun, 6 Apr 2014 18:06:14 -0400
Subject: [PATCH 02/13] Add helper to determine if a KDC is the master

Add a new function k5_kdc_is_master in locate_kdc.c to determine
whether a KDC matches one of the masters, and use it in
krb5_sendto_kdc.
---
 src/lib/krb5/os/locate_kdc.c | 110 +++++++++++++++++++++++++++++--------------
 src/lib/krb5/os/os-proto.h   |   3 ++
 src/lib/krb5/os/sendto_kdc.c |  31 +-----------
 3 files changed, 80 insertions(+), 64 deletions(-)

diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c
index 88d55a8..4479465 100644
--- a/src/lib/krb5/os/locate_kdc.c
+++ b/src/lib/krb5/os/locate_kdc.c
@@ -166,6 +166,24 @@ add_host_to_list(struct serverlist *list, const char *hostname, int port,
     return 0;
 }
 
+/* Return true if server is identical to an entry in list. */
+static krb5_boolean
+server_list_contains(struct serverlist *list, struct server_entry *server)
+{
+    struct server_entry *ent;
+
+    for (ent = list->servers; ent < list->servers + list->nservers; ent++) {
+        if (server->hostname != NULL && ent->hostname != NULL &&
+            strcmp(server->hostname, ent->hostname) == 0)
+            return TRUE;
+        if (server->hostname == NULL && ent->hostname == NULL &&
+            server->addrlen == ent->addrlen &&
+            memcmp(&server->addr, &ent->addr, server->addrlen) == 0)
+            return TRUE;
+    }
+    return FALSE;
+}
+
 static krb5_error_code
 locate_srv_conf_1(krb5_context context, const krb5_data *realm,
                   const char * name, struct serverlist *serverlist,
@@ -529,6 +547,41 @@ dns_locate_server(krb5_context context, const krb5_data *realm,
 }
 #endif /* KRB5_DNS_LOOKUP */
 
+static krb5_error_code
+locate_server(krb5_context context, const krb5_data *realm,
+              struct serverlist *serverlist, enum locate_service_type svc,
+              int socktype)
+{
+    krb5_error_code ret;
+    struct serverlist list = SERVERLIST_INIT;
+
+    *serverlist = list;
+
+    /* Try modules.  If a module returns 0 but leaves the list empty, return an
+     * empty list. */
+    ret = module_locate_server(context, realm, &list, svc, socktype);
+    if (ret != KRB5_PLUGIN_NO_HANDLE)
+        goto done;
+
+    /* Try the profile.  Fall back to DNS if it returns an empty list. */
+    ret = prof_locate_server(context, realm, &list, svc, socktype);
+    if (ret)
+        goto done;
+
+#ifdef KRB5_DNS_LOOKUP
+    if (list.nservers == 0)
+        ret = dns_locate_server(context, realm, &list, svc, socktype);
+#endif
+
+done:
+    if (ret) {
+        k5_free_serverlist(&list);
+        return ret;
+    }
+    *serverlist = list;
+    return 0;
+}
+
 /*
  * Wrapper function for the various backends
  */
@@ -538,54 +591,26 @@ k5_locate_server(krb5_context context, const krb5_data *realm,
                  struct serverlist *serverlist, enum locate_service_type svc,
                  int socktype)
 {
-    krb5_error_code code;
-    struct serverlist al = SERVERLIST_INIT;
-
-    *serverlist = al;
+    krb5_error_code ret;
 
+    memset(serverlist, 0, sizeof(*serverlist));
     if (realm == NULL || realm->data == NULL || realm->data[0] == 0) {
         krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE,
                                "Cannot find KDC for invalid realm name \"\"");
         return KRB5_REALM_CANT_RESOLVE;
     }
 
-    code = module_locate_server(context, realm, &al, svc, socktype);
-    Tprintf("module_locate_server returns %d\n", code);
-    if (code == KRB5_PLUGIN_NO_HANDLE) {
-        /*
-         * We always try the local file before DNS.  Note that there
-         * is no way to indicate "service not available" via the
-         * config file.
-         */
-
-        code = prof_locate_server(context, realm, &al, svc, socktype);
-
-#ifdef KRB5_DNS_LOOKUP
-        if (code == 0 && al.nservers == 0)
-            code = dns_locate_server(context, realm, &al, svc, socktype);
-#endif /* KRB5_DNS_LOOKUP */
+    ret = locate_server(context, realm, serverlist, svc, socktype);
+    if (ret)
+        return ret;
 
-        /* We could put more heuristics here, like looking up a hostname
-           of "kerberos."+REALM, etc.  */
-    }
-    if (code == 0)
-        Tprintf ("krb5int_locate_server found %d addresses\n",
-                 al.nservers);
-    else
-        Tprintf ("krb5int_locate_server returning error code %d/%s\n",
-                 code, error_message(code));
-    if (code != 0) {
-        k5_free_serverlist(&al);
-        return code;
-    }
-    if (al.nservers == 0) {       /* No good servers */
-        k5_free_serverlist(&al);
+    if (serverlist->nservers == 0) {
+        k5_free_serverlist(serverlist);
         krb5_set_error_message(context, KRB5_REALM_UNKNOWN,
                                _("Cannot find KDC for realm \"%.*s\""),
                                realm->length, realm->data);
         return KRB5_REALM_UNKNOWN;
     }
-    *serverlist = al;
     return 0;
 }
 
@@ -598,3 +623,18 @@ k5_locate_kdc(krb5_context context, const krb5_data *realm,
     stype = get_masters ? locate_service_master_kdc : locate_service_kdc;
     return k5_locate_server(context, realm, serverlist, stype, socktype);
 }
+
+krb5_boolean
+k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+                 struct server_entry *server)
+{
+    struct serverlist list;
+    krb5_boolean found;
+
+    if (locate_server(context, realm, &list, locate_service_master_kdc,
+                      server->socktype) != 0)
+        return FALSE;
+    found = server_list_contains(&list, server);
+    k5_free_serverlist(&list);
+    return found;
+}
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index c6b730f..9125ba0 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -76,6 +76,9 @@ krb5_error_code k5_locate_kdc(krb5_context context, const krb5_data *realm,
                               struct serverlist *serverlist, int get_masters,
                               int socktype);
 
+krb5_boolean k5_kdc_is_master(krb5_context context, const krb5_data *realm,
+                              struct server_entry *server);
+
 void k5_free_serverlist(struct serverlist *);
 
 #ifdef HAVE_NETINET_IN_H
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 5f781d3..e3855a3 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -293,25 +293,6 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
 }
 
 static int
-in_addrlist(struct server_entry *entry, struct serverlist *list)
-{
-    size_t i;
-    struct server_entry *le;
-
-    for (i = 0; i < list->nservers; i++) {
-        le = &list->servers[i];
-        if (entry->hostname != NULL && le->hostname != NULL &&
-            strcmp(entry->hostname, le->hostname) == 0)
-            return 1;
-        if (entry->hostname == NULL && le->hostname == NULL &&
-            entry->addrlen == le->addrlen &&
-            memcmp(&entry->addr, &le->addr, entry->addrlen) == 0)
-            return 1;
-    }
-    return 0;
-}
-
-static int
 check_for_svc_unavailable (krb5_context context,
                            const krb5_data *reply,
                            void *msg_handler_data)
@@ -418,17 +399,9 @@ krb5_sendto_kdc(krb5_context context, const krb5_data *message,
     /* Set use_master to 1 if we ended up talking to a master when we didn't
      * explicitly request to. */
     if (*use_master == 0) {
-        struct serverlist mservers;
-        struct server_entry *entry = &servers.servers[server_used];
-        retval = k5_locate_kdc(context, realm, &mservers, TRUE,
-                               entry->socktype);
-        if (retval == 0) {
-            if (in_addrlist(entry, &mservers))
-                *use_master = 1;
-            k5_free_serverlist(&mservers);
-        }
+        *use_master = k5_kdc_is_master(context, realm,
+                                       &servers.servers[server_used]);
         TRACE_SENDTO_KDC_MASTER(context, *use_master);
-        retval = 0;
     }
 
 cleanup:
-- 
2.1.0