1 /* $NetBSD: ticket.c,v 1.1.1.2 2014/04/24 12:45:51 pettai Exp $ */
4 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include "krb5_locl.h"
41 * Free ticket and content
43 * @param context a Kerberos 5 context
44 * @param ticket ticket to free
46 * @return Returns 0 to indicate success. Otherwise an kerberos et
47 * error code is returned, see krb5_get_error_message().
52 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
53 krb5_free_ticket(krb5_context context
,
56 free_EncTicketPart(&ticket
->ticket
);
57 krb5_free_principal(context
, ticket
->client
);
58 krb5_free_principal(context
, ticket
->server
);
64 * Copy ticket and content
66 * @param context a Kerberos 5 context
67 * @param from ticket to copy
68 * @param to new copy of ticket, free with krb5_free_ticket()
70 * @return Returns 0 to indicate success. Otherwise an kerberos et
71 * error code is returned, see krb5_get_error_message().
76 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
77 krb5_copy_ticket(krb5_context context
,
78 const krb5_ticket
*from
,
85 tmp
= malloc(sizeof(*tmp
));
87 krb5_set_error_message(context
, ENOMEM
,
88 N_("malloc: out of memory", ""));
91 if((ret
= copy_EncTicketPart(&from
->ticket
, &tmp
->ticket
))){
95 ret
= krb5_copy_principal(context
, from
->client
, &tmp
->client
);
97 free_EncTicketPart(&tmp
->ticket
);
101 ret
= krb5_copy_principal(context
, from
->server
, &tmp
->server
);
103 krb5_free_principal(context
, tmp
->client
);
104 free_EncTicketPart(&tmp
->ticket
);
113 * Return client principal in ticket
115 * @param context a Kerberos 5 context
116 * @param ticket ticket to copy
117 * @param client client principal, free with krb5_free_principal()
119 * @return Returns 0 to indicate success. Otherwise an kerberos et
120 * error code is returned, see krb5_get_error_message().
125 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
126 krb5_ticket_get_client(krb5_context context
,
127 const krb5_ticket
*ticket
,
128 krb5_principal
*client
)
130 return krb5_copy_principal(context
, ticket
->client
, client
);
134 * Return server principal in ticket
136 * @param context a Kerberos 5 context
137 * @param ticket ticket to copy
138 * @param server server principal, free with krb5_free_principal()
140 * @return Returns 0 to indicate success. Otherwise an kerberos et
141 * error code is returned, see krb5_get_error_message().
146 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
147 krb5_ticket_get_server(krb5_context context
,
148 const krb5_ticket
*ticket
,
149 krb5_principal
*server
)
151 return krb5_copy_principal(context
, ticket
->server
, server
);
155 * Return end time of ticket
157 * @param context a Kerberos 5 context
158 * @param ticket ticket to copy
160 * @return end time of ticket
165 KRB5_LIB_FUNCTION
time_t KRB5_LIB_CALL
166 krb5_ticket_get_endtime(krb5_context context
,
167 const krb5_ticket
*ticket
)
169 return ticket
->ticket
.endtime
;
173 * Get the flags from the Kerberos ticket
175 * @param context Kerberos context
176 * @param ticket Kerberos ticket
178 * @return ticket flags
180 * @ingroup krb5_ticket
182 KRB5_LIB_FUNCTION
unsigned long KRB5_LIB_CALL
183 krb5_ticket_get_flags(krb5_context context
,
184 const krb5_ticket
*ticket
)
186 return TicketFlags2int(ticket
->ticket
.flags
);
190 find_type_in_ad(krb5_context context
,
195 krb5_keyblock
*sessionkey
,
196 const AuthorizationData
*ad
,
199 krb5_error_code ret
= 0;
203 ret
= ENOENT
; /* XXX */
204 krb5_set_error_message(context
, ret
,
205 N_("Authorization data nested deeper "
206 "then %d levels, stop searching", ""),
212 * Only copy out the element the first time we get to it, we need
213 * to run over the whole authorization data fields to check if
214 * there are any container clases we need to care about.
216 for (i
= 0; i
< ad
->len
; i
++) {
217 if (!*found
&& ad
->val
[i
].ad_type
== type
) {
218 ret
= der_copy_octet_string(&ad
->val
[i
].ad_data
, data
);
220 krb5_set_error_message(context
, ret
,
221 N_("malloc: out of memory", ""));
227 switch (ad
->val
[i
].ad_type
) {
228 case KRB5_AUTHDATA_IF_RELEVANT
: {
229 AuthorizationData child
;
230 ret
= decode_AuthorizationData(ad
->val
[i
].ad_data
.data
,
231 ad
->val
[i
].ad_data
.length
,
235 krb5_set_error_message(context
, ret
,
236 N_("Failed to decode "
237 "IF_RELEVANT with %d", ""),
241 ret
= find_type_in_ad(context
, type
, data
, found
, FALSE
,
242 sessionkey
, &child
, level
+ 1);
243 free_AuthorizationData(&child
);
249 case KRB5_AUTHDATA_KDC_ISSUED
: {
252 ret
= decode_AD_KDCIssued(ad
->val
[i
].ad_data
.data
,
253 ad
->val
[i
].ad_data
.length
,
257 krb5_set_error_message(context
, ret
,
258 N_("Failed to decode "
259 "AD_KDCIssued with %d", ""),
268 ASN1_MALLOC_ENCODE(AuthorizationData
, buf
.data
, buf
.length
,
269 &child
.elements
, &len
, ret
);
271 free_AD_KDCIssued(&child
);
272 krb5_clear_error_message(context
);
275 if(buf
.length
!= len
)
276 krb5_abortx(context
, "internal error in ASN.1 encoder");
278 ret
= krb5_c_verify_checksum(context
, sessionkey
, 19, &buf
,
279 &child
.ad_checksum
, &valid
);
280 krb5_data_free(&buf
);
282 free_AD_KDCIssued(&child
);
286 krb5_clear_error_message(context
);
288 free_AD_KDCIssued(&child
);
292 ret
= find_type_in_ad(context
, type
, data
, found
, failp
, sessionkey
,
293 &child
.elements
, level
+ 1);
294 free_AD_KDCIssued(&child
);
300 case KRB5_AUTHDATA_AND_OR
:
303 ret
= ENOENT
; /* XXX */
304 krb5_set_error_message(context
, ret
,
305 N_("Authorization data contains "
306 "AND-OR element that is unknown to the "
312 ret
= ENOENT
; /* XXX */
313 krb5_set_error_message(context
, ret
,
314 N_("Authorization data contains "
315 "unknown type (%d) ", ""),
323 krb5_data_free(data
);
331 * Extract the authorization data type of type from the ticket. Store
332 * the field in data. This function is to use for kerberos
335 * @param context a Kerberos 5 context
336 * @param ticket Kerberos ticket
337 * @param type type to fetch
338 * @param data returned data, free with krb5_data_free()
343 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
344 krb5_ticket_get_authorization_data_type(krb5_context context
,
349 AuthorizationData
*ad
;
351 krb5_boolean found
= FALSE
;
353 krb5_data_zero(data
);
355 ad
= ticket
->ticket
.authorization_data
;
356 if (ticket
->ticket
.authorization_data
== NULL
) {
357 krb5_set_error_message(context
, ENOENT
,
358 N_("Ticket have not authorization data", ""));
359 return ENOENT
; /* XXX */
362 ret
= find_type_in_ad(context
, type
, data
, &found
, TRUE
,
363 &ticket
->ticket
.key
, ad
, 0);
367 krb5_set_error_message(context
, ENOENT
,
368 N_("Ticket have not "
369 "authorization data of type %d", ""),
371 return ENOENT
; /* XXX */
376 static krb5_error_code
377 check_server_referral(krb5_context context
,
380 krb5_const_principal requested
,
381 krb5_const_principal returned
,
385 PA_ServerReferralData ref
;
393 if (rep
->kdc_rep
.padata
== NULL
)
396 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
397 rep
->kdc_rep
.padata
->len
,
398 KRB5_PADATA_SERVER_REFERRAL
, &i
);
402 memset(&ed
, 0, sizeof(ed
));
403 memset(&ref
, 0, sizeof(ref
));
405 ret
= decode_EncryptedData(pa
->padata_value
.data
,
406 pa
->padata_value
.length
,
410 if (len
!= pa
->padata_value
.length
) {
411 free_EncryptedData(&ed
);
412 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
413 N_("Referral EncryptedData wrong for realm %s",
414 "realm"), requested
->realm
);
415 return KRB5KRB_AP_ERR_MODIFIED
;
418 ret
= krb5_crypto_init(context
, key
, 0, &session
);
420 free_EncryptedData(&ed
);
424 ret
= krb5_decrypt_EncryptedData(context
, session
,
425 KRB5_KU_PA_SERVER_REFERRAL
,
427 free_EncryptedData(&ed
);
428 krb5_crypto_destroy(context
, session
);
432 ret
= decode_PA_ServerReferralData(data
.data
, data
.length
, &ref
, &len
);
434 krb5_data_free(&data
);
437 krb5_data_free(&data
);
439 if (strcmp(requested
->realm
, returned
->realm
) != 0) {
440 free_PA_ServerReferralData(&ref
);
441 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
442 N_("server ref realm mismatch, "
443 "requested realm %s got back %s", ""),
444 requested
->realm
, returned
->realm
);
445 return KRB5KRB_AP_ERR_MODIFIED
;
448 if (krb5_principal_is_krbtgt(context
, returned
)) {
449 const char *realm
= returned
->name
.name_string
.val
[1];
451 if (ref
.referred_realm
== NULL
452 || strcmp(*ref
.referred_realm
, realm
) != 0)
454 free_PA_ServerReferralData(&ref
);
455 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
456 N_("tgt returned with wrong ref", ""));
457 return KRB5KRB_AP_ERR_MODIFIED
;
459 } else if (krb5_principal_compare(context
, returned
, requested
) == 0) {
460 free_PA_ServerReferralData(&ref
);
461 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
462 N_("req princ no same as returned", ""));
463 return KRB5KRB_AP_ERR_MODIFIED
;
466 if (ref
.requested_principal_name
) {
467 cmp
= _krb5_principal_compare_PrincipalName(context
,
469 ref
.requested_principal_name
);
471 free_PA_ServerReferralData(&ref
);
472 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
473 N_("referred principal not same "
474 "as requested", ""));
475 return KRB5KRB_AP_ERR_MODIFIED
;
477 } else if (flags
& EXTRACT_TICKET_AS_REQ
) {
478 free_PA_ServerReferralData(&ref
);
479 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
480 N_("Requested principal missing on AS-REQ", ""));
481 return KRB5KRB_AP_ERR_MODIFIED
;
484 free_PA_ServerReferralData(&ref
);
489 * Expect excact match or that we got a krbtgt
491 if (krb5_principal_compare(context
, requested
, returned
) != TRUE
&&
492 (krb5_realm_compare(context
, requested
, returned
) != TRUE
&&
493 krb5_principal_is_krbtgt(context
, returned
) != TRUE
))
495 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
496 N_("Not same server principal returned "
497 "as requested", ""));
498 return KRB5KRB_AP_ERR_MODIFIED
;
505 * Verify referral data
509 static krb5_error_code
510 check_client_referral(krb5_context context
,
512 krb5_const_principal requested
,
513 krb5_const_principal mapped
,
514 krb5_keyblock
const * key
)
517 PA_ClientCanonicalized canon
;
524 if (rep
->kdc_rep
.padata
== NULL
)
527 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
528 rep
->kdc_rep
.padata
->len
,
529 KRB5_PADATA_CLIENT_CANONICALIZED
, &i
);
533 ret
= decode_PA_ClientCanonicalized(pa
->padata_value
.data
,
534 pa
->padata_value
.length
,
537 krb5_set_error_message(context
, ret
,
538 N_("Failed to decode ClientCanonicalized "
539 "from realm %s", ""), requested
->realm
);
543 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames
, data
.data
, data
.length
,
544 &canon
.names
, &len
, ret
);
546 free_PA_ClientCanonicalized(&canon
);
549 if (data
.length
!= len
)
550 krb5_abortx(context
, "internal asn.1 error");
552 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
555 free_PA_ClientCanonicalized(&canon
);
559 ret
= krb5_verify_checksum(context
, crypto
, KRB5_KU_CANONICALIZED_NAMES
,
560 data
.data
, data
.length
,
561 &canon
.canon_checksum
);
562 krb5_crypto_destroy(context
, crypto
);
565 krb5_set_error_message(context
, ret
,
566 N_("Failed to verify client canonicalized "
567 "data from realm %s", ""),
569 free_PA_ClientCanonicalized(&canon
);
573 if (!_krb5_principal_compare_PrincipalName(context
,
575 &canon
.names
.requested_name
))
577 free_PA_ClientCanonicalized(&canon
);
578 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
579 N_("Requested name doesn't match"
580 " in client referral", ""));
581 return KRB5_PRINC_NOMATCH
;
583 if (!_krb5_principal_compare_PrincipalName(context
,
585 &canon
.names
.mapped_name
))
587 free_PA_ClientCanonicalized(&canon
);
588 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
589 N_("Mapped name doesn't match"
590 " in client referral", ""));
591 return KRB5_PRINC_NOMATCH
;
597 if (krb5_principal_compare(context
, requested
, mapped
) == FALSE
) {
598 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
599 N_("Not same client principal returned "
600 "as requested", ""));
601 return KRB5KRB_AP_ERR_MODIFIED
;
607 static krb5_error_code KRB5_CALLCONV
608 decrypt_tkt (krb5_context context
,
610 krb5_key_usage usage
,
611 krb5_const_pointer decrypt_arg
,
612 krb5_kdc_rep
*dec_rep
)
619 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
623 ret
= krb5_decrypt_EncryptedData (context
,
626 &dec_rep
->kdc_rep
.enc_part
,
628 krb5_crypto_destroy(context
, crypto
);
633 ret
= decode_EncASRepPart(data
.data
,
638 ret
= decode_EncTGSRepPart(data
.data
,
642 krb5_data_free (&data
);
644 krb5_set_error_message(context
, ret
,
645 N_("Failed to decode encpart in ticket", ""));
652 _krb5_extract_ticket(krb5_context context
,
656 krb5_const_pointer keyseed
,
657 krb5_key_usage key_usage
,
658 krb5_addresses
*addrs
,
661 krb5_decrypt_proc decrypt_proc
,
662 krb5_const_pointer decryptarg
)
665 krb5_principal tmp_principal
;
668 krb5_timestamp sec_now
;
672 if (decrypt_proc
== NULL
)
673 decrypt_proc
= decrypt_tkt
;
675 ret
= (*decrypt_proc
)(context
, key
, key_usage
, decryptarg
, rep
);
679 /* save session key */
681 creds
->session
.keyvalue
.length
= 0;
682 creds
->session
.keyvalue
.data
= NULL
;
683 creds
->session
.keytype
= rep
->enc_part
.key
.keytype
;
684 ret
= krb5_data_copy (&creds
->session
.keyvalue
,
685 rep
->enc_part
.key
.keyvalue
.data
,
686 rep
->enc_part
.key
.keyvalue
.length
);
688 krb5_clear_error_message(context
);
692 /* compare client and save */
693 ret
= _krb5_principalname2krb5_principal (context
,
696 rep
->kdc_rep
.crealm
);
700 /* check client referral and save principal */
701 /* anonymous here ? */
702 if((flags
& EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
) == 0) {
703 ret
= check_client_referral(context
, rep
,
708 krb5_free_principal (context
, tmp_principal
);
712 krb5_free_principal (context
, creds
->client
);
713 creds
->client
= tmp_principal
;
715 /* check server referral and save principal */
716 ret
= _krb5_principalname2krb5_principal (context
,
718 rep
->kdc_rep
.ticket
.sname
,
719 rep
->kdc_rep
.ticket
.realm
);
722 if((flags
& EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
) == 0){
723 ret
= check_server_referral(context
,
730 krb5_free_principal (context
, tmp_principal
);
734 krb5_free_principal(context
, creds
->server
);
735 creds
->server
= tmp_principal
;
738 if(flags
& EXTRACT_TICKET_MATCH_REALM
){
739 const char *srealm
= krb5_principal_get_realm(context
, creds
->server
);
740 const char *crealm
= krb5_principal_get_realm(context
, creds
->client
);
742 if (strcmp(rep
->enc_part
.srealm
, srealm
) != 0 ||
743 strcmp(rep
->enc_part
.srealm
, crealm
) != 0)
745 ret
= KRB5KRB_AP_ERR_MODIFIED
;
746 krb5_clear_error_message(context
);
753 if (nonce
!= (unsigned)rep
->enc_part
.nonce
) {
754 ret
= KRB5KRB_AP_ERR_MODIFIED
;
755 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
761 krb5_timeofday (context
, &sec_now
);
762 if (rep
->enc_part
.flags
.initial
763 && (flags
& EXTRACT_TICKET_TIMESYNC
)
764 && context
->kdc_sec_offset
== 0
765 && krb5_config_get_bool (context
, NULL
,
769 context
->kdc_sec_offset
= rep
->enc_part
.authtime
- sec_now
;
770 krb5_timeofday (context
, &sec_now
);
773 /* check all times */
775 if (rep
->enc_part
.starttime
) {
776 tmp_time
= *rep
->enc_part
.starttime
;
778 tmp_time
= rep
->enc_part
.authtime
;
780 if (creds
->times
.starttime
== 0
781 && abs(tmp_time
- sec_now
) > context
->max_skew
) {
782 ret
= KRB5KRB_AP_ERR_SKEW
;
783 krb5_set_error_message (context
, ret
,
784 N_("time skew (%d) larger than max (%d)", ""),
785 abs(tmp_time
- sec_now
),
786 (int)context
->max_skew
);
790 if (creds
->times
.starttime
!= 0
791 && tmp_time
!= creds
->times
.starttime
) {
792 krb5_clear_error_message (context
);
793 ret
= KRB5KRB_AP_ERR_MODIFIED
;
797 creds
->times
.starttime
= tmp_time
;
799 if (rep
->enc_part
.renew_till
) {
800 tmp_time
= *rep
->enc_part
.renew_till
;
804 if (creds
->times
.renew_till
!= 0
805 && tmp_time
> creds
->times
.renew_till
) {
806 krb5_clear_error_message (context
);
807 ret
= KRB5KRB_AP_ERR_MODIFIED
;
811 creds
->times
.renew_till
= tmp_time
;
813 creds
->times
.authtime
= rep
->enc_part
.authtime
;
815 if (creds
->times
.endtime
!= 0
816 && rep
->enc_part
.endtime
> creds
->times
.endtime
) {
817 krb5_clear_error_message (context
);
818 ret
= KRB5KRB_AP_ERR_MODIFIED
;
822 creds
->times
.endtime
= rep
->enc_part
.endtime
;
824 if(rep
->enc_part
.caddr
)
825 krb5_copy_addresses (context
, rep
->enc_part
.caddr
, &creds
->addresses
);
827 krb5_copy_addresses (context
, addrs
, &creds
->addresses
);
829 creds
->addresses
.len
= 0;
830 creds
->addresses
.val
= NULL
;
832 creds
->flags
.b
= rep
->enc_part
.flags
;
834 creds
->authdata
.len
= 0;
835 creds
->authdata
.val
= NULL
;
838 ASN1_MALLOC_ENCODE(Ticket
, creds
->ticket
.data
, creds
->ticket
.length
,
839 &rep
->kdc_rep
.ticket
, &len
, ret
);
842 if (creds
->ticket
.length
!= len
)
843 krb5_abortx(context
, "internal error in ASN.1 encoder");
844 creds
->second_ticket
.length
= 0;
845 creds
->second_ticket
.data
= NULL
;
849 memset (rep
->enc_part
.key
.keyvalue
.data
, 0,
850 rep
->enc_part
.key
.keyvalue
.length
);