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
);
326 * Extract the authorization data type of type from the ticket. Store
327 * the field in data. This function is to use for kerberos
330 * @param context a Kerberos 5 context
331 * @param ticket Kerberos ticket
332 * @param type type to fetch
333 * @param data returned data, free with krb5_data_free()
338 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
339 krb5_ticket_get_authorization_data_type(krb5_context context
,
344 AuthorizationData
*ad
;
346 krb5_boolean found
= FALSE
;
348 krb5_data_zero(data
);
350 ad
= ticket
->ticket
.authorization_data
;
351 if (ticket
->ticket
.authorization_data
== NULL
) {
352 krb5_set_error_message(context
, ENOENT
,
353 N_("Ticket have not authorization data", ""));
354 return ENOENT
; /* XXX */
357 ret
= find_type_in_ad(context
, type
, data
, &found
, TRUE
,
358 &ticket
->ticket
.key
, ad
, 0);
362 krb5_set_error_message(context
, ENOENT
,
363 N_("Ticket have not "
364 "authorization data of type %d", ""),
366 return ENOENT
; /* XXX */
371 static krb5_error_code
372 check_server_referral(krb5_context context
,
375 krb5_const_principal requested
,
376 krb5_const_principal returned
,
380 PA_ServerReferralData ref
;
388 if (rep
->kdc_rep
.padata
== NULL
)
391 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
392 rep
->kdc_rep
.padata
->len
,
393 KRB5_PADATA_SERVER_REFERRAL
, &i
);
397 memset(&ed
, 0, sizeof(ed
));
398 memset(&ref
, 0, sizeof(ref
));
400 ret
= decode_EncryptedData(pa
->padata_value
.data
,
401 pa
->padata_value
.length
,
405 if (len
!= pa
->padata_value
.length
) {
406 free_EncryptedData(&ed
);
407 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
408 N_("Referral EncryptedData wrong for realm %s",
409 "realm"), requested
->realm
);
410 return KRB5KRB_AP_ERR_MODIFIED
;
413 ret
= krb5_crypto_init(context
, key
, 0, &session
);
415 free_EncryptedData(&ed
);
419 ret
= krb5_decrypt_EncryptedData(context
, session
,
420 KRB5_KU_PA_SERVER_REFERRAL
,
422 free_EncryptedData(&ed
);
423 krb5_crypto_destroy(context
, session
);
427 ret
= decode_PA_ServerReferralData(data
.data
, data
.length
, &ref
, &len
);
429 krb5_data_free(&data
);
432 krb5_data_free(&data
);
434 if (strcmp(requested
->realm
, returned
->realm
) != 0) {
435 free_PA_ServerReferralData(&ref
);
436 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
437 N_("server ref realm mismatch, "
438 "requested realm %s got back %s", ""),
439 requested
->realm
, returned
->realm
);
440 return KRB5KRB_AP_ERR_MODIFIED
;
443 if (krb5_principal_is_krbtgt(context
, returned
)) {
444 const char *realm
= returned
->name
.name_string
.val
[1];
446 if (ref
.referred_realm
== NULL
447 || strcmp(*ref
.referred_realm
, realm
) != 0)
449 free_PA_ServerReferralData(&ref
);
450 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
451 N_("tgt returned with wrong ref", ""));
452 return KRB5KRB_AP_ERR_MODIFIED
;
454 } else if (krb5_principal_compare(context
, returned
, requested
) == 0) {
455 free_PA_ServerReferralData(&ref
);
456 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
457 N_("req princ no same as returned", ""));
458 return KRB5KRB_AP_ERR_MODIFIED
;
461 if (ref
.requested_principal_name
) {
462 cmp
= _krb5_principal_compare_PrincipalName(context
,
464 ref
.requested_principal_name
);
466 free_PA_ServerReferralData(&ref
);
467 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
468 N_("referred principal not same "
469 "as requested", ""));
470 return KRB5KRB_AP_ERR_MODIFIED
;
472 } else if (flags
& EXTRACT_TICKET_AS_REQ
) {
473 free_PA_ServerReferralData(&ref
);
474 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
475 N_("Requested principal missing on AS-REQ", ""));
476 return KRB5KRB_AP_ERR_MODIFIED
;
479 free_PA_ServerReferralData(&ref
);
484 * Expect excact match or that we got a krbtgt
486 if (krb5_principal_compare(context
, requested
, returned
) != TRUE
&&
487 (krb5_realm_compare(context
, requested
, returned
) != TRUE
&&
488 krb5_principal_is_krbtgt(context
, returned
) != TRUE
))
490 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
491 N_("Not same server principal returned "
492 "as requested", ""));
493 return KRB5KRB_AP_ERR_MODIFIED
;
500 * Verify referral data
504 static krb5_error_code
505 check_client_referral(krb5_context context
,
507 krb5_const_principal requested
,
508 krb5_const_principal mapped
,
509 krb5_keyblock
const * key
)
512 PA_ClientCanonicalized canon
;
519 if (rep
->kdc_rep
.padata
== NULL
)
522 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
523 rep
->kdc_rep
.padata
->len
,
524 KRB5_PADATA_CLIENT_CANONICALIZED
, &i
);
528 ret
= decode_PA_ClientCanonicalized(pa
->padata_value
.data
,
529 pa
->padata_value
.length
,
532 krb5_set_error_message(context
, ret
,
533 N_("Failed to decode ClientCanonicalized "
534 "from realm %s", ""), requested
->realm
);
538 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames
, data
.data
, data
.length
,
539 &canon
.names
, &len
, ret
);
541 free_PA_ClientCanonicalized(&canon
);
544 if (data
.length
!= len
)
545 krb5_abortx(context
, "internal asn.1 error");
547 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
550 free_PA_ClientCanonicalized(&canon
);
554 ret
= krb5_verify_checksum(context
, crypto
, KRB5_KU_CANONICALIZED_NAMES
,
555 data
.data
, data
.length
,
556 &canon
.canon_checksum
);
557 krb5_crypto_destroy(context
, crypto
);
560 krb5_set_error_message(context
, ret
,
561 N_("Failed to verify client canonicalized "
562 "data from realm %s", ""),
564 free_PA_ClientCanonicalized(&canon
);
568 if (!_krb5_principal_compare_PrincipalName(context
,
570 &canon
.names
.requested_name
))
572 free_PA_ClientCanonicalized(&canon
);
573 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
574 N_("Requested name doesn't match"
575 " in client referral", ""));
576 return KRB5_PRINC_NOMATCH
;
578 if (!_krb5_principal_compare_PrincipalName(context
,
580 &canon
.names
.mapped_name
))
582 free_PA_ClientCanonicalized(&canon
);
583 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
584 N_("Mapped name doesn't match"
585 " in client referral", ""));
586 return KRB5_PRINC_NOMATCH
;
592 if (krb5_principal_compare(context
, requested
, mapped
) == FALSE
&&
593 !rep
->enc_part
.flags
.enc_pa_rep
)
595 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
596 N_("Not same client principal returned "
597 "as requested", ""));
598 return KRB5KRB_AP_ERR_MODIFIED
;
604 static krb5_error_code KRB5_CALLCONV
605 decrypt_tkt (krb5_context context
,
607 krb5_key_usage usage
,
608 krb5_const_pointer decrypt_arg
,
609 krb5_kdc_rep
*dec_rep
)
616 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
620 ret
= krb5_decrypt_EncryptedData (context
,
623 &dec_rep
->kdc_rep
.enc_part
,
625 krb5_crypto_destroy(context
, crypto
);
630 ret
= decode_EncASRepPart(data
.data
,
635 ret
= decode_EncTGSRepPart(data
.data
,
639 krb5_data_free (&data
);
641 krb5_set_error_message(context
, ret
,
642 N_("Failed to decode encpart in ticket", ""));
649 _krb5_extract_ticket(krb5_context context
,
653 krb5_const_pointer keyseed
,
654 krb5_key_usage key_usage
,
655 krb5_addresses
*addrs
,
659 krb5_decrypt_proc decrypt_proc
,
660 krb5_const_pointer decryptarg
)
663 krb5_principal tmp_principal
;
666 krb5_timestamp sec_now
;
670 if (decrypt_proc
== NULL
)
671 decrypt_proc
= decrypt_tkt
;
673 ret
= (*decrypt_proc
)(context
, key
, key_usage
, decryptarg
, rep
);
677 if (rep
->enc_part
.flags
.enc_pa_rep
&& request
) {
678 krb5_crypto crypto
= NULL
;
683 _krb5_debug(context
, 5, "processing enc-ap-rep");
685 if (rep
->enc_part
.encrypted_pa_data
== NULL
||
686 (pa
= krb5_find_padata(rep
->enc_part
.encrypted_pa_data
->val
,
687 rep
->enc_part
.encrypted_pa_data
->len
,
688 KRB5_PADATA_REQ_ENC_PA_REP
,
691 _krb5_debug(context
, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
692 ret
= KRB5KRB_AP_ERR_MODIFIED
;
696 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
700 ret
= decode_Checksum(pa
->padata_value
.data
,
701 pa
->padata_value
.length
,
704 krb5_crypto_destroy(context
, crypto
);
708 ret
= krb5_verify_checksum(context
, crypto
,
710 request
->data
, request
->length
,
712 krb5_crypto_destroy(context
, crypto
);
713 free_Checksum(&cksum
);
714 _krb5_debug(context
, 5, "enc-ap-rep: %svalid", (ret
== 0) ? "" : "in");
719 /* save session key */
721 creds
->session
.keyvalue
.length
= 0;
722 creds
->session
.keyvalue
.data
= NULL
;
723 creds
->session
.keytype
= rep
->enc_part
.key
.keytype
;
724 ret
= krb5_data_copy (&creds
->session
.keyvalue
,
725 rep
->enc_part
.key
.keyvalue
.data
,
726 rep
->enc_part
.key
.keyvalue
.length
);
728 krb5_clear_error_message(context
);
732 /* compare client and save */
733 ret
= _krb5_principalname2krb5_principal(context
,
736 rep
->kdc_rep
.crealm
);
740 /* check client referral and save principal */
741 /* anonymous here ? */
742 if((flags
& EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
) == 0) {
743 ret
= check_client_referral(context
, rep
,
748 krb5_free_principal (context
, tmp_principal
);
752 krb5_free_principal (context
, creds
->client
);
753 creds
->client
= tmp_principal
;
755 /* check server referral and save principal */
756 ret
= _krb5_principalname2krb5_principal (context
,
758 rep
->kdc_rep
.ticket
.sname
,
759 rep
->kdc_rep
.ticket
.realm
);
762 if((flags
& EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
) == 0){
763 ret
= check_server_referral(context
,
770 krb5_free_principal (context
, tmp_principal
);
774 krb5_free_principal(context
, creds
->server
);
775 creds
->server
= tmp_principal
;
778 if(flags
& EXTRACT_TICKET_MATCH_REALM
){
779 const char *srealm
= krb5_principal_get_realm(context
, creds
->server
);
780 const char *crealm
= krb5_principal_get_realm(context
, creds
->client
);
782 if (strcmp(rep
->enc_part
.srealm
, srealm
) != 0 ||
783 strcmp(rep
->enc_part
.srealm
, crealm
) != 0)
785 ret
= KRB5KRB_AP_ERR_MODIFIED
;
786 krb5_clear_error_message(context
);
793 if (nonce
!= (unsigned)rep
->enc_part
.nonce
) {
794 ret
= KRB5KRB_AP_ERR_MODIFIED
;
795 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
801 krb5_timeofday (context
, &sec_now
);
802 if (rep
->enc_part
.flags
.initial
803 && (flags
& EXTRACT_TICKET_TIMESYNC
)
804 && context
->kdc_sec_offset
== 0
805 && krb5_config_get_bool (context
, NULL
,
809 context
->kdc_sec_offset
= rep
->enc_part
.authtime
- sec_now
;
810 krb5_timeofday (context
, &sec_now
);
813 /* check all times */
815 if (rep
->enc_part
.starttime
) {
816 tmp_time
= *rep
->enc_part
.starttime
;
818 tmp_time
= rep
->enc_part
.authtime
;
820 if (creds
->times
.starttime
== 0
821 && abs(tmp_time
- sec_now
) > context
->max_skew
) {
822 ret
= KRB5KRB_AP_ERR_SKEW
;
823 krb5_set_error_message (context
, ret
,
824 N_("time skew (%d) larger than max (%d)", ""),
825 abs(tmp_time
- sec_now
),
826 (int)context
->max_skew
);
830 if (creds
->times
.starttime
!= 0
831 && tmp_time
!= creds
->times
.starttime
) {
832 krb5_clear_error_message (context
);
833 ret
= KRB5KRB_AP_ERR_MODIFIED
;
837 creds
->times
.starttime
= tmp_time
;
839 if (rep
->enc_part
.renew_till
) {
840 tmp_time
= *rep
->enc_part
.renew_till
;
844 if (creds
->times
.renew_till
!= 0
845 && tmp_time
> creds
->times
.renew_till
) {
846 krb5_clear_error_message (context
);
847 ret
= KRB5KRB_AP_ERR_MODIFIED
;
851 creds
->times
.renew_till
= tmp_time
;
853 creds
->times
.authtime
= rep
->enc_part
.authtime
;
855 if (creds
->times
.endtime
!= 0
856 && rep
->enc_part
.endtime
> creds
->times
.endtime
) {
857 krb5_clear_error_message (context
);
858 ret
= KRB5KRB_AP_ERR_MODIFIED
;
862 creds
->times
.endtime
= rep
->enc_part
.endtime
;
864 if(rep
->enc_part
.caddr
)
865 krb5_copy_addresses (context
, rep
->enc_part
.caddr
, &creds
->addresses
);
867 krb5_copy_addresses (context
, addrs
, &creds
->addresses
);
869 creds
->addresses
.len
= 0;
870 creds
->addresses
.val
= NULL
;
872 creds
->flags
.b
= rep
->enc_part
.flags
;
874 creds
->authdata
.len
= 0;
875 creds
->authdata
.val
= NULL
;
878 ASN1_MALLOC_ENCODE(Ticket
, creds
->ticket
.data
, creds
->ticket
.length
,
879 &rep
->kdc_rep
.ticket
, &len
, ret
);
882 if (creds
->ticket
.length
!= len
)
883 krb5_abortx(context
, "internal error in ASN.1 encoder");
884 creds
->second_ticket
.length
= 0;
885 creds
->second_ticket
.data
= NULL
;
889 memset (rep
->enc_part
.key
.keyvalue
.data
, 0,
890 rep
->enc_part
.key
.keyvalue
.length
);