lib/kerb: verify_user_opt_int pass krb5_creds by ptr verify_common
[heimdal.git] / kdc / krb5tgs.c
blob8715e2b1e8b786b14599590095b4ef50d79d35db
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 *client,
84 hdb_entry *server,
85 hdb_entry *krbtgt,
86 hdb_entry *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, config,
126 client_principal, delegated_proxy_principal,
127 client, server, krbtgt, &pac);
128 if (ret == 0) {
129 if (pac_canon_name) {
130 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
131 if (ret && ret != ENOENT) {
132 krb5_pac_free(context, pac);
133 return ret;
136 if (pac_attributes &&
137 _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0)
138 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
139 } else if (ret == KRB5_PLUGIN_NO_HANDLE) {
141 * We can't verify the KDC signatures if the ticket was issued by
142 * another realm's KDC.
144 if (krb5_realm_compare(context, server->principal,
145 ticket_server->principal)) {
146 ret = krb5_pac_verify(context, pac, 0, NULL, NULL,
147 krbtgt_check_key);
148 if (ret) {
149 krb5_pac_free(context, pac);
150 return ret;
154 if (pac_canon_name) {
155 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
156 if (ret && ret != ENOENT) {
157 krb5_pac_free(context, pac);
158 return ret;
160 if (pac_attributes &&
161 _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0)
162 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
165 /* Discard the PAC if the plugin didn't handle it */
166 krb5_pac_free(context, pac);
167 ret = krb5_pac_init(context, &pac);
168 if (ret)
169 return ret;
170 } else {
171 krb5_pac_free(context, pac);
172 return ret;
175 *kdc_issued = signedticket ||
176 krb5_principal_is_krbtgt(context,
177 ticket_server->principal);
178 *ppac = pac;
180 return 0;
183 static krb5_boolean
184 is_anon_tgs_request_p(const KDC_REQ_BODY *b,
185 const EncTicketPart *tgt)
187 KDCOptions f = b->kdc_options;
190 * Versions of Heimdal from 1.0 to 7.6, inclusive, send both the
191 * request-anonymous and cname-in-addl-tkt flags for constrained
192 * delegation requests. A true anonymous TGS request will only
193 * have the request-anonymous flag set. (A corollary of this is
194 * that it is not possible to support anonymous constrained
195 * delegation requests, although they would be of limited utility.)
197 return tgt->flags.anonymous ||
198 (f.request_anonymous && !f.cname_in_addl_tkt && !b->additional_tickets);
205 static krb5_error_code
206 check_tgs_flags(astgs_request_t r, KDC_REQ_BODY *b,
207 krb5_const_principal tgt_name,
208 const EncTicketPart *tgt, EncTicketPart *et)
210 KDCOptions f = b->kdc_options;
212 if(f.validate){
213 if (!tgt->flags.invalid || tgt->starttime == NULL) {
214 _kdc_audit_addreason((kdc_request_t)r,
215 "Bad request to validate ticket");
216 return KRB5KDC_ERR_BADOPTION;
218 if(*tgt->starttime > kdc_time){
219 _kdc_audit_addreason((kdc_request_t)r,
220 "Early request to validate ticket");
221 return KRB5KRB_AP_ERR_TKT_NYV;
223 /* XXX tkt = tgt */
224 et->flags.invalid = 0;
225 } else if (tgt->flags.invalid) {
226 _kdc_audit_addreason((kdc_request_t)r,
227 "Ticket-granting ticket has INVALID flag set");
228 return KRB5KRB_AP_ERR_TKT_INVALID;
231 if(f.forwardable){
232 if (!tgt->flags.forwardable) {
233 _kdc_audit_addreason((kdc_request_t)r,
234 "Bad request for forwardable ticket");
235 return KRB5KDC_ERR_BADOPTION;
237 et->flags.forwardable = 1;
239 if(f.forwarded){
240 if (!tgt->flags.forwardable) {
241 _kdc_audit_addreason((kdc_request_t)r,
242 "Request to forward non-forwardable ticket");
243 return KRB5KDC_ERR_BADOPTION;
245 et->flags.forwarded = 1;
246 et->caddr = b->addresses;
248 if(tgt->flags.forwarded)
249 et->flags.forwarded = 1;
251 if(f.proxiable){
252 if (!tgt->flags.proxiable) {
253 _kdc_audit_addreason((kdc_request_t)r,
254 "Bad request for proxiable ticket");
255 return KRB5KDC_ERR_BADOPTION;
257 et->flags.proxiable = 1;
259 if(f.proxy){
260 if (!tgt->flags.proxiable) {
261 _kdc_audit_addreason((kdc_request_t)r,
262 "Request to proxy non-proxiable ticket");
263 return KRB5KDC_ERR_BADOPTION;
265 et->flags.proxy = 1;
266 et->caddr = b->addresses;
268 if(tgt->flags.proxy)
269 et->flags.proxy = 1;
271 if(f.allow_postdate){
272 if (!tgt->flags.may_postdate) {
273 _kdc_audit_addreason((kdc_request_t)r,
274 "Bad request for post-datable ticket");
275 return KRB5KDC_ERR_BADOPTION;
277 et->flags.may_postdate = 1;
279 if(f.postdated){
280 if (!tgt->flags.may_postdate) {
281 _kdc_audit_addreason((kdc_request_t)r,
282 "Bad request for postdated ticket");
283 return KRB5KDC_ERR_BADOPTION;
285 if(b->from)
286 *et->starttime = *b->from;
287 et->flags.postdated = 1;
288 et->flags.invalid = 1;
289 } else if (b->from && *b->from > kdc_time + r->context->max_skew) {
290 _kdc_audit_addreason((kdc_request_t)r,
291 "Ticket cannot be postdated");
292 return KRB5KDC_ERR_CANNOT_POSTDATE;
295 if(f.renewable){
296 if (!tgt->flags.renewable || tgt->renew_till == NULL) {
297 _kdc_audit_addreason((kdc_request_t)r,
298 "Bad request for renewable ticket");
299 return KRB5KDC_ERR_BADOPTION;
301 et->flags.renewable = 1;
302 ALLOC(et->renew_till);
303 _kdc_fix_time(&b->rtime);
304 *et->renew_till = *b->rtime;
306 if(f.renew){
307 time_t old_life;
308 if (!tgt->flags.renewable || tgt->renew_till == NULL) {
309 _kdc_audit_addreason((kdc_request_t)r,
310 "Request to renew non-renewable ticket");
311 return KRB5KDC_ERR_BADOPTION;
313 old_life = tgt->endtime;
314 if(tgt->starttime)
315 old_life -= *tgt->starttime;
316 else
317 old_life -= tgt->authtime;
318 et->endtime = *et->starttime + old_life;
319 if (et->renew_till != NULL)
320 et->endtime = min(*et->renew_till, et->endtime);
324 * RFC 8062 section 3 defines an anonymous ticket as one containing
325 * the anonymous principal and the anonymous ticket flag.
327 if (tgt->flags.anonymous &&
328 !_kdc_is_anonymous(r->context, tgt_name)) {
329 _kdc_audit_addreason((kdc_request_t)r,
330 "Anonymous ticket flag set without "
331 "anonymous principal");
332 return KRB5KDC_ERR_BADOPTION;
336 * RFC 8062 section 4.2 states that if the TGT is anonymous, the
337 * anonymous KDC option SHOULD be set, but it is not required.
338 * Treat an anonymous TGT as if the anonymous flag was set.
340 if (is_anon_tgs_request_p(b, tgt))
341 et->flags.anonymous = 1;
343 return 0;
347 * Determine if s4u2self is allowed from this client to this server
349 * also:
351 * Check that the client (user2user TGT, enc-tkt-in-skey) hosts the
352 * service given by the client.
354 * For example, regardless of the principal being impersonated, if the
355 * 'client' and 'server' (target) are the same, or server is an SPN
356 * alias of client, then it's safe.
359 krb5_error_code
360 _kdc_check_client_matches_target_service(krb5_context context,
361 krb5_kdc_configuration *config,
362 HDB *clientdb,
363 hdb_entry *client,
364 hdb_entry *target_server,
365 krb5_const_principal target_server_principal)
367 krb5_error_code ret;
370 * Always allow the plugin to check, this might be faster, allow a
371 * policy or audit check and can look into the DB records
372 * directly
374 if (clientdb->hdb_check_client_matches_target_service) {
375 ret = clientdb->hdb_check_client_matches_target_service(context,
376 clientdb,
377 client,
378 target_server);
379 if (ret == 0)
380 return 0;
381 } else if (krb5_principal_compare(context,
382 client->principal,
383 target_server_principal) == TRUE) {
384 /* if client does a s4u2self to itself, and there is no plugin, that is ok */
385 return 0;
386 } else {
387 ret = KRB5KDC_ERR_BADOPTION;
389 return ret;
396 krb5_error_code
397 _kdc_verify_flags(krb5_context context,
398 krb5_kdc_configuration *config,
399 const EncTicketPart *et,
400 const char *pstr)
402 if(et->endtime < kdc_time){
403 kdc_log(context, config, 4, "Ticket expired (%s)", pstr);
404 return KRB5KRB_AP_ERR_TKT_EXPIRED;
406 if(et->flags.invalid){
407 kdc_log(context, config, 4, "Ticket not valid (%s)", pstr);
408 return KRB5KRB_AP_ERR_TKT_NYV;
410 return 0;
417 static krb5_error_code
418 fix_transited_encoding(krb5_context context,
419 krb5_kdc_configuration *config,
420 krb5_boolean check_policy,
421 const TransitedEncoding *tr,
422 EncTicketPart *et,
423 const char *client_realm,
424 const char *server_realm,
425 const char *tgt_realm)
427 krb5_error_code ret = 0;
428 char **realms, **tmp;
429 unsigned int num_realms;
430 size_t i;
432 switch (tr->tr_type) {
433 case domain_X500_Compress:
434 break;
435 case 0:
437 * Allow empty content of type 0 because that is was Microsoft
438 * generates in their TGT.
440 if (tr->contents.length == 0)
441 break;
442 kdc_log(context, config, 4,
443 "Transited type 0 with non empty content");
444 return KRB5KDC_ERR_TRTYPE_NOSUPP;
445 default:
446 kdc_log(context, config, 4,
447 "Unknown transited type: %u", tr->tr_type);
448 return KRB5KDC_ERR_TRTYPE_NOSUPP;
451 ret = krb5_domain_x500_decode(context,
452 tr->contents,
453 &realms,
454 &num_realms,
455 client_realm,
456 server_realm);
457 if(ret){
458 krb5_warn(context, ret,
459 "Decoding transited encoding");
460 return ret;
464 * If the realm of the presented tgt is neither the client nor the server
465 * realm, it is a transit realm and must be added to transited set.
467 if (strcmp(client_realm, tgt_realm) != 0 &&
468 strcmp(server_realm, tgt_realm) != 0) {
469 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
470 ret = ERANGE;
471 goto free_realms;
473 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
474 if(tmp == NULL){
475 ret = ENOMEM;
476 goto free_realms;
478 realms = tmp;
479 realms[num_realms] = strdup(tgt_realm);
480 if(realms[num_realms] == NULL){
481 ret = ENOMEM;
482 goto free_realms;
484 num_realms++;
486 if(num_realms == 0) {
487 if (strcmp(client_realm, server_realm) != 0)
488 kdc_log(context, config, 4,
489 "cross-realm %s -> %s", client_realm, server_realm);
490 } else {
491 size_t l = 0;
492 char *rs;
493 for(i = 0; i < num_realms; i++)
494 l += strlen(realms[i]) + 2;
495 rs = malloc(l);
496 if(rs != NULL) {
497 *rs = '\0';
498 for(i = 0; i < num_realms; i++) {
499 if(i > 0)
500 strlcat(rs, ", ", l);
501 strlcat(rs, realms[i], l);
503 kdc_log(context, config, 4,
504 "cross-realm %s -> %s via [%s]",
505 client_realm, server_realm, rs);
506 free(rs);
509 if(check_policy) {
510 ret = krb5_check_transited(context, client_realm,
511 server_realm,
512 realms, num_realms, NULL);
513 if(ret) {
514 krb5_warn(context, ret, "cross-realm %s -> %s",
515 client_realm, server_realm);
516 goto free_realms;
518 et->flags.transited_policy_checked = 1;
520 et->transited.tr_type = domain_X500_Compress;
521 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
522 if(ret)
523 krb5_warn(context, ret, "Encoding transited encoding");
524 free_realms:
525 for(i = 0; i < num_realms; i++)
526 free(realms[i]);
527 free(realms);
528 return ret;
532 static krb5_error_code
533 tgs_make_reply(astgs_request_t r,
534 const EncTicketPart *tgt,
535 const EncryptionKey *serverkey,
536 const EncryptionKey *krbtgtkey,
537 const krb5_keyblock *sessionkey,
538 krb5_kvno kvno,
539 AuthorizationData *auth_data,
540 hdb_entry *server,
541 krb5_principal server_principal,
542 hdb_entry *client,
543 krb5_principal client_principal,
544 const char *tgt_realm,
545 uint16_t rodc_id,
546 krb5_boolean add_ticket_sig)
548 KDC_REQ_BODY *b = &r->req.req_body;
549 krb5_data *reply = r->reply;
550 KDC_REP *rep = &r->rep;
551 EncTicketPart *et = &r->et;
552 EncKDCRepPart *ek = &r->ek;
553 KDCOptions f = b->kdc_options;
554 krb5_error_code ret;
555 int is_weak = 0;
557 heim_assert(r->client_princ != NULL, "invalid client name passed to tgs_make_reply");
559 rep->pvno = 5;
560 rep->msg_type = krb_tgs_rep;
562 et->authtime = tgt->authtime;
563 _kdc_fix_time(&b->till);
564 et->endtime = min(tgt->endtime, *b->till);
565 ALLOC(et->starttime);
566 *et->starttime = kdc_time;
568 ret = check_tgs_flags(r, b, r->client_princ, tgt, et);
569 if(ret)
570 goto out;
572 /* We should check the transited encoding if:
573 1) the request doesn't ask not to be checked
574 2) globally enforcing a check
575 3) principal requires checking
576 4) we allow non-check per-principal, but principal isn't marked as allowing this
577 5) we don't globally allow this
580 #define GLOBAL_FORCE_TRANSITED_CHECK \
581 (r->config->trpolicy == TRPOLICY_ALWAYS_CHECK)
582 #define GLOBAL_ALLOW_PER_PRINCIPAL \
583 (r->config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
584 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
585 (r->config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
587 /* these will consult the database in future release */
588 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
589 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
591 ret = fix_transited_encoding(r->context, r->config,
592 !f.disable_transited_check ||
593 GLOBAL_FORCE_TRANSITED_CHECK ||
594 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
595 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
596 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
597 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
598 &tgt->transited, et,
599 krb5_principal_get_realm(r->context, client_principal),
600 krb5_principal_get_realm(r->context, server->principal),
601 tgt_realm);
602 if(ret)
603 goto out;
605 ret = copy_Realm(&server_principal->realm, &rep->ticket.realm);
606 if (ret)
607 goto out;
608 _krb5_principal2principalname(&rep->ticket.sname, server_principal);
609 ret = copy_Realm(&r->client_princ->realm, &rep->crealm);
610 if (ret)
611 goto out;
614 * RFC 8062 states "if the ticket in the TGS request is an anonymous
615 * one, the client and client realm are copied from that ticket". So
616 * whilst the TGT flag check below is superfluous, it is included in
617 * order to follow the specification to its letter.
619 if (et->flags.anonymous && !tgt->flags.anonymous)
620 _kdc_make_anonymous_principalname(&rep->cname);
621 else
622 ret = copy_PrincipalName(&r->client_princ->name, &rep->cname);
623 if (ret)
624 goto out;
625 rep->ticket.tkt_vno = 5;
627 ek->caddr = et->caddr;
630 time_t life;
631 life = et->endtime - *et->starttime;
632 if(client && client->max_life)
633 life = min(life, *client->max_life);
634 if(server->max_life)
635 life = min(life, *server->max_life);
636 et->endtime = *et->starttime + life;
638 if(f.renewable_ok && tgt->flags.renewable &&
639 et->renew_till == NULL && et->endtime < *b->till &&
640 tgt->renew_till != NULL)
642 et->flags.renewable = 1;
643 ALLOC(et->renew_till);
644 *et->renew_till = *b->till;
646 if(et->renew_till){
647 time_t renew;
648 renew = *et->renew_till - *et->starttime;
649 if(client && client->max_renew)
650 renew = min(renew, *client->max_renew);
651 if(server->max_renew)
652 renew = min(renew, *server->max_renew);
653 *et->renew_till = *et->starttime + renew;
656 if(et->renew_till){
657 *et->renew_till = min(*et->renew_till, *tgt->renew_till);
658 *et->starttime = min(*et->starttime, *et->renew_till);
659 et->endtime = min(et->endtime, *et->renew_till);
662 *et->starttime = min(*et->starttime, et->endtime);
664 if(*et->starttime == et->endtime){
665 ret = KRB5KDC_ERR_NEVER_VALID;
666 goto out;
668 if(et->renew_till && et->endtime == *et->renew_till){
669 free(et->renew_till);
670 et->renew_till = NULL;
671 et->flags.renewable = 0;
674 et->flags.pre_authent = tgt->flags.pre_authent;
675 et->flags.hw_authent = tgt->flags.hw_authent;
676 et->flags.ok_as_delegate = server->flags.ok_as_delegate;
678 /* See MS-KILE 3.3.5.1 */
679 if (!server->flags.forwardable)
680 et->flags.forwardable = 0;
681 if (!server->flags.proxiable)
682 et->flags.proxiable = 0;
684 if (auth_data) {
685 unsigned int i = 0;
687 /* XXX check authdata */
689 if (et->authorization_data == NULL) {
690 et->authorization_data = calloc(1, sizeof(*et->authorization_data));
691 if (et->authorization_data == NULL) {
692 ret = ENOMEM;
693 krb5_set_error_message(r->context, ret, "malloc: out of memory");
694 goto out;
697 for(i = 0; i < auth_data->len ; i++) {
698 ret = add_AuthorizationData(et->authorization_data, &auth_data->val[i]);
699 if (ret) {
700 krb5_set_error_message(r->context, ret, "malloc: out of memory");
701 goto out;
706 ret = krb5_copy_keyblock_contents(r->context, sessionkey, &et->key);
707 if (ret)
708 goto out;
709 et->crealm = rep->crealm;
710 et->cname = rep->cname;
712 ek->key = et->key;
713 /* MIT must have at least one last_req */
714 ek->last_req.val = calloc(1, sizeof(*ek->last_req.val));
715 if (ek->last_req.val == NULL) {
716 ret = ENOMEM;
717 goto out;
719 ek->last_req.len = 1; /* set after alloc to avoid null deref on cleanup */
720 ek->nonce = b->nonce;
721 ek->flags = et->flags;
722 ek->authtime = et->authtime;
723 ek->starttime = et->starttime;
724 ek->endtime = et->endtime;
725 ek->renew_till = et->renew_till;
726 ek->srealm = rep->ticket.realm;
727 ek->sname = rep->ticket.sname;
729 _kdc_log_timestamp(r, "TGS-REQ", et->authtime, et->starttime,
730 et->endtime, et->renew_till);
732 if (krb5_enctype_valid(r->context, serverkey->keytype) != 0
733 && _kdc_is_weak_exception(server->principal, serverkey->keytype))
735 krb5_enctype_enable(r->context, serverkey->keytype);
736 is_weak = 1;
739 if (r->canon_client_princ) {
740 char *cpn;
742 (void) krb5_unparse_name(r->context, r->canon_client_princ, &cpn);
743 _kdc_audit_addkv((kdc_request_t)r, 0, "canon_client_name", "%s",
744 cpn ? cpn : "<unknown>");
745 krb5_xfree(cpn);
749 * For anonymous tickets, we should filter out positive authorization data
750 * that could reveal the client's identity, and return a policy error for
751 * restrictive authorization data. Policy for unknown authorization types
752 * is implementation dependent.
754 if (r->pac && !et->flags.anonymous) {
755 _kdc_audit_setkv_number((kdc_request_t)r, "pac_attributes",
756 r->pac_attributes);
759 * PACs are included when issuing TGTs, if there is no PAC_ATTRIBUTES
760 * buffer (legacy behavior) or if the attributes buffer indicates the
761 * AS client requested one.
763 if (_kdc_include_pac_p(r)) {
764 krb5_boolean is_tgs =
765 krb5_principal_is_krbtgt(r->context, server->principal);
767 ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, r->client_princ, serverkey,
768 krbtgtkey, rodc_id, NULL, r->canon_client_princ,
769 add_ticket_sig, et,
770 is_tgs ? &r->pac_attributes : NULL);
771 if (ret)
772 goto out;
776 ret = _kdc_finalize_reply(r);
777 if (ret)
778 goto out;
780 /* It is somewhat unclear where the etype in the following
781 encryption should come from. What we have is a session
782 key in the passed tgt, and a list of preferred etypes
783 *for the new ticket*. Should we pick the best possible
784 etype, given the keytype in the tgt, or should we look
785 at the etype list here as well? What if the tgt
786 session key is DES3 and we want a ticket with a (say)
787 CAST session key. Should the DES3 etype be added to the
788 etype list, even if we don't want a session key with
789 DES3? */
790 ret = _kdc_encode_reply(r->context, r->config, r, b->nonce,
791 serverkey->keytype, kvno,
792 serverkey, 0, r->rk_is_subkey, reply);
793 if (is_weak)
794 krb5_enctype_disable(r->context, serverkey->keytype);
796 _log_astgs_req(r, serverkey->keytype);
798 out:
799 return ret;
802 static krb5_error_code
803 tgs_check_authenticator(krb5_context context,
804 krb5_kdc_configuration *config,
805 krb5_auth_context ac,
806 KDC_REQ_BODY *b,
807 krb5_keyblock *key)
809 krb5_authenticator auth;
810 krb5_error_code ret;
811 krb5_crypto crypto;
813 krb5_auth_con_getauthenticator(context, ac, &auth);
814 if(auth->cksum == NULL){
815 kdc_log(context, config, 4, "No authenticator in request");
816 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
817 goto out;
820 if (!krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
821 kdc_log(context, config, 4, "Bad checksum type in authenticator: %d",
822 auth->cksum->cksumtype);
823 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
824 goto out;
827 ret = krb5_crypto_init(context, key, 0, &crypto);
828 if (ret) {
829 const char *msg = krb5_get_error_message(context, ret);
830 kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
831 krb5_free_error_message(context, msg);
832 goto out;
836 * RFC4120 says the checksum must be collision-proof, but it does
837 * not require it to be keyed (as the authenticator is encrypted).
839 _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
840 ret = _kdc_verify_checksum(context,
841 crypto,
842 KRB5_KU_TGS_REQ_AUTH_CKSUM,
843 &b->_save,
844 auth->cksum);
845 krb5_crypto_destroy(context, crypto);
846 if(ret){
847 const char *msg = krb5_get_error_message(context, ret);
848 kdc_log(context, config, 4,
849 "Failed to verify authenticator checksum: %s", msg);
850 krb5_free_error_message(context, msg);
852 out:
853 free_Authenticator(auth);
854 free(auth);
855 return ret;
858 static krb5_boolean
859 need_referral(krb5_context context, krb5_kdc_configuration *config,
860 const KDCOptions * const options, krb5_principal server,
861 krb5_realm **realms)
863 const char *name;
865 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
866 return FALSE;
868 if (server->name.name_string.len == 1)
869 name = server->name.name_string.val[0];
870 else if (server->name.name_string.len > 1)
871 name = server->name.name_string.val[1];
872 else
873 return FALSE;
875 kdc_log(context, config, 5, "Searching referral for %s", name);
877 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
880 static krb5_error_code
881 validate_fast_ad(astgs_request_t r, krb5_authdata *auth_data)
883 krb5_error_code ret;
884 krb5_data data;
886 krb5_data_zero(&data);
888 ret = _krb5_get_ad(r->context, auth_data, NULL,
889 KRB5_AUTHDATA_FX_FAST_USED, &data);
890 if (ret == 0) {
891 r->fast_asserted = 1;
892 krb5_data_free(&data);
895 ret = _krb5_get_ad(r->context, auth_data, NULL,
896 KRB5_AUTHDATA_FX_FAST_ARMOR, &data);
897 if (ret == 0) {
898 kdc_log(r->context, r->config, 2,
899 "Invalid ticket usage: TGS-REQ contains AD-fx-fast-armor");
900 krb5_data_free(&data);
901 return KRB5KRB_AP_ERR_BAD_INTEGRITY;
904 return 0;
907 static krb5_error_code
908 tgs_parse_request(astgs_request_t r,
909 const PA_DATA *tgs_req,
910 krb5_enctype *krbtgt_etype,
911 const char *from,
912 const struct sockaddr *from_addr,
913 time_t **csec,
914 int **cusec,
915 AuthorizationData **auth_data)
917 krb5_kdc_configuration *config = r->config;
918 KDC_REQ_BODY *b = &r->req.req_body;
919 static char failed[] = "<unparse_name failed>";
920 krb5_ap_req ap_req;
921 krb5_error_code ret;
922 krb5_principal princ;
923 krb5_auth_context ac = NULL;
924 krb5_flags ap_req_options;
925 krb5_flags verify_ap_req_flags = 0;
926 krb5_crypto crypto;
927 krb5uint32 krbtgt_kvno; /* kvno used for the PA-TGS-REQ AP-REQ Ticket */
928 krb5uint32 krbtgt_kvno_try;
929 int kvno_search_tries = 4; /* number of kvnos to try when tkt_vno == 0 */
930 const Keys *krbtgt_keys;/* keyset for TGT tkt_vno */
931 Key *tkey;
932 krb5_keyblock *subkey = NULL;
933 unsigned usage;
935 *auth_data = NULL;
936 *csec = NULL;
937 *cusec = NULL;
939 memset(&ap_req, 0, sizeof(ap_req));
940 ret = krb5_decode_ap_req(r->context, &tgs_req->padata_value, &ap_req);
941 if(ret){
942 const char *msg = krb5_get_error_message(r->context, ret);
943 kdc_log(r->context, config, 4, "Failed to decode AP-REQ: %s", msg);
944 krb5_free_error_message(r->context, msg);
945 goto out;
948 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
949 /* XXX check for ticket.sname == req.sname */
950 kdc_log(r->context, config, 4, "PA-DATA is not a ticket-granting ticket");
951 ret = KRB5KDC_ERR_POLICY; /* ? */
952 goto out;
955 _krb5_principalname2krb5_principal(r->context,
956 &princ,
957 ap_req.ticket.sname,
958 ap_req.ticket.realm);
960 krbtgt_kvno = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0;
961 ret = _kdc_db_fetch(r->context, config, princ, HDB_F_GET_KRBTGT,
962 &krbtgt_kvno, &r->krbtgtdb, &r->krbtgt);
964 if (ret == HDB_ERR_NOT_FOUND_HERE) {
965 /* XXX Factor out this unparsing of the same princ all over */
966 char *p;
967 ret = krb5_unparse_name(r->context, princ, &p);
968 if (ret != 0)
969 p = failed;
970 krb5_free_principal(r->context, princ);
971 kdc_log(r->context, config, 5,
972 "Ticket-granting ticket account %s does not have secrets at "
973 "this KDC, need to proxy", p);
974 if (ret == 0)
975 free(p);
976 ret = HDB_ERR_NOT_FOUND_HERE;
977 goto out;
978 } else if (ret == HDB_ERR_KVNO_NOT_FOUND) {
979 char *p;
980 ret = krb5_unparse_name(r->context, princ, &p);
981 if (ret != 0)
982 p = failed;
983 krb5_free_principal(r->context, princ);
984 kdc_log(r->context, config, 5,
985 "Ticket-granting ticket account %s does not have keys for "
986 "kvno %d at this KDC", p, krbtgt_kvno);
987 if (ret == 0)
988 free(p);
989 ret = HDB_ERR_KVNO_NOT_FOUND;
990 goto out;
991 } else if (ret == HDB_ERR_NO_MKEY) {
992 char *p;
993 ret = krb5_unparse_name(r->context, princ, &p);
994 if (ret != 0)
995 p = failed;
996 krb5_free_principal(r->context, princ);
997 kdc_log(r->context, config, 5,
998 "Missing master key for decrypting keys for ticket-granting "
999 "ticket account %s with kvno %d at this KDC", p, krbtgt_kvno);
1000 if (ret == 0)
1001 free(p);
1002 ret = HDB_ERR_KVNO_NOT_FOUND;
1003 goto out;
1004 } else if (ret) {
1005 const char *msg = krb5_get_error_message(r->context, ret);
1006 char *p;
1007 ret = krb5_unparse_name(r->context, princ, &p);
1008 if (ret != 0)
1009 p = failed;
1010 kdc_log(r->context, config, 4,
1011 "Ticket-granting ticket %s not found in database: %s", p, msg);
1012 krb5_free_principal(r->context, princ);
1013 krb5_free_error_message(r->context, msg);
1014 if (ret == 0)
1015 free(p);
1016 ret = KRB5KRB_AP_ERR_NOT_US;
1017 goto out;
1020 krbtgt_kvno_try = krbtgt_kvno ? krbtgt_kvno : r->krbtgt->kvno;
1021 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1023 next_kvno:
1024 krbtgt_keys = hdb_kvno2keys(r->context, r->krbtgt, krbtgt_kvno_try);
1025 ret = hdb_enctype2key(r->context, r->krbtgt, krbtgt_keys,
1026 ap_req.ticket.enc_part.etype, &tkey);
1027 if (ret && krbtgt_kvno == 0 && kvno_search_tries > 0) {
1028 kvno_search_tries--;
1029 krbtgt_kvno_try--;
1030 goto next_kvno;
1031 } else if (ret) {
1032 char *str = NULL, *p = NULL;
1034 krb5_enctype_to_string(r->context, ap_req.ticket.enc_part.etype, &str);
1035 krb5_unparse_name(r->context, princ, &p);
1036 kdc_log(r->context, config, 4,
1037 "No server key with enctype %s found for %s",
1038 str ? str : "<unknown enctype>",
1039 p ? p : "<unparse_name failed>");
1040 free(str);
1041 free(p);
1042 ret = KRB5KRB_AP_ERR_BADKEYVER;
1043 goto out;
1046 if (b->kdc_options.validate)
1047 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1049 if (r->config->warn_ticket_addresses)
1050 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_ADDRS;
1052 ret = krb5_verify_ap_req2(r->context,
1053 &ac,
1054 &ap_req,
1055 princ,
1056 &tkey->key,
1057 verify_ap_req_flags,
1058 &ap_req_options,
1059 &r->ticket,
1060 KRB5_KU_TGS_REQ_AUTH);
1061 if (r->ticket && r->ticket->ticket.caddr)
1062 _kdc_audit_addaddrs((kdc_request_t)r, r->ticket->ticket.caddr, "tixaddrs");
1063 if (r->config->warn_ticket_addresses && ret == KRB5KRB_AP_ERR_BADADDR &&
1064 r->ticket != NULL) {
1065 _kdc_audit_setkv_bool((kdc_request_t)r, "wrongaddr", TRUE);
1066 ret = 0;
1068 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY && kvno_search_tries > 0) {
1069 kvno_search_tries--;
1070 krbtgt_kvno_try--;
1071 goto next_kvno;
1074 krb5_free_principal(r->context, princ);
1075 if(ret) {
1076 const char *msg = krb5_get_error_message(r->context, ret);
1077 kdc_log(r->context, config, 4, "Failed to verify AP-REQ: %s", msg);
1078 krb5_free_error_message(r->context, msg);
1079 goto out;
1082 r->ticket_key = tkey;
1085 krb5_authenticator auth;
1087 ret = krb5_auth_con_getauthenticator(r->context, ac, &auth);
1088 if (ret == 0) {
1089 *csec = malloc(sizeof(**csec));
1090 if (*csec == NULL) {
1091 krb5_free_authenticator(r->context, &auth);
1092 kdc_log(r->context, config, 4, "malloc failed");
1093 goto out;
1095 **csec = auth->ctime;
1096 *cusec = malloc(sizeof(**cusec));
1097 if (*cusec == NULL) {
1098 krb5_free_authenticator(r->context, &auth);
1099 kdc_log(r->context, config, 4, "malloc failed");
1100 goto out;
1102 **cusec = auth->cusec;
1104 ret = validate_fast_ad(r, auth->authorization_data);
1105 krb5_free_authenticator(r->context, &auth);
1106 if (ret)
1107 goto out;
1111 ret = tgs_check_authenticator(r->context, config, ac, b, &r->ticket->ticket.key);
1112 if (ret) {
1113 krb5_auth_con_free(r->context, ac);
1114 goto out;
1117 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1118 r->rk_is_subkey = 1;
1120 ret = krb5_auth_con_getremotesubkey(r->context, ac, &subkey);
1121 if(ret){
1122 const char *msg = krb5_get_error_message(r->context, ret);
1123 krb5_auth_con_free(r->context, ac);
1124 kdc_log(r->context, config, 4, "Failed to get remote subkey: %s", msg);
1125 krb5_free_error_message(r->context, msg);
1126 goto out;
1128 if(subkey == NULL){
1129 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1130 r->rk_is_subkey = 0;
1132 ret = krb5_auth_con_getkey(r->context, ac, &subkey);
1133 if(ret) {
1134 const char *msg = krb5_get_error_message(r->context, ret);
1135 krb5_auth_con_free(r->context, ac);
1136 kdc_log(r->context, config, 4, "Failed to get session key: %s", msg);
1137 krb5_free_error_message(r->context, msg);
1138 goto out;
1141 if(subkey == NULL){
1142 krb5_auth_con_free(r->context, ac);
1143 kdc_log(r->context, config, 4,
1144 "Failed to get key for enc-authorization-data");
1145 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1146 goto out;
1149 krb5_free_keyblock_contents(r->context, &r->reply_key);
1150 ret = krb5_copy_keyblock_contents(r->context, subkey, &r->reply_key);
1151 krb5_free_keyblock(r->context, subkey);
1152 if (ret)
1153 goto out;
1155 if (b->enc_authorization_data) {
1156 krb5_data ad;
1158 ret = krb5_crypto_init(r->context, &r->reply_key, 0, &crypto);
1159 if (ret) {
1160 const char *msg = krb5_get_error_message(r->context, ret);
1161 krb5_auth_con_free(r->context, ac);
1162 kdc_log(r->context, config, 4, "krb5_crypto_init failed: %s", msg);
1163 krb5_free_error_message(r->context, msg);
1164 goto out;
1166 ret = krb5_decrypt_EncryptedData (r->context,
1167 crypto,
1168 usage,
1169 b->enc_authorization_data,
1170 &ad);
1171 krb5_crypto_destroy(r->context, crypto);
1172 if(ret){
1173 krb5_auth_con_free(r->context, ac);
1174 kdc_log(r->context, config, 4,
1175 "Failed to decrypt enc-authorization-data");
1176 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1177 goto out;
1179 ALLOC(*auth_data);
1180 if (*auth_data == NULL) {
1181 krb5_auth_con_free(r->context, ac);
1182 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1183 goto out;
1185 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1186 if(ret){
1187 krb5_auth_con_free(r->context, ac);
1188 free(*auth_data);
1189 *auth_data = NULL;
1190 kdc_log(r->context, config, 4, "Failed to decode authorization data");
1191 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1192 goto out;
1196 ret = validate_fast_ad(r, r->ticket->ticket.authorization_data);
1197 if (ret)
1198 goto out;
1202 * Check for FAST request
1205 ret = _kdc_fast_unwrap_request(r, r->ticket, ac);
1206 if (ret)
1207 goto out;
1209 krb5_auth_con_free(r->context, ac);
1211 out:
1212 free_AP_REQ(&ap_req);
1214 return ret;
1217 static krb5_error_code
1218 build_server_referral(krb5_context context,
1219 krb5_kdc_configuration *config,
1220 krb5_crypto session,
1221 krb5_const_realm referred_realm,
1222 const PrincipalName *true_principal_name,
1223 const PrincipalName *requested_principal,
1224 krb5_data *outdata)
1226 PA_ServerReferralData ref;
1227 krb5_error_code ret;
1228 EncryptedData ed;
1229 krb5_data data;
1230 size_t size = 0;
1232 memset(&ref, 0, sizeof(ref));
1234 if (referred_realm) {
1235 ALLOC(ref.referred_realm);
1236 if (ref.referred_realm == NULL)
1237 goto eout;
1238 *ref.referred_realm = strdup(referred_realm);
1239 if (*ref.referred_realm == NULL)
1240 goto eout;
1242 if (true_principal_name) {
1243 ALLOC(ref.true_principal_name);
1244 if (ref.true_principal_name == NULL)
1245 goto eout;
1246 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1247 if (ret)
1248 goto eout;
1250 if (requested_principal) {
1251 ALLOC(ref.requested_principal_name);
1252 if (ref.requested_principal_name == NULL)
1253 goto eout;
1254 ret = copy_PrincipalName(requested_principal,
1255 ref.requested_principal_name);
1256 if (ret)
1257 goto eout;
1260 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1261 data.data, data.length,
1262 &ref, &size, ret);
1263 free_PA_ServerReferralData(&ref);
1264 if (ret)
1265 return ret;
1266 if (data.length != size)
1267 krb5_abortx(context, "internal asn.1 encoder error");
1269 ret = krb5_encrypt_EncryptedData(context, session,
1270 KRB5_KU_PA_SERVER_REFERRAL,
1271 data.data, data.length,
1272 0 /* kvno */, &ed);
1273 free(data.data);
1274 if (ret)
1275 return ret;
1277 ASN1_MALLOC_ENCODE(EncryptedData,
1278 outdata->data, outdata->length,
1279 &ed, &size, ret);
1280 free_EncryptedData(&ed);
1281 if (ret)
1282 return ret;
1283 if (outdata->length != size)
1284 krb5_abortx(context, "internal asn.1 encoder error");
1286 return 0;
1287 eout:
1288 free_PA_ServerReferralData(&ref);
1289 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1290 return ENOMEM;
1294 * This function is intended to be used when failure to find the client is
1295 * acceptable.
1297 krb5_error_code
1298 _kdc_db_fetch_client(krb5_context context,
1299 krb5_kdc_configuration *config,
1300 int flags,
1301 krb5_principal cp,
1302 const char *cpn,
1303 const char *krbtgt_realm,
1304 HDB **clientdb,
1305 hdb_entry **client_out)
1307 krb5_error_code ret;
1308 hdb_entry *client = NULL;
1310 *client_out = NULL;
1312 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1313 NULL, clientdb, &client);
1314 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1316 * This is OK, we are just trying to find out if they have
1317 * been disabled or deleted in the meantime; missing secrets
1318 * are OK.
1320 } else if (ret) {
1322 * If the client belongs to the same realm as our TGS, it
1323 * should exist in the local database.
1325 const char *msg;
1327 if (strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1328 if (ret == HDB_ERR_NOENTRY)
1329 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1330 kdc_log(context, config, 4, "Client no longer in database: %s", cpn);
1331 return ret;
1334 msg = krb5_get_error_message(context, ret);
1335 kdc_log(context, config, 4, "Client not found in database: %s", msg);
1336 krb5_free_error_message(context, msg);
1337 } else if (client->flags.invalid || !client->flags.client) {
1338 kdc_log(context, config, 4, "Client has invalid bit set");
1339 _kdc_free_ent(context, *clientdb, client);
1340 return KRB5KDC_ERR_POLICY;
1343 *client_out = client;
1345 return 0;
1348 static krb5_error_code
1349 tgs_build_reply(astgs_request_t priv,
1350 krb5_enctype krbtgt_etype,
1351 AuthorizationData **auth_data,
1352 const struct sockaddr *from_addr)
1354 krb5_context context = priv->context;
1355 krb5_kdc_configuration *config = priv->config;
1356 KDC_REQ_BODY *b = &priv->req.req_body;
1357 const char *from = priv->from;
1358 krb5_error_code ret, ret2;
1359 krb5_principal rsp = NULL;
1360 krb5_principal krbtgt_out_principal = NULL;
1361 krb5_principal user2user_princ = NULL;
1362 char *spn = NULL, *cpn = NULL, *krbtgt_out_n = NULL;
1363 char *user2user_name = NULL;
1364 hdb_entry *server = NULL, *client = NULL;
1365 HDB *user2user_krbtgtdb;
1366 hdb_entry *user2user_krbtgt = NULL;
1367 HDB *clientdb;
1368 HDB *serverdb = NULL;
1369 krb5_realm ref_realm = NULL;
1370 EncTicketPart *tgt = &priv->ticket->ticket;
1371 const EncryptionKey *ekey;
1372 krb5_keyblock sessionkey;
1373 krb5_kvno kvno;
1374 krb5_pac user2user_pac = NULL;
1375 uint16_t rodc_id;
1376 krb5_boolean add_ticket_sig = FALSE;
1377 const char *tgt_realm = /* Realm of TGT issuer */
1378 krb5_principal_get_realm(context, priv->krbtgt->principal);
1379 const char *our_realm = /* Realm of this KDC */
1380 krb5_principal_get_comp_string(context, priv->krbtgt->principal, 1);
1381 char **capath = NULL;
1382 size_t num_capath = 0;
1384 HDB *krbtgt_outdb;
1385 hdb_entry *krbtgt_out = NULL;
1387 PrincipalName *s;
1388 Realm r;
1389 EncTicketPart adtkt;
1390 char opt_str[128];
1391 krb5_boolean kdc_issued = FALSE;
1393 Key *tkey_sign;
1394 int flags = HDB_F_FOR_TGS_REQ;
1396 int result;
1398 memset(&sessionkey, 0, sizeof(sessionkey));
1399 memset(&adtkt, 0, sizeof(adtkt));
1401 s = b->sname;
1402 r = b->realm;
1405 * The canonicalize KDC option is passed as a hint to the backend, but
1406 * can typically be ignored. Per RFC 6806, names are not canonicalized
1407 * in response to a TGS request (although we make an exception, see
1408 * force-canonicalize below).
1410 if (b->kdc_options.canonicalize)
1411 flags |= HDB_F_CANON;
1413 if (s == NULL) {
1414 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1415 _kdc_set_const_e_text(priv, "No server in request");
1416 goto out;
1419 _krb5_principalname2krb5_principal(context, &priv->server_princ, *s, r);
1420 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1421 if (ret)
1422 goto out;
1423 spn = priv->sname;
1424 _krb5_principalname2krb5_principal(context, &priv->client_princ,
1425 tgt->cname, tgt->crealm);
1426 ret = krb5_unparse_name(context, priv->client_princ, &priv->cname);
1427 if (ret)
1428 goto out;
1429 cpn = priv->cname;
1430 result = unparse_flags(KDCOptions2int(b->kdc_options),
1431 asn1_KDCOptions_units(),
1432 opt_str, sizeof(opt_str));
1433 if (result > 0)
1434 kdc_log(context, config, 4,
1435 "TGS-REQ %s from %s for %s [%s]",
1436 cpn, from, spn, opt_str);
1437 else
1438 kdc_log(context, config, 4,
1439 "TGS-REQ %s from %s for %s", cpn, from, spn);
1442 * Fetch server
1445 server_lookup:
1446 priv->server = NULL;
1447 if (server)
1448 _kdc_free_ent(context, serverdb, server);
1449 server = NULL;
1450 ret = _kdc_db_fetch(context, config, priv->server_princ,
1451 HDB_F_GET_SERVER | HDB_F_DELAY_NEW_KEYS | flags,
1452 NULL, &serverdb, &server);
1453 priv->server = server;
1454 priv->serverdb = serverdb;
1455 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1456 kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", spn);
1457 _kdc_audit_addreason((kdc_request_t)priv, "Target not found here");
1458 goto out;
1459 } else if (ret == HDB_ERR_WRONG_REALM) {
1460 free(ref_realm);
1461 ref_realm = strdup(server->principal->realm);
1462 if (ref_realm == NULL) {
1463 ret = krb5_enomem(context);
1464 goto out;
1467 kdc_log(context, config, 4,
1468 "Returning a referral to realm %s for "
1469 "server %s.",
1470 ref_realm, spn);
1471 krb5_free_principal(context, priv->server_princ);
1472 priv->server_princ = NULL;
1473 ret = krb5_make_principal(context, &priv->server_princ, r, KRB5_TGS_NAME,
1474 ref_realm, NULL);
1475 if (ret)
1476 goto out;
1477 free(priv->sname);
1478 priv->sname = NULL;
1479 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1480 if (ret)
1481 goto out;
1482 spn = priv->sname;
1484 goto server_lookup;
1485 } else if (ret) {
1486 const char *new_rlm, *msg;
1487 Realm req_rlm;
1488 krb5_realm *realms;
1490 priv->ret = ret; /* advise policy plugin of failure reason */
1491 ret2 = _kdc_referral_policy(priv);
1492 if (ret2 == 0) {
1493 krb5_xfree(priv->sname);
1494 priv->sname = NULL;
1495 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1496 if (ret)
1497 goto out;
1498 goto server_lookup;
1499 } else if (ret2 != KRB5_PLUGIN_NO_HANDLE) {
1500 ret = ret2;
1501 } else if ((req_rlm = get_krbtgt_realm(&priv->server_princ->name)) != NULL) {
1502 if (capath == NULL) {
1503 /* With referalls, hierarchical capaths are always enabled */
1504 ret2 = _krb5_find_capath(context, tgt->crealm, our_realm,
1505 req_rlm, TRUE, &capath, &num_capath);
1506 if (ret2) {
1507 ret = ret2;
1508 _kdc_audit_addreason((kdc_request_t)priv,
1509 "No trusted path from client realm to ours");
1510 goto out;
1513 new_rlm = num_capath > 0 ? capath[--num_capath] : NULL;
1514 if (new_rlm) {
1515 kdc_log(context, config, 5, "krbtgt from %s via %s for "
1516 "realm %s not found, trying %s", tgt->crealm,
1517 our_realm, req_rlm, new_rlm);
1519 free(ref_realm);
1520 ref_realm = strdup(new_rlm);
1521 if (ref_realm == NULL) {
1522 ret = krb5_enomem(context);
1523 goto out;
1526 krb5_free_principal(context, priv->server_princ);
1527 priv->server_princ = NULL;
1528 krb5_make_principal(context, &priv->server_princ, r,
1529 KRB5_TGS_NAME, ref_realm, NULL);
1530 free(priv->sname);
1531 priv->sname = NULL;
1532 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1533 if (ret)
1534 goto out;
1535 spn = priv->sname;
1536 goto server_lookup;
1538 } else if (need_referral(context, config, &b->kdc_options, priv->server_princ, &realms)) {
1539 if (strcmp(realms[0], priv->server_princ->realm) != 0) {
1540 kdc_log(context, config, 4,
1541 "Returning a referral to realm %s for "
1542 "server %s that was not found",
1543 realms[0], spn);
1544 krb5_free_principal(context, priv->server_princ);
1545 priv->server_princ = NULL;
1546 krb5_make_principal(context, &priv->server_princ, r, KRB5_TGS_NAME,
1547 realms[0], NULL);
1548 free(priv->sname);
1549 priv->sname = NULL;
1550 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1551 if (ret) {
1552 krb5_free_host_realm(context, realms);
1553 goto out;
1555 spn = priv->sname;
1557 free(ref_realm);
1558 ref_realm = strdup(realms[0]);
1560 krb5_free_host_realm(context, realms);
1561 goto server_lookup;
1563 krb5_free_host_realm(context, realms);
1565 msg = krb5_get_error_message(context, ret);
1566 kdc_log(context, config, 3,
1567 "Server not found in database: %s: %s", spn, msg);
1568 krb5_free_error_message(context, msg);
1569 if (ret == HDB_ERR_NOENTRY)
1570 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1571 _kdc_audit_addreason((kdc_request_t)priv,
1572 "Service principal unknown");
1573 goto out;
1577 * RFC 6806 notes that names MUST NOT be changed in the response to
1578 * a TGS request. Hence we ignore the setting of the canonicalize
1579 * KDC option. However, for legacy interoperability we do allow the
1580 * backend to override this by setting the force-canonicalize HDB
1581 * flag in the server entry.
1583 if (server->flags.force_canonicalize)
1584 rsp = server->principal;
1585 else
1586 rsp = priv->server_princ;
1589 * Now refetch the primary krbtgt, and get the current kvno (the
1590 * sign check may have been on an old kvno, and the server may
1591 * have been an incoming trust)
1594 ret = krb5_make_principal(context,
1595 &krbtgt_out_principal,
1596 our_realm,
1597 KRB5_TGS_NAME,
1598 our_realm,
1599 NULL);
1600 if (ret) {
1601 kdc_log(context, config, 4,
1602 "Failed to make krbtgt principal name object for "
1603 "authz-data signatures");
1604 goto out;
1606 ret = krb5_unparse_name(context, krbtgt_out_principal, &krbtgt_out_n);
1607 if (ret) {
1608 kdc_log(context, config, 4,
1609 "Failed to make krbtgt principal name object for "
1610 "authz-data signatures");
1611 goto out;
1614 ret = _kdc_db_fetch(context, config, krbtgt_out_principal,
1615 HDB_F_GET_KRBTGT, NULL, &krbtgt_outdb, &krbtgt_out);
1616 if (ret) {
1617 char *ktpn = NULL;
1618 ret = krb5_unparse_name(context, priv->krbtgt->principal, &ktpn);
1619 kdc_log(context, config, 4,
1620 "No such principal %s (needed for authz-data signature keys) "
1621 "while processing TGS-REQ for service %s with krbtg %s",
1622 krbtgt_out_n, spn, (ret == 0) ? ktpn : "<unknown>");
1623 free(ktpn);
1624 ret = KRB5KRB_AP_ERR_NOT_US;
1625 goto out;
1629 * Select enctype, return key and kvno.
1633 krb5_enctype etype;
1635 if(b->kdc_options.enc_tkt_in_skey) {
1636 Ticket *t;
1637 krb5_principal p;
1638 Key *uukey;
1639 krb5uint32 second_kvno = 0;
1640 krb5uint32 *kvno_ptr = NULL;
1641 size_t i;
1642 HDB *user2user_db;
1643 hdb_entry *user2user_client = NULL;
1644 krb5_boolean user2user_kdc_issued = FALSE;
1645 char *tpn;
1647 if(b->additional_tickets == NULL ||
1648 b->additional_tickets->len == 0){
1649 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1650 kdc_log(context, config, 4,
1651 "No second ticket present in user-to-user request");
1652 _kdc_audit_addreason((kdc_request_t)priv,
1653 "No second ticket present in user-to-user request");
1654 goto out;
1656 t = &b->additional_tickets->val[0];
1657 if(!get_krbtgt_realm(&t->sname)){
1658 kdc_log(context, config, 4,
1659 "Additional ticket is not a ticket-granting ticket");
1660 _kdc_audit_addreason((kdc_request_t)priv,
1661 "Additional ticket is not a ticket-granting ticket");
1662 ret = KRB5KDC_ERR_POLICY;
1663 goto out;
1665 ret = _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1666 if (ret)
1667 goto out;
1669 ret = krb5_unparse_name(context, p, &tpn);
1670 if (ret)
1671 goto out;
1672 if(t->enc_part.kvno){
1673 second_kvno = *t->enc_part.kvno;
1674 kvno_ptr = &second_kvno;
1676 ret = _kdc_db_fetch(context, config, p,
1677 HDB_F_GET_KRBTGT, kvno_ptr,
1678 &user2user_krbtgtdb, &user2user_krbtgt);
1679 krb5_free_principal(context, p);
1680 if(ret){
1681 if (ret == HDB_ERR_NOENTRY)
1682 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1683 _kdc_audit_addreason((kdc_request_t)priv,
1684 "User-to-user service principal (TGS) unknown");
1685 krb5_xfree(tpn);
1686 goto out;
1688 ret = hdb_enctype2key(context, user2user_krbtgt, NULL,
1689 t->enc_part.etype, &uukey);
1690 if(ret){
1691 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1692 _kdc_audit_addreason((kdc_request_t)priv,
1693 "User-to-user enctype not supported");
1694 krb5_xfree(tpn);
1695 goto out;
1697 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1698 if(ret) {
1699 _kdc_audit_addreason((kdc_request_t)priv,
1700 "User-to-user TGT decrypt failure");
1701 krb5_xfree(tpn);
1702 goto out;
1705 ret = _kdc_verify_flags(context, config, &adtkt, tpn);
1706 if (ret) {
1707 _kdc_audit_addreason((kdc_request_t)priv,
1708 "User-to-user TGT expired or invalid");
1709 krb5_xfree(tpn);
1710 goto out;
1712 krb5_xfree(tpn);
1714 /* Fetch the name from the TGT. */
1715 ret = _krb5_principalname2krb5_principal(context, &user2user_princ,
1716 adtkt.cname, adtkt.crealm);
1717 if (ret)
1718 goto out;
1720 ret = krb5_unparse_name(context, user2user_princ, &user2user_name);
1721 if (ret)
1722 goto out;
1725 * Look up the name given in the TGT in the database. The user
1726 * claims to have a ticket-granting-ticket to our KDC, so we should
1727 * fail hard if we can't find the user - otherwise we can't do
1728 * proper checks.
1730 ret = _kdc_db_fetch(context, config, user2user_princ,
1731 HDB_F_GET_CLIENT | flags,
1732 NULL, &user2user_db, &user2user_client);
1733 if (ret == HDB_ERR_NOENTRY)
1734 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1735 if (ret)
1736 goto out;
1739 * The account is present in the database, now check the
1740 * account flags.
1742 * We check this as a client (because the purpose of
1743 * user2user is that the server flag is not set, because
1744 * the long-term key is not strong, but this does mean
1745 * that a client with an expired password can't get accept
1746 * a user2user ticket.
1748 ret = kdc_check_flags(priv,
1749 FALSE,
1750 user2user_client,
1751 NULL);
1752 if (ret) {
1753 _kdc_free_ent(context, user2user_db, user2user_client);
1754 goto out;
1758 * Also check that the account is the same one specified in the
1759 * request.
1761 ret = _kdc_check_client_matches_target_service(context,
1762 config,
1763 serverdb,
1764 server,
1765 user2user_client,
1766 user2user_princ);
1767 if (ret) {
1768 _kdc_free_ent(context, user2user_db, user2user_client);
1769 goto out;
1772 /* Verify the PAC of the TGT. */
1773 ret = _kdc_check_pac(context, config, user2user_princ, NULL,
1774 user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt,
1775 &uukey->key, &priv->ticket_key->key, &adtkt,
1776 &user2user_kdc_issued, &user2user_pac, NULL, NULL);
1777 _kdc_free_ent(context, user2user_db, user2user_client);
1778 if (ret) {
1779 const char *msg = krb5_get_error_message(context, ret);
1780 kdc_log(context, config, 0,
1781 "Verify PAC failed for %s (%s) from %s with %s",
1782 spn, user2user_name, from, msg);
1783 krb5_free_error_message(context, msg);
1784 goto out;
1787 if ((config->require_pac && !user2user_pac)
1788 || (user2user_pac && !user2user_kdc_issued))
1790 ret = KRB5KDC_ERR_BADOPTION;
1791 kdc_log(context, config, 0,
1792 "Ticket not signed with PAC; user-to-user failed (%s).",
1793 user2user_pac ? "Ticket unsigned" : "No PAC");
1794 goto out;
1797 ekey = &adtkt.key;
1798 for(i = 0; i < b->etype.len; i++)
1799 if (b->etype.val[i] == adtkt.key.keytype)
1800 break;
1801 if(i == b->etype.len) {
1802 kdc_log(context, config, 4,
1803 "Addition ticket have not matching etypes");
1804 krb5_clear_error_message(context);
1805 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1806 _kdc_audit_addreason((kdc_request_t)priv,
1807 "No matching enctypes for 2nd ticket");
1808 goto out;
1810 etype = b->etype.val[i];
1811 kvno = 0;
1812 } else {
1813 Key *skey;
1815 ret = _kdc_find_etype(priv, krb5_principal_is_krbtgt(context, priv->server_princ)
1816 ? KFE_IS_TGS : 0,
1817 b->etype.val, b->etype.len, &etype, NULL,
1818 NULL);
1819 if(ret) {
1820 kdc_log(context, config, 4,
1821 "Server (%s) has no support for etypes", spn);
1822 _kdc_audit_addreason((kdc_request_t)priv,
1823 "Enctype not supported");
1824 goto out;
1826 ret = _kdc_get_preferred_key(context, config, server, spn,
1827 NULL, &skey);
1828 if(ret) {
1829 kdc_log(context, config, 4,
1830 "Server (%s) has no supported etypes", spn);
1831 _kdc_audit_addreason((kdc_request_t)priv,
1832 "Enctype not supported");
1833 goto out;
1835 ekey = &skey->key;
1836 kvno = server->kvno;
1839 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1840 if (ret)
1841 goto out;
1845 * Check that service is in the same realm as the krbtgt. If it's
1846 * not the same, it's someone that is using a uni-directional trust
1847 * backward.
1851 * The first realm is the realm of the service, the second is
1852 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1853 * encrypted to. The redirection via the krbtgt_out entry allows
1854 * the DB to possibly correct the case of the realm (Samba4 does
1855 * this) before the strcmp()
1857 if (strcmp(krb5_principal_get_realm(context, server->principal),
1858 krb5_principal_get_realm(context, krbtgt_out->principal)) != 0) {
1859 char *ktpn;
1860 ret = krb5_unparse_name(context, krbtgt_out->principal, &ktpn);
1861 kdc_log(context, config, 4,
1862 "Request with wrong krbtgt: %s",
1863 (ret == 0) ? ktpn : "<unknown>");
1864 if(ret == 0)
1865 free(ktpn);
1866 ret = KRB5KRB_AP_ERR_NOT_US;
1867 _kdc_audit_addreason((kdc_request_t)priv, "Request with wrong TGT");
1868 goto out;
1871 ret = _kdc_get_preferred_key(context, config, krbtgt_out, krbtgt_out_n,
1872 NULL, &tkey_sign);
1873 if (ret) {
1874 kdc_log(context, config, 4,
1875 "Failed to find key for krbtgt PAC signature");
1876 _kdc_audit_addreason((kdc_request_t)priv,
1877 "Failed to find key for krbtgt PAC signature");
1878 goto out;
1880 ret = hdb_enctype2key(context, krbtgt_out, NULL,
1881 tkey_sign->key.keytype, &tkey_sign);
1882 if(ret) {
1883 kdc_log(context, config, 4,
1884 "Failed to find key for krbtgt PAC signature");
1885 _kdc_audit_addreason((kdc_request_t)priv,
1886 "Failed to find key for krbtgt PAC signature");
1887 goto out;
1890 if (_kdc_synthetic_princ_used_p(context, priv->ticket))
1891 flags |= HDB_F_SYNTHETIC_OK;
1893 ret = _kdc_db_fetch_client(context, config, flags, priv->client_princ,
1894 cpn, our_realm, &clientdb, &client);
1895 if (ret)
1896 goto out;
1897 flags &= ~HDB_F_SYNTHETIC_OK;
1898 priv->client = client;
1899 priv->clientdb = clientdb;
1901 ret = _kdc_check_pac(context, config, priv->client_princ, NULL,
1902 priv->client, priv->server,
1903 priv->krbtgt, priv->krbtgt,
1904 &priv->ticket_key->key, &priv->ticket_key->key, tgt,
1905 &kdc_issued, &priv->pac, &priv->canon_client_princ,
1906 &priv->pac_attributes);
1907 if (ret) {
1908 const char *msg = krb5_get_error_message(context, ret);
1909 _kdc_audit_addreason((kdc_request_t)priv, "PAC check failed");
1910 kdc_log(context, config, 4,
1911 "Verify PAC failed for %s (%s) from %s with %s",
1912 spn, cpn, from, msg);
1913 krb5_free_error_message(context, msg);
1914 goto out;
1918 * Process request
1922 * Services for User: protocol transition and constrained delegation
1925 ret = _kdc_validate_services_for_user(priv);
1926 if (ret)
1927 goto out;
1930 * Check flags
1933 ret = kdc_check_flags(priv, FALSE, priv->client, priv->server);
1934 if(ret)
1935 goto out;
1937 if((b->kdc_options.validate || b->kdc_options.renew) &&
1938 !krb5_principal_compare(context,
1939 priv->krbtgt->principal,
1940 priv->server->principal)){
1941 _kdc_audit_addreason((kdc_request_t)priv, "Inconsistent request");
1942 kdc_log(context, config, 4, "Inconsistent request.");
1943 ret = KRB5KDC_ERR_SERVER_NOMATCH;
1944 goto out;
1947 /* check for valid set of addresses */
1948 if (!_kdc_check_addresses(priv, tgt->caddr, from_addr)) {
1949 if (config->check_ticket_addresses) {
1950 ret = KRB5KRB_AP_ERR_BADADDR;
1951 _kdc_audit_setkv_bool((kdc_request_t)priv, "wrongaddr", TRUE);
1952 kdc_log(context, config, 4, "Request from wrong address");
1953 _kdc_audit_addreason((kdc_request_t)priv, "Request from wrong address");
1954 goto out;
1955 } else if (config->warn_ticket_addresses) {
1956 _kdc_audit_setkv_bool((kdc_request_t)priv, "wrongaddr", TRUE);
1960 /* check local and per-principal anonymous ticket issuance policy */
1961 if (is_anon_tgs_request_p(b, tgt)) {
1962 ret = _kdc_check_anon_policy(priv);
1963 if (ret)
1964 goto out;
1968 * If this is an referral, add server referral data to the
1969 * auth_data reply .
1971 if (ref_realm) {
1972 PA_DATA pa;
1973 krb5_crypto crypto;
1975 kdc_log(context, config, 3,
1976 "Adding server referral to %s", ref_realm);
1978 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
1979 if (ret)
1980 goto out;
1982 ret = build_server_referral(context, config, crypto, ref_realm,
1983 NULL, s, &pa.padata_value);
1984 krb5_crypto_destroy(context, crypto);
1985 if (ret) {
1986 _kdc_audit_addreason((kdc_request_t)priv, "Referral build failed");
1987 kdc_log(context, config, 4,
1988 "Failed building server referral");
1989 goto out;
1991 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
1993 ret = add_METHOD_DATA(priv->rep.padata, &pa);
1994 krb5_data_free(&pa.padata_value);
1995 if (ret) {
1996 kdc_log(context, config, 4,
1997 "Add server referral METHOD-DATA failed");
1998 goto out;
2003 * Only add ticket signature if the requested server is not krbtgt, and
2004 * either the header server is krbtgt or, in the case of renewal/validation
2005 * if it was signed with PAC ticket signature and we verified it.
2006 * Currently Heimdal only allows renewal of krbtgt anyway but that might
2007 * change one day (see issue #763) so make sure to check for it.
2010 if (kdc_issued &&
2011 !krb5_principal_is_krbtgt(context, server->principal)) {
2013 /* Validate armor TGT before potentially including device claims */
2014 if (priv->armor_ticket) {
2015 ret = _kdc_fast_check_armor_pac(priv);
2016 if (ret)
2017 goto out;
2020 add_ticket_sig = TRUE;
2024 * Active-Directory implementations use the high part of the kvno as the
2025 * read-only-dc identifier, we need to embed it in the PAC KDC signatures.
2028 rodc_id = krbtgt_out->kvno >> 16;
2034 ret = tgs_make_reply(priv,
2035 tgt,
2036 ekey,
2037 &tkey_sign->key,
2038 &sessionkey,
2039 kvno,
2040 *auth_data,
2041 server,
2042 rsp,
2043 client,
2044 priv->client_princ,
2045 tgt_realm,
2046 rodc_id,
2047 add_ticket_sig);
2049 out:
2050 free(user2user_name);
2051 free(krbtgt_out_n);
2052 _krb5_free_capath(context, capath);
2054 krb5_free_keyblock_contents(context, &sessionkey);
2055 if(krbtgt_out)
2056 _kdc_free_ent(context, krbtgt_outdb, krbtgt_out);
2057 if(user2user_krbtgt)
2058 _kdc_free_ent(context, user2user_krbtgtdb, user2user_krbtgt);
2060 krb5_free_principal(context, user2user_princ);
2061 krb5_free_principal(context, krbtgt_out_principal);
2062 free(ref_realm);
2064 free_EncTicketPart(&adtkt);
2066 krb5_pac_free(context, user2user_pac);
2068 return ret;
2075 krb5_error_code
2076 _kdc_tgs_rep(astgs_request_t r)
2078 krb5_kdc_configuration *config = r->config;
2079 KDC_REQ *req = &r->req;
2080 krb5_data *data = r->reply;
2081 const char *from = r->from;
2082 struct sockaddr *from_addr = r->addr;
2083 int datagram_reply = r->datagram_reply;
2084 AuthorizationData *auth_data = NULL;
2085 krb5_error_code ret;
2086 int i = 0;
2087 const PA_DATA *tgs_req, *pa;
2088 krb5_enctype krbtgt_etype = ETYPE_NULL;
2090 time_t *csec = NULL;
2091 int *cusec = NULL;
2093 r->e_text = NULL;
2095 if(req->padata == NULL){
2096 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2097 kdc_log(r->context, config, 4,
2098 "TGS-REQ from %s without PA-DATA", from);
2099 goto out;
2102 i = 0;
2103 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST_ARMOR);
2104 if (pa) {
2105 kdc_log(r->context, r->config, 10, "Found TGS-REQ FAST armor inside TGS-REQ pa-data");
2106 ret = KRB5KRB_ERR_GENERIC;
2107 goto out;
2110 i = 0;
2111 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2112 if(tgs_req == NULL){
2113 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2115 kdc_log(r->context, config, 4,
2116 "TGS-REQ from %s without PA-TGS-REQ", from);
2117 goto out;
2119 ret = tgs_parse_request(r, tgs_req,
2120 &krbtgt_etype,
2121 from, from_addr,
2122 &csec, &cusec,
2123 &auth_data);
2124 if (ret == HDB_ERR_NOT_FOUND_HERE) {
2125 /* kdc_log() is called in tgs_parse_request() */
2126 goto out;
2128 if (ret) {
2129 kdc_log(r->context, config, 4,
2130 "Failed parsing TGS-REQ from %s", from);
2131 goto out;
2134 ret = _kdc_fast_strengthen_reply_key(r);
2135 if (ret)
2136 goto out;
2138 ALLOC(r->rep.padata);
2139 if (r->rep.padata == NULL) {
2140 ret = ENOMEM;
2141 krb5_set_error_message(r->context, ret, N_("malloc: out of memory", ""));
2142 goto out;
2145 ret = tgs_build_reply(r,
2146 krbtgt_etype,
2147 &auth_data,
2148 from_addr);
2149 if (ret) {
2150 kdc_log(r->context, config, 4,
2151 "Failed building TGS-REP to %s", from);
2152 goto out;
2155 /* */
2156 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2157 krb5_data_free(data);
2158 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2159 _kdc_set_const_e_text(r, "Reply packet too large");
2162 out:
2163 r->ret = ret;
2164 _kdc_audit_request(r);
2166 if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2167 METHOD_DATA error_method = { 0, NULL };
2169 kdc_log(r->context, config, 5, "tgs-req: sending error: %d to client", ret);
2170 ret = _kdc_fast_mk_error(r,
2171 &error_method,
2172 r->armor_crypto,
2173 &req->req_body,
2174 r->ret,
2175 r->client_princ,
2176 r->server_princ,
2177 csec, cusec,
2178 data);
2179 free_METHOD_DATA(&error_method);
2181 free(csec);
2182 free(cusec);
2184 free_TGS_REP(&r->rep);
2185 free_TransitedEncoding(&r->et.transited);
2186 free(r->et.starttime);
2187 free(r->et.renew_till);
2188 if(r->et.authorization_data) {
2189 free_AuthorizationData(r->et.authorization_data);
2190 free(r->et.authorization_data);
2192 free_LastReq(&r->ek.last_req);
2193 if (r->et.key.keyvalue.data) {
2194 memset_s(r->et.key.keyvalue.data, 0, r->et.key.keyvalue.length,
2195 r->et.key.keyvalue.length);
2197 free_EncryptionKey(&r->et.key);
2199 if (r->canon_client_princ) {
2200 krb5_free_principal(r->context, r->canon_client_princ);
2201 r->canon_client_princ = NULL;
2203 if (r->armor_crypto) {
2204 krb5_crypto_destroy(r->context, r->armor_crypto);
2205 r->armor_crypto = NULL;
2207 if (r->armor_ticket)
2208 krb5_free_ticket(r->context, r->armor_ticket);
2209 if (r->armor_server)
2210 _kdc_free_ent(r->context, r->armor_serverdb, r->armor_server);
2211 krb5_free_keyblock_contents(r->context, &r->reply_key);
2212 krb5_free_keyblock_contents(r->context, &r->strengthen_key);
2214 if (r->ticket)
2215 krb5_free_ticket(r->context, r->ticket);
2216 if (r->krbtgt)
2217 _kdc_free_ent(r->context, r->krbtgtdb, r->krbtgt);
2219 if (r->client)
2220 _kdc_free_ent(r->context, r->clientdb, r->client);
2221 krb5_free_principal(r->context, r->client_princ);
2222 if (r->server)
2223 _kdc_free_ent(r->context, r->serverdb, r->server);
2224 krb5_free_principal(r->context, r->server_princ);
2225 _kdc_free_fast_state(&r->fast);
2226 krb5_pac_free(r->context, r->pac);
2228 if (auth_data) {
2229 free_AuthorizationData(auth_data);
2230 free(auth_data);
2233 return ret;