get_cred.c revision 55682
155682Smarkm/*
255682Smarkm * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include <krb5_locl.h>
3555682Smarkm
3655682SmarkmRCSID("$Id: get_cred.c,v 1.75 1999/12/02 17:05:09 joda Exp $");
3755682Smarkm
3855682Smarkm/*
3955682Smarkm * Take the `body' and encode it into `padata' using the credentials
4055682Smarkm * in `creds'.
4155682Smarkm */
4255682Smarkm
4355682Smarkmstatic krb5_error_code
4455682Smarkmmake_pa_tgs_req(krb5_context context,
4555682Smarkm		krb5_auth_context ac,
4655682Smarkm		KDC_REQ_BODY *body,
4755682Smarkm		PA_DATA *padata,
4855682Smarkm		krb5_creds *creds)
4955682Smarkm{
5055682Smarkm    u_char *buf;
5155682Smarkm    size_t buf_size;
5255682Smarkm    size_t len;
5355682Smarkm    krb5_data in_data;
5455682Smarkm    krb5_error_code ret;
5555682Smarkm
5655682Smarkm    buf_size = 1024;
5755682Smarkm    buf = malloc (buf_size);
5855682Smarkm    if (buf == NULL)
5955682Smarkm	return ENOMEM;
6055682Smarkm
6155682Smarkm    do {
6255682Smarkm	ret = encode_KDC_REQ_BODY(buf + buf_size - 1, buf_size,
6355682Smarkm				  body, &len);
6455682Smarkm	if (ret){
6555682Smarkm	    if (ret == ASN1_OVERFLOW) {
6655682Smarkm		u_char *tmp;
6755682Smarkm
6855682Smarkm		buf_size *= 2;
6955682Smarkm		tmp = realloc (buf, buf_size);
7055682Smarkm		if (tmp == NULL) {
7155682Smarkm		    ret = ENOMEM;
7255682Smarkm		    goto out;
7355682Smarkm		}
7455682Smarkm		buf = tmp;
7555682Smarkm	    } else {
7655682Smarkm		goto out;
7755682Smarkm	    }
7855682Smarkm	}
7955682Smarkm    } while (ret == ASN1_OVERFLOW);
8055682Smarkm
8155682Smarkm    in_data.length = len;
8255682Smarkm    in_data.data   = buf + buf_size - len;
8355682Smarkm    ret = krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
8455682Smarkm			       &padata->padata_value,
8555682Smarkm			       KRB5_KU_TGS_REQ_AUTH_CKSUM);
8655682Smarkmout:
8755682Smarkm    free (buf);
8855682Smarkm    if(ret)
8955682Smarkm	return ret;
9055682Smarkm    padata->padata_type = pa_tgs_req;
9155682Smarkm    return 0;
9255682Smarkm}
9355682Smarkm
9455682Smarkm/*
9555682Smarkm * Set the `enc-authorization-data' in `req_body' based on `authdata'
9655682Smarkm */
9755682Smarkm
9855682Smarkmstatic krb5_error_code
9955682Smarkmset_auth_data (krb5_context context,
10055682Smarkm	       KDC_REQ_BODY *req_body,
10155682Smarkm	       krb5_authdata *authdata,
10255682Smarkm	       krb5_keyblock *key)
10355682Smarkm{
10455682Smarkm    if(authdata->len) {
10555682Smarkm	size_t len;
10655682Smarkm	unsigned char *buf;
10755682Smarkm	krb5_crypto crypto;
10855682Smarkm	krb5_error_code ret;
10955682Smarkm
11055682Smarkm	len = length_AuthorizationData(authdata);
11155682Smarkm	buf = malloc(len);
11255682Smarkm	if (buf == NULL)
11355682Smarkm	    return ENOMEM;
11455682Smarkm	ret = encode_AuthorizationData(buf + len - 1,
11555682Smarkm				       len, authdata, &len);
11655682Smarkm	if (ret) {
11755682Smarkm	    free (buf);
11855682Smarkm	    return ret;
11955682Smarkm	}
12055682Smarkm
12155682Smarkm	ALLOC(req_body->enc_authorization_data, 1);
12255682Smarkm	if (req_body->enc_authorization_data == NULL) {
12355682Smarkm	    free (buf);
12455682Smarkm	    return ret;
12555682Smarkm	}
12655682Smarkm	ret = krb5_crypto_init(context, key, 0, &crypto);
12755682Smarkm	if (ret) {
12855682Smarkm	    free (buf);
12955682Smarkm	    free (req_body->enc_authorization_data);
13055682Smarkm	    return ret;
13155682Smarkm	}
13255682Smarkm	krb5_encrypt_EncryptedData(context,
13355682Smarkm				   crypto,
13455682Smarkm				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
13555682Smarkm				   /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
13655682Smarkm				   buf,
13755682Smarkm				   len,
13855682Smarkm				   0,
13955682Smarkm				   req_body->enc_authorization_data);
14055682Smarkm	free (buf);
14155682Smarkm	krb5_crypto_destroy(context, crypto);
14255682Smarkm    } else {
14355682Smarkm	req_body->enc_authorization_data = NULL;
14455682Smarkm    }
14555682Smarkm    return 0;
14655682Smarkm}
14755682Smarkm
14855682Smarkm/*
14955682Smarkm * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
15055682Smarkm * (if not-NULL), `in_creds', `krbtgt', and returning the generated
15155682Smarkm * subkey in `subkey'.
15255682Smarkm */
15355682Smarkm
15455682Smarkmstatic krb5_error_code
15555682Smarkminit_tgs_req (krb5_context context,
15655682Smarkm	      krb5_ccache ccache,
15755682Smarkm	      krb5_addresses *addresses,
15855682Smarkm	      krb5_kdc_flags flags,
15955682Smarkm	      Ticket *second_ticket,
16055682Smarkm	      krb5_creds *in_creds,
16155682Smarkm	      krb5_creds *krbtgt,
16255682Smarkm	      unsigned nonce,
16355682Smarkm	      krb5_keyblock **subkey,
16455682Smarkm	      TGS_REQ *t)
16555682Smarkm{
16655682Smarkm    krb5_error_code ret;
16755682Smarkm
16855682Smarkm    memset(t, 0, sizeof(*t));
16955682Smarkm    t->pvno = 5;
17055682Smarkm    t->msg_type = krb_tgs_req;
17155682Smarkm    if (in_creds->session.keytype) {
17255682Smarkm	ret = krb5_keytype_to_enctypes_default (context,
17355682Smarkm						in_creds->session.keytype,
17455682Smarkm						&t->req_body.etype.len,
17555682Smarkm						&t->req_body.etype.val);
17655682Smarkm    } else {
17755682Smarkm	ret = krb5_init_etype(context,
17855682Smarkm			      &t->req_body.etype.len,
17955682Smarkm			      &t->req_body.etype.val,
18055682Smarkm			      NULL);
18155682Smarkm    }
18255682Smarkm    if (ret)
18355682Smarkm	goto fail;
18455682Smarkm    t->req_body.addresses = addresses;
18555682Smarkm    t->req_body.kdc_options = flags.b;
18655682Smarkm    ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
18755682Smarkm    if (ret)
18855682Smarkm	goto fail;
18955682Smarkm    ALLOC(t->req_body.sname, 1);
19055682Smarkm    if (t->req_body.sname == NULL) {
19155682Smarkm	ret = ENOMEM;
19255682Smarkm	goto fail;
19355682Smarkm    }
19455682Smarkm    ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
19555682Smarkm    if (ret)
19655682Smarkm	goto fail;
19755682Smarkm
19855682Smarkm    /* req_body.till should be NULL if there is no endtime specified,
19955682Smarkm       but old MIT code (like DCE secd) doesn't like that */
20055682Smarkm    ALLOC(t->req_body.till, 1);
20155682Smarkm    if(t->req_body.till == NULL){
20255682Smarkm	ret = ENOMEM;
20355682Smarkm	goto fail;
20455682Smarkm    }
20555682Smarkm    *t->req_body.till = in_creds->times.endtime;
20655682Smarkm
20755682Smarkm    t->req_body.nonce = nonce;
20855682Smarkm    if(second_ticket){
20955682Smarkm	ALLOC(t->req_body.additional_tickets, 1);
21055682Smarkm	if (t->req_body.additional_tickets == NULL) {
21155682Smarkm	    ret = ENOMEM;
21255682Smarkm	    goto fail;
21355682Smarkm	}
21455682Smarkm	ALLOC_SEQ(t->req_body.additional_tickets, 1);
21555682Smarkm	if (t->req_body.additional_tickets->val == NULL) {
21655682Smarkm	    ret = ENOMEM;
21755682Smarkm	    goto fail;
21855682Smarkm	}
21955682Smarkm	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
22055682Smarkm	if (ret)
22155682Smarkm	    goto fail;
22255682Smarkm    }
22355682Smarkm    ALLOC(t->padata, 1);
22455682Smarkm    if (t->padata == NULL) {
22555682Smarkm	ret = ENOMEM;
22655682Smarkm	goto fail;
22755682Smarkm    }
22855682Smarkm    ALLOC_SEQ(t->padata, 1);
22955682Smarkm    if (t->padata->val == NULL) {
23055682Smarkm	ret = ENOMEM;
23155682Smarkm	goto fail;
23255682Smarkm    }
23355682Smarkm
23455682Smarkm    {
23555682Smarkm	krb5_auth_context ac;
23655682Smarkm	krb5_keyblock *key;
23755682Smarkm
23855682Smarkm	ret = krb5_auth_con_init(context, &ac);
23955682Smarkm	if(ret)
24055682Smarkm	    goto fail;
24155682Smarkm	ret = krb5_generate_subkey (context, &krbtgt->session, &key);
24255682Smarkm	if (ret) {
24355682Smarkm	    krb5_auth_con_free (context, ac);
24455682Smarkm	    goto fail;
24555682Smarkm	}
24655682Smarkm	ret = krb5_auth_con_setlocalsubkey(context, ac, key);
24755682Smarkm	if (ret) {
24855682Smarkm	    krb5_free_keyblock (context, key);
24955682Smarkm	    krb5_auth_con_free (context, ac);
25055682Smarkm	    goto fail;
25155682Smarkm	}
25255682Smarkm
25355682Smarkm	ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key);
25455682Smarkm	if (ret) {
25555682Smarkm	    krb5_free_keyblock (context, key);
25655682Smarkm	    krb5_auth_con_free (context, ac);
25755682Smarkm	    goto fail;
25855682Smarkm	}
25955682Smarkm
26055682Smarkm	ret = make_pa_tgs_req(context,
26155682Smarkm			      ac,
26255682Smarkm			      &t->req_body,
26355682Smarkm			      t->padata->val,
26455682Smarkm			      krbtgt);
26555682Smarkm	if(ret) {
26655682Smarkm	    krb5_free_keyblock (context, key);
26755682Smarkm	    krb5_auth_con_free(context, ac);
26855682Smarkm	    goto fail;
26955682Smarkm	}
27055682Smarkm	*subkey = key;
27155682Smarkm
27255682Smarkm	krb5_auth_con_free(context, ac);
27355682Smarkm    }
27455682Smarkmfail:
27555682Smarkm    if (ret)
27655682Smarkm	free_TGS_REQ (t);
27755682Smarkm    return ret;
27855682Smarkm}
27955682Smarkm
28055682Smarkmstatic krb5_error_code
28155682Smarkmget_krbtgt(krb5_context context,
28255682Smarkm	   krb5_ccache  id,
28355682Smarkm	   krb5_realm realm,
28455682Smarkm	   krb5_creds **cred)
28555682Smarkm{
28655682Smarkm    krb5_error_code ret;
28755682Smarkm    krb5_creds tmp_cred;
28855682Smarkm
28955682Smarkm    memset(&tmp_cred, 0, sizeof(tmp_cred));
29055682Smarkm
29155682Smarkm    ret = krb5_make_principal(context,
29255682Smarkm			      &tmp_cred.server,
29355682Smarkm			      realm,
29455682Smarkm			      KRB5_TGS_NAME,
29555682Smarkm			      realm,
29655682Smarkm			      NULL);
29755682Smarkm    if(ret)
29855682Smarkm	return ret;
29955682Smarkm    ret = krb5_get_credentials(context,
30055682Smarkm			       KRB5_GC_CACHED,
30155682Smarkm			       id,
30255682Smarkm			       &tmp_cred,
30355682Smarkm			       cred);
30455682Smarkm    krb5_free_principal(context, tmp_cred.server);
30555682Smarkm    if(ret)
30655682Smarkm	return ret;
30755682Smarkm    return 0;
30855682Smarkm}
30955682Smarkm
31055682Smarkm/* DCE compatible decrypt proc */
31155682Smarkmstatic krb5_error_code
31255682Smarkmdecrypt_tkt_with_subkey (krb5_context context,
31355682Smarkm			 krb5_keyblock *key,
31455682Smarkm			 krb5_key_usage usage,
31555682Smarkm			 krb5_const_pointer subkey,
31655682Smarkm			 krb5_kdc_rep *dec_rep)
31755682Smarkm{
31855682Smarkm    krb5_error_code ret;
31955682Smarkm    krb5_data data;
32055682Smarkm    size_t size;
32155682Smarkm    krb5_crypto crypto;
32255682Smarkm
32355682Smarkm    krb5_crypto_init(context, key, 0, &crypto);
32455682Smarkm    ret = krb5_decrypt_EncryptedData (context,
32555682Smarkm				      crypto,
32655682Smarkm				      usage,
32755682Smarkm				      &dec_rep->kdc_rep.enc_part,
32855682Smarkm				      &data);
32955682Smarkm    krb5_crypto_destroy(context, crypto);
33055682Smarkm    if(ret && subkey){
33155682Smarkm	/* DCE compat -- try to decrypt with subkey */
33255682Smarkm	krb5_crypto_init(context, (krb5_keyblock*)subkey, 0, &crypto);
33355682Smarkm	ret = krb5_decrypt_EncryptedData (context,
33455682Smarkm					  crypto,
33555682Smarkm					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
33655682Smarkm					  &dec_rep->kdc_rep.enc_part,
33755682Smarkm					  &data);
33855682Smarkm	krb5_crypto_destroy(context, crypto);
33955682Smarkm    }
34055682Smarkm    if (ret)
34155682Smarkm	return ret;
34255682Smarkm
34355682Smarkm    ret = krb5_decode_EncASRepPart(context,
34455682Smarkm				   data.data,
34555682Smarkm				   data.length,
34655682Smarkm				   &dec_rep->enc_part,
34755682Smarkm				   &size);
34855682Smarkm    if (ret)
34955682Smarkm	ret = krb5_decode_EncTGSRepPart(context,
35055682Smarkm					data.data,
35155682Smarkm					data.length,
35255682Smarkm					&dec_rep->enc_part,
35355682Smarkm					&size);
35455682Smarkm    krb5_data_free (&data);
35555682Smarkm    return ret;
35655682Smarkm}
35755682Smarkm
35855682Smarkmstatic krb5_error_code
35955682Smarkmget_cred_kdc(krb5_context context,
36055682Smarkm	     krb5_ccache id,
36155682Smarkm	     krb5_kdc_flags flags,
36255682Smarkm	     krb5_addresses *addresses,
36355682Smarkm	     krb5_creds *in_creds,
36455682Smarkm	     krb5_creds *krbtgt,
36555682Smarkm	     krb5_creds *out_creds)
36655682Smarkm{
36755682Smarkm    TGS_REQ req;
36855682Smarkm    krb5_data enc;
36955682Smarkm    krb5_data resp;
37055682Smarkm    krb5_kdc_rep rep;
37155682Smarkm    KRB_ERROR error;
37255682Smarkm    krb5_error_code ret;
37355682Smarkm    unsigned nonce;
37455682Smarkm    krb5_keyblock *subkey = NULL;
37555682Smarkm    u_char *buf = NULL;
37655682Smarkm    size_t buf_size;
37755682Smarkm    size_t len;
37855682Smarkm    Ticket second_ticket;
37955682Smarkm
38055682Smarkm    krb5_generate_random_block(&nonce, sizeof(nonce));
38155682Smarkm    nonce &= 0xffffffff;
38255682Smarkm
38355682Smarkm    if(flags.b.enc_tkt_in_skey){
38455682Smarkm	ret = decode_Ticket(in_creds->second_ticket.data,
38555682Smarkm			    in_creds->second_ticket.length,
38655682Smarkm			    &second_ticket, &len);
38755682Smarkm	if(ret)
38855682Smarkm	    return ret;
38955682Smarkm    }
39055682Smarkm
39155682Smarkm    ret = init_tgs_req (context,
39255682Smarkm			id,
39355682Smarkm			addresses,
39455682Smarkm			flags,
39555682Smarkm			flags.b.enc_tkt_in_skey ? &second_ticket : NULL,
39655682Smarkm			in_creds,
39755682Smarkm			krbtgt,
39855682Smarkm			nonce,
39955682Smarkm			&subkey,
40055682Smarkm			&req);
40155682Smarkm    if(flags.b.enc_tkt_in_skey)
40255682Smarkm	free_Ticket(&second_ticket);
40355682Smarkm    if (ret)
40455682Smarkm	goto out;
40555682Smarkm
40655682Smarkm    buf_size = 1024;
40755682Smarkm    buf = malloc (buf_size);
40855682Smarkm    if (buf == NULL) {
40955682Smarkm	ret = ENOMEM;
41055682Smarkm	goto out;
41155682Smarkm    }
41255682Smarkm
41355682Smarkm    do {
41455682Smarkm	ret = encode_TGS_REQ  (buf + buf_size - 1, buf_size,
41555682Smarkm			       &req, &enc.length);
41655682Smarkm	if (ret) {
41755682Smarkm	    if (ret == ASN1_OVERFLOW) {
41855682Smarkm		u_char *tmp;
41955682Smarkm
42055682Smarkm		buf_size *= 2;
42155682Smarkm		tmp = realloc (buf, buf_size);
42255682Smarkm		if (tmp == NULL) {
42355682Smarkm		    ret = ENOMEM;
42455682Smarkm		    goto out;
42555682Smarkm		}
42655682Smarkm		buf = tmp;
42755682Smarkm	    } else {
42855682Smarkm		goto out;
42955682Smarkm	    }
43055682Smarkm	}
43155682Smarkm    } while (ret == ASN1_OVERFLOW);
43255682Smarkm
43355682Smarkm    /* don't free addresses */
43455682Smarkm    req.req_body.addresses = NULL;
43555682Smarkm    free_TGS_REQ(&req);
43655682Smarkm
43755682Smarkm    enc.data = buf + buf_size - enc.length;
43855682Smarkm    if (ret)
43955682Smarkm	goto out;
44055682Smarkm
44155682Smarkm    /*
44255682Smarkm     * Send and receive
44355682Smarkm     */
44455682Smarkm
44555682Smarkm    ret = krb5_sendto_kdc (context, &enc,
44655682Smarkm			   &krbtgt->server->name.name_string.val[1], &resp);
44755682Smarkm    if(ret)
44855682Smarkm	goto out;
44955682Smarkm
45055682Smarkm    memset(&rep, 0, sizeof(rep));
45155682Smarkm    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
45255682Smarkm	ret = krb5_copy_principal(context,
45355682Smarkm				  in_creds->client,
45455682Smarkm				  &out_creds->client);
45555682Smarkm	if(ret)
45655682Smarkm	    goto out;
45755682Smarkm	ret = krb5_copy_principal(context,
45855682Smarkm				  in_creds->server,
45955682Smarkm				  &out_creds->server);
46055682Smarkm	if(ret)
46155682Smarkm	    goto out;
46255682Smarkm	/* this should go someplace else */
46355682Smarkm	out_creds->times.endtime = in_creds->times.endtime;
46455682Smarkm
46555682Smarkm	ret = _krb5_extract_ticket(context,
46655682Smarkm				   &rep,
46755682Smarkm				   out_creds,
46855682Smarkm				   &krbtgt->session,
46955682Smarkm				   NULL,
47055682Smarkm				   KRB5_KU_TGS_REP_ENC_PART_SESSION,
47155682Smarkm				   &krbtgt->addresses,
47255682Smarkm				   nonce,
47355682Smarkm				   TRUE,
47455682Smarkm				   decrypt_tkt_with_subkey,
47555682Smarkm				   subkey);
47655682Smarkm	krb5_free_kdc_rep(context, &rep);
47755682Smarkm	if (ret)
47855682Smarkm	    goto out;
47955682Smarkm    }else if(krb5_rd_error(context, &resp, &error) == 0){
48055682Smarkm	ret = error.error_code;
48155682Smarkm	free_KRB_ERROR(&error);
48255682Smarkm    }else if(resp.data && ((char*)resp.data)[0] == 4)
48355682Smarkm	ret = KRB5KRB_AP_ERR_V4_REPLY;
48455682Smarkm    else
48555682Smarkm	ret = KRB5KRB_AP_ERR_MSG_TYPE;
48655682Smarkm    krb5_data_free(&resp);
48755682Smarkmout:
48855682Smarkm    if(subkey){
48955682Smarkm	krb5_free_keyblock_contents(context, subkey);
49055682Smarkm	free(subkey);
49155682Smarkm    }
49255682Smarkm    if (buf)
49355682Smarkm	free (buf);
49455682Smarkm    return ret;
49555682Smarkm
49655682Smarkm}
49755682Smarkm
49855682Smarkm/* same as above, just get local addresses first */
49955682Smarkm
50055682Smarkmstatic krb5_error_code
50155682Smarkmget_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags,
50255682Smarkm		krb5_creds *in_creds, krb5_creds *krbtgt,
50355682Smarkm		krb5_creds *out_creds)
50455682Smarkm{
50555682Smarkm    krb5_error_code ret;
50655682Smarkm    krb5_addresses addresses;
50755682Smarkm
50855682Smarkm    krb5_get_all_client_addrs(context, &addresses);
50955682Smarkm    ret = get_cred_kdc(context, id, flags, &addresses,
51055682Smarkm		       in_creds, krbtgt, out_creds);
51155682Smarkm    krb5_free_addresses(context, &addresses);
51255682Smarkm    return ret;
51355682Smarkm}
51455682Smarkm
51555682Smarkmkrb5_error_code
51655682Smarkmkrb5_get_kdc_cred(krb5_context context,
51755682Smarkm		  krb5_ccache id,
51855682Smarkm		  krb5_kdc_flags flags,
51955682Smarkm		  krb5_addresses *addresses,
52055682Smarkm		  Ticket  *second_ticket,
52155682Smarkm		  krb5_creds *in_creds,
52255682Smarkm		  krb5_creds **out_creds
52355682Smarkm		  )
52455682Smarkm{
52555682Smarkm    krb5_error_code ret;
52655682Smarkm    krb5_creds *krbtgt;
52755682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
52855682Smarkm    if(*out_creds == NULL)
52955682Smarkm	return ENOMEM;
53055682Smarkm    ret = get_krbtgt (context,
53155682Smarkm		      id,
53255682Smarkm		      in_creds->server->realm,
53355682Smarkm		      &krbtgt);
53455682Smarkm    if(ret) {
53555682Smarkm	free(*out_creds);
53655682Smarkm	return ret;
53755682Smarkm    }
53855682Smarkm    ret = get_cred_kdc(context, id, flags, addresses,
53955682Smarkm		       in_creds, krbtgt, *out_creds);
54055682Smarkm    krb5_free_creds (context, krbtgt);
54155682Smarkm    if(ret)
54255682Smarkm	free(*out_creds);
54355682Smarkm    return ret;
54455682Smarkm}
54555682Smarkm
54655682Smarkm
54755682Smarkmstatic krb5_error_code
54855682Smarkmfind_cred(krb5_context context,
54955682Smarkm	  krb5_ccache id,
55055682Smarkm	  krb5_principal server,
55155682Smarkm	  krb5_creds **tgts,
55255682Smarkm	  krb5_creds *out_creds)
55355682Smarkm{
55455682Smarkm    krb5_error_code ret;
55555682Smarkm    krb5_creds mcreds;
55655682Smarkm    mcreds.server = server;
55755682Smarkm    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
55855682Smarkm				&mcreds, out_creds);
55955682Smarkm    if(ret == 0)
56055682Smarkm	return 0;
56155682Smarkm    while(tgts && *tgts){
56255682Smarkm	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
56355682Smarkm			      &mcreds, *tgts)){
56455682Smarkm	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
56555682Smarkm	    return ret;
56655682Smarkm	}
56755682Smarkm	tgts++;
56855682Smarkm    }
56955682Smarkm    return KRB5_CC_NOTFOUND;
57055682Smarkm}
57155682Smarkm
57255682Smarkmstatic krb5_error_code
57355682Smarkmadd_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
57455682Smarkm{
57555682Smarkm    int i;
57655682Smarkm    krb5_error_code ret;
57755682Smarkm    krb5_creds **tmp = *tgts;
57855682Smarkm    for(i = 0; tmp && tmp[i]; i++); /* XXX */
57955682Smarkm    tmp = realloc(tmp, (i+2)*sizeof(*tmp));
58055682Smarkm    if(tmp == NULL)
58155682Smarkm	return ENOMEM;
58255682Smarkm    *tgts = tmp;
58355682Smarkm    ret = krb5_copy_creds(context, tkt, &tmp[i]);
58455682Smarkm    tmp[i+1] = NULL;
58555682Smarkm    return ret;
58655682Smarkm}
58755682Smarkm
58855682Smarkm/*
58955682Smarkmget_cred(server)
59055682Smarkm	creds = cc_get_cred(server)
59155682Smarkm	if(creds) return creds
59255682Smarkm	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
59355682Smarkm	if(tgt)
59455682Smarkm		return get_cred_tgt(server, tgt)
59555682Smarkm	if(client_realm == server_realm)
59655682Smarkm		return NULL
59755682Smarkm	tgt = get_cred(krbtgt/server_realm@client_realm)
59855682Smarkm	while(tgt_inst != server_realm)
59955682Smarkm		tgt = get_cred(krbtgt/server_realm@tgt_inst)
60055682Smarkm	return get_cred_tgt(server, tgt)
60155682Smarkm	*/
60255682Smarkm
60355682Smarkmstatic krb5_error_code
60455682Smarkmget_cred_from_kdc_flags(krb5_context context,
60555682Smarkm			krb5_kdc_flags flags,
60655682Smarkm			krb5_ccache ccache,
60755682Smarkm			krb5_creds *in_creds,
60855682Smarkm			krb5_creds **out_creds,
60955682Smarkm			krb5_creds ***ret_tgts)
61055682Smarkm{
61155682Smarkm    krb5_error_code ret;
61255682Smarkm    krb5_creds *tgt, tmp_creds;
61355682Smarkm    krb5_realm client_realm, server_realm;
61455682Smarkm
61555682Smarkm    *out_creds = NULL;
61655682Smarkm
61755682Smarkm    client_realm = *krb5_princ_realm(context, in_creds->client);
61855682Smarkm    server_realm = *krb5_princ_realm(context, in_creds->server);
61955682Smarkm    memset(&tmp_creds, 0, sizeof(tmp_creds));
62055682Smarkm    ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
62155682Smarkm    if(ret)
62255682Smarkm	return ret;
62355682Smarkm    ret = krb5_make_principal(context,
62455682Smarkm			      &tmp_creds.server,
62555682Smarkm			      client_realm,
62655682Smarkm			      KRB5_TGS_NAME,
62755682Smarkm			      server_realm,
62855682Smarkm			      NULL);
62955682Smarkm    if(ret){
63055682Smarkm	krb5_free_principal(context, tmp_creds.client);
63155682Smarkm	return ret;
63255682Smarkm    }
63355682Smarkm    {
63455682Smarkm	krb5_creds tgts;
63555682Smarkm	/* XXX try krb5_cc_retrieve_cred first? */
63655682Smarkm	ret = find_cred(context, ccache, tmp_creds.server,
63755682Smarkm			*ret_tgts, &tgts);
63855682Smarkm	if(ret == 0){
63955682Smarkm	    *out_creds = calloc(1, sizeof(**out_creds));
64055682Smarkm	    if(*out_creds == NULL)
64155682Smarkm		ret = ENOMEM;
64255682Smarkm	    else {
64355682Smarkm		ret = get_cred_kdc_la(context, ccache, flags,
64455682Smarkm				      in_creds, &tgts, *out_creds);
64555682Smarkm		if (ret)
64655682Smarkm		    free (*out_creds);
64755682Smarkm	    }
64855682Smarkm	    krb5_free_creds_contents(context, &tgts);
64955682Smarkm	    krb5_free_principal(context, tmp_creds.server);
65055682Smarkm	    krb5_free_principal(context, tmp_creds.client);
65155682Smarkm	    return ret;
65255682Smarkm	}
65355682Smarkm    }
65455682Smarkm    if(krb5_realm_compare(context, in_creds->client, in_creds->server))
65555682Smarkm	return KRB5_CC_NOTFOUND;
65655682Smarkm    /* XXX this can loop forever */
65755682Smarkm    while(1){
65855682Smarkm	general_string tgt_inst;
65955682Smarkm	krb5_kdc_flags f;
66055682Smarkm	f.i = 0;
66155682Smarkm	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
66255682Smarkm				      &tgt, ret_tgts);
66355682Smarkm	if(ret) {
66455682Smarkm	    krb5_free_principal(context, tmp_creds.server);
66555682Smarkm	    krb5_free_principal(context, tmp_creds.client);
66655682Smarkm	    return ret;
66755682Smarkm	}
66855682Smarkm	ret = add_cred(context, ret_tgts, tgt);
66955682Smarkm	if(ret) {
67055682Smarkm	    krb5_free_principal(context, tmp_creds.server);
67155682Smarkm	    krb5_free_principal(context, tmp_creds.client);
67255682Smarkm	    return ret;
67355682Smarkm	}
67455682Smarkm	tgt_inst = tgt->server->name.name_string.val[1];
67555682Smarkm	if(strcmp(tgt_inst, server_realm) == 0)
67655682Smarkm	    break;
67755682Smarkm	krb5_free_principal(context, tmp_creds.server);
67855682Smarkm	ret = krb5_make_principal(context, &tmp_creds.server,
67955682Smarkm				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
68055682Smarkm	if(ret) {
68155682Smarkm	    krb5_free_principal(context, tmp_creds.server);
68255682Smarkm	    krb5_free_principal(context, tmp_creds.client);
68355682Smarkm	    return ret;
68455682Smarkm	}
68555682Smarkm	ret = krb5_free_creds(context, tgt);
68655682Smarkm	if(ret) {
68755682Smarkm	    krb5_free_principal(context, tmp_creds.server);
68855682Smarkm	    krb5_free_principal(context, tmp_creds.client);
68955682Smarkm	    return ret;
69055682Smarkm	}
69155682Smarkm    }
69255682Smarkm
69355682Smarkm    krb5_free_principal(context, tmp_creds.server);
69455682Smarkm    krb5_free_principal(context, tmp_creds.client);
69555682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
69655682Smarkm    if(*out_creds == NULL)
69755682Smarkm	ret = ENOMEM;
69855682Smarkm    else {
69955682Smarkm	ret = get_cred_kdc_la(context, ccache, flags,
70055682Smarkm				      in_creds, tgt, *out_creds);
70155682Smarkm	if (ret)
70255682Smarkm	    free (*out_creds);
70355682Smarkm    }
70455682Smarkm    krb5_free_creds(context, tgt);
70555682Smarkm    return ret;
70655682Smarkm}
70755682Smarkm
70855682Smarkmkrb5_error_code
70955682Smarkmkrb5_get_cred_from_kdc(krb5_context context,
71055682Smarkm		       krb5_ccache ccache,
71155682Smarkm		       krb5_creds *in_creds,
71255682Smarkm		       krb5_creds **out_creds,
71355682Smarkm		       krb5_creds ***ret_tgts)
71455682Smarkm{
71555682Smarkm    krb5_kdc_flags f;
71655682Smarkm    f.i = 0;
71755682Smarkm    return get_cred_from_kdc_flags(context, f, ccache,
71855682Smarkm				   in_creds, out_creds, ret_tgts);
71955682Smarkm}
72055682Smarkm
72155682Smarkm
72255682Smarkmkrb5_error_code
72355682Smarkmkrb5_get_credentials_with_flags(krb5_context context,
72455682Smarkm				krb5_flags options,
72555682Smarkm				krb5_kdc_flags flags,
72655682Smarkm				krb5_ccache ccache,
72755682Smarkm				krb5_creds *in_creds,
72855682Smarkm				krb5_creds **out_creds)
72955682Smarkm{
73055682Smarkm    krb5_error_code ret;
73155682Smarkm    krb5_creds **tgts;
73255682Smarkm    int i;
73355682Smarkm
73455682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
73555682Smarkm    if (*out_creds == NULL)
73655682Smarkm	return ENOMEM;
73755682Smarkm
73855682Smarkm    ret = krb5_cc_retrieve_cred(context,
73955682Smarkm				ccache,
74055682Smarkm				in_creds->session.keytype ?
74155682Smarkm				KRB5_TC_MATCH_KEYTYPE : 0,
74255682Smarkm				in_creds, *out_creds);
74355682Smarkm    if(ret == 0)
74455682Smarkm	return 0;
74555682Smarkm    free(*out_creds);
74655682Smarkm    if(ret != KRB5_CC_END)
74755682Smarkm	return ret;
74855682Smarkm    if(options & KRB5_GC_CACHED)
74955682Smarkm	return KRB5_CC_NOTFOUND;
75055682Smarkm    if(options & KRB5_GC_USER_USER)
75155682Smarkm	flags.b.enc_tkt_in_skey = 1;
75255682Smarkm    tgts = NULL;
75355682Smarkm    ret = get_cred_from_kdc_flags(context, flags, ccache,
75455682Smarkm				  in_creds, out_creds, &tgts);
75555682Smarkm    for(i = 0; tgts && tgts[i]; i++){
75655682Smarkm	krb5_cc_store_cred(context, ccache, tgts[i]);
75755682Smarkm	krb5_free_creds(context, tgts[i]);
75855682Smarkm    }
75955682Smarkm    free(tgts);
76055682Smarkm    if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
76155682Smarkm	krb5_cc_store_cred(context, ccache, *out_creds);
76255682Smarkm    return ret;
76355682Smarkm}
76455682Smarkm
76555682Smarkmkrb5_error_code
76655682Smarkmkrb5_get_credentials(krb5_context context,
76755682Smarkm		     krb5_flags options,
76855682Smarkm		     krb5_ccache ccache,
76955682Smarkm		     krb5_creds *in_creds,
77055682Smarkm		     krb5_creds **out_creds)
77155682Smarkm{
77255682Smarkm    krb5_kdc_flags flags;
77355682Smarkm    flags.i = 0;
77455682Smarkm    return krb5_get_credentials_with_flags(context, options, flags,
77555682Smarkm					   ccache, in_creds, out_creds);
77655682Smarkm}
777