2 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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
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
36 #include "krb5_locl.h"
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().
50 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
51 krb5_free_ticket(krb5_context context
,
54 free_EncTicketPart(&ticket
->ticket
);
55 krb5_free_principal(context
, ticket
->client
);
56 krb5_free_principal(context
, ticket
->server
);
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().
74 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
75 krb5_copy_ticket(krb5_context context
,
76 const krb5_ticket
*from
,
83 tmp
= malloc(sizeof(*tmp
));
85 return krb5_enomem(context
);
86 if((ret
= copy_EncTicketPart(&from
->ticket
, &tmp
->ticket
))){
90 ret
= krb5_copy_principal(context
, from
->client
, &tmp
->client
);
92 free_EncTicketPart(&tmp
->ticket
);
96 ret
= krb5_copy_principal(context
, from
->server
, &tmp
->server
);
98 krb5_free_principal(context
, tmp
->client
);
99 free_EncTicketPart(&tmp
->ticket
);
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().
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().
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 ticket
152 * @param context a Kerberos 5 context
153 * @param ticket ticket to copy
155 * @return end time of ticket
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 * Get the flags from the Kerberos ticket
170 * @param context Kerberos context
171 * @param ticket Kerberos ticket
173 * @return ticket flags
175 * @ingroup krb5_ticket
177 KRB5_LIB_FUNCTION
unsigned long KRB5_LIB_CALL
178 krb5_ticket_get_flags(krb5_context context
,
179 const krb5_ticket
*ticket
)
181 return TicketFlags2int(ticket
->ticket
.flags
);
185 find_type_in_ad(krb5_context context
,
190 krb5_keyblock
*sessionkey
,
191 const AuthorizationData
*ad
,
194 krb5_error_code ret
= 0;
198 ret
= ENOENT
; /* XXX */
199 krb5_set_error_message(context
, ret
,
200 N_("Authorization data nested deeper "
201 "then %d levels, stop searching", ""),
207 * Only copy out the element the first time we get to it, we need
208 * to run over the whole authorization data fields to check if
209 * there are any container clases we need to care about.
211 for (i
= 0; i
< ad
->len
; i
++) {
212 if (!*found
&& ad
->val
[i
].ad_type
== type
) {
213 ret
= der_copy_octet_string(&ad
->val
[i
].ad_data
, data
);
215 krb5_set_error_message(context
, ret
,
216 N_("malloc: out of memory", ""));
222 switch (ad
->val
[i
].ad_type
) {
223 case KRB5_AUTHDATA_IF_RELEVANT
: {
224 AuthorizationData child
;
225 ret
= decode_AuthorizationData(ad
->val
[i
].ad_data
.data
,
226 ad
->val
[i
].ad_data
.length
,
230 krb5_set_error_message(context
, ret
,
231 N_("Failed to decode "
232 "IF_RELEVANT with %d", ""),
236 ret
= find_type_in_ad(context
, type
, data
, found
, FALSE
,
237 sessionkey
, &child
, level
+ 1);
238 free_AuthorizationData(&child
);
244 case KRB5_AUTHDATA_KDC_ISSUED
: {
247 ret
= decode_AD_KDCIssued(ad
->val
[i
].ad_data
.data
,
248 ad
->val
[i
].ad_data
.length
,
252 krb5_set_error_message(context
, ret
,
253 N_("Failed to decode "
254 "AD_KDCIssued with %d", ""),
263 ASN1_MALLOC_ENCODE(AuthorizationData
, buf
.data
, buf
.length
,
264 &child
.elements
, &len
, ret
);
266 free_AD_KDCIssued(&child
);
267 krb5_clear_error_message(context
);
270 if(buf
.length
!= len
)
271 krb5_abortx(context
, "internal error in ASN.1 encoder");
273 ret
= krb5_c_verify_checksum(context
, sessionkey
, 19, &buf
,
274 &child
.ad_checksum
, &valid
);
275 krb5_data_free(&buf
);
277 free_AD_KDCIssued(&child
);
281 krb5_clear_error_message(context
);
283 free_AD_KDCIssued(&child
);
287 ret
= find_type_in_ad(context
, type
, data
, found
, failp
, sessionkey
,
288 &child
.elements
, level
+ 1);
289 free_AD_KDCIssued(&child
);
295 case KRB5_AUTHDATA_AND_OR
:
298 ret
= ENOENT
; /* XXX */
299 krb5_set_error_message(context
, ret
,
300 N_("Authorization data contains "
301 "AND-OR element that is unknown to the "
307 ret
= ENOENT
; /* XXX */
308 krb5_set_error_message(context
, ret
,
309 N_("Authorization data contains "
310 "unknown type (%d) ", ""),
318 krb5_data_free(data
);
325 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
326 _krb5_get_ad(krb5_context context
,
327 const AuthorizationData
*ad
,
328 krb5_keyblock
*sessionkey
,
332 krb5_boolean found
= FALSE
;
335 krb5_data_zero(data
);
338 krb5_set_error_message(context
, ENOENT
,
339 N_("No authorization data", ""));
340 return ENOENT
; /* XXX */
343 ret
= find_type_in_ad(context
, type
, data
, &found
, TRUE
, sessionkey
, ad
, 0);
347 krb5_set_error_message(context
, ENOENT
,
348 N_("Have no authorization data of type %d", ""),
350 return ENOENT
; /* XXX */
357 * Extract the authorization data type of type from the ticket. Store
358 * the field in data. This function is to use for kerberos
361 * @param context a Kerberos 5 context
362 * @param ticket Kerberos ticket
363 * @param type type to fetch
364 * @param data returned data, free with krb5_data_free()
369 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
370 krb5_ticket_get_authorization_data_type(krb5_context context
,
375 AuthorizationData
*ad
;
377 krb5_boolean found
= FALSE
;
379 krb5_data_zero(data
);
381 ad
= ticket
->ticket
.authorization_data
;
382 if (ticket
->ticket
.authorization_data
== NULL
) {
383 krb5_set_error_message(context
, ENOENT
,
384 N_("Ticket have not authorization data", ""));
385 return ENOENT
; /* XXX */
388 ret
= find_type_in_ad(context
, type
, data
, &found
, TRUE
,
389 &ticket
->ticket
.key
, ad
, 0);
393 krb5_set_error_message(context
, ENOENT
,
394 N_("Ticket have not "
395 "authorization data of type %d", ""),
397 return ENOENT
; /* XXX */
402 static krb5_error_code
403 check_server_referral(krb5_context context
,
406 krb5_const_principal requested
,
407 krb5_const_principal returned
,
411 PA_ServerReferralData ref
;
419 if (rep
->kdc_rep
.padata
== NULL
)
422 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
423 rep
->kdc_rep
.padata
->len
,
424 KRB5_PADATA_SERVER_REFERRAL
, &i
);
428 memset(&ed
, 0, sizeof(ed
));
429 memset(&ref
, 0, sizeof(ref
));
431 ret
= decode_EncryptedData(pa
->padata_value
.data
,
432 pa
->padata_value
.length
,
436 if (len
!= pa
->padata_value
.length
) {
437 free_EncryptedData(&ed
);
438 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
439 N_("Referral EncryptedData wrong for realm %s",
440 "realm"), requested
->realm
);
441 return KRB5KRB_AP_ERR_MODIFIED
;
444 ret
= krb5_crypto_init(context
, key
, 0, &session
);
446 free_EncryptedData(&ed
);
450 ret
= krb5_decrypt_EncryptedData(context
, session
,
451 KRB5_KU_PA_SERVER_REFERRAL
,
453 free_EncryptedData(&ed
);
454 krb5_crypto_destroy(context
, session
);
458 ret
= decode_PA_ServerReferralData(data
.data
, data
.length
, &ref
, &len
);
460 krb5_data_free(&data
);
463 krb5_data_free(&data
);
465 if (strcmp(requested
->realm
, returned
->realm
) != 0) {
466 free_PA_ServerReferralData(&ref
);
467 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
468 N_("server ref realm mismatch, "
469 "requested realm %s got back %s", ""),
470 requested
->realm
, returned
->realm
);
471 return KRB5KRB_AP_ERR_MODIFIED
;
474 if (krb5_principal_is_krbtgt(context
, returned
)) {
475 const char *realm
= returned
->name
.name_string
.val
[1];
477 if (ref
.referred_realm
== NULL
478 || strcmp(*ref
.referred_realm
, realm
) != 0)
480 free_PA_ServerReferralData(&ref
);
481 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
482 N_("tgt returned with wrong ref", ""));
483 return KRB5KRB_AP_ERR_MODIFIED
;
485 } else if (krb5_principal_compare(context
, returned
, requested
) == 0) {
486 free_PA_ServerReferralData(&ref
);
487 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
488 N_("req princ no same as returned", ""));
489 return KRB5KRB_AP_ERR_MODIFIED
;
492 if (ref
.requested_principal_name
) {
493 cmp
= _krb5_principal_compare_PrincipalName(context
,
495 ref
.requested_principal_name
);
497 free_PA_ServerReferralData(&ref
);
498 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
499 N_("referred principal not same "
500 "as requested", ""));
501 return KRB5KRB_AP_ERR_MODIFIED
;
503 } else if (flags
& EXTRACT_TICKET_AS_REQ
) {
504 free_PA_ServerReferralData(&ref
);
505 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
506 N_("Requested principal missing on AS-REQ", ""));
507 return KRB5KRB_AP_ERR_MODIFIED
;
510 free_PA_ServerReferralData(&ref
);
515 * Expect excact match or that we got a krbtgt
517 if (krb5_principal_compare(context
, requested
, returned
) != TRUE
&&
518 (krb5_realm_compare(context
, requested
, returned
) != TRUE
&&
519 krb5_principal_is_krbtgt(context
, returned
) != TRUE
))
521 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
522 N_("Not same server principal returned "
523 "as requested", ""));
524 return KRB5KRB_AP_ERR_MODIFIED
;
531 * Verify referral data
535 static krb5_error_code
536 check_client_referral(krb5_context context
,
538 krb5_const_principal requested
,
539 krb5_const_principal mapped
,
540 krb5_keyblock
const * key
)
542 if (krb5_principal_compare(context
, requested
, mapped
) == FALSE
&&
543 !rep
->enc_part
.flags
.enc_pa_rep
)
545 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
546 N_("Not same client principal returned "
547 "as requested", ""));
548 return KRB5KRB_AP_ERR_MODIFIED
;
554 static krb5_error_code KRB5_CALLCONV
555 decrypt_tkt (krb5_context context
,
557 krb5_key_usage usage
,
558 krb5_const_pointer decrypt_arg
,
559 krb5_kdc_rep
*dec_rep
)
566 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
570 ret
= krb5_decrypt_EncryptedData (context
,
573 &dec_rep
->kdc_rep
.enc_part
,
575 krb5_crypto_destroy(context
, crypto
);
580 ret
= decode_EncASRepPart(data
.data
,
585 ret
= decode_EncTGSRepPart(data
.data
,
589 krb5_data_free (&data
);
591 krb5_set_error_message(context
, ret
,
592 N_("Failed to decode encpart in ticket", ""));
598 KRB5_LIB_FUNCTION
int KRB5_LIB_CALL
599 _krb5_extract_ticket(krb5_context context
,
603 krb5_const_pointer keyseed
,
604 krb5_key_usage key_usage
,
605 krb5_addresses
*addrs
,
609 krb5_decrypt_proc decrypt_proc
,
610 krb5_const_pointer decryptarg
)
613 krb5_principal tmp_principal
;
616 krb5_timestamp sec_now
;
620 if (decrypt_proc
== NULL
)
621 decrypt_proc
= decrypt_tkt
;
623 ret
= (*decrypt_proc
)(context
, key
, key_usage
, decryptarg
, rep
);
627 if (rep
->enc_part
.flags
.enc_pa_rep
&& request
) {
628 krb5_crypto crypto
= NULL
;
633 _krb5_debug(context
, 5, "processing enc-ap-rep");
635 if (rep
->enc_part
.encrypted_pa_data
== NULL
||
636 (pa
= krb5_find_padata(rep
->enc_part
.encrypted_pa_data
->val
,
637 rep
->enc_part
.encrypted_pa_data
->len
,
638 KRB5_PADATA_REQ_ENC_PA_REP
,
641 _krb5_debug(context
, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
642 ret
= KRB5KRB_AP_ERR_MODIFIED
;
646 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
650 ret
= decode_Checksum(pa
->padata_value
.data
,
651 pa
->padata_value
.length
,
654 krb5_crypto_destroy(context
, crypto
);
658 ret
= krb5_verify_checksum(context
, crypto
,
660 request
->data
, request
->length
,
662 krb5_crypto_destroy(context
, crypto
);
663 free_Checksum(&cksum
);
664 _krb5_debug(context
, 5, "enc-ap-rep: %svalid", (ret
== 0) ? "" : "in");
669 /* save session key */
671 creds
->session
.keyvalue
.length
= 0;
672 creds
->session
.keyvalue
.data
= NULL
;
673 creds
->session
.keytype
= rep
->enc_part
.key
.keytype
;
674 ret
= krb5_data_copy (&creds
->session
.keyvalue
,
675 rep
->enc_part
.key
.keyvalue
.data
,
676 rep
->enc_part
.key
.keyvalue
.length
);
678 krb5_clear_error_message(context
);
682 /* compare client and save */
683 ret
= _krb5_principalname2krb5_principal(context
,
686 rep
->kdc_rep
.crealm
);
690 /* check client referral and save principal */
691 /* anonymous here ? */
692 if((flags
& EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
) == 0) {
693 ret
= check_client_referral(context
, rep
,
698 krb5_free_principal (context
, tmp_principal
);
702 krb5_free_principal (context
, creds
->client
);
703 creds
->client
= tmp_principal
;
705 /* check server referral and save principal */
706 ret
= _krb5_principalname2krb5_principal (context
,
708 rep
->kdc_rep
.ticket
.sname
,
709 rep
->kdc_rep
.ticket
.realm
);
712 if((flags
& EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
) == 0){
713 ret
= check_server_referral(context
,
720 krb5_free_principal (context
, tmp_principal
);
724 krb5_free_principal(context
, creds
->server
);
725 creds
->server
= tmp_principal
;
728 if(flags
& EXTRACT_TICKET_MATCH_REALM
){
729 const char *srealm
= krb5_principal_get_realm(context
, creds
->server
);
730 const char *crealm
= krb5_principal_get_realm(context
, creds
->client
);
732 if (strcmp(rep
->enc_part
.srealm
, srealm
) != 0 ||
733 strcmp(rep
->enc_part
.srealm
, crealm
) != 0)
735 ret
= KRB5KRB_AP_ERR_MODIFIED
;
736 krb5_clear_error_message(context
);
743 if (nonce
!= (unsigned)rep
->enc_part
.nonce
) {
744 ret
= KRB5KRB_AP_ERR_MODIFIED
;
745 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
751 krb5_timeofday (context
, &sec_now
);
752 if (rep
->enc_part
.flags
.initial
753 && (flags
& EXTRACT_TICKET_TIMESYNC
)
754 && context
->kdc_sec_offset
== 0
755 && krb5_config_get_bool (context
, NULL
,
759 context
->kdc_sec_offset
= rep
->enc_part
.authtime
- sec_now
;
760 krb5_timeofday (context
, &sec_now
);
763 /* check all times */
765 if (rep
->enc_part
.starttime
) {
766 tmp_time
= *rep
->enc_part
.starttime
;
768 tmp_time
= rep
->enc_part
.authtime
;
770 if (creds
->times
.starttime
== 0
771 && labs(tmp_time
- sec_now
) > context
->max_skew
) {
772 ret
= KRB5KRB_AP_ERR_SKEW
;
773 krb5_set_error_message (context
, ret
,
774 N_("time skew (%ld) larger than max (%ld)", ""),
775 labs(tmp_time
- sec_now
),
776 (long)context
->max_skew
);
780 if (creds
->times
.starttime
!= 0
781 && tmp_time
!= creds
->times
.starttime
) {
782 krb5_clear_error_message (context
);
783 ret
= KRB5KRB_AP_ERR_MODIFIED
;
787 creds
->times
.starttime
= tmp_time
;
789 if (rep
->enc_part
.renew_till
) {
790 tmp_time
= *rep
->enc_part
.renew_till
;
794 if (creds
->times
.renew_till
!= 0
795 && tmp_time
> creds
->times
.renew_till
) {
796 krb5_clear_error_message (context
);
797 ret
= KRB5KRB_AP_ERR_MODIFIED
;
801 creds
->times
.renew_till
= tmp_time
;
803 creds
->times
.authtime
= rep
->enc_part
.authtime
;
805 if (creds
->times
.endtime
!= 0
806 && rep
->enc_part
.endtime
> creds
->times
.endtime
) {
807 krb5_clear_error_message (context
);
808 ret
= KRB5KRB_AP_ERR_MODIFIED
;
812 creds
->times
.endtime
= rep
->enc_part
.endtime
;
814 if(rep
->enc_part
.caddr
)
815 krb5_copy_addresses (context
, rep
->enc_part
.caddr
, &creds
->addresses
);
817 krb5_copy_addresses (context
, addrs
, &creds
->addresses
);
819 creds
->addresses
.len
= 0;
820 creds
->addresses
.val
= NULL
;
822 creds
->flags
.b
= rep
->enc_part
.flags
;
824 creds
->authdata
.len
= 0;
825 creds
->authdata
.val
= NULL
;
828 ASN1_MALLOC_ENCODE(Ticket
, creds
->ticket
.data
, creds
->ticket
.length
,
829 &rep
->kdc_rep
.ticket
, &len
, ret
);
832 if (creds
->ticket
.length
!= len
)
833 krb5_abortx(context
, "internal error in ASN.1 encoder");
834 creds
->second_ticket
.length
= 0;
835 creds
->second_ticket
.data
= NULL
;
839 memset (rep
->enc_part
.key
.keyvalue
.data
, 0,
840 rep
->enc_part
.key
.keyvalue
.length
);