8a0af23e408f77e05c211dd442047ebf6bcc4d50
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / krb5 / get_cred.c
1 /*
2  * Copyright (c) 1997 - 2007 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 21327 2007-06-26 10:54:15Z lha $");
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     METHOD_DATA padata;
415     
416     krb5_data_zero(&resp);
417     krb5_data_zero(&enc);
418     padata.val = NULL;
419     padata.len = 0;
420
421     krb5_generate_random_block(&nonce, sizeof(nonce));
422     nonce &= 0xffffffff;
423     
424     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
425         ret = decode_Ticket(in_creds->second_ticket.data, 
426                             in_creds->second_ticket.length, 
427                             &second_ticket_data, &len);
428         if(ret)
429             return ret;
430         second_ticket = &second_ticket_data;
431     }
432
433
434     if (impersonate_principal) {
435         krb5_crypto crypto;
436         PA_S4U2Self self;
437         krb5_data data;
438         void *buf;
439         size_t size;
440
441         self.name = impersonate_principal->name;
442         self.realm = impersonate_principal->realm;
443         self.auth = estrdup("Kerberos");
444         
445         ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
446         if (ret) {
447             free(self.auth);
448             goto out;
449         }
450
451         ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
452         if (ret) {
453             free(self.auth);
454             krb5_data_free(&data);
455             goto out;
456         }
457
458         ret = krb5_create_checksum(context,
459                                    crypto,
460                                    KRB5_KU_OTHER_CKSUM,
461                                    0,
462                                    data.data,
463                                    data.length, 
464                                    &self.cksum);
465         krb5_crypto_destroy(context, crypto);
466         krb5_data_free(&data);
467         if (ret) {
468             free(self.auth);
469             goto out;
470         }
471
472         ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
473         free(self.auth);
474         free_Checksum(&self.cksum);
475         if (ret)
476             goto out;
477         if (len != size)
478             krb5_abortx(context, "internal asn1 error");
479         
480         ret = krb5_padata_add(context, &padata, KRB5_PADATA_S4U2SELF, buf, len);
481         if (ret)
482             goto out;
483     }
484
485     ret = init_tgs_req (context,
486                         id,
487                         addresses,
488                         flags,
489                         second_ticket,
490                         in_creds,
491                         krbtgt,
492                         nonce,
493                         &padata,
494                         &subkey, 
495                         &req,
496                         usage);
497     if (ret)
498         goto out;
499
500     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
501     if (ret) 
502         goto out;
503     if(enc.length != len)
504         krb5_abortx(context, "internal error in ASN.1 encoder");
505
506     /* don't free addresses */
507     req.req_body.addresses = NULL;
508     free_TGS_REQ(&req);
509
510     /*
511      * Send and receive
512      */
513     {
514         krb5_sendto_ctx stctx;
515         ret = krb5_sendto_ctx_alloc(context, &stctx);
516         if (ret)
517             return ret;
518         krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
519
520         ret = krb5_sendto_context (context, stctx, &enc,
521                                    krbtgt->server->name.name_string.val[1],
522                                    &resp);
523         krb5_sendto_ctx_free(context, stctx);
524     }
525     if(ret)
526         goto out;
527
528     memset(&rep, 0, sizeof(rep));
529     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0){
530         ret = krb5_copy_principal(context, 
531                                   in_creds->client, 
532                                   &out_creds->client);
533         if(ret)
534             goto out;
535         ret = krb5_copy_principal(context, 
536                                   in_creds->server, 
537                                   &out_creds->server);
538         if(ret)
539             goto out;
540         /* this should go someplace else */
541         out_creds->times.endtime = in_creds->times.endtime;
542
543         ret = _krb5_extract_ticket(context,
544                                    &rep,
545                                    out_creds,
546                                    &krbtgt->session,
547                                    NULL,
548                                    KRB5_KU_TGS_REP_ENC_PART_SESSION,
549                                    &krbtgt->addresses,
550                                    nonce,
551                                    EXTRACT_TICKET_ALLOW_CNAME_MISMATCH|
552                                    EXTRACT_TICKET_ALLOW_SERVER_MISMATCH,
553                                    decrypt_tkt_with_subkey,
554                                    subkey);
555         krb5_free_kdc_rep(context, &rep);
556     } else if(krb5_rd_error(context, &resp, &error) == 0) {
557         ret = krb5_error_from_rd_error(context, &error, in_creds);
558         krb5_free_error_contents(context, &error);
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 static void
663 not_found(krb5_context context, krb5_const_principal p)
664 {
665     krb5_error_code ret;
666     char *str;
667
668     ret = krb5_unparse_name(context, p, &str);
669     if(ret) {
670         krb5_clear_error_string(context);
671         return;
672     }
673     krb5_set_error_string(context, "Matching credential (%s) not found", str);
674     free(str);
675 }
676
677 static krb5_error_code
678 find_cred(krb5_context context,
679           krb5_ccache id,
680           krb5_principal server,
681           krb5_creds **tgts,
682           krb5_creds *out_creds)
683 {
684     krb5_error_code ret;
685     krb5_creds mcreds;
686
687     krb5_cc_clear_mcred(&mcreds);
688     mcreds.server = server;
689     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM, 
690                                 &mcreds, out_creds);
691     if(ret == 0)
692         return 0;
693     while(tgts && *tgts){
694         if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, 
695                               &mcreds, *tgts)){
696             ret = krb5_copy_creds_contents(context, *tgts, out_creds);
697             return ret;
698         }
699         tgts++;
700     }
701     not_found(context, server);
702     return KRB5_CC_NOTFOUND;
703 }
704
705 static krb5_error_code
706 add_cred(krb5_context context, krb5_creds ***tgts, krb5_creds *tkt)
707 {
708     int i;
709     krb5_error_code ret;
710     krb5_creds **tmp = *tgts;
711
712     for(i = 0; tmp && tmp[i]; i++); /* XXX */
713     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
714     if(tmp == NULL) {
715         krb5_set_error_string(context, "malloc: out of memory");
716         return ENOMEM;
717     }
718     *tgts = tmp;
719     ret = krb5_copy_creds(context, tkt, &tmp[i]);
720     tmp[i+1] = NULL;
721     return ret;
722 }
723
724 /*
725 get_cred(server)
726         creds = cc_get_cred(server)
727         if(creds) return creds
728         tgt = cc_get_cred(krbtgt/server_realm@any_realm)
729         if(tgt)
730                 return get_cred_tgt(server, tgt)
731         if(client_realm == server_realm)
732                 return NULL
733         tgt = get_cred(krbtgt/server_realm@client_realm)
734         while(tgt_inst != server_realm)
735                 tgt = get_cred(krbtgt/server_realm@tgt_inst)
736         return get_cred_tgt(server, tgt)
737         */
738
739 static krb5_error_code
740 get_cred_from_kdc_flags(krb5_context context,
741                         krb5_kdc_flags flags,
742                         krb5_ccache ccache,
743                         krb5_creds *in_creds,
744                         krb5_principal impersonate_principal,
745                         Ticket *second_ticket,                  
746                         krb5_creds **out_creds,
747                         krb5_creds ***ret_tgts)
748 {
749     krb5_error_code ret;
750     krb5_creds *tgt, tmp_creds;
751     krb5_const_realm client_realm, server_realm, try_realm;
752
753     *out_creds = NULL;
754
755     client_realm = krb5_principal_get_realm(context, in_creds->client);
756     server_realm = krb5_principal_get_realm(context, in_creds->server);
757     memset(&tmp_creds, 0, sizeof(tmp_creds));
758     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
759     if(ret)
760         return ret;
761
762     try_realm = krb5_config_get_string(context, NULL, "capaths", 
763                                        client_realm, server_realm, NULL);
764     
765 #if 1
766     /* XXX remove in future release */
767     if(try_realm == NULL)
768         try_realm = krb5_config_get_string(context, NULL, "libdefaults", 
769                                            "capath", server_realm, NULL);
770 #endif
771
772     if (try_realm == NULL)
773         try_realm = client_realm;
774
775     ret = krb5_make_principal(context,
776                               &tmp_creds.server,
777                               try_realm,
778                               KRB5_TGS_NAME,
779                               server_realm, 
780                               NULL);
781     if(ret){
782         krb5_free_principal(context, tmp_creds.client);
783         return ret;
784     }
785     {
786         krb5_creds tgts;
787         /* XXX try krb5_cc_retrieve_cred first? */
788         ret = find_cred(context, ccache, tmp_creds.server, 
789                         *ret_tgts, &tgts);
790         if(ret == 0){
791             *out_creds = calloc(1, sizeof(**out_creds));
792             if(*out_creds == NULL) {
793                 krb5_set_error_string(context, "malloc: out of memory");
794                 ret = ENOMEM;
795             } else {
796                 krb5_boolean noaddr;
797
798                 krb5_appdefault_boolean(context, NULL, tgts.server->realm,
799                                         "no-addresses", FALSE, &noaddr);
800
801                 if (noaddr)
802                     ret = get_cred_kdc(context, ccache, flags, NULL,
803                                        in_creds, &tgts,
804                                        impersonate_principal, 
805                                        second_ticket,
806                                        *out_creds);
807                 else
808                     ret = get_cred_kdc_la(context, ccache, flags, 
809                                           in_creds, &tgts, 
810                                           impersonate_principal, 
811                                           second_ticket,
812                                           *out_creds);
813                 if (ret) {
814                     free (*out_creds);
815                     *out_creds = NULL;
816                 }
817             }
818             krb5_free_cred_contents(context, &tgts);
819             krb5_free_principal(context, tmp_creds.server);
820             krb5_free_principal(context, tmp_creds.client);
821             return ret;
822         }
823     }
824     if(krb5_realm_compare(context, in_creds->client, in_creds->server)) {
825         not_found(context, in_creds->server);
826         return KRB5_CC_NOTFOUND;
827     }
828     /* XXX this can loop forever */
829     while(1){
830         heim_general_string tgt_inst;
831
832         ret = get_cred_from_kdc_flags(context, flags, ccache, &tmp_creds, 
833                                       NULL, NULL, &tgt, ret_tgts);
834         if(ret) {
835             krb5_free_principal(context, tmp_creds.server);
836             krb5_free_principal(context, tmp_creds.client);
837             return ret;
838         }
839         ret = add_cred(context, ret_tgts, tgt);
840         if(ret) {
841             krb5_free_principal(context, tmp_creds.server);
842             krb5_free_principal(context, tmp_creds.client);
843             return ret;
844         }
845         tgt_inst = tgt->server->name.name_string.val[1];
846         if(strcmp(tgt_inst, server_realm) == 0)
847             break;
848         krb5_free_principal(context, tmp_creds.server);
849         ret = krb5_make_principal(context, &tmp_creds.server, 
850                                   tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
851         if(ret) {
852             krb5_free_principal(context, tmp_creds.server);
853             krb5_free_principal(context, tmp_creds.client);
854             return ret;
855         }
856         ret = krb5_free_creds(context, tgt);
857         if(ret) {
858             krb5_free_principal(context, tmp_creds.server);
859             krb5_free_principal(context, tmp_creds.client);
860             return ret;
861         }
862     }
863         
864     krb5_free_principal(context, tmp_creds.server);
865     krb5_free_principal(context, tmp_creds.client);
866     *out_creds = calloc(1, sizeof(**out_creds));
867     if(*out_creds == NULL) {
868         krb5_set_error_string(context, "malloc: out of memory");
869         ret = ENOMEM;
870     } else {
871         krb5_boolean noaddr;
872
873         krb5_appdefault_boolean(context, NULL, tgt->server->realm,
874                                 "no-addresses", KRB5_ADDRESSLESS_DEFAULT,
875                                 &noaddr);
876         if (noaddr)
877             ret = get_cred_kdc (context, ccache, flags, NULL,
878                                 in_creds, tgt, NULL, NULL,
879                                 *out_creds);
880         else
881             ret = get_cred_kdc_la(context, ccache, flags, 
882                                   in_creds, tgt, NULL, NULL,
883                                   *out_creds);
884         if (ret) {
885             free (*out_creds);
886             *out_creds = NULL;
887         }
888     }
889     krb5_free_creds(context, tgt);
890     return ret;
891 }
892
893 krb5_error_code KRB5_LIB_FUNCTION
894 krb5_get_cred_from_kdc_opt(krb5_context context,
895                            krb5_ccache ccache,
896                            krb5_creds *in_creds,
897                            krb5_creds **out_creds,
898                            krb5_creds ***ret_tgts,
899                            krb5_flags flags)
900 {
901     krb5_kdc_flags f;
902     f.i = flags;
903     return get_cred_from_kdc_flags(context, f, ccache, 
904                                    in_creds, NULL, NULL,
905                                    out_creds, ret_tgts);
906 }
907
908 krb5_error_code KRB5_LIB_FUNCTION
909 krb5_get_cred_from_kdc(krb5_context context,
910                        krb5_ccache ccache,
911                        krb5_creds *in_creds,
912                        krb5_creds **out_creds,
913                        krb5_creds ***ret_tgts)
914 {
915     return krb5_get_cred_from_kdc_opt(context, ccache, 
916                                       in_creds, out_creds, ret_tgts, 0);
917 }
918      
919
920 krb5_error_code KRB5_LIB_FUNCTION
921 krb5_get_credentials_with_flags(krb5_context context,
922                                 krb5_flags options,
923                                 krb5_kdc_flags flags,
924                                 krb5_ccache ccache,
925                                 krb5_creds *in_creds,
926                                 krb5_creds **out_creds)
927 {
928     krb5_error_code ret;
929     krb5_creds **tgts;
930     krb5_creds *res_creds;
931     int i;
932     
933     *out_creds = NULL;
934     res_creds = calloc(1, sizeof(*res_creds));
935     if (res_creds == NULL) {
936         krb5_set_error_string(context, "malloc: out of memory");
937         return ENOMEM;
938     }
939
940     if (in_creds->session.keytype)
941         options |= KRB5_TC_MATCH_KEYTYPE;
942
943     /* 
944      * If we got a credential, check if credential is expired before
945      * returning it.
946      */
947     ret = krb5_cc_retrieve_cred(context,
948                                 ccache,
949                                 in_creds->session.keytype ?
950                                 KRB5_TC_MATCH_KEYTYPE : 0,
951                                 in_creds, res_creds);
952     /* 
953      * If we got a credential, check if credential is expired before
954      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
955      */
956     if (ret == 0) {
957         krb5_timestamp timeret;
958
959         /* If expired ok, don't bother checking */
960         if(options & KRB5_GC_EXPIRED_OK) {
961             *out_creds = res_creds;
962             return 0;
963         }
964             
965         krb5_timeofday(context, &timeret);
966         if(res_creds->times.endtime > timeret) {
967             *out_creds = res_creds;
968             return 0;
969         }
970         if(options & KRB5_GC_CACHED)
971             krb5_cc_remove_cred(context, ccache, 0, res_creds);
972
973     } else if(ret != KRB5_CC_END) {
974         free(res_creds);
975         return ret;
976     }
977     free(res_creds);
978     if(options & KRB5_GC_CACHED) {
979         not_found(context, in_creds->server);
980         return KRB5_CC_NOTFOUND;
981     }
982     if(options & KRB5_GC_USER_USER)
983         flags.b.enc_tkt_in_skey = 1;
984     if (flags.b.enc_tkt_in_skey)
985         options |= KRB5_GC_NO_STORE;
986
987     tgts = NULL;
988     ret = get_cred_from_kdc_flags(context, flags, ccache, 
989                                   in_creds, NULL, NULL, out_creds, &tgts);
990     for(i = 0; tgts && tgts[i]; i++) {
991         krb5_cc_store_cred(context, ccache, tgts[i]);
992         krb5_free_creds(context, tgts[i]);
993     }
994     free(tgts);
995     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
996         krb5_cc_store_cred(context, ccache, *out_creds);
997     return ret;
998 }
999
1000 krb5_error_code KRB5_LIB_FUNCTION
1001 krb5_get_credentials(krb5_context context,
1002                      krb5_flags options,
1003                      krb5_ccache ccache,
1004                      krb5_creds *in_creds,
1005                      krb5_creds **out_creds)
1006 {
1007     krb5_kdc_flags flags;
1008     flags.i = 0;
1009     return krb5_get_credentials_with_flags(context, options, flags,
1010                                            ccache, in_creds, out_creds);
1011 }
1012
1013 struct krb5_get_creds_opt_data {
1014     krb5_principal self;
1015     krb5_flags options;
1016     krb5_enctype enctype;
1017     Ticket *ticket;
1018 };
1019
1020
1021 krb5_error_code KRB5_LIB_FUNCTION
1022 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1023 {
1024     *opt = calloc(1, sizeof(**opt));
1025     if (*opt == NULL) {
1026         krb5_set_error_string(context, "malloc: out of memory");
1027         return ENOMEM;
1028     }
1029     return 0;
1030 }
1031
1032 void KRB5_LIB_FUNCTION
1033 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1034 {
1035     if (opt->self)
1036         krb5_free_principal(context, opt->self);
1037     memset(opt, 0, sizeof(*opt));
1038     free(opt);
1039 }
1040
1041 void KRB5_LIB_FUNCTION
1042 krb5_get_creds_opt_set_options(krb5_context context,
1043                                krb5_get_creds_opt opt,
1044                                krb5_flags options)
1045 {
1046     opt->options = options;
1047 }
1048
1049 void KRB5_LIB_FUNCTION
1050 krb5_get_creds_opt_add_options(krb5_context context,
1051                                krb5_get_creds_opt opt,
1052                                krb5_flags options)
1053 {
1054     opt->options |= options;
1055 }
1056
1057 void KRB5_LIB_FUNCTION
1058 krb5_get_creds_opt_set_enctype(krb5_context context,
1059                                krb5_get_creds_opt opt,
1060                                krb5_enctype enctype)
1061 {
1062     opt->enctype = enctype;
1063 }
1064
1065 krb5_error_code KRB5_LIB_FUNCTION
1066 krb5_get_creds_opt_set_impersonate(krb5_context context,
1067                                    krb5_get_creds_opt opt,
1068                                    krb5_const_principal self)
1069 {
1070     if (opt->self)
1071         krb5_free_principal(context, opt->self);
1072     return krb5_copy_principal(context, self, &opt->self);
1073 }
1074
1075 krb5_error_code KRB5_LIB_FUNCTION
1076 krb5_get_creds_opt_set_ticket(krb5_context context,
1077                               krb5_get_creds_opt opt,
1078                               const Ticket *ticket)
1079 {
1080     if (opt->ticket) {
1081         free_Ticket(opt->ticket);
1082         free(opt->ticket);
1083         opt->ticket = NULL;
1084     }
1085     if (ticket) {
1086         krb5_error_code ret;
1087
1088         opt->ticket = malloc(sizeof(*ticket));
1089         if (opt->ticket == NULL) {
1090             krb5_set_error_string(context, "malloc: out of memory");
1091             return ENOMEM;
1092         }
1093         ret = copy_Ticket(ticket, opt->ticket);
1094         if (ret) {
1095             free(opt->ticket);
1096             opt->ticket = NULL;
1097             krb5_set_error_string(context, "malloc: out of memory");
1098             return ret;
1099         }
1100     }
1101     return 0;
1102 }
1103
1104
1105
1106 krb5_error_code KRB5_LIB_FUNCTION
1107 krb5_get_creds(krb5_context context,
1108                krb5_get_creds_opt opt,
1109                krb5_ccache ccache,
1110                krb5_const_principal inprinc,
1111                krb5_creds **out_creds)
1112 {
1113     krb5_kdc_flags flags;
1114     krb5_flags options;
1115     krb5_creds in_creds;
1116     krb5_error_code ret;
1117     krb5_creds **tgts;
1118     krb5_creds *res_creds;
1119     int i;
1120     
1121     memset(&in_creds, 0, sizeof(in_creds));
1122     in_creds.server = rk_UNCONST(inprinc);
1123
1124     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1125     if (ret)
1126         return ret;
1127
1128     options = opt->options;
1129     flags.i = 0;
1130
1131     *out_creds = NULL;
1132     res_creds = calloc(1, sizeof(*res_creds));
1133     if (res_creds == NULL) {
1134         krb5_free_principal(context, in_creds.client);
1135         krb5_set_error_string(context, "malloc: out of memory");
1136         return ENOMEM;
1137     }
1138
1139     if (opt->enctype) {
1140         in_creds.session.keytype = opt->enctype;
1141         options |= KRB5_TC_MATCH_KEYTYPE;
1142     }
1143
1144     /* 
1145      * If we got a credential, check if credential is expired before
1146      * returning it.
1147      */
1148     ret = krb5_cc_retrieve_cred(context,
1149                                 ccache,
1150                                 opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
1151                                 &in_creds, res_creds);
1152     /* 
1153      * If we got a credential, check if credential is expired before
1154      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1155      */
1156     if (ret == 0) {
1157         krb5_timestamp timeret;
1158
1159         /* If expired ok, don't bother checking */
1160         if(options & KRB5_GC_EXPIRED_OK) {
1161             *out_creds = res_creds;
1162             krb5_free_principal(context, in_creds.client);
1163             return 0;
1164         }
1165             
1166         krb5_timeofday(context, &timeret);
1167         if(res_creds->times.endtime > timeret) {
1168             *out_creds = res_creds;
1169             krb5_free_principal(context, in_creds.client);
1170             return 0;
1171         }
1172         if(options & KRB5_GC_CACHED)
1173             krb5_cc_remove_cred(context, ccache, 0, res_creds);
1174
1175     } else if(ret != KRB5_CC_END) {
1176         free(res_creds);
1177         krb5_free_principal(context, in_creds.client);
1178         return ret;
1179     }
1180     free(res_creds);
1181     if(options & KRB5_GC_CACHED) {
1182         not_found(context, in_creds.server);
1183         krb5_free_principal(context, in_creds.client);
1184         return KRB5_CC_NOTFOUND;
1185     }
1186     if(options & KRB5_GC_USER_USER) {
1187         flags.b.enc_tkt_in_skey = 1;
1188         options |= KRB5_GC_NO_STORE;
1189     }
1190     if (options & KRB5_GC_FORWARDABLE)
1191         flags.b.forwardable = 1;
1192     if (options & KRB5_GC_NO_TRANSIT_CHECK)
1193         flags.b.disable_transited_check = 1;
1194     if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1195         flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1196         flags.b.constrained_delegation = 1;
1197     }
1198
1199     tgts = NULL;
1200     ret = get_cred_from_kdc_flags(context, flags, ccache, 
1201                                   &in_creds, opt->self, opt->ticket,
1202                                   out_creds, &tgts);
1203     krb5_free_principal(context, in_creds.client);
1204     for(i = 0; tgts && tgts[i]; i++) {
1205         krb5_cc_store_cred(context, ccache, tgts[i]);
1206         krb5_free_creds(context, tgts[i]);
1207     }
1208     free(tgts);
1209     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1210         krb5_cc_store_cred(context, ccache, *out_creds);
1211     return ret;
1212 }
1213
1214 /*
1215  *
1216  */
1217
1218 krb5_error_code KRB5_LIB_FUNCTION
1219 krb5_get_renewed_creds(krb5_context context,
1220                        krb5_creds *creds,
1221                        krb5_const_principal client,
1222                        krb5_ccache ccache,
1223                        const char *in_tkt_service)
1224 {
1225     krb5_error_code ret;
1226     krb5_kdc_flags flags;
1227     krb5_creds in, *template;
1228
1229     memset(&in, 0, sizeof(in));
1230
1231     ret = krb5_copy_principal(context, client, &in.client);
1232     if (ret)
1233         return ret;
1234
1235     if (in_tkt_service) {
1236         ret = krb5_parse_name(context, in_tkt_service, &in.server);
1237         if (ret) {
1238             krb5_free_principal(context, in.client);
1239             return ret;
1240         }
1241     } else {
1242         const char *realm = krb5_principal_get_realm(context, client);
1243         
1244         ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1245                                   realm, NULL);
1246         if (ret) {
1247             krb5_free_principal(context, in.client);
1248             return ret;
1249         }
1250     }
1251
1252     flags.i = 0;
1253     flags.b.renewable = flags.b.renew = 1;
1254
1255     /*
1256      * Get template from old credential cache for the same entry, if
1257      * this failes, no worries.
1258      */
1259     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1260     if (ret == 0) {
1261         flags.b.forwardable = template->flags.b.forwardable;
1262         flags.b.proxiable = template->flags.b.proxiable;
1263         krb5_free_creds (context, template);
1264     }
1265
1266     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &creds);
1267     krb5_free_principal(context, in.client);
1268     krb5_free_principal(context, in.server);
1269
1270     return ret;
1271 }