Ejemplo n.º 1
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. */
#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;
}
Ejemplo n.º 2
0
/* 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);
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
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);
}
Ejemplo n.º 5
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;
}
Ejemplo n.º 6
0
/*
 * chase_offpath()
 *
 * Chase off-path TGT referrals.
 *
 * If we are traversing a trusted path (either hierarchically derived
 * or explicit capath) and get a TGT pointing to a realm off this
 * path, query the realm referenced by that off-path TGT.  Repeat
 * until we get to the destination realm or encounter an error.
 *
 * CUR_TGT is always either pointing into REFTGTS or is an alias for
 * TS->OFFPATH_TGT.
 */
static krb5_error_code
chase_offpath(struct tr_state *ts,
	      krb5_principal client, krb5_principal server)
{
    krb5_error_code retval;
    krb5_creds mcred;
    krb5_creds *cur_tgt, *nxt_tgt, *reftgts[KRB5_REFERRAL_MAXHOPS];
    krb5_data *rsrc, *rdst, *r1;
    int rcount, i;

    rdst = krb5_princ_realm(ts->ctx, server);
    cur_tgt = ts->offpath_tgt;

    for (rcount = 0; rcount < KRB5_REFERRAL_MAXHOPS; rcount++) {
	nxt_tgt = NULL;
	memset(&mcred, 0, sizeof(mcred));
	rsrc = krb5_princ_component(ts->ctx, cur_tgt->server, 1);
	retval = krb5_tgtname(ts->ctx, rdst, rsrc, &mcred.server);
	if (retval)
	    goto cleanup;
	mcred.client = client;
        retval = krb5_get_cred_via_tkt(ts->ctx, cur_tgt,
				       FLAGS2OPTS(cur_tgt->ticket_flags),
				       cur_tgt->addresses, &mcred, &nxt_tgt);
	mcred.client = NULL;
	krb5_free_principal(ts->ctx, mcred.server);
	mcred.server = NULL;
	if (retval)
	    goto cleanup;
	if (!IS_TGS_PRINC(ts->ctx, nxt_tgt->server)) {
	    retval = KRB5_KDCREP_MODIFIED;
	    goto cleanup;
	}
	r1 = krb5_princ_component(ts->ctx, nxt_tgt->server, 1);
	if (rdst->length == r1->length &&
	    !memcmp(rdst->data, r1->data, rdst->length)) {
	    retval = 0;
	    goto cleanup;
	}
	retval = offpath_loopchk(ts, nxt_tgt, reftgts, rcount);
	if (retval)
	    goto cleanup;
	reftgts[rcount] = nxt_tgt;
	cur_tgt = nxt_tgt;
	nxt_tgt = NULL;
    }
    /* Max hop count exceeded. */
    retval = KRB5_KDCREP_MODIFIED;

cleanup:
    if (mcred.server != NULL) {
	krb5_free_principal(ts->ctx, mcred.server);
    }
    /*
     * Don't free TS->OFFPATH_TGT if it's in the list of cacheable
     * TGTs to be returned by do_traversal().
     */
    if (ts->offpath_tgt != ts->nxt_tgt) {
	krb5_free_creds(ts->ctx, ts->offpath_tgt);
    }
    ts->offpath_tgt = NULL;
    if (nxt_tgt != NULL) {
	if (retval)
	    krb5_free_creds(ts->ctx, nxt_tgt);
	else
	    ts->offpath_tgt = nxt_tgt;
    }
    for (i = 0; i < rcount; i++) {
	krb5_free_creds(ts->ctx, reftgts[i]);
    }
    return retval;
}
Ejemplo n.º 7
0
/* Advance the referral request loop. */
static krb5_error_code
step_referrals(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    const krb5_data *referral_realm;

    /* Possibly retry with the fallback realm on error. */
    if (ctx->reply_code != 0)
        return try_fallback_realm(context, ctx);

    if (krb5_principal_compare(context, ctx->reply_creds->server,
                               ctx->server)) {
        /* We got the ticket we asked for... but we didn't necessarily ask for
         * it with the right enctypes.  Try a non-referral request if so. */
        if (wrong_enctype(context, ctx->reply_creds->keyblock.enctype)) {
            TRACE_TKT_CREDS_WRONG_ENCTYPE(context);
            return begin_non_referral(context, ctx);
        }

        return complete(context, ctx);
    }

    /* Old versions of Active Directory can rewrite the server name instead of
     * returning a referral.  Try a non-referral query if we see this. */
    if (!IS_TGS_PRINC(context, ctx->reply_creds->server)) {
        TRACE_TKT_CREDS_NON_TGT(context, ctx->reply_creds->server);
        return begin_non_referral(context, ctx);
    }

    if (ctx->referral_count == 1) {
        /* Cache the referral TGT only if it's from the local realm.
         * Make sure to note the associated authdata, if any. */
        code = krb5_copy_authdata(context, ctx->authdata,
                                  &ctx->reply_creds->authdata);
        if (code != 0)
            return code;
        (void) krb5_cc_store_cred(context, ctx->ccache, ctx->reply_creds);

        /* The authdata in this TGT will be copied into subsequent TGTs or the
         * final credentials, so we don't need to request it again. */
        krb5_free_authdata(context, ctx->in_creds->authdata);
        ctx->in_creds->authdata = NULL;
    }

    /* Give up if we've gotten too many referral TGTs. */
    if (ctx->referral_count++ >= KRB5_REFERRAL_MAXHOPS)
        return KRB5_KDC_UNREACH;

    /* Check for referral loops. */
    referral_realm = &ctx->reply_creds->server->data[1];
    if (seen_realm_before(context, ctx, referral_realm))
        return KRB5_KDC_UNREACH;
    code = remember_realm(context, ctx, referral_realm);
    if (code != 0)
        return code;

    /* Use the referral TGT for the next request. */
    krb5_free_creds(context, ctx->cur_tgt);
    ctx->cur_tgt = ctx->reply_creds;
    ctx->reply_creds = NULL;
    TRACE_TKT_CREDS_REFERRAL(context, ctx->cur_tgt->server);

    /* Rewrite the server realm to be the referral realm. */
    krb5_free_data_contents(context, &ctx->server->realm);
    code = krb5int_copy_data_contents(context, referral_realm,
                                      &ctx->server->realm);
    if (code != 0)
        return code;

    /* Generate the next referral request. */
    return make_request_for_service(context, ctx, TRUE);
}
Ejemplo n.º 8
0
krb5_error_code
krb5int_process_tgs_reply(krb5_context context,
                          struct krb5int_fast_request_state *fast_state,
                          krb5_data *response_data,
                          krb5_creds *tkt,
                          krb5_flags kdcoptions,
                          krb5_address *const *address,
                          krb5_pa_data **in_padata,
                          krb5_creds *in_cred,
                          krb5_timestamp timestamp,
                          krb5_int32 nonce,
                          krb5_keyblock *subkey,
                          krb5_pa_data ***out_padata,
                          krb5_pa_data ***out_enc_padata,
                          krb5_creds **out_cred)
{
    krb5_error_code retval;
    krb5_kdc_rep *dec_rep = NULL;
    krb5_error *err_reply = NULL;
    krb5_boolean s4u2self;

    s4u2self = krb5int_find_pa_data(context, in_padata,
                                    KRB5_PADATA_S4U_X509_USER) ||
        krb5int_find_pa_data(context, in_padata,
                             KRB5_PADATA_FOR_USER);

    if (krb5_is_krb_error(response_data)) {
        retval = decode_krb5_error(response_data, &err_reply);
        if (retval != 0)
            goto cleanup;
        retval = krb5int_fast_process_error(context, fast_state,
                                            &err_reply, NULL, NULL);
        if (retval)
            goto cleanup;
        retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5;
        if (err_reply->text.length > 0) {
            switch (err_reply->error) {
            case KRB_ERR_GENERIC:
                k5_setmsg(context, retval,
                          _("KDC returned error string: %.*s"),
                          err_reply->text.length, err_reply->text.data);
                break;
            case KDC_ERR_S_PRINCIPAL_UNKNOWN:
            {
                char *s_name;
                if (err_reply->server &&
                    krb5_unparse_name(context, err_reply->server, &s_name) == 0) {
                    k5_setmsg(context, retval,
                              _("Server %s not found in Kerberos database"),
                              s_name);
                    krb5_free_unparsed_name(context, s_name);
                } else
                    /* In case there's a stale S_PRINCIPAL_UNKNOWN
                       report already noted.  */
                    krb5_clear_error_message(context);
            }
            break;
            }
        }
        krb5_free_error(context, err_reply);
        goto cleanup;
    } else if (!krb5_is_tgs_rep(response_data)) {
        retval = KRB5KRB_AP_ERR_MSG_TYPE;
        goto cleanup;
    }

    /* Unfortunately, Heimdal at least up through 1.2  encrypts using
       the session key not the subsession key.  So we try both. */
    retval = krb5int_decode_tgs_rep(context, fast_state, response_data, subkey,
                                    KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY,
                                    &dec_rep);
    if (retval) {
        TRACE_TGS_REPLY_DECODE_SESSION(context, &tkt->keyblock);
        if ((krb5int_decode_tgs_rep(context, fast_state, response_data,
                                    &tkt->keyblock,
                                    KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0)
            retval = 0;
        else
            goto cleanup;
    }

    if (dec_rep->msg_type != KRB5_TGS_REP) {
        retval = KRB5KRB_AP_ERR_MSG_TYPE;
        goto cleanup;
    }

    /*
     * Don't trust the ok-as-delegate flag from foreign KDCs unless the
     * cross-realm TGT also had the ok-as-delegate flag set.
     */
    if (!tgt_is_local_realm(tkt)
        && !(tkt->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
        dec_rep->enc_part2->flags &= ~TKT_FLG_OK_AS_DELEGATE;

    /* make sure the response hasn't been tampered with..... */
    retval = 0;

    if (s4u2self && !IS_TGS_PRINC(dec_rep->ticket->server)) {
        /* Final hop, check whether KDC supports S4U2Self */
        if (krb5_principal_compare(context, dec_rep->client, in_cred->server))
            retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
    } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) {
        /* XXX for constrained delegation this check must be performed by caller
         * as we don't have access to the key to decrypt the evidence ticket.
         */
        if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
            retval = KRB5_KDCREP_MODIFIED;
    }

    if (retval == 0)
        retval = check_reply_server(context, kdcoptions, in_cred, dec_rep);

    if (dec_rep->enc_part2->nonce != nonce)
        retval = KRB5_KDCREP_MODIFIED;

    if ((kdcoptions & KDC_OPT_POSTDATED) &&
        (in_cred->times.starttime != 0) &&
        (in_cred->times.starttime != dec_rep->enc_part2->times.starttime))
        retval = KRB5_KDCREP_MODIFIED;

    if ((in_cred->times.endtime != 0) &&
        (dec_rep->enc_part2->times.endtime > in_cred->times.endtime))
        retval = KRB5_KDCREP_MODIFIED;

    if ((kdcoptions & KDC_OPT_RENEWABLE) &&
        (in_cred->times.renew_till != 0) &&
        (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till))
        retval = KRB5_KDCREP_MODIFIED;

    if ((kdcoptions & KDC_OPT_RENEWABLE_OK) &&
        (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) &&
        (in_cred->times.endtime != 0) &&
        (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime))
        retval = KRB5_KDCREP_MODIFIED;

    if (retval != 0)
        goto cleanup;

    if (!in_cred->times.starttime &&
        !in_clock_skew(dec_rep->enc_part2->times.starttime,
                       timestamp)) {
        retval = KRB5_KDCREP_SKEW;
        goto cleanup;
    }

    if (out_padata != NULL) {
        *out_padata = dec_rep->padata;
        dec_rep->padata = NULL;
    }
    if (out_enc_padata != NULL) {
        *out_enc_padata = dec_rep->enc_part2->enc_padata;
        dec_rep->enc_part2->enc_padata = NULL;
    }

    retval = kdcrep2creds(context, dec_rep, address,
                          &in_cred->second_ticket, out_cred);
    if (retval != 0)
        goto cleanup;

cleanup:
    if (dec_rep != NULL) {
        memset(dec_rep->enc_part2->session->contents, 0,
               dec_rep->enc_part2->session->length);
        krb5_free_kdc_rep(context, dec_rep);
    }

    return retval;
}