155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
755682Smarkm *
8233294Sstas * Redistribution and use in source and binary forms, with or without
9233294Sstas * modification, are permitted provided that the following conditions
10233294Sstas * are met:
1155682Smarkm *
12233294Sstas * 1. Redistributions of source code must retain the above copyright
13233294Sstas *    notice, this list of conditions and the following disclaimer.
1455682Smarkm *
15233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
16233294Sstas *    notice, this list of conditions and the following disclaimer in the
17233294Sstas *    documentation and/or other materials provided with the distribution.
1855682Smarkm *
19233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
20233294Sstas *    may be used to endorse or promote products derived from this software
21233294Sstas *    without specific prior written permission.
22233294Sstas *
23233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33233294Sstas * SUCH DAMAGE.
3455682Smarkm */
3555682Smarkm
36233294Sstas#include "krb5_locl.h"
37233294Sstas#include <assert.h>
3855682Smarkm
39233294Sstasstatic krb5_error_code
40233294Sstasget_cred_kdc_capath(krb5_context, krb5_kdc_flags,
41233294Sstas		    krb5_ccache, krb5_creds *, krb5_principal,
42233294Sstas		    Ticket *, krb5_creds **, krb5_creds ***);
4355682Smarkm
4455682Smarkm/*
4555682Smarkm * Take the `body' and encode it into `padata' using the credentials
4655682Smarkm * in `creds'.
4755682Smarkm */
4855682Smarkm
4955682Smarkmstatic krb5_error_code
50233294Sstasmake_pa_tgs_req(krb5_context context,
5155682Smarkm		krb5_auth_context ac,
5255682Smarkm		KDC_REQ_BODY *body,
5355682Smarkm		PA_DATA *padata,
54233294Sstas		krb5_creds *creds)
5555682Smarkm{
5655682Smarkm    u_char *buf;
5755682Smarkm    size_t buf_size;
58233294Sstas    size_t len = 0;
5955682Smarkm    krb5_data in_data;
6055682Smarkm    krb5_error_code ret;
6155682Smarkm
62103423Snectar    ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
63103423Snectar    if (ret)
64103423Snectar	goto out;
65103423Snectar    if(buf_size != len)
66103423Snectar	krb5_abortx(context, "internal error in ASN.1 encoder");
6755682Smarkm
6855682Smarkm    in_data.length = len;
69103423Snectar    in_data.data   = buf;
70178825Sdfr    ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
71178825Sdfr				&padata->padata_value,
72178825Sdfr				KRB5_KU_TGS_REQ_AUTH_CKSUM,
73233294Sstas				KRB5_KU_TGS_REQ_AUTH);
74178825Sdfr out:
7555682Smarkm    free (buf);
7655682Smarkm    if(ret)
7755682Smarkm	return ret;
7872445Sassar    padata->padata_type = KRB5_PADATA_TGS_REQ;
7955682Smarkm    return 0;
8055682Smarkm}
8155682Smarkm
8255682Smarkm/*
8355682Smarkm * Set the `enc-authorization-data' in `req_body' based on `authdata'
8455682Smarkm */
8555682Smarkm
8655682Smarkmstatic krb5_error_code
8755682Smarkmset_auth_data (krb5_context context,
8855682Smarkm	       KDC_REQ_BODY *req_body,
8955682Smarkm	       krb5_authdata *authdata,
90233294Sstas	       krb5_keyblock *subkey)
9155682Smarkm{
9255682Smarkm    if(authdata->len) {
93233294Sstas	size_t len = 0, buf_size;
9455682Smarkm	unsigned char *buf;
9555682Smarkm	krb5_crypto crypto;
9655682Smarkm	krb5_error_code ret;
9755682Smarkm
98178825Sdfr	ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
99178825Sdfr			   &len, ret);
100103423Snectar	if (ret)
10155682Smarkm	    return ret;
102178825Sdfr	if (buf_size != len)
103178825Sdfr	    krb5_abortx(context, "internal error in ASN.1 encoder");
10455682Smarkm
10555682Smarkm	ALLOC(req_body->enc_authorization_data, 1);
10655682Smarkm	if (req_body->enc_authorization_data == NULL) {
10755682Smarkm	    free (buf);
108233294Sstas	    krb5_set_error_message(context, ENOMEM,
109233294Sstas				   N_("malloc: out of memory", ""));
11078527Sassar	    return ENOMEM;
11155682Smarkm	}
112233294Sstas	ret = krb5_crypto_init(context, subkey, 0, &crypto);
11355682Smarkm	if (ret) {
11455682Smarkm	    free (buf);
11555682Smarkm	    free (req_body->enc_authorization_data);
116178825Sdfr	    req_body->enc_authorization_data = NULL;
11755682Smarkm	    return ret;
11855682Smarkm	}
119233294Sstas	krb5_encrypt_EncryptedData(context,
12055682Smarkm				   crypto,
121233294Sstas				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
12255682Smarkm				   buf,
12355682Smarkm				   len,
12455682Smarkm				   0,
12555682Smarkm				   req_body->enc_authorization_data);
12655682Smarkm	free (buf);
12755682Smarkm	krb5_crypto_destroy(context, crypto);
12855682Smarkm    } else {
12955682Smarkm	req_body->enc_authorization_data = NULL;
13055682Smarkm    }
13155682Smarkm    return 0;
132233294Sstas}
13355682Smarkm
13455682Smarkm/*
13555682Smarkm * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
13655682Smarkm * (if not-NULL), `in_creds', `krbtgt', and returning the generated
13755682Smarkm * subkey in `subkey'.
13855682Smarkm */
13955682Smarkm
14055682Smarkmstatic krb5_error_code
14155682Smarkminit_tgs_req (krb5_context context,
14255682Smarkm	      krb5_ccache ccache,
14355682Smarkm	      krb5_addresses *addresses,
14455682Smarkm	      krb5_kdc_flags flags,
14555682Smarkm	      Ticket *second_ticket,
14655682Smarkm	      krb5_creds *in_creds,
14755682Smarkm	      krb5_creds *krbtgt,
14855682Smarkm	      unsigned nonce,
149178825Sdfr	      const METHOD_DATA *padata,
15055682Smarkm	      krb5_keyblock **subkey,
151233294Sstas	      TGS_REQ *t)
15255682Smarkm{
153233294Sstas    krb5_auth_context ac = NULL;
154103423Snectar    krb5_error_code ret = 0;
15555682Smarkm
15655682Smarkm    memset(t, 0, sizeof(*t));
15755682Smarkm    t->pvno = 5;
15855682Smarkm    t->msg_type = krb_tgs_req;
15955682Smarkm    if (in_creds->session.keytype) {
160103423Snectar	ALLOC_SEQ(&t->req_body.etype, 1);
161103423Snectar	if(t->req_body.etype.val == NULL) {
162103423Snectar	    ret = ENOMEM;
163233294Sstas	    krb5_set_error_message(context, ret,
164233294Sstas				   N_("malloc: out of memory", ""));
165103423Snectar	    goto fail;
166103423Snectar	}
167103423Snectar	t->req_body.etype.val[0] = in_creds->session.keytype;
16855682Smarkm    } else {
169233294Sstas	ret = _krb5_init_etype(context,
170233294Sstas			       KRB5_PDU_TGS_REQUEST,
171233294Sstas			       &t->req_body.etype.len,
172233294Sstas			       &t->req_body.etype.val,
173233294Sstas			       NULL);
17455682Smarkm    }
17555682Smarkm    if (ret)
17655682Smarkm	goto fail;
17755682Smarkm    t->req_body.addresses = addresses;
17855682Smarkm    t->req_body.kdc_options = flags.b;
17955682Smarkm    ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
18055682Smarkm    if (ret)
18155682Smarkm	goto fail;
18255682Smarkm    ALLOC(t->req_body.sname, 1);
18355682Smarkm    if (t->req_body.sname == NULL) {
18455682Smarkm	ret = ENOMEM;
185233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
18655682Smarkm	goto fail;
18755682Smarkm    }
18872445Sassar
18972445Sassar    /* some versions of some code might require that the client be
19072445Sassar       present in TGS-REQs, but this is clearly against the spec */
19172445Sassar
19255682Smarkm    ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
19355682Smarkm    if (ret)
19455682Smarkm	goto fail;
19555682Smarkm
19655682Smarkm    /* req_body.till should be NULL if there is no endtime specified,
19755682Smarkm       but old MIT code (like DCE secd) doesn't like that */
19855682Smarkm    ALLOC(t->req_body.till, 1);
19955682Smarkm    if(t->req_body.till == NULL){
20055682Smarkm	ret = ENOMEM;
201233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
20255682Smarkm	goto fail;
20355682Smarkm    }
20455682Smarkm    *t->req_body.till = in_creds->times.endtime;
205233294Sstas
20655682Smarkm    t->req_body.nonce = nonce;
20755682Smarkm    if(second_ticket){
20855682Smarkm	ALLOC(t->req_body.additional_tickets, 1);
20955682Smarkm	if (t->req_body.additional_tickets == NULL) {
21055682Smarkm	    ret = ENOMEM;
211233294Sstas	    krb5_set_error_message(context, ret,
212233294Sstas				   N_("malloc: out of memory", ""));
21355682Smarkm	    goto fail;
21455682Smarkm	}
21555682Smarkm	ALLOC_SEQ(t->req_body.additional_tickets, 1);
21655682Smarkm	if (t->req_body.additional_tickets->val == NULL) {
21755682Smarkm	    ret = ENOMEM;
218233294Sstas	    krb5_set_error_message(context, ret,
219233294Sstas				   N_("malloc: out of memory", ""));
22055682Smarkm	    goto fail;
22155682Smarkm	}
222233294Sstas	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
22355682Smarkm	if (ret)
22455682Smarkm	    goto fail;
22555682Smarkm    }
22655682Smarkm    ALLOC(t->padata, 1);
22755682Smarkm    if (t->padata == NULL) {
22855682Smarkm	ret = ENOMEM;
229233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
23055682Smarkm	goto fail;
23155682Smarkm    }
232178825Sdfr    ALLOC_SEQ(t->padata, 1 + padata->len);
23355682Smarkm    if (t->padata->val == NULL) {
23455682Smarkm	ret = ENOMEM;
235233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
23655682Smarkm	goto fail;
23755682Smarkm    }
238178825Sdfr    {
239233294Sstas	size_t i;
240178825Sdfr	for (i = 0; i < padata->len; i++) {
241178825Sdfr	    ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
242178825Sdfr	    if (ret) {
243233294Sstas		krb5_set_error_message(context, ret,
244233294Sstas				       N_("malloc: out of memory", ""));
245178825Sdfr		goto fail;
246178825Sdfr	    }
247178825Sdfr	}
248178825Sdfr    }
24955682Smarkm
250233294Sstas    ret = krb5_auth_con_init(context, &ac);
251233294Sstas    if(ret)
252233294Sstas	goto fail;
25355682Smarkm
254233294Sstas    ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
255233294Sstas    if (ret)
256233294Sstas	goto fail;
257127808Snectar
258233294Sstas    ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
259233294Sstas			 ac->local_subkey);
260233294Sstas    if (ret)
261233294Sstas	goto fail;
262127808Snectar
263233294Sstas    ret = make_pa_tgs_req(context,
264233294Sstas			  ac,
265233294Sstas			  &t->req_body,
266233294Sstas			  &t->padata->val[0],
267233294Sstas			  krbtgt);
268233294Sstas    if(ret)
269233294Sstas	goto fail;
27055682Smarkm
271233294Sstas    ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
272233294Sstas    if (ret)
273233294Sstas	goto fail;
27455682Smarkm
275233294Sstasfail:
276233294Sstas    if (ac)
27755682Smarkm	krb5_auth_con_free(context, ac);
278127808Snectar    if (ret) {
279127808Snectar	t->req_body.addresses = NULL;
28055682Smarkm	free_TGS_REQ (t);
281127808Snectar    }
28255682Smarkm    return ret;
28355682Smarkm}
28455682Smarkm
285127808Snectarkrb5_error_code
286127808Snectar_krb5_get_krbtgt(krb5_context context,
287127808Snectar		 krb5_ccache  id,
288127808Snectar		 krb5_realm realm,
289127808Snectar		 krb5_creds **cred)
29055682Smarkm{
29155682Smarkm    krb5_error_code ret;
29255682Smarkm    krb5_creds tmp_cred;
29355682Smarkm
29455682Smarkm    memset(&tmp_cred, 0, sizeof(tmp_cred));
29555682Smarkm
296127808Snectar    ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
297127808Snectar    if (ret)
298127808Snectar	return ret;
299127808Snectar
300233294Sstas    ret = krb5_make_principal(context,
30155682Smarkm			      &tmp_cred.server,
30255682Smarkm			      realm,
30355682Smarkm			      KRB5_TGS_NAME,
30455682Smarkm			      realm,
30555682Smarkm			      NULL);
306127808Snectar    if(ret) {
307127808Snectar	krb5_free_principal(context, tmp_cred.client);
30855682Smarkm	return ret;
309127808Snectar    }
31055682Smarkm    ret = krb5_get_credentials(context,
31155682Smarkm			       KRB5_GC_CACHED,
31255682Smarkm			       id,
31355682Smarkm			       &tmp_cred,
31455682Smarkm			       cred);
315127808Snectar    krb5_free_principal(context, tmp_cred.client);
31655682Smarkm    krb5_free_principal(context, tmp_cred.server);
31755682Smarkm    if(ret)
31855682Smarkm	return ret;
31955682Smarkm    return 0;
32055682Smarkm}
32155682Smarkm
32255682Smarkm/* DCE compatible decrypt proc */
323233294Sstasstatic krb5_error_code KRB5_CALLCONV
32455682Smarkmdecrypt_tkt_with_subkey (krb5_context context,
32555682Smarkm			 krb5_keyblock *key,
32655682Smarkm			 krb5_key_usage usage,
327233294Sstas			 krb5_const_pointer skey,
32855682Smarkm			 krb5_kdc_rep *dec_rep)
32955682Smarkm{
330233294Sstas    const krb5_keyblock *subkey = skey;
331233294Sstas    krb5_error_code ret = 0;
33255682Smarkm    krb5_data data;
33355682Smarkm    size_t size;
33455682Smarkm    krb5_crypto crypto;
335233294Sstas
336233294Sstas    assert(usage == 0);
337233294Sstas
338233294Sstas    krb5_data_zero(&data);
339233294Sstas
340233294Sstas    /*
341233294Sstas     * start out with trying with subkey if we have one
342233294Sstas     */
343233294Sstas    if (subkey) {
344178825Sdfr	ret = krb5_crypto_init(context, subkey, 0, &crypto);
34572445Sassar	if (ret)
34672445Sassar	    return ret;
34755682Smarkm	ret = krb5_decrypt_EncryptedData (context,
34855682Smarkm					  crypto,
34955682Smarkm					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
35055682Smarkm					  &dec_rep->kdc_rep.enc_part,
35155682Smarkm					  &data);
352233294Sstas	/*
353233294Sstas	 * If the is Windows 2000 DC, we need to retry with key usage
354233294Sstas	 * 8 when doing ARCFOUR.
355233294Sstas	 */
356233294Sstas	if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
357233294Sstas	    ret = krb5_decrypt_EncryptedData(context,
358233294Sstas					     crypto,
359233294Sstas					     8,
360233294Sstas					     &dec_rep->kdc_rep.enc_part,
361233294Sstas					     &data);
362233294Sstas	}
36355682Smarkm	krb5_crypto_destroy(context, crypto);
36455682Smarkm    }
365233294Sstas    if (subkey == NULL || ret) {
366233294Sstas	ret = krb5_crypto_init(context, key, 0, &crypto);
367233294Sstas	if (ret)
368233294Sstas	    return ret;
369233294Sstas	ret = krb5_decrypt_EncryptedData (context,
370233294Sstas					  crypto,
371233294Sstas					  KRB5_KU_TGS_REP_ENC_PART_SESSION,
372233294Sstas					  &dec_rep->kdc_rep.enc_part,
373233294Sstas					  &data);
374233294Sstas	krb5_crypto_destroy(context, crypto);
375233294Sstas    }
37655682Smarkm    if (ret)
37755682Smarkm	return ret;
378233294Sstas
379233294Sstas    ret = decode_EncASRepPart(data.data,
380233294Sstas			      data.length,
381233294Sstas			      &dec_rep->enc_part,
382233294Sstas			      &size);
383233294Sstas    if (ret)
384233294Sstas	ret = decode_EncTGSRepPart(data.data,
38555682Smarkm				   data.length,
386233294Sstas				   &dec_rep->enc_part,
38755682Smarkm				   &size);
38855682Smarkm    if (ret)
389233294Sstas      krb5_set_error_message(context, ret,
390233294Sstas			     N_("Failed to decode encpart in ticket", ""));
39155682Smarkm    krb5_data_free (&data);
39255682Smarkm    return ret;
39355682Smarkm}
39455682Smarkm
39555682Smarkmstatic krb5_error_code
396233294Sstasget_cred_kdc(krb5_context context,
397233294Sstas	     krb5_ccache id,
398233294Sstas	     krb5_kdc_flags flags,
399233294Sstas	     krb5_addresses *addresses,
400233294Sstas	     krb5_creds *in_creds,
401233294Sstas	     krb5_creds *krbtgt,
402233294Sstas	     krb5_principal impersonate_principal,
403233294Sstas	     Ticket *second_ticket,
404233294Sstas	     krb5_creds *out_creds)
40555682Smarkm{
40655682Smarkm    TGS_REQ req;
40755682Smarkm    krb5_data enc;
40855682Smarkm    krb5_data resp;
40955682Smarkm    krb5_kdc_rep rep;
41055682Smarkm    KRB_ERROR error;
41155682Smarkm    krb5_error_code ret;
41255682Smarkm    unsigned nonce;
41355682Smarkm    krb5_keyblock *subkey = NULL;
414233294Sstas    size_t len = 0;
415178825Sdfr    Ticket second_ticket_data;
416178825Sdfr    METHOD_DATA padata;
417233294Sstas
418178825Sdfr    krb5_data_zero(&resp);
419178825Sdfr    krb5_data_zero(&enc);
420178825Sdfr    padata.val = NULL;
421178825Sdfr    padata.len = 0;
422178825Sdfr
42355682Smarkm    krb5_generate_random_block(&nonce, sizeof(nonce));
42455682Smarkm    nonce &= 0xffffffff;
425233294Sstas
426178825Sdfr    if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
427233294Sstas	ret = decode_Ticket(in_creds->second_ticket.data,
428233294Sstas			    in_creds->second_ticket.length,
429178825Sdfr			    &second_ticket_data, &len);
43055682Smarkm	if(ret)
43155682Smarkm	    return ret;
432178825Sdfr	second_ticket = &second_ticket_data;
43355682Smarkm    }
43455682Smarkm
435178825Sdfr
436178825Sdfr    if (impersonate_principal) {
437178825Sdfr	krb5_crypto crypto;
438178825Sdfr	PA_S4U2Self self;
439178825Sdfr	krb5_data data;
440178825Sdfr	void *buf;
441233294Sstas	size_t size = 0;
442178825Sdfr
443178825Sdfr	self.name = impersonate_principal->name;
444178825Sdfr	self.realm = impersonate_principal->realm;
445178825Sdfr	self.auth = estrdup("Kerberos");
446233294Sstas
447178825Sdfr	ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
448178825Sdfr	if (ret) {
449178825Sdfr	    free(self.auth);
450178825Sdfr	    goto out;
451178825Sdfr	}
452178825Sdfr
453178825Sdfr	ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
454178825Sdfr	if (ret) {
455178825Sdfr	    free(self.auth);
456178825Sdfr	    krb5_data_free(&data);
457178825Sdfr	    goto out;
458178825Sdfr	}
459178825Sdfr
460178825Sdfr	ret = krb5_create_checksum(context,
461178825Sdfr				   crypto,
462178825Sdfr				   KRB5_KU_OTHER_CKSUM,
463178825Sdfr				   0,
464178825Sdfr				   data.data,
465233294Sstas				   data.length,
466178825Sdfr				   &self.cksum);
467178825Sdfr	krb5_crypto_destroy(context, crypto);
468178825Sdfr	krb5_data_free(&data);
469178825Sdfr	if (ret) {
470178825Sdfr	    free(self.auth);
471178825Sdfr	    goto out;
472178825Sdfr	}
473178825Sdfr
474178825Sdfr	ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
475178825Sdfr	free(self.auth);
476178825Sdfr	free_Checksum(&self.cksum);
477178825Sdfr	if (ret)
478178825Sdfr	    goto out;
479178825Sdfr	if (len != size)
480178825Sdfr	    krb5_abortx(context, "internal asn1 error");
481233294Sstas
482233294Sstas	ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
483178825Sdfr	if (ret)
484178825Sdfr	    goto out;
485178825Sdfr    }
486178825Sdfr
48755682Smarkm    ret = init_tgs_req (context,
48855682Smarkm			id,
48955682Smarkm			addresses,
49055682Smarkm			flags,
491178825Sdfr			second_ticket,
49255682Smarkm			in_creds,
49355682Smarkm			krbtgt,
49455682Smarkm			nonce,
495178825Sdfr			&padata,
496233294Sstas			&subkey,
497233294Sstas			&req);
49855682Smarkm    if (ret)
49955682Smarkm	goto out;
50055682Smarkm
501178825Sdfr    ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
502233294Sstas    if (ret)
50355682Smarkm	goto out;
504178825Sdfr    if(enc.length != len)
505103423Snectar	krb5_abortx(context, "internal error in ASN.1 encoder");
50655682Smarkm
50755682Smarkm    /* don't free addresses */
50855682Smarkm    req.req_body.addresses = NULL;
50955682Smarkm    free_TGS_REQ(&req);
51055682Smarkm
51155682Smarkm    /*
51255682Smarkm     * Send and receive
51355682Smarkm     */
514178825Sdfr    {
515178825Sdfr	krb5_sendto_ctx stctx;
516178825Sdfr	ret = krb5_sendto_ctx_alloc(context, &stctx);
517178825Sdfr	if (ret)
518178825Sdfr	    return ret;
519178825Sdfr	krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
52055682Smarkm
521178825Sdfr	ret = krb5_sendto_context (context, stctx, &enc,
522178825Sdfr				   krbtgt->server->name.name_string.val[1],
523178825Sdfr				   &resp);
524178825Sdfr	krb5_sendto_ctx_free(context, stctx);
525178825Sdfr    }
52655682Smarkm    if(ret)
52755682Smarkm	goto out;
52855682Smarkm
52955682Smarkm    memset(&rep, 0, sizeof(rep));
530233294Sstas    if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
531233294Sstas	unsigned eflags = 0;
532233294Sstas
533233294Sstas	ret = krb5_copy_principal(context,
534233294Sstas				  in_creds->client,
53555682Smarkm				  &out_creds->client);
53655682Smarkm	if(ret)
537233294Sstas	    goto out2;
538233294Sstas	ret = krb5_copy_principal(context,
539233294Sstas				  in_creds->server,
54055682Smarkm				  &out_creds->server);
54155682Smarkm	if(ret)
542233294Sstas	    goto out2;
54355682Smarkm	/* this should go someplace else */
54455682Smarkm	out_creds->times.endtime = in_creds->times.endtime;
54555682Smarkm
546233294Sstas	/* XXX should do better testing */
547233294Sstas	if (flags.b.constrained_delegation || impersonate_principal)
548233294Sstas	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
549233294Sstas
55055682Smarkm	ret = _krb5_extract_ticket(context,
55155682Smarkm				   &rep,
55255682Smarkm				   out_creds,
55355682Smarkm				   &krbtgt->session,
55455682Smarkm				   NULL,
555233294Sstas				   0,
55655682Smarkm				   &krbtgt->addresses,
55755682Smarkm				   nonce,
558233294Sstas				   eflags,
55955682Smarkm				   decrypt_tkt_with_subkey,
56055682Smarkm				   subkey);
561233294Sstas    out2:
56255682Smarkm	krb5_free_kdc_rep(context, &rep);
56378527Sassar    } else if(krb5_rd_error(context, &resp, &error) == 0) {
56478527Sassar	ret = krb5_error_from_rd_error(context, &error, in_creds);
56578527Sassar	krb5_free_error_contents(context, &error);
566233294Sstas    } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
56755682Smarkm	ret = KRB5KRB_AP_ERR_V4_REPLY;
568233294Sstas	krb5_clear_error_message(context);
56978527Sassar    } else {
57055682Smarkm	ret = KRB5KRB_AP_ERR_MSG_TYPE;
571233294Sstas	krb5_clear_error_message(context);
57278527Sassar    }
573178825Sdfr
574178825Sdfrout:
575178825Sdfr    if (second_ticket == &second_ticket_data)
576178825Sdfr	free_Ticket(&second_ticket_data);
577178825Sdfr    free_METHOD_DATA(&padata);
57855682Smarkm    krb5_data_free(&resp);
579178825Sdfr    krb5_data_free(&enc);
580233294Sstas    if(subkey)
581233294Sstas	krb5_free_keyblock(context, subkey);
58255682Smarkm    return ret;
583233294Sstas
58455682Smarkm}
58555682Smarkm
586233294Sstas/*
587233294Sstas * same as above, just get local addresses first if the krbtgt have
588233294Sstas * them and the realm is not addressless
589233294Sstas */
590233294Sstas
59178527Sassarstatic krb5_error_code
592233294Sstasget_cred_kdc_address(krb5_context context,
593233294Sstas		     krb5_ccache id,
594233294Sstas		     krb5_kdc_flags flags,
595233294Sstas		     krb5_addresses *addrs,
596233294Sstas		     krb5_creds *in_creds,
597233294Sstas		     krb5_creds *krbtgt,
598233294Sstas		     krb5_principal impersonate_principal,
599233294Sstas		     Ticket *second_ticket,
600233294Sstas		     krb5_creds *out_creds)
60178527Sassar{
60278527Sassar    krb5_error_code ret;
603233294Sstas    krb5_addresses addresses = { 0, NULL };
60478527Sassar
605233294Sstas    /*
606233294Sstas     * Inherit the address-ness of the krbtgt if the address is not
607233294Sstas     * specified.
608233294Sstas     */
60978527Sassar
610233294Sstas    if (addrs == NULL && krbtgt->addresses.len != 0) {
611233294Sstas	krb5_boolean noaddr;
61255682Smarkm
613233294Sstas	krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
614233294Sstas				"no-addresses", FALSE, &noaddr);
615233294Sstas
616233294Sstas	if (!noaddr) {
617233294Sstas	    krb5_get_all_client_addrs(context, &addresses);
618233294Sstas	    /* XXX this sucks. */
619233294Sstas	    addrs = &addresses;
620233294Sstas	    if(addresses.len == 0)
621233294Sstas		addrs = NULL;
622233294Sstas	}
623233294Sstas    }
624233294Sstas    ret = get_cred_kdc(context, id, flags, addrs, in_creds,
625233294Sstas		       krbtgt, impersonate_principal,
626233294Sstas		       second_ticket, out_creds);
62755682Smarkm    krb5_free_addresses(context, &addresses);
62855682Smarkm    return ret;
62955682Smarkm}
63055682Smarkm
631233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
63255682Smarkmkrb5_get_kdc_cred(krb5_context context,
63355682Smarkm		  krb5_ccache id,
63455682Smarkm		  krb5_kdc_flags flags,
63555682Smarkm		  krb5_addresses *addresses,
63655682Smarkm		  Ticket  *second_ticket,
63755682Smarkm		  krb5_creds *in_creds,
63855682Smarkm		  krb5_creds **out_creds
63955682Smarkm		  )
64055682Smarkm{
64155682Smarkm    krb5_error_code ret;
64255682Smarkm    krb5_creds *krbtgt;
64378527Sassar
64455682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
64578527Sassar    if(*out_creds == NULL) {
646233294Sstas	krb5_set_error_message(context, ENOMEM,
647233294Sstas			       N_("malloc: out of memory", ""));
64855682Smarkm	return ENOMEM;
64978527Sassar    }
650127808Snectar    ret = _krb5_get_krbtgt (context,
651127808Snectar			    id,
652127808Snectar			    in_creds->server->realm,
653127808Snectar			    &krbtgt);
65455682Smarkm    if(ret) {
65555682Smarkm	free(*out_creds);
656233294Sstas	*out_creds = NULL;
65755682Smarkm	return ret;
65855682Smarkm    }
659233294Sstas    ret = get_cred_kdc(context, id, flags, addresses,
660178825Sdfr		       in_creds, krbtgt, NULL, NULL, *out_creds);
66155682Smarkm    krb5_free_creds (context, krbtgt);
662233294Sstas    if(ret) {
66355682Smarkm	free(*out_creds);
664233294Sstas	*out_creds = NULL;
665233294Sstas    }
66655682Smarkm    return ret;
66755682Smarkm}
66855682Smarkm
669233294Sstasstatic int
670233294Sstasnot_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
671178825Sdfr{
672178825Sdfr    krb5_error_code ret;
673178825Sdfr    char *str;
67455682Smarkm
675178825Sdfr    ret = krb5_unparse_name(context, p, &str);
676178825Sdfr    if(ret) {
677233294Sstas	krb5_clear_error_message(context);
678233294Sstas	return code;
679178825Sdfr    }
680233294Sstas    krb5_set_error_message(context, code,
681233294Sstas			   N_("Matching credential (%s) not found", ""), str);
682178825Sdfr    free(str);
683233294Sstas    return code;
684178825Sdfr}
685178825Sdfr
68655682Smarkmstatic krb5_error_code
68755682Smarkmfind_cred(krb5_context context,
68855682Smarkm	  krb5_ccache id,
68955682Smarkm	  krb5_principal server,
69055682Smarkm	  krb5_creds **tgts,
69155682Smarkm	  krb5_creds *out_creds)
69255682Smarkm{
69355682Smarkm    krb5_error_code ret;
69455682Smarkm    krb5_creds mcreds;
695178825Sdfr
696178825Sdfr    krb5_cc_clear_mcred(&mcreds);
69755682Smarkm    mcreds.server = server;
698233294Sstas    ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
69955682Smarkm				&mcreds, out_creds);
70055682Smarkm    if(ret == 0)
70155682Smarkm	return 0;
70255682Smarkm    while(tgts && *tgts){
703233294Sstas	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
70455682Smarkm			      &mcreds, *tgts)){
70555682Smarkm	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
70655682Smarkm	    return ret;
70755682Smarkm	}
70855682Smarkm	tgts++;
70955682Smarkm    }
710233294Sstas    return not_found(context, server, KRB5_CC_NOTFOUND);
71155682Smarkm}
71255682Smarkm
71355682Smarkmstatic krb5_error_code
714233294Sstasadd_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
71555682Smarkm{
71655682Smarkm    int i;
71755682Smarkm    krb5_error_code ret;
71855682Smarkm    krb5_creds **tmp = *tgts;
71978527Sassar
72055682Smarkm    for(i = 0; tmp && tmp[i]; i++); /* XXX */
72155682Smarkm    tmp = realloc(tmp, (i+2)*sizeof(*tmp));
72278527Sassar    if(tmp == NULL) {
723233294Sstas	krb5_set_error_message(context, ENOMEM,
724233294Sstas			       N_("malloc: out of memory", ""));
72555682Smarkm	return ENOMEM;
72678527Sassar    }
72755682Smarkm    *tgts = tmp;
72855682Smarkm    ret = krb5_copy_creds(context, tkt, &tmp[i]);
72955682Smarkm    tmp[i+1] = NULL;
73055682Smarkm    return ret;
73155682Smarkm}
73255682Smarkm
73355682Smarkmstatic krb5_error_code
734233294Sstasget_cred_kdc_capath_worker(krb5_context context,
735233294Sstas                           krb5_kdc_flags flags,
736233294Sstas                           krb5_ccache ccache,
737233294Sstas                           krb5_creds *in_creds,
738233294Sstas                           krb5_const_realm try_realm,
739233294Sstas                           krb5_principal impersonate_principal,
740233294Sstas                           Ticket *second_ticket,
741233294Sstas                           krb5_creds **out_creds,
742233294Sstas                           krb5_creds ***ret_tgts)
74355682Smarkm{
74455682Smarkm    krb5_error_code ret;
74555682Smarkm    krb5_creds *tgt, tmp_creds;
746233294Sstas    krb5_const_realm client_realm, server_realm;
747233294Sstas    int ok_as_delegate = 1;
74855682Smarkm
74955682Smarkm    *out_creds = NULL;
75055682Smarkm
751178825Sdfr    client_realm = krb5_principal_get_realm(context, in_creds->client);
752178825Sdfr    server_realm = krb5_principal_get_realm(context, in_creds->server);
75355682Smarkm    memset(&tmp_creds, 0, sizeof(tmp_creds));
75455682Smarkm    ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
75555682Smarkm    if(ret)
75655682Smarkm	return ret;
75772445Sassar
75855682Smarkm    ret = krb5_make_principal(context,
75955682Smarkm			      &tmp_creds.server,
76072445Sassar			      try_realm,
76155682Smarkm			      KRB5_TGS_NAME,
762233294Sstas			      server_realm,
76355682Smarkm			      NULL);
76455682Smarkm    if(ret){
76555682Smarkm	krb5_free_principal(context, tmp_creds.client);
76655682Smarkm	return ret;
76755682Smarkm    }
76855682Smarkm    {
76955682Smarkm	krb5_creds tgts;
770233294Sstas
771233294Sstas	ret = find_cred(context, ccache, tmp_creds.server,
77255682Smarkm			*ret_tgts, &tgts);
77355682Smarkm	if(ret == 0){
774233294Sstas	    /* only allow implicit ok_as_delegate if the realm is the clients realm */
775233294Sstas	    if (strcmp(try_realm, client_realm) != 0 || strcmp(try_realm, server_realm) != 0)
776233294Sstas		ok_as_delegate = tgts.flags.b.ok_as_delegate;
777233294Sstas
77855682Smarkm	    *out_creds = calloc(1, sizeof(**out_creds));
77978527Sassar	    if(*out_creds == NULL) {
78055682Smarkm		ret = ENOMEM;
781233294Sstas		krb5_set_error_message(context, ret,
782233294Sstas				       N_("malloc: out of memory", ""));
78378527Sassar	    } else {
784233294Sstas		ret = get_cred_kdc_address(context, ccache, flags, NULL,
785233294Sstas					   in_creds, &tgts,
786233294Sstas					   impersonate_principal,
787233294Sstas					   second_ticket,
788233294Sstas					   *out_creds);
78972445Sassar		if (ret) {
79055682Smarkm		    free (*out_creds);
79172445Sassar		    *out_creds = NULL;
792233294Sstas		} else if (ok_as_delegate == 0)
793233294Sstas		    (*out_creds)->flags.b.ok_as_delegate = 0;
79455682Smarkm	    }
795178825Sdfr	    krb5_free_cred_contents(context, &tgts);
79655682Smarkm	    krb5_free_principal(context, tmp_creds.server);
79755682Smarkm	    krb5_free_principal(context, tmp_creds.client);
79855682Smarkm	    return ret;
79955682Smarkm	}
80055682Smarkm    }
801233294Sstas    if(krb5_realm_compare(context, in_creds->client, in_creds->server))
802233294Sstas	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
803233294Sstas
80455682Smarkm    /* XXX this can loop forever */
80555682Smarkm    while(1){
806178825Sdfr	heim_general_string tgt_inst;
80772445Sassar
808233294Sstas	ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
809233294Sstas				  NULL, NULL, &tgt, ret_tgts);
81055682Smarkm	if(ret) {
81155682Smarkm	    krb5_free_principal(context, tmp_creds.server);
81255682Smarkm	    krb5_free_principal(context, tmp_creds.client);
81355682Smarkm	    return ret;
81455682Smarkm	}
815233294Sstas	/*
816233294Sstas	 * if either of the chain or the ok_as_delegate was stripped
817233294Sstas	 * by the kdc, make sure we strip it too.
818233294Sstas	 */
819233294Sstas	if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
820233294Sstas	    ok_as_delegate = 0;
821233294Sstas	    tgt->flags.b.ok_as_delegate = 0;
822233294Sstas	}
823233294Sstas
824233294Sstas	ret = add_cred(context, tgt, ret_tgts);
82555682Smarkm	if(ret) {
82655682Smarkm	    krb5_free_principal(context, tmp_creds.server);
82755682Smarkm	    krb5_free_principal(context, tmp_creds.client);
82855682Smarkm	    return ret;
82955682Smarkm	}
83055682Smarkm	tgt_inst = tgt->server->name.name_string.val[1];
83155682Smarkm	if(strcmp(tgt_inst, server_realm) == 0)
83255682Smarkm	    break;
83355682Smarkm	krb5_free_principal(context, tmp_creds.server);
834233294Sstas	ret = krb5_make_principal(context, &tmp_creds.server,
83555682Smarkm				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
83655682Smarkm	if(ret) {
83755682Smarkm	    krb5_free_principal(context, tmp_creds.server);
83855682Smarkm	    krb5_free_principal(context, tmp_creds.client);
83955682Smarkm	    return ret;
84055682Smarkm	}
84155682Smarkm	ret = krb5_free_creds(context, tgt);
84255682Smarkm	if(ret) {
84355682Smarkm	    krb5_free_principal(context, tmp_creds.server);
84455682Smarkm	    krb5_free_principal(context, tmp_creds.client);
84555682Smarkm	    return ret;
84655682Smarkm	}
84755682Smarkm    }
848233294Sstas
84955682Smarkm    krb5_free_principal(context, tmp_creds.server);
85055682Smarkm    krb5_free_principal(context, tmp_creds.client);
85155682Smarkm    *out_creds = calloc(1, sizeof(**out_creds));
85278527Sassar    if(*out_creds == NULL) {
85355682Smarkm	ret = ENOMEM;
854233294Sstas	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
85578527Sassar    } else {
856233294Sstas	ret = get_cred_kdc_address (context, ccache, flags, NULL,
857233294Sstas				    in_creds, tgt, impersonate_principal,
858233294Sstas				    second_ticket, *out_creds);
85972445Sassar	if (ret) {
86055682Smarkm	    free (*out_creds);
86172445Sassar	    *out_creds = NULL;
86272445Sassar	}
86355682Smarkm    }
86455682Smarkm    krb5_free_creds(context, tgt);
86555682Smarkm    return ret;
86655682Smarkm}
86755682Smarkm
868233294Sstas/*
869233294Sstasget_cred(server)
870233294Sstas	creds = cc_get_cred(server)
871233294Sstas	if(creds) return creds
872233294Sstas	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
873233294Sstas	if(tgt)
874233294Sstas		return get_cred_tgt(server, tgt)
875233294Sstas	if(client_realm == server_realm)
876233294Sstas		return NULL
877233294Sstas	tgt = get_cred(krbtgt/server_realm@client_realm)
878233294Sstas	while(tgt_inst != server_realm)
879233294Sstas		tgt = get_cred(krbtgt/server_realm@tgt_inst)
880233294Sstas	return get_cred_tgt(server, tgt)
881233294Sstas	*/
882233294Sstas
883233294Sstasstatic krb5_error_code
884233294Sstasget_cred_kdc_capath(krb5_context context,
885233294Sstas		    krb5_kdc_flags flags,
886233294Sstas		    krb5_ccache ccache,
887233294Sstas		    krb5_creds *in_creds,
888233294Sstas		    krb5_principal impersonate_principal,
889233294Sstas		    Ticket *second_ticket,
890233294Sstas		    krb5_creds **out_creds,
891233294Sstas		    krb5_creds ***ret_tgts)
89278527Sassar{
893233294Sstas    krb5_error_code ret;
894233294Sstas    krb5_const_realm client_realm, server_realm, try_realm;
895233294Sstas
896233294Sstas    client_realm = krb5_principal_get_realm(context, in_creds->client);
897233294Sstas    server_realm = krb5_principal_get_realm(context, in_creds->server);
898233294Sstas
899233294Sstas    try_realm = client_realm;
900233294Sstas    ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
901233294Sstas                                     impersonate_principal, second_ticket, out_creds,
902233294Sstas                                     ret_tgts);
903233294Sstas
904233294Sstas    if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
905233294Sstas        try_realm = krb5_config_get_string(context, NULL, "capaths",
906233294Sstas                                           client_realm, server_realm, NULL);
907233294Sstas
908233294Sstas        if (try_realm != NULL && strcmp(try_realm, client_realm)) {
909233294Sstas            ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
910233294Sstas                                             try_realm, impersonate_principal,
911233294Sstas                                             second_ticket, out_creds, ret_tgts);
912233294Sstas        }
913233294Sstas    }
914233294Sstas
915233294Sstas    return ret;
91678527Sassar}
91778527Sassar
918233294Sstasstatic krb5_error_code
919233294Sstasget_cred_kdc_referral(krb5_context context,
920233294Sstas		      krb5_kdc_flags flags,
921233294Sstas		      krb5_ccache ccache,
922233294Sstas		      krb5_creds *in_creds,
923233294Sstas		      krb5_principal impersonate_principal,
924233294Sstas		      Ticket *second_ticket,
925233294Sstas		      krb5_creds **out_creds,
926233294Sstas		      krb5_creds ***ret_tgts)
927233294Sstas{
928233294Sstas    krb5_const_realm client_realm;
929233294Sstas    krb5_error_code ret;
930233294Sstas    krb5_creds tgt, referral, ticket;
931233294Sstas    int loop = 0;
932233294Sstas    int ok_as_delegate = 1;
933233294Sstas
934233294Sstas    if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
935233294Sstas	krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
936233294Sstas			       N_("Name too short to do referals, skipping", ""));
937233294Sstas	return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
938233294Sstas    }
939233294Sstas
940233294Sstas    memset(&tgt, 0, sizeof(tgt));
941233294Sstas    memset(&ticket, 0, sizeof(ticket));
942233294Sstas
943233294Sstas    flags.b.canonicalize = 1;
944233294Sstas
945233294Sstas    *out_creds = NULL;
946233294Sstas
947233294Sstas    client_realm = krb5_principal_get_realm(context, in_creds->client);
948233294Sstas
949233294Sstas    /* find tgt for the clients base realm */
950233294Sstas    {
951233294Sstas	krb5_principal tgtname;
952233294Sstas
953233294Sstas	ret = krb5_make_principal(context, &tgtname,
954233294Sstas				  client_realm,
955233294Sstas				  KRB5_TGS_NAME,
956233294Sstas				  client_realm,
957233294Sstas				  NULL);
958233294Sstas	if(ret)
959233294Sstas	    return ret;
960233294Sstas
961233294Sstas	ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
962233294Sstas	krb5_free_principal(context, tgtname);
963233294Sstas	if (ret)
964233294Sstas	    return ret;
965233294Sstas    }
966233294Sstas
967233294Sstas    referral = *in_creds;
968233294Sstas    ret = krb5_copy_principal(context, in_creds->server, &referral.server);
969233294Sstas    if (ret) {
970233294Sstas	krb5_free_cred_contents(context, &tgt);
971233294Sstas	return ret;
972233294Sstas    }
973233294Sstas    ret = krb5_principal_set_realm(context, referral.server, client_realm);
974233294Sstas    if (ret) {
975233294Sstas	krb5_free_cred_contents(context, &tgt);
976233294Sstas	krb5_free_principal(context, referral.server);
977233294Sstas	return ret;
978233294Sstas    }
979233294Sstas
980233294Sstas    while (loop++ < 17) {
981233294Sstas	krb5_creds **tickets;
982233294Sstas	krb5_creds mcreds;
983233294Sstas	char *referral_realm;
984233294Sstas
985233294Sstas	/* Use cache if we are not doing impersonation or contrainte deleg */
986233294Sstas	if (impersonate_principal == NULL || flags.b.constrained_delegation) {
987233294Sstas	    krb5_cc_clear_mcred(&mcreds);
988233294Sstas	    mcreds.server = referral.server;
989233294Sstas	    ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
990233294Sstas	} else
991233294Sstas	    ret = EINVAL;
992233294Sstas
993233294Sstas	if (ret) {
994233294Sstas	    ret = get_cred_kdc_address(context, ccache, flags, NULL,
995233294Sstas				       &referral, &tgt, impersonate_principal,
996233294Sstas				       second_ticket, &ticket);
997233294Sstas	    if (ret)
998233294Sstas		goto out;
999233294Sstas	}
1000233294Sstas
1001233294Sstas	/* Did we get the right ticket ? */
1002233294Sstas	if (krb5_principal_compare_any_realm(context,
1003233294Sstas					     referral.server,
1004233294Sstas					     ticket.server))
1005233294Sstas	    break;
1006233294Sstas
1007233294Sstas	if (!krb5_principal_is_krbtgt(context, ticket.server)) {
1008233294Sstas	    krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
1009233294Sstas				   N_("Got back an non krbtgt "
1010233294Sstas				      "ticket referrals", ""));
1011233294Sstas	    ret = KRB5KRB_AP_ERR_NOT_US;
1012233294Sstas	    goto out;
1013233294Sstas	}
1014233294Sstas
1015233294Sstas	referral_realm = ticket.server->name.name_string.val[1];
1016233294Sstas
1017233294Sstas	/* check that there are no referrals loops */
1018233294Sstas	tickets = *ret_tgts;
1019233294Sstas
1020233294Sstas	krb5_cc_clear_mcred(&mcreds);
1021233294Sstas	mcreds.server = ticket.server;
1022233294Sstas
1023233294Sstas	while(tickets && *tickets){
1024233294Sstas	    if(krb5_compare_creds(context,
1025233294Sstas				  KRB5_TC_DONT_MATCH_REALM,
1026233294Sstas				  &mcreds,
1027233294Sstas				  *tickets))
1028233294Sstas	    {
1029233294Sstas		krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1030233294Sstas				       N_("Referral from %s "
1031233294Sstas					  "loops back to realm %s", ""),
1032233294Sstas				       tgt.server->realm,
1033233294Sstas				       referral_realm);
1034233294Sstas		ret = KRB5_GET_IN_TKT_LOOP;
1035233294Sstas                goto out;
1036233294Sstas	    }
1037233294Sstas	    tickets++;
1038233294Sstas	}
1039233294Sstas
1040233294Sstas	/*
1041233294Sstas	 * if either of the chain or the ok_as_delegate was stripped
1042233294Sstas	 * by the kdc, make sure we strip it too.
1043233294Sstas	 */
1044233294Sstas
1045233294Sstas	if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1046233294Sstas	    ok_as_delegate = 0;
1047233294Sstas	    ticket.flags.b.ok_as_delegate = 0;
1048233294Sstas	}
1049233294Sstas
1050233294Sstas	ret = add_cred(context, &ticket, ret_tgts);
1051233294Sstas	if (ret)
1052233294Sstas	    goto out;
1053233294Sstas
1054233294Sstas	/* try realm in the referral */
1055233294Sstas	ret = krb5_principal_set_realm(context,
1056233294Sstas				       referral.server,
1057233294Sstas				       referral_realm);
1058233294Sstas	krb5_free_cred_contents(context, &tgt);
1059233294Sstas	tgt = ticket;
1060233294Sstas	memset(&ticket, 0, sizeof(ticket));
1061233294Sstas	if (ret)
1062233294Sstas	    goto out;
1063233294Sstas    }
1064233294Sstas
1065233294Sstas    ret = krb5_copy_creds(context, &ticket, out_creds);
1066233294Sstas
1067233294Sstasout:
1068233294Sstas    krb5_free_principal(context, referral.server);
1069233294Sstas    krb5_free_cred_contents(context, &tgt);
1070233294Sstas    krb5_free_cred_contents(context, &ticket);
1071233294Sstas    return ret;
1072233294Sstas}
1073233294Sstas
1074233294Sstas
1075233294Sstas/*
1076233294Sstas * Glue function between referrals version and old client chasing
1077233294Sstas * codebase.
1078233294Sstas */
1079233294Sstas
1080233294Sstaskrb5_error_code
1081233294Sstas_krb5_get_cred_kdc_any(krb5_context context,
1082233294Sstas		       krb5_kdc_flags flags,
108355682Smarkm		       krb5_ccache ccache,
108455682Smarkm		       krb5_creds *in_creds,
1085233294Sstas		       krb5_principal impersonate_principal,
1086233294Sstas		       Ticket *second_ticket,
108755682Smarkm		       krb5_creds **out_creds,
108855682Smarkm		       krb5_creds ***ret_tgts)
108955682Smarkm{
1090233294Sstas    krb5_error_code ret;
1091233294Sstas    krb5_deltat offset;
1092233294Sstas
1093233294Sstas    ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
1094233294Sstas    if (ret) {
1095233294Sstas	context->kdc_sec_offset = offset;
1096233294Sstas	context->kdc_usec_offset = 0;
1097233294Sstas    }
1098233294Sstas
1099233294Sstas    ret = get_cred_kdc_referral(context,
1100233294Sstas				flags,
1101233294Sstas				ccache,
1102233294Sstas				in_creds,
1103233294Sstas				impersonate_principal,
1104233294Sstas				second_ticket,
1105233294Sstas				out_creds,
1106233294Sstas				ret_tgts);
1107233294Sstas    if (ret == 0 || flags.b.canonicalize)
1108233294Sstas	return ret;
1109233294Sstas    return get_cred_kdc_capath(context,
1110233294Sstas				flags,
1111233294Sstas				ccache,
1112233294Sstas				in_creds,
1113233294Sstas				impersonate_principal,
1114233294Sstas				second_ticket,
1115233294Sstas				out_creds,
1116233294Sstas				ret_tgts);
111755682Smarkm}
111855682Smarkm
1119233294Sstas
1120233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
112155682Smarkmkrb5_get_credentials_with_flags(krb5_context context,
112255682Smarkm				krb5_flags options,
112355682Smarkm				krb5_kdc_flags flags,
112455682Smarkm				krb5_ccache ccache,
112555682Smarkm				krb5_creds *in_creds,
112655682Smarkm				krb5_creds **out_creds)
112755682Smarkm{
112855682Smarkm    krb5_error_code ret;
112955682Smarkm    krb5_creds **tgts;
113072445Sassar    krb5_creds *res_creds;
113155682Smarkm    int i;
1132233294Sstas
1133233294Sstas    if (in_creds->session.keytype) {
1134233294Sstas	ret = krb5_enctype_valid(context, in_creds->session.keytype);
1135233294Sstas	if (ret)
1136233294Sstas	    return ret;
1137233294Sstas    }
1138233294Sstas
113972445Sassar    *out_creds = NULL;
114072445Sassar    res_creds = calloc(1, sizeof(*res_creds));
114178527Sassar    if (res_creds == NULL) {
1142233294Sstas	krb5_set_error_message(context, ENOMEM,
1143233294Sstas			       N_("malloc: out of memory", ""));
114455682Smarkm	return ENOMEM;
114578527Sassar    }
114655682Smarkm
1147178825Sdfr    if (in_creds->session.keytype)
1148178825Sdfr	options |= KRB5_TC_MATCH_KEYTYPE;
1149178825Sdfr
1150233294Sstas    /*
1151178825Sdfr     * If we got a credential, check if credential is expired before
1152178825Sdfr     * returning it.
1153178825Sdfr     */
115455682Smarkm    ret = krb5_cc_retrieve_cred(context,
1155178825Sdfr                                ccache,
1156178825Sdfr                                in_creds->session.keytype ?
1157178825Sdfr                                KRB5_TC_MATCH_KEYTYPE : 0,
1158178825Sdfr                                in_creds, res_creds);
1159233294Sstas    /*
1160178825Sdfr     * If we got a credential, check if credential is expired before
1161178825Sdfr     * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1162178825Sdfr     */
1163178825Sdfr    if (ret == 0) {
1164178825Sdfr	krb5_timestamp timeret;
1165178825Sdfr
1166178825Sdfr	/* If expired ok, don't bother checking */
1167178825Sdfr        if(options & KRB5_GC_EXPIRED_OK) {
1168178825Sdfr            *out_creds = res_creds;
1169178825Sdfr            return 0;
1170178825Sdfr        }
1171233294Sstas
1172178825Sdfr	krb5_timeofday(context, &timeret);
1173178825Sdfr	if(res_creds->times.endtime > timeret) {
1174178825Sdfr	    *out_creds = res_creds;
1175178825Sdfr	    return 0;
1176178825Sdfr	}
1177178825Sdfr	if(options & KRB5_GC_CACHED)
1178178825Sdfr	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1179178825Sdfr
1180178825Sdfr    } else if(ret != KRB5_CC_END) {
1181178825Sdfr        free(res_creds);
1182178825Sdfr        return ret;
118372445Sassar    }
118472445Sassar    free(res_creds);
1185233294Sstas    if(options & KRB5_GC_CACHED)
1186233294Sstas	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1187233294Sstas
118855682Smarkm    if(options & KRB5_GC_USER_USER)
118955682Smarkm	flags.b.enc_tkt_in_skey = 1;
1190178825Sdfr    if (flags.b.enc_tkt_in_skey)
1191178825Sdfr	options |= KRB5_GC_NO_STORE;
1192178825Sdfr
119355682Smarkm    tgts = NULL;
1194233294Sstas    ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1195233294Sstas				 in_creds, NULL, NULL, out_creds, &tgts);
119672445Sassar    for(i = 0; tgts && tgts[i]; i++) {
119755682Smarkm	krb5_cc_store_cred(context, ccache, tgts[i]);
119855682Smarkm	krb5_free_creds(context, tgts[i]);
119955682Smarkm    }
120055682Smarkm    free(tgts);
1201178825Sdfr    if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
120255682Smarkm	krb5_cc_store_cred(context, ccache, *out_creds);
120355682Smarkm    return ret;
120455682Smarkm}
120555682Smarkm
1206233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
120755682Smarkmkrb5_get_credentials(krb5_context context,
120855682Smarkm		     krb5_flags options,
120955682Smarkm		     krb5_ccache ccache,
121055682Smarkm		     krb5_creds *in_creds,
121155682Smarkm		     krb5_creds **out_creds)
121255682Smarkm{
121355682Smarkm    krb5_kdc_flags flags;
121455682Smarkm    flags.i = 0;
121555682Smarkm    return krb5_get_credentials_with_flags(context, options, flags,
121655682Smarkm					   ccache, in_creds, out_creds);
121755682Smarkm}
1218178825Sdfr
1219178825Sdfrstruct krb5_get_creds_opt_data {
1220178825Sdfr    krb5_principal self;
1221178825Sdfr    krb5_flags options;
1222178825Sdfr    krb5_enctype enctype;
1223178825Sdfr    Ticket *ticket;
1224178825Sdfr};
1225178825Sdfr
1226178825Sdfr
1227233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1228178825Sdfrkrb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1229178825Sdfr{
1230178825Sdfr    *opt = calloc(1, sizeof(**opt));
1231178825Sdfr    if (*opt == NULL) {
1232233294Sstas	krb5_set_error_message(context, ENOMEM,
1233233294Sstas			       N_("malloc: out of memory", ""));
1234178825Sdfr	return ENOMEM;
1235178825Sdfr    }
1236178825Sdfr    return 0;
1237178825Sdfr}
1238178825Sdfr
1239233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
1240178825Sdfrkrb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1241178825Sdfr{
1242178825Sdfr    if (opt->self)
1243178825Sdfr	krb5_free_principal(context, opt->self);
1244233294Sstas    if (opt->ticket) {
1245233294Sstas	free_Ticket(opt->ticket);
1246233294Sstas	free(opt->ticket);
1247233294Sstas    }
1248178825Sdfr    memset(opt, 0, sizeof(*opt));
1249178825Sdfr    free(opt);
1250178825Sdfr}
1251178825Sdfr
1252233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
1253178825Sdfrkrb5_get_creds_opt_set_options(krb5_context context,
1254178825Sdfr			       krb5_get_creds_opt opt,
1255178825Sdfr			       krb5_flags options)
1256178825Sdfr{
1257178825Sdfr    opt->options = options;
1258178825Sdfr}
1259178825Sdfr
1260233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
1261178825Sdfrkrb5_get_creds_opt_add_options(krb5_context context,
1262178825Sdfr			       krb5_get_creds_opt opt,
1263178825Sdfr			       krb5_flags options)
1264178825Sdfr{
1265178825Sdfr    opt->options |= options;
1266178825Sdfr}
1267178825Sdfr
1268233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
1269178825Sdfrkrb5_get_creds_opt_set_enctype(krb5_context context,
1270178825Sdfr			       krb5_get_creds_opt opt,
1271178825Sdfr			       krb5_enctype enctype)
1272178825Sdfr{
1273178825Sdfr    opt->enctype = enctype;
1274178825Sdfr}
1275178825Sdfr
1276233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1277178825Sdfrkrb5_get_creds_opt_set_impersonate(krb5_context context,
1278178825Sdfr				   krb5_get_creds_opt opt,
1279178825Sdfr				   krb5_const_principal self)
1280178825Sdfr{
1281178825Sdfr    if (opt->self)
1282178825Sdfr	krb5_free_principal(context, opt->self);
1283178825Sdfr    return krb5_copy_principal(context, self, &opt->self);
1284178825Sdfr}
1285178825Sdfr
1286233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1287178825Sdfrkrb5_get_creds_opt_set_ticket(krb5_context context,
1288178825Sdfr			      krb5_get_creds_opt opt,
1289178825Sdfr			      const Ticket *ticket)
1290178825Sdfr{
1291178825Sdfr    if (opt->ticket) {
1292178825Sdfr	free_Ticket(opt->ticket);
1293178825Sdfr	free(opt->ticket);
1294178825Sdfr	opt->ticket = NULL;
1295178825Sdfr    }
1296178825Sdfr    if (ticket) {
1297178825Sdfr	krb5_error_code ret;
1298178825Sdfr
1299178825Sdfr	opt->ticket = malloc(sizeof(*ticket));
1300178825Sdfr	if (opt->ticket == NULL) {
1301233294Sstas	    krb5_set_error_message(context, ENOMEM,
1302233294Sstas				   N_("malloc: out of memory", ""));
1303178825Sdfr	    return ENOMEM;
1304178825Sdfr	}
1305178825Sdfr	ret = copy_Ticket(ticket, opt->ticket);
1306178825Sdfr	if (ret) {
1307178825Sdfr	    free(opt->ticket);
1308178825Sdfr	    opt->ticket = NULL;
1309233294Sstas	    krb5_set_error_message(context, ret,
1310233294Sstas				   N_("malloc: out of memory", ""));
1311178825Sdfr	    return ret;
1312178825Sdfr	}
1313178825Sdfr    }
1314178825Sdfr    return 0;
1315178825Sdfr}
1316178825Sdfr
1317178825Sdfr
1318178825Sdfr
1319233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1320178825Sdfrkrb5_get_creds(krb5_context context,
1321178825Sdfr	       krb5_get_creds_opt opt,
1322178825Sdfr	       krb5_ccache ccache,
1323178825Sdfr	       krb5_const_principal inprinc,
1324178825Sdfr	       krb5_creds **out_creds)
1325178825Sdfr{
1326178825Sdfr    krb5_kdc_flags flags;
1327178825Sdfr    krb5_flags options;
1328178825Sdfr    krb5_creds in_creds;
1329178825Sdfr    krb5_error_code ret;
1330178825Sdfr    krb5_creds **tgts;
1331178825Sdfr    krb5_creds *res_creds;
1332178825Sdfr    int i;
1333233294Sstas
1334233294Sstas    if (opt && opt->enctype) {
1335233294Sstas	ret = krb5_enctype_valid(context, opt->enctype);
1336233294Sstas	if (ret)
1337233294Sstas	    return ret;
1338233294Sstas    }
1339233294Sstas
1340178825Sdfr    memset(&in_creds, 0, sizeof(in_creds));
1341178825Sdfr    in_creds.server = rk_UNCONST(inprinc);
1342178825Sdfr
1343178825Sdfr    ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1344178825Sdfr    if (ret)
1345178825Sdfr	return ret;
1346178825Sdfr
1347233294Sstas    if (opt)
1348233294Sstas	options = opt->options;
1349233294Sstas    else
1350233294Sstas	options = 0;
1351178825Sdfr    flags.i = 0;
1352178825Sdfr
1353178825Sdfr    *out_creds = NULL;
1354178825Sdfr    res_creds = calloc(1, sizeof(*res_creds));
1355178825Sdfr    if (res_creds == NULL) {
1356178825Sdfr	krb5_free_principal(context, in_creds.client);
1357233294Sstas	krb5_set_error_message(context, ENOMEM,
1358233294Sstas			       N_("malloc: out of memory", ""));
1359178825Sdfr	return ENOMEM;
1360178825Sdfr    }
1361178825Sdfr
1362233294Sstas    if (opt && opt->enctype) {
1363178825Sdfr	in_creds.session.keytype = opt->enctype;
1364178825Sdfr	options |= KRB5_TC_MATCH_KEYTYPE;
1365178825Sdfr    }
1366178825Sdfr
1367233294Sstas    /*
1368178825Sdfr     * If we got a credential, check if credential is expired before
1369178825Sdfr     * returning it.
1370178825Sdfr     */
1371178825Sdfr    ret = krb5_cc_retrieve_cred(context,
1372178825Sdfr                                ccache,
1373233294Sstas				options & KRB5_TC_MATCH_KEYTYPE,
1374178825Sdfr                                &in_creds, res_creds);
1375233294Sstas    /*
1376178825Sdfr     * If we got a credential, check if credential is expired before
1377178825Sdfr     * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1378178825Sdfr     */
1379178825Sdfr    if (ret == 0) {
1380178825Sdfr	krb5_timestamp timeret;
1381178825Sdfr
1382178825Sdfr	/* If expired ok, don't bother checking */
1383178825Sdfr        if(options & KRB5_GC_EXPIRED_OK) {
1384178825Sdfr            *out_creds = res_creds;
1385178825Sdfr	    krb5_free_principal(context, in_creds.client);
1386233294Sstas            goto out;
1387178825Sdfr        }
1388233294Sstas
1389178825Sdfr	krb5_timeofday(context, &timeret);
1390178825Sdfr	if(res_creds->times.endtime > timeret) {
1391178825Sdfr	    *out_creds = res_creds;
1392178825Sdfr	    krb5_free_principal(context, in_creds.client);
1393233294Sstas            goto out;
1394178825Sdfr	}
1395178825Sdfr	if(options & KRB5_GC_CACHED)
1396178825Sdfr	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1397178825Sdfr
1398178825Sdfr    } else if(ret != KRB5_CC_END) {
1399178825Sdfr        free(res_creds);
1400178825Sdfr	krb5_free_principal(context, in_creds.client);
1401233294Sstas	goto out;
1402178825Sdfr    }
1403178825Sdfr    free(res_creds);
1404178825Sdfr    if(options & KRB5_GC_CACHED) {
1405178825Sdfr	krb5_free_principal(context, in_creds.client);
1406233294Sstas	ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1407233294Sstas	goto out;
1408178825Sdfr    }
1409178825Sdfr    if(options & KRB5_GC_USER_USER) {
1410178825Sdfr	flags.b.enc_tkt_in_skey = 1;
1411178825Sdfr	options |= KRB5_GC_NO_STORE;
1412178825Sdfr    }
1413178825Sdfr    if (options & KRB5_GC_FORWARDABLE)
1414178825Sdfr	flags.b.forwardable = 1;
1415178825Sdfr    if (options & KRB5_GC_NO_TRANSIT_CHECK)
1416178825Sdfr	flags.b.disable_transited_check = 1;
1417178825Sdfr    if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1418178825Sdfr	flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1419178825Sdfr	flags.b.constrained_delegation = 1;
1420178825Sdfr    }
1421233294Sstas    if (options & KRB5_GC_CANONICALIZE)
1422233294Sstas	flags.b.canonicalize = 1;
1423178825Sdfr
1424178825Sdfr    tgts = NULL;
1425233294Sstas    ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1426233294Sstas				 &in_creds, opt->self, opt->ticket,
1427233294Sstas				 out_creds, &tgts);
1428178825Sdfr    krb5_free_principal(context, in_creds.client);
1429178825Sdfr    for(i = 0; tgts && tgts[i]; i++) {
1430178825Sdfr	krb5_cc_store_cred(context, ccache, tgts[i]);
1431178825Sdfr	krb5_free_creds(context, tgts[i]);
1432178825Sdfr    }
1433178825Sdfr    free(tgts);
1434178825Sdfr    if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1435178825Sdfr	krb5_cc_store_cred(context, ccache, *out_creds);
1436233294Sstas
1437233294Sstas out:
1438233294Sstas    _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
1439233294Sstas
1440178825Sdfr    return ret;
1441178825Sdfr}
1442178825Sdfr
1443178825Sdfr/*
1444178825Sdfr *
1445178825Sdfr */
1446178825Sdfr
1447233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1448178825Sdfrkrb5_get_renewed_creds(krb5_context context,
1449178825Sdfr		       krb5_creds *creds,
1450178825Sdfr		       krb5_const_principal client,
1451178825Sdfr		       krb5_ccache ccache,
1452178825Sdfr		       const char *in_tkt_service)
1453178825Sdfr{
1454178825Sdfr    krb5_error_code ret;
1455178825Sdfr    krb5_kdc_flags flags;
1456178825Sdfr    krb5_creds in, *template, *out = NULL;
1457178825Sdfr
1458178825Sdfr    memset(&in, 0, sizeof(in));
1459178825Sdfr    memset(creds, 0, sizeof(*creds));
1460178825Sdfr
1461178825Sdfr    ret = krb5_copy_principal(context, client, &in.client);
1462178825Sdfr    if (ret)
1463178825Sdfr	return ret;
1464178825Sdfr
1465178825Sdfr    if (in_tkt_service) {
1466178825Sdfr	ret = krb5_parse_name(context, in_tkt_service, &in.server);
1467178825Sdfr	if (ret) {
1468178825Sdfr	    krb5_free_principal(context, in.client);
1469178825Sdfr	    return ret;
1470178825Sdfr	}
1471178825Sdfr    } else {
1472178825Sdfr	const char *realm = krb5_principal_get_realm(context, client);
1473233294Sstas
1474178825Sdfr	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1475178825Sdfr				  realm, NULL);
1476178825Sdfr	if (ret) {
1477178825Sdfr	    krb5_free_principal(context, in.client);
1478178825Sdfr	    return ret;
1479178825Sdfr	}
1480178825Sdfr    }
1481178825Sdfr
1482178825Sdfr    flags.i = 0;
1483178825Sdfr    flags.b.renewable = flags.b.renew = 1;
1484178825Sdfr
1485178825Sdfr    /*
1486178825Sdfr     * Get template from old credential cache for the same entry, if
1487178825Sdfr     * this failes, no worries.
1488178825Sdfr     */
1489178825Sdfr    ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1490178825Sdfr    if (ret == 0) {
1491178825Sdfr	flags.b.forwardable = template->flags.b.forwardable;
1492178825Sdfr	flags.b.proxiable = template->flags.b.proxiable;
1493178825Sdfr	krb5_free_creds (context, template);
1494178825Sdfr    }
1495178825Sdfr
1496178825Sdfr    ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1497178825Sdfr    krb5_free_principal(context, in.client);
1498178825Sdfr    krb5_free_principal(context, in.server);
1499178825Sdfr    if (ret)
1500178825Sdfr	return ret;
1501178825Sdfr
1502178825Sdfr    ret = krb5_copy_creds_contents(context, out, creds);
1503178825Sdfr    krb5_free_creds(context, out);
1504178825Sdfr
1505178825Sdfr    return ret;
1506178825Sdfr}
1507