Use strerror_r
[heimdal.git] / lib / krb5 / get_cred.c
blob63152bbfa6567903e4651c1b37e665965dc089b4
1 /*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include <krb5_locl.h>
35 #include <assert.h>
38 * Take the `body' and encode it into `padata' using the credentials
39 * in `creds'.
42 static krb5_error_code
43 make_pa_tgs_req(krb5_context context,
44 krb5_auth_context ac,
45 KDC_REQ_BODY *body,
46 PA_DATA *padata,
47 krb5_creds *creds)
49 u_char *buf;
50 size_t buf_size;
51 size_t len;
52 krb5_data in_data;
53 krb5_error_code ret;
55 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
56 if (ret)
57 goto out;
58 if(buf_size != len)
59 krb5_abortx(context, "internal error in ASN.1 encoder");
61 in_data.length = len;
62 in_data.data = buf;
63 ret = _krb5_mk_req_internal(context, &ac, 0, &in_data, creds,
64 &padata->padata_value,
65 KRB5_KU_TGS_REQ_AUTH_CKSUM,
66 KRB5_KU_TGS_REQ_AUTH);
67 out:
68 free (buf);
69 if(ret)
70 return ret;
71 padata->padata_type = KRB5_PADATA_TGS_REQ;
72 return 0;
76 * Set the `enc-authorization-data' in `req_body' based on `authdata'
79 static krb5_error_code
80 set_auth_data (krb5_context context,
81 KDC_REQ_BODY *req_body,
82 krb5_authdata *authdata,
83 krb5_keyblock *subkey)
85 if(authdata->len) {
86 size_t len, buf_size;
87 unsigned char *buf;
88 krb5_crypto crypto;
89 krb5_error_code ret;
91 ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
92 &len, ret);
93 if (ret)
94 return ret;
95 if (buf_size != len)
96 krb5_abortx(context, "internal error in ASN.1 encoder");
98 ALLOC(req_body->enc_authorization_data, 1);
99 if (req_body->enc_authorization_data == NULL) {
100 free (buf);
101 krb5_set_error_message(context, ENOMEM,
102 N_("malloc: out of memory", ""));
103 return ENOMEM;
105 ret = krb5_crypto_init(context, subkey, 0, &crypto);
106 if (ret) {
107 free (buf);
108 free (req_body->enc_authorization_data);
109 req_body->enc_authorization_data = NULL;
110 return ret;
112 krb5_encrypt_EncryptedData(context,
113 crypto,
114 KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
115 buf,
116 len,
118 req_body->enc_authorization_data);
119 free (buf);
120 krb5_crypto_destroy(context, crypto);
121 } else {
122 req_body->enc_authorization_data = NULL;
124 return 0;
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'.
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)
146 krb5_auth_context ac = NULL;
147 krb5_error_code ret = 0;
148 krb5_keyblock *key = NULL;
150 memset(t, 0, sizeof(*t));
151 t->pvno = 5;
152 t->msg_type = krb_tgs_req;
153 if (in_creds->session.keytype) {
154 ALLOC_SEQ(&t->req_body.etype, 1);
155 if(t->req_body.etype.val == NULL) {
156 ret = ENOMEM;
157 krb5_set_error_message(context, ret,
158 N_("malloc: out of memory", ""));
159 goto fail;
161 t->req_body.etype.val[0] = in_creds->session.keytype;
162 } else {
163 ret = krb5_init_etype(context,
164 &t->req_body.etype.len,
165 &t->req_body.etype.val,
166 NULL);
168 if (ret)
169 goto fail;
170 t->req_body.addresses = addresses;
171 t->req_body.kdc_options = flags.b;
172 ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
173 if (ret)
174 goto fail;
175 ALLOC(t->req_body.sname, 1);
176 if (t->req_body.sname == NULL) {
177 ret = ENOMEM;
178 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
179 goto fail;
182 /* some versions of some code might require that the client be
183 present in TGS-REQs, but this is clearly against the spec */
185 ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
186 if (ret)
187 goto fail;
189 /* req_body.till should be NULL if there is no endtime specified,
190 but old MIT code (like DCE secd) doesn't like that */
191 ALLOC(t->req_body.till, 1);
192 if(t->req_body.till == NULL){
193 ret = ENOMEM;
194 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
195 goto fail;
197 *t->req_body.till = in_creds->times.endtime;
199 t->req_body.nonce = nonce;
200 if(second_ticket){
201 ALLOC(t->req_body.additional_tickets, 1);
202 if (t->req_body.additional_tickets == NULL) {
203 ret = ENOMEM;
204 krb5_set_error_message(context, ret,
205 N_("malloc: out of memory", ""));
206 goto fail;
208 ALLOC_SEQ(t->req_body.additional_tickets, 1);
209 if (t->req_body.additional_tickets->val == NULL) {
210 ret = ENOMEM;
211 krb5_set_error_message(context, ret,
212 N_("malloc: out of memory", ""));
213 goto fail;
215 ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
216 if (ret)
217 goto fail;
219 ALLOC(t->padata, 1);
220 if (t->padata == NULL) {
221 ret = ENOMEM;
222 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
223 goto fail;
225 ALLOC_SEQ(t->padata, 1 + padata->len);
226 if (t->padata->val == NULL) {
227 ret = ENOMEM;
228 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
229 goto fail;
232 int i;
233 for (i = 0; i < padata->len; i++) {
234 ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
235 if (ret) {
236 krb5_set_error_message(context, ret,
237 N_("malloc: out of memory", ""));
238 goto fail;
243 ret = krb5_auth_con_init(context, &ac);
244 if(ret)
245 goto fail;
247 ret = krb5_generate_subkey_extended(context, &krbtgt->session,
248 ETYPE_NULL, &key);
249 if (ret)
250 goto fail;
252 ret = krb5_auth_con_setlocalsubkey(context, ac, key);
253 if (ret)
254 goto fail;
256 ret = set_auth_data (context, &t->req_body, &in_creds->authdata, key);
257 if (ret)
258 goto fail;
260 ret = make_pa_tgs_req(context,
262 &t->req_body,
263 &t->padata->val[0],
264 krbtgt);
265 if(ret)
266 goto fail;
268 *subkey = key;
269 key = NULL;
271 fail:
272 if (key)
273 krb5_free_keyblock (context, key);
274 if (ac)
275 krb5_auth_con_free(context, ac);
276 if (ret) {
277 t->req_body.addresses = NULL;
278 free_TGS_REQ (t);
280 return ret;
283 krb5_error_code
284 _krb5_get_krbtgt(krb5_context context,
285 krb5_ccache id,
286 krb5_realm realm,
287 krb5_creds **cred)
289 krb5_error_code ret;
290 krb5_creds tmp_cred;
292 memset(&tmp_cred, 0, sizeof(tmp_cred));
294 ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
295 if (ret)
296 return ret;
298 ret = krb5_make_principal(context,
299 &tmp_cred.server,
300 realm,
301 KRB5_TGS_NAME,
302 realm,
303 NULL);
304 if(ret) {
305 krb5_free_principal(context, tmp_cred.client);
306 return ret;
308 ret = krb5_get_credentials(context,
309 KRB5_GC_CACHED,
311 &tmp_cred,
312 cred);
313 krb5_free_principal(context, tmp_cred.client);
314 krb5_free_principal(context, tmp_cred.server);
315 if(ret)
316 return ret;
317 return 0;
320 /* DCE compatible decrypt proc */
321 static krb5_error_code
322 decrypt_tkt_with_subkey (krb5_context context,
323 krb5_keyblock *key,
324 krb5_key_usage usage,
325 krb5_const_pointer subkey,
326 krb5_kdc_rep *dec_rep)
328 krb5_error_code ret;
329 krb5_data data;
330 size_t size;
331 krb5_crypto crypto;
333 assert(usage == 0);
336 * start out with trying with subkey if we have one
338 if (subkey) {
339 ret = krb5_crypto_init(context, subkey, 0, &crypto);
340 if (ret)
341 return ret;
342 ret = krb5_decrypt_EncryptedData (context,
343 crypto,
344 KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
345 &dec_rep->kdc_rep.enc_part,
346 &data);
347 krb5_crypto_destroy(context, crypto);
349 if (subkey == NULL || ret) {
350 ret = krb5_crypto_init(context, key, 0, &crypto);
351 if (ret)
352 return ret;
353 ret = krb5_decrypt_EncryptedData (context,
354 crypto,
355 KRB5_KU_TGS_REP_ENC_PART_SESSION,
356 &dec_rep->kdc_rep.enc_part,
357 &data);
358 krb5_crypto_destroy(context, crypto);
360 if (ret)
361 return ret;
363 ret = decode_EncASRepPart(data.data,
364 data.length,
365 &dec_rep->enc_part,
366 &size);
367 if (ret)
368 ret = decode_EncTGSRepPart(data.data,
369 data.length,
370 &dec_rep->enc_part,
371 &size);
372 if (ret)
373 krb5_set_error_message(context, ret,
374 N_("Failed to decode encpart in ticket", ""));
375 krb5_data_free (&data);
376 return ret;
379 static krb5_error_code
380 get_cred_kdc(krb5_context context,
381 krb5_ccache id,
382 krb5_kdc_flags flags,
383 krb5_addresses *addresses,
384 krb5_creds *in_creds,
385 krb5_creds *krbtgt,
386 krb5_principal impersonate_principal,
387 Ticket *second_ticket,
388 krb5_creds *out_creds)
390 TGS_REQ req;
391 krb5_data enc;
392 krb5_data resp;
393 krb5_kdc_rep rep;
394 KRB_ERROR error;
395 krb5_error_code ret;
396 unsigned nonce;
397 krb5_keyblock *subkey = NULL;
398 size_t len;
399 Ticket second_ticket_data;
400 METHOD_DATA padata;
402 krb5_data_zero(&resp);
403 krb5_data_zero(&enc);
404 padata.val = NULL;
405 padata.len = 0;
407 krb5_generate_random_block(&nonce, sizeof(nonce));
408 nonce &= 0xffffffff;
410 if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
411 ret = decode_Ticket(in_creds->second_ticket.data,
412 in_creds->second_ticket.length,
413 &second_ticket_data, &len);
414 if(ret)
415 return ret;
416 second_ticket = &second_ticket_data;
420 if (impersonate_principal) {
421 krb5_crypto crypto;
422 PA_S4U2Self self;
423 krb5_data data;
424 void *buf;
425 size_t size;
427 self.name = impersonate_principal->name;
428 self.realm = impersonate_principal->realm;
429 self.auth = estrdup("Kerberos");
431 ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
432 if (ret) {
433 free(self.auth);
434 goto out;
437 ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
438 if (ret) {
439 free(self.auth);
440 krb5_data_free(&data);
441 goto out;
444 ret = krb5_create_checksum(context,
445 crypto,
446 KRB5_KU_OTHER_CKSUM,
448 data.data,
449 data.length,
450 &self.cksum);
451 krb5_crypto_destroy(context, crypto);
452 krb5_data_free(&data);
453 if (ret) {
454 free(self.auth);
455 goto out;
458 ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
459 free(self.auth);
460 free_Checksum(&self.cksum);
461 if (ret)
462 goto out;
463 if (len != size)
464 krb5_abortx(context, "internal asn1 error");
466 ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
467 if (ret)
468 goto out;
471 ret = init_tgs_req (context,
473 addresses,
474 flags,
475 second_ticket,
476 in_creds,
477 krbtgt,
478 nonce,
479 &padata,
480 &subkey,
481 &req);
482 if (ret)
483 goto out;
485 ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
486 if (ret)
487 goto out;
488 if(enc.length != len)
489 krb5_abortx(context, "internal error in ASN.1 encoder");
491 /* don't free addresses */
492 req.req_body.addresses = NULL;
493 free_TGS_REQ(&req);
496 * Send and receive
499 krb5_sendto_ctx stctx;
500 ret = krb5_sendto_ctx_alloc(context, &stctx);
501 if (ret)
502 return ret;
503 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
505 ret = krb5_sendto_context (context, stctx, &enc,
506 krbtgt->server->name.name_string.val[1],
507 &resp);
508 krb5_sendto_ctx_free(context, stctx);
510 if(ret)
511 goto out;
513 memset(&rep, 0, sizeof(rep));
514 if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
515 unsigned eflags = 0;
517 ret = krb5_copy_principal(context,
518 in_creds->client,
519 &out_creds->client);
520 if(ret)
521 goto out2;
522 ret = krb5_copy_principal(context,
523 in_creds->server,
524 &out_creds->server);
525 if(ret)
526 goto out2;
527 /* this should go someplace else */
528 out_creds->times.endtime = in_creds->times.endtime;
530 /* XXX should do better testing */
531 if (flags.b.constrained_delegation || impersonate_principal)
532 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
534 ret = _krb5_extract_ticket(context,
535 &rep,
536 out_creds,
537 &krbtgt->session,
538 NULL,
540 &krbtgt->addresses,
541 nonce,
542 eflags,
543 decrypt_tkt_with_subkey,
544 subkey);
545 out2:
546 krb5_free_kdc_rep(context, &rep);
547 } else if(krb5_rd_error(context, &resp, &error) == 0) {
548 ret = krb5_error_from_rd_error(context, &error, in_creds);
549 krb5_free_error_contents(context, &error);
550 } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
551 ret = KRB5KRB_AP_ERR_V4_REPLY;
552 krb5_clear_error_message(context);
553 } else {
554 ret = KRB5KRB_AP_ERR_MSG_TYPE;
555 krb5_clear_error_message(context);
558 out:
559 if (second_ticket == &second_ticket_data)
560 free_Ticket(&second_ticket_data);
561 free_METHOD_DATA(&padata);
562 krb5_data_free(&resp);
563 krb5_data_free(&enc);
564 if(subkey)
565 krb5_free_keyblock(context, subkey);
566 return ret;
571 * same as above, just get local addresses first if the krbtgt have
572 * them and the realm is not addressless
575 static krb5_error_code
576 get_cred_kdc_address(krb5_context context,
577 krb5_ccache id,
578 krb5_kdc_flags flags,
579 krb5_addresses *addrs,
580 krb5_creds *in_creds,
581 krb5_creds *krbtgt,
582 krb5_principal impersonate_principal,
583 Ticket *second_ticket,
584 krb5_creds *out_creds)
586 krb5_error_code ret;
587 krb5_addresses addresses = { 0, NULL };
590 * Inherit the address-ness of the krbtgt if the address is not
591 * specified.
594 if (addrs == NULL && krbtgt->addresses.len != 0) {
595 krb5_boolean noaddr;
597 krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
598 "no-addresses", FALSE, &noaddr);
600 if (!noaddr) {
601 krb5_get_all_client_addrs(context, &addresses);
602 /* XXX this sucks. */
603 addrs = &addresses;
604 if(addresses.len == 0)
605 addrs = NULL;
608 ret = get_cred_kdc(context, id, flags, addrs, in_creds,
609 krbtgt, impersonate_principal,
610 second_ticket, out_creds);
611 krb5_free_addresses(context, &addresses);
612 return ret;
615 krb5_error_code KRB5_LIB_FUNCTION
616 krb5_get_kdc_cred(krb5_context context,
617 krb5_ccache id,
618 krb5_kdc_flags flags,
619 krb5_addresses *addresses,
620 Ticket *second_ticket,
621 krb5_creds *in_creds,
622 krb5_creds **out_creds
625 krb5_error_code ret;
626 krb5_creds *krbtgt;
628 *out_creds = calloc(1, sizeof(**out_creds));
629 if(*out_creds == NULL) {
630 krb5_set_error_message(context, ENOMEM,
631 N_("malloc: out of memory", ""));
632 return ENOMEM;
634 ret = _krb5_get_krbtgt (context,
636 in_creds->server->realm,
637 &krbtgt);
638 if(ret) {
639 free(*out_creds);
640 *out_creds = NULL;
641 return ret;
643 ret = get_cred_kdc(context, id, flags, addresses,
644 in_creds, krbtgt, NULL, NULL, *out_creds);
645 krb5_free_creds (context, krbtgt);
646 if(ret) {
647 free(*out_creds);
648 *out_creds = NULL;
650 return ret;
653 static int
654 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
656 krb5_error_code ret;
657 char *str;
659 ret = krb5_unparse_name(context, p, &str);
660 if(ret) {
661 krb5_clear_error_message(context);
662 return code;
664 krb5_set_error_message(context, code,
665 N_("Matching credential (%s) not found", ""), str);
666 free(str);
667 return code;
670 static krb5_error_code
671 find_cred(krb5_context context,
672 krb5_ccache id,
673 krb5_principal server,
674 krb5_creds **tgts,
675 krb5_creds *out_creds)
677 krb5_error_code ret;
678 krb5_creds mcreds;
680 krb5_cc_clear_mcred(&mcreds);
681 mcreds.server = server;
682 ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
683 &mcreds, out_creds);
684 if(ret == 0)
685 return 0;
686 while(tgts && *tgts){
687 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
688 &mcreds, *tgts)){
689 ret = krb5_copy_creds_contents(context, *tgts, out_creds);
690 return ret;
692 tgts++;
694 return not_found(context, server, KRB5_CC_NOTFOUND);
697 static krb5_error_code
698 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
700 int i;
701 krb5_error_code ret;
702 krb5_creds **tmp = *tgts;
704 for(i = 0; tmp && tmp[i]; i++); /* XXX */
705 tmp = realloc(tmp, (i+2)*sizeof(*tmp));
706 if(tmp == NULL) {
707 krb5_set_error_message(context, ENOMEM,
708 N_("malloc: out of memory", ""));
709 return ENOMEM;
711 *tgts = tmp;
712 ret = krb5_copy_creds(context, tkt, &tmp[i]);
713 tmp[i+1] = NULL;
714 return ret;
718 get_cred(server)
719 creds = cc_get_cred(server)
720 if(creds) return creds
721 tgt = cc_get_cred(krbtgt/server_realm@any_realm)
722 if(tgt)
723 return get_cred_tgt(server, tgt)
724 if(client_realm == server_realm)
725 return NULL
726 tgt = get_cred(krbtgt/server_realm@client_realm)
727 while(tgt_inst != server_realm)
728 tgt = get_cred(krbtgt/server_realm@tgt_inst)
729 return get_cred_tgt(server, tgt)
732 static krb5_error_code
733 get_cred_kdc_capath(krb5_context context,
734 krb5_kdc_flags flags,
735 krb5_ccache ccache,
736 krb5_creds *in_creds,
737 krb5_principal impersonate_principal,
738 Ticket *second_ticket,
739 krb5_creds **out_creds,
740 krb5_creds ***ret_tgts)
742 krb5_error_code ret;
743 krb5_creds *tgt, tmp_creds;
744 krb5_const_realm client_realm, server_realm, try_realm;
745 int ok_as_delegate = 1;
747 *out_creds = NULL;
749 client_realm = krb5_principal_get_realm(context, in_creds->client);
750 server_realm = krb5_principal_get_realm(context, in_creds->server);
751 memset(&tmp_creds, 0, sizeof(tmp_creds));
752 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
753 if(ret)
754 return ret;
756 try_realm = krb5_config_get_string(context, NULL, "capaths",
757 client_realm, server_realm, NULL);
758 if (try_realm == NULL)
759 try_realm = client_realm;
761 ret = krb5_make_principal(context,
762 &tmp_creds.server,
763 try_realm,
764 KRB5_TGS_NAME,
765 server_realm,
766 NULL);
767 if(ret){
768 krb5_free_principal(context, tmp_creds.client);
769 return ret;
772 krb5_creds tgts;
774 ret = find_cred(context, ccache, tmp_creds.server,
775 *ret_tgts, &tgts);
776 if(ret == 0){
777 if (try_realm != client_realm)
778 ok_as_delegate = tgts.flags.b.ok_as_delegate;
780 *out_creds = calloc(1, sizeof(**out_creds));
781 if(*out_creds == NULL) {
782 ret = ENOMEM;
783 krb5_set_error_message(context, ret,
784 N_("malloc: out of memory", ""));
785 } else {
786 ret = get_cred_kdc_address(context, ccache, flags, NULL,
787 in_creds, &tgts,
788 impersonate_principal,
789 second_ticket,
790 *out_creds);
791 if (ret) {
792 free (*out_creds);
793 *out_creds = NULL;
794 } else if (ok_as_delegate == 0)
795 (*out_creds)->flags.b.ok_as_delegate = 0;
797 krb5_free_cred_contents(context, &tgts);
798 krb5_free_principal(context, tmp_creds.server);
799 krb5_free_principal(context, tmp_creds.client);
800 return ret;
803 if(krb5_realm_compare(context, in_creds->client, in_creds->server))
804 return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
806 /* XXX this can loop forever */
807 while(1){
808 heim_general_string tgt_inst;
810 ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
811 NULL, NULL, &tgt, ret_tgts);
812 if(ret) {
813 krb5_free_principal(context, tmp_creds.server);
814 krb5_free_principal(context, tmp_creds.client);
815 return ret;
818 * if either of the chain or the ok_as_delegate was stripped
819 * by the kdc, make sure we strip it too.
821 if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
822 ok_as_delegate = 0;
823 tgt->flags.b.ok_as_delegate = 0;
826 ret = add_cred(context, 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;
832 tgt_inst = tgt->server->name.name_string.val[1];
833 if(strcmp(tgt_inst, server_realm) == 0)
834 break;
835 krb5_free_principal(context, tmp_creds.server);
836 ret = krb5_make_principal(context, &tmp_creds.server,
837 tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
838 if(ret) {
839 krb5_free_principal(context, tmp_creds.server);
840 krb5_free_principal(context, tmp_creds.client);
841 return ret;
843 ret = krb5_free_creds(context, tgt);
844 if(ret) {
845 krb5_free_principal(context, tmp_creds.server);
846 krb5_free_principal(context, tmp_creds.client);
847 return ret;
851 krb5_free_principal(context, tmp_creds.server);
852 krb5_free_principal(context, tmp_creds.client);
853 *out_creds = calloc(1, sizeof(**out_creds));
854 if(*out_creds == NULL) {
855 ret = ENOMEM;
856 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
857 } else {
858 ret = get_cred_kdc_address (context, ccache, flags, NULL,
859 in_creds, tgt, impersonate_principal,
860 second_ticket, *out_creds);
861 if (ret) {
862 free (*out_creds);
863 *out_creds = NULL;
866 krb5_free_creds(context, tgt);
867 return ret;
870 static krb5_error_code
871 get_cred_kdc_referral(krb5_context context,
872 krb5_kdc_flags flags,
873 krb5_ccache ccache,
874 krb5_creds *in_creds,
875 krb5_principal impersonate_principal,
876 Ticket *second_ticket,
877 krb5_creds **out_creds,
878 krb5_creds ***ret_tgts)
880 krb5_const_realm client_realm;
881 krb5_error_code ret;
882 krb5_creds tgt, referral, ticket;
883 int loop = 0;
884 int ok_as_delegate = 1;
886 if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
887 krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
888 N_("Name too short to do referals, skipping", ""));
889 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
892 memset(&tgt, 0, sizeof(tgt));
893 memset(&ticket, 0, sizeof(ticket));
895 flags.b.canonicalize = 1;
897 *out_creds = NULL;
899 client_realm = krb5_principal_get_realm(context, in_creds->client);
901 /* find tgt for the clients base realm */
903 krb5_principal tgtname;
905 ret = krb5_make_principal(context, &tgtname,
906 client_realm,
907 KRB5_TGS_NAME,
908 client_realm,
909 NULL);
910 if(ret)
911 return ret;
913 ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
914 krb5_free_principal(context, tgtname);
915 if (ret)
916 return ret;
919 referral = *in_creds;
920 ret = krb5_copy_principal(context, in_creds->server, &referral.server);
921 if (ret) {
922 krb5_free_cred_contents(context, &tgt);
923 return ret;
925 ret = krb5_principal_set_realm(context, referral.server, client_realm);
926 if (ret) {
927 krb5_free_cred_contents(context, &tgt);
928 krb5_free_principal(context, referral.server);
929 return ret;
932 while (loop++ < 17) {
933 krb5_creds **tickets;
934 krb5_creds mcreds;
935 char *referral_realm;
937 /* Use cache if we are not doing impersonation or contrainte deleg */
938 if (impersonate_principal == NULL || flags.b.constrained_delegation) {
939 krb5_cc_clear_mcred(&mcreds);
940 mcreds.server = referral.server;
941 ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
942 } else
943 ret = EINVAL;
945 if (ret) {
946 ret = get_cred_kdc_address (context, ccache, flags, NULL,
947 &referral, &tgt, impersonate_principal,
948 second_ticket, &ticket);
949 if (ret)
950 goto out;
953 /* Did we get the right ticket ? */
954 if (krb5_principal_compare_any_realm(context,
955 referral.server,
956 ticket.server))
957 break;
959 if (ticket.server->name.name_string.len != 2 &&
960 strcmp(ticket.server->name.name_string.val[0], KRB5_TGS_NAME) != 0)
962 krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
963 N_("Got back an non krbtgt "
964 "ticket referrals", ""));
965 krb5_free_cred_contents(context, &ticket);
966 return KRB5KRB_AP_ERR_NOT_US;
969 referral_realm = ticket.server->name.name_string.val[1];
971 /* check that there are no referrals loops */
972 tickets = *ret_tgts;
974 krb5_cc_clear_mcred(&mcreds);
975 mcreds.server = ticket.server;
977 while(tickets && *tickets){
978 if(krb5_compare_creds(context,
979 KRB5_TC_DONT_MATCH_REALM,
980 &mcreds,
981 *tickets))
983 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
984 N_("Referral from %s "
985 "loops back to realm %s", ""),
986 tgt.server->realm,
987 referral_realm);
988 krb5_free_cred_contents(context, &ticket);
989 return KRB5_GET_IN_TKT_LOOP;
991 tickets++;
995 * if either of the chain or the ok_as_delegate was stripped
996 * by the kdc, make sure we strip it too.
999 if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1000 ok_as_delegate = 0;
1001 ticket.flags.b.ok_as_delegate = 0;
1004 ret = add_cred(context, &ticket, ret_tgts);
1005 if (ret) {
1006 krb5_free_cred_contents(context, &ticket);
1007 goto out;
1010 /* try realm in the referral */
1011 ret = krb5_principal_set_realm(context,
1012 referral.server,
1013 referral_realm);
1014 krb5_free_cred_contents(context, &tgt);
1015 tgt = ticket;
1016 memset(&ticket, 0, sizeof(ticket));
1017 if (ret)
1018 goto out;
1021 ret = krb5_copy_creds(context, &ticket, out_creds);
1023 out:
1024 krb5_free_principal(context, referral.server);
1025 krb5_free_cred_contents(context, &tgt);
1026 return ret;
1031 * Glue function between referrals version and old client chasing
1032 * codebase.
1035 krb5_error_code
1036 _krb5_get_cred_kdc_any(krb5_context context,
1037 krb5_kdc_flags flags,
1038 krb5_ccache ccache,
1039 krb5_creds *in_creds,
1040 krb5_principal impersonate_principal,
1041 Ticket *second_ticket,
1042 krb5_creds **out_creds,
1043 krb5_creds ***ret_tgts)
1045 krb5_error_code ret;
1047 ret = get_cred_kdc_referral(context,
1048 flags,
1049 ccache,
1050 in_creds,
1051 impersonate_principal,
1052 second_ticket,
1053 out_creds,
1054 ret_tgts);
1055 if (ret == 0 || flags.b.canonicalize)
1056 return ret;
1057 return get_cred_kdc_capath(context,
1058 flags,
1059 ccache,
1060 in_creds,
1061 impersonate_principal,
1062 second_ticket,
1063 out_creds,
1064 ret_tgts);
1068 krb5_error_code KRB5_LIB_FUNCTION
1069 krb5_get_credentials_with_flags(krb5_context context,
1070 krb5_flags options,
1071 krb5_kdc_flags flags,
1072 krb5_ccache ccache,
1073 krb5_creds *in_creds,
1074 krb5_creds **out_creds)
1076 krb5_error_code ret;
1077 krb5_creds **tgts;
1078 krb5_creds *res_creds;
1079 int i;
1081 if (in_creds->session.keytype) {
1082 ret = krb5_enctype_valid(context, in_creds->session.keytype);
1083 if (ret)
1084 return ret;
1087 *out_creds = NULL;
1088 res_creds = calloc(1, sizeof(*res_creds));
1089 if (res_creds == NULL) {
1090 krb5_set_error_message(context, ENOMEM,
1091 N_("malloc: out of memory", ""));
1092 return ENOMEM;
1095 if (in_creds->session.keytype)
1096 options |= KRB5_TC_MATCH_KEYTYPE;
1099 * If we got a credential, check if credential is expired before
1100 * returning it.
1102 ret = krb5_cc_retrieve_cred(context,
1103 ccache,
1104 in_creds->session.keytype ?
1105 KRB5_TC_MATCH_KEYTYPE : 0,
1106 in_creds, res_creds);
1108 * If we got a credential, check if credential is expired before
1109 * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1111 if (ret == 0) {
1112 krb5_timestamp timeret;
1114 /* If expired ok, don't bother checking */
1115 if(options & KRB5_GC_EXPIRED_OK) {
1116 *out_creds = res_creds;
1117 return 0;
1120 krb5_timeofday(context, &timeret);
1121 if(res_creds->times.endtime > timeret) {
1122 *out_creds = res_creds;
1123 return 0;
1125 if(options & KRB5_GC_CACHED)
1126 krb5_cc_remove_cred(context, ccache, 0, res_creds);
1128 } else if(ret != KRB5_CC_END) {
1129 free(res_creds);
1130 return ret;
1132 free(res_creds);
1133 if(options & KRB5_GC_CACHED)
1134 return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1136 if(options & KRB5_GC_USER_USER)
1137 flags.b.enc_tkt_in_skey = 1;
1138 if (flags.b.enc_tkt_in_skey)
1139 options |= KRB5_GC_NO_STORE;
1141 tgts = NULL;
1142 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1143 in_creds, NULL, NULL, out_creds, &tgts);
1144 for(i = 0; tgts && tgts[i]; i++) {
1145 krb5_cc_store_cred(context, ccache, tgts[i]);
1146 krb5_free_creds(context, tgts[i]);
1148 free(tgts);
1149 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1150 krb5_cc_store_cred(context, ccache, *out_creds);
1151 return ret;
1154 krb5_error_code KRB5_LIB_FUNCTION
1155 krb5_get_credentials(krb5_context context,
1156 krb5_flags options,
1157 krb5_ccache ccache,
1158 krb5_creds *in_creds,
1159 krb5_creds **out_creds)
1161 krb5_kdc_flags flags;
1162 flags.i = 0;
1163 return krb5_get_credentials_with_flags(context, options, flags,
1164 ccache, in_creds, out_creds);
1167 struct krb5_get_creds_opt_data {
1168 krb5_principal self;
1169 krb5_flags options;
1170 krb5_enctype enctype;
1171 Ticket *ticket;
1175 krb5_error_code KRB5_LIB_FUNCTION
1176 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1178 *opt = calloc(1, sizeof(**opt));
1179 if (*opt == NULL) {
1180 krb5_set_error_message(context, ENOMEM,
1181 N_("malloc: out of memory", ""));
1182 return ENOMEM;
1184 return 0;
1187 void KRB5_LIB_FUNCTION
1188 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1190 if (opt->self)
1191 krb5_free_principal(context, opt->self);
1192 if (opt->ticket) {
1193 free_Ticket(opt->ticket);
1194 free(opt->ticket);
1196 memset(opt, 0, sizeof(*opt));
1197 free(opt);
1200 void KRB5_LIB_FUNCTION
1201 krb5_get_creds_opt_set_options(krb5_context context,
1202 krb5_get_creds_opt opt,
1203 krb5_flags options)
1205 opt->options = options;
1208 void KRB5_LIB_FUNCTION
1209 krb5_get_creds_opt_add_options(krb5_context context,
1210 krb5_get_creds_opt opt,
1211 krb5_flags options)
1213 opt->options |= options;
1216 void KRB5_LIB_FUNCTION
1217 krb5_get_creds_opt_set_enctype(krb5_context context,
1218 krb5_get_creds_opt opt,
1219 krb5_enctype enctype)
1221 opt->enctype = enctype;
1224 krb5_error_code KRB5_LIB_FUNCTION
1225 krb5_get_creds_opt_set_impersonate(krb5_context context,
1226 krb5_get_creds_opt opt,
1227 krb5_const_principal self)
1229 if (opt->self)
1230 krb5_free_principal(context, opt->self);
1231 return krb5_copy_principal(context, self, &opt->self);
1234 krb5_error_code KRB5_LIB_FUNCTION
1235 krb5_get_creds_opt_set_ticket(krb5_context context,
1236 krb5_get_creds_opt opt,
1237 const Ticket *ticket)
1239 if (opt->ticket) {
1240 free_Ticket(opt->ticket);
1241 free(opt->ticket);
1242 opt->ticket = NULL;
1244 if (ticket) {
1245 krb5_error_code ret;
1247 opt->ticket = malloc(sizeof(*ticket));
1248 if (opt->ticket == NULL) {
1249 krb5_set_error_message(context, ENOMEM,
1250 N_("malloc: out of memory", ""));
1251 return ENOMEM;
1253 ret = copy_Ticket(ticket, opt->ticket);
1254 if (ret) {
1255 free(opt->ticket);
1256 opt->ticket = NULL;
1257 krb5_set_error_message(context, ret,
1258 N_("malloc: out of memory", ""));
1259 return ret;
1262 return 0;
1267 krb5_error_code KRB5_LIB_FUNCTION
1268 krb5_get_creds(krb5_context context,
1269 krb5_get_creds_opt opt,
1270 krb5_ccache ccache,
1271 krb5_const_principal inprinc,
1272 krb5_creds **out_creds)
1274 krb5_kdc_flags flags;
1275 krb5_flags options;
1276 krb5_creds in_creds;
1277 krb5_error_code ret;
1278 krb5_creds **tgts;
1279 krb5_creds *res_creds;
1280 int i;
1282 if (opt && opt->enctype) {
1283 ret = krb5_enctype_valid(context, opt->enctype);
1284 if (ret)
1285 return ret;
1288 memset(&in_creds, 0, sizeof(in_creds));
1289 in_creds.server = rk_UNCONST(inprinc);
1291 ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1292 if (ret)
1293 return ret;
1295 if (opt)
1296 options = opt->options;
1297 else
1298 options = 0;
1299 flags.i = 0;
1301 *out_creds = NULL;
1302 res_creds = calloc(1, sizeof(*res_creds));
1303 if (res_creds == NULL) {
1304 krb5_free_principal(context, in_creds.client);
1305 krb5_set_error_message(context, ENOMEM,
1306 N_("malloc: out of memory", ""));
1307 return ENOMEM;
1310 if (opt && opt->enctype) {
1311 in_creds.session.keytype = opt->enctype;
1312 options |= KRB5_TC_MATCH_KEYTYPE;
1316 * If we got a credential, check if credential is expired before
1317 * returning it.
1319 ret = krb5_cc_retrieve_cred(context,
1320 ccache,
1321 options & KRB5_TC_MATCH_KEYTYPE,
1322 &in_creds, res_creds);
1324 * If we got a credential, check if credential is expired before
1325 * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1327 if (ret == 0) {
1328 krb5_timestamp timeret;
1330 /* If expired ok, don't bother checking */
1331 if(options & KRB5_GC_EXPIRED_OK) {
1332 *out_creds = res_creds;
1333 krb5_free_principal(context, in_creds.client);
1334 return 0;
1337 krb5_timeofday(context, &timeret);
1338 if(res_creds->times.endtime > timeret) {
1339 *out_creds = res_creds;
1340 krb5_free_principal(context, in_creds.client);
1341 return 0;
1343 if(options & KRB5_GC_CACHED)
1344 krb5_cc_remove_cred(context, ccache, 0, res_creds);
1346 } else if(ret != KRB5_CC_END) {
1347 free(res_creds);
1348 krb5_free_principal(context, in_creds.client);
1349 return ret;
1351 free(res_creds);
1352 if(options & KRB5_GC_CACHED) {
1353 krb5_free_principal(context, in_creds.client);
1354 return not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1356 if(options & KRB5_GC_USER_USER) {
1357 flags.b.enc_tkt_in_skey = 1;
1358 options |= KRB5_GC_NO_STORE;
1360 if (options & KRB5_GC_FORWARDABLE)
1361 flags.b.forwardable = 1;
1362 if (options & KRB5_GC_NO_TRANSIT_CHECK)
1363 flags.b.disable_transited_check = 1;
1364 if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1365 flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1366 flags.b.constrained_delegation = 1;
1368 if (options & KRB5_GC_CANONICALIZE)
1369 flags.b.canonicalize = 1;
1371 tgts = NULL;
1372 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1373 &in_creds, opt->self, opt->ticket,
1374 out_creds, &tgts);
1375 krb5_free_principal(context, in_creds.client);
1376 for(i = 0; tgts && tgts[i]; i++) {
1377 krb5_cc_store_cred(context, ccache, tgts[i]);
1378 krb5_free_creds(context, tgts[i]);
1380 free(tgts);
1381 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1382 krb5_cc_store_cred(context, ccache, *out_creds);
1383 return ret;
1390 krb5_error_code KRB5_LIB_FUNCTION
1391 krb5_get_renewed_creds(krb5_context context,
1392 krb5_creds *creds,
1393 krb5_const_principal client,
1394 krb5_ccache ccache,
1395 const char *in_tkt_service)
1397 krb5_error_code ret;
1398 krb5_kdc_flags flags;
1399 krb5_creds in, *template, *out = NULL;
1401 memset(&in, 0, sizeof(in));
1402 memset(creds, 0, sizeof(*creds));
1404 ret = krb5_copy_principal(context, client, &in.client);
1405 if (ret)
1406 return ret;
1408 if (in_tkt_service) {
1409 ret = krb5_parse_name(context, in_tkt_service, &in.server);
1410 if (ret) {
1411 krb5_free_principal(context, in.client);
1412 return ret;
1414 } else {
1415 const char *realm = krb5_principal_get_realm(context, client);
1417 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1418 realm, NULL);
1419 if (ret) {
1420 krb5_free_principal(context, in.client);
1421 return ret;
1425 flags.i = 0;
1426 flags.b.renewable = flags.b.renew = 1;
1429 * Get template from old credential cache for the same entry, if
1430 * this failes, no worries.
1432 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1433 if (ret == 0) {
1434 flags.b.forwardable = template->flags.b.forwardable;
1435 flags.b.proxiable = template->flags.b.proxiable;
1436 krb5_free_creds (context, template);
1439 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1440 krb5_free_principal(context, in.client);
1441 krb5_free_principal(context, in.server);
1442 if (ret)
1443 return ret;
1445 ret = krb5_copy_creds_contents(context, out, creds);
1446 krb5_free_creds(context, out);
1448 return ret;