示例#1
0
/*
 * Construct list of realms between client and server.
 */
static krb5_error_code
rtree_hier_realms(krb5_context context,
                  const krb5_data *client,
                  const krb5_data *server,
                  krb5_data **realms,
                  size_t *nrealms,
                  int sep)
{
    krb5_error_code retval;
    struct hstate c, s;
    krb5_data *ctweens = NULL, *stweens = NULL, *twp, *r, *rp;
    size_t nctween, nstween;

    *realms = NULL;
    *nrealms = 0;

    r = rp = NULL;
    c.str = client->data;
    c.len = client->length;
    c.dot = c.tail = NULL;
    s.str = server->data;
    s.len = server->length;
    s.dot = s.tail = NULL;

    comtail(&c, &s, sep);
    adjtail(&c, &s, sep);

    retval = rtree_hier_tweens(context, &c, &ctweens, &nctween, 1, sep);
    if (retval) goto error;
    retval = rtree_hier_tweens(context, &s, &stweens, &nstween, 0, sep);
    if (retval) goto error;

    rp = r = calloc(nctween + nstween, sizeof(krb5_data));
    if (r == NULL) {
        retval = ENOMEM;
        goto error;
    }
    /* Copy client realm "tweens" forward. */
    for (twp = ctweens; twp < &ctweens[nctween]; twp++) {
        retval = krb5int_copy_data_contents(context, twp, rp);
        if (retval) goto error;
        rp++;
    }
    /* Copy server realm "tweens" backward. */
    for (twp = &stweens[nstween]; twp-- > stweens;) {
        retval = krb5int_copy_data_contents(context, twp, rp);
        if (retval) goto error;
        rp++;
    }
error:
    free(ctweens);
    free(stweens);
    if (retval) {
        free_realmlist(context, r, rp - r);
        return retval;
    }
    *realms = r;
    *nrealms = rp - r;
    return 0;
}
示例#2
0
static krb5_error_code
mspac_get_attribute_types(krb5_context kcontext,
                          krb5_authdata_context context,
                          void *plugin_context,
                          void *request_context,
                          krb5_data **out_attrs)
{
    struct mspac_context *pacctx = (struct mspac_context *)request_context;
    unsigned int i, j;
    krb5_data *attrs;
    krb5_error_code code;

    if (pacctx->pac == NULL)
        return ENOENT;

    attrs = calloc(1 + pacctx->pac->pac->cBuffers + 1, sizeof(krb5_data));
    if (attrs == NULL)
        return ENOMEM;

    j = 0;

    /* The entire PAC */
    code = krb5int_copy_data_contents(kcontext,
                                      &mspac_attribute_types[0].attribute,
                                      &attrs[j++]);
    if (code != 0) {
        free(attrs);
        return code;
    }

    /* PAC buffers */
    for (i = 0; i < pacctx->pac->pac->cBuffers; i++) {
        krb5_data attr;

        code = mspac_type2attr(pacctx->pac->pac->Buffers[i].ulType, &attr);
        if (code == 0) {
            code = krb5int_copy_data_contents(kcontext, &attr, &attrs[j++]);
            if (code != 0) {
                krb5int_free_data_list(kcontext, attrs);
                return code;
            }
        } else {
            int length;

            length = asprintf(&attrs[j].data, "urn:mspac:%d",
                              pacctx->pac->pac->Buffers[i].ulType);
            if (length < 0) {
                krb5int_free_data_list(kcontext, attrs);
                return ENOMEM;
            }
            attrs[j++].length = length;
        }
    }
    attrs[j].data = NULL;
    attrs[j].length = 0;

    *out_attrs = attrs;

    return 0;
}
示例#3
0
/*
 * Fill in the caller out, realm, and flags output variables.  out is filled in
 * with ctx->previous_request, which the caller should set, and realm is filled
 * in with the realm of ctx->cur_tgt.
 */
static krb5_error_code
set_caller_request(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    const krb5_data *req = &ctx->previous_request;
    const krb5_data *realm = &ctx->cur_tgt->server->data[1];
    krb5_data out_copy = empty_data(), realm_copy = empty_data();

    code = krb5int_copy_data_contents(context, req, &out_copy);
    if (code != 0)
        goto cleanup;
    code = krb5int_copy_data_contents(context, realm, &realm_copy);
    if (code != 0)
        goto cleanup;

    *ctx->caller_out = out_copy;
    *ctx->caller_realm = realm_copy;
    *ctx->caller_flags = KRB5_TKT_CREDS_STEP_FLAG_CONTINUE;
    return 0;

cleanup:
    krb5_free_data_contents(context, &out_copy);
    krb5_free_data_contents(context, &realm_copy);
    return code;
}
示例#4
0
文件: ccfns.c 项目: Akasurde/krb5
krb5_error_code KRB5_CALLCONV
krb5_cc_get_config(krb5_context context, krb5_ccache id,
                   krb5_const_principal principal,
                   const char *key, krb5_data *data)
{
    krb5_creds mcred, cred;
    krb5_error_code ret;

    memset(&cred, 0, sizeof(cred));
    memset(data, 0, sizeof(*data));

    ret = k5_build_conf_principals(context, id, principal, key, &mcred);
    if (ret)
        goto out;

    ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred);
    if (ret)
        goto out;

    ret = krb5int_copy_data_contents(context, &cred.ticket, data);
    if (ret)
        goto out;

    TRACE_CC_GET_CONFIG(context, id, principal, key, data);

out:
    krb5_free_cred_contents(context, &cred);
    krb5_free_cred_contents(context, &mcred);
    return ret;
}
示例#5
0
文件: ccfns.c 项目: Akasurde/krb5
krb5_error_code KRB5_CALLCONV
krb5_cc_set_config(krb5_context context, krb5_ccache id,
                   krb5_const_principal principal,
                   const char *key, krb5_data *data)
{
    krb5_error_code ret;
    krb5_creds cred;
    memset(&cred, 0, sizeof(cred));

    TRACE_CC_SET_CONFIG(context, id, principal, key, data);

    ret = k5_build_conf_principals(context, id, principal, key, &cred);
    if (ret)
        goto out;

    if (data == NULL) {
        ret = krb5_cc_remove_cred(context, id, 0, &cred);
    } else {
        ret = krb5int_copy_data_contents(context, data, &cred.ticket);
        if (ret)
            goto out;
        ret = krb5_cc_store_cred(context, id, &cred);
    }
out:
    krb5_free_cred_contents(context, &cred);
    return ret;
}
示例#6
0
static krb5_error_code
mspac_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 mspac_context *pacctx = (struct mspac_context *)request_context;
    krb5_error_code code;
    krb5_ui_4 type;

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

    if (*more != -1 || pacctx->pac == NULL)
        return ENOENT;

    /* If it didn't verify, pretend it didn't exist. */
    if (!pacctx->pac->verified) {
        TRACE_MSPAC_DISCARD_UNVERF(kcontext);
        return ENOENT;
    }

    code = mspac_attr2type(attribute, &type);
    if (code != 0)
        return code;

    /* -1 is a magic type that refers to the entire PAC */
    if (type == (krb5_ui_4)-1) {
        if (value != NULL)
            code = krb5int_copy_data_contents(kcontext,
                                              &pacctx->pac->data,
                                              value);
        else
            code = 0;
    } else {
        if (value != NULL)
            code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value);
        else
            code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL);
    }
    if (code == 0) {
        *authenticated = pacctx->pac->verified;
        *complete = TRUE;
    }

    *more = 0;

    return code;
}
示例#7
0
krb5_error_code
k5_client_realm_path(krb5_context context, const krb5_data *client,
                     const krb5_data *server, krb5_data **rpath_out)
{
    krb5_error_code retval;
    char **capvals;
    size_t i;
    krb5_data *rpath = NULL, d;

    retval = rtree_capath_vals(context, client, server, &capvals);
    if (retval)
        return retval;

    /* Count capaths (if any) and allocate space.  Leave room for the client
     * realm, server realm, and terminator. */
    for (i = 0; capvals != NULL && capvals[i] != NULL; i++);
    rpath = calloc(i + 3, sizeof(*rpath));
    if (rpath == NULL)
        return ENOMEM;

    /* Populate rpath with the client realm, capaths, and server realm. */
    retval = krb5int_copy_data_contents(context, client, &rpath[0]);
    if (retval)
        goto cleanup;
    for (i = 0; capvals != NULL && capvals[i] != NULL; i++) {
        d = make_data(capvals[i], strcspn(capvals[i], "\t "));
        retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]);
        if (retval)
            goto cleanup;
    }
    retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]);
    if (retval)
        goto cleanup;

    /* Terminate rpath and return it. */
    rpath[i + 2] = empty_data();
    *rpath_out = rpath;
    rpath = NULL;

cleanup:
    krb5int_free_data_list(context, rpath);
    return retval;
}
示例#8
0
krb5_error_code krb5_kt_find_realm(krb5_context context, krb5_keytab keytab,
    krb5_principal princ, krb5_data *realm) {

	krb5_kt_cursor cur;
	krb5_keytab_entry ent;
	krb5_boolean match;
	krb5_data tmp_realm;
	krb5_error_code ret, ret2;

	ret = krb5_kt_start_seq_get(context, keytab, &cur);
	if (ret != 0) {
		return (ret);
	}

	while ((ret = krb5_kt_next_entry(context, keytab, &ent, &cur)) == 0) {
		/* For the comparison the realms should be the same. */
		memcpy(&tmp_realm, &ent.principal->realm, sizeof (krb5_data));
		memcpy(&ent.principal->realm, &princ->realm,
		    sizeof (krb5_data));

		match = krb5_principal_compare(context, ent.principal, princ);

		/* Copy the realm back */
		memcpy(&ent.principal->realm, &tmp_realm, sizeof (krb5_data));

		if (match) {
			/*
			 * A suitable entry was found in the keytab.
			 * Copy its realm
			 */
			ret = krb5int_copy_data_contents(context,
			    &ent.principal->realm, realm);
			if (ret) {
				krb5_kt_free_entry(context, &ent);
				krb5_kt_end_seq_get(context, keytab, &cur);
				return (ret);
			}

			krb5_kt_free_entry(context, &ent);
			break;
		}

		krb5_kt_free_entry(context, &ent);
	}

	ret2 = krb5_kt_end_seq_get(context, keytab, &cur);

	if (ret == KRB5_KT_END) {
		return (KRB5_KT_NOTFOUND);
	}

	return (ret ? ret : ret2);
}
示例#9
0
文件: chpw.c 项目: jmoldow/krb5
krb5_error_code
krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context,
                    krb5_data *packet, int *result_code_out,
                    krb5_data *result_data_out)
{
    krb5_error_code ret;
    krb5_data result_data, *clear = NULL;
    krb5_boolean is_error;
    char *ptr;
    int result_code;

    *result_code_out = 0;
    *result_data_out = empty_data();

    ret = get_clear_result(context, auth_context, packet, &clear, &is_error);
    if (ret)
        return ret;

    if (clear->length < 2) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        goto cleanup;
    }

    /* Decode and check the result code. */
    ptr = clear->data;
    result_code = (*ptr++ & 0xff);
    result_code = (result_code << 8) | (*ptr++ & 0xff);
    if (result_code < KRB5_KPASSWD_SUCCESS ||
        result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        goto cleanup;
    }

    /* Successful replies must not come from errors. */
    if (is_error && result_code == KRB5_KPASSWD_SUCCESS) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        goto cleanup;
    }

    result_data = make_data(ptr, clear->data + clear->length - ptr);
    ret = krb5int_copy_data_contents(context, &result_data, result_data_out);
    if (ret)
        goto cleanup;
    *result_code_out = result_code;

cleanup:
    krb5_free_data(context, clear);
    return ret;
}
示例#10
0
/*
 * Copy a principal structure, with fresh allocation.
 */
krb5_error_code KRB5_CALLCONV
krb5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
{
    register krb5_principal tempprinc;
    register int i, nelems;

    tempprinc = (krb5_principal)malloc(sizeof(krb5_principal_data));

    if (tempprinc == 0)
	return ENOMEM;

    *tempprinc = *inprinc;

    nelems = (int) krb5_princ_size(context, inprinc);
    tempprinc->data = malloc(nelems * sizeof(krb5_data));

    if (tempprinc->data == 0) {
	free((char *)tempprinc);
	return ENOMEM;
    }

    for (i = 0; i < nelems; i++) {
	if (krb5int_copy_data_contents(context,
				       krb5_princ_component(context, inprinc, i),
				       krb5_princ_component(context, tempprinc, i)) != 0) {
	    while (--i >= 0)
		free(krb5_princ_component(context, tempprinc, i)->data);
	    free (tempprinc->data);
	    free (tempprinc);
	    return ENOMEM;
        }
    }

    if (krb5int_copy_data_contents_add0(context, &inprinc->realm,
					&tempprinc->realm) != 0) {
        for (i = 0; i < nelems; i++)
	    free(krb5_princ_component(context, tempprinc, i)->data);
	free(tempprinc->data);
	free(tempprinc);
	return ENOMEM;
    }

    *outprinc = tempprinc;
    return 0;
}
示例#11
0
/* Set salt in rock based on pw-salt or afs3-salt elements in padata. */
static krb5_error_code
get_salt(krb5_context context, krb5_pa_data **padata,
         krb5_kdc_req *request, krb5_clpreauth_rock rock)
{
    krb5_error_code ret;
    krb5_pa_data *pa;
    krb5_data d;
    const char *p;

    /* Look for a pw-salt or afs3-salt element. */
    pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_PW_SALT);
    if (pa == NULL)
        pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_AFS3_SALT);
    if (pa == NULL)
        return 0;

    /* Set rock->salt based on the element we found. */
    krb5_free_data_contents(context, rock->salt);
    d = padata2data(*pa);
    ret = krb5int_copy_data_contents(context, &d, rock->salt);
    if (ret)
        return ret;

    /* Adjust the salt if we got it from an afs3-salt element. */
    if (pa->pa_type == KRB5_PADATA_AFS3_SALT) {
        /* Work around a (possible) old Heimdal KDC foible. */
        p = memchr(rock->salt->data, '@', rock->salt->length);
        if (p != NULL)
            rock->salt->length = p - rock->salt->data;
        /* Tolerate extra null in MIT KDC afs3-salt value. */
        if (rock->salt->length > 0 &&
            rock->salt->data[rock->salt->length - 1] == '\0')
            rock->salt->length--;
        /* Set an s2kparams value to indicate AFS string-to-key. */
        krb5_free_data_contents(context, rock->s2kparams);
        ret = alloc_data(rock->s2kparams, 1);
        if (ret)
            return ret;
        rock->s2kparams->data[0] = '\1';
    }

    *rock->default_salt = FALSE;
    TRACE_PREAUTH_SALT(context, rock->salt, pa->pa_type);
    return 0;
}
示例#12
0
/* Add realm to ctx->realms_seen so that we can avoid revisiting it later. */
static krb5_error_code
remember_realm(krb5_context context, krb5_tkt_creds_context ctx,
               const krb5_data *realm)
{
    size_t len = 0;
    krb5_data *new_list;

    if (ctx->realms_seen != NULL) {
        for (len = 0; ctx->realms_seen[len].data != NULL; len++);
    }
    new_list = realloc(ctx->realms_seen, (len + 2) * sizeof(krb5_data));
    if (new_list == NULL)
        return ENOMEM;
    ctx->realms_seen = new_list;
    new_list[len] = empty_data();
    new_list[len + 1] = empty_data();
    return krb5int_copy_data_contents(context, realm, &new_list[len]);
}
示例#13
0
static krb5_error_code
mspac_export_authdata(krb5_context kcontext,
                      krb5_authdata_context context,
                      void *plugin_context,
                      void *request_context,
                      krb5_flags usage,
                      krb5_authdata ***out_authdata)
{
    struct mspac_context *pacctx = (struct mspac_context *)request_context;
    krb5_error_code code;
    krb5_authdata **authdata;
    krb5_data data;

    if (pacctx->pac == NULL)
        return 0;

    authdata = calloc(2, sizeof(krb5_authdata *));
    if (authdata == NULL)
        return ENOMEM;

    authdata[0] = calloc(1, sizeof(krb5_authdata));
    if (authdata[0] == NULL) {
        free(authdata);
        return ENOMEM;
    }
    authdata[1] = NULL;

    code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, &data);
    if (code != 0) {
        krb5_free_authdata(kcontext, authdata);
        return code;
    }

    authdata[0]->magic = KV5M_AUTHDATA;
    authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
    authdata[0]->length = data.length;
    authdata[0]->contents = (krb5_octet *)data.data;

    authdata[1] = NULL;

    *out_authdata = authdata;

    return 0;
}
示例#14
0
文件: copy_princ.c 项目: PADL/krb5
/*
 * Copy a principal structure, with fresh allocation.
 */
krb5_error_code KRB5_CALLCONV
krb5_copy_principal(krb5_context context, krb5_const_principal inprinc, krb5_principal *outprinc)
{
    krb5_principal tempprinc;
    krb5_int32 i;

    tempprinc = (krb5_principal)malloc(sizeof(krb5_principal_data));

    if (tempprinc == 0)
        return ENOMEM;

    *tempprinc = *inprinc;

    tempprinc->data = malloc(inprinc->length * sizeof(krb5_data));

    if (tempprinc->data == 0) {
        free(tempprinc);
        return ENOMEM;
    }

    for (i = 0; i < inprinc->length; i++) {
        if (krb5int_copy_data_contents(context, &inprinc->data[i],
                                       &tempprinc->data[i]) != 0) {
            while (--i >= 0)
                free(tempprinc->data[i].data);
            free (tempprinc->data);
            free (tempprinc);
            return ENOMEM;
        }
    }

    if (krb5int_copy_data_contents_add0(context, &inprinc->realm,
                                        &tempprinc->realm) != 0) {
        for (i = 0; i < inprinc->length; i++)
            free(tempprinc->data[i].data);
        free(tempprinc->data);
        free(tempprinc);
        return ENOMEM;
    }

    *outprinc = tempprinc;
    return 0;
}
示例#15
0
static krb5_error_code
s4u2proxy_get_attribute_types(krb5_context kcontext,
                              krb5_authdata_context context,
                              void *plugin_context,
                              void *request_context,
                              krb5_data **out_attrs)
{
    struct s4u2proxy_context *s4uctx = (struct s4u2proxy_context *)request_context;
    krb5_error_code code;
    krb5_data *attrs;
    int i = 0;

    if (s4uctx->count == 0)
        return ENOENT;

    attrs = k5calloc(2, sizeof(krb5_data), &code);
    if (attrs == NULL)
        goto cleanup;

    code = krb5int_copy_data_contents(kcontext,
                                      &s4u2proxy_transited_services_attr,
                                      &attrs[i++]);
    if (code != 0)
        goto cleanup;

    attrs[i].data = NULL;
    attrs[i].length = 0;

    *out_attrs = attrs;
    attrs = NULL;

cleanup:
    if (attrs != NULL) {
        for (i = 0; attrs[i].data; i++)
            krb5_free_data_contents(kcontext, &attrs[i]);
        free(attrs);
    }

    return 0;
}
示例#16
0
static krb5_error_code
k5_pac_copy(krb5_context context,
            krb5_pac src,
            krb5_pac *dst)
{
    size_t header_len;
    krb5_ui_4 cbuffers;
    krb5_error_code code;
    krb5_pac pac;

    cbuffers = src->pac->cBuffers;
    if (cbuffers != 0)
        cbuffers--;

    header_len = sizeof(PACTYPE) + cbuffers * sizeof(PAC_INFO_BUFFER);

    pac = (krb5_pac)malloc(sizeof(*pac));
    if (pac == NULL)
        return ENOMEM;

    pac->pac = (PACTYPE *)malloc(header_len);
    if (pac->pac == NULL) {
        free(pac);
        return ENOMEM;
    }

    memcpy(pac->pac, src->pac, header_len);

    code = krb5int_copy_data_contents(context, &src->data, &pac->data);
    if (code != 0) {
        free(pac->pac);
        free(pac);
        return ENOMEM;
    }

    pac->verified = src->verified;
    *dst = pac;

    return 0;
}
示例#17
0
/* Decide where to begin the acquisition process. */
static krb5_error_code
begin(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;

    code = check_cache(context, ctx);
    if (code != 0 || ctx->state == STATE_COMPLETE)
        return code;

    /* If the server realm is unspecified, start with the client realm. */
    if (krb5_is_referral_realm(&ctx->server->realm)) {
        krb5_free_data_contents(context, &ctx->server->realm);
        code = krb5int_copy_data_contents(context, &ctx->client->realm,
                                          &ctx->server->realm);
        TRACE_TKT_CREDS_REFERRAL_REALM(context, ctx->server);
        if (code != 0)
            return code;
    }

    /* Obtain a TGT for the service realm. */
    ctx->getting_tgt_for = STATE_REFERRALS;
    return begin_get_tgt(context, ctx);
}
示例#18
0
/*ARGSUSED*/
static
krb5_error_code pa_salt(krb5_context context,
			krb5_kdc_req *request,
			krb5_pa_data *in_padata,
			krb5_pa_data **out_padata,
			krb5_data *salt,
			krb5_data *s2kparams,
			krb5_enctype *etype,
			krb5_keyblock *as_key,
			krb5_prompter_fct prompter, void *prompter_data,
			krb5_gic_get_as_key_fct gak_fct, void *gak_data)
{
    krb5_data tmp;

    tmp.data = (char *)in_padata->contents;
    tmp.length = in_padata->length;
    krb5_free_data_contents(context, salt);
    krb5int_copy_data_contents(context, &tmp, salt);

    if (in_padata->pa_type == KRB5_PADATA_AFS3_SALT)
	salt->length = -1;

    return(0);
}
示例#19
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;
}
示例#20
0
krb5_error_code
krb5_do_preauth(krb5_context context,
		krb5_kdc_req *request,
		krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
		krb5_data *salt, krb5_data *s2kparams,
		krb5_enctype *etype,
		krb5_keyblock *as_key,
		krb5_prompter_fct prompter, void *prompter_data,
		krb5_gic_get_as_key_fct gak_fct, void *gak_data)
{
    int h, i, j, out_pa_list_size;
    int seen_etype_info2 = 0;
    krb5_pa_data *out_pa = NULL, **out_pa_list = NULL;
    krb5_data scratch;
    krb5_etype_info etype_info = NULL;
    krb5_error_code ret;
    static const int paorder[] = { PA_INFO, PA_REAL };
    int realdone;

    KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() start");

    if (in_padata == NULL) {
	*out_padata = NULL;
	return(0);
    }

#ifdef DEBUG
    if (salt && salt->data && salt->length > 0) {
    	fprintf (stderr, "salt len=%d", salt->length);
	    if (salt->length > 0)
		fprintf (stderr, " '%*s'", salt->length, salt->data);
	    fprintf (stderr, "; preauth data types:");
	    for (i = 0; in_padata[i]; i++) {
		fprintf (stderr, " %d", in_padata[i]->pa_type);
    	}
    	fprintf (stderr, "\n");
    }
#endif

    out_pa_list = NULL;
    out_pa_list_size = 0;

    /* first do all the informational preauths, then the first real one */

    for (h=0; h<(sizeof(paorder)/sizeof(paorder[0])); h++) {
	realdone = 0;
	for (i=0; in_padata[i] && !realdone; i++) {
	    int k, l, etype_found, valid_etype_found;
	    /*
	     * This is really gross, but is necessary to prevent
	     * lossge when talking to a 1.0.x KDC, which returns an
	     * erroneous PA-PW-SALT when it returns a KRB-ERROR
	     * requiring additional preauth.
	     */
	    switch (in_padata[i]->pa_type) {
	    case KRB5_PADATA_ETYPE_INFO:
	    case KRB5_PADATA_ETYPE_INFO2:
	    {
		krb5_preauthtype pa_type = in_padata[i]->pa_type;
		if (etype_info) {
		    if (seen_etype_info2 || pa_type != KRB5_PADATA_ETYPE_INFO2)
			continue;
		    if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
                        krb5_free_etype_info( context, etype_info);
			etype_info = NULL;
                    }
		}

		scratch.length = in_padata[i]->length;
		scratch.data = (char *) in_padata[i]->contents;
		if (pa_type == KRB5_PADATA_ETYPE_INFO2) {
                    seen_etype_info2++;
                    ret = decode_krb5_etype_info2(&scratch, &etype_info);
		}
		else ret = decode_krb5_etype_info(&scratch, &etype_info);
		if (ret) {
                    ret = 0; /*Ignore error and etype_info element*/
                    krb5_free_etype_info( context, etype_info);
                    etype_info = NULL;
                    continue;
		}
		if (etype_info[0] == NULL) {
		    krb5_free_etype_info(context, etype_info);
		    etype_info = NULL;
		    break;
		}
		/*
		 * Select first etype in our request which is also in
		 * etype-info (preferring client request ktype order).
		 */
		for (etype_found = 0, valid_etype_found = 0, k = 0;
		       	!etype_found && k < request->nktypes; k++) {
		    for (l = 0; etype_info[l]; l++) {
			if (etype_info[l]->etype == request->ktype[k]) {
			    etype_found++;
			    break;
			}
			/* check if program has support for this etype for more
			 * precise error reporting.
			 */
			if (valid_enctype(etype_info[l]->etype))
			    valid_etype_found++;
		    }
		}
		if (!etype_found) {
		    KRB5_LOG(KRB5_ERR, "error !etype_found, "
				"valid_etype_found = %d",
				valid_etype_found); 
		    if (valid_etype_found) {
			/* supported enctype but not requested */
			ret = KRB5_CONFIG_ETYPE_NOSUPP;
			goto cleanup;
		    }
		    else {
			/* unsupported enctype */
			ret = KRB5_PROG_ETYPE_NOSUPP;
			goto cleanup;
		    }

		}
		scratch.data = (char *) etype_info[l]->salt;
		scratch.length = etype_info[l]->length;
		krb5_free_data_contents(context, salt);
		if (scratch.length == KRB5_ETYPE_NO_SALT)
		  salt->data = NULL;
		else
                    if ((ret = krb5int_copy_data_contents( context,
				&scratch, salt)) != 0)
			goto cleanup;
		*etype = etype_info[l]->etype;
		krb5_free_data_contents(context, s2kparams);
		if ((ret = krb5int_copy_data_contents(context,
				&etype_info[l]->s2kparams,
				s2kparams)) != 0)
		  goto cleanup;
		break;
	    }
	    case KRB5_PADATA_PW_SALT:
	    case KRB5_PADATA_AFS3_SALT:
		if (etype_info)
		    continue;
		break;
	    default:
		;
	    }
	    for (j=0; pa_types[j].type >= 0; j++) {
		if ((in_padata[i]->pa_type == pa_types[j].type) &&
		    (pa_types[j].flags & paorder[h])) {
		    out_pa = NULL;

		    if ((ret = ((*pa_types[j].fct)(context, request,
					in_padata[i], &out_pa,
					salt, s2kparams, etype, as_key,
					prompter, prompter_data,
					gak_fct, gak_data)))) {
			goto cleanup;
		    }

		    if (out_pa) {
			if (out_pa_list == NULL) {
			    if ((out_pa_list =
				 (krb5_pa_data **)
				 malloc(2*sizeof(krb5_pa_data *)))
				== NULL) {
				ret = ENOMEM;
				goto cleanup;
			     }
			} else {
			    if ((out_pa_list =
				 (krb5_pa_data **)
				 realloc(out_pa_list,
					 (out_pa_list_size+2)*
					 sizeof(krb5_pa_data *)))
				== NULL) {
				/* XXX this will leak the pointers which
				   have already been allocated.  oh well. */
				ret = ENOMEM;
				goto cleanup;
			    }
			}
			
			out_pa_list[out_pa_list_size++] = out_pa;
		    }
		    if (paorder[h] == PA_REAL)
			realdone = 1;
		}
	    }
	}
    }

    if (out_pa_list)
	out_pa_list[out_pa_list_size++] = NULL;

    *out_padata = out_pa_list;
    if (etype_info)
	krb5_free_etype_info(context, etype_info);
   
    KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end");
    return(0);
cleanup:
    if (out_pa_list) {
	out_pa_list[out_pa_list_size++] = NULL;
	krb5_free_pa_data(context, out_pa_list);
    }
    if (etype_info)
	krb5_free_etype_info(context, etype_info);

    KRB5_LOG0(KRB5_INFO, "krb5_do_preauth() end");
    return (ret);

}
示例#21
0
/* Builds a request using the specified tokeninfo, value and pin. */
static krb5_error_code
make_request(krb5_context ctx, krb5_otp_tokeninfo *ti, const krb5_data *value,
             const krb5_data *pin, krb5_pa_otp_req **out_req)
{
    krb5_pa_otp_req *req = NULL;
    krb5_error_code retval = 0;

    if (ti == NULL)
        return 0;

    if (ti->format == KRB5_OTP_FORMAT_BASE64)
        return ENOTSUP;

    req = calloc(1, sizeof(krb5_pa_otp_req));
    if (req == NULL)
        return ENOMEM;

    req->flags = ti->flags & KRB5_OTP_FLAG_NEXTOTP;

    retval = krb5int_copy_data_contents(ctx, &ti->vendor, &req->vendor);
    if (retval != 0)
        goto error;

    req->format = ti->format;

    retval = krb5int_copy_data_contents(ctx, &ti->token_id, &req->token_id);
    if (retval != 0)
        goto error;

    retval = krb5int_copy_data_contents(ctx, &ti->alg_id, &req->alg_id);
    if (retval != 0)
        goto error;

    retval = krb5int_copy_data_contents(ctx, value, &req->otp_value);
    if (retval != 0)
        goto error;

    if (ti->flags & KRB5_OTP_FLAG_COLLECT_PIN) {
        if (ti->flags & KRB5_OTP_FLAG_SEPARATE_PIN) {
            if (pin == NULL || pin->data == NULL) {
                retval = EINVAL; /* No pin found! */
                goto error;
            }

            retval = krb5int_copy_data_contents(ctx, pin, &req->pin);
            if (retval != 0)
                goto error;
        } else if (pin != NULL && pin->data != NULL) {
            krb5_free_data_contents(ctx, &req->otp_value);
            retval = asprintf(&req->otp_value.data, "%.*s%.*s",
                              pin->length, pin->data,
                              value->length, value->data);
            if (retval < 0) {
                retval = ENOMEM;
                req->otp_value = empty_data();
                goto error;
            }
            req->otp_value.length = req->pin.length + req->otp_value.length;
        } /* Otherwise, the responder has already combined them. */
    }

    *out_req = req;
    return 0;

error:
    k5_free_pa_otp_req(ctx, req);
    return retval;
}
示例#22
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);
}