Simplify subkey usage for tgs-req, don't rewrite tgs-rep-sub-key keyuage for arcfour...
[heimdal.git] / lib / krb5 / get_cred.c
blob707213733848033f38febe14ab499f70d9664e2c
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_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
248 if (ret)
249 goto fail;
251 ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
252 ac->local_subkey);
253 if (ret)
254 goto fail;
256 ret = make_pa_tgs_req(context,
258 &t->req_body,
259 &t->padata->val[0],
260 krbtgt);
261 if(ret)
262 goto fail;
264 ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
265 if (ret)
266 goto fail;
268 fail:
269 if (ac)
270 krb5_auth_con_free(context, ac);
271 if (ret) {
272 t->req_body.addresses = NULL;
273 free_TGS_REQ (t);
275 return ret;
278 krb5_error_code
279 _krb5_get_krbtgt(krb5_context context,
280 krb5_ccache id,
281 krb5_realm realm,
282 krb5_creds **cred)
284 krb5_error_code ret;
285 krb5_creds tmp_cred;
287 memset(&tmp_cred, 0, sizeof(tmp_cred));
289 ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
290 if (ret)
291 return ret;
293 ret = krb5_make_principal(context,
294 &tmp_cred.server,
295 realm,
296 KRB5_TGS_NAME,
297 realm,
298 NULL);
299 if(ret) {
300 krb5_free_principal(context, tmp_cred.client);
301 return ret;
303 ret = krb5_get_credentials(context,
304 KRB5_GC_CACHED,
306 &tmp_cred,
307 cred);
308 krb5_free_principal(context, tmp_cred.client);
309 krb5_free_principal(context, tmp_cred.server);
310 if(ret)
311 return ret;
312 return 0;
315 /* DCE compatible decrypt proc */
316 static krb5_error_code
317 decrypt_tkt_with_subkey (krb5_context context,
318 krb5_keyblock *key,
319 krb5_key_usage usage,
320 krb5_const_pointer subkey,
321 krb5_kdc_rep *dec_rep)
323 krb5_error_code ret;
324 krb5_data data;
325 size_t size;
326 krb5_crypto crypto;
328 assert(usage == 0);
331 * start out with trying with subkey if we have one
333 if (subkey) {
334 ret = krb5_crypto_init(context, subkey, 0, &crypto);
335 if (ret)
336 return ret;
337 ret = krb5_decrypt_EncryptedData (context,
338 crypto,
339 KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
340 &dec_rep->kdc_rep.enc_part,
341 &data);
342 krb5_crypto_destroy(context, crypto);
344 if (subkey == NULL || ret) {
345 ret = krb5_crypto_init(context, key, 0, &crypto);
346 if (ret)
347 return ret;
348 ret = krb5_decrypt_EncryptedData (context,
349 crypto,
350 KRB5_KU_TGS_REP_ENC_PART_SESSION,
351 &dec_rep->kdc_rep.enc_part,
352 &data);
353 krb5_crypto_destroy(context, crypto);
355 if (ret)
356 return ret;
358 ret = decode_EncASRepPart(data.data,
359 data.length,
360 &dec_rep->enc_part,
361 &size);
362 if (ret)
363 ret = decode_EncTGSRepPart(data.data,
364 data.length,
365 &dec_rep->enc_part,
366 &size);
367 if (ret)
368 krb5_set_error_message(context, ret,
369 N_("Failed to decode encpart in ticket", ""));
370 krb5_data_free (&data);
371 return ret;
374 static krb5_error_code
375 get_cred_kdc(krb5_context context,
376 krb5_ccache id,
377 krb5_kdc_flags flags,
378 krb5_addresses *addresses,
379 krb5_creds *in_creds,
380 krb5_creds *krbtgt,
381 krb5_principal impersonate_principal,
382 Ticket *second_ticket,
383 krb5_creds *out_creds)
385 TGS_REQ req;
386 krb5_data enc;
387 krb5_data resp;
388 krb5_kdc_rep rep;
389 KRB_ERROR error;
390 krb5_error_code ret;
391 unsigned nonce;
392 krb5_keyblock *subkey = NULL;
393 size_t len;
394 Ticket second_ticket_data;
395 METHOD_DATA padata;
397 krb5_data_zero(&resp);
398 krb5_data_zero(&enc);
399 padata.val = NULL;
400 padata.len = 0;
402 krb5_generate_random_block(&nonce, sizeof(nonce));
403 nonce &= 0xffffffff;
405 if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
406 ret = decode_Ticket(in_creds->second_ticket.data,
407 in_creds->second_ticket.length,
408 &second_ticket_data, &len);
409 if(ret)
410 return ret;
411 second_ticket = &second_ticket_data;
415 if (impersonate_principal) {
416 krb5_crypto crypto;
417 PA_S4U2Self self;
418 krb5_data data;
419 void *buf;
420 size_t size;
422 self.name = impersonate_principal->name;
423 self.realm = impersonate_principal->realm;
424 self.auth = estrdup("Kerberos");
426 ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
427 if (ret) {
428 free(self.auth);
429 goto out;
432 ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
433 if (ret) {
434 free(self.auth);
435 krb5_data_free(&data);
436 goto out;
439 ret = krb5_create_checksum(context,
440 crypto,
441 KRB5_KU_OTHER_CKSUM,
443 data.data,
444 data.length,
445 &self.cksum);
446 krb5_crypto_destroy(context, crypto);
447 krb5_data_free(&data);
448 if (ret) {
449 free(self.auth);
450 goto out;
453 ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
454 free(self.auth);
455 free_Checksum(&self.cksum);
456 if (ret)
457 goto out;
458 if (len != size)
459 krb5_abortx(context, "internal asn1 error");
461 ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
462 if (ret)
463 goto out;
466 ret = init_tgs_req (context,
468 addresses,
469 flags,
470 second_ticket,
471 in_creds,
472 krbtgt,
473 nonce,
474 &padata,
475 &subkey,
476 &req);
477 if (ret)
478 goto out;
480 ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
481 if (ret)
482 goto out;
483 if(enc.length != len)
484 krb5_abortx(context, "internal error in ASN.1 encoder");
486 /* don't free addresses */
487 req.req_body.addresses = NULL;
488 free_TGS_REQ(&req);
491 * Send and receive
494 krb5_sendto_ctx stctx;
495 ret = krb5_sendto_ctx_alloc(context, &stctx);
496 if (ret)
497 return ret;
498 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
500 ret = krb5_sendto_context (context, stctx, &enc,
501 krbtgt->server->name.name_string.val[1],
502 &resp);
503 krb5_sendto_ctx_free(context, stctx);
505 if(ret)
506 goto out;
508 memset(&rep, 0, sizeof(rep));
509 if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
510 unsigned eflags = 0;
512 ret = krb5_copy_principal(context,
513 in_creds->client,
514 &out_creds->client);
515 if(ret)
516 goto out2;
517 ret = krb5_copy_principal(context,
518 in_creds->server,
519 &out_creds->server);
520 if(ret)
521 goto out2;
522 /* this should go someplace else */
523 out_creds->times.endtime = in_creds->times.endtime;
525 /* XXX should do better testing */
526 if (flags.b.constrained_delegation || impersonate_principal)
527 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
529 ret = _krb5_extract_ticket(context,
530 &rep,
531 out_creds,
532 &krbtgt->session,
533 NULL,
535 &krbtgt->addresses,
536 nonce,
537 eflags,
538 decrypt_tkt_with_subkey,
539 subkey);
540 out2:
541 krb5_free_kdc_rep(context, &rep);
542 } else if(krb5_rd_error(context, &resp, &error) == 0) {
543 ret = krb5_error_from_rd_error(context, &error, in_creds);
544 krb5_free_error_contents(context, &error);
545 } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
546 ret = KRB5KRB_AP_ERR_V4_REPLY;
547 krb5_clear_error_message(context);
548 } else {
549 ret = KRB5KRB_AP_ERR_MSG_TYPE;
550 krb5_clear_error_message(context);
553 out:
554 if (second_ticket == &second_ticket_data)
555 free_Ticket(&second_ticket_data);
556 free_METHOD_DATA(&padata);
557 krb5_data_free(&resp);
558 krb5_data_free(&enc);
559 if(subkey)
560 krb5_free_keyblock(context, subkey);
561 return ret;
566 * same as above, just get local addresses first if the krbtgt have
567 * them and the realm is not addressless
570 static krb5_error_code
571 get_cred_kdc_address(krb5_context context,
572 krb5_ccache id,
573 krb5_kdc_flags flags,
574 krb5_addresses *addrs,
575 krb5_creds *in_creds,
576 krb5_creds *krbtgt,
577 krb5_principal impersonate_principal,
578 Ticket *second_ticket,
579 krb5_creds *out_creds)
581 krb5_error_code ret;
582 krb5_addresses addresses = { 0, NULL };
585 * Inherit the address-ness of the krbtgt if the address is not
586 * specified.
589 if (addrs == NULL && krbtgt->addresses.len != 0) {
590 krb5_boolean noaddr;
592 krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
593 "no-addresses", FALSE, &noaddr);
595 if (!noaddr) {
596 krb5_get_all_client_addrs(context, &addresses);
597 /* XXX this sucks. */
598 addrs = &addresses;
599 if(addresses.len == 0)
600 addrs = NULL;
603 ret = get_cred_kdc(context, id, flags, addrs, in_creds,
604 krbtgt, impersonate_principal,
605 second_ticket, out_creds);
606 krb5_free_addresses(context, &addresses);
607 return ret;
610 krb5_error_code KRB5_LIB_FUNCTION
611 krb5_get_kdc_cred(krb5_context context,
612 krb5_ccache id,
613 krb5_kdc_flags flags,
614 krb5_addresses *addresses,
615 Ticket *second_ticket,
616 krb5_creds *in_creds,
617 krb5_creds **out_creds
620 krb5_error_code ret;
621 krb5_creds *krbtgt;
623 *out_creds = calloc(1, sizeof(**out_creds));
624 if(*out_creds == NULL) {
625 krb5_set_error_message(context, ENOMEM,
626 N_("malloc: out of memory", ""));
627 return ENOMEM;
629 ret = _krb5_get_krbtgt (context,
631 in_creds->server->realm,
632 &krbtgt);
633 if(ret) {
634 free(*out_creds);
635 *out_creds = NULL;
636 return ret;
638 ret = get_cred_kdc(context, id, flags, addresses,
639 in_creds, krbtgt, NULL, NULL, *out_creds);
640 krb5_free_creds (context, krbtgt);
641 if(ret) {
642 free(*out_creds);
643 *out_creds = NULL;
645 return ret;
648 static int
649 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
651 krb5_error_code ret;
652 char *str;
654 ret = krb5_unparse_name(context, p, &str);
655 if(ret) {
656 krb5_clear_error_message(context);
657 return code;
659 krb5_set_error_message(context, code,
660 N_("Matching credential (%s) not found", ""), str);
661 free(str);
662 return code;
665 static krb5_error_code
666 find_cred(krb5_context context,
667 krb5_ccache id,
668 krb5_principal server,
669 krb5_creds **tgts,
670 krb5_creds *out_creds)
672 krb5_error_code ret;
673 krb5_creds mcreds;
675 krb5_cc_clear_mcred(&mcreds);
676 mcreds.server = server;
677 ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
678 &mcreds, out_creds);
679 if(ret == 0)
680 return 0;
681 while(tgts && *tgts){
682 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
683 &mcreds, *tgts)){
684 ret = krb5_copy_creds_contents(context, *tgts, out_creds);
685 return ret;
687 tgts++;
689 return not_found(context, server, KRB5_CC_NOTFOUND);
692 static krb5_error_code
693 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
695 int i;
696 krb5_error_code ret;
697 krb5_creds **tmp = *tgts;
699 for(i = 0; tmp && tmp[i]; i++); /* XXX */
700 tmp = realloc(tmp, (i+2)*sizeof(*tmp));
701 if(tmp == NULL) {
702 krb5_set_error_message(context, ENOMEM,
703 N_("malloc: out of memory", ""));
704 return ENOMEM;
706 *tgts = tmp;
707 ret = krb5_copy_creds(context, tkt, &tmp[i]);
708 tmp[i+1] = NULL;
709 return ret;
713 get_cred(server)
714 creds = cc_get_cred(server)
715 if(creds) return creds
716 tgt = cc_get_cred(krbtgt/server_realm@any_realm)
717 if(tgt)
718 return get_cred_tgt(server, tgt)
719 if(client_realm == server_realm)
720 return NULL
721 tgt = get_cred(krbtgt/server_realm@client_realm)
722 while(tgt_inst != server_realm)
723 tgt = get_cred(krbtgt/server_realm@tgt_inst)
724 return get_cred_tgt(server, tgt)
727 static krb5_error_code
728 get_cred_kdc_capath(krb5_context context,
729 krb5_kdc_flags flags,
730 krb5_ccache ccache,
731 krb5_creds *in_creds,
732 krb5_principal impersonate_principal,
733 Ticket *second_ticket,
734 krb5_creds **out_creds,
735 krb5_creds ***ret_tgts)
737 krb5_error_code ret;
738 krb5_creds *tgt, tmp_creds;
739 krb5_const_realm client_realm, server_realm, try_realm;
740 int ok_as_delegate = 1;
742 *out_creds = NULL;
744 client_realm = krb5_principal_get_realm(context, in_creds->client);
745 server_realm = krb5_principal_get_realm(context, in_creds->server);
746 memset(&tmp_creds, 0, sizeof(tmp_creds));
747 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
748 if(ret)
749 return ret;
751 try_realm = krb5_config_get_string(context, NULL, "capaths",
752 client_realm, server_realm, NULL);
753 if (try_realm == NULL)
754 try_realm = client_realm;
756 ret = krb5_make_principal(context,
757 &tmp_creds.server,
758 try_realm,
759 KRB5_TGS_NAME,
760 server_realm,
761 NULL);
762 if(ret){
763 krb5_free_principal(context, tmp_creds.client);
764 return ret;
767 krb5_creds tgts;
769 ret = find_cred(context, ccache, tmp_creds.server,
770 *ret_tgts, &tgts);
771 if(ret == 0){
772 if (try_realm != client_realm)
773 ok_as_delegate = tgts.flags.b.ok_as_delegate;
775 *out_creds = calloc(1, sizeof(**out_creds));
776 if(*out_creds == NULL) {
777 ret = ENOMEM;
778 krb5_set_error_message(context, ret,
779 N_("malloc: out of memory", ""));
780 } else {
781 ret = get_cred_kdc_address(context, ccache, flags, NULL,
782 in_creds, &tgts,
783 impersonate_principal,
784 second_ticket,
785 *out_creds);
786 if (ret) {
787 free (*out_creds);
788 *out_creds = NULL;
789 } else if (ok_as_delegate == 0)
790 (*out_creds)->flags.b.ok_as_delegate = 0;
792 krb5_free_cred_contents(context, &tgts);
793 krb5_free_principal(context, tmp_creds.server);
794 krb5_free_principal(context, tmp_creds.client);
795 return ret;
798 if(krb5_realm_compare(context, in_creds->client, in_creds->server))
799 return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
801 /* XXX this can loop forever */
802 while(1){
803 heim_general_string tgt_inst;
805 ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
806 NULL, NULL, &tgt, ret_tgts);
807 if(ret) {
808 krb5_free_principal(context, tmp_creds.server);
809 krb5_free_principal(context, tmp_creds.client);
810 return ret;
813 * if either of the chain or the ok_as_delegate was stripped
814 * by the kdc, make sure we strip it too.
816 if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
817 ok_as_delegate = 0;
818 tgt->flags.b.ok_as_delegate = 0;
821 ret = add_cred(context, tgt, ret_tgts);
822 if(ret) {
823 krb5_free_principal(context, tmp_creds.server);
824 krb5_free_principal(context, tmp_creds.client);
825 return ret;
827 tgt_inst = tgt->server->name.name_string.val[1];
828 if(strcmp(tgt_inst, server_realm) == 0)
829 break;
830 krb5_free_principal(context, tmp_creds.server);
831 ret = krb5_make_principal(context, &tmp_creds.server,
832 tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
833 if(ret) {
834 krb5_free_principal(context, tmp_creds.server);
835 krb5_free_principal(context, tmp_creds.client);
836 return ret;
838 ret = krb5_free_creds(context, tgt);
839 if(ret) {
840 krb5_free_principal(context, tmp_creds.server);
841 krb5_free_principal(context, tmp_creds.client);
842 return ret;
846 krb5_free_principal(context, tmp_creds.server);
847 krb5_free_principal(context, tmp_creds.client);
848 *out_creds = calloc(1, sizeof(**out_creds));
849 if(*out_creds == NULL) {
850 ret = ENOMEM;
851 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
852 } else {
853 ret = get_cred_kdc_address (context, ccache, flags, NULL,
854 in_creds, tgt, impersonate_principal,
855 second_ticket, *out_creds);
856 if (ret) {
857 free (*out_creds);
858 *out_creds = NULL;
861 krb5_free_creds(context, tgt);
862 return ret;
865 static krb5_error_code
866 get_cred_kdc_referral(krb5_context context,
867 krb5_kdc_flags flags,
868 krb5_ccache ccache,
869 krb5_creds *in_creds,
870 krb5_principal impersonate_principal,
871 Ticket *second_ticket,
872 krb5_creds **out_creds,
873 krb5_creds ***ret_tgts)
875 krb5_const_realm client_realm;
876 krb5_error_code ret;
877 krb5_creds tgt, referral, ticket;
878 int loop = 0;
879 int ok_as_delegate = 1;
881 if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
882 krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
883 N_("Name too short to do referals, skipping", ""));
884 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
887 memset(&tgt, 0, sizeof(tgt));
888 memset(&ticket, 0, sizeof(ticket));
890 flags.b.canonicalize = 1;
892 *out_creds = NULL;
894 client_realm = krb5_principal_get_realm(context, in_creds->client);
896 /* find tgt for the clients base realm */
898 krb5_principal tgtname;
900 ret = krb5_make_principal(context, &tgtname,
901 client_realm,
902 KRB5_TGS_NAME,
903 client_realm,
904 NULL);
905 if(ret)
906 return ret;
908 ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
909 krb5_free_principal(context, tgtname);
910 if (ret)
911 return ret;
914 referral = *in_creds;
915 ret = krb5_copy_principal(context, in_creds->server, &referral.server);
916 if (ret) {
917 krb5_free_cred_contents(context, &tgt);
918 return ret;
920 ret = krb5_principal_set_realm(context, referral.server, client_realm);
921 if (ret) {
922 krb5_free_cred_contents(context, &tgt);
923 krb5_free_principal(context, referral.server);
924 return ret;
927 while (loop++ < 17) {
928 krb5_creds **tickets;
929 krb5_creds mcreds;
930 char *referral_realm;
932 /* Use cache if we are not doing impersonation or contrainte deleg */
933 if (impersonate_principal == NULL || flags.b.constrained_delegation) {
934 krb5_cc_clear_mcred(&mcreds);
935 mcreds.server = referral.server;
936 ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
937 } else
938 ret = EINVAL;
940 if (ret) {
941 ret = get_cred_kdc_address (context, ccache, flags, NULL,
942 &referral, &tgt, impersonate_principal,
943 second_ticket, &ticket);
944 if (ret)
945 goto out;
948 /* Did we get the right ticket ? */
949 if (krb5_principal_compare_any_realm(context,
950 referral.server,
951 ticket.server))
952 break;
954 if (ticket.server->name.name_string.len != 2 &&
955 strcmp(ticket.server->name.name_string.val[0], KRB5_TGS_NAME) != 0)
957 krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
958 N_("Got back an non krbtgt "
959 "ticket referrals", ""));
960 krb5_free_cred_contents(context, &ticket);
961 return KRB5KRB_AP_ERR_NOT_US;
964 referral_realm = ticket.server->name.name_string.val[1];
966 /* check that there are no referrals loops */
967 tickets = *ret_tgts;
969 krb5_cc_clear_mcred(&mcreds);
970 mcreds.server = ticket.server;
972 while(tickets && *tickets){
973 if(krb5_compare_creds(context,
974 KRB5_TC_DONT_MATCH_REALM,
975 &mcreds,
976 *tickets))
978 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
979 N_("Referral from %s "
980 "loops back to realm %s", ""),
981 tgt.server->realm,
982 referral_realm);
983 krb5_free_cred_contents(context, &ticket);
984 return KRB5_GET_IN_TKT_LOOP;
986 tickets++;
990 * if either of the chain or the ok_as_delegate was stripped
991 * by the kdc, make sure we strip it too.
994 if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
995 ok_as_delegate = 0;
996 ticket.flags.b.ok_as_delegate = 0;
999 ret = add_cred(context, &ticket, ret_tgts);
1000 if (ret) {
1001 krb5_free_cred_contents(context, &ticket);
1002 goto out;
1005 /* try realm in the referral */
1006 ret = krb5_principal_set_realm(context,
1007 referral.server,
1008 referral_realm);
1009 krb5_free_cred_contents(context, &tgt);
1010 tgt = ticket;
1011 memset(&ticket, 0, sizeof(ticket));
1012 if (ret)
1013 goto out;
1016 ret = krb5_copy_creds(context, &ticket, out_creds);
1018 out:
1019 krb5_free_principal(context, referral.server);
1020 krb5_free_cred_contents(context, &tgt);
1021 return ret;
1026 * Glue function between referrals version and old client chasing
1027 * codebase.
1030 krb5_error_code
1031 _krb5_get_cred_kdc_any(krb5_context context,
1032 krb5_kdc_flags flags,
1033 krb5_ccache ccache,
1034 krb5_creds *in_creds,
1035 krb5_principal impersonate_principal,
1036 Ticket *second_ticket,
1037 krb5_creds **out_creds,
1038 krb5_creds ***ret_tgts)
1040 krb5_error_code ret;
1042 ret = get_cred_kdc_referral(context,
1043 flags,
1044 ccache,
1045 in_creds,
1046 impersonate_principal,
1047 second_ticket,
1048 out_creds,
1049 ret_tgts);
1050 if (ret == 0 || flags.b.canonicalize)
1051 return ret;
1052 return get_cred_kdc_capath(context,
1053 flags,
1054 ccache,
1055 in_creds,
1056 impersonate_principal,
1057 second_ticket,
1058 out_creds,
1059 ret_tgts);
1063 krb5_error_code KRB5_LIB_FUNCTION
1064 krb5_get_credentials_with_flags(krb5_context context,
1065 krb5_flags options,
1066 krb5_kdc_flags flags,
1067 krb5_ccache ccache,
1068 krb5_creds *in_creds,
1069 krb5_creds **out_creds)
1071 krb5_error_code ret;
1072 krb5_creds **tgts;
1073 krb5_creds *res_creds;
1074 int i;
1076 if (in_creds->session.keytype) {
1077 ret = krb5_enctype_valid(context, in_creds->session.keytype);
1078 if (ret)
1079 return ret;
1082 *out_creds = NULL;
1083 res_creds = calloc(1, sizeof(*res_creds));
1084 if (res_creds == NULL) {
1085 krb5_set_error_message(context, ENOMEM,
1086 N_("malloc: out of memory", ""));
1087 return ENOMEM;
1090 if (in_creds->session.keytype)
1091 options |= KRB5_TC_MATCH_KEYTYPE;
1094 * If we got a credential, check if credential is expired before
1095 * returning it.
1097 ret = krb5_cc_retrieve_cred(context,
1098 ccache,
1099 in_creds->session.keytype ?
1100 KRB5_TC_MATCH_KEYTYPE : 0,
1101 in_creds, res_creds);
1103 * If we got a credential, check if credential is expired before
1104 * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1106 if (ret == 0) {
1107 krb5_timestamp timeret;
1109 /* If expired ok, don't bother checking */
1110 if(options & KRB5_GC_EXPIRED_OK) {
1111 *out_creds = res_creds;
1112 return 0;
1115 krb5_timeofday(context, &timeret);
1116 if(res_creds->times.endtime > timeret) {
1117 *out_creds = res_creds;
1118 return 0;
1120 if(options & KRB5_GC_CACHED)
1121 krb5_cc_remove_cred(context, ccache, 0, res_creds);
1123 } else if(ret != KRB5_CC_END) {
1124 free(res_creds);
1125 return ret;
1127 free(res_creds);
1128 if(options & KRB5_GC_CACHED)
1129 return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1131 if(options & KRB5_GC_USER_USER)
1132 flags.b.enc_tkt_in_skey = 1;
1133 if (flags.b.enc_tkt_in_skey)
1134 options |= KRB5_GC_NO_STORE;
1136 tgts = NULL;
1137 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1138 in_creds, NULL, NULL, out_creds, &tgts);
1139 for(i = 0; tgts && tgts[i]; i++) {
1140 krb5_cc_store_cred(context, ccache, tgts[i]);
1141 krb5_free_creds(context, tgts[i]);
1143 free(tgts);
1144 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1145 krb5_cc_store_cred(context, ccache, *out_creds);
1146 return ret;
1149 krb5_error_code KRB5_LIB_FUNCTION
1150 krb5_get_credentials(krb5_context context,
1151 krb5_flags options,
1152 krb5_ccache ccache,
1153 krb5_creds *in_creds,
1154 krb5_creds **out_creds)
1156 krb5_kdc_flags flags;
1157 flags.i = 0;
1158 return krb5_get_credentials_with_flags(context, options, flags,
1159 ccache, in_creds, out_creds);
1162 struct krb5_get_creds_opt_data {
1163 krb5_principal self;
1164 krb5_flags options;
1165 krb5_enctype enctype;
1166 Ticket *ticket;
1170 krb5_error_code KRB5_LIB_FUNCTION
1171 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1173 *opt = calloc(1, sizeof(**opt));
1174 if (*opt == NULL) {
1175 krb5_set_error_message(context, ENOMEM,
1176 N_("malloc: out of memory", ""));
1177 return ENOMEM;
1179 return 0;
1182 void KRB5_LIB_FUNCTION
1183 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1185 if (opt->self)
1186 krb5_free_principal(context, opt->self);
1187 if (opt->ticket) {
1188 free_Ticket(opt->ticket);
1189 free(opt->ticket);
1191 memset(opt, 0, sizeof(*opt));
1192 free(opt);
1195 void KRB5_LIB_FUNCTION
1196 krb5_get_creds_opt_set_options(krb5_context context,
1197 krb5_get_creds_opt opt,
1198 krb5_flags options)
1200 opt->options = options;
1203 void KRB5_LIB_FUNCTION
1204 krb5_get_creds_opt_add_options(krb5_context context,
1205 krb5_get_creds_opt opt,
1206 krb5_flags options)
1208 opt->options |= options;
1211 void KRB5_LIB_FUNCTION
1212 krb5_get_creds_opt_set_enctype(krb5_context context,
1213 krb5_get_creds_opt opt,
1214 krb5_enctype enctype)
1216 opt->enctype = enctype;
1219 krb5_error_code KRB5_LIB_FUNCTION
1220 krb5_get_creds_opt_set_impersonate(krb5_context context,
1221 krb5_get_creds_opt opt,
1222 krb5_const_principal self)
1224 if (opt->self)
1225 krb5_free_principal(context, opt->self);
1226 return krb5_copy_principal(context, self, &opt->self);
1229 krb5_error_code KRB5_LIB_FUNCTION
1230 krb5_get_creds_opt_set_ticket(krb5_context context,
1231 krb5_get_creds_opt opt,
1232 const Ticket *ticket)
1234 if (opt->ticket) {
1235 free_Ticket(opt->ticket);
1236 free(opt->ticket);
1237 opt->ticket = NULL;
1239 if (ticket) {
1240 krb5_error_code ret;
1242 opt->ticket = malloc(sizeof(*ticket));
1243 if (opt->ticket == NULL) {
1244 krb5_set_error_message(context, ENOMEM,
1245 N_("malloc: out of memory", ""));
1246 return ENOMEM;
1248 ret = copy_Ticket(ticket, opt->ticket);
1249 if (ret) {
1250 free(opt->ticket);
1251 opt->ticket = NULL;
1252 krb5_set_error_message(context, ret,
1253 N_("malloc: out of memory", ""));
1254 return ret;
1257 return 0;
1262 krb5_error_code KRB5_LIB_FUNCTION
1263 krb5_get_creds(krb5_context context,
1264 krb5_get_creds_opt opt,
1265 krb5_ccache ccache,
1266 krb5_const_principal inprinc,
1267 krb5_creds **out_creds)
1269 krb5_kdc_flags flags;
1270 krb5_flags options;
1271 krb5_creds in_creds;
1272 krb5_error_code ret;
1273 krb5_creds **tgts;
1274 krb5_creds *res_creds;
1275 int i;
1277 if (opt && opt->enctype) {
1278 ret = krb5_enctype_valid(context, opt->enctype);
1279 if (ret)
1280 return ret;
1283 memset(&in_creds, 0, sizeof(in_creds));
1284 in_creds.server = rk_UNCONST(inprinc);
1286 ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1287 if (ret)
1288 return ret;
1290 if (opt)
1291 options = opt->options;
1292 else
1293 options = 0;
1294 flags.i = 0;
1296 *out_creds = NULL;
1297 res_creds = calloc(1, sizeof(*res_creds));
1298 if (res_creds == NULL) {
1299 krb5_free_principal(context, in_creds.client);
1300 krb5_set_error_message(context, ENOMEM,
1301 N_("malloc: out of memory", ""));
1302 return ENOMEM;
1305 if (opt && opt->enctype) {
1306 in_creds.session.keytype = opt->enctype;
1307 options |= KRB5_TC_MATCH_KEYTYPE;
1311 * If we got a credential, check if credential is expired before
1312 * returning it.
1314 ret = krb5_cc_retrieve_cred(context,
1315 ccache,
1316 options & KRB5_TC_MATCH_KEYTYPE,
1317 &in_creds, res_creds);
1319 * If we got a credential, check if credential is expired before
1320 * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
1322 if (ret == 0) {
1323 krb5_timestamp timeret;
1325 /* If expired ok, don't bother checking */
1326 if(options & KRB5_GC_EXPIRED_OK) {
1327 *out_creds = res_creds;
1328 krb5_free_principal(context, in_creds.client);
1329 return 0;
1332 krb5_timeofday(context, &timeret);
1333 if(res_creds->times.endtime > timeret) {
1334 *out_creds = res_creds;
1335 krb5_free_principal(context, in_creds.client);
1336 return 0;
1338 if(options & KRB5_GC_CACHED)
1339 krb5_cc_remove_cred(context, ccache, 0, res_creds);
1341 } else if(ret != KRB5_CC_END) {
1342 free(res_creds);
1343 krb5_free_principal(context, in_creds.client);
1344 return ret;
1346 free(res_creds);
1347 if(options & KRB5_GC_CACHED) {
1348 krb5_free_principal(context, in_creds.client);
1349 return not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
1351 if(options & KRB5_GC_USER_USER) {
1352 flags.b.enc_tkt_in_skey = 1;
1353 options |= KRB5_GC_NO_STORE;
1355 if (options & KRB5_GC_FORWARDABLE)
1356 flags.b.forwardable = 1;
1357 if (options & KRB5_GC_NO_TRANSIT_CHECK)
1358 flags.b.disable_transited_check = 1;
1359 if (options & KRB5_GC_CONSTRAINED_DELEGATION) {
1360 flags.b.request_anonymous = 1; /* XXX ARGH confusion */
1361 flags.b.constrained_delegation = 1;
1363 if (options & KRB5_GC_CANONICALIZE)
1364 flags.b.canonicalize = 1;
1366 tgts = NULL;
1367 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1368 &in_creds, opt->self, opt->ticket,
1369 out_creds, &tgts);
1370 krb5_free_principal(context, in_creds.client);
1371 for(i = 0; tgts && tgts[i]; i++) {
1372 krb5_cc_store_cred(context, ccache, tgts[i]);
1373 krb5_free_creds(context, tgts[i]);
1375 free(tgts);
1376 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1377 krb5_cc_store_cred(context, ccache, *out_creds);
1378 return ret;
1385 krb5_error_code KRB5_LIB_FUNCTION
1386 krb5_get_renewed_creds(krb5_context context,
1387 krb5_creds *creds,
1388 krb5_const_principal client,
1389 krb5_ccache ccache,
1390 const char *in_tkt_service)
1392 krb5_error_code ret;
1393 krb5_kdc_flags flags;
1394 krb5_creds in, *template, *out = NULL;
1396 memset(&in, 0, sizeof(in));
1397 memset(creds, 0, sizeof(*creds));
1399 ret = krb5_copy_principal(context, client, &in.client);
1400 if (ret)
1401 return ret;
1403 if (in_tkt_service) {
1404 ret = krb5_parse_name(context, in_tkt_service, &in.server);
1405 if (ret) {
1406 krb5_free_principal(context, in.client);
1407 return ret;
1409 } else {
1410 const char *realm = krb5_principal_get_realm(context, client);
1412 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1413 realm, NULL);
1414 if (ret) {
1415 krb5_free_principal(context, in.client);
1416 return ret;
1420 flags.i = 0;
1421 flags.b.renewable = flags.b.renew = 1;
1424 * Get template from old credential cache for the same entry, if
1425 * this failes, no worries.
1427 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1428 if (ret == 0) {
1429 flags.b.forwardable = template->flags.b.forwardable;
1430 flags.b.proxiable = template->flags.b.proxiable;
1431 krb5_free_creds (context, template);
1434 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1435 krb5_free_principal(context, in.client);
1436 krb5_free_principal(context, in.server);
1437 if (ret)
1438 return ret;
1440 ret = krb5_copy_creds_contents(context, out, creds);
1441 krb5_free_creds(context, out);
1443 return ret;