krb5_error_code 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; 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) { /* Try DNS for all profile errors? */ krb5_error_code code2; code2 = dns_locate_server(context, realm, &al, svc, socktype); if (code2 != KRB5_PLUGIN_NO_HANDLE) code = code2; } #endif /* KRB5_DNS_LOOKUP */ /* 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); krb5_set_error_message(context, KRB5_REALM_CANT_RESOLVE, _("Cannot resolve servers for KDC in realm " "\"%.*s\""), realm->length, realm->data); return KRB5_REALM_CANT_RESOLVE; } *serverlist = al; return 0; }
krb5_error_code k5_locate_server(krb5_context context, const krb5_data *realm, struct serverlist *serverlist, enum locate_service_type svc, krb5_boolean no_udp) { krb5_error_code ret; k5_transport transport = no_udp ? TCP : TCP_OR_UDP; memset(serverlist, 0, sizeof(*serverlist)); if (realm == NULL || realm->data == NULL || realm->data[0] == 0) { k5_setmsg(context, KRB5_REALM_CANT_RESOLVE, "Cannot find KDC for invalid realm name \"\""); return KRB5_REALM_CANT_RESOLVE; } ret = locate_server(context, realm, serverlist, svc, transport); if (ret) return ret; if (serverlist->nservers == 0) { k5_free_serverlist(serverlist); k5_setmsg(context, KRB5_REALM_UNKNOWN, _("Cannot find KDC for realm \"%.*s\""), realm->length, realm->data); return KRB5_REALM_UNKNOWN; } return 0; }
/* * Try all of the server location methods in sequence. transport must be * TCP_OR_UDP, TCP, or UDP. It is applied to hostname entries in the profile * and affects whether we query modules or DNS for UDP or TCP or both, but does * not restrict a method from returning entries of other transports. */ static krb5_error_code locate_server(krb5_context context, const krb5_data *realm, struct serverlist *serverlist, enum locate_service_type svc, k5_transport transport) { 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, transport); 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, transport); if (ret) goto done; #ifdef KRB5_DNS_LOOKUP if (list.nservers == 0) ret = dns_locate_server(context, realm, &list, svc, transport); #endif done: if (ret) { k5_free_serverlist(&list); return ret; } *serverlist = list; return 0; }
static void test_locate_kdc(krb5_context ctx, char *realm) { struct serverlist servers; size_t i; int get_masters = FALSE; krb5_data rlm; krb5_error_code retval; rlm.data = realm; rlm.length = strlen(realm); retval = k5_locate_kdc(ctx, &rlm, &servers, get_masters, 0); if (retval) { com_err("krb5_locate_kdc", retval, 0); return; } printf("krb_locate_kdc(%s) returned:", realm); for (i = 0; i < servers.nservers; i++) { struct server_entry *entry = &servers.servers[i]; if (entry->hostname) { printf(" host:%s/%d", entry->hostname, ntohs(entry->port)); continue; } switch (entry->family) { case AF_INET: { struct sockaddr_in *s_sin = (struct sockaddr_in *)&entry->addr; printf(" inet:%s/%d", inet_ntoa(s_sin->sin_addr), ntohs(s_sin->sin_port)); } break; case AF_INET6: { struct sockaddr_in6 *s_sin6 = (struct sockaddr_in6 *)&entry->addr; int j; printf(" inet6"); for (j = 0; j < 8; j++) printf(":%x", (s_sin6->sin6_addr.s6_addr[2*j] * 256 + s_sin6->sin6_addr.s6_addr[2*j+1])); printf("/%d", ntohs(s_sin6->sin6_port)); break; } default: printf(" unknown-af-%d", entry->family); break; } } k5_free_serverlist(&servers); printf("\n"); }
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->transport) != 0) return FALSE; found = server_list_contains(&list, server); k5_free_serverlist(&list); return found; }
krb5_error_code krb5_sendto_kdc(krb5_context context, const krb5_data *message, const krb5_data *realm, krb5_data *reply, int *use_master, int no_udp) { krb5_error_code retval, err; struct serverlist servers; int server_used; k5_transport_strategy strategy; /* * find KDC location(s) for realm */ /* * BUG: This code won't return "interesting" errors (e.g., out of mem, * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be * ignored from one query of two, but if only one query is done, or * both return that error, it should be returned to the caller. Also, * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} * should probably be returned as well. */ TRACE_SENDTO_KDC(context, message->length, realm, *use_master, no_udp); if (!no_udp && context->udp_pref_limit < 0) { int tmp; retval = profile_get_integer(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0, DEFAULT_UDP_PREF_LIMIT, &tmp); if (retval) return retval; if (tmp < 0) tmp = DEFAULT_UDP_PREF_LIMIT; else if (tmp > HARD_UDP_LIMIT) /* In the unlikely case that a *really* big value is given, let 'em use as big as we think we can support. */ tmp = HARD_UDP_LIMIT; context->udp_pref_limit = tmp; } if (no_udp) strategy = NO_UDP; else if (message->length <= (unsigned int) context->udp_pref_limit) strategy = UDP_FIRST; else strategy = UDP_LAST; retval = k5_locate_kdc(context, realm, &servers, *use_master, no_udp); if (retval) return retval; err = 0; retval = k5_sendto(context, message, realm, &servers, strategy, NULL, reply, NULL, NULL, &server_used, check_for_svc_unavailable, &err); if (retval == KRB5_KDC_UNREACH) { if (err == KDC_ERR_SVC_UNAVAILABLE) { retval = KRB5KDC_ERR_SVC_UNAVAILABLE; } else { k5_setmsg(context, retval, _("Cannot contact any KDC for realm '%.*s'"), realm->length, realm->data); } } if (retval) goto cleanup; /* Set use_master to 1 if we ended up talking to a master when we didn't * explicitly request to. */ if (*use_master == 0) { *use_master = k5_kdc_is_master(context, realm, &servers.servers[server_used]); TRACE_SENDTO_KDC_MASTER(context, *use_master); } cleanup: k5_free_serverlist(&servers); return retval; }
int main (int argc, char *argv[]) { char *p, *realmname; krb5_data realm; krb5_context ctx; krb5_error_code err; int master = 0; p = strrchr (argv[0], '/'); if (p) prog = p+1; else prog = argv[0]; switch (argc) { case 2: /* foo $realm */ realmname = argv[1]; break; case 3: if (!strcmp (argv[1], "-c")) how = LOOKUP_CONF; else if (!strcmp (argv[1], "-d")) how = LOOKUP_DNS; else if (!strcmp (argv[1], "-m")) master = 1; else goto usage; realmname = argv[2]; break; default: usage: fprintf (stderr, "%s: usage: %s [-c | -d | -m] realm\n", prog, prog); return 1; } err = krb5_init_context (&ctx); if (err) kfatal (err); realm.data = realmname; realm.length = strlen (realmname); switch (how) { case LOOKUP_CONF: err = krb5_locate_srv_conf(ctx, &realm, "kdc", &sl, htons(88)); break; case LOOKUP_DNS: err = locate_srv_dns_1(&realm, "_kerberos", "_udp", &sl); break; case LOOKUP_WHATEVER: err = k5_locate_kdc(ctx, &realm, &sl, master, FALSE); break; } if (err) kfatal (err); print_addrs(); k5_free_serverlist(&sl); krb5_free_context(ctx); return 0; }
/* ** The logic for setting and changing a password is mostly the same ** change_set_password handles both cases ** if set_password_for is NULL, then a password change is performed, ** otherwise, the password is set for the principal indicated in set_password_for */ static krb5_error_code change_set_password(krb5_context context, krb5_creds *creds, char *newpw, krb5_principal set_password_for, int *result_code, krb5_data *result_code_string, krb5_data *result_string) { krb5_data chpw_rep; krb5_address remote_kaddr; krb5_boolean use_tcp = 0; GETSOCKNAME_ARG3_TYPE addrlen; krb5_error_code code = 0; char *code_string; int local_result_code; struct sendto_callback_context callback_ctx; struct sendto_callback_info callback_info; struct sockaddr_storage remote_addr; struct serverlist sl = SERVERLIST_INIT; memset(&chpw_rep, 0, sizeof(krb5_data)); memset( &callback_ctx, 0, sizeof(struct sendto_callback_context)); callback_ctx.context = context; callback_ctx.newpw = newpw; callback_ctx.set_password_for = set_password_for; if ((code = krb5_auth_con_init(callback_ctx.context, &callback_ctx.auth_context))) goto cleanup; if ((code = krb5_mk_req_extended(callback_ctx.context, &callback_ctx.auth_context, AP_OPTS_USE_SUBKEY, NULL, creds, &callback_ctx.ap_req))) goto cleanup; callback_ctx.remote_seq_num = callback_ctx.auth_context->remote_seq_number; callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number; do { int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM); code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl, socktype); if (code) break; addrlen = sizeof(remote_addr); callback_info.data = &callback_ctx; callback_info.pfn_callback = kpasswd_sendto_msg_callback; callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; krb5_free_data_contents(callback_ctx.context, &chpw_rep); code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0, &callback_info, &chpw_rep, ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL); if (code) { /* * Here we may want to switch to TCP on some errors. * right? */ break; } if (remote_addr.ss_family == AF_INET) { remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; } else if (remote_addr.ss_family == AF_INET6) { remote_kaddr.addrtype = ADDRTYPE_INET6; remote_kaddr.length = sizeof(ss2sin6(&remote_addr)->sin6_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin6(&remote_addr)->sin6_addr; } else { break; } if ((code = krb5_auth_con_setaddrs(callback_ctx.context, callback_ctx.auth_context, NULL, &remote_kaddr))) break; code = krb5int_rd_chpw_rep(callback_ctx.context, callback_ctx.auth_context, &chpw_rep, &local_result_code, result_string); if (code) { if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; continue; } break; } if (result_code) *result_code = local_result_code; if (result_code_string) { code = krb5_chpw_result_code_string(callback_ctx.context, local_result_code, &code_string); if (code) goto cleanup; result_code_string->length = strlen(code_string); result_code_string->data = malloc(result_code_string->length); if (result_code_string->data == NULL) { code = ENOMEM; goto cleanup; } strncpy(result_code_string->data, code_string, result_code_string->length); } if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; } else { break; } } while (TRUE); cleanup: if (callback_ctx.auth_context != NULL) krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context); k5_free_serverlist(&sl); krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req); krb5_free_data_contents(callback_ctx.context, &chpw_rep); return(code); }
krb5_error_code krb5_sendto_kdc(krb5_context context, const krb5_data *message, const krb5_data *realm, krb5_data *reply, int *use_master, int tcp_only) { krb5_error_code retval, err; struct serverlist servers; int socktype1 = 0, socktype2 = 0, server_used; /* * find KDC location(s) for realm */ /* * BUG: This code won't return "interesting" errors (e.g., out of mem, * bad config file) from locate_kdc. KRB5_REALM_CANT_RESOLVE can be * ignored from one query of two, but if only one query is done, or * both return that error, it should be returned to the caller. Also, * "interesting" errors (not KRB5_KDC_UNREACH) from sendto_{udp,tcp} * should probably be returned as well. */ dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", message->length, message->data, realm, *use_master, tcp_only); TRACE_SENDTO_KDC(context, message->length, realm, *use_master, tcp_only); if (!tcp_only && context->udp_pref_limit < 0) { int tmp; retval = profile_get_integer(context->profile, KRB5_CONF_LIBDEFAULTS, KRB5_CONF_UDP_PREFERENCE_LIMIT, 0, DEFAULT_UDP_PREF_LIMIT, &tmp); if (retval) return retval; if (tmp < 0) tmp = DEFAULT_UDP_PREF_LIMIT; else if (tmp > HARD_UDP_LIMIT) /* In the unlikely case that a *really* big value is given, let 'em use as big as we think we can support. */ tmp = HARD_UDP_LIMIT; context->udp_pref_limit = tmp; } if (tcp_only) socktype1 = SOCK_STREAM, socktype2 = 0; else if (message->length <= (unsigned int) context->udp_pref_limit) socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; else socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; retval = k5_locate_kdc(context, realm, &servers, *use_master, tcp_only ? SOCK_STREAM : 0); if (retval) return retval; retval = k5_sendto(context, message, &servers, socktype1, socktype2, NULL, reply, NULL, NULL, &server_used, check_for_svc_unavailable, &err); if (retval == KRB5_KDC_UNREACH) { if (err == KDC_ERR_SVC_UNAVAILABLE) { retval = KRB5KDC_ERR_SVC_UNAVAILABLE; } else { krb5_set_error_message(context, retval, "Cannot contact any KDC for realm '%.*s'", realm->length, realm->data); } } if (retval) goto cleanup; /* 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); } TRACE_SENDTO_KDC_MASTER(context, *use_master); retval = 0; } cleanup: k5_free_serverlist(&servers); return retval; }