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