etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / krb5 / get_cred.c
blob7328b0d6a3576d6894a68eff2a2693d8486ff709
1 /* $NetBSD: get_cred.c,v 1.1.1.2 2014/04/24 12:45:50 pettai Exp $ */
3 /*
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
38 #include "krb5_locl.h"
39 #include <assert.h>
41 static krb5_error_code
42 get_cred_kdc_capath(krb5_context, krb5_kdc_flags,
43 krb5_ccache, krb5_creds *, krb5_principal,
44 Ticket *, krb5_creds **, krb5_creds ***);
47 * Take the `body' and encode it into `padata' using the credentials
48 * in `creds'.
51 static krb5_error_code
52 make_pa_tgs_req(krb5_context context,
53 krb5_auth_context ac,
54 KDC_REQ_BODY *body,
55 PA_DATA *padata,
56 krb5_creds *creds)
58 u_char *buf;
59 size_t buf_size;
60 size_t len = 0;
61 krb5_data in_data;
62 krb5_error_code ret;
64 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
65 if (ret)
66 goto out;
67 if(buf_size != len)
68 krb5_abortx(context, "internal error in ASN.1 encoder");
70 in_data.length = len;
71 in_data.data = buf;
72 ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
73 &padata->padata_value,
74 KRB5_KU_TGS_REQ_AUTH_CKSUM,
75 KRB5_KU_TGS_REQ_AUTH);
76 out:
77 free (buf);
78 if(ret)
79 return ret;
80 padata->padata_type = KRB5_PADATA_TGS_REQ;
81 return 0;
85 * Set the `enc-authorization-data' in `req_body' based on `authdata'
88 static krb5_error_code
89 set_auth_data (krb5_context context,
90 KDC_REQ_BODY *req_body,
91 krb5_authdata *authdata,
92 krb5_keyblock *subkey)
94 if(authdata->len) {
95 size_t len = 0, buf_size;
96 unsigned char *buf;
97 krb5_crypto crypto;
98 krb5_error_code ret;
100 ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
101 &len, ret);
102 if (ret)
103 return ret;
104 if (buf_size != len)
105 krb5_abortx(context, "internal error in ASN.1 encoder");
107 ALLOC(req_body->enc_authorization_data, 1);
108 if (req_body->enc_authorization_data == NULL) {
109 free (buf);
110 krb5_set_error_message(context, ENOMEM,
111 N_("malloc: out of memory", ""));
112 return ENOMEM;
114 ret = krb5_crypto_init(context, subkey, 0, &crypto);
115 if (ret) {
116 free (buf);
117 free (req_body->enc_authorization_data);
118 req_body->enc_authorization_data = NULL;
119 return ret;
121 krb5_encrypt_EncryptedData(context,
122 crypto,
123 KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
124 buf,
125 len,
127 req_body->enc_authorization_data);
128 free (buf);
129 krb5_crypto_destroy(context, crypto);
130 } else {
131 req_body->enc_authorization_data = NULL;
133 return 0;
137 * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
138 * (if not-NULL), `in_creds', `krbtgt', and returning the generated
139 * subkey in `subkey'.
142 static krb5_error_code
143 init_tgs_req (krb5_context context,
144 krb5_ccache ccache,
145 krb5_addresses *addresses,
146 krb5_kdc_flags flags,
147 Ticket *second_ticket,
148 krb5_creds *in_creds,
149 krb5_creds *krbtgt,
150 unsigned nonce,
151 const METHOD_DATA *padata,
152 krb5_keyblock **subkey,
153 TGS_REQ *t)
155 krb5_auth_context ac = NULL;
156 krb5_error_code ret = 0;
158 memset(t, 0, sizeof(*t));
159 t->pvno = 5;
160 t->msg_type = krb_tgs_req;
161 if (in_creds->session.keytype) {
162 ALLOC_SEQ(&t->req_body.etype, 1);
163 if(t->req_body.etype.val == NULL) {
164 ret = ENOMEM;
165 krb5_set_error_message(context, ret,
166 N_("malloc: out of memory", ""));
167 goto fail;
169 t->req_body.etype.val[0] = in_creds->session.keytype;
170 } else {
171 ret = _krb5_init_etype(context,
172 KRB5_PDU_TGS_REQUEST,
173 &t->req_body.etype.len,
174 &t->req_body.etype.val,
175 NULL);
177 if (ret)
178 goto fail;
179 t->req_body.addresses = addresses;
180 t->req_body.kdc_options = flags.b;
181 t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
182 t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
183 t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
184 ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
185 if (ret)
186 goto fail;
187 ALLOC(t->req_body.sname, 1);
188 if (t->req_body.sname == NULL) {
189 ret = ENOMEM;
190 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
191 goto fail;
194 /* some versions of some code might require that the client be
195 present in TGS-REQs, but this is clearly against the spec */
197 ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
198 if (ret)
199 goto fail;
201 if (krbtgt->times.starttime) {
202 ALLOC(t->req_body.from, 1);
203 if(t->req_body.from == NULL){
204 ret = krb5_enomem(context);
205 goto fail;
207 *t->req_body.from = in_creds->times.starttime;
210 /* req_body.till should be NULL if there is no endtime specified,
211 but old MIT code (like DCE secd) doesn't like that */
212 ALLOC(t->req_body.till, 1);
213 if(t->req_body.till == NULL){
214 ret = ENOMEM;
215 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
216 goto fail;
218 *t->req_body.till = in_creds->times.endtime;
220 if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
221 ALLOC(t->req_body.rtime, 1);
222 if(t->req_body.rtime == NULL){
223 ret = krb5_enomem(context);
224 goto fail;
226 *t->req_body.rtime = in_creds->times.renew_till;
229 t->req_body.nonce = nonce;
230 if(second_ticket){
231 ALLOC(t->req_body.additional_tickets, 1);
232 if (t->req_body.additional_tickets == NULL) {
233 ret = ENOMEM;
234 krb5_set_error_message(context, ret,
235 N_("malloc: out of memory", ""));
236 goto fail;
238 ALLOC_SEQ(t->req_body.additional_tickets, 1);
239 if (t->req_body.additional_tickets->val == NULL) {
240 ret = ENOMEM;
241 krb5_set_error_message(context, ret,
242 N_("malloc: out of memory", ""));
243 goto fail;
245 ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
246 if (ret)
247 goto fail;
249 ALLOC(t->padata, 1);
250 if (t->padata == NULL) {
251 ret = ENOMEM;
252 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
253 goto fail;
255 ALLOC_SEQ(t->padata, 1 + padata->len);
256 if (t->padata->val == NULL) {
257 ret = ENOMEM;
258 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
259 goto fail;
262 size_t i;
263 for (i = 0; i < padata->len; i++) {
264 ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
265 if (ret) {
266 krb5_set_error_message(context, ret,
267 N_("malloc: out of memory", ""));
268 goto fail;
273 ret = krb5_auth_con_init(context, &ac);
274 if(ret)
275 goto fail;
277 ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
278 if (ret)
279 goto fail;
281 ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
282 ac->local_subkey);
283 if (ret)
284 goto fail;
286 ret = make_pa_tgs_req(context,
288 &t->req_body,
289 &t->padata->val[0],
290 krbtgt);
291 if(ret)
292 goto fail;
294 ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
295 if (ret)
296 goto fail;
298 fail:
299 if (ac)
300 krb5_auth_con_free(context, ac);
301 if (ret) {
302 t->req_body.addresses = NULL;
303 free_TGS_REQ (t);
305 return ret;
308 krb5_error_code
309 _krb5_get_krbtgt(krb5_context context,
310 krb5_ccache id,
311 krb5_realm realm,
312 krb5_creds **cred)
314 krb5_error_code ret;
315 krb5_creds tmp_cred;
317 memset(&tmp_cred, 0, sizeof(tmp_cred));
319 ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
320 if (ret)
321 return ret;
323 ret = krb5_make_principal(context,
324 &tmp_cred.server,
325 realm,
326 KRB5_TGS_NAME,
327 realm,
328 NULL);
329 if(ret) {
330 krb5_free_principal(context, tmp_cred.client);
331 return ret;
333 ret = krb5_get_credentials(context,
334 KRB5_GC_CACHED,
336 &tmp_cred,
337 cred);
338 krb5_free_principal(context, tmp_cred.client);
339 krb5_free_principal(context, tmp_cred.server);
340 if(ret)
341 return ret;
342 return 0;
345 /* DCE compatible decrypt proc */
346 static krb5_error_code KRB5_CALLCONV
347 decrypt_tkt_with_subkey (krb5_context context,
348 krb5_keyblock *key,
349 krb5_key_usage usage,
350 krb5_const_pointer skey,
351 krb5_kdc_rep *dec_rep)
353 const krb5_keyblock *subkey = skey;
354 krb5_error_code ret = 0;
355 krb5_data data;
356 size_t size;
357 krb5_crypto crypto;
359 assert(usage == 0);
361 krb5_data_zero(&data);
364 * start out with trying with subkey if we have one
366 if (subkey) {
367 ret = krb5_crypto_init(context, subkey, 0, &crypto);
368 if (ret)
369 return ret;
370 ret = krb5_decrypt_EncryptedData (context,
371 crypto,
372 KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
373 &dec_rep->kdc_rep.enc_part,
374 &data);
376 * If the is Windows 2000 DC, we need to retry with key usage
377 * 8 when doing ARCFOUR.
379 if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
380 ret = krb5_decrypt_EncryptedData(context,
381 crypto,
383 &dec_rep->kdc_rep.enc_part,
384 &data);
386 krb5_crypto_destroy(context, crypto);
388 if (subkey == NULL || ret) {
389 ret = krb5_crypto_init(context, key, 0, &crypto);
390 if (ret)
391 return ret;
392 ret = krb5_decrypt_EncryptedData (context,
393 crypto,
394 KRB5_KU_TGS_REP_ENC_PART_SESSION,
395 &dec_rep->kdc_rep.enc_part,
396 &data);
397 krb5_crypto_destroy(context, crypto);
399 if (ret)
400 return ret;
402 ret = decode_EncASRepPart(data.data,
403 data.length,
404 &dec_rep->enc_part,
405 &size);
406 if (ret)
407 ret = decode_EncTGSRepPart(data.data,
408 data.length,
409 &dec_rep->enc_part,
410 &size);
411 if (ret)
412 krb5_set_error_message(context, ret,
413 N_("Failed to decode encpart in ticket", ""));
414 krb5_data_free (&data);
415 return ret;
418 static krb5_error_code
419 get_cred_kdc(krb5_context context,
420 krb5_ccache id,
421 krb5_kdc_flags flags,
422 krb5_addresses *addresses,
423 krb5_creds *in_creds,
424 krb5_creds *krbtgt,
425 krb5_principal impersonate_principal,
426 Ticket *second_ticket,
427 krb5_creds *out_creds)
429 TGS_REQ req;
430 krb5_data enc;
431 krb5_data resp;
432 krb5_kdc_rep rep;
433 KRB_ERROR error;
434 krb5_error_code ret;
435 unsigned nonce;
436 krb5_keyblock *subkey = NULL;
437 size_t len = 0;
438 Ticket second_ticket_data;
439 METHOD_DATA padata;
441 krb5_data_zero(&resp);
442 krb5_data_zero(&enc);
443 padata.val = NULL;
444 padata.len = 0;
446 krb5_generate_random_block(&nonce, sizeof(nonce));
447 nonce &= 0xffffffff;
449 if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
450 ret = decode_Ticket(in_creds->second_ticket.data,
451 in_creds->second_ticket.length,
452 &second_ticket_data, &len);
453 if(ret)
454 return ret;
455 second_ticket = &second_ticket_data;
459 if (impersonate_principal) {
460 krb5_crypto crypto;
461 PA_S4U2Self self;
462 krb5_data data;
463 void *buf;
464 size_t size = 0;
466 self.name = impersonate_principal->name;
467 self.realm = impersonate_principal->realm;
468 self.auth = estrdup("Kerberos");
470 ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
471 if (ret) {
472 free(self.auth);
473 goto out;
476 ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
477 if (ret) {
478 free(self.auth);
479 krb5_data_free(&data);
480 goto out;
483 ret = krb5_create_checksum(context,
484 crypto,
485 KRB5_KU_OTHER_CKSUM,
487 data.data,
488 data.length,
489 &self.cksum);
490 krb5_crypto_destroy(context, crypto);
491 krb5_data_free(&data);
492 if (ret) {
493 free(self.auth);
494 goto out;
497 ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
498 free(self.auth);
499 free_Checksum(&self.cksum);
500 if (ret)
501 goto out;
502 if (len != size)
503 krb5_abortx(context, "internal asn1 error");
505 ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
506 if (ret)
507 goto out;
510 ret = init_tgs_req (context,
512 addresses,
513 flags,
514 second_ticket,
515 in_creds,
516 krbtgt,
517 nonce,
518 &padata,
519 &subkey,
520 &req);
521 if (ret)
522 goto out;
524 ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
525 if (ret)
526 goto out;
527 if(enc.length != len)
528 krb5_abortx(context, "internal error in ASN.1 encoder");
530 /* don't free addresses */
531 req.req_body.addresses = NULL;
532 free_TGS_REQ(&req);
535 * Send and receive
538 krb5_sendto_ctx stctx;
539 ret = krb5_sendto_ctx_alloc(context, &stctx);
540 if (ret)
541 return ret;
542 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
544 ret = krb5_sendto_context (context, stctx, &enc,
545 krbtgt->server->name.name_string.val[1],
546 &resp);
547 krb5_sendto_ctx_free(context, stctx);
549 if(ret)
550 goto out;
552 memset(&rep, 0, sizeof(rep));
553 if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
554 unsigned eflags = 0;
556 ret = krb5_copy_principal(context,
557 in_creds->client,
558 &out_creds->client);
559 if(ret)
560 goto out2;
561 ret = krb5_copy_principal(context,
562 in_creds->server,
563 &out_creds->server);
564 if(ret)
565 goto out2;
566 /* this should go someplace else */
567 out_creds->times.endtime = in_creds->times.endtime;
569 /* XXX should do better testing */
570 if (flags.b.constrained_delegation || impersonate_principal)
571 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
573 ret = _krb5_extract_ticket(context,
574 &rep,
575 out_creds,
576 &krbtgt->session,
577 NULL,
579 &krbtgt->addresses,
580 nonce,
581 eflags,
582 decrypt_tkt_with_subkey,
583 subkey);
584 out2:
585 krb5_free_kdc_rep(context, &rep);
586 } else if(krb5_rd_error(context, &resp, &error) == 0) {
587 ret = krb5_error_from_rd_error(context, &error, in_creds);
588 krb5_free_error_contents(context, &error);
589 } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
590 ret = KRB5KRB_AP_ERR_V4_REPLY;
591 krb5_clear_error_message(context);
592 } else {
593 ret = KRB5KRB_AP_ERR_MSG_TYPE;
594 krb5_clear_error_message(context);
597 out:
598 if (second_ticket == &second_ticket_data)
599 free_Ticket(&second_ticket_data);
600 free_METHOD_DATA(&padata);
601 krb5_data_free(&resp);
602 krb5_data_free(&enc);
603 if(subkey)
604 krb5_free_keyblock(context, subkey);
605 return ret;
610 * same as above, just get local addresses first if the krbtgt have
611 * them and the realm is not addressless
614 static krb5_error_code
615 get_cred_kdc_address(krb5_context context,
616 krb5_ccache id,
617 krb5_kdc_flags flags,
618 krb5_addresses *addrs,
619 krb5_creds *in_creds,
620 krb5_creds *krbtgt,
621 krb5_principal impersonate_principal,
622 Ticket *second_ticket,
623 krb5_creds *out_creds)
625 krb5_error_code ret;
626 krb5_addresses addresses = { 0, NULL };
629 * Inherit the address-ness of the krbtgt if the address is not
630 * specified.
633 if (addrs == NULL && krbtgt->addresses.len != 0) {
634 krb5_boolean noaddr;
636 krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
637 "no-addresses", FALSE, &noaddr);
639 if (!noaddr) {
640 krb5_get_all_client_addrs(context, &addresses);
641 /* XXX this sucks. */
642 addrs = &addresses;
643 if(addresses.len == 0)
644 addrs = NULL;
647 ret = get_cred_kdc(context, id, flags, addrs, in_creds,
648 krbtgt, impersonate_principal,
649 second_ticket, out_creds);
650 krb5_free_addresses(context, &addresses);
651 return ret;
654 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
655 krb5_get_kdc_cred(krb5_context context,
656 krb5_ccache id,
657 krb5_kdc_flags flags,
658 krb5_addresses *addresses,
659 Ticket *second_ticket,
660 krb5_creds *in_creds,
661 krb5_creds **out_creds
664 krb5_error_code ret;
665 krb5_creds *krbtgt;
667 *out_creds = calloc(1, sizeof(**out_creds));
668 if(*out_creds == NULL) {
669 krb5_set_error_message(context, ENOMEM,
670 N_("malloc: out of memory", ""));
671 return ENOMEM;
673 ret = _krb5_get_krbtgt (context,
675 in_creds->server->realm,
676 &krbtgt);
677 if(ret) {
678 free(*out_creds);
679 *out_creds = NULL;
680 return ret;
682 ret = get_cred_kdc(context, id, flags, addresses,
683 in_creds, krbtgt, NULL, NULL, *out_creds);
684 krb5_free_creds (context, krbtgt);
685 if(ret) {
686 free(*out_creds);
687 *out_creds = NULL;
689 return ret;
692 static int
693 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
695 krb5_error_code ret;
696 char *str;
698 ret = krb5_unparse_name(context, p, &str);
699 if(ret) {
700 krb5_clear_error_message(context);
701 return code;
703 krb5_set_error_message(context, code,
704 N_("Matching credential (%s) not found", ""), str);
705 free(str);
706 return code;
709 static krb5_error_code
710 find_cred(krb5_context context,
711 krb5_ccache id,
712 krb5_principal server,
713 krb5_creds **tgts,
714 krb5_creds *out_creds)
716 krb5_error_code ret;
717 krb5_creds mcreds;
719 krb5_cc_clear_mcred(&mcreds);
720 mcreds.server = server;
721 ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
722 &mcreds, out_creds);
723 if(ret == 0)
724 return 0;
725 while(tgts && *tgts){
726 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
727 &mcreds, *tgts)){
728 ret = krb5_copy_creds_contents(context, *tgts, out_creds);
729 return ret;
731 tgts++;
733 return not_found(context, server, KRB5_CC_NOTFOUND);
736 static krb5_error_code
737 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
739 int i;
740 krb5_error_code ret;
741 krb5_creds **tmp = *tgts;
743 for(i = 0; tmp && tmp[i]; i++); /* XXX */
744 tmp = realloc(tmp, (i+2)*sizeof(*tmp));
745 if(tmp == NULL) {
746 krb5_set_error_message(context, ENOMEM,
747 N_("malloc: out of memory", ""));
748 return ENOMEM;
750 *tgts = tmp;
751 ret = krb5_copy_creds(context, tkt, &tmp[i]);
752 tmp[i+1] = NULL;
753 return ret;
756 static krb5_error_code
757 get_cred_kdc_capath_worker(krb5_context context,
758 krb5_kdc_flags flags,
759 krb5_ccache ccache,
760 krb5_creds *in_creds,
761 krb5_const_realm try_realm,
762 krb5_principal impersonate_principal,
763 Ticket *second_ticket,
764 krb5_creds **out_creds,
765 krb5_creds ***ret_tgts)
767 krb5_error_code ret;
768 krb5_creds *tgt, tmp_creds;
769 krb5_const_realm client_realm, server_realm;
770 int ok_as_delegate = 1;
772 *out_creds = NULL;
774 client_realm = krb5_principal_get_realm(context, in_creds->client);
775 server_realm = krb5_principal_get_realm(context, in_creds->server);
776 memset(&tmp_creds, 0, sizeof(tmp_creds));
777 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
778 if(ret)
779 return ret;
781 ret = krb5_make_principal(context,
782 &tmp_creds.server,
783 try_realm,
784 KRB5_TGS_NAME,
785 server_realm,
786 NULL);
787 if(ret){
788 krb5_free_principal(context, tmp_creds.client);
789 return ret;
792 krb5_creds tgts;
794 ret = find_cred(context, ccache, tmp_creds.server,
795 *ret_tgts, &tgts);
796 if(ret == 0){
797 /* only allow implicit ok_as_delegate if the realm is the clients realm */
798 if (strcmp(try_realm, client_realm) != 0 || strcmp(try_realm, server_realm) != 0)
799 ok_as_delegate = tgts.flags.b.ok_as_delegate;
801 *out_creds = calloc(1, sizeof(**out_creds));
802 if(*out_creds == NULL) {
803 ret = ENOMEM;
804 krb5_set_error_message(context, ret,
805 N_("malloc: out of memory", ""));
806 } else {
807 ret = get_cred_kdc_address(context, ccache, flags, NULL,
808 in_creds, &tgts,
809 impersonate_principal,
810 second_ticket,
811 *out_creds);
812 if (ret) {
813 free (*out_creds);
814 *out_creds = NULL;
815 } else if (ok_as_delegate == 0)
816 (*out_creds)->flags.b.ok_as_delegate = 0;
818 krb5_free_cred_contents(context, &tgts);
819 krb5_free_principal(context, tmp_creds.server);
820 krb5_free_principal(context, tmp_creds.client);
821 return ret;
824 if(krb5_realm_compare(context, in_creds->client, in_creds->server))
825 return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
827 /* XXX this can loop forever */
828 while(1){
829 heim_general_string tgt_inst;
831 ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
832 NULL, NULL, &tgt, ret_tgts);
833 if(ret) {
834 krb5_free_principal(context, tmp_creds.server);
835 krb5_free_principal(context, tmp_creds.client);
836 return ret;
839 * if either of the chain or the ok_as_delegate was stripped
840 * by the kdc, make sure we strip it too.
842 if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
843 ok_as_delegate = 0;
844 tgt->flags.b.ok_as_delegate = 0;
847 ret = add_cred(context, tgt, ret_tgts);
848 if(ret) {
849 krb5_free_principal(context, tmp_creds.server);
850 krb5_free_principal(context, tmp_creds.client);
851 return ret;
853 tgt_inst = tgt->server->name.name_string.val[1];
854 if(strcmp(tgt_inst, server_realm) == 0)
855 break;
856 krb5_free_principal(context, tmp_creds.server);
857 ret = krb5_make_principal(context, &tmp_creds.server,
858 tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
859 if(ret) {
860 krb5_free_principal(context, tmp_creds.server);
861 krb5_free_principal(context, tmp_creds.client);
862 return ret;
864 ret = krb5_free_creds(context, tgt);
865 if(ret) {
866 krb5_free_principal(context, tmp_creds.server);
867 krb5_free_principal(context, tmp_creds.client);
868 return ret;
872 krb5_free_principal(context, tmp_creds.server);
873 krb5_free_principal(context, tmp_creds.client);
874 *out_creds = calloc(1, sizeof(**out_creds));
875 if(*out_creds == NULL) {
876 ret = ENOMEM;
877 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
878 } else {
879 ret = get_cred_kdc_address (context, ccache, flags, NULL,
880 in_creds, tgt, impersonate_principal,
881 second_ticket, *out_creds);
882 if (ret) {
883 free (*out_creds);
884 *out_creds = NULL;
887 krb5_free_creds(context, tgt);
888 return ret;
892 get_cred(server)
893 creds = cc_get_cred(server)
894 if(creds) return creds
895 tgt = cc_get_cred(krbtgt/server_realm@any_realm)
896 if(tgt)
897 return get_cred_tgt(server, tgt)
898 if(client_realm == server_realm)
899 return NULL
900 tgt = get_cred(krbtgt/server_realm@client_realm)
901 while(tgt_inst != server_realm)
902 tgt = get_cred(krbtgt/server_realm@tgt_inst)
903 return get_cred_tgt(server, tgt)
906 static krb5_error_code
907 get_cred_kdc_capath(krb5_context context,
908 krb5_kdc_flags flags,
909 krb5_ccache ccache,
910 krb5_creds *in_creds,
911 krb5_principal impersonate_principal,
912 Ticket *second_ticket,
913 krb5_creds **out_creds,
914 krb5_creds ***ret_tgts)
916 krb5_error_code ret;
917 krb5_const_realm client_realm, server_realm, try_realm;
919 client_realm = krb5_principal_get_realm(context, in_creds->client);
920 server_realm = krb5_principal_get_realm(context, in_creds->server);
922 try_realm = client_realm;
923 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
924 impersonate_principal, second_ticket, out_creds,
925 ret_tgts);
927 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
928 try_realm = krb5_config_get_string(context, NULL, "capaths",
929 client_realm, server_realm, NULL);
931 if (try_realm != NULL && strcmp(try_realm, client_realm)) {
932 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
933 try_realm, impersonate_principal,
934 second_ticket, out_creds, ret_tgts);
938 return ret;
941 static krb5_error_code
942 get_cred_kdc_referral(krb5_context context,
943 krb5_kdc_flags flags,
944 krb5_ccache ccache,
945 krb5_creds *in_creds,
946 krb5_principal impersonate_principal,
947 Ticket *second_ticket,
948 krb5_creds **out_creds,
949 krb5_creds ***ret_tgts)
951 krb5_const_realm client_realm;
952 krb5_error_code ret;
953 krb5_creds tgt, referral, ticket;
954 int loop = 0;
955 int ok_as_delegate = 1;
957 if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
958 krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
959 N_("Name too short to do referals, skipping", ""));
960 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
963 memset(&tgt, 0, sizeof(tgt));
964 memset(&ticket, 0, sizeof(ticket));
966 flags.b.canonicalize = 1;
968 *out_creds = NULL;
970 client_realm = krb5_principal_get_realm(context, in_creds->client);
972 /* find tgt for the clients base realm */
974 krb5_principal tgtname;
976 ret = krb5_make_principal(context, &tgtname,
977 client_realm,
978 KRB5_TGS_NAME,
979 client_realm,
980 NULL);
981 if(ret)
982 return ret;
984 ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
985 krb5_free_principal(context, tgtname);
986 if (ret)
987 return ret;
990 referral = *in_creds;
991 ret = krb5_copy_principal(context, in_creds->server, &referral.server);
992 if (ret) {
993 krb5_free_cred_contents(context, &tgt);
994 return ret;
996 ret = krb5_principal_set_realm(context, referral.server, client_realm);
997 if (ret) {
998 krb5_free_cred_contents(context, &tgt);
999 krb5_free_principal(context, referral.server);
1000 return ret;
1003 while (loop++ < 17) {
1004 krb5_creds **tickets;
1005 krb5_creds mcreds;
1006 char *referral_realm;
1008 /* Use cache if we are not doing impersonation or contrainte deleg */
1009 if (impersonate_principal == NULL || flags.b.constrained_delegation) {
1010 krb5_cc_clear_mcred(&mcreds);
1011 mcreds.server = referral.server;
1012 ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
1013 } else
1014 ret = EINVAL;
1016 if (ret) {
1017 ret = get_cred_kdc_address(context, ccache, flags, NULL,
1018 &referral, &tgt, impersonate_principal,
1019 second_ticket, &ticket);
1020 if (ret)
1021 goto out;
1024 /* Did we get the right ticket ? */
1025 if (krb5_principal_compare_any_realm(context,
1026 referral.server,
1027 ticket.server))
1028 break;
1030 if (!krb5_principal_is_krbtgt(context, ticket.server)) {
1031 krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
1032 N_("Got back an non krbtgt "
1033 "ticket referrals", ""));
1034 ret = KRB5KRB_AP_ERR_NOT_US;
1035 goto out;
1038 referral_realm = ticket.server->name.name_string.val[1];
1040 /* check that there are no referrals loops */
1041 tickets = *ret_tgts;
1043 krb5_cc_clear_mcred(&mcreds);
1044 mcreds.server = ticket.server;
1046 while(tickets && *tickets){
1047 if(krb5_compare_creds(context,
1048 KRB5_TC_DONT_MATCH_REALM,
1049 &mcreds,
1050 *tickets))
1052 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1053 N_("Referral from %s "
1054 "loops back to realm %s", ""),
1055 tgt.server->realm,
1056 referral_realm);
1057 ret = KRB5_GET_IN_TKT_LOOP;
1058 goto out;
1060 tickets++;
1064 * if either of the chain or the ok_as_delegate was stripped
1065 * by the kdc, make sure we strip it too.
1068 if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1069 ok_as_delegate = 0;
1070 ticket.flags.b.ok_as_delegate = 0;
1073 ret = add_cred(context, &ticket, ret_tgts);
1074 if (ret)
1075 goto out;
1077 /* try realm in the referral */
1078 ret = krb5_principal_set_realm(context,
1079 referral.server,
1080 referral_realm);
1081 krb5_free_cred_contents(context, &tgt);
1082 tgt = ticket;
1083 memset(&ticket, 0, sizeof(ticket));
1084 if (ret)
1085 goto out;
1088 ret = krb5_copy_creds(context, &ticket, out_creds);
1090 out:
1091 krb5_free_principal(context, referral.server);
1092 krb5_free_cred_contents(context, &tgt);
1093 krb5_free_cred_contents(context, &ticket);
1094 return ret;
1099 * Glue function between referrals version and old client chasing
1100 * codebase.
1103 krb5_error_code
1104 _krb5_get_cred_kdc_any(krb5_context context,
1105 krb5_kdc_flags flags,
1106 krb5_ccache ccache,
1107 krb5_creds *in_creds,
1108 krb5_principal impersonate_principal,
1109 Ticket *second_ticket,
1110 krb5_creds **out_creds,
1111 krb5_creds ***ret_tgts)
1113 krb5_error_code ret;
1114 krb5_deltat offset;
1116 ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
1117 if (ret) {
1118 context->kdc_sec_offset = offset;
1119 context->kdc_usec_offset = 0;
1122 ret = get_cred_kdc_referral(context,
1123 flags,
1124 ccache,
1125 in_creds,
1126 impersonate_principal,
1127 second_ticket,
1128 out_creds,
1129 ret_tgts);
1130 if (ret == 0 || flags.b.canonicalize)
1131 return ret;
1132 return get_cred_kdc_capath(context,
1133 flags,
1134 ccache,
1135 in_creds,
1136 impersonate_principal,
1137 second_ticket,
1138 out_creds,
1139 ret_tgts);
1143 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1144 krb5_get_credentials_with_flags(krb5_context context,
1145 krb5_flags options,
1146 krb5_kdc_flags flags,
1147 krb5_ccache ccache,
1148 krb5_creds *in_creds,
1149 krb5_creds **out_creds)
1151 krb5_error_code ret;
1152 krb5_creds **tgts;
1153 krb5_creds *res_creds;
1154 int i;
1156 if (in_creds->session.keytype) {
1157 ret = krb5_enctype_valid(context, in_creds->session.keytype);
1158 if (ret)
1159 return ret;
1162 *out_creds = NULL;
1163 res_creds = calloc(1, sizeof(*res_creds));
1164 if (res_creds == NULL) {
1165 krb5_set_error_message(context, ENOMEM,
1166 N_("malloc: out of memory", ""));
1167 return ENOMEM;
1170 if (in_creds->session.keytype)
1171 options |= KRB5_TC_MATCH_KEYTYPE;
1174 * If we got a credential, check if credential is expired before
1175 * returning it.
1177 ret = krb5_cc_retrieve_cred(context,
1178 ccache,
1179 in_creds->session.keytype ?
1180 KRB5_TC_MATCH_KEYTYPE : 0,
1181 in_creds, res_creds);
1183 * If we got a credential, check if credential is expired before
1184 * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1186 if (ret == 0) {
1187 krb5_timestamp timeret;
1189 /* If expired ok, don't bother checking */
1190 if(options & KRB5_GC_EXPIRED_OK) {
1191 *out_creds = res_creds;
1192 return 0;
1195 krb5_timeofday(context, &timeret);
1196 if(res_creds->times.endtime > timeret) {
1197 *out_creds = res_creds;
1198 return 0;
1200 if(options & KRB5_GC_CACHED)
1201 krb5_cc_remove_cred(context, ccache, 0, res_creds);
1203 } else if(ret != KRB5_CC_END) {
1204 free(res_creds);
1205 return ret;
1207 free(res_creds);
1208 if(options & KRB5_GC_CACHED)
1209 return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1211 if(options & KRB5_GC_USER_USER)
1212 flags.b.enc_tkt_in_skey = 1;
1213 if (flags.b.enc_tkt_in_skey)
1214 options |= KRB5_GC_NO_STORE;
1216 tgts = NULL;
1217 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1218 in_creds, NULL, NULL, out_creds, &tgts);
1219 for(i = 0; tgts && tgts[i]; i++) {
1220 krb5_cc_store_cred(context, ccache, tgts[i]);
1221 krb5_free_creds(context, tgts[i]);
1223 free(tgts);
1224 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1225 krb5_cc_store_cred(context, ccache, *out_creds);
1226 return ret;
1229 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1230 krb5_get_credentials(krb5_context context,
1231 krb5_flags options,
1232 krb5_ccache ccache,
1233 krb5_creds *in_creds,
1234 krb5_creds **out_creds)
1236 krb5_kdc_flags flags;
1237 flags.i = 0;
1238 return krb5_get_credentials_with_flags(context, options, flags,
1239 ccache, in_creds, out_creds);
1242 struct krb5_get_creds_opt_data {
1243 krb5_principal self;
1244 krb5_flags options;
1245 krb5_enctype enctype;
1246 Ticket *ticket;
1250 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1251 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1253 *opt = calloc(1, sizeof(**opt));
1254 if (*opt == NULL) {
1255 krb5_set_error_message(context, ENOMEM,
1256 N_("malloc: out of memory", ""));
1257 return ENOMEM;
1259 return 0;
1262 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1263 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1265 if (opt->self)
1266 krb5_free_principal(context, opt->self);
1267 if (opt->ticket) {
1268 free_Ticket(opt->ticket);
1269 free(opt->ticket);
1271 memset(opt, 0, sizeof(*opt));
1272 free(opt);
1275 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1276 krb5_get_creds_opt_set_options(krb5_context context,
1277 krb5_get_creds_opt opt,
1278 krb5_flags options)
1280 opt->options = options;
1283 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1284 krb5_get_creds_opt_add_options(krb5_context context,
1285 krb5_get_creds_opt opt,
1286 krb5_flags options)
1288 opt->options |= options;
1291 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1292 krb5_get_creds_opt_set_enctype(krb5_context context,
1293 krb5_get_creds_opt opt,
1294 krb5_enctype enctype)
1296 opt->enctype = enctype;
1299 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1300 krb5_get_creds_opt_set_impersonate(krb5_context context,
1301 krb5_get_creds_opt opt,
1302 krb5_const_principal self)
1304 if (opt->self)
1305 krb5_free_principal(context, opt->self);
1306 return krb5_copy_principal(context, self, &opt->self);
1309 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1310 krb5_get_creds_opt_set_ticket(krb5_context context,
1311 krb5_get_creds_opt opt,
1312 const Ticket *ticket)
1314 if (opt->ticket) {
1315 free_Ticket(opt->ticket);
1316 free(opt->ticket);
1317 opt->ticket = NULL;
1319 if (ticket) {
1320 krb5_error_code ret;
1322 opt->ticket = malloc(sizeof(*ticket));
1323 if (opt->ticket == NULL) {
1324 krb5_set_error_message(context, ENOMEM,
1325 N_("malloc: out of memory", ""));
1326 return ENOMEM;
1328 ret = copy_Ticket(ticket, opt->ticket);
1329 if (ret) {
1330 free(opt->ticket);
1331 opt->ticket = NULL;
1332 krb5_set_error_message(context, ret,
1333 N_("malloc: out of memory", ""));
1334 return ret;
1337 return 0;
1342 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1343 krb5_get_creds(krb5_context context,
1344 krb5_get_creds_opt opt,
1345 krb5_ccache ccache,
1346 krb5_const_principal inprinc,
1347 krb5_creds **out_creds)
1349 krb5_kdc_flags flags;
1350 krb5_flags options;
1351 krb5_creds in_creds;
1352 krb5_error_code ret;
1353 krb5_creds **tgts;
1354 krb5_creds *res_creds;
1355 int i;
1357 if (opt && opt->enctype) {
1358 ret = krb5_enctype_valid(context, opt->enctype);
1359 if (ret)
1360 return ret;
1363 memset(&in_creds, 0, sizeof(in_creds));
1364 in_creds.server = rk_UNCONST(inprinc);
1366 ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1367 if (ret)
1368 return ret;
1370 if (opt)
1371 options = opt->options;
1372 else
1373 options = 0;
1374 flags.i = 0;
1376 *out_creds = NULL;
1377 res_creds = calloc(1, sizeof(*res_creds));
1378 if (res_creds == NULL) {
1379 krb5_free_principal(context, in_creds.client);
1380 krb5_set_error_message(context, ENOMEM,
1381 N_("malloc: out of memory", ""));
1382 return ENOMEM;
1385 if (opt && opt->enctype) {
1386 in_creds.session.keytype = opt->enctype;
1387 options |= KRB5_TC_MATCH_KEYTYPE;
1391 * If we got a credential, check if credential is expired before
1392 * returning it.
1394 ret = krb5_cc_retrieve_cred(context,
1395 ccache,
1396 options & KRB5_TC_MATCH_KEYTYPE,
1397 &in_creds, res_creds);
1399 * If we got a credential, check if credential is expired before
1400 * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1402 if (ret == 0) {
1403 krb5_timestamp timeret;
1405 /* If expired ok, don't bother checking */
1406 if(options & KRB5_GC_EXPIRED_OK) {
1407 *out_creds = res_creds;
1408 krb5_free_principal(context, in_creds.client);
1409 goto out;
1412 krb5_timeofday(context, &timeret);
1413 if(res_creds->times.endtime > timeret) {
1414 *out_creds = res_creds;
1415 krb5_free_principal(context, in_creds.client);
1416 goto out;
1418 if(options & KRB5_GC_CACHED)
1419 krb5_cc_remove_cred(context, ccache, 0, res_creds);
1421 } else if(ret != KRB5_CC_END) {
1422 free(res_creds);
1423 krb5_free_principal(context, in_creds.client);
1424 goto out;
1426 free(res_creds);
1427 if(options & KRB5_GC_CACHED) {
1428 krb5_free_principal(context, in_creds.client);
1429 ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1430 goto out;
1432 if(options & KRB5_GC_USER_USER) {
1433 flags.b.enc_tkt_in_skey = 1;
1434 options |= KRB5_GC_NO_STORE;
1436 if (options & KRB5_GC_FORWARDABLE)
1437 flags.b.forwardable = 1;
1438 if (options & KRB5_GC_NO_TRANSIT_CHECK)
1439 flags.b.disable_transited_check = 1;
1440 if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1441 flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1442 flags.b.constrained_delegation = 1;
1444 if (options & KRB5_GC_CANONICALIZE)
1445 flags.b.canonicalize = 1;
1447 tgts = NULL;
1448 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1449 &in_creds, opt->self, opt->ticket,
1450 out_creds, &tgts);
1451 krb5_free_principal(context, in_creds.client);
1452 for(i = 0; tgts && tgts[i]; i++) {
1453 krb5_cc_store_cred(context, ccache, tgts[i]);
1454 krb5_free_creds(context, tgts[i]);
1456 free(tgts);
1457 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1458 krb5_cc_store_cred(context, ccache, *out_creds);
1460 out:
1461 _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
1463 return ret;
1470 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1471 krb5_get_renewed_creds(krb5_context context,
1472 krb5_creds *creds,
1473 krb5_const_principal client,
1474 krb5_ccache ccache,
1475 const char *in_tkt_service)
1477 krb5_error_code ret;
1478 krb5_kdc_flags flags;
1479 krb5_creds in, *template, *out = NULL;
1481 memset(&in, 0, sizeof(in));
1482 memset(creds, 0, sizeof(*creds));
1484 ret = krb5_copy_principal(context, client, &in.client);
1485 if (ret)
1486 return ret;
1488 if (in_tkt_service) {
1489 ret = krb5_parse_name(context, in_tkt_service, &in.server);
1490 if (ret) {
1491 krb5_free_principal(context, in.client);
1492 return ret;
1494 } else {
1495 const char *realm = krb5_principal_get_realm(context, client);
1497 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1498 realm, NULL);
1499 if (ret) {
1500 krb5_free_principal(context, in.client);
1501 return ret;
1505 flags.i = 0;
1506 flags.b.renewable = flags.b.renew = 1;
1509 * Get template from old credential cache for the same entry, if
1510 * this failes, no worries.
1512 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1513 if (ret == 0) {
1514 flags.b.forwardable = template->flags.b.forwardable;
1515 flags.b.proxiable = template->flags.b.proxiable;
1516 krb5_free_creds (context, template);
1519 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1520 krb5_free_principal(context, in.client);
1521 krb5_free_principal(context, in.server);
1522 if (ret)
1523 return ret;
1525 ret = krb5_copy_creds_contents(context, out, creds);
1526 krb5_free_creds(context, out);
1528 return ret;