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