/* Make a non-referrals request for the desired service ticket. */
static krb5_error_code
begin_non_referral(krb5_context context, krb5_tkt_creds_context ctx)
{
    ctx->state = STATE_NON_REFERRAL;
    return make_request_for_service(context, ctx, FALSE);
}
Exemple #2
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 try a non-referral fallback request on error. */
    if (ctx->reply_code != 0)
        return try_fallback(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);
    }

    /* Active Directory may return a TGT to the local realm.  Try a
     * non-referral query if we see this. */
    referral_realm = &ctx->reply_creds->server->data[1];
    if (data_eq(*referral_realm, ctx->cur_tgt->server->data[1])) {
        TRACE_TKT_CREDS_SAME_REALM_TGT(context, referral_realm);
        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. */
    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);
}