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 (returned
->name
.name_string
.len
== 2 &&
445 strcmp(returned
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0)
447 const char *realm
= returned
->name
.name_string
.val
[1];
449 if (ref
.referred_realm
== NULL
450 || strcmp(*ref
.referred_realm
, realm
) != 0)
452 free_PA_ServerReferralData(&ref
);
453 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
454 N_("tgt returned with wrong ref", ""));
455 return KRB5KRB_AP_ERR_MODIFIED
;
457 } else if (krb5_principal_compare(context
, returned
, requested
) == 0) {
458 free_PA_ServerReferralData(&ref
);
459 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
460 N_("req princ no same as returned", ""));
461 return KRB5KRB_AP_ERR_MODIFIED
;
464 if (ref
.requested_principal_name
) {
465 cmp
= _krb5_principal_compare_PrincipalName(context
,
467 ref
.requested_principal_name
);
469 free_PA_ServerReferralData(&ref
);
470 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
471 N_("referred principal not same "
472 "as requested", ""));
473 return KRB5KRB_AP_ERR_MODIFIED
;
475 } else if (flags
& EXTRACT_TICKET_AS_REQ
) {
476 free_PA_ServerReferralData(&ref
);
477 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
478 N_("Requested principal missing on AS-REQ", ""));
479 return KRB5KRB_AP_ERR_MODIFIED
;
482 free_PA_ServerReferralData(&ref
);
486 if (krb5_principal_compare(context
, requested
, returned
) == FALSE
) {
487 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
488 N_("Not same server principal returned "
489 "as requested", ""));
490 return KRB5KRB_AP_ERR_MODIFIED
;
497 * Verify referral data
501 static krb5_error_code
502 check_client_referral(krb5_context context
,
504 krb5_const_principal requested
,
505 krb5_const_principal mapped
,
506 krb5_keyblock
const * key
)
509 PA_ClientCanonicalized canon
;
516 if (rep
->kdc_rep
.padata
== NULL
)
519 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
520 rep
->kdc_rep
.padata
->len
,
521 KRB5_PADATA_CLIENT_CANONICALIZED
, &i
);
525 ret
= decode_PA_ClientCanonicalized(pa
->padata_value
.data
,
526 pa
->padata_value
.length
,
529 krb5_set_error_message(context
, ret
,
530 N_("Failed to decode ClientCanonicalized "
531 "from realm %s", ""), requested
->realm
);
535 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames
, data
.data
, data
.length
,
536 &canon
.names
, &len
, ret
);
538 free_PA_ClientCanonicalized(&canon
);
541 if (data
.length
!= len
)
542 krb5_abortx(context
, "internal asn.1 error");
544 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
547 free_PA_ClientCanonicalized(&canon
);
551 ret
= krb5_verify_checksum(context
, crypto
, KRB5_KU_CANONICALIZED_NAMES
,
552 data
.data
, data
.length
,
553 &canon
.canon_checksum
);
554 krb5_crypto_destroy(context
, crypto
);
557 krb5_set_error_message(context
, ret
,
558 N_("Failed to verify client canonicalized "
559 "data from realm %s", ""),
561 free_PA_ClientCanonicalized(&canon
);
565 if (!_krb5_principal_compare_PrincipalName(context
,
567 &canon
.names
.requested_name
))
569 free_PA_ClientCanonicalized(&canon
);
570 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
571 N_("Requested name doesn't match"
572 " in client referral", ""));
573 return KRB5_PRINC_NOMATCH
;
575 if (!_krb5_principal_compare_PrincipalName(context
,
577 &canon
.names
.mapped_name
))
579 free_PA_ClientCanonicalized(&canon
);
580 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
581 N_("Mapped name doesn't match"
582 " in client referral", ""));
583 return KRB5_PRINC_NOMATCH
;
589 if (krb5_principal_compare(context
, requested
, mapped
) == FALSE
) {
590 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
591 N_("Not same client principal returned "
592 "as requested", ""));
593 return KRB5KRB_AP_ERR_MODIFIED
;
599 static krb5_error_code
600 decrypt_tkt (krb5_context context
,
602 krb5_key_usage usage
,
603 krb5_const_pointer decrypt_arg
,
604 krb5_kdc_rep
*dec_rep
)
611 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
615 ret
= krb5_decrypt_EncryptedData (context
,
618 &dec_rep
->kdc_rep
.enc_part
,
620 krb5_crypto_destroy(context
, crypto
);
625 ret
= decode_EncASRepPart(data
.data
,
630 ret
= decode_EncTGSRepPart(data
.data
,
634 krb5_data_free (&data
);
636 krb5_set_error_message(context
, ret
,
637 N_("Failed to decode encpart in ticket", ""));
644 _krb5_extract_ticket(krb5_context context
,
648 krb5_const_pointer keyseed
,
649 krb5_key_usage key_usage
,
650 krb5_addresses
*addrs
,
653 krb5_decrypt_proc decrypt_proc
,
654 krb5_const_pointer decryptarg
)
657 krb5_principal tmp_principal
;
660 krb5_timestamp sec_now
;
664 if (decrypt_proc
== NULL
)
665 decrypt_proc
= decrypt_tkt
;
667 ret
= (*decrypt_proc
)(context
, key
, key_usage
, decryptarg
, rep
);
671 /* save session key */
673 creds
->session
.keyvalue
.length
= 0;
674 creds
->session
.keyvalue
.data
= NULL
;
675 creds
->session
.keytype
= rep
->enc_part
.key
.keytype
;
676 ret
= krb5_data_copy (&creds
->session
.keyvalue
,
677 rep
->enc_part
.key
.keyvalue
.data
,
678 rep
->enc_part
.key
.keyvalue
.length
);
680 krb5_clear_error_message(context
);
686 * this is really a ugly hack, to support using the Netbios Domain Name
687 * as realm against windows KDC's, they always return the full realm
688 * based on the DNS Name.
690 flags
|= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
;
691 flags
|= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
;
693 /* compare client and save */
694 ret
= _krb5_principalname2krb5_principal (context
,
697 rep
->kdc_rep
.crealm
);
701 /* check client referral and save principal */
702 /* anonymous here ? */
703 if((flags
& EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
) == 0) {
704 ret
= check_client_referral(context
, rep
,
709 krb5_free_principal (context
, tmp_principal
);
713 krb5_free_principal (context
, creds
->client
);
714 creds
->client
= tmp_principal
;
716 /* check server referral and save principal */
717 ret
= _krb5_principalname2krb5_principal (context
,
719 rep
->kdc_rep
.ticket
.sname
,
720 rep
->kdc_rep
.ticket
.realm
);
723 if((flags
& EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
) == 0){
724 ret
= check_server_referral(context
,
731 krb5_free_principal (context
, tmp_principal
);
735 krb5_free_principal(context
, creds
->server
);
736 creds
->server
= tmp_principal
;
739 if(flags
& EXTRACT_TICKET_MATCH_REALM
){
740 const char *srealm
= krb5_principal_get_realm(context
, creds
->server
);
741 const char *crealm
= krb5_principal_get_realm(context
, creds
->client
);
743 if (strcmp(rep
->enc_part
.srealm
, srealm
) != 0 ||
744 strcmp(rep
->enc_part
.srealm
, crealm
) != 0)
746 ret
= KRB5KRB_AP_ERR_MODIFIED
;
747 krb5_clear_error_message(context
);
754 if (nonce
!= rep
->enc_part
.nonce
) {
755 ret
= KRB5KRB_AP_ERR_MODIFIED
;
756 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
762 krb5_timeofday (context
, &sec_now
);
763 if (rep
->enc_part
.flags
.initial
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
);