kdc: simplify client_access windc plugin API
[heimdal.git] / kdc / krb5tgs.c
blob1f115b43e04c5961118f97395a50a55360164441
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 /* Verify the KDC signatures. */
125 ret = _kdc_pac_verify(context, client_principal, delegated_proxy_principal,
126 client, server, krbtgt, &pac);
127 if (ret == 0) {
128 if (pac_canon_name) {
129 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
130 if (ret && ret != ENOENT) {
131 krb5_pac_free(context, pac);
132 return ret;
135 if (pac_attributes &&
136 _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0)
137 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
138 } else if (ret == KRB5_PLUGIN_NO_HANDLE) {
140 * We can't verify the KDC signatures if the ticket was issued by
141 * another realm's KDC.
143 if (krb5_realm_compare(context, server->entry.principal,
144 ticket_server->entry.principal)) {
145 ret = krb5_pac_verify(context, pac, 0, NULL, NULL,
146 krbtgt_check_key);
147 if (ret) {
148 krb5_pac_free(context, pac);
149 return ret;
153 if (pac_canon_name) {
154 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
155 if (ret && ret != ENOENT) {
156 krb5_pac_free(context, pac);
157 return ret;
159 if (pac_attributes &&
160 _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0)
161 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
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 const char **e_text = &r->e_text;
607 krb5_data *reply = r->reply;
608 KDC_REP *rep = &r->rep;
609 EncTicketPart *et = &r->et;
610 EncKDCRepPart *ek = &r->ek;
611 KDCOptions f = b->kdc_options;
612 krb5_error_code ret;
613 int is_weak = 0;
615 rep->pvno = 5;
616 rep->msg_type = krb_tgs_rep;
618 et->authtime = tgt->authtime;
619 _kdc_fix_time(&b->till);
620 et->endtime = min(tgt->endtime, *b->till);
621 ALLOC(et->starttime);
622 *et->starttime = kdc_time;
624 ret = check_tgs_flags(r, b, tgt_name, tgt, et);
625 if(ret)
626 goto out;
628 /* We should check the transited encoding if:
629 1) the request doesn't ask not to be checked
630 2) globally enforcing a check
631 3) principal requires checking
632 4) we allow non-check per-principal, but principal isn't marked as allowing this
633 5) we don't globally allow this
636 #define GLOBAL_FORCE_TRANSITED_CHECK \
637 (r->config->trpolicy == TRPOLICY_ALWAYS_CHECK)
638 #define GLOBAL_ALLOW_PER_PRINCIPAL \
639 (r->config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
640 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
641 (r->config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
643 /* these will consult the database in future release */
644 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
645 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
647 ret = fix_transited_encoding(r->context, r->config,
648 !f.disable_transited_check ||
649 GLOBAL_FORCE_TRANSITED_CHECK ||
650 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
651 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
652 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
653 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
654 &tgt->transited, et,
655 krb5_principal_get_realm(r->context, client_principal),
656 krb5_principal_get_realm(r->context, server->entry.principal),
657 tgt_realm);
658 if(ret)
659 goto out;
661 ret = copy_Realm(&server_principal->realm, &rep->ticket.realm);
662 if (ret)
663 goto out;
664 _krb5_principal2principalname(&rep->ticket.sname, server_principal);
665 ret = copy_Realm(&tgt_name->realm, &rep->crealm);
666 if (ret)
667 goto out;
670 * RFC 8062 states "if the ticket in the TGS request is an anonymous
671 * one, the client and client realm are copied from that ticket". So
672 * whilst the TGT flag check below is superfluous, it is included in
673 * order to follow the specification to its letter.
675 if (et->flags.anonymous && !tgt->flags.anonymous)
676 _kdc_make_anonymous_principalname(&rep->cname);
677 else
678 ret = copy_PrincipalName(&tgt_name->name, &rep->cname);
679 if (ret)
680 goto out;
681 rep->ticket.tkt_vno = 5;
683 ek->caddr = et->caddr;
686 time_t life;
687 life = et->endtime - *et->starttime;
688 if(client && client->entry.max_life)
689 life = min(life, *client->entry.max_life);
690 if(server->entry.max_life)
691 life = min(life, *server->entry.max_life);
692 et->endtime = *et->starttime + life;
694 if(f.renewable_ok && tgt->flags.renewable &&
695 et->renew_till == NULL && et->endtime < *b->till &&
696 tgt->renew_till != NULL)
698 et->flags.renewable = 1;
699 ALLOC(et->renew_till);
700 *et->renew_till = *b->till;
702 if(et->renew_till){
703 time_t renew;
704 renew = *et->renew_till - *et->starttime;
705 if(client && client->entry.max_renew)
706 renew = min(renew, *client->entry.max_renew);
707 if(server->entry.max_renew)
708 renew = min(renew, *server->entry.max_renew);
709 *et->renew_till = *et->starttime + renew;
712 if(et->renew_till){
713 *et->renew_till = min(*et->renew_till, *tgt->renew_till);
714 *et->starttime = min(*et->starttime, *et->renew_till);
715 et->endtime = min(et->endtime, *et->renew_till);
718 *et->starttime = min(*et->starttime, et->endtime);
720 if(*et->starttime == et->endtime){
721 ret = KRB5KDC_ERR_NEVER_VALID;
722 goto out;
724 if(et->renew_till && et->endtime == *et->renew_till){
725 free(et->renew_till);
726 et->renew_till = NULL;
727 et->flags.renewable = 0;
730 et->flags.pre_authent = tgt->flags.pre_authent;
731 et->flags.hw_authent = tgt->flags.hw_authent;
732 et->flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
734 /* See MS-KILE 3.3.5.1 */
735 if (!server->entry.flags.forwardable)
736 et->flags.forwardable = 0;
737 if (!server->entry.flags.proxiable)
738 et->flags.proxiable = 0;
740 if (auth_data) {
741 unsigned int i = 0;
743 /* XXX check authdata */
745 if (et->authorization_data == NULL) {
746 et->authorization_data = calloc(1, sizeof(*et->authorization_data));
747 if (et->authorization_data == NULL) {
748 ret = ENOMEM;
749 krb5_set_error_message(r->context, ret, "malloc: out of memory");
750 goto out;
753 for(i = 0; i < auth_data->len ; i++) {
754 ret = add_AuthorizationData(et->authorization_data, &auth_data->val[i]);
755 if (ret) {
756 krb5_set_error_message(r->context, ret, "malloc: out of memory");
757 goto out;
762 ret = krb5_copy_keyblock_contents(r->context, sessionkey, &et->key);
763 if (ret)
764 goto out;
765 et->crealm = rep->crealm;
766 et->cname = rep->cname;
768 ek->key = et->key;
769 /* MIT must have at least one last_req */
770 ek->last_req.val = calloc(1, sizeof(*ek->last_req.val));
771 if (ek->last_req.val == NULL) {
772 ret = ENOMEM;
773 goto out;
775 ek->last_req.len = 1; /* set after alloc to avoid null deref on cleanup */
776 ek->nonce = b->nonce;
777 ek->flags = et->flags;
778 ek->authtime = et->authtime;
779 ek->starttime = et->starttime;
780 ek->endtime = et->endtime;
781 ek->renew_till = et->renew_till;
782 ek->srealm = rep->ticket.realm;
783 ek->sname = rep->ticket.sname;
785 _kdc_log_timestamp(r, "TGS-REQ", et->authtime, et->starttime,
786 et->endtime, et->renew_till);
788 rep->padata = r->outpadata.len ? &r->outpadata : NULL;
790 if (krb5_enctype_valid(r->context, serverkey->keytype) != 0
791 && _kdc_is_weak_exception(server->entry.principal, serverkey->keytype))
793 krb5_enctype_enable(r->context, serverkey->keytype);
794 is_weak = 1;
797 if (r->client_princ) {
798 char *cpn;
800 krb5_unparse_name(r->context, r->client_princ, &cpn);
801 _kdc_audit_addkv((kdc_request_t)r, 0, "canon_client_name", "%s",
802 cpn ? cpn : "<unknown>");
803 krb5_xfree(cpn);
807 * For anonymous tickets, we should filter out positive authorization data
808 * that could reveal the client's identity, and return a policy error for
809 * restrictive authorization data. Policy for unknown authorization types
810 * is implementation dependent.
812 if (r->pac && !et->flags.anonymous) {
813 _kdc_audit_addkv((kdc_request_t)r, 0, "pac_attributes", "%lx",
814 (long)r->pac_attributes);
817 * PACs are included when issuing TGTs, if there is no PAC_ATTRIBUTES
818 * buffer (legacy behavior) or if the attributes buffer indicates the
819 * AS client requested one.
821 if (_kdc_include_pac_p(r)) {
822 krb5_boolean is_tgs =
823 krb5_principal_is_krbtgt(r->context, server->entry.principal);
825 ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, tgt_name, serverkey,
826 krbtgtkey, rodc_id, NULL, r->client_princ,
827 add_ticket_sig, et,
828 is_tgs ? &r->pac_attributes : NULL);
829 if (ret)
830 goto out;
834 ret = _kdc_finalize_reply(r);
835 if (ret)
836 goto out;
838 /* It is somewhat unclear where the etype in the following
839 encryption should come from. What we have is a session
840 key in the passed tgt, and a list of preferred etypes
841 *for the new ticket*. Should we pick the best possible
842 etype, given the keytype in the tgt, or should we look
843 at the etype list here as well? What if the tgt
844 session key is DES3 and we want a ticket with a (say)
845 CAST session key. Should the DES3 etype be added to the
846 etype list, even if we don't want a session key with
847 DES3? */
848 ret = _kdc_encode_reply(r->context, r->config, r, b->nonce,
849 serverkey->keytype, kvno,
850 serverkey, 0, r->rk_is_subkey,
851 e_text, reply);
852 if (is_weak)
853 krb5_enctype_disable(r->context, serverkey->keytype);
855 _log_astgs_req(r, serverkey->keytype);
857 out:
858 return ret;
861 static krb5_error_code
862 tgs_check_authenticator(krb5_context context,
863 krb5_kdc_configuration *config,
864 krb5_auth_context ac,
865 KDC_REQ_BODY *b,
866 const char **e_text,
867 krb5_keyblock *key)
869 krb5_authenticator auth;
870 krb5_error_code ret;
871 krb5_crypto crypto;
873 krb5_auth_con_getauthenticator(context, ac, &auth);
874 if(auth->cksum == NULL){
875 kdc_log(context, config, 4, "No authenticator in request");
876 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
877 goto out;
880 if (!krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
881 kdc_log(context, config, 4, "Bad checksum type in authenticator: %d",
882 auth->cksum->cksumtype);
883 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
884 goto out;
887 ret = krb5_crypto_init(context, key, 0, &crypto);
888 if (ret) {
889 const char *msg = krb5_get_error_message(context, ret);
890 kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
891 krb5_free_error_message(context, msg);
892 goto out;
896 * RFC4120 says the checksum must be collision-proof, but it does
897 * not require it to be keyed (as the authenticator is encrypted).
899 _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
900 ret = _kdc_verify_checksum(context,
901 crypto,
902 KRB5_KU_TGS_REQ_AUTH_CKSUM,
903 &b->_save,
904 auth->cksum);
905 krb5_crypto_destroy(context, crypto);
906 if(ret){
907 const char *msg = krb5_get_error_message(context, ret);
908 kdc_log(context, config, 4,
909 "Failed to verify authenticator checksum: %s", msg);
910 krb5_free_error_message(context, msg);
912 out:
913 free_Authenticator(auth);
914 free(auth);
915 return ret;
918 static krb5_boolean
919 need_referral(krb5_context context, krb5_kdc_configuration *config,
920 const KDCOptions * const options, krb5_principal server,
921 krb5_realm **realms)
923 const char *name;
925 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
926 return FALSE;
928 if (server->name.name_string.len == 1)
929 name = server->name.name_string.val[0];
930 else if (server->name.name_string.len == 3) {
932 This is used to give referrals for the
933 E3514235-4B06-11D1-AB04-00C04FC2DCD2/NTDSGUID/DNSDOMAIN
934 SPN form, which is used for inter-domain communication in AD
936 name = server->name.name_string.val[2];
937 kdc_log(context, config, 4, "Giving 3 part referral for %s", name);
938 *realms = malloc(sizeof(char *)*2);
939 if (*realms == NULL) {
940 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
941 return FALSE;
943 (*realms)[0] = strdup(name);
944 (*realms)[1] = NULL;
945 return TRUE;
946 } else if (server->name.name_string.len > 1)
947 name = server->name.name_string.val[1];
948 else
949 return FALSE;
951 kdc_log(context, config, 5, "Searching referral for %s", name);
953 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
956 static krb5_error_code
957 validate_fast_ad(astgs_request_t r, krb5_authdata *auth_data)
959 krb5_error_code ret;
960 krb5_data data;
962 krb5_data_zero(&data);
964 ret = _krb5_get_ad(r->context, auth_data, NULL,
965 KRB5_AUTHDATA_FX_FAST_USED, &data);
966 if (ret == 0) {
967 r->fast_asserted = 1;
968 krb5_data_free(&data);
971 ret = _krb5_get_ad(r->context, auth_data, NULL,
972 KRB5_AUTHDATA_FX_FAST_ARMOR, &data);
973 if (ret == 0) {
974 kdc_log(r->context, r->config, 2,
975 "Invalid ticket usage: TGS-REQ contains AD-fx-fast-armor");
976 krb5_data_free(&data);
977 return KRB5KRB_AP_ERR_BAD_INTEGRITY;
980 return 0;
983 static krb5_error_code
984 tgs_parse_request(astgs_request_t r,
985 const PA_DATA *tgs_req,
986 hdb_entry_ex **krbtgt,
987 krb5_enctype *krbtgt_etype,
988 krb5_ticket **ticket,
989 const char **e_text,
990 const char *from,
991 const struct sockaddr *from_addr,
992 time_t **csec,
993 int **cusec,
994 AuthorizationData **auth_data)
996 krb5_kdc_configuration *config = r->config;
997 KDC_REQ_BODY *b = &r->req.req_body;
998 static char failed[] = "<unparse_name failed>";
999 krb5_ap_req ap_req;
1000 krb5_error_code ret;
1001 krb5_principal princ;
1002 krb5_auth_context ac = NULL;
1003 krb5_flags ap_req_options;
1004 krb5_flags verify_ap_req_flags = 0;
1005 krb5_crypto crypto;
1006 krb5uint32 krbtgt_kvno; /* kvno used for the PA-TGS-REQ AP-REQ Ticket */
1007 krb5uint32 krbtgt_kvno_try;
1008 int kvno_search_tries = 4; /* number of kvnos to try when tkt_vno == 0 */
1009 const Keys *krbtgt_keys;/* keyset for TGT tkt_vno */
1010 Key *tkey;
1011 krb5_keyblock *subkey = NULL;
1012 unsigned usage;
1014 *auth_data = NULL;
1015 *csec = NULL;
1016 *cusec = NULL;
1018 memset(&ap_req, 0, sizeof(ap_req));
1019 ret = krb5_decode_ap_req(r->context, &tgs_req->padata_value, &ap_req);
1020 if(ret){
1021 const char *msg = krb5_get_error_message(r->context, ret);
1022 kdc_log(r->context, config, 4, "Failed to decode AP-REQ: %s", msg);
1023 krb5_free_error_message(r->context, msg);
1024 goto out;
1027 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1028 /* XXX check for ticket.sname == req.sname */
1029 kdc_log(r->context, config, 4, "PA-DATA is not a ticket-granting ticket");
1030 ret = KRB5KDC_ERR_POLICY; /* ? */
1031 goto out;
1034 _krb5_principalname2krb5_principal(r->context,
1035 &princ,
1036 ap_req.ticket.sname,
1037 ap_req.ticket.realm);
1039 krbtgt_kvno = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0;
1040 ret = _kdc_db_fetch(r->context, config, princ, HDB_F_GET_KRBTGT,
1041 &krbtgt_kvno, NULL, krbtgt);
1043 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1044 /* XXX Factor out this unparsing of the same princ all over */
1045 char *p;
1046 ret = krb5_unparse_name(r->context, princ, &p);
1047 if (ret != 0)
1048 p = failed;
1049 krb5_free_principal(r->context, princ);
1050 kdc_log(r->context, config, 5,
1051 "Ticket-granting ticket account %s does not have secrets at "
1052 "this KDC, need to proxy", p);
1053 if (ret == 0)
1054 free(p);
1055 ret = HDB_ERR_NOT_FOUND_HERE;
1056 goto out;
1057 } else if (ret == HDB_ERR_KVNO_NOT_FOUND) {
1058 char *p;
1059 ret = krb5_unparse_name(r->context, princ, &p);
1060 if (ret != 0)
1061 p = failed;
1062 krb5_free_principal(r->context, princ);
1063 kdc_log(r->context, config, 5,
1064 "Ticket-granting ticket account %s does not have keys for "
1065 "kvno %d at this KDC", p, krbtgt_kvno);
1066 if (ret == 0)
1067 free(p);
1068 ret = HDB_ERR_KVNO_NOT_FOUND;
1069 goto out;
1070 } else if (ret == HDB_ERR_NO_MKEY) {
1071 char *p;
1072 ret = krb5_unparse_name(r->context, princ, &p);
1073 if (ret != 0)
1074 p = failed;
1075 krb5_free_principal(r->context, princ);
1076 kdc_log(r->context, config, 5,
1077 "Missing master key for decrypting keys for ticket-granting "
1078 "ticket account %s with kvno %d at this KDC", p, krbtgt_kvno);
1079 if (ret == 0)
1080 free(p);
1081 ret = HDB_ERR_KVNO_NOT_FOUND;
1082 goto out;
1083 } else if (ret) {
1084 const char *msg = krb5_get_error_message(r->context, ret);
1085 char *p;
1086 ret = krb5_unparse_name(r->context, princ, &p);
1087 if (ret != 0)
1088 p = failed;
1089 kdc_log(r->context, config, 4,
1090 "Ticket-granting ticket %s not found in database: %s", p, msg);
1091 krb5_free_principal(r->context, princ);
1092 krb5_free_error_message(r->context, msg);
1093 if (ret == 0)
1094 free(p);
1095 ret = KRB5KRB_AP_ERR_NOT_US;
1096 goto out;
1099 krbtgt_kvno_try = krbtgt_kvno ? krbtgt_kvno : (*krbtgt)->entry.kvno;
1100 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1102 next_kvno:
1103 krbtgt_keys = hdb_kvno2keys(r->context, &(*krbtgt)->entry, krbtgt_kvno_try);
1104 ret = hdb_enctype2key(r->context, &(*krbtgt)->entry, krbtgt_keys,
1105 ap_req.ticket.enc_part.etype, &tkey);
1106 if (ret && krbtgt_kvno == 0 && kvno_search_tries > 0) {
1107 kvno_search_tries--;
1108 krbtgt_kvno_try--;
1109 goto next_kvno;
1110 } else if (ret) {
1111 char *str = NULL, *p = NULL;
1113 krb5_enctype_to_string(r->context, ap_req.ticket.enc_part.etype, &str);
1114 krb5_unparse_name(r->context, princ, &p);
1115 kdc_log(r->context, config, 4,
1116 "No server key with enctype %s found for %s",
1117 str ? str : "<unknown enctype>",
1118 p ? p : "<unparse_name failed>");
1119 free(str);
1120 free(p);
1121 ret = KRB5KRB_AP_ERR_BADKEYVER;
1122 goto out;
1125 if (b->kdc_options.validate)
1126 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1128 if (r->config->warn_ticket_addresses)
1129 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_ADDRS;
1131 ret = krb5_verify_ap_req2(r->context,
1132 &ac,
1133 &ap_req,
1134 princ,
1135 &tkey->key,
1136 verify_ap_req_flags,
1137 &ap_req_options,
1138 ticket,
1139 KRB5_KU_TGS_REQ_AUTH);
1140 if (ticket && *ticket && (*ticket)->ticket.caddr)
1141 _kdc_audit_addaddrs((kdc_request_t)r, (*ticket)->ticket.caddr, "tixaddrs");
1142 if (r->config->warn_ticket_addresses && ret == KRB5KRB_AP_ERR_BADADDR &&
1143 *ticket != NULL) {
1144 _kdc_audit_addkv((kdc_request_t)r, 0, "wrongaddr", "yes");
1145 ret = 0;
1147 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY && kvno_search_tries > 0) {
1148 kvno_search_tries--;
1149 krbtgt_kvno_try--;
1150 goto next_kvno;
1153 krb5_free_principal(r->context, princ);
1154 if(ret) {
1155 const char *msg = krb5_get_error_message(r->context, ret);
1156 kdc_log(r->context, config, 4, "Failed to verify AP-REQ: %s", msg);
1157 krb5_free_error_message(r->context, msg);
1158 goto out;
1161 r->ticket_key = tkey;
1164 krb5_authenticator auth;
1166 ret = krb5_auth_con_getauthenticator(r->context, ac, &auth);
1167 if (ret == 0) {
1168 *csec = malloc(sizeof(**csec));
1169 if (*csec == NULL) {
1170 krb5_free_authenticator(r->context, &auth);
1171 kdc_log(r->context, config, 4, "malloc failed");
1172 goto out;
1174 **csec = auth->ctime;
1175 *cusec = malloc(sizeof(**cusec));
1176 if (*cusec == NULL) {
1177 krb5_free_authenticator(r->context, &auth);
1178 kdc_log(r->context, config, 4, "malloc failed");
1179 goto out;
1181 **cusec = auth->cusec;
1183 ret = validate_fast_ad(r, auth->authorization_data);
1184 krb5_free_authenticator(r->context, &auth);
1185 if (ret)
1186 goto out;
1190 ret = tgs_check_authenticator(r->context, config,
1191 ac, b, e_text, &(*ticket)->ticket.key);
1192 if (ret) {
1193 krb5_auth_con_free(r->context, ac);
1194 goto out;
1197 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1198 r->rk_is_subkey = 1;
1200 ret = krb5_auth_con_getremotesubkey(r->context, ac, &subkey);
1201 if(ret){
1202 const char *msg = krb5_get_error_message(r->context, ret);
1203 krb5_auth_con_free(r->context, ac);
1204 kdc_log(r->context, config, 4, "Failed to get remote subkey: %s", msg);
1205 krb5_free_error_message(r->context, msg);
1206 goto out;
1208 if(subkey == NULL){
1209 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1210 r->rk_is_subkey = 0;
1212 ret = krb5_auth_con_getkey(r->context, ac, &subkey);
1213 if(ret) {
1214 const char *msg = krb5_get_error_message(r->context, ret);
1215 krb5_auth_con_free(r->context, ac);
1216 kdc_log(r->context, config, 4, "Failed to get session key: %s", msg);
1217 krb5_free_error_message(r->context, msg);
1218 goto out;
1221 if(subkey == NULL){
1222 krb5_auth_con_free(r->context, ac);
1223 kdc_log(r->context, config, 4,
1224 "Failed to get key for enc-authorization-data");
1225 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1226 goto out;
1229 krb5_free_keyblock_contents(r->context, &r->reply_key);
1230 ret = krb5_copy_keyblock_contents(r->context, subkey, &r->reply_key);
1231 krb5_free_keyblock(r->context, subkey);
1232 if (ret)
1233 goto out;
1235 if (b->enc_authorization_data) {
1236 krb5_data ad;
1238 ret = krb5_crypto_init(r->context, &r->reply_key, 0, &crypto);
1239 if (ret) {
1240 const char *msg = krb5_get_error_message(r->context, ret);
1241 krb5_auth_con_free(r->context, ac);
1242 kdc_log(r->context, config, 4, "krb5_crypto_init failed: %s", msg);
1243 krb5_free_error_message(r->context, msg);
1244 goto out;
1246 ret = krb5_decrypt_EncryptedData (r->context,
1247 crypto,
1248 usage,
1249 b->enc_authorization_data,
1250 &ad);
1251 krb5_crypto_destroy(r->context, crypto);
1252 if(ret){
1253 krb5_auth_con_free(r->context, ac);
1254 kdc_log(r->context, config, 4,
1255 "Failed to decrypt enc-authorization-data");
1256 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1257 goto out;
1259 ALLOC(*auth_data);
1260 if (*auth_data == NULL) {
1261 krb5_auth_con_free(r->context, ac);
1262 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1263 goto out;
1265 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1266 if(ret){
1267 krb5_auth_con_free(r->context, ac);
1268 free(*auth_data);
1269 *auth_data = NULL;
1270 kdc_log(r->context, config, 4, "Failed to decode authorization data");
1271 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1272 goto out;
1276 ret = validate_fast_ad(r, (*ticket)->ticket.authorization_data);
1277 if (ret)
1278 goto out;
1282 * Check for FAST request
1285 ret = _kdc_fast_unwrap_request(r, *ticket, ac);
1286 if (ret)
1287 goto out;
1289 krb5_auth_con_free(r->context, ac);
1291 out:
1292 free_AP_REQ(&ap_req);
1294 return ret;
1297 static krb5_error_code
1298 build_server_referral(krb5_context context,
1299 krb5_kdc_configuration *config,
1300 krb5_crypto session,
1301 krb5_const_realm referred_realm,
1302 const PrincipalName *true_principal_name,
1303 const PrincipalName *requested_principal,
1304 krb5_data *outdata)
1306 PA_ServerReferralData ref;
1307 krb5_error_code ret;
1308 EncryptedData ed;
1309 krb5_data data;
1310 size_t size = 0;
1312 memset(&ref, 0, sizeof(ref));
1314 if (referred_realm) {
1315 ALLOC(ref.referred_realm);
1316 if (ref.referred_realm == NULL)
1317 goto eout;
1318 *ref.referred_realm = strdup(referred_realm);
1319 if (*ref.referred_realm == NULL)
1320 goto eout;
1322 if (true_principal_name) {
1323 ALLOC(ref.true_principal_name);
1324 if (ref.true_principal_name == NULL)
1325 goto eout;
1326 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1327 if (ret)
1328 goto eout;
1330 if (requested_principal) {
1331 ALLOC(ref.requested_principal_name);
1332 if (ref.requested_principal_name == NULL)
1333 goto eout;
1334 ret = copy_PrincipalName(requested_principal,
1335 ref.requested_principal_name);
1336 if (ret)
1337 goto eout;
1340 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1341 data.data, data.length,
1342 &ref, &size, ret);
1343 free_PA_ServerReferralData(&ref);
1344 if (ret)
1345 return ret;
1346 if (data.length != size)
1347 krb5_abortx(context, "internal asn.1 encoder error");
1349 ret = krb5_encrypt_EncryptedData(context, session,
1350 KRB5_KU_PA_SERVER_REFERRAL,
1351 data.data, data.length,
1352 0 /* kvno */, &ed);
1353 free(data.data);
1354 if (ret)
1355 return ret;
1357 ASN1_MALLOC_ENCODE(EncryptedData,
1358 outdata->data, outdata->length,
1359 &ed, &size, ret);
1360 free_EncryptedData(&ed);
1361 if (ret)
1362 return ret;
1363 if (outdata->length != size)
1364 krb5_abortx(context, "internal asn.1 encoder error");
1366 return 0;
1367 eout:
1368 free_PA_ServerReferralData(&ref);
1369 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1370 return ENOMEM;
1374 * This function is intended to be used when failure to find the client is
1375 * acceptable.
1377 krb5_error_code
1378 _kdc_db_fetch_client(krb5_context context,
1379 krb5_kdc_configuration *config,
1380 int flags,
1381 krb5_principal cp,
1382 const char *cpn,
1383 const char *krbtgt_realm,
1384 HDB **clientdb,
1385 hdb_entry_ex **client_out)
1387 krb5_error_code ret;
1388 hdb_entry_ex *client = NULL;
1390 *client_out = NULL;
1392 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1393 NULL, clientdb, &client);
1394 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1396 * This is OK, we are just trying to find out if they have
1397 * been disabled or deleted in the meantime; missing secrets
1398 * are OK.
1400 } else if (ret) {
1402 * If the client belongs to the same realm as our TGS, it
1403 * should exist in the local database.
1405 const char *msg;
1407 if (strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1408 if (ret == HDB_ERR_NOENTRY)
1409 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1410 kdc_log(context, config, 4, "Client no longer in database: %s", cpn);
1411 return ret;
1414 msg = krb5_get_error_message(context, ret);
1415 kdc_log(context, config, 4, "Client not found in database: %s", msg);
1416 krb5_free_error_message(context, msg);
1417 } else if (client->entry.flags.invalid || !client->entry.flags.client) {
1418 kdc_log(context, config, 4, "Client has invalid bit set");
1419 _kdc_free_ent(context, client);
1420 return KRB5KDC_ERR_POLICY;
1423 *client_out = client;
1425 return 0;
1428 static krb5_error_code
1429 tgs_build_reply(astgs_request_t priv,
1430 hdb_entry_ex *krbtgt,
1431 krb5_enctype krbtgt_etype,
1432 krb5_ticket *ticket,
1433 const char **e_text,
1434 AuthorizationData **auth_data,
1435 const struct sockaddr *from_addr)
1437 krb5_context context = priv->context;
1438 krb5_kdc_configuration *config = priv->config;
1439 KDC_REQ *req = &priv->req;
1440 KDC_REQ_BODY *b = &priv->req.req_body;
1441 const char *from = priv->from;
1442 krb5_error_code ret, ret2;
1443 krb5_principal cp = NULL, sp = NULL, rsp = NULL, tp = NULL, dp = NULL;
1444 krb5_principal krbtgt_out_principal = NULL;
1445 krb5_principal user2user_princ = NULL;
1446 char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL, *krbtgt_out_n = NULL;
1447 char *user2user_name = NULL;
1448 hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
1449 hdb_entry_ex *user2user_krbtgt = NULL;
1450 HDB *clientdb, *s4u2self_impersonated_clientdb;
1451 HDB *serverdb = NULL;
1452 krb5_realm ref_realm = NULL;
1453 EncTicketPart *tgt = &ticket->ticket;
1454 const EncryptionKey *ekey;
1455 krb5_keyblock sessionkey;
1456 krb5_kvno kvno;
1457 krb5_pac user2user_pac = NULL;
1458 uint16_t rodc_id;
1459 krb5_boolean add_ticket_sig = FALSE;
1460 const char *tgt_realm = /* Realm of TGT issuer */
1461 krb5_principal_get_realm(context, krbtgt->entry.principal);
1462 const char *our_realm = /* Realm of this KDC */
1463 krb5_principal_get_comp_string(context, krbtgt->entry.principal, 1);
1464 char **capath = NULL;
1465 size_t num_capath = 0;
1467 hdb_entry_ex *krbtgt_out = NULL;
1469 PrincipalName *s;
1470 Realm r;
1471 EncTicketPart adtkt;
1472 char opt_str[128];
1473 krb5_boolean kdc_issued = FALSE;
1475 Key *tkey_sign;
1476 int flags = HDB_F_FOR_TGS_REQ;
1478 int result;
1480 memset(&sessionkey, 0, sizeof(sessionkey));
1481 memset(&adtkt, 0, sizeof(adtkt));
1483 s = b->sname;
1484 r = b->realm;
1487 * The canonicalize KDC option is passed as a hint to the backend, but
1488 * can typically be ignored. Per RFC 6806, names are not canonicalized
1489 * in response to a TGS request (although we make an exception, see
1490 * force-canonicalize below).
1492 if (b->kdc_options.canonicalize)
1493 flags |= HDB_F_CANON;
1495 if (s == NULL) {
1496 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1497 _kdc_set_e_text(priv, "No server in request");
1498 goto out;
1501 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1502 ret = krb5_unparse_name(context, sp, &priv->sname);
1503 if (ret)
1504 goto out;
1505 spn = priv->sname;
1506 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1507 ret = krb5_unparse_name(context, cp, &priv->cname);
1508 if (ret)
1509 goto out;
1510 cpn = priv->cname;
1511 result = unparse_flags(KDCOptions2int(b->kdc_options),
1512 asn1_KDCOptions_units(),
1513 opt_str, sizeof(opt_str));
1514 if (result > 0)
1515 kdc_log(context, config, 4,
1516 "TGS-REQ %s from %s for %s [%s]",
1517 cpn, from, spn, opt_str);
1518 else
1519 kdc_log(context, config, 4,
1520 "TGS-REQ %s from %s for %s", cpn, from, spn);
1523 * Fetch server
1526 server_lookup:
1527 priv->server = NULL;
1528 if (server)
1529 _kdc_free_ent(context, server);
1530 server = NULL;
1531 ret = _kdc_db_fetch(context, config, sp,
1532 HDB_F_GET_SERVER | HDB_F_DELAY_NEW_KEYS | flags,
1533 NULL, &serverdb, &server);
1534 priv->server = server;
1535 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1536 kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", spn);
1537 _kdc_audit_addreason((kdc_request_t)priv, "Target not found here");
1538 goto out;
1539 } else if (ret == HDB_ERR_WRONG_REALM) {
1540 free(ref_realm);
1541 ref_realm = strdup(server->entry.principal->realm);
1542 if (ref_realm == NULL) {
1543 ret = krb5_enomem(context);
1544 goto out;
1547 kdc_log(context, config, 4,
1548 "Returning a referral to realm %s for "
1549 "server %s.",
1550 ref_realm, spn);
1551 krb5_free_principal(context, sp);
1552 sp = NULL;
1553 ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1554 ref_realm, NULL);
1555 if (ret)
1556 goto out;
1557 free(priv->sname);
1558 priv->sname = NULL;
1559 ret = krb5_unparse_name(context, sp, &priv->sname);
1560 if (ret)
1561 goto out;
1562 spn = priv->sname;
1564 goto server_lookup;
1565 } else if (ret) {
1566 const char *new_rlm, *msg;
1567 Realm req_rlm;
1568 krb5_realm *realms;
1570 if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1571 if (capath == NULL) {
1572 /* With referalls, hierarchical capaths are always enabled */
1573 ret2 = _krb5_find_capath(context, tgt->crealm, our_realm,
1574 req_rlm, TRUE, &capath, &num_capath);
1575 if (ret2) {
1576 ret = ret2;
1577 _kdc_audit_addreason((kdc_request_t)priv,
1578 "No trusted path from client realm to ours");
1579 goto out;
1582 new_rlm = num_capath > 0 ? capath[--num_capath] : NULL;
1583 if (new_rlm) {
1584 kdc_log(context, config, 5, "krbtgt from %s via %s for "
1585 "realm %s not found, trying %s", tgt->crealm,
1586 our_realm, req_rlm, new_rlm);
1588 free(ref_realm);
1589 ref_realm = strdup(new_rlm);
1590 if (ref_realm == NULL) {
1591 ret = krb5_enomem(context);
1592 goto out;
1595 krb5_free_principal(context, sp);
1596 sp = NULL;
1597 krb5_make_principal(context, &sp, r,
1598 KRB5_TGS_NAME, ref_realm, NULL);
1599 free(priv->sname);
1600 priv->sname = NULL;
1601 ret = krb5_unparse_name(context, sp, &priv->sname);
1602 if (ret)
1603 goto out;
1604 spn = priv->sname;
1605 goto server_lookup;
1607 } else if (need_referral(context, config, &b->kdc_options, sp, &realms)) {
1608 if (strcmp(realms[0], sp->realm) != 0) {
1609 kdc_log(context, config, 4,
1610 "Returning a referral to realm %s for "
1611 "server %s that was not found",
1612 realms[0], spn);
1613 krb5_free_principal(context, sp);
1614 sp = NULL;
1615 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1616 realms[0], NULL);
1617 free(priv->sname);
1618 priv->sname = NULL;
1619 ret = krb5_unparse_name(context, sp, &priv->sname);
1620 if (ret) {
1621 krb5_free_host_realm(context, realms);
1622 goto out;
1624 spn = priv->sname;
1626 free(ref_realm);
1627 ref_realm = strdup(realms[0]);
1629 krb5_free_host_realm(context, realms);
1630 goto server_lookup;
1632 krb5_free_host_realm(context, realms);
1634 msg = krb5_get_error_message(context, ret);
1635 kdc_log(context, config, 3,
1636 "Server not found in database: %s: %s", spn, msg);
1637 krb5_free_error_message(context, msg);
1638 if (ret == HDB_ERR_NOENTRY)
1639 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1640 _kdc_audit_addreason((kdc_request_t)priv,
1641 "Service principal unknown");
1642 goto out;
1646 * RFC 6806 notes that names MUST NOT be changed in the response to
1647 * a TGS request. Hence we ignore the setting of the canonicalize
1648 * KDC option. However, for legacy interoperability we do allow the
1649 * backend to override this by setting the force-canonicalize HDB
1650 * flag in the server entry.
1652 if (server->entry.flags.force_canonicalize)
1653 rsp = server->entry.principal;
1654 else
1655 rsp = sp;
1658 * Now refetch the primary krbtgt, and get the current kvno (the
1659 * sign check may have been on an old kvno, and the server may
1660 * have been an incoming trust)
1663 ret = krb5_make_principal(context,
1664 &krbtgt_out_principal,
1665 our_realm,
1666 KRB5_TGS_NAME,
1667 our_realm,
1668 NULL);
1669 if (ret) {
1670 kdc_log(context, config, 4,
1671 "Failed to make krbtgt principal name object for "
1672 "authz-data signatures");
1673 goto out;
1675 ret = krb5_unparse_name(context, krbtgt_out_principal, &krbtgt_out_n);
1676 if (ret) {
1677 kdc_log(context, config, 4,
1678 "Failed to make krbtgt principal name object for "
1679 "authz-data signatures");
1680 goto out;
1683 ret = _kdc_db_fetch(context, config, krbtgt_out_principal,
1684 HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
1685 if (ret) {
1686 char *ktpn = NULL;
1687 ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn);
1688 kdc_log(context, config, 4,
1689 "No such principal %s (needed for authz-data signature keys) "
1690 "while processing TGS-REQ for service %s with krbtg %s",
1691 krbtgt_out_n, spn, (ret == 0) ? ktpn : "<unknown>");
1692 free(ktpn);
1693 ret = KRB5KRB_AP_ERR_NOT_US;
1694 goto out;
1698 * Select enctype, return key and kvno.
1702 krb5_enctype etype;
1704 if(b->kdc_options.enc_tkt_in_skey) {
1705 Ticket *t;
1706 krb5_principal p;
1707 Key *uukey;
1708 krb5uint32 second_kvno = 0;
1709 krb5uint32 *kvno_ptr = NULL;
1710 size_t i;
1711 hdb_entry_ex *user2user_client = NULL;
1712 krb5_boolean user2user_kdc_issued = FALSE;
1714 if(b->additional_tickets == NULL ||
1715 b->additional_tickets->len == 0){
1716 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1717 kdc_log(context, config, 4,
1718 "No second ticket present in user-to-user request");
1719 _kdc_audit_addreason((kdc_request_t)priv,
1720 "No second ticket present in user-to-user request");
1721 goto out;
1723 t = &b->additional_tickets->val[0];
1724 if(!get_krbtgt_realm(&t->sname)){
1725 kdc_log(context, config, 4,
1726 "Additional ticket is not a ticket-granting ticket");
1727 _kdc_audit_addreason((kdc_request_t)priv,
1728 "Additional ticket is not a ticket-granting ticket");
1729 ret = KRB5KDC_ERR_POLICY;
1730 goto out;
1732 ret = _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1733 if (ret)
1734 goto out;
1736 ret = krb5_unparse_name(context, p, &tpn);
1737 if (ret)
1738 goto out;
1739 if(t->enc_part.kvno){
1740 second_kvno = *t->enc_part.kvno;
1741 kvno_ptr = &second_kvno;
1743 ret = _kdc_db_fetch(context, config, p,
1744 HDB_F_GET_KRBTGT, kvno_ptr,
1745 NULL, &user2user_krbtgt);
1746 krb5_free_principal(context, p);
1747 if(ret){
1748 if (ret == HDB_ERR_NOENTRY)
1749 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1750 _kdc_audit_addreason((kdc_request_t)priv,
1751 "User-to-user service principal (TGS) unknown");
1752 goto out;
1754 ret = hdb_enctype2key(context, &user2user_krbtgt->entry, NULL,
1755 t->enc_part.etype, &uukey);
1756 if(ret){
1757 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1758 _kdc_audit_addreason((kdc_request_t)priv,
1759 "User-to-user enctype not supported");
1760 goto out;
1762 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1763 if(ret) {
1764 _kdc_audit_addreason((kdc_request_t)priv,
1765 "User-to-user TGT decrypt failure");
1766 goto out;
1769 ret = _kdc_verify_flags(context, config, &adtkt, tpn);
1770 if (ret) {
1771 _kdc_audit_addreason((kdc_request_t)priv,
1772 "User-to-user TGT expired or invalid");
1773 goto out;
1776 /* Fetch the name from the TGT. */
1777 ret = _krb5_principalname2krb5_principal(context, &user2user_princ,
1778 adtkt.cname, adtkt.crealm);
1779 if (ret)
1780 goto out;
1782 ret = krb5_unparse_name(context, user2user_princ, &user2user_name);
1783 if (ret)
1784 goto out;
1787 * Look up the name given in the TGT in the database. The user
1788 * claims to have a ticket-granting-ticket to our KDC, so we should
1789 * fail hard if we can't find the user - otherwise we can't do
1790 * proper checks.
1792 ret = _kdc_db_fetch(context, config, user2user_princ,
1793 HDB_F_GET_CLIENT | flags,
1794 NULL, NULL, &user2user_client);
1795 if (ret == HDB_ERR_NOENTRY)
1796 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1797 if (ret)
1798 goto out;
1801 * The account is present in the database, now check the
1802 * account flags.
1804 * We check this as a client (because the purpose of
1805 * user2user is that the server flag is not set, because
1806 * the long-term key is not strong, but this does mean
1807 * that a client with an expired password can't get accept
1808 * a user2user ticket.
1810 ret = kdc_check_flags(priv,
1811 FALSE,
1812 user2user_client,
1813 NULL);
1814 if (ret) {
1815 _kdc_free_ent(context, user2user_client);
1816 goto out;
1820 * Also check that the account is the same one specified in the
1821 * request.
1823 ret = check_client_matches_target_service(context,
1824 config,
1825 serverdb,
1826 server,
1827 user2user_client,
1828 user2user_princ);
1829 if (ret) {
1830 _kdc_free_ent(context, user2user_client);
1831 goto out;
1834 /* Verify the PAC of the TGT. */
1835 ret = _kdc_check_pac(context, config, user2user_princ, NULL,
1836 user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt,
1837 &uukey->key, &priv->ticket_key->key, &adtkt,
1838 &user2user_kdc_issued, &user2user_pac, NULL, NULL);
1839 _kdc_free_ent(context, user2user_client);
1840 if (ret) {
1841 const char *msg = krb5_get_error_message(context, ret);
1842 kdc_log(context, config, 0,
1843 "Verify PAC failed for %s (%s) from %s with %s",
1844 spn, user2user_name, from, msg);
1845 krb5_free_error_message(context, msg);
1846 goto out;
1849 if ((config->require_pac && !user2user_pac)
1850 || (user2user_pac && !user2user_kdc_issued))
1852 ret = KRB5KDC_ERR_BADOPTION;
1853 kdc_log(context, config, 0,
1854 "Ticket not signed with PAC; user-to-user failed (%s).",
1855 user2user_pac ? "Ticket unsigned" : "No PAC");
1856 goto out;
1859 ekey = &adtkt.key;
1860 for(i = 0; i < b->etype.len; i++)
1861 if (b->etype.val[i] == adtkt.key.keytype)
1862 break;
1863 if(i == b->etype.len) {
1864 kdc_log(context, config, 4,
1865 "Addition ticket have not matching etypes");
1866 krb5_clear_error_message(context);
1867 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1868 _kdc_audit_addreason((kdc_request_t)priv,
1869 "No matching enctypes for 2nd ticket");
1870 goto out;
1872 etype = b->etype.val[i];
1873 kvno = 0;
1874 } else {
1875 Key *skey;
1877 ret = _kdc_find_etype(priv, krb5_principal_is_krbtgt(context, sp)
1878 ? KFE_IS_TGS : 0,
1879 b->etype.val, b->etype.len, &etype, NULL,
1880 NULL);
1881 if(ret) {
1882 kdc_log(context, config, 4,
1883 "Server (%s) has no support for etypes", spn);
1884 _kdc_audit_addreason((kdc_request_t)priv,
1885 "Enctype not supported");
1886 goto out;
1888 ret = _kdc_get_preferred_key(context, config, server, spn,
1889 NULL, &skey);
1890 if(ret) {
1891 kdc_log(context, config, 4,
1892 "Server (%s) has no supported etypes", spn);
1893 _kdc_audit_addreason((kdc_request_t)priv,
1894 "Enctype not supported");
1895 goto out;
1897 ekey = &skey->key;
1898 kvno = server->entry.kvno;
1901 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1902 if (ret)
1903 goto out;
1907 * Check that service is in the same realm as the krbtgt. If it's
1908 * not the same, it's someone that is using a uni-directional trust
1909 * backward.
1913 * The first realm is the realm of the service, the second is
1914 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1915 * encrypted to. The redirection via the krbtgt_out entry allows
1916 * the DB to possibly correct the case of the realm (Samba4 does
1917 * this) before the strcmp()
1919 if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
1920 krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
1921 char *ktpn;
1922 ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn);
1923 kdc_log(context, config, 4,
1924 "Request with wrong krbtgt: %s",
1925 (ret == 0) ? ktpn : "<unknown>");
1926 if(ret == 0)
1927 free(ktpn);
1928 ret = KRB5KRB_AP_ERR_NOT_US;
1929 _kdc_audit_addreason((kdc_request_t)priv, "Request with wrong TGT");
1930 goto out;
1933 ret = _kdc_get_preferred_key(context, config, krbtgt_out, krbtgt_out_n,
1934 NULL, &tkey_sign);
1935 if (ret) {
1936 kdc_log(context, config, 4,
1937 "Failed to find key for krbtgt PAC signature");
1938 _kdc_audit_addreason((kdc_request_t)priv,
1939 "Failed to find key for krbtgt PAC signature");
1940 goto out;
1942 ret = hdb_enctype2key(context, &krbtgt_out->entry, NULL,
1943 tkey_sign->key.keytype, &tkey_sign);
1944 if(ret) {
1945 kdc_log(context, config, 4,
1946 "Failed to find key for krbtgt PAC signature");
1947 _kdc_audit_addreason((kdc_request_t)priv,
1948 "Failed to find key for krbtgt PAC signature");
1949 goto out;
1952 if (_kdc_synthetic_princ_used_p(context, ticket))
1953 flags |= HDB_F_SYNTHETIC_OK;
1955 ret = _kdc_db_fetch_client(context, config, flags, cp, cpn, our_realm,
1956 &clientdb, &client);
1957 if (ret)
1958 goto out;
1959 flags &= ~HDB_F_SYNTHETIC_OK;
1960 priv->client = client;
1962 heim_assert(priv->client_princ == NULL, "client_princ should be NULL for TGS");
1964 ret = _kdc_check_pac(context, config, cp, NULL, client, server, krbtgt, krbtgt,
1965 &priv->ticket_key->key, &priv->ticket_key->key, tgt,
1966 &kdc_issued, &priv->pac, &priv->client_princ, &priv->pac_attributes);
1967 if (ret) {
1968 const char *msg = krb5_get_error_message(context, ret);
1969 _kdc_audit_addreason((kdc_request_t)priv, "PAC check failed");
1970 kdc_log(context, config, 4,
1971 "Verify PAC failed for %s (%s) from %s with %s",
1972 spn, cpn, from, msg);
1973 krb5_free_error_message(context, msg);
1974 goto out;
1978 * Process request
1981 /* by default the tgt principal matches the client principal */
1982 tp = cp;
1983 tpn = cpn;
1985 if (client) {
1986 const PA_DATA *sdata;
1987 int i = 0;
1989 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1990 if (sdata) {
1991 krb5_crypto crypto;
1992 krb5_data datack;
1993 PA_S4U2Self self;
1994 const char *str;
1996 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1997 sdata->padata_value.length,
1998 &self, NULL);
1999 if (ret) {
2000 _kdc_audit_addreason((kdc_request_t)priv,
2001 "Failed to decode PA-S4U2Self");
2002 kdc_log(context, config, 4, "Failed to decode PA-S4U2Self");
2003 goto out;
2006 if (!krb5_checksum_is_keyed(context, self.cksum.cksumtype)) {
2007 free_PA_S4U2Self(&self);
2008 _kdc_audit_addreason((kdc_request_t)priv,
2009 "PA-S4U2Self with unkeyed checksum");
2010 kdc_log(context, config, 4, "Reject PA-S4U2Self with unkeyed checksum");
2011 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
2012 goto out;
2015 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
2016 if (ret)
2017 goto out;
2019 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
2020 if (ret) {
2021 const char *msg = krb5_get_error_message(context, ret);
2022 free_PA_S4U2Self(&self);
2023 krb5_data_free(&datack);
2024 kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
2025 krb5_free_error_message(context, msg);
2026 goto out;
2029 /* Allow HMAC_MD5 checksum with any key type */
2030 if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
2031 struct krb5_crypto_iov iov;
2032 unsigned char csdata[16];
2033 Checksum cs;
2035 cs.checksum.length = sizeof(csdata);
2036 cs.checksum.data = &csdata;
2038 iov.data.data = datack.data;
2039 iov.data.length = datack.length;
2040 iov.flags = KRB5_CRYPTO_TYPE_DATA;
2042 ret = _krb5_HMAC_MD5_checksum(context, NULL, &crypto->key,
2043 KRB5_KU_OTHER_CKSUM, &iov, 1,
2044 &cs);
2045 if (ret == 0 &&
2046 krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0)
2047 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
2049 else {
2050 ret = _kdc_verify_checksum(context,
2051 crypto,
2052 KRB5_KU_OTHER_CKSUM,
2053 &datack,
2054 &self.cksum);
2056 krb5_data_free(&datack);
2057 krb5_crypto_destroy(context, crypto);
2058 if (ret) {
2059 const char *msg = krb5_get_error_message(context, ret);
2060 free_PA_S4U2Self(&self);
2061 _kdc_audit_addreason((kdc_request_t)priv,
2062 "S4U2Self checksum failed");
2063 kdc_log(context, config, 4,
2064 "krb5_verify_checksum failed for S4U2Self: %s", msg);
2065 krb5_free_error_message(context, msg);
2066 goto out;
2069 ret = _krb5_principalname2krb5_principal(context,
2070 &tp,
2071 self.name,
2072 self.realm);
2073 free_PA_S4U2Self(&self);
2074 if (ret)
2075 goto out;
2077 ret = krb5_unparse_name(context, tp, &tpn);
2078 if (ret)
2079 goto out;
2082 * Note no HDB_F_SYNTHETIC_OK -- impersonating non-existent clients
2083 * is probably not desirable!
2085 ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags,
2086 NULL, &s4u2self_impersonated_clientdb,
2087 &s4u2self_impersonated_client);
2088 if (ret) {
2089 const char *msg;
2092 * If the client belongs to the same realm as our krbtgt, it
2093 * should exist in the local database.
2097 if (ret == HDB_ERR_NOENTRY)
2098 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
2099 msg = krb5_get_error_message(context, ret);
2100 _kdc_audit_addreason((kdc_request_t)priv,
2101 "S4U2Self principal to impersonate not found");
2102 kdc_log(context, config, 2,
2103 "S4U2Self principal to impersonate %s not found in database: %s",
2104 tpn, msg);
2105 krb5_free_error_message(context, msg);
2106 goto out;
2109 /* Ignore require_pwchange and pw_end attributes (as Windows does),
2110 * since S4U2Self is not password authentication. */
2111 s4u2self_impersonated_client->entry.flags.require_pwchange = FALSE;
2112 free(s4u2self_impersonated_client->entry.pw_end);
2113 s4u2self_impersonated_client->entry.pw_end = NULL;
2115 ret = kdc_check_flags(priv, FALSE, s4u2self_impersonated_client, priv->server);
2116 if (ret)
2117 goto out; /* kdc_check_flags() calls _kdc_audit_addreason() */
2119 /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
2120 krb5_pac_free(context, priv->pac);
2121 priv->pac = NULL;
2123 ret = _kdc_pac_generate(context,
2124 s4u2self_impersonated_client,
2125 server,
2126 NULL,
2127 KRB5_PAC_WAS_GIVEN_IMPLICITLY,
2128 &priv->pac);
2129 if (ret) {
2130 kdc_log(context, config, 4, "PAC generation failed for -- %s", tpn);
2131 goto out;
2135 * Check that service doing the impersonating is
2136 * requesting a ticket to it-self.
2138 ret = check_client_matches_target_service(context,
2139 config,
2140 clientdb,
2141 client,
2142 server,
2143 sp);
2144 if (ret) {
2145 kdc_log(context, config, 4, "S4U2Self: %s is not allowed "
2146 "to impersonate to service "
2147 "(tried for user %s to service %s)",
2148 cpn, tpn, spn);
2149 goto out;
2153 * If the service isn't trusted for authentication to
2154 * delegation or if the impersonate client is disallowed
2155 * forwardable, remove the forwardable flag.
2158 if (client->entry.flags.trusted_for_delegation &&
2159 s4u2self_impersonated_client->entry.flags.forwardable) {
2160 str = "[forwardable]";
2161 } else {
2162 b->kdc_options.forwardable = 0;
2163 str = "";
2165 kdc_log(context, config, 4, "s4u2self %s impersonating %s to "
2166 "service %s %s", cpn, tpn, spn, str);
2171 * Constrained delegation
2174 if (client != NULL
2175 && b->additional_tickets != NULL
2176 && b->additional_tickets->len != 0
2177 && b->kdc_options.cname_in_addl_tkt
2178 && b->kdc_options.enc_tkt_in_skey == 0)
2180 hdb_entry_ex *adclient = NULL;
2181 krb5_boolean ad_kdc_issued = FALSE;
2182 Key *clientkey;
2183 Ticket *t;
2186 * We require that the service's krbtgt has a PAC.
2188 if (priv->pac == NULL) {
2189 ret = KRB5KDC_ERR_BADOPTION;
2190 _kdc_audit_addreason((kdc_request_t)priv, "Missing PAC");
2191 kdc_log(context, config, 4,
2192 "Constrained delegation without PAC, %s/%s",
2193 cpn, spn);
2194 goto out;
2197 krb5_pac_free(context, priv->pac);
2198 priv->pac = NULL;
2200 krb5_free_principal(context, priv->client_princ);
2201 priv->client_princ = NULL;
2203 t = &b->additional_tickets->val[0];
2205 ret = hdb_enctype2key(context, &client->entry,
2206 hdb_kvno2keys(context, &client->entry,
2207 t->enc_part.kvno ? * t->enc_part.kvno : 0),
2208 t->enc_part.etype, &clientkey);
2209 if(ret){
2210 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
2211 goto out;
2214 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
2215 if (ret) {
2216 _kdc_audit_addreason((kdc_request_t)priv,
2217 "Failed to decrypt constrained delegation ticket");
2218 kdc_log(context, config, 4,
2219 "failed to decrypt ticket for "
2220 "constrained delegation from %s to %s ", cpn, spn);
2221 goto out;
2224 ret = _krb5_principalname2krb5_principal(context,
2225 &tp,
2226 adtkt.cname,
2227 adtkt.crealm);
2228 if (ret)
2229 goto out;
2231 ret = krb5_unparse_name(context, tp, &tpn);
2232 if (ret)
2233 goto out;
2235 _kdc_audit_addkv((kdc_request_t)priv, 0, "impersonatee", "%s", tpn);
2237 ret = _krb5_principalname2krb5_principal(context,
2238 &dp,
2239 t->sname,
2240 t->realm);
2241 if (ret)
2242 goto out;
2244 ret = krb5_unparse_name(context, dp, &dpn);
2245 if (ret)
2246 goto out;
2248 /* check that ticket is valid */
2249 if (adtkt.flags.forwardable == 0) {
2250 _kdc_audit_addreason((kdc_request_t)priv,
2251 "Missing forwardable flag on ticket for constrained delegation");
2252 kdc_log(context, config, 4,
2253 "Missing forwardable flag on ticket for "
2254 "constrained delegation from %s (%s) as %s to %s ",
2255 cpn, dpn, tpn, spn);
2256 ret = KRB5KDC_ERR_BADOPTION;
2257 goto out;
2260 ret = check_constrained_delegation(context, config, clientdb,
2261 client, server, sp);
2262 if (ret) {
2263 _kdc_audit_addreason((kdc_request_t)priv,
2264 "Constrained delegation not allowed");
2265 kdc_log(context, config, 4,
2266 "constrained delegation from %s (%s) as %s to %s not allowed",
2267 cpn, dpn, tpn, spn);
2268 goto out;
2271 ret = _kdc_verify_flags(context, config, &adtkt, tpn);
2272 if (ret) {
2273 _kdc_audit_addreason((kdc_request_t)priv,
2274 "Constrained delegation ticket expired or invalid");
2275 goto out;
2278 /* Try lookup the delegated client in DB */
2279 ret = _kdc_db_fetch_client(context, config, flags, tp, tpn, our_realm,
2280 NULL, &adclient);
2281 if (ret)
2282 goto out;
2284 if (adclient != NULL) {
2285 ret = kdc_check_flags(priv, FALSE, adclient, priv->server);
2286 if (ret) {
2287 _kdc_free_ent(context, adclient);
2288 goto out;
2293 * TODO: pass in t->sname and t->realm and build
2294 * a S4U_DELEGATION_INFO blob to the PAC.
2296 ret = _kdc_check_pac(context, config, tp, dp, adclient, server, krbtgt, client,
2297 &clientkey->key, &priv->ticket_key->key, &adtkt,
2298 &ad_kdc_issued, &priv->pac, &priv->client_princ, &priv->pac_attributes);
2299 if (adclient)
2300 _kdc_free_ent(context, adclient);
2301 if (ret) {
2302 const char *msg = krb5_get_error_message(context, ret);
2303 _kdc_audit_addreason((kdc_request_t)priv,
2304 "Constrained delegation ticket PAC check failed");
2305 kdc_log(context, config, 4,
2306 "Verify delegated PAC failed to %s for client"
2307 "%s (%s) as %s from %s with %s",
2308 spn, cpn, dpn, tpn, from, msg);
2309 krb5_free_error_message(context, msg);
2310 goto out;
2313 if (priv->pac == NULL || !ad_kdc_issued) {
2314 ret = KRB5KDC_ERR_BADOPTION;
2315 kdc_log(context, config, 4,
2316 "Ticket not signed with PAC; service %s failed for "
2317 "for delegation to %s for client %s (%s) from %s; (%s).",
2318 spn, tpn, dpn, cpn, from, priv->pac ? "Ticket unsigned" : "No PAC");
2319 _kdc_audit_addreason((kdc_request_t)priv,
2320 "Constrained delegation ticket not signed");
2321 goto out;
2324 kdc_log(context, config, 4, "constrained delegation for %s "
2325 "from %s (%s) to %s", tpn, cpn, dpn, spn);
2329 * Check flags
2332 ret = kdc_check_flags(priv, FALSE, priv->client, priv->server);
2333 if(ret)
2334 goto out;
2336 if((b->kdc_options.validate || b->kdc_options.renew) &&
2337 !krb5_principal_compare(context,
2338 krbtgt->entry.principal,
2339 server->entry.principal)){
2340 _kdc_audit_addreason((kdc_request_t)priv, "Inconsistent request");
2341 kdc_log(context, config, 4, "Inconsistent request.");
2342 ret = KRB5KDC_ERR_SERVER_NOMATCH;
2343 goto out;
2346 /* check for valid set of addresses */
2347 if (!_kdc_check_addresses(priv, tgt->caddr, from_addr)) {
2348 if (config->check_ticket_addresses) {
2349 ret = KRB5KRB_AP_ERR_BADADDR;
2350 _kdc_audit_addkv((kdc_request_t)priv, 0, "wrongaddr", "yes");
2351 kdc_log(context, config, 4, "Request from wrong address");
2352 _kdc_audit_addreason((kdc_request_t)priv, "Request from wrong address");
2353 goto out;
2354 } else if (config->warn_ticket_addresses) {
2355 _kdc_audit_addkv((kdc_request_t)priv, 0, "wrongaddr", "yes");
2359 /* check local and per-principal anonymous ticket issuance policy */
2360 if (is_anon_tgs_request_p(b, tgt)) {
2361 ret = _kdc_check_anon_policy(priv);
2362 if (ret)
2363 goto out;
2367 * If this is an referral, add server referral data to the
2368 * auth_data reply .
2370 if (ref_realm) {
2371 PA_DATA pa;
2372 krb5_crypto crypto;
2374 kdc_log(context, config, 3,
2375 "Adding server referral to %s", ref_realm);
2377 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
2378 if (ret)
2379 goto out;
2381 ret = build_server_referral(context, config, crypto, ref_realm,
2382 NULL, s, &pa.padata_value);
2383 krb5_crypto_destroy(context, crypto);
2384 if (ret) {
2385 _kdc_audit_addreason((kdc_request_t)priv, "Referral build failed");
2386 kdc_log(context, config, 4,
2387 "Failed building server referral");
2388 goto out;
2390 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
2392 ret = add_METHOD_DATA(&priv->outpadata, &pa);
2393 krb5_data_free(&pa.padata_value);
2394 if (ret) {
2395 kdc_log(context, config, 4,
2396 "Add server referral METHOD-DATA failed");
2397 goto out;
2402 * Only add ticket signature if the requested server is not krbtgt, and
2403 * either the header server is krbtgt or, in the case of renewal/validation
2404 * if it was signed with PAC ticket signature and we verified it.
2405 * Currently Heimdal only allows renewal of krbtgt anyway but that might
2406 * change one day (see issue #763) so make sure to check for it.
2409 if (kdc_issued &&
2410 !krb5_principal_is_krbtgt(context, server->entry.principal)) {
2412 /* Validate armor TGT before potentially including device claims */
2413 if (priv->armor_ticket) {
2414 ret = _kdc_fast_check_armor_pac(priv);
2415 if (ret)
2416 goto out;
2419 add_ticket_sig = TRUE;
2423 * Active-Directory implementations use the high part of the kvno as the
2424 * read-only-dc identifier, we need to embed it in the PAC KDC signatures.
2427 rodc_id = krbtgt_out->entry.kvno >> 16;
2433 ret = tgs_make_reply(priv,
2435 tgt,
2436 ekey,
2437 &tkey_sign->key,
2438 &sessionkey,
2439 kvno,
2440 *auth_data,
2441 server,
2442 rsp,
2443 client,
2445 tgt_realm,
2446 rodc_id,
2447 add_ticket_sig);
2449 out:
2450 free(user2user_name);
2451 if (tpn != cpn)
2452 free(tpn);
2453 free(dpn);
2454 free(krbtgt_out_n);
2455 _krb5_free_capath(context, capath);
2457 krb5_free_keyblock_contents(context, &sessionkey);
2458 if(krbtgt_out)
2459 _kdc_free_ent(context, krbtgt_out);
2460 if(server)
2461 _kdc_free_ent(context, server);
2462 if(client)
2463 _kdc_free_ent(context, client);
2464 if(s4u2self_impersonated_client)
2465 _kdc_free_ent(context, s4u2self_impersonated_client);
2466 if(user2user_krbtgt)
2467 _kdc_free_ent(context, user2user_krbtgt);
2469 krb5_free_principal(context, user2user_princ);
2470 if (tp && tp != cp)
2471 krb5_free_principal(context, tp);
2472 krb5_free_principal(context, cp);
2473 krb5_free_principal(context, dp);
2474 krb5_free_principal(context, sp);
2475 krb5_free_principal(context, krbtgt_out_principal);
2476 free(ref_realm);
2478 free_EncTicketPart(&adtkt);
2480 krb5_pac_free(context, user2user_pac);
2482 return ret;
2489 krb5_error_code
2490 _kdc_tgs_rep(astgs_request_t r)
2492 krb5_kdc_configuration *config = r->config;
2493 KDC_REQ *req = &r->req;
2494 krb5_data *data = r->reply;
2495 const char *from = r->from;
2496 struct sockaddr *from_addr = r->addr;
2497 int datagram_reply = r->datagram_reply;
2498 AuthorizationData *auth_data = NULL;
2499 krb5_error_code ret;
2500 int i = 0;
2501 const PA_DATA *tgs_req, *pa;
2503 hdb_entry_ex *krbtgt = NULL;
2504 krb5_ticket *ticket = NULL;
2505 const char *e_text = NULL;
2506 krb5_enctype krbtgt_etype = ETYPE_NULL;
2508 time_t *csec = NULL;
2509 int *cusec = NULL;
2511 if(req->padata == NULL){
2512 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2513 kdc_log(r->context, config, 4,
2514 "TGS-REQ from %s without PA-DATA", from);
2515 goto out;
2518 i = 0;
2519 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST_ARMOR);
2520 if (pa) {
2521 kdc_log(r->context, r->config, 10, "Found TGS-REQ FAST armor inside TGS-REQ pa-data");
2522 ret = KRB5KRB_ERR_GENERIC;
2523 goto out;
2526 i = 0;
2527 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2528 if(tgs_req == NULL){
2529 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2531 kdc_log(r->context, config, 4,
2532 "TGS-REQ from %s without PA-TGS-REQ", from);
2533 goto out;
2535 ret = tgs_parse_request(r, tgs_req,
2536 &krbtgt,
2537 &krbtgt_etype,
2538 &ticket,
2539 &e_text,
2540 from, from_addr,
2541 &csec, &cusec,
2542 &auth_data);
2543 if (ret == HDB_ERR_NOT_FOUND_HERE) {
2544 /* kdc_log() is called in tgs_parse_request() */
2545 goto out;
2547 if (ret) {
2548 kdc_log(r->context, config, 4,
2549 "Failed parsing TGS-REQ from %s", from);
2550 goto out;
2553 ret = _kdc_fast_strengthen_reply_key(r);
2554 if (ret)
2555 goto out;
2557 ret = tgs_build_reply(r,
2558 krbtgt,
2559 krbtgt_etype,
2560 ticket,
2561 &e_text,
2562 &auth_data,
2563 from_addr);
2564 if (ret) {
2565 kdc_log(r->context, config, 4,
2566 "Failed building TGS-REP to %s", from);
2567 goto out;
2570 /* */
2571 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2572 krb5_data_free(data);
2573 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2574 e_text = "Reply packet too large";
2577 out:
2578 if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2579 METHOD_DATA error_method = { 0, NULL };
2581 kdc_log(r->context, config, 5, "tgs-req: sending error: %d to client", ret);
2582 ret = _kdc_fast_mk_error(r,
2583 &error_method,
2584 r->armor_crypto,
2585 &req->req_body,
2586 ret, r->e_text,
2587 ticket != NULL ? ticket->client : NULL,
2588 ticket != NULL ? ticket->server : NULL,
2589 csec, cusec,
2590 data);
2591 free_METHOD_DATA(&error_method);
2593 free(csec);
2594 free(cusec);
2596 r->rep.padata = NULL; /* may point to outpadata */
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 (r->outpadata.len)
2636 free_METHOD_DATA(&r->outpadata);
2638 if (auth_data) {
2639 free_AuthorizationData(auth_data);
2640 free(auth_data);
2643 return ret;