s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e89...
[sfrench/samba-autobuild/.git] / source4 / heimdal / lib / krb5 / get_cred.c
1 /*
2  * Copyright (c) 1997 - 2008 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 /*
37  * Take the `body' and encode it into `padata' using the credentials
38  * in `creds'.
39  */
40
41 static krb5_error_code
42 make_pa_tgs_req(krb5_context context,
43                 krb5_auth_context ac,
44                 KDC_REQ_BODY *body,
45                 PA_DATA *padata,
46                 krb5_creds *creds)
47 {
48     u_char *buf;
49     size_t buf_size;
50     size_t len;
51     krb5_data in_data;
52     krb5_error_code ret;
53
54     ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
55     if (ret)
56         goto out;
57     if(buf_size != len)
58         krb5_abortx(context, "internal error in ASN.1 encoder");
59
60     in_data.length = len;
61     in_data.data   = buf;
62     ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
63                                 &padata->padata_value,
64                                 KRB5_KU_TGS_REQ_AUTH_CKSUM,
65                                 KRB5_KU_TGS_REQ_AUTH);
66  out:
67     free (buf);
68     if(ret)
69         return ret;
70     padata->padata_type = KRB5_PADATA_TGS_REQ;
71     return 0;
72 }
73
74 /*
75  * Set the `enc-authorization-data' in `req_body' based on `authdata'
76  */
77
78 static krb5_error_code
79 set_auth_data (krb5_context context,
80                KDC_REQ_BODY *req_body,
81                krb5_authdata *authdata,
82                krb5_keyblock *key)
83 {
84     if(authdata->len) {
85         size_t len, buf_size;
86         unsigned char *buf;
87         krb5_crypto crypto;
88         krb5_error_code ret;
89
90         ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
91                            &len, ret);
92         if (ret)
93             return ret;
94         if (buf_size != len)
95             krb5_abortx(context, "internal error in ASN.1 encoder");
96
97         ALLOC(req_body->enc_authorization_data, 1);
98         if (req_body->enc_authorization_data == NULL) {
99             free (buf);
100             krb5_set_error_message(context, ENOMEM,
101                                    N_("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             req_body->enc_authorization_data = NULL;
109             return ret;
110         }
111         krb5_encrypt_EncryptedData(context,
112                                    crypto,
113                                    KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
114                                    /* KRB5_KU_TGS_REQ_AUTH_DAT_SESSION? */
115                                    buf,
116                                    len,
117                                    0,
118                                    req_body->enc_authorization_data);
119         free (buf);
120         krb5_crypto_destroy(context, crypto);
121     } else {
122         req_body->enc_authorization_data = NULL;
123     }
124     return 0;
125 }
126
127 /*
128  * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
129  * (if not-NULL), `in_creds', `krbtgt', and returning the generated
130  * subkey in `subkey'.
131  */
132
133 static krb5_error_code
134 init_tgs_req (krb5_context context,
135               krb5_ccache ccache,
136               krb5_addresses *addresses,
137               krb5_kdc_flags flags,
138               Ticket *second_ticket,
139               krb5_creds *in_creds,
140               krb5_creds *krbtgt,
141               unsigned nonce,
142               const METHOD_DATA *padata,
143               krb5_keyblock **subkey,
144               TGS_REQ *t)
145 {
146     krb5_error_code ret = 0;
147
148     memset(t, 0, sizeof(*t));
149     t->pvno = 5;
150     t->msg_type = krb_tgs_req;
151     if (in_creds->session.keytype) {
152         ALLOC_SEQ(&t->req_body.etype, 1);
153         if(t->req_body.etype.val == NULL) {
154             ret = ENOMEM;
155             krb5_set_error_message(context, ret,
156                                    N_("malloc: out of memory", ""));
157             goto fail;
158         }
159         t->req_body.etype.val[0] = in_creds->session.keytype;
160     } else {
161         ret = krb5_init_etype(context,
162                               &t->req_body.etype.len,
163                               &t->req_body.etype.val,
164                               NULL);
165     }
166     if (ret)
167         goto fail;
168     t->req_body.addresses = addresses;
169     t->req_body.kdc_options = flags.b;
170     ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
171     if (ret)
172         goto fail;
173     ALLOC(t->req_body.sname, 1);
174     if (t->req_body.sname == NULL) {
175         ret = ENOMEM;
176         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
177         goto fail;
178     }
179
180     /* some versions of some code might require that the client be
181        present in TGS-REQs, but this is clearly against the spec */
182
183     ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
184     if (ret)
185         goto fail;
186
187     /* req_body.till should be NULL if there is no endtime specified,
188        but old MIT code (like DCE secd) doesn't like that */
189     ALLOC(t->req_body.till, 1);
190     if(t->req_body.till == NULL){
191         ret = ENOMEM;
192         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
193         goto fail;
194     }
195     *t->req_body.till = in_creds->times.endtime;
196
197     t->req_body.nonce = nonce;
198     if(second_ticket){
199         ALLOC(t->req_body.additional_tickets, 1);
200         if (t->req_body.additional_tickets == NULL) {
201             ret = ENOMEM;
202             krb5_set_error_message(context, ret,
203                                    N_("malloc: out of memory", ""));
204             goto fail;
205         }
206         ALLOC_SEQ(t->req_body.additional_tickets, 1);
207         if (t->req_body.additional_tickets->val == NULL) {
208             ret = ENOMEM;
209             krb5_set_error_message(context, ret,
210                                    N_("malloc: out of memory", ""));
211             goto fail;
212         }
213         ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
214         if (ret)
215             goto fail;
216     }
217     ALLOC(t->padata, 1);
218     if (t->padata == NULL) {
219         ret = ENOMEM;
220         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
221         goto fail;
222     }
223     ALLOC_SEQ(t->padata, 1 + padata->len);
224     if (t->padata->val == NULL) {
225         ret = ENOMEM;
226         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
227         goto fail;
228     }
229     {
230         int i;
231         for (i = 0; i < padata->len; i++) {
232             ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
233             if (ret) {
234                 krb5_set_error_message(context, ret,
235                                        N_("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         if(ret) {
285             if (key)
286                 krb5_free_keyblock (context, key);
287             krb5_auth_con_free(context, ac);
288             goto fail;
289         }
290         *subkey = key;
291         
292         krb5_auth_con_free(context, ac);
293     }
294 fail:
295     if (ret) {
296         t->req_body.addresses = NULL;
297         free_TGS_REQ (t);
298     }
299     return ret;
300 }
301
302 krb5_error_code
303 _krb5_get_krbtgt(krb5_context context,
304                  krb5_ccache  id,
305                  krb5_realm realm,
306                  krb5_creds **cred)
307 {
308     krb5_error_code ret;
309     krb5_creds tmp_cred;
310
311     memset(&tmp_cred, 0, sizeof(tmp_cred));
312
313     ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
314     if (ret)
315         return ret;
316
317     ret = krb5_make_principal(context,
318                               &tmp_cred.server,
319                               realm,
320                               KRB5_TGS_NAME,
321                               realm,
322                               NULL);
323     if(ret) {
324         krb5_free_principal(context, tmp_cred.client);
325         return ret;
326     }
327     ret = krb5_get_credentials(context,
328                                KRB5_GC_CACHED,
329                                id,
330                                &tmp_cred,
331                                cred);
332     krb5_free_principal(context, tmp_cred.client);
333     krb5_free_principal(context, tmp_cred.server);
334     if(ret)
335         return ret;
336     return 0;
337 }
338
339 /* DCE compatible decrypt proc */
340 static krb5_error_code
341 decrypt_tkt_with_subkey (krb5_context context,
342                          krb5_keyblock *key,
343                          krb5_key_usage usage,
344                          krb5_const_pointer subkey,
345                          krb5_kdc_rep *dec_rep)
346 {
347     krb5_error_code ret;
348     krb5_data data;
349     size_t size;
350     krb5_crypto crypto;
351
352     ret = krb5_crypto_init(context, key, 0, &crypto);
353     if (ret)
354         return ret;
355     ret = krb5_decrypt_EncryptedData (context,
356                                       crypto,
357                                       usage,
358                                       &dec_rep->kdc_rep.enc_part,
359                                       &data);
360     krb5_crypto_destroy(context, crypto);
361     if(ret && subkey){
362         /* DCE compat -- try to decrypt with subkey */
363         ret = krb5_crypto_init(context, subkey, 0, &crypto);
364         if (ret)
365             return ret;
366         ret = krb5_decrypt_EncryptedData (context,
367                                           crypto,
368                                           KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
369                                           &dec_rep->kdc_rep.enc_part,
370                                           &data);
371         krb5_crypto_destroy(context, crypto);
372     }
373     if (ret)
374         return ret;
375
376     ret = decode_EncASRepPart(data.data,
377                               data.length,
378                               &dec_rep->enc_part,
379                               &size);
380     if (ret)
381         ret = decode_EncTGSRepPart(data.data,
382                                    data.length,
383                                    &dec_rep->enc_part,
384                                    &size);
385     if (ret)
386       krb5_set_error_message(context, ret, 
387                              N_("Failed to decode encpart in ticket", ""));
388     krb5_data_free (&data);
389     return ret;
390 }
391
392 static krb5_error_code
393 get_cred_kdc(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 {
403     TGS_REQ req;
404     krb5_data enc;
405     krb5_data resp;
406     krb5_kdc_rep rep;
407     KRB_ERROR error;
408     krb5_error_code ret;
409     unsigned nonce;
410     krb5_keyblock *subkey = NULL;
411     size_t len;
412     Ticket second_ticket_data;
413     METHOD_DATA padata;
414
415     krb5_data_zero(&resp);
416     krb5_data_zero(&enc);
417     padata.val = NULL;
418     padata.len = 0;
419
420     krb5_generate_random_block(&nonce, sizeof(nonce));
421     nonce &= 0xffffffff;
422
423     if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
424         ret = decode_Ticket(in_creds->second_ticket.data,
425                             in_creds->second_ticket.length,
426                             &second_ticket_data, &len);
427         if(ret)
428             return ret;
429         second_ticket = &second_ticket_data;
430     }
431
432
433     if (impersonate_principal) {
434         krb5_crypto crypto;
435         PA_S4U2Self self;
436         krb5_data data;
437         void *buf;
438         size_t size;
439
440         self.name = impersonate_principal->name;
441         self.realm = impersonate_principal->realm;
442         self.auth = estrdup("Kerberos");
443         
444         ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
445         if (ret) {
446             free(self.auth);
447             goto out;
448         }
449
450         ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
451         if (ret) {
452             free(self.auth);
453             krb5_data_free(&data);
454             goto out;
455         }
456
457         ret = krb5_create_checksum(context,
458                                    crypto,
459                                    KRB5_KU_OTHER_CKSUM,
460                                    0,
461                                    data.data,
462                                    data.length,
463                                    &self.cksum);
464         krb5_crypto_destroy(context, crypto);
465         krb5_data_free(&data);
466         if (ret) {
467             free(self.auth);
468             goto out;
469         }
470
471         ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
472         free(self.auth);
473         free_Checksum(&self.cksum);
474         if (ret)
475             goto out;
476         if (len != size)
477             krb5_abortx(context, "internal asn1 error");
478         
479         ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
480         if (ret)
481             goto out;
482     }
483
484     ret = init_tgs_req (context,
485                         id,
486                         addresses,
487                         flags,
488                         second_ticket,
489                         in_creds,
490                         krbtgt,
491                         nonce,
492                         &padata,
493                         &subkey,
494                         &req);
495     if (ret)
496         goto out;
497
498     ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
499     if (ret)
500         goto out;
501     if(enc.length != len)
502         krb5_abortx(context, "internal error in ASN.1 encoder");
503
504     /* don't free addresses */
505     req.req_body.addresses = NULL;
506     free_TGS_REQ(&req);
507
508     /*
509      * Send and receive
510      */
511     {
512         krb5_sendto_ctx stctx;
513         ret = krb5_sendto_ctx_alloc(context, &stctx);
514         if (ret)
515             return ret;
516         krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
517
518         ret = krb5_sendto_context (context, stctx, &enc,
519                                    krbtgt->server->name.name_string.val[1],
520                                    &resp);
521         krb5_sendto_ctx_free(context, stctx);
522     }
523     if(ret)
524         goto out;
525
526     memset(&rep, 0, sizeof(rep));
527     if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
528         unsigned eflags = 0;
529
530         ret = krb5_copy_principal(context,
531                                   in_creds->client,
532                                   &out_creds->client);
533         if(ret)
534             goto out2;
535         ret = krb5_copy_principal(context,
536                                   in_creds->server,
537                                   &out_creds->server);
538         if(ret)
539             goto out2;
540         /* this should go someplace else */
541         out_creds->times.endtime = in_creds->times.endtime;
542
543         /* XXX should do better testing */
544         if (flags.b.constrained_delegation || impersonate_principal)
545             eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
546
547         ret = _krb5_extract_ticket(context,
548                                    &rep,
549                                    out_creds,
550                                    &krbtgt->session,
551                                    NULL,
552                                    KRB5_KU_TGS_REP_ENC_PART_SESSION,
553                                    &krbtgt->addresses,
554                                    nonce,
555                                    eflags,
556                                    decrypt_tkt_with_subkey,
557                                    subkey);
558     out2:
559         krb5_free_kdc_rep(context, &rep);
560     } else if(krb5_rd_error(context, &resp, &error) == 0) {
561         ret = krb5_error_from_rd_error(context, &error, in_creds);
562         krb5_free_error_contents(context, &error);
563     } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
564         ret = KRB5KRB_AP_ERR_V4_REPLY;
565         krb5_clear_error_message(context);
566     } else {
567         ret = KRB5KRB_AP_ERR_MSG_TYPE;
568         krb5_clear_error_message(context);
569     }
570
571 out:
572     if (second_ticket == &second_ticket_data)
573         free_Ticket(&second_ticket_data);
574     free_METHOD_DATA(&padata);
575     krb5_data_free(&resp);
576     krb5_data_free(&enc);
577     if(subkey){
578         krb5_free_keyblock_contents(context, subkey);
579         free(subkey);
580     }
581     return ret;
582
583 }
584
585 /*
586  * same as above, just get local addresses first if the krbtgt have
587  * them and the realm is not addressless
588  */
589
590 static krb5_error_code
591 get_cred_kdc_address(krb5_context context,
592                      krb5_ccache id,
593                      krb5_kdc_flags flags,
594                      krb5_addresses *addrs,
595                      krb5_creds *in_creds,
596                      krb5_creds *krbtgt,
597                      krb5_principal impersonate_principal,
598                      Ticket *second_ticket,
599                      krb5_creds *out_creds)
600 {
601     krb5_error_code ret;
602     krb5_addresses addresses = { 0, NULL };
603
604     /*
605      * Inherit the address-ness of the krbtgt if the address is not
606      * specified.
607      */
608
609     if (addrs == NULL && krbtgt->addresses.len != 0) {
610         krb5_boolean noaddr;
611
612         krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
613                                 "no-addresses", FALSE, &noaddr);
614         
615         if (!noaddr) {
616             krb5_get_all_client_addrs(context, &addresses);
617             /* XXX this sucks. */
618             addrs = &addresses;
619             if(addresses.len == 0)
620                 addrs = NULL;
621         }
622     }
623     ret = get_cred_kdc(context, id, flags, addrs, in_creds,
624                        krbtgt, impersonate_principal,
625                        second_ticket, out_creds);
626     krb5_free_addresses(context, &addresses);
627     return ret;
628 }
629
630 krb5_error_code KRB5_LIB_FUNCTION
631 krb5_get_kdc_cred(krb5_context context,
632                   krb5_ccache id,
633                   krb5_kdc_flags flags,
634                   krb5_addresses *addresses,
635                   Ticket  *second_ticket,
636                   krb5_creds *in_creds,
637                   krb5_creds **out_creds
638                   )
639 {
640     krb5_error_code ret;
641     krb5_creds *krbtgt;
642
643     *out_creds = calloc(1, sizeof(**out_creds));
644     if(*out_creds == NULL) {
645         krb5_set_error_message(context, ENOMEM,
646                                N_("malloc: out of memory", ""));
647         return ENOMEM;
648     }
649     ret = _krb5_get_krbtgt (context,
650                             id,
651                             in_creds->server->realm,
652                             &krbtgt);
653     if(ret) {
654         free(*out_creds);
655         return ret;
656     }
657     ret = get_cred_kdc(context, id, flags, addresses,
658                        in_creds, krbtgt, NULL, NULL, *out_creds);
659     krb5_free_creds (context, krbtgt);
660     if(ret)
661         free(*out_creds);
662     return ret;
663 }
664
665 static int
666 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
667 {
668     krb5_error_code ret;
669     char *str;
670
671     ret = krb5_unparse_name(context, p, &str);
672     if(ret) {
673         krb5_clear_error_message(context);
674         return code;
675     }
676     krb5_set_error_message(context, code,
677                            N_("Matching credential (%s) not found", ""), str);
678     free(str);
679     return code;
680 }
681
682 static krb5_error_code
683 find_cred(krb5_context context,
684           krb5_ccache id,
685           krb5_principal server,
686           krb5_creds **tgts,
687           krb5_creds *out_creds)
688 {
689     krb5_error_code ret;
690     krb5_creds mcreds;
691
692     krb5_cc_clear_mcred(&mcreds);
693     mcreds.server = server;
694     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
695                                 &mcreds, out_creds);
696     if(ret == 0)
697         return 0;
698     while(tgts && *tgts){
699         if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
700                               &mcreds, *tgts)){
701             ret = krb5_copy_creds_contents(context, *tgts, out_creds);
702             return ret;
703         }
704         tgts++;
705     }
706     return not_found(context, server, KRB5_CC_NOTFOUND);
707 }
708
709 static krb5_error_code
710 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
711 {
712     int i;
713     krb5_error_code ret;
714     krb5_creds **tmp = *tgts;
715
716     for(i = 0; tmp && tmp[i]; i++); /* XXX */
717     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
718     if(tmp == NULL) {
719         krb5_set_error_message(context, ENOMEM,
720                                N_("malloc: out of memory", ""));
721         return ENOMEM;
722     }
723     *tgts = tmp;
724     ret = krb5_copy_creds(context, tkt, &tmp[i]);
725     tmp[i+1] = NULL;
726     return ret;
727 }
728
729 /*
730 get_cred(server)
731         creds = cc_get_cred(server)
732         if(creds) return creds
733         tgt = cc_get_cred(krbtgt/server_realm@any_realm)
734         if(tgt)
735                 return get_cred_tgt(server, tgt)
736         if(client_realm == server_realm)
737                 return NULL
738         tgt = get_cred(krbtgt/server_realm@client_realm)
739         while(tgt_inst != server_realm)
740                 tgt = get_cred(krbtgt/server_realm@tgt_inst)
741         return get_cred_tgt(server, tgt)
742         */
743
744 static krb5_error_code
745 get_cred_kdc_capath(krb5_context context,
746                     krb5_kdc_flags flags,
747                     krb5_ccache ccache,
748                     krb5_creds *in_creds,
749                     krb5_principal impersonate_principal,
750                     Ticket *second_ticket,                      
751                     krb5_creds **out_creds,
752                     krb5_creds ***ret_tgts)
753 {
754     krb5_error_code ret;
755     krb5_creds *tgt, tmp_creds;
756     krb5_const_realm client_realm, server_realm, try_realm;
757     int ok_as_delegate = 1;
758
759     *out_creds = NULL;
760
761     client_realm = krb5_principal_get_realm(context, in_creds->client);
762     server_realm = krb5_principal_get_realm(context, in_creds->server);
763     memset(&tmp_creds, 0, sizeof(tmp_creds));
764     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
765     if(ret)
766         return ret;
767
768     try_realm = krb5_config_get_string(context, NULL, "capaths",
769                                        client_realm, server_realm, NULL);
770     if (try_realm == NULL)
771         try_realm = client_realm;
772
773     ret = krb5_make_principal(context,
774                               &tmp_creds.server,
775                               try_realm,
776                               KRB5_TGS_NAME,
777                               server_realm,
778                               NULL);
779     if(ret){
780         krb5_free_principal(context, tmp_creds.client);
781         return ret;
782     }
783     {
784         krb5_creds tgts;
785
786         ret = find_cred(context, ccache, tmp_creds.server,
787                         *ret_tgts, &tgts);
788         if(ret == 0){
789             if (try_realm != client_realm)
790                 ok_as_delegate = tgts.flags.b.ok_as_delegate;
791
792             *out_creds = calloc(1, sizeof(**out_creds));
793             if(*out_creds == NULL) {
794                 ret = ENOMEM;
795                 krb5_set_error_message(context, ret,
796                                        N_("malloc: out of memory", ""));
797             } else {
798                 ret = get_cred_kdc_address(context, ccache, flags, NULL,
799                                            in_creds, &tgts,
800                                            impersonate_principal,
801                                            second_ticket,
802                                            *out_creds);
803                 if (ret) {
804                     free (*out_creds);
805                     *out_creds = NULL;
806                 } else if (ok_as_delegate == 0)
807                     (*out_creds)->flags.b.ok_as_delegate = 0;
808             }
809             krb5_free_cred_contents(context, &tgts);
810             krb5_free_principal(context, tmp_creds.server);
811             krb5_free_principal(context, tmp_creds.client);
812             return ret;
813         }
814     }
815     if(krb5_realm_compare(context, in_creds->client, in_creds->server))
816         return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
817
818     /* XXX this can loop forever */
819     while(1){
820         heim_general_string tgt_inst;
821
822         ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
823                                   NULL, NULL, &tgt, ret_tgts);
824         if(ret) {
825             krb5_free_principal(context, tmp_creds.server);
826             krb5_free_principal(context, tmp_creds.client);
827             return ret;
828         }
829         /* 
830          * if either of the chain or the ok_as_delegate was stripped
831          * by the kdc, make sure we strip it too.
832          */
833         if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
834             ok_as_delegate = 0;
835             tgt->flags.b.ok_as_delegate = 0;
836         }
837
838         ret = add_cred(context, tgt, ret_tgts);
839         if(ret) {
840             krb5_free_principal(context, tmp_creds.server);
841             krb5_free_principal(context, tmp_creds.client);
842             return ret;
843         }
844         tgt_inst = tgt->server->name.name_string.val[1];
845         if(strcmp(tgt_inst, server_realm) == 0)
846             break;
847         krb5_free_principal(context, tmp_creds.server);
848         ret = krb5_make_principal(context, &tmp_creds.server,
849                                   tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
850         if(ret) {
851             krb5_free_principal(context, tmp_creds.server);
852             krb5_free_principal(context, tmp_creds.client);
853             return ret;
854         }
855         ret = krb5_free_creds(context, tgt);
856         if(ret) {
857             krb5_free_principal(context, tmp_creds.server);
858             krb5_free_principal(context, tmp_creds.client);
859             return ret;
860         }
861     }
862         
863     krb5_free_principal(context, tmp_creds.server);
864     krb5_free_principal(context, tmp_creds.client);
865     *out_creds = calloc(1, sizeof(**out_creds));
866     if(*out_creds == NULL) {
867         ret = ENOMEM;
868         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
869     } else {
870         ret = get_cred_kdc_address (context, ccache, flags, NULL,
871                                     in_creds, tgt, impersonate_principal,
872                                     second_ticket, *out_creds);
873         if (ret) {
874             free (*out_creds);
875             *out_creds = NULL;
876         }
877     }
878     krb5_free_creds(context, tgt);
879     return ret;
880 }
881
882 static krb5_error_code
883 get_cred_kdc_referral(krb5_context context,
884                       krb5_kdc_flags flags,
885                       krb5_ccache ccache,
886                       krb5_creds *in_creds,
887                       krb5_principal impersonate_principal,
888                       Ticket *second_ticket,                    
889                       krb5_creds **out_creds,
890                       krb5_creds ***ret_tgts)
891 {
892     krb5_const_realm client_realm;
893     krb5_error_code ret;
894     krb5_creds tgt, referral, ticket;
895     int loop = 0;
896     int ok_as_delegate = 1;
897
898     memset(&tgt, 0, sizeof(tgt));
899     memset(&ticket, 0, sizeof(ticket));
900
901     flags.b.canonicalize = 1;
902
903     *out_creds = NULL;
904
905     client_realm = krb5_principal_get_realm(context, in_creds->client);
906
907     /* find tgt for the clients base realm */
908     {
909         krb5_principal tgtname;
910         
911         ret = krb5_make_principal(context, &tgtname,
912                                   client_realm,
913                                   KRB5_TGS_NAME,
914                                   client_realm,
915                                   NULL);
916         if(ret)
917             return ret;
918         
919         ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
920         krb5_free_principal(context, tgtname);
921         if (ret)
922             return ret;
923     }
924
925     referral = *in_creds;
926     ret = krb5_copy_principal(context, in_creds->server, &referral.server);
927     if (ret) {
928         krb5_free_cred_contents(context, &tgt);
929         return ret;
930     }
931     ret = krb5_principal_set_realm(context, referral.server, client_realm);
932     if (ret) {
933         krb5_free_cred_contents(context, &tgt);
934         krb5_free_principal(context, referral.server);
935         return ret;
936     }
937
938     while (loop++ < 17) {
939         krb5_creds **tickets;
940         krb5_creds mcreds;
941         char *referral_realm;
942
943         /* Use cache if we are not doing impersonation or contrainte deleg */
944         if (impersonate_principal == NULL || flags.b.constrained_delegation) {
945             krb5_cc_clear_mcred(&mcreds);
946             mcreds.server = referral.server;
947             ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
948         } else
949             ret = EINVAL;
950
951         if (ret) {
952             ret = get_cred_kdc_address (context, ccache, flags, NULL,
953                                         &referral, &tgt, impersonate_principal,
954                                         second_ticket, &ticket);
955             if (ret)
956                 goto out;
957         }
958
959         /* Did we get the right ticket ? */
960         if (krb5_principal_compare_any_realm(context,
961                                              referral.server,
962                                              ticket.server))
963             break;
964
965         if (ticket.server->name.name_string.len != 2 &&
966             strcmp(ticket.server->name.name_string.val[0], KRB5_TGS_NAME) != 0)
967         {
968             krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
969                                    N_("Got back an non krbtgt "
970                                       "ticket referrals", ""));
971             krb5_free_cred_contents(context, &ticket);
972             return KRB5KRB_AP_ERR_NOT_US;
973         }
974
975         referral_realm = ticket.server->name.name_string.val[1];
976
977         /* check that there are no referrals loops */
978         tickets = *ret_tgts;
979
980         krb5_cc_clear_mcred(&mcreds);
981         mcreds.server = ticket.server;
982
983         while(tickets && *tickets){
984             if(krb5_compare_creds(context,
985                                   KRB5_TC_DONT_MATCH_REALM,
986                                   &mcreds,
987                                   *tickets))
988             {
989                 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
990                                        N_("Referral from %s "
991                                           "loops back to realm %s", ""),
992                                        tgt.server->realm,
993                                        referral_realm);
994                 krb5_free_cred_contents(context, &ticket);
995                 return KRB5_GET_IN_TKT_LOOP;
996             }
997             tickets++;
998         }       
999
1000         /* 
1001          * if either of the chain or the ok_as_delegate was stripped
1002          * by the kdc, make sure we strip it too.
1003          */
1004
1005         if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1006             ok_as_delegate = 0;
1007             ticket.flags.b.ok_as_delegate = 0;
1008         }
1009
1010         ret = add_cred(context, &ticket, ret_tgts);
1011         if (ret) {
1012             krb5_free_cred_contents(context, &ticket);
1013             goto out;
1014         }
1015
1016         /* try realm in the referral */
1017         ret = krb5_principal_set_realm(context,
1018                                        referral.server,
1019                                        referral_realm);
1020         krb5_free_cred_contents(context, &tgt);
1021         tgt = ticket;
1022         memset(&ticket, 0, sizeof(ticket));
1023         if (ret)
1024             goto out;
1025     }
1026
1027     ret = krb5_copy_creds(context, &ticket, out_creds);
1028
1029 out:
1030     krb5_free_principal(context, referral.server);
1031     krb5_free_cred_contents(context, &tgt);
1032     return ret;
1033 }
1034
1035
1036 /*
1037  * Glue function between referrals version and old client chasing
1038  * codebase.
1039  */
1040
1041 static krb5_error_code
1042 get_cred_kdc_any(krb5_context context,
1043                  krb5_kdc_flags flags,
1044                  krb5_ccache ccache,
1045                  krb5_creds *in_creds,
1046                  krb5_principal impersonate_principal,
1047                  Ticket *second_ticket,                 
1048                  krb5_creds **out_creds,
1049                  krb5_creds ***ret_tgts)
1050 {
1051     krb5_error_code ret;
1052
1053     ret = get_cred_kdc_referral(context,
1054                                 flags,
1055                                 ccache,
1056                                 in_creds,
1057                                 impersonate_principal,
1058                                 second_ticket,
1059                                 out_creds,
1060                                 ret_tgts);
1061     if (ret == 0 || flags.b.canonicalize)
1062         return ret;
1063     return get_cred_kdc_capath(context,
1064                                 flags,
1065                                 ccache,
1066                                 in_creds,
1067                                 impersonate_principal,
1068                                 second_ticket,
1069                                 out_creds,
1070                                 ret_tgts);
1071 }
1072
1073
1074 krb5_error_code KRB5_LIB_FUNCTION
1075 krb5_get_cred_from_kdc_opt(krb5_context context,
1076                            krb5_ccache ccache,
1077                            krb5_creds *in_creds,
1078                            krb5_creds **out_creds,
1079                            krb5_creds ***ret_tgts,
1080                            krb5_flags flags)
1081 {
1082     krb5_kdc_flags f;
1083     f.i = flags;
1084     return get_cred_kdc_any(context, f, ccache,
1085                             in_creds, NULL, NULL,
1086                             out_creds, ret_tgts);
1087 }
1088
1089 krb5_error_code KRB5_LIB_FUNCTION
1090 krb5_get_cred_from_kdc(krb5_context context,
1091                        krb5_ccache ccache,
1092                        krb5_creds *in_creds,
1093                        krb5_creds **out_creds,
1094                        krb5_creds ***ret_tgts)
1095 {
1096     return krb5_get_cred_from_kdc_opt(context, ccache,
1097                                       in_creds, out_creds, ret_tgts, 0);
1098 }
1099
1100
1101 krb5_error_code KRB5_LIB_FUNCTION
1102 krb5_get_credentials_with_flags(krb5_context context,
1103                                 krb5_flags options,
1104                                 krb5_kdc_flags flags,
1105                                 krb5_ccache ccache,
1106                                 krb5_creds *in_creds,
1107                                 krb5_creds **out_creds)
1108 {
1109     krb5_error_code ret;
1110     krb5_creds **tgts;
1111     krb5_creds *res_creds;
1112     int i;
1113
1114     *out_creds = NULL;
1115     res_creds = calloc(1, sizeof(*res_creds));
1116     if (res_creds == NULL) {
1117         krb5_set_error_message(context, ENOMEM,
1118                                N_("malloc: out of memory", ""));
1119         return ENOMEM;
1120     }
1121
1122     if (in_creds->session.keytype)
1123         options |= KRB5_TC_MATCH_KEYTYPE;
1124
1125     /*
1126      * If we got a credential, check if credential is expired before
1127      * returning it.
1128      */
1129     ret = krb5_cc_retrieve_cred(context,
1130                                 ccache,
1131                                 in_creds->session.keytype ?
1132                                 KRB5_TC_MATCH_KEYTYPE : 0,
1133                                 in_creds, res_creds);
1134     /*
1135      * If we got a credential, check if credential is expired before
1136      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1137      */
1138     if (ret == 0) {
1139         krb5_timestamp timeret;
1140
1141         /* If expired ok, don't bother checking */
1142         if(options & KRB5_GC_EXPIRED_OK) {
1143             *out_creds = res_creds;
1144             return 0;
1145         }
1146         
1147         krb5_timeofday(context, &timeret);
1148         if(res_creds->times.endtime > timeret) {
1149             *out_creds = res_creds;
1150             return 0;
1151         }
1152         if(options & KRB5_GC_CACHED)
1153             krb5_cc_remove_cred(context, ccache, 0, res_creds);
1154
1155     } else if(ret != KRB5_CC_END) {
1156         free(res_creds);
1157         return ret;
1158     }
1159     free(res_creds);
1160     if(options & KRB5_GC_CACHED)
1161         return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1162
1163     if(options & KRB5_GC_USER_USER)
1164         flags.b.enc_tkt_in_skey = 1;
1165     if (flags.b.enc_tkt_in_skey)
1166         options |= KRB5_GC_NO_STORE;
1167
1168     tgts = NULL;
1169     ret = get_cred_kdc_any(context, flags, ccache,
1170                            in_creds, NULL, NULL, out_creds, &tgts);
1171     for(i = 0; tgts && tgts[i]; i++) {
1172         krb5_cc_store_cred(context, ccache, tgts[i]);
1173         krb5_free_creds(context, tgts[i]);
1174     }
1175     free(tgts);
1176     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1177         krb5_cc_store_cred(context, ccache, *out_creds);
1178     return ret;
1179 }
1180
1181 krb5_error_code KRB5_LIB_FUNCTION
1182 krb5_get_credentials(krb5_context context,
1183                      krb5_flags options,
1184                      krb5_ccache ccache,
1185                      krb5_creds *in_creds,
1186                      krb5_creds **out_creds)
1187 {
1188     krb5_kdc_flags flags;
1189     flags.i = 0;
1190     return krb5_get_credentials_with_flags(context, options, flags,
1191                                            ccache, in_creds, out_creds);
1192 }
1193
1194 struct krb5_get_creds_opt_data {
1195     krb5_principal self;
1196     krb5_flags options;
1197     krb5_enctype enctype;
1198     Ticket *ticket;
1199 };
1200
1201
1202 krb5_error_code KRB5_LIB_FUNCTION
1203 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1204 {
1205     *opt = calloc(1, sizeof(**opt));
1206     if (*opt == NULL) {
1207         krb5_set_error_message(context, ENOMEM,
1208                                N_("malloc: out of memory", ""));
1209         return ENOMEM;
1210     }
1211     return 0;
1212 }
1213
1214 void KRB5_LIB_FUNCTION
1215 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1216 {
1217     if (opt->self)
1218         krb5_free_principal(context, opt->self);
1219     if (opt->ticket) {
1220         free_Ticket(opt->ticket);
1221         free(opt->ticket);
1222     }
1223     memset(opt, 0, sizeof(*opt));
1224     free(opt);
1225 }
1226
1227 void KRB5_LIB_FUNCTION
1228 krb5_get_creds_opt_set_options(krb5_context context,
1229                                krb5_get_creds_opt opt,
1230                                krb5_flags options)
1231 {
1232     opt->options = options;
1233 }
1234
1235 void KRB5_LIB_FUNCTION
1236 krb5_get_creds_opt_add_options(krb5_context context,
1237                                krb5_get_creds_opt opt,
1238                                krb5_flags options)
1239 {
1240     opt->options |= options;
1241 }
1242
1243 void KRB5_LIB_FUNCTION
1244 krb5_get_creds_opt_set_enctype(krb5_context context,
1245                                krb5_get_creds_opt opt,
1246                                krb5_enctype enctype)
1247 {
1248     opt->enctype = enctype;
1249 }
1250
1251 krb5_error_code KRB5_LIB_FUNCTION
1252 krb5_get_creds_opt_set_impersonate(krb5_context context,
1253                                    krb5_get_creds_opt opt,
1254                                    krb5_const_principal self)
1255 {
1256     if (opt->self)
1257         krb5_free_principal(context, opt->self);
1258     return krb5_copy_principal(context, self, &opt->self);
1259 }
1260
1261 krb5_error_code KRB5_LIB_FUNCTION
1262 krb5_get_creds_opt_set_ticket(krb5_context context,
1263                               krb5_get_creds_opt opt,
1264                               const Ticket *ticket)
1265 {
1266     if (opt->ticket) {
1267         free_Ticket(opt->ticket);
1268         free(opt->ticket);
1269         opt->ticket = NULL;
1270     }
1271     if (ticket) {
1272         krb5_error_code ret;
1273
1274         opt->ticket = malloc(sizeof(*ticket));
1275         if (opt->ticket == NULL) {
1276             krb5_set_error_message(context, ENOMEM,
1277                                    N_("malloc: out of memory", ""));
1278             return ENOMEM;
1279         }
1280         ret = copy_Ticket(ticket, opt->ticket);
1281         if (ret) {
1282             free(opt->ticket);
1283             opt->ticket = NULL;
1284             krb5_set_error_message(context, ret,
1285                                    N_("malloc: out of memory", ""));
1286             return ret;
1287         }
1288     }
1289     return 0;
1290 }
1291
1292
1293
1294 krb5_error_code KRB5_LIB_FUNCTION
1295 krb5_get_creds(krb5_context context,
1296                krb5_get_creds_opt opt,
1297                krb5_ccache ccache,
1298                krb5_const_principal inprinc,
1299                krb5_creds **out_creds)
1300 {
1301     krb5_kdc_flags flags;
1302     krb5_flags options;
1303     krb5_creds in_creds;
1304     krb5_error_code ret;
1305     krb5_creds **tgts;
1306     krb5_creds *res_creds;
1307     int i;
1308
1309     memset(&in_creds, 0, sizeof(in_creds));
1310     in_creds.server = rk_UNCONST(inprinc);
1311
1312     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1313     if (ret)
1314         return ret;
1315
1316     options = opt->options;
1317     flags.i = 0;
1318
1319     *out_creds = NULL;
1320     res_creds = calloc(1, sizeof(*res_creds));
1321     if (res_creds == NULL) {
1322         krb5_free_principal(context, in_creds.client);
1323         krb5_set_error_message(context, ENOMEM,
1324                                N_("malloc: out of memory", ""));
1325         return ENOMEM;
1326     }
1327
1328     if (opt->enctype) {
1329         in_creds.session.keytype = opt->enctype;
1330         options |= KRB5_TC_MATCH_KEYTYPE;
1331     }
1332
1333     /*
1334      * If we got a credential, check if credential is expired before
1335      * returning it.
1336      */
1337     ret = krb5_cc_retrieve_cred(context,
1338                                 ccache,
1339                                 opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
1340                                 &in_creds, res_creds);
1341     /*
1342      * If we got a credential, check if credential is expired before
1343      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1344      */
1345     if (ret == 0) {
1346         krb5_timestamp timeret;
1347
1348         /* If expired ok, don't bother checking */
1349         if(options & KRB5_GC_EXPIRED_OK) {
1350             *out_creds = res_creds;
1351             krb5_free_principal(context, in_creds.client);
1352             return 0;
1353         }
1354         
1355         krb5_timeofday(context, &timeret);
1356         if(res_creds->times.endtime > timeret) {
1357             *out_creds = res_creds;
1358             krb5_free_principal(context, in_creds.client);
1359             return 0;
1360         }
1361         if(options & KRB5_GC_CACHED)
1362             krb5_cc_remove_cred(context, ccache, 0, res_creds);
1363
1364     } else if(ret != KRB5_CC_END) {
1365         free(res_creds);
1366         krb5_free_principal(context, in_creds.client);
1367         return ret;
1368     }
1369     free(res_creds);
1370     if(options & KRB5_GC_CACHED) {
1371         krb5_free_principal(context, in_creds.client);
1372         return not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1373     }
1374     if(options & KRB5_GC_USER_USER) {
1375         flags.b.enc_tkt_in_skey = 1;
1376         options |= KRB5_GC_NO_STORE;
1377     }
1378     if (options & KRB5_GC_FORWARDABLE)
1379         flags.b.forwardable = 1;
1380     if (options & KRB5_GC_NO_TRANSIT_CHECK)
1381         flags.b.disable_transited_check = 1;
1382     if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1383         flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1384         flags.b.constrained_delegation = 1;
1385     }
1386     if (options & KRB5_GC_CANONICALIZE)
1387         flags.b.canonicalize = 1;
1388
1389     tgts = NULL;
1390     ret = get_cred_kdc_any(context, flags, ccache,
1391                            &in_creds, opt->self, opt->ticket,
1392                            out_creds, &tgts);
1393     krb5_free_principal(context, in_creds.client);
1394     for(i = 0; tgts && tgts[i]; i++) {
1395         krb5_cc_store_cred(context, ccache, tgts[i]);
1396         krb5_free_creds(context, tgts[i]);
1397     }
1398     free(tgts);
1399     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1400         krb5_cc_store_cred(context, ccache, *out_creds);
1401     return ret;
1402 }
1403
1404 /*
1405  *
1406  */
1407
1408 krb5_error_code KRB5_LIB_FUNCTION
1409 krb5_get_renewed_creds(krb5_context context,
1410                        krb5_creds *creds,
1411                        krb5_const_principal client,
1412                        krb5_ccache ccache,
1413                        const char *in_tkt_service)
1414 {
1415     krb5_error_code ret;
1416     krb5_kdc_flags flags;
1417     krb5_creds in, *template, *out = NULL;
1418
1419     memset(&in, 0, sizeof(in));
1420     memset(creds, 0, sizeof(*creds));
1421
1422     ret = krb5_copy_principal(context, client, &in.client);
1423     if (ret)
1424         return ret;
1425
1426     if (in_tkt_service) {
1427         ret = krb5_parse_name(context, in_tkt_service, &in.server);
1428         if (ret) {
1429             krb5_free_principal(context, in.client);
1430             return ret;
1431         }
1432     } else {
1433         const char *realm = krb5_principal_get_realm(context, client);
1434         
1435         ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1436                                   realm, NULL);
1437         if (ret) {
1438             krb5_free_principal(context, in.client);
1439             return ret;
1440         }
1441     }
1442
1443     flags.i = 0;
1444     flags.b.renewable = flags.b.renew = 1;
1445
1446     /*
1447      * Get template from old credential cache for the same entry, if
1448      * this failes, no worries.
1449      */
1450     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1451     if (ret == 0) {
1452         flags.b.forwardable = template->flags.b.forwardable;
1453         flags.b.proxiable = template->flags.b.proxiable;
1454         krb5_free_creds (context, template);
1455     }
1456
1457     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1458     krb5_free_principal(context, in.client);
1459     krb5_free_principal(context, in.server);
1460     if (ret)
1461         return ret;
1462
1463     ret = krb5_copy_creds_contents(context, out, creds);
1464     krb5_free_creds(context, out);
1465
1466     return ret;
1467 }