Exemple #1
0
/* Return true if princ is the local krbtgt principal for local_realm. */
static krb5_boolean
is_local_tgt(krb5_principal princ, krb5_data *realm)
{
    return princ->length == 2 && data_eq(princ->realm, *realm) &&
        data_eq_string(princ->data[0], KRB5_TGS_NAME) &&
        data_eq(princ->data[1], *realm);
}
Exemple #2
0
/* Return true if a TGS credential is for the client's local realm. */
static inline int
tgt_is_local_realm(krb5_creds *tgt)
{
    return (tgt->server->length == 2
            && data_eq_string(tgt->server->data[0], KRB5_TGS_NAME)
            && data_eq(tgt->server->data[1], tgt->client->realm)
            && data_eq(tgt->server->realm, tgt->client->realm));
}
Exemple #3
0
int
ktest_equal_kkdcp_message(krb5_kkdcp_message *ref, krb5_kkdcp_message *var)
{
    int p = TRUE;
    if (ref == var) return TRUE;
    else if (ref == NULL || var == NULL) return FALSE;
    p = p && data_eq(ref->kerb_message, var->kerb_message);
    p = p && data_eq(ref->target_domain, var->target_domain);
    p = p && (ref->dclocator_hint == var->dclocator_hint);
    return p;
}
/* Process a TGS reply and advance the path traversal to get a foreign TGT. */
static krb5_error_code
step_get_tgt(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    const krb5_data *tgt_realm, *path_realm;

    if (ctx->reply_code != 0) {
        /* The last request failed.  Try the next-closest realm to
         * ctx->cur_realm. */
        ctx->next_realm--;
        if (ctx->next_realm == ctx->cur_realm) {
            /* We've tried all the realms we could and couldn't progress beyond
             * ctx->cur_realm, so it's time to give up. */
            return ctx->reply_code;
        }
        TRACE_TKT_CREDS_CLOSER_REALM(context, ctx->next_realm);
    } else {
        /* Verify that we got a TGT. */
        if (!IS_TGS_PRINC(context, ctx->reply_creds->server))
            return KRB5_KDCREP_MODIFIED;

        /* Use this tgt for the next request regardless of what it is. */
        krb5_free_creds(context, ctx->cur_tgt);
        ctx->cur_tgt = ctx->reply_creds;
        ctx->reply_creds = NULL;

        /* Remember that we saw this realm. */
        tgt_realm = &ctx->cur_tgt->server->data[1];
        code = remember_realm(context, ctx, tgt_realm);
        if (code != 0)
            return code;

        /* See where we wound up on the path (or off it). */
        path_realm = find_realm_in_path(context, ctx, tgt_realm);
        if (path_realm != NULL) {
            /* We got a realm on the expected path, so we can cache it. */
            (void) krb5_cc_store_cred(context, ctx->ccache, ctx->cur_tgt);
            if (path_realm == ctx->last_realm) {
                /* We received a TGT for the target realm. */
                TRACE_TKT_CREDS_TARGET_TGT(context, ctx->cur_tgt->server);
                return end_get_tgt(context, ctx);
            } else if (path_realm != NULL) {
                /* We still have further to go; advance the traversal. */
                TRACE_TKT_CREDS_ADVANCE(context, tgt_realm);
                ctx->cur_realm = path_realm;
                ctx->next_realm = ctx->last_realm;
            }
        } else if (data_eq(*tgt_realm, ctx->client->realm)) {
            /* We were referred back to the local realm, which is bad. */
            return KRB5_KDCREP_MODIFIED;
        } else {
            /* We went off the path; start the off-path chase. */
            TRACE_TKT_CREDS_OFFPATH(context, tgt_realm);
            return begin_get_tgt_offpath(context, ctx);
        }
    }

    /* Generate the next request in the path traversal. */
    return get_tgt_request(context, ctx);
}
Exemple #5
0
static void
test_insert_entry(void **state)
{
    struct entry *e;
    krb5_context context = *state;
    krb5_data req = string2data("I'm a test request");
    krb5_data rep = string2data("I'm a test response");

    e = insert_entry(context, &req, &rep, 15);

    assert_ptr_equal(k5_hashtab_get(hash_table, req.data, req.length), e);
    assert_ptr_equal(K5_TAILQ_FIRST(&expiration_queue), e);
    assert_true(data_eq(e->req_packet, req));
    assert_true(data_eq(e->reply_packet, rep));
    assert_int_equal(e->timein, 15);
}
Exemple #6
0
/* Return true if princ is a local (not cross-realm) krbtgt principal. */
krb5_boolean
is_local_tgt(krb5_principal princ)
{
    return princ->length == 2 &&
        data_eq_string(princ->data[0], KRB5_TGS_NAME) &&
        data_eq(princ->realm, princ->data[1]);
}
Exemple #7
0
static void
test_kdc_check_lookaside_hit_multiple(void **state)
{
    struct entry *e1, *e2;
    krb5_boolean result;
    krb5_data *result_data;
    krb5_context context = *state;
    krb5_data req1 = string2data("I'm a test request");
    krb5_data rep1 = string2data("I'm a test response");
    krb5_data req2 = string2data("I'm a different test request");

    e1 = insert_entry(context, &req1, &rep1, 0);
    e2 = insert_entry(context, &req2, NULL, 0);

    result = kdc_check_lookaside(context, &req1, &result_data);

    assert_true(result);
    assert_true(data_eq(rep1, *result_data));
    assert_int_equal(hits, 1);
    assert_int_equal(e1->num_hits, 1);
    assert_int_equal(e2->num_hits, 0);

    krb5_free_data(context, result_data);

    /* Set result_data so we can verify that it is reset to NULL. */
    result_data = &req1;
    result = kdc_check_lookaside(context, &req2, &result_data);

    assert_true(result);
    assert_null(result_data);
    assert_int_equal(hits, 2);
    assert_int_equal(e1->num_hits, 1);
    assert_int_equal(e2->num_hits, 1);
}
Exemple #8
0
/*
 * find_nxt_kdc()
 *
 * A NXT_TGT gotten from an intermediate KDC might actually be a
 * referral.  Search KDC_LIST forward starting from CUR_KDC, looking
 * for the KDC with the same remote realm as NXT_TGT.  If we don't
 * find it, the intermediate KDC is leading us off the transit path.
 *
 * Match on CUR_KDC's remote realm, not local realm, because, among
 * other reasons, we can get a referral to the final realm; e.g.,
 * given
 *
 *     KDC_LIST == { krbtgt/R1@R1, krbtgt/R2@R1, krbtgt/R3@R2,
 *                   krbtgt/R4@R3, NULL }
 *     CUR_TGT->SERVER == krbtgt/R2@R1
 *     NXT_TGT->SERVER == krbtgt/R4@R2
 *
 * i.e., we got a ticket issued by R2 with remote realm R4, we want to
 * find krbtgt/R4@R3, not krbtgt/R3@R2, even though we have no TGT
 * with R3 as its local realm.
 *
 * Set up for next iteration of do_traversal() loop by pointing
 * NXT_KDC to one entry forward of the match.
 */
static krb5_error_code
find_nxt_kdc(struct tr_state *ts)
{
    krb5_data *r1, *r2;
    krb5_principal *kdcptr;

    TR_DBG(ts, "find_nxt_kdc");
    assert(ts->ntgts > 0);
    assert(ts->nxt_tgt == ts->kdc_tgts[ts->ntgts-1]);
    if (krb5_princ_size(ts->ctx, ts->nxt_tgt->server) != 2)
	return KRB5_KDCREP_MODIFIED;

    r1 = krb5_princ_component(ts->ctx, ts->nxt_tgt->server, 1);

    for (kdcptr = ts->cur_kdc + 1; *kdcptr != NULL; kdcptr++) {

	r2 = krb5_princ_component(ts->ctx, *kdcptr, 1);

	if (r1 != NULL && r2 != NULL && data_eq(*r1, *r2)) {
	    break;
	}
    }
    if (*kdcptr != NULL) {
	ts->nxt_kdc = kdcptr;
	TR_DBG_RET(ts, "find_nxt_kdc", 0);
	return 0;
    }

    r2 = krb5_princ_component(ts->ctx, ts->kdc_list[0], 1);
    if (r1 != NULL && r2 != NULL &&
	r1->length == r2->length &&
	!memcmp(r1->data, r2->data, r1->length)) {
	TR_DBG_RET(ts, "find_nxt_kdc: looped back to local",
		   KRB5_KDCREP_MODIFIED);
	return KRB5_KDCREP_MODIFIED;
    }

    /*
     * Realm is not in our list; we probably got an unexpected realm
     * referral.
     */
    ts->offpath_tgt = ts->nxt_tgt;
    if (ts->cur_kdc == ts->kdc_list) {
	/*
	 * Local KDC referred us off path; trust it for caching
	 * purposes.
	 */
	return 0;
    }
    /*
     * Unlink the off-path TGT from KDC_TGTS but don't free it,
     * because we should return it.
     */
    ts->kdc_tgts[--ts->ntgts] = NULL;
    ts->nxt_tgt = ts->cur_tgt;
    TR_DBG_RET(ts, "find_nxt_kdc", 0);
    return 0;
}
Exemple #9
0
krb5_boolean KRB5_CALLCONV
krb5_realm_compare(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2)
{
    if (!data_eq(*krb5_princ_realm(context, princ1),
		 *krb5_princ_realm(context, princ2)))
	return FALSE;

    return TRUE;
}
Exemple #10
0
static void
test_kdc_insert_lookaside_no_reply(void **state)
{
    krb5_context context = *state;
    krb5_data req = string2data("I'm a test request");
    struct entry *hash_ent, *exp_ent;

    time_return(0, 0);
    kdc_insert_lookaside(context, &req, NULL);

    hash_ent = k5_hashtab_get(hash_table, req.data, req.length);
    assert_non_null(hash_ent);
    assert_true(data_eq(hash_ent->req_packet, req));
    assert_int_equal(hash_ent->reply_packet.length, 0);
    exp_ent = K5_TAILQ_FIRST(&expiration_queue);
    assert_true(data_eq(exp_ent->req_packet, req));
    assert_int_equal(exp_ent->reply_packet.length, 0);
    assert_int_equal(num_entries, 1);
    assert_int_equal(total_size, entry_size(&req, NULL));
}
Exemple #11
0
static krb5_key
find_cached_dkey(struct derived_key *list, const krb5_data *constant)
{
    for (; list; list = list->next) {
        if (data_eq(list->constant, *constant)) {
            krb5_k_reference_key(NULL, list->dkey);
            return list->dkey;
        }
    }
    return NULL;
}
Exemple #12
0
/* Find realm within the portion of ctx->realm_path following
 * ctx->cur_realm.  Return NULL if it is not found. */
static const krb5_data *
find_realm_in_path(krb5_context context, krb5_tkt_creds_context ctx,
                   const krb5_data *realm)
{
    const krb5_data *r;

    for (r = ctx->cur_realm + 1; r->data != NULL; r++) {
        if (data_eq(*r, *realm))
            return r;
    }
    return NULL;
}
static krb5_boolean
data_match(const krb5_data *data1, const krb5_data *data2)
{
    if (!data1) {
        if (!data2)
            return TRUE;
        else
            return FALSE;
    }
    if (!data2) return FALSE;

    return data_eq(*data1, *data2) ? TRUE : FALSE;
}
static krb5_error_code
check_reply_server(krb5_context context, krb5_flags kdcoptions,
                   krb5_creds *in_cred, krb5_kdc_rep *dec_rep)
{

    if (!krb5_principal_compare(context, dec_rep->ticket->server,
                                dec_rep->enc_part2->server))
        return KRB5_KDCREP_MODIFIED;

    /* Reply is self-consistent. */

    if (krb5_principal_compare(context, dec_rep->ticket->server,
                               in_cred->server))
        return 0;

    /* Server in reply differs from what we requested. */

    if (kdcoptions & KDC_OPT_CANONICALIZE) {
        /* in_cred server differs from ticket returned, but ticket
           returned is consistent and we requested canonicalization. */
#if 0
#ifdef DEBUG_REFERRALS
        printf("gc_via_tkt: in_cred and encoding don't match but referrals requested\n");
        krb5int_dbgref_dump_principal("gc_via_tkt: in_cred",in_cred->server);
        krb5int_dbgref_dump_principal("gc_via_tkt: encoded server",dec_rep->enc_part2->server);
#endif
#endif
        return 0;
    }

    /* We didn't request canonicalization. */

    if (!IS_TGS_PRINC(context, in_cred->server) ||
        !IS_TGS_PRINC(context, dec_rep->ticket->server)) {
        /* Canonicalization not requested, and not a TGS referral. */
        return KRB5_KDCREP_MODIFIED;
    }
#if 0
    /*
     * Is this check needed?  find_nxt_kdc() in gc_frm_kdc.c already
     * effectively checks this.
     */
    if (krb5_realm_compare(context, in_cred->client, in_cred->server) &&
        data_eq(*in_cred->server->data[1], *in_cred->client->realm)) {
        /* Attempted to rewrite local TGS. */
        return KRB5_KDCREP_MODIFIED;
    }
#endif
    return 0;
}
Exemple #15
0
/* Return TRUE if realm appears to ctx->realms_seen. */
static krb5_boolean
seen_realm_before(krb5_context context, krb5_tkt_creds_context ctx,
                  const krb5_data *realm)
{
    size_t i;

    if (ctx->realms_seen != NULL) {
        for (i = 0; ctx->realms_seen[i].data != NULL; i++) {
            if (data_eq(ctx->realms_seen[i], *realm))
                return TRUE;
        }
    }
    return FALSE;
}
Exemple #16
0
static krb5_error_code
check_realm_in_list (krb5_data *realm, void *data)
{
    struct check_data *cdata = data;
    int i;

    Tprintf ((".. checking '%.*s'\n", (int) realm->length, realm->data));
    for (i = 0; cdata->tgs[i]; i++) {
        if (data_eq (cdata->tgs[i]->realm, *realm))
            return 0;
    }
    Tprintf (("BAD!\n"));
    return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
Exemple #17
0
/*
 * See if two data entries match.  If e1 is a wildcard (matching a whole
 * component only) and targetflag is false, save an alias to e2 into
 * ws->backref.  If e1 is a back-reference and targetflag is true, compare the
 * appropriate entry in ws->backref to e2.  If ws is NULL, do not store or
 * match back-references.
 */
static krb5_boolean
match_data(const krb5_data *e1, const krb5_data *e2, krb5_boolean targetflag,
           struct wildstate *ws)
{
    int n;

    if (data_eq_string(*e1, "*")) {
        if (ws != NULL && !targetflag) {
            if (ws->nwild < 9)
                ws->backref[ws->nwild++] = e2;
        }
        return TRUE;
    }

    if (ws != NULL && targetflag && e1->length == 2 && e1->data[0] == '*' &&
        e1->data[1] >= '1' && e1->data[1] <= '9') {
        n = e1->data[1] - '1';
        if (n >= ws->nwild)
            return FALSE;
        return data_eq(*e2, *ws->backref[n]);
    } else {
        return data_eq(*e2, *e1);
    }
}
Exemple #18
0
static krb5_error_code
s4u2proxy_set_attribute(krb5_context kcontext,
                        krb5_authdata_context context,
                        void *plugin_context,
                        void *request_context,
                        krb5_boolean complete,
                        const krb5_data *attribute,
                        const krb5_data *value)
{
    /* Only the KDC can set this attribute. */
    if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
        return ENOENT;

    return EPERM;
}
Exemple #19
0
krb5_error_code
krb5int_dk_cmac_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
                        krb5_keyusage usage, const krb5_data *ivec,
                        krb5_crypto_iov *data, size_t num_data)
{
    const struct krb5_enc_provider *enc = ktp->enc;
    krb5_error_code ret;
    krb5_crypto_iov *header, *trailer;
    krb5_data cksum;
    krb5_key ke = NULL, ki = NULL;

    /* E(Confounder | Plaintext | Pad) | Checksum */

    /* Validate header and trailer lengths. */
    header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
    if (header == NULL || header->data.length != enc->block_size)
        return KRB5_BAD_MSIZE;
    trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
    if (trailer == NULL || trailer->data.length != enc->block_size)
        return KRB5_BAD_MSIZE;

    /* Derive the encryption and integrity keys. */
    ret = derive_keys(enc, key, usage, &ke, &ki);
    if (ret != 0)
        goto cleanup;

    /* Decrypt the plaintext (header | data | padding). */
    ret = enc->decrypt(ke, ivec, data, num_data);
    if (ret != 0)
        goto cleanup;

    /* Verify the hash. */
    ret = alloc_data(&cksum, enc->block_size);
    if (ret != 0)
        goto cleanup;
    ret = krb5int_cmac_checksum(enc, ki, data, num_data, &cksum);
    if (ret != 0)
        goto cleanup;
    if (!data_eq(cksum, trailer->data))
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;

cleanup:
    krb5_k_free_key(NULL, ke);
    krb5_k_free_key(NULL, ki);
    zapfree(cksum.data, cksum.length);
    return ret;
}
Exemple #20
0
static krb5_error_code
check_reply_server(krb5_context context, krb5_flags kdcoptions,
                   krb5_creds *in_cred, krb5_kdc_rep *dec_rep)
{

    if (!krb5_principal_compare(context, dec_rep->ticket->server,
                                dec_rep->enc_part2->server))
        return KRB5_KDCREP_MODIFIED;

    /* Reply is self-consistent. */

    if (krb5_principal_compare(context, dec_rep->ticket->server,
                               in_cred->server))
        return 0;

    /* Server in reply differs from what we requested. */

    if (kdcoptions & KDC_OPT_CANONICALIZE) {
        /* in_cred server differs from ticket returned, but ticket
           returned is consistent and we requested canonicalization. */

        TRACE_CHECK_REPLY_SERVER_DIFFERS(context, in_cred->server,
                                         dec_rep->enc_part2->server);
        return 0;
    }

    /* We didn't request canonicalization. */

    if (!IS_TGS_PRINC(in_cred->server) ||
        !IS_TGS_PRINC(dec_rep->ticket->server)) {
        /* Canonicalization not requested, and not a TGS referral. */
        return KRB5_KDCREP_MODIFIED;
    }
#if 0
    /*
     * Is this check needed?  find_nxt_kdc() in gc_frm_kdc.c already
     * effectively checks this.
     */
    if (krb5_realm_compare(context, in_cred->client, in_cred->server) &&
        data_eq(*in_cred->server->data[1], *in_cred->client->realm)) {
        /* Attempted to rewrite local TGS. */
        return KRB5_KDCREP_MODIFIED;
    }
#endif
    return 0;
}
Exemple #21
0
/*
 * Point *TGT at an allocated credentials structure containing a TGT for realm
 * retrieved from ctx->ccache.  If we are retrieving a foreign TGT, accept any
 * issuing realm (i.e. match only the service principal name).  If the TGT is
 * not found in the cache, return successfully but set *tgt to NULL.
 */
static krb5_error_code
get_cached_tgt(krb5_context context, krb5_tkt_creds_context ctx,
               const krb5_data *realm, krb5_creds **tgt)
{
    krb5_creds mcreds;
    krb5_error_code code;
    krb5_principal tgtname = NULL;
    krb5_flags flags;
    krb5_boolean local_realm = data_eq(*realm, ctx->client->realm);

    *tgt = NULL;

    /* Construct the principal krbtgt/<realm>@<client realm>.  The realm
     * won't matter if we're getting a foreign TGT. */
    code = krb5int_tgtname(context, realm, &ctx->client->realm, &tgtname);
    if (code != 0)
        goto cleanup;

    /* Don't match the TGT realm if we're getting a foreign TGT. */
    flags = KRB5_TC_SUPPORTED_KTYPES;
    if (!local_realm)
        flags |= KRB5_TC_MATCH_SRV_NAMEONLY;

    /* Construct a matching cred for the ccache query. */
    memset(&mcreds, 0, sizeof(mcreds));
    mcreds.client = ctx->client;
    mcreds.server = tgtname;

    /* Fetch the TGT credential. */
    context->use_conf_ktypes = TRUE;
    code = cache_get(context, ctx->ccache, flags, &mcreds, tgt);
    context->use_conf_ktypes = FALSE;

    /* Handle not-found errors.  Make a note if we couldn't find a local TGT
     * because of enctypes. */
    if (local_realm && code == KRB5_CC_NOT_KTYPE)
        ctx->cache_code = KRB5_CC_NOT_KTYPE;
    if (code != 0 && code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE)
        goto cleanup;
    code = 0;

cleanup:
    krb5_free_principal(context, tgtname);
    return code;
}
Exemple #22
0
static krb5_error_code
realm_choose(krb5_context context, krb5_ccselect_moddata data,
             krb5_principal server, krb5_ccache *cache_out,
             krb5_principal *princ_out)
{
    krb5_error_code ret;
    krb5_cccol_cursor cursor;
    krb5_ccache cache;
    krb5_principal princ;

    *cache_out = NULL;
    *princ_out = NULL;

    if (krb5_is_referral_realm(&server->realm))
        return KRB5_PLUGIN_NO_HANDLE;

    /* Scan the collection for a cache with a client principal in the same
     * realm as the server principal. */
    ret = krb5_cccol_cursor_new(context, &cursor);
    if (ret)
        return ret;
    while ((ret = krb5_cccol_cursor_next(context, cursor, &cache)) == 0 &&
           cache != NULL) {
        ret = krb5_cc_get_principal(context, cache, &princ);
        if (ret == 0) {
            if (data_eq(princ->realm, server->realm))
                break;
            krb5_free_principal(context, princ);
        }
        krb5_cc_close(context, cache);
    }
    krb5_cccol_cursor_free(context, &cursor);
    if (ret)
        return ret;

    if (cache == NULL)
        return KRB5_PLUGIN_NO_HANDLE;
    *cache_out = cache;
    *princ_out = princ;
    return 0;
}
Exemple #23
0
static void
test_kdc_check_lookaside_hit(void **state)
{
    struct entry *e;
    krb5_boolean result;
    krb5_data *result_data;
    krb5_context context = *state;
    krb5_data req = string2data("I'm a test request");
    krb5_data rep = string2data("I'm a test response");

    e = insert_entry(context, &req, &rep, 0);

    result = kdc_check_lookaside(context, &req, &result_data);

    assert_true(result);
    assert_true(data_eq(rep, *result_data));
    assert_int_equal(hits, 1);
    assert_int_equal(e->num_hits, 1);

    krb5_free_data(context, result_data);
}
Exemple #24
0
krb5_boolean KRB5_CALLCONV
krb5_principal_compare(krb5_context context, krb5_const_principal princ1, krb5_const_principal princ2)
{
    register int i;
    krb5_int32 nelem;

    nelem = krb5_princ_size(context, princ1);
    if (nelem != krb5_princ_size(context, princ2))
	return FALSE;

    if (! krb5_realm_compare(context, princ1, princ2))
	return FALSE;

    for (i = 0; i < (int) nelem; i++) {
	register const krb5_data *p1 = krb5_princ_component(context, princ1, i);
	register const krb5_data *p2 = krb5_princ_component(context, princ2, i);
	if (!data_eq(*p1, *p2))
	    return FALSE;
    }
    return TRUE;
}
Exemple #25
0
static void
test_kdc_insert_lookaside_cache_expire(void **state)
{
    struct entry *e;
    krb5_context context = *state;
    krb5_data req1 = string2data("I'm a test request");
    krb5_data rep1 = string2data("I'm a test response");
    size_t e1_size = entry_size(&req1, &rep1);
    krb5_data req2 = string2data("I'm a different test request");
    size_t e2_size = entry_size(&req2, NULL);
    struct entry *hash1_ent, *hash2_ent, *exp_ent;

    time_return(0, 0);
    kdc_insert_lookaside(context, &req1, &rep1);

    hash1_ent = k5_hashtab_get(hash_table, req1.data, req1.length);
    assert_non_null(hash1_ent);
    assert_true(data_eq(hash1_ent->req_packet, req1));
    assert_true(data_eq(hash1_ent->reply_packet, rep1));
    exp_ent = K5_TAILQ_FIRST(&expiration_queue);
    assert_true(data_eq(exp_ent->req_packet, req1));
    assert_true(data_eq(exp_ent->reply_packet, rep1));
    assert_int_equal(num_entries, 1);
    assert_int_equal(total_size, e1_size);

    /* Increase hits on entry */
    e = k5_hashtab_get(hash_table, req1.data, req1.length);
    assert_non_null(e);
    e->num_hits = 5;

    time_return(STALE_TIME + 1, 0);
    kdc_insert_lookaside(context, &req2, NULL);

    assert_null(k5_hashtab_get(hash_table, req1.data, req1.length));
    assert_int_equal(max_hits_per_entry, 5);

    hash2_ent = k5_hashtab_get(hash_table, req2.data, req2.length);
    assert_non_null(hash2_ent);
    assert_true(data_eq(hash2_ent->req_packet, req2));
    assert_int_equal(hash2_ent-> reply_packet.length, 0);
    exp_ent = K5_TAILQ_FIRST(&expiration_queue);
    assert_true(data_eq(exp_ent->req_packet, req2));
    assert_int_equal(exp_ent->reply_packet.length, 0);
    assert_int_equal(num_entries, 1);
    assert_int_equal(total_size, e2_size);
}
Exemple #26
0
/* Advance the process of chasing off-path TGTs. */
static krb5_error_code
step_get_tgt_offpath(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    const krb5_data *tgt_realm;

    /* We have no fallback if the last request failed, so just give up. */
    if (ctx->reply_code != 0)
        return ctx->reply_code;

    /* Verify that we got a TGT. */
    if (!IS_TGS_PRINC(context, ctx->reply_creds->server))
        return KRB5_KDCREP_MODIFIED;

    /* Use this tgt for the next request. */
    krb5_free_creds(context, ctx->cur_tgt);
    ctx->cur_tgt = ctx->reply_creds;
    ctx->reply_creds = NULL;

    /* Check if we've seen this realm before, and remember it. */
    tgt_realm = &ctx->cur_tgt->server->data[1];
    if (seen_realm_before(context, ctx, tgt_realm))
        return KRB5_KDC_UNREACH;
    code = remember_realm(context, ctx, tgt_realm);
    if (code != 0)
        return code;

    if (data_eq(*tgt_realm, ctx->server->realm)) {
        /* We received the server realm TGT we asked for. */
        TRACE_TKT_CREDS_TARGET_TGT_OFFPATH(context, ctx->cur_tgt->server);
        return end_get_tgt(context, ctx);
    } else if (ctx->offpath_count++ >= KRB5_REFERRAL_MAXHOPS) {
        /* Time to give up. */
        return KRB5_KDCREP_MODIFIED;
    }

    return make_request_for_tgt(context, ctx, &ctx->server->realm);
}
Exemple #27
0
static void
test_kdc_insert_lookaside_multiple(void **state)
{
    krb5_context context = *state;
    krb5_data req1 = string2data("I'm a test request");
    krb5_data rep1 = string2data("I'm a test response");
    size_t e1_size = entry_size(&req1, &rep1);
    krb5_data req2 = string2data("I'm a different test request");
    size_t e2_size = entry_size(&req2, NULL);
    struct entry *hash1_ent, *hash2_ent, *exp_first, *exp_last;

    time_return(0, 0);
    kdc_insert_lookaside(context, &req1, &rep1);

    hash1_ent = k5_hashtab_get(hash_table, req1.data, req1.length);
    assert_non_null(hash1_ent);
    assert_true(data_eq(hash1_ent->req_packet, req1));
    assert_true(data_eq(hash1_ent->reply_packet, rep1));
    exp_first = K5_TAILQ_FIRST(&expiration_queue);
    assert_true(data_eq(exp_first->req_packet, req1));
    assert_true(data_eq(exp_first->reply_packet, rep1));
    assert_int_equal(num_entries, 1);
    assert_int_equal(total_size, e1_size);

    time_return(0, 0);
    kdc_insert_lookaside(context, &req2, NULL);

    hash2_ent = k5_hashtab_get(hash_table, req2.data, req2.length);
    assert_non_null(hash2_ent);
    assert_true(data_eq(hash2_ent->req_packet, req2));
    assert_int_equal(hash2_ent->reply_packet.length, 0);
    exp_last = K5_TAILQ_LAST(&expiration_queue, entry_queue);
    assert_true(data_eq(exp_last->req_packet, req2));
    assert_int_equal(exp_last->reply_packet.length, 0);
    assert_int_equal(num_entries, 2);
    assert_int_equal(total_size, e1_size + e2_size);
}
/*ARGSUSED*/
krb5_error_code
process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
                krb5_data **response)
{
    krb5_keyblock * subkey = 0;
    krb5_kdc_req *request = 0;
    krb5_db_entry server;
    krb5_kdc_rep reply;
    krb5_enc_kdc_rep_part reply_encpart;
    krb5_ticket ticket_reply, *header_ticket = 0;
    int st_idx = 0;
    krb5_enc_tkt_part enc_tkt_reply;
    krb5_transited enc_tkt_transited;
    int newtransited = 0;
    krb5_error_code retval = 0;
    krb5_keyblock encrypting_key;
    int nprincs = 0;
    krb5_boolean more;
    krb5_timestamp kdc_time, authtime=0;
    krb5_keyblock session_key;
    krb5_timestamp until, rtime;
    krb5_keyblock *reply_key = NULL;
    krb5_keyblock *mkey_ptr;
    krb5_key_data  *server_key;
    char *cname = 0, *sname = 0, *altcname = 0;
    krb5_last_req_entry *nolrarray[2], nolrentry;
    krb5_enctype useenctype;
    int errcode, errcode2;
    register int i;
    int firstpass = 1;
    const char        *status = 0;
    krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */
    krb5_db_entry client, krbtgt;
    int c_nprincs = 0, k_nprincs = 0;
    krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */
    krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
    unsigned int c_flags = 0, s_flags = 0;       /* client/server KDB flags */
    char *s4u_name = NULL;
    krb5_boolean is_referral, db_ref_done = FALSE;
    const char *emsg = NULL;
    krb5_data *tgs_1 =NULL, *server_1 = NULL;
    krb5_principal krbtgt_princ;
    krb5_kvno ticket_kvno = 0;
    struct kdc_request_state *state = NULL;
    krb5_pa_data *pa_tgs_req; /*points into request*/
    krb5_data scratch;

    session_key.contents = NULL;

    retval = decode_krb5_tgs_req(pkt, &request);
    if (retval)
        return retval;

    /*
     * setup_server_realm() sets up the global realm-specific data pointer.
     */
    if ((retval = setup_server_realm(request->server))) {
        krb5_free_kdc_req(kdc_context, request);
        return retval;
    }
    errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket,
                                  &krbtgt, &k_nprincs, &subkey, &pa_tgs_req);
    if (header_ticket && header_ticket->enc_part2 &&
        (errcode2 = krb5_unparse_name(kdc_context,
                                      header_ticket->enc_part2->client,
                                      &cname))) {
        status = "UNPARSING CLIENT";
        errcode = errcode2;
        goto cleanup;
    }
    limit_string(cname);
    
    if (errcode) {
        status = "PROCESS_TGS";
        goto cleanup;
    }

    if (!header_ticket) {
        errcode = KRB5_NO_TKT_SUPPLIED;        /* XXX? */
        status="UNEXPECTED NULL in header_ticket";
        goto cleanup;
    }
    errcode = kdc_make_rstate(&state);
    if (errcode !=0) {
	status = "making state";
	goto cleanup;
    }
    scratch.length = pa_tgs_req->length;
    scratch.data = (char *) pa_tgs_req->contents;
    errcode = kdc_find_fast(&request, &scratch, subkey, header_ticket->enc_part2->session, state);
    if (errcode !=0) {
	status = "kdc_find_fast";
		goto cleanup;
    }


    /*
     * Pointer to the encrypted part of the header ticket, which may be
     * replaced to point to the encrypted part of the evidence ticket
     * if constrained delegation is used. This simplifies the number of
     * special cases for constrained delegation.
     */
    header_enc_tkt = header_ticket->enc_part2;
    
    /*
     * We've already dealt with the AP_REQ authentication, so we can
     * use header_ticket freely.  The encrypted part (if any) has been
     * decrypted with the session key.
     */

    /* XXX make sure server here has the proper realm...taken from AP_REQ
       header? */

    if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
        setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
        setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
    }

    db_ref_done = FALSE;
ref_tgt_again:
    nprincs = 1;
    if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) {
        status = "UNPARSING SERVER";
        goto cleanup;
    }
    limit_string(sname);

    errcode = krb5_db_get_principal_ext(kdc_context,
                                        request->server,
                                        s_flags,
                                        &server,
                                        &nprincs,
                                        &more);
    if (errcode) {
        status = "LOOKING_UP_SERVER";
        nprincs = 0;
        goto cleanup;
    }
tgt_again:
    if (more) {
        status = "NON_UNIQUE_PRINCIPAL";
        errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
        goto cleanup;
    } else if (nprincs != 1) {
        /*
         * might be a request for a TGT for some other realm; we
         * should do our best to find such a TGS in this db
         */
        if (firstpass ) {

            if ( krb5_is_tgs_principal(request->server) == TRUE) { /* Principal is a name of krb ticket service */
                if (krb5_princ_size(kdc_context, request->server) == 2) {

                    server_1 = krb5_princ_component(kdc_context, request->server, 1);
                    tgs_1 = krb5_princ_component(kdc_context, tgs_server, 1);

                    if (!tgs_1 || !data_eq(*server_1, *tgs_1)) {
                        krb5_db_free_principal(kdc_context, &server, nprincs);
                        find_alternate_tgs(request, &server, &more, &nprincs);
                        firstpass = 0;
                        goto tgt_again;
                    }
                }
                krb5_db_free_principal(kdc_context, &server, nprincs);
                status = "UNKNOWN_SERVER";
                errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
                goto cleanup;

            } else if ( db_ref_done == FALSE) {
                retval = prep_reprocess_req(request, &krbtgt_princ);
                if (!retval) {
                    krb5_free_principal(kdc_context, request->server);
                    retval = krb5_copy_principal(kdc_context, krbtgt_princ, &(request->server));
                    if (!retval) {
                        db_ref_done = TRUE;
                        if (sname != NULL)
                            free(sname);
                        goto ref_tgt_again;
                    }
                }
            }
        }

        krb5_db_free_principal(kdc_context, &server, nprincs);
        status = "UNKNOWN_SERVER";
        errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
        goto cleanup;
    }

    if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
        status = "TIME_OF_DAY";
        goto cleanup;
    }
    
    if ((retval = validate_tgs_request(request, server, header_ticket,
                                       kdc_time, &status))) {
    if (!status)
        status = "UNKNOWN_REASON";
        errcode = retval + ERROR_TABLE_BASE_krb5;
        goto cleanup;
    }

    if (!is_local_principal(header_enc_tkt->client))
        setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);

    is_referral = krb5_is_tgs_principal(server.princ) &&
        !krb5_principal_compare(kdc_context, tgs_server, server.princ);

    /* Check for protocol transition */
    errcode = kdc_process_s4u2self_req(kdc_context,
				       request,
				       header_enc_tkt->client,
                                       &server,
				       subkey,
				       header_enc_tkt->session,
				       kdc_time,
                                       &s4u_x509_user,
				       &client,
				       &c_nprincs,
				       &status);
    if (errcode)
        goto cleanup;
    if (s4u_x509_user != NULL)
        setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);

    /*
     * We pick the session keytype here....
     * 
     * Some special care needs to be taken in the user-to-user
     * case, since we don't know what keytypes the application server
     * which is doing user-to-user authentication can support.  We
     * know that it at least must be able to support the encryption
     * type of the session key in the TGT, since otherwise it won't be
     * able to decrypt the U2U ticket!  So we use that in preference
     * to anything else.
     */
    useenctype = 0;
    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY |
                        KDC_OPT_CNAME_IN_ADDL_TKT)) {
        krb5_keyblock *        st_sealing_key;
        krb5_kvno       st_srv_kvno;
        krb5_enctype    etype;
        krb5_db_entry    st_client;
        int             st_nprincs = 0;

        /*
         * Get the key for the second ticket, and decrypt it.
         */
        if ((errcode = kdc_get_server_key(request->second_ticket[st_idx],
                                          c_flags,
                                          TRUE, /* match_enctype */
                                          &st_client,
                                          &st_nprincs,
                                          &st_sealing_key,
                                          &st_srv_kvno))) {
            status = "2ND_TKT_SERVER";
            goto cleanup;
        }
        errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key,
                                    request->second_ticket[st_idx]);
        krb5_free_keyblock(kdc_context, st_sealing_key);
        if (errcode) {
            status = "2ND_TKT_DECRYPT";
            krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
            goto cleanup;
        }

        etype = request->second_ticket[st_idx]->enc_part2->session->enctype;
        if (!krb5_c_valid_enctype(etype)) {
            status = "BAD_ETYPE_IN_2ND_TKT";
            errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
            krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
            goto cleanup;
        }

        for (i = 0; i < request->nktypes; i++) {
            if (request->ktype[i] == etype) {
                useenctype = etype;
                break;
            }
        }

        if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
            /* Do constrained delegation protocol and authorization checks */
            errcode = kdc_process_s4u2proxy_req(kdc_context,
                                                request,
                                                request->second_ticket[st_idx]->enc_part2,
                                                &st_client,
                                                header_ticket->enc_part2->client,
                                                request->server,
                                                &status);
            if (errcode)
                goto cleanup;

            setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);

            assert(krb5_is_tgs_principal(header_ticket->server));

            /* From now on, use evidence ticket as header ticket */
            header_enc_tkt = request->second_ticket[st_idx]->enc_part2;

            assert(c_nprincs == 0); /* assured by kdc_process_s4u2self_req() */

            client = st_client;
            c_nprincs = st_nprincs;
        } else {
            /* "client" is not used for user2user */
            krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
        }
    }

    /*
     * Select the keytype for the ticket session key.
     */
    if ((useenctype == 0) &&
        (useenctype = select_session_keytype(kdc_context, &server,
        request->nktypes,
        request->ktype)) == 0) {
        /* unsupported ktype */
        status = "BAD_ENCRYPTION_TYPE";
        errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
        goto cleanup;
    }
    
    errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key);

    if (errcode) {
        /* random key failed */
        status = "RANDOM_KEY_FAILED";
        goto cleanup;
    }

    authtime = header_enc_tkt->times.authtime;

    if (is_referral)
        ticket_reply.server = server.princ;
    else
        ticket_reply.server = request->server; /* XXX careful for realm... */

    enc_tkt_reply.flags = 0;
    enc_tkt_reply.times.starttime = 0;

    if (isflagset(server.attributes, KRB5_KDB_OK_AS_DELEGATE))
        setflag(enc_tkt_reply.flags, TKT_FLG_OK_AS_DELEGATE);

    /*
     * Fix header_ticket's starttime; if it's zero, fill in the
     * authtime's value.
     */
    if (!(header_enc_tkt->times.starttime))
        header_enc_tkt->times.starttime = header_enc_tkt->times.authtime;

    /* don't use new addresses unless forwarded, see below */

    enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
    /* noaddrarray[0] = 0; */
    reply_encpart.caddrs = 0;/* optional...don't put it in */
    reply_encpart.enc_padata = NULL;

    /* It should be noted that local policy may affect the  */
    /* processing of any of these flags.  For example, some */
    /* realms may refuse to issue renewable tickets         */

    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE)) {
        setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);

        if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
            /*
             * If S4U2Self principal is not forwardable, then mark ticket as
             * unforwardable. This behaviour matches Windows, but it is
             * different to the MIT AS-REQ path, which returns an error
             * (KDC_ERR_POLICY) if forwardable tickets cannot be issued.
             *
             * Consider this block the S4U2Self equivalent to
             * validate_forwardable().
             */
            if (c_nprincs &&
                isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
                clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
            /*
             * OK_TO_AUTH_AS_DELEGATE must be set on the service requesting
             * S4U2Self in order for forwardable tickets to be returned.
             */
            else if (!is_referral &&
                !isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
                clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
        }
    }

    if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
        setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);

        /* include new addresses in ticket & reply */

        enc_tkt_reply.caddrs = request->addresses;
        reply_encpart.caddrs = request->addresses;
    }
    if (isflagset(header_enc_tkt->flags, TKT_FLG_FORWARDED))
        setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);

    if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
        setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);

    if (isflagset(request->kdc_options, KDC_OPT_PROXY)) {
        setflag(enc_tkt_reply.flags, TKT_FLG_PROXY);

        /* include new addresses in ticket & reply */

        enc_tkt_reply.caddrs = request->addresses;
        reply_encpart.caddrs = request->addresses;
    }

    if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
        setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);

    if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
        setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
        setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
        enc_tkt_reply.times.starttime = request->from;
    } else
        enc_tkt_reply.times.starttime = kdc_time;

    if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
        assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
        /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
           to the caller */
        ticket_reply = *(header_ticket);
        enc_tkt_reply = *(header_ticket->enc_part2);
        clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
    }

    if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
        krb5_deltat old_life;

        assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
        /* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
           to the caller */
        ticket_reply = *(header_ticket);
        enc_tkt_reply = *(header_ticket->enc_part2);

        old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime;

        enc_tkt_reply.times.starttime = kdc_time;
        enc_tkt_reply.times.endtime =
            min(header_ticket->enc_part2->times.renew_till,
                kdc_time + old_life);
    } else {
        /* not a renew request */
        enc_tkt_reply.times.starttime = kdc_time;
        until = (request->till == 0) ? kdc_infinity : request->till;
        enc_tkt_reply.times.endtime =
            min(until, min(enc_tkt_reply.times.starttime + server.max_life,
               min(enc_tkt_reply.times.starttime + max_life_for_realm,
                   header_enc_tkt->times.endtime)));
        if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
            (enc_tkt_reply.times.endtime < request->till) &&
            isflagset(header_enc_tkt->flags, TKT_FLG_RENEWABLE)) {
            setflag(request->kdc_options, KDC_OPT_RENEWABLE);
            request->rtime =
                min(request->till, header_enc_tkt->times.renew_till);
        }
    }
    rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;

    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) {
        /* already checked above in policy check to reject request for a
           renewable ticket using a non-renewable ticket */
        setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
        enc_tkt_reply.times.renew_till =
                        min(rtime,
                            min(header_enc_tkt->times.renew_till,
                                enc_tkt_reply.times.starttime +
                                min(server.max_renewable_life,
                                max_renewable_life_for_realm)));
    } else {
        enc_tkt_reply.times.renew_till = 0;
    }
    
    /*
     * Set authtime to be the same as header_ticket's
     */
    enc_tkt_reply.times.authtime = header_enc_tkt->times.authtime;
    
    /*
     * Propagate the preauthentication flags through to the returned ticket.
     */
    if (isflagset(header_enc_tkt->flags, TKT_FLG_PRE_AUTH))
        setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);

    if (isflagset(header_enc_tkt->flags, TKT_FLG_HW_AUTH))
        setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
    
    /* starttime is optional, and treated as authtime if not present.
       so we can nuke it if it matches */
    if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
        enc_tkt_reply.times.starttime = 0;

    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
        errcode = krb5_unparse_name(kdc_context, s4u_x509_user->user_id.user, &s4u_name);
    } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
        errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name);
    } else {
        errcode = 0;
    }
    if (errcode) {
        status = "UNPARSING S4U CLIENT";
        goto cleanup;
    }

    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
        krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
        encrypting_key = *(t2enc->session);
    } else {
        /*
         * Find the server key
         */
        if ((errcode = krb5_dbe_find_enctype(kdc_context, &server,
                             -1, /* ignore keytype */
                                             -1, /* Ignore salttype */
                                             0,/* Get highest kvno */
                                             &server_key))) {
            status = "FINDING_SERVER_KEY";
            goto cleanup;
        }

        if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server,
                                          &mkey_ptr))) {
            krb5_keylist_node *tmp_mkey_list;
            /* try refreshing master key list */
            /* XXX it would nice if we had the mkvno here for optimization */
            if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
                                        &master_keyblock, 0, &tmp_mkey_list) == 0) {
                krb5_dbe_free_key_list(kdc_context, master_keylist);
                master_keylist = tmp_mkey_list;
                if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist,
                                                  &server, &mkey_ptr))) {
                    status = "FINDING_MASTER_KEY";
                    goto cleanup;
                }
            } else {
                status = "FINDING_MASTER_KEY";
                goto cleanup;
            }
        }

        /* convert server.key into a real key (it may be encrypted
         *        in the database) */
        if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
                                                   mkey_ptr,
                                                   server_key, &encrypting_key,
                                                   NULL))) {
            status = "DECRYPT_SERVER_KEY";
            goto cleanup;
        }
    }

    if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
        /*
         * Don't allow authorization data to be disabled if constrained
         * delegation is requested. We don't want to deny the server
         * the ability to validate that delegation was used.
         */
        clear(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
    }
    if (isflagset(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) {
        /*
         * If we are not doing protocol transition/constrained delegation
         * and there was no authorization data included, try to lookup
         * the client principal as it may be mapped to a local account.
         *
         * Always validate authorization data for constrained delegation
         * because we must validate the KDC signatures.
         */
        if (!isflagset(c_flags, KRB5_KDB_FLAGS_S4U) &&
            header_enc_tkt->authorization_data == NULL) {

            /* Generate authorization data so we can include it in ticket */
            setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
            /* Map principals from foreign (possibly non-AD) realms */
            setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);

            assert(c_nprincs == 0); /* should not have been looked up already */

            c_nprincs = 1;
            errcode = krb5_db_get_principal_ext(kdc_context,
                                                header_enc_tkt->client,
                                                c_flags,
                                                &client,
                                                &c_nprincs,
                                                &more);
            /*
             * We can ignore errors because the principal may be a
             * valid cross-realm principal for which we have no local
             * mapping. But we do want to check that at most one entry
             * was returned.
             */
            if (errcode == 0 && (more || c_nprincs > 1)) {
                errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
                goto cleanup;
            } else if (errcode) {
                c_nprincs = 0;
            }
        }
    }

    enc_tkt_reply.authorization_data = NULL;

    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
        !isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM))
        enc_tkt_reply.client = s4u_x509_user->user_id.user;
    else
        enc_tkt_reply.client = header_enc_tkt->client;

    enc_tkt_reply.session = &session_key;
    enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
    enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */

    errcode = handle_authdata(kdc_context,
                              c_flags,
                              (c_nprincs != 0) ? &client : NULL,
                              &server,
                              (k_nprincs != 0) ? &krbtgt : NULL,
                              subkey != NULL ? subkey :
                              header_ticket->enc_part2->session,
                              &encrypting_key, /* U2U or server key */
                              pkt,
                              request,
                              s4u_x509_user ?
				s4u_x509_user->user_id.user : NULL,
                              header_enc_tkt,
                              &enc_tkt_reply);
    if (errcode) {
        krb5_klog_syslog(LOG_INFO, "TGS_REQ : handle_authdata (%d)", errcode);
        status = "HANDLE_AUTHDATA";
        goto cleanup;
    }

    if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
        errcode = return_svr_referral_data(kdc_context,
                                           &server, &reply_encpart);
        if (errcode) {
            status = "KDC_RETURN_ENC_PADATA";
            goto cleanup;
        }
    }

    /*
     * Only add the realm of the presented tgt to the transited list if 
     * it is different than the local realm (cross-realm) and it is different
     * than the realm of the client (since the realm of the client is already
     * implicitly part of the transited list and should not be explicitly
     * listed).
     */

    /* realm compare is like strcmp, but knows how to deal with these args */
    if (realm_compare(header_ticket->server, tgs_server) ||
        realm_compare(header_ticket->server, enc_tkt_reply.client)) {
        /* tgt issued by local realm or issued by realm of client */
        enc_tkt_reply.transited = header_enc_tkt->transited;
    } else {
        /* tgt issued by some other realm and not the realm of the client */
        /* assemble new transited field into allocated storage */
        if (header_enc_tkt->transited.tr_type !=
            KRB5_DOMAIN_X500_COMPRESS) {
            status = "BAD_TRTYPE";
            errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
            goto cleanup;
        }
        enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
        enc_tkt_transited.magic = 0;
        enc_tkt_transited.tr_contents.magic = 0;
        enc_tkt_transited.tr_contents.data = 0;
        enc_tkt_transited.tr_contents.length = 0;
        enc_tkt_reply.transited = enc_tkt_transited;
        if ((errcode =
            add_to_transited(&header_enc_tkt->transited.tr_contents,
                             &enc_tkt_reply.transited.tr_contents,
                             header_ticket->server,
                             enc_tkt_reply.client,
                             request->server))) {
                                 status = "ADD_TR_FAIL";
                                 goto cleanup;
        }
        newtransited = 1;
    }
    if (isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) {
        errcode = validate_transit_path(kdc_context, header_enc_tkt->client,
        &server,
        (k_nprincs != 0) ? &krbtgt : NULL);
        if (errcode) {
            status = "NON_TRANSITIVE";
            goto cleanup;
        }
    }
    if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
        unsigned int tlen;
        char *tdots;

        errcode = kdc_check_transited_list (kdc_context,
                                            &enc_tkt_reply.transited.tr_contents,
                                            krb5_princ_realm (kdc_context, header_enc_tkt->client),
                                            krb5_princ_realm (kdc_context, request->server));
        tlen = enc_tkt_reply.transited.tr_contents.length;
        tdots = tlen > 125 ? "..." : "";
        tlen = tlen > 125 ? 125 : tlen;

        if (errcode == 0) {
            setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED);
        } else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT)
            krb5_klog_syslog (LOG_INFO,
                              "bad realm transit path from '%s' to '%s' "
                              "via '%.*s%s'",
                              cname ? cname : "<unknown client>",
                              sname ? sname : "<unknown server>",
                              tlen,
                              enc_tkt_reply.transited.tr_contents.data,
                              tdots);
        else {
            emsg = krb5_get_error_message(kdc_context, errcode);
            krb5_klog_syslog (LOG_ERR,
                              "unexpected error checking transit from "
                              "'%s' to '%s' via '%.*s%s': %s",
                              cname ? cname : "<unknown client>",
                              sname ? sname : "<unknown server>",
                              tlen,
                              enc_tkt_reply.transited.tr_contents.data,
                              tdots, emsg);
            krb5_free_error_message(kdc_context, emsg);
            emsg = NULL;
        }
    } else
        krb5_klog_syslog (LOG_INFO, "not checking transit path");
    if (reject_bad_transit
        && !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) {
        errcode = KRB5KDC_ERR_POLICY;
        status = "BAD_TRANSIT";
        goto cleanup;
    }

    ticket_reply.enc_part2 = &enc_tkt_reply;

    /*
     * If we are doing user-to-user authentication, then make sure
     * that the client for the second ticket matches the request
     * server, and then encrypt the ticket using the session key of
     * the second ticket.
     */
    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
        /*
         * Make sure the client for the second ticket matches
         * requested server.
         */
        krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
        krb5_principal client2 = t2enc->client;
        if (!krb5_principal_compare(kdc_context, request->server, client2)) {
            if ((errcode = krb5_unparse_name(kdc_context, client2, &altcname)))
                altcname = 0;
            if (altcname != NULL)
                limit_string(altcname);

            errcode = KRB5KDC_ERR_SERVER_NOMATCH;
            status = "2ND_TKT_MISMATCH";
            goto cleanup;
        }

        ticket_kvno = 0;
        ticket_reply.enc_part.enctype = t2enc->session->enctype;
        st_idx++;
    } else {
        ticket_kvno = server_key->key_data_kvno;
    }

    errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key,
                    &ticket_reply);
    if (!isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY))
        krb5_free_keyblock_contents(kdc_context, &encrypting_key);
    if (errcode) {
        status = "TKT_ENCRYPT";
        goto cleanup;
    }
    ticket_reply.enc_part.kvno = ticket_kvno;
    /* Start assembling the response */
    reply.msg_type = KRB5_TGS_REP;
    reply.padata = 0;/* always */
    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
        find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER) != NULL) {
        errcode = kdc_make_s4u2self_rep(kdc_context,
                                        subkey,
                                        header_ticket->enc_part2->session,
                                        s4u_x509_user,
                                        &reply,
                                        &reply_encpart);
        if (errcode) {
            status = "KDC_RETURN_S4U2SELF_PADATA";
            goto cleanup;
        }
    }

    reply.client = enc_tkt_reply.client;
    reply.enc_part.kvno = 0;/* We are using the session key */
    reply.ticket = &ticket_reply;

    reply_encpart.session = &session_key;
    reply_encpart.nonce = request->nonce;

    /* copy the time fields EXCEPT for authtime; its location
       is used for ktime */
    reply_encpart.times = enc_tkt_reply.times;
    reply_encpart.times.authtime = header_enc_tkt->times.authtime;

    /* starttime is optional, and treated as authtime if not present.
       so we can nuke it if it matches */
    if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
        enc_tkt_reply.times.starttime = 0;

    nolrentry.lr_type = KRB5_LRQ_NONE;
    nolrentry.value = 0;
    nolrarray[0] = &nolrentry;
    nolrarray[1] = 0;
    reply_encpart.last_req = nolrarray;        /* not available for TGS reqs */
    reply_encpart.key_exp = 0;/* ditto */
    reply_encpart.flags = enc_tkt_reply.flags;
    reply_encpart.server = ticket_reply.server;
    
    /* use the session key in the ticket, unless there's a subsession key
       in the AP_REQ */

    reply.enc_part.enctype = subkey ? subkey->enctype :
    header_ticket->enc_part2->session->enctype;
    errcode  = kdc_fast_response_handle_padata(state, request, &reply,
					       subkey?subkey->enctype:header_ticket->enc_part2->session->enctype);
    if (errcode !=0 ) {
	status = "Preparing FAST padata";
	goto cleanup;
    }
    errcode =kdc_fast_handle_reply_key(state, subkey?subkey:header_ticket->enc_part2->session, &reply_key);
    if (errcode) {
      status  = "generating reply key";
      goto cleanup;
    }
            errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
                  subkey ? 1 : 0,
					  reply_key,
                  &reply, response);
    if (errcode) {
        status = "ENCODE_KDC_REP";
    } else {
        status = "ISSUE";
    }

    memset(ticket_reply.enc_part.ciphertext.data, 0,
           ticket_reply.enc_part.ciphertext.length);
    free(ticket_reply.enc_part.ciphertext.data);
    /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
       can use them in raw form if needed.  But, we don't... */
    memset(reply.enc_part.ciphertext.data, 0,
           reply.enc_part.ciphertext.length);
    free(reply.enc_part.ciphertext.data);
    
cleanup:
    assert(status != NULL);
    if (reply_key)
      krb5_free_keyblock(kdc_context, reply_key);
    if (errcode)
        emsg = krb5_get_error_message (kdc_context, errcode);
    log_tgs_req(from, request, &reply, cname, sname, altcname, authtime,
                c_flags, s4u_name, status, errcode, emsg);
    if (errcode) {
        krb5_free_error_message (kdc_context, emsg);
        emsg = NULL;
    }

    if (errcode) {
        int got_err = 0;
        if (status == 0) {
            status = krb5_get_error_message (kdc_context, errcode);
            got_err = 1;
        }
        errcode -= ERROR_TABLE_BASE_krb5;
        if (errcode < 0 || errcode > 128)
            errcode = KRB_ERR_GENERIC;

        retval = prepare_error_tgs(state, request, header_ticket, errcode,
        nprincs ? server.princ : NULL,
                   response, status);
        if (got_err) {
            krb5_free_error_message (kdc_context, status);
            status = 0;
        }
    }
    
    if (header_ticket != NULL)
        krb5_free_ticket(kdc_context, header_ticket);
    if (request != NULL)
        krb5_free_kdc_req(kdc_context, request);
    if (state)
	kdc_free_rstate(state);
    if (cname != NULL)
        free(cname);
    if (sname != NULL)
        free(sname);
    if (nprincs != 0)
        krb5_db_free_principal(kdc_context, &server, 1);
    if (session_key.contents != NULL)
        krb5_free_keyblock_contents(kdc_context, &session_key);
    if (newtransited)
        free(enc_tkt_reply.transited.tr_contents.data);
    if (k_nprincs)
        krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs);
    if (c_nprincs)
        krb5_db_free_principal(kdc_context, &client, c_nprincs);
    if (s4u_x509_user != NULL)
        krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user);
    if (kdc_issued_auth_data != NULL)
        krb5_free_authdata(kdc_context, kdc_issued_auth_data);
    if (s4u_name != NULL)
        free(s4u_name);
    if (subkey != NULL)
        krb5_free_keyblock(kdc_context, subkey);
    if (reply.padata)
        krb5_free_pa_data(kdc_context, reply.padata);
    if (reply_encpart.enc_padata)
        krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);

    return retval;
}
Exemple #29
0
static krb5_error_code
krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
			   krb5_creds *in_cred, krb5_creds **out_cred,
			   krb5_creds ***tgts, int kdcopt)
{
    krb5_error_code retval, subretval;
    krb5_principal client, server, supplied_server, out_supplied_server;
    krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS];
    krb5_creds *otgtptr = NULL;
    int tgtptr_isoffpath = 0;
    krb5_boolean old_use_conf_ktypes;
    char **hrealms;
    unsigned int referral_count, i;

    /* 
     * Set up client and server pointers.  Make a fresh and modifyable
     * copy of the in_cred server and save the supplied version.
     */
    client = in_cred->client;
    if ((retval=krb5_copy_principal(context, in_cred->server, &server)))
        return retval;
    /* We need a second copy for the output creds. */
    if ((retval = krb5_copy_principal(context, server,
				      &out_supplied_server)) != 0 ) {
	krb5_free_principal(context, server);
	return retval;
    }
    supplied_server = in_cred->server;
    in_cred->server=server;

    DUMP_PRINC("gc_from_kdc initial client", client);
    DUMP_PRINC("gc_from_kdc initial server", server);
    memset(&cc_tgt, 0, sizeof(cc_tgt));
    memset(&tgtq, 0, sizeof(tgtq));
    memset(&referral_tgts, 0, sizeof(referral_tgts));

    tgtptr = NULL;
    *tgts = NULL;
    *out_cred=NULL;
    old_use_conf_ktypes = context->use_conf_ktypes;

    /* Copy client realm to server if no hint. */
    if (krb5_is_referral_realm(&server->realm)) {
        /* Use the client realm. */
        DPRINTF(("gc_from_kdc: no server realm supplied, "
		 "using client realm.\n"));
	krb5_free_data_contents(context, &server->realm);
	if (!( server->realm.data = (char *)malloc(client->realm.length+1)))
	    return ENOMEM;
	memcpy(server->realm.data, client->realm.data, client->realm.length);
	server->realm.length = client->realm.length;
	server->realm.data[server->realm.length] = 0;
    }
    /*
     * Retreive initial TGT to match the specified server, either for the
     * local realm in the default (referral) case or for the remote
     * realm if we're starting someplace non-local.
     */
    retval = tgt_mcred(context, client, server, client, &tgtq);
    if (retval)
	goto cleanup;

    /* Fast path: Is it in the ccache? */
    context->use_conf_ktypes = 1;
    retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
				   &tgtq, &cc_tgt);
    if (!retval) {
	tgtptr = &cc_tgt;
    } else if (!HARD_CC_ERR(retval)) {
        DPRINTF(("gc_from_kdc: starting do_traversal to find "
		 "initial TGT for referral\n"));
	tgtptr_isoffpath = 0;
	otgtptr = NULL;
	retval = do_traversal(context, ccache, client, server,
			      &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath);
    }
    if (retval) {
        DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n"));
        goto cleanup;
    }

    DUMP_PRINC("gc_from_kdc: server as requested", supplied_server);

    /*
     * Try requesting a service ticket from our local KDC with referrals
     * turned on.  If the first referral succeeds, follow a referral-only
     * path, otherwise fall back to old-style assumptions.
     */

    /*
     * Save TGTPTR because we rewrite it in the referral loop, and
     * we might need to explicitly free it later.
     */
    otgtptr = tgtptr;
    for (referral_count = 0;
	 referral_count < KRB5_REFERRAL_MAXHOPS;
	 referral_count++) {
#if 0
        DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server);
        DUMP_PRINC("gc_from_kdc: referral loop: request is for", server);
#endif
        retval = krb5_get_cred_via_tkt(context, tgtptr,
				       KDC_OPT_CANONICALIZE | 
				       FLAGS2OPTS(tgtptr->ticket_flags) |  
				       kdcopt |
				       (in_cred->second_ticket.length ?
					KDC_OPT_ENC_TKT_IN_SKEY : 0),
				       tgtptr->addresses, in_cred, out_cred);
	if (retval) {
	    DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n",
		     error_message(retval)));
	    /* If we haven't gone anywhere yet, fail through to the
	       non-referral case. */
	    if (referral_count==0) {
	        DPRINTF(("gc_from_kdc: initial referral failed; "
			 "punting to fallback.\n"));
	        break;
	    }
	    /* Otherwise, try the same query without canonicalization
	       set, and fail hard if that doesn't work. */
	    DPRINTF(("gc_from_kdc: referral #%d failed; "
		     "retrying without option.\n", referral_count + 1));
	    retval = krb5_get_cred_via_tkt(context, tgtptr,
					   FLAGS2OPTS(tgtptr->ticket_flags) |  
					   kdcopt |
					   (in_cred->second_ticket.length ?
					    KDC_OPT_ENC_TKT_IN_SKEY : 0),
					   tgtptr->addresses,
					   in_cred, out_cred);
	    /* Whether or not that succeeded, we're done. */
	    goto cleanup;
	}
	/* Referral request succeeded; let's see what it is. */
	if (krb5_principal_compare(context, in_cred->server,
				   (*out_cred)->server)) {
	    DPRINTF(("gc_from_kdc: request generated ticket "
		     "for requested server principal\n"));
	    DUMP_PRINC("gc_from_kdc final referred reply",
		       in_cred->server);

	    /*
	     * Check if the return enctype is one that we requested if
	     * needed.
	     */
	    if (old_use_conf_ktypes || context->tgs_ktype_count == 0)
		goto cleanup;
	    for (i = 0; i < context->tgs_ktype_count; i++) {
		if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) {
		    /* Found an allowable etype, so we're done */
		    goto cleanup;
		}
	    }
	    /*
	     *  We need to try again, but this time use the
	     *  tgs_ktypes in the context. At this point we should
	     *  have all the tgts to succeed.
	     */

	    /* Free "wrong" credential */
	    krb5_free_creds(context, *out_cred);
	    *out_cred = NULL;
	    /* Re-establish tgs etypes */
	    context->use_conf_ktypes = old_use_conf_ktypes;
	    retval = krb5_get_cred_via_tkt(context, tgtptr,
					   KDC_OPT_CANONICALIZE | 
					   FLAGS2OPTS(tgtptr->ticket_flags) |  
					   kdcopt |
					   (in_cred->second_ticket.length ?
					    KDC_OPT_ENC_TKT_IN_SKEY : 0),
					   tgtptr->addresses,
					   in_cred, out_cred);
	    goto cleanup;
	}
	else if (IS_TGS_PRINC(context, (*out_cred)->server)) {
	    krb5_data *r1, *r2;

	    DPRINTF(("gc_from_kdc: request generated referral tgt\n"));
	    DUMP_PRINC("gc_from_kdc credential received",
		       (*out_cred)->server);

	    if (referral_count == 0)
		r1 = &tgtptr->server->data[1];
	    else
		r1 = &referral_tgts[referral_count-1]->server->data[1];

	    r2 = &(*out_cred)->server->data[1];
	    if (data_eq(*r1, *r2)) {
		DPRINTF(("gc_from_kdc: referred back to "
			 "previous realm; fall back\n"));
		krb5_free_creds(context, *out_cred);
		*out_cred = NULL;
		break;
	    }
	    /* Check for referral routing loop. */
	    for (i=0;i<referral_count;i++) {
#if 0
		DUMP_PRINC("gc_from_kdc: loop compare #1",
			   (*out_cred)->server);
		DUMP_PRINC("gc_from_kdc: loop compare #2",
			   referral_tgts[i]->server);
#endif
		if (krb5_principal_compare(context,
					   (*out_cred)->server,
					   referral_tgts[i]->server)) {
		    DFPRINTF((stderr,
			      "krb5_get_cred_from_kdc_opt: "
			      "referral routing loop - "
			      "got referral back to hop #%d\n", i));
		    retval=KRB5_KDC_UNREACH;
		    goto cleanup;
		}
	    }
	    /* Point current tgt pointer at newly-received TGT. */
	    if (tgtptr == &cc_tgt)
		krb5_free_cred_contents(context, tgtptr);
	    tgtptr=*out_cred;
	    /* Save pointer to tgt in referral_tgts. */
	    referral_tgts[referral_count]=*out_cred;
	    *out_cred = NULL;
	    /* Copy krbtgt realm to server principal. */
	    krb5_free_data_contents(context, &server->realm);
	    retval = krb5int_copy_data_contents(context,
						&tgtptr->server->data[1],
						&server->realm);
	    if (retval)
		return retval;
	    /*
	     * Future work: rewrite server principal per any
	     * supplied padata.
	     */
	} else {
	    /* Not a TGT; punt to fallback. */
	    krb5_free_creds(context, *out_cred);
	    *out_cred = NULL;
	    break;
	}
    }

    DUMP_PRINC("gc_from_kdc client at fallback", client);
    DUMP_PRINC("gc_from_kdc server at fallback", server);

    /*
     * At this point referrals have been tried and have failed.  Go
     * back to the server principal as originally issued and try the
     * conventional path.
     */

    /*
     * Referrals have failed.  Look up fallback realm if not
     * originally provided.
     */
    if (krb5_is_referral_realm(&supplied_server->realm)) {
        if (server->length >= 2) {
	    retval=krb5_get_fallback_host_realm(context, &server->data[1],
						&hrealms);
	    if (retval) goto cleanup;
#if 0
	    DPRINTF(("gc_from_kdc: using fallback realm of %s\n",
		     hrealms[0]));
#endif
	    krb5_free_data_contents(context,&in_cred->server->realm);
	    server->realm.data=hrealms[0];
	    server->realm.length=strlen(hrealms[0]);
	    free(hrealms);
	}
	else {
	    /*
	     * Problem case: Realm tagged for referral but apparently not
	     * in a <type>/<host> format that
	     * krb5_get_fallback_host_realm can deal with.
	     */
	    DPRINTF(("gc_from_kdc: referral specified "
		     "but no fallback realm avaiable!\n"));
	    return KRB5_ERR_HOST_REALM_UNKNOWN;
	}
    }

    DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite",
	       server);

    /*
     * Get a TGT for the target realm.
     */

    krb5_free_cred_contents(context, &tgtq);
    retval = tgt_mcred(context, client, server, client, &tgtq);
    if (retval)
	goto cleanup;

    /* Fast path: Is it in the ccache? */
    /* Free tgtptr data if reused from above. */
    if (tgtptr == &cc_tgt)
	krb5_free_cred_contents(context, tgtptr);
    tgtptr = NULL;
    /* Free saved TGT in OTGTPTR if it was off-path. */
    if (tgtptr_isoffpath)
	krb5_free_creds(context, otgtptr);
    otgtptr = NULL;
    /* Free TGTS if previously filled by do_traversal() */
    if (*tgts != NULL) {
	for (i = 0; (*tgts)[i] != NULL; i++) {
	    krb5_free_creds(context, (*tgts)[i]);
	}
	free(*tgts);
	*tgts = NULL;
    }
    context->use_conf_ktypes = 1;
    retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
				   &tgtq, &cc_tgt);
    if (!retval) {
	tgtptr = &cc_tgt;
    } else if (!HARD_CC_ERR(retval)) {
	tgtptr_isoffpath = 0;
	retval = do_traversal(context, ccache, client, server,
			      &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath);
    }
    if (retval)
	goto cleanup;
    otgtptr = tgtptr;

    /*
     * Finally have TGT for target realm!  Try using it to get creds.
     */

    if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) {
	retval = KRB5_PROG_ETYPE_NOSUPP;
	goto cleanup;
    }

    context->use_conf_ktypes = old_use_conf_ktypes;
    retval = krb5_get_cred_via_tkt(context, tgtptr,
				   FLAGS2OPTS(tgtptr->ticket_flags) |
				   kdcopt |
				   (in_cred->second_ticket.length ?
				    KDC_OPT_ENC_TKT_IN_SKEY : 0),
				   tgtptr->addresses, in_cred, out_cred);

cleanup:
    krb5_free_cred_contents(context, &tgtq);
    if (tgtptr == &cc_tgt)
	krb5_free_cred_contents(context, tgtptr);
    if (tgtptr_isoffpath)
	krb5_free_creds(context, otgtptr);
    context->use_conf_ktypes = old_use_conf_ktypes;
    /* Drop the original principal back into in_cred so that it's cached
       in the expected format. */
    DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup",
	       server);
    krb5_free_principal(context, server);
    in_cred->server = supplied_server;
    if (*out_cred && !retval) {
        /* Success: free server, swap supplied server back in. */
        krb5_free_principal (context, (*out_cred)->server);
	(*out_cred)->server= out_supplied_server;
    }
    else {
        /* 
	 * Failure: free out_supplied_server.  Don't free out_cred here
	 * since it's either null or a referral TGT that we free below,
	 * and we may need it to return.
	 */
        krb5_free_principal (context, out_supplied_server);
    }
    DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server);
    /*
     * Deal with ccache TGT management: If tgts has been set from
     * initial non-referral TGT discovery, leave it alone.  Otherwise, if
     * referral_tgts[0] exists return it as the only entry in tgts.
     * (Further referrals are never cached, only the referral from the
     * local KDC.)  This is part of cleanup because useful received TGTs
     * should be cached even if the main request resulted in failure.
     */

    if (*tgts == NULL) {
        if (referral_tgts[0]) {
#if 0
  	    /*
	     * This should possibly be a check on the candidate return
	     * credential against the cache, in the circumstance where we
	     * don't want to clutter the cache with near-duplicate
	     * credentials on subsequent iterations.  For now, it is
	     * disabled.
	     */
	    subretval=...?;
	    if (subretval) {
#endif
	        /* Allocate returnable TGT list. */
	        if (!(*tgts=calloc(sizeof (krb5_creds *), 2)))
		    return ENOMEM;
		subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0]));
		if(subretval)
		    return subretval;
		(*tgts)[1]=NULL;
		DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache",
			   (*tgts)[0]->server);
#if 0
	    }
#endif
	}
    }

    /* Free referral TGTs list. */
    for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) {
        if(referral_tgts[i]) {
	    krb5_free_creds(context, referral_tgts[i]);
	}
    }
    DPRINTF(("gc_from_kdc finishing with %s\n",
	     retval ? error_message(retval) : "no error"));
    return retval;
}
Exemple #30
0
static krb5_error_code
s4u2proxy_get_attribute(krb5_context kcontext,
                        krb5_authdata_context context,
                        void *plugin_context,
                        void *request_context,
                        const krb5_data *attribute,
                        krb5_boolean *authenticated,
                        krb5_boolean *complete,
                        krb5_data *value,
                        krb5_data *display_value,
                        int *more)
{
    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
    krb5_error_code code;
    krb5_principal principal;
    int i;

    if (display_value != NULL) {
        display_value->data = NULL;
        display_value->length = 0;
    }

    if (!data_eq(*attribute, s4u2proxy_transited_services_attr))
        return ENOENT;

    i = -(*more) - 1;
    if (i < 0)
        return EINVAL;
    else if (i >= s4uctx->count)
        return ENOENT;

    principal = s4uctx->delegated[i];
    assert(principal != NULL);

    code = krb5_unparse_name_flags(kcontext, principal, 0, &value->data);
    if (code != 0)
        return code;

    value->length = strlen(value->data);

    if (display_value != NULL) {
        code = krb5_unparse_name_flags(kcontext, principal,
                                       KRB5_PRINCIPAL_UNPARSE_DISPLAY,
                                       &display_value->data);
        if (code != 0)
            return code;

        display_value->length = strlen(display_value->data);
    }

    i++;

    if (i == s4uctx->count)
        *more = 0;
    else
        *more = -(i + 1);

    *authenticated = s4uctx->authenticated;
    *complete = TRUE;

    return 0;
}