/* * Solaris Kerberos * Same as krb5_sendto_kdc plus an extra arg to return the FQDN * of the KDC sent the request. * Caller (at top of stack) needs to free hostname_used. */ krb5_error_code krb5_sendto_kdc2 (krb5_context context, const krb5_data *message, const krb5_data *realm, krb5_data *reply, int *use_master, int tcp_only, char **hostname_used) { krb5_error_code retval, retval2; struct addrlist addrs = ADDRLIST_INIT; /* Solaris Kerberos */ int socktype1 = 0, socktype2 = 0, addr_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. */ /*LINTED*/ dprint("krb5_sendto_kdc(%d@%p, \"%D\", use_master=%d, tcp_only=%d)\n", /*LINTED*/ message->length, message->data, realm, *use_master, tcp_only); if (!tcp_only && context->udp_pref_limit < 0) { int tmp; retval = profile_get_integer(context->profile, "libdefaults", "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; } retval = (*use_master ? KRB5_KDC_UNREACH : KRB5_REALM_UNKNOWN); if (tcp_only) socktype1 = SOCK_STREAM, socktype2 = 0; else if (message->length <= context->udp_pref_limit) socktype1 = SOCK_DGRAM, socktype2 = SOCK_STREAM; else socktype1 = SOCK_STREAM, socktype2 = SOCK_DGRAM; retval = krb5_locate_kdc(context, realm, &addrs, *use_master, socktype1, 0); if (socktype2) { struct addrlist addrs2; retval2 = krb5_locate_kdc(context, realm, &addrs2, *use_master, socktype2, 0); #if 0 if (retval2 == 0) { (void) merge_addrlists(&addrs, &addrs2); krb5int_free_addrlist(&addrs2); retval = 0; } else if (retval == KRB5_REALM_CANT_RESOLVE) { retval = retval2; } #else retval = retval2; if (retval == 0) { (void) merge_addrlists(&addrs, &addrs2); krb5int_free_addrlist(&addrs2); } #endif } if (addrs.naddrs > 0) { krb5_error_code err = 0; retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0, 0, 0, &addr_used, check_for_svc_unavailable, &err); switch (retval) { case 0: /* * 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 addrlist addrs3; retval = krb5_locate_kdc(context, realm, &addrs3, 1, addrs.addrs[addr_used].ai->ai_socktype, addrs.addrs[addr_used].ai->ai_family); if (retval == 0) { if (in_addrlist(addrs.addrs[addr_used].ai, &addrs3)) *use_master = 1; krb5int_free_addrlist (&addrs3); } } if (hostname_used) { struct sockaddr *sa; char buf[NI_MAXHOST]; int err; *hostname_used = NULL; sa = addrs.addrs[addr_used].ai->ai_addr; err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0, AI_CANONNAME); if (err) err = getnameinfo (sa, socklen (sa), buf, sizeof (buf), 0, 0, NI_NUMERICHOST); if (!err) *hostname_used = strdup(buf); /* don't sweat strdup fail */ } krb5int_free_addrlist (&addrs); return 0; default: break; /* Cases here are for constructing useful error messages. */ case KRB5_KDC_UNREACH: if (err == KDC_ERR_SVC_UNAVAILABLE) { retval = KRB5KDC_ERR_SVC_UNAVAILABLE; } else { krb5_set_error_message(context, retval, dgettext(TEXT_DOMAIN, "Cannot contact any KDC for realm '%.*s'"), realm->length, realm->data); } break; } krb5int_free_addrlist (&addrs); } return retval; }
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; }