1 /*	$NetBSD: get_cred.c,v 1.1.1.1 2011/04/13 18:15:33 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * 3. Neither the name of the Institute nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include "krb5_locl.h"
39 #include <assert.h>
40 
41 static krb5_error_code
42 get_cred_kdc_capath(krb5_context, krb5_kdc_flags,
43 		    krb5_ccache, krb5_creds *, krb5_principal,
44 		    Ticket *, krb5_creds **, krb5_creds ***);
45 
46 /*
47  * Take the `body' and encode it into `padata' using the credentials
48  * in `creds'.
49  */
50 
51 static krb5_error_code
52 make_pa_tgs_req(krb5_context context,
53 		krb5_auth_context ac,
54 		KDC_REQ_BODY *body,
55 		PA_DATA *padata,
56 		krb5_creds *creds)
57 {
58     u_char *buf;
59     size_t buf_size;
60     size_t len;
61     krb5_data in_data;
62     krb5_error_code ret;
63 
64     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
65     if (ret)
66 	goto out;
67     if(buf_size != len)
68 	krb5_abortx(context, "internal error in ASN.1 encoder");
69 
70     in_data.length = len;
71     in_data.data   = buf;
72     ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
73 				&padata->padata_value,
74 				KRB5_KU_TGS_REQ_AUTH_CKSUM,
75 				KRB5_KU_TGS_REQ_AUTH);
76  out:
77     free (buf);
78     if(ret)
79 	return ret;
80     padata->padata_type = KRB5_PADATA_TGS_REQ;
81     return 0;
82 }
83 
84 /*
85  * Set the `enc-authorization-data' in `req_body' based on `authdata'
86  */
87 
88 static krb5_error_code
89 set_auth_data (krb5_context context,
90 	       KDC_REQ_BODY *req_body,
91 	       krb5_authdata *authdata,
92 	       krb5_keyblock *subkey)
93 {
94     if(authdata->len) {
95 	size_t len, buf_size;
96 	unsigned char *buf;
97 	krb5_crypto crypto;
98 	krb5_error_code ret;
99 
100 	ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
101 			   &len, ret);
102 	if (ret)
103 	    return ret;
104 	if (buf_size != len)
105 	    krb5_abortx(context, "internal error in ASN.1 encoder");
106 
107 	ALLOC(req_body->enc_authorization_data, 1);
108 	if (req_body->enc_authorization_data == NULL) {
109 	    free (buf);
110 	    krb5_set_error_message(context, ENOMEM,
111 				   N_("malloc: out of memory", ""));
112 	    return ENOMEM;
113 	}
114 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
115 	if (ret) {
116 	    free (buf);
117 	    free (req_body->enc_authorization_data);
118 	    req_body->enc_authorization_data = NULL;
119 	    return ret;
120 	}
121 	krb5_encrypt_EncryptedData(context,
122 				   crypto,
123 				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
124 				   buf,
125 				   len,
126 				   0,
127 				   req_body->enc_authorization_data);
128 	free (buf);
129 	krb5_crypto_destroy(context, crypto);
130     } else {
131 	req_body->enc_authorization_data = NULL;
132     }
133     return 0;
134 }
135 
136 /*
137  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
138  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
139  * subkey in `subkey'.
140  */
141 
142 static krb5_error_code
143 init_tgs_req (krb5_context context,
144 	      krb5_ccache ccache,
145 	      krb5_addresses *addresses,
146 	      krb5_kdc_flags flags,
147 	      Ticket *second_ticket,
148 	      krb5_creds *in_creds,
149 	      krb5_creds *krbtgt,
150 	      unsigned nonce,
151 	      const METHOD_DATA *padata,
152 	      krb5_keyblock **subkey,
153 	      TGS_REQ *t)
154 {
155     krb5_auth_context ac = NULL;
156     krb5_error_code ret = 0;
157 
158     memset(t, 0, sizeof(*t));
159     t->pvno = 5;
160     t->msg_type = krb_tgs_req;
161     if (in_creds->session.keytype) {
162 	ALLOC_SEQ(&t->req_body.etype, 1);
163 	if(t->req_body.etype.val == NULL) {
164 	    ret = ENOMEM;
165 	    krb5_set_error_message(context, ret,
166 				   N_("malloc: out of memory", ""));
167 	    goto fail;
168 	}
169 	t->req_body.etype.val[0] = in_creds->session.keytype;
170     } else {
171 	ret = krb5_init_etype(context,
172 			      &t->req_body.etype.len,
173 			      &t->req_body.etype.val,
174 			      NULL);
175     }
176     if (ret)
177 	goto fail;
178     t->req_body.addresses = addresses;
179     t->req_body.kdc_options = flags.b;
180     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
181     if (ret)
182 	goto fail;
183     ALLOC(t->req_body.sname, 1);
184     if (t->req_body.sname == NULL) {
185 	ret = ENOMEM;
186 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
187 	goto fail;
188     }
189 
190     /* some versions of some code might require that the client be
191        present in TGS-REQs, but this is clearly against the spec */
192 
193     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
194     if (ret)
195 	goto fail;
196 
197     /* req_body.till should be NULL if there is no endtime specified,
198        but old MIT code (like DCE secd) doesn't like that */
199     ALLOC(t->req_body.till, 1);
200     if(t->req_body.till == NULL){
201 	ret = ENOMEM;
202 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
203 	goto fail;
204     }
205     *t->req_body.till = in_creds->times.endtime;
206 
207     t->req_body.nonce = nonce;
208     if(second_ticket){
209 	ALLOC(t->req_body.additional_tickets, 1);
210 	if (t->req_body.additional_tickets == NULL) {
211 	    ret = ENOMEM;
212 	    krb5_set_error_message(context, ret,
213 				   N_("malloc: out of memory", ""));
214 	    goto fail;
215 	}
216 	ALLOC_SEQ(t->req_body.additional_tickets, 1);
217 	if (t->req_body.additional_tickets->val == NULL) {
218 	    ret = ENOMEM;
219 	    krb5_set_error_message(context, ret,
220 				   N_("malloc: out of memory", ""));
221 	    goto fail;
222 	}
223 	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
224 	if (ret)
225 	    goto fail;
226     }
227     ALLOC(t->padata, 1);
228     if (t->padata == NULL) {
229 	ret = ENOMEM;
230 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
231 	goto fail;
232     }
233     ALLOC_SEQ(t->padata, 1 + padata->len);
234     if (t->padata->val == NULL) {
235 	ret = ENOMEM;
236 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
237 	goto fail;
238     }
239     {
240 	int i;
241 	for (i = 0; i < padata->len; i++) {
242 	    ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
243 	    if (ret) {
244 		krb5_set_error_message(context, ret,
245 				       N_("malloc: out of memory", ""));
246 		goto fail;
247 	    }
248 	}
249     }
250 
251     ret = krb5_auth_con_init(context, &ac);
252     if(ret)
253 	goto fail;
254 
255     ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
256     if (ret)
257 	goto fail;
258 
259     ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
260 			 ac->local_subkey);
261     if (ret)
262 	goto fail;
263 
264     ret = make_pa_tgs_req(context,
265 			  ac,
266 			  &t->req_body,
267 			  &t->padata->val[0],
268 			  krbtgt);
269     if(ret)
270 	goto fail;
271 
272     ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
273     if (ret)
274 	goto fail;
275 
276 fail:
277     if (ac)
278 	krb5_auth_con_free(context, ac);
279     if (ret) {
280 	t->req_body.addresses = NULL;
281 	free_TGS_REQ (t);
282     }
283     return ret;
284 }
285 
286 krb5_error_code
287 _krb5_get_krbtgt(krb5_context context,
288 		 krb5_ccache  id,
289 		 krb5_realm realm,
290 		 krb5_creds **cred)
291 {
292     krb5_error_code ret;
293     krb5_creds tmp_cred;
294 
295     memset(&tmp_cred, 0, sizeof(tmp_cred));
296 
297     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
298     if (ret)
299 	return ret;
300 
301     ret = krb5_make_principal(context,
302 			      &tmp_cred.server,
303 			      realm,
304 			      KRB5_TGS_NAME,
305 			      realm,
306 			      NULL);
307     if(ret) {
308 	krb5_free_principal(context, tmp_cred.client);
309 	return ret;
310     }
311     ret = krb5_get_credentials(context,
312 			       KRB5_GC_CACHED,
313 			       id,
314 			       &tmp_cred,
315 			       cred);
316     krb5_free_principal(context, tmp_cred.client);
317     krb5_free_principal(context, tmp_cred.server);
318     if(ret)
319 	return ret;
320     return 0;
321 }
322 
323 /* DCE compatible decrypt proc */
324 static krb5_error_code KRB5_CALLCONV
325 decrypt_tkt_with_subkey (krb5_context context,
326 			 krb5_keyblock *key,
327 			 krb5_key_usage usage,
328 			 krb5_const_pointer skey,
329 			 krb5_kdc_rep *dec_rep)
330 {
331     const krb5_keyblock *subkey = skey;
332     krb5_error_code ret = 0;
333     krb5_data data;
334     size_t size;
335     krb5_crypto crypto;
336 
337     assert(usage == 0);
338 
339     /*
340      * start out with trying with subkey if we have one
341      */
342     if (subkey) {
343 	ret = krb5_crypto_init(context, subkey, 0, &crypto);
344 	if (ret)
345 	    return ret;
346 	ret = krb5_decrypt_EncryptedData (context,
347 					  crypto,
348 					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
349 					  &dec_rep->kdc_rep.enc_part,
350 					  &data);
351 	/*
352 	 * If the is Windows 2000 DC, we need to retry with key usage
353 	 * 8 when doing ARCFOUR.
354 	 */
355 	if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
356 	    ret = krb5_decrypt_EncryptedData(context,
357 					     crypto,
358 					     8,
359 					     &dec_rep->kdc_rep.enc_part,
360 					     &data);
361 	}
362 	krb5_crypto_destroy(context, crypto);
363     }
364     if (subkey == NULL || ret) {
365 	ret = krb5_crypto_init(context, key, 0, &crypto);
366 	if (ret)
367 	    return ret;
368 	ret = krb5_decrypt_EncryptedData (context,
369 					  crypto,
370 					  KRB5_KU_TGS_REP_ENC_PART_SESSION,
371 					  &dec_rep->kdc_rep.enc_part,
372 					  &data);
373 	krb5_crypto_destroy(context, crypto);
374     }
375     if (ret)
376 	return ret;
377 
378     ret = decode_EncASRepPart(data.data,
379 			      data.length,
380 			      &dec_rep->enc_part,
381 			      &size);
382     if (ret)
383 	ret = decode_EncTGSRepPart(data.data,
384 				   data.length,
385 				   &dec_rep->enc_part,
386 				   &size);
387     if (ret)
388       krb5_set_error_message(context, ret,
389 			     N_("Failed to decode encpart in ticket", ""));
390     krb5_data_free (&data);
391     return ret;
392 }
393 
394 static krb5_error_code
395 get_cred_kdc(krb5_context context,
396 	     krb5_ccache id,
397 	     krb5_kdc_flags flags,
398 	     krb5_addresses *addresses,
399 	     krb5_creds *in_creds,
400 	     krb5_creds *krbtgt,
401 	     krb5_principal impersonate_principal,
402 	     Ticket *second_ticket,
403 	     krb5_creds *out_creds)
404 {
405     TGS_REQ req;
406     krb5_data enc;
407     krb5_data resp;
408     krb5_kdc_rep rep;
409     KRB_ERROR error;
410     krb5_error_code ret;
411     unsigned nonce;
412     krb5_keyblock *subkey = NULL;
413     size_t len;
414     Ticket second_ticket_data;
415     METHOD_DATA padata;
416 
417     krb5_data_zero(&resp);
418     krb5_data_zero(&enc);
419     padata.val = NULL;
420     padata.len = 0;
421 
422     krb5_generate_random_block(&nonce, sizeof(nonce));
423     nonce &= 0xffffffff;
424 
425     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
426 	ret = decode_Ticket(in_creds->second_ticket.data,
427 			    in_creds->second_ticket.length,
428 			    &second_ticket_data, &len);
429 	if(ret)
430 	    return ret;
431 	second_ticket = &second_ticket_data;
432     }
433 
434 
435     if (impersonate_principal) {
436 	krb5_crypto crypto;
437 	PA_S4U2Self self;
438 	krb5_data data;
439 	void *buf;
440 	size_t size;
441 
442 	self.name = impersonate_principal->name;
443 	self.realm = impersonate_principal->realm;
444 	self.auth = estrdup("Kerberos");
445 
446 	ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
447 	if (ret) {
448 	    free(self.auth);
449 	    goto out;
450 	}
451 
452 	ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
453 	if (ret) {
454 	    free(self.auth);
455 	    krb5_data_free(&data);
456 	    goto out;
457 	}
458 
459 	ret = krb5_create_checksum(context,
460 				   crypto,
461 				   KRB5_KU_OTHER_CKSUM,
462 				   0,
463 				   data.data,
464 				   data.length,
465 				   &self.cksum);
466 	krb5_crypto_destroy(context, crypto);
467 	krb5_data_free(&data);
468 	if (ret) {
469 	    free(self.auth);
470 	    goto out;
471 	}
472 
473 	ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
474 	free(self.auth);
475 	free_Checksum(&self.cksum);
476 	if (ret)
477 	    goto out;
478 	if (len != size)
479 	    krb5_abortx(context, "internal asn1 error");
480 
481 	ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
482 	if (ret)
483 	    goto out;
484     }
485 
486     ret = init_tgs_req (context,
487 			id,
488 			addresses,
489 			flags,
490 			second_ticket,
491 			in_creds,
492 			krbtgt,
493 			nonce,
494 			&padata,
495 			&subkey,
496 			&req);
497     if (ret)
498 	goto out;
499 
500     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
501     if (ret)
502 	goto out;
503     if(enc.length != len)
504 	krb5_abortx(context, "internal error in ASN.1 encoder");
505 
506     /* don't free addresses */
507     req.req_body.addresses = NULL;
508     free_TGS_REQ(&req);
509 
510     /*
511      * Send and receive
512      */
513     {
514 	krb5_sendto_ctx stctx;
515 	ret = krb5_sendto_ctx_alloc(context, &stctx);
516 	if (ret)
517 	    return ret;
518 	krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
519 
520 	ret = krb5_sendto_context (context, stctx, &enc,
521 				   krbtgt->server->name.name_string.val[1],
522 				   &resp);
523 	krb5_sendto_ctx_free(context, stctx);
524     }
525     if(ret)
526 	goto out;
527 
528     memset(&rep, 0, sizeof(rep));
529     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
530 	unsigned eflags = 0;
531 
532 	ret = krb5_copy_principal(context,
533 				  in_creds->client,
534 				  &out_creds->client);
535 	if(ret)
536 	    goto out2;
537 	ret = krb5_copy_principal(context,
538 				  in_creds->server,
539 				  &out_creds->server);
540 	if(ret)
541 	    goto out2;
542 	/* this should go someplace else */
543 	out_creds->times.endtime = in_creds->times.endtime;
544 
545 	/* XXX should do better testing */
546 	if (flags.b.constrained_delegation || impersonate_principal)
547 	    eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
548 
549 	ret = _krb5_extract_ticket(context,
550 				   &rep,
551 				   out_creds,
552 				   &krbtgt->session,
553 				   NULL,
554 				   0,
555 				   &krbtgt->addresses,
556 				   nonce,
557 				   eflags,
558 				   decrypt_tkt_with_subkey,
559 				   subkey);
560     out2:
561 	krb5_free_kdc_rep(context, &rep);
562     } else if(krb5_rd_error(context, &resp, &error) == 0) {
563 	ret = krb5_error_from_rd_error(context, &error, in_creds);
564 	krb5_free_error_contents(context, &error);
565     } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
566 	ret = KRB5KRB_AP_ERR_V4_REPLY;
567 	krb5_clear_error_message(context);
568     } else {
569 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
570 	krb5_clear_error_message(context);
571     }
572 
573 out:
574     if (second_ticket == &second_ticket_data)
575 	free_Ticket(&second_ticket_data);
576     free_METHOD_DATA(&padata);
577     krb5_data_free(&resp);
578     krb5_data_free(&enc);
579     if(subkey)
580 	krb5_free_keyblock(context, subkey);
581     return ret;
582 
583 }
584 
585 /*
586  * same as above, just get local addresses first if the krbtgt have
587  * them and the realm is not addressless
588  */
589 
590 static krb5_error_code
591 get_cred_kdc_address(krb5_context context,
592 		     krb5_ccache id,
593 		     krb5_kdc_flags flags,
594 		     krb5_addresses *addrs,
595 		     krb5_creds *in_creds,
596 		     krb5_creds *krbtgt,
597 		     krb5_principal impersonate_principal,
598 		     Ticket *second_ticket,
599 		     krb5_creds *out_creds)
600 {
601     krb5_error_code ret;
602     krb5_addresses addresses = { 0, NULL };
603 
604     /*
605      * Inherit the address-ness of the krbtgt if the address is not
606      * specified.
607      */
608 
609     if (addrs == NULL && krbtgt->addresses.len != 0) {
610 	krb5_boolean noaddr;
611 
612 	krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
613 				"no-addresses", FALSE, &noaddr);
614 
615 	if (!noaddr) {
616 	    krb5_get_all_client_addrs(context, &addresses);
617 	    /* XXX this sucks. */
618 	    addrs = &addresses;
619 	    if(addresses.len == 0)
620 		addrs = NULL;
621 	}
622     }
623     ret = get_cred_kdc(context, id, flags, addrs, in_creds,
624 		       krbtgt, impersonate_principal,
625 		       second_ticket, out_creds);
626     krb5_free_addresses(context, &addresses);
627     return ret;
628 }
629 
630 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
631 krb5_get_kdc_cred(krb5_context context,
632 		  krb5_ccache id,
633 		  krb5_kdc_flags flags,
634 		  krb5_addresses *addresses,
635 		  Ticket  *second_ticket,
636 		  krb5_creds *in_creds,
637 		  krb5_creds **out_creds
638 		  )
639 {
640     krb5_error_code ret;
641     krb5_creds *krbtgt;
642 
643     *out_creds = calloc(1, sizeof(**out_creds));
644     if(*out_creds == NULL) {
645 	krb5_set_error_message(context, ENOMEM,
646 			       N_("malloc: out of memory", ""));
647 	return ENOMEM;
648     }
649     ret = _krb5_get_krbtgt (context,
650 			    id,
651 			    in_creds->server->realm,
652 			    &krbtgt);
653     if(ret) {
654 	free(*out_creds);
655 	*out_creds = NULL;
656 	return ret;
657     }
658     ret = get_cred_kdc(context, id, flags, addresses,
659 		       in_creds, krbtgt, NULL, NULL, *out_creds);
660     krb5_free_creds (context, krbtgt);
661     if(ret) {
662 	free(*out_creds);
663 	*out_creds = NULL;
664     }
665     return ret;
666 }
667 
668 static int
669 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
670 {
671     krb5_error_code ret;
672     char *str;
673 
674     ret = krb5_unparse_name(context, p, &str);
675     if(ret) {
676 	krb5_clear_error_message(context);
677 	return code;
678     }
679     krb5_set_error_message(context, code,
680 			   N_("Matching credential (%s) not found", ""), str);
681     free(str);
682     return code;
683 }
684 
685 static krb5_error_code
686 find_cred(krb5_context context,
687 	  krb5_ccache id,
688 	  krb5_principal server,
689 	  krb5_creds **tgts,
690 	  krb5_creds *out_creds)
691 {
692     krb5_error_code ret;
693     krb5_creds mcreds;
694 
695     krb5_cc_clear_mcred(&mcreds);
696     mcreds.server = server;
697     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
698 				&mcreds, out_creds);
699     if(ret == 0)
700 	return 0;
701     while(tgts && *tgts){
702 	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
703 			      &mcreds, *tgts)){
704 	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
705 	    return ret;
706 	}
707 	tgts++;
708     }
709     return not_found(context, server, KRB5_CC_NOTFOUND);
710 }
711 
712 static krb5_error_code
713 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
714 {
715     int i;
716     krb5_error_code ret;
717     krb5_creds **tmp = *tgts;
718 
719     for(i = 0; tmp && tmp[i]; i++); /* XXX */
720     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
721     if(tmp == NULL) {
722 	krb5_set_error_message(context, ENOMEM,
723 			       N_("malloc: out of memory", ""));
724 	return ENOMEM;
725     }
726     *tgts = tmp;
727     ret = krb5_copy_creds(context, tkt, &tmp[i]);
728     tmp[i+1] = NULL;
729     return ret;
730 }
731 
732 static krb5_error_code
733 get_cred_kdc_capath_worker(krb5_context context,
734                            krb5_kdc_flags flags,
735                            krb5_ccache ccache,
736                            krb5_creds *in_creds,
737                            krb5_const_realm try_realm,
738                            krb5_principal impersonate_principal,
739                            Ticket *second_ticket,
740                            krb5_creds **out_creds,
741                            krb5_creds ***ret_tgts)
742 {
743     krb5_error_code ret;
744     krb5_creds *tgt, tmp_creds;
745     krb5_const_realm client_realm, server_realm;
746     int ok_as_delegate = 1;
747 
748     *out_creds = NULL;
749 
750     client_realm = krb5_principal_get_realm(context, in_creds->client);
751     server_realm = krb5_principal_get_realm(context, in_creds->server);
752     memset(&tmp_creds, 0, sizeof(tmp_creds));
753     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
754     if(ret)
755 	return ret;
756 
757     ret = krb5_make_principal(context,
758 			      &tmp_creds.server,
759 			      try_realm,
760 			      KRB5_TGS_NAME,
761 			      server_realm,
762 			      NULL);
763     if(ret){
764 	krb5_free_principal(context, tmp_creds.client);
765 	return ret;
766     }
767     {
768 	krb5_creds tgts;
769 
770 	ret = find_cred(context, ccache, tmp_creds.server,
771 			*ret_tgts, &tgts);
772 	if(ret == 0){
773 	    /* only allow implicit ok_as_delegate if the realm is the clients realm */
774 	    if (strcmp(try_realm, client_realm) != 0 || strcmp(try_realm, server_realm) != 0)
775 		ok_as_delegate = tgts.flags.b.ok_as_delegate;
776 
777 	    *out_creds = calloc(1, sizeof(**out_creds));
778 	    if(*out_creds == NULL) {
779 		ret = ENOMEM;
780 		krb5_set_error_message(context, ret,
781 				       N_("malloc: out of memory", ""));
782 	    } else {
783 		ret = get_cred_kdc_address(context, ccache, flags, NULL,
784 					   in_creds, &tgts,
785 					   impersonate_principal,
786 					   second_ticket,
787 					   *out_creds);
788 		if (ret) {
789 		    free (*out_creds);
790 		    *out_creds = NULL;
791 		} else if (ok_as_delegate == 0)
792 		    (*out_creds)->flags.b.ok_as_delegate = 0;
793 	    }
794 	    krb5_free_cred_contents(context, &tgts);
795 	    krb5_free_principal(context, tmp_creds.server);
796 	    krb5_free_principal(context, tmp_creds.client);
797 	    return ret;
798 	}
799     }
800     if(krb5_realm_compare(context, in_creds->client, in_creds->server))
801 	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
802 
803     /* XXX this can loop forever */
804     while(1){
805 	heim_general_string tgt_inst;
806 
807 	ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
808 				  NULL, NULL, &tgt, ret_tgts);
809 	if(ret) {
810 	    krb5_free_principal(context, tmp_creds.server);
811 	    krb5_free_principal(context, tmp_creds.client);
812 	    return ret;
813 	}
814 	/*
815 	 * if either of the chain or the ok_as_delegate was stripped
816 	 * by the kdc, make sure we strip it too.
817 	 */
818 	if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
819 	    ok_as_delegate = 0;
820 	    tgt->flags.b.ok_as_delegate = 0;
821 	}
822 
823 	ret = add_cred(context, tgt, ret_tgts);
824 	if(ret) {
825 	    krb5_free_principal(context, tmp_creds.server);
826 	    krb5_free_principal(context, tmp_creds.client);
827 	    return ret;
828 	}
829 	tgt_inst = tgt->server->name.name_string.val[1];
830 	if(strcmp(tgt_inst, server_realm) == 0)
831 	    break;
832 	krb5_free_principal(context, tmp_creds.server);
833 	ret = krb5_make_principal(context, &tmp_creds.server,
834 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
835 	if(ret) {
836 	    krb5_free_principal(context, tmp_creds.server);
837 	    krb5_free_principal(context, tmp_creds.client);
838 	    return ret;
839 	}
840 	ret = krb5_free_creds(context, tgt);
841 	if(ret) {
842 	    krb5_free_principal(context, tmp_creds.server);
843 	    krb5_free_principal(context, tmp_creds.client);
844 	    return ret;
845 	}
846     }
847 
848     krb5_free_principal(context, tmp_creds.server);
849     krb5_free_principal(context, tmp_creds.client);
850     *out_creds = calloc(1, sizeof(**out_creds));
851     if(*out_creds == NULL) {
852 	ret = ENOMEM;
853 	krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
854     } else {
855 	ret = get_cred_kdc_address (context, ccache, flags, NULL,
856 				    in_creds, tgt, impersonate_principal,
857 				    second_ticket, *out_creds);
858 	if (ret) {
859 	    free (*out_creds);
860 	    *out_creds = NULL;
861 	}
862     }
863     krb5_free_creds(context, tgt);
864     return ret;
865 }
866 
867 /*
868 get_cred(server)
869 	creds = cc_get_cred(server)
870 	if(creds) return creds
871 	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
872 	if(tgt)
873 		return get_cred_tgt(server, tgt)
874 	if(client_realm == server_realm)
875 		return NULL
876 	tgt = get_cred(krbtgt/server_realm@client_realm)
877 	while(tgt_inst != server_realm)
878 		tgt = get_cred(krbtgt/server_realm@tgt_inst)
879 	return get_cred_tgt(server, tgt)
880 	*/
881 
882 static krb5_error_code
883 get_cred_kdc_capath(krb5_context context,
884 		    krb5_kdc_flags flags,
885 		    krb5_ccache ccache,
886 		    krb5_creds *in_creds,
887 		    krb5_principal impersonate_principal,
888 		    Ticket *second_ticket,
889 		    krb5_creds **out_creds,
890 		    krb5_creds ***ret_tgts)
891 {
892     krb5_error_code ret;
893     krb5_const_realm client_realm, server_realm, try_realm;
894 
895     client_realm = krb5_principal_get_realm(context, in_creds->client);
896     server_realm = krb5_principal_get_realm(context, in_creds->server);
897 
898     try_realm = client_realm;
899     ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
900                                      impersonate_principal, second_ticket, out_creds,
901                                      ret_tgts);
902 
903     if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
904         try_realm = krb5_config_get_string(context, NULL, "capaths",
905                                            client_realm, server_realm, NULL);
906 
907         if (try_realm != NULL && strcmp(try_realm, client_realm)) {
908             ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
909                                              try_realm, impersonate_principal,
910                                              second_ticket, out_creds, ret_tgts);
911         }
912     }
913 
914     return ret;
915 }
916 
917 static krb5_error_code
918 get_cred_kdc_referral(krb5_context context,
919 		      krb5_kdc_flags flags,
920 		      krb5_ccache ccache,
921 		      krb5_creds *in_creds,
922 		      krb5_principal impersonate_principal,
923 		      Ticket *second_ticket,
924 		      krb5_creds **out_creds,
925 		      krb5_creds ***ret_tgts)
926 {
927     krb5_const_realm client_realm;
928     krb5_error_code ret;
929     krb5_creds tgt, referral, ticket;
930     int loop = 0;
931     int ok_as_delegate = 1;
932 
933     if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
934 	krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
935 			       N_("Name too short to do referals, skipping", ""));
936 	return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
937     }
938 
939     memset(&tgt, 0, sizeof(tgt));
940     memset(&ticket, 0, sizeof(ticket));
941 
942     flags.b.canonicalize = 1;
943 
944     *out_creds = NULL;
945 
946     client_realm = krb5_principal_get_realm(context, in_creds->client);
947 
948     /* find tgt for the clients base realm */
949     {
950 	krb5_principal tgtname;
951 
952 	ret = krb5_make_principal(context, &tgtname,
953 				  client_realm,
954 				  KRB5_TGS_NAME,
955 				  client_realm,
956 				  NULL);
957 	if(ret)
958 	    return ret;
959 
960 	ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
961 	krb5_free_principal(context, tgtname);
962 	if (ret)
963 	    return ret;
964     }
965 
966     referral = *in_creds;
967     ret = krb5_copy_principal(context, in_creds->server, &referral.server);
968     if (ret) {
969 	krb5_free_cred_contents(context, &tgt);
970 	return ret;
971     }
972     ret = krb5_principal_set_realm(context, referral.server, client_realm);
973     if (ret) {
974 	krb5_free_cred_contents(context, &tgt);
975 	krb5_free_principal(context, referral.server);
976 	return ret;
977     }
978 
979     while (loop++ < 17) {
980 	krb5_creds **tickets;
981 	krb5_creds mcreds;
982 	char *referral_realm;
983 
984 	/* Use cache if we are not doing impersonation or contrainte deleg */
985 	if (impersonate_principal == NULL || flags.b.constrained_delegation) {
986 	    krb5_cc_clear_mcred(&mcreds);
987 	    mcreds.server = referral.server;
988 	    ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
989 	} else
990 	    ret = EINVAL;
991 
992 	if (ret) {
993 	    ret = get_cred_kdc_address(context, ccache, flags, NULL,
994 				       &referral, &tgt, impersonate_principal,
995 				       second_ticket, &ticket);
996 	    if (ret)
997 		goto out;
998 	}
999 
1000 	/* Did we get the right ticket ? */
1001 	if (krb5_principal_compare_any_realm(context,
1002 					     referral.server,
1003 					     ticket.server))
1004 	    break;
1005 
1006 	if (!krb5_principal_is_krbtgt(context, ticket.server)) {
1007 	    krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
1008 				   N_("Got back an non krbtgt "
1009 				      "ticket referrals", ""));
1010 	    ret = KRB5KRB_AP_ERR_NOT_US;
1011 	    goto out;
1012 	}
1013 
1014 	referral_realm = ticket.server->name.name_string.val[1];
1015 
1016 	/* check that there are no referrals loops */
1017 	tickets = *ret_tgts;
1018 
1019 	krb5_cc_clear_mcred(&mcreds);
1020 	mcreds.server = ticket.server;
1021 
1022 	while(tickets && *tickets){
1023 	    if(krb5_compare_creds(context,
1024 				  KRB5_TC_DONT_MATCH_REALM,
1025 				  &mcreds,
1026 				  *tickets))
1027 	    {
1028 		krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1029 				       N_("Referral from %s "
1030 					  "loops back to realm %s", ""),
1031 				       tgt.server->realm,
1032 				       referral_realm);
1033 		ret = KRB5_GET_IN_TKT_LOOP;
1034                 goto out;
1035 	    }
1036 	    tickets++;
1037 	}
1038 
1039 	/*
1040 	 * if either of the chain or the ok_as_delegate was stripped
1041 	 * by the kdc, make sure we strip it too.
1042 	 */
1043 
1044 	if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1045 	    ok_as_delegate = 0;
1046 	    ticket.flags.b.ok_as_delegate = 0;
1047 	}
1048 
1049 	ret = add_cred(context, &ticket, ret_tgts);
1050 	if (ret)
1051 	    goto out;
1052 
1053 	/* try realm in the referral */
1054 	ret = krb5_principal_set_realm(context,
1055 				       referral.server,
1056 				       referral_realm);
1057 	krb5_free_cred_contents(context, &tgt);
1058 	tgt = ticket;
1059 	memset(&ticket, 0, sizeof(ticket));
1060 	if (ret)
1061 	    goto out;
1062     }
1063 
1064     ret = krb5_copy_creds(context, &ticket, out_creds);
1065 
1066 out:
1067     krb5_free_principal(context, referral.server);
1068     krb5_free_cred_contents(context, &tgt);
1069     krb5_free_cred_contents(context, &ticket);
1070     return ret;
1071 }
1072 
1073 
1074 /*
1075  * Glue function between referrals version and old client chasing
1076  * codebase.
1077  */
1078 
1079 krb5_error_code
1080 _krb5_get_cred_kdc_any(krb5_context context,
1081 		       krb5_kdc_flags flags,
1082 		       krb5_ccache ccache,
1083 		       krb5_creds *in_creds,
1084 		       krb5_principal impersonate_principal,
1085 		       Ticket *second_ticket,
1086 		       krb5_creds **out_creds,
1087 		       krb5_creds ***ret_tgts)
1088 {
1089     krb5_error_code ret;
1090     krb5_deltat offset;
1091 
1092     ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
1093     if (ret) {
1094 	context->kdc_sec_offset = offset;
1095 	context->kdc_usec_offset = 0;
1096     }
1097 
1098     ret = get_cred_kdc_referral(context,
1099 				flags,
1100 				ccache,
1101 				in_creds,
1102 				impersonate_principal,
1103 				second_ticket,
1104 				out_creds,
1105 				ret_tgts);
1106     if (ret == 0 || flags.b.canonicalize)
1107 	return ret;
1108     return get_cred_kdc_capath(context,
1109 				flags,
1110 				ccache,
1111 				in_creds,
1112 				impersonate_principal,
1113 				second_ticket,
1114 				out_creds,
1115 				ret_tgts);
1116 }
1117 
1118 
1119 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1120 krb5_get_credentials_with_flags(krb5_context context,
1121 				krb5_flags options,
1122 				krb5_kdc_flags flags,
1123 				krb5_ccache ccache,
1124 				krb5_creds *in_creds,
1125 				krb5_creds **out_creds)
1126 {
1127     krb5_error_code ret;
1128     krb5_creds **tgts;
1129     krb5_creds *res_creds;
1130     int i;
1131 
1132     if (in_creds->session.keytype) {
1133 	ret = krb5_enctype_valid(context, in_creds->session.keytype);
1134 	if (ret)
1135 	    return ret;
1136     }
1137 
1138     *out_creds = NULL;
1139     res_creds = calloc(1, sizeof(*res_creds));
1140     if (res_creds == NULL) {
1141 	krb5_set_error_message(context, ENOMEM,
1142 			       N_("malloc: out of memory", ""));
1143 	return ENOMEM;
1144     }
1145 
1146     if (in_creds->session.keytype)
1147 	options |= KRB5_TC_MATCH_KEYTYPE;
1148 
1149     /*
1150      * If we got a credential, check if credential is expired before
1151      * returning it.
1152      */
1153     ret = krb5_cc_retrieve_cred(context,
1154                                 ccache,
1155                                 in_creds->session.keytype ?
1156                                 KRB5_TC_MATCH_KEYTYPE : 0,
1157                                 in_creds, res_creds);
1158     /*
1159      * If we got a credential, check if credential is expired before
1160      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1161      */
1162     if (ret == 0) {
1163 	krb5_timestamp timeret;
1164 
1165 	/* If expired ok, don't bother checking */
1166         if(options & KRB5_GC_EXPIRED_OK) {
1167             *out_creds = res_creds;
1168             return 0;
1169         }
1170 
1171 	krb5_timeofday(context, &timeret);
1172 	if(res_creds->times.endtime > timeret) {
1173 	    *out_creds = res_creds;
1174 	    return 0;
1175 	}
1176 	if(options & KRB5_GC_CACHED)
1177 	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1178 
1179     } else if(ret != KRB5_CC_END) {
1180         free(res_creds);
1181         return ret;
1182     }
1183     free(res_creds);
1184     if(options & KRB5_GC_CACHED)
1185 	return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1186 
1187     if(options & KRB5_GC_USER_USER)
1188 	flags.b.enc_tkt_in_skey = 1;
1189     if (flags.b.enc_tkt_in_skey)
1190 	options |= KRB5_GC_NO_STORE;
1191 
1192     tgts = NULL;
1193     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1194 				 in_creds, NULL, NULL, out_creds, &tgts);
1195     for(i = 0; tgts && tgts[i]; i++) {
1196 	krb5_cc_store_cred(context, ccache, tgts[i]);
1197 	krb5_free_creds(context, tgts[i]);
1198     }
1199     free(tgts);
1200     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1201 	krb5_cc_store_cred(context, ccache, *out_creds);
1202     return ret;
1203 }
1204 
1205 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1206 krb5_get_credentials(krb5_context context,
1207 		     krb5_flags options,
1208 		     krb5_ccache ccache,
1209 		     krb5_creds *in_creds,
1210 		     krb5_creds **out_creds)
1211 {
1212     krb5_kdc_flags flags;
1213     flags.i = 0;
1214     return krb5_get_credentials_with_flags(context, options, flags,
1215 					   ccache, in_creds, out_creds);
1216 }
1217 
1218 struct krb5_get_creds_opt_data {
1219     krb5_principal self;
1220     krb5_flags options;
1221     krb5_enctype enctype;
1222     Ticket *ticket;
1223 };
1224 
1225 
1226 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1227 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1228 {
1229     *opt = calloc(1, sizeof(**opt));
1230     if (*opt == NULL) {
1231 	krb5_set_error_message(context, ENOMEM,
1232 			       N_("malloc: out of memory", ""));
1233 	return ENOMEM;
1234     }
1235     return 0;
1236 }
1237 
1238 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1239 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1240 {
1241     if (opt->self)
1242 	krb5_free_principal(context, opt->self);
1243     if (opt->ticket) {
1244 	free_Ticket(opt->ticket);
1245 	free(opt->ticket);
1246     }
1247     memset(opt, 0, sizeof(*opt));
1248     free(opt);
1249 }
1250 
1251 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1252 krb5_get_creds_opt_set_options(krb5_context context,
1253 			       krb5_get_creds_opt opt,
1254 			       krb5_flags options)
1255 {
1256     opt->options = options;
1257 }
1258 
1259 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1260 krb5_get_creds_opt_add_options(krb5_context context,
1261 			       krb5_get_creds_opt opt,
1262 			       krb5_flags options)
1263 {
1264     opt->options |= options;
1265 }
1266 
1267 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1268 krb5_get_creds_opt_set_enctype(krb5_context context,
1269 			       krb5_get_creds_opt opt,
1270 			       krb5_enctype enctype)
1271 {
1272     opt->enctype = enctype;
1273 }
1274 
1275 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1276 krb5_get_creds_opt_set_impersonate(krb5_context context,
1277 				   krb5_get_creds_opt opt,
1278 				   krb5_const_principal self)
1279 {
1280     if (opt->self)
1281 	krb5_free_principal(context, opt->self);
1282     return krb5_copy_principal(context, self, &opt->self);
1283 }
1284 
1285 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1286 krb5_get_creds_opt_set_ticket(krb5_context context,
1287 			      krb5_get_creds_opt opt,
1288 			      const Ticket *ticket)
1289 {
1290     if (opt->ticket) {
1291 	free_Ticket(opt->ticket);
1292 	free(opt->ticket);
1293 	opt->ticket = NULL;
1294     }
1295     if (ticket) {
1296 	krb5_error_code ret;
1297 
1298 	opt->ticket = malloc(sizeof(*ticket));
1299 	if (opt->ticket == NULL) {
1300 	    krb5_set_error_message(context, ENOMEM,
1301 				   N_("malloc: out of memory", ""));
1302 	    return ENOMEM;
1303 	}
1304 	ret = copy_Ticket(ticket, opt->ticket);
1305 	if (ret) {
1306 	    free(opt->ticket);
1307 	    opt->ticket = NULL;
1308 	    krb5_set_error_message(context, ret,
1309 				   N_("malloc: out of memory", ""));
1310 	    return ret;
1311 	}
1312     }
1313     return 0;
1314 }
1315 
1316 
1317 
1318 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1319 krb5_get_creds(krb5_context context,
1320 	       krb5_get_creds_opt opt,
1321 	       krb5_ccache ccache,
1322 	       krb5_const_principal inprinc,
1323 	       krb5_creds **out_creds)
1324 {
1325     krb5_kdc_flags flags;
1326     krb5_flags options;
1327     krb5_creds in_creds;
1328     krb5_error_code ret;
1329     krb5_creds **tgts;
1330     krb5_creds *res_creds;
1331     int i;
1332 
1333     if (opt && opt->enctype) {
1334 	ret = krb5_enctype_valid(context, opt->enctype);
1335 	if (ret)
1336 	    return ret;
1337     }
1338 
1339     memset(&in_creds, 0, sizeof(in_creds));
1340     in_creds.server = rk_UNCONST(inprinc);
1341 
1342     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1343     if (ret)
1344 	return ret;
1345 
1346     if (opt)
1347 	options = opt->options;
1348     else
1349 	options = 0;
1350     flags.i = 0;
1351 
1352     *out_creds = NULL;
1353     res_creds = calloc(1, sizeof(*res_creds));
1354     if (res_creds == NULL) {
1355 	krb5_free_principal(context, in_creds.client);
1356 	krb5_set_error_message(context, ENOMEM,
1357 			       N_("malloc: out of memory", ""));
1358 	return ENOMEM;
1359     }
1360 
1361     if (opt && opt->enctype) {
1362 	in_creds.session.keytype = opt->enctype;
1363 	options |= KRB5_TC_MATCH_KEYTYPE;
1364     }
1365 
1366     /*
1367      * If we got a credential, check if credential is expired before
1368      * returning it.
1369      */
1370     ret = krb5_cc_retrieve_cred(context,
1371                                 ccache,
1372 				options & KRB5_TC_MATCH_KEYTYPE,
1373                                 &in_creds, res_creds);
1374     /*
1375      * If we got a credential, check if credential is expired before
1376      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1377      */
1378     if (ret == 0) {
1379 	krb5_timestamp timeret;
1380 
1381 	/* If expired ok, don't bother checking */
1382         if(options & KRB5_GC_EXPIRED_OK) {
1383             *out_creds = res_creds;
1384 	    krb5_free_principal(context, in_creds.client);
1385             goto out;
1386         }
1387 
1388 	krb5_timeofday(context, &timeret);
1389 	if(res_creds->times.endtime > timeret) {
1390 	    *out_creds = res_creds;
1391 	    krb5_free_principal(context, in_creds.client);
1392             goto out;
1393 	}
1394 	if(options & KRB5_GC_CACHED)
1395 	    krb5_cc_remove_cred(context, ccache, 0, res_creds);
1396 
1397     } else if(ret != KRB5_CC_END) {
1398         free(res_creds);
1399 	krb5_free_principal(context, in_creds.client);
1400 	goto out;
1401     }
1402     free(res_creds);
1403     if(options & KRB5_GC_CACHED) {
1404 	krb5_free_principal(context, in_creds.client);
1405 	ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1406 	goto out;
1407     }
1408     if(options & KRB5_GC_USER_USER) {
1409 	flags.b.enc_tkt_in_skey = 1;
1410 	options |= KRB5_GC_NO_STORE;
1411     }
1412     if (options & KRB5_GC_FORWARDABLE)
1413 	flags.b.forwardable = 1;
1414     if (options & KRB5_GC_NO_TRANSIT_CHECK)
1415 	flags.b.disable_transited_check = 1;
1416     if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1417 	flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1418 	flags.b.constrained_delegation = 1;
1419     }
1420     if (options & KRB5_GC_CANONICALIZE)
1421 	flags.b.canonicalize = 1;
1422 
1423     tgts = NULL;
1424     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1425 				 &in_creds, opt->self, opt->ticket,
1426 				 out_creds, &tgts);
1427     krb5_free_principal(context, in_creds.client);
1428     for(i = 0; tgts && tgts[i]; i++) {
1429 	krb5_cc_store_cred(context, ccache, tgts[i]);
1430 	krb5_free_creds(context, tgts[i]);
1431     }
1432     free(tgts);
1433     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1434 	krb5_cc_store_cred(context, ccache, *out_creds);
1435 
1436  out:
1437     _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
1438 
1439     return ret;
1440 }
1441 
1442 /*
1443  *
1444  */
1445 
1446 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1447 krb5_get_renewed_creds(krb5_context context,
1448 		       krb5_creds *creds,
1449 		       krb5_const_principal client,
1450 		       krb5_ccache ccache,
1451 		       const char *in_tkt_service)
1452 {
1453     krb5_error_code ret;
1454     krb5_kdc_flags flags;
1455     krb5_creds in, *template, *out = NULL;
1456 
1457     memset(&in, 0, sizeof(in));
1458     memset(creds, 0, sizeof(*creds));
1459 
1460     ret = krb5_copy_principal(context, client, &in.client);
1461     if (ret)
1462 	return ret;
1463 
1464     if (in_tkt_service) {
1465 	ret = krb5_parse_name(context, in_tkt_service, &in.server);
1466 	if (ret) {
1467 	    krb5_free_principal(context, in.client);
1468 	    return ret;
1469 	}
1470     } else {
1471 	const char *realm = krb5_principal_get_realm(context, client);
1472 
1473 	ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1474 				  realm, NULL);
1475 	if (ret) {
1476 	    krb5_free_principal(context, in.client);
1477 	    return ret;
1478 	}
1479     }
1480 
1481     flags.i = 0;
1482     flags.b.renewable = flags.b.renew = 1;
1483 
1484     /*
1485      * Get template from old credential cache for the same entry, if
1486      * this failes, no worries.
1487      */
1488     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1489     if (ret == 0) {
1490 	flags.b.forwardable = template->flags.b.forwardable;
1491 	flags.b.proxiable = template->flags.b.proxiable;
1492 	krb5_free_creds (context, template);
1493     }
1494 
1495     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1496     krb5_free_principal(context, in.client);
1497     krb5_free_principal(context, in.server);
1498     if (ret)
1499 	return ret;
1500 
1501     ret = krb5_copy_creds_contents(context, out, creds);
1502     krb5_free_creds(context, out);
1503 
1504     return ret;
1505 }
1506