2 * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include "krb5_locl.h"
37 * Free ticket and content
39 * @param context a Kerberos 5 context
40 * @param ticket ticket to free
42 * @return Returns 0 to indicate success. Otherwise an kerberos et
43 * error code is returned, see krb5_get_error_message().
48 krb5_error_code KRB5_LIB_FUNCTION
49 krb5_free_ticket(krb5_context context
,
52 free_EncTicketPart(&ticket
->ticket
);
53 krb5_free_principal(context
, ticket
->client
);
54 krb5_free_principal(context
, ticket
->server
);
60 * Copy ticket and content
62 * @param context a Kerberos 5 context
63 * @param from ticket to copy
64 * @param to new copy of ticket, free with krb5_free_ticket()
66 * @return Returns 0 to indicate success. Otherwise an kerberos et
67 * error code is returned, see krb5_get_error_message().
72 krb5_error_code KRB5_LIB_FUNCTION
73 krb5_copy_ticket(krb5_context context
,
74 const krb5_ticket
*from
,
81 tmp
= malloc(sizeof(*tmp
));
83 krb5_set_error_message(context
, ENOMEM
,
84 N_("malloc: out of memory", ""));
87 if((ret
= copy_EncTicketPart(&from
->ticket
, &tmp
->ticket
))){
91 ret
= krb5_copy_principal(context
, from
->client
, &tmp
->client
);
93 free_EncTicketPart(&tmp
->ticket
);
97 ret
= krb5_copy_principal(context
, from
->server
, &tmp
->server
);
99 krb5_free_principal(context
, tmp
->client
);
100 free_EncTicketPart(&tmp
->ticket
);
109 * Return client principal in ticket
111 * @param context a Kerberos 5 context
112 * @param ticket ticket to copy
113 * @param client client principal, free with krb5_free_principal()
115 * @return Returns 0 to indicate success. Otherwise an kerberos et
116 * error code is returned, see krb5_get_error_message().
121 krb5_error_code KRB5_LIB_FUNCTION
122 krb5_ticket_get_client(krb5_context context
,
123 const krb5_ticket
*ticket
,
124 krb5_principal
*client
)
126 return krb5_copy_principal(context
, ticket
->client
, client
);
130 * Return server principal in ticket
132 * @param context a Kerberos 5 context
133 * @param ticket ticket to copy
134 * @param server server principal, free with krb5_free_principal()
136 * @return Returns 0 to indicate success. Otherwise an kerberos et
137 * error code is returned, see krb5_get_error_message().
142 krb5_error_code KRB5_LIB_FUNCTION
143 krb5_ticket_get_server(krb5_context context
,
144 const krb5_ticket
*ticket
,
145 krb5_principal
*server
)
147 return krb5_copy_principal(context
, ticket
->server
, server
);
151 * Return end time of ticket
153 * @param context a Kerberos 5 context
154 * @param ticket ticket to copy
156 * @return end time of ticket
161 time_t KRB5_LIB_FUNCTION
162 krb5_ticket_get_endtime(krb5_context context
,
163 const krb5_ticket
*ticket
)
165 return ticket
->ticket
.endtime
;
169 * Get the flags from the Kerberos ticket
171 * @param context Kerberos context
172 * @param ticket Kerberos ticket
174 * @return ticket flags
176 * @ingroup krb5_ticket
179 krb5_ticket_get_flags(krb5_context context
,
180 const krb5_ticket
*ticket
)
182 return TicketFlags2int(ticket
->ticket
.flags
);
186 find_type_in_ad(krb5_context context
,
191 krb5_keyblock
*sessionkey
,
192 const AuthorizationData
*ad
,
195 krb5_error_code ret
= 0;
199 ret
= ENOENT
; /* XXX */
200 krb5_set_error_message(context
, ret
,
201 N_("Authorization data nested deeper "
202 "then %d levels, stop searching", ""),
208 * Only copy out the element the first time we get to it, we need
209 * to run over the whole authorization data fields to check if
210 * there are any container clases we need to care about.
212 for (i
= 0; i
< ad
->len
; i
++) {
213 if (!*found
&& ad
->val
[i
].ad_type
== type
) {
214 ret
= der_copy_octet_string(&ad
->val
[i
].ad_data
, data
);
216 krb5_set_error_message(context
, ret
,
217 N_("malloc: out of memory", ""));
223 switch (ad
->val
[i
].ad_type
) {
224 case KRB5_AUTHDATA_IF_RELEVANT
: {
225 AuthorizationData child
;
226 ret
= decode_AuthorizationData(ad
->val
[i
].ad_data
.data
,
227 ad
->val
[i
].ad_data
.length
,
231 krb5_set_error_message(context
, ret
,
232 N_("Failed to decode "
233 "IF_RELEVANT with %d", ""),
237 ret
= find_type_in_ad(context
, type
, data
, found
, FALSE
,
238 sessionkey
, &child
, level
+ 1);
239 free_AuthorizationData(&child
);
245 case KRB5_AUTHDATA_KDC_ISSUED
: {
248 ret
= decode_AD_KDCIssued(ad
->val
[i
].ad_data
.data
,
249 ad
->val
[i
].ad_data
.length
,
253 krb5_set_error_message(context
, ret
,
254 N_("Failed to decode "
255 "AD_KDCIssued with %d", ""),
264 ASN1_MALLOC_ENCODE(AuthorizationData
, buf
.data
, buf
.length
,
265 &child
.elements
, &len
, ret
);
267 free_AD_KDCIssued(&child
);
268 krb5_clear_error_message(context
);
271 if(buf
.length
!= len
)
272 krb5_abortx(context
, "internal error in ASN.1 encoder");
274 ret
= krb5_c_verify_checksum(context
, sessionkey
, 19, &buf
,
275 &child
.ad_checksum
, &valid
);
276 krb5_data_free(&buf
);
278 free_AD_KDCIssued(&child
);
282 krb5_clear_error_message(context
);
284 free_AD_KDCIssued(&child
);
288 ret
= find_type_in_ad(context
, type
, data
, found
, failp
, sessionkey
,
289 &child
.elements
, level
+ 1);
290 free_AD_KDCIssued(&child
);
296 case KRB5_AUTHDATA_AND_OR
:
299 ret
= ENOENT
; /* XXX */
300 krb5_set_error_message(context
, ret
,
301 N_("Authorization data contains "
302 "AND-OR element that is unknown to the "
308 ret
= ENOENT
; /* XXX */
309 krb5_set_error_message(context
, ret
,
310 N_("Authorization data contains "
311 "unknown type (%d) ", ""),
319 krb5_data_free(data
);
327 * Extract the authorization data type of type from the ticket. Store
328 * the field in data. This function is to use for kerberos
331 * @param context a Kerberos 5 context
332 * @param ticket Kerberos ticket
333 * @param type type to fetch
334 * @param data returned data, free with krb5_data_free()
339 krb5_error_code KRB5_LIB_FUNCTION
340 krb5_ticket_get_authorization_data_type(krb5_context context
,
345 AuthorizationData
*ad
;
347 krb5_boolean found
= FALSE
;
349 krb5_data_zero(data
);
351 ad
= ticket
->ticket
.authorization_data
;
352 if (ticket
->ticket
.authorization_data
== NULL
) {
353 krb5_set_error_message(context
, ENOENT
,
354 N_("Ticket have not authorization data", ""));
355 return ENOENT
; /* XXX */
358 ret
= find_type_in_ad(context
, type
, data
, &found
, TRUE
,
359 &ticket
->ticket
.key
, ad
, 0);
363 krb5_set_error_message(context
, ENOENT
,
364 N_("Ticket have not "
365 "authorization data of type %d", ""),
367 return ENOENT
; /* XXX */
372 static krb5_error_code
373 check_server_referral(krb5_context context
,
376 krb5_const_principal requested
,
377 krb5_const_principal returned
,
381 PA_ServerReferralData ref
;
389 if (rep
->kdc_rep
.padata
== NULL
)
392 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
393 rep
->kdc_rep
.padata
->len
,
394 KRB5_PADATA_SERVER_REFERRAL
, &i
);
398 memset(&ed
, 0, sizeof(ed
));
399 memset(&ref
, 0, sizeof(ref
));
401 ret
= decode_EncryptedData(pa
->padata_value
.data
,
402 pa
->padata_value
.length
,
406 if (len
!= pa
->padata_value
.length
) {
407 free_EncryptedData(&ed
);
408 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
409 N_("Referral EncryptedData wrong for realm %s",
410 "realm"), requested
->realm
);
411 return KRB5KRB_AP_ERR_MODIFIED
;
414 ret
= krb5_crypto_init(context
, key
, 0, &session
);
416 free_EncryptedData(&ed
);
420 ret
= krb5_decrypt_EncryptedData(context
, session
,
421 KRB5_KU_PA_SERVER_REFERRAL
,
423 free_EncryptedData(&ed
);
424 krb5_crypto_destroy(context
, session
);
428 ret
= decode_PA_ServerReferralData(data
.data
, data
.length
, &ref
, &len
);
430 krb5_data_free(&data
);
433 krb5_data_free(&data
);
435 if (strcmp(requested
->realm
, returned
->realm
) != 0) {
436 free_PA_ServerReferralData(&ref
);
437 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
438 N_("server ref realm mismatch, "
439 "requested realm %s got back %s", ""),
440 requested
->realm
, returned
->realm
);
441 return KRB5KRB_AP_ERR_MODIFIED
;
444 if (krb5_principal_is_krbtgt(context
, returned
)) {
445 const char *realm
= returned
->name
.name_string
.val
[1];
447 if (ref
.referred_realm
== NULL
448 || strcmp(*ref
.referred_realm
, realm
) != 0)
450 free_PA_ServerReferralData(&ref
);
451 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
452 N_("tgt returned with wrong ref", ""));
453 return KRB5KRB_AP_ERR_MODIFIED
;
455 } else if (krb5_principal_compare(context
, returned
, requested
) == 0) {
456 free_PA_ServerReferralData(&ref
);
457 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
458 N_("req princ no same as returned", ""));
459 return KRB5KRB_AP_ERR_MODIFIED
;
462 if (ref
.requested_principal_name
) {
463 cmp
= _krb5_principal_compare_PrincipalName(context
,
465 ref
.requested_principal_name
);
467 free_PA_ServerReferralData(&ref
);
468 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
469 N_("referred principal not same "
470 "as requested", ""));
471 return KRB5KRB_AP_ERR_MODIFIED
;
473 } else if (flags
& EXTRACT_TICKET_AS_REQ
) {
474 free_PA_ServerReferralData(&ref
);
475 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
476 N_("Requested principal missing on AS-REQ", ""));
477 return KRB5KRB_AP_ERR_MODIFIED
;
480 free_PA_ServerReferralData(&ref
);
485 * Expect excact match or that we got a krbtgt
487 if (krb5_principal_compare(context
, requested
, returned
) != TRUE
&&
488 (krb5_realm_compare(context
, requested
, returned
) != TRUE
&&
489 krb5_principal_is_krbtgt(context
, returned
) != TRUE
))
491 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
492 N_("Not same server principal returned "
493 "as requested", ""));
494 return KRB5KRB_AP_ERR_MODIFIED
;
501 * Verify referral data
505 static krb5_error_code
506 check_client_referral(krb5_context context
,
508 krb5_const_principal requested
,
509 krb5_const_principal mapped
,
510 krb5_keyblock
const * key
)
513 PA_ClientCanonicalized canon
;
520 if (rep
->kdc_rep
.padata
== NULL
)
523 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
524 rep
->kdc_rep
.padata
->len
,
525 KRB5_PADATA_CLIENT_CANONICALIZED
, &i
);
529 ret
= decode_PA_ClientCanonicalized(pa
->padata_value
.data
,
530 pa
->padata_value
.length
,
533 krb5_set_error_message(context
, ret
,
534 N_("Failed to decode ClientCanonicalized "
535 "from realm %s", ""), requested
->realm
);
539 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames
, data
.data
, data
.length
,
540 &canon
.names
, &len
, ret
);
542 free_PA_ClientCanonicalized(&canon
);
545 if (data
.length
!= len
)
546 krb5_abortx(context
, "internal asn.1 error");
548 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
551 free_PA_ClientCanonicalized(&canon
);
555 ret
= krb5_verify_checksum(context
, crypto
, KRB5_KU_CANONICALIZED_NAMES
,
556 data
.data
, data
.length
,
557 &canon
.canon_checksum
);
558 krb5_crypto_destroy(context
, crypto
);
561 krb5_set_error_message(context
, ret
,
562 N_("Failed to verify client canonicalized "
563 "data from realm %s", ""),
565 free_PA_ClientCanonicalized(&canon
);
569 if (!_krb5_principal_compare_PrincipalName(context
,
571 &canon
.names
.requested_name
))
573 free_PA_ClientCanonicalized(&canon
);
574 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
575 N_("Requested name doesn't match"
576 " in client referral", ""));
577 return KRB5_PRINC_NOMATCH
;
579 if (!_krb5_principal_compare_PrincipalName(context
,
581 &canon
.names
.mapped_name
))
583 free_PA_ClientCanonicalized(&canon
);
584 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
585 N_("Mapped name doesn't match"
586 " in client referral", ""));
587 return KRB5_PRINC_NOMATCH
;
593 if (krb5_principal_compare(context
, requested
, mapped
) == FALSE
) {
594 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
595 N_("Not same client principal returned "
596 "as requested", ""));
597 return KRB5KRB_AP_ERR_MODIFIED
;
603 static krb5_error_code
604 decrypt_tkt (krb5_context context
,
606 krb5_key_usage usage
,
607 krb5_const_pointer decrypt_arg
,
608 krb5_kdc_rep
*dec_rep
)
615 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
619 ret
= krb5_decrypt_EncryptedData (context
,
622 &dec_rep
->kdc_rep
.enc_part
,
624 krb5_crypto_destroy(context
, crypto
);
629 ret
= decode_EncASRepPart(data
.data
,
634 ret
= decode_EncTGSRepPart(data
.data
,
638 krb5_data_free (&data
);
640 krb5_set_error_message(context
, ret
,
641 N_("Failed to decode encpart in ticket", ""));
648 _krb5_extract_ticket(krb5_context context
,
652 krb5_const_pointer keyseed
,
653 krb5_key_usage key_usage
,
654 krb5_addresses
*addrs
,
657 krb5_decrypt_proc decrypt_proc
,
658 krb5_const_pointer decryptarg
)
661 krb5_principal tmp_principal
;
664 krb5_timestamp sec_now
;
668 if (decrypt_proc
== NULL
)
669 decrypt_proc
= decrypt_tkt
;
671 ret
= (*decrypt_proc
)(context
, key
, key_usage
, decryptarg
, rep
);
675 /* save session key */
677 creds
->session
.keyvalue
.length
= 0;
678 creds
->session
.keyvalue
.data
= NULL
;
679 creds
->session
.keytype
= rep
->enc_part
.key
.keytype
;
680 ret
= krb5_data_copy (&creds
->session
.keyvalue
,
681 rep
->enc_part
.key
.keyvalue
.data
,
682 rep
->enc_part
.key
.keyvalue
.length
);
684 krb5_clear_error_message(context
);
688 /* compare client and save */
689 ret
= _krb5_principalname2krb5_principal (context
,
692 rep
->kdc_rep
.crealm
);
696 /* check client referral and save principal */
697 /* anonymous here ? */
698 if((flags
& EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
) == 0) {
699 ret
= check_client_referral(context
, rep
,
704 krb5_free_principal (context
, tmp_principal
);
708 krb5_free_principal (context
, creds
->client
);
709 creds
->client
= tmp_principal
;
711 /* check server referral and save principal */
712 ret
= _krb5_principalname2krb5_principal (context
,
714 rep
->kdc_rep
.ticket
.sname
,
715 rep
->kdc_rep
.ticket
.realm
);
718 if((flags
& EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
) == 0){
719 ret
= check_server_referral(context
,
726 krb5_free_principal (context
, tmp_principal
);
730 krb5_free_principal(context
, creds
->server
);
731 creds
->server
= tmp_principal
;
734 if(flags
& EXTRACT_TICKET_MATCH_REALM
){
735 const char *srealm
= krb5_principal_get_realm(context
, creds
->server
);
736 const char *crealm
= krb5_principal_get_realm(context
, creds
->client
);
738 if (strcmp(rep
->enc_part
.srealm
, srealm
) != 0 ||
739 strcmp(rep
->enc_part
.srealm
, crealm
) != 0)
741 ret
= KRB5KRB_AP_ERR_MODIFIED
;
742 krb5_clear_error_message(context
);
749 if (nonce
!= rep
->enc_part
.nonce
) {
750 ret
= KRB5KRB_AP_ERR_MODIFIED
;
751 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
757 krb5_timeofday (context
, &sec_now
);
758 if (rep
->enc_part
.flags
.initial
759 && context
->kdc_sec_offset
== 0
760 && krb5_config_get_bool (context
, NULL
,
764 context
->kdc_sec_offset
= rep
->enc_part
.authtime
- sec_now
;
765 krb5_timeofday (context
, &sec_now
);
768 /* check all times */
770 if (rep
->enc_part
.starttime
) {
771 tmp_time
= *rep
->enc_part
.starttime
;
773 tmp_time
= rep
->enc_part
.authtime
;
775 if (creds
->times
.starttime
== 0
776 && abs(tmp_time
- sec_now
) > context
->max_skew
) {
777 ret
= KRB5KRB_AP_ERR_SKEW
;
778 krb5_set_error_message (context
, ret
,
779 N_("time skew (%d) larger than max (%d)", ""),
780 abs(tmp_time
- sec_now
),
781 (int)context
->max_skew
);
785 if (creds
->times
.starttime
!= 0
786 && tmp_time
!= creds
->times
.starttime
) {
787 krb5_clear_error_message (context
);
788 ret
= KRB5KRB_AP_ERR_MODIFIED
;
792 creds
->times
.starttime
= tmp_time
;
794 if (rep
->enc_part
.renew_till
) {
795 tmp_time
= *rep
->enc_part
.renew_till
;
799 if (creds
->times
.renew_till
!= 0
800 && tmp_time
> creds
->times
.renew_till
) {
801 krb5_clear_error_message (context
);
802 ret
= KRB5KRB_AP_ERR_MODIFIED
;
806 creds
->times
.renew_till
= tmp_time
;
808 creds
->times
.authtime
= rep
->enc_part
.authtime
;
810 if (creds
->times
.endtime
!= 0
811 && rep
->enc_part
.endtime
> creds
->times
.endtime
) {
812 krb5_clear_error_message (context
);
813 ret
= KRB5KRB_AP_ERR_MODIFIED
;
817 creds
->times
.endtime
= rep
->enc_part
.endtime
;
819 if(rep
->enc_part
.caddr
)
820 krb5_copy_addresses (context
, rep
->enc_part
.caddr
, &creds
->addresses
);
822 krb5_copy_addresses (context
, addrs
, &creds
->addresses
);
824 creds
->addresses
.len
= 0;
825 creds
->addresses
.val
= NULL
;
827 creds
->flags
.b
= rep
->enc_part
.flags
;
829 creds
->authdata
.len
= 0;
830 creds
->authdata
.val
= NULL
;
833 ASN1_MALLOC_ENCODE(Ticket
, creds
->ticket
.data
, creds
->ticket
.length
,
834 &rep
->kdc_rep
.ticket
, &len
, ret
);
837 if (creds
->ticket
.length
!= len
)
838 krb5_abortx(context
, "internal error in ASN.1 encoder");
839 creds
->second_ticket
.length
= 0;
840 creds
->second_ticket
.data
= NULL
;
844 memset (rep
->enc_part
.key
.keyvalue
.data
, 0,
845 rep
->enc_part
.key
.keyvalue
.length
);