xref: /freebsd/crypto/heimdal/lib/krb5/get_cred.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*
2  * Copyright (c) 1997 - 2004 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.4.3 2004/01/09 00:47:17 lha 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 = NULL;
229 
230 	ret = krb5_auth_con_init(context, &ac);
231 	if(ret)
232 	    goto fail;
233 
234 	if (krb5_config_get_bool_default(context, NULL, FALSE,
235 					 "realms",
236 					 krbtgt->server->realm,
237 					 "tgs_require_subkey",
238 					 NULL))
239 	{
240 	    ret = krb5_generate_subkey (context, &krbtgt->session, &key);
241 	    if (ret) {
242 		krb5_auth_con_free (context, ac);
243 		goto fail;
244 	    }
245 
246 	    ret = krb5_auth_con_setlocalsubkey(context, ac, key);
247 	    if (ret) {
248 		if (key)
249 		    krb5_free_keyblock (context, key);
250 		krb5_auth_con_free (context, ac);
251 		goto fail;
252 	    }
253 	}
254 
255 	ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key);
256 	if (ret) {
257 	    if (key)
258 		krb5_free_keyblock (context, key);
259 	    krb5_auth_con_free (context, ac);
260 	    goto fail;
261 	}
262 
263 	ret = make_pa_tgs_req(context,
264 			      ac,
265 			      &t->req_body,
266 			      t->padata->val,
267 			      krbtgt,
268 			      usage);
269 	if(ret) {
270 	    if (key)
271 		krb5_free_keyblock (context, key);
272 	    krb5_auth_con_free(context, ac);
273 	    goto fail;
274 	}
275 	*subkey = key;
276 
277 	krb5_auth_con_free(context, ac);
278     }
279 fail:
280     if (ret) {
281 	t->req_body.addresses = NULL;
282 	free_TGS_REQ (t);
283     }
284     return ret;
285 }
286 
287 krb5_error_code
288 _krb5_get_krbtgt(krb5_context context,
289 		 krb5_ccache  id,
290 		 krb5_realm realm,
291 		 krb5_creds **cred)
292 {
293     krb5_error_code ret;
294     krb5_creds tmp_cred;
295 
296     memset(&tmp_cred, 0, sizeof(tmp_cred));
297 
298     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
299     if (ret)
300 	return ret;
301 
302     ret = krb5_make_principal(context,
303 			      &tmp_cred.server,
304 			      realm,
305 			      KRB5_TGS_NAME,
306 			      realm,
307 			      NULL);
308     if(ret) {
309 	krb5_free_principal(context, tmp_cred.client);
310 	return ret;
311     }
312     ret = krb5_get_credentials(context,
313 			       KRB5_GC_CACHED,
314 			       id,
315 			       &tmp_cred,
316 			       cred);
317     krb5_free_principal(context, tmp_cred.client);
318     krb5_free_principal(context, tmp_cred.server);
319     if(ret)
320 	return ret;
321     return 0;
322 }
323 
324 /* DCE compatible decrypt proc */
325 static krb5_error_code
326 decrypt_tkt_with_subkey (krb5_context context,
327 			 krb5_keyblock *key,
328 			 krb5_key_usage usage,
329 			 krb5_const_pointer subkey,
330 			 krb5_kdc_rep *dec_rep)
331 {
332     krb5_error_code ret;
333     krb5_data data;
334     size_t size;
335     krb5_crypto crypto;
336 
337     ret = krb5_crypto_init(context, key, 0, &crypto);
338     if (ret)
339 	return ret;
340     ret = krb5_decrypt_EncryptedData (context,
341 				      crypto,
342 				      usage,
343 				      &dec_rep->kdc_rep.enc_part,
344 				      &data);
345     krb5_crypto_destroy(context, crypto);
346     if(ret && subkey){
347 	/* DCE compat -- try to decrypt with subkey */
348 	ret = krb5_crypto_init(context, (krb5_keyblock*)subkey, 0, &crypto);
349 	if (ret)
350 	    return ret;
351 	ret = krb5_decrypt_EncryptedData (context,
352 					  crypto,
353 					  KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
354 					  &dec_rep->kdc_rep.enc_part,
355 					  &data);
356 	krb5_crypto_destroy(context, crypto);
357     }
358     if (ret)
359 	return ret;
360 
361     ret = krb5_decode_EncASRepPart(context,
362 				   data.data,
363 				   data.length,
364 				   &dec_rep->enc_part,
365 				   &size);
366     if (ret)
367 	ret = krb5_decode_EncTGSRepPart(context,
368 					data.data,
369 					data.length,
370 					&dec_rep->enc_part,
371 					&size);
372     krb5_data_free (&data);
373     return ret;
374 }
375 
376 static krb5_error_code
377 get_cred_kdc_usage(krb5_context context,
378 		   krb5_ccache id,
379 		   krb5_kdc_flags flags,
380 		   krb5_addresses *addresses,
381 		   krb5_creds *in_creds,
382 		   krb5_creds *krbtgt,
383 		   krb5_creds *out_creds,
384 		   krb5_key_usage usage)
385 {
386     TGS_REQ req;
387     krb5_data enc;
388     krb5_data resp;
389     krb5_kdc_rep rep;
390     KRB_ERROR error;
391     krb5_error_code ret;
392     unsigned nonce;
393     krb5_keyblock *subkey = NULL;
394     u_char *buf = NULL;
395     size_t buf_size;
396     size_t len;
397     Ticket second_ticket;
398 
399     krb5_generate_random_block(&nonce, sizeof(nonce));
400     nonce &= 0xffffffff;
401 
402     if(flags.b.enc_tkt_in_skey){
403 	ret = decode_Ticket(in_creds->second_ticket.data,
404 			    in_creds->second_ticket.length,
405 			    &second_ticket, &len);
406 	if(ret)
407 	    return ret;
408     }
409 
410     ret = init_tgs_req (context,
411 			id,
412 			addresses,
413 			flags,
414 			flags.b.enc_tkt_in_skey ? &second_ticket : NULL,
415 			in_creds,
416 			krbtgt,
417 			nonce,
418 			&subkey,
419 			&req,
420 			usage);
421     if(flags.b.enc_tkt_in_skey)
422 	free_Ticket(&second_ticket);
423     if (ret)
424 	goto out;
425 
426     ASN1_MALLOC_ENCODE(TGS_REQ, buf, buf_size, &req, &enc.length, ret);
427     if (ret)
428 	goto out;
429     if(enc.length != buf_size)
430 	krb5_abortx(context, "internal error in ASN.1 encoder");
431 
432     /* don't free addresses */
433     req.req_body.addresses = NULL;
434     free_TGS_REQ(&req);
435 
436     enc.data = buf + buf_size - enc.length;
437     if (ret)
438 	goto out;
439 
440     /*
441      * Send and receive
442      */
443 
444     ret = krb5_sendto_kdc (context, &enc,
445 			   &krbtgt->server->name.name_string.val[1], &resp);
446     if(ret)
447 	goto out;
448 
449     memset(&rep, 0, sizeof(rep));
450     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
451 	ret = krb5_copy_principal(context,
452 				  in_creds->client,
453 				  &out_creds->client);
454 	if(ret)
455 	    goto out;
456 	ret = krb5_copy_principal(context,
457 				  in_creds->server,
458 				  &out_creds->server);
459 	if(ret)
460 	    goto out;
461 	/* this should go someplace else */
462 	out_creds->times.endtime = in_creds->times.endtime;
463 
464 	ret = _krb5_extract_ticket(context,
465 				   &rep,
466 				   out_creds,
467 				   &krbtgt->session,
468 				   NULL,
469 				   KRB5_KU_TGS_REP_ENC_PART_SESSION,
470 				   &krbtgt->addresses,
471 				   nonce,
472 				   TRUE,
473 				   flags.b.request_anonymous,
474 				   decrypt_tkt_with_subkey,
475 				   subkey);
476 	krb5_free_kdc_rep(context, &rep);
477 	if (ret)
478 	    goto out;
479     } else if(krb5_rd_error(context, &resp, &error) == 0) {
480 	ret = krb5_error_from_rd_error(context, &error, in_creds);
481 	krb5_free_error_contents(context, &error);
482     } else if(resp.data && ((char*)resp.data)[0] == 4) {
483 	ret = KRB5KRB_AP_ERR_V4_REPLY;
484 	krb5_clear_error_string(context);
485     } else {
486 	ret = KRB5KRB_AP_ERR_MSG_TYPE;
487 	krb5_clear_error_string(context);
488     }
489     krb5_data_free(&resp);
490  out:
491     if(subkey){
492 	krb5_free_keyblock_contents(context, subkey);
493 	free(subkey);
494     }
495     if (buf)
496 	free (buf);
497     return ret;
498 
499 }
500 
501 static krb5_error_code
502 get_cred_kdc(krb5_context context,
503 	     krb5_ccache id,
504 	     krb5_kdc_flags flags,
505 	     krb5_addresses *addresses,
506 	     krb5_creds *in_creds,
507 	     krb5_creds *krbtgt,
508 	     krb5_creds *out_creds)
509 {
510     krb5_error_code ret;
511 
512     ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
513 			     krbtgt, out_creds, KRB5_KU_TGS_REQ_AUTH);
514     if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
515 	krb5_clear_error_string (context);
516 	ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
517 				 krbtgt, out_creds, KRB5_KU_AP_REQ_AUTH);
518     }
519     return ret;
520 }
521 
522 /* same as above, just get local addresses first */
523 
524 static krb5_error_code
525 get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags,
526 		krb5_creds *in_creds, krb5_creds *krbtgt,
527 		krb5_creds *out_creds)
528 {
529     krb5_error_code ret;
530     krb5_addresses addresses, *addrs = &addresses;
531 
532     krb5_get_all_client_addrs(context, &addresses);
533     /* XXX this sucks. */
534     if(addresses.len == 0)
535 	addrs = NULL;
536     ret = get_cred_kdc(context, id, flags, addrs,
537 		       in_creds, krbtgt, out_creds);
538     krb5_free_addresses(context, &addresses);
539     return ret;
540 }
541 
542 krb5_error_code
543 krb5_get_kdc_cred(krb5_context context,
544 		  krb5_ccache id,
545 		  krb5_kdc_flags flags,
546 		  krb5_addresses *addresses,
547 		  Ticket  *second_ticket,
548 		  krb5_creds *in_creds,
549 		  krb5_creds **out_creds
550 		  )
551 {
552     krb5_error_code ret;
553     krb5_creds *krbtgt;
554 
555     *out_creds = calloc(1, sizeof(**out_creds));
556     if(*out_creds == NULL) {
557 	krb5_set_error_string(context, "malloc: out of memory");
558 	return ENOMEM;
559     }
560     ret = _krb5_get_krbtgt (context,
561 			    id,
562 			    in_creds->server->realm,
563 			    &krbtgt);
564     if(ret) {
565 	free(*out_creds);
566 	return ret;
567     }
568     ret = get_cred_kdc(context, id, flags, addresses,
569 		       in_creds, krbtgt, *out_creds);
570     krb5_free_creds (context, krbtgt);
571     if(ret)
572 	free(*out_creds);
573     return ret;
574 }
575 
576 
577 static krb5_error_code
578 find_cred(krb5_context context,
579 	  krb5_ccache id,
580 	  krb5_principal server,
581 	  krb5_creds **tgts,
582 	  krb5_creds *out_creds)
583 {
584     krb5_error_code ret;
585     krb5_creds mcreds;
586     mcreds.server = server;
587     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
588 				&mcreds, out_creds);
589     if(ret == 0)
590 	return 0;
591     while(tgts && *tgts){
592 	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
593 			      &mcreds, *tgts)){
594 	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
595 	    return ret;
596 	}
597 	tgts++;
598     }
599     krb5_clear_error_string(context);
600     return KRB5_CC_NOTFOUND;
601 }
602 
603 static krb5_error_code
604 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
605 {
606     int i;
607     krb5_error_code ret;
608     krb5_creds **tmp = *tgts;
609 
610     for(i = 0; tmp && tmp[i]; i++); /* XXX */
611     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
612     if(tmp == NULL) {
613 	krb5_set_error_string(context, "malloc: out of memory");
614 	return ENOMEM;
615     }
616     *tgts = tmp;
617     ret = krb5_copy_creds(context, tkt, &tmp[i]);
618     tmp[i+1] = NULL;
619     return ret;
620 }
621 
622 /*
623 get_cred(server)
624 	creds = cc_get_cred(server)
625 	if(creds) return creds
626 	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
627 	if(tgt)
628 		return get_cred_tgt(server, tgt)
629 	if(client_realm == server_realm)
630 		return NULL
631 	tgt = get_cred(krbtgt/server_realm@client_realm)
632 	while(tgt_inst != server_realm)
633 		tgt = get_cred(krbtgt/server_realm@tgt_inst)
634 	return get_cred_tgt(server, tgt)
635 	*/
636 
637 static krb5_error_code
638 get_cred_from_kdc_flags(krb5_context context,
639 			krb5_kdc_flags flags,
640 			krb5_ccache ccache,
641 			krb5_creds *in_creds,
642 			krb5_creds **out_creds,
643 			krb5_creds ***ret_tgts)
644 {
645     krb5_error_code ret;
646     krb5_creds *tgt, tmp_creds;
647     krb5_const_realm client_realm, server_realm, try_realm;
648 
649     *out_creds = NULL;
650 
651     client_realm = *krb5_princ_realm(context, in_creds->client);
652     server_realm = *krb5_princ_realm(context, in_creds->server);
653     memset(&tmp_creds, 0, sizeof(tmp_creds));
654     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
655     if(ret)
656 	return ret;
657 
658     try_realm = krb5_config_get_string(context, NULL, "capaths",
659 				       client_realm, server_realm, NULL);
660 
661 #if 1
662     /* XXX remove in future release */
663     if(try_realm == NULL)
664 	try_realm = krb5_config_get_string(context, NULL, "libdefaults",
665 					   "capath", server_realm, NULL);
666 #endif
667 
668     if (try_realm == NULL)
669 	try_realm = client_realm;
670 
671     ret = krb5_make_principal(context,
672 			      &tmp_creds.server,
673 			      try_realm,
674 			      KRB5_TGS_NAME,
675 			      server_realm,
676 			      NULL);
677     if(ret){
678 	krb5_free_principal(context, tmp_creds.client);
679 	return ret;
680     }
681     {
682 	krb5_creds tgts;
683 	/* XXX try krb5_cc_retrieve_cred first? */
684 	ret = find_cred(context, ccache, tmp_creds.server,
685 			*ret_tgts, &tgts);
686 	if(ret == 0){
687 	    *out_creds = calloc(1, sizeof(**out_creds));
688 	    if(*out_creds == NULL) {
689 		krb5_set_error_string(context, "malloc: out of memory");
690 		ret = ENOMEM;
691 	    } else {
692 		krb5_boolean noaddr;
693 
694 		krb5_appdefault_boolean(context, NULL, tgts.server->realm,
695 					"no-addresses", FALSE, &noaddr);
696 
697 		if (noaddr)
698 		    ret = get_cred_kdc(context, ccache, flags, NULL,
699 				       in_creds, &tgts, *out_creds);
700 		else
701 		    ret = get_cred_kdc_la(context, ccache, flags,
702 					  in_creds, &tgts, *out_creds);
703 		if (ret) {
704 		    free (*out_creds);
705 		    *out_creds = NULL;
706 		}
707 	    }
708 	    krb5_free_creds_contents(context, &tgts);
709 	    krb5_free_principal(context, tmp_creds.server);
710 	    krb5_free_principal(context, tmp_creds.client);
711 	    return ret;
712 	}
713     }
714     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
715 	krb5_clear_error_string (context);
716 	return KRB5_CC_NOTFOUND;
717     }
718     /* XXX this can loop forever */
719     while(1){
720 	general_string tgt_inst;
721 
722 	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
723 				      &tgt, ret_tgts);
724 	if(ret) {
725 	    krb5_free_principal(context, tmp_creds.server);
726 	    krb5_free_principal(context, tmp_creds.client);
727 	    return ret;
728 	}
729 	ret = add_cred(context, ret_tgts, tgt);
730 	if(ret) {
731 	    krb5_free_principal(context, tmp_creds.server);
732 	    krb5_free_principal(context, tmp_creds.client);
733 	    return ret;
734 	}
735 	tgt_inst = tgt->server->name.name_string.val[1];
736 	if(strcmp(tgt_inst, server_realm) == 0)
737 	    break;
738 	krb5_free_principal(context, tmp_creds.server);
739 	ret = krb5_make_principal(context, &tmp_creds.server,
740 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
741 	if(ret) {
742 	    krb5_free_principal(context, tmp_creds.server);
743 	    krb5_free_principal(context, tmp_creds.client);
744 	    return ret;
745 	}
746 	ret = krb5_free_creds(context, tgt);
747 	if(ret) {
748 	    krb5_free_principal(context, tmp_creds.server);
749 	    krb5_free_principal(context, tmp_creds.client);
750 	    return ret;
751 	}
752     }
753 
754     krb5_free_principal(context, tmp_creds.server);
755     krb5_free_principal(context, tmp_creds.client);
756     *out_creds = calloc(1, sizeof(**out_creds));
757     if(*out_creds == NULL) {
758 	krb5_set_error_string(context, "malloc: out of memory");
759 	ret = ENOMEM;
760     } else {
761 	krb5_boolean noaddr;
762 
763 	krb5_appdefault_boolean(context, NULL, tgt->server->realm,
764 				"no-addresses", FALSE, &noaddr);
765 	if (noaddr)
766 	    ret = get_cred_kdc (context, ccache, flags, NULL,
767 				in_creds, tgt, *out_creds);
768 	else
769 	    ret = get_cred_kdc_la(context, ccache, flags,
770 				  in_creds, tgt, *out_creds);
771 	if (ret) {
772 	    free (*out_creds);
773 	    *out_creds = NULL;
774 	}
775     }
776     krb5_free_creds(context, tgt);
777     return ret;
778 }
779 
780 krb5_error_code
781 krb5_get_cred_from_kdc_opt(krb5_context context,
782 			   krb5_ccache ccache,
783 			   krb5_creds *in_creds,
784 			   krb5_creds **out_creds,
785 			   krb5_creds ***ret_tgts,
786 			   krb5_flags flags)
787 {
788     krb5_kdc_flags f;
789     f.i = flags;
790     return get_cred_from_kdc_flags(context, f, ccache,
791 				   in_creds, out_creds, ret_tgts);
792 }
793 
794 krb5_error_code
795 krb5_get_cred_from_kdc(krb5_context context,
796 		       krb5_ccache ccache,
797 		       krb5_creds *in_creds,
798 		       krb5_creds **out_creds,
799 		       krb5_creds ***ret_tgts)
800 {
801     return krb5_get_cred_from_kdc_opt(context, ccache,
802 				      in_creds, out_creds, ret_tgts, 0);
803 }
804 
805 
806 krb5_error_code
807 krb5_get_credentials_with_flags(krb5_context context,
808 				krb5_flags options,
809 				krb5_kdc_flags flags,
810 				krb5_ccache ccache,
811 				krb5_creds *in_creds,
812 				krb5_creds **out_creds)
813 {
814     krb5_error_code ret;
815     krb5_creds **tgts;
816     krb5_creds *res_creds;
817     int i;
818 
819     *out_creds = NULL;
820     res_creds = calloc(1, sizeof(*res_creds));
821     if (res_creds == NULL) {
822 	krb5_set_error_string(context, "malloc: out of memory");
823 	return ENOMEM;
824     }
825 
826     ret = krb5_cc_retrieve_cred(context,
827 				ccache,
828 				in_creds->session.keytype ?
829 				KRB5_TC_MATCH_KEYTYPE : 0,
830 				in_creds, res_creds);
831     if(ret == 0) {
832 	*out_creds = res_creds;
833 	return 0;
834     }
835     free(res_creds);
836     if(ret != KRB5_CC_END)
837 	return ret;
838     if(options & KRB5_GC_CACHED) {
839 	krb5_clear_error_string (context);
840 	return KRB5_CC_NOTFOUND;
841     }
842     if(options & KRB5_GC_USER_USER)
843 	flags.b.enc_tkt_in_skey = 1;
844     tgts = NULL;
845     ret = get_cred_from_kdc_flags(context, flags, ccache,
846 				  in_creds, out_creds, &tgts);
847     for(i = 0; tgts && tgts[i]; i++) {
848 	krb5_cc_store_cred(context, ccache, tgts[i]);
849 	krb5_free_creds(context, tgts[i]);
850     }
851     free(tgts);
852     if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
853 	krb5_cc_store_cred(context, ccache, *out_creds);
854     return ret;
855 }
856 
857 krb5_error_code
858 krb5_get_credentials(krb5_context context,
859 		     krb5_flags options,
860 		     krb5_ccache ccache,
861 		     krb5_creds *in_creds,
862 		     krb5_creds **out_creds)
863 {
864     krb5_kdc_flags flags;
865     flags.i = 0;
866     return krb5_get_credentials_with_flags(context, options, flags,
867 					   ccache, in_creds, out_creds);
868 }
869