Merge from vendor branch CVS:
[dragonfly.git] / crypto / heimdal-0.6.3 / lib / krb5 / get_cred.c
1 /*
2  * Copyright (c) 1997 - 2004 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.4.3 2004/01/09 00:47:17 lha 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 = NULL;
229
230         ret = krb5_auth_con_init(context, &ac);
231         if(ret)
232             goto fail;
233
234         if (krb5_config_get_bool_default(context, NULL, FALSE,
235                                          "realms",
236                                          krbtgt->server->realm,
237                                          "tgs_require_subkey",
238                                          NULL))
239         {
240             ret = krb5_generate_subkey (context, &krbtgt->session, &key);
241             if (ret) {
242                 krb5_auth_con_free (context, ac);
243                 goto fail;
244             }
245
246             ret = krb5_auth_con_setlocalsubkey(context, ac, key);
247             if (ret) {
248                 if (key)
249                     krb5_free_keyblock (context, key);
250                 krb5_auth_con_free (context, ac);
251                 goto fail;
252             }
253         }
254
255         ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key);
256         if (ret) {
257             if (key)
258                 krb5_free_keyblock (context, key);
259             krb5_auth_con_free (context, ac);
260             goto fail;
261         }
262
263         ret = make_pa_tgs_req(context,
264                               ac,
265                               &t->req_body, 
266                               t->padata->val,
267                               krbtgt,
268                               usage);
269         if(ret) {
270             if (key)
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         t->req_body.addresses = NULL;
282         free_TGS_REQ (t);
283     }
284     return ret;
285 }
286
287 krb5_error_code
288 _krb5_get_krbtgt(krb5_context context,
289                  krb5_ccache  id,
290                  krb5_realm realm,
291                  krb5_creds **cred)
292 {
293     krb5_error_code ret;
294     krb5_creds tmp_cred;
295
296     memset(&tmp_cred, 0, sizeof(tmp_cred));
297
298     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
299     if (ret)
300         return ret;
301
302     ret = krb5_make_principal(context, 
303                               &tmp_cred.server,
304                               realm,
305                               KRB5_TGS_NAME,
306                               realm,
307                               NULL);
308     if(ret) {
309         krb5_free_principal(context, tmp_cred.client);
310         return ret;
311     }
312     ret = krb5_get_credentials(context,
313                                KRB5_GC_CACHED,
314                                id,
315                                &tmp_cred,
316                                cred);
317     krb5_free_principal(context, tmp_cred.client);
318     krb5_free_principal(context, tmp_cred.server);
319     if(ret)
320         return ret;
321     return 0;
322 }
323
324 /* DCE compatible decrypt proc */
325 static krb5_error_code
326 decrypt_tkt_with_subkey (krb5_context context,
327                          krb5_keyblock *key,
328                          krb5_key_usage usage,
329                          krb5_const_pointer subkey,
330                          krb5_kdc_rep *dec_rep)
331 {
332     krb5_error_code ret;
333     krb5_data data;
334     size_t size;
335     krb5_crypto crypto;
336     
337     ret = krb5_crypto_init(context, key, 0, &crypto);
338     if (ret)
339         return ret;
340     ret = krb5_decrypt_EncryptedData (context,
341                                       crypto,
342                                       usage,
343                                       &dec_rep->kdc_rep.enc_part,
344                                       &data);
345     krb5_crypto_destroy(context, crypto);
346     if(ret && subkey){
347         /* DCE compat -- try to decrypt with subkey */
348         ret = krb5_crypto_init(context, (krb5_keyblock*)subkey, 0, &crypto);
349         if (ret)
350             return ret;
351         ret = krb5_decrypt_EncryptedData (context,
352                                           crypto,
353                                           KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
354                                           &dec_rep->kdc_rep.enc_part,
355                                           &data);
356         krb5_crypto_destroy(context, crypto);
357     }
358     if (ret)
359         return ret;
360     
361     ret = krb5_decode_EncASRepPart(context,
362                                    data.data,
363                                    data.length,
364                                    &dec_rep->enc_part, 
365                                    &size);
366     if (ret)
367         ret = krb5_decode_EncTGSRepPart(context,
368                                         data.data,
369                                         data.length,
370                                         &dec_rep->enc_part, 
371                                         &size);
372     krb5_data_free (&data);
373     return ret;
374 }
375
376 static krb5_error_code
377 get_cred_kdc_usage(krb5_context context, 
378                    krb5_ccache id, 
379                    krb5_kdc_flags flags,
380                    krb5_addresses *addresses, 
381                    krb5_creds *in_creds, 
382                    krb5_creds *krbtgt,
383                    krb5_creds *out_creds,
384                    krb5_key_usage usage)
385 {
386     TGS_REQ req;
387     krb5_data enc;
388     krb5_data resp;
389     krb5_kdc_rep rep;
390     KRB_ERROR error;
391     krb5_error_code ret;
392     unsigned nonce;
393     krb5_keyblock *subkey = NULL;
394     u_char *buf = NULL;
395     size_t buf_size;
396     size_t len;
397     Ticket second_ticket;
398     
399     krb5_generate_random_block(&nonce, sizeof(nonce));
400     nonce &= 0xffffffff;
401     
402     if(flags.b.enc_tkt_in_skey){
403         ret = decode_Ticket(in_creds->second_ticket.data, 
404                             in_creds->second_ticket.length, 
405                             &second_ticket, &len);
406         if(ret)
407             return ret;
408     }
409
410     ret = init_tgs_req (context,
411                         id,
412                         addresses,
413                         flags,
414                         flags.b.enc_tkt_in_skey ? &second_ticket : NULL,
415                         in_creds,
416                         krbtgt,
417                         nonce,
418                         &subkey, 
419                         &req,
420                         usage);
421     if(flags.b.enc_tkt_in_skey)
422         free_Ticket(&second_ticket);
423     if (ret)
424         goto out;
425
426     ASN1_MALLOC_ENCODE(TGS_REQ, buf, buf_size, &req, &enc.length, ret);
427     if (ret) 
428         goto out;
429     if(enc.length != buf_size)
430         krb5_abortx(context, "internal error in ASN.1 encoder");
431
432     /* don't free addresses */
433     req.req_body.addresses = NULL;
434     free_TGS_REQ(&req);
435
436     enc.data = buf + buf_size - enc.length;
437     if (ret)
438         goto out;
439     
440     /*
441      * Send and receive
442      */
443
444     ret = krb5_sendto_kdc (context, &enc, 
445                            &krbtgt->server->name.name_string.val[1], &resp);
446     if(ret)
447         goto out;
448
449     memset(&rep, 0, sizeof(rep));
450     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
451         ret = krb5_copy_principal(context, 
452                                   in_creds->client, 
453                                   &out_creds->client);
454         if(ret)
455             goto out;
456         ret = krb5_copy_principal(context, 
457                                   in_creds->server, 
458                                   &out_creds->server);
459         if(ret)
460             goto out;
461         /* this should go someplace else */
462         out_creds->times.endtime = in_creds->times.endtime;
463
464         ret = _krb5_extract_ticket(context,
465                                    &rep,
466                                    out_creds,
467                                    &krbtgt->session,
468                                    NULL,
469                                    KRB5_KU_TGS_REP_ENC_PART_SESSION,
470                                    &krbtgt->addresses,
471                                    nonce,
472                                    TRUE,
473                                    flags.b.request_anonymous,
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 = krb5_error_from_rd_error(context, &error, in_creds);
481         krb5_free_error_contents(context, &error);
482     } else if(resp.data && ((char*)resp.data)[0] == 4) {
483         ret = KRB5KRB_AP_ERR_V4_REPLY;
484         krb5_clear_error_string(context);
485     } else {
486         ret = KRB5KRB_AP_ERR_MSG_TYPE;
487         krb5_clear_error_string(context);
488     }
489     krb5_data_free(&resp);
490  out:
491     if(subkey){
492         krb5_free_keyblock_contents(context, subkey);
493         free(subkey);
494     }
495     if (buf)
496         free (buf);
497     return ret;
498     
499 }
500
501 static krb5_error_code
502 get_cred_kdc(krb5_context context, 
503              krb5_ccache id, 
504              krb5_kdc_flags flags,
505              krb5_addresses *addresses, 
506              krb5_creds *in_creds, 
507              krb5_creds *krbtgt,
508              krb5_creds *out_creds)
509 {
510     krb5_error_code ret;
511
512     ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
513                              krbtgt, out_creds, KRB5_KU_TGS_REQ_AUTH);
514     if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
515         krb5_clear_error_string (context);
516         ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
517                                  krbtgt, out_creds, KRB5_KU_AP_REQ_AUTH);
518     }
519     return ret;
520 }
521
522 /* same as above, just get local addresses first */
523
524 static krb5_error_code
525 get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, 
526                 krb5_creds *in_creds, krb5_creds *krbtgt, 
527                 krb5_creds *out_creds)
528 {
529     krb5_error_code ret;
530     krb5_addresses addresses, *addrs = &addresses;
531     
532     krb5_get_all_client_addrs(context, &addresses);
533     /* XXX this sucks. */
534     if(addresses.len == 0)
535         addrs = NULL;
536     ret = get_cred_kdc(context, id, flags, addrs, 
537                        in_creds, krbtgt, out_creds);
538     krb5_free_addresses(context, &addresses);
539     return ret;
540 }
541
542 krb5_error_code
543 krb5_get_kdc_cred(krb5_context context,
544                   krb5_ccache id,
545                   krb5_kdc_flags flags,
546                   krb5_addresses *addresses,
547                   Ticket  *second_ticket,
548                   krb5_creds *in_creds,
549                   krb5_creds **out_creds
550                   )
551 {
552     krb5_error_code ret;
553     krb5_creds *krbtgt;
554
555     *out_creds = calloc(1, sizeof(**out_creds));
556     if(*out_creds == NULL) {
557         krb5_set_error_string(context, "malloc: out of memory");
558         return ENOMEM;
559     }
560     ret = _krb5_get_krbtgt (context,
561                             id,
562                             in_creds->server->realm,
563                             &krbtgt);
564     if(ret) {
565         free(*out_creds);
566         return ret;
567     }
568     ret = get_cred_kdc(context, id, flags, addresses, 
569                        in_creds, krbtgt, *out_creds);
570     krb5_free_creds (context, krbtgt);
571     if(ret)
572         free(*out_creds);
573     return ret;
574 }
575
576
577 static krb5_error_code
578 find_cred(krb5_context context,
579           krb5_ccache id,
580           krb5_principal server,
581           krb5_creds **tgts,
582           krb5_creds *out_creds)
583 {
584     krb5_error_code ret;
585     krb5_creds mcreds;
586     mcreds.server = server;
587     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM, 
588                                 &mcreds, out_creds);
589     if(ret == 0)
590         return 0;
591     while(tgts && *tgts){
592         if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 
593                               &mcreds, *tgts)){
594             ret = krb5_copy_creds_contents(context, *tgts, out_creds);
595             return ret;
596         }
597         tgts++;
598     }
599     krb5_clear_error_string(context);
600     return KRB5_CC_NOTFOUND;
601 }
602
603 static krb5_error_code
604 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
605 {
606     int i;
607     krb5_error_code ret;
608     krb5_creds **tmp = *tgts;
609
610     for(i = 0; tmp && tmp[i]; i++); /* XXX */
611     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
612     if(tmp == NULL) {
613         krb5_set_error_string(context, "malloc: out of memory");
614         return ENOMEM;
615     }
616     *tgts = tmp;
617     ret = krb5_copy_creds(context, tkt, &tmp[i]);
618     tmp[i+1] = NULL;
619     return ret;
620 }
621
622 /*
623 get_cred(server)
624         creds = cc_get_cred(server)
625         if(creds) return creds
626         tgt = cc_get_cred(krbtgt/server_realm@any_realm)
627         if(tgt)
628                 return get_cred_tgt(server, tgt)
629         if(client_realm == server_realm)
630                 return NULL
631         tgt = get_cred(krbtgt/server_realm@client_realm)
632         while(tgt_inst != server_realm)
633                 tgt = get_cred(krbtgt/server_realm@tgt_inst)
634         return get_cred_tgt(server, tgt)
635         */
636
637 static krb5_error_code
638 get_cred_from_kdc_flags(krb5_context context,
639                         krb5_kdc_flags flags,
640                         krb5_ccache ccache,
641                         krb5_creds *in_creds,
642                         krb5_creds **out_creds,
643                         krb5_creds ***ret_tgts)
644 {
645     krb5_error_code ret;
646     krb5_creds *tgt, tmp_creds;
647     krb5_const_realm client_realm, server_realm, try_realm;
648
649     *out_creds = NULL;
650
651     client_realm = *krb5_princ_realm(context, in_creds->client);
652     server_realm = *krb5_princ_realm(context, in_creds->server);
653     memset(&tmp_creds, 0, sizeof(tmp_creds));
654     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
655     if(ret)
656         return ret;
657
658     try_realm = krb5_config_get_string(context, NULL, "capaths", 
659                                        client_realm, server_realm, NULL);
660     
661 #if 1
662     /* XXX remove in future release */
663     if(try_realm == NULL)
664         try_realm = krb5_config_get_string(context, NULL, "libdefaults", 
665                                            "capath", server_realm, NULL);
666 #endif
667
668     if (try_realm == NULL)
669         try_realm = client_realm;
670
671     ret = krb5_make_principal(context,
672                               &tmp_creds.server,
673                               try_realm,
674                               KRB5_TGS_NAME,
675                               server_realm, 
676                               NULL);
677     if(ret){
678         krb5_free_principal(context, tmp_creds.client);
679         return ret;
680     }
681     {
682         krb5_creds tgts;
683         /* XXX try krb5_cc_retrieve_cred first? */
684         ret = find_cred(context, ccache, tmp_creds.server, 
685                         *ret_tgts, &tgts);
686         if(ret == 0){
687             *out_creds = calloc(1, sizeof(**out_creds));
688             if(*out_creds == NULL) {
689                 krb5_set_error_string(context, "malloc: out of memory");
690                 ret = ENOMEM;
691             } else {
692                 krb5_boolean noaddr;
693
694                 krb5_appdefault_boolean(context, NULL, tgts.server->realm,
695                                         "no-addresses", FALSE, &noaddr);
696
697                 if (noaddr)
698                     ret = get_cred_kdc(context, ccache, flags, NULL,
699                                        in_creds, &tgts, *out_creds);
700                 else
701                     ret = get_cred_kdc_la(context, ccache, flags, 
702                                           in_creds, &tgts, *out_creds);
703                 if (ret) {
704                     free (*out_creds);
705                     *out_creds = NULL;
706                 }
707             }
708             krb5_free_creds_contents(context, &tgts);
709             krb5_free_principal(context, tmp_creds.server);
710             krb5_free_principal(context, tmp_creds.client);
711             return ret;
712         }
713     }
714     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
715         krb5_clear_error_string (context);
716         return KRB5_CC_NOTFOUND;
717     }
718     /* XXX this can loop forever */
719     while(1){
720         general_string tgt_inst;
721
722         ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds, 
723                                       &tgt, ret_tgts);
724         if(ret) {
725             krb5_free_principal(context, tmp_creds.server);
726             krb5_free_principal(context, tmp_creds.client);
727             return ret;
728         }
729         ret = add_cred(context, ret_tgts, tgt);
730         if(ret) {
731             krb5_free_principal(context, tmp_creds.server);
732             krb5_free_principal(context, tmp_creds.client);
733             return ret;
734         }
735         tgt_inst = tgt->server->name.name_string.val[1];
736         if(strcmp(tgt_inst, server_realm) == 0)
737             break;
738         krb5_free_principal(context, tmp_creds.server);
739         ret = krb5_make_principal(context, &tmp_creds.server, 
740                                   tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
741         if(ret) {
742             krb5_free_principal(context, tmp_creds.server);
743             krb5_free_principal(context, tmp_creds.client);
744             return ret;
745         }
746         ret = krb5_free_creds(context, tgt);
747         if(ret) {
748             krb5_free_principal(context, tmp_creds.server);
749             krb5_free_principal(context, tmp_creds.client);
750             return ret;
751         }
752     }
753         
754     krb5_free_principal(context, tmp_creds.server);
755     krb5_free_principal(context, tmp_creds.client);
756     *out_creds = calloc(1, sizeof(**out_creds));
757     if(*out_creds == NULL) {
758         krb5_set_error_string(context, "malloc: out of memory");
759         ret = ENOMEM;
760     } else {
761         krb5_boolean noaddr;
762
763         krb5_appdefault_boolean(context, NULL, tgt->server->realm,
764                                 "no-addresses", FALSE, &noaddr);
765         if (noaddr)
766             ret = get_cred_kdc (context, ccache, flags, NULL,
767                                 in_creds, tgt, *out_creds);
768         else
769             ret = get_cred_kdc_la(context, ccache, flags, 
770                                   in_creds, tgt, *out_creds);
771         if (ret) {
772             free (*out_creds);
773             *out_creds = NULL;
774         }
775     }
776     krb5_free_creds(context, tgt);
777     return ret;
778 }
779
780 krb5_error_code
781 krb5_get_cred_from_kdc_opt(krb5_context context,
782                            krb5_ccache ccache,
783                            krb5_creds *in_creds,
784                            krb5_creds **out_creds,
785                            krb5_creds ***ret_tgts,
786                            krb5_flags flags)
787 {
788     krb5_kdc_flags f;
789     f.i = flags;
790     return get_cred_from_kdc_flags(context, f, ccache, 
791                                    in_creds, out_creds, ret_tgts);
792 }
793
794 krb5_error_code
795 krb5_get_cred_from_kdc(krb5_context context,
796                        krb5_ccache ccache,
797                        krb5_creds *in_creds,
798                        krb5_creds **out_creds,
799                        krb5_creds ***ret_tgts)
800 {
801     return krb5_get_cred_from_kdc_opt(context, ccache, 
802                                       in_creds, out_creds, ret_tgts, 0);
803 }
804      
805
806 krb5_error_code
807 krb5_get_credentials_with_flags(krb5_context context,
808                                 krb5_flags options,
809                                 krb5_kdc_flags flags,
810                                 krb5_ccache ccache,
811                                 krb5_creds *in_creds,
812                                 krb5_creds **out_creds)
813 {
814     krb5_error_code ret;
815     krb5_creds **tgts;
816     krb5_creds *res_creds;
817     int i;
818     
819     *out_creds = NULL;
820     res_creds = calloc(1, sizeof(*res_creds));
821     if (res_creds == NULL) {
822         krb5_set_error_string(context, "malloc: out of memory");
823         return ENOMEM;
824     }
825
826     ret = krb5_cc_retrieve_cred(context,
827                                 ccache,
828                                 in_creds->session.keytype ?
829                                 KRB5_TC_MATCH_KEYTYPE : 0,
830                                 in_creds, res_creds);
831     if(ret == 0) {
832         *out_creds = res_creds;
833         return 0;
834     }
835     free(res_creds);
836     if(ret != KRB5_CC_END)
837         return ret;
838     if(options & KRB5_GC_CACHED) {
839         krb5_clear_error_string (context);
840         return KRB5_CC_NOTFOUND;
841     }
842     if(options & KRB5_GC_USER_USER)
843         flags.b.enc_tkt_in_skey = 1;
844     tgts = NULL;
845     ret = get_cred_from_kdc_flags(context, flags, ccache, 
846                                   in_creds, out_creds, &tgts);
847     for(i = 0; tgts && tgts[i]; i++) {
848         krb5_cc_store_cred(context, ccache, tgts[i]);
849         krb5_free_creds(context, tgts[i]);
850     }
851     free(tgts);
852     if(ret == 0 && flags.b.enc_tkt_in_skey == 0)
853         krb5_cc_store_cred(context, ccache, *out_creds);
854     return ret;
855 }
856
857 krb5_error_code
858 krb5_get_credentials(krb5_context context,
859                      krb5_flags options,
860                      krb5_ccache ccache,
861                      krb5_creds *in_creds,
862                      krb5_creds **out_creds)
863 {
864     krb5_kdc_flags flags;
865     flags.i = 0;
866     return krb5_get_credentials_with_flags(context, options, flags,
867                                            ccache, in_creds, out_creds);
868 }