xref: /freebsd/crypto/heimdal/lib/krb5/get_cred.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /*
2  * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <krb5_locl.h>
35 
36 RCSID("$Id: get_cred.c,v 1.85 2001/05/14 06:14:46 assar Exp $");
37 
38 /*
39  * Take the `body' and encode it into `padata' using the credentials
40  * in `creds'.
41  */
42 
43 static krb5_error_code
44 make_pa_tgs_req(krb5_context context,
45 		krb5_auth_context ac,
46 		KDC_REQ_BODY *body,
47 		PA_DATA *padata,
48 		krb5_creds *creds,
49 		krb5_key_usage usage)
50 {
51     u_char *buf;
52     size_t buf_size;
53     size_t len;
54     krb5_data in_data;
55     krb5_error_code ret;
56 
57     buf_size = 1024;
58     buf = malloc (buf_size);
59     if (buf == NULL) {
60 	krb5_set_error_string(context, "malloc: out of memory");
61 	return ENOMEM;
62     }
63 
64     do {
65 	ret = encode_KDC_REQ_BODY(buf + buf_size - 1, buf_size,
66 				  body, &len);
67 	if (ret){
68 	    if (ret == ASN1_OVERFLOW) {
69 		u_char *tmp;
70 
71 		buf_size *= 2;
72 		tmp = realloc (buf, buf_size);
73 		if (tmp == NULL) {
74 		    krb5_set_error_string(context, "malloc: out of memory");
75 		    ret = ENOMEM;
76 		    goto out;
77 		}
78 		buf = tmp;
79 	    } else {
80 		goto out;
81 	    }
82 	}
83     } while (ret == ASN1_OVERFLOW);
84 
85     in_data.length = len;
86     in_data.data   = buf + buf_size - len;
87     ret = krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
88 			       &padata->padata_value,
89 			       KRB5_KU_TGS_REQ_AUTH_CKSUM,
90 			       usage
91 			       /* KRB5_KU_TGS_REQ_AUTH */);
92 out:
93     free (buf);
94     if(ret)
95 	return ret;
96     padata->padata_type = KRB5_PADATA_TGS_REQ;
97     return 0;
98 }
99 
100 /*
101  * Set the `enc-authorization-data' in `req_body' based on `authdata'
102  */
103 
104 static krb5_error_code
105 set_auth_data (krb5_context context,
106 	       KDC_REQ_BODY *req_body,
107 	       krb5_authdata *authdata,
108 	       krb5_keyblock *key)
109 {
110     if(authdata->len) {
111 	size_t len;
112 	unsigned char *buf;
113 	krb5_crypto crypto;
114 	krb5_error_code ret;
115 
116 	len = length_AuthorizationData(authdata);
117 	buf = malloc(len);
118 	if (buf == NULL) {
119 	    krb5_set_error_string(context, "malloc: out of memory");
120 	    return ENOMEM;
121 	}
122 	ret = encode_AuthorizationData(buf + len - 1,
123 				       len, authdata, &len);
124 	if (ret) {
125 	    free (buf);
126 	    return ret;
127 	}
128 
129 	ALLOC(req_body->enc_authorization_data, 1);
130 	if (req_body->enc_authorization_data == NULL) {
131 	    free (buf);
132 	    krb5_set_error_string(context, "malloc: out of memory");
133 	    return ENOMEM;
134 	}
135 	ret = krb5_crypto_init(context, key, 0, &crypto);
136 	if (ret) {
137 	    free (buf);
138 	    free (req_body->enc_authorization_data);
139 	    return ret;
140 	}
141 	krb5_encrypt_EncryptedData(context,
142 				   crypto,
143 				   KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
144 				   /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
145 				   buf,
146 				   len,
147 				   0,
148 				   req_body->enc_authorization_data);
149 	free (buf);
150 	krb5_crypto_destroy(context, crypto);
151     } else {
152 	req_body->enc_authorization_data = NULL;
153     }
154     return 0;
155 }
156 
157 /*
158  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
159  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
160  * subkey in `subkey'.
161  */
162 
163 static krb5_error_code
164 init_tgs_req (krb5_context context,
165 	      krb5_ccache ccache,
166 	      krb5_addresses *addresses,
167 	      krb5_kdc_flags flags,
168 	      Ticket *second_ticket,
169 	      krb5_creds *in_creds,
170 	      krb5_creds *krbtgt,
171 	      unsigned nonce,
172 	      krb5_keyblock **subkey,
173 	      TGS_REQ *t,
174 	      krb5_key_usage usage)
175 {
176     krb5_error_code ret;
177 
178     memset(t, 0, sizeof(*t));
179     t->pvno = 5;
180     t->msg_type = krb_tgs_req;
181     if (in_creds->session.keytype) {
182 	ret = krb5_keytype_to_enctypes_default (context,
183 						in_creds->session.keytype,
184 						&t->req_body.etype.len,
185 						&t->req_body.etype.val);
186     } else {
187 	ret = krb5_init_etype(context,
188 			      &t->req_body.etype.len,
189 			      &t->req_body.etype.val,
190 			      NULL);
191     }
192     if (ret)
193 	goto fail;
194     t->req_body.addresses = addresses;
195     t->req_body.kdc_options = flags.b;
196     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
197     if (ret)
198 	goto fail;
199     ALLOC(t->req_body.sname, 1);
200     if (t->req_body.sname == NULL) {
201 	ret = ENOMEM;
202 	krb5_set_error_string(context, "malloc: out of memory");
203 	goto fail;
204     }
205 
206     /* some versions of some code might require that the client be
207        present in TGS-REQs, but this is clearly against the spec */
208 
209     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
210     if (ret)
211 	goto fail;
212 
213     /* req_body.till should be NULL if there is no endtime specified,
214        but old MIT code (like DCE secd) doesn't like that */
215     ALLOC(t->req_body.till, 1);
216     if(t->req_body.till == NULL){
217 	ret = ENOMEM;
218 	krb5_set_error_string(context, "malloc: out of memory");
219 	goto fail;
220     }
221     *t->req_body.till = in_creds->times.endtime;
222 
223     t->req_body.nonce = nonce;
224     if(second_ticket){
225 	ALLOC(t->req_body.additional_tickets, 1);
226 	if (t->req_body.additional_tickets == NULL) {
227 	    ret = ENOMEM;
228 	    krb5_set_error_string(context, "malloc: out of memory");
229 	    goto fail;
230 	}
231 	ALLOC_SEQ(t->req_body.additional_tickets, 1);
232 	if (t->req_body.additional_tickets->val == NULL) {
233 	    ret = ENOMEM;
234 	    krb5_set_error_string(context, "malloc: out of memory");
235 	    goto fail;
236 	}
237 	ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
238 	if (ret)
239 	    goto fail;
240     }
241     ALLOC(t->padata, 1);
242     if (t->padata == NULL) {
243 	ret = ENOMEM;
244 	krb5_set_error_string(context, "malloc: out of memory");
245 	goto fail;
246     }
247     ALLOC_SEQ(t->padata, 1);
248     if (t->padata->val == NULL) {
249 	ret = ENOMEM;
250 	krb5_set_error_string(context, "malloc: out of memory");
251 	goto fail;
252     }
253 
254     {
255 	krb5_auth_context ac;
256 	krb5_keyblock *key;
257 
258 	ret = krb5_auth_con_init(context, &ac);
259 	if(ret)
260 	    goto fail;
261 	ret = krb5_generate_subkey (context, &krbtgt->session, &key);
262 	if (ret) {
263 	    krb5_auth_con_free (context, ac);
264 	    goto fail;
265 	}
266 	ret = krb5_auth_con_setlocalsubkey(context, ac, key);
267 	if (ret) {
268 	    krb5_free_keyblock (context, key);
269 	    krb5_auth_con_free (context, ac);
270 	    goto fail;
271 	}
272 
273 	ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key);
274 	if (ret) {
275 	    krb5_free_keyblock (context, key);
276 	    krb5_auth_con_free (context, ac);
277 	    goto fail;
278 	}
279 
280 	ret = make_pa_tgs_req(context,
281 			      ac,
282 			      &t->req_body,
283 			      t->padata->val,
284 			      krbtgt,
285 			      usage);
286 	if(ret) {
287 	    krb5_free_keyblock (context, key);
288 	    krb5_auth_con_free(context, ac);
289 	    goto fail;
290 	}
291 	*subkey = key;
292 
293 	krb5_auth_con_free(context, ac);
294     }
295 fail:
296     if (ret)
297 	/* XXX - don't free addresses? */
298 	free_TGS_REQ (t);
299     return ret;
300 }
301 
302 static krb5_error_code
303 get_krbtgt(krb5_context context,
304 	   krb5_ccache  id,
305 	   krb5_realm realm,
306 	   krb5_creds **cred)
307 {
308     krb5_error_code ret;
309     krb5_creds tmp_cred;
310 
311     memset(&tmp_cred, 0, sizeof(tmp_cred));
312 
313     ret = krb5_make_principal(context,
314 			      &tmp_cred.server,
315 			      realm,
316 			      KRB5_TGS_NAME,
317 			      realm,
318 			      NULL);
319     if(ret)
320 	return ret;
321     ret = krb5_get_credentials(context,
322 			       KRB5_GC_CACHED,
323 			       id,
324 			       &tmp_cred,
325 			       cred);
326     krb5_free_principal(context, tmp_cred.server);
327     if(ret)
328 	return ret;
329     return 0;
330 }
331 
332 /* DCE compatible decrypt proc */
333 static krb5_error_code
334 decrypt_tkt_with_subkey (krb5_context context,
335 			 krb5_keyblock *key,
336 			 krb5_key_usage usage,
337 			 krb5_const_pointer subkey,
338 			 krb5_kdc_rep *dec_rep)
339 {
340     krb5_error_code ret;
341     krb5_data data;
342     size_t size;
343     krb5_crypto crypto;
344 
345     ret = krb5_crypto_init(context, key, 0, &crypto);
346     if (ret)
347 	return ret;
348     ret = krb5_decrypt_EncryptedData (context,
349 				      crypto,
350 				      usage,
351 				      &dec_rep->kdc_rep.enc_part,
352 				      &data);
353     krb5_crypto_destroy(context, crypto);
354     if(ret && subkey){
355 	/* DCE compat -- try to decrypt with subkey */
356 	ret = krb5_crypto_init(context, (krb5_keyblock*)subkey, 0, &crypto);
357 	if (ret)
358 	    return ret;
359 	ret = krb5_decrypt_EncryptedData (context,
360 					  crypto,
361 					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
362 					  &dec_rep->kdc_rep.enc_part,
363 					  &data);
364 	krb5_crypto_destroy(context, crypto);
365     }
366     if (ret)
367 	return ret;
368 
369     ret = krb5_decode_EncASRepPart(context,
370 				   data.data,
371 				   data.length,
372 				   &dec_rep->enc_part,
373 				   &size);
374     if (ret)
375 	ret = krb5_decode_EncTGSRepPart(context,
376 					data.data,
377 					data.length,
378 					&dec_rep->enc_part,
379 					&size);
380     krb5_data_free (&data);
381     return ret;
382 }
383 
384 static krb5_error_code
385 get_cred_kdc_usage(krb5_context context,
386 		   krb5_ccache id,
387 		   krb5_kdc_flags flags,
388 		   krb5_addresses *addresses,
389 		   krb5_creds *in_creds,
390 		   krb5_creds *krbtgt,
391 		   krb5_creds *out_creds,
392 		   krb5_key_usage usage)
393 {
394     TGS_REQ req;
395     krb5_data enc;
396     krb5_data resp;
397     krb5_kdc_rep rep;
398     KRB_ERROR error;
399     krb5_error_code ret;
400     unsigned nonce;
401     krb5_keyblock *subkey = NULL;
402     u_char *buf = NULL;
403     size_t buf_size;
404     size_t len;
405     Ticket second_ticket;
406 
407     krb5_generate_random_block(&nonce, sizeof(nonce));
408     nonce &= 0xffffffff;
409 
410     if(flags.b.enc_tkt_in_skey){
411 	ret = decode_Ticket(in_creds->second_ticket.data,
412 			    in_creds->second_ticket.length,
413 			    &second_ticket, &len);
414 	if(ret)
415 	    return ret;
416     }
417 
418     ret = init_tgs_req (context,
419 			id,
420 			addresses,
421 			flags,
422 			flags.b.enc_tkt_in_skey ? &second_ticket : NULL,
423 			in_creds,
424 			krbtgt,
425 			nonce,
426 			&subkey,
427 			&req,
428 			usage);
429     if(flags.b.enc_tkt_in_skey)
430 	free_Ticket(&second_ticket);
431     if (ret)
432 	goto out;
433 
434     buf_size = 1024;
435     buf = malloc (buf_size);
436     if (buf == NULL) {
437 	krb5_set_error_string(context, "malloc: out of memory");
438 	ret = ENOMEM;
439 	goto out;
440     }
441 
442     do {
443 	ret = encode_TGS_REQ  (buf + buf_size - 1, buf_size,
444 			       &req, &enc.length);
445 	if (ret) {
446 	    if (ret == ASN1_OVERFLOW) {
447 		u_char *tmp;
448 
449 		buf_size *= 2;
450 		tmp = realloc (buf, buf_size);
451 		if (tmp == NULL) {
452 		    krb5_set_error_string(context, "malloc: out of memory");
453 		    ret = ENOMEM;
454 		    goto out;
455 		}
456 		buf = tmp;
457 	    } else {
458 		goto out;
459 	    }
460 	}
461     } while (ret == ASN1_OVERFLOW);
462 
463     /* don't free addresses */
464     req.req_body.addresses = NULL;
465     free_TGS_REQ(&req);
466 
467     enc.data = buf + buf_size - enc.length;
468     if (ret)
469 	goto out;
470 
471     /*
472      * Send and receive
473      */
474 
475     ret = krb5_sendto_kdc (context, &enc,
476 			   &krbtgt->server->name.name_string.val[1], &resp);
477     if(ret)
478 	goto out;
479 
480     memset(&rep, 0, sizeof(rep));
481     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
482 	ret = krb5_copy_principal(context,
483 				  in_creds->client,
484 				  &out_creds->client);
485 	if(ret)
486 	    goto out;
487 	ret = krb5_copy_principal(context,
488 				  in_creds->server,
489 				  &out_creds->server);
490 	if(ret)
491 	    goto out;
492 	/* this should go someplace else */
493 	out_creds->times.endtime = in_creds->times.endtime;
494 
495 	ret = _krb5_extract_ticket(context,
496 				   &rep,
497 				   out_creds,
498 				   &krbtgt->session,
499 				   NULL,
500 				   KRB5_KU_TGS_REP_ENC_PART_SESSION,
501 				   &krbtgt->addresses,
502 				   nonce,
503 				   TRUE,
504 				   flags.b.request_anonymous,
505 				   decrypt_tkt_with_subkey,
506 				   subkey);
507 	krb5_free_kdc_rep(context, &rep);
508 	if (ret)
509 	    goto out;
510     } else if(krb5_rd_error(context, &resp, &error) == 0) {
511 	ret = krb5_error_from_rd_error(context, &error, in_creds);
512 	krb5_free_error_contents(context, &error);
513     } else if(resp.data && ((char*)resp.data)[0] == 4) {
514 	ret = KRB5KRB_AP_ERR_V4_REPLY;
515 	krb5_clear_error_string(context);
516     } else {
517 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
518 	krb5_clear_error_string(context);
519     }
520     krb5_data_free(&resp);
521 out:
522     if(subkey){
523 	krb5_free_keyblock_contents(context, subkey);
524 	free(subkey);
525     }
526     if (buf)
527 	free (buf);
528     return ret;
529 
530 }
531 
532 static krb5_error_code
533 get_cred_kdc(krb5_context context,
534 	     krb5_ccache id,
535 	     krb5_kdc_flags flags,
536 	     krb5_addresses *addresses,
537 	     krb5_creds *in_creds,
538 	     krb5_creds *krbtgt,
539 	     krb5_creds *out_creds)
540 {
541     krb5_error_code ret;
542 
543     ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
544 			     krbtgt, out_creds, KRB5_KU_TGS_REQ_AUTH);
545     if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
546 	krb5_clear_error_string (context);
547 	ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
548 				 krbtgt, out_creds, KRB5_KU_AP_REQ_AUTH);
549     }
550     return ret;
551 }
552 
553 /* same as above, just get local addresses first */
554 
555 static krb5_error_code
556 get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags,
557 		krb5_creds *in_creds, krb5_creds *krbtgt,
558 		krb5_creds *out_creds)
559 {
560     krb5_error_code ret;
561     krb5_addresses addresses;
562 
563     krb5_get_all_client_addrs(context, &addresses);
564     ret = get_cred_kdc(context, id, flags, &addresses,
565 		       in_creds, krbtgt, out_creds);
566     krb5_free_addresses(context, &addresses);
567     return ret;
568 }
569 
570 krb5_error_code
571 krb5_get_kdc_cred(krb5_context context,
572 		  krb5_ccache id,
573 		  krb5_kdc_flags flags,
574 		  krb5_addresses *addresses,
575 		  Ticket  *second_ticket,
576 		  krb5_creds *in_creds,
577 		  krb5_creds **out_creds
578 		  )
579 {
580     krb5_error_code ret;
581     krb5_creds *krbtgt;
582 
583     *out_creds = calloc(1, sizeof(**out_creds));
584     if(*out_creds == NULL) {
585 	krb5_set_error_string(context, "malloc: out of memory");
586 	return ENOMEM;
587     }
588     ret = get_krbtgt (context,
589 		      id,
590 		      in_creds->server->realm,
591 		      &krbtgt);
592     if(ret) {
593 	free(*out_creds);
594 	return ret;
595     }
596     ret = get_cred_kdc(context, id, flags, addresses,
597 		       in_creds, krbtgt, *out_creds);
598     krb5_free_creds (context, krbtgt);
599     if(ret)
600 	free(*out_creds);
601     return ret;
602 }
603 
604 
605 static krb5_error_code
606 find_cred(krb5_context context,
607 	  krb5_ccache id,
608 	  krb5_principal server,
609 	  krb5_creds **tgts,
610 	  krb5_creds *out_creds)
611 {
612     krb5_error_code ret;
613     krb5_creds mcreds;
614     mcreds.server = server;
615     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
616 				&mcreds, out_creds);
617     if(ret == 0)
618 	return 0;
619     while(tgts && *tgts){
620 	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
621 			      &mcreds, *tgts)){
622 	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
623 	    return ret;
624 	}
625 	tgts++;
626     }
627     krb5_clear_error_string(context);
628     return KRB5_CC_NOTFOUND;
629 }
630 
631 static krb5_error_code
632 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
633 {
634     int i;
635     krb5_error_code ret;
636     krb5_creds **tmp = *tgts;
637 
638     for(i = 0; tmp && tmp[i]; i++); /* XXX */
639     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
640     if(tmp == NULL) {
641 	krb5_set_error_string(context, "malloc: out of memory");
642 	return ENOMEM;
643     }
644     *tgts = tmp;
645     ret = krb5_copy_creds(context, tkt, &tmp[i]);
646     tmp[i+1] = NULL;
647     return ret;
648 }
649 
650 /*
651 get_cred(server)
652 	creds = cc_get_cred(server)
653 	if(creds) return creds
654 	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
655 	if(tgt)
656 		return get_cred_tgt(server, tgt)
657 	if(client_realm == server_realm)
658 		return NULL
659 	tgt = get_cred(krbtgt/server_realm@client_realm)
660 	while(tgt_inst != server_realm)
661 		tgt = get_cred(krbtgt/server_realm@tgt_inst)
662 	return get_cred_tgt(server, tgt)
663 	*/
664 
665 static krb5_error_code
666 get_cred_from_kdc_flags(krb5_context context,
667 			krb5_kdc_flags flags,
668 			krb5_ccache ccache,
669 			krb5_creds *in_creds,
670 			krb5_creds **out_creds,
671 			krb5_creds ***ret_tgts)
672 {
673     krb5_error_code ret;
674     krb5_creds *tgt, tmp_creds;
675     krb5_const_realm client_realm, server_realm, try_realm;
676 
677     *out_creds = NULL;
678 
679     client_realm = *krb5_princ_realm(context, in_creds->client);
680     server_realm = *krb5_princ_realm(context, in_creds->server);
681     memset(&tmp_creds, 0, sizeof(tmp_creds));
682     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
683     if(ret)
684 	return ret;
685 
686     try_realm = krb5_config_get_string(context, NULL, "libdefaults",
687 				       "capath", server_realm, NULL);
688     if (try_realm == NULL)
689 	try_realm = client_realm;
690 
691     ret = krb5_make_principal(context,
692 			      &tmp_creds.server,
693 			      try_realm,
694 			      KRB5_TGS_NAME,
695 			      server_realm,
696 			      NULL);
697     if(ret){
698 	krb5_free_principal(context, tmp_creds.client);
699 	return ret;
700     }
701     {
702 	krb5_creds tgts;
703 	/* XXX try krb5_cc_retrieve_cred first? */
704 	ret = find_cred(context, ccache, tmp_creds.server,
705 			*ret_tgts, &tgts);
706 	if(ret == 0){
707 	    *out_creds = calloc(1, sizeof(**out_creds));
708 	    if(*out_creds == NULL) {
709 		krb5_set_error_string(context, "malloc: out of memory");
710 		ret = ENOMEM;
711 	    } else {
712 		ret = get_cred_kdc_la(context, ccache, flags,
713 				      in_creds, &tgts, *out_creds);
714 		if (ret) {
715 		    free (*out_creds);
716 		    *out_creds = NULL;
717 		}
718 	    }
719 	    krb5_free_creds_contents(context, &tgts);
720 	    krb5_free_principal(context, tmp_creds.server);
721 	    krb5_free_principal(context, tmp_creds.client);
722 	    return ret;
723 	}
724     }
725     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
726 	krb5_clear_error_string (context);
727 	return KRB5_CC_NOTFOUND;
728     }
729     /* XXX this can loop forever */
730     while(1){
731 	general_string tgt_inst;
732 
733 	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
734 				      &tgt, ret_tgts);
735 	if(ret) {
736 	    krb5_free_principal(context, tmp_creds.server);
737 	    krb5_free_principal(context, tmp_creds.client);
738 	    return ret;
739 	}
740 	ret = add_cred(context, ret_tgts, tgt);
741 	if(ret) {
742 	    krb5_free_principal(context, tmp_creds.server);
743 	    krb5_free_principal(context, tmp_creds.client);
744 	    return ret;
745 	}
746 	tgt_inst = tgt->server->name.name_string.val[1];
747 	if(strcmp(tgt_inst, server_realm) == 0)
748 	    break;
749 	krb5_free_principal(context, tmp_creds.server);
750 	ret = krb5_make_principal(context, &tmp_creds.server,
751 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
752 	if(ret) {
753 	    krb5_free_principal(context, tmp_creds.server);
754 	    krb5_free_principal(context, tmp_creds.client);
755 	    return ret;
756 	}
757 	ret = krb5_free_creds(context, tgt);
758 	if(ret) {
759 	    krb5_free_principal(context, tmp_creds.server);
760 	    krb5_free_principal(context, tmp_creds.client);
761 	    return ret;
762 	}
763     }
764 
765     krb5_free_principal(context, tmp_creds.server);
766     krb5_free_principal(context, tmp_creds.client);
767     *out_creds = calloc(1, sizeof(**out_creds));
768     if(*out_creds == NULL) {
769 	krb5_set_error_string(context, "malloc: out of memory");
770 	ret = ENOMEM;
771     } else {
772 	ret = get_cred_kdc_la(context, ccache, flags,
773 				      in_creds, tgt, *out_creds);
774 	if (ret) {
775 	    free (*out_creds);
776 	    *out_creds = NULL;
777 	}
778     }
779     krb5_free_creds(context, tgt);
780     return ret;
781 }
782 
783 krb5_error_code
784 krb5_get_cred_from_kdc_opt(krb5_context context,
785 			   krb5_ccache ccache,
786 			   krb5_creds *in_creds,
787 			   krb5_creds **out_creds,
788 			   krb5_creds ***ret_tgts,
789 			   krb5_flags flags)
790 {
791     krb5_kdc_flags f;
792     f.i = flags;
793     return get_cred_from_kdc_flags(context, f, ccache,
794 				   in_creds, out_creds, ret_tgts);
795 }
796 
797 krb5_error_code
798 krb5_get_cred_from_kdc(krb5_context context,
799 		       krb5_ccache ccache,
800 		       krb5_creds *in_creds,
801 		       krb5_creds **out_creds,
802 		       krb5_creds ***ret_tgts)
803 {
804     return krb5_get_cred_from_kdc_opt(context, ccache,
805 				      in_creds, out_creds, ret_tgts, 0);
806 }
807 
808 
809 krb5_error_code
810 krb5_get_credentials_with_flags(krb5_context context,
811 				krb5_flags options,
812 				krb5_kdc_flags flags,
813 				krb5_ccache ccache,
814 				krb5_creds *in_creds,
815 				krb5_creds **out_creds)
816 {
817     krb5_error_code ret;
818     krb5_creds **tgts;
819     krb5_creds *res_creds;
820     int i;
821 
822     *out_creds = NULL;
823     res_creds = calloc(1, sizeof(*res_creds));
824     if (res_creds == NULL) {
825 	krb5_set_error_string(context, "malloc: out of memory");
826 	return ENOMEM;
827     }
828 
829     ret = krb5_cc_retrieve_cred(context,
830 				ccache,
831 				in_creds->session.keytype ?
832 				KRB5_TC_MATCH_KEYTYPE : 0,
833 				in_creds, res_creds);
834     if(ret == 0) {
835 	*out_creds = res_creds;
836 	return 0;
837     }
838     free(res_creds);
839     if(ret != KRB5_CC_END)
840 	return ret;
841     if(options & KRB5_GC_CACHED) {
842 	krb5_clear_error_string (context);
843 	return KRB5_CC_NOTFOUND;
844     }
845     if(options & KRB5_GC_USER_USER)
846 	flags.b.enc_tkt_in_skey = 1;
847     tgts = NULL;
848     ret = get_cred_from_kdc_flags(context, flags, ccache,
849 				  in_creds, out_creds, &tgts);
850     for(i = 0; tgts && tgts[i]; i++) {
851 	krb5_cc_store_cred(context, ccache, tgts[i]);
852 	krb5_free_creds(context, tgts[i]);
853     }
854     free(tgts);
855     if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
856 	krb5_cc_store_cred(context, ccache, *out_creds);
857     return ret;
858 }
859 
860 krb5_error_code
861 krb5_get_credentials(krb5_context context,
862 		     krb5_flags options,
863 		     krb5_ccache ccache,
864 		     krb5_creds *in_creds,
865 		     krb5_creds **out_creds)
866 {
867     krb5_kdc_flags flags;
868     flags.i = 0;
869     return krb5_get_credentials_with_flags(context, options, flags,
870 					   ccache, in_creds, out_creds);
871 }
872