Commit message: Fix S4U2Self KDC crash when anon is restricted

In validate_as_request(), when enforcing restrict_anonymous_to_tgt,
use client.princ instead of request->client; the latter is NULL when
validating S4U2Self requests.

CVE-2016-3120:

In MIT krb5 1.9 and later, an authenticated attacker can cause krb5kdc
to dereference a null pointer if the restrict_anonymous_to_tgt option
is set to true, by making an S4U2Self request.

  CVSSv2 Vector: AV:N/AC:H/Au:S/C:N/I:N/A:C/E:H/RL:OF/RC:C

ticket: 8458 (new)
target_version: 1.14-next
target_version: 1.13-next
CVE: CVE-2016-3120
CWE: CWE-476
Vulnerability description: The validate_as_request function in kdc_util.c in the Key Distribution Center (KDC) in MIT Kerberos 5 (aka krb5) before 1.13.6 and 1.4.x before 1.14.3, when restrict_anonymous_to_tgt is enabled, uses an incorrect client data structure, which allows remote authenticated users to cause a denial of service (NULL pointer dereference and daemon crash) via an S4U2Self request.

Project: krb5
File: kdc_util.c

add_pa_data_element(krb5_context context,
                    krb5_pa_data *padata,
                    krb5_pa_data ***inout_padata,
                    krb5_boolean copy)
{
    int                         i;
    krb5_pa_data                **p;

    if (*inout_padata != NULL) {
        for (i = 0; (*inout_padata)[i] != NULL; i++)
            ;
    } else
        i = 0;

    p = realloc(*inout_padata, (i + 2) * sizeof(krb5_pa_data *));
    if (p == NULL)
        return ENOMEM;

    *inout_padata = p;

    p[i] = (krb5_pa_data *)malloc(sizeof(krb5_pa_data));
    if (p[i] == NULL)
        return ENOMEM;
    *(p[i]) = *padata;

    p[i + 1] = NULL;

    if (copy) {
        p[i]->contents = (krb5_octet *)malloc(padata->length);
        if (p[i]->contents == NULL) {
            free(p[i]);
            p[i] = NULL;
            return ENOMEM;
        }

        memcpy(p[i]->contents, padata->contents, padata->length);
    }

    return 0;
}

Project: krb5
File: kdc_util.c

asn1length(unsigned char **astream)
{
    int length;         /* resulting length */
    int sublen;         /* sublengths */
    int blen;           /* bytes of length */
    unsigned char *p;   /* substring searching */

    if (**astream & 0x80) {
        blen = **astream & 0x7f;
        if (blen > 3) {
            return(-1);
        }
        for (++*astream, length = 0; blen; ++*astream, blen--) {
            length = (length << 8) | **astream;
        }
        if (length == 0) {
            /* indefinite length, figure out by hand */
            p = *astream;
            p++;
            while (1) {
                /* compute value length. */
                if ((sublen = asn1length(&p)) < 0) {
                    return(-1);
                }
                p += sublen;
                /* check for termination */
                if ((!*p++) && (!*p)) {
                    p++;
                    break;
                }
            }
            length = p - *astream;
        }
    } else {
        length = **astream;
        ++*astream;
    }
    return(length);
}

Project: krb5
File: kdc_util.c

check_allowed_to_delegate_to(krb5_context context, krb5_const_principal client,
                             const krb5_db_entry *server,
                             krb5_const_principal proxy)
{
    /* Can't get a TGT (otherwise it would be unconstrained delegation) */
    if (krb5_is_tgs_principal(proxy))
        return KRB5KDC_ERR_POLICY;

    /* Must be in same realm */
    if (!krb5_realm_compare(context, server->princ, proxy))
        return KRB5KDC_ERR_POLICY;

    return krb5_db_check_allowed_to_delegate(context, client, server, proxy);
}

Project: krb5
File: kdc_util.c

check_anon(kdc_realm_t *kdc_active_realm,
           krb5_principal client, krb5_principal server)
{
    /* If restrict_anon is set, reject requests from anonymous to principals
     * other than the local TGT. */
    if (kdc_active_realm->realm_restrict_anon &&
        krb5_principal_compare_any_realm(kdc_context, client,
                                         krb5_anonymous_principal()) &&
        !krb5_principal_compare(kdc_context, server, tgs_server))
        return -1;
    return 0;
}

Project: krb5
File: kdc_util.c

check_hot_list(krb5_ticket *ticket)
{
    return 0;
}

Project: krb5
File: kdc_util.c

check_indicators(krb5_context context, krb5_db_entry *server,
                 krb5_data *const *indicators)
{
    krb5_error_code ret;
    char *str = NULL, *copy = NULL, *save, *ind;

    ret = krb5_dbe_get_string(context, server, KRB5_KDB_SK_REQUIRE_AUTH, &str);
    if (ret || str == NULL)
        goto cleanup;
    copy = strdup(str);
    if (copy == NULL) {
        ret = ENOMEM;
        goto cleanup;
    }

    /* Look for any of the space-separated strings in indicators. */
    ind = strtok_r(copy, " ", &save);
    while (ind != NULL) {
        if (authind_contains(indicators, ind))
            goto cleanup;
        ind = strtok_r(NULL, " ", &save);
    }

    ret = KRB5KDC_ERR_POLICY;
    k5_setmsg(context, ret,
              _("Required auth indicators not present in ticket: %s"), str);

cleanup:
    krb5_dbe_free_string(context, str);
    free(copy);
    return ret;
}

Project: krb5
File: kdc_util.c

comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
           krb5_checksum *his_cksum)
{
    krb5_error_code       retval;
    krb5_boolean          valid;

    if (!krb5_c_valid_cksumtype(his_cksum->checksum_type))
        return KRB5KDC_ERR_SUMTYPE_NOSUPP;

    /* must be collision proof */
    if (!krb5_c_is_coll_proof_cksum(his_cksum->checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    /* verify checksum */
    if ((retval = krb5_c_verify_checksum(kcontext, ticket->enc_part2->session,
                                         KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
                                         source, his_cksum, &valid)))
        return(retval);

    if (!valid)
        return(KRB5KRB_AP_ERR_BAD_INTEGRITY);

    return(0);
}

Project: krb5
File: kdc_util.c

concat_authorization_data(krb5_context context,
                          krb5_authdata **first, krb5_authdata **second,
                          krb5_authdata ***output)
{
    register int i, j;
    register krb5_authdata **ptr, **retdata;

    /* count up the entries */
    i = 0;
    if (first)
        for (ptr = first; *ptr; ptr++)
            i++;
    if (second)
        for (ptr = second; *ptr; ptr++)
            i++;

    retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata));
    if (!retdata)
        return ENOMEM;
    retdata[i] = 0;                     /* null-terminated array */
    for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++)
        while (ptr && *ptr) {
            /* now walk & copy */
            retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i]));
            if (!retdata[i]) {
                krb5_free_authdata(context, retdata);
                return ENOMEM;
            }
            *retdata[i] = **ptr;
            if (!(retdata[i]->contents =
                  (krb5_octet *)malloc(retdata[i]->length))) {
                free(retdata[i]);
                retdata[i] = 0;
                krb5_free_authdata(context, retdata);
                return ENOMEM;
            }
            memcpy(retdata[i]->contents, (*ptr)->contents, retdata[i]->length);

            ptr++;
            i++;
        }
    *output = retdata;
    return 0;
}

Project: krb5
File: kdc_util.c

enctype_requires_etype_info_2(krb5_enctype enctype)
{
    switch(enctype) {
    case ENCTYPE_DES_CBC_CRC:
    case ENCTYPE_DES_CBC_MD4:
    case ENCTYPE_DES_CBC_MD5:
    case ENCTYPE_DES3_CBC_SHA1:
    case ENCTYPE_DES3_CBC_RAW:
    case ENCTYPE_ARCFOUR_HMAC:
    case ENCTYPE_ARCFOUR_HMAC_EXP :
        return 0;
    default:
        return krb5_c_valid_enctype(enctype);
    }
}

Project: krb5
File: kdc_util.c

fetch_asn1_field(unsigned char *astream, unsigned int level,
                 unsigned int field, krb5_data *data)
{
    unsigned char *estream;     /* end of stream */
    int classes;                /* # classes seen so far this level */
    unsigned int levels = 0;            /* levels seen so far */
    int lastlevel = 1000;       /* last level seen */
    int length;                 /* various lengths */
    int tag;                    /* tag number */
    unsigned char savelen;      /* saved length of our field */

    classes = -1;
    /* we assume that the first identifier/length will tell us
       how long the entire stream is. */
    astream++;
    estream = astream;
    if ((length = asn1length(&astream)) < 0) {
        return(-1);
    }
    estream += length;
    /* search down the stream, checking identifiers.  we process identifiers
       until we hit the "level" we want, and then process that level for our
       subfield, always making sure we don't go off the end of the stream.  */
    while (astream < estream) {
        if (!asn1_id_constructed(*astream)) {
            return(-1);
        }
        if (asn1_id_class(*astream) == ASN1_CLASS_CTX) {
            if ((tag = (int)asn1_id_tag(*astream)) <= lastlevel) {
                levels++;
                classes = -1;
            }
            lastlevel = tag;
            if (levels == level) {
                /* in our context-dependent class, is this the one we're looking for ? */
                if (tag == (int)field) {
                    /* return length and data */
                    astream++;
                    savelen = *astream;
                    if ((length = asn1length(&astream)) < 0) {
                        return(-1);
                    }
                    data->length = length;
                    /* if the field length is indefinite, we will have to subtract two
                       (terminating octets) from the length returned since we don't want
                       to pass any info from the "wrapper" back.  asn1length will always return
                       the *total* length of the field, not just what's contained in it */
                    if ((savelen & 0xff) == 0x80) {
                        data->length -=2 ;
                    }
                    data->data = (char *)astream;
                    return(0);
                } else if (tag <= classes) {
                    /* we've seen this class before, something must be wrong */
                    return(-1);
                } else {
                    classes = tag;
                }
            }
        }
        /* if we're not on our level yet, process this value.  otherwise skip over it */
        astream++;
        if ((length = asn1length(&astream)) < 0) {
            return(-1);
        }
        if (levels == level) {
            astream += length;
        }
    }
    return(-1);
}

Project: krb5
File: kdc_util.c

fetch_last_req_info(krb5_db_entry *dbentry, krb5_last_req_entry ***lrentry)
{
    *lrentry = nolrarray;
    return 0;
}

Project: krb5
File: kdc_util.c

get_local_tgt(krb5_context context, const krb5_data *realm,
              krb5_db_entry *candidate, krb5_db_entry **alias_out,
              krb5_db_entry **storage_out)
{
    krb5_error_code ret;
    krb5_principal princ;
    krb5_db_entry *tgt;

    *alias_out = NULL;
    *storage_out = NULL;

    ret = krb5_build_principal_ext(context, &princ, realm->length, realm->data,
                                   KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
                                   realm->length, realm->data, 0);
    if (ret)
        return ret;

    if (!krb5_principal_compare(context, candidate->princ, princ)) {
        ret = krb5_db_get_principal(context, princ, 0, &tgt);
        if (!ret)
            *storage_out = *alias_out = tgt;
    } else {
        *alias_out = candidate;
    }

    krb5_free_principal(context, princ);
    return ret;
}

Project: krb5
File: kdc_util.c

is_cross_tgs_principal(krb5_const_principal principal)
{
    if (!krb5_is_tgs_principal(principal))
        return FALSE;
    if (!data_eq(*krb5_princ_component(kdc_context, principal, 1),
                 *krb5_princ_realm(kdc_context, principal)))
        return TRUE;
    else
        return FALSE;
}

Project: krb5
File: kdc_util.c

is_local_principal(kdc_realm_t *kdc_active_realm, krb5_const_principal princ1)
{
    return krb5_realm_compare(kdc_context, princ1, tgs_server);
}

Project: krb5
File: kdc_util.c

kdc_check_transited_list(kdc_realm_t *kdc_active_realm,
                         const krb5_data *trans,
                         const krb5_data *realm1,
                         const krb5_data *realm2)
{
    krb5_error_code             code;

    /* Check against the KDB module.  Treat this answer as authoritative if the
     * method is supported and doesn't explicitly pass control. */
    code = krb5_db_check_transited_realms(kdc_context, trans, realm1, realm2);
    if (code != KRB5_PLUGIN_OP_NOTSUPP && code != KRB5_PLUGIN_NO_HANDLE)
        return code;

    /* Check using krb5.conf [capaths] or hierarchical relationships. */
    return krb5_check_transited_list(kdc_context, trans, realm1, realm2);
}

Project: krb5
File: kdc_util.c

kdc_get_server_key(krb5_context context,
                   krb5_ticket *ticket, unsigned int flags,
                   krb5_boolean match_enctype, krb5_db_entry **server_ptr,
                   krb5_keyblock **key, krb5_kvno *kvno)
{
    krb5_error_code       retval;
    krb5_db_entry       * server = NULL;
    krb5_enctype          search_enctype = -1;
    krb5_kvno             search_kvno = -1;

    if (match_enctype)
        search_enctype = ticket->enc_part.enctype;
    if (ticket->enc_part.kvno)
        search_kvno = ticket->enc_part.kvno;

    *server_ptr = NULL;

    retval = krb5_db_get_principal(context, ticket->server, flags,
                                   &server);
    if (retval == KRB5_KDB_NOENTRY) {
        char *sname;
        if (!krb5_unparse_name(context, ticket->server, &sname)) {
            limit_string(sname);
            krb5_klog_syslog(LOG_ERR,
                             _("TGS_REQ: UNKNOWN SERVER: server='%s'"), sname);
            free(sname);
        }
        return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
    } else if (retval)
        return retval;
    if (server->attributes & KRB5_KDB_DISALLOW_SVR ||
        server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) {
        retval = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
        goto errout;
    }

    if (key) {
        retval = find_server_key(context, server, search_enctype, search_kvno,
                                 key, kvno);
        if (retval)
            goto errout;
    }
    *server_ptr = server;
    server = NULL;
    return 0;

errout:
    krb5_db_free_principal(context, server);
    return retval;
}

Project: krb5
File: kdc_util.c

kdc_get_ticket_endtime(kdc_realm_t *kdc_active_realm,
                       krb5_timestamp starttime,
                       krb5_timestamp endtime,
                       krb5_timestamp till,
                       krb5_db_entry *client,
                       krb5_db_entry *server,
                       krb5_timestamp *out_endtime)
{
    krb5_timestamp until, life;

    if (till == 0)
        till = kdc_infinity;

    until = min(till, endtime);

    life = until - starttime;

    if (client != NULL && client->max_life != 0)
        life = min(life, client->max_life);
    if (server->max_life != 0)
        life = min(life, server->max_life);
    if (kdc_active_realm->realm_maxlife != 0)
        life = min(life, kdc_active_realm->realm_maxlife);

    *out_endtime = starttime + life;
}

Project: krb5
File: kdc_util.c

kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request,
                         krb5_enc_tkt_part *tgt, krb5_db_entry *client,
                         krb5_db_entry *server, krb5_enc_tkt_part *tkt)
{
    krb5_timestamp rtime, max_rlife;

    tkt->times.renew_till = 0;

    /* Don't issue renewable tickets if the client or server don't allow it,
     * or if this is a TGS request and the TGT isn't renewable. */
    if (server->attributes & KRB5_KDB_DISALLOW_RENEWABLE)
        return;
    if (client != NULL && (client->attributes & KRB5_KDB_DISALLOW_RENEWABLE))
        return;
    if (tgt != NULL && !(tgt->flags & TKT_FLG_RENEWABLE))
        return;

    /* Determine the requested renewable time. */
    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE))
        rtime = request->rtime ? request->rtime : kdc_infinity;
    else if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
             tkt->times.endtime < request->till)
        rtime = request->till;
    else
        return;

    /* Truncate it to the allowable renewable time. */
    if (tgt != NULL)
        rtime = min(rtime, tgt->times.renew_till);
    max_rlife = min(server->max_renewable_life, realm->realm_maxrlife);
    if (client != NULL)
        max_rlife = min(max_rlife, client->max_renewable_life);
    rtime = min(rtime, tkt->times.starttime + max_rlife);

    /* Make the ticket renewable if the truncated requested time is larger than
     * the ticket end time. */
    if (rtime > tkt->times.endtime) {
        setflag(tkt->flags, TKT_FLG_RENEWABLE);
        tkt->times.renew_till = rtime;
    }
}

Project: krb5
File: kdc_util.c

kdc_handle_protected_negotiation(krb5_context context,
                                 krb5_data *req_pkt, krb5_kdc_req *request,
                                 const krb5_keyblock *reply_key,
                                 krb5_pa_data ***out_enc_padata)
{
    krb5_error_code retval = 0;
    krb5_checksum checksum;
    krb5_data *out = NULL;
    krb5_pa_data pa, *pa_in;
    pa_in = krb5int_find_pa_data(context, request->padata,
                                 KRB5_ENCPADATA_REQ_ENC_PA_REP);
    if (pa_in == NULL)
        return 0;
    pa.magic = KV5M_PA_DATA;
    pa.pa_type = KRB5_ENCPADATA_REQ_ENC_PA_REP;
    memset(&checksum, 0, sizeof(checksum));
    retval = krb5_c_make_checksum(context,0, reply_key,
                                  KRB5_KEYUSAGE_AS_REQ, req_pkt, &checksum);
    if (retval != 0)
        goto cleanup;
    retval = encode_krb5_checksum(&checksum, &out);
    if (retval != 0)
        goto cleanup;
    pa.contents = (krb5_octet *) out->data;
    pa.length = out->length;
    retval = add_pa_data_element(context, &pa, out_enc_padata, FALSE);
    if (retval)
        goto cleanup;
    out->data = NULL;
    pa.magic = KV5M_PA_DATA;
    pa.pa_type = KRB5_PADATA_FX_FAST;
    pa.length = 0;
    pa.contents = NULL;
    retval = add_pa_data_element(context, &pa, out_enc_padata, FALSE);
cleanup:
    if (checksum.contents)
        krb5_free_checksum_contents(context, &checksum);
    if (out != NULL)
        krb5_free_data(context, out);
    return retval;
}

Project: krb5
File: kdc_util.c

kdc_make_s4u2self_rep(krb5_context context,
                      krb5_keyblock *tgs_subkey,
                      krb5_keyblock *tgs_session,
                      krb5_pa_s4u_x509_user *req_s4u_user,
                      krb5_kdc_rep *reply,
                      krb5_enc_kdc_rep_part *reply_encpart)
{
    krb5_error_code             code;
    krb5_data                   *data = NULL;
    krb5_pa_s4u_x509_user       rep_s4u_user;
    krb5_pa_data                padata;
    krb5_enctype                enctype;
    krb5_keyusage               usage;

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

    rep_s4u_user.user_id.nonce   = req_s4u_user->user_id.nonce;
    rep_s4u_user.user_id.user    = req_s4u_user->user_id.user;
    rep_s4u_user.user_id.options =
        req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE;

    code = encode_krb5_s4u_userid(&rep_s4u_user.user_id, &data);
    if (code != 0)
        goto cleanup;

    if (req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE)
        usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY;
    else
        usage = KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST;

    code = krb5_c_make_checksum(context, req_s4u_user->cksum.checksum_type,
                                tgs_subkey != NULL ? tgs_subkey : tgs_session,
                                usage, data,
                                &rep_s4u_user.cksum);
    if (code != 0)
        goto cleanup;

    krb5_free_data(context, data);
    data = NULL;

    code = encode_krb5_pa_s4u_x509_user(&rep_s4u_user, &data);
    if (code != 0)
        goto cleanup;

    padata.magic = KV5M_PA_DATA;
    padata.pa_type = KRB5_PADATA_S4U_X509_USER;
    padata.length = data->length;
    padata.contents = (krb5_octet *)data->data;

    code = add_pa_data_element(context, &padata, &reply->padata, FALSE);
    if (code != 0)
        goto cleanup;

    free(data);
    data = NULL;

    if (tgs_subkey != NULL)
        enctype = tgs_subkey->enctype;
    else
        enctype = tgs_session->enctype;

    /*
     * Owing to a bug in Windows, unkeyed checksums were used for older
     * enctypes, including rc4-hmac. A forthcoming workaround for this
     * includes the checksum bytes in the encrypted padata.
     */
    if ((req_s4u_user->user_id.options & KRB5_S4U_OPTS_USE_REPLY_KEY_USAGE) &&
        enctype_requires_etype_info_2(enctype) == FALSE) {
        padata.length = req_s4u_user->cksum.length +
            rep_s4u_user.cksum.length;
        padata.contents = malloc(padata.length);
        if (padata.contents == NULL) {
            code = ENOMEM;
            goto cleanup;
        }

        memcpy(padata.contents,
               req_s4u_user->cksum.contents,
               req_s4u_user->cksum.length);
        memcpy(&padata.contents[req_s4u_user->cksum.length],
               rep_s4u_user.cksum.contents,
               rep_s4u_user.cksum.length);

        code = add_pa_data_element(context,&padata,
                                   &reply_encpart->enc_padata, FALSE);
        if (code != 0) {
            free(padata.contents);
            goto cleanup;
        }
    }

cleanup:
    if (rep_s4u_user.cksum.contents != NULL)
        krb5_free_checksum_contents(context, &rep_s4u_user.cksum);
    krb5_free_data(context, data);

    return code;
}

Project: krb5
File: kdc_util.c

kdc_rd_ap_req(kdc_realm_t *kdc_active_realm,
              krb5_ap_req *apreq, krb5_auth_context auth_context,
              krb5_db_entry **server, krb5_keyblock **tgskey)
{
    krb5_error_code     retval;
    krb5_enctype        search_enctype = apreq->ticket->enc_part.enctype;
    krb5_boolean        match_enctype = 1;
    krb5_kvno           kvno;
    size_t              tries = 3;

    /*
     * When we issue tickets we use the first key in the principals' highest
     * kvno keyset.  For non-cross-realm krbtgt principals we want to only
     * allow the use of the first key of the principal's keyset that matches
     * the given kvno.
     */
    if (krb5_is_tgs_principal(apreq->ticket->server) &&
        !is_cross_tgs_principal(apreq->ticket->server)) {
        search_enctype = -1;
        match_enctype = 0;
    }

    retval = kdc_get_server_key(kdc_context, apreq->ticket,
                                KRB5_KDB_FLAG_ALIAS_OK, match_enctype, server,
                                NULL, NULL);
    if (retval)
        return retval;

    *tgskey = NULL;
    kvno = apreq->ticket->enc_part.kvno;
    do {
        krb5_free_keyblock(kdc_context, *tgskey);
        retval = find_server_key(kdc_context,
                                 *server, search_enctype, kvno, tgskey, &kvno);
        if (retval)
            continue;

        /* Make the TGS key available to krb5_rd_req_decoded_anyflag() */
        retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context,
                                              *tgskey);
        if (retval)
            return retval;

        retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
                                             apreq->ticket->server,
                                             kdc_active_realm->realm_keytab,
                                             NULL, NULL);

        /* If the ticket was decrypted, don't try any more keys. */
        if (apreq->ticket->enc_part2 != NULL)
            break;

    } while (retval && apreq->ticket->enc_part.kvno == 0 && kvno-- > 1 &&
             --tries > 0);

    return retval;
}

Project: krb5
File: kdc_util.c

krb5_is_tgs_principal(krb5_const_principal principal)
{
    if (krb5_princ_size(kdc_context, principal) != 2)
        return FALSE;
    if (data_eq_string(*krb5_princ_component(kdc_context, principal, 0),
                       KRB5_TGS_NAME))
        return TRUE;
    else
        return FALSE;
}

Project: krb5

ktypes2str(char *s, size_t len, int nktypes, krb5_enctype *ktype)
{
    int i;
    char stmp[D_LEN(krb5_enctype) + 1];
    char *p;

    if (nktypes < 0
        || len < (sizeof(" etypes {...}") + D_LEN(int))) {
        *s = '\0';
        return;
    }

    snprintf(s, len, "%d etypes {", nktypes);
    for (i = 0; i < nktypes; i++) {
        snprintf(stmp, sizeof(stmp), "%s%ld", i ? " " : "", (long)ktype[i]);
        if (strlen(s) + strlen(stmp) + sizeof("}") > len)
            break;
        strlcat(s, stmp, len);
    }
    if (i < nktypes) {
        /*
         * We broke out of the loop. Try to truncate the list.
         */
        p = s + strlen(s);
        while (p - s + sizeof("...}") > len) {
            while (p > s && *p != ' ' && *p != '{')
                *p-- = '\0';
            if (p > s && *p == ' ') {
                *p-- = '\0';
                continue;
            }
        }
        strlcat(s, "...", len);
    }
    strlcat(s, "}", len);

Project: krb5
File: kdc_util.c

make_toolong_error (void *handle, krb5_data **out)
{
    krb5_error errpkt;
    krb5_error_code retval;
    krb5_data *scratch;
    struct server_handle *h = handle;

    retval = krb5_us_timeofday(h->kdc_err_context,
                               &errpkt.stime, &errpkt.susec);
    if (retval)
        return retval;
    errpkt.error = KRB_ERR_FIELD_TOOLONG;
    errpkt.server = h->kdc_realmlist[0]->realm_tgsprinc;
    errpkt.client = NULL;
    errpkt.cusec = 0;
    errpkt.ctime = 0;
    errpkt.text.length = 0;
    errpkt.text.data = 0;
    errpkt.e_data.length = 0;
    errpkt.e_data.data = 0;
    scratch = malloc(sizeof(*scratch));
    if (scratch == NULL)
        return ENOMEM;
    retval = krb5_mk_error(h->kdc_err_context, &errpkt, scratch);
    if (retval) {
        free(scratch);
        return retval;
    }

    *out = scratch;
    return 0;
}

Project: krb5
File: kdc_util.c

rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep)
{
    char stmp[sizeof("ses=") + D_LEN(krb5_enctype)];

    if (len < (3 * D_LEN(krb5_enctype)
               + sizeof("etypes {rep= tkt= ses=}"))) {
        *s = '\0';
        return;
    }

    snprintf(s, len, "etypes {rep=%ld", (long)rep->enc_part.enctype);

    if (rep->ticket != NULL) {
        snprintf(stmp, sizeof(stmp),
                 " tkt=%ld", (long)rep->ticket->enc_part.enctype);
        strlcat(s, stmp, len);
    }

    if (rep->ticket != NULL
        && rep->ticket->enc_part2 != NULL
        && rep->ticket->enc_part2->session != NULL) {
        snprintf(stmp, sizeof(stmp), " ses=%ld",
                 (long)rep->ticket->enc_part2->session->enctype);
        strlcat(s, stmp, len);
    }
    strlcat(s, "}", len);
    return;
}

Project: krb5
File: kdc_util.c

void reset_for_hangup(void *ctx)
{
    int k;
    struct server_handle *h = ctx;

    for (k = 0; k < h->kdc_numrealms; k++)
        krb5_db_refresh_config(h->kdc_realmlist[k]->realm_context);
}

Project: krb5
File: kdc_util.c

select_session_keytype(kdc_realm_t *kdc_active_realm, krb5_db_entry *server,
                       int nktypes, krb5_enctype *ktype)
{
    int         i;

    for (i = 0; i < nktypes; i++) {
        if (!krb5_c_valid_enctype(ktype[i]))
            continue;

        if (!krb5_is_permitted_enctype(kdc_context, ktype[i]))
            continue;

        if (dbentry_supports_enctype(kdc_active_realm, server, ktype[i]))
            return ktype[i];
    }

    return 0;
}

Project: krb5
File: kdc_util.c

validate_forwardable(krb5_kdc_req *request, krb5_db_entry client,
                     krb5_db_entry server, krb5_timestamp kdc_time,
                     const char **status)
{
    *status = NULL;
    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
        (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
         isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
        *status = "FORWARDABLE NOT ALLOWED";
        return(KDC_ERR_POLICY);
    } else
        return 0;
}

Project: krb5
File: kdc_util.c

validate_transit_path(krb5_context context,
                      krb5_const_principal client,
                      krb5_db_entry *server,
                      krb5_db_entry *header_srv)
{
    /* Incoming */
    if (isflagset(server->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE)) {
        return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
    }

    /* Outgoing */
    if (isflagset(header_srv->attributes, KRB5_KDB_XREALM_NON_TRANSITIVE) &&
        (!krb5_principal_compare(context, server->princ, header_srv->princ) ||
         !krb5_realm_compare(context, client, header_srv->princ))) {
        return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
    }

    return 0;
}

Project: krb5
File: kdc_util.c

verify_for_user_checksum(krb5_context context,
                         krb5_keyblock *key,
                         krb5_pa_for_user *req)
{
    krb5_error_code             code;
    int                         i;
    krb5_int32                  name_type;
    char                        *p;
    krb5_data                   data;
    krb5_boolean                valid = FALSE;

    if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
        return KRB5KRB_AP_ERR_INAPP_CKSUM;
    }

    /*
     * Checksum is over name type and string components of
     * client principal name and auth_package.
     */
    data.length = 4;
    for (i = 0; i < krb5_princ_size(context, req->user); i++) {
        data.length += krb5_princ_component(context, req->user, i)->length;
    }
    data.length += krb5_princ_realm(context, req->user)->length;
    data.length += req->auth_package.length;

    p = data.data = malloc(data.length);
    if (data.data == NULL) {
        return ENOMEM;
    }

    name_type = krb5_princ_type(context, req->user);
    p[0] = (name_type >> 0 ) & 0xFF;
    p[1] = (name_type >> 8 ) & 0xFF;
    p[2] = (name_type >> 16) & 0xFF;
    p[3] = (name_type >> 24) & 0xFF;
    p += 4;

    for (i = 0; i < krb5_princ_size(context, req->user); i++) {
        if (krb5_princ_component(context, req->user, i)->length > 0) {
            memcpy(p, krb5_princ_component(context, req->user, i)->data,
                   krb5_princ_component(context, req->user, i)->length);
        }
        p += krb5_princ_component(context, req->user, i)->length;
    }

    if (krb5_princ_realm(context, req->user)->length > 0) {
        memcpy(p, krb5_princ_realm(context, req->user)->data,
               krb5_princ_realm(context, req->user)->length);
    }
    p += krb5_princ_realm(context, req->user)->length;

    if (req->auth_package.length > 0)
        memcpy(p, req->auth_package.data, req->auth_package.length);
    p += req->auth_package.length;

    code = krb5_c_verify_checksum(context,
                                  key,
                                  KRB5_KEYUSAGE_APP_DATA_CKSUM,
                                  &data,
                                  &req->cksum,
                                  &valid);

    if (code == 0 && valid == FALSE)
        code = KRB5KRB_AP_ERR_MODIFIED;

    free(data.data);

    return code;
}

Project: krb5
File: kdc_util.c

verify_s4u_x509_user_checksum(krb5_context context,
                              krb5_keyblock *key,
                              krb5_data *req_data,
                              krb5_int32 kdc_req_nonce,
                              krb5_pa_s4u_x509_user *req)
{
    krb5_error_code             code;
    krb5_data                   scratch;
    krb5_boolean                valid = FALSE;

    if (enctype_requires_etype_info_2(key->enctype) &&
        !krb5_c_is_keyed_cksum(req->cksum.checksum_type))
        return KRB5KRB_AP_ERR_INAPP_CKSUM;

    if (req->user_id.nonce != kdc_req_nonce)
        return KRB5KRB_AP_ERR_MODIFIED;

    /*
     * Verify checksum over the encoded userid. If that fails,
     * re-encode, and verify that. This is similar to the
     * behaviour in kdc_process_tgs_req().
     */
    if (fetch_asn1_field((unsigned char *)req_data->data, 1, 0, &scratch) < 0)
        return ASN1_PARSE_ERROR;

    code = krb5_c_verify_checksum(context,
                                  key,
                                  KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
                                  &scratch,
                                  &req->cksum,
                                  &valid);
    if (code != 0)
        return code;

    if (valid == FALSE) {
        krb5_data *data;

        code = encode_krb5_s4u_userid(&req->user_id, &data);
        if (code != 0)
            return code;

        code = krb5_c_verify_checksum(context,
                                      key,
                                      KRB5_KEYUSAGE_PA_S4U_X509_USER_REQUEST,
                                      data,
                                      &req->cksum,
                                      &valid);

        krb5_free_data(context, data);

        if (code != 0)
            return code;
    }

    return valid ? 0 : KRB5KRB_AP_ERR_MODIFIED;
}