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