get_cred.c revision 127808
155682Smarkm/*
2127808Snectar * Copyright (c) 1997 - 2004 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
36127808SnectarRCSID("$Id: get_cred.c,v 1.91.4.3 2004/01/09 00:47:17 lha 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,
4878527Sassar		krb5_creds *creds,
4978527Sassar		krb5_key_usage usage)
5055682Smarkm{
5155682Smarkm    u_char *buf;
5255682Smarkm    size_t buf_size;
5355682Smarkm    size_t len;
5455682Smarkm    krb5_data in_data;
5555682Smarkm    krb5_error_code ret;
5655682Smarkm
57103423Snectar    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
58103423Snectar    if (ret)
59103423Snectar	goto out;
60103423Snectar    if(buf_size != len)
61103423Snectar	krb5_abortx(context, "internal error in ASN.1 encoder");
6255682Smarkm
6355682Smarkm    in_data.length = len;
64103423Snectar    in_data.data   = buf;
6555682Smarkm    ret = krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
6655682Smarkm			       &padata->padata_value,
6772445Sassar			       KRB5_KU_TGS_REQ_AUTH_CKSUM,
6878527Sassar			       usage
6978527Sassar			       /* KRB5_KU_TGS_REQ_AUTH */);
7055682Smarkmout:
7155682Smarkm    free (buf);
7255682Smarkm    if(ret)
7355682Smarkm	return ret;
7472445Sassar    padata->padata_type = KRB5_PADATA_TGS_REQ;
7555682Smarkm    return 0;
7655682Smarkm}
7755682Smarkm
7855682Smarkm/*
7955682Smarkm * Set the `enc-authorization-data' in `req_body' based on `authdata'
8055682Smarkm */
8155682Smarkm
8255682Smarkmstatic krb5_error_code
8355682Smarkmset_auth_data (krb5_context context,
8455682Smarkm	       KDC_REQ_BODY *req_body,
8555682Smarkm	       krb5_authdata *authdata,
8655682Smarkm	       krb5_keyblock *key)
8755682Smarkm{
8855682Smarkm    if(authdata->len) {
8955682Smarkm	size_t len;
9055682Smarkm	unsigned char *buf;
9155682Smarkm	krb5_crypto crypto;
9255682Smarkm	krb5_error_code ret;
9355682Smarkm
94103423Snectar	ASN1_MALLOC_ENCODE(AuthorizationData, buf, len, authdata, &len, ret);
95103423Snectar	if (ret)
9655682Smarkm	    return ret;
9755682Smarkm
9855682Smarkm	ALLOC(req_body->enc_authorization_data, 1);
9955682Smarkm	if (req_body->enc_authorization_data == NULL) {
10055682Smarkm	    free (buf);
10178527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
10278527Sassar	    return ENOMEM;
10355682Smarkm	}
10455682Smarkm	ret = krb5_crypto_init(context, key, 0, &crypto);
10555682Smarkm	if (ret) {
10655682Smarkm	    free (buf);
10755682Smarkm	    free (req_body->enc_authorization_data);
10855682Smarkm	    return ret;
10955682Smarkm	}
11055682Smarkm	krb5_encrypt_EncryptedData(context,
11155682Smarkm				   crypto,
11255682Smarkm				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
11355682Smarkm				   /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
11455682Smarkm				   buf,
11555682Smarkm				   len,
11655682Smarkm				   0,
11755682Smarkm				   req_body->enc_authorization_data);
11855682Smarkm	free (buf);
11955682Smarkm	krb5_crypto_destroy(context, crypto);
12055682Smarkm    } else {
12155682Smarkm	req_body->enc_authorization_data = NULL;
12255682Smarkm    }
12355682Smarkm    return 0;
12455682Smarkm}
12555682Smarkm
12655682Smarkm/*
12755682Smarkm * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
12855682Smarkm * (if not-NULL), `in_creds', `krbtgt', and returning the generated
12955682Smarkm * subkey in `subkey'.
13055682Smarkm */
13155682Smarkm
13255682Smarkmstatic krb5_error_code
13355682Smarkminit_tgs_req (krb5_context context,
13455682Smarkm	      krb5_ccache ccache,
13555682Smarkm	      krb5_addresses *addresses,
13655682Smarkm	      krb5_kdc_flags flags,
13755682Smarkm	      Ticket *second_ticket,
13855682Smarkm	      krb5_creds *in_creds,
13955682Smarkm	      krb5_creds *krbtgt,
14055682Smarkm	      unsigned nonce,
14155682Smarkm	      krb5_keyblock **subkey,
14278527Sassar	      TGS_REQ *t,
14378527Sassar	      krb5_key_usage usage)
14455682Smarkm{
145103423Snectar    krb5_error_code ret = 0;
14655682Smarkm
14755682Smarkm    memset(t, 0, sizeof(*t));
14855682Smarkm    t->pvno = 5;
14955682Smarkm    t->msg_type = krb_tgs_req;
15055682Smarkm    if (in_creds->session.keytype) {
151103423Snectar	ALLOC_SEQ(&t->req_body.etype, 1);
152103423Snectar	if(t->req_body.etype.val == NULL) {
153103423Snectar	    ret = ENOMEM;
154103423Snectar	    krb5_set_error_string(context, "malloc: out of memory");
155103423Snectar	    goto fail;
156103423Snectar	}
157103423Snectar	t->req_body.etype.val[0] = in_creds->session.keytype;
15855682Smarkm    } else {
15955682Smarkm	ret = krb5_init_etype(context,
16055682Smarkm			      &t->req_body.etype.len,
16155682Smarkm			      &t->req_body.etype.val,
16255682Smarkm			      NULL);
16355682Smarkm    }
16455682Smarkm    if (ret)
16555682Smarkm	goto fail;
16655682Smarkm    t->req_body.addresses = addresses;
16755682Smarkm    t->req_body.kdc_options = flags.b;
16855682Smarkm    ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
16955682Smarkm    if (ret)
17055682Smarkm	goto fail;
17155682Smarkm    ALLOC(t->req_body.sname, 1);
17255682Smarkm    if (t->req_body.sname == NULL) {
17355682Smarkm	ret = ENOMEM;
17478527Sassar	krb5_set_error_string(context, "malloc: out of memory");
17555682Smarkm	goto fail;
17655682Smarkm    }
17772445Sassar
17872445Sassar    /* some versions of some code might require that the client be
17972445Sassar       present in TGS-REQs, but this is clearly against the spec */
18072445Sassar
18155682Smarkm    ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
18255682Smarkm    if (ret)
18355682Smarkm	goto fail;
18455682Smarkm
18555682Smarkm    /* req_body.till should be NULL if there is no endtime specified,
18655682Smarkm       but old MIT code (like DCE secd) doesn't like that */
18755682Smarkm    ALLOC(t->req_body.till, 1);
18855682Smarkm    if(t->req_body.till == NULL){
18955682Smarkm	ret = ENOMEM;
19078527Sassar	krb5_set_error_string(context, "malloc: out of memory");
19155682Smarkm	goto fail;
19255682Smarkm    }
19355682Smarkm    *t->req_body.till = in_creds->times.endtime;
19455682Smarkm
19555682Smarkm    t->req_body.nonce = nonce;
19655682Smarkm    if(second_ticket){
19755682Smarkm	ALLOC(t->req_body.additional_tickets, 1);
19855682Smarkm	if (t->req_body.additional_tickets == NULL) {
19955682Smarkm	    ret = ENOMEM;
20078527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
20155682Smarkm	    goto fail;
20255682Smarkm	}
20355682Smarkm	ALLOC_SEQ(t->req_body.additional_tickets, 1);
20455682Smarkm	if (t->req_body.additional_tickets->val == NULL) {
20555682Smarkm	    ret = ENOMEM;
20678527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
20755682Smarkm	    goto fail;
20855682Smarkm	}
20955682Smarkm	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
21055682Smarkm	if (ret)
21155682Smarkm	    goto fail;
21255682Smarkm    }
21355682Smarkm    ALLOC(t->padata, 1);
21455682Smarkm    if (t->padata == NULL) {
21555682Smarkm	ret = ENOMEM;
21678527Sassar	krb5_set_error_string(context, "malloc: out of memory");
21755682Smarkm	goto fail;
21855682Smarkm    }
21955682Smarkm    ALLOC_SEQ(t->padata, 1);
22055682Smarkm    if (t->padata->val == NULL) {
22155682Smarkm	ret = ENOMEM;
22278527Sassar	krb5_set_error_string(context, "malloc: out of memory");
22355682Smarkm	goto fail;
22455682Smarkm    }
22555682Smarkm
22655682Smarkm    {
22755682Smarkm	krb5_auth_context ac;
228127808Snectar	krb5_keyblock *key = NULL;
22955682Smarkm
23055682Smarkm	ret = krb5_auth_con_init(context, &ac);
23155682Smarkm	if(ret)
23255682Smarkm	    goto fail;
233127808Snectar
234127808Snectar	if (krb5_config_get_bool_default(context, NULL, FALSE,
235127808Snectar					 "realms",
236127808Snectar					 krbtgt->server->realm,
237127808Snectar					 "tgs_require_subkey",
238127808Snectar					 NULL))
239127808Snectar	{
240127808Snectar	    ret = krb5_generate_subkey (context, &krbtgt->session, &key);
241127808Snectar	    if (ret) {
242127808Snectar		krb5_auth_con_free (context, ac);
243127808Snectar		goto fail;
244127808Snectar	    }
245127808Snectar
246127808Snectar	    ret = krb5_auth_con_setlocalsubkey(context, ac, key);
247127808Snectar	    if (ret) {
248127808Snectar		if (key)
249127808Snectar		    krb5_free_keyblock (context, key);
250127808Snectar		krb5_auth_con_free (context, ac);
251127808Snectar		goto fail;
252127808Snectar	    }
25355682Smarkm	}
25455682Smarkm
25555682Smarkm	ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key);
25655682Smarkm	if (ret) {
257127808Snectar	    if (key)
258127808Snectar		krb5_free_keyblock (context, key);
25955682Smarkm	    krb5_auth_con_free (context, ac);
26055682Smarkm	    goto fail;
26155682Smarkm	}
26255682Smarkm
26355682Smarkm	ret = make_pa_tgs_req(context,
26455682Smarkm			      ac,
26555682Smarkm			      &t->req_body,
26655682Smarkm			      t->padata->val,
26778527Sassar			      krbtgt,
26878527Sassar			      usage);
26955682Smarkm	if(ret) {
270127808Snectar	    if (key)
271127808Snectar		krb5_free_keyblock (context, key);
27255682Smarkm	    krb5_auth_con_free(context, ac);
27355682Smarkm	    goto fail;
27455682Smarkm	}
27555682Smarkm	*subkey = key;
27655682Smarkm
27755682Smarkm	krb5_auth_con_free(context, ac);
27855682Smarkm    }
27955682Smarkmfail:
280127808Snectar    if (ret) {
281127808Snectar	t->req_body.addresses = NULL;
28255682Smarkm	free_TGS_REQ (t);
283127808Snectar    }
28455682Smarkm    return ret;
28555682Smarkm}
28655682Smarkm
287127808Snectarkrb5_error_code
288127808Snectar_krb5_get_krbtgt(krb5_context context,
289127808Snectar		 krb5_ccache  id,
290127808Snectar		 krb5_realm realm,
291127808Snectar		 krb5_creds **cred)
29255682Smarkm{
29355682Smarkm    krb5_error_code ret;
29455682Smarkm    krb5_creds tmp_cred;
29555682Smarkm
29655682Smarkm    memset(&tmp_cred, 0, sizeof(tmp_cred));
29755682Smarkm
298127808Snectar    ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
299127808Snectar    if (ret)
300127808Snectar	return ret;
301127808Snectar
30255682Smarkm    ret = krb5_make_principal(context,
30355682Smarkm			      &tmp_cred.server,
30455682Smarkm			      realm,
30555682Smarkm			      KRB5_TGS_NAME,
30655682Smarkm			      realm,
30755682Smarkm			      NULL);
308127808Snectar    if(ret) {
309127808Snectar	krb5_free_principal(context, tmp_cred.client);
31055682Smarkm	return ret;
311127808Snectar    }
31255682Smarkm    ret = krb5_get_credentials(context,
31355682Smarkm			       KRB5_GC_CACHED,
31455682Smarkm			       id,
31555682Smarkm			       &tmp_cred,
31655682Smarkm			       cred);
317127808Snectar    krb5_free_principal(context, tmp_cred.client);
31855682Smarkm    krb5_free_principal(context, tmp_cred.server);
31955682Smarkm    if(ret)
32055682Smarkm	return ret;
32155682Smarkm    return 0;
32255682Smarkm}
32355682Smarkm
32455682Smarkm/* DCE compatible decrypt proc */
32555682Smarkmstatic krb5_error_code
32655682Smarkmdecrypt_tkt_with_subkey (krb5_context context,
32755682Smarkm			 krb5_keyblock *key,
32855682Smarkm			 krb5_key_usage usage,
32955682Smarkm			 krb5_const_pointer subkey,
33055682Smarkm			 krb5_kdc_rep *dec_rep)
33155682Smarkm{
33255682Smarkm    krb5_error_code ret;
33355682Smarkm    krb5_data data;
33455682Smarkm    size_t size;
33555682Smarkm    krb5_crypto crypto;
33655682Smarkm
33772445Sassar    ret = krb5_crypto_init(context, key, 0, &crypto);
33872445Sassar    if (ret)
33972445Sassar	return ret;
34055682Smarkm    ret = krb5_decrypt_EncryptedData (context,
34155682Smarkm				      crypto,
34255682Smarkm				      usage,
34355682Smarkm				      &dec_rep->kdc_rep.enc_part,
34455682Smarkm				      &data);
34555682Smarkm    krb5_crypto_destroy(context, crypto);
34655682Smarkm    if(ret && subkey){
34755682Smarkm	/* DCE compat -- try to decrypt with subkey */
34872445Sassar	ret = krb5_crypto_init(context, (krb5_keyblock*)subkey, 0, &crypto);
34972445Sassar	if (ret)
35072445Sassar	    return ret;
35155682Smarkm	ret = krb5_decrypt_EncryptedData (context,
35255682Smarkm					  crypto,
35355682Smarkm					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
35455682Smarkm					  &dec_rep->kdc_rep.enc_part,
35555682Smarkm					  &data);
35655682Smarkm	krb5_crypto_destroy(context, crypto);
35755682Smarkm    }
35855682Smarkm    if (ret)
35955682Smarkm	return ret;
36055682Smarkm
36155682Smarkm    ret = krb5_decode_EncASRepPart(context,
36255682Smarkm				   data.data,
36355682Smarkm				   data.length,
36455682Smarkm				   &dec_rep->enc_part,
36555682Smarkm				   &size);
36655682Smarkm    if (ret)
36755682Smarkm	ret = krb5_decode_EncTGSRepPart(context,
36855682Smarkm					data.data,
36955682Smarkm					data.length,
37055682Smarkm					&dec_rep->enc_part,
37155682Smarkm					&size);
37255682Smarkm    krb5_data_free (&data);
37355682Smarkm    return ret;
37455682Smarkm}
37555682Smarkm
37655682Smarkmstatic krb5_error_code
37778527Sassarget_cred_kdc_usage(krb5_context context,
37878527Sassar		   krb5_ccache id,
37978527Sassar		   krb5_kdc_flags flags,
38078527Sassar		   krb5_addresses *addresses,
38178527Sassar		   krb5_creds *in_creds,
38278527Sassar		   krb5_creds *krbtgt,
38378527Sassar		   krb5_creds *out_creds,
38478527Sassar		   krb5_key_usage usage)
38555682Smarkm{
38655682Smarkm    TGS_REQ req;
38755682Smarkm    krb5_data enc;
38855682Smarkm    krb5_data resp;
38955682Smarkm    krb5_kdc_rep rep;
39055682Smarkm    KRB_ERROR error;
39155682Smarkm    krb5_error_code ret;
39255682Smarkm    unsigned nonce;
39355682Smarkm    krb5_keyblock *subkey = NULL;
39455682Smarkm    u_char *buf = NULL;
39555682Smarkm    size_t buf_size;
39655682Smarkm    size_t len;
39755682Smarkm    Ticket second_ticket;
39855682Smarkm
39955682Smarkm    krb5_generate_random_block(&nonce, sizeof(nonce));
40055682Smarkm    nonce &= 0xffffffff;
40155682Smarkm
40255682Smarkm    if(flags.b.enc_tkt_in_skey){
40355682Smarkm	ret = decode_Ticket(in_creds->second_ticket.data,
40455682Smarkm			    in_creds->second_ticket.length,
40555682Smarkm			    &second_ticket, &len);
40655682Smarkm	if(ret)
40755682Smarkm	    return ret;
40855682Smarkm    }
40955682Smarkm
41055682Smarkm    ret = init_tgs_req (context,
41155682Smarkm			id,
41255682Smarkm			addresses,
41355682Smarkm			flags,
41455682Smarkm			flags.b.enc_tkt_in_skey ? &second_ticket : NULL,
41555682Smarkm			in_creds,
41655682Smarkm			krbtgt,
41755682Smarkm			nonce,
41855682Smarkm			&subkey,
41978527Sassar			&req,
42078527Sassar			usage);
42155682Smarkm    if(flags.b.enc_tkt_in_skey)
42255682Smarkm	free_Ticket(&second_ticket);
42355682Smarkm    if (ret)
42455682Smarkm	goto out;
42555682Smarkm
426103423Snectar    ASN1_MALLOC_ENCODE(TGS_REQ, buf, buf_size, &req, &enc.length, ret);
427103423Snectar    if (ret)
42855682Smarkm	goto out;
429103423Snectar    if(enc.length != buf_size)
430103423Snectar	krb5_abortx(context, "internal error in ASN.1 encoder");
43155682Smarkm
43255682Smarkm    /* don't free addresses */
43355682Smarkm    req.req_body.addresses = NULL;
43455682Smarkm    free_TGS_REQ(&req);
43555682Smarkm
43655682Smarkm    enc.data = buf + buf_size - enc.length;
43755682Smarkm    if (ret)
43855682Smarkm	goto out;
43955682Smarkm
44055682Smarkm    /*
44155682Smarkm     * Send and receive
44255682Smarkm     */
44355682Smarkm
44455682Smarkm    ret = krb5_sendto_kdc (context, &enc,
44555682Smarkm			   &krbtgt->server->name.name_string.val[1], &resp);
44655682Smarkm    if(ret)
44755682Smarkm	goto out;
44855682Smarkm
44955682Smarkm    memset(&rep, 0, sizeof(rep));
45055682Smarkm    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
45155682Smarkm	ret = krb5_copy_principal(context,
45255682Smarkm				  in_creds->client,
45355682Smarkm				  &out_creds->client);
45455682Smarkm	if(ret)
45555682Smarkm	    goto out;
45655682Smarkm	ret = krb5_copy_principal(context,
45755682Smarkm				  in_creds->server,
45855682Smarkm				  &out_creds->server);
45955682Smarkm	if(ret)
46055682Smarkm	    goto out;
46155682Smarkm	/* this should go someplace else */
46255682Smarkm	out_creds->times.endtime = in_creds->times.endtime;
46355682Smarkm
46455682Smarkm	ret = _krb5_extract_ticket(context,
46555682Smarkm				   &rep,
46655682Smarkm				   out_creds,
46755682Smarkm				   &krbtgt->session,
46855682Smarkm				   NULL,
46955682Smarkm				   KRB5_KU_TGS_REP_ENC_PART_SESSION,
47055682Smarkm				   &krbtgt->addresses,
47155682Smarkm				   nonce,
47255682Smarkm				   TRUE,
47372445Sassar				   flags.b.request_anonymous,
47455682Smarkm				   decrypt_tkt_with_subkey,
47555682Smarkm				   subkey);
47655682Smarkm	krb5_free_kdc_rep(context, &rep);
47755682Smarkm	if (ret)
47855682Smarkm	    goto out;
47978527Sassar    } else if(krb5_rd_error(context, &resp, &error) == 0) {
48078527Sassar	ret = krb5_error_from_rd_error(context, &error, in_creds);
48178527Sassar	krb5_free_error_contents(context, &error);
48278527Sassar    } else if(resp.data && ((char*)resp.data)[0] == 4) {
48355682Smarkm	ret = KRB5KRB_AP_ERR_V4_REPLY;
48478527Sassar	krb5_clear_error_string(context);
48578527Sassar    } else {
48655682Smarkm	ret = KRB5KRB_AP_ERR_MSG_TYPE;
48778527Sassar	krb5_clear_error_string(context);
48878527Sassar    }
48955682Smarkm    krb5_data_free(&resp);
490127808Snectar out:
49155682Smarkm    if(subkey){
49255682Smarkm	krb5_free_keyblock_contents(context, subkey);
49355682Smarkm	free(subkey);
49455682Smarkm    }
49555682Smarkm    if (buf)
49655682Smarkm	free (buf);
49755682Smarkm    return ret;
49855682Smarkm
49955682Smarkm}
50055682Smarkm
50178527Sassarstatic krb5_error_code
50278527Sassarget_cred_kdc(krb5_context context,
50378527Sassar	     krb5_ccache id,
50478527Sassar	     krb5_kdc_flags flags,
50578527Sassar	     krb5_addresses *addresses,
50678527Sassar	     krb5_creds *in_creds,
50778527Sassar	     krb5_creds *krbtgt,
50878527Sassar	     krb5_creds *out_creds)
50978527Sassar{
51078527Sassar    krb5_error_code ret;
51178527Sassar
51278527Sassar    ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
51378527Sassar			     krbtgt, out_creds, KRB5_KU_TGS_REQ_AUTH);
51478527Sassar    if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
51578527Sassar	krb5_clear_error_string (context);
51678527Sassar	ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
51778527Sassar				 krbtgt, out_creds, KRB5_KU_AP_REQ_AUTH);
51878527Sassar    }
51978527Sassar    return ret;
52078527Sassar}
52178527Sassar
52255682Smarkm/* same as above, just get local addresses first */
52355682Smarkm
52455682Smarkmstatic krb5_error_code
52555682Smarkmget_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags,
52655682Smarkm		krb5_creds *in_creds, krb5_creds *krbtgt,
52755682Smarkm		krb5_creds *out_creds)
52855682Smarkm{
52955682Smarkm    krb5_error_code ret;
53090926Snectar    krb5_addresses addresses, *addrs = &addresses;
53155682Smarkm
53255682Smarkm    krb5_get_all_client_addrs(context, &addresses);
53390926Snectar    /* XXX this sucks. */
53490926Snectar    if(addresses.len == 0)
53590926Snectar	addrs = NULL;
53690926Snectar    ret = get_cred_kdc(context, id, flags, addrs,
53755682Smarkm		       in_creds, krbtgt, out_creds);
53855682Smarkm    krb5_free_addresses(context, &addresses);
53955682Smarkm    return ret;
54055682Smarkm}
54155682Smarkm
54255682Smarkmkrb5_error_code
54355682Smarkmkrb5_get_kdc_cred(krb5_context context,
54455682Smarkm		  krb5_ccache id,
54555682Smarkm		  krb5_kdc_flags flags,
54655682Smarkm		  krb5_addresses *addresses,
54755682Smarkm		  Ticket  *second_ticket,
54855682Smarkm		  krb5_creds *in_creds,
54955682Smarkm		  krb5_creds **out_creds
55055682Smarkm		  )
55155682Smarkm{
55255682Smarkm    krb5_error_code ret;
55355682Smarkm    krb5_creds *krbtgt;
55478527Sassar
55555682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
55678527Sassar    if(*out_creds == NULL) {
55778527Sassar	krb5_set_error_string(context, "malloc: out of memory");
55855682Smarkm	return ENOMEM;
55978527Sassar    }
560127808Snectar    ret = _krb5_get_krbtgt (context,
561127808Snectar			    id,
562127808Snectar			    in_creds->server->realm,
563127808Snectar			    &krbtgt);
56455682Smarkm    if(ret) {
56555682Smarkm	free(*out_creds);
56655682Smarkm	return ret;
56755682Smarkm    }
56855682Smarkm    ret = get_cred_kdc(context, id, flags, addresses,
56955682Smarkm		       in_creds, krbtgt, *out_creds);
57055682Smarkm    krb5_free_creds (context, krbtgt);
57155682Smarkm    if(ret)
57255682Smarkm	free(*out_creds);
57355682Smarkm    return ret;
57455682Smarkm}
57555682Smarkm
57655682Smarkm
57755682Smarkmstatic krb5_error_code
57855682Smarkmfind_cred(krb5_context context,
57955682Smarkm	  krb5_ccache id,
58055682Smarkm	  krb5_principal server,
58155682Smarkm	  krb5_creds **tgts,
58255682Smarkm	  krb5_creds *out_creds)
58355682Smarkm{
58455682Smarkm    krb5_error_code ret;
58555682Smarkm    krb5_creds mcreds;
58655682Smarkm    mcreds.server = server;
58755682Smarkm    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
58855682Smarkm				&mcreds, out_creds);
58955682Smarkm    if(ret == 0)
59055682Smarkm	return 0;
59155682Smarkm    while(tgts && *tgts){
59255682Smarkm	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
59355682Smarkm			      &mcreds, *tgts)){
59455682Smarkm	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
59555682Smarkm	    return ret;
59655682Smarkm	}
59755682Smarkm	tgts++;
59855682Smarkm    }
59978527Sassar    krb5_clear_error_string(context);
60055682Smarkm    return KRB5_CC_NOTFOUND;
60155682Smarkm}
60255682Smarkm
60355682Smarkmstatic krb5_error_code
60455682Smarkmadd_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
60555682Smarkm{
60655682Smarkm    int i;
60755682Smarkm    krb5_error_code ret;
60855682Smarkm    krb5_creds **tmp = *tgts;
60978527Sassar
61055682Smarkm    for(i = 0; tmp && tmp[i]; i++); /* XXX */
61155682Smarkm    tmp = realloc(tmp, (i+2)*sizeof(*tmp));
61278527Sassar    if(tmp == NULL) {
61378527Sassar	krb5_set_error_string(context, "malloc: out of memory");
61455682Smarkm	return ENOMEM;
61578527Sassar    }
61655682Smarkm    *tgts = tmp;
61755682Smarkm    ret = krb5_copy_creds(context, tkt, &tmp[i]);
61855682Smarkm    tmp[i+1] = NULL;
61955682Smarkm    return ret;
62055682Smarkm}
62155682Smarkm
62255682Smarkm/*
62355682Smarkmget_cred(server)
62455682Smarkm	creds = cc_get_cred(server)
62555682Smarkm	if(creds) return creds
62655682Smarkm	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
62755682Smarkm	if(tgt)
62855682Smarkm		return get_cred_tgt(server, tgt)
62955682Smarkm	if(client_realm == server_realm)
63055682Smarkm		return NULL
63155682Smarkm	tgt = get_cred(krbtgt/server_realm@client_realm)
63255682Smarkm	while(tgt_inst != server_realm)
63355682Smarkm		tgt = get_cred(krbtgt/server_realm@tgt_inst)
63455682Smarkm	return get_cred_tgt(server, tgt)
63555682Smarkm	*/
63655682Smarkm
63755682Smarkmstatic krb5_error_code
63855682Smarkmget_cred_from_kdc_flags(krb5_context context,
63955682Smarkm			krb5_kdc_flags flags,
64055682Smarkm			krb5_ccache ccache,
64155682Smarkm			krb5_creds *in_creds,
64255682Smarkm			krb5_creds **out_creds,
64355682Smarkm			krb5_creds ***ret_tgts)
64455682Smarkm{
64555682Smarkm    krb5_error_code ret;
64655682Smarkm    krb5_creds *tgt, tmp_creds;
64772445Sassar    krb5_const_realm client_realm, server_realm, try_realm;
64855682Smarkm
64955682Smarkm    *out_creds = NULL;
65055682Smarkm
65155682Smarkm    client_realm = *krb5_princ_realm(context, in_creds->client);
65255682Smarkm    server_realm = *krb5_princ_realm(context, in_creds->server);
65355682Smarkm    memset(&tmp_creds, 0, sizeof(tmp_creds));
65455682Smarkm    ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
65555682Smarkm    if(ret)
65655682Smarkm	return ret;
65772445Sassar
658127808Snectar    try_realm = krb5_config_get_string(context, NULL, "capaths",
659127808Snectar				       client_realm, server_realm, NULL);
660127808Snectar
661127808Snectar#if 1
662127808Snectar    /* XXX remove in future release */
663127808Snectar    if(try_realm == NULL)
664127808Snectar	try_realm = krb5_config_get_string(context, NULL, "libdefaults",
665127808Snectar					   "capath", server_realm, NULL);
666127808Snectar#endif
667127808Snectar
66872445Sassar    if (try_realm == NULL)
66972445Sassar	try_realm = client_realm;
67072445Sassar
67155682Smarkm    ret = krb5_make_principal(context,
67255682Smarkm			      &tmp_creds.server,
67372445Sassar			      try_realm,
67455682Smarkm			      KRB5_TGS_NAME,
675127808Snectar			      server_realm,
67655682Smarkm			      NULL);
67755682Smarkm    if(ret){
67855682Smarkm	krb5_free_principal(context, tmp_creds.client);
67955682Smarkm	return ret;
68055682Smarkm    }
68155682Smarkm    {
68255682Smarkm	krb5_creds tgts;
68355682Smarkm	/* XXX try krb5_cc_retrieve_cred first? */
68455682Smarkm	ret = find_cred(context, ccache, tmp_creds.server,
68555682Smarkm			*ret_tgts, &tgts);
68655682Smarkm	if(ret == 0){
68755682Smarkm	    *out_creds = calloc(1, sizeof(**out_creds));
68878527Sassar	    if(*out_creds == NULL) {
68978527Sassar		krb5_set_error_string(context, "malloc: out of memory");
69055682Smarkm		ret = ENOMEM;
69178527Sassar	    } else {
692102644Snectar		krb5_boolean noaddr;
693102644Snectar
694102644Snectar		krb5_appdefault_boolean(context, NULL, tgts.server->realm,
695102644Snectar					"no-addresses", FALSE, &noaddr);
696102644Snectar
697102644Snectar		if (noaddr)
698102644Snectar		    ret = get_cred_kdc(context, ccache, flags, NULL,
699102644Snectar				       in_creds, &tgts, *out_creds);
700102644Snectar		else
701102644Snectar		    ret = get_cred_kdc_la(context, ccache, flags,
702102644Snectar					  in_creds, &tgts, *out_creds);
70372445Sassar		if (ret) {
70455682Smarkm		    free (*out_creds);
70572445Sassar		    *out_creds = NULL;
70672445Sassar		}
70755682Smarkm	    }
70855682Smarkm	    krb5_free_creds_contents(context, &tgts);
70955682Smarkm	    krb5_free_principal(context, tmp_creds.server);
71055682Smarkm	    krb5_free_principal(context, tmp_creds.client);
71155682Smarkm	    return ret;
71255682Smarkm	}
71355682Smarkm    }
71478527Sassar    if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
71578527Sassar	krb5_clear_error_string (context);
71655682Smarkm	return KRB5_CC_NOTFOUND;
71778527Sassar    }
71855682Smarkm    /* XXX this can loop forever */
71955682Smarkm    while(1){
72055682Smarkm	general_string tgt_inst;
72172445Sassar
72255682Smarkm	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
72355682Smarkm				      &tgt, ret_tgts);
72455682Smarkm	if(ret) {
72555682Smarkm	    krb5_free_principal(context, tmp_creds.server);
72655682Smarkm	    krb5_free_principal(context, tmp_creds.client);
72755682Smarkm	    return ret;
72855682Smarkm	}
72955682Smarkm	ret = add_cred(context, ret_tgts, tgt);
73055682Smarkm	if(ret) {
73155682Smarkm	    krb5_free_principal(context, tmp_creds.server);
73255682Smarkm	    krb5_free_principal(context, tmp_creds.client);
73355682Smarkm	    return ret;
73455682Smarkm	}
73555682Smarkm	tgt_inst = tgt->server->name.name_string.val[1];
73655682Smarkm	if(strcmp(tgt_inst, server_realm) == 0)
73755682Smarkm	    break;
73855682Smarkm	krb5_free_principal(context, tmp_creds.server);
73955682Smarkm	ret = krb5_make_principal(context, &tmp_creds.server,
74055682Smarkm				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
74155682Smarkm	if(ret) {
74255682Smarkm	    krb5_free_principal(context, tmp_creds.server);
74355682Smarkm	    krb5_free_principal(context, tmp_creds.client);
74455682Smarkm	    return ret;
74555682Smarkm	}
74655682Smarkm	ret = krb5_free_creds(context, tgt);
74755682Smarkm	if(ret) {
74855682Smarkm	    krb5_free_principal(context, tmp_creds.server);
74955682Smarkm	    krb5_free_principal(context, tmp_creds.client);
75055682Smarkm	    return ret;
75155682Smarkm	}
75255682Smarkm    }
75355682Smarkm
75455682Smarkm    krb5_free_principal(context, tmp_creds.server);
75555682Smarkm    krb5_free_principal(context, tmp_creds.client);
75655682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
75778527Sassar    if(*out_creds == NULL) {
75878527Sassar	krb5_set_error_string(context, "malloc: out of memory");
75955682Smarkm	ret = ENOMEM;
76078527Sassar    } else {
761102644Snectar	krb5_boolean noaddr;
762102644Snectar
763102644Snectar	krb5_appdefault_boolean(context, NULL, tgt->server->realm,
764102644Snectar				"no-addresses", FALSE, &noaddr);
765102644Snectar	if (noaddr)
766102644Snectar	    ret = get_cred_kdc (context, ccache, flags, NULL,
767102644Snectar				in_creds, tgt, *out_creds);
768102644Snectar	else
769102644Snectar	    ret = get_cred_kdc_la(context, ccache, flags,
770102644Snectar				  in_creds, tgt, *out_creds);
77172445Sassar	if (ret) {
77255682Smarkm	    free (*out_creds);
77372445Sassar	    *out_creds = NULL;
77472445Sassar	}
77555682Smarkm    }
77655682Smarkm    krb5_free_creds(context, tgt);
77755682Smarkm    return ret;
77855682Smarkm}
77955682Smarkm
78055682Smarkmkrb5_error_code
78178527Sassarkrb5_get_cred_from_kdc_opt(krb5_context context,
78278527Sassar			   krb5_ccache ccache,
78378527Sassar			   krb5_creds *in_creds,
78478527Sassar			   krb5_creds **out_creds,
78578527Sassar			   krb5_creds ***ret_tgts,
78678527Sassar			   krb5_flags flags)
78778527Sassar{
78878527Sassar    krb5_kdc_flags f;
78978527Sassar    f.i = flags;
79078527Sassar    return get_cred_from_kdc_flags(context, f, ccache,
79178527Sassar				   in_creds, out_creds, ret_tgts);
79278527Sassar}
79378527Sassar
79478527Sassarkrb5_error_code
79555682Smarkmkrb5_get_cred_from_kdc(krb5_context context,
79655682Smarkm		       krb5_ccache ccache,
79755682Smarkm		       krb5_creds *in_creds,
79855682Smarkm		       krb5_creds **out_creds,
79955682Smarkm		       krb5_creds ***ret_tgts)
80055682Smarkm{
80178527Sassar    return krb5_get_cred_from_kdc_opt(context, ccache,
80278527Sassar				      in_creds, out_creds, ret_tgts, 0);
80355682Smarkm}
80455682Smarkm
80555682Smarkm
80655682Smarkmkrb5_error_code
80755682Smarkmkrb5_get_credentials_with_flags(krb5_context context,
80855682Smarkm				krb5_flags options,
80955682Smarkm				krb5_kdc_flags flags,
81055682Smarkm				krb5_ccache ccache,
81155682Smarkm				krb5_creds *in_creds,
81255682Smarkm				krb5_creds **out_creds)
81355682Smarkm{
81455682Smarkm    krb5_error_code ret;
81555682Smarkm    krb5_creds **tgts;
81672445Sassar    krb5_creds *res_creds;
81755682Smarkm    int i;
81855682Smarkm
81972445Sassar    *out_creds = NULL;
82072445Sassar    res_creds = calloc(1, sizeof(*res_creds));
82178527Sassar    if (res_creds == NULL) {
82278527Sassar	krb5_set_error_string(context, "malloc: out of memory");
82355682Smarkm	return ENOMEM;
82478527Sassar    }
82555682Smarkm
82655682Smarkm    ret = krb5_cc_retrieve_cred(context,
82755682Smarkm				ccache,
82855682Smarkm				in_creds->session.keytype ?
82955682Smarkm				KRB5_TC_MATCH_KEYTYPE : 0,
83072445Sassar				in_creds, res_creds);
83172445Sassar    if(ret == 0) {
83272445Sassar	*out_creds = res_creds;
83355682Smarkm	return 0;
83472445Sassar    }
83572445Sassar    free(res_creds);
83655682Smarkm    if(ret != KRB5_CC_END)
83755682Smarkm	return ret;
83878527Sassar    if(options & KRB5_GC_CACHED) {
83978527Sassar	krb5_clear_error_string (context);
84055682Smarkm	return KRB5_CC_NOTFOUND;
84178527Sassar    }
84255682Smarkm    if(options & KRB5_GC_USER_USER)
84355682Smarkm	flags.b.enc_tkt_in_skey = 1;
84455682Smarkm    tgts = NULL;
84555682Smarkm    ret = get_cred_from_kdc_flags(context, flags, ccache,
84655682Smarkm				  in_creds, out_creds, &tgts);
84772445Sassar    for(i = 0; tgts && tgts[i]; i++) {
84855682Smarkm	krb5_cc_store_cred(context, ccache, tgts[i]);
84955682Smarkm	krb5_free_creds(context, tgts[i]);
85055682Smarkm    }
85155682Smarkm    free(tgts);
85255682Smarkm    if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
85355682Smarkm	krb5_cc_store_cred(context, ccache, *out_creds);
85455682Smarkm    return ret;
85555682Smarkm}
85655682Smarkm
85755682Smarkmkrb5_error_code
85855682Smarkmkrb5_get_credentials(krb5_context context,
85955682Smarkm		     krb5_flags options,
86055682Smarkm		     krb5_ccache ccache,
86155682Smarkm		     krb5_creds *in_creds,
86255682Smarkm		     krb5_creds **out_creds)
86355682Smarkm{
86455682Smarkm    krb5_kdc_flags flags;
86555682Smarkm    flags.i = 0;
86655682Smarkm    return krb5_get_credentials_with_flags(context, options, flags,
86755682Smarkm					   ccache, in_creds, out_creds);
86855682Smarkm}
869