From f4b1a7e7b80ce68e57912edcd48c39ea62c73e43 Mon Sep 17 00:00:00 2001 From: Greg Hudson 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