xref: /freebsd/crypto/heimdal/lib/krb5/get_cred.c (revision 8373020d34ceb1ac55d8f43333c1ca3680185b39)
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.88 2002/03/10 23:11:29 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, *addrs = &addresses;
562 
563     krb5_get_all_client_addrs(context, &addresses);
564     /* XXX this sucks. */
565     if(addresses.len == 0)
566 	addrs = NULL;
567     ret = get_cred_kdc(context, id, flags, addrs,
568 		       in_creds, krbtgt, out_creds);
569     krb5_free_addresses(context, &addresses);
570     return ret;
571 }
572 
573 krb5_error_code
574 krb5_get_kdc_cred(krb5_context context,
575 		  krb5_ccache id,
576 		  krb5_kdc_flags flags,
577 		  krb5_addresses *addresses,
578 		  Ticket  *second_ticket,
579 		  krb5_creds *in_creds,
580 		  krb5_creds **out_creds
581 		  )
582 {
583     krb5_error_code ret;
584     krb5_creds *krbtgt;
585 
586     *out_creds = calloc(1, sizeof(**out_creds));
587     if(*out_creds == NULL) {
588 	krb5_set_error_string(context, "malloc: out of memory");
589 	return ENOMEM;
590     }
591     ret = get_krbtgt (context,
592 		      id,
593 		      in_creds->server->realm,
594 		      &krbtgt);
595     if(ret) {
596 	free(*out_creds);
597 	return ret;
598     }
599     ret = get_cred_kdc(context, id, flags, addresses,
600 		       in_creds, krbtgt, *out_creds);
601     krb5_free_creds (context, krbtgt);
602     if(ret)
603 	free(*out_creds);
604     return ret;
605 }
606 
607 
608 static krb5_error_code
609 find_cred(krb5_context context,
610 	  krb5_ccache id,
611 	  krb5_principal server,
612 	  krb5_creds **tgts,
613 	  krb5_creds *out_creds)
614 {
615     krb5_error_code ret;
616     krb5_creds mcreds;
617     mcreds.server = server;
618     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
619 				&mcreds, out_creds);
620     if(ret == 0)
621 	return 0;
622     while(tgts && *tgts){
623 	if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
624 			      &mcreds, *tgts)){
625 	    ret = krb5_copy_creds_contents(context, *tgts, out_creds);
626 	    return ret;
627 	}
628 	tgts++;
629     }
630     krb5_clear_error_string(context);
631     return KRB5_CC_NOTFOUND;
632 }
633 
634 static krb5_error_code
635 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
636 {
637     int i;
638     krb5_error_code ret;
639     krb5_creds **tmp = *tgts;
640 
641     for(i = 0; tmp && tmp[i]; i++); /* XXX */
642     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
643     if(tmp == NULL) {
644 	krb5_set_error_string(context, "malloc: out of memory");
645 	return ENOMEM;
646     }
647     *tgts = tmp;
648     ret = krb5_copy_creds(context, tkt, &tmp[i]);
649     tmp[i+1] = NULL;
650     return ret;
651 }
652 
653 /*
654 get_cred(server)
655 	creds = cc_get_cred(server)
656 	if(creds) return creds
657 	tgt = cc_get_cred(krbtgt/server_realm@any_realm)
658 	if(tgt)
659 		return get_cred_tgt(server, tgt)
660 	if(client_realm == server_realm)
661 		return NULL
662 	tgt = get_cred(krbtgt/server_realm@client_realm)
663 	while(tgt_inst != server_realm)
664 		tgt = get_cred(krbtgt/server_realm@tgt_inst)
665 	return get_cred_tgt(server, tgt)
666 	*/
667 
668 static krb5_error_code
669 get_cred_from_kdc_flags(krb5_context context,
670 			krb5_kdc_flags flags,
671 			krb5_ccache ccache,
672 			krb5_creds *in_creds,
673 			krb5_creds **out_creds,
674 			krb5_creds ***ret_tgts)
675 {
676     krb5_error_code ret;
677     krb5_creds *tgt, tmp_creds;
678     krb5_const_realm client_realm, server_realm, try_realm;
679 
680     *out_creds = NULL;
681 
682     client_realm = *krb5_princ_realm(context, in_creds->client);
683     server_realm = *krb5_princ_realm(context, in_creds->server);
684     memset(&tmp_creds, 0, sizeof(tmp_creds));
685     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
686     if(ret)
687 	return ret;
688 
689     try_realm = krb5_config_get_string(context, NULL, "libdefaults",
690 				       "capath", server_realm, NULL);
691     if (try_realm == NULL)
692 	try_realm = client_realm;
693 
694     ret = krb5_make_principal(context,
695 			      &tmp_creds.server,
696 			      try_realm,
697 			      KRB5_TGS_NAME,
698 			      server_realm,
699 			      NULL);
700     if(ret){
701 	krb5_free_principal(context, tmp_creds.client);
702 	return ret;
703     }
704     {
705 	krb5_creds tgts;
706 	/* XXX try krb5_cc_retrieve_cred first? */
707 	ret = find_cred(context, ccache, tmp_creds.server,
708 			*ret_tgts, &tgts);
709 	if(ret == 0){
710 	    *out_creds = calloc(1, sizeof(**out_creds));
711 	    if(*out_creds == NULL) {
712 		krb5_set_error_string(context, "malloc: out of memory");
713 		ret = ENOMEM;
714 	    } else {
715 		krb5_boolean noaddr;
716 
717 		krb5_appdefault_boolean(context, NULL, tgts.server->realm,
718 					"no-addresses", FALSE, &noaddr);
719 
720 		if (noaddr)
721 		    ret = get_cred_kdc(context, ccache, flags, NULL,
722 				       in_creds, &tgts, *out_creds);
723 		else
724 		    ret = get_cred_kdc_la(context, ccache, flags,
725 					  in_creds, &tgts, *out_creds);
726 		if (ret) {
727 		    free (*out_creds);
728 		    *out_creds = NULL;
729 		}
730 	    }
731 	    krb5_free_creds_contents(context, &tgts);
732 	    krb5_free_principal(context, tmp_creds.server);
733 	    krb5_free_principal(context, tmp_creds.client);
734 	    return ret;
735 	}
736     }
737     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
738 	krb5_clear_error_string (context);
739 	return KRB5_CC_NOTFOUND;
740     }
741     /* XXX this can loop forever */
742     while(1){
743 	general_string tgt_inst;
744 
745 	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
746 				      &tgt, ret_tgts);
747 	if(ret) {
748 	    krb5_free_principal(context, tmp_creds.server);
749 	    krb5_free_principal(context, tmp_creds.client);
750 	    return ret;
751 	}
752 	ret = add_cred(context, ret_tgts, tgt);
753 	if(ret) {
754 	    krb5_free_principal(context, tmp_creds.server);
755 	    krb5_free_principal(context, tmp_creds.client);
756 	    return ret;
757 	}
758 	tgt_inst = tgt->server->name.name_string.val[1];
759 	if(strcmp(tgt_inst, server_realm) == 0)
760 	    break;
761 	krb5_free_principal(context, tmp_creds.server);
762 	ret = krb5_make_principal(context, &tmp_creds.server,
763 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
764 	if(ret) {
765 	    krb5_free_principal(context, tmp_creds.server);
766 	    krb5_free_principal(context, tmp_creds.client);
767 	    return ret;
768 	}
769 	ret = krb5_free_creds(context, tgt);
770 	if(ret) {
771 	    krb5_free_principal(context, tmp_creds.server);
772 	    krb5_free_principal(context, tmp_creds.client);
773 	    return ret;
774 	}
775     }
776 
777     krb5_free_principal(context, tmp_creds.server);
778     krb5_free_principal(context, tmp_creds.client);
779     *out_creds = calloc(1, sizeof(**out_creds));
780     if(*out_creds == NULL) {
781 	krb5_set_error_string(context, "malloc: out of memory");
782 	ret = ENOMEM;
783     } else {
784 	krb5_boolean noaddr;
785 
786 	krb5_appdefault_boolean(context, NULL, tgt->server->realm,
787 				"no-addresses", FALSE, &noaddr);
788 	if (noaddr)
789 	    ret = get_cred_kdc (context, ccache, flags, NULL,
790 				in_creds, tgt, *out_creds);
791 	else
792 	    ret = get_cred_kdc_la(context, ccache, flags,
793 				  in_creds, tgt, *out_creds);
794 	if (ret) {
795 	    free (*out_creds);
796 	    *out_creds = NULL;
797 	}
798     }
799     krb5_free_creds(context, tgt);
800     return ret;
801 }
802 
803 krb5_error_code
804 krb5_get_cred_from_kdc_opt(krb5_context context,
805 			   krb5_ccache ccache,
806 			   krb5_creds *in_creds,
807 			   krb5_creds **out_creds,
808 			   krb5_creds ***ret_tgts,
809 			   krb5_flags flags)
810 {
811     krb5_kdc_flags f;
812     f.i = flags;
813     return get_cred_from_kdc_flags(context, f, ccache,
814 				   in_creds, out_creds, ret_tgts);
815 }
816 
817 krb5_error_code
818 krb5_get_cred_from_kdc(krb5_context context,
819 		       krb5_ccache ccache,
820 		       krb5_creds *in_creds,
821 		       krb5_creds **out_creds,
822 		       krb5_creds ***ret_tgts)
823 {
824     return krb5_get_cred_from_kdc_opt(context, ccache,
825 				      in_creds, out_creds, ret_tgts, 0);
826 }
827 
828 
829 krb5_error_code
830 krb5_get_credentials_with_flags(krb5_context context,
831 				krb5_flags options,
832 				krb5_kdc_flags flags,
833 				krb5_ccache ccache,
834 				krb5_creds *in_creds,
835 				krb5_creds **out_creds)
836 {
837     krb5_error_code ret;
838     krb5_creds **tgts;
839     krb5_creds *res_creds;
840     int i;
841 
842     *out_creds = NULL;
843     res_creds = calloc(1, sizeof(*res_creds));
844     if (res_creds == NULL) {
845 	krb5_set_error_string(context, "malloc: out of memory");
846 	return ENOMEM;
847     }
848 
849     ret = krb5_cc_retrieve_cred(context,
850 				ccache,
851 				in_creds->session.keytype ?
852 				KRB5_TC_MATCH_KEYTYPE : 0,
853 				in_creds, res_creds);
854     if(ret == 0) {
855 	*out_creds = res_creds;
856 	return 0;
857     }
858     free(res_creds);
859     if(ret != KRB5_CC_END)
860 	return ret;
861     if(options & KRB5_GC_CACHED) {
862 	krb5_clear_error_string (context);
863 	return KRB5_CC_NOTFOUND;
864     }
865     if(options & KRB5_GC_USER_USER)
866 	flags.b.enc_tkt_in_skey = 1;
867     tgts = NULL;
868     ret = get_cred_from_kdc_flags(context, flags, ccache,
869 				  in_creds, out_creds, &tgts);
870     for(i = 0; tgts && tgts[i]; i++) {
871 	krb5_cc_store_cred(context, ccache, tgts[i]);
872 	krb5_free_creds(context, tgts[i]);
873     }
874     free(tgts);
875     if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
876 	krb5_cc_store_cred(context, ccache, *out_creds);
877     return ret;
878 }
879 
880 krb5_error_code
881 krb5_get_credentials(krb5_context context,
882 		     krb5_flags options,
883 		     krb5_ccache ccache,
884 		     krb5_creds *in_creds,
885 		     krb5_creds **out_creds)
886 {
887     krb5_kdc_flags flags;
888     flags.i = 0;
889     return krb5_get_credentials_with_flags(context, options, flags,
890 					   ccache, in_creds, out_creds);
891 }
892