HEIMDAL: move code from source4/heimdal* to third_party/heimdal*
[Samba.git] / third_party / heimdal / kdc / krb5tgs.c
blobc9878dd6af52f010c2245314b733269ae167d143
1 /*
2 * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 #include "kdc_locl.h"
37 * return the realm of a krbtgt-ticket or NULL
40 static Realm
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];
46 else
47 return NULL;
51 * return TRUE if client was a synthetic principal, as indicated by
52 * authorization data
54 krb5_boolean
55 _kdc_synthetic_princ_used_p(krb5_context context, krb5_ticket *ticket)
57 krb5_data synthetic_princ_used;
58 krb5_error_code ret;
60 ret = krb5_ticket_get_authorization_data_type(context, ticket,
61 KRB5_AUTHDATA_SYNTHETIC_PRINC_USED,
62 &synthetic_princ_used);
63 if (ret == ENOENT)
64 ret = krb5_ticket_get_authorization_data_type(context, ticket,
65 KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
66 &synthetic_princ_used);
68 if (ret == 0)
69 krb5_data_free(&synthetic_princ_used);
71 return ret == 0;
78 krb5_error_code
79 _kdc_check_pac(krb5_context context,
80 krb5_kdc_configuration *config,
81 const krb5_principal client_principal,
82 const krb5_principal delegated_proxy_principal,
83 hdb_entry_ex *client,
84 hdb_entry_ex *server,
85 hdb_entry_ex *krbtgt,
86 hdb_entry_ex *ticket_server,
87 const EncryptionKey *server_check_key,
88 const EncryptionKey *krbtgt_check_key,
89 EncTicketPart *tkt,
90 krb5_boolean *kdc_issued,
91 krb5_pac *ppac,
92 krb5_principal *pac_canon_name,
93 uint64_t *pac_attributes)
95 krb5_pac pac = NULL;
96 krb5_error_code ret;
97 krb5_boolean signedticket;
99 *kdc_issued = FALSE;
100 *ppac = NULL;
101 if (pac_canon_name)
102 *pac_canon_name = NULL;
103 if (pac_attributes)
104 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
106 ret = _krb5_kdc_pac_ticket_parse(context, tkt, &signedticket, &pac);
107 if (ret)
108 return ret;
110 if (pac == NULL) {
111 if (config->require_pac)
112 ret = KRB5KDC_ERR_TGT_REVOKED;
113 return ret;
116 /* Verify the server signature. */
117 ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal,
118 server_check_key, NULL);
119 if (ret) {
120 krb5_pac_free(context, pac);
121 return ret;
124 if (pac_canon_name) {
125 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
126 if (ret && ret != ENOENT) {
127 krb5_pac_free(context, pac);
128 return ret;
131 if (pac_attributes) {
132 ret = _krb5_pac_get_attributes_info(context, pac, pac_attributes);
133 if (ret && ret != ENOENT) {
134 krb5_pac_free(context, pac);
135 return ret;
137 if (ret == ENOENT)
138 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
141 /* Verify the KDC signatures. */
142 ret = _kdc_pac_verify(context, client_principal, delegated_proxy_principal,
143 client, server, krbtgt, &pac);
144 if (ret == 0) {
145 if (pac == NULL) {
146 /* the plugin may indicate no PAC should be generated */
147 *pac_attributes = 0;
149 } else if (ret == KRB5_PLUGIN_NO_HANDLE) {
151 * We can't verify the KDC signatures if the ticket was issued by
152 * another realm's KDC.
154 if (krb5_realm_compare(context, server->entry.principal,
155 ticket_server->entry.principal)) {
156 ret = krb5_pac_verify(context, pac, 0, NULL, NULL,
157 krbtgt_check_key);
158 if (ret) {
159 krb5_pac_free(context, pac);
160 return ret;
164 /* Discard the PAC if the plugin didn't handle it */
165 krb5_pac_free(context, pac);
166 ret = krb5_pac_init(context, &pac);
167 if (ret)
168 return ret;
169 } else {
170 krb5_pac_free(context, pac);
171 return ret;
174 *kdc_issued = signedticket ||
175 krb5_principal_is_krbtgt(context,
176 ticket_server->entry.principal);
177 *ppac = pac;
179 return 0;
182 static krb5_boolean
183 is_anon_tgs_request_p(const KDC_REQ_BODY *b,
184 const EncTicketPart *tgt)
186 KDCOptions f = b->kdc_options;
189 * Versions of Heimdal from 1.0 to 7.6, inclusive, send both the
190 * request-anonymous and cname-in-addl-tkt flags for constrained
191 * delegation requests. A true anonymous TGS request will only
192 * have the request-anonymous flag set. (A corollary of this is
193 * that it is not possible to support anonymous constrained
194 * delegation requests, although they would be of limited utility.)
196 return tgt->flags.anonymous ||
197 (f.request_anonymous && !f.cname_in_addl_tkt && !b->additional_tickets);
204 static krb5_error_code
205 check_tgs_flags(astgs_request_t r, KDC_REQ_BODY *b,
206 krb5_const_principal tgt_name,
207 const EncTicketPart *tgt, EncTicketPart *et)
209 KDCOptions f = b->kdc_options;
211 if(f.validate){
212 if (!tgt->flags.invalid || tgt->starttime == NULL) {
213 _kdc_audit_addreason((kdc_request_t)r,
214 "Bad request to validate ticket");
215 return KRB5KDC_ERR_BADOPTION;
217 if(*tgt->starttime > kdc_time){
218 _kdc_audit_addreason((kdc_request_t)r,
219 "Early request to validate ticket");
220 return KRB5KRB_AP_ERR_TKT_NYV;
222 /* XXX tkt = tgt */
223 et->flags.invalid = 0;
224 } else if (tgt->flags.invalid) {
225 _kdc_audit_addreason((kdc_request_t)r,
226 "Ticket-granting ticket has INVALID flag set");
227 return KRB5KRB_AP_ERR_TKT_INVALID;
230 if(f.forwardable){
231 if (!tgt->flags.forwardable) {
232 _kdc_audit_addreason((kdc_request_t)r,
233 "Bad request for forwardable ticket");
234 return KRB5KDC_ERR_BADOPTION;
236 et->flags.forwardable = 1;
238 if(f.forwarded){
239 if (!tgt->flags.forwardable) {
240 _kdc_audit_addreason((kdc_request_t)r,
241 "Request to forward non-forwardable ticket");
242 return KRB5KDC_ERR_BADOPTION;
244 et->flags.forwarded = 1;
245 et->caddr = b->addresses;
247 if(tgt->flags.forwarded)
248 et->flags.forwarded = 1;
250 if(f.proxiable){
251 if (!tgt->flags.proxiable) {
252 _kdc_audit_addreason((kdc_request_t)r,
253 "Bad request for proxiable ticket");
254 return KRB5KDC_ERR_BADOPTION;
256 et->flags.proxiable = 1;
258 if(f.proxy){
259 if (!tgt->flags.proxiable) {
260 _kdc_audit_addreason((kdc_request_t)r,
261 "Request to proxy non-proxiable ticket");
262 return KRB5KDC_ERR_BADOPTION;
264 et->flags.proxy = 1;
265 et->caddr = b->addresses;
267 if(tgt->flags.proxy)
268 et->flags.proxy = 1;
270 if(f.allow_postdate){
271 if (!tgt->flags.may_postdate) {
272 _kdc_audit_addreason((kdc_request_t)r,
273 "Bad request for post-datable ticket");
274 return KRB5KDC_ERR_BADOPTION;
276 et->flags.may_postdate = 1;
278 if(f.postdated){
279 if (!tgt->flags.may_postdate) {
280 _kdc_audit_addreason((kdc_request_t)r,
281 "Bad request for postdated ticket");
282 return KRB5KDC_ERR_BADOPTION;
284 if(b->from)
285 *et->starttime = *b->from;
286 et->flags.postdated = 1;
287 et->flags.invalid = 1;
288 } else if (b->from && *b->from > kdc_time + r->context->max_skew) {
289 _kdc_audit_addreason((kdc_request_t)r,
290 "Ticket cannot be postdated");
291 return KRB5KDC_ERR_CANNOT_POSTDATE;
294 if(f.renewable){
295 if (!tgt->flags.renewable || tgt->renew_till == NULL) {
296 _kdc_audit_addreason((kdc_request_t)r,
297 "Bad request for renewable ticket");
298 return KRB5KDC_ERR_BADOPTION;
300 et->flags.renewable = 1;
301 ALLOC(et->renew_till);
302 _kdc_fix_time(&b->rtime);
303 *et->renew_till = *b->rtime;
305 if(f.renew){
306 time_t old_life;
307 if (!tgt->flags.renewable || tgt->renew_till == NULL) {
308 _kdc_audit_addreason((kdc_request_t)r,
309 "Request to renew non-renewable ticket");
310 return KRB5KDC_ERR_BADOPTION;
312 old_life = tgt->endtime;
313 if(tgt->starttime)
314 old_life -= *tgt->starttime;
315 else
316 old_life -= tgt->authtime;
317 et->endtime = *et->starttime + old_life;
318 if (et->renew_till != NULL)
319 et->endtime = min(*et->renew_till, et->endtime);
323 * RFC 8062 section 3 defines an anonymous ticket as one containing
324 * the anonymous principal and the anonymous ticket flag.
326 if (tgt->flags.anonymous &&
327 !_kdc_is_anonymous(r->context, tgt_name)) {
328 _kdc_audit_addreason((kdc_request_t)r,
329 "Anonymous ticket flag set without "
330 "anonymous principal");
331 return KRB5KDC_ERR_BADOPTION;
335 * RFC 8062 section 4.2 states that if the TGT is anonymous, the
336 * anonymous KDC option SHOULD be set, but it is not required.
337 * Treat an anonymous TGT as if the anonymous flag was set.
339 if (is_anon_tgs_request_p(b, tgt))
340 et->flags.anonymous = 1;
342 return 0;
346 * Determine if constrained delegation is allowed from this client to this server
349 static krb5_error_code
350 check_constrained_delegation(krb5_context context,
351 krb5_kdc_configuration *config,
352 HDB *clientdb,
353 hdb_entry_ex *client,
354 hdb_entry_ex *server,
355 krb5_const_principal target)
357 const HDB_Ext_Constrained_delegation_acl *acl;
358 krb5_error_code ret;
359 size_t i;
362 * constrained_delegation (S4U2Proxy) only works within
363 * the same realm. We use the already canonicalized version
364 * of the principals here, while "target" is the principal
365 * provided by the client.
367 if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) {
368 ret = KRB5KDC_ERR_BADOPTION;
369 kdc_log(context, config, 4,
370 "Bad request for constrained delegation");
371 return ret;
374 if (clientdb->hdb_check_constrained_delegation) {
375 ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
376 if (ret == 0)
377 return 0;
378 } else {
379 /* if client delegates to itself, that ok */
380 if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE)
381 return 0;
383 ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
384 if (ret) {
385 krb5_clear_error_message(context);
386 return ret;
389 if (acl) {
390 for (i = 0; i < acl->len; i++) {
391 if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
392 return 0;
395 ret = KRB5KDC_ERR_BADOPTION;
397 kdc_log(context, config, 4,
398 "Bad request for constrained delegation");
399 return ret;
403 * Determine if s4u2self is allowed from this client to this server
405 * also:
407 * Check that the client (user2user TGT, enc-tkt-in-skey) hosts the
408 * service given by the client.
410 * For example, regardless of the principal being impersonated, if the
411 * 'client' and 'server' (target) are the same, or server is an SPN
412 * alias of client, then it's safe.
415 static krb5_error_code
416 check_client_matches_target_service(krb5_context context,
417 krb5_kdc_configuration *config,
418 HDB *clientdb,
419 hdb_entry_ex *client,
420 hdb_entry_ex *target_server,
421 krb5_const_principal target_server_principal)
423 krb5_error_code ret;
426 * Always allow the plugin to check, this might be faster, allow a
427 * policy or audit check and can look into the DB records
428 * directly
430 if (clientdb->hdb_check_client_matches_target_service) {
431 ret = clientdb->hdb_check_client_matches_target_service(context,
432 clientdb,
433 client,
434 target_server);
435 if (ret == 0)
436 return 0;
437 } else if (krb5_principal_compare(context,
438 client->entry.principal,
439 target_server_principal) == TRUE) {
440 /* if client does a s4u2self to itself, and there is no plugin, that is ok */
441 return 0;
442 } else {
443 ret = KRB5KDC_ERR_BADOPTION;
445 return ret;
452 krb5_error_code
453 _kdc_verify_flags(krb5_context context,
454 krb5_kdc_configuration *config,
455 const EncTicketPart *et,
456 const char *pstr)
458 if(et->endtime < kdc_time){
459 kdc_log(context, config, 4, "Ticket expired (%s)", pstr);
460 return KRB5KRB_AP_ERR_TKT_EXPIRED;
462 if(et->flags.invalid){
463 kdc_log(context, config, 4, "Ticket not valid (%s)", pstr);
464 return KRB5KRB_AP_ERR_TKT_NYV;
466 return 0;
473 static krb5_error_code
474 fix_transited_encoding(krb5_context context,
475 krb5_kdc_configuration *config,
476 krb5_boolean check_policy,
477 const TransitedEncoding *tr,
478 EncTicketPart *et,
479 const char *client_realm,
480 const char *server_realm,
481 const char *tgt_realm)
483 krb5_error_code ret = 0;
484 char **realms, **tmp;
485 unsigned int num_realms;
486 size_t i;
488 switch (tr->tr_type) {
489 case domain_X500_Compress:
490 break;
491 case 0:
493 * Allow empty content of type 0 because that is was Microsoft
494 * generates in their TGT.
496 if (tr->contents.length == 0)
497 break;
498 kdc_log(context, config, 4,
499 "Transited type 0 with non empty content");
500 return KRB5KDC_ERR_TRTYPE_NOSUPP;
501 default:
502 kdc_log(context, config, 4,
503 "Unknown transited type: %u", tr->tr_type);
504 return KRB5KDC_ERR_TRTYPE_NOSUPP;
507 ret = krb5_domain_x500_decode(context,
508 tr->contents,
509 &realms,
510 &num_realms,
511 client_realm,
512 server_realm);
513 if(ret){
514 krb5_warn(context, ret,
515 "Decoding transited encoding");
516 return ret;
520 * If the realm of the presented tgt is neither the client nor the server
521 * realm, it is a transit realm and must be added to transited set.
523 if (strcmp(client_realm, tgt_realm) != 0 &&
524 strcmp(server_realm, tgt_realm) != 0) {
525 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
526 ret = ERANGE;
527 goto free_realms;
529 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
530 if(tmp == NULL){
531 ret = ENOMEM;
532 goto free_realms;
534 realms = tmp;
535 realms[num_realms] = strdup(tgt_realm);
536 if(realms[num_realms] == NULL){
537 ret = ENOMEM;
538 goto free_realms;
540 num_realms++;
542 if(num_realms == 0) {
543 if (strcmp(client_realm, server_realm) != 0)
544 kdc_log(context, config, 4,
545 "cross-realm %s -> %s", client_realm, server_realm);
546 } else {
547 size_t l = 0;
548 char *rs;
549 for(i = 0; i < num_realms; i++)
550 l += strlen(realms[i]) + 2;
551 rs = malloc(l);
552 if(rs != NULL) {
553 *rs = '\0';
554 for(i = 0; i < num_realms; i++) {
555 if(i > 0)
556 strlcat(rs, ", ", l);
557 strlcat(rs, realms[i], l);
559 kdc_log(context, config, 4,
560 "cross-realm %s -> %s via [%s]",
561 client_realm, server_realm, rs);
562 free(rs);
565 if(check_policy) {
566 ret = krb5_check_transited(context, client_realm,
567 server_realm,
568 realms, num_realms, NULL);
569 if(ret) {
570 krb5_warn(context, ret, "cross-realm %s -> %s",
571 client_realm, server_realm);
572 goto free_realms;
574 et->flags.transited_policy_checked = 1;
576 et->transited.tr_type = domain_X500_Compress;
577 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
578 if(ret)
579 krb5_warn(context, ret, "Encoding transited encoding");
580 free_realms:
581 for(i = 0; i < num_realms; i++)
582 free(realms[i]);
583 free(realms);
584 return ret;
588 static krb5_error_code
589 tgs_make_reply(astgs_request_t r,
590 krb5_principal tgt_name,
591 const EncTicketPart *tgt,
592 const EncryptionKey *serverkey,
593 const EncryptionKey *krbtgtkey,
594 const krb5_keyblock *sessionkey,
595 krb5_kvno kvno,
596 AuthorizationData *auth_data,
597 hdb_entry_ex *server,
598 krb5_principal server_principal,
599 hdb_entry_ex *client,
600 krb5_principal client_principal,
601 const char *tgt_realm,
602 uint16_t rodc_id,
603 krb5_boolean add_ticket_sig)
605 KDC_REQ_BODY *b = &r->req.req_body;
606 krb5_data *reply = r->reply;
607 KDC_REP *rep = &r->rep;
608 EncTicketPart *et = &r->et;
609 EncKDCRepPart *ek = &r->ek;
610 KDCOptions f = b->kdc_options;
611 krb5_error_code ret;
612 int is_weak = 0;
614 rep->pvno = 5;
615 rep->msg_type = krb_tgs_rep;
617 et->authtime = tgt->authtime;
618 _kdc_fix_time(&b->till);
619 et->endtime = min(tgt->endtime, *b->till);
620 ALLOC(et->starttime);
621 *et->starttime = kdc_time;
623 ret = check_tgs_flags(r, b, tgt_name, tgt, et);
624 if(ret)
625 goto out;
627 /* We should check the transited encoding if:
628 1) the request doesn't ask not to be checked
629 2) globally enforcing a check
630 3) principal requires checking
631 4) we allow non-check per-principal, but principal isn't marked as allowing this
632 5) we don't globally allow this
635 #define GLOBAL_FORCE_TRANSITED_CHECK \
636 (r->config->trpolicy == TRPOLICY_ALWAYS_CHECK)
637 #define GLOBAL_ALLOW_PER_PRINCIPAL \
638 (r->config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
639 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
640 (r->config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
642 /* these will consult the database in future release */
643 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
644 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
646 ret = fix_transited_encoding(r->context, r->config,
647 !f.disable_transited_check ||
648 GLOBAL_FORCE_TRANSITED_CHECK ||
649 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
650 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
651 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
652 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
653 &tgt->transited, et,
654 krb5_principal_get_realm(r->context, client_principal),
655 krb5_principal_get_realm(r->context, server->entry.principal),
656 tgt_realm);
657 if(ret)
658 goto out;
660 ret = copy_Realm(&server_principal->realm, &rep->ticket.realm);
661 if (ret)
662 goto out;
663 _krb5_principal2principalname(&rep->ticket.sname, server_principal);
664 ret = copy_Realm(&tgt_name->realm, &rep->crealm);
665 if (ret)
666 goto out;
669 * RFC 8062 states "if the ticket in the TGS request is an anonymous
670 * one, the client and client realm are copied from that ticket". So
671 * whilst the TGT flag check below is superfluous, it is included in
672 * order to follow the specification to its letter.
674 if (et->flags.anonymous && !tgt->flags.anonymous)
675 _kdc_make_anonymous_principalname(&rep->cname);
676 else
677 ret = copy_PrincipalName(&tgt_name->name, &rep->cname);
678 if (ret)
679 goto out;
680 rep->ticket.tkt_vno = 5;
682 ek->caddr = et->caddr;
685 time_t life;
686 life = et->endtime - *et->starttime;
687 if(client && client->entry.max_life)
688 life = min(life, *client->entry.max_life);
689 if(server->entry.max_life)
690 life = min(life, *server->entry.max_life);
691 et->endtime = *et->starttime + life;
693 if(f.renewable_ok && tgt->flags.renewable &&
694 et->renew_till == NULL && et->endtime < *b->till &&
695 tgt->renew_till != NULL)
697 et->flags.renewable = 1;
698 ALLOC(et->renew_till);
699 *et->renew_till = *b->till;
701 if(et->renew_till){
702 time_t renew;
703 renew = *et->renew_till - *et->starttime;
704 if(client && client->entry.max_renew)
705 renew = min(renew, *client->entry.max_renew);
706 if(server->entry.max_renew)
707 renew = min(renew, *server->entry.max_renew);
708 *et->renew_till = *et->starttime + renew;
711 if(et->renew_till){
712 *et->renew_till = min(*et->renew_till, *tgt->renew_till);
713 *et->starttime = min(*et->starttime, *et->renew_till);
714 et->endtime = min(et->endtime, *et->renew_till);
717 *et->starttime = min(*et->starttime, et->endtime);
719 if(*et->starttime == et->endtime){
720 ret = KRB5KDC_ERR_NEVER_VALID;
721 goto out;
723 if(et->renew_till && et->endtime == *et->renew_till){
724 free(et->renew_till);
725 et->renew_till = NULL;
726 et->flags.renewable = 0;
729 et->flags.pre_authent = tgt->flags.pre_authent;
730 et->flags.hw_authent = tgt->flags.hw_authent;
731 et->flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
733 /* See MS-KILE 3.3.5.1 */
734 if (!server->entry.flags.forwardable)
735 et->flags.forwardable = 0;
736 if (!server->entry.flags.proxiable)
737 et->flags.proxiable = 0;
739 if (auth_data) {
740 unsigned int i = 0;
742 /* XXX check authdata */
744 if (et->authorization_data == NULL) {
745 et->authorization_data = calloc(1, sizeof(*et->authorization_data));
746 if (et->authorization_data == NULL) {
747 ret = ENOMEM;
748 krb5_set_error_message(r->context, ret, "malloc: out of memory");
749 goto out;
752 for(i = 0; i < auth_data->len ; i++) {
753 ret = add_AuthorizationData(et->authorization_data, &auth_data->val[i]);
754 if (ret) {
755 krb5_set_error_message(r->context, ret, "malloc: out of memory");
756 goto out;
761 ret = krb5_copy_keyblock_contents(r->context, sessionkey, &et->key);
762 if (ret)
763 goto out;
764 et->crealm = rep->crealm;
765 et->cname = rep->cname;
767 ek->key = et->key;
768 /* MIT must have at least one last_req */
769 ek->last_req.val = calloc(1, sizeof(*ek->last_req.val));
770 if (ek->last_req.val == NULL) {
771 ret = ENOMEM;
772 goto out;
774 ek->last_req.len = 1; /* set after alloc to avoid null deref on cleanup */
775 ek->nonce = b->nonce;
776 ek->flags = et->flags;
777 ek->authtime = et->authtime;
778 ek->starttime = et->starttime;
779 ek->endtime = et->endtime;
780 ek->renew_till = et->renew_till;
781 ek->srealm = rep->ticket.realm;
782 ek->sname = rep->ticket.sname;
784 _kdc_log_timestamp(r, "TGS-REQ", et->authtime, et->starttime,
785 et->endtime, et->renew_till);
787 if (krb5_enctype_valid(r->context, serverkey->keytype) != 0
788 && _kdc_is_weak_exception(server->entry.principal, serverkey->keytype))
790 krb5_enctype_enable(r->context, serverkey->keytype);
791 is_weak = 1;
794 if (r->client_princ) {
795 char *cpn;
797 krb5_unparse_name(r->context, r->client_princ, &cpn);
798 _kdc_audit_addkv((kdc_request_t)r, 0, "canon_client_name", "%s",
799 cpn ? cpn : "<unknown>");
800 krb5_xfree(cpn);
804 * For anonymous tickets, we should filter out positive authorization data
805 * that could reveal the client's identity, and return a policy error for
806 * restrictive authorization data. Policy for unknown authorization types
807 * is implementation dependent.
809 if (r->pac && !et->flags.anonymous) {
810 _kdc_audit_addkv((kdc_request_t)r, 0, "pac_attributes", "%lx",
811 (long)r->pac_attributes);
814 * PACs are included when issuing TGTs, if there is no PAC_ATTRIBUTES
815 * buffer (legacy behavior) or if the attributes buffer indicates the
816 * AS client requested one.
818 if (_kdc_include_pac_p(r)) {
819 krb5_boolean is_tgs =
820 krb5_principal_is_krbtgt(r->context, server->entry.principal);
822 ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, tgt_name, serverkey,
823 krbtgtkey, rodc_id, NULL, r->client_princ,
824 add_ticket_sig, et,
825 is_tgs ? &r->pac_attributes : NULL);
826 if (ret)
827 goto out;
831 ret = _kdc_finalize_reply(r);
832 if (ret)
833 goto out;
835 /* It is somewhat unclear where the etype in the following
836 encryption should come from. What we have is a session
837 key in the passed tgt, and a list of preferred etypes
838 *for the new ticket*. Should we pick the best possible
839 etype, given the keytype in the tgt, or should we look
840 at the etype list here as well? What if the tgt
841 session key is DES3 and we want a ticket with a (say)
842 CAST session key. Should the DES3 etype be added to the
843 etype list, even if we don't want a session key with
844 DES3? */
845 ret = _kdc_encode_reply(r->context, r->config, r, b->nonce,
846 serverkey->keytype, kvno,
847 serverkey, 0, r->rk_is_subkey, reply);
848 if (is_weak)
849 krb5_enctype_disable(r->context, serverkey->keytype);
851 _log_astgs_req(r, serverkey->keytype);
853 out:
854 return ret;
857 static krb5_error_code
858 tgs_check_authenticator(krb5_context context,
859 krb5_kdc_configuration *config,
860 krb5_auth_context ac,
861 KDC_REQ_BODY *b,
862 krb5_keyblock *key)
864 krb5_authenticator auth;
865 krb5_error_code ret;
866 krb5_crypto crypto;
868 krb5_auth_con_getauthenticator(context, ac, &auth);
869 if(auth->cksum == NULL){
870 kdc_log(context, config, 4, "No authenticator in request");
871 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
872 goto out;
875 if (!krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
876 kdc_log(context, config, 4, "Bad checksum type in authenticator: %d",
877 auth->cksum->cksumtype);
878 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
879 goto out;
882 ret = krb5_crypto_init(context, key, 0, &crypto);
883 if (ret) {
884 const char *msg = krb5_get_error_message(context, ret);
885 kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
886 krb5_free_error_message(context, msg);
887 goto out;
891 * RFC4120 says the checksum must be collision-proof, but it does
892 * not require it to be keyed (as the authenticator is encrypted).
894 _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
895 ret = _kdc_verify_checksum(context,
896 crypto,
897 KRB5_KU_TGS_REQ_AUTH_CKSUM,
898 &b->_save,
899 auth->cksum);
900 krb5_crypto_destroy(context, crypto);
901 if(ret){
902 const char *msg = krb5_get_error_message(context, ret);
903 kdc_log(context, config, 4,
904 "Failed to verify authenticator checksum: %s", msg);
905 krb5_free_error_message(context, msg);
907 out:
908 free_Authenticator(auth);
909 free(auth);
910 return ret;
913 static krb5_boolean
914 need_referral(krb5_context context, krb5_kdc_configuration *config,
915 const KDCOptions * const options, krb5_principal server,
916 krb5_realm **realms)
918 const char *name;
920 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
921 return FALSE;
923 if (server->name.name_string.len == 1)
924 name = server->name.name_string.val[0];
925 else if (server->name.name_string.len == 3) {
927 This is used to give referrals for the
928 E3514235-4B06-11D1-AB04-00C04FC2DCD2/NTDSGUID/DNSDOMAIN
929 SPN form, which is used for inter-domain communication in AD
931 name = server->name.name_string.val[2];
932 kdc_log(context, config, 4, "Giving 3 part referral for %s", name);
933 *realms = malloc(sizeof(char *)*2);
934 if (*realms == NULL) {
935 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
936 return FALSE;
938 (*realms)[0] = strdup(name);
939 (*realms)[1] = NULL;
940 return TRUE;
941 } else if (server->name.name_string.len > 1)
942 name = server->name.name_string.val[1];
943 else
944 return FALSE;
946 kdc_log(context, config, 5, "Searching referral for %s", name);
948 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
951 static krb5_error_code
952 validate_fast_ad(astgs_request_t r, krb5_authdata *auth_data)
954 krb5_error_code ret;
955 krb5_data data;
957 krb5_data_zero(&data);
959 ret = _krb5_get_ad(r->context, auth_data, NULL,
960 KRB5_AUTHDATA_FX_FAST_USED, &data);
961 if (ret == 0) {
962 r->fast_asserted = 1;
963 krb5_data_free(&data);
966 ret = _krb5_get_ad(r->context, auth_data, NULL,
967 KRB5_AUTHDATA_FX_FAST_ARMOR, &data);
968 if (ret == 0) {
969 kdc_log(r->context, r->config, 2,
970 "Invalid ticket usage: TGS-REQ contains AD-fx-fast-armor");
971 krb5_data_free(&data);
972 return KRB5KRB_AP_ERR_BAD_INTEGRITY;
975 return 0;
978 static krb5_error_code
979 tgs_parse_request(astgs_request_t r,
980 const PA_DATA *tgs_req,
981 hdb_entry_ex **krbtgt,
982 krb5_enctype *krbtgt_etype,
983 krb5_ticket **ticket,
984 const char *from,
985 const struct sockaddr *from_addr,
986 time_t **csec,
987 int **cusec,
988 AuthorizationData **auth_data)
990 krb5_kdc_configuration *config = r->config;
991 KDC_REQ_BODY *b = &r->req.req_body;
992 static char failed[] = "<unparse_name failed>";
993 krb5_ap_req ap_req;
994 krb5_error_code ret;
995 krb5_principal princ;
996 krb5_auth_context ac = NULL;
997 krb5_flags ap_req_options;
998 krb5_flags verify_ap_req_flags = 0;
999 krb5_crypto crypto;
1000 krb5uint32 krbtgt_kvno; /* kvno used for the PA-TGS-REQ AP-REQ Ticket */
1001 krb5uint32 krbtgt_kvno_try;
1002 int kvno_search_tries = 4; /* number of kvnos to try when tkt_vno == 0 */
1003 const Keys *krbtgt_keys;/* keyset for TGT tkt_vno */
1004 Key *tkey;
1005 krb5_keyblock *subkey = NULL;
1006 unsigned usage;
1008 *auth_data = NULL;
1009 *csec = NULL;
1010 *cusec = NULL;
1012 memset(&ap_req, 0, sizeof(ap_req));
1013 ret = krb5_decode_ap_req(r->context, &tgs_req->padata_value, &ap_req);
1014 if(ret){
1015 const char *msg = krb5_get_error_message(r->context, ret);
1016 kdc_log(r->context, config, 4, "Failed to decode AP-REQ: %s", msg);
1017 krb5_free_error_message(r->context, msg);
1018 goto out;
1021 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1022 /* XXX check for ticket.sname == req.sname */
1023 kdc_log(r->context, config, 4, "PA-DATA is not a ticket-granting ticket");
1024 ret = KRB5KDC_ERR_POLICY; /* ? */
1025 goto out;
1028 _krb5_principalname2krb5_principal(r->context,
1029 &princ,
1030 ap_req.ticket.sname,
1031 ap_req.ticket.realm);
1033 krbtgt_kvno = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0;
1034 ret = _kdc_db_fetch(r->context, config, princ, HDB_F_GET_KRBTGT,
1035 &krbtgt_kvno, NULL, krbtgt);
1037 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1038 /* XXX Factor out this unparsing of the same princ all over */
1039 char *p;
1040 ret = krb5_unparse_name(r->context, princ, &p);
1041 if (ret != 0)
1042 p = failed;
1043 krb5_free_principal(r->context, princ);
1044 kdc_log(r->context, config, 5,
1045 "Ticket-granting ticket account %s does not have secrets at "
1046 "this KDC, need to proxy", p);
1047 if (ret == 0)
1048 free(p);
1049 ret = HDB_ERR_NOT_FOUND_HERE;
1050 goto out;
1051 } else if (ret == HDB_ERR_KVNO_NOT_FOUND) {
1052 char *p;
1053 ret = krb5_unparse_name(r->context, princ, &p);
1054 if (ret != 0)
1055 p = failed;
1056 krb5_free_principal(r->context, princ);
1057 kdc_log(r->context, config, 5,
1058 "Ticket-granting ticket account %s does not have keys for "
1059 "kvno %d at this KDC", p, krbtgt_kvno);
1060 if (ret == 0)
1061 free(p);
1062 ret = HDB_ERR_KVNO_NOT_FOUND;
1063 goto out;
1064 } else if (ret == HDB_ERR_NO_MKEY) {
1065 char *p;
1066 ret = krb5_unparse_name(r->context, princ, &p);
1067 if (ret != 0)
1068 p = failed;
1069 krb5_free_principal(r->context, princ);
1070 kdc_log(r->context, config, 5,
1071 "Missing master key for decrypting keys for ticket-granting "
1072 "ticket account %s with kvno %d at this KDC", p, krbtgt_kvno);
1073 if (ret == 0)
1074 free(p);
1075 ret = HDB_ERR_KVNO_NOT_FOUND;
1076 goto out;
1077 } else if (ret) {
1078 const char *msg = krb5_get_error_message(r->context, ret);
1079 char *p;
1080 ret = krb5_unparse_name(r->context, princ, &p);
1081 if (ret != 0)
1082 p = failed;
1083 kdc_log(r->context, config, 4,
1084 "Ticket-granting ticket %s not found in database: %s", p, msg);
1085 krb5_free_principal(r->context, princ);
1086 krb5_free_error_message(r->context, msg);
1087 if (ret == 0)
1088 free(p);
1089 ret = KRB5KRB_AP_ERR_NOT_US;
1090 goto out;
1093 krbtgt_kvno_try = krbtgt_kvno ? krbtgt_kvno : (*krbtgt)->entry.kvno;
1094 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1096 next_kvno:
1097 krbtgt_keys = hdb_kvno2keys(r->context, &(*krbtgt)->entry, krbtgt_kvno_try);
1098 ret = hdb_enctype2key(r->context, &(*krbtgt)->entry, krbtgt_keys,
1099 ap_req.ticket.enc_part.etype, &tkey);
1100 if (ret && krbtgt_kvno == 0 && kvno_search_tries > 0) {
1101 kvno_search_tries--;
1102 krbtgt_kvno_try--;
1103 goto next_kvno;
1104 } else if (ret) {
1105 char *str = NULL, *p = NULL;
1107 krb5_enctype_to_string(r->context, ap_req.ticket.enc_part.etype, &str);
1108 krb5_unparse_name(r->context, princ, &p);
1109 kdc_log(r->context, config, 4,
1110 "No server key with enctype %s found for %s",
1111 str ? str : "<unknown enctype>",
1112 p ? p : "<unparse_name failed>");
1113 free(str);
1114 free(p);
1115 ret = KRB5KRB_AP_ERR_BADKEYVER;
1116 goto out;
1119 if (b->kdc_options.validate)
1120 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1122 if (r->config->warn_ticket_addresses)
1123 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_ADDRS;
1125 ret = krb5_verify_ap_req2(r->context,
1126 &ac,
1127 &ap_req,
1128 princ,
1129 &tkey->key,
1130 verify_ap_req_flags,
1131 &ap_req_options,
1132 ticket,
1133 KRB5_KU_TGS_REQ_AUTH);
1134 if (*ticket && (*ticket)->ticket.caddr)
1135 _kdc_audit_addaddrs((kdc_request_t)r, (*ticket)->ticket.caddr, "tixaddrs");
1136 if (r->config->warn_ticket_addresses && ret == KRB5KRB_AP_ERR_BADADDR &&
1137 *ticket != NULL) {
1138 _kdc_audit_addkv((kdc_request_t)r, 0, "wrongaddr", "yes");
1139 ret = 0;
1141 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY && kvno_search_tries > 0) {
1142 kvno_search_tries--;
1143 krbtgt_kvno_try--;
1144 goto next_kvno;
1147 krb5_free_principal(r->context, princ);
1148 if(ret) {
1149 const char *msg = krb5_get_error_message(r->context, ret);
1150 kdc_log(r->context, config, 4, "Failed to verify AP-REQ: %s", msg);
1151 krb5_free_error_message(r->context, msg);
1152 goto out;
1155 r->ticket_key = tkey;
1158 krb5_authenticator auth;
1160 ret = krb5_auth_con_getauthenticator(r->context, ac, &auth);
1161 if (ret == 0) {
1162 *csec = malloc(sizeof(**csec));
1163 if (*csec == NULL) {
1164 krb5_free_authenticator(r->context, &auth);
1165 kdc_log(r->context, config, 4, "malloc failed");
1166 goto out;
1168 **csec = auth->ctime;
1169 *cusec = malloc(sizeof(**cusec));
1170 if (*cusec == NULL) {
1171 krb5_free_authenticator(r->context, &auth);
1172 kdc_log(r->context, config, 4, "malloc failed");
1173 goto out;
1175 **cusec = auth->cusec;
1177 ret = validate_fast_ad(r, auth->authorization_data);
1178 krb5_free_authenticator(r->context, &auth);
1179 if (ret)
1180 goto out;
1184 ret = tgs_check_authenticator(r->context, config, ac, b,
1185 &(*ticket)->ticket.key);
1186 if (ret) {
1187 krb5_auth_con_free(r->context, ac);
1188 goto out;
1191 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1192 r->rk_is_subkey = 1;
1194 ret = krb5_auth_con_getremotesubkey(r->context, ac, &subkey);
1195 if(ret){
1196 const char *msg = krb5_get_error_message(r->context, ret);
1197 krb5_auth_con_free(r->context, ac);
1198 kdc_log(r->context, config, 4, "Failed to get remote subkey: %s", msg);
1199 krb5_free_error_message(r->context, msg);
1200 goto out;
1202 if(subkey == NULL){
1203 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1204 r->rk_is_subkey = 0;
1206 ret = krb5_auth_con_getkey(r->context, ac, &subkey);
1207 if(ret) {
1208 const char *msg = krb5_get_error_message(r->context, ret);
1209 krb5_auth_con_free(r->context, ac);
1210 kdc_log(r->context, config, 4, "Failed to get session key: %s", msg);
1211 krb5_free_error_message(r->context, msg);
1212 goto out;
1215 if(subkey == NULL){
1216 krb5_auth_con_free(r->context, ac);
1217 kdc_log(r->context, config, 4,
1218 "Failed to get key for enc-authorization-data");
1219 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1220 goto out;
1223 krb5_free_keyblock_contents(r->context, &r->reply_key);
1224 ret = krb5_copy_keyblock_contents(r->context, subkey, &r->reply_key);
1225 krb5_free_keyblock(r->context, subkey);
1226 if (ret)
1227 goto out;
1229 if (b->enc_authorization_data) {
1230 krb5_data ad;
1232 ret = krb5_crypto_init(r->context, &r->reply_key, 0, &crypto);
1233 if (ret) {
1234 const char *msg = krb5_get_error_message(r->context, ret);
1235 krb5_auth_con_free(r->context, ac);
1236 kdc_log(r->context, config, 4, "krb5_crypto_init failed: %s", msg);
1237 krb5_free_error_message(r->context, msg);
1238 goto out;
1240 ret = krb5_decrypt_EncryptedData (r->context,
1241 crypto,
1242 usage,
1243 b->enc_authorization_data,
1244 &ad);
1245 krb5_crypto_destroy(r->context, crypto);
1246 if(ret){
1247 krb5_auth_con_free(r->context, ac);
1248 kdc_log(r->context, config, 4,
1249 "Failed to decrypt enc-authorization-data");
1250 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1251 goto out;
1253 ALLOC(*auth_data);
1254 if (*auth_data == NULL) {
1255 krb5_auth_con_free(r->context, ac);
1256 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1257 goto out;
1259 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1260 if(ret){
1261 krb5_auth_con_free(r->context, ac);
1262 free(*auth_data);
1263 *auth_data = NULL;
1264 kdc_log(r->context, config, 4, "Failed to decode authorization data");
1265 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1266 goto out;
1270 ret = validate_fast_ad(r, (*ticket)->ticket.authorization_data);
1271 if (ret)
1272 goto out;
1276 * Check for FAST request
1279 ret = _kdc_fast_unwrap_request(r, *ticket, ac);
1280 if (ret)
1281 goto out;
1283 krb5_auth_con_free(r->context, ac);
1285 out:
1286 free_AP_REQ(&ap_req);
1288 return ret;
1291 static krb5_error_code
1292 build_server_referral(krb5_context context,
1293 krb5_kdc_configuration *config,
1294 krb5_crypto session,
1295 krb5_const_realm referred_realm,
1296 const PrincipalName *true_principal_name,
1297 const PrincipalName *requested_principal,
1298 krb5_data *outdata)
1300 PA_ServerReferralData ref;
1301 krb5_error_code ret;
1302 EncryptedData ed;
1303 krb5_data data;
1304 size_t size = 0;
1306 memset(&ref, 0, sizeof(ref));
1308 if (referred_realm) {
1309 ALLOC(ref.referred_realm);
1310 if (ref.referred_realm == NULL)
1311 goto eout;
1312 *ref.referred_realm = strdup(referred_realm);
1313 if (*ref.referred_realm == NULL)
1314 goto eout;
1316 if (true_principal_name) {
1317 ALLOC(ref.true_principal_name);
1318 if (ref.true_principal_name == NULL)
1319 goto eout;
1320 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1321 if (ret)
1322 goto eout;
1324 if (requested_principal) {
1325 ALLOC(ref.requested_principal_name);
1326 if (ref.requested_principal_name == NULL)
1327 goto eout;
1328 ret = copy_PrincipalName(requested_principal,
1329 ref.requested_principal_name);
1330 if (ret)
1331 goto eout;
1334 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1335 data.data, data.length,
1336 &ref, &size, ret);
1337 free_PA_ServerReferralData(&ref);
1338 if (ret)
1339 return ret;
1340 if (data.length != size)
1341 krb5_abortx(context, "internal asn.1 encoder error");
1343 ret = krb5_encrypt_EncryptedData(context, session,
1344 KRB5_KU_PA_SERVER_REFERRAL,
1345 data.data, data.length,
1346 0 /* kvno */, &ed);
1347 free(data.data);
1348 if (ret)
1349 return ret;
1351 ASN1_MALLOC_ENCODE(EncryptedData,
1352 outdata->data, outdata->length,
1353 &ed, &size, ret);
1354 free_EncryptedData(&ed);
1355 if (ret)
1356 return ret;
1357 if (outdata->length != size)
1358 krb5_abortx(context, "internal asn.1 encoder error");
1360 return 0;
1361 eout:
1362 free_PA_ServerReferralData(&ref);
1363 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1364 return ENOMEM;
1368 * This function is intended to be used when failure to find the client is
1369 * acceptable.
1371 krb5_error_code
1372 _kdc_db_fetch_client(krb5_context context,
1373 krb5_kdc_configuration *config,
1374 int flags,
1375 krb5_principal cp,
1376 const char *cpn,
1377 const char *krbtgt_realm,
1378 HDB **clientdb,
1379 hdb_entry_ex **client_out)
1381 krb5_error_code ret;
1382 hdb_entry_ex *client = NULL;
1384 *client_out = NULL;
1386 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1387 NULL, clientdb, &client);
1388 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1390 * This is OK, we are just trying to find out if they have
1391 * been disabled or deleted in the meantime; missing secrets
1392 * are OK.
1394 } else if (ret) {
1396 * If the client belongs to the same realm as our TGS, it
1397 * should exist in the local database.
1399 const char *msg;
1401 if (strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1402 if (ret == HDB_ERR_NOENTRY)
1403 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1404 kdc_log(context, config, 4, "Client no longer in database: %s", cpn);
1405 return ret;
1408 msg = krb5_get_error_message(context, ret);
1409 kdc_log(context, config, 4, "Client not found in database: %s", msg);
1410 krb5_free_error_message(context, msg);
1411 } else if (client->entry.flags.invalid || !client->entry.flags.client) {
1412 kdc_log(context, config, 4, "Client has invalid bit set");
1413 _kdc_free_ent(context, client);
1414 return KRB5KDC_ERR_POLICY;
1417 *client_out = client;
1419 return 0;
1422 static krb5_error_code
1423 tgs_build_reply(astgs_request_t priv,
1424 hdb_entry_ex *krbtgt,
1425 krb5_enctype krbtgt_etype,
1426 krb5_ticket *ticket,
1427 AuthorizationData **auth_data,
1428 const struct sockaddr *from_addr)
1430 krb5_context context = priv->context;
1431 krb5_kdc_configuration *config = priv->config;
1432 KDC_REQ *req = &priv->req;
1433 KDC_REQ_BODY *b = &priv->req.req_body;
1434 const char *from = priv->from;
1435 krb5_error_code ret, ret2;
1436 krb5_principal cp = NULL, sp = NULL, rsp = NULL, tp = NULL, dp = NULL;
1437 krb5_principal krbtgt_out_principal = NULL;
1438 krb5_principal user2user_princ = NULL;
1439 char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL, *krbtgt_out_n = NULL;
1440 char *user2user_name = NULL;
1441 hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
1442 hdb_entry_ex *user2user_krbtgt = NULL;
1443 HDB *clientdb, *s4u2self_impersonated_clientdb;
1444 HDB *serverdb = NULL;
1445 krb5_realm ref_realm = NULL;
1446 EncTicketPart *tgt = &ticket->ticket;
1447 const EncryptionKey *ekey;
1448 krb5_keyblock sessionkey;
1449 krb5_kvno kvno;
1450 krb5_pac user2user_pac = NULL;
1451 uint16_t rodc_id;
1452 krb5_boolean add_ticket_sig = FALSE;
1453 const char *tgt_realm = /* Realm of TGT issuer */
1454 krb5_principal_get_realm(context, krbtgt->entry.principal);
1455 const char *our_realm = /* Realm of this KDC */
1456 krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1);
1457 char **capath = NULL;
1458 size_t num_capath = 0;
1460 hdb_entry_ex *krbtgt_out = NULL;
1462 PrincipalName *s;
1463 Realm r;
1464 EncTicketPart adtkt;
1465 char opt_str[128];
1466 krb5_boolean kdc_issued = FALSE;
1468 Key *tkey_sign;
1469 int flags = HDB_F_FOR_TGS_REQ;
1471 int result;
1473 memset(&sessionkey, 0, sizeof(sessionkey));
1474 memset(&adtkt, 0, sizeof(adtkt));
1476 s = b->sname;
1477 r = b->realm;
1480 * The canonicalize KDC option is passed as a hint to the backend, but
1481 * can typically be ignored. Per RFC 6806, names are not canonicalized
1482 * in response to a TGS request (although we make an exception, see
1483 * force-canonicalize below).
1485 if (b->kdc_options.canonicalize)
1486 flags |= HDB_F_CANON;
1488 if (s == NULL) {
1489 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1490 _kdc_set_const_e_text(priv, "No server in request");
1491 goto out;
1494 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1495 ret = krb5_unparse_name(context, sp, &priv->sname);
1496 if (ret)
1497 goto out;
1498 spn = priv->sname;
1499 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1500 ret = krb5_unparse_name(context, cp, &priv->cname);
1501 if (ret)
1502 goto out;
1503 cpn = priv->cname;
1504 result = unparse_flags(KDCOptions2int(b->kdc_options),
1505 asn1_KDCOptions_units(),
1506 opt_str, sizeof(opt_str));
1507 if (result > 0)
1508 kdc_log(context, config, 4,
1509 "TGS-REQ %s from %s for %s [%s]",
1510 cpn, from, spn, opt_str);
1511 else
1512 kdc_log(context, config, 4,
1513 "TGS-REQ %s from %s for %s", cpn, from, spn);
1516 * Fetch server
1519 server_lookup:
1520 priv->server = NULL;
1521 if (server)
1522 _kdc_free_ent(context, server);
1523 server = NULL;
1524 ret = _kdc_db_fetch(context, config, sp,
1525 HDB_F_GET_SERVER | HDB_F_DELAY_NEW_KEYS | flags,
1526 NULL, &serverdb, &server);
1527 priv->server = server;
1528 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1529 kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", spn);
1530 _kdc_audit_addreason((kdc_request_t)priv, "Target not found here");
1531 goto out;
1532 } else if (ret == HDB_ERR_WRONG_REALM) {
1533 free(ref_realm);
1534 ref_realm = strdup(server->entry.principal->realm);
1535 if (ref_realm == NULL) {
1536 ret = krb5_enomem(context);
1537 goto out;
1540 kdc_log(context, config, 4,
1541 "Returning a referral to realm %s for "
1542 "server %s.",
1543 ref_realm, spn);
1544 krb5_free_principal(context, sp);
1545 sp = NULL;
1546 ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1547 ref_realm, NULL);
1548 if (ret)
1549 goto out;
1550 free(priv->sname);
1551 priv->sname = NULL;
1552 ret = krb5_unparse_name(context, sp, &priv->sname);
1553 if (ret)
1554 goto out;
1555 spn = priv->sname;
1557 goto server_lookup;
1558 } else if (ret) {
1559 const char *new_rlm, *msg;
1560 Realm req_rlm;
1561 krb5_realm *realms;
1563 if (!config->autodetect_referrals) {
1564 /* noop */
1565 } else if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1566 if (capath == NULL) {
1567 /* With referalls, hierarchical capaths are always enabled */
1568 ret2 = _krb5_find_capath(context, tgt->crealm, our_realm,
1569 req_rlm, TRUE, &capath, &num_capath);
1570 if (ret2) {
1571 ret = ret2;
1572 _kdc_audit_addreason((kdc_request_t)priv,
1573 "No trusted path from client realm to ours");
1574 goto out;
1577 new_rlm = num_capath > 0 ? capath[--num_capath] : NULL;
1578 if (new_rlm) {
1579 kdc_log(context, config, 5, "krbtgt from %s via %s for "
1580 "realm %s not found, trying %s", tgt->crealm,
1581 our_realm, req_rlm, new_rlm);
1583 free(ref_realm);
1584 ref_realm = strdup(new_rlm);
1585 if (ref_realm == NULL) {
1586 ret = krb5_enomem(context);
1587 goto out;
1590 krb5_free_principal(context, sp);
1591 sp = NULL;
1592 krb5_make_principal(context, &sp, r,
1593 KRB5_TGS_NAME, ref_realm, NULL);
1594 free(priv->sname);
1595 priv->sname = NULL;
1596 ret = krb5_unparse_name(context, sp, &priv->sname);
1597 if (ret)
1598 goto out;
1599 spn = priv->sname;
1600 goto server_lookup;
1602 } else if (need_referral(context, config, &b->kdc_options, sp, &realms)) {
1603 if (strcmp(realms[0], sp->realm) != 0) {
1604 kdc_log(context, config, 4,
1605 "Returning a referral to realm %s for "
1606 "server %s that was not found",
1607 realms[0], spn);
1608 krb5_free_principal(context, sp);
1609 sp = NULL;
1610 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1611 realms[0], NULL);
1612 free(priv->sname);
1613 priv->sname = NULL;
1614 ret = krb5_unparse_name(context, sp, &priv->sname);
1615 if (ret) {
1616 krb5_free_host_realm(context, realms);
1617 goto out;
1619 spn = priv->sname;
1621 free(ref_realm);
1622 ref_realm = strdup(realms[0]);
1624 krb5_free_host_realm(context, realms);
1625 goto server_lookup;
1627 krb5_free_host_realm(context, realms);
1629 msg = krb5_get_error_message(context, ret);
1630 kdc_log(context, config, 3,
1631 "Server not found in database: %s: %s", spn, msg);
1632 krb5_free_error_message(context, msg);
1633 if (ret == HDB_ERR_NOENTRY)
1634 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1635 _kdc_audit_addreason((kdc_request_t)priv,
1636 "Service principal unknown");
1637 goto out;
1641 * RFC 6806 notes that names MUST NOT be changed in the response to
1642 * a TGS request. Hence we ignore the setting of the canonicalize
1643 * KDC option. However, for legacy interoperability we do allow the
1644 * backend to override this by setting the force-canonicalize HDB
1645 * flag in the server entry.
1647 if (server->entry.flags.force_canonicalize)
1648 rsp = server->entry.principal;
1649 else
1650 rsp = sp;
1653 * Now refetch the primary krbtgt, and get the current kvno (the
1654 * sign check may have been on an old kvno, and the server may
1655 * have been an incoming trust)
1658 ret = krb5_make_principal(context,
1659 &krbtgt_out_principal,
1660 our_realm,
1661 KRB5_TGS_NAME,
1662 our_realm,
1663 NULL);
1664 if (ret) {
1665 kdc_log(context, config, 4,
1666 "Failed to make krbtgt principal name object for "
1667 "authz-data signatures");
1668 goto out;
1670 ret = krb5_unparse_name(context, krbtgt_out_principal, &krbtgt_out_n);
1671 if (ret) {
1672 kdc_log(context, config, 4,
1673 "Failed to make krbtgt principal name object for "
1674 "authz-data signatures");
1675 goto out;
1678 ret = _kdc_db_fetch(context, config, krbtgt_out_principal,
1679 HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
1680 if (ret) {
1681 char *ktpn = NULL;
1682 ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn);
1683 kdc_log(context, config, 4,
1684 "No such principal %s (needed for authz-data signature keys) "
1685 "while processing TGS-REQ for service %s with krbtg %s",
1686 krbtgt_out_n, spn, (ret == 0) ? ktpn : "<unknown>");
1687 free(ktpn);
1688 ret = KRB5KRB_AP_ERR_NOT_US;
1689 goto out;
1693 * Select enctype, return key and kvno.
1697 krb5_enctype etype;
1699 if(b->kdc_options.enc_tkt_in_skey) {
1700 Ticket *t;
1701 krb5_principal p;
1702 Key *uukey;
1703 krb5uint32 second_kvno = 0;
1704 krb5uint32 *kvno_ptr = NULL;
1705 size_t i;
1706 hdb_entry_ex *user2user_client = NULL;
1707 krb5_boolean user2user_kdc_issued = FALSE;
1709 if(b->additional_tickets == NULL ||
1710 b->additional_tickets->len == 0){
1711 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1712 kdc_log(context, config, 4,
1713 "No second ticket present in user-to-user request");
1714 _kdc_audit_addreason((kdc_request_t)priv,
1715 "No second ticket present in user-to-user request");
1716 goto out;
1718 t = &b->additional_tickets->val[0];
1719 if(!get_krbtgt_realm(&t->sname)){
1720 kdc_log(context, config, 4,
1721 "Additional ticket is not a ticket-granting ticket");
1722 _kdc_audit_addreason((kdc_request_t)priv,
1723 "Additional ticket is not a ticket-granting ticket");
1724 ret = KRB5KDC_ERR_POLICY;
1725 goto out;
1727 ret = _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1728 if (ret)
1729 goto out;
1731 ret = krb5_unparse_name(context, p, &tpn);
1732 if (ret)
1733 goto out;
1734 if(t->enc_part.kvno){
1735 second_kvno = *t->enc_part.kvno;
1736 kvno_ptr = &second_kvno;
1738 ret = _kdc_db_fetch(context, config, p,
1739 HDB_F_GET_KRBTGT, kvno_ptr,
1740 NULL, &user2user_krbtgt);
1741 krb5_free_principal(context, p);
1742 if(ret){
1743 if (ret == HDB_ERR_NOENTRY)
1744 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1745 _kdc_audit_addreason((kdc_request_t)priv,
1746 "User-to-user service principal (TGS) unknown");
1747 goto out;
1749 ret = hdb_enctype2key(context, &user2user_krbtgt->entry, NULL,
1750 t->enc_part.etype, &uukey);
1751 if(ret){
1752 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1753 _kdc_audit_addreason((kdc_request_t)priv,
1754 "User-to-user enctype not supported");
1755 goto out;
1757 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1758 if(ret) {
1759 _kdc_audit_addreason((kdc_request_t)priv,
1760 "User-to-user TGT decrypt failure");
1761 goto out;
1764 ret = _kdc_verify_flags(context, config, &adtkt, tpn);
1765 if (ret) {
1766 _kdc_audit_addreason((kdc_request_t)priv,
1767 "User-to-user TGT expired or invalid");
1768 goto out;
1771 /* Fetch the name from the TGT. */
1772 ret = _krb5_principalname2krb5_principal(context, &user2user_princ,
1773 adtkt.cname, adtkt.crealm);
1774 if (ret)
1775 goto out;
1777 ret = krb5_unparse_name(context, user2user_princ, &user2user_name);
1778 if (ret)
1779 goto out;
1782 * Look up the name given in the TGT in the database. The user
1783 * claims to have a ticket-granting-ticket to our KDC, so we should
1784 * fail hard if we can't find the user - otherwise we can't do
1785 * proper checks.
1787 ret = _kdc_db_fetch(context, config, user2user_princ,
1788 HDB_F_GET_CLIENT | flags,
1789 NULL, NULL, &user2user_client);
1790 if (ret == HDB_ERR_NOENTRY)
1791 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1792 if (ret)
1793 goto out;
1796 * The account is present in the database, now check the
1797 * account flags.
1799 * We check this as a client (because the purpose of
1800 * user2user is that the server flag is not set, because
1801 * the long-term key is not strong, but this does mean
1802 * that a client with an expired password can't get accept
1803 * a user2user ticket.
1805 ret = kdc_check_flags(priv,
1806 FALSE,
1807 user2user_client,
1808 NULL);
1809 if (ret) {
1810 _kdc_free_ent(context, user2user_client);
1811 goto out;
1815 * Also check that the account is the same one specified in the
1816 * request.
1818 ret = check_client_matches_target_service(context,
1819 config,
1820 serverdb,
1821 server,
1822 user2user_client,
1823 user2user_princ);
1824 if (ret) {
1825 _kdc_free_ent(context, user2user_client);
1826 goto out;
1829 /* Verify the PAC of the TGT. */
1830 ret = _kdc_check_pac(context, config, user2user_princ, NULL,
1831 user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt,
1832 &uukey->key, &priv->ticket_key->key, &adtkt,
1833 &user2user_kdc_issued, &user2user_pac, NULL, NULL);
1834 _kdc_free_ent(context, user2user_client);
1835 if (ret) {
1836 const char *msg = krb5_get_error_message(context, ret);
1837 kdc_log(context, config, 0,
1838 "Verify PAC failed for %s (%s) from %s with %s",
1839 spn, user2user_name, from, msg);
1840 krb5_free_error_message(context, msg);
1841 goto out;
1844 if ((config->require_pac && !user2user_pac)
1845 || (user2user_pac && !user2user_kdc_issued))
1847 ret = KRB5KDC_ERR_BADOPTION;
1848 kdc_log(context, config, 0,
1849 "Ticket not signed with PAC; user-to-user failed (%s).",
1850 user2user_pac ? "Ticket unsigned" : "No PAC");
1851 goto out;
1854 ekey = &adtkt.key;
1855 for(i = 0; i < b->etype.len; i++)
1856 if (b->etype.val[i] == adtkt.key.keytype)
1857 break;
1858 if(i == b->etype.len) {
1859 kdc_log(context, config, 4,
1860 "Addition ticket have not matching etypes");
1861 krb5_clear_error_message(context);
1862 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1863 _kdc_audit_addreason((kdc_request_t)priv,
1864 "No matching enctypes for 2nd ticket");
1865 goto out;
1867 etype = b->etype.val[i];
1868 kvno = 0;
1869 } else {
1870 Key *skey;
1872 ret = _kdc_find_etype(priv, krb5_principal_is_krbtgt(context, sp)
1873 ? KFE_IS_TGS : 0,
1874 b->etype.val, b->etype.len, &etype, NULL,
1875 NULL);
1876 if(ret) {
1877 kdc_log(context, config, 4,
1878 "Server (%s) has no support for etypes", spn);
1879 _kdc_audit_addreason((kdc_request_t)priv,
1880 "Enctype not supported");
1881 goto out;
1883 ret = _kdc_get_preferred_key(context, config, server, spn,
1884 NULL, &skey);
1885 if(ret) {
1886 kdc_log(context, config, 4,
1887 "Server (%s) has no supported etypes", spn);
1888 _kdc_audit_addreason((kdc_request_t)priv,
1889 "Enctype not supported");
1890 goto out;
1892 ekey = &skey->key;
1893 kvno = server->entry.kvno;
1896 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1897 if (ret)
1898 goto out;
1902 * Check that service is in the same realm as the krbtgt. If it's
1903 * not the same, it's someone that is using a uni-directional trust
1904 * backward.
1908 * The first realm is the realm of the service, the second is
1909 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1910 * encrypted to. The redirection via the krbtgt_out entry allows
1911 * the DB to possibly correct the case of the realm (Samba4 does
1912 * this) before the strcmp()
1914 if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
1915 krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
1916 char *ktpn;
1917 ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn);
1918 kdc_log(context, config, 4,
1919 "Request with wrong krbtgt: %s",
1920 (ret == 0) ? ktpn : "<unknown>");
1921 if(ret == 0)
1922 free(ktpn);
1923 ret = KRB5KRB_AP_ERR_NOT_US;
1924 _kdc_audit_addreason((kdc_request_t)priv, "Request with wrong TGT");
1925 goto out;
1928 ret = _kdc_get_preferred_key(context, config, krbtgt_out, krbtgt_out_n,
1929 NULL, &tkey_sign);
1930 if (ret) {
1931 kdc_log(context, config, 4,
1932 "Failed to find key for krbtgt PAC signature");
1933 _kdc_audit_addreason((kdc_request_t)priv,
1934 "Failed to find key for krbtgt PAC signature");
1935 goto out;
1937 ret = hdb_enctype2key(context, &krbtgt_out->entry, NULL,
1938 tkey_sign->key.keytype, &tkey_sign);
1939 if(ret) {
1940 kdc_log(context, config, 4,
1941 "Failed to find key for krbtgt PAC signature");
1942 _kdc_audit_addreason((kdc_request_t)priv,
1943 "Failed to find key for krbtgt PAC signature");
1944 goto out;
1947 if (_kdc_synthetic_princ_used_p(context, ticket))
1948 flags |= HDB_F_SYNTHETIC_OK;
1950 ret = _kdc_db_fetch_client(context, config, flags, cp, cpn, our_realm,
1951 &clientdb, &client);
1952 if (ret)
1953 goto out;
1954 flags &= ~HDB_F_SYNTHETIC_OK;
1955 priv->client = client;
1957 heim_assert(priv->client_princ == NULL, "client_princ should be NULL for TGS");
1959 ret = _kdc_check_pac(context, config, cp, NULL, client, server, krbtgt, krbtgt,
1960 &priv->ticket_key->key, &priv->ticket_key->key, tgt,
1961 &kdc_issued, &priv->pac, &priv->client_princ, &priv->pac_attributes);
1962 if (ret) {
1963 const char *msg = krb5_get_error_message(context, ret);
1964 _kdc_audit_addreason((kdc_request_t)priv, "PAC check failed");
1965 kdc_log(context, config, 4,
1966 "Verify PAC failed for %s (%s) from %s with %s",
1967 spn, cpn, from, msg);
1968 krb5_free_error_message(context, msg);
1969 goto out;
1973 * Process request
1976 /* by default the tgt principal matches the client principal */
1977 tp = cp;
1978 tpn = cpn;
1980 if (client) {
1981 const PA_DATA *sdata;
1982 int i = 0;
1984 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1985 if (sdata) {
1986 krb5_crypto crypto;
1987 krb5_data datack;
1988 PA_S4U2Self self;
1989 const char *str;
1991 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1992 sdata->padata_value.length,
1993 &self, NULL);
1994 if (ret) {
1995 _kdc_audit_addreason((kdc_request_t)priv,
1996 "Failed to decode PA-S4U2Self");
1997 kdc_log(context, config, 4, "Failed to decode PA-S4U2Self");
1998 goto out;
2001 if (!krb5_checksum_is_keyed(context, self.cksum.cksumtype)) {
2002 free_PA_S4U2Self(&self);
2003 _kdc_audit_addreason((kdc_request_t)priv,
2004 "PA-S4U2Self with unkeyed checksum");
2005 kdc_log(context, config, 4, "Reject PA-S4U2Self with unkeyed checksum");
2006 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
2007 goto out;
2010 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
2011 if (ret)
2012 goto out;
2014 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
2015 if (ret) {
2016 const char *msg = krb5_get_error_message(context, ret);
2017 free_PA_S4U2Self(&self);
2018 krb5_data_free(&datack);
2019 kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
2020 krb5_free_error_message(context, msg);
2021 goto out;
2024 /* Allow HMAC_MD5 checksum with any key type */
2025 if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
2026 struct krb5_crypto_iov iov;
2027 unsigned char csdata[16];
2028 Checksum cs;
2030 cs.checksum.length = sizeof(csdata);
2031 cs.checksum.data = &csdata;
2033 iov.data.data = datack.data;
2034 iov.data.length = datack.length;
2035 iov.flags = KRB5_CRYPTO_TYPE_DATA;
2037 ret = _krb5_HMAC_MD5_checksum(context, NULL, &crypto->key,
2038 KRB5_KU_OTHER_CKSUM, &iov, 1,
2039 &cs);
2040 if (ret == 0 &&
2041 krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0)
2042 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2044 else {
2045 ret = _kdc_verify_checksum(context,
2046 crypto,
2047 KRB5_KU_OTHER_CKSUM,
2048 &datack,
2049 &self.cksum);
2051 krb5_data_free(&datack);
2052 krb5_crypto_destroy(context, crypto);
2053 if (ret) {
2054 const char *msg = krb5_get_error_message(context, ret);
2055 free_PA_S4U2Self(&self);
2056 _kdc_audit_addreason((kdc_request_t)priv,
2057 "S4U2Self checksum failed");
2058 kdc_log(context, config, 4,
2059 "krb5_verify_checksum failed for S4U2Self: %s", msg);
2060 krb5_free_error_message(context, msg);
2061 goto out;
2064 ret = _krb5_principalname2krb5_principal(context,
2065 &tp,
2066 self.name,
2067 self.realm);
2068 free_PA_S4U2Self(&self);
2069 if (ret)
2070 goto out;
2072 ret = krb5_unparse_name(context, tp, &tpn);
2073 if (ret)
2074 goto out;
2077 * Note no HDB_F_SYNTHETIC_OK -- impersonating non-existent clients
2078 * is probably not desirable!
2080 ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags,
2081 NULL, &s4u2self_impersonated_clientdb,
2082 &s4u2self_impersonated_client);
2083 if (ret) {
2084 const char *msg;
2087 * If the client belongs to the same realm as our krbtgt, it
2088 * should exist in the local database.
2092 if (ret == HDB_ERR_NOENTRY)
2093 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2094 msg = krb5_get_error_message(context, ret);
2095 _kdc_audit_addreason((kdc_request_t)priv,
2096 "S4U2Self principal to impersonate not found");
2097 kdc_log(context, config, 2,
2098 "S4U2Self principal to impersonate %s not found in database: %s",
2099 tpn, msg);
2100 krb5_free_error_message(context, msg);
2101 goto out;
2104 /* Ignore require_pwchange and pw_end attributes (as Windows does),
2105 * since S4U2Self is not password authentication. */
2106 s4u2self_impersonated_client->entry.flags.require_pwchange = FALSE;
2107 free(s4u2self_impersonated_client->entry.pw_end);
2108 s4u2self_impersonated_client->entry.pw_end = NULL;
2110 ret = kdc_check_flags(priv, FALSE, s4u2self_impersonated_client, priv->server);
2111 if (ret)
2112 goto out; /* kdc_check_flags() calls _kdc_audit_addreason() */
2114 /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
2115 krb5_pac_free(context, priv->pac);
2116 priv->pac = NULL;
2118 ret = _kdc_pac_generate(context,
2119 s4u2self_impersonated_client,
2120 server,
2121 NULL,
2122 KRB5_PAC_WAS_GIVEN_IMPLICITLY,
2123 &priv->pac);
2124 if (ret) {
2125 kdc_log(context, config, 4, "PAC generation failed for -- %s", tpn);
2126 goto out;
2130 * Check that service doing the impersonating is
2131 * requesting a ticket to it-self.
2133 ret = check_client_matches_target_service(context,
2134 config,
2135 clientdb,
2136 client,
2137 server,
2138 sp);
2139 if (ret) {
2140 kdc_log(context, config, 4, "S4U2Self: %s is not allowed "
2141 "to impersonate to service "
2142 "(tried for user %s to service %s)",
2143 cpn, tpn, spn);
2144 goto out;
2148 * If the service isn't trusted for authentication to
2149 * delegation or if the impersonate client is disallowed
2150 * forwardable, remove the forwardable flag.
2153 if (client->entry.flags.trusted_for_delegation &&
2154 s4u2self_impersonated_client->entry.flags.forwardable) {
2155 str = "[forwardable]";
2156 } else {
2157 b->kdc_options.forwardable = 0;
2158 str = "";
2160 kdc_log(context, config, 4, "s4u2self %s impersonating %s to "
2161 "service %s %s", cpn, tpn, spn, str);
2166 * Constrained delegation
2169 if (client != NULL
2170 && b->additional_tickets != NULL
2171 && b->additional_tickets->len != 0
2172 && b->kdc_options.cname_in_addl_tkt
2173 && b->kdc_options.enc_tkt_in_skey == 0)
2175 hdb_entry_ex *adclient = NULL;
2176 krb5_boolean ad_kdc_issued = FALSE;
2177 Key *clientkey;
2178 Ticket *t;
2181 * We require that the service's krbtgt has a PAC.
2183 if (priv->pac == NULL) {
2184 ret = KRB5KDC_ERR_BADOPTION;
2185 _kdc_audit_addreason((kdc_request_t)priv, "Missing PAC");
2186 kdc_log(context, config, 4,
2187 "Constrained delegation without PAC, %s/%s",
2188 cpn, spn);
2189 goto out;
2192 krb5_pac_free(context, priv->pac);
2193 priv->pac = NULL;
2195 krb5_free_principal(context, priv->client_princ);
2196 priv->client_princ = NULL;
2198 t = &b->additional_tickets->val[0];
2200 ret = hdb_enctype2key(context, &client->entry,
2201 hdb_kvno2keys(context, &client->entry,
2202 t->enc_part.kvno ? * t->enc_part.kvno : 0),
2203 t->enc_part.etype, &clientkey);
2204 if(ret){
2205 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
2206 goto out;
2209 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
2210 if (ret) {
2211 _kdc_audit_addreason((kdc_request_t)priv,
2212 "Failed to decrypt constrained delegation ticket");
2213 kdc_log(context, config, 4,
2214 "failed to decrypt ticket for "
2215 "constrained delegation from %s to %s ", cpn, spn);
2216 goto out;
2219 ret = _krb5_principalname2krb5_principal(context,
2220 &tp,
2221 adtkt.cname,
2222 adtkt.crealm);
2223 if (ret)
2224 goto out;
2226 ret = krb5_unparse_name(context, tp, &tpn);
2227 if (ret)
2228 goto out;
2230 _kdc_audit_addkv((kdc_request_t)priv, 0, "impersonatee", "%s", tpn);
2232 ret = _krb5_principalname2krb5_principal(context,
2233 &dp,
2234 t->sname,
2235 t->realm);
2236 if (ret)
2237 goto out;
2239 ret = krb5_unparse_name(context, dp, &dpn);
2240 if (ret)
2241 goto out;
2243 /* check that ticket is valid */
2244 if (adtkt.flags.forwardable == 0) {
2245 _kdc_audit_addreason((kdc_request_t)priv,
2246 "Missing forwardable flag on ticket for constrained delegation");
2247 kdc_log(context, config, 4,
2248 "Missing forwardable flag on ticket for "
2249 "constrained delegation from %s (%s) as %s to %s ",
2250 cpn, dpn, tpn, spn);
2251 ret = KRB5KDC_ERR_BADOPTION;
2252 goto out;
2255 ret = check_constrained_delegation(context, config, clientdb,
2256 client, server, sp);
2257 if (ret) {
2258 _kdc_audit_addreason((kdc_request_t)priv,
2259 "Constrained delegation not allowed");
2260 kdc_log(context, config, 4,
2261 "constrained delegation from %s (%s) as %s to %s not allowed",
2262 cpn, dpn, tpn, spn);
2263 goto out;
2266 ret = _kdc_verify_flags(context, config, &adtkt, tpn);
2267 if (ret) {
2268 _kdc_audit_addreason((kdc_request_t)priv,
2269 "Constrained delegation ticket expired or invalid");
2270 goto out;
2273 /* Try lookup the delegated client in DB */
2274 ret = _kdc_db_fetch_client(context, config, flags, tp, tpn, our_realm,
2275 NULL, &adclient);
2276 if (ret)
2277 goto out;
2279 if (adclient != NULL) {
2280 ret = kdc_check_flags(priv, FALSE, adclient, priv->server);
2281 if (ret) {
2282 _kdc_free_ent(context, adclient);
2283 goto out;
2288 * TODO: pass in t->sname and t->realm and build
2289 * a S4U_DELEGATION_INFO blob to the PAC.
2291 ret = _kdc_check_pac(context, config, tp, dp, adclient, server, krbtgt, client,
2292 &clientkey->key, &priv->ticket_key->key, &adtkt,
2293 &ad_kdc_issued, &priv->pac, &priv->client_princ, &priv->pac_attributes);
2294 if (adclient)
2295 _kdc_free_ent(context, adclient);
2296 if (ret) {
2297 const char *msg = krb5_get_error_message(context, ret);
2298 _kdc_audit_addreason((kdc_request_t)priv,
2299 "Constrained delegation ticket PAC check failed");
2300 kdc_log(context, config, 4,
2301 "Verify delegated PAC failed to %s for client"
2302 "%s (%s) as %s from %s with %s",
2303 spn, cpn, dpn, tpn, from, msg);
2304 krb5_free_error_message(context, msg);
2305 goto out;
2308 if (priv->pac == NULL || !ad_kdc_issued) {
2309 ret = KRB5KDC_ERR_BADOPTION;
2310 kdc_log(context, config, 4,
2311 "Ticket not signed with PAC; service %s failed for "
2312 "for delegation to %s for client %s (%s) from %s; (%s).",
2313 spn, tpn, dpn, cpn, from, priv->pac ? "Ticket unsigned" : "No PAC");
2314 _kdc_audit_addreason((kdc_request_t)priv,
2315 "Constrained delegation ticket not signed");
2316 goto out;
2319 kdc_log(context, config, 4, "constrained delegation for %s "
2320 "from %s (%s) to %s", tpn, cpn, dpn, spn);
2324 * Check flags
2327 ret = kdc_check_flags(priv, FALSE, priv->client, priv->server);
2328 if(ret)
2329 goto out;
2331 if((b->kdc_options.validate || b->kdc_options.renew) &&
2332 !krb5_principal_compare(context,
2333 krbtgt->entry.principal,
2334 server->entry.principal)){
2335 _kdc_audit_addreason((kdc_request_t)priv, "Inconsistent request");
2336 kdc_log(context, config, 4, "Inconsistent request.");
2337 ret = KRB5KDC_ERR_SERVER_NOMATCH;
2338 goto out;
2341 /* check for valid set of addresses */
2342 if (!_kdc_check_addresses(priv, tgt->caddr, from_addr)) {
2343 if (config->check_ticket_addresses) {
2344 ret = KRB5KRB_AP_ERR_BADADDR;
2345 _kdc_audit_addkv((kdc_request_t)priv, 0, "wrongaddr", "yes");
2346 kdc_log(context, config, 4, "Request from wrong address");
2347 _kdc_audit_addreason((kdc_request_t)priv, "Request from wrong address");
2348 goto out;
2349 } else if (config->warn_ticket_addresses) {
2350 _kdc_audit_addkv((kdc_request_t)priv, 0, "wrongaddr", "yes");
2354 /* check local and per-principal anonymous ticket issuance policy */
2355 if (is_anon_tgs_request_p(b, tgt)) {
2356 ret = _kdc_check_anon_policy(priv);
2357 if (ret)
2358 goto out;
2362 * If this is an referral, add server referral data to the
2363 * auth_data reply .
2365 if (ref_realm) {
2366 PA_DATA pa;
2367 krb5_crypto crypto;
2369 kdc_log(context, config, 3,
2370 "Adding server referral to %s", ref_realm);
2372 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
2373 if (ret)
2374 goto out;
2376 ret = build_server_referral(context, config, crypto, ref_realm,
2377 NULL, s, &pa.padata_value);
2378 krb5_crypto_destroy(context, crypto);
2379 if (ret) {
2380 _kdc_audit_addreason((kdc_request_t)priv, "Referral build failed");
2381 kdc_log(context, config, 4,
2382 "Failed building server referral");
2383 goto out;
2385 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
2387 ret = add_METHOD_DATA(priv->rep.padata, &pa);
2388 krb5_data_free(&pa.padata_value);
2389 if (ret) {
2390 kdc_log(context, config, 4,
2391 "Add server referral METHOD-DATA failed");
2392 goto out;
2397 * Only add ticket signature if the requested server is not krbtgt, and
2398 * either the header server is krbtgt or, in the case of renewal/validation
2399 * if it was signed with PAC ticket signature and we verified it.
2400 * Currently Heimdal only allows renewal of krbtgt anyway but that might
2401 * change one day (see issue #763) so make sure to check for it.
2404 if (kdc_issued &&
2405 !krb5_principal_is_krbtgt(context, server->entry.principal)) {
2407 /* Validate armor TGT before potentially including device claims */
2408 if (priv->armor_ticket) {
2409 ret = _kdc_fast_check_armor_pac(priv);
2410 if (ret)
2411 goto out;
2414 add_ticket_sig = TRUE;
2418 * Active-Directory implementations use the high part of the kvno as the
2419 * read-only-dc identifier, we need to embed it in the PAC KDC signatures.
2422 rodc_id = krbtgt_out->entry.kvno >> 16;
2428 ret = tgs_make_reply(priv,
2430 tgt,
2431 ekey,
2432 &tkey_sign->key,
2433 &sessionkey,
2434 kvno,
2435 *auth_data,
2436 server,
2437 rsp,
2438 client,
2440 tgt_realm,
2441 rodc_id,
2442 add_ticket_sig);
2444 out:
2445 free(user2user_name);
2446 if (tpn != cpn)
2447 free(tpn);
2448 free(dpn);
2449 free(krbtgt_out_n);
2450 _krb5_free_capath(context, capath);
2452 krb5_free_keyblock_contents(context, &sessionkey);
2453 if(krbtgt_out)
2454 _kdc_free_ent(context, krbtgt_out);
2455 if(server)
2456 _kdc_free_ent(context, server);
2457 if(client)
2458 _kdc_free_ent(context, client);
2459 if(s4u2self_impersonated_client)
2460 _kdc_free_ent(context, s4u2self_impersonated_client);
2461 if(user2user_krbtgt)
2462 _kdc_free_ent(context, user2user_krbtgt);
2464 krb5_free_principal(context, user2user_princ);
2465 if (tp && tp != cp)
2466 krb5_free_principal(context, tp);
2467 krb5_free_principal(context, cp);
2468 krb5_free_principal(context, dp);
2469 krb5_free_principal(context, sp);
2470 krb5_free_principal(context, krbtgt_out_principal);
2471 free(ref_realm);
2473 free_EncTicketPart(&adtkt);
2475 krb5_pac_free(context, user2user_pac);
2477 return ret;
2484 krb5_error_code
2485 _kdc_tgs_rep(astgs_request_t r)
2487 krb5_kdc_configuration *config = r->config;
2488 KDC_REQ *req = &r->req;
2489 krb5_data *data = r->reply;
2490 const char *from = r->from;
2491 struct sockaddr *from_addr = r->addr;
2492 int datagram_reply = r->datagram_reply;
2493 AuthorizationData *auth_data = NULL;
2494 krb5_error_code ret;
2495 int i = 0;
2496 const PA_DATA *tgs_req, *pa;
2498 hdb_entry_ex *krbtgt = NULL;
2499 krb5_ticket *ticket = NULL;
2500 krb5_enctype krbtgt_etype = ETYPE_NULL;
2502 time_t *csec = NULL;
2503 int *cusec = NULL;
2505 r->e_text = NULL;
2507 if(req->padata == NULL){
2508 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2509 kdc_log(r->context, config, 4,
2510 "TGS-REQ from %s without PA-DATA", from);
2511 goto out;
2514 i = 0;
2515 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST_ARMOR);
2516 if (pa) {
2517 kdc_log(r->context, r->config, 10, "Found TGS-REQ FAST armor inside TGS-REQ pa-data");
2518 ret = KRB5KRB_ERR_GENERIC;
2519 goto out;
2522 i = 0;
2523 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2524 if(tgs_req == NULL){
2525 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2527 kdc_log(r->context, config, 4,
2528 "TGS-REQ from %s without PA-TGS-REQ", from);
2529 goto out;
2531 ret = tgs_parse_request(r, tgs_req,
2532 &krbtgt,
2533 &krbtgt_etype,
2534 &ticket,
2535 from, from_addr,
2536 &csec, &cusec,
2537 &auth_data);
2538 if (ret == HDB_ERR_NOT_FOUND_HERE) {
2539 /* kdc_log() is called in tgs_parse_request() */
2540 goto out;
2542 if (ret) {
2543 kdc_log(r->context, config, 4,
2544 "Failed parsing TGS-REQ from %s", from);
2545 goto out;
2548 ret = _kdc_fast_strengthen_reply_key(r);
2549 if (ret)
2550 goto out;
2552 ALLOC(r->rep.padata);
2553 if (r->rep.padata == NULL) {
2554 ret = ENOMEM;
2555 krb5_set_error_message(r->context, ret, N_("malloc: out of memory", ""));
2556 goto out;
2559 ret = tgs_build_reply(r,
2560 krbtgt,
2561 krbtgt_etype,
2562 ticket,
2563 &auth_data,
2564 from_addr);
2565 if (ret) {
2566 kdc_log(r->context, config, 4,
2567 "Failed building TGS-REP to %s", from);
2568 goto out;
2571 /* */
2572 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2573 krb5_data_free(data);
2574 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2575 _kdc_set_const_e_text(r, "Reply packet too large");
2578 out:
2579 if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2580 METHOD_DATA error_method = { 0, NULL };
2582 kdc_log(r->context, config, 5, "tgs-req: sending error: %d to client", ret);
2583 ret = _kdc_fast_mk_error(r,
2584 &error_method,
2585 r->armor_crypto,
2586 &req->req_body,
2587 r->ret = ret,
2588 ticket != NULL ? ticket->client : NULL,
2589 ticket != NULL ? ticket->server : NULL,
2590 csec, cusec,
2591 data);
2592 free_METHOD_DATA(&error_method);
2594 free(csec);
2595 free(cusec);
2597 free_TGS_REP(&r->rep);
2598 free_TransitedEncoding(&r->et.transited);
2599 free(r->et.starttime);
2600 free(r->et.renew_till);
2601 if(r->et.authorization_data) {
2602 free_AuthorizationData(r->et.authorization_data);
2603 free(r->et.authorization_data);
2605 free_LastReq(&r->ek.last_req);
2606 if (r->et.key.keyvalue.data) {
2607 memset_s(r->et.key.keyvalue.data, 0, r->et.key.keyvalue.length,
2608 r->et.key.keyvalue.length);
2610 free_EncryptionKey(&r->et.key);
2612 if (r->client_princ) {
2613 krb5_free_principal(r->context, r->client_princ);
2614 r->client_princ = NULL;
2616 if (r->armor_crypto) {
2617 krb5_crypto_destroy(r->context, r->armor_crypto);
2618 r->armor_crypto = NULL;
2620 if (r->armor_ticket)
2621 krb5_free_ticket(r->context, r->armor_ticket);
2622 if (r->armor_server)
2623 _kdc_free_ent(r->context, r->armor_server);
2624 krb5_free_keyblock_contents(r->context, &r->reply_key);
2625 krb5_free_keyblock_contents(r->context, &r->strengthen_key);
2627 if (ticket)
2628 krb5_free_ticket(r->context, ticket);
2629 if(krbtgt)
2630 _kdc_free_ent(r->context, krbtgt);
2632 _kdc_free_fast_state(&r->fast);
2633 krb5_pac_free(r->context, r->pac);
2635 if (auth_data) {
2636 free_AuthorizationData(auth_data);
2637 free(auth_data);
2640 return ret;