Exemple #1
0
/*
 * 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;
}