kafs: Fix a warning
[heimdal.git] / lib / krb5 / ticket.c
blobe2f2ab2085c6532c46cd100cc2298f1c39af8825
1 /*
2 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include "krb5_locl.h"
38 /**
39 * Free ticket and content
41 * @param context a Kerberos 5 context
42 * @param ticket ticket to free
44 * @return Returns 0 to indicate success. Otherwise an kerberos et
45 * error code is returned, see krb5_get_error_message().
47 * @ingroup krb5
50 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
51 krb5_free_ticket(krb5_context context,
52 krb5_ticket *ticket)
54 free_EncTicketPart(&ticket->ticket);
55 krb5_free_principal(context, ticket->client);
56 krb5_free_principal(context, ticket->server);
57 free(ticket);
58 return 0;
61 /**
62 * Copy ticket and content
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()
68 * @return Returns 0 to indicate success. Otherwise an kerberos et
69 * error code is returned, see krb5_get_error_message().
71 * @ingroup krb5
74 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75 krb5_copy_ticket(krb5_context context,
76 const krb5_ticket *from,
77 krb5_ticket **to)
79 krb5_error_code ret;
80 krb5_ticket *tmp;
82 *to = NULL;
83 tmp = malloc(sizeof(*tmp));
84 if (tmp == NULL)
85 return krb5_enomem(context);
86 if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
87 free(tmp);
88 return ret;
90 ret = krb5_copy_principal(context, from->client, &tmp->client);
91 if(ret){
92 free_EncTicketPart(&tmp->ticket);
93 free(tmp);
94 return ret;
96 ret = krb5_copy_principal(context, from->server, &tmp->server);
97 if(ret){
98 krb5_free_principal(context, tmp->client);
99 free_EncTicketPart(&tmp->ticket);
100 free(tmp);
101 return ret;
103 *to = tmp;
104 return 0;
108 * Return client principal in ticket
110 * @param context a Kerberos 5 context
111 * @param ticket ticket to copy
112 * @param client client principal, free with krb5_free_principal()
114 * @return Returns 0 to indicate success. Otherwise an kerberos et
115 * error code is returned, see krb5_get_error_message().
117 * @ingroup krb5
120 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
121 krb5_ticket_get_client(krb5_context context,
122 const krb5_ticket *ticket,
123 krb5_principal *client)
125 return krb5_copy_principal(context, ticket->client, client);
129 * Return server principal in ticket
131 * @param context a Kerberos 5 context
132 * @param ticket ticket to copy
133 * @param server server principal, free with krb5_free_principal()
135 * @return Returns 0 to indicate success. Otherwise an kerberos et
136 * error code is returned, see krb5_get_error_message().
138 * @ingroup krb5
141 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
142 krb5_ticket_get_server(krb5_context context,
143 const krb5_ticket *ticket,
144 krb5_principal *server)
146 return krb5_copy_principal(context, ticket->server, server);
150 * Return end time of a ticket
152 * @param context a Kerberos 5 context
153 * @param ticket ticket to copy
155 * @return end time of ticket
157 * @ingroup krb5
160 KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
161 krb5_ticket_get_endtime(krb5_context context,
162 const krb5_ticket *ticket)
164 return ticket->ticket.endtime;
168 * Return authentication, start, end, and renew limit times of a ticket
170 * @param context a Kerberos 5 context
171 * @param ticket ticket to copy
172 * @param t pointer to krb5_times structure
174 * @ingroup krb5
177 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
178 krb5_ticket_get_times(krb5_context context,
179 const krb5_ticket *ticket,
180 krb5_times *t)
182 t->authtime = ticket->ticket.authtime;
183 t->starttime = ticket->ticket.starttime ? *ticket->ticket.starttime :
184 t->authtime;
185 t->endtime = ticket->ticket.endtime;
186 t->renew_till = ticket->ticket.renew_till ? *ticket->ticket.renew_till :
187 t->endtime;
191 * Get the flags from the Kerberos ticket
193 * @param context Kerberos context
194 * @param ticket Kerberos ticket
196 * @return ticket flags
198 * @ingroup krb5_ticket
200 KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
201 krb5_ticket_get_flags(krb5_context context,
202 const krb5_ticket *ticket)
204 return TicketFlags2int(ticket->ticket.flags);
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).
211 * All AD-KDC-ISSUED will be validated (if requested) even when `type' is
212 * `KRB5_AUTHDATA_KDC_ISSUED'.
214 * Only the first matching element will be output (via `data').
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.
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:
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).
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.
232 static int
233 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)
242 krb5_error_code ret = 0;
243 size_t i;
245 if (level > 9) {
246 ret = ENOENT; /* XXX */
247 krb5_set_error_message(context, ret,
248 N_("Authorization data nested deeper "
249 "then %d levels, stop searching", ""),
250 level);
251 goto out;
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.
259 for (i = 0; i < ad->len; i++) {
260 if (!*found && ad->val[i].ad_type == type) {
261 if (data) {
262 ret = der_copy_octet_string(&ad->val[i].ad_data, data);
263 if (ret) {
264 krb5_set_error_message(context, ret,
265 N_("malloc: out of memory", ""));
266 goto out;
269 *found = TRUE;
270 if (type != KRB5_AUTHDATA_KDC_ISSUED ||
271 !failp || !sessionkey || !sessionkey->keyvalue.length)
272 continue;
273 /* else go on to validate the AD-KDC-ISSUED's keyed checksum */
275 switch (ad->val[i].ad_type) {
276 case KRB5_AUTHDATA_IF_RELEVANT: {
277 AuthorizationData child;
278 ret = decode_AuthorizationData(ad->val[i].ad_data.data,
279 ad->val[i].ad_data.length,
280 &child,
281 NULL);
282 if (ret) {
283 krb5_set_error_message(context, ret,
284 N_("Failed to decode "
285 "IF_RELEVANT with %d", ""),
286 (int)ret);
287 goto out;
289 ret = find_type_in_ad(context, type, data, found, FALSE,
290 sessionkey, &child, level + 1);
291 free_AuthorizationData(&child);
292 if (ret)
293 goto out;
294 break;
296 case KRB5_AUTHDATA_KDC_ISSUED: {
297 AD_KDCIssued child;
299 ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
300 ad->val[i].ad_data.length,
301 &child,
302 NULL);
303 if (ret) {
304 krb5_set_error_message(context, ret,
305 N_("Failed to decode "
306 "AD_KDCIssued with %d", ""),
307 ret);
308 goto out;
310 if (failp && sessionkey && sessionkey->keyvalue.length) {
311 krb5_boolean valid;
312 krb5_data buf;
313 size_t len;
315 ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
316 &child.elements, &len, ret);
317 if (ret) {
318 free_AD_KDCIssued(&child);
319 krb5_clear_error_message(context);
320 goto out;
322 if(buf.length != len)
323 krb5_abortx(context, "internal error in ASN.1 encoder");
325 ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
326 &child.ad_checksum, &valid);
327 krb5_data_free(&buf);
328 if (ret) {
329 free_AD_KDCIssued(&child);
330 goto out;
332 if (!valid) {
333 krb5_clear_error_message(context);
334 ret = ENOENT;
335 free_AD_KDCIssued(&child);
336 goto out;
338 } else if (failp) {
339 krb5_clear_error_message(context);
340 ret = ENOENT;
341 free_AD_KDCIssued(&child);
342 goto out;
344 ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
345 &child.elements, level + 1);
346 free_AD_KDCIssued(&child);
347 if (ret)
348 goto out;
349 break;
351 case KRB5_AUTHDATA_AND_OR:
352 if (!failp)
353 break;
354 ret = ENOENT; /* XXX */
355 krb5_set_error_message(context, ret,
356 N_("Authorization data contains "
357 "AND-OR element that is unknown to the "
358 "application", ""));
359 goto out;
360 default:
361 if (!failp)
362 break;
363 ret = ENOENT; /* XXX */
364 krb5_set_error_message(context, ret,
365 N_("Authorization data contains "
366 "unknown type (%d) ", ""),
367 ad->val[i].ad_type);
368 goto out;
371 out:
372 if (ret) {
373 if (*found) {
374 if (data)
375 krb5_data_free(data);
376 *found = 0;
379 return ret;
382 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
383 _krb5_get_ad(krb5_context context,
384 const AuthorizationData *ad,
385 krb5_keyblock *sessionkey,
386 int type,
387 krb5_data *data)
389 krb5_boolean found = FALSE;
390 krb5_error_code ret;
392 if (data)
393 krb5_data_zero(data);
395 if (ad == NULL) {
396 krb5_set_error_message(context, ENOENT,
397 N_("No authorization data", ""));
398 return ENOENT; /* XXX */
401 ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
402 if (ret)
403 return ret;
404 if (!found) {
405 krb5_set_error_message(context, ENOENT,
406 N_("Have no authorization data of type %d", ""),
407 type);
408 return ENOENT; /* XXX */
410 return 0;
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.
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()
424 * @ingroup krb5
427 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
428 krb5_ticket_get_authorization_data_type(krb5_context context,
429 krb5_ticket *ticket,
430 int type,
431 krb5_data *data)
433 AuthorizationData *ad;
434 krb5_error_code ret;
435 krb5_boolean found = FALSE;
437 if (data)
438 krb5_data_zero(data);
440 ad = ticket->ticket.authorization_data;
441 if (ticket->ticket.authorization_data == NULL) {
442 krb5_set_error_message(context, ENOENT,
443 N_("Ticket has no authorization data", ""));
444 return ENOENT; /* XXX */
447 ret = find_type_in_ad(context, type, data, &found, TRUE,
448 &ticket->ticket.key, ad, 0);
449 if (ret)
450 return ret;
451 if (!found) {
452 krb5_set_error_message(context, ENOENT,
453 N_("Ticket has no "
454 "authorization data of type %d", ""),
455 type);
456 return ENOENT; /* XXX */
458 return 0;
461 static krb5_error_code
462 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)
469 krb5_error_code ret;
470 PA_ServerReferralData ref;
471 krb5_crypto session;
472 EncryptedData ed;
473 size_t len;
474 krb5_data data;
475 PA_DATA *pa;
476 int i = 0, cmp;
478 if (rep->kdc_rep.padata == NULL)
479 goto noreferral;
481 pa = krb5_find_padata(rep->kdc_rep.padata->val,
482 rep->kdc_rep.padata->len,
483 KRB5_PADATA_SERVER_REFERRAL, &i);
484 if (pa == NULL)
485 goto noreferral;
487 memset(&ed, 0, sizeof(ed));
488 memset(&ref, 0, sizeof(ref));
490 ret = decode_EncryptedData(pa->padata_value.data,
491 pa->padata_value.length,
492 &ed, &len);
493 if (ret)
494 return ret;
495 if (len != pa->padata_value.length) {
496 free_EncryptedData(&ed);
497 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
498 N_("Referral EncryptedData wrong for realm %s",
499 "realm"), requested->realm);
500 return KRB5KRB_AP_ERR_MODIFIED;
503 ret = krb5_crypto_init(context, key, 0, &session);
504 if (ret) {
505 free_EncryptedData(&ed);
506 return ret;
509 ret = krb5_decrypt_EncryptedData(context, session,
510 KRB5_KU_PA_SERVER_REFERRAL,
511 &ed, &data);
512 free_EncryptedData(&ed);
513 krb5_crypto_destroy(context, session);
514 if (ret)
515 return ret;
517 ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
518 if (ret) {
519 krb5_data_free(&data);
520 return ret;
522 krb5_data_free(&data);
524 if (strcmp(requested->realm, returned->realm) != 0) {
525 free_PA_ServerReferralData(&ref);
526 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
527 N_("server ref realm mismatch, "
528 "requested realm %s got back %s", ""),
529 requested->realm, returned->realm);
530 return KRB5KRB_AP_ERR_MODIFIED;
533 if (krb5_principal_is_krbtgt(context, returned)) {
534 const char *realm = returned->name.name_string.val[1];
536 if (ref.referred_realm == NULL
537 || strcmp(*ref.referred_realm, realm) != 0)
539 free_PA_ServerReferralData(&ref);
540 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
541 N_("tgt returned with wrong ref", ""));
542 return KRB5KRB_AP_ERR_MODIFIED;
544 } else if (krb5_principal_compare(context, returned, requested) == 0) {
545 free_PA_ServerReferralData(&ref);
546 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
547 N_("req princ no same as returned", ""));
548 return KRB5KRB_AP_ERR_MODIFIED;
551 if (ref.requested_principal_name) {
552 cmp = _krb5_principal_compare_PrincipalName(context,
553 requested,
554 ref.requested_principal_name);
555 if (!cmp) {
556 free_PA_ServerReferralData(&ref);
557 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
558 N_("referred principal not same "
559 "as requested", ""));
560 return KRB5KRB_AP_ERR_MODIFIED;
562 } else if (flags & EXTRACT_TICKET_AS_REQ) {
563 free_PA_ServerReferralData(&ref);
564 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
565 N_("Requested principal missing on AS-REQ", ""));
566 return KRB5KRB_AP_ERR_MODIFIED;
569 free_PA_ServerReferralData(&ref);
571 return ret;
572 noreferral:
574 * Expect excact match or that we got a krbtgt
576 if (krb5_principal_compare(context, requested, returned) != TRUE &&
577 (krb5_realm_compare(context, requested, returned) != TRUE &&
578 krb5_principal_is_krbtgt(context, returned) != TRUE))
580 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
581 N_("Not same server principal returned "
582 "as requested", ""));
583 return KRB5KRB_AP_ERR_MODIFIED;
585 return 0;
589 * Verify KDC supported anonymous if requested
591 static krb5_error_code
592 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)
598 int flags;
600 if (!rep->enc_part.flags.anonymous)
601 return KRB5KDC_ERR_BADOPTION;
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?)
614 if (is_tgs_rep)
615 flags = KRB5_ANON_MATCH_ANY_NONT;
616 else if (krb5_principal_is_anonymous(context, requested,
617 KRB5_ANON_MATCH_ANY_NONT))
618 flags = KRB5_ANON_MATCH_UNAUTHENTICATED | KRB5_ANON_IGNORE_NAME_TYPE;
619 else
620 flags = KRB5_ANON_MATCH_AUTHENTICATED;
622 if (!krb5_principal_is_anonymous(context, mapped, flags))
623 return KRB5KRB_AP_ERR_MODIFIED;
625 return 0;
629 * Verify returned client principal name in anonymous/referral case
632 static krb5_error_code
633 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)
639 if (rep->enc_part.flags.anonymous) {
640 if (!krb5_principal_is_anonymous(context, mapped,
641 KRB5_ANON_MATCH_ANY_NONT)) {
642 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
643 N_("Anonymous ticket does not contain anonymous "
644 "principal", ""));
645 return KRB5KRB_AP_ERR_MODIFIED;
647 } else {
648 if (krb5_principal_compare(context, requested, mapped) == FALSE &&
649 !rep->enc_part.flags.enc_pa_rep) {
650 krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
651 N_("Not same client principal returned "
652 "as requested", ""));
653 return KRB5KRB_AP_ERR_MODIFIED;
657 return 0;
661 static krb5_error_code KRB5_CALLCONV
662 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)
668 krb5_error_code ret;
669 krb5_data data;
670 size_t size;
671 krb5_crypto crypto;
673 ret = krb5_crypto_init(context, key, 0, &crypto);
674 if (ret)
675 return ret;
677 ret = krb5_decrypt_EncryptedData (context,
678 crypto,
679 usage,
680 &dec_rep->kdc_rep.enc_part,
681 &data);
682 krb5_crypto_destroy(context, crypto);
684 if (ret)
685 return ret;
687 ret = decode_EncASRepPart(data.data,
688 data.length,
689 &dec_rep->enc_part,
690 &size);
691 if (ret)
692 ret = decode_EncTGSRepPart(data.data,
693 data.length,
694 &dec_rep->enc_part,
695 &size);
696 krb5_data_free (&data);
697 if (ret) {
698 krb5_set_error_message(context, ret,
699 N_("Failed to decode encpart in ticket", ""));
700 return ret;
702 return 0;
705 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
706 _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)
719 krb5_error_code ret;
720 krb5_principal tmp_principal;
721 size_t len = 0;
722 time_t tmp_time;
723 krb5_timestamp sec_now;
725 /* decrypt */
727 if (decrypt_proc == NULL)
728 decrypt_proc = decrypt_tkt;
730 ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
731 if (ret)
732 goto out;
734 if (rep->enc_part.flags.enc_pa_rep && request) {
735 krb5_crypto crypto = NULL;
736 Checksum cksum;
737 PA_DATA *pa = NULL;
738 int idx = 0;
740 _krb5_debug(context, 5, "processing enc-ap-rep");
742 if (rep->enc_part.encrypted_pa_data == NULL ||
743 (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
744 rep->enc_part.encrypted_pa_data->len,
745 KRB5_PADATA_REQ_ENC_PA_REP,
746 &idx)) == NULL)
748 _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
749 ret = KRB5KRB_AP_ERR_MODIFIED;
750 goto out;
753 ret = krb5_crypto_init(context, key, 0, &crypto);
754 if (ret)
755 goto out;
757 ret = decode_Checksum(pa->padata_value.data,
758 pa->padata_value.length,
759 &cksum, NULL);
760 if (ret) {
761 krb5_crypto_destroy(context, crypto);
762 goto out;
765 ret = krb5_verify_checksum(context, crypto,
766 KRB5_KU_AS_REQ,
767 request->data, request->length,
768 &cksum);
769 krb5_crypto_destroy(context, crypto);
770 free_Checksum(&cksum);
771 _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
772 if (ret)
773 goto out;
776 /* save session key */
778 creds->session.keyvalue.length = 0;
779 creds->session.keyvalue.data = NULL;
780 creds->session.keytype = rep->enc_part.key.keytype;
781 ret = krb5_data_copy (&creds->session.keyvalue,
782 rep->enc_part.key.keyvalue.data,
783 rep->enc_part.key.keyvalue.length);
784 if (ret) {
785 krb5_clear_error_message(context);
786 goto out;
789 /* compare client and save */
790 ret = _krb5_principalname2krb5_principal(context,
791 &tmp_principal,
792 rep->kdc_rep.cname,
793 rep->kdc_rep.crealm);
794 if (ret)
795 goto out;
797 /* check KDC supported anonymous if it was requested */
798 if (flags & EXTRACT_TICKET_MATCH_ANON) {
799 ret = check_client_anonymous(context,rep,
800 creds->client,
801 tmp_principal,
802 request == NULL); /* is TGS */
803 if (ret) {
804 krb5_free_principal(context, tmp_principal);
805 goto out;
809 /* check client referral and save principal */
810 if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
811 ret = check_client_mismatch(context, rep,
812 creds->client,
813 tmp_principal,
814 &creds->session);
815 if (ret) {
816 krb5_free_principal (context, tmp_principal);
817 goto out;
820 krb5_free_principal (context, creds->client);
821 creds->client = tmp_principal;
823 /* check server referral and save principal */
824 ret = _krb5_kdcrep2krb5_principal(context, &tmp_principal, &rep->enc_part);
825 if (ret)
826 goto out;
828 tmp_principal->nameattrs->peer_realm =
829 calloc(1, sizeof(tmp_principal->nameattrs->peer_realm[0]));
830 if (tmp_principal->nameattrs->peer_realm == NULL) {
831 ret = krb5_enomem(context);
832 goto out;
834 ret = copy_Realm(&creds->client->realm, tmp_principal->nameattrs->peer_realm);
835 if (ret) goto out;
837 if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
838 ret = check_server_referral(context,
839 rep,
840 flags,
841 creds->server,
842 tmp_principal,
843 &creds->session);
844 if (ret) {
845 krb5_free_principal (context, tmp_principal);
846 goto out;
849 krb5_free_principal(context, creds->server);
850 creds->server = tmp_principal;
852 /* verify names */
853 if(flags & EXTRACT_TICKET_MATCH_REALM){
854 const char *srealm = krb5_principal_get_realm(context, creds->server);
855 const char *crealm = krb5_principal_get_realm(context, creds->client);
857 if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
858 strcmp(rep->enc_part.srealm, crealm) != 0)
860 ret = KRB5KRB_AP_ERR_MODIFIED;
861 krb5_clear_error_message(context);
862 goto out;
866 /* compare nonces */
868 if (nonce != (unsigned)rep->enc_part.nonce) {
869 ret = KRB5KRB_AP_ERR_MODIFIED;
870 krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
871 goto out;
874 /* set kdc-offset */
876 krb5_timeofday (context, &sec_now);
877 if (rep->enc_part.flags.initial
878 && (flags & EXTRACT_TICKET_TIMESYNC)
879 && context->kdc_sec_offset == 0
880 && krb5_config_get_bool (context, NULL,
881 "libdefaults",
882 "kdc_timesync",
883 NULL)) {
884 context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
885 krb5_timeofday (context, &sec_now);
888 /* check all times */
890 if (rep->enc_part.starttime) {
891 tmp_time = *rep->enc_part.starttime;
892 } else
893 tmp_time = rep->enc_part.authtime;
895 if (creds->times.starttime == 0
896 && krb5_time_abs(tmp_time, sec_now) > context->max_skew) {
897 ret = KRB5KRB_AP_ERR_SKEW;
898 krb5_set_error_message (context, ret,
899 N_("time skew (%ld) larger than max (%ld)", ""),
900 krb5_time_abs(tmp_time, sec_now),
901 (long)context->max_skew);
902 goto out;
905 if (creds->times.starttime != 0
906 && tmp_time != creds->times.starttime) {
907 krb5_clear_error_message (context);
908 ret = KRB5KRB_AP_ERR_MODIFIED;
909 goto out;
912 creds->times.starttime = tmp_time;
914 if (rep->enc_part.renew_till) {
915 tmp_time = *rep->enc_part.renew_till;
916 } else
917 tmp_time = 0;
919 if (creds->times.renew_till != 0
920 && tmp_time > creds->times.renew_till) {
921 krb5_clear_error_message (context);
922 ret = KRB5KRB_AP_ERR_MODIFIED;
923 goto out;
926 creds->times.renew_till = tmp_time;
928 creds->times.authtime = rep->enc_part.authtime;
930 if (creds->times.endtime != 0
931 && rep->enc_part.endtime > creds->times.endtime) {
932 krb5_clear_error_message (context);
933 ret = KRB5KRB_AP_ERR_MODIFIED;
934 goto out;
937 creds->times.endtime = rep->enc_part.endtime;
939 if(rep->enc_part.caddr)
940 krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
941 else if(addrs)
942 krb5_copy_addresses (context, addrs, &creds->addresses);
943 else {
944 creds->addresses.len = 0;
945 creds->addresses.val = NULL;
947 creds->flags.b = rep->enc_part.flags;
949 creds->authdata.len = 0;
950 creds->authdata.val = NULL;
952 /* extract ticket */
953 ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
954 &rep->kdc_rep.ticket, &len, ret);
955 if(ret)
956 goto out;
957 if (creds->ticket.length != len)
958 krb5_abortx(context, "internal error in ASN.1 encoder");
959 creds->second_ticket.length = 0;
960 creds->second_ticket.data = NULL;
963 out:
964 memset (rep->enc_part.key.keyvalue.data, 0,
965 rep->enc_part.key.keyvalue.length);
966 return ret;