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"
36 krb5_error_code KRB5_LIB_FUNCTION
37 krb5_free_ticket(krb5_context context
,
40 free_EncTicketPart(&ticket
->ticket
);
41 krb5_free_principal(context
, ticket
->client
);
42 krb5_free_principal(context
, ticket
->server
);
47 krb5_error_code KRB5_LIB_FUNCTION
48 krb5_copy_ticket(krb5_context context
,
49 const krb5_ticket
*from
,
56 tmp
= malloc(sizeof(*tmp
));
58 krb5_set_error_message(context
, ENOMEM
,
59 N_("malloc: out of memory", ""));
62 if((ret
= copy_EncTicketPart(&from
->ticket
, &tmp
->ticket
))){
66 ret
= krb5_copy_principal(context
, from
->client
, &tmp
->client
);
68 free_EncTicketPart(&tmp
->ticket
);
72 ret
= krb5_copy_principal(context
, from
->server
, &tmp
->server
);
74 krb5_free_principal(context
, tmp
->client
);
75 free_EncTicketPart(&tmp
->ticket
);
83 krb5_error_code KRB5_LIB_FUNCTION
84 krb5_ticket_get_client(krb5_context context
,
85 const krb5_ticket
*ticket
,
86 krb5_principal
*client
)
88 return krb5_copy_principal(context
, ticket
->client
, client
);
91 krb5_error_code KRB5_LIB_FUNCTION
92 krb5_ticket_get_server(krb5_context context
,
93 const krb5_ticket
*ticket
,
94 krb5_principal
*server
)
96 return krb5_copy_principal(context
, ticket
->server
, server
);
99 time_t KRB5_LIB_FUNCTION
100 krb5_ticket_get_endtime(krb5_context context
,
101 const krb5_ticket
*ticket
)
103 return ticket
->ticket
.endtime
;
107 * Get the flags from the Kerberos ticket
109 * @param context Kerberos context
110 * @param ticket Kerberos ticket
112 * @return ticket flags
114 * @ingroup krb5_ticket
117 krb5_ticket_get_flags(krb5_context context
,
118 const krb5_ticket
*ticket
)
120 return TicketFlags2int(ticket
->ticket
.flags
);
124 find_type_in_ad(krb5_context context
,
129 krb5_keyblock
*sessionkey
,
130 const AuthorizationData
*ad
,
133 krb5_error_code ret
= 0;
137 ret
= ENOENT
; /* XXX */
138 krb5_set_error_message(context
, ret
,
139 N_("Authorization data nested deeper "
140 "then %d levels, stop searching", ""),
146 * Only copy out the element the first time we get to it, we need
147 * to run over the whole authorization data fields to check if
148 * there are any container clases we need to care about.
150 for (i
= 0; i
< ad
->len
; i
++) {
151 if (!*found
&& ad
->val
[i
].ad_type
== type
) {
152 ret
= der_copy_octet_string(&ad
->val
[i
].ad_data
, data
);
154 krb5_set_error_message(context
, ret
,
155 N_("malloc: out of memory", ""));
161 switch (ad
->val
[i
].ad_type
) {
162 case KRB5_AUTHDATA_IF_RELEVANT
: {
163 AuthorizationData child
;
164 ret
= decode_AuthorizationData(ad
->val
[i
].ad_data
.data
,
165 ad
->val
[i
].ad_data
.length
,
169 krb5_set_error_message(context
, ret
,
170 N_("Failed to decode "
171 "IF_RELEVANT with %d", ""),
175 ret
= find_type_in_ad(context
, type
, data
, found
, FALSE
,
176 sessionkey
, &child
, level
+ 1);
177 free_AuthorizationData(&child
);
183 case KRB5_AUTHDATA_KDC_ISSUED
: {
186 ret
= decode_AD_KDCIssued(ad
->val
[i
].ad_data
.data
,
187 ad
->val
[i
].ad_data
.length
,
191 krb5_set_error_message(context
, ret
,
192 N_("Failed to decode "
193 "AD_KDCIssued with %d", ""),
202 ASN1_MALLOC_ENCODE(AuthorizationData
, buf
.data
, buf
.length
,
203 &child
.elements
, &len
, ret
);
205 free_AD_KDCIssued(&child
);
206 krb5_clear_error_message(context
);
209 if(buf
.length
!= len
)
210 krb5_abortx(context
, "internal error in ASN.1 encoder");
212 ret
= krb5_c_verify_checksum(context
, sessionkey
, 19, &buf
,
213 &child
.ad_checksum
, &valid
);
214 krb5_data_free(&buf
);
216 free_AD_KDCIssued(&child
);
220 krb5_clear_error_message(context
);
222 free_AD_KDCIssued(&child
);
226 ret
= find_type_in_ad(context
, type
, data
, found
, failp
, sessionkey
,
227 &child
.elements
, level
+ 1);
228 free_AD_KDCIssued(&child
);
234 case KRB5_AUTHDATA_AND_OR
:
237 ret
= ENOENT
; /* XXX */
238 krb5_set_error_message(context
, ret
,
239 N_("Authorization data contains "
240 "AND-OR element that is unknown to the "
246 ret
= ENOENT
; /* XXX */
247 krb5_set_error_message(context
, ret
,
248 N_("Authorization data contains "
249 "unknown type (%d) ", ""),
257 krb5_data_free(data
);
265 * Extract the authorization data type of `type' from the
266 * 'ticket'. Store the field in `data'. This function is to use for
267 * kerberos applications.
270 krb5_error_code KRB5_LIB_FUNCTION
271 krb5_ticket_get_authorization_data_type(krb5_context context
,
276 AuthorizationData
*ad
;
278 krb5_boolean found
= FALSE
;
280 krb5_data_zero(data
);
282 ad
= ticket
->ticket
.authorization_data
;
283 if (ticket
->ticket
.authorization_data
== NULL
) {
284 krb5_set_error_message(context
, ENOENT
,
285 N_("Ticket have not authorization data", ""));
286 return ENOENT
; /* XXX */
289 ret
= find_type_in_ad(context
, type
, data
, &found
, TRUE
,
290 &ticket
->ticket
.key
, ad
, 0);
294 krb5_set_error_message(context
, ENOENT
,
295 N_("Ticket have not "
296 "authorization data of type %d", ""),
298 return ENOENT
; /* XXX */
303 static krb5_error_code
304 check_server_referral(krb5_context context
,
307 krb5_const_principal requested
,
308 krb5_const_principal returned
,
312 PA_ServerReferralData ref
;
320 if (rep
->kdc_rep
.padata
== NULL
)
323 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
324 rep
->kdc_rep
.padata
->len
,
325 KRB5_PADATA_SERVER_REFERRAL
, &i
);
329 memset(&ed
, 0, sizeof(ed
));
330 memset(&ref
, 0, sizeof(ref
));
332 ret
= decode_EncryptedData(pa
->padata_value
.data
,
333 pa
->padata_value
.length
,
337 if (len
!= pa
->padata_value
.length
) {
338 free_EncryptedData(&ed
);
339 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
340 N_("Referral EncryptedData wrong for realm %s",
341 "realm"), requested
->realm
);
342 return KRB5KRB_AP_ERR_MODIFIED
;
345 ret
= krb5_crypto_init(context
, key
, 0, &session
);
347 free_EncryptedData(&ed
);
351 ret
= krb5_decrypt_EncryptedData(context
, session
,
352 KRB5_KU_PA_SERVER_REFERRAL
,
354 free_EncryptedData(&ed
);
355 krb5_crypto_destroy(context
, session
);
359 ret
= decode_PA_ServerReferralData(data
.data
, data
.length
, &ref
, &len
);
361 krb5_data_free(&data
);
364 krb5_data_free(&data
);
366 if (strcmp(requested
->realm
, returned
->realm
) != 0) {
367 free_PA_ServerReferralData(&ref
);
368 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
369 N_("server ref realm mismatch, "
370 "requested realm %s got back %s", ""),
371 requested
->realm
, returned
->realm
);
372 return KRB5KRB_AP_ERR_MODIFIED
;
375 if (returned
->name
.name_string
.len
== 2 &&
376 strcmp(returned
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0)
378 const char *realm
= returned
->name
.name_string
.val
[1];
380 if (ref
.referred_realm
== NULL
381 || strcmp(*ref
.referred_realm
, realm
) != 0)
383 free_PA_ServerReferralData(&ref
);
384 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
385 N_("tgt returned with wrong ref", ""));
386 return KRB5KRB_AP_ERR_MODIFIED
;
388 } else if (krb5_principal_compare(context
, returned
, requested
) == 0) {
389 free_PA_ServerReferralData(&ref
);
390 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
391 N_("req princ no same as returned", ""));
392 return KRB5KRB_AP_ERR_MODIFIED
;
395 if (ref
.requested_principal_name
) {
396 cmp
= _krb5_principal_compare_PrincipalName(context
,
398 ref
.requested_principal_name
);
400 free_PA_ServerReferralData(&ref
);
401 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
402 N_("referred principal not same "
403 "as requested", ""));
404 return KRB5KRB_AP_ERR_MODIFIED
;
406 } else if (flags
& EXTRACT_TICKET_AS_REQ
) {
407 free_PA_ServerReferralData(&ref
);
408 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
409 N_("Requested principal missing on AS-REQ", ""));
410 return KRB5KRB_AP_ERR_MODIFIED
;
413 free_PA_ServerReferralData(&ref
);
417 if (krb5_principal_compare(context
, requested
, returned
) == FALSE
) {
418 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
419 N_("Not same server principal returned "
420 "as requested", ""));
421 return KRB5KRB_AP_ERR_MODIFIED
;
428 * Verify referral data
432 static krb5_error_code
433 check_client_referral(krb5_context context
,
435 krb5_const_principal requested
,
436 krb5_const_principal mapped
,
437 krb5_keyblock
const * key
)
440 PA_ClientCanonicalized canon
;
447 if (rep
->kdc_rep
.padata
== NULL
)
450 pa
= krb5_find_padata(rep
->kdc_rep
.padata
->val
,
451 rep
->kdc_rep
.padata
->len
,
452 KRB5_PADATA_CLIENT_CANONICALIZED
, &i
);
456 ret
= decode_PA_ClientCanonicalized(pa
->padata_value
.data
,
457 pa
->padata_value
.length
,
460 krb5_set_error_message(context
, ret
,
461 N_("Failed to decode ClientCanonicalized "
462 "from realm %s", ""), requested
->realm
);
466 ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames
, data
.data
, data
.length
,
467 &canon
.names
, &len
, ret
);
469 free_PA_ClientCanonicalized(&canon
);
472 if (data
.length
!= len
)
473 krb5_abortx(context
, "internal asn.1 error");
475 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
478 free_PA_ClientCanonicalized(&canon
);
482 ret
= krb5_verify_checksum(context
, crypto
, KRB5_KU_CANONICALIZED_NAMES
,
483 data
.data
, data
.length
,
484 &canon
.canon_checksum
);
485 krb5_crypto_destroy(context
, crypto
);
488 krb5_set_error_message(context
, ret
,
489 N_("Failed to verify client canonicalized "
490 "data from realm %s", ""),
492 free_PA_ClientCanonicalized(&canon
);
496 if (!_krb5_principal_compare_PrincipalName(context
,
498 &canon
.names
.requested_name
))
500 free_PA_ClientCanonicalized(&canon
);
501 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
502 N_("Requested name doesn't match"
503 " in client referral", ""));
504 return KRB5_PRINC_NOMATCH
;
506 if (!_krb5_principal_compare_PrincipalName(context
,
508 &canon
.names
.mapped_name
))
510 free_PA_ClientCanonicalized(&canon
);
511 krb5_set_error_message(context
, KRB5_PRINC_NOMATCH
,
512 N_("Mapped name doesn't match"
513 " in client referral", ""));
514 return KRB5_PRINC_NOMATCH
;
520 if (krb5_principal_compare(context
, requested
, mapped
) == FALSE
) {
521 krb5_set_error_message(context
, KRB5KRB_AP_ERR_MODIFIED
,
522 N_("Not same client principal returned "
523 "as requested", ""));
524 return KRB5KRB_AP_ERR_MODIFIED
;
530 static krb5_error_code
531 decrypt_tkt (krb5_context context
,
533 krb5_key_usage usage
,
534 krb5_const_pointer decrypt_arg
,
535 krb5_kdc_rep
*dec_rep
)
542 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
546 ret
= krb5_decrypt_EncryptedData (context
,
549 &dec_rep
->kdc_rep
.enc_part
,
551 krb5_crypto_destroy(context
, crypto
);
556 ret
= decode_EncASRepPart(data
.data
,
561 ret
= decode_EncTGSRepPart(data
.data
,
565 krb5_data_free (&data
);
567 krb5_set_error_message(context
, ret
,
568 N_("Failed to decode encpart in ticket", ""));
575 _krb5_extract_ticket(krb5_context context
,
579 krb5_const_pointer keyseed
,
580 krb5_key_usage key_usage
,
581 krb5_addresses
*addrs
,
584 krb5_decrypt_proc decrypt_proc
,
585 krb5_const_pointer decryptarg
)
588 krb5_principal tmp_principal
;
591 krb5_timestamp sec_now
;
595 if (decrypt_proc
== NULL
)
596 decrypt_proc
= decrypt_tkt
;
598 ret
= (*decrypt_proc
)(context
, key
, key_usage
, decryptarg
, rep
);
602 /* save session key */
604 creds
->session
.keyvalue
.length
= 0;
605 creds
->session
.keyvalue
.data
= NULL
;
606 creds
->session
.keytype
= rep
->enc_part
.key
.keytype
;
607 ret
= krb5_data_copy (&creds
->session
.keyvalue
,
608 rep
->enc_part
.key
.keyvalue
.data
,
609 rep
->enc_part
.key
.keyvalue
.length
);
611 krb5_clear_error_message(context
);
615 /* compare client and save */
616 ret
= _krb5_principalname2krb5_principal (context
,
619 rep
->kdc_rep
.crealm
);
623 /* check client referral and save principal */
624 /* anonymous here ? */
625 if((flags
& EXTRACT_TICKET_ALLOW_CNAME_MISMATCH
) == 0) {
626 ret
= check_client_referral(context
, rep
,
631 krb5_free_principal (context
, tmp_principal
);
635 krb5_free_principal (context
, creds
->client
);
636 creds
->client
= tmp_principal
;
638 /* check server referral and save principal */
639 ret
= _krb5_principalname2krb5_principal (context
,
641 rep
->kdc_rep
.ticket
.sname
,
642 rep
->kdc_rep
.ticket
.realm
);
645 if((flags
& EXTRACT_TICKET_ALLOW_SERVER_MISMATCH
) == 0){
646 ret
= check_server_referral(context
,
653 krb5_free_principal (context
, tmp_principal
);
657 krb5_free_principal(context
, creds
->server
);
658 creds
->server
= tmp_principal
;
661 if(flags
& EXTRACT_TICKET_MATCH_REALM
){
662 const char *srealm
= krb5_principal_get_realm(context
, creds
->server
);
663 const char *crealm
= krb5_principal_get_realm(context
, creds
->client
);
665 if (strcmp(rep
->enc_part
.srealm
, srealm
) != 0 ||
666 strcmp(rep
->enc_part
.srealm
, crealm
) != 0)
668 ret
= KRB5KRB_AP_ERR_MODIFIED
;
669 krb5_clear_error_message(context
);
676 if (nonce
!= rep
->enc_part
.nonce
) {
677 ret
= KRB5KRB_AP_ERR_MODIFIED
;
678 krb5_set_error_message(context
, ret
, N_("malloc: out of memory", ""));
684 krb5_timeofday (context
, &sec_now
);
685 if (rep
->enc_part
.flags
.initial
686 && context
->kdc_sec_offset
== 0
687 && krb5_config_get_bool (context
, NULL
,
691 context
->kdc_sec_offset
= rep
->enc_part
.authtime
- sec_now
;
692 krb5_timeofday (context
, &sec_now
);
695 /* check all times */
697 if (rep
->enc_part
.starttime
) {
698 tmp_time
= *rep
->enc_part
.starttime
;
700 tmp_time
= rep
->enc_part
.authtime
;
702 if (creds
->times
.starttime
== 0
703 && abs(tmp_time
- sec_now
) > context
->max_skew
) {
704 ret
= KRB5KRB_AP_ERR_SKEW
;
705 krb5_set_error_message (context
, ret
,
706 N_("time skew (%d) larger than max (%d)", ""),
707 abs(tmp_time
- sec_now
),
708 (int)context
->max_skew
);
712 if (creds
->times
.starttime
!= 0
713 && tmp_time
!= creds
->times
.starttime
) {
714 krb5_clear_error_message (context
);
715 ret
= KRB5KRB_AP_ERR_MODIFIED
;
719 creds
->times
.starttime
= tmp_time
;
721 if (rep
->enc_part
.renew_till
) {
722 tmp_time
= *rep
->enc_part
.renew_till
;
726 if (creds
->times
.renew_till
!= 0
727 && tmp_time
> creds
->times
.renew_till
) {
728 krb5_clear_error_message (context
);
729 ret
= KRB5KRB_AP_ERR_MODIFIED
;
733 creds
->times
.renew_till
= tmp_time
;
735 creds
->times
.authtime
= rep
->enc_part
.authtime
;
737 if (creds
->times
.endtime
!= 0
738 && rep
->enc_part
.endtime
> creds
->times
.endtime
) {
739 krb5_clear_error_message (context
);
740 ret
= KRB5KRB_AP_ERR_MODIFIED
;
744 creds
->times
.endtime
= rep
->enc_part
.endtime
;
746 if(rep
->enc_part
.caddr
)
747 krb5_copy_addresses (context
, rep
->enc_part
.caddr
, &creds
->addresses
);
749 krb5_copy_addresses (context
, addrs
, &creds
->addresses
);
751 creds
->addresses
.len
= 0;
752 creds
->addresses
.val
= NULL
;
754 creds
->flags
.b
= rep
->enc_part
.flags
;
756 creds
->authdata
.len
= 0;
757 creds
->authdata
.val
= NULL
;
760 ASN1_MALLOC_ENCODE(Ticket
, creds
->ticket
.data
, creds
->ticket
.length
,
761 &rep
->kdc_rep
.ticket
, &len
, ret
);
764 if (creds
->ticket
.length
!= len
)
765 krb5_abortx(context
, "internal error in ASN.1 encoder");
766 creds
->second_ticket
.length
= 0;
767 creds
->second_ticket
.data
= NULL
;
771 memset (rep
->enc_part
.key
.keyvalue
.data
, 0,
772 rep
->enc_part
.key
.keyvalue
.length
);