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