663b5e7f1b92f5282d9cb0b07328ab8ef0461653
[sfrench/samba-autobuild/.git] / source4 / heimdal / 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.113 2006/11/21 05:14:01 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, buf_size;
90         unsigned char *buf;
91         krb5_crypto crypto;
92         krb5_error_code ret;
93
94         ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
95                            &len, ret);
96         if (ret)
97             return ret;
98         if (buf_size != len)
99             krb5_abortx(context, "internal error in ASN.1 encoder");
100
101         ALLOC(req_body->enc_authorization_data, 1);
102         if (req_body->enc_authorization_data == NULL) {
103             free (buf);
104             krb5_set_error_string(context, "malloc: out of memory");
105             return ENOMEM;
106         }
107         ret = krb5_crypto_init(context, key, 0, &crypto);
108         if (ret) {
109             free (buf);
110             free (req_body->enc_authorization_data);
111             req_body->enc_authorization_data = NULL;
112             return ret;
113         }
114         krb5_encrypt_EncryptedData(context, 
115                                    crypto,
116                                    KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, 
117                                    /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
118                                    buf,
119                                    len,
120                                    0,
121                                    req_body->enc_authorization_data);
122         free (buf);
123         krb5_crypto_destroy(context, crypto);
124     } else {
125         req_body->enc_authorization_data = NULL;
126     }
127     return 0;
128 }    
129
130 /*
131  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
132  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
133  * subkey in `subkey'.
134  */
135
136 static krb5_error_code
137 init_tgs_req (krb5_context context,
138               krb5_ccache ccache,
139               krb5_addresses *addresses,
140               krb5_kdc_flags flags,
141               Ticket *second_ticket,
142               krb5_creds *in_creds,
143               krb5_creds *krbtgt,
144               unsigned nonce,
145               const METHOD_DATA *padata,
146               krb5_keyblock **subkey,
147               TGS_REQ *t,
148               krb5_key_usage usage)
149 {
150     krb5_error_code ret = 0;
151
152     memset(t, 0, sizeof(*t));
153     t->pvno = 5;
154     t->msg_type = krb_tgs_req;
155     if (in_creds->session.keytype) {
156         ALLOC_SEQ(&t->req_body.etype, 1);
157         if(t->req_body.etype.val == NULL) {
158             ret = ENOMEM;
159             krb5_set_error_string(context, "malloc: out of memory");
160             goto fail;
161         }
162         t->req_body.etype.val[0] = in_creds->session.keytype;
163     } else {
164         ret = krb5_init_etype(context, 
165                               &t->req_body.etype.len, 
166                               &t->req_body.etype.val, 
167                               NULL);
168     }
169     if (ret)
170         goto fail;
171     t->req_body.addresses = addresses;
172     t->req_body.kdc_options = flags.b;
173     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
174     if (ret)
175         goto fail;
176     ALLOC(t->req_body.sname, 1);
177     if (t->req_body.sname == NULL) {
178         ret = ENOMEM;
179         krb5_set_error_string(context, "malloc: out of memory");
180         goto fail;
181     }
182
183     /* some versions of some code might require that the client be
184        present in TGS-REQs, but this is clearly against the spec */
185
186     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
187     if (ret)
188         goto fail;
189
190     /* req_body.till should be NULL if there is no endtime specified,
191        but old MIT code (like DCE secd) doesn't like that */
192     ALLOC(t->req_body.till, 1);
193     if(t->req_body.till == NULL){
194         ret = ENOMEM;
195         krb5_set_error_string(context, "malloc: out of memory");
196         goto fail;
197     }
198     *t->req_body.till = in_creds->times.endtime;
199     
200     t->req_body.nonce = nonce;
201     if(second_ticket){
202         ALLOC(t->req_body.additional_tickets, 1);
203         if (t->req_body.additional_tickets == NULL) {
204             ret = ENOMEM;
205             krb5_set_error_string(context, "malloc: out of memory");
206             goto fail;
207         }
208         ALLOC_SEQ(t->req_body.additional_tickets, 1);
209         if (t->req_body.additional_tickets->val == NULL) {
210             ret = ENOMEM;
211             krb5_set_error_string(context, "malloc: out of memory");
212             goto fail;
213         }
214         ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); 
215         if (ret)
216             goto fail;
217     }
218     ALLOC(t->padata, 1);
219     if (t->padata == NULL) {
220         ret = ENOMEM;
221         krb5_set_error_string(context, "malloc: out of memory");
222         goto fail;
223     }
224     ALLOC_SEQ(t->padata, 1 + padata->len);
225     if (t->padata->val == NULL) {
226         ret = ENOMEM;
227         krb5_set_error_string(context, "malloc: out of memory");
228         goto fail;
229     }
230     {
231         int i;
232         for (i = 0; i < padata->len; i++) {
233             ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
234             if (ret) {
235                 krb5_set_error_string(context, "malloc: out of memory");
236                 goto fail;
237             }
238         }
239     }
240
241     {
242         krb5_auth_context ac;
243         krb5_keyblock *key = NULL;
244
245         ret = krb5_auth_con_init(context, &ac);
246         if(ret)
247             goto fail;
248
249         if (krb5_config_get_bool_default(context, NULL, FALSE,
250                                          "realms",
251                                          krbtgt->server->realm,
252                                          "tgs_require_subkey",
253                                          NULL))
254         {
255             ret = krb5_generate_subkey (context, &krbtgt->session, &key);
256             if (ret) {
257                 krb5_auth_con_free (context, ac);
258                 goto fail;
259             }
260
261             ret = krb5_auth_con_setlocalsubkey(context, ac, key);
262             if (ret) {
263                 if (key)
264                     krb5_free_keyblock (context, key);
265                 krb5_auth_con_free (context, ac);
266                 goto fail;
267             }
268         }
269
270         ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
271                              key ? key : &krbtgt->session);
272         if (ret) {
273             if (key)
274                 krb5_free_keyblock (context, key);
275             krb5_auth_con_free (context, ac);
276             goto fail;
277         }
278
279         ret = make_pa_tgs_req(context,
280                               ac,
281                               &t->req_body, 
282                               &t->padata->val[0],
283                               krbtgt,
284                               usage);
285         if(ret) {
286             if (key)
287                 krb5_free_keyblock (context, key);
288             krb5_auth_con_free(context, ac);
289             goto fail;
290         }
291         *subkey = key;
292         
293         krb5_auth_con_free(context, ac);
294     }
295 fail:
296     if (ret) {
297         t->req_body.addresses = NULL;
298         free_TGS_REQ (t);
299     }
300     return ret;
301 }
302
303 krb5_error_code
304 _krb5_get_krbtgt(krb5_context context,
305                  krb5_ccache  id,
306                  krb5_realm realm,
307                  krb5_creds **cred)
308 {
309     krb5_error_code ret;
310     krb5_creds tmp_cred;
311
312     memset(&tmp_cred, 0, sizeof(tmp_cred));
313
314     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
315     if (ret)
316         return ret;
317
318     ret = krb5_make_principal(context, 
319                               &tmp_cred.server,
320                               realm,
321                               KRB5_TGS_NAME,
322                               realm,
323                               NULL);
324     if(ret) {
325         krb5_free_principal(context, tmp_cred.client);
326         return ret;
327     }
328     ret = krb5_get_credentials(context,
329                                KRB5_GC_CACHED,
330                                id,
331                                &tmp_cred,
332                                cred);
333     krb5_free_principal(context, tmp_cred.client);
334     krb5_free_principal(context, tmp_cred.server);
335     if(ret)
336         return ret;
337     return 0;
338 }
339
340 /* DCE compatible decrypt proc */
341 static krb5_error_code
342 decrypt_tkt_with_subkey (krb5_context context,
343                          krb5_keyblock *key,
344                          krb5_key_usage usage,
345                          krb5_const_pointer subkey,
346                          krb5_kdc_rep *dec_rep)
347 {
348     krb5_error_code ret;
349     krb5_data data;
350     size_t size;
351     krb5_crypto crypto;
352     
353     ret = krb5_crypto_init(context, key, 0, &crypto);
354     if (ret)
355         return ret;
356     ret = krb5_decrypt_EncryptedData (context,
357                                       crypto,
358                                       usage,
359                                       &dec_rep->kdc_rep.enc_part,
360                                       &data);
361     krb5_crypto_destroy(context, crypto);
362     if(ret && subkey){
363         /* DCE compat -- try to decrypt with subkey */
364         ret = krb5_crypto_init(context, subkey, 0, &crypto);
365         if (ret)
366             return ret;
367         ret = krb5_decrypt_EncryptedData (context,
368                                           crypto,
369                                           KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
370                                           &dec_rep->kdc_rep.enc_part,
371                                           &data);
372         krb5_crypto_destroy(context, crypto);
373     }
374     if (ret)
375         return ret;
376     
377     ret = krb5_decode_EncASRepPart(context,
378                                    data.data,
379                                    data.length,
380                                    &dec_rep->enc_part, 
381                                    &size);
382     if (ret)
383         ret = krb5_decode_EncTGSRepPart(context,
384                                         data.data,
385                                         data.length,
386                                         &dec_rep->enc_part, 
387                                         &size);
388     krb5_data_free (&data);
389     return ret;
390 }
391
392 static krb5_error_code
393 get_cred_kdc_usage(krb5_context context, 
394                    krb5_ccache id, 
395                    krb5_kdc_flags flags,
396                    krb5_addresses *addresses, 
397                    krb5_creds *in_creds,
398                    krb5_creds *krbtgt,
399                    krb5_principal impersonate_principal,
400                    Ticket *second_ticket,
401                    krb5_creds *out_creds,
402                    krb5_key_usage usage)
403 {
404     TGS_REQ req;
405     krb5_data enc;
406     krb5_data resp;
407     krb5_kdc_rep rep;
408     KRB_ERROR error;
409     krb5_error_code ret;
410     unsigned nonce;
411     krb5_keyblock *subkey = NULL;
412     size_t len;
413     Ticket second_ticket_data;
414     int send_to_kdc_flags = 0;
415     METHOD_DATA padata;
416     
417     krb5_data_zero(&resp);
418     krb5_data_zero(&enc);
419     padata.val = NULL;
420     padata.len = 0;
421
422     krb5_generate_random_block(&nonce, sizeof(nonce));
423     nonce &= 0xffffffff;
424     
425     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
426         ret = decode_Ticket(in_creds->second_ticket.data, 
427                             in_creds->second_ticket.length, 
428                             &second_ticket_data, &len);
429         if(ret)
430             return ret;
431         second_ticket = &second_ticket_data;
432     }
433
434
435     if (impersonate_principal) {
436         krb5_crypto crypto;
437         PA_S4U2Self self;
438         krb5_data data;
439         void *buf;
440         size_t size;
441
442         self.name = impersonate_principal->name;
443         self.realm = impersonate_principal->realm;
444         self.auth = estrdup("Kerberos");
445         
446         ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
447         if (ret) {
448             free(self.auth);
449             goto out;
450         }
451
452         ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
453         if (ret) {
454             free(self.auth);
455             krb5_data_free(&data);
456             goto out;
457         }
458
459         ret = krb5_create_checksum(context,
460                                    crypto,
461                                    KRB5_KU_OTHER_CKSUM,
462                                    0,
463                                    data.data,
464                                    data.length, 
465                                    &self.cksum);
466         krb5_crypto_destroy(context, crypto);
467         krb5_data_free(&data);
468         if (ret) {
469             free(self.auth);
470             goto out;
471         }
472
473         ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
474         free(self.auth);
475         free_Checksum(&self.cksum);
476         if (ret)
477             goto out;
478         if (len != size)
479             krb5_abortx(context, "internal asn1 error");
480         
481         ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len);
482         if (ret)
483             goto out;
484     }
485
486     ret = init_tgs_req (context,
487                         id,
488                         addresses,
489                         flags,
490                         second_ticket,
491                         in_creds,
492                         krbtgt,
493                         nonce,
494                         &padata,
495                         &subkey, 
496                         &req,
497                         usage);
498     if (ret)
499         goto out;
500
501     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
502     if (ret) 
503         goto out;
504     if(enc.length != len)
505         krb5_abortx(context, "internal error in ASN.1 encoder");
506
507     /* don't free addresses */
508     req.req_body.addresses = NULL;
509     free_TGS_REQ(&req);
510
511     /*
512      * Send and receive
513      */
514 again:
515     ret = krb5_sendto_kdc_flags (context, &enc, 
516                                  &krbtgt->server->name.name_string.val[1],
517                                  &resp,
518                                  send_to_kdc_flags);
519     if(ret)
520         goto out;
521
522     memset(&rep, 0, sizeof(rep));
523     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
524         ret = krb5_copy_principal(context, 
525                                   in_creds->client, 
526                                   &out_creds->client);
527         if(ret)
528             goto out;
529         ret = krb5_copy_principal(context, 
530                                   in_creds->server, 
531                                   &out_creds->server);
532         if(ret)
533             goto out;
534         /* this should go someplace else */
535         out_creds->times.endtime = in_creds->times.endtime;
536
537         ret = _krb5_extract_ticket(context,
538                                    &rep,
539                                    out_creds,
540                                    &krbtgt->session,
541                                    NULL,
542                                    KRB5_KU_TGS_REP_ENC_PART_SESSION,
543                                    &krbtgt->addresses,
544                                    nonce,
545                                    TRUE,
546                                    TRUE /* flags.b.request_anonymous */,
547                                    decrypt_tkt_with_subkey,
548                                    subkey);
549         krb5_free_kdc_rep(context, &rep);
550     } else if(krb5_rd_error(context, &resp, &error) == 0) {
551         ret = krb5_error_from_rd_error(context, &error, in_creds);
552         krb5_free_error_contents(context, &error);
553
554         if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !(send_to_kdc_flags & KRB5_KRBHST_FLAGS_LARGE_MSG)) {
555             send_to_kdc_flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
556             krb5_data_free(&resp);
557             goto again;
558         }
559     } else if(resp.data && ((char*)resp.data)[0] == 4) {
560         ret = KRB5KRB_AP_ERR_V4_REPLY;
561         krb5_clear_error_string(context);
562     } else {
563         ret = KRB5KRB_AP_ERR_MSG_TYPE;
564         krb5_clear_error_string(context);
565     }
566
567 out:
568     if (second_ticket == &second_ticket_data)
569         free_Ticket(&second_ticket_data);
570     free_METHOD_DATA(&padata);
571     krb5_data_free(&resp);
572     krb5_data_free(&enc);
573     if(subkey){
574         krb5_free_keyblock_contents(context, subkey);
575         free(subkey);
576     }
577     return ret;
578     
579 }
580
581 static krb5_error_code
582 get_cred_kdc(krb5_context context, 
583              krb5_ccache id, 
584              krb5_kdc_flags flags,
585              krb5_addresses *addresses, 
586              krb5_creds *in_creds, 
587              krb5_creds *krbtgt,
588              krb5_principal impersonate_principal,
589              Ticket *second_ticket,
590              krb5_creds *out_creds)
591 {
592     krb5_error_code ret;
593
594     ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
595                              krbtgt, impersonate_principal, second_ticket,
596                              out_creds, KRB5_KU_TGS_REQ_AUTH);
597     if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
598         krb5_clear_error_string (context);
599         ret = get_cred_kdc_usage(context, id, flags, addresses, in_creds,
600                                  krbtgt, impersonate_principal, second_ticket,
601                                  out_creds, KRB5_KU_AP_REQ_AUTH);
602     }
603     return ret;
604 }
605
606 /* same as above, just get local addresses first */
607
608 static krb5_error_code
609 get_cred_kdc_la(krb5_context context, krb5_ccache id, krb5_kdc_flags flags, 
610                 krb5_creds *in_creds, krb5_creds *krbtgt, 
611                 krb5_principal impersonate_principal, Ticket *second_ticket,
612                 krb5_creds *out_creds)
613 {
614     krb5_error_code ret;
615     krb5_addresses addresses, *addrs = &addresses;
616     
617     krb5_get_all_client_addrs(context, &addresses);
618     /* XXX this sucks. */
619     if(addresses.len == 0)
620         addrs = NULL;
621     ret = get_cred_kdc(context, id, flags, addrs, 
622                        in_creds, krbtgt, impersonate_principal, second_ticket,
623                        out_creds);
624     krb5_free_addresses(context, &addresses);
625     return ret;
626 }
627
628 krb5_error_code KRB5_LIB_FUNCTION
629 krb5_get_kdc_cred(krb5_context context,
630                   krb5_ccache id,
631                   krb5_kdc_flags flags,
632                   krb5_addresses *addresses,
633                   Ticket  *second_ticket,
634                   krb5_creds *in_creds,
635                   krb5_creds **out_creds
636                   )
637 {
638     krb5_error_code ret;
639     krb5_creds *krbtgt;
640
641     *out_creds = calloc(1, sizeof(**out_creds));
642     if(*out_creds == NULL) {
643         krb5_set_error_string(context, "malloc: out of memory");
644         return ENOMEM;
645     }
646     ret = _krb5_get_krbtgt (context,
647                             id,
648                             in_creds->server->realm,
649                             &krbtgt);
650     if(ret) {
651         free(*out_creds);
652         return ret;
653     }
654     ret = get_cred_kdc(context, id, flags, addresses, 
655                        in_creds, krbtgt, NULL, NULL, *out_creds);
656     krb5_free_creds (context, krbtgt);
657     if(ret)
658         free(*out_creds);
659     return ret;
660 }
661
662
663 static krb5_error_code
664 find_cred(krb5_context context,
665           krb5_ccache id,
666           krb5_principal server,
667           krb5_creds **tgts,
668           krb5_creds *out_creds)
669 {
670     krb5_error_code ret;
671     krb5_creds mcreds;
672
673     krb5_cc_clear_mcred(&mcreds);
674     mcreds.server = server;
675     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM, 
676                                 &mcreds, out_creds);
677     if(ret == 0)
678         return 0;
679     while(tgts && *tgts){
680         if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 
681                               &mcreds, *tgts)){
682             ret = krb5_copy_creds_contents(context, *tgts, out_creds);
683             return ret;
684         }
685         tgts++;
686     }
687     {
688         char *str;
689         ret = krb5_unparse_name(context, server, &str);
690         if(ret == 0) {
691             krb5_set_error_string(context, "Matching credential "
692                                   "(%s) not found", str);
693             free(str);
694         } else {
695             krb5_clear_error_string(context);
696         }
697     }
698     return KRB5_CC_NOTFOUND;
699 }
700
701 static krb5_error_code
702 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
703 {
704     int i;
705     krb5_error_code ret;
706     krb5_creds **tmp = *tgts;
707
708     for(i = 0; tmp && tmp[i]; i++); /* XXX */
709     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
710     if(tmp == NULL) {
711         krb5_set_error_string(context, "malloc: out of memory");
712         return ENOMEM;
713     }
714     *tgts = tmp;
715     ret = krb5_copy_creds(context, tkt, &tmp[i]);
716     tmp[i+1] = NULL;
717     return ret;
718 }
719
720 /*
721 get_cred(server)
722         creds = cc_get_cred(server)
723         if(creds) return creds
724         tgt = cc_get_cred(krbtgt/server_realm@any_realm)
725         if(tgt)
726                 return get_cred_tgt(server, tgt)
727         if(client_realm == server_realm)
728                 return NULL
729         tgt = get_cred(krbtgt/server_realm@client_realm)
730         while(tgt_inst != server_realm)
731                 tgt = get_cred(krbtgt/server_realm@tgt_inst)
732         return get_cred_tgt(server, tgt)
733         */
734
735 static krb5_error_code
736 get_cred_from_kdc_flags(krb5_context context,
737                         krb5_kdc_flags flags,
738                         krb5_ccache ccache,
739                         krb5_creds *in_creds,
740                         krb5_principal impersonate_principal,
741                         Ticket *second_ticket,                  
742                         krb5_creds **out_creds,
743                         krb5_creds ***ret_tgts)
744 {
745     krb5_error_code ret;
746     krb5_creds *tgt, tmp_creds;
747     krb5_const_realm client_realm, server_realm, try_realm;
748
749     *out_creds = NULL;
750
751     client_realm = krb5_principal_get_realm(context, in_creds->client);
752     server_realm = krb5_principal_get_realm(context, in_creds->server);
753     memset(&tmp_creds, 0, sizeof(tmp_creds));
754     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
755     if(ret)
756         return ret;
757
758     try_realm = krb5_config_get_string(context, NULL, "capaths", 
759                                        client_realm, server_realm, NULL);
760     
761 #if 1
762     /* XXX remove in future release */
763     if(try_realm == NULL)
764         try_realm = krb5_config_get_string(context, NULL, "libdefaults", 
765                                            "capath", server_realm, NULL);
766 #endif
767
768     if (try_realm == NULL)
769         try_realm = client_realm;
770
771     ret = krb5_make_principal(context,
772                               &tmp_creds.server,
773                               try_realm,
774                               KRB5_TGS_NAME,
775                               server_realm, 
776                               NULL);
777     if(ret){
778         krb5_free_principal(context, tmp_creds.client);
779         return ret;
780     }
781     {
782         krb5_creds tgts;
783         /* XXX try krb5_cc_retrieve_cred first? */
784         ret = find_cred(context, ccache, tmp_creds.server, 
785                         *ret_tgts, &tgts);
786         if(ret == 0){
787             *out_creds = calloc(1, sizeof(**out_creds));
788             if(*out_creds == NULL) {
789                 krb5_set_error_string(context, "malloc: out of memory");
790                 ret = ENOMEM;
791             } else {
792                 krb5_boolean noaddr;
793
794                 krb5_appdefault_boolean(context, NULL, tgts.server->realm,
795                                         "no-addresses", FALSE, &noaddr);
796
797                 if (noaddr)
798                     ret = get_cred_kdc(context, ccache, flags, NULL,
799                                        in_creds, &tgts,
800                                        impersonate_principal, 
801                                        second_ticket,
802                                        *out_creds);
803                 else
804                     ret = get_cred_kdc_la(context, ccache, flags, 
805                                           in_creds, &tgts, 
806                                           impersonate_principal, 
807                                           second_ticket,
808                                           *out_creds);
809                 if (ret) {
810                     free (*out_creds);
811                     *out_creds = NULL;
812                 }
813             }
814             krb5_free_cred_contents(context, &tgts);
815             krb5_free_principal(context, tmp_creds.server);
816             krb5_free_principal(context, tmp_creds.client);
817             return ret;
818         }
819     }
820     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
821         krb5_clear_error_string (context);
822         return KRB5_CC_NOTFOUND;
823     }
824     /* XXX this can loop forever */
825     while(1){
826         heim_general_string tgt_inst;
827
828         ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds, 
829                                       NULL, NULL, &tgt, ret_tgts);
830         if(ret) {
831             krb5_free_principal(context, tmp_creds.server);
832             krb5_free_principal(context, tmp_creds.client);
833             return ret;
834         }
835         ret = add_cred(context, ret_tgts, tgt);
836         if(ret) {
837             krb5_free_principal(context, tmp_creds.server);
838             krb5_free_principal(context, tmp_creds.client);
839             return ret;
840         }
841         tgt_inst = tgt->server->name.name_string.val[1];
842         if(strcmp(tgt_inst, server_realm) == 0)
843             break;
844         krb5_free_principal(context, tmp_creds.server);
845         ret = krb5_make_principal(context, &tmp_creds.server, 
846                                   tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
847         if(ret) {
848             krb5_free_principal(context, tmp_creds.server);
849             krb5_free_principal(context, tmp_creds.client);
850             return ret;
851         }
852         ret = krb5_free_creds(context, tgt);
853         if(ret) {
854             krb5_free_principal(context, tmp_creds.server);
855             krb5_free_principal(context, tmp_creds.client);
856             return ret;
857         }
858     }
859         
860     krb5_free_principal(context, tmp_creds.server);
861     krb5_free_principal(context, tmp_creds.client);
862     *out_creds = calloc(1, sizeof(**out_creds));
863     if(*out_creds == NULL) {
864         krb5_set_error_string(context, "malloc: out of memory");
865         ret = ENOMEM;
866     } else {
867         krb5_boolean noaddr;
868
869         krb5_appdefault_boolean(context, NULL, tgt->server->realm,
870                                 "no-addresses", KRB5_ADDRESSLESS_DEFAULT,
871                                 &noaddr);
872         if (noaddr)
873             ret = get_cred_kdc (context, ccache, flags, NULL,
874                                 in_creds, tgt, NULL, NULL,
875                                 *out_creds);
876         else
877             ret = get_cred_kdc_la(context, ccache, flags, 
878                                   in_creds, tgt, NULL, NULL,
879                                   *out_creds);
880         if (ret) {
881             free (*out_creds);
882             *out_creds = NULL;
883         }
884     }
885     krb5_free_creds(context, tgt);
886     return ret;
887 }
888
889 krb5_error_code KRB5_LIB_FUNCTION
890 krb5_get_cred_from_kdc_opt(krb5_context context,
891                            krb5_ccache ccache,
892                            krb5_creds *in_creds,
893                            krb5_creds **out_creds,
894                            krb5_creds ***ret_tgts,
895                            krb5_flags flags)
896 {
897     krb5_kdc_flags f;
898     f.i = flags;
899     return get_cred_from_kdc_flags(context, f, ccache, 
900                                    in_creds, NULL, NULL,
901                                    out_creds, ret_tgts);
902 }
903
904 krb5_error_code KRB5_LIB_FUNCTION
905 krb5_get_cred_from_kdc(krb5_context context,
906                        krb5_ccache ccache,
907                        krb5_creds *in_creds,
908                        krb5_creds **out_creds,
909                        krb5_creds ***ret_tgts)
910 {
911     return krb5_get_cred_from_kdc_opt(context, ccache, 
912                                       in_creds, out_creds, ret_tgts, 0);
913 }
914      
915
916 krb5_error_code KRB5_LIB_FUNCTION
917 krb5_get_credentials_with_flags(krb5_context context,
918                                 krb5_flags options,
919                                 krb5_kdc_flags flags,
920                                 krb5_ccache ccache,
921                                 krb5_creds *in_creds,
922                                 krb5_creds **out_creds)
923 {
924     krb5_error_code ret;
925     krb5_creds **tgts;
926     krb5_creds *res_creds;
927     int i;
928     
929     *out_creds = NULL;
930     res_creds = calloc(1, sizeof(*res_creds));
931     if (res_creds == NULL) {
932         krb5_set_error_string(context, "malloc: out of memory");
933         return ENOMEM;
934     }
935
936     if (in_creds->session.keytype)
937         options |= KRB5_TC_MATCH_KEYTYPE;
938
939     /* 
940      * If we got a credential, check if credential is expired before
941      * returning it.
942      */
943     ret = krb5_cc_retrieve_cred(context,
944                                 ccache,
945                                 in_creds->session.keytype ?
946                                 KRB5_TC_MATCH_KEYTYPE : 0,
947                                 in_creds, res_creds);
948     /* 
949      * If we got a credential, check if credential is expired before
950      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
951      */
952     if (ret == 0) {
953         krb5_timestamp timeret;
954
955         /* If expired ok, don't bother checking */
956         if(options & KRB5_GC_EXPIRED_OK) {
957             *out_creds = res_creds;
958             return 0;
959         }
960             
961         krb5_timeofday(context, &timeret);
962         if(res_creds->times.endtime > timeret) {
963             *out_creds = res_creds;
964             return 0;
965         }
966         if(options & KRB5_GC_CACHED)
967             krb5_cc_remove_cred(context, ccache, 0, res_creds);
968
969     } else if(ret != KRB5_CC_END) {
970         free(res_creds);
971         return ret;
972     }
973     free(res_creds);
974     if(options & KRB5_GC_CACHED) {
975         krb5_clear_error_string (context);        
976         return KRB5_CC_NOTFOUND;
977     }
978     if(options & KRB5_GC_USER_USER)
979         flags.b.enc_tkt_in_skey = 1;
980     if (flags.b.enc_tkt_in_skey)
981         options |= KRB5_GC_NO_STORE;
982
983     tgts = NULL;
984     ret = get_cred_from_kdc_flags(context, flags, ccache, 
985                                   in_creds, NULL, NULL, out_creds, &tgts);
986     for(i = 0; tgts && tgts[i]; i++) {
987         krb5_cc_store_cred(context, ccache, tgts[i]);
988         krb5_free_creds(context, tgts[i]);
989     }
990     free(tgts);
991     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
992         krb5_cc_store_cred(context, ccache, *out_creds);
993     return ret;
994 }
995
996 krb5_error_code KRB5_LIB_FUNCTION
997 krb5_get_credentials(krb5_context context,
998                      krb5_flags options,
999                      krb5_ccache ccache,
1000                      krb5_creds *in_creds,
1001                      krb5_creds **out_creds)
1002 {
1003     krb5_kdc_flags flags;
1004     flags.i = 0;
1005     return krb5_get_credentials_with_flags(context, options, flags,
1006                                            ccache, in_creds, out_creds);
1007 }
1008
1009 struct krb5_get_creds_opt_data {
1010     krb5_principal self;
1011     krb5_flags options;
1012     krb5_enctype enctype;
1013     Ticket *ticket;
1014 };
1015
1016
1017 krb5_error_code KRB5_LIB_FUNCTION
1018 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1019 {
1020     *opt = calloc(1, sizeof(**opt));
1021     if (*opt == NULL) {
1022         krb5_set_error_string(context, "malloc: out of memory");
1023         return ENOMEM;
1024     }
1025     return 0;
1026 }
1027
1028 void KRB5_LIB_FUNCTION
1029 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1030 {
1031     if (opt->self)
1032         krb5_free_principal(context, opt->self);
1033     memset(opt, 0, sizeof(*opt));
1034     free(opt);
1035 }
1036
1037 void KRB5_LIB_FUNCTION
1038 krb5_get_creds_opt_set_options(krb5_context context,
1039                                krb5_get_creds_opt opt,
1040                                krb5_flags options)
1041 {
1042     opt->options = options;
1043 }
1044
1045 void KRB5_LIB_FUNCTION
1046 krb5_get_creds_opt_add_options(krb5_context context,
1047                                krb5_get_creds_opt opt,
1048                                krb5_flags options)
1049 {
1050     opt->options |= options;
1051 }
1052
1053 void KRB5_LIB_FUNCTION
1054 krb5_get_creds_opt_set_enctype(krb5_context context,
1055                                krb5_get_creds_opt opt,
1056                                krb5_enctype enctype)
1057 {
1058     opt->enctype = enctype;
1059 }
1060
1061 krb5_error_code KRB5_LIB_FUNCTION
1062 krb5_get_creds_opt_set_impersonate(krb5_context context,
1063                                    krb5_get_creds_opt opt,
1064                                    krb5_const_principal self)
1065 {
1066     if (opt->self)
1067         krb5_free_principal(context, opt->self);
1068     return krb5_copy_principal(context, self, &opt->self);
1069 }
1070
1071 krb5_error_code KRB5_LIB_FUNCTION
1072 krb5_get_creds_opt_set_ticket(krb5_context context,
1073                               krb5_get_creds_opt opt,
1074                               const Ticket *ticket)
1075 {
1076     if (opt->ticket) {
1077         free_Ticket(opt->ticket);
1078         free(opt->ticket);
1079         opt->ticket = NULL;
1080     }
1081     if (ticket) {
1082         krb5_error_code ret;
1083
1084         opt->ticket = malloc(sizeof(*ticket));
1085         if (opt->ticket == NULL) {
1086             krb5_set_error_string(context, "malloc: out of memory");
1087             return ENOMEM;
1088         }
1089         ret = copy_Ticket(ticket, opt->ticket);
1090         if (ret) {
1091             free(opt->ticket);
1092             opt->ticket = NULL;
1093             krb5_set_error_string(context, "malloc: out of memory");
1094             return ret;
1095         }
1096     }
1097     return 0;
1098 }
1099
1100
1101
1102 krb5_error_code KRB5_LIB_FUNCTION
1103 krb5_get_creds(krb5_context context,
1104                krb5_get_creds_opt opt,
1105                krb5_ccache ccache,
1106                krb5_const_principal inprinc,
1107                krb5_creds **out_creds)
1108 {
1109     krb5_kdc_flags flags;
1110     krb5_flags options;
1111     krb5_creds in_creds;
1112     krb5_error_code ret;
1113     krb5_creds **tgts;
1114     krb5_creds *res_creds;
1115     int i;
1116     
1117     memset(&in_creds, 0, sizeof(in_creds));
1118     in_creds.server = rk_UNCONST(inprinc);
1119
1120     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1121     if (ret)
1122         return ret;
1123
1124     options = opt->options;
1125     flags.i = 0;
1126
1127     *out_creds = NULL;
1128     res_creds = calloc(1, sizeof(*res_creds));
1129     if (res_creds == NULL) {
1130         krb5_free_principal(context, in_creds.client);
1131         krb5_set_error_string(context, "malloc: out of memory");
1132         return ENOMEM;
1133     }
1134
1135     if (opt->enctype) {
1136         in_creds.session.keytype = opt->enctype;
1137         options |= KRB5_TC_MATCH_KEYTYPE;
1138     }
1139
1140     /* 
1141      * If we got a credential, check if credential is expired before
1142      * returning it.
1143      */
1144     ret = krb5_cc_retrieve_cred(context,
1145                                 ccache,
1146                                 opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
1147                                 &in_creds, res_creds);
1148     /* 
1149      * If we got a credential, check if credential is expired before
1150      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1151      */
1152     if (ret == 0) {
1153         krb5_timestamp timeret;
1154
1155         /* If expired ok, don't bother checking */
1156         if(options & KRB5_GC_EXPIRED_OK) {
1157             *out_creds = res_creds;
1158             krb5_free_principal(context, in_creds.client);
1159             return 0;
1160         }
1161             
1162         krb5_timeofday(context, &timeret);
1163         if(res_creds->times.endtime > timeret) {
1164             *out_creds = res_creds;
1165             krb5_free_principal(context, in_creds.client);
1166             return 0;
1167         }
1168         if(options & KRB5_GC_CACHED)
1169             krb5_cc_remove_cred(context, ccache, 0, res_creds);
1170
1171     } else if(ret != KRB5_CC_END) {
1172         free(res_creds);
1173         krb5_free_principal(context, in_creds.client);
1174         return ret;
1175     }
1176     free(res_creds);
1177     if(options & KRB5_GC_CACHED) {
1178         krb5_clear_error_string (context);        
1179         krb5_free_principal(context, in_creds.client);
1180         return KRB5_CC_NOTFOUND;
1181     }
1182     if(options & KRB5_GC_USER_USER) {
1183         flags.b.enc_tkt_in_skey = 1;
1184         options |= KRB5_GC_NO_STORE;
1185     }
1186     if (options & KRB5_GC_FORWARDABLE)
1187         flags.b.forwardable = 1;
1188     if (options & KRB5_GC_NO_TRANSIT_CHECK)
1189         flags.b.disable_transited_check = 1;
1190
1191     tgts = NULL;
1192     ret = get_cred_from_kdc_flags(context, flags, ccache, 
1193                                   &in_creds, opt->self, opt->ticket,
1194                                   out_creds, &tgts);
1195     krb5_free_principal(context, in_creds.client);
1196     for(i = 0; tgts && tgts[i]; i++) {
1197         krb5_cc_store_cred(context, ccache, tgts[i]);
1198         krb5_free_creds(context, tgts[i]);
1199     }
1200     free(tgts);
1201     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1202         krb5_cc_store_cred(context, ccache, *out_creds);
1203     return ret;
1204 }