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