2 * Copyright (c) 1997-2008 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
37 * return the realm of a krbtgt-ticket or NULL
41 get_krbtgt_realm(const PrincipalName
*p
)
43 if(p
->name_string
.len
== 2
44 && strcmp(p
->name_string
.val
[0], KRB5_TGS_NAME
) == 0)
45 return p
->name_string
.val
[1];
54 static krb5_error_code
55 check_PAC(krb5_context context
,
56 krb5_kdc_configuration
*config
,
57 const krb5_principal client_principal
,
58 const krb5_principal delegated_proxy_principal
,
62 hdb_entry_ex
*ticket_server
,
63 const EncryptionKey
*server_check_key
,
64 const EncryptionKey
*krbtgt_check_key
,
66 krb5_boolean
*kdc_issued
,
71 krb5_boolean signedticket
;
76 ret
= _krb5_kdc_pac_ticket_parse(context
, tkt
, &signedticket
, &pac
);
81 return KRB5KDC_ERR_TGT_REVOKED
;
83 /* Verify the server signature. */
84 ret
= krb5_pac_verify(context
, pac
, tkt
->authtime
, client_principal
,
85 server_check_key
, NULL
);
87 krb5_pac_free(context
, pac
);
91 /* Verify the KDC signatures. */
92 ret
= _kdc_pac_verify(context
, client_principal
, delegated_proxy_principal
,
93 client
, server
, krbtgt
, &pac
);
94 if (ret
== KRB5_PLUGIN_NO_HANDLE
) {
96 * We can't verify the KDC signatures if the ticket was issued by
97 * another realm's KDC.
99 if (krb5_realm_compare(context
, server
->entry
.principal
,
100 ticket_server
->entry
.principal
)) {
101 ret
= krb5_pac_verify(context
, pac
, 0, NULL
, NULL
,
104 krb5_pac_free(context
, pac
);
108 /* Discard the PAC if the plugin didn't handle it */
109 krb5_pac_free(context
, pac
);
110 ret
= krb5_pac_init(context
, &pac
);
114 krb5_pac_free(context
, pac
);
118 *kdc_issued
= signedticket
||
119 krb5_principal_is_krbtgt(context
,
120 ticket_server
->entry
.principal
);
130 static krb5_error_code
131 check_tgs_flags(krb5_context context
,
132 krb5_kdc_configuration
*config
,
133 KDC_REQ_BODY
*b
, const EncTicketPart
*tgt
, EncTicketPart
*et
)
135 KDCOptions f
= b
->kdc_options
;
138 if(!tgt
->flags
.invalid
|| tgt
->starttime
== NULL
){
139 kdc_log(context
, config
, 0,
140 "Bad request to validate ticket");
141 return KRB5KDC_ERR_BADOPTION
;
143 if(*tgt
->starttime
> kdc_time
){
144 kdc_log(context
, config
, 0,
145 "Early request to validate ticket");
146 return KRB5KRB_AP_ERR_TKT_NYV
;
149 et
->flags
.invalid
= 0;
150 }else if(tgt
->flags
.invalid
){
151 kdc_log(context
, config
, 0,
152 "Ticket-granting ticket has INVALID flag set");
153 return KRB5KRB_AP_ERR_TKT_INVALID
;
157 if(!tgt
->flags
.forwardable
){
158 kdc_log(context
, config
, 0,
159 "Bad request for forwardable ticket");
160 return KRB5KDC_ERR_BADOPTION
;
162 et
->flags
.forwardable
= 1;
165 if(!tgt
->flags
.forwardable
){
166 kdc_log(context
, config
, 0,
167 "Request to forward non-forwardable ticket");
168 return KRB5KDC_ERR_BADOPTION
;
170 et
->flags
.forwarded
= 1;
171 et
->caddr
= b
->addresses
;
173 if(tgt
->flags
.forwarded
)
174 et
->flags
.forwarded
= 1;
177 if(!tgt
->flags
.proxiable
){
178 kdc_log(context
, config
, 0,
179 "Bad request for proxiable ticket");
180 return KRB5KDC_ERR_BADOPTION
;
182 et
->flags
.proxiable
= 1;
185 if(!tgt
->flags
.proxiable
){
186 kdc_log(context
, config
, 0,
187 "Request to proxy non-proxiable ticket");
188 return KRB5KDC_ERR_BADOPTION
;
191 et
->caddr
= b
->addresses
;
196 if(f
.allow_postdate
){
197 if(!tgt
->flags
.may_postdate
){
198 kdc_log(context
, config
, 0,
199 "Bad request for post-datable ticket");
200 return KRB5KDC_ERR_BADOPTION
;
202 et
->flags
.may_postdate
= 1;
205 if(!tgt
->flags
.may_postdate
){
206 kdc_log(context
, config
, 0,
207 "Bad request for postdated ticket");
208 return KRB5KDC_ERR_BADOPTION
;
211 *et
->starttime
= *b
->from
;
212 et
->flags
.postdated
= 1;
213 et
->flags
.invalid
= 1;
214 }else if(b
->from
&& *b
->from
> kdc_time
+ context
->max_skew
){
215 kdc_log(context
, config
, 0, "Ticket cannot be postdated");
216 return KRB5KDC_ERR_CANNOT_POSTDATE
;
220 if(!tgt
->flags
.renewable
|| tgt
->renew_till
== NULL
){
221 kdc_log(context
, config
, 0,
222 "Bad request for renewable ticket");
223 return KRB5KDC_ERR_BADOPTION
;
225 et
->flags
.renewable
= 1;
226 ALLOC(et
->renew_till
);
227 _kdc_fix_time(&b
->rtime
);
228 *et
->renew_till
= *b
->rtime
;
232 if(!tgt
->flags
.renewable
|| tgt
->renew_till
== NULL
){
233 kdc_log(context
, config
, 0,
234 "Request to renew non-renewable ticket");
235 return KRB5KDC_ERR_BADOPTION
;
237 old_life
= tgt
->endtime
;
239 old_life
-= *tgt
->starttime
;
241 old_life
-= tgt
->authtime
;
242 et
->endtime
= *et
->starttime
+ old_life
;
243 if (et
->renew_till
!= NULL
)
244 et
->endtime
= min(*et
->renew_till
, et
->endtime
);
248 /* checks for excess flags */
249 if(f
.request_anonymous
&& !config
->allow_anonymous
){
250 kdc_log(context
, config
, 0,
251 "Request for anonymous ticket");
252 return KRB5KDC_ERR_BADOPTION
;
259 * Determine if constrained delegation is allowed from this client to this server
262 static krb5_error_code
263 check_constrained_delegation(krb5_context context
,
264 krb5_kdc_configuration
*config
,
266 hdb_entry_ex
*client
,
267 hdb_entry_ex
*server
,
268 krb5_const_principal target
)
270 const HDB_Ext_Constrained_delegation_acl
*acl
;
275 * constrained_delegation (S4U2Proxy) only works within
276 * the same realm. We use the already canonicalized version
277 * of the principals here, while "target" is the principal
278 * provided by the client.
280 if(!krb5_realm_compare(context
, client
->entry
.principal
, server
->entry
.principal
)) {
281 ret
= KRB5KDC_ERR_BADOPTION
;
282 kdc_log(context
, config
, 0,
283 "Bad request for constrained delegation");
287 if (clientdb
->hdb_check_constrained_delegation
) {
288 ret
= clientdb
->hdb_check_constrained_delegation(context
, clientdb
, client
, target
);
292 /* if client delegates to itself, that ok */
293 if (krb5_principal_compare(context
, client
->entry
.principal
, server
->entry
.principal
) == TRUE
)
296 ret
= hdb_entry_get_ConstrainedDelegACL(&client
->entry
, &acl
);
298 krb5_clear_error_message(context
);
303 for (i
= 0; i
< acl
->len
; i
++) {
304 if (krb5_principal_compare(context
, target
, &acl
->val
[i
]) == TRUE
)
308 ret
= KRB5KDC_ERR_BADOPTION
;
310 kdc_log(context
, config
, 0,
311 "Bad request for constrained delegation");
316 * Determine if s4u2self is allowed from this client to this server
318 * For example, regardless of the principal being impersonated, if the
319 * 'client' and 'server' (target) are the same, then it's safe.
322 static krb5_error_code
323 check_s4u2self(krb5_context context
,
324 krb5_kdc_configuration
*config
,
326 hdb_entry_ex
*client
,
327 hdb_entry_ex
*target_server
,
328 krb5_const_principal target_server_principal
)
333 * Always allow the plugin to check, this might be faster, allow a
334 * policy or audit check and can look into the DB records
337 if (clientdb
->hdb_check_s4u2self
) {
338 ret
= clientdb
->hdb_check_s4u2self(context
,
344 } else if (krb5_principal_compare(context
,
345 client
->entry
.principal
,
346 target_server_principal
) == TRUE
) {
347 /* if client does a s4u2self to itself, and there is no plugin, that is ok */
350 ret
= KRB5KDC_ERR_BADOPTION
;
359 static krb5_error_code
360 verify_flags (krb5_context context
,
361 krb5_kdc_configuration
*config
,
362 const EncTicketPart
*et
,
365 if(et
->endtime
< kdc_time
){
366 kdc_log(context
, config
, 0, "Ticket expired (%s)", pstr
);
367 return KRB5KRB_AP_ERR_TKT_EXPIRED
;
369 if(et
->flags
.invalid
){
370 kdc_log(context
, config
, 0, "Ticket not valid (%s)", pstr
);
371 return KRB5KRB_AP_ERR_TKT_NYV
;
380 static krb5_error_code
381 fix_transited_encoding(krb5_context context
,
382 krb5_kdc_configuration
*config
,
383 krb5_boolean check_policy
,
384 const TransitedEncoding
*tr
,
386 const char *client_realm
,
387 const char *server_realm
,
388 const char *tgt_realm
)
390 krb5_error_code ret
= 0;
391 char **realms
, **tmp
;
392 unsigned int num_realms
;
395 switch (tr
->tr_type
) {
396 case DOMAIN_X500_COMPRESS
:
400 * Allow empty content of type 0 because that is was Microsoft
401 * generates in their TGT.
403 if (tr
->contents
.length
== 0)
405 kdc_log(context
, config
, 0,
406 "Transited type 0 with non empty content");
407 return KRB5KDC_ERR_TRTYPE_NOSUPP
;
409 kdc_log(context
, config
, 0,
410 "Unknown transited type: %u", tr
->tr_type
);
411 return KRB5KDC_ERR_TRTYPE_NOSUPP
;
414 ret
= krb5_domain_x500_decode(context
,
421 krb5_warn(context
, ret
,
422 "Decoding transited encoding");
427 * If the realm of the presented tgt is neither the client nor the server
428 * realm, it is a transit realm and must be added to transited set.
430 if(strcmp(client_realm
, tgt_realm
) && strcmp(server_realm
, tgt_realm
)) {
431 if (num_realms
+ 1 > UINT_MAX
/sizeof(*realms
)) {
435 tmp
= realloc(realms
, (num_realms
+ 1) * sizeof(*realms
));
441 realms
[num_realms
] = strdup(tgt_realm
);
442 if(realms
[num_realms
] == NULL
){
448 if(num_realms
== 0) {
449 if(strcmp(client_realm
, server_realm
))
450 kdc_log(context
, config
, 0,
451 "cross-realm %s -> %s", client_realm
, server_realm
);
455 for(i
= 0; i
< num_realms
; i
++)
456 l
+= strlen(realms
[i
]) + 2;
460 for(i
= 0; i
< num_realms
; i
++) {
462 strlcat(rs
, ", ", l
);
463 strlcat(rs
, realms
[i
], l
);
465 kdc_log(context
, config
, 0,
466 "cross-realm %s -> %s via [%s]",
467 client_realm
, server_realm
, rs
);
472 ret
= krb5_check_transited(context
, client_realm
,
474 realms
, num_realms
, NULL
);
476 krb5_warn(context
, ret
, "cross-realm %s -> %s",
477 client_realm
, server_realm
);
480 et
->flags
.transited_policy_checked
= 1;
482 et
->transited
.tr_type
= DOMAIN_X500_COMPRESS
;
483 ret
= krb5_domain_x500_encode(realms
, num_realms
, &et
->transited
.contents
);
485 krb5_warn(context
, ret
, "Encoding transited encoding");
487 for(i
= 0; i
< num_realms
; i
++)
494 static krb5_error_code
495 tgs_make_reply(krb5_context context
,
496 krb5_kdc_configuration
*config
,
498 krb5_principal tgt_name
,
499 const EncTicketPart
*tgt
,
500 const krb5_keyblock
*replykey
,
502 const EncryptionKey
*serverkey
,
503 const EncryptionKey
*krbtgtkey
,
504 const krb5_keyblock
*sessionkey
,
506 AuthorizationData
*auth_data
,
507 hdb_entry_ex
*server
,
508 krb5_principal server_principal
,
509 const char *server_name
,
510 hdb_entry_ex
*client
,
511 krb5_principal client_principal
,
512 const char *tgt_realm
,
513 hdb_entry_ex
*krbtgt
,
516 krb5_boolean add_ticket_sig
,
517 const METHOD_DATA
*enc_pa_data
,
524 KDCOptions f
= b
->kdc_options
;
528 memset(&rep
, 0, sizeof(rep
));
529 memset(&et
, 0, sizeof(et
));
530 memset(&ek
, 0, sizeof(ek
));
533 rep
.msg_type
= krb_tgs_rep
;
535 et
.authtime
= tgt
->authtime
;
536 _kdc_fix_time(&b
->till
);
537 et
.endtime
= min(tgt
->endtime
, *b
->till
);
539 *et
.starttime
= kdc_time
;
541 ret
= check_tgs_flags(context
, config
, b
, tgt
, &et
);
545 /* We should check the transited encoding if:
546 1) the request doesn't ask not to be checked
547 2) globally enforcing a check
548 3) principal requires checking
549 4) we allow non-check per-principal, but principal isn't marked as allowing this
550 5) we don't globally allow this
553 #define GLOBAL_FORCE_TRANSITED_CHECK \
554 (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
555 #define GLOBAL_ALLOW_PER_PRINCIPAL \
556 (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
557 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
558 (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
560 /* these will consult the database in future release */
561 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
562 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
564 ret
= fix_transited_encoding(context
, config
,
565 !f
.disable_transited_check
||
566 GLOBAL_FORCE_TRANSITED_CHECK
||
567 PRINCIPAL_FORCE_TRANSITED_CHECK(server
) ||
568 !((GLOBAL_ALLOW_PER_PRINCIPAL
&&
569 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server
)) ||
570 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK
),
571 &tgt
->transited
, &et
,
572 krb5_principal_get_realm(context
, client_principal
),
573 krb5_principal_get_realm(context
, server
->entry
.principal
),
578 copy_Realm(&server_principal
->realm
, &rep
.ticket
.realm
);
579 _krb5_principal2principalname(&rep
.ticket
.sname
, server_principal
);
580 copy_Realm(&tgt_name
->realm
, &rep
.crealm
);
582 if (f.request_anonymous)
583 _kdc_make_anonymous_principalname (&rep.cname);
586 copy_PrincipalName(&tgt_name
->name
, &rep
.cname
);
587 rep
.ticket
.tkt_vno
= 5;
591 et
.caddr
= tgt
->caddr
;
595 life
= et
.endtime
- *et
.starttime
;
596 if(client
&& client
->entry
.max_life
)
597 life
= min(life
, *client
->entry
.max_life
);
598 if(server
->entry
.max_life
)
599 life
= min(life
, *server
->entry
.max_life
);
600 et
.endtime
= *et
.starttime
+ life
;
602 if(f
.renewable_ok
&& tgt
->flags
.renewable
&&
603 et
.renew_till
== NULL
&& et
.endtime
< *b
->till
&&
604 tgt
->renew_till
!= NULL
)
606 et
.flags
.renewable
= 1;
607 ALLOC(et
.renew_till
);
608 *et
.renew_till
= *b
->till
;
612 renew
= *et
.renew_till
- et
.authtime
;
613 if(client
&& client
->entry
.max_renew
)
614 renew
= min(renew
, *client
->entry
.max_renew
);
615 if(server
->entry
.max_renew
)
616 renew
= min(renew
, *server
->entry
.max_renew
);
617 *et
.renew_till
= et
.authtime
+ renew
;
621 *et
.renew_till
= min(*et
.renew_till
, *tgt
->renew_till
);
622 *et
.starttime
= min(*et
.starttime
, *et
.renew_till
);
623 et
.endtime
= min(et
.endtime
, *et
.renew_till
);
626 *et
.starttime
= min(*et
.starttime
, et
.endtime
);
628 if(*et
.starttime
== et
.endtime
){
629 ret
= KRB5KDC_ERR_NEVER_VALID
;
632 if(et
.renew_till
&& et
.endtime
== *et
.renew_till
){
634 et
.renew_till
= NULL
;
635 et
.flags
.renewable
= 0;
638 et
.flags
.pre_authent
= tgt
->flags
.pre_authent
;
639 et
.flags
.hw_authent
= tgt
->flags
.hw_authent
;
640 et
.flags
.anonymous
= tgt
->flags
.anonymous
;
641 et
.flags
.ok_as_delegate
= server
->entry
.flags
.ok_as_delegate
;
643 /* See MS-KILE 3.3.5.1 */
644 if (!server
->entry
.flags
.forwardable
)
645 et
.flags
.forwardable
= 0;
646 if (!server
->entry
.flags
.proxiable
)
647 et
.flags
.proxiable
= 0;
652 /* XXX check authdata */
654 if (et
.authorization_data
== NULL
) {
655 et
.authorization_data
= calloc(1, sizeof(*et
.authorization_data
));
656 if (et
.authorization_data
== NULL
) {
658 krb5_set_error_message(context
, ret
, "malloc: out of memory");
662 for(i
= 0; i
< auth_data
->len
; i
++) {
663 ret
= add_AuthorizationData(et
.authorization_data
, &auth_data
->val
[i
]);
665 krb5_set_error_message(context
, ret
, "malloc: out of memory");
671 ret
= krb5_copy_keyblock_contents(context
, sessionkey
, &et
.key
);
674 et
.crealm
= tgt_name
->realm
;
675 et
.cname
= tgt_name
->name
;
678 /* MIT must have at least one last_req */
680 ek
.last_req
.val
= calloc(1, sizeof(*ek
.last_req
.val
));
681 if (ek
.last_req
.val
== NULL
) {
687 ek
.authtime
= et
.authtime
;
688 ek
.starttime
= et
.starttime
;
689 ek
.endtime
= et
.endtime
;
690 ek
.renew_till
= et
.renew_till
;
691 ek
.srealm
= rep
.ticket
.realm
;
692 ek
.sname
= rep
.ticket
.sname
;
694 _kdc_log_timestamp(context
, config
, "TGS-REQ", et
.authtime
, et
.starttime
,
695 et
.endtime
, et
.renew_till
);
697 if (enc_pa_data
->len
) {
698 rep
.padata
= calloc(1, sizeof(*rep
.padata
));
699 if (rep
.padata
== NULL
) {
703 ret
= copy_METHOD_DATA(enc_pa_data
, rep
.padata
);
708 if (krb5_enctype_valid(context
, et
.key
.keytype
) != 0
709 && _kdc_is_weak_exception(server
->entry
.principal
, et
.key
.keytype
))
711 krb5_enctype_enable(context
, et
.key
.keytype
);
715 /* The PAC should be the last change to the ticket. */
717 ret
= _krb5_kdc_pac_sign_ticket(context
, mspac
, tgt_name
, serverkey
,
718 krbtgtkey
, rodc_id
, add_ticket_sig
, &et
);
723 /* It is somewhat unclear where the etype in the following
724 encryption should come from. What we have is a session
725 key in the passed tgt, and a list of preferred etypes
726 *for the new ticket*. Should we pick the best possible
727 etype, given the keytype in the tgt, or should we look
728 at the etype list here as well? What if the tgt
729 session key is DES3 and we want a ticket with a (say)
730 CAST session key. Should the DES3 etype be added to the
731 etype list, even if we don't want a session key with
733 ret
= _kdc_encode_reply(context
, config
,
734 &rep
, &et
, &ek
, et
.key
.keytype
,
736 serverkey
, 0, replykey
, rk_is_subkey
,
739 krb5_enctype_disable(context
, et
.key
.keytype
);
743 free_TransitedEncoding(&et
.transited
);
748 if(et
.authorization_data
) {
749 free_AuthorizationData(et
.authorization_data
);
750 free(et
.authorization_data
);
752 free_LastReq(&ek
.last_req
);
753 memset(et
.key
.keyvalue
.data
, 0, et
.key
.keyvalue
.length
);
754 free_EncryptionKey(&et
.key
);
758 static krb5_error_code
759 tgs_check_authenticator(krb5_context context
,
760 krb5_kdc_configuration
*config
,
761 krb5_auth_context ac
,
766 krb5_authenticator auth
;
773 krb5_auth_con_getauthenticator(context
, ac
, &auth
);
774 if(auth
->cksum
== NULL
){
775 kdc_log(context
, config
, 0, "No authenticator in request");
776 ret
= KRB5KRB_AP_ERR_INAPP_CKSUM
;
780 * according to RFC1510 it doesn't need to be keyed,
781 * but according to the latest draft it needs to.
785 !krb5_checksum_is_keyed(context
, auth
->cksum
->cksumtype
)
788 !krb5_checksum_is_collision_proof(context
, auth
->cksum
->cksumtype
)) {
789 kdc_log(context
, config
, 0, "Bad checksum type in authenticator: %d",
790 auth
->cksum
->cksumtype
);
791 ret
= KRB5KRB_AP_ERR_INAPP_CKSUM
;
795 /* XXX should not re-encode this */
796 ASN1_MALLOC_ENCODE(KDC_REQ_BODY
, buf
, buf_size
, b
, &len
, ret
);
798 const char *msg
= krb5_get_error_message(context
, ret
);
799 kdc_log(context
, config
, 0, "Failed to encode KDC-REQ-BODY: %s", msg
);
800 krb5_free_error_message(context
, msg
);
803 if(buf_size
!= len
) {
805 kdc_log(context
, config
, 0, "Internal error in ASN.1 encoder");
806 *e_text
= "KDC internal error";
807 ret
= KRB5KRB_ERR_GENERIC
;
810 ret
= krb5_crypto_init(context
, key
, 0, &crypto
);
812 const char *msg
= krb5_get_error_message(context
, ret
);
814 kdc_log(context
, config
, 0, "krb5_crypto_init failed: %s", msg
);
815 krb5_free_error_message(context
, msg
);
818 ret
= krb5_verify_checksum(context
,
820 KRB5_KU_TGS_REQ_AUTH_CKSUM
,
825 krb5_crypto_destroy(context
, crypto
);
827 const char *msg
= krb5_get_error_message(context
, ret
);
828 kdc_log(context
, config
, 0,
829 "Failed to verify authenticator checksum: %s", msg
);
830 krb5_free_error_message(context
, msg
);
833 free_Authenticator(auth
);
843 find_rpath(krb5_context context
, Realm crealm
, Realm srealm
)
845 const char *new_realm
= krb5_config_get_string(context
,
856 need_referral(krb5_context context
, krb5_kdc_configuration
*config
,
857 const KDCOptions
* const options
, krb5_principal server
,
862 if(!options
->canonicalize
&& server
->name
.name_type
!= KRB5_NT_SRV_INST
)
865 if (server
->name
.name_string
.len
== 1)
866 name
= server
->name
.name_string
.val
[0];
867 else if (server
->name
.name_string
.len
== 3) {
869 This is used to give referrals for the
870 E3514235-4B06-11D1-AB04-00C04FC2DCD2/NTDSGUID/DNSDOMAIN
871 SPN form, which is used for inter-domain communication in AD
873 name
= server
->name
.name_string
.val
[2];
874 kdc_log(context
, config
, 0, "Giving 3 part referral for %s", name
);
875 *realms
= malloc(sizeof(char *)*2);
876 if (*realms
== NULL
) {
877 krb5_set_error_message(context
, ENOMEM
, N_("malloc: out of memory", ""));
880 (*realms
)[0] = strdup(name
);
883 } else if (server
->name
.name_string
.len
> 1)
884 name
= server
->name
.name_string
.val
[1];
888 kdc_log(context
, config
, 0, "Searching referral for %s", name
);
890 return _krb5_get_host_realm_int(context
, name
, FALSE
, realms
) == 0;
893 static krb5_error_code
894 tgs_parse_request(krb5_context context
,
895 krb5_kdc_configuration
*config
,
897 const PA_DATA
*tgs_req
,
898 hdb_entry_ex
**krbtgt
,
899 krb5_enctype
*krbtgt_etype
,
900 krb5_ticket
**ticket
,
903 const struct sockaddr
*from_addr
,
906 AuthorizationData
**auth_data
,
907 krb5_keyblock
**replykey
,
911 static char failed
[] = "<unparse_name failed>";
914 krb5_principal princ
;
915 krb5_auth_context ac
= NULL
;
916 krb5_flags ap_req_options
;
917 krb5_flags verify_ap_req_flags
;
920 krb5_keyblock
*subkey
= NULL
;
923 krb5uint32
*kvno_ptr
= NULL
;
930 memset(&ap_req
, 0, sizeof(ap_req
));
931 ret
= krb5_decode_ap_req(context
, &tgs_req
->padata_value
, &ap_req
);
933 const char *msg
= krb5_get_error_message(context
, ret
);
934 kdc_log(context
, config
, 0, "Failed to decode AP-REQ: %s", msg
);
935 krb5_free_error_message(context
, msg
);
939 if(!get_krbtgt_realm(&ap_req
.ticket
.sname
)){
940 /* XXX check for ticket.sname == req.sname */
941 kdc_log(context
, config
, 0, "PA-DATA is not a ticket-granting ticket");
942 ret
= KRB5KDC_ERR_POLICY
; /* ? */
946 _krb5_principalname2krb5_principal(context
,
949 ap_req
.ticket
.realm
);
951 if (ap_req
.ticket
.enc_part
.kvno
) {
952 kvno
= *ap_req
.ticket
.enc_part
.kvno
;
955 ret
= _kdc_db_fetch(context
, config
, princ
, HDB_F_GET_KRBTGT
, kvno_ptr
,
958 if(ret
== HDB_ERR_NOT_FOUND_HERE
) {
960 ret
= krb5_unparse_name(context
, princ
, &p
);
963 krb5_free_principal(context
, princ
);
964 kdc_log(context
, config
, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p
);
967 ret
= HDB_ERR_NOT_FOUND_HERE
;
970 const char *msg
= krb5_get_error_message(context
, ret
);
972 ret
= krb5_unparse_name(context
, princ
, &p
);
975 krb5_free_principal(context
, princ
);
976 kdc_log(context
, config
, 0,
977 "Ticket-granting ticket not found in database: %s", msg
);
978 krb5_free_error_message(context
, msg
);
981 ret
= KRB5KRB_AP_ERR_NOT_US
;
985 if(ap_req
.ticket
.enc_part
.kvno
&&
986 *ap_req
.ticket
.enc_part
.kvno
!= (*krbtgt
)->entry
.kvno
){
989 ret
= krb5_unparse_name (context
, princ
, &p
);
990 krb5_free_principal(context
, princ
);
993 kdc_log(context
, config
, 0,
994 "Ticket kvno = %d, DB kvno = %d (%s)",
995 *ap_req
.ticket
.enc_part
.kvno
,
996 (*krbtgt
)->entry
.kvno
,
1000 ret
= KRB5KRB_AP_ERR_BADKEYVER
;
1004 *krbtgt_etype
= ap_req
.ticket
.enc_part
.etype
;
1006 ret
= hdb_enctype2key(context
, &(*krbtgt
)->entry
,
1007 ap_req
.ticket
.enc_part
.etype
, &tkey
);
1009 char *str
= NULL
, *p
= NULL
;
1011 krb5_enctype_to_string(context
, ap_req
.ticket
.enc_part
.etype
, &str
);
1012 krb5_unparse_name(context
, princ
, &p
);
1013 kdc_log(context
, config
, 0,
1014 "No server key with enctype %s found for %s",
1015 str
? str
: "<unknown enctype>",
1016 p
? p
: "<unparse_name failed>");
1019 ret
= KRB5KRB_AP_ERR_BADKEYVER
;
1023 if (b
->kdc_options
.validate
)
1024 verify_ap_req_flags
= KRB5_VERIFY_AP_REQ_IGNORE_INVALID
;
1026 verify_ap_req_flags
= 0;
1028 ret
= krb5_verify_ap_req2(context
,
1033 verify_ap_req_flags
,
1036 KRB5_KU_TGS_REQ_AUTH
);
1038 krb5_free_principal(context
, princ
);
1040 const char *msg
= krb5_get_error_message(context
, ret
);
1041 kdc_log(context
, config
, 0, "Failed to verify AP-REQ: %s", msg
);
1042 krb5_free_error_message(context
, msg
);
1049 krb5_authenticator auth
;
1051 ret
= krb5_auth_con_getauthenticator(context
, ac
, &auth
);
1053 *csec
= malloc(sizeof(**csec
));
1054 if (*csec
== NULL
) {
1055 krb5_free_authenticator(context
, &auth
);
1056 kdc_log(context
, config
, 0, "malloc failed");
1059 **csec
= auth
->ctime
;
1060 *cusec
= malloc(sizeof(**cusec
));
1061 if (*cusec
== NULL
) {
1062 krb5_free_authenticator(context
, &auth
);
1063 kdc_log(context
, config
, 0, "malloc failed");
1066 **cusec
= auth
->cusec
;
1067 krb5_free_authenticator(context
, &auth
);
1071 ret
= tgs_check_authenticator(context
, config
,
1072 ac
, b
, e_text
, &(*ticket
)->ticket
.key
);
1074 krb5_auth_con_free(context
, ac
);
1078 usage
= KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY
;
1081 ret
= krb5_auth_con_getremotesubkey(context
, ac
, &subkey
);
1083 const char *msg
= krb5_get_error_message(context
, ret
);
1084 krb5_auth_con_free(context
, ac
);
1085 kdc_log(context
, config
, 0, "Failed to get remote subkey: %s", msg
);
1086 krb5_free_error_message(context
, msg
);
1090 usage
= KRB5_KU_TGS_REQ_AUTH_DAT_SESSION
;
1093 ret
= krb5_auth_con_getkey(context
, ac
, &subkey
);
1095 const char *msg
= krb5_get_error_message(context
, ret
);
1096 krb5_auth_con_free(context
, ac
);
1097 kdc_log(context
, config
, 0, "Failed to get session key: %s", msg
);
1098 krb5_free_error_message(context
, msg
);
1103 krb5_auth_con_free(context
, ac
);
1104 kdc_log(context
, config
, 0,
1105 "Failed to get key for enc-authorization-data");
1106 ret
= KRB5KRB_AP_ERR_BAD_INTEGRITY
; /* ? */
1112 if (b
->enc_authorization_data
) {
1115 ret
= krb5_crypto_init(context
, subkey
, 0, &crypto
);
1117 const char *msg
= krb5_get_error_message(context
, ret
);
1118 krb5_auth_con_free(context
, ac
);
1119 kdc_log(context
, config
, 0, "krb5_crypto_init failed: %s", msg
);
1120 krb5_free_error_message(context
, msg
);
1123 ret
= krb5_decrypt_EncryptedData (context
,
1126 b
->enc_authorization_data
,
1128 krb5_crypto_destroy(context
, crypto
);
1130 krb5_auth_con_free(context
, ac
);
1131 kdc_log(context
, config
, 0,
1132 "Failed to decrypt enc-authorization-data");
1133 ret
= KRB5KRB_AP_ERR_BAD_INTEGRITY
; /* ? */
1137 if (*auth_data
== NULL
) {
1138 krb5_auth_con_free(context
, ac
);
1139 ret
= KRB5KRB_AP_ERR_BAD_INTEGRITY
; /* ? */
1142 ret
= decode_AuthorizationData(ad
.data
, ad
.length
, *auth_data
, NULL
);
1144 krb5_auth_con_free(context
, ac
);
1147 kdc_log(context
, config
, 0, "Failed to decode authorization data");
1148 ret
= KRB5KRB_AP_ERR_BAD_INTEGRITY
; /* ? */
1153 krb5_auth_con_free(context
, ac
);
1156 free_AP_REQ(&ap_req
);
1161 static krb5_error_code
1162 build_server_referral(krb5_context context
,
1163 krb5_kdc_configuration
*config
,
1164 krb5_crypto session
,
1165 krb5_const_realm referred_realm
,
1166 const PrincipalName
*true_principal_name
,
1167 const PrincipalName
*requested_principal
,
1170 PA_ServerReferralData ref
;
1171 krb5_error_code ret
;
1176 memset(&ref
, 0, sizeof(ref
));
1178 if (referred_realm
) {
1179 ALLOC(ref
.referred_realm
);
1180 if (ref
.referred_realm
== NULL
)
1182 *ref
.referred_realm
= strdup(referred_realm
);
1183 if (*ref
.referred_realm
== NULL
)
1186 if (true_principal_name
) {
1187 ALLOC(ref
.true_principal_name
);
1188 if (ref
.true_principal_name
== NULL
)
1190 ret
= copy_PrincipalName(true_principal_name
, ref
.true_principal_name
);
1194 if (requested_principal
) {
1195 ALLOC(ref
.requested_principal_name
);
1196 if (ref
.requested_principal_name
== NULL
)
1198 ret
= copy_PrincipalName(requested_principal
,
1199 ref
.requested_principal_name
);
1204 ASN1_MALLOC_ENCODE(PA_ServerReferralData
,
1205 data
.data
, data
.length
,
1207 free_PA_ServerReferralData(&ref
);
1210 if (data
.length
!= size
)
1211 krb5_abortx(context
, "internal asn.1 encoder error");
1213 ret
= krb5_encrypt_EncryptedData(context
, session
,
1214 KRB5_KU_PA_SERVER_REFERRAL
,
1215 data
.data
, data
.length
,
1221 ASN1_MALLOC_ENCODE(EncryptedData
,
1222 outdata
->data
, outdata
->length
,
1224 free_EncryptedData(&ed
);
1227 if (outdata
->length
!= size
)
1228 krb5_abortx(context
, "internal asn.1 encoder error");
1232 free_PA_ServerReferralData(&ref
);
1233 krb5_set_error_message(context
, ENOMEM
, "malloc: out of memory");
1237 static krb5_error_code
1238 db_fetch_client(krb5_context context
,
1239 krb5_kdc_configuration
*config
,
1243 const char *krbtgt_realm
,
1245 hdb_entry_ex
**client_out
)
1247 krb5_error_code ret
;
1248 hdb_entry_ex
*client
= NULL
;
1252 ret
= _kdc_db_fetch(context
, config
, cp
, HDB_F_GET_CLIENT
| flags
,
1253 NULL
, clientdb
, &client
);
1254 if (ret
== HDB_ERR_NOT_FOUND_HERE
) {
1256 * This is OK, we are just trying to find out if they have
1257 * been disabled or deleted in the meantime; missing secrets
1262 * If the client belongs to the same realm as our TGS, it
1263 * should exist in the local database.
1267 if (strcmp(krb5_principal_get_realm(context
, cp
), krbtgt_realm
) == 0) {
1268 if (ret
== HDB_ERR_NOENTRY
)
1269 ret
= KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN
;
1270 kdc_log(context
, config
, 4, "Client no longer in database: %s", cpn
);
1274 msg
= krb5_get_error_message(context
, ret
);
1275 kdc_log(context
, config
, 4, "Client not found in database: %s", msg
);
1276 krb5_free_error_message(context
, msg
);
1277 } else if (client
->entry
.flags
.invalid
|| !client
->entry
.flags
.client
) {
1278 kdc_log(context
, config
, 4, "Client has invalid bit set");
1279 _kdc_free_ent(context
, client
);
1280 return KRB5KDC_ERR_POLICY
;
1283 *client_out
= client
;
1288 static krb5_error_code
1289 tgs_build_reply(krb5_context context
,
1290 krb5_kdc_configuration
*config
,
1293 hdb_entry_ex
*krbtgt
,
1294 krb5_enctype krbtgt_etype
,
1296 const krb5_keyblock
*replykey
,
1298 krb5_ticket
*ticket
,
1301 const char **e_text
,
1302 AuthorizationData
**auth_data
,
1303 const struct sockaddr
*from_addr
)
1305 krb5_error_code ret
;
1306 krb5_principal cp
= NULL
, sp
= NULL
, tp
= NULL
, dp
= NULL
;
1307 krb5_principal krbtgt_principal
= NULL
;
1308 krb5_principal user2user_princ
= NULL
;
1309 char *spn
= NULL
, *cpn
= NULL
, *tpn
= NULL
, *dpn
= NULL
;
1310 char *user2user_name
= NULL
;
1311 hdb_entry_ex
*server
= NULL
, *client
= NULL
, *s4u2self_impersonated_client
= NULL
;
1312 hdb_entry_ex
*user2user_krbtgt
= NULL
;
1313 HDB
*clientdb
, *s4u2self_impersonated_clientdb
;
1314 HDB
*serverdb
= NULL
;
1315 krb5_realm ref_realm
= NULL
;
1316 EncTicketPart
*tgt
= &ticket
->ticket
;
1317 const char *tgt_realm
= /* Realm of TGT issuer */
1318 krb5_principal_get_realm(context
, krbtgt
->entry
.principal
);
1319 const EncryptionKey
*ekey
;
1320 krb5_keyblock sessionkey
;
1322 krb5_pac mspac
= NULL
;
1323 krb5_pac user2user_pac
= NULL
;
1325 krb5_boolean add_ticket_sig
= FALSE
;
1326 hdb_entry_ex
*krbtgt_out
= NULL
;
1328 METHOD_DATA enc_pa_data
;
1333 EncTicketPart adtkt
;
1335 krb5_boolean kdc_issued
= FALSE
;
1338 int flags
= HDB_F_FOR_TGS_REQ
;
1340 memset(&sessionkey
, 0, sizeof(sessionkey
));
1341 memset(&adtkt
, 0, sizeof(adtkt
));
1342 memset(&enc_pa_data
, 0, sizeof(enc_pa_data
));
1347 if (b
->kdc_options
.canonicalize
)
1348 flags
|= HDB_F_CANON
;
1351 ret
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
1352 krb5_set_error_message(context
, ret
, "No server in request");
1356 _krb5_principalname2krb5_principal(context
, &sp
, *s
, r
);
1357 ret
= krb5_unparse_name(context
, sp
, &spn
);
1360 _krb5_principalname2krb5_principal(context
, &cp
, tgt
->cname
, tgt
->crealm
);
1361 ret
= krb5_unparse_name(context
, cp
, &cpn
);
1364 unparse_flags (KDCOptions2int(b
->kdc_options
),
1365 asn1_KDCOptions_units(),
1366 opt_str
, sizeof(opt_str
));
1368 kdc_log(context
, config
, 0,
1369 "TGS-REQ %s from %s for %s [%s]",
1370 cpn
, from
, spn
, opt_str
);
1372 kdc_log(context
, config
, 0,
1373 "TGS-REQ %s from %s for %s", cpn
, from
, spn
);
1380 ret
= _kdc_db_fetch(context
, config
, sp
, HDB_F_GET_SERVER
| flags
,
1381 NULL
, &serverdb
, &server
);
1383 if(ret
== HDB_ERR_NOT_FOUND_HERE
) {
1384 kdc_log(context
, config
, 5, "target %s does not have secrets at this KDC, need to proxy", sp
);
1386 } else if (ret
== HDB_ERR_WRONG_REALM
) {
1389 ref_realm
= strdup(server
->entry
.principal
->realm
);
1390 if (ref_realm
== NULL
) {
1395 kdc_log(context
, config
, 5,
1396 "Returning a referral to realm %s for "
1399 krb5_free_principal(context
, sp
);
1403 ret
= krb5_make_principal(context
, &sp
, r
, KRB5_TGS_NAME
,
1407 ret
= krb5_unparse_name(context
, sp
, &spn
);
1413 const char *new_rlm
, *msg
;
1417 if (!config
->autodetect_referrals
) {
1419 } else if ((req_rlm
= get_krbtgt_realm(&sp
->name
)) != NULL
) {
1421 new_rlm
= find_rpath(context
, tgt
->crealm
, req_rlm
);
1423 kdc_log(context
, config
, 5, "krbtgt for realm %s "
1424 "not found, trying %s",
1426 krb5_free_principal(context
, sp
);
1428 krb5_make_principal(context
, &sp
, r
,
1429 KRB5_TGS_NAME
, new_rlm
, NULL
);
1430 ret
= krb5_unparse_name(context
, sp
, &spn
);
1436 ref_realm
= strdup(new_rlm
);
1440 } else if(need_referral(context
, config
, &b
->kdc_options
, sp
, &realms
)) {
1441 if (strcmp(realms
[0], sp
->realm
) != 0) {
1442 kdc_log(context
, config
, 5,
1443 "Returning a referral to realm %s for "
1444 "server %s that was not found",
1446 krb5_free_principal(context
, sp
);
1448 krb5_make_principal(context
, &sp
, r
, KRB5_TGS_NAME
,
1450 ret
= krb5_unparse_name(context
, sp
, &spn
);
1456 ref_realm
= strdup(realms
[0]);
1458 krb5_free_host_realm(context
, realms
);
1461 krb5_free_host_realm(context
, realms
);
1463 msg
= krb5_get_error_message(context
, ret
);
1464 kdc_log(context
, config
, 0,
1465 "Server not found in database: %s: %s", spn
, msg
);
1466 krb5_free_error_message(context
, msg
);
1467 if (ret
== HDB_ERR_NOENTRY
)
1468 ret
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
1472 /* Now refetch the primary krbtgt, and get the current kvno (the
1473 * sign check may have been on an old kvno, and the server may
1474 * have been an incoming trust) */
1475 ret
= krb5_make_principal(context
, &krbtgt_principal
,
1476 krb5_principal_get_comp_string(context
,
1477 krbtgt
->entry
.principal
,
1480 krb5_principal_get_comp_string(context
,
1481 krbtgt
->entry
.principal
,
1484 kdc_log(context
, config
, 0,
1485 "Failed to generate krbtgt principal");
1489 ret
= _kdc_db_fetch(context
, config
, krbtgt_principal
, HDB_F_GET_KRBTGT
, NULL
, NULL
, &krbtgt_out
);
1490 krb5_free_principal(context
, krbtgt_principal
);
1492 krb5_error_code ret2
;
1494 ret
= krb5_unparse_name(context
, krbtgt
->entry
.principal
, &ktpn
);
1495 ret2
= krb5_unparse_name(context
, krbtgt_principal
, &ktpn2
);
1496 kdc_log(context
, config
, 0,
1497 "Request with wrong krbtgt: %s, %s not found in our database",
1498 (ret
== 0) ? ktpn
: "<unknown>", (ret2
== 0) ? ktpn2
: "<unknown>");
1503 ret
= KRB5KRB_AP_ERR_NOT_US
;
1508 * Select enctype, return key and kvno.
1514 if(b
->kdc_options
.enc_tkt_in_skey
) {
1518 krb5uint32 second_kvno
= 0;
1519 krb5uint32
*kvno_ptr
= NULL
;
1521 hdb_entry_ex
*user2user_client
= NULL
;
1522 krb5_boolean user2user_kdc_issued
= FALSE
;
1524 if(b
->additional_tickets
== NULL
||
1525 b
->additional_tickets
->len
== 0){
1526 ret
= KRB5KDC_ERR_BADOPTION
; /* ? */
1527 kdc_log(context
, config
, 0,
1528 "No second ticket present in request");
1531 t
= &b
->additional_tickets
->val
[0];
1532 if(!get_krbtgt_realm(&t
->sname
)){
1533 kdc_log(context
, config
, 0,
1534 "Additional ticket is not a ticket-granting ticket");
1535 ret
= KRB5KDC_ERR_POLICY
;
1538 ret
= _krb5_principalname2krb5_principal(context
, &p
, t
->sname
, t
->realm
);
1542 if(t
->enc_part
.kvno
){
1543 second_kvno
= *t
->enc_part
.kvno
;
1544 kvno_ptr
= &second_kvno
;
1546 ret
= _kdc_db_fetch(context
, config
, p
,
1547 HDB_F_GET_KRBTGT
, kvno_ptr
,
1548 NULL
, &user2user_krbtgt
);
1549 krb5_free_principal(context
, p
);
1551 if (ret
== HDB_ERR_NOENTRY
)
1552 ret
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
1555 ret
= hdb_enctype2key(context
, &user2user_krbtgt
->entry
,
1556 t
->enc_part
.etype
, &uukey
);
1558 ret
= KRB5KDC_ERR_ETYPE_NOSUPP
; /* XXX */
1561 ret
= krb5_decrypt_ticket(context
, t
, &uukey
->key
, &adtkt
, 0);
1565 ret
= verify_flags(context
, config
, &adtkt
, spn
);
1569 /* Fetch the name from the TGT. */
1570 ret
= _krb5_principalname2krb5_principal(context
, &user2user_princ
,
1571 adtkt
.cname
, adtkt
.crealm
);
1576 ret
= krb5_unparse_name(context
, user2user_princ
, &user2user_name
);
1581 /* Look up the name given in the TGT in the database. */
1582 ret
= db_fetch_client(context
, config
, flags
, user2user_princ
, user2user_name
,
1583 krb5_principal_get_realm(context
, krbtgt_out
->entry
.principal
),
1584 NULL
, &user2user_client
);
1589 if (user2user_client
!= NULL
) {
1591 * If the account is present in the database, check the account
1594 ret
= kdc_check_flags(context
, config
,
1595 user2user_client
, user2user_name
,
1599 _kdc_free_ent(context
, user2user_client
);
1604 * Also check that the account is the same one specified in the
1607 ret
= check_s4u2self(context
, config
, serverdb
, server
, user2user_client
, user2user_princ
);
1609 _kdc_free_ent(context
, user2user_client
);
1614 /* Verify the PAC of the TGT. */
1615 ret
= check_PAC(context
, config
, user2user_princ
, NULL
,
1616 user2user_client
, user2user_krbtgt
, user2user_krbtgt
, user2user_krbtgt
,
1617 &uukey
->key
, &tkey_check
->key
, &adtkt
, &user2user_kdc_issued
, &user2user_pac
);
1618 _kdc_free_ent(context
, user2user_client
);
1620 const char *msg
= krb5_get_error_message(context
, ret
);
1621 kdc_log(context
, config
, 0,
1622 "Verify PAC failed for %s (%s) from %s with %s",
1623 spn
, user2user_name
, from
, msg
);
1624 krb5_free_error_message(context
, msg
);
1628 if (user2user_pac
== NULL
|| !user2user_kdc_issued
) {
1629 ret
= KRB5KDC_ERR_BADOPTION
;
1630 kdc_log(context
, config
, 0,
1631 "Ticket not signed with PAC; user-to-user failed (%s).",
1632 user2user_pac
? "Ticket unsigned" : "No PAC");
1637 for(i
= 0; i
< b
->etype
.len
; i
++)
1638 if (b
->etype
.val
[i
] == adtkt
.key
.keytype
)
1640 if(i
== b
->etype
.len
) {
1641 kdc_log(context
, config
, 0,
1642 "Addition ticket have not matching etypes");
1643 krb5_clear_error_message(context
);
1644 ret
= KRB5KDC_ERR_ETYPE_NOSUPP
;
1647 etype
= b
->etype
.val
[i
];
1652 ret
= _kdc_find_etype(context
,
1653 config
->tgs_use_strongest_session_key
, FALSE
,
1654 server
, b
->etype
.val
, b
->etype
.len
, NULL
,
1657 kdc_log(context
, config
, 0,
1658 "Server (%s) has no support for etypes", spn
);
1662 etype
= skey
->key
.keytype
;
1663 kvno
= server
->entry
.kvno
;
1666 ret
= krb5_generate_random_keyblock(context
, etype
, &sessionkey
);
1672 * Check that service is in the same realm as the krbtgt. If it's
1673 * not the same, it's someone that is using a uni-directional trust
1677 /* The first realm is the realm of the service, the second is
1678 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1679 * encrypted to. The redirection via the krbtgt_out entry allows
1680 * the DB to possibly correct the case of the realm (Samba4 does
1681 * this) before the strcmp() */
1682 if (strcmp(krb5_principal_get_realm(context
, server
->entry
.principal
),
1683 krb5_principal_get_realm(context
, krbtgt_out
->entry
.principal
)) != 0) {
1685 ret
= krb5_unparse_name(context
, krbtgt_out
->entry
.principal
, &ktpn
);
1686 kdc_log(context
, config
, 0,
1687 "Request with wrong krbtgt: %s",
1688 (ret
== 0) ? ktpn
: "<unknown>");
1691 ret
= KRB5KRB_AP_ERR_NOT_US
;
1694 ret
= hdb_enctype2key(context
, &krbtgt_out
->entry
,
1695 krbtgt_etype
, &tkey_sign
);
1697 kdc_log(context
, config
, 0,
1698 "Failed to find key for krbtgt PAC signature");
1702 ret
= db_fetch_client(context
, config
, flags
, cp
, cpn
,
1703 krb5_principal_get_realm(context
, krbtgt_out
->entry
.principal
),
1704 &clientdb
, &client
);
1708 ret
= check_PAC(context
, config
, cp
, NULL
, client
, server
, krbtgt
, krbtgt
,
1709 &tkey_check
->key
, &tkey_check
->key
, tgt
, &kdc_issued
, &mspac
);
1711 const char *msg
= krb5_get_error_message(context
, ret
);
1712 kdc_log(context
, config
, 0,
1713 "Verify PAC failed for %s (%s) from %s with %s",
1714 spn
, cpn
, from
, msg
);
1715 krb5_free_error_message(context
, msg
);
1723 /* by default the tgt principal matches the client principal */
1728 const PA_DATA
*sdata
;
1731 sdata
= _kdc_find_padata(req
, &i
, KRB5_PADATA_FOR_USER
);
1738 ret
= decode_PA_S4U2Self(sdata
->padata_value
.data
,
1739 sdata
->padata_value
.length
,
1742 kdc_log(context
, config
, 0, "Failed to decode PA-S4U2Self");
1746 if (!krb5_checksum_is_keyed(context
, self
.cksum
.cksumtype
)) {
1747 free_PA_S4U2Self(&self
);
1748 kdc_log(context
, config
, 0, "Reject PA-S4U2Self with unkeyed checksum");
1749 ret
= KRB5KRB_AP_ERR_INAPP_CKSUM
;
1753 ret
= _krb5_s4u2self_to_checksumdata(context
, &self
, &datack
);
1757 ret
= krb5_crypto_init(context
, &tgt
->key
, 0, &crypto
);
1759 const char *msg
= krb5_get_error_message(context
, ret
);
1760 free_PA_S4U2Self(&self
);
1761 krb5_data_free(&datack
);
1762 kdc_log(context
, config
, 0, "krb5_crypto_init failed: %s", msg
);
1763 krb5_free_error_message(context
, msg
);
1767 /* Allow HMAC_MD5 checksum with any key type */
1768 if (self
.cksum
.cksumtype
== CKSUMTYPE_HMAC_MD5
) {
1769 unsigned char csdata
[16];
1772 cs
.checksum
.length
= sizeof(csdata
);
1773 cs
.checksum
.data
= &csdata
;
1775 ret
= _krb5_HMAC_MD5_checksum(context
, &crypto
->key
,
1776 datack
.data
, datack
.length
,
1777 KRB5_KU_OTHER_CKSUM
, &cs
);
1779 krb5_data_ct_cmp(&cs
.checksum
, &self
.cksum
.checksum
) != 0)
1780 ret
= KRB5KRB_AP_ERR_BAD_INTEGRITY
;
1783 ret
= krb5_verify_checksum(context
,
1785 KRB5_KU_OTHER_CKSUM
,
1790 krb5_data_free(&datack
);
1791 krb5_crypto_destroy(context
, crypto
);
1793 const char *msg
= krb5_get_error_message(context
, ret
);
1794 free_PA_S4U2Self(&self
);
1795 kdc_log(context
, config
, 0,
1796 "krb5_verify_checksum failed for S4U2Self: %s", msg
);
1797 krb5_free_error_message(context
, msg
);
1801 ret
= _krb5_principalname2krb5_principal(context
,
1805 free_PA_S4U2Self(&self
);
1809 ret
= krb5_unparse_name(context
, tp
, &tpn
);
1813 ret
= _kdc_db_fetch(context
, config
, tp
, HDB_F_GET_CLIENT
| flags
,
1814 NULL
, &s4u2self_impersonated_clientdb
,
1815 &s4u2self_impersonated_client
);
1820 * If the client belongs to the same realm as our krbtgt, it
1821 * should exist in the local database.
1825 if (ret
== HDB_ERR_NOENTRY
)
1826 ret
= KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN
;
1827 msg
= krb5_get_error_message(context
, ret
);
1828 kdc_log(context
, config
, 1,
1829 "S2U4Self principal to impersonate %s not found in database: %s",
1831 krb5_free_error_message(context
, msg
);
1835 /* Ignore pw_end attributes (as Windows does),
1836 * since S4U2Self is not password authentication. */
1837 free(s4u2self_impersonated_client
->entry
.pw_end
);
1838 s4u2self_impersonated_client
->entry
.pw_end
= NULL
;
1840 ret
= kdc_check_flags(context
, config
, s4u2self_impersonated_client
, tpn
,
1845 /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
1847 krb5_pac_free(context
, mspac
);
1851 ret
= _kdc_pac_generate(context
, s4u2self_impersonated_client
, server
,
1852 NULL
, NULL
, &mspac
);
1854 kdc_log(context
, config
, 0, "PAC generation failed for -- %s",
1860 * Check that service doing the impersonating is
1861 * requesting a ticket to it-self.
1863 ret
= check_s4u2self(context
, config
, clientdb
, client
, server
, sp
);
1865 kdc_log(context
, config
, 0, "S4U2Self: %s is not allowed "
1866 "to impersonate to service "
1867 "(tried for user %s to service %s)",
1873 * If the service isn't trusted for authentication to
1874 * delegation or if the impersonate client is disallowed
1875 * forwardable, remove the forwardable flag.
1878 if (client
->entry
.flags
.trusted_for_delegation
&&
1879 s4u2self_impersonated_client
->entry
.flags
.forwardable
) {
1880 str
= "[forwardable]";
1882 b
->kdc_options
.forwardable
= 0;
1885 kdc_log(context
, config
, 0, "s4u2self %s impersonating %s to "
1886 "service %s %s", cpn
, tpn
, spn
, str
);
1891 * Constrained delegation
1895 && b
->additional_tickets
!= NULL
1896 && b
->additional_tickets
->len
!= 0
1897 && b
->kdc_options
.enc_tkt_in_skey
== 0)
1899 hdb_entry_ex
*adclient
= NULL
;
1900 krb5_boolean ad_kdc_issued
= FALSE
;
1905 * We require that the service's krbtgt has a PAC.
1907 if (mspac
== NULL
) {
1908 ret
= KRB5KDC_ERR_BADOPTION
;
1909 kdc_log(context
, config
, 0,
1910 "Constrained delegation without PAC %s/%s",
1915 krb5_pac_free(context
, mspac
);
1918 t
= &b
->additional_tickets
->val
[0];
1920 ret
= hdb_enctype2key(context
, &client
->entry
,
1921 t
->enc_part
.etype
, &clientkey
);
1923 ret
= KRB5KDC_ERR_ETYPE_NOSUPP
; /* XXX */
1927 ret
= krb5_decrypt_ticket(context
, t
, &clientkey
->key
, &adtkt
, 0);
1929 kdc_log(context
, config
, 0,
1930 "failed to decrypt ticket for "
1931 "constrained delegation from %s to %s ", cpn
, spn
);
1935 ret
= _krb5_principalname2krb5_principal(context
,
1942 ret
= krb5_unparse_name(context
, tp
, &tpn
);
1946 ret
= _krb5_principalname2krb5_principal(context
,
1953 ret
= krb5_unparse_name(context
, dp
, &dpn
);
1957 /* check that ticket is valid */
1958 if (adtkt
.flags
.forwardable
== 0) {
1959 kdc_log(context
, config
, 0,
1960 "Missing forwardable flag on ticket for "
1961 "constrained delegation from %s (%s) as %s to %s ",
1962 cpn
, dpn
, tpn
, spn
);
1963 ret
= KRB5KDC_ERR_BADOPTION
;
1967 ret
= check_constrained_delegation(context
, config
, clientdb
,
1968 client
, server
, sp
);
1970 kdc_log(context
, config
, 0,
1971 "constrained delegation from %s (%s) as %s to %s not allowed",
1972 cpn
, dpn
, tpn
, spn
);
1976 ret
= verify_flags(context
, config
, &adtkt
, tpn
);
1981 /* Try lookup the delegated client in DB */
1982 ret
= db_fetch_client(context
, config
, flags
, tp
, tpn
,
1983 krb5_principal_get_realm(context
, krbtgt_out
->entry
.principal
),
1988 if (adclient
!= NULL
) {
1989 ret
= kdc_check_flags(context
, config
,
1994 _kdc_free_ent(context
, adclient
);
2000 * TODO: pass in t->sname and t->realm and build
2001 * a S4U_DELEGATION_INFO blob to the PAC.
2003 ret
= check_PAC(context
, config
, tp
, dp
, adclient
, server
, krbtgt
, client
,
2004 &clientkey
->key
, &tkey_check
->key
, &adtkt
, &ad_kdc_issued
, &mspac
);
2006 _kdc_free_ent(context
, adclient
);
2008 const char *msg
= krb5_get_error_message(context
, ret
);
2009 kdc_log(context
, config
, 0,
2010 "Verify delegated PAC failed to %s for client"
2011 "%s (%s) as %s from %s with %s",
2012 spn
, cpn
, dpn
, tpn
, from
, msg
);
2013 krb5_free_error_message(context
, msg
);
2017 if (mspac
== NULL
|| !ad_kdc_issued
) {
2018 ret
= KRB5KDC_ERR_BADOPTION
;
2019 kdc_log(context
, config
, 0,
2020 "Ticket not signed with PAC; service %s failed for "
2021 "for delegation to %s for client %s (%s) from %s; (%s).",
2022 spn
, tpn
, dpn
, cpn
, from
, mspac
? "Ticket unsigned" : "No PAC");
2026 kdc_log(context
, config
, 0, "constrained delegation for %s "
2027 "from %s (%s) to %s", tpn
, cpn
, dpn
, spn
);
2034 ret
= kdc_check_flags(context
, config
,
2041 if((b
->kdc_options
.validate
|| b
->kdc_options
.renew
) &&
2042 !krb5_principal_compare(context
,
2043 krbtgt
->entry
.principal
,
2044 server
->entry
.principal
)){
2045 kdc_log(context
, config
, 0, "Inconsistent request.");
2046 ret
= KRB5KDC_ERR_SERVER_NOMATCH
;
2050 /* check for valid set of addresses */
2051 if(!_kdc_check_addresses(context
, config
, tgt
->caddr
, from_addr
)) {
2052 ret
= KRB5KRB_AP_ERR_BADADDR
;
2053 kdc_log(context
, config
, 0, "Request from wrong address");
2058 * If this is an referral, add server referral data to the
2065 kdc_log(context
, config
, 0,
2066 "Adding server referral to %s", ref_realm
);
2068 ret
= krb5_crypto_init(context
, &sessionkey
, 0, &crypto
);
2072 ret
= build_server_referral(context
, config
, crypto
, ref_realm
,
2073 NULL
, s
, &pa
.padata_value
);
2074 krb5_crypto_destroy(context
, crypto
);
2076 kdc_log(context
, config
, 0,
2077 "Failed building server referral");
2080 pa
.padata_type
= KRB5_PADATA_SERVER_REFERRAL
;
2082 ret
= add_METHOD_DATA(&enc_pa_data
, &pa
);
2083 krb5_data_free(&pa
.padata_value
);
2085 kdc_log(context
, config
, 0,
2086 "Add server referral METHOD-DATA failed");
2092 * Only add ticket signature if the requested server is not krbtgt, and
2093 * either the header server is krbtgt or, in the case of renewal/validation
2094 * if it was signed with PAC ticket signature and we verified it.
2095 * Currently Heimdal only allows renewal of krbtgt anyway but that might
2096 * change one day (see issue #763) so make sure to check for it.
2100 !krb5_principal_is_krbtgt(context
, server
->entry
.principal
))
2101 add_ticket_sig
= TRUE
;
2104 * Active-Directory implementations use the high part of the kvno as the
2105 * read-only-dc identifier, we need to embed it in the PAC KDC signatures.
2108 rodc_id
= krbtgt_out
->entry
.kvno
>> 16;
2114 ret
= tgs_make_reply(context
,
2127 server
->entry
.principal
,
2141 free(user2user_name
);
2149 krb5_free_keyblock_contents(context
, &sessionkey
);
2151 _kdc_free_ent(context
, krbtgt_out
);
2153 _kdc_free_ent(context
, server
);
2155 _kdc_free_ent(context
, client
);
2156 if(s4u2self_impersonated_client
)
2157 _kdc_free_ent(context
, s4u2self_impersonated_client
);
2158 if (user2user_krbtgt
)
2159 _kdc_free_ent(context
, user2user_krbtgt
);
2161 if (user2user_princ
)
2162 krb5_free_principal(context
, user2user_princ
);
2164 krb5_free_principal(context
, tp
);
2166 krb5_free_principal(context
, cp
);
2168 krb5_free_principal(context
, dp
);
2170 krb5_free_principal(context
, sp
);
2173 free_METHOD_DATA(&enc_pa_data
);
2175 free_EncTicketPart(&adtkt
);
2177 krb5_pac_free(context
, mspac
);
2178 krb5_pac_free(context
, user2user_pac
);
2188 _kdc_tgs_rep(krb5_context context
,
2189 krb5_kdc_configuration
*config
,
2193 struct sockaddr
*from_addr
,
2196 AuthorizationData
*auth_data
= NULL
;
2197 krb5_error_code ret
;
2199 const PA_DATA
*tgs_req
;
2200 Key
*header_key
= NULL
;
2202 hdb_entry_ex
*krbtgt
= NULL
;
2203 krb5_ticket
*ticket
= NULL
;
2204 const char *e_text
= NULL
;
2205 krb5_enctype krbtgt_etype
= ETYPE_NULL
;
2207 krb5_keyblock
*replykey
= NULL
;
2208 int rk_is_subkey
= 0;
2209 time_t *csec
= NULL
;
2212 if(req
->padata
== NULL
){
2213 ret
= KRB5KDC_ERR_PREAUTH_REQUIRED
; /* XXX ??? */
2214 kdc_log(context
, config
, 0,
2215 "TGS-REQ from %s without PA-DATA", from
);
2219 tgs_req
= _kdc_find_padata(req
, &i
, KRB5_PADATA_TGS_REQ
);
2221 if(tgs_req
== NULL
){
2222 ret
= KRB5KDC_ERR_PADATA_TYPE_NOSUPP
;
2224 kdc_log(context
, config
, 0,
2225 "TGS-REQ from %s without PA-TGS-REQ", from
);
2228 ret
= tgs_parse_request(context
, config
,
2229 &req
->req_body
, tgs_req
,
2240 if (ret
== HDB_ERR_NOT_FOUND_HERE
) {
2241 /* kdc_log() is called in tgs_parse_request() */
2245 kdc_log(context
, config
, 0,
2246 "Failed parsing TGS-REQ from %s", from
);
2250 ret
= tgs_build_reply(context
,
2266 kdc_log(context
, config
, 0,
2267 "Failed building TGS-REP to %s", from
);
2272 if (datagram_reply
&& data
->length
> config
->max_datagram_reply_length
) {
2273 krb5_data_free(data
);
2274 ret
= KRB5KRB_ERR_RESPONSE_TOO_BIG
;
2275 e_text
= "Reply packet too large";
2280 krb5_free_keyblock(context
, replykey
);
2281 if(ret
&& ret
!= HDB_ERR_NOT_FOUND_HERE
&& data
->data
== NULL
){
2282 krb5_mk_error(context
,
2296 krb5_free_ticket(context
, ticket
);
2298 _kdc_free_ent(context
, krbtgt
);
2301 free_AuthorizationData(auth_data
);