Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include "krb5_locl.h"
37 : #include <assert.h>
38 :
39 : static krb5_error_code
40 : get_cred_kdc_capath(krb5_context, krb5_kdc_flags,
41 : krb5_ccache, struct krb5_fast_state *,
42 : krb5_creds *, krb5_principal,
43 : Ticket *, const char *, const char *,
44 : 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
52 48002 : make_pa_tgs_req(krb5_context context,
53 : krb5_auth_context *ac,
54 : KDC_REQ_BODY *body,
55 : krb5_ccache ccache,
56 : krb5_creds *creds,
57 : krb5_data *tgs_req)
58 : {
59 1658 : krb5_error_code ret;
60 1658 : krb5_data in_data;
61 1658 : size_t buf_size;
62 48002 : size_t len = 0;
63 1658 : uint8_t *buf;
64 :
65 48002 : ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret);
66 48002 : if (ret)
67 0 : return ret;
68 :
69 48002 : if(buf_size != len)
70 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
71 :
72 48002 : in_data.length = len;
73 48002 : in_data.data = buf;
74 48002 : ret = _krb5_mk_req_internal(context, ac, 0, &in_data,
75 : creds, tgs_req,
76 : KRB5_KU_TGS_REQ_AUTH_CKSUM,
77 : KRB5_KU_TGS_REQ_AUTH);
78 48002 : free (buf);
79 48002 : return ret;
80 : }
81 :
82 : /*
83 : * Set the `enc-authorization-data' in `req_body' based on `authdata'
84 : */
85 :
86 : static krb5_error_code
87 48002 : set_auth_data (krb5_context context,
88 : KDC_REQ_BODY *req_body,
89 : krb5_authdata *authdata,
90 : krb5_keyblock *subkey)
91 : {
92 48002 : if(authdata->len) {
93 0 : size_t len = 0, buf_size;
94 0 : unsigned char *buf;
95 0 : krb5_crypto crypto;
96 0 : krb5_error_code ret;
97 :
98 0 : ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata,
99 : &len, ret);
100 0 : if (ret)
101 0 : return ret;
102 0 : if (buf_size != len)
103 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
104 :
105 0 : ALLOC(req_body->enc_authorization_data, 1);
106 0 : if (req_body->enc_authorization_data == NULL) {
107 0 : free (buf);
108 0 : return krb5_enomem(context);
109 : }
110 0 : ret = krb5_crypto_init(context, subkey, 0, &crypto);
111 0 : if (ret) {
112 0 : free (buf);
113 0 : free (req_body->enc_authorization_data);
114 0 : req_body->enc_authorization_data = NULL;
115 0 : return ret;
116 : }
117 0 : ret = krb5_encrypt_EncryptedData(context,
118 : crypto,
119 : KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
120 : buf,
121 : len,
122 : 0,
123 : req_body->enc_authorization_data);
124 0 : free (buf);
125 0 : krb5_crypto_destroy(context, crypto);
126 0 : return ret;
127 : } else {
128 48002 : req_body->enc_authorization_data = NULL;
129 48002 : return 0;
130 : }
131 : }
132 :
133 : /*
134 : * Create a tgs-req in `t' with `addresses', `flags', `second_ticket'
135 : * (if not-NULL), `in_creds', `krbtgt', and returning the generated
136 : * subkey in `subkey'.
137 : */
138 :
139 : static krb5_error_code
140 48002 : init_tgs_req (krb5_context context,
141 : krb5_ccache ccache,
142 : struct krb5_fast_state *state,
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 48002 : krb5_auth_context ac = NULL;
154 48002 : krb5_error_code ret = 0;
155 1658 : krb5_data tgs_req;
156 :
157 48002 : krb5_data_zero(&tgs_req);
158 48002 : memset(t, 0, sizeof(*t));
159 :
160 48002 : t->pvno = 5;
161 48002 : t->msg_type = krb_tgs_req;
162 48002 : if (in_creds->session.keytype) {
163 0 : ALLOC_SEQ(&t->req_body.etype, 1);
164 0 : if(t->req_body.etype.val == NULL) {
165 0 : ret = krb5_enomem(context);
166 0 : goto fail;
167 : }
168 0 : t->req_body.etype.val[0] = in_creds->session.keytype;
169 : } else {
170 48002 : ret = _krb5_init_etype(context,
171 : KRB5_PDU_TGS_REQUEST,
172 : &t->req_body.etype.len,
173 48002 : &t->req_body.etype.val,
174 : NULL);
175 : }
176 48002 : if (ret)
177 0 : goto fail;
178 48002 : t->req_body.addresses = addresses;
179 48002 : t->req_body.kdc_options = flags.b;
180 48002 : t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
181 48002 : t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
182 48002 : t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
183 48002 : ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
184 48002 : if (ret)
185 0 : goto fail;
186 48002 : ALLOC(t->req_body.sname, 1);
187 48002 : if (t->req_body.sname == NULL) {
188 0 : ret = krb5_enomem(context);
189 0 : goto fail;
190 : }
191 :
192 : /* some versions of some code might require that the client be
193 : present in TGS-REQs, but this is clearly against the spec */
194 :
195 48002 : ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname);
196 48002 : if (ret)
197 0 : goto fail;
198 :
199 48002 : if (krbtgt->times.starttime) {
200 48002 : ALLOC(t->req_body.from, 1);
201 48002 : if(t->req_body.from == NULL){
202 0 : ret = krb5_enomem(context);
203 0 : goto fail;
204 : }
205 48002 : *t->req_body.from = in_creds->times.starttime;
206 : }
207 :
208 : /* req_body.till should be NULL if there is no endtime specified,
209 : but old MIT code (like DCE secd) doesn't like that */
210 48002 : ALLOC(t->req_body.till, 1);
211 48002 : if(t->req_body.till == NULL){
212 0 : ret = krb5_enomem(context);
213 0 : goto fail;
214 : }
215 48002 : *t->req_body.till = in_creds->times.endtime;
216 :
217 48002 : if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
218 3085 : ALLOC(t->req_body.rtime, 1);
219 3085 : if(t->req_body.rtime == NULL){
220 0 : ret = krb5_enomem(context);
221 0 : goto fail;
222 : }
223 3085 : *t->req_body.rtime = in_creds->times.renew_till;
224 : }
225 :
226 48002 : t->req_body.nonce = nonce;
227 48002 : if(second_ticket){
228 20 : ALLOC(t->req_body.additional_tickets, 1);
229 20 : if (t->req_body.additional_tickets == NULL) {
230 0 : ret = krb5_enomem(context);
231 0 : goto fail;
232 : }
233 20 : ALLOC_SEQ(t->req_body.additional_tickets, 1);
234 20 : if (t->req_body.additional_tickets->val == NULL) {
235 0 : ret = krb5_enomem(context);
236 0 : goto fail;
237 : }
238 20 : ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
239 20 : if (ret)
240 0 : goto fail;
241 : }
242 :
243 48002 : ret = krb5_auth_con_init(context, &ac);
244 48002 : if(ret)
245 0 : goto fail;
246 :
247 48002 : ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session);
248 48002 : if (ret)
249 0 : goto fail;
250 :
251 48002 : if (state) {
252 1658 : krb5_data empty;
253 :
254 48002 : krb5_data_zero(&empty);
255 48002 : ret = krb5_auth_con_add_AuthorizationData(context, ac,
256 : KRB5_AUTHDATA_FX_FAST_USED,
257 : &empty);
258 48002 : if (ret)
259 0 : goto fail;
260 : }
261 :
262 49660 : ret = set_auth_data(context, &t->req_body,
263 48002 : &in_creds->authdata, ac->local_subkey);
264 48002 : if (ret)
265 0 : goto fail;
266 :
267 48002 : ret = make_pa_tgs_req(context,
268 : &ac,
269 : &t->req_body,
270 : ccache,
271 : krbtgt,
272 : &tgs_req);
273 48002 : if(ret)
274 0 : goto fail;
275 :
276 : /*
277 : * Add KRB5_PADATA_TGS_REQ first
278 : * followed by all others.
279 : */
280 :
281 48002 : if (t->padata == NULL) {
282 48002 : ALLOC(t->padata, 1);
283 48002 : if (t->padata == NULL) {
284 0 : ret = krb5_enomem(context);
285 0 : goto fail;
286 : }
287 : }
288 :
289 48002 : ret = krb5_padata_add(context, t->padata, KRB5_PADATA_TGS_REQ,
290 : tgs_req.data, tgs_req.length);
291 48002 : if (ret)
292 0 : goto fail;
293 :
294 48002 : krb5_data_zero(&tgs_req);
295 :
296 : {
297 1658 : size_t i;
298 50562 : for (i = 0; i < padata->len; i++) {
299 902 : const PA_DATA *val1 = &padata->val[i];
300 0 : PA_DATA val2;
301 :
302 902 : ret = copy_PA_DATA(val1, &val2);
303 902 : if (ret) {
304 0 : krb5_set_error_message(context, ret,
305 0 : N_("malloc: out of memory", ""));
306 0 : goto fail;
307 : }
308 :
309 902 : ret = krb5_padata_add(context, t->padata,
310 902 : val2.padata_type,
311 : val2.padata_value.data,
312 : val2.padata_value.length);
313 902 : if (ret) {
314 0 : free_PA_DATA(&val2);
315 :
316 0 : krb5_set_error_message(context, ret,
317 0 : N_("malloc: out of memory", ""));
318 0 : goto fail;
319 : }
320 : }
321 : }
322 :
323 48002 : if (state) {
324 48002 : state->armor_ac = ac;
325 48002 : ret = _krb5_fast_create_armor(context, state, NULL);
326 48002 : state->armor_ac = NULL;
327 48002 : if (ret)
328 0 : goto fail;
329 :
330 48002 : ret = _krb5_fast_wrap_req(context, state, t);
331 48002 : if (ret)
332 0 : goto fail;
333 :
334 : /* Its ok if there is no fast in the TGS-REP, older heimdal only support it in the AS code path */
335 48002 : state->flags &= ~KRB5_FAST_EXPECTED;
336 : }
337 :
338 48002 : ret = krb5_auth_con_getlocalsubkey(context, ac, subkey);
339 48002 : if (ret)
340 0 : goto fail;
341 :
342 48002 : fail:
343 48002 : if (ac)
344 48002 : krb5_auth_con_free(context, ac);
345 48002 : if (ret) {
346 0 : t->req_body.addresses = NULL;
347 0 : free_TGS_REQ (t);
348 : }
349 48002 : krb5_data_free(&tgs_req);
350 :
351 48002 : return ret;
352 : }
353 :
354 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
355 23851 : _krb5_get_krbtgt(krb5_context context,
356 : krb5_ccache id,
357 : krb5_realm realm,
358 : krb5_creds **cred)
359 : {
360 1035 : krb5_error_code ret;
361 1035 : krb5_creds tmp_cred;
362 :
363 23851 : memset(&tmp_cred, 0, sizeof(tmp_cred));
364 :
365 23851 : ret = krb5_cc_get_principal(context, id, &tmp_cred.client);
366 23851 : if (ret)
367 0 : return ret;
368 :
369 23851 : if (realm == NULL)
370 0 : realm = tmp_cred.client->realm;
371 :
372 23851 : ret = krb5_make_principal(context,
373 : &tmp_cred.server,
374 : realm,
375 : KRB5_TGS_NAME,
376 : realm,
377 : NULL);
378 23851 : if(ret) {
379 0 : krb5_free_principal(context, tmp_cred.client);
380 0 : return ret;
381 : }
382 : /*
383 : * The forwardable TGT might not be the start TGT, in which case, it is
384 : * generally, but not always already cached. Just in case, get it again if
385 : * lost.
386 : */
387 23851 : ret = krb5_get_credentials(context,
388 : 0,
389 : id,
390 : &tmp_cred,
391 : cred);
392 23851 : krb5_free_principal(context, tmp_cred.client);
393 23851 : krb5_free_principal(context, tmp_cred.server);
394 23851 : if(ret)
395 9 : return ret;
396 22807 : return 0;
397 : }
398 :
399 : static krb5_error_code
400 46174 : fast_tgs_strengthen_key(krb5_context context,
401 : struct krb5_fast_state *state,
402 : krb5_keyblock *reply_key,
403 : krb5_keyblock *extract_key)
404 : {
405 1658 : krb5_error_code ret;
406 :
407 46174 : if (state && state->strengthen_key) {
408 42504 : _krb5_debug(context, 5, "_krb5_fast_tgs_strengthen_key");
409 :
410 42504 : if (state->strengthen_key->keytype != reply_key->keytype) {
411 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
412 0 : N_("strengthen_key %d not same enctype as reply key %d", ""),
413 0 : state->strengthen_key->keytype, reply_key->keytype);
414 0 : return KRB5KRB_AP_ERR_MODIFIED;
415 : }
416 :
417 42504 : ret = _krb5_fast_cf2(context,
418 : state->strengthen_key,
419 : "strengthenkey",
420 : reply_key,
421 : "replykey",
422 : extract_key,
423 : NULL);
424 42504 : if (ret)
425 0 : return ret;
426 : } else {
427 3670 : ret = krb5_copy_keyblock_contents(context, reply_key, extract_key);
428 3670 : if (ret)
429 0 : return ret;
430 : }
431 :
432 44516 : return 0;
433 : }
434 :
435 : /* DCE compatible decrypt proc */
436 : static krb5_error_code KRB5_CALLCONV
437 46174 : decrypt_tkt_with_subkey (krb5_context context,
438 : krb5_keyblock *key,
439 : krb5_key_usage usage,
440 : krb5_const_pointer skey,
441 : krb5_kdc_rep *dec_rep)
442 : {
443 1658 : struct krb5_decrypt_tkt_with_subkey_state *state;
444 46174 : krb5_error_code ret = 0;
445 1658 : krb5_data data;
446 1658 : size_t size;
447 1658 : krb5_crypto crypto;
448 1658 : krb5_keyblock extract_key;
449 :
450 46174 : state = (struct krb5_decrypt_tkt_with_subkey_state *)skey;
451 :
452 46174 : assert(usage == 0);
453 :
454 46174 : krb5_data_zero(&data);
455 :
456 : /*
457 : * start out with trying with subkey if we have one
458 : */
459 46174 : if (state->subkey) {
460 46174 : ret = fast_tgs_strengthen_key(context, state->fast_state,
461 : state->subkey, &extract_key);
462 46174 : if (ret)
463 0 : return ret;
464 :
465 46174 : ret = krb5_crypto_init(context, &extract_key, 0, &crypto);
466 46174 : krb5_free_keyblock_contents(context, &extract_key);
467 46174 : if (ret)
468 0 : return ret;
469 47832 : ret = krb5_decrypt_EncryptedData (context,
470 : crypto,
471 : KRB5_KU_TGS_REP_ENC_PART_SUB_KEY,
472 46174 : &dec_rep->kdc_rep.enc_part,
473 : &data);
474 : /*
475 : * If the is Windows 2000 DC, we need to retry with key usage
476 : * 8 when doing ARCFOUR.
477 : */
478 46174 : if (ret && state->subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) {
479 0 : ret = krb5_decrypt_EncryptedData(context,
480 : crypto,
481 : 8,
482 0 : &dec_rep->kdc_rep.enc_part,
483 : &data);
484 : }
485 46174 : krb5_crypto_destroy(context, crypto);
486 : }
487 46174 : if (state->subkey == NULL || ret) {
488 0 : ret = fast_tgs_strengthen_key(context, state->fast_state, key, &extract_key);
489 0 : if (ret)
490 0 : return ret;
491 :
492 0 : ret = krb5_crypto_init(context, key, 0, &crypto);
493 0 : if (ret)
494 0 : return ret;
495 0 : ret = krb5_decrypt_EncryptedData (context,
496 : crypto,
497 : KRB5_KU_TGS_REP_ENC_PART_SESSION,
498 0 : &dec_rep->kdc_rep.enc_part,
499 : &data);
500 0 : krb5_crypto_destroy(context, crypto);
501 : }
502 46174 : if (ret)
503 0 : return ret;
504 :
505 47832 : ret = decode_EncASRepPart(data.data,
506 : data.length,
507 46174 : &dec_rep->enc_part,
508 : &size);
509 46174 : if (ret)
510 46174 : ret = decode_EncTGSRepPart(data.data,
511 : data.length,
512 44516 : &dec_rep->enc_part,
513 : &size);
514 46174 : if (ret)
515 0 : krb5_set_error_message(context, ret,
516 0 : N_("Failed to decode encpart in ticket", ""));
517 46174 : krb5_data_free (&data);
518 46174 : return ret;
519 : }
520 :
521 : static krb5_error_code
522 48002 : get_cred_kdc(krb5_context context,
523 : krb5_ccache id,
524 : struct krb5_fast_state *fast_state,
525 : krb5_kdc_flags flags,
526 : krb5_addresses *addresses,
527 : krb5_creds *in_creds,
528 : krb5_creds *krbtgt,
529 : krb5_principal impersonate_principal,
530 : Ticket *second_ticket,
531 : const char *kdc_hostname,
532 : const char *sitename,
533 : krb5_creds *out_creds)
534 : {
535 1658 : TGS_REQ req;
536 1658 : krb5_data enc;
537 1658 : krb5_data resp;
538 1658 : krb5_kdc_rep rep;
539 1658 : krb5_error_code ret;
540 1658 : unsigned nonce;
541 48002 : krb5_keyblock *subkey = NULL;
542 48002 : size_t len = 0;
543 1658 : Ticket second_ticket_data;
544 1658 : METHOD_DATA padata;
545 :
546 48002 : memset(&rep, 0, sizeof(rep));
547 48002 : krb5_data_zero(&resp);
548 48002 : krb5_data_zero(&enc);
549 48002 : padata.val = NULL;
550 48002 : padata.len = 0;
551 :
552 48002 : krb5_generate_random_block(&nonce, sizeof(nonce));
553 48002 : nonce &= 0xffffffff;
554 :
555 48002 : if(flags.b.enc_tkt_in_skey && second_ticket == NULL){
556 0 : ret = decode_Ticket(in_creds->second_ticket.data,
557 : in_creds->second_ticket.length,
558 : &second_ticket_data, &len);
559 0 : if(ret)
560 0 : return ret;
561 0 : second_ticket = &second_ticket_data;
562 : }
563 :
564 :
565 48002 : if (impersonate_principal) {
566 0 : krb5_crypto crypto;
567 0 : PA_S4U2Self self;
568 0 : krb5_data data;
569 0 : void *buf;
570 902 : size_t size = 0;
571 :
572 902 : self.name = impersonate_principal->name;
573 902 : self.realm = impersonate_principal->realm;
574 902 : self.auth = estrdup("Kerberos");
575 :
576 902 : ret = _krb5_s4u2self_to_checksumdata(context, &self, &data);
577 902 : if (ret) {
578 0 : free(self.auth);
579 0 : goto out;
580 : }
581 :
582 902 : ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto);
583 902 : if (ret) {
584 0 : free(self.auth);
585 0 : krb5_data_free(&data);
586 0 : goto out;
587 : }
588 :
589 902 : ret = krb5_create_checksum(context,
590 : crypto,
591 : KRB5_KU_OTHER_CKSUM,
592 : 0,
593 : data.data,
594 : data.length,
595 : &self.cksum);
596 902 : krb5_crypto_destroy(context, crypto);
597 902 : krb5_data_free(&data);
598 902 : if (ret) {
599 0 : free(self.auth);
600 0 : goto out;
601 : }
602 :
603 902 : ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret);
604 902 : free(self.auth);
605 902 : free_Checksum(&self.cksum);
606 902 : if (ret)
607 0 : goto out;
608 902 : if (len != size)
609 0 : krb5_abortx(context, "internal asn1 error");
610 :
611 902 : ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len);
612 902 : if (ret)
613 0 : goto out;
614 : }
615 :
616 48002 : ret = init_tgs_req (context,
617 : id,
618 : fast_state,
619 : addresses,
620 : flags,
621 : second_ticket,
622 : in_creds,
623 : krbtgt,
624 : nonce,
625 : &padata,
626 : &subkey,
627 : &req);
628 48002 : if (ret)
629 0 : goto out;
630 :
631 48002 : ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret);
632 48002 : if (ret)
633 0 : goto out;
634 48002 : if(enc.length != len)
635 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
636 :
637 : /* don't free addresses */
638 48002 : req.req_body.addresses = NULL;
639 48002 : free_TGS_REQ(&req);
640 :
641 : /*
642 : * Send and receive
643 : */
644 : {
645 1658 : krb5_sendto_ctx stctx;
646 48002 : ret = krb5_sendto_ctx_alloc(context, &stctx);
647 48002 : if (ret)
648 0 : return ret;
649 48002 : krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL);
650 :
651 48002 : if (kdc_hostname)
652 0 : krb5_sendto_set_hostname(context, stctx, kdc_hostname);
653 48002 : if (sitename)
654 0 : krb5_sendto_set_sitename(context, stctx, sitename);
655 :
656 49660 : ret = krb5_sendto_context (context, stctx, &enc,
657 48002 : krbtgt->server->name.name_string.val[1],
658 : &resp);
659 48002 : krb5_sendto_ctx_free(context, stctx);
660 : }
661 48002 : if(ret)
662 576 : goto out;
663 :
664 47426 : if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) {
665 1658 : struct krb5_decrypt_tkt_with_subkey_state state;
666 46174 : unsigned eflags = 0;
667 1658 : krb5_data data;
668 1658 : size_t size;
669 :
670 46174 : ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
671 : &rep.kdc_rep.ticket, &size, ret);
672 46174 : if (ret)
673 0 : goto out;
674 46174 : heim_assert(data.length == size, "ASN.1 internal error");
675 :
676 46174 : ret = _krb5_fast_unwrap_kdc_rep(context, nonce, &data,
677 : fast_state, &rep.kdc_rep);
678 46174 : krb5_data_free(&data);
679 46174 : if (ret)
680 0 : goto out;
681 :
682 47832 : ret = krb5_copy_principal(context,
683 46174 : in_creds->client,
684 : &out_creds->client);
685 46174 : if(ret)
686 0 : goto out;
687 47832 : ret = krb5_copy_principal(context,
688 46174 : in_creds->server,
689 : &out_creds->server);
690 46174 : if(ret)
691 0 : goto out;
692 : /* this should go someplace else */
693 46174 : out_creds->times.endtime = in_creds->times.endtime;
694 :
695 : /* XXX should do better testing */
696 46174 : if (flags.b.cname_in_addl_tkt || impersonate_principal)
697 630 : eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
698 46174 : if (flags.b.request_anonymous)
699 0 : eflags |= EXTRACT_TICKET_MATCH_ANON;
700 :
701 46174 : state.subkey = subkey;
702 46174 : state.fast_state = fast_state;
703 :
704 46174 : ret = _krb5_extract_ticket(context,
705 : &rep,
706 : out_creds,
707 : &krbtgt->session,
708 : NULL,
709 : 0,
710 : &krbtgt->addresses,
711 : nonce,
712 : eflags,
713 : NULL,
714 : decrypt_tkt_with_subkey,
715 : &state);
716 1252 : } else if(krb5_rd_error(context, &resp, &rep.error) == 0) {
717 0 : METHOD_DATA md;
718 :
719 1252 : memset(&md, 0, sizeof(md));
720 :
721 1252 : if (rep.error.e_data) {
722 0 : KERB_ERROR_DATA error_data;
723 :
724 1226 : memset(&error_data, 0, sizeof(error_data));
725 :
726 : /* First try to decode the e-data as KERB-ERROR-DATA. */
727 1226 : ret = decode_KERB_ERROR_DATA(rep.error.e_data->data,
728 1226 : rep.error.e_data->length,
729 : &error_data,
730 : &len);
731 1226 : if (ret) {
732 : /* That failed, so try to decode it as METHOD-DATA. */
733 1220 : ret = decode_METHOD_DATA(rep.error.e_data->data,
734 1220 : rep.error.e_data->length,
735 : &md, NULL);
736 1220 : if (ret) {
737 0 : krb5_set_error_message(context, ret,
738 0 : N_("Failed to decode METHOD-DATA", ""));
739 0 : goto out;
740 : }
741 6 : } else if (len != rep.error.e_data->length) {
742 : /* Trailing data — just ignore the error. */
743 0 : free_KERB_ERROR_DATA(&error_data);
744 : } else {
745 : /* OK. */
746 6 : free_KERB_ERROR_DATA(&error_data);
747 : }
748 : }
749 :
750 1252 : ret = _krb5_fast_unwrap_error(context, nonce, fast_state, &md, &rep.error);
751 1252 : free_METHOD_DATA(&md);
752 1252 : if (ret)
753 32 : goto out;
754 :
755 1220 : ret = krb5_error_from_rd_error(context, &rep.error, in_creds);
756 :
757 : /* log the failure */
758 1220 : if (_krb5_have_debug(context, 5)) {
759 0 : const char *str = krb5_get_error_message(context, ret);
760 0 : _krb5_debug(context, 5, "parse_tgs_rep: KRB-ERROR %d/%s", ret, str);
761 0 : krb5_free_error_message(context, str);
762 : }
763 0 : } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) {
764 0 : ret = KRB5KRB_AP_ERR_V4_REPLY;
765 0 : krb5_clear_error_message(context);
766 : } else {
767 0 : ret = KRB5KRB_AP_ERR_MSG_TYPE;
768 0 : krb5_clear_error_message(context);
769 : }
770 :
771 48002 : out:
772 48002 : krb5_free_kdc_rep(context, &rep);
773 48002 : if (second_ticket == &second_ticket_data)
774 0 : free_Ticket(&second_ticket_data);
775 48002 : free_METHOD_DATA(&padata);
776 48002 : krb5_data_free(&resp);
777 48002 : krb5_data_free(&enc);
778 48002 : if(subkey)
779 48002 : krb5_free_keyblock(context, subkey);
780 46344 : return ret;
781 :
782 : }
783 :
784 : /*
785 : * same as above, just get local addresses first if the krbtgt have
786 : * them and the realm is not addressless
787 : */
788 :
789 : static krb5_error_code
790 24948 : get_cred_kdc_address(krb5_context context,
791 : krb5_ccache id,
792 : struct krb5_fast_state *fast_state,
793 : krb5_kdc_flags flags,
794 : krb5_addresses *addrs,
795 : krb5_creds *in_creds,
796 : krb5_creds *krbtgt,
797 : krb5_principal impersonate_principal,
798 : Ticket *second_ticket,
799 : const char *kdc_hostname,
800 : const char *sitename,
801 : krb5_creds *out_creds)
802 : {
803 623 : krb5_error_code ret;
804 24948 : krb5_addresses addresses = { 0, NULL };
805 :
806 : /*
807 : * Inherit the address-ness of the krbtgt if the address is not
808 : * specified.
809 : */
810 :
811 24948 : if (addrs == NULL && krbtgt->addresses.len != 0) {
812 0 : krb5_boolean noaddr;
813 :
814 0 : krb5_appdefault_boolean(context, NULL, krbtgt->server->realm,
815 : "no-addresses", FALSE, &noaddr);
816 :
817 0 : if (!noaddr) {
818 0 : ret = krb5_get_all_client_addrs(context, &addresses);
819 0 : if (ret)
820 0 : return ret;
821 : /* XXX this sucks. */
822 0 : addrs = &addresses;
823 0 : if(addresses.len == 0)
824 0 : addrs = NULL;
825 : }
826 : }
827 24948 : ret = get_cred_kdc(context, id, fast_state, flags, addrs,
828 : in_creds, krbtgt, impersonate_principal,
829 : second_ticket, kdc_hostname, sitename, out_creds);
830 24948 : krb5_free_addresses(context, &addresses);
831 24948 : return ret;
832 : }
833 :
834 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
835 23063 : krb5_get_kdc_cred(krb5_context context,
836 : krb5_ccache id,
837 : krb5_kdc_flags flags,
838 : krb5_addresses *addresses,
839 : Ticket *second_ticket,
840 : krb5_creds *in_creds,
841 : krb5_creds **out_creds
842 : )
843 : {
844 1035 : krb5_error_code ret;
845 1035 : krb5_creds *krbtgt;
846 1035 : struct krb5_fast_state fast_state;
847 :
848 23063 : memset(&fast_state, 0, sizeof(fast_state));
849 :
850 23063 : *out_creds = calloc(1, sizeof(**out_creds));
851 23063 : if(*out_creds == NULL)
852 0 : return krb5_enomem(context);
853 24098 : ret = _krb5_get_krbtgt (context,
854 : id,
855 23063 : in_creds->server->realm,
856 : &krbtgt);
857 23063 : if(ret) {
858 9 : free(*out_creds);
859 9 : *out_creds = NULL;
860 9 : return ret;
861 : }
862 23054 : ret = get_cred_kdc(context, id, &fast_state, flags,
863 : addresses, in_creds, krbtgt,
864 : NULL, NULL, NULL, NULL, *out_creds);
865 23054 : krb5_free_creds (context, krbtgt);
866 23054 : _krb5_fast_free(context, &fast_state);
867 23054 : if(ret) {
868 2 : free(*out_creds);
869 2 : *out_creds = NULL;
870 : }
871 22019 : return ret;
872 : }
873 :
874 : static int
875 2966 : not_found(krb5_context context, krb5_const_principal p, krb5_error_code code)
876 : {
877 0 : krb5_error_code ret;
878 0 : char *str;
879 0 : const char *err;
880 :
881 2966 : ret = krb5_unparse_name(context, p, &str);
882 2966 : if(ret) {
883 0 : krb5_clear_error_message(context);
884 0 : return code;
885 : }
886 2966 : err = krb5_get_error_message(context, code);
887 2966 : krb5_set_error_message(context, code, N_("%s (%s)", ""), err, str);
888 2966 : krb5_free_error_message(context, err);
889 2966 : free(str);
890 2966 : return code;
891 : }
892 :
893 : static krb5_error_code
894 24438 : find_cred(krb5_context context,
895 : krb5_ccache id,
896 : krb5_principal server,
897 : krb5_creds **tgts,
898 : krb5_creds *out_creds)
899 : {
900 623 : krb5_error_code ret;
901 623 : krb5_creds mcreds;
902 :
903 24438 : krb5_cc_clear_mcred(&mcreds);
904 24438 : mcreds.server = server;
905 24438 : krb5_timeofday(context, &mcreds.times.endtime);
906 24438 : ret = krb5_cc_retrieve_cred(context, id,
907 : KRB5_TC_DONT_MATCH_REALM |
908 : KRB5_TC_MATCH_TIMES,
909 : &mcreds, out_creds);
910 24438 : if(ret == 0)
911 23667 : return 0;
912 148 : while(tgts && *tgts){
913 0 : if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM,
914 : &mcreds, *tgts)){
915 0 : ret = krb5_copy_creds_contents(context, *tgts, out_creds);
916 0 : return ret;
917 : }
918 0 : tgts++;
919 : }
920 148 : return not_found(context, server, KRB5_CC_NOTFOUND);
921 : }
922 :
923 : static krb5_error_code
924 2098 : add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
925 : {
926 0 : int i;
927 0 : krb5_error_code ret;
928 2098 : krb5_creds **tmp = *tgts;
929 :
930 2098 : for(i = 0; tmp && tmp[i]; i++); /* XXX */
931 2098 : tmp = realloc(tmp, (i+2)*sizeof(*tmp));
932 2098 : if(tmp == NULL)
933 0 : return krb5_enomem(context);
934 2098 : *tgts = tmp;
935 2098 : ret = krb5_copy_creds(context, tkt, &tmp[i]);
936 2098 : tmp[i+1] = NULL;
937 2098 : return ret;
938 : }
939 :
940 : static krb5_error_code
941 21881 : get_cred_kdc_capath_worker(krb5_context context,
942 : krb5_kdc_flags flags,
943 : krb5_ccache ccache,
944 : struct krb5_fast_state *fast_state,
945 : krb5_creds *in_creds,
946 : krb5_const_realm try_realm,
947 : krb5_principal impersonate_principal,
948 : Ticket *second_ticket,
949 : const char *kdc_hostname,
950 : const char *sitename,
951 : krb5_creds **out_creds,
952 : krb5_creds ***ret_tgts)
953 : {
954 623 : krb5_error_code ret;
955 21881 : krb5_creds *tgt = NULL;
956 623 : krb5_creds tmp_creds;
957 623 : krb5_const_realm client_realm, server_realm;
958 21881 : int ok_as_delegate = 1;
959 :
960 21881 : *out_creds = calloc(1, sizeof(**out_creds));
961 21881 : if (*out_creds == NULL)
962 0 : return krb5_enomem(context);
963 :
964 21881 : memset(&tmp_creds, 0, sizeof(tmp_creds));
965 :
966 21881 : client_realm = krb5_principal_get_realm(context, in_creds->client);
967 21881 : server_realm = krb5_principal_get_realm(context, in_creds->server);
968 21881 : ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
969 21881 : if (ret)
970 0 : goto out;
971 :
972 21881 : ret = krb5_make_principal(context,
973 : &tmp_creds.server,
974 : try_realm,
975 : KRB5_TGS_NAME,
976 : server_realm,
977 : NULL);
978 21881 : if (ret)
979 0 : goto out;
980 :
981 : {
982 623 : krb5_creds tgts;
983 :
984 : /*
985 : * If we have krbtgt/server_realm@try_realm cached, use it and we're
986 : * done.
987 : */
988 21881 : ret = find_cred(context, ccache, tmp_creds.server,
989 : *ret_tgts, &tgts);
990 21881 : if (ret == 0) {
991 : /* only allow implicit ok_as_delegate if the realm is the clients realm */
992 21761 : if (strcmp(try_realm, client_realm) != 0
993 21761 : || strcmp(try_realm, server_realm) != 0) {
994 15 : ok_as_delegate = tgts.flags.b.ok_as_delegate;
995 : }
996 :
997 21761 : ret = get_cred_kdc_address(context, ccache, fast_state,
998 : flags, NULL,
999 : in_creds, &tgts,
1000 : impersonate_principal,
1001 : second_ticket,
1002 : kdc_hostname,
1003 : sitename,
1004 : *out_creds);
1005 21761 : krb5_free_cred_contents(context, &tgts);
1006 42769 : if (ret == 0 &&
1007 21008 : !krb5_principal_compare(context, in_creds->server,
1008 21008 : (*out_creds)->server)) {
1009 2016 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1010 : }
1011 21761 : if (ret == 0 && ok_as_delegate == 0)
1012 3 : (*out_creds)->flags.b.ok_as_delegate = 0;
1013 :
1014 21761 : goto out;
1015 : }
1016 : }
1017 :
1018 120 : if (krb5_realm_compare(context, in_creds->client, in_creds->server)) {
1019 28 : ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
1020 28 : goto out;
1021 : }
1022 :
1023 : /*
1024 : * XXX This can loop forever, plus we recurse, so we can't just keep a
1025 : * count here. The count would have to get passed around by reference.
1026 : *
1027 : * The KDCs check for transit loops for us, and capath data is finite, so
1028 : * in fact we'll fall out of this loop at some point. We should do our own
1029 : * transit loop checking (like get_cred_kdc_referral()), and we should
1030 : * impose a max number of iterations altogether. But barring malicious or
1031 : * broken KDCs, this is good enough.
1032 : */
1033 0 : while (1) {
1034 0 : heim_general_string tgt_inst;
1035 :
1036 92 : ret = get_cred_kdc_capath(context, flags, ccache, fast_state,
1037 : &tmp_creds, NULL, NULL,
1038 : kdc_hostname, sitename,
1039 : &tgt, ret_tgts);
1040 92 : if (ret)
1041 10 : goto out;
1042 :
1043 : /*
1044 : * if either of the chain or the ok_as_delegate was stripped
1045 : * by the kdc, make sure we strip it too.
1046 : */
1047 82 : if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) {
1048 82 : ok_as_delegate = 0;
1049 82 : tgt->flags.b.ok_as_delegate = 0;
1050 : }
1051 :
1052 82 : ret = add_cred(context, tgt, ret_tgts);
1053 82 : if (ret)
1054 0 : goto out;
1055 82 : tgt_inst = tgt->server->name.name_string.val[1];
1056 82 : if (strcmp(tgt_inst, server_realm) == 0)
1057 82 : break;
1058 0 : krb5_free_principal(context, tmp_creds.server);
1059 0 : tmp_creds.server = NULL;
1060 0 : ret = krb5_make_principal(context, &tmp_creds.server,
1061 : tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
1062 0 : if (ret)
1063 0 : goto out;
1064 0 : ret = krb5_free_creds(context, tgt);
1065 0 : tgt = NULL;
1066 0 : if (ret)
1067 0 : goto out;
1068 : }
1069 :
1070 82 : ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL,
1071 : in_creds, tgt, impersonate_principal,
1072 : second_ticket, kdc_hostname, sitename, *out_creds);
1073 116 : if (ret == 0 &&
1074 34 : !krb5_principal_compare(context, in_creds->server,
1075 34 : (*out_creds)->server)) {
1076 0 : krb5_free_cred_contents(context, *out_creds);
1077 0 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1078 : }
1079 82 : if (ret == 0 && ok_as_delegate == 0)
1080 34 : (*out_creds)->flags.b.ok_as_delegate = 0;
1081 :
1082 48 : out:
1083 21881 : if (ret) {
1084 2855 : krb5_free_creds(context, *out_creds);
1085 2855 : *out_creds = NULL;
1086 : }
1087 21881 : if (tmp_creds.server)
1088 21881 : krb5_free_principal(context, tmp_creds.server);
1089 21881 : if (tmp_creds.client)
1090 21881 : krb5_free_principal(context, tmp_creds.client);
1091 21881 : if (tgt)
1092 82 : krb5_free_creds(context, tgt);
1093 21258 : return ret;
1094 : }
1095 :
1096 : /*
1097 : get_cred(server)
1098 : creds = cc_get_cred(server)
1099 : if(creds) return creds
1100 : tgt = cc_get_cred(krbtgt/server_realm@any_realm)
1101 : if(tgt)
1102 : return get_cred_tgt(server, tgt)
1103 : if(client_realm == server_realm)
1104 : return NULL
1105 : tgt = get_cred(krbtgt/server_realm@client_realm)
1106 : while(tgt_inst != server_realm)
1107 : tgt = get_cred(krbtgt/server_realm@tgt_inst)
1108 : return get_cred_tgt(server, tgt)
1109 : */
1110 :
1111 : static krb5_error_code
1112 21881 : get_cred_kdc_capath(krb5_context context,
1113 : krb5_kdc_flags flags,
1114 : krb5_ccache ccache,
1115 : struct krb5_fast_state *fast_state,
1116 : krb5_creds *in_creds,
1117 : krb5_principal impersonate_principal,
1118 : Ticket *second_ticket,
1119 : const char *kdc_hostname,
1120 : const char *sitename,
1121 : krb5_creds **out_creds,
1122 : krb5_creds ***ret_tgts)
1123 : {
1124 623 : krb5_error_code ret;
1125 623 : krb5_const_realm client_realm, server_realm, try_realm;
1126 :
1127 21881 : client_realm = krb5_principal_get_realm(context, in_creds->client);
1128 21881 : server_realm = krb5_principal_get_realm(context, in_creds->server);
1129 :
1130 21881 : try_realm = client_realm;
1131 21881 : ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state,
1132 : in_creds, try_realm, impersonate_principal,
1133 : second_ticket, kdc_hostname, sitename,
1134 : out_creds, ret_tgts);
1135 :
1136 21881 : if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) {
1137 2797 : try_realm = krb5_config_get_string(context, NULL, "capaths",
1138 : client_realm, server_realm, NULL);
1139 :
1140 2797 : if (try_realm != NULL && strcmp(try_realm, client_realm) != 0) {
1141 0 : ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state,
1142 : in_creds, try_realm, impersonate_principal,
1143 : second_ticket, kdc_hostname, sitename,
1144 : out_creds, ret_tgts);
1145 : }
1146 : }
1147 :
1148 21881 : return ret;
1149 : }
1150 :
1151 5402 : static krb5_boolean skip_referrals(krb5_principal server,
1152 : krb5_kdc_flags *flags)
1153 : {
1154 5402 : return server->name.name_string.len < 2 && !flags->b.canonicalize;
1155 : }
1156 :
1157 : /*
1158 : * Get a service ticket from a KDC by chasing referrals from a start realm.
1159 : *
1160 : * All referral TGTs produced in the process are thrown away when we're done.
1161 : * We don't store them, and we don't allow other search mechanisms (capaths) to
1162 : * use referral TGTs produced here.
1163 : */
1164 : static krb5_error_code
1165 2557 : get_cred_kdc_referral(krb5_context context,
1166 : krb5_kdc_flags flags,
1167 : krb5_ccache ccache,
1168 : struct krb5_fast_state *fast_state,
1169 : krb5_creds *in_creds,
1170 : krb5_principal impersonate_principal,
1171 : Ticket *second_ticket,
1172 : const char *kdc_hostname,
1173 : const char *sitename,
1174 : krb5_creds **out_creds)
1175 : {
1176 2557 : krb5_realm start_realm = NULL;
1177 0 : krb5_data config_start_realm;
1178 0 : krb5_error_code ret;
1179 0 : krb5_creds tgt, referral, ticket;
1180 2557 : krb5_creds **referral_tgts = NULL; /* used for loop detection */
1181 2557 : int loop = 0;
1182 2557 : int ok_as_delegate = 1;
1183 0 : int want_tgt;
1184 0 : size_t i;
1185 :
1186 2557 : if (skip_referrals(in_creds->server, &flags)) {
1187 0 : krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
1188 0 : N_("Name too short to do referals, skipping", ""));
1189 0 : return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
1190 : }
1191 :
1192 2557 : memset(&tgt, 0, sizeof(tgt));
1193 2557 : memset(&ticket, 0, sizeof(ticket));
1194 :
1195 2557 : flags.b.canonicalize = 1;
1196 :
1197 2557 : *out_creds = NULL;
1198 :
1199 :
1200 2557 : ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm);
1201 2557 : if (ret == 0) {
1202 2529 : start_realm = strndup(config_start_realm.data, config_start_realm.length);
1203 2529 : krb5_data_free(&config_start_realm);
1204 : } else {
1205 28 : start_realm = strdup(krb5_principal_get_realm(context, in_creds->client));
1206 : }
1207 2557 : if (start_realm == NULL)
1208 0 : return krb5_enomem(context);
1209 :
1210 : /* find tgt for the clients base realm */
1211 : {
1212 0 : krb5_principal tgtname;
1213 :
1214 2557 : ret = krb5_make_principal(context, &tgtname,
1215 : start_realm,
1216 : KRB5_TGS_NAME,
1217 : start_realm,
1218 : NULL);
1219 2557 : if (ret) {
1220 0 : free(start_realm);
1221 28 : return ret;
1222 : }
1223 :
1224 2557 : ret = find_cred(context, ccache, tgtname, NULL, &tgt);
1225 2557 : krb5_free_principal(context, tgtname);
1226 2557 : if (ret) {
1227 28 : free(start_realm);
1228 28 : return ret;
1229 : }
1230 : }
1231 :
1232 : /*
1233 : * If the desired service principal service/host@REALM is not a TGT, start
1234 : * by asking for a ticket for service/host@START_REALM and process referrals
1235 : * from there.
1236 : *
1237 : * However, when we ask for a TGT, krbtgt/A@B, we're actually looking for a
1238 : * path to realm B, so that we can explicitly obtain a ticket for krbtgt/A
1239 : * from B, and not some other realm. Therefore, in this case our starting
1240 : * point will be krbtgt/B@START_REALM. Only once we obtain a ticket for
1241 : * krbtgt/B@some-transit, do we switch to requesting krbtgt/A@B on our
1242 : * final request.
1243 : */
1244 2529 : referral = *in_creds;
1245 5058 : want_tgt = in_creds->server->realm[0] != '\0' &&
1246 2529 : krb5_principal_is_krbtgt(context, in_creds->server);
1247 2529 : if (!want_tgt)
1248 1089 : ret = krb5_copy_principal(context, in_creds->server, &referral.server);
1249 : else
1250 1440 : ret = krb5_make_principal(context, &referral.server, start_realm,
1251 1440 : KRB5_TGS_NAME, in_creds->server->realm, NULL);
1252 :
1253 2529 : if (ret) {
1254 0 : krb5_free_cred_contents(context, &tgt);
1255 0 : free(start_realm);
1256 0 : return ret;
1257 : }
1258 2529 : if (!want_tgt)
1259 1089 : ret = krb5_principal_set_realm(context, referral.server, start_realm);
1260 2529 : free(start_realm);
1261 2529 : start_realm = NULL;
1262 2529 : if (ret) {
1263 0 : krb5_free_cred_contents(context, &tgt);
1264 0 : krb5_free_principal(context, referral.server);
1265 0 : return ret;
1266 : }
1267 :
1268 4545 : while (loop++ < 17) {
1269 0 : krb5_creds **tickets;
1270 0 : krb5_creds mcreds;
1271 0 : char *referral_realm;
1272 :
1273 : /* Use cache if we are not doing impersonation or contrained deleg */
1274 4545 : if (impersonate_principal == NULL && !flags.b.cname_in_addl_tkt) {
1275 4399 : krb5_cc_clear_mcred(&mcreds);
1276 4399 : mcreds.server = referral.server;
1277 4399 : krb5_timeofday(context, &mcreds.times.endtime);
1278 4399 : ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES,
1279 : &mcreds, &ticket);
1280 : } else
1281 146 : ret = EINVAL;
1282 :
1283 4545 : if (ret) {
1284 3105 : ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL,
1285 : &referral, &tgt, impersonate_principal,
1286 : second_ticket, kdc_hostname, sitename, &ticket);
1287 3105 : if (ret)
1288 2465 : goto out;
1289 : }
1290 :
1291 : /*
1292 : * Did we get the right ticket?
1293 : *
1294 : * If we weren't asking for a TGT, then we don't mind if we took a realm
1295 : * change (referral.server has a referral realm, not necessarily the
1296 : * original).
1297 : *
1298 : * However, if we were looking for a TGT (which wouldn't be the start
1299 : * TGT, since that one must be in the ccache) then we actually want the
1300 : * one from the realm we wanted, since otherwise a _referral_ will
1301 : * confuse us and we will store that referral. In Heimdal we mostly
1302 : * never ask krb5_get_cred*() for TGTs, but some sites have code to ask
1303 : * for a ktbgt/REMOTE.REALM@REMOTE.REALM, and one could always use
1304 : * kgetcred(1) to get here asking for a krbtgt/C@D and we need to handle
1305 : * the case where last hop we get is krbtgt/C@B (in which case we must
1306 : * stop so we don't beat up on B for the remaining tries).
1307 : */
1308 5600 : if (!want_tgt &&
1309 2080 : krb5_principal_compare(context, referral.server, ticket.server))
1310 64 : break;
1311 :
1312 3456 : if (!krb5_principal_is_krbtgt(context, ticket.server)) {
1313 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US,
1314 0 : N_("Got back an non krbtgt "
1315 : "ticket referrals", ""));
1316 0 : ret = KRB5KRB_AP_ERR_NOT_US;
1317 0 : goto out;
1318 : }
1319 :
1320 3456 : referral_realm = ticket.server->name.name_string.val[1];
1321 :
1322 : /* check that there are no referrals loops */
1323 3456 : tickets = referral_tgts;
1324 :
1325 3456 : krb5_cc_clear_mcred(&mcreds);
1326 3456 : mcreds.server = ticket.server;
1327 :
1328 3456 : while (tickets && *tickets){
1329 1440 : if (krb5_compare_creds(context,
1330 : KRB5_TC_DONT_MATCH_REALM,
1331 : &mcreds,
1332 : *tickets)) {
1333 1440 : krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
1334 1440 : N_("Referral from %s "
1335 : "loops back to realm %s", ""),
1336 1440 : tgt.server->realm,
1337 : referral_realm);
1338 1440 : ret = KRB5_GET_IN_TKT_LOOP;
1339 1440 : goto out;
1340 : }
1341 0 : tickets++;
1342 : }
1343 :
1344 : /*
1345 : * if either of the chain or the ok_as_delegate was stripped
1346 : * by the kdc, make sure we strip it too.
1347 : */
1348 :
1349 2016 : if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) {
1350 2016 : ok_as_delegate = 0;
1351 2016 : ticket.flags.b.ok_as_delegate = 0;
1352 : }
1353 :
1354 2016 : _krb5_debug(context, 6, "get_cred_kdc_referral: got referral "
1355 2016 : "to %s from %s", referral_realm, referral.server->realm);
1356 2016 : ret = add_cred(context, &ticket, &referral_tgts);
1357 2016 : if (ret)
1358 0 : goto out;
1359 :
1360 : /* try realm in the referral */
1361 2016 : if (!want_tgt || strcmp(referral_realm, in_creds->server->realm) != 0)
1362 576 : ret = krb5_principal_set_realm(context,
1363 : referral.server,
1364 : referral_realm);
1365 : else {
1366 : /*
1367 : * Now that we have a ticket for the desired realm, we reset
1368 : * want_tgt and reinstate the desired principal so that the we can
1369 : * match it and break out of the loop.
1370 : */
1371 1440 : want_tgt = 0;
1372 1440 : krb5_free_principal(context, referral.server);
1373 1440 : referral.server = NULL;
1374 1440 : ret = krb5_copy_principal(context, in_creds->server, &referral.server);
1375 : }
1376 2016 : krb5_free_cred_contents(context, &tgt);
1377 2016 : tgt = ticket;
1378 2016 : memset(&ticket, 0, sizeof(ticket));
1379 2016 : if (ret)
1380 0 : goto out;
1381 : }
1382 :
1383 64 : ret = krb5_copy_creds(context, &ticket, out_creds);
1384 :
1385 2529 : out:
1386 4545 : for (i = 0; referral_tgts && referral_tgts[i]; i++)
1387 2016 : krb5_free_creds(context, referral_tgts[i]);
1388 2529 : free(referral_tgts);
1389 2529 : krb5_free_principal(context, referral.server);
1390 2529 : krb5_free_cred_contents(context, &tgt);
1391 2529 : krb5_free_cred_contents(context, &ticket);
1392 2529 : return ret;
1393 : }
1394 :
1395 :
1396 : /*
1397 : * Glue function between referrals version and old client chasing
1398 : * codebase.
1399 : */
1400 :
1401 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1402 21789 : _krb5_get_cred_kdc_any(krb5_context context,
1403 : krb5_kdc_flags flags,
1404 : krb5_ccache ccache,
1405 : struct krb5_fast_state *fast_state,
1406 : krb5_creds *in_creds,
1407 : krb5_principal impersonate_principal,
1408 : Ticket *second_ticket,
1409 : krb5_creds **out_creds,
1410 : krb5_creds ***ret_tgts)
1411 : {
1412 21789 : char *kdc_hostname = NULL;
1413 21789 : char *sitename = NULL;
1414 623 : krb5_error_code ret;
1415 623 : krb5_deltat offset;
1416 623 : krb5_data data;
1417 :
1418 21789 : krb5_data_zero(&data);
1419 :
1420 : /*
1421 : * If we are using LKDC, lets pull out the addreses from the
1422 : * ticket and use that.
1423 : */
1424 :
1425 21789 : ret = krb5_cc_get_config(context, ccache, NULL, "lkdc-hostname", &data);
1426 21789 : if (ret == 0) {
1427 0 : if ((kdc_hostname = strndup(data.data, data.length)) == NULL) {
1428 0 : ret = krb5_enomem(context);
1429 0 : goto out;
1430 : }
1431 0 : krb5_data_free(&data);
1432 : }
1433 :
1434 21789 : ret = krb5_cc_get_config(context, ccache, NULL, "sitename", &data);
1435 21789 : if (ret == 0) {
1436 0 : if ((sitename = strndup(data.data, data.length)) == NULL) {
1437 0 : ret = krb5_enomem(context);
1438 0 : goto out;
1439 : }
1440 0 : krb5_data_free(&data);
1441 : }
1442 :
1443 21789 : ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
1444 21789 : if (ret == 0) {
1445 21789 : context->kdc_sec_offset = offset;
1446 21789 : context->kdc_usec_offset = 0;
1447 : }
1448 :
1449 21789 : if (strcmp(in_creds->server->realm, "") != 0) {
1450 : /*
1451 : * Non-empty realm? Try capaths first. We might have local
1452 : * policy (capaths) to honor.
1453 : */
1454 21789 : ret = get_cred_kdc_capath(context,
1455 : flags,
1456 : ccache,
1457 : fast_state,
1458 : in_creds,
1459 : impersonate_principal,
1460 : second_ticket,
1461 : kdc_hostname,
1462 : sitename,
1463 : out_creds,
1464 : ret_tgts);
1465 21789 : if (ret == 0 || skip_referrals(in_creds->server, &flags))
1466 19232 : goto out;
1467 : }
1468 :
1469 : /* Otherwise try referrals */
1470 2557 : ret = get_cred_kdc_referral(context,
1471 : flags,
1472 : ccache,
1473 : fast_state,
1474 : in_creds,
1475 : impersonate_principal,
1476 : second_ticket,
1477 : kdc_hostname,
1478 : sitename,
1479 : out_creds);
1480 :
1481 21789 : out:
1482 21789 : krb5_data_free(&data);
1483 21789 : free(kdc_hostname);
1484 21789 : free(sitename);
1485 21789 : return ret;
1486 : }
1487 :
1488 : static krb5_error_code
1489 105229 : check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache,
1490 : krb5_creds *in_creds, krb5_creds *out_creds)
1491 : {
1492 4140 : krb5_error_code ret;
1493 4140 : krb5_timestamp now;
1494 105229 : krb5_creds mcreds = *in_creds;
1495 :
1496 105229 : krb5_timeofday(context, &now);
1497 :
1498 105229 : if (!(options & KRB5_GC_EXPIRED_OK) &&
1499 105205 : mcreds.times.endtime < now) {
1500 105155 : mcreds.times.renew_till = 0;
1501 105155 : krb5_timeofday(context, &mcreds.times.endtime);
1502 105155 : options |= KRB5_TC_MATCH_TIMES;
1503 : }
1504 :
1505 105229 : if (mcreds.server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
1506 : /* Avoid name canonicalization in krb5_cc_retrieve_cred() */
1507 0 : krb5_principal_set_type(context, mcreds.server, KRB5_NT_SRV_HST);
1508 : }
1509 :
1510 105229 : if (options & KRB5_GC_ANONYMOUS) {
1511 0 : ret = krb5_make_principal(context,
1512 : &mcreds.client,
1513 0 : krb5_principal_get_realm(context, mcreds.client),
1514 : KRB5_WELLKNOWN_NAME,
1515 : KRB5_ANON_NAME,
1516 : NULL);
1517 0 : if (ret)
1518 0 : return ret;
1519 : }
1520 :
1521 105229 : ret = krb5_cc_retrieve_cred(context, ccache,
1522 : (options &
1523 : (KRB5_TC_DONT_MATCH_REALM |
1524 : KRB5_TC_MATCH_KEYTYPE |
1525 : KRB5_TC_MATCH_TIMES)),
1526 : &mcreds, out_creds);
1527 :
1528 105229 : if (options & KRB5_GC_ANONYMOUS)
1529 0 : krb5_free_principal(context, mcreds.client);
1530 :
1531 105229 : if (ret == 0 && out_creds->server->realm &&
1532 83449 : out_creds->server->realm[0] == '\0') {
1533 0 : Ticket ticket;
1534 :
1535 : /*
1536 : * We only write tickets to the ccache that have been validated, as in,
1537 : * the sname/srealm from the KDC-REP enc-part have been checked to
1538 : * match the sname/realm from the Ticket from the KDC-REP.
1539 : *
1540 : * Our caller needs the canonical realm of the service in order to be
1541 : * able to get forwarded credentials for it when destination-TGT
1542 : * forwarding is enabled.
1543 : *
1544 : * As well, gss_init_sec_context() ought to arrange for
1545 : * gss_inquire_context() to output the canonical acceptor name on the
1546 : * initiator side.
1547 : */
1548 0 : ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length,
1549 : &ticket, NULL);
1550 0 : if (ret == 0) {
1551 0 : ret = krb5_principal_set_realm(context, out_creds->server,
1552 0 : ticket.realm);
1553 0 : free_Ticket(&ticket);
1554 : } else {
1555 0 : krb5_free_cred_contents(context, out_creds);
1556 : }
1557 : }
1558 101089 : return ret;
1559 : }
1560 :
1561 : static void
1562 17129 : store_cred(krb5_context context, krb5_ccache ccache,
1563 : krb5_const_principal server_princ, krb5_creds *creds)
1564 : {
1565 17129 : if (context->no_ticket_store)
1566 0 : return;
1567 17193 : if (!krb5_principal_compare(context, creds->server, server_princ) &&
1568 64 : !krb5_principal_is_krbtgt(context, server_princ)) {
1569 64 : krb5_principal tmp_princ = creds->server;
1570 : /*
1571 : * Store the cred with the pre-canon server princ first so it
1572 : * can be found quickly in the future.
1573 : */
1574 64 : creds->server = (krb5_principal)server_princ;
1575 64 : krb5_cc_store_cred(context, ccache, creds);
1576 64 : creds->server = tmp_princ;
1577 : /* Then store again with the canonicalized server princ */
1578 : }
1579 17129 : krb5_cc_store_cred(context, ccache, creds);
1580 : }
1581 :
1582 :
1583 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1584 100868 : krb5_get_credentials_with_flags(krb5_context context,
1585 : krb5_flags options,
1586 : krb5_kdc_flags flags,
1587 : krb5_ccache ccache,
1588 : krb5_creds *in_creds,
1589 : krb5_creds **out_creds)
1590 : {
1591 4140 : struct krb5_fast_state fast_state;
1592 4140 : krb5_error_code ret;
1593 100868 : krb5_name_canon_iterator name_canon_iter = NULL;
1594 4140 : krb5_name_canon_rule_options rule_opts;
1595 100868 : krb5_const_principal try_princ = NULL;
1596 100868 : krb5_principal save_princ = in_creds->server;
1597 4140 : krb5_creds **tgts;
1598 4140 : krb5_creds *res_creds;
1599 4140 : int i;
1600 :
1601 100868 : memset(&fast_state, 0, sizeof(fast_state));
1602 :
1603 100868 : if (_krb5_have_debug(context, 5)) {
1604 0 : char *unparsed;
1605 :
1606 0 : ret = krb5_unparse_name(context, in_creds->server, &unparsed);
1607 0 : if (ret) {
1608 0 : _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1609 : "requested service principal");
1610 : } else {
1611 0 : _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
1612 : "for %s", unparsed);
1613 0 : free(unparsed);
1614 : }
1615 : }
1616 :
1617 100868 : if (in_creds->session.keytype) {
1618 0 : ret = krb5_enctype_valid(context, in_creds->session.keytype);
1619 0 : if (ret)
1620 0 : return ret;
1621 0 : options |= KRB5_TC_MATCH_KEYTYPE;
1622 : }
1623 :
1624 100868 : *out_creds = NULL;
1625 100868 : res_creds = calloc(1, sizeof(*res_creds));
1626 100868 : if (res_creds == NULL)
1627 0 : return krb5_enomem(context);
1628 :
1629 100868 : ret = krb5_name_canon_iterator_start(context, in_creds->server,
1630 : &name_canon_iter);
1631 100868 : if (ret)
1632 0 : goto out;
1633 :
1634 100868 : next_rule:
1635 101174 : krb5_free_cred_contents(context, res_creds);
1636 101174 : memset(res_creds, 0, sizeof (*res_creds));
1637 101174 : ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
1638 : &rule_opts);
1639 101174 : in_creds->server = rk_UNCONST(try_princ);
1640 101174 : if (ret)
1641 0 : goto out;
1642 :
1643 101174 : if (name_canon_iter == NULL) {
1644 306 : if (options & KRB5_GC_CACHED)
1645 9 : ret = KRB5_CC_NOTFOUND;
1646 : else
1647 297 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1648 306 : goto out;
1649 : }
1650 :
1651 100868 : ret = check_cc(context, options, ccache, in_creds, res_creds);
1652 100868 : if (ret == 0) {
1653 83449 : *out_creds = res_creds;
1654 83449 : res_creds = NULL;
1655 83449 : goto out;
1656 17419 : } else if(ret != KRB5_CC_END) {
1657 0 : goto out;
1658 : }
1659 17419 : if (options & KRB5_GC_CACHED)
1660 9 : goto next_rule;
1661 :
1662 17410 : if(options & KRB5_GC_USER_USER)
1663 0 : flags.b.enc_tkt_in_skey = 1;
1664 17410 : if (flags.b.enc_tkt_in_skey)
1665 0 : options |= KRB5_GC_NO_STORE;
1666 :
1667 17410 : tgts = NULL;
1668 17410 : ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state,
1669 : in_creds, NULL, NULL, out_creds, &tgts);
1670 18114 : for (i = 0; tgts && tgts[i]; i++) {
1671 81 : if ((options & KRB5_GC_NO_STORE) == 0)
1672 81 : krb5_cc_store_cred(context, ccache, tgts[i]);
1673 81 : krb5_free_creds(context, tgts[i]);
1674 : }
1675 17410 : free(tgts);
1676 :
1677 : /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
1678 17410 : if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
1679 297 : !(rule_opts & KRB5_NCRO_USE_FAST))
1680 297 : goto next_rule;
1681 :
1682 17113 : if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1683 17079 : store_cred(context, ccache, in_creds->server, *out_creds);
1684 :
1685 17113 : if (ret == 0 && _krb5_have_debug(context, 5)) {
1686 0 : char *unparsed;
1687 :
1688 0 : ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
1689 0 : if (ret) {
1690 0 : _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1691 : "service principal");
1692 : } else {
1693 0 : _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
1694 : unparsed);
1695 0 : free(unparsed);
1696 : }
1697 : }
1698 :
1699 17113 : out:
1700 100868 : in_creds->server = save_princ;
1701 100868 : krb5_free_creds(context, res_creds);
1702 100868 : krb5_free_name_canon_iterator(context, name_canon_iter);
1703 100868 : _krb5_fast_free(context, &fast_state);
1704 100868 : if (ret)
1705 340 : return not_found(context, in_creds->server, ret);
1706 96388 : return 0;
1707 : }
1708 :
1709 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1710 100868 : krb5_get_credentials(krb5_context context,
1711 : krb5_flags options,
1712 : krb5_ccache ccache,
1713 : krb5_creds *in_creds,
1714 : krb5_creds **out_creds)
1715 : {
1716 4140 : krb5_kdc_flags flags;
1717 100868 : flags.i = 0;
1718 100868 : return krb5_get_credentials_with_flags(context, options, flags,
1719 : ccache, in_creds, out_creds);
1720 : }
1721 :
1722 : struct krb5_get_creds_opt_data {
1723 : krb5_principal self;
1724 : krb5_flags options;
1725 : krb5_enctype enctype;
1726 : Ticket *ticket;
1727 : };
1728 :
1729 :
1730 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1731 3227 : krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
1732 : {
1733 3227 : *opt = calloc(1, sizeof(**opt));
1734 3227 : if (*opt == NULL)
1735 0 : return krb5_enomem(context);
1736 3227 : return 0;
1737 : }
1738 :
1739 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1740 345 : krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt)
1741 : {
1742 345 : if (opt->self)
1743 38 : krb5_free_principal(context, opt->self);
1744 345 : if (opt->ticket) {
1745 16 : free_Ticket(opt->ticket);
1746 16 : free(opt->ticket);
1747 : }
1748 345 : memset(opt, 0, sizeof(*opt));
1749 345 : free(opt);
1750 345 : }
1751 :
1752 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1753 36 : krb5_get_creds_opt_set_options(krb5_context context,
1754 : krb5_get_creds_opt opt,
1755 : krb5_flags options)
1756 : {
1757 36 : opt->options = options;
1758 36 : }
1759 :
1760 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1761 6216 : krb5_get_creds_opt_add_options(krb5_context context,
1762 : krb5_get_creds_opt opt,
1763 : krb5_flags options)
1764 : {
1765 6216 : opt->options |= options;
1766 6216 : }
1767 :
1768 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
1769 0 : krb5_get_creds_opt_set_enctype(krb5_context context,
1770 : krb5_get_creds_opt opt,
1771 : krb5_enctype enctype)
1772 : {
1773 0 : opt->enctype = enctype;
1774 0 : }
1775 :
1776 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1777 758 : krb5_get_creds_opt_set_impersonate(krb5_context context,
1778 : krb5_get_creds_opt opt,
1779 : krb5_const_principal self)
1780 : {
1781 758 : if (opt->self)
1782 0 : krb5_free_principal(context, opt->self);
1783 758 : return krb5_copy_principal(context, self, &opt->self);
1784 : }
1785 :
1786 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1787 18 : krb5_get_creds_opt_set_ticket(krb5_context context,
1788 : krb5_get_creds_opt opt,
1789 : const Ticket *ticket)
1790 : {
1791 18 : if (opt->ticket) {
1792 0 : free_Ticket(opt->ticket);
1793 0 : free(opt->ticket);
1794 0 : opt->ticket = NULL;
1795 : }
1796 18 : if (ticket) {
1797 0 : krb5_error_code ret;
1798 :
1799 18 : opt->ticket = malloc(sizeof(*ticket));
1800 18 : if (opt->ticket == NULL)
1801 0 : return krb5_enomem(context);
1802 18 : ret = copy_Ticket(ticket, opt->ticket);
1803 18 : if (ret) {
1804 0 : free(opt->ticket);
1805 0 : opt->ticket = NULL;
1806 0 : krb5_set_error_message(context, ret,
1807 0 : N_("malloc: out of memory", ""));
1808 0 : return ret;
1809 : }
1810 : }
1811 18 : return 0;
1812 : }
1813 :
1814 :
1815 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1816 4379 : krb5_get_creds(krb5_context context,
1817 : krb5_get_creds_opt opt,
1818 : krb5_ccache ccache,
1819 : krb5_const_principal inprinc,
1820 : krb5_creds **out_creds)
1821 : {
1822 0 : struct krb5_fast_state fast_state;
1823 0 : krb5_kdc_flags flags;
1824 0 : krb5_flags options;
1825 0 : krb5_creds in_creds;
1826 0 : krb5_error_code ret;
1827 0 : krb5_creds **tgts;
1828 0 : krb5_creds *res_creds;
1829 4379 : krb5_const_principal try_princ = NULL;
1830 4379 : krb5_name_canon_iterator name_canon_iter = NULL;
1831 0 : krb5_name_canon_rule_options rule_opts;
1832 0 : int i;
1833 0 : int type;
1834 0 : const char *comp;
1835 :
1836 4379 : memset(&fast_state, 0, sizeof(fast_state));
1837 4379 : memset(&in_creds, 0, sizeof(in_creds));
1838 4379 : in_creds.server = rk_UNCONST(inprinc);
1839 :
1840 4379 : if (_krb5_have_debug(context, 5)) {
1841 0 : char *unparsed;
1842 :
1843 0 : ret = krb5_unparse_name(context, in_creds.server, &unparsed);
1844 0 : if (ret) {
1845 0 : _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1846 : "requested service principal");
1847 : } else {
1848 0 : _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
1849 : "for %s", unparsed);
1850 0 : free(unparsed);
1851 : }
1852 : }
1853 :
1854 4379 : if (opt && opt->enctype) {
1855 0 : ret = krb5_enctype_valid(context, opt->enctype);
1856 0 : if (ret)
1857 0 : return ret;
1858 : }
1859 :
1860 4379 : ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
1861 4379 : if (ret)
1862 0 : return ret;
1863 :
1864 4379 : if (opt)
1865 4379 : options = opt->options;
1866 : else
1867 0 : options = 0;
1868 4379 : flags.i = 0;
1869 :
1870 4379 : *out_creds = NULL;
1871 4379 : res_creds = calloc(1, sizeof(*res_creds));
1872 4379 : if (res_creds == NULL) {
1873 0 : krb5_free_principal(context, in_creds.client);
1874 0 : return krb5_enomem(context);
1875 : }
1876 :
1877 4379 : if (opt && opt->enctype) {
1878 0 : in_creds.session.keytype = opt->enctype;
1879 0 : options |= KRB5_TC_MATCH_KEYTYPE;
1880 : }
1881 :
1882 4379 : ret = krb5_name_canon_iterator_start(context, in_creds.server,
1883 : &name_canon_iter);
1884 4379 : if (ret)
1885 0 : goto out;
1886 :
1887 4379 : next_rule:
1888 4811 : ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
1889 : &rule_opts);
1890 4811 : in_creds.server = rk_UNCONST(try_princ);
1891 4811 : if (ret)
1892 0 : goto out;
1893 :
1894 4811 : if (name_canon_iter == NULL) {
1895 432 : if (options & KRB5_GC_CACHED)
1896 0 : ret = KRB5_CC_NOTFOUND;
1897 : else
1898 432 : ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1899 432 : goto out;
1900 : }
1901 :
1902 4379 : if ((options & KRB5_GC_CONSTRAINED_DELEGATION) == 0) {
1903 4361 : ret = check_cc(context, options, ccache, &in_creds, res_creds);
1904 4361 : if (ret == 0) {
1905 0 : *out_creds = res_creds;
1906 0 : res_creds = NULL;
1907 0 : goto out;
1908 4361 : } else if (ret != KRB5_CC_END) {
1909 0 : goto out;
1910 : }
1911 : }
1912 4379 : if (options & KRB5_GC_CACHED)
1913 0 : goto next_rule;
1914 :
1915 4379 : type = krb5_principal_get_type(context, try_princ);
1916 4379 : comp = krb5_principal_get_comp_string(context, try_princ, 0);
1917 4379 : if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) &&
1918 94 : comp != NULL && strcmp(comp, "host") == 0)
1919 30 : flags.b.canonicalize = 1;
1920 4379 : if (rule_opts & KRB5_NCRO_NO_REFERRALS)
1921 0 : flags.b.canonicalize = 0;
1922 : else
1923 4379 : flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0;
1924 4379 : if (options & KRB5_GC_USER_USER) {
1925 0 : flags.b.enc_tkt_in_skey = 1;
1926 0 : options |= KRB5_GC_NO_STORE;
1927 : }
1928 4379 : if (options & KRB5_GC_FORWARDABLE)
1929 18 : flags.b.forwardable = 1;
1930 4379 : if (options & KRB5_GC_NO_TRANSIT_CHECK)
1931 0 : flags.b.disable_transited_check = 1;
1932 4379 : if (options & KRB5_GC_CONSTRAINED_DELEGATION)
1933 18 : flags.b.cname_in_addl_tkt = 1;
1934 4379 : if (options & KRB5_GC_ANONYMOUS)
1935 0 : flags.b.request_anonymous = 1;
1936 :
1937 4379 : tgts = NULL;
1938 4379 : ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state,
1939 : &in_creds, opt ? opt->self : 0,
1940 : opt ? opt->ticket : 0, out_creds,
1941 : &tgts);
1942 4380 : for (i = 0; tgts && tgts[i]; i++) {
1943 1 : if ((options & KRB5_GC_NO_STORE) == 0)
1944 1 : krb5_cc_store_cred(context, ccache, tgts[i]);
1945 1 : krb5_free_creds(context, tgts[i]);
1946 : }
1947 4379 : free(tgts);
1948 :
1949 : /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
1950 4379 : if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
1951 432 : !(rule_opts & KRB5_NCRO_USE_FAST))
1952 432 : goto next_rule;
1953 :
1954 3947 : if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
1955 50 : store_cred(context, ccache, inprinc, *out_creds);
1956 :
1957 3947 : if (ret == 0 && _krb5_have_debug(context, 5)) {
1958 0 : char *unparsed;
1959 :
1960 0 : ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
1961 0 : if (ret) {
1962 0 : _krb5_debug(context, 5, "krb5_get_creds: unable to display "
1963 : "service principal");
1964 : } else {
1965 0 : _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
1966 : unparsed);
1967 0 : free(unparsed);
1968 : }
1969 : }
1970 :
1971 3947 : out:
1972 4379 : _krb5_fast_free(context, &fast_state);
1973 4379 : krb5_free_creds(context, res_creds);
1974 4379 : krb5_free_principal(context, in_creds.client);
1975 4379 : krb5_free_name_canon_iterator(context, name_canon_iter);
1976 4379 : if (ret)
1977 2450 : return not_found(context, inprinc, ret);
1978 1929 : return ret;
1979 : }
1980 :
1981 : /*
1982 : *
1983 : */
1984 :
1985 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1986 0 : krb5_get_renewed_creds(krb5_context context,
1987 : krb5_creds *creds,
1988 : krb5_const_principal client,
1989 : krb5_ccache ccache,
1990 : const char *in_tkt_service)
1991 : {
1992 0 : krb5_error_code ret;
1993 0 : krb5_kdc_flags flags;
1994 0 : krb5_creds in, *template, *out = NULL;
1995 :
1996 0 : memset(&in, 0, sizeof(in));
1997 0 : memset(creds, 0, sizeof(*creds));
1998 :
1999 0 : ret = krb5_copy_principal(context, client, &in.client);
2000 0 : if (ret)
2001 0 : return ret;
2002 :
2003 0 : if (in_tkt_service) {
2004 0 : ret = krb5_parse_name(context, in_tkt_service, &in.server);
2005 0 : if (ret) {
2006 0 : krb5_free_principal(context, in.client);
2007 0 : return ret;
2008 : }
2009 : } else {
2010 0 : const char *realm = krb5_principal_get_realm(context, client);
2011 :
2012 0 : ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME,
2013 : realm, NULL);
2014 0 : if (ret) {
2015 0 : krb5_free_principal(context, in.client);
2016 0 : return ret;
2017 : }
2018 : }
2019 :
2020 0 : flags.i = 0;
2021 0 : flags.b.renewable = flags.b.renew = 1;
2022 :
2023 : /*
2024 : * Get template from old credential cache for the same entry, if
2025 : * this failes, no worries.
2026 : */
2027 0 : ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template);
2028 0 : if (ret == 0) {
2029 0 : flags.b.forwardable = template->flags.b.forwardable;
2030 0 : flags.b.proxiable = template->flags.b.proxiable;
2031 0 : krb5_free_creds (context, template);
2032 : }
2033 :
2034 0 : ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out);
2035 0 : krb5_free_principal(context, in.client);
2036 0 : krb5_free_principal(context, in.server);
2037 0 : if (ret)
2038 0 : return ret;
2039 :
2040 0 : ret = krb5_copy_creds_contents(context, out, creds);
2041 0 : krb5_free_creds(context, out);
2042 :
2043 0 : return ret;
2044 : }
|