1 /* $NetBSD: get_cred.c,v 1.4 2023/06/19 21:41:44 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
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.
20 *
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.
24 *
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.
36 */
37
38 #include "krb5_locl.h"
39 #include <assert.h>
40
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 ***);
45
46 /*
47 * Take the `body' and encode it into `padata' using the credentials
48 * in `creds'.
49 */
50
51 static krb5_error_code
make_pa_tgs_req(krb5_context context,krb5_auth_context ac,KDC_REQ_BODY * body,PA_DATA * padata,krb5_creds * creds)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)
57 {
58 u_char *buf;
59 size_t buf_size;
60 size_t len = 0;
61 krb5_data in_data;
62 krb5_error_code ret;
63
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");
69
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;
82 }
83
84 /*
85 * Set the `enc-authorization-data' in `req_body' based on `authdata'
86 */
87
88 static krb5_error_code
set_auth_data(krb5_context context,KDC_REQ_BODY * req_body,krb5_authdata * authdata,krb5_keyblock * subkey)89 set_auth_data (krb5_context context,
90 KDC_REQ_BODY *req_body,
91 krb5_authdata *authdata,
92 krb5_keyblock *subkey)
93 {
94 if(authdata->len) {
95 size_t len = 0, buf_size;
96 unsigned char *buf;
97 krb5_crypto crypto;
98 krb5_error_code ret;
99
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");
106
107 ALLOC(req_body->enc_authorization_data, 1);
108 if (req_body->enc_authorization_data == NULL) {
109 free (buf);
110 return krb5_enomem(context);
111 }
112 ret = krb5_crypto_init(context, subkey, 0, &crypto);
113 if (ret) {
114 free (buf);
115 free (req_body->enc_authorization_data);
116 req_body->enc_authorization_data = NULL;
117 return ret;
118 }
119 krb5_encrypt_EncryptedData(context,
120 crypto,
121 KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
122 buf,
123 len,
124 0,
125 req_body->enc_authorization_data);
126 free (buf);
127 krb5_crypto_destroy(context, crypto);
128 } else {
129 req_body->enc_authorization_data = NULL;
130 }
131 return 0;
132 }
133
134 /*
135 * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
136 * (if not-NULL), `in_creds', `krbtgt', and returning the generated
137 * subkey in `subkey'.
138 */
139
140 static krb5_error_code
init_tgs_req(krb5_context context,krb5_ccache ccache,krb5_addresses * addresses,krb5_kdc_flags flags,Ticket * second_ticket,krb5_creds * in_creds,krb5_creds * krbtgt,unsigned nonce,const METHOD_DATA * padata,krb5_keyblock ** subkey,TGS_REQ * t)141 init_tgs_req (krb5_context context,
142 krb5_ccache ccache,
143 krb5_addresses *addresses,
144 krb5_kdc_flags flags,
145 Ticket *second_ticket,
146 krb5_creds *in_creds,
147 krb5_creds *krbtgt,
148 unsigned nonce,
149 const METHOD_DATA *padata,
150 krb5_keyblock **subkey,
151 TGS_REQ *t)
152 {
153 krb5_auth_context ac = NULL;
154 krb5_error_code ret = 0;
155
156 memset(t, 0, sizeof(*t));
157 t->pvno = 5;
158 t->msg_type = krb_tgs_req;
159 if (in_creds->session.keytype) {
160 ALLOC_SEQ(&t->req_body.etype, 1);
161 if(t->req_body.etype.val == NULL) {
162 ret = krb5_enomem(context);
163 goto fail;
164 }
165 t->req_body.etype.val[0] = in_creds->session.keytype;
166 } else {
167 ret = _krb5_init_etype(context,
168 KRB5_PDU_TGS_REQUEST,
169 &t->req_body.etype.len,
170 &t->req_body.etype.val,
171 NULL);
172 }
173 if (ret)
174 goto fail;
175 t->req_body.addresses = addresses;
176 t->req_body.kdc_options = flags.b;
177 t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
178 t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
179 t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
180 ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
181 if (ret)
182 goto fail;
183 ALLOC(t->req_body.sname, 1);
184 if (t->req_body.sname == NULL) {
185 ret = krb5_enomem(context);
186 goto fail;
187 }
188
189 /* some versions of some code might require that the client be
190 present in TGS-REQs, but this is clearly against the spec */
191
192 ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
193 if (ret)
194 goto fail;
195
196 if (krbtgt->times.starttime) {
197 ALLOC(t->req_body.from, 1);
198 if(t->req_body.from == NULL){
199 ret = krb5_enomem(context);
200 goto fail;
201 }
202 *t->req_body.from = in_creds->times.starttime;
203 }
204
205 /* req_body.till should be NULL if there is no endtime specified,
206 but old MIT code (like DCE secd) doesn't like that */
207 ALLOC(t->req_body.till, 1);
208 if(t->req_body.till == NULL){
209 ret = krb5_enomem(context);
210 goto fail;
211 }
212 *t->req_body.till = in_creds->times.endtime;
213
214 if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
215 ALLOC(t->req_body.rtime, 1);
216 if(t->req_body.rtime == NULL){
217 ret = krb5_enomem(context);
218 goto fail;
219 }
220 *t->req_body.rtime = in_creds->times.renew_till;
221 }
222
223 t->req_body.nonce = nonce;
224 if(second_ticket){
225 ALLOC(t->req_body.additional_tickets, 1);
226 if (t->req_body.additional_tickets == NULL) {
227 ret = krb5_enomem(context);
228 goto fail;
229 }
230 ALLOC_SEQ(t->req_body.additional_tickets, 1);
231 if (t->req_body.additional_tickets->val == NULL) {
232 ret = krb5_enomem(context);
233 goto fail;
234 }
235 ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
236 if (ret)
237 goto fail;
238 }
239 ALLOC(t->padata, 1);
240 if (t->padata == NULL) {
241 ret = krb5_enomem(context);
242 goto fail;
243 }
244 ALLOC_SEQ(t->padata, 1 + padata->len);
245 if (t->padata->val == NULL) {
246 ret = krb5_enomem(context);
247 goto fail;
248 }
249 {
250 size_t i;
251 for (i = 0; i < padata->len; i++) {
252 ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]);
253 if (ret) {
254 krb5_set_error_message(context, ret,
255 N_("malloc: out of memory", ""));
256 goto fail;
257 }
258 }
259 }
260
261 ret = krb5_auth_con_init(context, &ac);
262 if(ret)
263 goto fail;
264
265 ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
266 if (ret)
267 goto fail;
268
269 ret = set_auth_data (context, &t->req_body, &in_creds->authdata,
270 ac->local_subkey);
271 if (ret)
272 goto fail;
273
274 ret = make_pa_tgs_req(context,
275 ac,
276 &t->req_body,
277 &t->padata->val[0],
278 krbtgt);
279 if(ret)
280 goto fail;
281
282 ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
283 if (ret)
284 goto fail;
285
286 fail:
287 if (ac)
288 krb5_auth_con_free(context, ac);
289 if (ret) {
290 t->req_body.addresses = NULL;
291 free_TGS_REQ (t);
292 }
293 return ret;
294 }
295
296 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_krbtgt(krb5_context context,krb5_ccache id,krb5_realm realm,krb5_creds ** cred)297 _krb5_get_krbtgt(krb5_context context,
298 krb5_ccache id,
299 krb5_realm realm,
300 krb5_creds **cred)
301 {
302 krb5_error_code ret;
303 krb5_creds tmp_cred;
304
305 memset(&tmp_cred, 0, sizeof(tmp_cred));
306
307 ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
308 if (ret)
309 return ret;
310
311 ret = krb5_make_principal(context,
312 &tmp_cred.server,
313 realm,
314 KRB5_TGS_NAME,
315 realm,
316 NULL);
317 if(ret) {
318 krb5_free_principal(context, tmp_cred.client);
319 return ret;
320 }
321 /*
322 * The forwardable TGT might not be the start TGT, in which case, it is
323 * generally, but not always already cached. Just in case, get it again if
324 * lost.
325 */
326 ret = krb5_get_credentials(context,
327 0,
328 id,
329 &tmp_cred,
330 cred);
331 krb5_free_principal(context, tmp_cred.client);
332 krb5_free_principal(context, tmp_cred.server);
333 if(ret)
334 return ret;
335 return 0;
336 }
337
338 /* DCE compatible decrypt proc */
339 static krb5_error_code KRB5_CALLCONV
decrypt_tkt_with_subkey(krb5_context context,krb5_keyblock * key,krb5_key_usage usage,krb5_const_pointer skey,krb5_kdc_rep * dec_rep)340 decrypt_tkt_with_subkey (krb5_context context,
341 krb5_keyblock *key,
342 krb5_key_usage usage,
343 krb5_const_pointer skey,
344 krb5_kdc_rep *dec_rep)
345 {
346 const krb5_keyblock *subkey = skey;
347 krb5_error_code ret = 0;
348 krb5_data data;
349 size_t size;
350 krb5_crypto crypto;
351
352 assert(usage == 0);
353
354 krb5_data_zero(&data);
355
356 /*
357 * start out with trying with subkey if we have one
358 */
359 if (subkey) {
360 ret = krb5_crypto_init(context, subkey, 0, &crypto);
361 if (ret)
362 return ret;
363 ret = krb5_decrypt_EncryptedData (context,
364 crypto,
365 KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
366 &dec_rep->kdc_rep.enc_part,
367 &data);
368 /*
369 * If the is Windows 2000 DC, we need to retry with key usage
370 * 8 when doing ARCFOUR.
371 */
372 if (ret && subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
373 ret = krb5_decrypt_EncryptedData(context,
374 crypto,
375 8,
376 &dec_rep->kdc_rep.enc_part,
377 &data);
378 }
379 krb5_crypto_destroy(context, crypto);
380 }
381 if (subkey == NULL || ret) {
382 ret = krb5_crypto_init(context, key, 0, &crypto);
383 if (ret)
384 return ret;
385 ret = krb5_decrypt_EncryptedData (context,
386 crypto,
387 KRB5_KU_TGS_REP_ENC_PART_SESSION,
388 &dec_rep->kdc_rep.enc_part,
389 &data);
390 krb5_crypto_destroy(context, crypto);
391 }
392 if (ret)
393 return ret;
394
395 ret = decode_EncASRepPart(data.data,
396 data.length,
397 &dec_rep->enc_part,
398 &size);
399 if (ret)
400 ret = decode_EncTGSRepPart(data.data,
401 data.length,
402 &dec_rep->enc_part,
403 &size);
404 if (ret)
405 krb5_set_error_message(context, ret,
406 N_("Failed to decode encpart in ticket", ""));
407 krb5_data_free (&data);
408 return ret;
409 }
410
411 static krb5_error_code
get_cred_kdc(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_addresses * addresses,krb5_creds * in_creds,krb5_creds * krbtgt,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds * out_creds)412 get_cred_kdc(krb5_context context,
413 krb5_ccache id,
414 krb5_kdc_flags flags,
415 krb5_addresses *addresses,
416 krb5_creds *in_creds,
417 krb5_creds *krbtgt,
418 krb5_principal impersonate_principal,
419 Ticket *second_ticket,
420 krb5_creds *out_creds)
421 {
422 TGS_REQ req;
423 krb5_data enc;
424 krb5_data resp;
425 krb5_kdc_rep rep = {0};
426 KRB_ERROR error;
427 krb5_error_code ret;
428 unsigned nonce;
429 krb5_keyblock *subkey = NULL;
430 size_t len = 0;
431 Ticket second_ticket_data;
432 METHOD_DATA padata;
433
434 krb5_data_zero(&resp);
435 krb5_data_zero(&enc);
436 padata.val = NULL;
437 padata.len = 0;
438
439 krb5_generate_random_block(&nonce, sizeof(nonce));
440 nonce &= 0xffffffff;
441
442 if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
443 ret = decode_Ticket(in_creds->second_ticket.data,
444 in_creds->second_ticket.length,
445 &second_ticket_data, &len);
446 if(ret)
447 return ret;
448 second_ticket = &second_ticket_data;
449 }
450
451
452 if (impersonate_principal) {
453 krb5_crypto crypto;
454 PA_S4U2Self self;
455 krb5_data data;
456 void *buf;
457 size_t size = 0;
458
459 self.name = impersonate_principal->name;
460 self.realm = impersonate_principal->realm;
461 self.auth = estrdup("Kerberos");
462
463 ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
464 if (ret) {
465 free(self.auth);
466 goto out;
467 }
468
469 ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
470 if (ret) {
471 free(self.auth);
472 krb5_data_free(&data);
473 goto out;
474 }
475
476 ret = krb5_create_checksum(context,
477 crypto,
478 KRB5_KU_OTHER_CKSUM,
479 0,
480 data.data,
481 data.length,
482 &self.cksum);
483 krb5_crypto_destroy(context, crypto);
484 krb5_data_free(&data);
485 if (ret) {
486 free(self.auth);
487 goto out;
488 }
489
490 ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
491 free(self.auth);
492 free_Checksum(&self.cksum);
493 if (ret)
494 goto out;
495 if (len != size)
496 krb5_abortx(context, "internal asn1 error");
497
498 ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
499 if (ret)
500 goto out;
501 }
502
503 ret = init_tgs_req (context,
504 id,
505 addresses,
506 flags,
507 second_ticket,
508 in_creds,
509 krbtgt,
510 nonce,
511 &padata,
512 &subkey,
513 &req);
514 if (ret)
515 goto out;
516
517 ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
518 if (ret)
519 goto out;
520 if(enc.length != len)
521 krb5_abortx(context, "internal error in ASN.1 encoder");
522
523 /* don't free addresses */
524 req.req_body.addresses = NULL;
525 free_TGS_REQ(&req);
526
527 /*
528 * Send and receive
529 */
530 {
531 krb5_sendto_ctx stctx;
532 ret = krb5_sendto_ctx_alloc(context, &stctx);
533 if (ret)
534 return ret;
535 krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
536
537 ret = krb5_sendto_context (context, stctx, &enc,
538 krbtgt->server->name.name_string.val[1],
539 &resp);
540 krb5_sendto_ctx_free(context, stctx);
541 }
542 if(ret)
543 goto out;
544
545 if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
546 unsigned eflags = 0;
547
548 ret = krb5_copy_principal(context,
549 in_creds->client,
550 &out_creds->client);
551 if(ret)
552 goto out2;
553 ret = krb5_copy_principal(context,
554 in_creds->server,
555 &out_creds->server);
556 if(ret)
557 goto out2;
558 /* this should go someplace else */
559 out_creds->times.endtime = in_creds->times.endtime;
560
561 /* XXX should do better testing */
562 if (flags.b.cname_in_addl_tkt || impersonate_principal)
563 eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
564 if (flags.b.request_anonymous)
565 eflags |= EXTRACT_TICKET_MATCH_ANON;
566
567 ret = _krb5_extract_ticket(context,
568 &rep,
569 out_creds,
570 &krbtgt->session,
571 NULL,
572 0,
573 &krbtgt->addresses,
574 nonce,
575 eflags,
576 NULL,
577 decrypt_tkt_with_subkey,
578 subkey);
579 out2:
580 krb5_free_kdc_rep(context, &rep);
581 } else if(krb5_rd_error(context, &resp, &error) == 0) {
582 ret = krb5_error_from_rd_error(context, &error, in_creds);
583 krb5_free_error_contents(context, &error);
584 } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
585 ret = KRB5KRB_AP_ERR_V4_REPLY;
586 krb5_clear_error_message(context);
587 } else {
588 ret = KRB5KRB_AP_ERR_MSG_TYPE;
589 krb5_clear_error_message(context);
590 }
591
592 out:
593 if (second_ticket == &second_ticket_data)
594 free_Ticket(&second_ticket_data);
595 free_METHOD_DATA(&padata);
596 krb5_data_free(&resp);
597 krb5_data_free(&enc);
598 if(subkey)
599 krb5_free_keyblock(context, subkey);
600 return ret;
601
602 }
603
604 /*
605 * same as above, just get local addresses first if the krbtgt have
606 * them and the realm is not addressless
607 */
608
609 static krb5_error_code
get_cred_kdc_address(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_addresses * addrs,krb5_creds * in_creds,krb5_creds * krbtgt,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds * out_creds)610 get_cred_kdc_address(krb5_context context,
611 krb5_ccache id,
612 krb5_kdc_flags flags,
613 krb5_addresses *addrs,
614 krb5_creds *in_creds,
615 krb5_creds *krbtgt,
616 krb5_principal impersonate_principal,
617 Ticket *second_ticket,
618 krb5_creds *out_creds)
619 {
620 krb5_error_code ret;
621 krb5_addresses addresses = { 0, NULL };
622
623 /*
624 * Inherit the address-ness of the krbtgt if the address is not
625 * specified.
626 */
627
628 if (addrs == NULL && krbtgt->addresses.len != 0) {
629 krb5_boolean noaddr;
630
631 krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
632 "no-addresses", FALSE, &noaddr);
633
634 if (!noaddr) {
635 krb5_get_all_client_addrs(context, &addresses);
636 /* XXX this sucks. */
637 addrs = &addresses;
638 if(addresses.len == 0)
639 addrs = NULL;
640 }
641 }
642 ret = get_cred_kdc(context, id, flags, addrs, in_creds,
643 krbtgt, impersonate_principal,
644 second_ticket, out_creds);
645 krb5_free_addresses(context, &addresses);
646 return ret;
647 }
648
649 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_kdc_cred(krb5_context context,krb5_ccache id,krb5_kdc_flags flags,krb5_addresses * addresses,Ticket * second_ticket,krb5_creds * in_creds,krb5_creds ** out_creds)650 krb5_get_kdc_cred(krb5_context context,
651 krb5_ccache id,
652 krb5_kdc_flags flags,
653 krb5_addresses *addresses,
654 Ticket *second_ticket,
655 krb5_creds *in_creds,
656 krb5_creds **out_creds
657 )
658 {
659 krb5_error_code ret;
660 krb5_creds *krbtgt;
661
662 *out_creds = calloc(1, sizeof(**out_creds));
663 if(*out_creds == NULL)
664 return krb5_enomem(context);
665 ret = _krb5_get_krbtgt (context,
666 id,
667 in_creds->server->realm,
668 &krbtgt);
669 if(ret) {
670 free(*out_creds);
671 *out_creds = NULL;
672 return ret;
673 }
674 ret = get_cred_kdc(context, id, flags, addresses,
675 in_creds, krbtgt, NULL, NULL, *out_creds);
676 krb5_free_creds (context, krbtgt);
677 if(ret) {
678 free(*out_creds);
679 *out_creds = NULL;
680 }
681 return ret;
682 }
683
684 static int
not_found(krb5_context context,krb5_const_principal p,krb5_error_code code)685 not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
686 {
687 krb5_error_code ret;
688 const char *err;
689 char *str;
690
691 err = krb5_get_error_message(context, code);
692 ret = krb5_unparse_name(context, p, &str);
693 if(ret) {
694 krb5_clear_error_message(context);
695 return code;
696 }
697 krb5_set_error_message(context, code, N_("%s (%s)", ""), err, str);
698 free(str);
699 return code;
700 }
701
702 static krb5_error_code
find_cred(krb5_context context,krb5_ccache id,krb5_principal server,krb5_creds ** tgts,krb5_creds * out_creds)703 find_cred(krb5_context context,
704 krb5_ccache id,
705 krb5_principal server,
706 krb5_creds **tgts,
707 krb5_creds *out_creds)
708 {
709 krb5_error_code ret;
710 krb5_creds mcreds;
711
712 krb5_cc_clear_mcred(&mcreds);
713 mcreds.server = server;
714 krb5_timeofday(context, &mcreds.times.endtime);
715 ret = krb5_cc_retrieve_cred(context, id,
716 KRB5_TC_DONT_MATCH_REALM |
717 KRB5_TC_MATCH_TIMES,
718 &mcreds, out_creds);
719 if(ret == 0)
720 return 0;
721 while(tgts && *tgts){
722 if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
723 &mcreds, *tgts)){
724 ret = krb5_copy_creds_contents(context, *tgts, out_creds);
725 return ret;
726 }
727 tgts++;
728 }
729 return not_found(context, server, KRB5_CC_NOTFOUND);
730 }
731
732 static krb5_error_code
add_cred(krb5_context context,krb5_creds const * tkt,krb5_creds *** tgts)733 add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
734 {
735 int i;
736 krb5_error_code ret;
737 krb5_creds **tmp = *tgts;
738
739 for(i = 0; tmp && tmp[i]; i++); /* XXX */
740 tmp = realloc(tmp, (i+2)*sizeof(*tmp));
741 if(tmp == NULL)
742 return krb5_enomem(context);
743 *tgts = tmp;
744 ret = krb5_copy_creds(context, tkt, &tmp[i]);
745 tmp[i+1] = NULL;
746 return ret;
747 }
748
749 static krb5_error_code
get_cred_kdc_capath_worker(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_const_realm try_realm,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)750 get_cred_kdc_capath_worker(krb5_context context,
751 krb5_kdc_flags flags,
752 krb5_ccache ccache,
753 krb5_creds *in_creds,
754 krb5_const_realm try_realm,
755 krb5_principal impersonate_principal,
756 Ticket *second_ticket,
757 krb5_creds **out_creds,
758 krb5_creds ***ret_tgts)
759 {
760 krb5_error_code ret;
761 krb5_creds *tgt = NULL;
762 krb5_creds tmp_creds;
763 krb5_const_realm client_realm, server_realm;
764 int ok_as_delegate = 1;
765
766 *out_creds = calloc(1, sizeof(**out_creds));
767 if (*out_creds == NULL)
768 return krb5_enomem(context);
769
770 memset(&tmp_creds, 0, sizeof(tmp_creds));
771
772 client_realm = krb5_principal_get_realm(context, in_creds->client);
773 server_realm = krb5_principal_get_realm(context, in_creds->server);
774 ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
775 if (ret)
776 goto out;
777
778 ret = krb5_make_principal(context,
779 &tmp_creds.server,
780 try_realm,
781 KRB5_TGS_NAME,
782 server_realm,
783 NULL);
784 if (ret)
785 goto out;
786
787 {
788 krb5_creds tgts;
789
790 /*
791 * If we have krbtgt/server_realm@try_realm cached, use it and we're
792 * done.
793 */
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
799 || strcmp(try_realm, server_realm) != 0) {
800 ok_as_delegate = tgts.flags.b.ok_as_delegate;
801 }
802
803 ret = get_cred_kdc_address(context, ccache, flags, NULL,
804 in_creds, &tgts,
805 impersonate_principal,
806 second_ticket,
807 *out_creds);
808 krb5_free_cred_contents(context, &tgts);
809 if (ret == 0 &&
810 !krb5_principal_compare(context, in_creds->server,
811 (*out_creds)->server)) {
812 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
813 }
814 if (ret == 0 && ok_as_delegate == 0)
815 (*out_creds)->flags.b.ok_as_delegate = 0;
816
817 goto out;
818 }
819 }
820
821 if (krb5_realm_compare(context, in_creds->client, in_creds->server)) {
822 ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
823 goto out;
824 }
825
826 /*
827 * XXX This can loop forever, plus we recurse, so we can't just keep a
828 * count here. The count would have to get passed around by reference.
829 *
830 * The KDCs check for transit loops for us, and capath data is finite, so
831 * in fact we'll fall out of this loop at some point. We should do our own
832 * transit loop checking (like get_cred_kdc_referral()), and we should
833 * impose a max number of iterations altogether. But barring malicious or
834 * broken KDCs, this is good enough.
835 */
836 while (1) {
837 heim_general_string tgt_inst;
838
839 ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
840 NULL, NULL, &tgt, ret_tgts);
841 if (ret)
842 goto out;
843
844 /*
845 * if either of the chain or the ok_as_delegate was stripped
846 * by the kdc, make sure we strip it too.
847 */
848 if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
849 ok_as_delegate = 0;
850 tgt->flags.b.ok_as_delegate = 0;
851 }
852
853 ret = add_cred(context, tgt, ret_tgts);
854 if (ret)
855 goto out;
856 tgt_inst = tgt->server->name.name_string.val[1];
857 if (strcmp(tgt_inst, server_realm) == 0)
858 break;
859 krb5_free_principal(context, tmp_creds.server);
860 tmp_creds.server = NULL;
861 ret = krb5_make_principal(context, &tmp_creds.server,
862 tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
863 if (ret)
864 goto out;
865 ret = krb5_free_creds(context, tgt);
866 tgt = NULL;
867 if (ret)
868 goto out;
869 }
870
871 ret = get_cred_kdc_address(context, ccache, flags, NULL,
872 in_creds, tgt, impersonate_principal,
873 second_ticket, *out_creds);
874 if (ret == 0 &&
875 !krb5_principal_compare(context, in_creds->server,
876 (*out_creds)->server)) {
877 krb5_free_cred_contents(context, *out_creds);
878 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
879 }
880 if (ret == 0 && ok_as_delegate == 0)
881 (*out_creds)->flags.b.ok_as_delegate = 0;
882
883 out:
884 if (ret) {
885 krb5_free_creds(context, *out_creds);
886 *out_creds = NULL;
887 }
888 if (tmp_creds.server)
889 krb5_free_principal(context, tmp_creds.server);
890 if (tmp_creds.client)
891 krb5_free_principal(context, tmp_creds.client);
892 if (tgt)
893 krb5_free_creds(context, tgt);
894 return ret;
895 }
896
897 /*
898 get_cred(server)
899 creds = cc_get_cred(server)
900 if(creds) return creds
901 tgt = cc_get_cred(krbtgt/server_realm@any_realm)
902 if(tgt)
903 return get_cred_tgt(server, tgt)
904 if(client_realm == server_realm)
905 return NULL
906 tgt = get_cred(krbtgt/server_realm@client_realm)
907 while(tgt_inst != server_realm)
908 tgt = get_cred(krbtgt/server_realm@tgt_inst)
909 return get_cred_tgt(server, tgt)
910 */
911
912 static krb5_error_code
get_cred_kdc_capath(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)913 get_cred_kdc_capath(krb5_context context,
914 krb5_kdc_flags flags,
915 krb5_ccache ccache,
916 krb5_creds *in_creds,
917 krb5_principal impersonate_principal,
918 Ticket *second_ticket,
919 krb5_creds **out_creds,
920 krb5_creds ***ret_tgts)
921 {
922 krb5_error_code ret;
923 krb5_const_realm client_realm, server_realm, try_realm;
924
925 client_realm = krb5_principal_get_realm(context, in_creds->client);
926 server_realm = krb5_principal_get_realm(context, in_creds->server);
927
928 try_realm = client_realm;
929 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds, try_realm,
930 impersonate_principal, second_ticket, out_creds,
931 ret_tgts);
932
933 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
934 try_realm = krb5_config_get_string(context, NULL, "capaths",
935 client_realm, server_realm, NULL);
936
937 if (try_realm != NULL && strcmp(try_realm, client_realm)) {
938 ret = get_cred_kdc_capath_worker(context, flags, ccache, in_creds,
939 try_realm, impersonate_principal,
940 second_ticket, out_creds, ret_tgts);
941 }
942 }
943
944 return ret;
945 }
946
947 /*
948 * Get a service ticket from a KDC by chasing referrals from a start realm.
949 *
950 * All referral TGTs produced in the process are thrown away when we're done.
951 * We don't store them, and we don't allow other search mechanisms (capaths) to
952 * use referral TGTs produced here.
953 */
954 static krb5_error_code
get_cred_kdc_referral(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds)955 get_cred_kdc_referral(krb5_context context,
956 krb5_kdc_flags flags,
957 krb5_ccache ccache,
958 krb5_creds *in_creds,
959 krb5_principal impersonate_principal,
960 Ticket *second_ticket,
961 krb5_creds **out_creds)
962 {
963 krb5_realm start_realm = NULL;
964 krb5_data config_start_realm;
965 krb5_error_code ret;
966 krb5_creds tgt, referral, ticket;
967 krb5_creds **referral_tgts = NULL; /* used for loop detection */
968 int loop = 0;
969 int ok_as_delegate = 1;
970 size_t i;
971
972 if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
973 krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
974 N_("Name too short to do referals, skipping", ""));
975 return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
976 }
977
978 memset(&tgt, 0, sizeof(tgt));
979 memset(&ticket, 0, sizeof(ticket));
980
981 flags.b.canonicalize = 1;
982
983 *out_creds = NULL;
984
985
986 ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm);
987 if (ret == 0) {
988 start_realm = strndup(config_start_realm.data, config_start_realm.length);
989 krb5_data_free(&config_start_realm);
990 } else {
991 start_realm = strdup(krb5_principal_get_realm(context, in_creds->client));
992 }
993 if (start_realm == NULL)
994 return krb5_enomem(context);
995
996 /* find tgt for the clients base realm */
997 {
998 krb5_principal tgtname;
999
1000 ret = krb5_make_principal(context, &tgtname,
1001 start_realm,
1002 KRB5_TGS_NAME,
1003 start_realm,
1004 NULL);
1005 if (ret) {
1006 free(start_realm);
1007 return ret;
1008 }
1009
1010 ret = find_cred(context, ccache, tgtname, NULL, &tgt);
1011 krb5_free_principal(context, tgtname);
1012 if (ret) {
1013 free(start_realm);
1014 return ret;
1015 }
1016 }
1017
1018 referral = *in_creds;
1019 ret = krb5_copy_principal(context, in_creds->server, &referral.server);
1020 if (ret) {
1021 krb5_free_cred_contents(context, &tgt);
1022 free(start_realm);
1023 return ret;
1024 }
1025 ret = krb5_principal_set_realm(context, referral.server, start_realm);
1026 free(start_realm);
1027 start_realm = NULL;
1028 if (ret) {
1029 krb5_free_cred_contents(context, &tgt);
1030 krb5_free_principal(context, referral.server);
1031 return ret;
1032 }
1033
1034 while (loop++ < 17) {
1035 krb5_creds **tickets;
1036 krb5_creds mcreds;
1037 char *referral_realm;
1038
1039 /* Use cache if we are not doing impersonation or contrained deleg */
1040 if (impersonate_principal == NULL || flags.b.cname_in_addl_tkt) {
1041 krb5_cc_clear_mcred(&mcreds);
1042 mcreds.server = referral.server;
1043 krb5_timeofday(context, &mcreds.times.endtime);
1044 ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES,
1045 &mcreds, &ticket);
1046 } else
1047 ret = EINVAL;
1048
1049 if (ret) {
1050 ret = get_cred_kdc_address(context, ccache, flags, NULL,
1051 &referral, &tgt, impersonate_principal,
1052 second_ticket, &ticket);
1053 if (ret)
1054 goto out;
1055 }
1056
1057 /* Did we get the right ticket ? */
1058 if (krb5_principal_compare_any_realm(context,
1059 referral.server,
1060 ticket.server))
1061 break;
1062
1063 if (!krb5_principal_is_krbtgt(context, ticket.server)) {
1064 krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
1065 N_("Got back an non krbtgt "
1066 "ticket referrals", ""));
1067 ret = KRB5KRB_AP_ERR_NOT_US;
1068 goto out;
1069 }
1070
1071 referral_realm = ticket.server->name.name_string.val[1];
1072
1073 /* check that there are no referrals loops */
1074 tickets = referral_tgts;
1075
1076 krb5_cc_clear_mcred(&mcreds);
1077 mcreds.server = ticket.server;
1078
1079 while (tickets && *tickets){
1080 if (krb5_compare_creds(context,
1081 KRB5_TC_DONT_MATCH_REALM,
1082 &mcreds,
1083 *tickets)) {
1084 krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1085 N_("Referral from %s "
1086 "loops back to realm %s", ""),
1087 tgt.server->realm,
1088 referral_realm);
1089 ret = KRB5_GET_IN_TKT_LOOP;
1090 goto out;
1091 }
1092 tickets++;
1093 }
1094
1095 /*
1096 * if either of the chain or the ok_as_delegate was stripped
1097 * by the kdc, make sure we strip it too.
1098 */
1099
1100 if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1101 ok_as_delegate = 0;
1102 ticket.flags.b.ok_as_delegate = 0;
1103 }
1104
1105 _krb5_debug(context, 6, "get_cred_kdc_referral: got referral "
1106 "to %s from %s", referral_realm, referral.server->realm);
1107 ret = add_cred(context, &ticket, &referral_tgts);
1108 if (ret)
1109 goto out;
1110
1111 /* try realm in the referral */
1112 ret = krb5_principal_set_realm(context,
1113 referral.server,
1114 referral_realm);
1115 krb5_free_cred_contents(context, &tgt);
1116 tgt = ticket;
1117 memset(&ticket, 0, sizeof(ticket));
1118 if (ret)
1119 goto out;
1120 }
1121
1122 ret = krb5_copy_creds(context, &ticket, out_creds);
1123
1124 out:
1125 for (i = 0; referral_tgts && referral_tgts[i]; i++)
1126 krb5_free_creds(context, referral_tgts[i]);
1127 free(referral_tgts);
1128 krb5_free_principal(context, referral.server);
1129 krb5_free_cred_contents(context, &tgt);
1130 krb5_free_cred_contents(context, &ticket);
1131 return ret;
1132 }
1133
1134
1135 /*
1136 * Glue function between referrals version and old client chasing
1137 * codebase.
1138 */
1139
1140 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_cred_kdc_any(krb5_context context,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_principal impersonate_principal,Ticket * second_ticket,krb5_creds ** out_creds,krb5_creds *** ret_tgts)1141 _krb5_get_cred_kdc_any(krb5_context context,
1142 krb5_kdc_flags flags,
1143 krb5_ccache ccache,
1144 krb5_creds *in_creds,
1145 krb5_principal impersonate_principal,
1146 Ticket *second_ticket,
1147 krb5_creds **out_creds,
1148 krb5_creds ***ret_tgts)
1149 {
1150 krb5_error_code ret;
1151 krb5_deltat offset;
1152
1153 ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
1154 if (ret == 0) {
1155 context->kdc_sec_offset = offset;
1156 context->kdc_usec_offset = 0;
1157 }
1158
1159 if (strcmp(in_creds->server->realm, "") != 0) {
1160 /*
1161 * Non-empty realm? Try capaths first. We might have local
1162 * policy (capaths) to honor.
1163 */
1164 ret = get_cred_kdc_capath(context,
1165 flags,
1166 ccache,
1167 in_creds,
1168 impersonate_principal,
1169 second_ticket,
1170 out_creds,
1171 ret_tgts);
1172 if (ret == 0)
1173 return ret;
1174 }
1175
1176 /* Otherwise try referrals */
1177 return get_cred_kdc_referral(context,
1178 flags,
1179 ccache,
1180 in_creds,
1181 impersonate_principal,
1182 second_ticket,
1183 out_creds);
1184 }
1185
1186 static krb5_error_code
check_cc(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds * out_creds)1187 check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache,
1188 krb5_creds *in_creds, krb5_creds *out_creds)
1189 {
1190 krb5_error_code ret;
1191 krb5_timestamp now;
1192 krb5_creds mcreds = *in_creds;
1193
1194 krb5_timeofday(context, &now);
1195
1196 if (!(options & KRB5_GC_EXPIRED_OK) &&
1197 mcreds.times.endtime < now) {
1198 mcreds.times.renew_till = 0;
1199 krb5_timeofday(context, &mcreds.times.endtime);
1200 options |= KRB5_TC_MATCH_TIMES;
1201 }
1202
1203 if (mcreds.server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
1204 /* Avoid name canonicalization in krb5_cc_retrieve_cred() */
1205 krb5_principal_set_type(context, mcreds.server, KRB5_NT_SRV_HST);
1206 }
1207
1208 if (options & KRB5_GC_ANONYMOUS) {
1209 ret = krb5_make_principal(context,
1210 &mcreds.client,
1211 krb5_principal_get_realm(context, mcreds.client),
1212 KRB5_WELLKNOWN_NAME,
1213 KRB5_ANON_NAME,
1214 NULL);
1215 if (ret)
1216 return ret;
1217 }
1218
1219 ret = krb5_cc_retrieve_cred(context, ccache,
1220 (options &
1221 (KRB5_TC_DONT_MATCH_REALM |
1222 KRB5_TC_MATCH_KEYTYPE |
1223 KRB5_TC_MATCH_TIMES)),
1224 &mcreds, out_creds);
1225
1226 if (options & KRB5_GC_ANONYMOUS)
1227 krb5_free_principal(context, mcreds.client);
1228
1229 return ret;
1230 }
1231
1232 static void
store_cred(krb5_context context,krb5_ccache ccache,krb5_const_principal server_princ,krb5_creds * creds)1233 store_cred(krb5_context context, krb5_ccache ccache,
1234 krb5_const_principal server_princ, krb5_creds *creds)
1235 {
1236 if (!krb5_principal_compare(context, creds->server, server_princ)) {
1237 krb5_principal tmp_princ = creds->server;
1238 /*
1239 * Store the cred with the pre-canon server princ first so it
1240 * can be found quickly in the future.
1241 */
1242 creds->server = (krb5_principal)server_princ;
1243 krb5_cc_store_cred(context, ccache, creds);
1244 creds->server = tmp_princ;
1245 /* Then store again with the canonicalized server princ */
1246 }
1247 krb5_cc_store_cred(context, ccache, creds);
1248 }
1249
1250
1251 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_credentials_with_flags(krb5_context context,krb5_flags options,krb5_kdc_flags flags,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)1252 krb5_get_credentials_with_flags(krb5_context context,
1253 krb5_flags options,
1254 krb5_kdc_flags flags,
1255 krb5_ccache ccache,
1256 krb5_creds *in_creds,
1257 krb5_creds **out_creds)
1258 {
1259 krb5_error_code ret;
1260 krb5_name_canon_iterator name_canon_iter = NULL;
1261 krb5_name_canon_rule_options rule_opts;
1262 krb5_const_principal try_princ = NULL;
1263 krb5_principal save_princ = in_creds->server;
1264 krb5_creds **tgts;
1265 krb5_creds *res_creds;
1266 int i;
1267
1268 if (_krb5_have_debug(context, 5)) {
1269 char *unparsed;
1270
1271 ret = krb5_unparse_name(context, in_creds->server, &unparsed);
1272 if (ret) {
1273 _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1274 "requested service principal");
1275 } else {
1276 _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
1277 "for %s", unparsed);
1278 free(unparsed);
1279 }
1280 }
1281
1282 if (in_creds->session.keytype) {
1283 ret = krb5_enctype_valid(context, in_creds->session.keytype);
1284 if (ret)
1285 return ret;
1286 options |= KRB5_TC_MATCH_KEYTYPE;
1287 }
1288
1289 *out_creds = NULL;
1290 res_creds = calloc(1, sizeof(*res_creds));
1291 if (res_creds == NULL)
1292 return krb5_enomem(context);
1293
1294 ret = krb5_name_canon_iterator_start(context, in_creds->server,
1295 &name_canon_iter);
1296 if (ret)
1297 return ret;
1298
1299 next_rule:
1300 krb5_free_cred_contents(context, res_creds);
1301 memset(res_creds, 0, sizeof (*res_creds));
1302 ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
1303 &rule_opts);
1304 in_creds->server = rk_UNCONST(try_princ);
1305 if (ret)
1306 goto out;
1307
1308 if (name_canon_iter == NULL) {
1309 if (options & KRB5_GC_CACHED)
1310 ret = KRB5_CC_NOTFOUND;
1311 else
1312 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1313 goto out;
1314 }
1315
1316 ret = check_cc(context, options, ccache, in_creds, res_creds);
1317 if (ret == 0) {
1318 *out_creds = res_creds;
1319 res_creds = NULL;
1320 goto out;
1321 } else if(ret != KRB5_CC_END) {
1322 goto out;
1323 }
1324 if (options & KRB5_GC_CACHED)
1325 goto next_rule;
1326
1327 if(options & KRB5_GC_USER_USER)
1328 flags.b.enc_tkt_in_skey = 1;
1329 if (flags.b.enc_tkt_in_skey)
1330 options |= KRB5_GC_NO_STORE;
1331
1332 tgts = NULL;
1333 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1334 in_creds, NULL, NULL, out_creds, &tgts);
1335 for (i = 0; tgts && tgts[i]; i++) {
1336 if ((options & KRB5_GC_NO_STORE) == 0)
1337 krb5_cc_store_cred(context, ccache, tgts[i]);
1338 krb5_free_creds(context, tgts[i]);
1339 }
1340 free(tgts);
1341
1342 /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
1343 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
1344 !(rule_opts & KRB5_NCRO_USE_FAST))
1345 goto next_rule;
1346
1347 if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1348 store_cred(context, ccache, in_creds->server, *out_creds);
1349
1350 if (ret == 0 && _krb5_have_debug(context, 5)) {
1351 char *unparsed;
1352
1353 ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
1354 if (ret) {
1355 _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1356 "service principal");
1357 } else {
1358 _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
1359 unparsed);
1360 free(unparsed);
1361 }
1362 }
1363
1364 out:
1365 in_creds->server = save_princ;
1366 krb5_free_creds(context, res_creds);
1367 krb5_free_name_canon_iterator(context, name_canon_iter);
1368 if (ret)
1369 return not_found(context, in_creds->server, ret);
1370 return 0;
1371 }
1372
1373 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_credentials(krb5_context context,krb5_flags options,krb5_ccache ccache,krb5_creds * in_creds,krb5_creds ** out_creds)1374 krb5_get_credentials(krb5_context context,
1375 krb5_flags options,
1376 krb5_ccache ccache,
1377 krb5_creds *in_creds,
1378 krb5_creds **out_creds)
1379 {
1380 krb5_kdc_flags flags;
1381 flags.i = 0;
1382 return krb5_get_credentials_with_flags(context, options, flags,
1383 ccache, in_creds, out_creds);
1384 }
1385
1386 struct krb5_get_creds_opt_data {
1387 krb5_principal self;
1388 krb5_flags options;
1389 krb5_enctype enctype;
1390 Ticket *ticket;
1391 };
1392
1393
1394 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_alloc(krb5_context context,krb5_get_creds_opt * opt)1395 krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1396 {
1397 *opt = calloc(1, sizeof(**opt));
1398 if (*opt == NULL)
1399 return krb5_enomem(context);
1400 return 0;
1401 }
1402
1403 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_free(krb5_context context,krb5_get_creds_opt opt)1404 krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1405 {
1406 if (opt->self)
1407 krb5_free_principal(context, opt->self);
1408 if (opt->ticket) {
1409 free_Ticket(opt->ticket);
1410 free(opt->ticket);
1411 }
1412 memset(opt, 0, sizeof(*opt));
1413 free(opt);
1414 }
1415
1416 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_set_options(krb5_context context,krb5_get_creds_opt opt,krb5_flags options)1417 krb5_get_creds_opt_set_options(krb5_context context,
1418 krb5_get_creds_opt opt,
1419 krb5_flags options)
1420 {
1421 opt->options = options;
1422 }
1423
1424 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_add_options(krb5_context context,krb5_get_creds_opt opt,krb5_flags options)1425 krb5_get_creds_opt_add_options(krb5_context context,
1426 krb5_get_creds_opt opt,
1427 krb5_flags options)
1428 {
1429 opt->options |= options;
1430 }
1431
1432 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_creds_opt_set_enctype(krb5_context context,krb5_get_creds_opt opt,krb5_enctype enctype)1433 krb5_get_creds_opt_set_enctype(krb5_context context,
1434 krb5_get_creds_opt opt,
1435 krb5_enctype enctype)
1436 {
1437 opt->enctype = enctype;
1438 }
1439
1440 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_set_impersonate(krb5_context context,krb5_get_creds_opt opt,krb5_const_principal self)1441 krb5_get_creds_opt_set_impersonate(krb5_context context,
1442 krb5_get_creds_opt opt,
1443 krb5_const_principal self)
1444 {
1445 if (opt->self)
1446 krb5_free_principal(context, opt->self);
1447 return krb5_copy_principal(context, self, &opt->self);
1448 }
1449
1450 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_set_ticket(krb5_context context,krb5_get_creds_opt opt,const Ticket * ticket)1451 krb5_get_creds_opt_set_ticket(krb5_context context,
1452 krb5_get_creds_opt opt,
1453 const Ticket *ticket)
1454 {
1455 if (opt->ticket) {
1456 free_Ticket(opt->ticket);
1457 free(opt->ticket);
1458 opt->ticket = NULL;
1459 }
1460 if (ticket) {
1461 krb5_error_code ret;
1462
1463 opt->ticket = malloc(sizeof(*ticket));
1464 if (opt->ticket == NULL)
1465 return krb5_enomem(context);
1466 ret = copy_Ticket(ticket, opt->ticket);
1467 if (ret) {
1468 free(opt->ticket);
1469 opt->ticket = NULL;
1470 krb5_set_error_message(context, ret,
1471 N_("malloc: out of memory", ""));
1472 return ret;
1473 }
1474 }
1475 return 0;
1476 }
1477
1478
1479 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds(krb5_context context,krb5_get_creds_opt opt,krb5_ccache ccache,krb5_const_principal inprinc,krb5_creds ** out_creds)1480 krb5_get_creds(krb5_context context,
1481 krb5_get_creds_opt opt,
1482 krb5_ccache ccache,
1483 krb5_const_principal inprinc,
1484 krb5_creds **out_creds)
1485 {
1486 krb5_kdc_flags flags;
1487 krb5_flags options;
1488 krb5_creds in_creds;
1489 krb5_error_code ret;
1490 krb5_creds **tgts;
1491 krb5_creds *res_creds;
1492 krb5_const_principal try_princ = NULL;
1493 krb5_name_canon_iterator name_canon_iter = NULL;
1494 krb5_name_canon_rule_options rule_opts;
1495 int i;
1496 int type;
1497 const char *comp;
1498
1499 memset(&in_creds, 0, sizeof(in_creds));
1500 in_creds.server = rk_UNCONST(inprinc);
1501
1502 if (_krb5_have_debug(context, 5)) {
1503 char *unparsed;
1504
1505 ret = krb5_unparse_name(context, in_creds.server, &unparsed);
1506 if (ret) {
1507 _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1508 "requested service principal");
1509 } else {
1510 _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
1511 "for %s", unparsed);
1512 free(unparsed);
1513 }
1514 }
1515
1516 if (opt && opt->enctype) {
1517 ret = krb5_enctype_valid(context, opt->enctype);
1518 if (ret)
1519 return ret;
1520 }
1521
1522 ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1523 if (ret)
1524 return ret;
1525
1526 if (opt)
1527 options = opt->options;
1528 else
1529 options = 0;
1530 flags.i = 0;
1531
1532 *out_creds = NULL;
1533 res_creds = calloc(1, sizeof(*res_creds));
1534 if (res_creds == NULL) {
1535 krb5_free_principal(context, in_creds.client);
1536 return krb5_enomem(context);
1537 }
1538
1539 if (opt && opt->enctype) {
1540 in_creds.session.keytype = opt->enctype;
1541 options |= KRB5_TC_MATCH_KEYTYPE;
1542 }
1543
1544 ret = krb5_name_canon_iterator_start(context, in_creds.server,
1545 &name_canon_iter);
1546 if (ret)
1547 goto out;
1548
1549 next_rule:
1550 ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
1551 &rule_opts);
1552 in_creds.server = rk_UNCONST(try_princ);
1553 if (ret)
1554 goto out;
1555
1556 if (name_canon_iter == NULL) {
1557 if (options & KRB5_GC_CACHED)
1558 ret = KRB5_CC_NOTFOUND;
1559 else
1560 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1561 goto out;
1562 }
1563
1564 ret = check_cc(context, options, ccache, &in_creds, res_creds);
1565 if (ret == 0) {
1566 *out_creds = res_creds;
1567 res_creds = NULL;
1568 goto out;
1569 } else if (ret != KRB5_CC_END) {
1570 goto out;
1571 }
1572 if (options & KRB5_GC_CACHED)
1573 goto next_rule;
1574
1575 type = krb5_principal_get_type(context, try_princ);
1576 comp = krb5_principal_get_comp_string(context, try_princ, 0);
1577 if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) &&
1578 comp != NULL && strcmp(comp, "host") == 0)
1579 flags.b.canonicalize = 1;
1580 if (rule_opts & KRB5_NCRO_NO_REFERRALS)
1581 flags.b.canonicalize = 0;
1582 else
1583 flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0;
1584 if (options & KRB5_GC_USER_USER) {
1585 flags.b.enc_tkt_in_skey = 1;
1586 options |= KRB5_GC_NO_STORE;
1587 }
1588 if (options & KRB5_GC_FORWARDABLE)
1589 flags.b.forwardable = 1;
1590 if (options & KRB5_GC_NO_TRANSIT_CHECK)
1591 flags.b.disable_transited_check = 1;
1592 if (options & KRB5_GC_CONSTRAINED_DELEGATION)
1593 flags.b.cname_in_addl_tkt = 1;
1594 if (options & KRB5_GC_ANONYMOUS)
1595 flags.b.request_anonymous = 1;
1596
1597 tgts = NULL;
1598 ret = _krb5_get_cred_kdc_any(context, flags, ccache,
1599 &in_creds, opt ? opt->self : 0,
1600 opt ? opt->ticket : 0, out_creds,
1601 &tgts);
1602 for (i = 0; tgts && tgts[i]; i++) {
1603 if ((options & KRB5_GC_NO_STORE) == 0)
1604 krb5_cc_store_cred(context, ccache, tgts[i]);
1605 krb5_free_creds(context, tgts[i]);
1606 }
1607 free(tgts);
1608
1609 /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
1610 if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
1611 !(rule_opts & KRB5_NCRO_USE_FAST))
1612 goto next_rule;
1613
1614 if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1615 store_cred(context, ccache, inprinc, *out_creds);
1616
1617 if (ret == 0 && _krb5_have_debug(context, 5)) {
1618 char *unparsed;
1619
1620 ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
1621 if (ret) {
1622 _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1623 "service principal");
1624 } else {
1625 _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
1626 unparsed);
1627 free(unparsed);
1628 }
1629 }
1630
1631 out:
1632 krb5_free_creds(context, res_creds);
1633 krb5_free_principal(context, in_creds.client);
1634 krb5_free_name_canon_iterator(context, name_canon_iter);
1635 if (ret)
1636 return not_found(context, inprinc, ret);
1637 return ret;
1638 }
1639
1640 /*
1641 *
1642 */
1643
1644 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_renewed_creds(krb5_context context,krb5_creds * creds,krb5_const_principal client,krb5_ccache ccache,const char * in_tkt_service)1645 krb5_get_renewed_creds(krb5_context context,
1646 krb5_creds *creds,
1647 krb5_const_principal client,
1648 krb5_ccache ccache,
1649 const char *in_tkt_service)
1650 {
1651 krb5_error_code ret;
1652 krb5_kdc_flags flags;
1653 krb5_creds in, *template, *out = NULL;
1654
1655 memset(&in, 0, sizeof(in));
1656 memset(creds, 0, sizeof(*creds));
1657
1658 ret = krb5_copy_principal(context, client, &in.client);
1659 if (ret)
1660 return ret;
1661
1662 if (in_tkt_service) {
1663 ret = krb5_parse_name(context, in_tkt_service, &in.server);
1664 if (ret) {
1665 krb5_free_principal(context, in.client);
1666 return ret;
1667 }
1668 } else {
1669 const char *realm = krb5_principal_get_realm(context, client);
1670
1671 ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
1672 realm, NULL);
1673 if (ret) {
1674 krb5_free_principal(context, in.client);
1675 return ret;
1676 }
1677 }
1678
1679 flags.i = 0;
1680 flags.b.renewable = flags.b.renew = 1;
1681
1682 /*
1683 * Get template from old credential cache for the same entry, if
1684 * this failes, no worries.
1685 */
1686 ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
1687 if (ret == 0) {
1688 flags.b.forwardable = template->flags.b.forwardable;
1689 flags.b.proxiable = template->flags.b.proxiable;
1690 krb5_free_creds (context, template);
1691 }
1692
1693 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
1694 krb5_free_principal(context, in.client);
1695 krb5_free_principal(context, in.server);
1696 if (ret)
1697 return ret;
1698
1699 ret = krb5_copy_creds_contents(context, out, creds);
1700 krb5_free_creds(context, out);
1701
1702 return ret;
1703 }
1704