10417f1a529f1bcb309a3cf4b995cc2787b46ca7
[abartlet/samba.git/.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         *out_creds = NULL;
656         return ret;
657     }
658     ret = get_cred_kdc(context, id, flags, addresses,
659                        in_creds, krbtgt, NULL, NULL, *out_creds);
660     krb5_free_creds (context, krbtgt);
661     if(ret) {
662         free(*out_creds);
663         *out_creds = NULL;
664     }
665     return ret;
666 }
667
668 static int
669 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
670 {
671     krb5_error_code ret;
672     char *str;
673
674     ret = krb5_unparse_name(context, p, &str);
675     if(ret) {
676         krb5_clear_error_message(context);
677         return code;
678     }
679     krb5_set_error_message(context, code,
680                            N_("Matching credential (%s) not found", ""), str);
681     free(str);
682     return code;
683 }
684
685 static krb5_error_code
686 find_cred(krb5_context context,
687           krb5_ccache id,
688           krb5_principal server,
689           krb5_creds **tgts,
690           krb5_creds *out_creds)
691 {
692     krb5_error_code ret;
693     krb5_creds mcreds;
694
695     krb5_cc_clear_mcred(&mcreds);
696     mcreds.server = server;
697     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
698                                 &mcreds, out_creds);
699     if(ret == 0)
700         return 0;
701     while(tgts && *tgts){
702         if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
703                               &mcreds, *tgts)){
704             ret = krb5_copy_creds_contents(context, *tgts, out_creds);
705             return ret;
706         }
707         tgts++;
708     }
709     return not_found(context, server, KRB5_CC_NOTFOUND);
710 }
711
712 static krb5_error_code
713 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
714 {
715     int i;
716     krb5_error_code ret;
717     krb5_creds **tmp = *tgts;
718
719     for(i = 0; tmp && tmp[i]; i++); /* XXX */
720     tmp = realloc(tmp, (i+2)*sizeof(*tmp));
721     if(tmp == NULL) {
722         krb5_set_error_message(context, ENOMEM,
723                                N_("malloc: out of memory", ""));
724         return ENOMEM;
725     }
726     *tgts = tmp;
727     ret = krb5_copy_creds(context, tkt, &tmp[i]);
728     tmp[i+1] = NULL;
729     return ret;
730 }
731
732 /*
733 get_cred(server)
734         creds = cc_get_cred(server)
735         if(creds) return creds
736         tgt = cc_get_cred(krbtgt/server_realm@any_realm)
737         if(tgt)
738                 return get_cred_tgt(server, tgt)
739         if(client_realm == server_realm)
740                 return NULL
741         tgt = get_cred(krbtgt/server_realm@client_realm)
742         while(tgt_inst != server_realm)
743                 tgt = get_cred(krbtgt/server_realm@tgt_inst)
744         return get_cred_tgt(server, tgt)
745         */
746
747 static krb5_error_code
748 get_cred_kdc_capath(krb5_context context,
749                     krb5_kdc_flags flags,
750                     krb5_ccache ccache,
751                     krb5_creds *in_creds,
752                     krb5_principal impersonate_principal,
753                     Ticket *second_ticket,                      
754                     krb5_creds **out_creds,
755                     krb5_creds ***ret_tgts)
756 {
757     krb5_error_code ret;
758     krb5_creds *tgt, tmp_creds;
759     krb5_const_realm client_realm, server_realm, try_realm;
760     int ok_as_delegate = 1;
761
762     *out_creds = NULL;
763
764     client_realm = krb5_principal_get_realm(context, in_creds->client);
765     server_realm = krb5_principal_get_realm(context, in_creds->server);
766     memset(&tmp_creds, 0, sizeof(tmp_creds));
767     ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
768     if(ret)
769         return ret;
770
771     try_realm = krb5_config_get_string(context, NULL, "capaths",
772                                        client_realm, server_realm, NULL);
773     if (try_realm == NULL)
774         try_realm = client_realm;
775
776     ret = krb5_make_principal(context,
777                               &tmp_creds.server,
778                               try_realm,
779                               KRB5_TGS_NAME,
780                               server_realm,
781                               NULL);
782     if(ret){
783         krb5_free_principal(context, tmp_creds.client);
784         return ret;
785     }
786     {
787         krb5_creds tgts;
788
789         ret = find_cred(context, ccache, tmp_creds.server,
790                         *ret_tgts, &tgts);
791         if(ret == 0){
792             if (try_realm != client_realm)
793                 ok_as_delegate = tgts.flags.b.ok_as_delegate;
794
795             *out_creds = calloc(1, sizeof(**out_creds));
796             if(*out_creds == NULL) {
797                 ret = ENOMEM;
798                 krb5_set_error_message(context, ret,
799                                        N_("malloc: out of memory", ""));
800             } else {
801                 ret = get_cred_kdc_address(context, ccache, flags, NULL,
802                                            in_creds, &tgts,
803                                            impersonate_principal,
804                                            second_ticket,
805                                            *out_creds);
806                 if (ret) {
807                     free (*out_creds);
808                     *out_creds = NULL;
809                 } else if (ok_as_delegate == 0)
810                     (*out_creds)->flags.b.ok_as_delegate = 0;
811             }
812             krb5_free_cred_contents(context, &tgts);
813             krb5_free_principal(context, tmp_creds.server);
814             krb5_free_principal(context, tmp_creds.client);
815             return ret;
816         }
817     }
818     if(krb5_realm_compare(context, in_creds->client, in_creds->server))
819         return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
820
821     /* XXX this can loop forever */
822     while(1){
823         heim_general_string tgt_inst;
824
825         ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
826                                   NULL, NULL, &tgt, ret_tgts);
827         if(ret) {
828             krb5_free_principal(context, tmp_creds.server);
829             krb5_free_principal(context, tmp_creds.client);
830             return ret;
831         }
832         /* 
833          * if either of the chain or the ok_as_delegate was stripped
834          * by the kdc, make sure we strip it too.
835          */
836         if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
837             ok_as_delegate = 0;
838             tgt->flags.b.ok_as_delegate = 0;
839         }
840
841         ret = add_cred(context, tgt, ret_tgts);
842         if(ret) {
843             krb5_free_principal(context, tmp_creds.server);
844             krb5_free_principal(context, tmp_creds.client);
845             return ret;
846         }
847         tgt_inst = tgt->server->name.name_string.val[1];
848         if(strcmp(tgt_inst, server_realm) == 0)
849             break;
850         krb5_free_principal(context, tmp_creds.server);
851         ret = krb5_make_principal(context, &tmp_creds.server,
852                                   tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
853         if(ret) {
854             krb5_free_principal(context, tmp_creds.server);
855             krb5_free_principal(context, tmp_creds.client);
856             return ret;
857         }
858         ret = krb5_free_creds(context, tgt);
859         if(ret) {
860             krb5_free_principal(context, tmp_creds.server);
861             krb5_free_principal(context, tmp_creds.client);
862             return ret;
863         }
864     }
865         
866     krb5_free_principal(context, tmp_creds.server);
867     krb5_free_principal(context, tmp_creds.client);
868     *out_creds = calloc(1, sizeof(**out_creds));
869     if(*out_creds == NULL) {
870         ret = ENOMEM;
871         krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
872     } else {
873         ret = get_cred_kdc_address (context, ccache, flags, NULL,
874                                     in_creds, tgt, impersonate_principal,
875                                     second_ticket, *out_creds);
876         if (ret) {
877             free (*out_creds);
878             *out_creds = NULL;
879         }
880     }
881     krb5_free_creds(context, tgt);
882     return ret;
883 }
884
885 static krb5_error_code
886 get_cred_kdc_referral(krb5_context context,
887                       krb5_kdc_flags flags,
888                       krb5_ccache ccache,
889                       krb5_creds *in_creds,
890                       krb5_principal impersonate_principal,
891                       Ticket *second_ticket,                    
892                       krb5_creds **out_creds,
893                       krb5_creds ***ret_tgts)
894 {
895     krb5_const_realm client_realm;
896     krb5_error_code ret;
897     krb5_creds tgt, referral, ticket;
898     int loop = 0;
899     int ok_as_delegate = 1;
900
901     memset(&tgt, 0, sizeof(tgt));
902     memset(&ticket, 0, sizeof(ticket));
903
904     flags.b.canonicalize = 1;
905
906     *out_creds = NULL;
907
908     client_realm = krb5_principal_get_realm(context, in_creds->client);
909
910     /* find tgt for the clients base realm */
911     {
912         krb5_principal tgtname;
913         
914         ret = krb5_make_principal(context, &tgtname,
915                                   client_realm,
916                                   KRB5_TGS_NAME,
917                                   client_realm,
918                                   NULL);
919         if(ret)
920             return ret;
921         
922         ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
923         krb5_free_principal(context, tgtname);
924         if (ret)
925             return ret;
926     }
927
928     referral = *in_creds;
929     ret = krb5_copy_principal(context, in_creds->server, &referral.server);
930     if (ret) {
931         krb5_free_cred_contents(context, &tgt);
932         return ret;
933     }
934     ret = krb5_principal_set_realm(context, referral.server, client_realm);
935     if (ret) {
936         krb5_free_cred_contents(context, &tgt);
937         krb5_free_principal(context, referral.server);
938         return ret;
939     }
940
941     while (loop++ < 17) {
942         krb5_creds **tickets;
943         krb5_creds mcreds;
944         char *referral_realm;
945
946         /* Use cache if we are not doing impersonation or contrainte deleg */
947         if (impersonate_principal == NULL || flags.b.constrained_delegation) {
948             krb5_cc_clear_mcred(&mcreds);
949             mcreds.server = referral.server;
950             ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
951         } else
952             ret = EINVAL;
953
954         if (ret) {
955             ret = get_cred_kdc_address (context, ccache, flags, NULL,
956                                         &referral, &tgt, impersonate_principal,
957                                         second_ticket, &ticket);
958             if (ret)
959                 goto out;
960         }
961
962         /* Did we get the right ticket ? */
963         if (krb5_principal_compare_any_realm(context,
964                                              referral.server,
965                                              ticket.server))
966             break;
967
968         if (ticket.server->name.name_string.len != 2 &&
969             strcmp(ticket.server->name.name_string.val[0], KRB5_TGS_NAME) != 0)
970         {
971             krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
972                                    N_("Got back an non krbtgt "
973                                       "ticket referrals", ""));
974             krb5_free_cred_contents(context, &ticket);
975             return KRB5KRB_AP_ERR_NOT_US;
976         }
977
978         referral_realm = ticket.server->name.name_string.val[1];
979
980         /* check that there are no referrals loops */
981         tickets = *ret_tgts;
982
983         krb5_cc_clear_mcred(&mcreds);
984         mcreds.server = ticket.server;
985
986         while(tickets && *tickets){
987             if(krb5_compare_creds(context,
988                                   KRB5_TC_DONT_MATCH_REALM,
989                                   &mcreds,
990                                   *tickets))
991             {
992                 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
993                                        N_("Referral from %s "
994                                           "loops back to realm %s", ""),
995                                        tgt.server->realm,
996                                        referral_realm);
997                 krb5_free_cred_contents(context, &ticket);
998                 return KRB5_GET_IN_TKT_LOOP;
999             }
1000             tickets++;
1001         }       
1002
1003         /* 
1004          * if either of the chain or the ok_as_delegate was stripped
1005          * by the kdc, make sure we strip it too.
1006          */
1007
1008         if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1009             ok_as_delegate = 0;
1010             ticket.flags.b.ok_as_delegate = 0;
1011         }
1012
1013         ret = add_cred(context, &ticket, ret_tgts);
1014         if (ret) {
1015             krb5_free_cred_contents(context, &ticket);
1016             goto out;
1017         }
1018
1019         /* try realm in the referral */
1020         ret = krb5_principal_set_realm(context,
1021                                        referral.server,
1022                                        referral_realm);
1023         krb5_free_cred_contents(context, &tgt);
1024         tgt = ticket;
1025         memset(&ticket, 0, sizeof(ticket));
1026         if (ret)
1027             goto out;
1028     }
1029
1030     ret = krb5_copy_creds(context, &ticket, out_creds);
1031
1032 out:
1033     krb5_free_principal(context, referral.server);
1034     krb5_free_cred_contents(context, &tgt);
1035     return ret;
1036 }
1037
1038
1039 /*
1040  * Glue function between referrals version and old client chasing
1041  * codebase.
1042  */
1043
1044 krb5_error_code
1045 _krb5_get_cred_kdc_any(krb5_context context,
1046                        krb5_kdc_flags flags,
1047                        krb5_ccache ccache,
1048                        krb5_creds *in_creds,
1049                        krb5_principal impersonate_principal,
1050                        Ticket *second_ticket,                   
1051                        krb5_creds **out_creds,
1052                        krb5_creds ***ret_tgts)
1053 {
1054     krb5_error_code ret;
1055
1056     ret = get_cred_kdc_referral(context,
1057                                 flags,
1058                                 ccache,
1059                                 in_creds,
1060                                 impersonate_principal,
1061                                 second_ticket,
1062                                 out_creds,
1063                                 ret_tgts);
1064     if (ret == 0 || flags.b.canonicalize)
1065         return ret;
1066     return get_cred_kdc_capath(context,
1067                                 flags,
1068                                 ccache,
1069                                 in_creds,
1070                                 impersonate_principal,
1071                                 second_ticket,
1072                                 out_creds,
1073                                 ret_tgts);
1074 }
1075
1076
1077 krb5_error_code KRB5_LIB_FUNCTION
1078 krb5_get_credentials_with_flags(krb5_context context,
1079                                 krb5_flags options,
1080                                 krb5_kdc_flags flags,
1081                                 krb5_ccache ccache,
1082                                 krb5_creds *in_creds,
1083                                 krb5_creds **out_creds)
1084 {
1085     krb5_error_code ret;
1086     krb5_creds **tgts;
1087     krb5_creds *res_creds;
1088     int i;
1089
1090     *out_creds = NULL;
1091     res_creds = calloc(1, sizeof(*res_creds));
1092     if (res_creds == NULL) {
1093         krb5_set_error_message(context, ENOMEM,
1094                                N_("malloc: out of memory", ""));
1095         return ENOMEM;
1096     }
1097
1098     if (in_creds->session.keytype)
1099         options |= KRB5_TC_MATCH_KEYTYPE;
1100
1101     /*
1102      * If we got a credential, check if credential is expired before
1103      * returning it.
1104      */
1105     ret = krb5_cc_retrieve_cred(context,
1106                                 ccache,
1107                                 in_creds->session.keytype ?
1108                                 KRB5_TC_MATCH_KEYTYPE : 0,
1109                                 in_creds, res_creds);
1110     /*
1111      * If we got a credential, check if credential is expired before
1112      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1113      */
1114     if (ret == 0) {
1115         krb5_timestamp timeret;
1116
1117         /* If expired ok, don't bother checking */
1118         if(options & KRB5_GC_EXPIRED_OK) {
1119             *out_creds = res_creds;
1120             return 0;
1121         }
1122         
1123         krb5_timeofday(context, &timeret);
1124         if(res_creds->times.endtime > timeret) {
1125             *out_creds = res_creds;
1126             return 0;
1127         }
1128         if(options & KRB5_GC_CACHED)
1129             krb5_cc_remove_cred(context, ccache, 0, res_creds);
1130
1131     } else if(ret != KRB5_CC_END) {
1132         free(res_creds);
1133         return ret;
1134     }
1135     free(res_creds);
1136     if(options & KRB5_GC_CACHED)
1137         return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1138
1139     if(options & KRB5_GC_USER_USER)
1140         flags.b.enc_tkt_in_skey = 1;
1141     if (flags.b.enc_tkt_in_skey)
1142         options |= KRB5_GC_NO_STORE;
1143
1144     tgts = NULL;
1145     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1146                                  in_creds, NULL, NULL, out_creds, &tgts);
1147     for(i = 0; tgts && tgts[i]; i++) {
1148         krb5_cc_store_cred(context, ccache, tgts[i]);
1149         krb5_free_creds(context, tgts[i]);
1150     }
1151     free(tgts);
1152     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1153         krb5_cc_store_cred(context, ccache, *out_creds);
1154     return ret;
1155 }
1156
1157 krb5_error_code KRB5_LIB_FUNCTION
1158 krb5_get_credentials(krb5_context context,
1159                      krb5_flags options,
1160                      krb5_ccache ccache,
1161                      krb5_creds *in_creds,
1162                      krb5_creds **out_creds)
1163 {
1164     krb5_kdc_flags flags;
1165     flags.i = 0;
1166     return krb5_get_credentials_with_flags(context, options, flags,
1167                                            ccache, in_creds, out_creds);
1168 }
1169
1170 struct krb5_get_creds_opt_data {
1171     krb5_principal self;
1172     krb5_flags options;
1173     krb5_enctype enctype;
1174     Ticket *ticket;
1175 };
1176
1177
1178 krb5_error_code KRB5_LIB_FUNCTION
1179 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1180 {
1181     *opt = calloc(1, sizeof(**opt));
1182     if (*opt == NULL) {
1183         krb5_set_error_message(context, ENOMEM,
1184                                N_("malloc: out of memory", ""));
1185         return ENOMEM;
1186     }
1187     return 0;
1188 }
1189
1190 void KRB5_LIB_FUNCTION
1191 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1192 {
1193     if (opt->self)
1194         krb5_free_principal(context, opt->self);
1195     if (opt->ticket) {
1196         free_Ticket(opt->ticket);
1197         free(opt->ticket);
1198     }
1199     memset(opt, 0, sizeof(*opt));
1200     free(opt);
1201 }
1202
1203 void KRB5_LIB_FUNCTION
1204 krb5_get_creds_opt_set_options(krb5_context context,
1205                                krb5_get_creds_opt opt,
1206                                krb5_flags options)
1207 {
1208     opt->options = options;
1209 }
1210
1211 void KRB5_LIB_FUNCTION
1212 krb5_get_creds_opt_add_options(krb5_context context,
1213                                krb5_get_creds_opt opt,
1214                                krb5_flags options)
1215 {
1216     opt->options |= options;
1217 }
1218
1219 void KRB5_LIB_FUNCTION
1220 krb5_get_creds_opt_set_enctype(krb5_context context,
1221                                krb5_get_creds_opt opt,
1222                                krb5_enctype enctype)
1223 {
1224     opt->enctype = enctype;
1225 }
1226
1227 krb5_error_code KRB5_LIB_FUNCTION
1228 krb5_get_creds_opt_set_impersonate(krb5_context context,
1229                                    krb5_get_creds_opt opt,
1230                                    krb5_const_principal self)
1231 {
1232     if (opt->self)
1233         krb5_free_principal(context, opt->self);
1234     return krb5_copy_principal(context, self, &opt->self);
1235 }
1236
1237 krb5_error_code KRB5_LIB_FUNCTION
1238 krb5_get_creds_opt_set_ticket(krb5_context context,
1239                               krb5_get_creds_opt opt,
1240                               const Ticket *ticket)
1241 {
1242     if (opt->ticket) {
1243         free_Ticket(opt->ticket);
1244         free(opt->ticket);
1245         opt->ticket = NULL;
1246     }
1247     if (ticket) {
1248         krb5_error_code ret;
1249
1250         opt->ticket = malloc(sizeof(*ticket));
1251         if (opt->ticket == NULL) {
1252             krb5_set_error_message(context, ENOMEM,
1253                                    N_("malloc: out of memory", ""));
1254             return ENOMEM;
1255         }
1256         ret = copy_Ticket(ticket, opt->ticket);
1257         if (ret) {
1258             free(opt->ticket);
1259             opt->ticket = NULL;
1260             krb5_set_error_message(context, ret,
1261                                    N_("malloc: out of memory", ""));
1262             return ret;
1263         }
1264     }
1265     return 0;
1266 }
1267
1268
1269
1270 krb5_error_code KRB5_LIB_FUNCTION
1271 krb5_get_creds(krb5_context context,
1272                krb5_get_creds_opt opt,
1273                krb5_ccache ccache,
1274                krb5_const_principal inprinc,
1275                krb5_creds **out_creds)
1276 {
1277     krb5_kdc_flags flags;
1278     krb5_flags options;
1279     krb5_creds in_creds;
1280     krb5_error_code ret;
1281     krb5_creds **tgts;
1282     krb5_creds *res_creds;
1283     int i;
1284
1285     memset(&in_creds, 0, sizeof(in_creds));
1286     in_creds.server = rk_UNCONST(inprinc);
1287
1288     ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1289     if (ret)
1290         return ret;
1291
1292     options = opt->options;
1293     flags.i = 0;
1294
1295     *out_creds = NULL;
1296     res_creds = calloc(1, sizeof(*res_creds));
1297     if (res_creds == NULL) {
1298         krb5_free_principal(context, in_creds.client);
1299         krb5_set_error_message(context, ENOMEM,
1300                                N_("malloc: out of memory", ""));
1301         return ENOMEM;
1302     }
1303
1304     if (opt->enctype) {
1305         in_creds.session.keytype = opt->enctype;
1306         options |= KRB5_TC_MATCH_KEYTYPE;
1307     }
1308
1309     /*
1310      * If we got a credential, check if credential is expired before
1311      * returning it.
1312      */
1313     ret = krb5_cc_retrieve_cred(context,
1314                                 ccache,
1315                                 opt->enctype ? KRB5_TC_MATCH_KEYTYPE : 0,
1316                                 &in_creds, res_creds);
1317     /*
1318      * If we got a credential, check if credential is expired before
1319      * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1320      */
1321     if (ret == 0) {
1322         krb5_timestamp timeret;
1323
1324         /* If expired ok, don't bother checking */
1325         if(options & KRB5_GC_EXPIRED_OK) {
1326             *out_creds = res_creds;
1327             krb5_free_principal(context, in_creds.client);
1328             return 0;
1329         }
1330         
1331         krb5_timeofday(context, &timeret);
1332         if(res_creds->times.endtime > timeret) {
1333             *out_creds = res_creds;
1334             krb5_free_principal(context, in_creds.client);
1335             return 0;
1336         }
1337         if(options & KRB5_GC_CACHED)
1338             krb5_cc_remove_cred(context, ccache, 0, res_creds);
1339
1340     } else if(ret != KRB5_CC_END) {
1341         free(res_creds);
1342         krb5_free_principal(context, in_creds.client);
1343         return ret;
1344     }
1345     free(res_creds);
1346     if(options & KRB5_GC_CACHED) {
1347         krb5_free_principal(context, in_creds.client);
1348         return not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1349     }
1350     if(options & KRB5_GC_USER_USER) {
1351         flags.b.enc_tkt_in_skey = 1;
1352         options |= KRB5_GC_NO_STORE;
1353     }
1354     if (options & KRB5_GC_FORWARDABLE)
1355         flags.b.forwardable = 1;
1356     if (options & KRB5_GC_NO_TRANSIT_CHECK)
1357         flags.b.disable_transited_check = 1;
1358     if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1359         flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1360         flags.b.constrained_delegation = 1;
1361     }
1362     if (options & KRB5_GC_CANONICALIZE)
1363         flags.b.canonicalize = 1;
1364
1365     tgts = NULL;
1366     ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1367                                  &in_creds, opt->self, opt->ticket,
1368                                  out_creds, &tgts);
1369     krb5_free_principal(context, in_creds.client);
1370     for(i = 0; tgts && tgts[i]; i++) {
1371         krb5_cc_store_cred(context, ccache, tgts[i]);
1372         krb5_free_creds(context, tgts[i]);
1373     }
1374     free(tgts);
1375     if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1376         krb5_cc_store_cred(context, ccache, *out_creds);
1377     return ret;
1378 }
1379
1380 /*
1381  *
1382  */
1383
1384 krb5_error_code KRB5_LIB_FUNCTION
1385 krb5_get_renewed_creds(krb5_context context,
1386                        krb5_creds *creds,
1387                        krb5_const_principal client,
1388                        krb5_ccache ccache,
1389                        const char *in_tkt_service)
1390 {
1391     krb5_error_code ret;
1392     krb5_kdc_flags flags;
1393     krb5_creds in, *template, *out = NULL;
1394
1395     memset(&in, 0, sizeof(in));
1396     memset(creds, 0, sizeof(*creds));
1397
1398     ret = krb5_copy_principal(context, client, &in.client);
1399     if (ret)
1400         return ret;
1401
1402     if (in_tkt_service) {
1403         ret = krb5_parse_name(context, in_tkt_service, &in.server);
1404         if (ret) {
1405             krb5_free_principal(context, in.client);
1406             return ret;
1407         }
1408     } else {
1409         const char *realm = krb5_principal_get_realm(context, client);
1410         
1411         ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1412                                   realm, NULL);
1413         if (ret) {
1414             krb5_free_principal(context, in.client);
1415             return ret;
1416         }
1417     }
1418
1419     flags.i = 0;
1420     flags.b.renewable = flags.b.renew = 1;
1421
1422     /*
1423      * Get template from old credential cache for the same entry, if
1424      * this failes, no worries.
1425      */
1426     ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1427     if (ret == 0) {
1428         flags.b.forwardable = template->flags.b.forwardable;
1429         flags.b.proxiable = template->flags.b.proxiable;
1430         krb5_free_creds (context, template);
1431     }
1432
1433     ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1434     krb5_free_principal(context, in.client);
1435     krb5_free_principal(context, in.server);
1436     if (ret)
1437         return ret;
1438
1439     ret = krb5_copy_creds_contents(context, out, creds);
1440     krb5_free_creds(context, out);
1441
1442     return ret;
1443 }