Line data Source code
1 : /*
2 : * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 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 :
38 : /**
39 : * Free ticket and content
40 : *
41 : * @param context a Kerberos 5 context
42 : * @param ticket ticket to free
43 : *
44 : * @return Returns 0 to indicate success. Otherwise an kerberos et
45 : * error code is returned, see krb5_get_error_message().
46 : *
47 : * @ingroup krb5
48 : */
49 :
50 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
51 158857 : krb5_free_ticket(krb5_context context,
52 : krb5_ticket *ticket)
53 : {
54 158857 : free_EncTicketPart(&ticket->ticket);
55 158857 : krb5_free_principal(context, ticket->client);
56 158857 : krb5_free_principal(context, ticket->server);
57 158857 : free(ticket);
58 158857 : return 0;
59 : }
60 :
61 : /**
62 : * Copy ticket and content
63 : *
64 : * @param context a Kerberos 5 context
65 : * @param from ticket to copy
66 : * @param to new copy of ticket, free with krb5_free_ticket()
67 : *
68 : * @return Returns 0 to indicate success. Otherwise an kerberos et
69 : * error code is returned, see krb5_get_error_message().
70 : *
71 : * @ingroup krb5
72 : */
73 :
74 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75 53442 : krb5_copy_ticket(krb5_context context,
76 : const krb5_ticket *from,
77 : krb5_ticket **to)
78 : {
79 881 : krb5_error_code ret;
80 881 : krb5_ticket *tmp;
81 :
82 53442 : *to = NULL;
83 53442 : tmp = malloc(sizeof(*tmp));
84 53442 : if (tmp == NULL)
85 0 : return krb5_enomem(context);
86 53442 : if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
87 0 : free(tmp);
88 0 : return ret;
89 : }
90 53442 : ret = krb5_copy_principal(context, from->client, &tmp->client);
91 53442 : if(ret){
92 0 : free_EncTicketPart(&tmp->ticket);
93 0 : free(tmp);
94 0 : return ret;
95 : }
96 53442 : ret = krb5_copy_principal(context, from->server, &tmp->server);
97 53442 : if(ret){
98 0 : krb5_free_principal(context, tmp->client);
99 0 : free_EncTicketPart(&tmp->ticket);
100 0 : free(tmp);
101 0 : return ret;
102 : }
103 53442 : *to = tmp;
104 53442 : return 0;
105 : }
106 :
107 : /**
108 : * Return client principal in ticket
109 : *
110 : * @param context a Kerberos 5 context
111 : * @param ticket ticket to copy
112 : * @param client client principal, free with krb5_free_principal()
113 : *
114 : * @return Returns 0 to indicate success. Otherwise an kerberos et
115 : * error code is returned, see krb5_get_error_message().
116 : *
117 : * @ingroup krb5
118 : */
119 :
120 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
121 1212 : krb5_ticket_get_client(krb5_context context,
122 : const krb5_ticket *ticket,
123 : krb5_principal *client)
124 : {
125 1212 : return krb5_copy_principal(context, ticket->client, client);
126 : }
127 :
128 : /**
129 : * Return server principal in ticket
130 : *
131 : * @param context a Kerberos 5 context
132 : * @param ticket ticket to copy
133 : * @param server server principal, free with krb5_free_principal()
134 : *
135 : * @return Returns 0 to indicate success. Otherwise an kerberos et
136 : * error code is returned, see krb5_get_error_message().
137 : *
138 : * @ingroup krb5
139 : */
140 :
141 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
142 0 : krb5_ticket_get_server(krb5_context context,
143 : const krb5_ticket *ticket,
144 : krb5_principal *server)
145 : {
146 0 : return krb5_copy_principal(context, ticket->server, server);
147 : }
148 :
149 : /**
150 : * Return end time of a ticket
151 : *
152 : * @param context a Kerberos 5 context
153 : * @param ticket ticket to copy
154 : *
155 : * @return end time of ticket
156 : *
157 : * @ingroup krb5
158 : */
159 :
160 : KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
161 0 : krb5_ticket_get_endtime(krb5_context context,
162 : const krb5_ticket *ticket)
163 : {
164 0 : return ticket->ticket.endtime;
165 : }
166 :
167 : /**
168 : * Return authentication, start, end, and renew limit times of a ticket
169 : *
170 : * @param context a Kerberos 5 context
171 : * @param ticket ticket to copy
172 : * @param t pointer to krb5_times structure
173 : *
174 : * @ingroup krb5
175 : */
176 :
177 : KRB5_LIB_FUNCTION void KRB5_LIB_CALL
178 0 : krb5_ticket_get_times(krb5_context context,
179 : const krb5_ticket *ticket,
180 : krb5_times *t)
181 : {
182 0 : t->authtime = ticket->ticket.authtime;
183 0 : t->starttime = ticket->ticket.starttime ? *ticket->ticket.starttime :
184 : t->authtime;
185 0 : t->endtime = ticket->ticket.endtime;
186 0 : t->renew_till = ticket->ticket.renew_till ? *ticket->ticket.renew_till :
187 : t->endtime;
188 0 : }
189 :
190 : /**
191 : * Get the flags from the Kerberos ticket
192 : *
193 : * @param context Kerberos context
194 : * @param ticket Kerberos ticket
195 : *
196 : * @return ticket flags
197 : *
198 : * @ingroup krb5_ticket
199 : */
200 : KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
201 0 : krb5_ticket_get_flags(krb5_context context,
202 : const krb5_ticket *ticket)
203 : {
204 0 : return TicketFlags2int(ticket->ticket.flags);
205 : }
206 :
207 : /*
208 : * Find an authz-data element in the given `ad'. If `failp', then validate any
209 : * containing AD-KDC-ISSUED's keyed checksum with the `sessionkey' (if given).
210 : *
211 : * All AD-KDC-ISSUED will be validated (if requested) even when `type' is
212 : * `KRB5_AUTHDATA_KDC_ISSUED'.
213 : *
214 : * Only the first matching element will be output (via `data').
215 : *
216 : * Note that all AD-KDC-ISSUEDs found while traversing the authz-data will be
217 : * validated, though only the first one will be returned.
218 : *
219 : * XXX We really need a better interface though. First, forget AD-AND-OR --
220 : * just remove it. Second, probably forget AD-KDC-ISSUED, but still, between
221 : * that, the PAC, and the CAMMAC, we need an interface that can:
222 : *
223 : * a) take the derived keys instead of the service key or the session key,
224 : * b) can indicate whether the element was marked critical,
225 : * c) can indicate whether the element was authenticated to the KDC,
226 : * d) can iterate over all the instances found (if more than one is found).
227 : *
228 : * Also, we need to know here if the authz-data is from a Ticket or from an
229 : * Authenticator -- if the latter then we must refuse to find AD-KDC-ISSUED /
230 : * PAC / CAMMAC or anything of the sort, ever.
231 : */
232 : static int
233 1055704 : find_type_in_ad(krb5_context context,
234 : int type,
235 : krb5_data *data, /* optional */
236 : krb5_boolean *found,
237 : krb5_boolean failp, /* validate AD-KDC-ISSUED */
238 : krb5_keyblock *sessionkey, /* ticket session key */
239 : const AuthorizationData *ad,
240 : int level)
241 : {
242 1055704 : krb5_error_code ret = 0;
243 27048 : size_t i;
244 :
245 1055704 : if (level > 9) {
246 0 : ret = ENOENT; /* XXX */
247 0 : krb5_set_error_message(context, ret,
248 0 : N_("Authorization data nested deeper "
249 : "then %d levels, stop searching", ""),
250 : level);
251 0 : goto out;
252 : }
253 :
254 : /*
255 : * Only copy out the element the first time we get to it, we need
256 : * to run over the whole authorization data fields to check if
257 : * there are any container clases we need to care about.
258 : */
259 2019872 : for (i = 0; i < ad->len; i++) {
260 1055884 : if (!*found && ad->val[i].ad_type == type) {
261 202897 : if (data) {
262 202897 : ret = der_copy_octet_string(&ad->val[i].ad_data, data);
263 202897 : if (ret) {
264 0 : krb5_set_error_message(context, ret,
265 0 : N_("malloc: out of memory", ""));
266 0 : goto out;
267 : }
268 : }
269 202897 : *found = TRUE;
270 207198 : if (type != KRB5_AUTHDATA_KDC_ISSUED ||
271 4301 : !failp || !sessionkey || !sessionkey->keyvalue.length)
272 202897 : continue;
273 : /* else go on to validate the AD-KDC-ISSUED's keyed checksum */
274 : }
275 852987 : switch (ad->val[i].ad_type) {
276 460096 : case KRB5_AUTHDATA_IF_RELEVANT: {
277 11037 : AuthorizationData child;
278 460096 : ret = decode_AuthorizationData(ad->val[i].ad_data.data,
279 449059 : ad->val[i].ad_data.length,
280 : &child,
281 : NULL);
282 460096 : if (ret) {
283 0 : krb5_set_error_message(context, ret,
284 0 : N_("Failed to decode "
285 : "IF_RELEVANT with %d", ""),
286 : (int)ret);
287 0 : goto out;
288 : }
289 460096 : ret = find_type_in_ad(context, type, data, found, FALSE,
290 : sessionkey, &child, level + 1);
291 460096 : free_AuthorizationData(&child);
292 460096 : if (ret)
293 0 : goto out;
294 460096 : break;
295 : }
296 0 : case KRB5_AUTHDATA_KDC_ISSUED: {
297 0 : AD_KDCIssued child;
298 :
299 0 : ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
300 0 : ad->val[i].ad_data.length,
301 : &child,
302 : NULL);
303 0 : if (ret) {
304 0 : krb5_set_error_message(context, ret,
305 0 : N_("Failed to decode "
306 : "AD_KDCIssued with %d", ""),
307 : ret);
308 0 : goto out;
309 : }
310 0 : if (failp && sessionkey && sessionkey->keyvalue.length) {
311 0 : krb5_boolean valid;
312 0 : krb5_data buf;
313 0 : size_t len;
314 :
315 0 : ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
316 : &child.elements, &len, ret);
317 0 : if (ret) {
318 0 : free_AD_KDCIssued(&child);
319 0 : krb5_clear_error_message(context);
320 0 : goto out;
321 : }
322 0 : if(buf.length != len)
323 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
324 :
325 0 : ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
326 : &child.ad_checksum, &valid);
327 0 : krb5_data_free(&buf);
328 0 : if (ret) {
329 0 : free_AD_KDCIssued(&child);
330 0 : goto out;
331 : }
332 0 : if (!valid) {
333 0 : krb5_clear_error_message(context);
334 0 : ret = ENOENT;
335 0 : free_AD_KDCIssued(&child);
336 0 : goto out;
337 : }
338 0 : } else if (failp) {
339 0 : krb5_clear_error_message(context);
340 0 : ret = ENOENT;
341 0 : free_AD_KDCIssued(&child);
342 0 : goto out;
343 : }
344 0 : ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
345 : &child.elements, level + 1);
346 0 : free_AD_KDCIssued(&child);
347 0 : if (ret)
348 0 : goto out;
349 0 : break;
350 : }
351 0 : case KRB5_AUTHDATA_AND_OR:
352 0 : if (!failp)
353 0 : break;
354 0 : ret = ENOENT; /* XXX */
355 0 : krb5_set_error_message(context, ret,
356 0 : N_("Authorization data contains "
357 : "AND-OR element that is unknown to the "
358 : "application", ""));
359 0 : goto out;
360 392891 : default:
361 392891 : if (!failp)
362 292781 : break;
363 91716 : ret = ENOENT; /* XXX */
364 91716 : krb5_set_error_message(context, ret,
365 91716 : N_("Authorization data contains "
366 : "unknown type (%d) ", ""),
367 88400 : ad->val[i].ad_type);
368 91716 : goto out;
369 : }
370 : }
371 963988 : out:
372 1055704 : if (ret) {
373 91716 : if (*found) {
374 0 : if (data)
375 0 : krb5_data_free(data);
376 0 : *found = 0;
377 : }
378 : }
379 1055704 : return ret;
380 : }
381 :
382 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
383 347019 : _krb5_get_ad(krb5_context context,
384 : const AuthorizationData *ad,
385 : krb5_keyblock *sessionkey,
386 : int type,
387 : krb5_data *data)
388 : {
389 347019 : krb5_boolean found = FALSE;
390 10052 : krb5_error_code ret;
391 :
392 347019 : if (data)
393 347019 : krb5_data_zero(data);
394 :
395 347019 : if (ad == NULL) {
396 12353 : krb5_set_error_message(context, ENOENT,
397 12353 : N_("No authorization data", ""));
398 12353 : return ENOENT; /* XXX */
399 : }
400 :
401 334666 : ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
402 334666 : if (ret)
403 88398 : return ret;
404 242952 : if (!found) {
405 146841 : krb5_set_error_message(context, ENOENT,
406 146841 : N_("Have no authorization data of type %d", ""),
407 : type);
408 146841 : return ENOENT; /* XXX */
409 : }
410 93572 : return 0;
411 : }
412 :
413 :
414 : /**
415 : * Extract the authorization data type of type from the ticket. Store
416 : * the field in data. This function is to use for kerberos
417 : * applications.
418 : *
419 : * @param context a Kerberos 5 context
420 : * @param ticket Kerberos ticket
421 : * @param type type to fetch
422 : * @param data returned data, free with krb5_data_free()
423 : *
424 : * @ingroup krb5
425 : */
426 :
427 : KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
428 261000 : krb5_ticket_get_authorization_data_type(krb5_context context,
429 : krb5_ticket *ticket,
430 : int type,
431 : krb5_data *data)
432 : {
433 5959 : AuthorizationData *ad;
434 5959 : krb5_error_code ret;
435 261000 : krb5_boolean found = FALSE;
436 :
437 261000 : if (data)
438 207558 : krb5_data_zero(data);
439 :
440 261000 : ad = ticket->ticket.authorization_data;
441 261000 : if (ticket->ticket.authorization_data == NULL) {
442 58 : krb5_set_error_message(context, ENOENT,
443 58 : N_("Ticket has no authorization data", ""));
444 58 : return ENOENT; /* XXX */
445 : }
446 :
447 266901 : ret = find_type_in_ad(context, type, data, &found, TRUE,
448 260942 : &ticket->ticket.key, ad, 0);
449 260942 : if (ret)
450 2 : return ret;
451 260940 : if (!found) {
452 154154 : krb5_set_error_message(context, ENOENT,
453 154154 : N_("Ticket has no "
454 : "authorization data of type %d", ""),
455 : type);
456 154154 : return ENOENT; /* XXX */
457 : }
458 105024 : return 0;
459 : }
460 :
461 : static krb5_error_code
462 46949 : check_server_referral(krb5_context context,
463 : krb5_kdc_rep *rep,
464 : unsigned flags,
465 : krb5_const_principal requested,
466 : krb5_const_principal returned,
467 : krb5_keyblock * key)
468 : {
469 1658 : krb5_error_code ret;
470 1658 : PA_ServerReferralData ref;
471 1658 : krb5_crypto session;
472 1658 : EncryptedData ed;
473 1658 : size_t len;
474 1658 : krb5_data data;
475 1658 : PA_DATA *pa;
476 46949 : int i = 0, cmp;
477 :
478 46949 : if (rep->kdc_rep.padata == NULL)
479 3766 : goto noreferral;
480 :
481 43183 : pa = krb5_find_padata(rep->kdc_rep.padata->val,
482 41525 : rep->kdc_rep.padata->len,
483 : KRB5_PADATA_SERVER_REFERRAL, &i);
484 43183 : if (pa == NULL)
485 42031 : goto noreferral;
486 :
487 1152 : memset(&ed, 0, sizeof(ed));
488 1152 : memset(&ref, 0, sizeof(ref));
489 :
490 1152 : ret = decode_EncryptedData(pa->padata_value.data,
491 : pa->padata_value.length,
492 : &ed, &len);
493 1152 : if (ret)
494 0 : return ret;
495 1152 : if (len != pa->padata_value.length) {
496 0 : free_EncryptedData(&ed);
497 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
498 0 : N_("Referral EncryptedData wrong for realm %s",
499 0 : "realm"), requested->realm);
500 0 : return KRB5KRB_AP_ERR_MODIFIED;
501 : }
502 :
503 1152 : ret = krb5_crypto_init(context, key, 0, &session);
504 1152 : if (ret) {
505 0 : free_EncryptedData(&ed);
506 0 : return ret;
507 : }
508 :
509 1152 : ret = krb5_decrypt_EncryptedData(context, session,
510 : KRB5_KU_PA_SERVER_REFERRAL,
511 : &ed, &data);
512 1152 : free_EncryptedData(&ed);
513 1152 : krb5_crypto_destroy(context, session);
514 1152 : if (ret)
515 0 : return ret;
516 :
517 1152 : ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
518 1152 : if (ret) {
519 0 : krb5_data_free(&data);
520 0 : return ret;
521 : }
522 1152 : krb5_data_free(&data);
523 :
524 1152 : if (strcmp(requested->realm, returned->realm) != 0) {
525 0 : free_PA_ServerReferralData(&ref);
526 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
527 0 : N_("server ref realm mismatch, "
528 : "requested realm %s got back %s", ""),
529 0 : requested->realm, returned->realm);
530 0 : return KRB5KRB_AP_ERR_MODIFIED;
531 : }
532 :
533 1152 : if (krb5_principal_is_krbtgt(context, returned)) {
534 1152 : const char *realm = returned->name.name_string.val[1];
535 :
536 1152 : if (ref.referred_realm == NULL
537 1152 : || strcmp(*ref.referred_realm, realm) != 0)
538 : {
539 0 : free_PA_ServerReferralData(&ref);
540 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
541 0 : N_("tgt returned with wrong ref", ""));
542 0 : return KRB5KRB_AP_ERR_MODIFIED;
543 : }
544 0 : } else if (krb5_principal_compare(context, returned, requested) == 0) {
545 0 : free_PA_ServerReferralData(&ref);
546 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
547 0 : N_("req princ no same as returned", ""));
548 0 : return KRB5KRB_AP_ERR_MODIFIED;
549 : }
550 :
551 1152 : if (ref.requested_principal_name) {
552 1152 : cmp = _krb5_principal_compare_PrincipalName(context,
553 : requested,
554 : ref.requested_principal_name);
555 1152 : if (!cmp) {
556 0 : free_PA_ServerReferralData(&ref);
557 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
558 0 : N_("referred principal not same "
559 : "as requested", ""));
560 0 : return KRB5KRB_AP_ERR_MODIFIED;
561 : }
562 0 : } else if (flags & EXTRACT_TICKET_AS_REQ) {
563 0 : free_PA_ServerReferralData(&ref);
564 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
565 0 : N_("Requested principal missing on AS-REQ", ""));
566 0 : return KRB5KRB_AP_ERR_MODIFIED;
567 : }
568 :
569 1152 : free_PA_ServerReferralData(&ref);
570 :
571 1152 : return ret;
572 45797 : noreferral:
573 : /*
574 : * Expect excact match or that we got a krbtgt
575 : */
576 48677 : if (krb5_principal_compare(context, requested, returned) != TRUE &&
577 2880 : (krb5_realm_compare(context, requested, returned) != TRUE &&
578 0 : krb5_principal_is_krbtgt(context, returned) != TRUE))
579 : {
580 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
581 0 : N_("Not same server principal returned "
582 : "as requested", ""));
583 0 : return KRB5KRB_AP_ERR_MODIFIED;
584 : }
585 44139 : return 0;
586 : }
587 :
588 : /*
589 : * Verify KDC supported anonymous if requested
590 : */
591 : static krb5_error_code
592 0 : check_client_anonymous(krb5_context context,
593 : krb5_kdc_rep *rep,
594 : krb5_const_principal requested,
595 : krb5_const_principal mapped,
596 : krb5_boolean is_tgs_rep)
597 : {
598 0 : int flags;
599 :
600 0 : if (!rep->enc_part.flags.anonymous)
601 0 : return KRB5KDC_ERR_BADOPTION;
602 :
603 : /*
604 : * Here we must validate that the AS returned a ticket of the expected type
605 : * for either a fully anonymous request, or authenticated request for an
606 : * anonymous ticket. If this is a TGS request, we're done. Then if the
607 : * 'requested' principal was anonymous, we'll check the 'mapped' principal
608 : * accordingly (without enforcing the name type and perhaps the realm).
609 : * Finally, if the 'requested' principal was not anonymous, well check
610 : * that the 'mapped' principal has an anonymous name and type, in a
611 : * non-anonymous realm. (Should we also be checking for a realm match
612 : * between the request and the mapped name in this case?)
613 : */
614 0 : if (is_tgs_rep)
615 0 : flags = KRB5_ANON_MATCH_ANY_NONT;
616 0 : else if (krb5_principal_is_anonymous(context, requested,
617 : KRB5_ANON_MATCH_ANY_NONT))
618 0 : flags = KRB5_ANON_MATCH_UNAUTHENTICATED | KRB5_ANON_IGNORE_NAME_TYPE;
619 : else
620 0 : flags = KRB5_ANON_MATCH_AUTHENTICATED;
621 :
622 0 : if (!krb5_principal_is_anonymous(context, mapped, flags))
623 0 : return KRB5KRB_AP_ERR_MODIFIED;
624 :
625 0 : return 0;
626 : }
627 :
628 : /*
629 : * Verify returned client principal name in anonymous/referral case
630 : */
631 :
632 : static krb5_error_code
633 46549 : check_client_mismatch(krb5_context context,
634 : krb5_kdc_rep *rep,
635 : krb5_const_principal requested,
636 : krb5_const_principal mapped,
637 : krb5_keyblock const * key)
638 : {
639 46549 : if (rep->enc_part.flags.anonymous) {
640 0 : if (!krb5_principal_is_anonymous(context, mapped,
641 : KRB5_ANON_MATCH_ANY_NONT)) {
642 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
643 0 : N_("Anonymous ticket does not contain anonymous "
644 : "principal", ""));
645 0 : return KRB5KRB_AP_ERR_MODIFIED;
646 : }
647 : } else {
648 46549 : if (krb5_principal_compare(context, requested, mapped) == FALSE &&
649 384 : !rep->enc_part.flags.enc_pa_rep) {
650 0 : krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
651 0 : N_("Not same client principal returned "
652 : "as requested", ""));
653 0 : return KRB5KRB_AP_ERR_MODIFIED;
654 : }
655 : }
656 :
657 44891 : return 0;
658 : }
659 :
660 :
661 : static krb5_error_code KRB5_CALLCONV
662 14095 : decrypt_tkt (krb5_context context,
663 : krb5_keyblock *key,
664 : krb5_key_usage usage,
665 : krb5_const_pointer decrypt_arg,
666 : krb5_kdc_rep *dec_rep)
667 : {
668 585 : krb5_error_code ret;
669 585 : krb5_data data;
670 585 : size_t size;
671 585 : krb5_crypto crypto;
672 :
673 14095 : ret = krb5_crypto_init(context, key, 0, &crypto);
674 14095 : if (ret)
675 0 : return ret;
676 :
677 14680 : ret = krb5_decrypt_EncryptedData (context,
678 : crypto,
679 : usage,
680 14095 : &dec_rep->kdc_rep.enc_part,
681 : &data);
682 14095 : krb5_crypto_destroy(context, crypto);
683 :
684 14095 : if (ret)
685 0 : return ret;
686 :
687 14680 : ret = decode_EncASRepPart(data.data,
688 : data.length,
689 14095 : &dec_rep->enc_part,
690 : &size);
691 14095 : if (ret)
692 0 : ret = decode_EncTGSRepPart(data.data,
693 : data.length,
694 0 : &dec_rep->enc_part,
695 : &size);
696 14095 : krb5_data_free (&data);
697 14095 : if (ret) {
698 0 : krb5_set_error_message(context, ret,
699 0 : N_("Failed to decode encpart in ticket", ""));
700 0 : return ret;
701 : }
702 13510 : return 0;
703 : }
704 :
705 : KRB5_LIB_FUNCTION int KRB5_LIB_CALL
706 60269 : _krb5_extract_ticket(krb5_context context,
707 : krb5_kdc_rep *rep,
708 : krb5_creds *creds,
709 : krb5_keyblock *key,
710 : krb5_const_pointer keyseed,
711 : krb5_key_usage key_usage,
712 : krb5_addresses *addrs,
713 : unsigned nonce,
714 : unsigned flags,
715 : krb5_data *request,
716 : krb5_decrypt_proc decrypt_proc,
717 : krb5_const_pointer decryptarg)
718 : {
719 2243 : krb5_error_code ret;
720 2243 : krb5_principal tmp_principal;
721 60269 : size_t len = 0;
722 2243 : time_t tmp_time;
723 2243 : krb5_timestamp sec_now;
724 :
725 : /* decrypt */
726 :
727 60269 : if (decrypt_proc == NULL)
728 14095 : decrypt_proc = decrypt_tkt;
729 :
730 60269 : ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
731 60269 : if (ret)
732 0 : goto out;
733 :
734 60269 : if (rep->enc_part.flags.enc_pa_rep && request) {
735 14095 : krb5_crypto crypto = NULL;
736 585 : Checksum cksum;
737 14095 : PA_DATA *pa = NULL;
738 14095 : int idx = 0;
739 :
740 14095 : _krb5_debug(context, 5, "processing enc-ap-rep");
741 :
742 27605 : if (rep->enc_part.encrypted_pa_data == NULL ||
743 14095 : (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
744 13510 : rep->enc_part.encrypted_pa_data->len,
745 : KRB5_PADATA_REQ_ENC_PA_REP,
746 : &idx)) == NULL)
747 : {
748 0 : _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
749 0 : ret = KRB5KRB_AP_ERR_MODIFIED;
750 12 : goto out;
751 : }
752 :
753 14095 : ret = krb5_crypto_init(context, key, 0, &crypto);
754 14095 : if (ret)
755 0 : goto out;
756 :
757 14095 : ret = decode_Checksum(pa->padata_value.data,
758 : pa->padata_value.length,
759 : &cksum, NULL);
760 14095 : if (ret) {
761 0 : krb5_crypto_destroy(context, crypto);
762 0 : goto out;
763 : }
764 :
765 14095 : ret = krb5_verify_checksum(context, crypto,
766 : KRB5_KU_AS_REQ,
767 : request->data, request->length,
768 : &cksum);
769 14095 : krb5_crypto_destroy(context, crypto);
770 14095 : free_Checksum(&cksum);
771 14095 : _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
772 14095 : if (ret)
773 12 : goto out;
774 : }
775 :
776 : /* save session key */
777 :
778 60257 : creds->session.keyvalue.length = 0;
779 60257 : creds->session.keyvalue.data = NULL;
780 60257 : creds->session.keytype = rep->enc_part.key.keytype;
781 62500 : ret = krb5_data_copy (&creds->session.keyvalue,
782 60257 : rep->enc_part.key.keyvalue.data,
783 : rep->enc_part.key.keyvalue.length);
784 60257 : if (ret) {
785 0 : krb5_clear_error_message(context);
786 0 : goto out;
787 : }
788 :
789 : /* compare client and save */
790 60257 : ret = _krb5_principalname2krb5_principal(context,
791 : &tmp_principal,
792 : rep->kdc_rep.cname,
793 : rep->kdc_rep.crealm);
794 60257 : if (ret)
795 0 : goto out;
796 :
797 : /* check KDC supported anonymous if it was requested */
798 60257 : if (flags & EXTRACT_TICKET_MATCH_ANON) {
799 0 : ret = check_client_anonymous(context,rep,
800 0 : creds->client,
801 : tmp_principal,
802 : request == NULL); /* is TGS */
803 0 : if (ret) {
804 0 : krb5_free_principal(context, tmp_principal);
805 0 : goto out;
806 : }
807 : }
808 :
809 : /* check client referral and save principal */
810 60257 : if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
811 48207 : ret = check_client_mismatch(context, rep,
812 46549 : creds->client,
813 : tmp_principal,
814 46549 : &creds->session);
815 46549 : if (ret) {
816 0 : krb5_free_principal (context, tmp_principal);
817 0 : goto out;
818 : }
819 : }
820 60257 : krb5_free_principal (context, creds->client);
821 60257 : creds->client = tmp_principal;
822 :
823 : /* check server referral and save principal */
824 60257 : ret = _krb5_kdcrep2krb5_principal(context, &tmp_principal, &rep->enc_part);
825 60257 : if (ret)
826 0 : goto out;
827 :
828 60257 : tmp_principal->nameattrs->peer_realm =
829 60257 : calloc(1, sizeof(tmp_principal->nameattrs->peer_realm[0]));
830 60257 : if (tmp_principal->nameattrs->peer_realm == NULL) {
831 0 : ret = krb5_enomem(context);
832 0 : goto out;
833 : }
834 60257 : ret = copy_Realm(&creds->client->realm, tmp_principal->nameattrs->peer_realm);
835 60257 : if (ret) goto out;
836 :
837 60257 : if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
838 48607 : ret = check_server_referral(context,
839 : rep,
840 : flags,
841 46949 : creds->server,
842 : tmp_principal,
843 : &creds->session);
844 46949 : if (ret) {
845 0 : krb5_free_principal (context, tmp_principal);
846 0 : goto out;
847 : }
848 : }
849 60257 : krb5_free_principal(context, creds->server);
850 60257 : creds->server = tmp_principal;
851 :
852 : /* verify names */
853 60257 : if(flags & EXTRACT_TICKET_MATCH_REALM){
854 13308 : const char *srealm = krb5_principal_get_realm(context, creds->server);
855 13308 : const char *crealm = krb5_principal_get_realm(context, creds->client);
856 :
857 13308 : if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
858 13308 : strcmp(rep->enc_part.srealm, crealm) != 0)
859 : {
860 0 : ret = KRB5KRB_AP_ERR_MODIFIED;
861 0 : krb5_clear_error_message(context);
862 0 : goto out;
863 : }
864 : }
865 :
866 : /* compare nonces */
867 :
868 60257 : if (nonce != (unsigned)rep->enc_part.nonce) {
869 0 : ret = KRB5KRB_AP_ERR_MODIFIED;
870 0 : krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
871 0 : goto out;
872 : }
873 :
874 : /* set kdc-offset */
875 :
876 60257 : krb5_timeofday (context, &sec_now);
877 60257 : if (rep->enc_part.flags.initial
878 14083 : && (flags & EXTRACT_TICKET_TIMESYNC)
879 14083 : && context->kdc_sec_offset == 0
880 14083 : && krb5_config_get_bool (context, NULL,
881 : "libdefaults",
882 : "kdc_timesync",
883 : NULL)) {
884 0 : context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
885 0 : krb5_timeofday (context, &sec_now);
886 : }
887 :
888 : /* check all times */
889 :
890 60257 : if (rep->enc_part.starttime) {
891 46174 : tmp_time = *rep->enc_part.starttime;
892 : } else
893 14083 : tmp_time = rep->enc_part.authtime;
894 :
895 60257 : if (creds->times.starttime == 0
896 60257 : && krb5_time_abs(tmp_time, sec_now) > context->max_skew) {
897 0 : ret = KRB5KRB_AP_ERR_SKEW;
898 0 : krb5_set_error_message (context, ret,
899 0 : N_("time skew (%ld) larger than max (%ld)", ""),
900 0 : (long)krb5_time_abs(tmp_time, sec_now),
901 0 : (long)context->max_skew);
902 0 : goto out;
903 : }
904 :
905 60257 : if (creds->times.starttime != 0
906 0 : && tmp_time != creds->times.starttime) {
907 0 : krb5_clear_error_message (context);
908 0 : ret = KRB5KRB_AP_ERR_MODIFIED;
909 0 : goto out;
910 : }
911 :
912 60257 : creds->times.starttime = tmp_time;
913 :
914 60257 : if (rep->enc_part.renew_till) {
915 4716 : tmp_time = *rep->enc_part.renew_till;
916 : } else
917 53298 : tmp_time = 0;
918 :
919 60257 : if (creds->times.renew_till != 0
920 1635 : && tmp_time > creds->times.renew_till) {
921 0 : krb5_clear_error_message (context);
922 0 : ret = KRB5KRB_AP_ERR_MODIFIED;
923 0 : goto out;
924 : }
925 :
926 60257 : creds->times.renew_till = tmp_time;
927 :
928 60257 : creds->times.authtime = rep->enc_part.authtime;
929 :
930 60257 : if (creds->times.endtime != 0
931 14133 : && rep->enc_part.endtime > creds->times.endtime) {
932 0 : krb5_clear_error_message (context);
933 0 : ret = KRB5KRB_AP_ERR_MODIFIED;
934 0 : goto out;
935 : }
936 :
937 60257 : creds->times.endtime = rep->enc_part.endtime;
938 :
939 60257 : if(rep->enc_part.caddr)
940 23031 : krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
941 37226 : else if(addrs)
942 23146 : krb5_copy_addresses (context, addrs, &creds->addresses);
943 : else {
944 14080 : creds->addresses.len = 0;
945 14080 : creds->addresses.val = NULL;
946 : }
947 60257 : creds->flags.b = rep->enc_part.flags;
948 :
949 60257 : creds->authdata.len = 0;
950 60257 : creds->authdata.val = NULL;
951 :
952 : /* extract ticket */
953 60257 : ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
954 : &rep->kdc_rep.ticket, &len, ret);
955 60257 : if(ret)
956 0 : goto out;
957 60257 : if (creds->ticket.length != len)
958 0 : krb5_abortx(context, "internal error in ASN.1 encoder");
959 60257 : creds->second_ticket.length = 0;
960 60257 : creds->second_ticket.data = NULL;
961 :
962 :
963 60269 : out:
964 60269 : memset (rep->enc_part.key.keyvalue.data, 0,
965 : rep->enc_part.key.keyvalue.length);
966 60269 : return ret;
967 : }
|