Пример #1
0
static void
one_addr(krb5_address *a)
{
    struct sockaddr_storage ss;
    struct sockaddr_in *sinp;
    struct sockaddr_in6 *sin6p;
    int err;
    char namebuf[NI_MAXHOST];

    memset(&ss, 0, sizeof(ss));

    switch (a->addrtype) {
    case ADDRTYPE_INET:
        if (a->length != 4) {
            printf(_("broken address (type %d length %d)"),
                   a->addrtype, a->length);
            return;
        }
        sinp = ss2sin(&ss);
        sinp->sin_family = AF_INET;
        memcpy(&sinp->sin_addr, a->contents, 4);
        break;
    case ADDRTYPE_INET6:
        if (a->length != 16) {
            printf(_("broken address (type %d length %d)"),
                   a->addrtype, a->length);
            return;
        }
        sin6p = ss2sin6(&ss);
        sin6p->sin6_family = AF_INET6;
        memcpy(&sin6p->sin6_addr, a->contents, 16);
        break;
    default:
        printf(_("unknown addrtype %d"), a->addrtype);
        return;
    }

    namebuf[0] = 0;
    err = getnameinfo(ss2sa(&ss), sa_socklen(ss2sa(&ss)), namebuf,
                      sizeof(namebuf), 0, 0,
                      no_resolve ? NI_NUMERICHOST : 0U);
    if (err) {
        printf(_("unprintable address (type %d, error %d %s)"), a->addrtype,
               err, gai_strerror(err));
        return;
    }
    printf("%s", namebuf);
}
Пример #2
0
static void *cvtaddr (struct sockaddr_storage *a, struct addrpair *ap)
{
    switch (ss2sa(a)->sa_family) {
    case AF_INET:
        SET (ap->port, ss2sin(a)->sin_port, ADDRTYPE_IPPORT);
        SET (ap->addr, ss2sin(a)->sin_addr, ADDRTYPE_INET);
        return a;
    case AF_INET6:
        SET (ap->port, ss2sin6(a)->sin6_port, ADDRTYPE_IPPORT);
        if (IN6_IS_ADDR_V4MAPPED (&ss2sin6(a)->sin6_addr)) {
            ap->addr.addrtype = ADDRTYPE_INET;
            ap->addr.contents = 12 + (krb5_octet *) &ss2sin6(a)->sin6_addr;
            ap->addr.length = 4;
        } else
            SET (ap->addr, ss2sin6(a)->sin6_addr, ADDRTYPE_INET6);
        return a;
    default:
        return 0;
    }
}
Пример #3
0
/*
 * Bind a socket to a privileged IP port
 */
int
bindresvport_sa(int sd, struct sockaddr *sa)
{
	int res;
	static short port;
	struct sockaddr_storage myaddr;
	socklen_t salen;
	int i;

#define STARTPORT 600
#define ENDPORT (IPPORT_RESERVED - 1)
#define NPORTS	(ENDPORT - STARTPORT + 1)
	if (sa == NULL) {
		salen = sizeof(myaddr);
		sa = ss2sa(&myaddr);
		res = getsockname(sd, sa, &salen);
		if (res < 0)
			return (-1);
	}
	if (!sa_is_inet(sa)) {
		errno = EPFNOSUPPORT;
		return (-1);
	}
	if (port == 0) {
		port = (getpid() % NPORTS) + STARTPORT;
	}
	res = -1;
	errno = EADDRINUSE;
	for (i = 0; i < NPORTS && res < 0 && errno == EADDRINUSE; i++) {
		sa_setport(sa, htons(port++));
		if (port > ENDPORT) {
			port = STARTPORT;
		}
		res = bind(sd, sa, socklen(sa));
	}
	return (res);
}
Пример #4
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);
}
Пример #5
0
static int
kpasswd_sendto_msg_callback(SOCKET fd, void *data, krb5_data *message)
{
    krb5_error_code                     code = 0;
    struct sockaddr_storage             local_addr;
    krb5_address                        local_kaddr;
    struct sendto_callback_context      *ctx = data;
    GETSOCKNAME_ARG3_TYPE               addrlen;
    krb5_data                           output;

    memset (message, 0, sizeof(krb5_data));

    /*
     * We need the local addr from the connection socket
     */
    addrlen = sizeof(local_addr);

    if (getsockname(fd, ss2sa(&local_addr), &addrlen) < 0) {
        code = SOCKET_ERRNO;
        goto cleanup;
    }

    /* some brain-dead OS's don't return useful information from
     * the getsockname call.  Namely, windows and solaris.  */

    if (local_addr.ss_family == AF_INET &&
        ss2sin(&local_addr)->sin_addr.s_addr != 0) {
        local_kaddr.addrtype = ADDRTYPE_INET;
        local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr);
        local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr;
    } else if (local_addr.ss_family == AF_INET6 &&
               memcmp(ss2sin6(&local_addr)->sin6_addr.s6_addr,
                      in6addr_any.s6_addr, sizeof(in6addr_any.s6_addr)) != 0) {
        local_kaddr.addrtype = ADDRTYPE_INET6;
        local_kaddr.length = sizeof(ss2sin6(&local_addr)->sin6_addr);
        local_kaddr.contents = (krb5_octet *) &ss2sin6(&local_addr)->sin6_addr;
    } else {
        krb5_address **addrs;

        code = krb5_os_localaddr(ctx->context, &addrs);
        if (code)
            goto cleanup;

        local_kaddr.magic = addrs[0]->magic;
        local_kaddr.addrtype = addrs[0]->addrtype;
        local_kaddr.length = addrs[0]->length;
        local_kaddr.contents = k5memdup(addrs[0]->contents, addrs[0]->length,
                                        &code);
        krb5_free_addresses(ctx->context, addrs);
        if (local_kaddr.contents == NULL)
            goto cleanup;
    }


    /*
     * TBD:  Does this tamper w/ the auth context in such a way
     * to break us?  Yes - provide 1 per conn-state / host...
     */


    if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context,
                                       &local_kaddr, NULL)))
        goto cleanup;

    ctx->auth_context->remote_seq_number = ctx->remote_seq_num;
    ctx->auth_context->local_seq_number = ctx->local_seq_num;

    if (ctx->set_password_for)
        code = krb5int_mk_setpw_req(ctx->context,
                                    ctx->auth_context,
                                    &ctx->ap_req,
                                    ctx->set_password_for,
                                    ctx->newpw,
                                    &output);
    else
        code = krb5int_mk_chpw_req(ctx->context,
                                   ctx->auth_context,
                                   &ctx->ap_req,
                                   ctx->newpw,
                                   &output);
    if (code)
        goto cleanup;

    message->length = output.length;
    message->data = output.data;

cleanup:
    return code;
}
Пример #6
0
static krb5_error_code
process_chpw_request(krb5_context context, void *server_handle, char *realm,
                     krb5_keytab keytab, const krb5_fulladdr *local_faddr,
                     const krb5_fulladdr *remote_faddr, krb5_data *req,
                     krb5_data *rep)
{
    krb5_error_code ret;
    char *ptr;
    unsigned int plen, vno;
    krb5_data ap_req, ap_rep = empty_data();
    krb5_data cipher = empty_data(), clear = empty_data();
    krb5_auth_context auth_context = NULL;
    krb5_principal changepw = NULL;
    krb5_principal client, target = NULL;
    krb5_ticket *ticket = NULL;
    krb5_replay_data replay;
    krb5_error krberror;
    int numresult;
    char strresult[1024];
    char *clientstr = NULL, *targetstr = NULL;
    const char *errmsg = NULL;
    size_t clen;
    char *cdots;
    struct sockaddr_storage ss;
    socklen_t salen;
    char addrbuf[100];
    krb5_address *addr = remote_faddr->address;

    *rep = empty_data();

    if (req->length < 4) {
        /* either this, or the server is printing bad messages,
           or the caller passed in garbage */
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated", sizeof(strresult));
        goto bailout;
    }

    ptr = req->data;

    /* verify length */

    plen = (*ptr++ & 0xff);
    plen = (plen<<8) | (*ptr++ & 0xff);

    if (plen != req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request length was inconsistent",
                sizeof(strresult));
        goto bailout;
    }

    /* verify version number */

    vno = (*ptr++ & 0xff) ;
    vno = (vno<<8) | (*ptr++ & 0xff);

    if (vno != 1 && vno != RFC3244_VERSION) {
        ret = KRB5KDC_ERR_BAD_PVNO;
        numresult = KRB5_KPASSWD_BAD_VERSION;
        snprintf(strresult, sizeof(strresult),
                 "Request contained unknown protocol version number %d", vno);
        goto bailout;
    }

    /* read, check ap-req length */

    ap_req.length = (*ptr++ & 0xff);
    ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);

    if (ptr + ap_req.length >= req->data + req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated in AP-REQ",
                sizeof(strresult));
        goto bailout;
    }

    /* verify ap_req */

    ap_req.data = ptr;
    ptr += ap_req.length;

    ret = krb5_auth_con_init(context, &auth_context);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_auth_con_setflags(context, auth_context,
                                 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
                               "kadmin", "changepw", NULL);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed building kadmin/changepw principal",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
                      NULL, &ticket);

    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed reading application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* construct the ap-rep */

    ret = krb5_mk_rep(context, auth_context, &ap_rep);
    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed replying to application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* decrypt the ChangePasswdData */

    cipher.length = (req->data + req->length) - ptr;
    cipher.data = ptr;

    /*
     * Don't set a remote address in auth_context before calling krb5_rd_priv,
     * so that we can work against clients behind a NAT.  Reflection attacks
     * aren't a concern since we use sequence numbers and since our requests
     * don't look anything like our responses.  Also don't set a local address,
     * since we don't know what interface the request was received on.
     */

    ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
        goto chpwfail;
    }

    client = ticket->enc_part2->client;

    /* decode ChangePasswdData for setpw requests */
    if (vno == RFC3244_VERSION) {
        krb5_data *clear_data;

        ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
        if (ret != 0) {
            numresult = KRB5_KPASSWD_MALFORMED;
            strlcpy(strresult, "Failed decoding ChangePasswdData",
                    sizeof(strresult));
            goto chpwfail;
        }

        zapfree(clear.data, clear.length);

        clear = *clear_data;
        free(clear_data);

        if (target != NULL) {
            ret = krb5_unparse_name(context, target, &targetstr);
            if (ret != 0) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed unparsing target name for log",
                        sizeof(strresult));
                goto chpwfail;
            }
        }
    }

    ret = krb5_unparse_name(context, client, &clientstr);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed unparsing client name for log",
                sizeof(strresult));
        goto chpwfail;
    }

    /* for cpw, verify that this is an AS_REQ ticket */
    if (vno == 1 &&
        (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
        strlcpy(strresult, "Ticket must be derived from a password",
                sizeof(strresult));
        goto chpwfail;
    }

    /* change the password */

    ptr = k5memdup0(clear.data, clear.length, &ret);
    ret = schpw_util_wrapper(server_handle, client, target,
                             (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
                             ptr, NULL, strresult, sizeof(strresult));
    if (ret)
        errmsg = krb5_get_error_message(context, ret);

    /* zap the password */
    zapfree(clear.data, clear.length);
    zapfree(ptr, clear.length);
    clear = empty_data();

    clen = strlen(clientstr);
    trunc_name(&clen, &cdots);

    switch (addr->addrtype) {
    case ADDRTYPE_INET: {
        struct sockaddr_in *sin = ss2sin(&ss);

        sin->sin_family = AF_INET;
        memcpy(&sin->sin_addr, addr->contents, addr->length);
        sin->sin_port = htons(remote_faddr->port);
        salen = sizeof(*sin);
        break;
    }
    case ADDRTYPE_INET6: {
        struct sockaddr_in6 *sin6 = ss2sin6(&ss);

        sin6->sin6_family = AF_INET6;
        memcpy(&sin6->sin6_addr, addr->contents, addr->length);
        sin6->sin6_port = htons(remote_faddr->port);
        salen = sizeof(*sin6);
        break;
    }
    default: {
        struct sockaddr *sa = ss2sa(&ss);

        sa->sa_family = AF_UNSPEC;
        salen = sizeof(*sa);
        break;
    }
    }

    if (getnameinfo(ss2sa(&ss), salen,
                    addrbuf, sizeof(addrbuf), NULL, 0,
                    NI_NUMERICHOST | NI_NUMERICSERV) != 0)
        strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));

    if (vno == RFC3244_VERSION) {
        size_t tlen;
        char *tdots;
        const char *targetp;

        if (target == NULL) {
            tlen = clen;
            tdots = cdots;
            targetp = targetstr;
        } else {
            tlen = strlen(targetstr);
            trunc_name(&tlen, &tdots);
            targetp = clientstr;
        }

        krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for "
                                       "%.*s%s: %s"), addrbuf, (int) clen,
                         clientstr, cdots, (int) tlen, targetp, tdots,
                         errmsg ? errmsg : "success");
    } else {
        krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"),
                         addrbuf, (int) clen, clientstr, cdots,
                         errmsg ? errmsg : "success");
    }
    switch (ret) {
    case KADM5_AUTH_CHANGEPW:
        numresult = KRB5_KPASSWD_ACCESSDENIED;
        break;
    case KADM5_PASS_Q_TOOSHORT:
    case KADM5_PASS_REUSE:
    case KADM5_PASS_Q_CLASS:
    case KADM5_PASS_Q_DICT:
    case KADM5_PASS_Q_GENERIC:
    case KADM5_PASS_TOOSOON:
        numresult = KRB5_KPASSWD_SOFTERROR;
        break;
    case 0:
        numresult = KRB5_KPASSWD_SUCCESS;
        strlcpy(strresult, "", sizeof(strresult));
        break;
    default:
        numresult = KRB5_KPASSWD_HARDERROR;
        break;
    }

chpwfail:

    ret = alloc_data(&clear, 2 + strlen(strresult));
    if (ret)
        goto bailout;

    ptr = clear.data;

    *ptr++ = (numresult>>8) & 0xff;
    *ptr++ = numresult & 0xff;

    memcpy(ptr, strresult, strlen(strresult));

    cipher = empty_data();

    if (ap_rep.length) {
        ret = krb5_auth_con_setaddrs(context, auth_context,
                                     local_faddr->address, NULL);
        if (ret) {
            numresult = KRB5_KPASSWD_HARDERROR;
            strlcpy(strresult,
                    "Failed storing client and server internet addresses",
                    sizeof(strresult));
        } else {
            ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
                               &replay);
            if (ret) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed encrypting reply",
                        sizeof(strresult));
            }
        }
    }

    /* if no KRB-PRIV was constructed, then we need a KRB-ERROR.
       if this fails, just bail.  there's nothing else we can do. */

    if (cipher.length == 0) {
        /* clear out ap_rep now, so that it won't be inserted in the
           reply */

        if (ap_rep.length) {
            free(ap_rep.data);
            ap_rep = empty_data();
        }

        krberror.ctime = 0;
        krberror.cusec = 0;
        krberror.susec = 0;
        ret = krb5_timeofday(context, &krberror.stime);
        if (ret)
            goto bailout;

        /* this is really icky.  but it's what all the other callers
           to mk_error do. */
        krberror.error = ret;
        krberror.error -= ERROR_TABLE_BASE_krb5;
        if (krberror.error < 0 || krberror.error > KRB_ERR_MAX)
            krberror.error = KRB_ERR_GENERIC;

        krberror.client = NULL;

        ret = krb5_build_principal(context, &krberror.server,
                                   strlen(realm), realm,
                                   "kadmin", "changepw", NULL);
        if (ret)
            goto bailout;
        krberror.text.length = 0;
        krberror.e_data = clear;

        ret = krb5_mk_error(context, &krberror, &cipher);

        krb5_free_principal(context, krberror.server);

        if (ret)
            goto bailout;
    }

    /* construct the reply */

    ret = alloc_data(rep, 6 + ap_rep.length + cipher.length);
    if (ret)
        goto bailout;
    ptr = rep->data;

    /* length */

    *ptr++ = (rep->length>>8) & 0xff;
    *ptr++ = rep->length & 0xff;

    /* version == 0x0001 big-endian */

    *ptr++ = 0;
    *ptr++ = 1;

    /* ap_rep length, big-endian */

    *ptr++ = (ap_rep.length>>8) & 0xff;
    *ptr++ = ap_rep.length & 0xff;

    /* ap-rep data */

    if (ap_rep.length) {
        memcpy(ptr, ap_rep.data, ap_rep.length);
        ptr += ap_rep.length;
    }

    /* krb-priv or krb-error */

    memcpy(ptr, cipher.data, cipher.length);

bailout:
    krb5_auth_con_free(context, auth_context);
    krb5_free_principal(context, changepw);
    krb5_free_ticket(context, ticket);
    free(ap_rep.data);
    free(clear.data);
    free(cipher.data);
    krb5_free_principal(context, target);
    krb5_free_unparsed_name(context, targetstr);
    krb5_free_unparsed_name(context, clientstr);
    krb5_free_error_message(context, errmsg);
    return ret;
}
Пример #7
0
int
foreach_localaddr (void *data,
		   int (*pass1fn) (void *, struct sockaddr *),
		   int (*betweenfn) (void *),
		   int (*pass2fn) (void *, struct sockaddr *))
{
    /* Okay, this is kind of odd.  We have to use each of the address
       families we care about, because with an AF_INET socket, extra
       interfaces like hme0:1 that have only AF_INET6 addresses will
       cause errors.  Similarly, if hme0 has more AF_INET addresses
       than AF_INET6 addresses, we won't be able to retrieve all of
       the AF_INET addresses if we use an AF_INET6 socket.  Since
       neither family is guaranteed to have the greater number of
       addresses, we should use both.

       If it weren't for this little quirk, we could use one socket of
       any type, and ask for addresses of all types.  At least, it
       seems to work that way.  */

    /* Solaris kerberos: avoid using AF_NS if no define */
#if defined (KRB5_USE_INET6) && defined (KRB5_USE_NS)
    static const int afs[] = { AF_INET, AF_NS, AF_INET6 };
#elif defined (KRB5_USE_INET6)
    static const int afs[] = { AF_INET, AF_INET6 };
#else
    static const int afs[] = { AF_INET };
#endif
    
#define N_AFS (sizeof (afs) / sizeof (afs[0]))
    struct {
	int af;
	int sock;
	void *buf;
	size_t buf_size;
	struct lifnum lifnum;
    } afp[N_AFS];
    int code, i, j;
    int retval = 0, afidx;
    krb5_error_code sock_err = 0;
    struct lifreq *lifr, lifreq, *lifr2;

#define FOREACH_AF() for (afidx = 0; afidx < N_AFS; afidx++)
#define P (afp[afidx])

    KRB5_LOG0(KRB5_INFO, "foreach_localaddr() start");
    /* init */
    FOREACH_AF () {
	P.af = afs[afidx];
	P.sock = -1;
	P.buf = 0;
    }

    /* first pass: get raw data, discard uninteresting addresses, callback */
    FOREACH_AF () {
	KRB5_LOG (KRB5_INFO, "foreach_localaddr() trying af %d", P.af);
	P.sock = socket (P.af, USE_TYPE, USE_PROTO);
	if (P.sock < 0) {
	    sock_err = SOCKET_ERROR;
	    Tperror ("socket");
	    continue;
	}

	P.lifnum.lifn_family = P.af;
	P.lifnum.lifn_flags = 0;
	P.lifnum.lifn_count = 0;
	code = ioctl (P.sock, SIOCGLIFNUM, &P.lifnum);
	if (code) {
	    Tperror ("ioctl(SIOCGLIFNUM)");
	    retval = errno;
	    goto punt;
	}

	KRB5_LOG (KRB5_INFO, "foreach_localaddr() lifn_count %d",
		P.lifnum.lifn_count);
	P.buf_size = P.lifnum.lifn_count * sizeof (struct lifreq) * 2;
	P.buf = malloc (P.buf_size);
	if (P.buf == NULL) {
	    retval = errno;
	    goto punt;
	}

	code = get_lifconf (P.af, P.sock, &P.buf_size, P.buf);
	if (code < 0) {
	    retval = errno;
	    goto punt;
	}

	for (i = 0; i < P.buf_size; i+= sizeof (*lifr)) {
	    /*LINTED*/
	    lifr = (struct lifreq *)((caddr_t) P.buf+i);

	    strncpy(lifreq.lifr_name, lifr->lifr_name,
		    sizeof (lifreq.lifr_name));
	    KRB5_LOG (KRB5_INFO, "foreach_localaddr() interface %s",
		    lifreq.lifr_name);
	    /* ioctl unknown to lclint */
	    if (ioctl (P.sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
		Tperror ("ioctl(SIOCGLIFFLAGS)");
	    skip:
		KRB5_LOG (KRB5_INFO, 
			"foreach_localaddr() skipping interface %s",
			lifr->lifr_name);
		/* mark for next pass */
		lifr->lifr_name[0] = '\0';
		continue;
	    }

#ifdef IFF_LOOPBACK
	    /* None of the current callers want loopback addresses.  */
	    if (lifreq.lifr_flags & IFF_LOOPBACK) {
		Tprintf (("  loopback\n"));
		goto skip;
	    }
#endif
	    /* Ignore interfaces that are down.  */
	    if ((lifreq.lifr_flags & IFF_UP) == 0) {
		Tprintf (("  down\n"));
		goto skip;
	    }

	    /* Make sure we didn't process this address already.  */
	    for (j = 0; j < i; j += sizeof (*lifr2)) {
		/*LINTED*/
		lifr2 = (struct lifreq *)((caddr_t) P.buf+j);
		if (lifr2->lifr_name[0] == '\0')
		    continue;
		if (lifr2->lifr_addr.ss_family == lifr->lifr_addr.ss_family
		    /* Compare address info.  If this isn't good enough --
		       i.e., if random padding bytes turn out to differ
		       when the addresses are the same -- then we'll have
		       to do it on a per address family basis.  */
		    && !memcmp (&lifr2->lifr_addr, &lifr->lifr_addr,
				sizeof (*lifr))) {
		    Tprintf (("  duplicate addr\n"));
		    KRB5_LOG0 (KRB5_INFO, "foreach_localaddr() dup addr");
		    goto skip;
		}
	    }

	    if ((*pass1fn) (data, ss2sa (&lifr->lifr_addr)))
		goto punt;
	}
    }

    /* Did we actually get any working sockets?  */
    FOREACH_AF ()
	if (P.sock != -1)
	    goto have_working_socket;
    retval = sock_err;
    goto punt;
have_working_socket:

    if (betweenfn != NULL && (*betweenfn)(data))
	goto punt;

    if (pass2fn)
	FOREACH_AF ()
	    if (P.sock >= 0) {
		for (i = 0; i < P.buf_size; i+= sizeof (*lifr)) {
		    /*LINTED*/
		    lifr = (struct lifreq *)((caddr_t) P.buf+i);

		    if (lifr->lifr_name[0] == '\0')
			/* Marked in first pass to be ignored.  */
			continue;

		    KRB5_LOG (KRB5_INFO,
			    "foreach_localaddr() doing pass2fn i = %d",
			    i);
		    if ((*pass2fn) (data, ss2sa (&lifr->lifr_addr)))
			goto punt;
		}
	    }
punt:
    FOREACH_AF () {
	closesocket(P.sock);
	free (P.buf);
    }

    return retval;
}
Пример #8
0
krb5_error_code KRB5_CALLCONV
krb5_auth_con_genaddrs(krb5_context context, krb5_auth_context auth_context, int infd, int flags)
{
    krb5_error_code       retval;
    krb5_address        * laddr;
    krb5_address        * lport;
    krb5_address        * raddr;
    krb5_address        * rport;
    SOCKET              fd = (SOCKET) infd;
    struct addrpair laddrs, raddrs;

#ifdef HAVE_NETINET_IN_H
    struct sockaddr_storage lsaddr, rsaddr;
    GETSOCKNAME_ARG3_TYPE ssize;

    ssize = sizeof(struct sockaddr_storage);
    if ((flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR) ||
        (flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR)) {
        retval = getsockname(fd, ss2sa(&lsaddr), &ssize);
        if (retval)
            return retval;

        if (cvtaddr (&lsaddr, &laddrs)) {
            laddr = &laddrs.addr;
            if (flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR)
                lport = &laddrs.port;
            else
                lport = 0;
        } else
            return KRB5_PROG_ATYPE_NOSUPP;
    } else {
        laddr = NULL;
        lport = NULL;
    }

    ssize = sizeof(struct sockaddr_storage);
    if ((flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR) ||
        (flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR)) {
        retval = getpeername(fd, ss2sa(&rsaddr), &ssize);
        if (retval)
            return errno;

        if (cvtaddr (&rsaddr, &raddrs)) {
            raddr = &raddrs.addr;
            if (flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)
                rport = &raddrs.port;
            else
                rport = 0;
        } else
            return KRB5_PROG_ATYPE_NOSUPP;
    } else {
        raddr = NULL;
        rport = NULL;
    }

    if (!(retval = krb5_auth_con_setaddrs(context, auth_context, laddr, raddr)))
        return (krb5_auth_con_setports(context, auth_context, lport, rport));
    return retval;
#else
    return KRB5_PROG_ATYPE_NOSUPP;
#endif
}