155682Smarkm/*
2178825Sdfr * Copyright (c) 1997 - 2007 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
36178825SdfrRCSID("$Id: get_cred.c 21668 2007-07-22 11:28:05Z lha $");
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;
65178825Sdfr    ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
66178825Sdfr				&padata->padata_value,
67178825Sdfr				KRB5_KU_TGS_REQ_AUTH_CKSUM,
68178825Sdfr				usage
69178825Sdfr				/* KRB5_KU_TGS_REQ_AUTH */);
70178825Sdfr out:
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) {
89178825Sdfr	size_t len, buf_size;
9055682Smarkm	unsigned char *buf;
9155682Smarkm	krb5_crypto crypto;
9255682Smarkm	krb5_error_code ret;
9355682Smarkm
94178825Sdfr	ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
95178825Sdfr			   &len, ret);
96103423Snectar	if (ret)
9755682Smarkm	    return ret;
98178825Sdfr	if (buf_size != len)
99178825Sdfr	    krb5_abortx(context, "internal error in ASN.1 encoder");
10055682Smarkm
10155682Smarkm	ALLOC(req_body->enc_authorization_data, 1);
10255682Smarkm	if (req_body->enc_authorization_data == NULL) {
10355682Smarkm	    free (buf);
10478527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
10578527Sassar	    return ENOMEM;
10655682Smarkm	}
10755682Smarkm	ret = krb5_crypto_init(context, key, 0, &crypto);
10855682Smarkm	if (ret) {
10955682Smarkm	    free (buf);
11055682Smarkm	    free (req_body->enc_authorization_data);
111178825Sdfr	    req_body->enc_authorization_data = NULL;
11255682Smarkm	    return ret;
11355682Smarkm	}
11455682Smarkm	krb5_encrypt_EncryptedData(context,
11555682Smarkm				   crypto,
11655682Smarkm				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
11755682Smarkm				   /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
11855682Smarkm				   buf,
11955682Smarkm				   len,
12055682Smarkm				   0,
12155682Smarkm				   req_body->enc_authorization_data);
12255682Smarkm	free (buf);
12355682Smarkm	krb5_crypto_destroy(context, crypto);
12455682Smarkm    } else {
12555682Smarkm	req_body->enc_authorization_data = NULL;
12655682Smarkm    }
12755682Smarkm    return 0;
12855682Smarkm}
12955682Smarkm
13055682Smarkm/*
13155682Smarkm * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
13255682Smarkm * (if not-NULL), `in_creds', `krbtgt', and returning the generated
13355682Smarkm * subkey in `subkey'.
13455682Smarkm */
13555682Smarkm
13655682Smarkmstatic krb5_error_code
13755682Smarkminit_tgs_req (krb5_context context,
13855682Smarkm	      krb5_ccache ccache,
13955682Smarkm	      krb5_addresses *addresses,
14055682Smarkm	      krb5_kdc_flags flags,
14155682Smarkm	      Ticket *second_ticket,
14255682Smarkm	      krb5_creds *in_creds,
14355682Smarkm	      krb5_creds *krbtgt,
14455682Smarkm	      unsigned nonce,
145178825Sdfr	      const METHOD_DATA *padata,
14655682Smarkm	      krb5_keyblock **subkey,
14778527Sassar	      TGS_REQ *t,
14878527Sassar	      krb5_key_usage usage)
14955682Smarkm{
150103423Snectar    krb5_error_code ret = 0;
15155682Smarkm
15255682Smarkm    memset(t, 0, sizeof(*t));
15355682Smarkm    t->pvno = 5;
15455682Smarkm    t->msg_type = krb_tgs_req;
15555682Smarkm    if (in_creds->session.keytype) {
156103423Snectar	ALLOC_SEQ(&t->req_body.etype, 1);
157103423Snectar	if(t->req_body.etype.val == NULL) {
158103423Snectar	    ret = ENOMEM;
159103423Snectar	    krb5_set_error_string(context, "malloc: out of memory");
160103423Snectar	    goto fail;
161103423Snectar	}
162103423Snectar	t->req_body.etype.val[0] = in_creds->session.keytype;
16355682Smarkm    } else {
16455682Smarkm	ret = krb5_init_etype(context,
16555682Smarkm			      &t->req_body.etype.len,
16655682Smarkm			      &t->req_body.etype.val,
16755682Smarkm			      NULL);
16855682Smarkm    }
16955682Smarkm    if (ret)
17055682Smarkm	goto fail;
17155682Smarkm    t->req_body.addresses = addresses;
17255682Smarkm    t->req_body.kdc_options = flags.b;
17355682Smarkm    ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
17455682Smarkm    if (ret)
17555682Smarkm	goto fail;
17655682Smarkm    ALLOC(t->req_body.sname, 1);
17755682Smarkm    if (t->req_body.sname == NULL) {
17855682Smarkm	ret = ENOMEM;
17978527Sassar	krb5_set_error_string(context, "malloc: out of memory");
18055682Smarkm	goto fail;
18155682Smarkm    }
18272445Sassar
18372445Sassar    /* some versions of some code might require that the client be
18472445Sassar       present in TGS-REQs, but this is clearly against the spec */
18572445Sassar
18655682Smarkm    ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
18755682Smarkm    if (ret)
18855682Smarkm	goto fail;
18955682Smarkm
19055682Smarkm    /* req_body.till should be NULL if there is no endtime specified,
19155682Smarkm       but old MIT code (like DCE secd) doesn't like that */
19255682Smarkm    ALLOC(t->req_body.till, 1);
19355682Smarkm    if(t->req_body.till == NULL){
19455682Smarkm	ret = ENOMEM;
19578527Sassar	krb5_set_error_string(context, "malloc: out of memory");
19655682Smarkm	goto fail;
19755682Smarkm    }
19855682Smarkm    *t->req_body.till = in_creds->times.endtime;
19955682Smarkm
20055682Smarkm    t->req_body.nonce = nonce;
20155682Smarkm    if(second_ticket){
20255682Smarkm	ALLOC(t->req_body.additional_tickets, 1);
20355682Smarkm	if (t->req_body.additional_tickets == NULL) {
20455682Smarkm	    ret = ENOMEM;
20578527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
20655682Smarkm	    goto fail;
20755682Smarkm	}
20855682Smarkm	ALLOC_SEQ(t->req_body.additional_tickets, 1);
20955682Smarkm	if (t->req_body.additional_tickets->val == NULL) {
21055682Smarkm	    ret = ENOMEM;
21178527Sassar	    krb5_set_error_string(context, "malloc: out of memory");
21255682Smarkm	    goto fail;
21355682Smarkm	}
21455682Smarkm	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
21555682Smarkm	if (ret)
21655682Smarkm	    goto fail;
21755682Smarkm    }
21855682Smarkm    ALLOC(t->padata, 1);
21955682Smarkm    if (t->padata == NULL) {
22055682Smarkm	ret = ENOMEM;
22178527Sassar	krb5_set_error_string(context, "malloc: out of memory");
22255682Smarkm	goto fail;
22355682Smarkm    }
224178825Sdfr    ALLOC_SEQ(t->padata, 1 + padata->len);
22555682Smarkm    if (t->padata->val == NULL) {
22655682Smarkm	ret = ENOMEM;
22778527Sassar	krb5_set_error_string(context, "malloc: out of memory");
22855682Smarkm	goto fail;
22955682Smarkm    }
230178825Sdfr    {
231178825Sdfr	int i;
232178825Sdfr	for (i = 0; i < padata->len; i++) {
233178825Sdfr	    ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
234178825Sdfr	    if (ret) {
235178825Sdfr		krb5_set_error_string(context, "malloc: out of memory");
236178825Sdfr		goto fail;
237178825Sdfr	    }
238178825Sdfr	}
239178825Sdfr    }
24055682Smarkm
24155682Smarkm    {
24255682Smarkm	krb5_auth_context ac;
243127808Snectar	krb5_keyblock *key = NULL;
24455682Smarkm
24555682Smarkm	ret = krb5_auth_con_init(context, &ac);
24655682Smarkm	if(ret)
24755682Smarkm	    goto fail;
248127808Snectar
249127808Snectar	if (krb5_config_get_bool_default(context, NULL, FALSE,
250127808Snectar					 "realms",
251127808Snectar					 krbtgt->server->realm,
252127808Snectar					 "tgs_require_subkey",
253127808Snectar					 NULL))
254127808Snectar	{
255127808Snectar	    ret = krb5_generate_subkey (context, &krbtgt->session, &key);
256127808Snectar	    if (ret) {
257127808Snectar		krb5_auth_con_free (context, ac);
258127808Snectar		goto fail;
259127808Snectar	    }
260127808Snectar
261127808Snectar	    ret = krb5_auth_con_setlocalsubkey(context, ac, key);
262127808Snectar	    if (ret) {
263127808Snectar		if (key)
264127808Snectar		    krb5_free_keyblock (context, key);
265127808Snectar		krb5_auth_con_free (context, ac);
266127808Snectar		goto fail;
267127808Snectar	    }
26855682Smarkm	}
26955682Smarkm
270178825Sdfr	ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
271178825Sdfr			     key ? key : &krbtgt->session);
27255682Smarkm	if (ret) {
273127808Snectar	    if (key)
274127808Snectar		krb5_free_keyblock (context, key);
27555682Smarkm	    krb5_auth_con_free (context, ac);
27655682Smarkm	    goto fail;
27755682Smarkm	}
27855682Smarkm
27955682Smarkm	ret = make_pa_tgs_req(context,
28055682Smarkm			      ac,
28155682Smarkm			      &t->req_body,
282178825Sdfr			      &t->padata->val[0],
28378527Sassar			      krbtgt,
28478527Sassar			      usage);
28555682Smarkm	if(ret) {
286127808Snectar	    if (key)
287127808Snectar		krb5_free_keyblock (context, key);
28855682Smarkm	    krb5_auth_con_free(context, ac);
28955682Smarkm	    goto fail;
29055682Smarkm	}
29155682Smarkm	*subkey = key;
29255682Smarkm
29355682Smarkm	krb5_auth_con_free(context, ac);
29455682Smarkm    }
29555682Smarkmfail:
296127808Snectar    if (ret) {
297127808Snectar	t->req_body.addresses = NULL;
29855682Smarkm	free_TGS_REQ (t);
299127808Snectar    }
30055682Smarkm    return ret;
30155682Smarkm}
30255682Smarkm
303127808Snectarkrb5_error_code
304127808Snectar_krb5_get_krbtgt(krb5_context context,
305127808Snectar		 krb5_ccache  id,
306127808Snectar		 krb5_realm realm,
307127808Snectar		 krb5_creds **cred)
30855682Smarkm{
30955682Smarkm    krb5_error_code ret;
31055682Smarkm    krb5_creds tmp_cred;
31155682Smarkm
31255682Smarkm    memset(&tmp_cred, 0, sizeof(tmp_cred));
31355682Smarkm
314127808Snectar    ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
315127808Snectar    if (ret)
316127808Snectar	return ret;
317127808Snectar
31855682Smarkm    ret = krb5_make_principal(context,
31955682Smarkm			      &tmp_cred.server,
32055682Smarkm			      realm,
32155682Smarkm			      KRB5_TGS_NAME,
32255682Smarkm			      realm,
32355682Smarkm			      NULL);
324127808Snectar    if(ret) {
325127808Snectar	krb5_free_principal(context, tmp_cred.client);
32655682Smarkm	return ret;
327127808Snectar    }
32855682Smarkm    ret = krb5_get_credentials(context,
32955682Smarkm			       KRB5_GC_CACHED,
33055682Smarkm			       id,
33155682Smarkm			       &tmp_cred,
33255682Smarkm			       cred);
333127808Snectar    krb5_free_principal(context, tmp_cred.client);
33455682Smarkm    krb5_free_principal(context, tmp_cred.server);
33555682Smarkm    if(ret)
33655682Smarkm	return ret;
33755682Smarkm    return 0;
33855682Smarkm}
33955682Smarkm
34055682Smarkm/* DCE compatible decrypt proc */
34155682Smarkmstatic krb5_error_code
34255682Smarkmdecrypt_tkt_with_subkey (krb5_context context,
34355682Smarkm			 krb5_keyblock *key,
34455682Smarkm			 krb5_key_usage usage,
34555682Smarkm			 krb5_const_pointer subkey,
34655682Smarkm			 krb5_kdc_rep *dec_rep)
34755682Smarkm{
34855682Smarkm    krb5_error_code ret;
34955682Smarkm    krb5_data data;
35055682Smarkm    size_t size;
35155682Smarkm    krb5_crypto crypto;
35255682Smarkm
35372445Sassar    ret = krb5_crypto_init(context, key, 0, &crypto);
35472445Sassar    if (ret)
35572445Sassar	return ret;
35655682Smarkm    ret = krb5_decrypt_EncryptedData (context,
35755682Smarkm				      crypto,
35855682Smarkm				      usage,
35955682Smarkm				      &dec_rep->kdc_rep.enc_part,
36055682Smarkm				      &data);
36155682Smarkm    krb5_crypto_destroy(context, crypto);
36255682Smarkm    if(ret && subkey){
36355682Smarkm	/* DCE compat -- try to decrypt with subkey */
364178825Sdfr	ret = krb5_crypto_init(context, subkey, 0, &crypto);
36572445Sassar	if (ret)
36672445Sassar	    return ret;
36755682Smarkm	ret = krb5_decrypt_EncryptedData (context,
36855682Smarkm					  crypto,
36955682Smarkm					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
37055682Smarkm					  &dec_rep->kdc_rep.enc_part,
37155682Smarkm					  &data);
37255682Smarkm	krb5_crypto_destroy(context, crypto);
37355682Smarkm    }
37455682Smarkm    if (ret)
37555682Smarkm	return ret;
37655682Smarkm
37755682Smarkm    ret = krb5_decode_EncASRepPart(context,
37855682Smarkm				   data.data,
37955682Smarkm				   data.length,
38055682Smarkm				   &dec_rep->enc_part,
38155682Smarkm				   &size);
38255682Smarkm    if (ret)
38355682Smarkm	ret = krb5_decode_EncTGSRepPart(context,
38455682Smarkm					data.data,
38555682Smarkm					data.length,
38655682Smarkm					&dec_rep->enc_part,
38755682Smarkm					&size);
38855682Smarkm    krb5_data_free (&data);
38955682Smarkm    return ret;
39055682Smarkm}
39155682Smarkm
39255682Smarkmstatic krb5_error_code
39378527Sassarget_cred_kdc_usage(krb5_context context,
39478527Sassar		   krb5_ccache id,
39578527Sassar		   krb5_kdc_flags flags,
39678527Sassar		   krb5_addresses *addresses,
397178825Sdfr		   krb5_creds *in_creds,
39878527Sassar		   krb5_creds *krbtgt,
399178825Sdfr		   krb5_principal impersonate_principal,
400178825Sdfr		   Ticket *second_ticket,
40178527Sassar		   krb5_creds *out_creds,
40278527Sassar		   krb5_key_usage usage)
40355682Smarkm{
40455682Smarkm    TGS_REQ req;
40555682Smarkm    krb5_data enc;
40655682Smarkm    krb5_data resp;
40755682Smarkm    krb5_kdc_rep rep;
40855682Smarkm    KRB_ERROR error;
40955682Smarkm    krb5_error_code ret;
41055682Smarkm    unsigned nonce;
41155682Smarkm    krb5_keyblock *subkey = NULL;
41255682Smarkm    size_t len;
413178825Sdfr    Ticket second_ticket_data;
414178825Sdfr    METHOD_DATA padata;
41555682Smarkm
416178825Sdfr    krb5_data_zero(&resp);
417178825Sdfr    krb5_data_zero(&enc);
418178825Sdfr    padata.val = NULL;
419178825Sdfr    padata.len = 0;
420178825Sdfr
42155682Smarkm    krb5_generate_random_block(&nonce, sizeof(nonce));
42255682Smarkm    nonce &= 0xffffffff;
42355682Smarkm
424178825Sdfr    if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
42555682Smarkm	ret = decode_Ticket(in_creds->second_ticket.data,
42655682Smarkm			    in_creds->second_ticket.length,
427178825Sdfr			    &second_ticket_data, &len);
42855682Smarkm	if(ret)
42955682Smarkm	    return ret;
430178825Sdfr	second_ticket = &second_ticket_data;
43155682Smarkm    }
43255682Smarkm
433178825Sdfr
434178825Sdfr    if (impersonate_principal) {
435178825Sdfr	krb5_crypto crypto;
436178825Sdfr	PA_S4U2Self self;
437178825Sdfr	krb5_data data;
438178825Sdfr	void *buf;
439178825Sdfr	size_t size;
440178825Sdfr
441178825Sdfr	self.name = impersonate_principal->name;
442178825Sdfr	self.realm = impersonate_principal->realm;
443178825Sdfr	self.auth = estrdup("Kerberos");
444178825Sdfr
445178825Sdfr	ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
446178825Sdfr	if (ret) {
447178825Sdfr	    free(self.auth);
448178825Sdfr	    goto out;
449178825Sdfr	}
450178825Sdfr
451178825Sdfr	ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
452178825Sdfr	if (ret) {
453178825Sdfr	    free(self.auth);
454178825Sdfr	    krb5_data_free(&data);
455178825Sdfr	    goto out;
456178825Sdfr	}
457178825Sdfr
458178825Sdfr	ret = krb5_create_checksum(context,
459178825Sdfr				   crypto,
460178825Sdfr				   KRB5_KU_OTHER_CKSUM,
461178825Sdfr				   0,
462178825Sdfr				   data.data,
463178825Sdfr				   data.length,
464178825Sdfr				   &self.cksum);
465178825Sdfr	krb5_crypto_destroy(context, crypto);
466178825Sdfr	krb5_data_free(&data);
467178825Sdfr	if (ret) {
468178825Sdfr	    free(self.auth);
469178825Sdfr	    goto out;
470178825Sdfr	}
471178825Sdfr
472178825Sdfr	ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
473178825Sdfr	free(self.auth);
474178825Sdfr	free_Checksum(&self.cksum);
475178825Sdfr	if (ret)
476178825Sdfr	    goto out;
477178825Sdfr	if (len != size)
478178825Sdfr	    krb5_abortx(context, "internal asn1 error");
479178825Sdfr
480178825Sdfr	ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len);
481178825Sdfr	if (ret)
482178825Sdfr	    goto out;
483178825Sdfr    }
484178825Sdfr
48555682Smarkm    ret = init_tgs_req (context,
48655682Smarkm			id,
48755682Smarkm			addresses,
48855682Smarkm			flags,
489178825Sdfr			second_ticket,
49055682Smarkm			in_creds,
49155682Smarkm			krbtgt,
49255682Smarkm			nonce,
493178825Sdfr			&padata,
49455682Smarkm			&subkey,
49578527Sassar			&req,
49678527Sassar			usage);
49755682Smarkm    if (ret)
49855682Smarkm	goto out;
49955682Smarkm
500178825Sdfr    ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
501103423Snectar    if (ret)
50255682Smarkm	goto out;
503178825Sdfr    if(enc.length != len)
504103423Snectar	krb5_abortx(context, "internal error in ASN.1 encoder");
50555682Smarkm
50655682Smarkm    /* don't free addresses */
50755682Smarkm    req.req_body.addresses = NULL;
50855682Smarkm    free_TGS_REQ(&req);
50955682Smarkm
51055682Smarkm    /*
51155682Smarkm     * Send and receive
51255682Smarkm     */
513178825Sdfr    {
514178825Sdfr	krb5_sendto_ctx stctx;
515178825Sdfr	ret = krb5_sendto_ctx_alloc(context, &stctx);
516178825Sdfr	if (ret)
517178825Sdfr	    return ret;
518178825Sdfr	krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
51955682Smarkm
520178825Sdfr	ret = krb5_sendto_context (context, stctx, &enc,
521178825Sdfr				   krbtgt->server->name.name_string.val[1],
522178825Sdfr				   &resp);
523178825Sdfr	krb5_sendto_ctx_free(context, stctx);
524178825Sdfr    }
52555682Smarkm    if(ret)
52655682Smarkm	goto out;
52755682Smarkm
52855682Smarkm    memset(&rep, 0, sizeof(rep));
52955682Smarkm    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
53055682Smarkm	ret = krb5_copy_principal(context,
53155682Smarkm				  in_creds->client,
53255682Smarkm				  &out_creds->client);
53355682Smarkm	if(ret)
53455682Smarkm	    goto out;
53555682Smarkm	ret = krb5_copy_principal(context,
53655682Smarkm				  in_creds->server,
53755682Smarkm				  &out_creds->server);
53855682Smarkm	if(ret)
53955682Smarkm	    goto out;
54055682Smarkm	/* this should go someplace else */
54155682Smarkm	out_creds->times.endtime = in_creds->times.endtime;
54255682Smarkm
54355682Smarkm	ret = _krb5_extract_ticket(context,
54455682Smarkm				   &rep,
54555682Smarkm				   out_creds,
54655682Smarkm				   &krbtgt->session,
54755682Smarkm				   NULL,
54855682Smarkm				   KRB5_KU_TGS_REP_ENC_PART_SESSION,
54955682Smarkm				   &krbtgt->addresses,
55055682Smarkm				   nonce,
551178825Sdfr				   EXTRACT_TICKET_ALLOW_CNAME_MISMATCH|
552178825Sdfr				   EXTRACT_TICKET_ALLOW_SERVER_MISMATCH,
55355682Smarkm				   decrypt_tkt_with_subkey,
55455682Smarkm				   subkey);
55555682Smarkm	krb5_free_kdc_rep(context, &rep);
55678527Sassar    } else if(krb5_rd_error(context, &resp, &error) == 0) {
55778527Sassar	ret = krb5_error_from_rd_error(context, &error, in_creds);
55878527Sassar	krb5_free_error_contents(context, &error);
55978527Sassar    } else if(resp.data && ((char*)resp.data)[0] == 4) {
56055682Smarkm	ret = KRB5KRB_AP_ERR_V4_REPLY;
56178527Sassar	krb5_clear_error_string(context);
56278527Sassar    } else {
56355682Smarkm	ret = KRB5KRB_AP_ERR_MSG_TYPE;
56478527Sassar	krb5_clear_error_string(context);
56578527Sassar    }
566178825Sdfr
567178825Sdfrout:
568178825Sdfr    if (second_ticket == &second_ticket_data)
569178825Sdfr	free_Ticket(&second_ticket_data);
570178825Sdfr    free_METHOD_DATA(&padata);
57155682Smarkm    krb5_data_free(&resp);
572178825Sdfr    krb5_data_free(&enc);
57355682Smarkm    if(subkey){
57455682Smarkm	krb5_free_keyblock_contents(context, subkey);
57555682Smarkm	free(subkey);
57655682Smarkm    }
57755682Smarkm    return ret;
57855682Smarkm
57955682Smarkm}
58055682Smarkm
58178527Sassarstatic krb5_error_code
58278527Sassarget_cred_kdc(krb5_context context,
58378527Sassar	     krb5_ccache id,
58478527Sassar	     krb5_kdc_flags flags,
58578527Sassar	     krb5_addresses *addresses,
58678527Sassar	     krb5_creds *in_creds,
58778527Sassar	     krb5_creds *krbtgt,
588178825Sdfr	     krb5_principal impersonate_principal,
589178825Sdfr	     Ticket *second_ticket,
59078527Sassar	     krb5_creds *out_creds)
59178527Sassar{
59278527Sassar    krb5_error_code ret;
59378527Sassar
59478527Sassar    ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
595178825Sdfr			     krbtgt, impersonate_principal, second_ticket,
596178825Sdfr			     out_creds, KRB5_KU_TGS_REQ_AUTH);
59778527Sassar    if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
59878527Sassar	krb5_clear_error_string (context);
59978527Sassar	ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
600178825Sdfr				 krbtgt, impersonate_principal, second_ticket,
601178825Sdfr				 out_creds, KRB5_KU_AP_REQ_AUTH);
60278527Sassar    }
60378527Sassar    return ret;
60478527Sassar}
60578527Sassar
60655682Smarkm/* same as above, just get local addresses first */
60755682Smarkm
60855682Smarkmstatic krb5_error_code
60955682Smarkmget_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags,
61055682Smarkm		krb5_creds *in_creds, krb5_creds *krbtgt,
611178825Sdfr		krb5_principal impersonate_principal, Ticket *second_ticket,
61255682Smarkm		krb5_creds *out_creds)
61355682Smarkm{
61455682Smarkm    krb5_error_code ret;
61590926Snectar    krb5_addresses addresses, *addrs = &addresses;
61655682Smarkm
61755682Smarkm    krb5_get_all_client_addrs(context, &addresses);
61890926Snectar    /* XXX this sucks. */
61990926Snectar    if(addresses.len == 0)
62090926Snectar	addrs = NULL;
62190926Snectar    ret = get_cred_kdc(context, id, flags, addrs,
622178825Sdfr		       in_creds, krbtgt, impersonate_principal, second_ticket,
623178825Sdfr		       out_creds);
62455682Smarkm    krb5_free_addresses(context, &addresses);
62555682Smarkm    return ret;
62655682Smarkm}
62755682Smarkm
628178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
62955682Smarkmkrb5_get_kdc_cred(krb5_context context,
63055682Smarkm		  krb5_ccache id,
63155682Smarkm		  krb5_kdc_flags flags,
63255682Smarkm		  krb5_addresses *addresses,
63355682Smarkm		  Ticket  *second_ticket,
63455682Smarkm		  krb5_creds *in_creds,
63555682Smarkm		  krb5_creds **out_creds
63655682Smarkm		  )
63755682Smarkm{
63855682Smarkm    krb5_error_code ret;
63955682Smarkm    krb5_creds *krbtgt;
64078527Sassar
64155682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
64278527Sassar    if(*out_creds == NULL) {
64378527Sassar	krb5_set_error_string(context, "malloc: out of memory");
64455682Smarkm	return ENOMEM;
64578527Sassar    }
646127808Snectar    ret = _krb5_get_krbtgt (context,
647127808Snectar			    id,
648127808Snectar			    in_creds->server->realm,
649127808Snectar			    &krbtgt);
65055682Smarkm    if(ret) {
65155682Smarkm	free(*out_creds);
65255682Smarkm	return ret;
65355682Smarkm    }
65455682Smarkm    ret = get_cred_kdc(context, id, flags, addresses,
655178825Sdfr		       in_creds, krbtgt, NULL, NULL, *out_creds);
65655682Smarkm    krb5_free_creds (context, krbtgt);
65755682Smarkm    if(ret)
65855682Smarkm	free(*out_creds);
65955682Smarkm    return ret;
66055682Smarkm}
66155682Smarkm
662178825Sdfrstatic void
663178825Sdfrnot_found(krb5_context context, krb5_const_principal p)
664178825Sdfr{
665178825Sdfr    krb5_error_code ret;
666178825Sdfr    char *str;
66755682Smarkm
668178825Sdfr    ret = krb5_unparse_name(context, p, &str);
669178825Sdfr    if(ret) {
670178825Sdfr	krb5_clear_error_string(context);
671178825Sdfr	return;
672178825Sdfr    }
673178825Sdfr    krb5_set_error_string(context, "Matching credential (%s) not found", str);
674178825Sdfr    free(str);
675178825Sdfr}
676178825Sdfr
67755682Smarkmstatic krb5_error_code
67855682Smarkmfind_cred(krb5_context context,
67955682Smarkm	  krb5_ccache id,
68055682Smarkm	  krb5_principal server,
68155682Smarkm	  krb5_creds **tgts,
68255682Smarkm	  krb5_creds *out_creds)
68355682Smarkm{
68455682Smarkm    krb5_error_code ret;
68555682Smarkm    krb5_creds mcreds;
686178825Sdfr
687178825Sdfr    krb5_cc_clear_mcred(&mcreds);
68855682Smarkm    mcreds.server = server;
68955682Smarkm    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
69055682Smarkm				&mcreds, out_creds);
69155682Smarkm    if(ret == 0)
69255682Smarkm	return 0;
69355682Smarkm    while(tgts && *tgts){
69455682Smarkm	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
69555682Smarkm			      &mcreds, *tgts)){
69655682Smarkm	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
69755682Smarkm	    return ret;
69855682Smarkm	}
69955682Smarkm	tgts++;
70055682Smarkm    }
701178825Sdfr    not_found(context, server);
70255682Smarkm    return KRB5_CC_NOTFOUND;
70355682Smarkm}
70455682Smarkm
70555682Smarkmstatic krb5_error_code
70655682Smarkmadd_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
70755682Smarkm{
70855682Smarkm    int i;
70955682Smarkm    krb5_error_code ret;
71055682Smarkm    krb5_creds **tmp = *tgts;
71178527Sassar
71255682Smarkm    for(i = 0; tmp && tmp[i]; i++); /* XXX */
71355682Smarkm    tmp = realloc(tmp, (i+2)*sizeof(*tmp));
71478527Sassar    if(tmp == NULL) {
71578527Sassar	krb5_set_error_string(context, "malloc: out of memory");
71655682Smarkm	return ENOMEM;
71778527Sassar    }
71855682Smarkm    *tgts = tmp;
71955682Smarkm    ret = krb5_copy_creds(context, tkt, &tmp[i]);
72055682Smarkm    tmp[i+1] = NULL;
72155682Smarkm    return ret;
72255682Smarkm}
72355682Smarkm
72455682Smarkm/*
72555682Smarkmget_cred(server)
72655682Smarkm	creds = cc_get_cred(server)
72755682Smarkm	if(creds) return creds
72855682Smarkm	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
72955682Smarkm	if(tgt)
73055682Smarkm		return get_cred_tgt(server, tgt)
73155682Smarkm	if(client_realm == server_realm)
73255682Smarkm		return NULL
73355682Smarkm	tgt = get_cred(krbtgt/server_realm@client_realm)
73455682Smarkm	while(tgt_inst != server_realm)
73555682Smarkm		tgt = get_cred(krbtgt/server_realm@tgt_inst)
73655682Smarkm	return get_cred_tgt(server, tgt)
73755682Smarkm	*/
73855682Smarkm
73955682Smarkmstatic krb5_error_code
74055682Smarkmget_cred_from_kdc_flags(krb5_context context,
74155682Smarkm			krb5_kdc_flags flags,
74255682Smarkm			krb5_ccache ccache,
74355682Smarkm			krb5_creds *in_creds,
744178825Sdfr			krb5_principal impersonate_principal,
745178825Sdfr			Ticket *second_ticket,
74655682Smarkm			krb5_creds **out_creds,
74755682Smarkm			krb5_creds ***ret_tgts)
74855682Smarkm{
74955682Smarkm    krb5_error_code ret;
75055682Smarkm    krb5_creds *tgt, tmp_creds;
75172445Sassar    krb5_const_realm client_realm, server_realm, try_realm;
75255682Smarkm
75355682Smarkm    *out_creds = NULL;
75455682Smarkm
755178825Sdfr    client_realm = krb5_principal_get_realm(context, in_creds->client);
756178825Sdfr    server_realm = krb5_principal_get_realm(context, in_creds->server);
75755682Smarkm    memset(&tmp_creds, 0, sizeof(tmp_creds));
75855682Smarkm    ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
75955682Smarkm    if(ret)
76055682Smarkm	return ret;
76172445Sassar
762127808Snectar    try_realm = krb5_config_get_string(context, NULL, "capaths",
763127808Snectar				       client_realm, server_realm, NULL);
764127808Snectar
765127808Snectar#if 1
766127808Snectar    /* XXX remove in future release */
767127808Snectar    if(try_realm == NULL)
768127808Snectar	try_realm = krb5_config_get_string(context, NULL, "libdefaults",
769127808Snectar					   "capath", server_realm, NULL);
770127808Snectar#endif
771127808Snectar
77272445Sassar    if (try_realm == NULL)
77372445Sassar	try_realm = client_realm;
77472445Sassar
77555682Smarkm    ret = krb5_make_principal(context,
77655682Smarkm			      &tmp_creds.server,
77772445Sassar			      try_realm,
77855682Smarkm			      KRB5_TGS_NAME,
779127808Snectar			      server_realm,
78055682Smarkm			      NULL);
78155682Smarkm    if(ret){
78255682Smarkm	krb5_free_principal(context, tmp_creds.client);
78355682Smarkm	return ret;
78455682Smarkm    }
78555682Smarkm    {
78655682Smarkm	krb5_creds tgts;
78755682Smarkm	/* XXX try krb5_cc_retrieve_cred first? */
78855682Smarkm	ret = find_cred(context, ccache, tmp_creds.server,
78955682Smarkm			*ret_tgts, &tgts);
79055682Smarkm	if(ret == 0){
79155682Smarkm	    *out_creds = calloc(1, sizeof(**out_creds));
79278527Sassar	    if(*out_creds == NULL) {
79378527Sassar		krb5_set_error_string(context, "malloc: out of memory");
79455682Smarkm		ret = ENOMEM;
79578527Sassar	    } else {
796102644Snectar		krb5_boolean noaddr;
797102644Snectar
798102644Snectar		krb5_appdefault_boolean(context, NULL, tgts.server->realm,
799102644Snectar					"no-addresses", FALSE, &noaddr);
800102644Snectar
801102644Snectar		if (noaddr)
802102644Snectar		    ret = get_cred_kdc(context, ccache, flags, NULL,
803178825Sdfr				       in_creds, &tgts,
804178825Sdfr				       impersonate_principal,
805178825Sdfr				       second_ticket,
806178825Sdfr				       *out_creds);
807102644Snectar		else
808102644Snectar		    ret = get_cred_kdc_la(context, ccache, flags,
809178825Sdfr					  in_creds, &tgts,
810178825Sdfr					  impersonate_principal,
811178825Sdfr					  second_ticket,
812178825Sdfr					  *out_creds);
81372445Sassar		if (ret) {
81455682Smarkm		    free (*out_creds);
81572445Sassar		    *out_creds = NULL;
81672445Sassar		}
81755682Smarkm	    }
818178825Sdfr	    krb5_free_cred_contents(context, &tgts);
81955682Smarkm	    krb5_free_principal(context, tmp_creds.server);
82055682Smarkm	    krb5_free_principal(context, tmp_creds.client);
82155682Smarkm	    return ret;
82255682Smarkm	}
82355682Smarkm    }
82478527Sassar    if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
825178825Sdfr	not_found(context, in_creds->server);
82655682Smarkm	return KRB5_CC_NOTFOUND;
82778527Sassar    }
82855682Smarkm    /* XXX this can loop forever */
82955682Smarkm    while(1){
830178825Sdfr	heim_general_string tgt_inst;
83172445Sassar
83255682Smarkm	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
833178825Sdfr				      NULL, NULL, &tgt, ret_tgts);
83455682Smarkm	if(ret) {
83555682Smarkm	    krb5_free_principal(context, tmp_creds.server);
83655682Smarkm	    krb5_free_principal(context, tmp_creds.client);
83755682Smarkm	    return ret;
83855682Smarkm	}
83955682Smarkm	ret = add_cred(context, ret_tgts, tgt);
84055682Smarkm	if(ret) {
84155682Smarkm	    krb5_free_principal(context, tmp_creds.server);
84255682Smarkm	    krb5_free_principal(context, tmp_creds.client);
84355682Smarkm	    return ret;
84455682Smarkm	}
84555682Smarkm	tgt_inst = tgt->server->name.name_string.val[1];
84655682Smarkm	if(strcmp(tgt_inst, server_realm) == 0)
84755682Smarkm	    break;
84855682Smarkm	krb5_free_principal(context, tmp_creds.server);
84955682Smarkm	ret = krb5_make_principal(context, &tmp_creds.server,
85055682Smarkm				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
85155682Smarkm	if(ret) {
85255682Smarkm	    krb5_free_principal(context, tmp_creds.server);
85355682Smarkm	    krb5_free_principal(context, tmp_creds.client);
85455682Smarkm	    return ret;
85555682Smarkm	}
85655682Smarkm	ret = krb5_free_creds(context, tgt);
85755682Smarkm	if(ret) {
85855682Smarkm	    krb5_free_principal(context, tmp_creds.server);
85955682Smarkm	    krb5_free_principal(context, tmp_creds.client);
86055682Smarkm	    return ret;
86155682Smarkm	}
86255682Smarkm    }
86355682Smarkm
86455682Smarkm    krb5_free_principal(context, tmp_creds.server);
86555682Smarkm    krb5_free_principal(context, tmp_creds.client);
86655682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
86778527Sassar    if(*out_creds == NULL) {
86878527Sassar	krb5_set_error_string(context, "malloc: out of memory");
86955682Smarkm	ret = ENOMEM;
87078527Sassar    } else {
871102644Snectar	krb5_boolean noaddr;
872102644Snectar
873102644Snectar	krb5_appdefault_boolean(context, NULL, tgt->server->realm,
874178825Sdfr				"no-addresses", KRB5_ADDRESSLESS_DEFAULT,
875178825Sdfr				&noaddr);
876102644Snectar	if (noaddr)
877102644Snectar	    ret = get_cred_kdc (context, ccache, flags, NULL,
878178825Sdfr				in_creds, tgt, NULL, NULL,
879178825Sdfr				*out_creds);
880102644Snectar	else
881102644Snectar	    ret = get_cred_kdc_la(context, ccache, flags,
882178825Sdfr				  in_creds, tgt, NULL, NULL,
883178825Sdfr				  *out_creds);
88472445Sassar	if (ret) {
88555682Smarkm	    free (*out_creds);
88672445Sassar	    *out_creds = NULL;
88772445Sassar	}
88855682Smarkm    }
88955682Smarkm    krb5_free_creds(context, tgt);
89055682Smarkm    return ret;
89155682Smarkm}
89255682Smarkm
893178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
89478527Sassarkrb5_get_cred_from_kdc_opt(krb5_context context,
89578527Sassar			   krb5_ccache ccache,
89678527Sassar			   krb5_creds *in_creds,
89778527Sassar			   krb5_creds **out_creds,
89878527Sassar			   krb5_creds ***ret_tgts,
89978527Sassar			   krb5_flags flags)
90078527Sassar{
90178527Sassar    krb5_kdc_flags f;
90278527Sassar    f.i = flags;
90378527Sassar    return get_cred_from_kdc_flags(context, f, ccache,
904178825Sdfr				   in_creds, NULL, NULL,
905178825Sdfr				   out_creds, ret_tgts);
90678527Sassar}
90778527Sassar
908178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
90955682Smarkmkrb5_get_cred_from_kdc(krb5_context context,
91055682Smarkm		       krb5_ccache ccache,
91155682Smarkm		       krb5_creds *in_creds,
91255682Smarkm		       krb5_creds **out_creds,
91355682Smarkm		       krb5_creds ***ret_tgts)
91455682Smarkm{
91578527Sassar    return krb5_get_cred_from_kdc_opt(context, ccache,
91678527Sassar				      in_creds, out_creds, ret_tgts, 0);
91755682Smarkm}
91855682Smarkm
91955682Smarkm
920178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
92155682Smarkmkrb5_get_credentials_with_flags(krb5_context context,
92255682Smarkm				krb5_flags options,
92355682Smarkm				krb5_kdc_flags flags,
92455682Smarkm				krb5_ccache ccache,
92555682Smarkm				krb5_creds *in_creds,
92655682Smarkm				krb5_creds **out_creds)
92755682Smarkm{
92855682Smarkm    krb5_error_code ret;
92955682Smarkm    krb5_creds **tgts;
93072445Sassar    krb5_creds *res_creds;
93155682Smarkm    int i;
93255682Smarkm
93372445Sassar    *out_creds = NULL;
93472445Sassar    res_creds = calloc(1, sizeof(*res_creds));
93578527Sassar    if (res_creds == NULL) {
93678527Sassar	krb5_set_error_string(context, "malloc: out of memory");
93755682Smarkm	return ENOMEM;
93878527Sassar    }
93955682Smarkm
940178825Sdfr    if (in_creds->session.keytype)
941178825Sdfr	options |= KRB5_TC_MATCH_KEYTYPE;
942178825Sdfr
943178825Sdfr    /*
944178825Sdfr     * If we got a credential, check if credential is expired before
945178825Sdfr     * returning it.
946178825Sdfr     */
94755682Smarkm    ret = krb5_cc_retrieve_cred(context,
948178825Sdfr                                ccache,
949178825Sdfr                                in_creds->session.keytype ?
950178825Sdfr                                KRB5_TC_MATCH_KEYTYPE : 0,
951178825Sdfr                                in_creds, res_creds);
952178825Sdfr    /*
953178825Sdfr     * If we got a credential, check if credential is expired before
954178825Sdfr     * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
955178825Sdfr     */
956178825Sdfr    if (ret == 0) {
957178825Sdfr	krb5_timestamp timeret;
958178825Sdfr
959178825Sdfr	/* If expired ok, don't bother checking */
960178825Sdfr        if(options & KRB5_GC_EXPIRED_OK) {
961178825Sdfr            *out_creds = res_creds;
962178825Sdfr            return 0;
963178825Sdfr        }
964178825Sdfr
965178825Sdfr	krb5_timeofday(context, &timeret);
966178825Sdfr	if(res_creds->times.endtime > timeret) {
967178825Sdfr	    *out_creds = res_creds;
968178825Sdfr	    return 0;
969178825Sdfr	}
970178825Sdfr	if(options & KRB5_GC_CACHED)
971178825Sdfr	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
972178825Sdfr
973178825Sdfr    } else if(ret != KRB5_CC_END) {
974178825Sdfr        free(res_creds);
975178825Sdfr        return ret;
97672445Sassar    }
97772445Sassar    free(res_creds);
97878527Sassar    if(options & KRB5_GC_CACHED) {
979178825Sdfr	not_found(context, in_creds->server);
980178825Sdfr        return KRB5_CC_NOTFOUND;
98178527Sassar    }
98255682Smarkm    if(options & KRB5_GC_USER_USER)
98355682Smarkm	flags.b.enc_tkt_in_skey = 1;
984178825Sdfr    if (flags.b.enc_tkt_in_skey)
985178825Sdfr	options |= KRB5_GC_NO_STORE;
986178825Sdfr
98755682Smarkm    tgts = NULL;
98855682Smarkm    ret = get_cred_from_kdc_flags(context, flags, ccache,
989178825Sdfr				  in_creds, NULL, NULL, out_creds, &tgts);
99072445Sassar    for(i = 0; tgts && tgts[i]; i++) {
99155682Smarkm	krb5_cc_store_cred(context, ccache, tgts[i]);
99255682Smarkm	krb5_free_creds(context, tgts[i]);
99355682Smarkm    }
99455682Smarkm    free(tgts);
995178825Sdfr    if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
99655682Smarkm	krb5_cc_store_cred(context, ccache, *out_creds);
99755682Smarkm    return ret;
99855682Smarkm}
99955682Smarkm
1000178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
100155682Smarkmkrb5_get_credentials(krb5_context context,
100255682Smarkm		     krb5_flags options,
100355682Smarkm		     krb5_ccache ccache,
100455682Smarkm		     krb5_creds *in_creds,
100555682Smarkm		     krb5_creds **out_creds)
100655682Smarkm{
100755682Smarkm    krb5_kdc_flags flags;
100855682Smarkm    flags.i = 0;
100955682Smarkm    return krb5_get_credentials_with_flags(context, options, flags,
101055682Smarkm					   ccache, in_creds, out_creds);
101155682Smarkm}
1012178825Sdfr
1013178825Sdfrstruct krb5_get_creds_opt_data {
1014178825Sdfr    krb5_principal self;
1015178825Sdfr    krb5_flags options;
1016178825Sdfr    krb5_enctype enctype;
1017178825Sdfr    Ticket *ticket;
1018178825Sdfr};
1019178825Sdfr
1020178825Sdfr
1021178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1022178825Sdfrkrb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1023178825Sdfr{
1024178825Sdfr    *opt = calloc(1, sizeof(**opt));
1025178825Sdfr    if (*opt == NULL) {
1026178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1027178825Sdfr	return ENOMEM;
1028178825Sdfr    }
1029178825Sdfr    return 0;
1030178825Sdfr}
1031178825Sdfr
1032178825Sdfrvoid KRB5_LIB_FUNCTION
1033178825Sdfrkrb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1034178825Sdfr{
1035178825Sdfr    if (opt->self)
1036178825Sdfr	krb5_free_principal(context, opt->self);
1037178825Sdfr    memset(opt, 0, sizeof(*opt));
1038178825Sdfr    free(opt);
1039178825Sdfr}
1040178825Sdfr
1041178825Sdfrvoid KRB5_LIB_FUNCTION
1042178825Sdfrkrb5_get_creds_opt_set_options(krb5_context context,
1043178825Sdfr			       krb5_get_creds_opt opt,
1044178825Sdfr			       krb5_flags options)
1045178825Sdfr{
1046178825Sdfr    opt->options = options;
1047178825Sdfr}
1048178825Sdfr
1049178825Sdfrvoid KRB5_LIB_FUNCTION
1050178825Sdfrkrb5_get_creds_opt_add_options(krb5_context context,
1051178825Sdfr			       krb5_get_creds_opt opt,
1052178825Sdfr			       krb5_flags options)
1053178825Sdfr{
1054178825Sdfr    opt->options |= options;
1055178825Sdfr}
1056178825Sdfr
1057178825Sdfrvoid KRB5_LIB_FUNCTION
1058178825Sdfrkrb5_get_creds_opt_set_enctype(krb5_context context,
1059178825Sdfr			       krb5_get_creds_opt opt,
1060178825Sdfr			       krb5_enctype enctype)
1061178825Sdfr{
1062178825Sdfr    opt->enctype = enctype;
1063178825Sdfr}
1064178825Sdfr
1065178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1066178825Sdfrkrb5_get_creds_opt_set_impersonate(krb5_context context,
1067178825Sdfr				   krb5_get_creds_opt opt,
1068178825Sdfr				   krb5_const_principal self)
1069178825Sdfr{
1070178825Sdfr    if (opt->self)
1071178825Sdfr	krb5_free_principal(context, opt->self);
1072178825Sdfr    return krb5_copy_principal(context, self, &opt->self);
1073178825Sdfr}
1074178825Sdfr
1075178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1076178825Sdfrkrb5_get_creds_opt_set_ticket(krb5_context context,
1077178825Sdfr			      krb5_get_creds_opt opt,
1078178825Sdfr			      const Ticket *ticket)
1079178825Sdfr{
1080178825Sdfr    if (opt->ticket) {
1081178825Sdfr	free_Ticket(opt->ticket);
1082178825Sdfr	free(opt->ticket);
1083178825Sdfr	opt->ticket = NULL;
1084178825Sdfr    }
1085178825Sdfr    if (ticket) {
1086178825Sdfr	krb5_error_code ret;
1087178825Sdfr
1088178825Sdfr	opt->ticket = malloc(sizeof(*ticket));
1089178825Sdfr	if (opt->ticket == NULL) {
1090178825Sdfr	    krb5_set_error_string(context, "malloc: out of memory");
1091178825Sdfr	    return ENOMEM;
1092178825Sdfr	}
1093178825Sdfr	ret = copy_Ticket(ticket, opt->ticket);
1094178825Sdfr	if (ret) {
1095178825Sdfr	    free(opt->ticket);
1096178825Sdfr	    opt->ticket = NULL;
1097178825Sdfr	    krb5_set_error_string(context, "malloc: out of memory");
1098178825Sdfr	    return ret;
1099178825Sdfr	}
1100178825Sdfr    }
1101178825Sdfr    return 0;
1102178825Sdfr}
1103178825Sdfr
1104178825Sdfr
1105178825Sdfr
1106178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1107178825Sdfrkrb5_get_creds(krb5_context context,
1108178825Sdfr	       krb5_get_creds_opt opt,
1109178825Sdfr	       krb5_ccache ccache,
1110178825Sdfr	       krb5_const_principal inprinc,
1111178825Sdfr	       krb5_creds **out_creds)
1112178825Sdfr{
1113178825Sdfr    krb5_kdc_flags flags;
1114178825Sdfr    krb5_flags options;
1115178825Sdfr    krb5_creds in_creds;
1116178825Sdfr    krb5_error_code ret;
1117178825Sdfr    krb5_creds **tgts;
1118178825Sdfr    krb5_creds *res_creds;
1119178825Sdfr    int i;
1120178825Sdfr
1121178825Sdfr    memset(&in_creds, 0, sizeof(in_creds));
1122178825Sdfr    in_creds.server = rk_UNCONST(inprinc);
1123178825Sdfr
1124178825Sdfr    ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1125178825Sdfr    if (ret)
1126178825Sdfr	return ret;
1127178825Sdfr
1128178825Sdfr    options = opt->options;
1129178825Sdfr    flags.i = 0;
1130178825Sdfr
1131178825Sdfr    *out_creds = NULL;
1132178825Sdfr    res_creds = calloc(1, sizeof(*res_creds));
1133178825Sdfr    if (res_creds == NULL) {
1134178825Sdfr	krb5_free_principal(context, in_creds.client);
1135178825Sdfr	krb5_set_error_string(context, "malloc: out of memory");
1136178825Sdfr	return ENOMEM;
1137178825Sdfr    }
1138178825Sdfr
1139178825Sdfr    if (opt->enctype) {
1140178825Sdfr	in_creds.session.keytype = opt->enctype;
1141178825Sdfr	options |= KRB5_TC_MATCH_KEYTYPE;
1142178825Sdfr    }
1143178825Sdfr
1144178825Sdfr    /*
1145178825Sdfr     * If we got a credential, check if credential is expired before
1146178825Sdfr     * returning it.
1147178825Sdfr     */
1148178825Sdfr    ret = krb5_cc_retrieve_cred(context,
1149178825Sdfr                                ccache,
1150178825Sdfr				opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
1151178825Sdfr                                &in_creds, res_creds);
1152178825Sdfr    /*
1153178825Sdfr     * If we got a credential, check if credential is expired before
1154178825Sdfr     * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1155178825Sdfr     */
1156178825Sdfr    if (ret == 0) {
1157178825Sdfr	krb5_timestamp timeret;
1158178825Sdfr
1159178825Sdfr	/* If expired ok, don't bother checking */
1160178825Sdfr        if(options & KRB5_GC_EXPIRED_OK) {
1161178825Sdfr            *out_creds = res_creds;
1162178825Sdfr	    krb5_free_principal(context, in_creds.client);
1163178825Sdfr            return 0;
1164178825Sdfr        }
1165178825Sdfr
1166178825Sdfr	krb5_timeofday(context, &timeret);
1167178825Sdfr	if(res_creds->times.endtime > timeret) {
1168178825Sdfr	    *out_creds = res_creds;
1169178825Sdfr	    krb5_free_principal(context, in_creds.client);
1170178825Sdfr	    return 0;
1171178825Sdfr	}
1172178825Sdfr	if(options & KRB5_GC_CACHED)
1173178825Sdfr	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1174178825Sdfr
1175178825Sdfr    } else if(ret != KRB5_CC_END) {
1176178825Sdfr        free(res_creds);
1177178825Sdfr	krb5_free_principal(context, in_creds.client);
1178178825Sdfr        return ret;
1179178825Sdfr    }
1180178825Sdfr    free(res_creds);
1181178825Sdfr    if(options & KRB5_GC_CACHED) {
1182178825Sdfr	not_found(context, in_creds.server);
1183178825Sdfr	krb5_free_principal(context, in_creds.client);
1184178825Sdfr        return KRB5_CC_NOTFOUND;
1185178825Sdfr    }
1186178825Sdfr    if(options & KRB5_GC_USER_USER) {
1187178825Sdfr	flags.b.enc_tkt_in_skey = 1;
1188178825Sdfr	options |= KRB5_GC_NO_STORE;
1189178825Sdfr    }
1190178825Sdfr    if (options & KRB5_GC_FORWARDABLE)
1191178825Sdfr	flags.b.forwardable = 1;
1192178825Sdfr    if (options & KRB5_GC_NO_TRANSIT_CHECK)
1193178825Sdfr	flags.b.disable_transited_check = 1;
1194178825Sdfr    if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1195178825Sdfr	flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1196178825Sdfr	flags.b.constrained_delegation = 1;
1197178825Sdfr    }
1198178825Sdfr
1199178825Sdfr    tgts = NULL;
1200178825Sdfr    ret = get_cred_from_kdc_flags(context, flags, ccache,
1201178825Sdfr				  &in_creds, opt->self, opt->ticket,
1202178825Sdfr				  out_creds, &tgts);
1203178825Sdfr    krb5_free_principal(context, in_creds.client);
1204178825Sdfr    for(i = 0; tgts && tgts[i]; i++) {
1205178825Sdfr	krb5_cc_store_cred(context, ccache, tgts[i]);
1206178825Sdfr	krb5_free_creds(context, tgts[i]);
1207178825Sdfr    }
1208178825Sdfr    free(tgts);
1209178825Sdfr    if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1210178825Sdfr	krb5_cc_store_cred(context, ccache, *out_creds);
1211178825Sdfr    return ret;
1212178825Sdfr}
1213178825Sdfr
1214178825Sdfr/*
1215178825Sdfr *
1216178825Sdfr */
1217178825Sdfr
1218178825Sdfrkrb5_error_code KRB5_LIB_FUNCTION
1219178825Sdfrkrb5_get_renewed_creds(krb5_context context,
1220178825Sdfr		       krb5_creds *creds,
1221178825Sdfr		       krb5_const_principal client,
1222178825Sdfr		       krb5_ccache ccache,
1223178825Sdfr		       const char *in_tkt_service)
1224178825Sdfr{
1225178825Sdfr    krb5_error_code ret;
1226178825Sdfr    krb5_kdc_flags flags;
1227178825Sdfr    krb5_creds in, *template, *out = NULL;
1228178825Sdfr
1229178825Sdfr    memset(&in, 0, sizeof(in));
1230178825Sdfr    memset(creds, 0, sizeof(*creds));
1231178825Sdfr
1232178825Sdfr    ret = krb5_copy_principal(context, client, &in.client);
1233178825Sdfr    if (ret)
1234178825Sdfr	return ret;
1235178825Sdfr
1236178825Sdfr    if (in_tkt_service) {
1237178825Sdfr	ret = krb5_parse_name(context, in_tkt_service, &in.server);
1238178825Sdfr	if (ret) {
1239178825Sdfr	    krb5_free_principal(context, in.client);
1240178825Sdfr	    return ret;
1241178825Sdfr	}
1242178825Sdfr    } else {
1243178825Sdfr	const char *realm = krb5_principal_get_realm(context, client);
1244178825Sdfr
1245178825Sdfr	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1246178825Sdfr				  realm, NULL);
1247178825Sdfr	if (ret) {
1248178825Sdfr	    krb5_free_principal(context, in.client);
1249178825Sdfr	    return ret;
1250178825Sdfr	}
1251178825Sdfr    }
1252178825Sdfr
1253178825Sdfr    flags.i = 0;
1254178825Sdfr    flags.b.renewable = flags.b.renew = 1;
1255178825Sdfr
1256178825Sdfr    /*
1257178825Sdfr     * Get template from old credential cache for the same entry, if
1258178825Sdfr     * this failes, no worries.
1259178825Sdfr     */
1260178825Sdfr    ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1261178825Sdfr    if (ret == 0) {
1262178825Sdfr	flags.b.forwardable = template->flags.b.forwardable;
1263178825Sdfr	flags.b.proxiable = template->flags.b.proxiable;
1264178825Sdfr	krb5_free_creds (context, template);
1265178825Sdfr    }
1266178825Sdfr
1267178825Sdfr    ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1268178825Sdfr    krb5_free_principal(context, in.client);
1269178825Sdfr    krb5_free_principal(context, in.server);
1270178825Sdfr    if (ret)
1271178825Sdfr	return ret;
1272178825Sdfr
1273178825Sdfr    ret = krb5_copy_creds_contents(context, out, creds);
1274178825Sdfr    krb5_free_creds(context, out);
1275178825Sdfr
1276178825Sdfr    return ret;
1277178825Sdfr}
1278