xref: /freebsd/crypto/heimdal/lib/krb5/get_cred.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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.87 2001/07/03 18:45:03 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 		ret = get_cred_kdc_la(context, ccache, flags,
716 				      in_creds, &tgts, *out_creds);
717 		if (ret) {
718 		    free (*out_creds);
719 		    *out_creds = NULL;
720 		}
721 	    }
722 	    krb5_free_creds_contents(context, &tgts);
723 	    krb5_free_principal(context, tmp_creds.server);
724 	    krb5_free_principal(context, tmp_creds.client);
725 	    return ret;
726 	}
727     }
728     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
729 	krb5_clear_error_string (context);
730 	return KRB5_CC_NOTFOUND;
731     }
732     /* XXX this can loop forever */
733     while(1){
734 	general_string tgt_inst;
735 
736 	ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds,
737 				      &tgt, ret_tgts);
738 	if(ret) {
739 	    krb5_free_principal(context, tmp_creds.server);
740 	    krb5_free_principal(context, tmp_creds.client);
741 	    return ret;
742 	}
743 	ret = add_cred(context, ret_tgts, tgt);
744 	if(ret) {
745 	    krb5_free_principal(context, tmp_creds.server);
746 	    krb5_free_principal(context, tmp_creds.client);
747 	    return ret;
748 	}
749 	tgt_inst = tgt->server->name.name_string.val[1];
750 	if(strcmp(tgt_inst, server_realm) == 0)
751 	    break;
752 	krb5_free_principal(context, tmp_creds.server);
753 	ret = krb5_make_principal(context, &tmp_creds.server,
754 				  tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
755 	if(ret) {
756 	    krb5_free_principal(context, tmp_creds.server);
757 	    krb5_free_principal(context, tmp_creds.client);
758 	    return ret;
759 	}
760 	ret = krb5_free_creds(context, tgt);
761 	if(ret) {
762 	    krb5_free_principal(context, tmp_creds.server);
763 	    krb5_free_principal(context, tmp_creds.client);
764 	    return ret;
765 	}
766     }
767 
768     krb5_free_principal(context, tmp_creds.server);
769     krb5_free_principal(context, tmp_creds.client);
770     *out_creds = calloc(1, sizeof(**out_creds));
771     if(*out_creds == NULL) {
772 	krb5_set_error_string(context, "malloc: out of memory");
773 	ret = ENOMEM;
774     } else {
775 	ret = get_cred_kdc_la(context, ccache, flags,
776 				      in_creds, tgt, *out_creds);
777 	if (ret) {
778 	    free (*out_creds);
779 	    *out_creds = NULL;
780 	}
781     }
782     krb5_free_creds(context, tgt);
783     return ret;
784 }
785 
786 krb5_error_code
787 krb5_get_cred_from_kdc_opt(krb5_context context,
788 			   krb5_ccache ccache,
789 			   krb5_creds *in_creds,
790 			   krb5_creds **out_creds,
791 			   krb5_creds ***ret_tgts,
792 			   krb5_flags flags)
793 {
794     krb5_kdc_flags f;
795     f.i = flags;
796     return get_cred_from_kdc_flags(context, f, ccache,
797 				   in_creds, out_creds, ret_tgts);
798 }
799 
800 krb5_error_code
801 krb5_get_cred_from_kdc(krb5_context context,
802 		       krb5_ccache ccache,
803 		       krb5_creds *in_creds,
804 		       krb5_creds **out_creds,
805 		       krb5_creds ***ret_tgts)
806 {
807     return krb5_get_cred_from_kdc_opt(context, ccache,
808 				      in_creds, out_creds, ret_tgts, 0);
809 }
810 
811 
812 krb5_error_code
813 krb5_get_credentials_with_flags(krb5_context context,
814 				krb5_flags options,
815 				krb5_kdc_flags flags,
816 				krb5_ccache ccache,
817 				krb5_creds *in_creds,
818 				krb5_creds **out_creds)
819 {
820     krb5_error_code ret;
821     krb5_creds **tgts;
822     krb5_creds *res_creds;
823     int i;
824 
825     *out_creds = NULL;
826     res_creds = calloc(1, sizeof(*res_creds));
827     if (res_creds == NULL) {
828 	krb5_set_error_string(context, "malloc: out of memory");
829 	return ENOMEM;
830     }
831 
832     ret = krb5_cc_retrieve_cred(context,
833 				ccache,
834 				in_creds->session.keytype ?
835 				KRB5_TC_MATCH_KEYTYPE : 0,
836 				in_creds, res_creds);
837     if(ret == 0) {
838 	*out_creds = res_creds;
839 	return 0;
840     }
841     free(res_creds);
842     if(ret != KRB5_CC_END)
843 	return ret;
844     if(options & KRB5_GC_CACHED) {
845 	krb5_clear_error_string (context);
846 	return KRB5_CC_NOTFOUND;
847     }
848     if(options & KRB5_GC_USER_USER)
849 	flags.b.enc_tkt_in_skey = 1;
850     tgts = NULL;
851     ret = get_cred_from_kdc_flags(context, flags, ccache,
852 				  in_creds, out_creds, &tgts);
853     for(i = 0; tgts && tgts[i]; i++) {
854 	krb5_cc_store_cred(context, ccache, tgts[i]);
855 	krb5_free_creds(context, tgts[i]);
856     }
857     free(tgts);
858     if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
859 	krb5_cc_store_cred(context, ccache, *out_creds);
860     return ret;
861 }
862 
863 krb5_error_code
864 krb5_get_credentials(krb5_context context,
865 		     krb5_flags options,
866 		     krb5_ccache ccache,
867 		     krb5_creds *in_creds,
868 		     krb5_creds **out_creds)
869 {
870     krb5_kdc_flags flags;
871     flags.i = 0;
872     return krb5_get_credentials_with_flags(context, options, flags,
873 					   ccache, in_creds, out_creds);
874 }
875