tests: Use here-doc kadmin in Java test
[heimdal.git] / kdc / krb5tgs.c
blobc7473c40972e3195f7cd22db3db7375ba5e6e93f
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(astgs_request_t r,
80 const krb5_principal client_principal,
81 const krb5_principal delegated_proxy_principal,
82 hdb_entry *client,
83 hdb_entry *server,
84 hdb_entry *krbtgt,
85 hdb_entry *ticket_server,
86 const EncryptionKey *server_check_key,
87 const EncryptionKey *krbtgt_check_key,
88 EncTicketPart *tkt,
89 krb5_boolean *kdc_issued,
90 krb5_pac *ppac,
91 krb5_principal *pac_canon_name,
92 uint64_t *pac_attributes)
94 krb5_context context = r->context;
95 krb5_kdc_configuration *config = r->config;
96 krb5_pac pac = NULL;
97 krb5_error_code ret;
98 krb5_boolean signedticket;
100 *kdc_issued = FALSE;
101 *ppac = NULL;
102 if (pac_canon_name)
103 *pac_canon_name = NULL;
104 if (pac_attributes)
105 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
107 ret = _krb5_kdc_pac_ticket_parse(context, tkt, &signedticket, &pac);
108 if (ret)
109 return ret;
111 if (pac == NULL) {
112 if (config->require_pac)
113 ret = KRB5KDC_ERR_TGT_REVOKED;
114 return ret;
117 /* Verify the server signature. */
118 ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal,
119 server_check_key, NULL);
120 if (ret) {
121 krb5_pac_free(context, pac);
122 return ret;
125 /* Verify the KDC signatures. */
126 ret = _kdc_pac_verify(r,
127 client_principal, delegated_proxy_principal,
128 client, server, krbtgt, &pac);
129 if (ret == 0) {
130 if (pac_canon_name) {
131 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
132 if (ret && ret != ENOENT) {
133 krb5_pac_free(context, pac);
134 return ret;
137 if (pac_attributes &&
138 _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0)
139 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
140 } else if (ret == KRB5_PLUGIN_NO_HANDLE) {
142 * We can't verify the KDC signatures if the ticket was issued by
143 * another realm's KDC.
145 if (krb5_realm_compare(context, server->principal,
146 ticket_server->principal)) {
147 ret = krb5_pac_verify(context, pac, 0, NULL, NULL,
148 krbtgt_check_key);
149 if (ret) {
150 krb5_pac_free(context, pac);
151 return ret;
155 if (pac_canon_name) {
156 ret = _krb5_pac_get_canon_principal(context, pac, pac_canon_name);
157 if (ret && ret != ENOENT) {
158 krb5_pac_free(context, pac);
159 return ret;
161 if (pac_attributes &&
162 _krb5_pac_get_attributes_info(context, pac, pac_attributes) != 0)
163 *pac_attributes = KRB5_PAC_WAS_GIVEN_IMPLICITLY;
166 /* Discard the PAC if the plugin didn't handle it */
167 krb5_pac_free(context, pac);
168 ret = krb5_pac_init(context, &pac);
169 if (ret)
170 return ret;
171 } else {
172 krb5_pac_free(context, pac);
173 return ret;
176 *kdc_issued = signedticket ||
177 krb5_principal_is_krbtgt(context,
178 ticket_server->principal);
179 *ppac = pac;
181 return 0;
184 static krb5_boolean
185 is_anon_tgs_request_p(const KDC_REQ_BODY *b,
186 const EncTicketPart *tgt)
188 KDCOptions f = b->kdc_options;
191 * Versions of Heimdal from 1.0 to 7.6, inclusive, send both the
192 * request-anonymous and cname-in-addl-tkt flags for constrained
193 * delegation requests. A true anonymous TGS request will only
194 * have the request-anonymous flag set. (A corollary of this is
195 * that it is not possible to support anonymous constrained
196 * delegation requests, although they would be of limited utility.)
198 return tgt->flags.anonymous ||
199 (f.request_anonymous && !f.cname_in_addl_tkt && !b->additional_tickets);
206 static krb5_error_code
207 check_tgs_flags(astgs_request_t r, KDC_REQ_BODY *b,
208 krb5_const_principal tgt_name,
209 const EncTicketPart *tgt, EncTicketPart *et)
211 KDCOptions f = b->kdc_options;
213 if(f.validate){
214 if (!tgt->flags.invalid || tgt->starttime == NULL) {
215 kdc_audit_addreason((kdc_request_t)r,
216 "Bad request to validate ticket");
217 return KRB5KDC_ERR_BADOPTION;
219 if(*tgt->starttime > kdc_time){
220 kdc_audit_addreason((kdc_request_t)r,
221 "Early request to validate ticket");
222 return KRB5KRB_AP_ERR_TKT_NYV;
224 /* XXX tkt = tgt */
225 et->flags.invalid = 0;
226 } else if (tgt->flags.invalid) {
227 kdc_audit_addreason((kdc_request_t)r,
228 "Ticket-granting ticket has INVALID flag set");
229 return KRB5KRB_AP_ERR_TKT_INVALID;
232 if(f.forwardable){
233 if (!tgt->flags.forwardable) {
234 kdc_audit_addreason((kdc_request_t)r,
235 "Bad request for forwardable ticket");
236 return KRB5KDC_ERR_BADOPTION;
238 et->flags.forwardable = 1;
240 if(f.forwarded){
241 if (!tgt->flags.forwardable) {
242 kdc_audit_addreason((kdc_request_t)r,
243 "Request to forward non-forwardable ticket");
244 return KRB5KDC_ERR_BADOPTION;
246 et->flags.forwarded = 1;
247 et->caddr = b->addresses;
249 if(tgt->flags.forwarded)
250 et->flags.forwarded = 1;
252 if(f.proxiable){
253 if (!tgt->flags.proxiable) {
254 kdc_audit_addreason((kdc_request_t)r,
255 "Bad request for proxiable ticket");
256 return KRB5KDC_ERR_BADOPTION;
258 et->flags.proxiable = 1;
260 if(f.proxy){
261 if (!tgt->flags.proxiable) {
262 kdc_audit_addreason((kdc_request_t)r,
263 "Request to proxy non-proxiable ticket");
264 return KRB5KDC_ERR_BADOPTION;
266 et->flags.proxy = 1;
267 et->caddr = b->addresses;
269 if(tgt->flags.proxy)
270 et->flags.proxy = 1;
272 if(f.allow_postdate){
273 if (!tgt->flags.may_postdate) {
274 kdc_audit_addreason((kdc_request_t)r,
275 "Bad request for post-datable ticket");
276 return KRB5KDC_ERR_BADOPTION;
278 et->flags.may_postdate = 1;
280 if(f.postdated){
281 if (!tgt->flags.may_postdate) {
282 kdc_audit_addreason((kdc_request_t)r,
283 "Bad request for postdated ticket");
284 return KRB5KDC_ERR_BADOPTION;
286 if(b->from)
287 *et->starttime = *b->from;
288 et->flags.postdated = 1;
289 et->flags.invalid = 1;
290 } else if (b->from && *b->from > kdc_time + r->context->max_skew) {
291 kdc_audit_addreason((kdc_request_t)r,
292 "Ticket cannot be postdated");
293 return KRB5KDC_ERR_CANNOT_POSTDATE;
296 if(f.renewable){
297 if (!tgt->flags.renewable || tgt->renew_till == NULL) {
298 kdc_audit_addreason((kdc_request_t)r,
299 "Bad request for renewable ticket");
300 return KRB5KDC_ERR_BADOPTION;
302 et->flags.renewable = 1;
303 ALLOC(et->renew_till);
304 _kdc_fix_time(&b->rtime);
305 *et->renew_till = *b->rtime;
307 if(f.renew){
308 time_t old_life;
309 if (!tgt->flags.renewable || tgt->renew_till == NULL) {
310 kdc_audit_addreason((kdc_request_t)r,
311 "Request to renew non-renewable ticket");
312 return KRB5KDC_ERR_BADOPTION;
314 old_life = tgt->endtime;
315 if(tgt->starttime)
316 old_life -= *tgt->starttime;
317 else
318 old_life -= tgt->authtime;
319 et->endtime = *et->starttime + old_life;
320 if (et->renew_till != NULL)
321 et->endtime = min(*et->renew_till, et->endtime);
325 * RFC 8062 section 3 defines an anonymous ticket as one containing
326 * the anonymous principal and the anonymous ticket flag.
328 if (tgt->flags.anonymous &&
329 !_kdc_is_anonymous(r->context, tgt_name)) {
330 kdc_audit_addreason((kdc_request_t)r,
331 "Anonymous ticket flag set without "
332 "anonymous principal");
333 return KRB5KDC_ERR_BADOPTION;
337 * RFC 8062 section 4.2 states that if the TGT is anonymous, the
338 * anonymous KDC option SHOULD be set, but it is not required.
339 * Treat an anonymous TGT as if the anonymous flag was set.
341 if (is_anon_tgs_request_p(b, tgt))
342 et->flags.anonymous = 1;
344 return 0;
348 * Determine if s4u2self is allowed from this client to this server
350 * also:
352 * Check that the client (user2user TGT, enc-tkt-in-skey) hosts the
353 * service given by the client.
355 * For example, regardless of the principal being impersonated, if the
356 * 'client' and 'server' (target) are the same, or server is an SPN
357 * alias of client, then it's safe.
360 krb5_error_code
361 _kdc_check_client_matches_target_service(krb5_context context,
362 krb5_kdc_configuration *config,
363 HDB *clientdb,
364 hdb_entry *client,
365 hdb_entry *target_server,
366 krb5_const_principal target_server_principal)
368 krb5_error_code ret;
371 * Always allow the plugin to check, this might be faster, allow a
372 * policy or audit check and can look into the DB records
373 * directly
375 if (clientdb->hdb_check_client_matches_target_service) {
376 ret = clientdb->hdb_check_client_matches_target_service(context,
377 clientdb,
378 client,
379 target_server);
380 if (ret == 0)
381 return 0;
382 } else if (krb5_principal_compare(context,
383 client->principal,
384 target_server_principal) == TRUE) {
385 /* if client does a s4u2self to itself, and there is no plugin, that is ok */
386 return 0;
387 } else {
388 ret = KRB5KDC_ERR_BADOPTION;
390 return ret;
397 krb5_error_code
398 _kdc_verify_flags(krb5_context context,
399 krb5_kdc_configuration *config,
400 const EncTicketPart *et,
401 const char *pstr)
403 if(et->endtime < kdc_time){
404 kdc_log(context, config, 4, "Ticket expired (%s)", pstr);
405 return KRB5KRB_AP_ERR_TKT_EXPIRED;
407 if(et->flags.invalid){
408 kdc_log(context, config, 4, "Ticket not valid (%s)", pstr);
409 return KRB5KRB_AP_ERR_TKT_NYV;
411 return 0;
418 static krb5_error_code
419 fix_transited_encoding(krb5_context context,
420 krb5_kdc_configuration *config,
421 krb5_boolean check_policy,
422 const TransitedEncoding *tr,
423 EncTicketPart *et,
424 const char *client_realm,
425 const char *server_realm,
426 const char *tgt_realm)
428 krb5_error_code ret = 0;
429 char **realms, **tmp;
430 unsigned int num_realms;
431 size_t i;
433 switch (tr->tr_type) {
434 case domain_X500_Compress:
435 break;
436 case 0:
438 * Allow empty content of type 0 because that is was Microsoft
439 * generates in their TGT.
441 if (tr->contents.length == 0)
442 break;
443 kdc_log(context, config, 4,
444 "Transited type 0 with non empty content");
445 return KRB5KDC_ERR_TRTYPE_NOSUPP;
446 default:
447 kdc_log(context, config, 4,
448 "Unknown transited type: %u", tr->tr_type);
449 return KRB5KDC_ERR_TRTYPE_NOSUPP;
452 ret = krb5_domain_x500_decode(context,
453 tr->contents,
454 &realms,
455 &num_realms,
456 client_realm,
457 server_realm);
458 if(ret){
459 krb5_warn(context, ret,
460 "Decoding transited encoding");
461 return ret;
465 * If the realm of the presented tgt is neither the client nor the server
466 * realm, it is a transit realm and must be added to transited set.
468 if (strcmp(client_realm, tgt_realm) != 0 &&
469 strcmp(server_realm, tgt_realm) != 0) {
470 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
471 ret = ERANGE;
472 goto free_realms;
474 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
475 if(tmp == NULL){
476 ret = ENOMEM;
477 goto free_realms;
479 realms = tmp;
480 realms[num_realms] = strdup(tgt_realm);
481 if(realms[num_realms] == NULL){
482 ret = ENOMEM;
483 goto free_realms;
485 num_realms++;
487 if(num_realms == 0) {
488 if (strcmp(client_realm, server_realm) != 0)
489 kdc_log(context, config, 4,
490 "cross-realm %s -> %s", client_realm, server_realm);
491 } else {
492 size_t l = 0;
493 char *rs;
494 for(i = 0; i < num_realms; i++)
495 l += strlen(realms[i]) + 2;
496 rs = malloc(l);
497 if(rs != NULL) {
498 *rs = '\0';
499 for(i = 0; i < num_realms; i++) {
500 if(i > 0)
501 strlcat(rs, ", ", l);
502 strlcat(rs, realms[i], l);
504 kdc_log(context, config, 4,
505 "cross-realm %s -> %s via [%s]",
506 client_realm, server_realm, rs);
507 free(rs);
510 if(check_policy) {
511 ret = krb5_check_transited(context, client_realm,
512 server_realm,
513 realms, num_realms, NULL);
514 if(ret) {
515 krb5_warn(context, ret, "cross-realm %s -> %s",
516 client_realm, server_realm);
517 goto free_realms;
519 et->flags.transited_policy_checked = 1;
521 et->transited.tr_type = domain_X500_Compress;
522 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
523 if(ret)
524 krb5_warn(context, ret, "Encoding transited encoding");
525 free_realms:
526 for(i = 0; i < num_realms; i++)
527 free(realms[i]);
528 free(realms);
529 return ret;
533 static krb5_error_code
534 tgs_make_reply(astgs_request_t r,
535 const EncTicketPart *tgt,
536 const EncryptionKey *serverkey,
537 const EncryptionKey *krbtgtkey,
538 const krb5_keyblock *sessionkey,
539 krb5_kvno kvno,
540 AuthorizationData *auth_data,
541 const char *tgt_realm,
542 uint16_t rodc_id,
543 krb5_boolean add_ticket_sig)
545 KDC_REQ_BODY *b = &r->req.req_body;
546 krb5_data *reply = r->reply;
547 KDC_REP *rep = &r->rep;
548 EncTicketPart *et = &r->et;
549 EncKDCRepPart *ek = &r->ek;
550 KDCOptions f = b->kdc_options;
551 krb5_error_code ret;
552 int is_weak = 0;
554 heim_assert(r->client_princ != NULL, "invalid client name passed to tgs_make_reply");
556 rep->pvno = 5;
557 rep->msg_type = krb_tgs_rep;
559 et->authtime = tgt->authtime;
560 _kdc_fix_time(&b->till);
561 et->endtime = min(tgt->endtime, *b->till);
562 ALLOC(et->starttime);
563 *et->starttime = kdc_time;
565 ret = check_tgs_flags(r, b, r->client_princ, tgt, et);
566 if(ret)
567 goto out;
569 /* We should check the transited encoding if:
570 1) the request doesn't ask not to be checked
571 2) globally enforcing a check
572 3) principal requires checking
573 4) we allow non-check per-principal, but principal isn't marked as allowing this
574 5) we don't globally allow this
577 #define GLOBAL_FORCE_TRANSITED_CHECK \
578 (r->config->trpolicy == TRPOLICY_ALWAYS_CHECK)
579 #define GLOBAL_ALLOW_PER_PRINCIPAL \
580 (r->config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
581 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
582 (r->config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
584 /* these will consult the database in future release */
585 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
586 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
588 ret = fix_transited_encoding(r->context, r->config,
589 !f.disable_transited_check ||
590 GLOBAL_FORCE_TRANSITED_CHECK ||
591 PRINCIPAL_FORCE_TRANSITED_CHECK(r->server) ||
592 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
593 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(r->server)) ||
594 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
595 &tgt->transited, et,
596 krb5_principal_get_realm(r->context, r->client_princ),
597 krb5_principal_get_realm(r->context, r->server->principal),
598 tgt_realm);
602 * RFC 6806 notes that names MUST NOT be changed in the response to a
603 * TGS request. Hence we ignore the setting of the canonicalize KDC
604 * option. However, for legacy interoperability we do allow the backend
605 * to override this by setting the force-canonicalize HDB flag in the
606 * server entry.
608 krb5_const_principal rsp;
610 if (r->server->flags.force_canonicalize)
611 rsp = r->server->principal;
612 else
613 rsp = r->server_princ;
614 if (ret == 0)
615 ret = copy_Realm(&rsp->realm, &rep->ticket.realm);
616 if (ret == 0)
617 ret = _krb5_principal2principalname(&rep->ticket.sname, rsp);
620 if (ret == 0)
621 ret = copy_Realm(&r->client_princ->realm, &rep->crealm);
622 if (ret)
623 goto out;
626 * RFC 8062 states "if the ticket in the TGS request is an anonymous
627 * one, the client and client realm are copied from that ticket". So
628 * whilst the TGT flag check below is superfluous, it is included in
629 * order to follow the specification to its letter.
631 if (et->flags.anonymous && !tgt->flags.anonymous)
632 _kdc_make_anonymous_principalname(&rep->cname);
633 else
634 ret = copy_PrincipalName(&r->client_princ->name, &rep->cname);
635 if (ret)
636 goto out;
637 rep->ticket.tkt_vno = 5;
639 ek->caddr = et->caddr;
642 time_t life;
643 life = et->endtime - *et->starttime;
644 if(r->client && r->client->max_life)
645 life = min(life, *r->client->max_life);
646 if(r->server->max_life)
647 life = min(life, *r->server->max_life);
648 et->endtime = *et->starttime + life;
650 if(f.renewable_ok && tgt->flags.renewable &&
651 et->renew_till == NULL && et->endtime < *b->till &&
652 tgt->renew_till != NULL)
654 et->flags.renewable = 1;
655 ALLOC(et->renew_till);
656 *et->renew_till = *b->till;
658 if(et->renew_till){
659 time_t renew;
660 renew = *et->renew_till - *et->starttime;
661 if(r->client && r->client->max_renew)
662 renew = min(renew, *r->client->max_renew);
663 if(r->server->max_renew)
664 renew = min(renew, *r->server->max_renew);
665 *et->renew_till = *et->starttime + renew;
668 if(et->renew_till){
669 *et->renew_till = min(*et->renew_till, *tgt->renew_till);
670 *et->starttime = min(*et->starttime, *et->renew_till);
671 et->endtime = min(et->endtime, *et->renew_till);
674 *et->starttime = min(*et->starttime, et->endtime);
676 if(*et->starttime == et->endtime){
677 ret = KRB5KDC_ERR_NEVER_VALID;
678 goto out;
680 if(et->renew_till && et->endtime == *et->renew_till){
681 free(et->renew_till);
682 et->renew_till = NULL;
683 et->flags.renewable = 0;
686 et->flags.pre_authent = tgt->flags.pre_authent;
687 et->flags.hw_authent = tgt->flags.hw_authent;
688 et->flags.ok_as_delegate = r->server->flags.ok_as_delegate;
690 /* See MS-KILE 3.3.5.1 */
691 if (!r->server->flags.forwardable)
692 et->flags.forwardable = 0;
693 if (!r->server->flags.proxiable)
694 et->flags.proxiable = 0;
696 if (auth_data) {
697 unsigned int i = 0;
699 /* XXX check authdata */
701 if (et->authorization_data == NULL) {
702 et->authorization_data = calloc(1, sizeof(*et->authorization_data));
703 if (et->authorization_data == NULL) {
704 ret = ENOMEM;
705 krb5_set_error_message(r->context, ret, "malloc: out of memory");
706 goto out;
709 for(i = 0; i < auth_data->len ; i++) {
710 ret = add_AuthorizationData(et->authorization_data, &auth_data->val[i]);
711 if (ret) {
712 krb5_set_error_message(r->context, ret, "malloc: out of memory");
713 goto out;
718 ret = krb5_copy_keyblock_contents(r->context, sessionkey, &et->key);
719 if (ret)
720 goto out;
721 et->crealm = rep->crealm;
722 et->cname = rep->cname;
724 ek->key = et->key;
725 /* MIT must have at least one last_req */
726 ek->last_req.val = calloc(1, sizeof(*ek->last_req.val));
727 if (ek->last_req.val == NULL) {
728 ret = ENOMEM;
729 goto out;
731 ek->last_req.len = 1; /* set after alloc to avoid null deref on cleanup */
732 ek->nonce = b->nonce;
733 ek->flags = et->flags;
734 ek->authtime = et->authtime;
735 ek->starttime = et->starttime;
736 ek->endtime = et->endtime;
737 ek->renew_till = et->renew_till;
738 ek->srealm = rep->ticket.realm;
739 ek->sname = rep->ticket.sname;
741 _kdc_log_timestamp(r, "TGS-REQ", et->authtime, et->starttime,
742 et->endtime, et->renew_till);
744 if (krb5_enctype_valid(r->context, serverkey->keytype) != 0
745 && _kdc_is_weak_exception(r->server->principal, serverkey->keytype))
747 krb5_enctype_enable(r->context, serverkey->keytype);
748 is_weak = 1;
751 if (r->canon_client_princ) {
752 char *cpn;
754 (void) krb5_unparse_name(r->context, r->canon_client_princ, &cpn);
755 kdc_audit_addkv((kdc_request_t)r, 0, "canon_client_name", "%s",
756 cpn ? cpn : "<unknown>");
757 krb5_xfree(cpn);
761 * For anonymous tickets, we should filter out positive authorization data
762 * that could reveal the client's identity, and return a policy error for
763 * restrictive authorization data. Policy for unknown authorization types
764 * is implementation dependent.
766 if (r->pac && !et->flags.anonymous) {
767 kdc_audit_setkv_number((kdc_request_t)r, "pac_attributes",
768 r->pac_attributes);
771 * PACs are included when issuing TGTs, if there is no PAC_ATTRIBUTES
772 * buffer (legacy behavior) or if the attributes buffer indicates the
773 * AS client requested one.
775 if (_kdc_include_pac_p(r)) {
776 krb5_boolean is_tgs =
777 krb5_principal_is_krbtgt(r->context, r->server->principal);
779 ret = _krb5_kdc_pac_sign_ticket(r->context, r->pac, r->client_princ, serverkey,
780 krbtgtkey, rodc_id, NULL, r->canon_client_princ,
781 add_ticket_sig, et,
782 is_tgs ? &r->pac_attributes : NULL);
783 if (ret)
784 goto out;
788 ret = _kdc_finalize_reply(r);
789 if (ret)
790 goto out;
792 /* It is somewhat unclear where the etype in the following
793 encryption should come from. What we have is a session
794 key in the passed tgt, and a list of preferred etypes
795 *for the new ticket*. Should we pick the best possible
796 etype, given the keytype in the tgt, or should we look
797 at the etype list here as well? What if the tgt
798 session key is DES3 and we want a ticket with a (say)
799 CAST session key. Should the DES3 etype be added to the
800 etype list, even if we don't want a session key with
801 DES3? */
802 ret = _kdc_encode_reply(r->context, r->config, r, b->nonce,
803 serverkey->keytype, kvno,
804 serverkey, 0, r->rk_is_subkey, reply);
805 if (is_weak)
806 krb5_enctype_disable(r->context, serverkey->keytype);
808 _log_astgs_req(r, serverkey->keytype);
810 out:
811 return ret;
814 static krb5_error_code
815 tgs_check_authenticator(krb5_context context,
816 krb5_kdc_configuration *config,
817 krb5_auth_context ac,
818 KDC_REQ_BODY *b,
819 krb5_keyblock *key)
821 krb5_authenticator auth;
822 krb5_error_code ret;
823 krb5_crypto crypto;
825 ret = krb5_auth_con_getauthenticator(context, ac, &auth);
826 if (ret) {
827 kdc_log(context, config, 2,
828 "Out of memory checking PA-TGS Authenticator");
829 goto out;
831 if(auth->cksum == NULL){
832 kdc_log(context, config, 4, "No authenticator in request");
833 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
834 goto out;
837 if (!krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
838 kdc_log(context, config, 4, "Bad checksum type in authenticator: %d",
839 auth->cksum->cksumtype);
840 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
841 goto out;
844 ret = krb5_crypto_init(context, key, 0, &crypto);
845 if (ret) {
846 const char *msg = krb5_get_error_message(context, ret);
847 kdc_log(context, config, 4, "krb5_crypto_init failed: %s", msg);
848 krb5_free_error_message(context, msg);
849 goto out;
853 * RFC4120 says the checksum must be collision-proof, but it does
854 * not require it to be keyed (as the authenticator is encrypted).
856 _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
857 ret = _kdc_verify_checksum(context,
858 crypto,
859 KRB5_KU_TGS_REQ_AUTH_CKSUM,
860 &b->_save,
861 auth->cksum);
862 krb5_crypto_destroy(context, crypto);
863 if(ret){
864 const char *msg = krb5_get_error_message(context, ret);
865 kdc_log(context, config, 4,
866 "Failed to verify authenticator checksum: %s", msg);
867 krb5_free_error_message(context, msg);
869 out:
870 free_Authenticator(auth);
871 free(auth);
872 return ret;
875 static krb5_boolean
876 need_referral(krb5_context context, krb5_kdc_configuration *config,
877 const KDCOptions * const options, krb5_principal server,
878 krb5_realm **realms)
880 const char *name;
882 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
883 return FALSE;
885 if (server->name.name_string.len == 1)
886 name = server->name.name_string.val[0];
887 else if (server->name.name_string.len > 1)
888 name = server->name.name_string.val[1];
889 else
890 return FALSE;
892 kdc_log(context, config, 5, "Searching referral for %s", name);
894 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
897 static krb5_error_code
898 validate_fast_ad(astgs_request_t r, krb5_authdata *auth_data)
900 krb5_error_code ret;
901 krb5_data data;
903 krb5_data_zero(&data);
905 if (!r->config->enable_fast)
906 return 0;
908 ret = _krb5_get_ad(r->context, auth_data, NULL,
909 KRB5_AUTHDATA_FX_FAST_USED, &data);
910 if (ret == 0) {
911 r->fast_asserted = 1;
912 krb5_data_free(&data);
915 ret = _krb5_get_ad(r->context, auth_data, NULL,
916 KRB5_AUTHDATA_FX_FAST_ARMOR, &data);
917 if (ret == 0) {
918 kdc_log(r->context, r->config, 2,
919 "Invalid ticket usage: TGS-REQ contains AD-fx-fast-armor");
920 krb5_data_free(&data);
921 return KRB5KRB_AP_ERR_BAD_INTEGRITY;
924 return 0;
927 static krb5_error_code
928 tgs_parse_request(astgs_request_t r,
929 const PA_DATA *tgs_req,
930 krb5_enctype *krbtgt_etype,
931 const char *from,
932 const struct sockaddr *from_addr,
933 time_t **csec,
934 int **cusec,
935 AuthorizationData **auth_data)
937 krb5_kdc_configuration *config = r->config;
938 KDC_REQ_BODY *b = &r->req.req_body;
939 static char failed[] = "<unparse_name failed>";
940 krb5_ap_req ap_req;
941 krb5_error_code ret;
942 krb5_principal princ;
943 krb5_auth_context ac = NULL;
944 krb5_flags ap_req_options;
945 krb5_flags verify_ap_req_flags = 0;
946 krb5_crypto crypto;
947 krb5uint32 krbtgt_kvno; /* kvno used for the PA-TGS-REQ AP-REQ Ticket */
948 krb5uint32 krbtgt_kvno_try;
949 int kvno_search_tries = 4; /* number of kvnos to try when tkt_vno == 0 */
950 const Keys *krbtgt_keys;/* keyset for TGT tkt_vno */
951 Key *tkey;
952 krb5_keyblock *subkey = NULL;
953 unsigned usage;
955 *auth_data = NULL;
956 *csec = NULL;
957 *cusec = NULL;
959 memset(&ap_req, 0, sizeof(ap_req));
960 ret = krb5_decode_ap_req(r->context, &tgs_req->padata_value, &ap_req);
961 if(ret){
962 const char *msg = krb5_get_error_message(r->context, ret);
963 kdc_log(r->context, config, 4, "Failed to decode AP-REQ: %s", msg);
964 krb5_free_error_message(r->context, msg);
965 goto out;
968 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
969 /* XXX check for ticket.sname == req.sname */
970 kdc_log(r->context, config, 4, "PA-DATA is not a ticket-granting ticket");
971 ret = KRB5KDC_ERR_POLICY; /* ? */
972 goto out;
975 _krb5_principalname2krb5_principal(r->context,
976 &princ,
977 ap_req.ticket.sname,
978 ap_req.ticket.realm);
980 krbtgt_kvno = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0;
981 ret = _kdc_db_fetch(r->context, config, princ, HDB_F_GET_KRBTGT,
982 &krbtgt_kvno, &r->krbtgtdb, &r->krbtgt);
984 if (ret == HDB_ERR_NOT_FOUND_HERE) {
985 /* XXX Factor out this unparsing of the same princ all over */
986 char *p;
987 ret = krb5_unparse_name(r->context, princ, &p);
988 if (ret != 0)
989 p = failed;
990 krb5_free_principal(r->context, princ);
991 kdc_log(r->context, config, 5,
992 "Ticket-granting ticket account %s does not have secrets at "
993 "this KDC, need to proxy", p);
994 if (ret == 0)
995 free(p);
996 ret = HDB_ERR_NOT_FOUND_HERE;
997 goto out;
998 } else if (ret == HDB_ERR_KVNO_NOT_FOUND) {
999 char *p;
1000 ret = krb5_unparse_name(r->context, princ, &p);
1001 if (ret != 0)
1002 p = failed;
1003 krb5_free_principal(r->context, princ);
1004 kdc_log(r->context, config, 5,
1005 "Ticket-granting ticket account %s does not have keys for "
1006 "kvno %d at this KDC", p, krbtgt_kvno);
1007 if (ret == 0)
1008 free(p);
1009 ret = HDB_ERR_KVNO_NOT_FOUND;
1010 goto out;
1011 } else if (ret == HDB_ERR_NO_MKEY) {
1012 char *p;
1013 ret = krb5_unparse_name(r->context, princ, &p);
1014 if (ret != 0)
1015 p = failed;
1016 krb5_free_principal(r->context, princ);
1017 kdc_log(r->context, config, 5,
1018 "Missing master key for decrypting keys for ticket-granting "
1019 "ticket account %s with kvno %d at this KDC", p, krbtgt_kvno);
1020 if (ret == 0)
1021 free(p);
1022 ret = HDB_ERR_KVNO_NOT_FOUND;
1023 goto out;
1024 } else if (ret) {
1025 const char *msg = krb5_get_error_message(r->context, ret);
1026 char *p;
1027 ret = krb5_unparse_name(r->context, princ, &p);
1028 if (ret != 0)
1029 p = failed;
1030 kdc_log(r->context, config, 4,
1031 "Ticket-granting ticket %s not found in database: %s", p, msg);
1032 krb5_free_principal(r->context, princ);
1033 krb5_free_error_message(r->context, msg);
1034 if (ret == 0)
1035 free(p);
1036 ret = KRB5KRB_AP_ERR_NOT_US;
1037 goto out;
1040 krbtgt_kvno_try = krbtgt_kvno ? krbtgt_kvno : r->krbtgt->kvno;
1041 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1043 next_kvno:
1044 krbtgt_keys = hdb_kvno2keys(r->context, r->krbtgt, krbtgt_kvno_try);
1045 ret = hdb_enctype2key(r->context, r->krbtgt, krbtgt_keys,
1046 ap_req.ticket.enc_part.etype, &tkey);
1047 if (ret && krbtgt_kvno == 0 && kvno_search_tries > 0) {
1048 kvno_search_tries--;
1049 krbtgt_kvno_try--;
1050 goto next_kvno;
1051 } else if (ret) {
1052 char *str = NULL, *p = NULL;
1054 /* We should implement the MIT `trace_format()' concept */
1055 (void) krb5_enctype_to_string(r->context, ap_req.ticket.enc_part.etype, &str);
1056 (void) krb5_unparse_name(r->context, princ, &p);
1057 kdc_log(r->context, config, 4,
1058 "No server key with enctype %s found for %s",
1059 str ? str : "<unknown enctype>",
1060 p ? p : "<unparse_name failed>");
1061 free(str);
1062 free(p);
1063 ret = KRB5KRB_AP_ERR_BADKEYVER;
1064 goto out;
1067 if (b->kdc_options.validate)
1068 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1070 if (r->config->warn_ticket_addresses)
1071 verify_ap_req_flags |= KRB5_VERIFY_AP_REQ_IGNORE_ADDRS;
1073 ret = krb5_verify_ap_req2(r->context,
1074 &ac,
1075 &ap_req,
1076 princ,
1077 &tkey->key,
1078 verify_ap_req_flags,
1079 &ap_req_options,
1080 &r->ticket,
1081 KRB5_KU_TGS_REQ_AUTH);
1082 if (r->ticket && r->ticket->ticket.caddr)
1083 kdc_audit_addaddrs((kdc_request_t)r, r->ticket->ticket.caddr, "tixaddrs");
1084 if (r->config->warn_ticket_addresses && ret == KRB5KRB_AP_ERR_BADADDR &&
1085 r->ticket != NULL) {
1086 kdc_audit_setkv_bool((kdc_request_t)r, "wrongaddr", TRUE);
1087 ret = 0;
1089 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY && kvno_search_tries > 0) {
1090 kvno_search_tries--;
1091 krbtgt_kvno_try--;
1092 goto next_kvno;
1095 krb5_free_principal(r->context, princ);
1096 if(ret) {
1097 const char *msg = krb5_get_error_message(r->context, ret);
1098 kdc_log(r->context, config, 4, "Failed to verify AP-REQ: %s", msg);
1099 krb5_free_error_message(r->context, msg);
1100 goto out;
1103 r->ticket_key = tkey;
1106 krb5_authenticator auth;
1108 ret = krb5_auth_con_getauthenticator(r->context, ac, &auth);
1109 if (ret == 0) {
1110 *csec = malloc(sizeof(**csec));
1111 if (*csec == NULL) {
1112 krb5_free_authenticator(r->context, &auth);
1113 kdc_log(r->context, config, 4, "malloc failed");
1114 goto out;
1116 **csec = auth->ctime;
1117 *cusec = malloc(sizeof(**cusec));
1118 if (*cusec == NULL) {
1119 krb5_free_authenticator(r->context, &auth);
1120 kdc_log(r->context, config, 4, "malloc failed");
1121 goto out;
1123 **cusec = auth->cusec;
1125 ret = validate_fast_ad(r, auth->authorization_data);
1126 krb5_free_authenticator(r->context, &auth);
1127 if (ret)
1128 goto out;
1132 ret = tgs_check_authenticator(r->context, config, ac, b, &r->ticket->ticket.key);
1133 if (ret) {
1134 krb5_auth_con_free(r->context, ac);
1135 goto out;
1138 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1139 r->rk_is_subkey = 1;
1141 ret = krb5_auth_con_getremotesubkey(r->context, ac, &subkey);
1142 if(ret){
1143 const char *msg = krb5_get_error_message(r->context, ret);
1144 krb5_auth_con_free(r->context, ac);
1145 kdc_log(r->context, config, 4, "Failed to get remote subkey: %s", msg);
1146 krb5_free_error_message(r->context, msg);
1147 goto out;
1149 if(subkey == NULL){
1150 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1151 r->rk_is_subkey = 0;
1153 ret = krb5_auth_con_getkey(r->context, ac, &subkey);
1154 if(ret) {
1155 const char *msg = krb5_get_error_message(r->context, ret);
1156 krb5_auth_con_free(r->context, ac);
1157 kdc_log(r->context, config, 4, "Failed to get session key: %s", msg);
1158 krb5_free_error_message(r->context, msg);
1159 goto out;
1162 if(subkey == NULL){
1163 krb5_auth_con_free(r->context, ac);
1164 kdc_log(r->context, config, 4,
1165 "Failed to get key for enc-authorization-data");
1166 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1167 goto out;
1170 krb5_free_keyblock_contents(r->context, &r->reply_key);
1171 ret = krb5_copy_keyblock_contents(r->context, subkey, &r->reply_key);
1172 krb5_free_keyblock(r->context, subkey);
1173 if (ret)
1174 goto out;
1176 if (b->enc_authorization_data) {
1177 krb5_data ad;
1179 ret = krb5_crypto_init(r->context, &r->reply_key, 0, &crypto);
1180 if (ret) {
1181 const char *msg = krb5_get_error_message(r->context, ret);
1182 krb5_auth_con_free(r->context, ac);
1183 kdc_log(r->context, config, 4, "krb5_crypto_init failed: %s", msg);
1184 krb5_free_error_message(r->context, msg);
1185 goto out;
1187 ret = krb5_decrypt_EncryptedData (r->context,
1188 crypto,
1189 usage,
1190 b->enc_authorization_data,
1191 &ad);
1192 krb5_crypto_destroy(r->context, crypto);
1193 if(ret){
1194 krb5_auth_con_free(r->context, ac);
1195 kdc_log(r->context, config, 4,
1196 "Failed to decrypt enc-authorization-data");
1197 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1198 goto out;
1200 ALLOC(*auth_data);
1201 if (*auth_data == NULL) {
1202 krb5_auth_con_free(r->context, ac);
1203 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1204 goto out;
1206 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1207 if(ret){
1208 krb5_auth_con_free(r->context, ac);
1209 free(*auth_data);
1210 *auth_data = NULL;
1211 kdc_log(r->context, config, 4, "Failed to decode authorization data");
1212 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1213 goto out;
1217 ret = validate_fast_ad(r, r->ticket->ticket.authorization_data);
1218 if (ret)
1219 goto out;
1223 * Check for FAST request
1226 ret = _kdc_fast_unwrap_request(r, r->ticket, ac);
1227 if (ret)
1228 goto out;
1230 krb5_auth_con_free(r->context, ac);
1232 out:
1233 free_AP_REQ(&ap_req);
1235 return ret;
1238 static krb5_error_code
1239 build_server_referral(krb5_context context,
1240 krb5_kdc_configuration *config,
1241 krb5_crypto session,
1242 krb5_const_realm referred_realm,
1243 const PrincipalName *true_principal_name,
1244 const PrincipalName *requested_principal,
1245 krb5_data *outdata)
1247 PA_ServerReferralData ref;
1248 krb5_error_code ret;
1249 EncryptedData ed;
1250 krb5_data data;
1251 size_t size = 0;
1253 memset(&ref, 0, sizeof(ref));
1255 if (referred_realm) {
1256 ALLOC(ref.referred_realm);
1257 if (ref.referred_realm == NULL)
1258 goto eout;
1259 *ref.referred_realm = strdup(referred_realm);
1260 if (*ref.referred_realm == NULL)
1261 goto eout;
1263 if (true_principal_name) {
1264 ALLOC(ref.true_principal_name);
1265 if (ref.true_principal_name == NULL)
1266 goto eout;
1267 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1268 if (ret)
1269 goto eout;
1271 if (requested_principal) {
1272 ALLOC(ref.requested_principal_name);
1273 if (ref.requested_principal_name == NULL)
1274 goto eout;
1275 ret = copy_PrincipalName(requested_principal,
1276 ref.requested_principal_name);
1277 if (ret)
1278 goto eout;
1281 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1282 data.data, data.length,
1283 &ref, &size, ret);
1284 free_PA_ServerReferralData(&ref);
1285 if (ret)
1286 return ret;
1287 if (data.length != size)
1288 krb5_abortx(context, "internal asn.1 encoder error");
1290 ret = krb5_encrypt_EncryptedData(context, session,
1291 KRB5_KU_PA_SERVER_REFERRAL,
1292 data.data, data.length,
1293 0 /* kvno */, &ed);
1294 free(data.data);
1295 if (ret)
1296 return ret;
1298 ASN1_MALLOC_ENCODE(EncryptedData,
1299 outdata->data, outdata->length,
1300 &ed, &size, ret);
1301 free_EncryptedData(&ed);
1302 if (ret)
1303 return ret;
1304 if (outdata->length != size)
1305 krb5_abortx(context, "internal asn.1 encoder error");
1307 return 0;
1308 eout:
1309 free_PA_ServerReferralData(&ref);
1310 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1311 return ENOMEM;
1315 * This function is intended to be used when failure to find the client is
1316 * acceptable.
1318 krb5_error_code
1319 _kdc_db_fetch_client(krb5_context context,
1320 krb5_kdc_configuration *config,
1321 int flags,
1322 krb5_principal cp,
1323 const char *cpn,
1324 const char *krbtgt_realm,
1325 HDB **clientdb,
1326 hdb_entry **client_out)
1328 krb5_error_code ret;
1329 hdb_entry *client = NULL;
1331 *clientdb = NULL;
1332 *client_out = NULL;
1334 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1335 NULL, clientdb, &client);
1336 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1338 * This is OK, we are just trying to find out if they have
1339 * been disabled or deleted in the meantime; missing secrets
1340 * are OK.
1342 } else if (ret) {
1344 * If the client belongs to the same realm as our TGS, it
1345 * should exist in the local database.
1347 const char *msg;
1349 if (strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1350 if (ret == HDB_ERR_NOENTRY)
1351 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1352 kdc_log(context, config, 4, "Client no longer in database: %s", cpn);
1353 return ret;
1356 msg = krb5_get_error_message(context, ret);
1357 kdc_log(context, config, 4, "Client not found in database: %s", msg);
1358 krb5_free_error_message(context, msg);
1359 } else if (client->flags.invalid || !client->flags.client) {
1360 kdc_log(context, config, 4, "Client has invalid bit set");
1361 _kdc_free_ent(context, *clientdb, client);
1362 return KRB5KDC_ERR_POLICY;
1365 *client_out = client;
1367 return 0;
1370 static krb5_error_code
1371 tgs_build_reply(astgs_request_t priv,
1372 krb5_enctype krbtgt_etype,
1373 AuthorizationData **auth_data,
1374 const struct sockaddr *from_addr)
1376 krb5_context context = priv->context;
1377 krb5_kdc_configuration *config = priv->config;
1378 KDC_REQ_BODY *b = &priv->req.req_body;
1379 const char *from = priv->from;
1380 krb5_error_code ret, ret2;
1381 krb5_principal krbtgt_out_principal = NULL;
1382 krb5_principal user2user_princ = NULL;
1383 char *spn = NULL, *cpn = NULL, *krbtgt_out_n = NULL;
1384 char *user2user_name = NULL;
1385 HDB *user2user_krbtgtdb;
1386 hdb_entry *user2user_krbtgt = NULL;
1387 HDB *clientdb = NULL;
1388 HDB *serverdb = NULL;
1389 krb5_realm ref_realm = NULL;
1390 EncTicketPart *tgt = &priv->ticket->ticket;
1391 const EncryptionKey *ekey;
1392 krb5_keyblock sessionkey;
1393 krb5_kvno kvno;
1394 krb5_pac user2user_pac = NULL;
1395 uint16_t rodc_id;
1396 krb5_boolean add_ticket_sig = FALSE;
1397 const char *tgt_realm = /* Realm of TGT issuer */
1398 krb5_principal_get_realm(context, priv->krbtgt->principal);
1399 const char *our_realm = /* Realm of this KDC */
1400 krb5_principal_get_comp_string(context, priv->krbtgt->principal, 1);
1401 char **capath = NULL;
1402 size_t num_capath = 0;
1404 HDB *krbtgt_outdb;
1405 hdb_entry *krbtgt_out = NULL;
1407 PrincipalName *s;
1408 Realm r;
1409 EncTicketPart adtkt;
1410 char opt_str[128];
1411 krb5_boolean kdc_issued = FALSE;
1413 Key *tkey_sign;
1414 int flags = HDB_F_FOR_TGS_REQ;
1416 int result;
1418 memset(&sessionkey, 0, sizeof(sessionkey));
1419 memset(&adtkt, 0, sizeof(adtkt));
1421 s = b->sname;
1422 r = b->realm;
1425 * The canonicalize KDC option is passed as a hint to the backend, but
1426 * can typically be ignored. Per RFC 6806, names are not canonicalized
1427 * in response to a TGS request (although we make an exception, see
1428 * force-canonicalize below).
1430 if (b->kdc_options.canonicalize)
1431 flags |= HDB_F_CANON;
1433 if (s == NULL) {
1434 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1435 _kdc_set_const_e_text(priv, "No server in request");
1436 goto out;
1439 _krb5_principalname2krb5_principal(context, &priv->server_princ, *s, r);
1440 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1441 if (ret)
1442 goto out;
1443 spn = priv->sname;
1444 _krb5_principalname2krb5_principal(context, &priv->client_princ,
1445 tgt->cname, tgt->crealm);
1446 ret = krb5_unparse_name(context, priv->client_princ, &priv->cname);
1447 if (ret)
1448 goto out;
1449 cpn = priv->cname;
1450 result = unparse_flags(KDCOptions2int(b->kdc_options),
1451 asn1_KDCOptions_units(),
1452 opt_str, sizeof(opt_str));
1453 if (result > 0)
1454 kdc_log(context, config, 4,
1455 "TGS-REQ %s from %s for %s [%s]",
1456 cpn, from, spn, opt_str);
1457 else
1458 kdc_log(context, config, 4,
1459 "TGS-REQ %s from %s for %s", cpn, from, spn);
1462 * Fetch server
1465 server_lookup:
1466 if (priv->server)
1467 _kdc_free_ent(context, serverdb, priv->server);
1468 priv->server = NULL;
1469 ret = _kdc_db_fetch(context, config, priv->server_princ,
1470 HDB_F_GET_SERVER | HDB_F_DELAY_NEW_KEYS | flags,
1471 NULL, &serverdb, &priv->server);
1472 priv->serverdb = serverdb;
1473 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1474 kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", spn);
1475 kdc_audit_addreason((kdc_request_t)priv, "Target not found here");
1476 goto out;
1477 } else if (ret == HDB_ERR_WRONG_REALM) {
1478 free(ref_realm);
1479 ref_realm = strdup(priv->server->principal->realm);
1480 if (ref_realm == NULL) {
1481 ret = krb5_enomem(context);
1482 goto out;
1485 kdc_log(context, config, 4,
1486 "Returning a referral to realm %s for "
1487 "server %s.",
1488 ref_realm, spn);
1489 krb5_free_principal(context, priv->server_princ);
1490 priv->server_princ = NULL;
1491 ret = krb5_make_principal(context, &priv->server_princ, r, KRB5_TGS_NAME,
1492 ref_realm, NULL);
1493 if (ret)
1494 goto out;
1495 free(priv->sname);
1496 priv->sname = NULL;
1497 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1498 if (ret)
1499 goto out;
1500 spn = priv->sname;
1502 goto server_lookup;
1503 } else if (ret) {
1504 const char *new_rlm, *msg;
1505 Realm req_rlm;
1506 krb5_realm *realms;
1508 priv->error_code = ret; /* advise policy plugin of failure reason */
1509 ret2 = _kdc_referral_policy(priv);
1510 if (ret2 == 0) {
1511 krb5_xfree(priv->sname);
1512 priv->sname = NULL;
1513 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1514 if (ret)
1515 goto out;
1516 goto server_lookup;
1517 } else if (ret2 != KRB5_PLUGIN_NO_HANDLE) {
1518 ret = ret2;
1519 } else if ((req_rlm = get_krbtgt_realm(&priv->server_princ->name)) != NULL) {
1520 if (capath == NULL) {
1521 /* With referalls, hierarchical capaths are always enabled */
1522 ret2 = _krb5_find_capath(context, tgt->crealm, our_realm,
1523 req_rlm, TRUE, &capath, &num_capath);
1524 if (ret2) {
1525 ret = ret2;
1526 kdc_audit_addreason((kdc_request_t)priv,
1527 "No trusted path from client realm to ours");
1528 goto out;
1531 new_rlm = num_capath > 0 ? capath[--num_capath] : NULL;
1532 if (new_rlm) {
1533 kdc_log(context, config, 5, "krbtgt from %s via %s for "
1534 "realm %s not found, trying %s", tgt->crealm,
1535 our_realm, req_rlm, new_rlm);
1537 free(ref_realm);
1538 ref_realm = strdup(new_rlm);
1539 if (ref_realm == NULL) {
1540 ret = krb5_enomem(context);
1541 goto out;
1544 krb5_free_principal(context, priv->server_princ);
1545 priv->server_princ = NULL;
1546 krb5_make_principal(context, &priv->server_princ, r,
1547 KRB5_TGS_NAME, ref_realm, NULL);
1548 free(priv->sname);
1549 priv->sname = NULL;
1550 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1551 if (ret)
1552 goto out;
1553 spn = priv->sname;
1554 goto server_lookup;
1556 } else if (need_referral(context, config, &b->kdc_options, priv->server_princ, &realms)) {
1557 if (strcmp(realms[0], priv->server_princ->realm) != 0) {
1558 kdc_log(context, config, 4,
1559 "Returning a referral to realm %s for "
1560 "server %s that was not found",
1561 realms[0], spn);
1562 krb5_free_principal(context, priv->server_princ);
1563 priv->server_princ = NULL;
1564 krb5_make_principal(context, &priv->server_princ, r, KRB5_TGS_NAME,
1565 realms[0], NULL);
1566 free(priv->sname);
1567 priv->sname = NULL;
1568 ret = krb5_unparse_name(context, priv->server_princ, &priv->sname);
1569 if (ret) {
1570 krb5_free_host_realm(context, realms);
1571 goto out;
1573 spn = priv->sname;
1575 free(ref_realm);
1576 ref_realm = strdup(realms[0]);
1578 krb5_free_host_realm(context, realms);
1579 goto server_lookup;
1581 krb5_free_host_realm(context, realms);
1583 msg = krb5_get_error_message(context, ret);
1584 kdc_log(context, config, 3,
1585 "Server not found in database: %s: %s", spn, msg);
1586 krb5_free_error_message(context, msg);
1587 if (ret == HDB_ERR_NOENTRY)
1588 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1589 kdc_audit_addreason((kdc_request_t)priv,
1590 "Service principal unknown");
1591 goto out;
1595 * Now refetch the primary krbtgt, and get the current kvno (the
1596 * sign check may have been on an old kvno, and the server may
1597 * have been an incoming trust)
1600 ret = krb5_make_principal(context,
1601 &krbtgt_out_principal,
1602 our_realm,
1603 KRB5_TGS_NAME,
1604 our_realm,
1605 NULL);
1606 if (ret) {
1607 kdc_log(context, config, 4,
1608 "Failed to make krbtgt principal name object for "
1609 "authz-data signatures");
1610 goto out;
1612 ret = krb5_unparse_name(context, krbtgt_out_principal, &krbtgt_out_n);
1613 if (ret) {
1614 kdc_log(context, config, 4,
1615 "Failed to make krbtgt principal name object for "
1616 "authz-data signatures");
1617 goto out;
1620 ret = _kdc_db_fetch(context, config, krbtgt_out_principal,
1621 HDB_F_GET_KRBTGT, NULL, &krbtgt_outdb, &krbtgt_out);
1622 if (ret) {
1623 char *ktpn = NULL;
1624 ret = krb5_unparse_name(context, priv->krbtgt->principal, &ktpn);
1625 kdc_log(context, config, 4,
1626 "No such principal %s (needed for authz-data signature keys) "
1627 "while processing TGS-REQ for service %s with krbtgt %s",
1628 krbtgt_out_n, spn, (ret == 0) ? ktpn : "<unknown>");
1629 free(ktpn);
1630 ret = KRB5KRB_AP_ERR_NOT_US;
1631 goto out;
1635 * Select enctype, return key and kvno.
1639 krb5_enctype etype;
1641 if(b->kdc_options.enc_tkt_in_skey) {
1642 Ticket *t;
1643 krb5_principal p;
1644 Key *uukey;
1645 krb5uint32 second_kvno = 0;
1646 krb5uint32 *kvno_ptr = NULL;
1647 size_t i;
1648 HDB *user2user_db;
1649 hdb_entry *user2user_client = NULL;
1650 krb5_boolean user2user_kdc_issued = FALSE;
1651 char *tpn;
1653 if(b->additional_tickets == NULL ||
1654 b->additional_tickets->len == 0){
1655 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1656 kdc_log(context, config, 4,
1657 "No second ticket present in user-to-user request");
1658 kdc_audit_addreason((kdc_request_t)priv,
1659 "No second ticket present in user-to-user request");
1660 goto out;
1662 t = &b->additional_tickets->val[0];
1663 if(!get_krbtgt_realm(&t->sname)){
1664 kdc_log(context, config, 4,
1665 "Additional ticket is not a ticket-granting ticket");
1666 kdc_audit_addreason((kdc_request_t)priv,
1667 "Additional ticket is not a ticket-granting ticket");
1668 ret = KRB5KDC_ERR_POLICY;
1669 goto out;
1671 ret = _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1672 if (ret)
1673 goto out;
1675 ret = krb5_unparse_name(context, p, &tpn);
1676 if (ret)
1677 goto out;
1678 if(t->enc_part.kvno){
1679 second_kvno = *t->enc_part.kvno;
1680 kvno_ptr = &second_kvno;
1682 ret = _kdc_db_fetch(context, config, p,
1683 HDB_F_GET_KRBTGT, kvno_ptr,
1684 &user2user_krbtgtdb, &user2user_krbtgt);
1685 krb5_free_principal(context, p);
1686 if(ret){
1687 if (ret == HDB_ERR_NOENTRY)
1688 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1689 kdc_audit_addreason((kdc_request_t)priv,
1690 "User-to-user service principal (TGS) unknown");
1691 krb5_xfree(tpn);
1692 goto out;
1694 ret = hdb_enctype2key(context, user2user_krbtgt, NULL,
1695 t->enc_part.etype, &uukey);
1696 if(ret){
1697 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1698 kdc_audit_addreason((kdc_request_t)priv,
1699 "User-to-user enctype not supported");
1700 krb5_xfree(tpn);
1701 goto out;
1703 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1704 if(ret) {
1705 kdc_audit_addreason((kdc_request_t)priv,
1706 "User-to-user TGT decrypt failure");
1707 krb5_xfree(tpn);
1708 goto out;
1711 ret = _kdc_verify_flags(context, config, &adtkt, tpn);
1712 if (ret) {
1713 kdc_audit_addreason((kdc_request_t)priv,
1714 "User-to-user TGT expired or invalid");
1715 krb5_xfree(tpn);
1716 goto out;
1718 krb5_xfree(tpn);
1720 /* Fetch the name from the TGT. */
1721 ret = _krb5_principalname2krb5_principal(context, &user2user_princ,
1722 adtkt.cname, adtkt.crealm);
1723 if (ret)
1724 goto out;
1726 ret = krb5_unparse_name(context, user2user_princ, &user2user_name);
1727 if (ret)
1728 goto out;
1731 * Look up the name given in the TGT in the database. The user
1732 * claims to have a ticket-granting-ticket to our KDC, so we should
1733 * fail hard if we can't find the user - otherwise we can't do
1734 * proper checks.
1736 ret = _kdc_db_fetch(context, config, user2user_princ,
1737 HDB_F_GET_CLIENT | flags,
1738 NULL, &user2user_db, &user2user_client);
1739 if (ret == HDB_ERR_NOENTRY)
1740 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1741 if (ret)
1742 goto out;
1745 * The account is present in the database, now check the
1746 * account flags.
1748 * We check this as a client (because the purpose of
1749 * user2user is that the server flag is not set, because
1750 * the long-term key is not strong, but this does mean
1751 * that a client with an expired password can't get accept
1752 * a user2user ticket.
1754 ret = kdc_check_flags(priv,
1755 FALSE,
1756 user2user_client,
1757 NULL);
1758 if (ret) {
1759 _kdc_free_ent(context, user2user_db, user2user_client);
1760 goto out;
1764 * Also check that the account is the same one specified in the
1765 * request.
1767 ret = _kdc_check_client_matches_target_service(context,
1768 config,
1769 serverdb,
1770 priv->server,
1771 user2user_client,
1772 user2user_princ);
1773 if (ret) {
1774 _kdc_free_ent(context, user2user_db, user2user_client);
1775 goto out;
1778 /* Verify the PAC of the TGT. */
1779 ret = _kdc_check_pac(priv, user2user_princ, NULL,
1780 user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt,
1781 &uukey->key, &priv->ticket_key->key, &adtkt,
1782 &user2user_kdc_issued, &user2user_pac, NULL, NULL);
1783 _kdc_free_ent(context, user2user_db, user2user_client);
1784 if (ret) {
1785 const char *msg = krb5_get_error_message(context, ret);
1786 kdc_log(context, config, 0,
1787 "Verify PAC failed for %s (%s) from %s with %s",
1788 spn, user2user_name, from, msg);
1789 krb5_free_error_message(context, msg);
1790 goto out;
1793 if ((config->require_pac && !user2user_pac)
1794 || (user2user_pac && !user2user_kdc_issued))
1796 ret = KRB5KDC_ERR_BADOPTION;
1797 kdc_log(context, config, 0,
1798 "Ticket not signed with PAC; user-to-user failed (%s).",
1799 user2user_pac ? "Ticket unsigned" : "No PAC");
1800 goto out;
1803 ekey = &adtkt.key;
1804 for(i = 0; i < b->etype.len; i++)
1805 if (b->etype.val[i] == adtkt.key.keytype)
1806 break;
1807 if(i == b->etype.len) {
1808 kdc_log(context, config, 4,
1809 "Addition ticket has no matching etypes");
1810 krb5_clear_error_message(context);
1811 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1812 kdc_audit_addreason((kdc_request_t)priv,
1813 "No matching enctypes for 2nd ticket");
1814 goto out;
1816 etype = b->etype.val[i];
1817 kvno = 0;
1818 } else {
1819 Key *skey;
1821 ret = _kdc_find_etype(priv, krb5_principal_is_krbtgt(context, priv->server_princ)
1822 ? KFE_IS_TGS : 0,
1823 b->etype.val, b->etype.len, &etype, NULL,
1824 NULL);
1825 if(ret) {
1826 kdc_log(context, config, 4,
1827 "Server (%s) has no support for etypes", spn);
1828 kdc_audit_addreason((kdc_request_t)priv,
1829 "Enctype not supported");
1830 goto out;
1832 ret = _kdc_get_preferred_key(context, config, priv->server, spn,
1833 NULL, &skey);
1834 if(ret) {
1835 kdc_log(context, config, 4,
1836 "Server (%s) has no supported etypes", spn);
1837 kdc_audit_addreason((kdc_request_t)priv,
1838 "Enctype not supported");
1839 goto out;
1841 ekey = &skey->key;
1842 kvno = priv->server->kvno;
1845 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1846 if (ret)
1847 goto out;
1851 * Check that service is in the same realm as the krbtgt. If it's
1852 * not the same, it's someone that is using a uni-directional trust
1853 * backward.
1857 * The first realm is the realm of the service, the second is
1858 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1859 * encrypted to. The redirection via the krbtgt_out entry allows
1860 * the DB to possibly correct the case of the realm (Samba4 does
1861 * this) before the strcmp()
1863 if (strcmp(krb5_principal_get_realm(context, priv->server->principal),
1864 krb5_principal_get_realm(context, krbtgt_out->principal)) != 0) {
1865 char *ktpn;
1866 ret = krb5_unparse_name(context, krbtgt_out->principal, &ktpn);
1867 kdc_log(context, config, 4,
1868 "Request with wrong krbtgt: %s",
1869 (ret == 0) ? ktpn : "<unknown>");
1870 if(ret == 0)
1871 free(ktpn);
1872 ret = KRB5KRB_AP_ERR_NOT_US;
1873 kdc_audit_addreason((kdc_request_t)priv, "Request with wrong TGT");
1874 goto out;
1877 ret = _kdc_get_preferred_key(context, config, krbtgt_out, krbtgt_out_n,
1878 NULL, &tkey_sign);
1879 if (ret) {
1880 kdc_log(context, config, 4,
1881 "Failed to find key for krbtgt PAC signature");
1882 kdc_audit_addreason((kdc_request_t)priv,
1883 "Failed to find key for krbtgt PAC signature");
1884 goto out;
1886 ret = hdb_enctype2key(context, krbtgt_out, NULL,
1887 tkey_sign->key.keytype, &tkey_sign);
1888 if(ret) {
1889 kdc_log(context, config, 4,
1890 "Failed to find key for krbtgt PAC signature");
1891 kdc_audit_addreason((kdc_request_t)priv,
1892 "Failed to find key for krbtgt PAC signature");
1893 goto out;
1896 if (_kdc_synthetic_princ_used_p(context, priv->ticket))
1897 flags |= HDB_F_SYNTHETIC_OK;
1899 ret = _kdc_db_fetch_client(context, config, flags, priv->client_princ,
1900 cpn, our_realm, &clientdb, &priv->client);
1901 if (ret)
1902 goto out;
1903 /* flags &= ~HDB_F_SYNTHETIC_OK; */ /* `flags' is not used again below */
1904 priv->clientdb = clientdb;
1906 ret = _kdc_check_pac(priv, priv->client_princ, NULL,
1907 priv->client, priv->server,
1908 priv->krbtgt, priv->krbtgt,
1909 &priv->ticket_key->key, &priv->ticket_key->key, tgt,
1910 &kdc_issued, &priv->pac, &priv->canon_client_princ,
1911 &priv->pac_attributes);
1912 if (ret) {
1913 const char *msg = krb5_get_error_message(context, ret);
1914 kdc_audit_addreason((kdc_request_t)priv, "PAC check failed");
1915 kdc_log(context, config, 4,
1916 "Verify PAC failed for %s (%s) from %s with %s",
1917 spn, cpn, from, msg);
1918 krb5_free_error_message(context, msg);
1919 goto out;
1923 * Process request
1927 * Services for User: protocol transition and constrained delegation
1930 ret = _kdc_validate_services_for_user(priv);
1931 if (ret)
1932 goto out;
1935 * Check flags
1938 ret = kdc_check_flags(priv, FALSE, priv->client, priv->server);
1939 if(ret)
1940 goto out;
1942 if((b->kdc_options.validate || b->kdc_options.renew) &&
1943 !krb5_principal_compare(context,
1944 priv->krbtgt->principal,
1945 priv->server->principal)){
1946 kdc_audit_addreason((kdc_request_t)priv, "Inconsistent request");
1947 kdc_log(context, config, 4, "Inconsistent request.");
1948 ret = KRB5KDC_ERR_SERVER_NOMATCH;
1949 goto out;
1952 /* check for valid set of addresses */
1953 if (!_kdc_check_addresses(priv, tgt->caddr, from_addr)) {
1954 if (config->check_ticket_addresses) {
1955 ret = KRB5KRB_AP_ERR_BADADDR;
1956 kdc_audit_setkv_bool((kdc_request_t)priv, "wrongaddr", TRUE);
1957 kdc_log(context, config, 4, "Request from wrong address");
1958 kdc_audit_addreason((kdc_request_t)priv, "Request from wrong address");
1959 goto out;
1960 } else if (config->warn_ticket_addresses) {
1961 kdc_audit_setkv_bool((kdc_request_t)priv, "wrongaddr", TRUE);
1965 /* check local and per-principal anonymous ticket issuance policy */
1966 if (is_anon_tgs_request_p(b, tgt)) {
1967 ret = _kdc_check_anon_policy(priv);
1968 if (ret)
1969 goto out;
1973 * If this is an referral, add server referral data to the
1974 * auth_data reply .
1976 if (ref_realm) {
1977 PA_DATA pa;
1978 krb5_crypto crypto;
1980 kdc_log(context, config, 3,
1981 "Adding server referral to %s", ref_realm);
1983 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
1984 if (ret)
1985 goto out;
1987 ret = build_server_referral(context, config, crypto, ref_realm,
1988 NULL, s, &pa.padata_value);
1989 krb5_crypto_destroy(context, crypto);
1990 if (ret) {
1991 kdc_audit_addreason((kdc_request_t)priv, "Referral build failed");
1992 kdc_log(context, config, 4,
1993 "Failed building server referral");
1994 goto out;
1996 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
1998 ret = add_METHOD_DATA(priv->rep.padata, &pa);
1999 krb5_data_free(&pa.padata_value);
2000 if (ret) {
2001 kdc_log(context, config, 4,
2002 "Add server referral METHOD-DATA failed");
2003 goto out;
2008 * Only add ticket signature if the requested server is not krbtgt, and
2009 * either the header server is krbtgt or, in the case of renewal/validation
2010 * if it was signed with PAC ticket signature and we verified it.
2011 * Currently Heimdal only allows renewal of krbtgt anyway but that might
2012 * change one day (see issue #763) so make sure to check for it.
2015 if (kdc_issued &&
2016 !krb5_principal_is_krbtgt(context, priv->server->principal)) {
2018 /* Validate armor TGT before potentially including device claims */
2019 if (priv->armor_ticket) {
2020 ret = _kdc_fast_check_armor_pac(priv, HDB_F_FOR_TGS_REQ);
2021 if (ret)
2022 goto out;
2025 add_ticket_sig = TRUE;
2029 * Active-Directory implementations use the high part of the kvno as the
2030 * read-only-dc identifier, we need to embed it in the PAC KDC signatures.
2033 rodc_id = krbtgt_out->kvno >> 16;
2039 ret = tgs_make_reply(priv,
2040 tgt,
2041 ekey,
2042 &tkey_sign->key,
2043 &sessionkey,
2044 kvno,
2045 *auth_data,
2046 tgt_realm,
2047 rodc_id,
2048 add_ticket_sig);
2050 out:
2051 free(user2user_name);
2052 free(krbtgt_out_n);
2053 _krb5_free_capath(context, capath);
2055 krb5_free_keyblock_contents(context, &sessionkey);
2056 if(krbtgt_out)
2057 _kdc_free_ent(context, krbtgt_outdb, krbtgt_out);
2058 if(user2user_krbtgt)
2059 _kdc_free_ent(context, user2user_krbtgtdb, user2user_krbtgt);
2061 krb5_free_principal(context, user2user_princ);
2062 krb5_free_principal(context, krbtgt_out_principal);
2063 free(ref_realm);
2065 free_EncTicketPart(&adtkt);
2067 krb5_pac_free(context, user2user_pac);
2069 return ret;
2076 krb5_error_code
2077 _kdc_tgs_rep(astgs_request_t r)
2079 krb5_kdc_configuration *config = r->config;
2080 KDC_REQ *req = &r->req;
2081 krb5_data *data = r->reply;
2082 const char *from = r->from;
2083 struct sockaddr *from_addr = r->addr;
2084 int datagram_reply = r->datagram_reply;
2085 AuthorizationData *auth_data = NULL;
2086 krb5_error_code ret;
2087 int i = 0;
2088 const PA_DATA *tgs_req, *pa;
2089 krb5_enctype krbtgt_etype = ETYPE_NULL;
2091 time_t *csec = NULL;
2092 int *cusec = NULL;
2094 r->e_text = NULL;
2096 if(req->padata == NULL){
2097 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2098 kdc_log(r->context, config, 4,
2099 "TGS-REQ from %s without PA-DATA", from);
2100 goto out;
2103 i = 0;
2104 pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST_ARMOR);
2105 if (pa) {
2106 kdc_log(r->context, r->config, 10, "Found TGS-REQ FAST armor inside TGS-REQ pa-data");
2107 ret = KRB5KRB_ERR_GENERIC;
2108 goto out;
2111 i = 0;
2112 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2113 if(tgs_req == NULL){
2114 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2116 kdc_log(r->context, config, 4,
2117 "TGS-REQ from %s without PA-TGS-REQ", from);
2118 goto out;
2120 ret = tgs_parse_request(r, tgs_req,
2121 &krbtgt_etype,
2122 from, from_addr,
2123 &csec, &cusec,
2124 &auth_data);
2125 if (ret == HDB_ERR_NOT_FOUND_HERE) {
2126 /* kdc_log() is called in tgs_parse_request() */
2127 goto out;
2129 if (ret) {
2130 kdc_log(r->context, config, 4,
2131 "Failed parsing TGS-REQ from %s", from);
2132 goto out;
2135 ret = _kdc_fast_strengthen_reply_key(r);
2136 if (ret)
2137 goto out;
2139 ALLOC(r->rep.padata);
2140 if (r->rep.padata == NULL) {
2141 ret = ENOMEM;
2142 krb5_set_error_message(r->context, ret, N_("malloc: out of memory", ""));
2143 goto out;
2146 ret = tgs_build_reply(r,
2147 krbtgt_etype,
2148 &auth_data,
2149 from_addr);
2150 if (ret) {
2151 kdc_log(r->context, config, 4,
2152 "Failed building TGS-REP to %s", from);
2153 goto out;
2156 /* */
2157 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2158 krb5_data_free(data);
2159 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2160 _kdc_set_const_e_text(r, "Reply packet too large");
2163 out:
2164 if (ret) {
2165 /* Overwrite ‘error_code’ only if we have an actual error. */
2166 r->error_code = ret;
2169 krb5_error_code ret2 = _kdc_audit_request(r);
2170 if (ret2) {
2171 krb5_data_free(data);
2172 ret = ret2;
2176 if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2177 METHOD_DATA error_method = { 0, NULL };
2179 kdc_log(r->context, config, 5, "tgs-req: sending error: %d to client", ret);
2180 ret = _kdc_fast_mk_error(r,
2181 &error_method,
2182 r->armor_crypto,
2183 &req->req_body,
2184 r->error_code ? r->error_code : ret,
2185 r->client_princ ? r->client_princ :(r->ticket != NULL ? r->ticket->client : NULL),
2186 r->server_princ ? r->server_princ :(r->ticket != NULL ? r->ticket->server : NULL),
2187 csec, cusec,
2188 data);
2189 free_METHOD_DATA(&error_method);
2191 free(csec);
2192 free(cusec);
2194 free_TGS_REP(&r->rep);
2195 free_TransitedEncoding(&r->et.transited);
2196 free(r->et.starttime);
2197 free(r->et.renew_till);
2198 if(r->et.authorization_data) {
2199 free_AuthorizationData(r->et.authorization_data);
2200 free(r->et.authorization_data);
2202 free_LastReq(&r->ek.last_req);
2203 if (r->et.key.keyvalue.data) {
2204 memset_s(r->et.key.keyvalue.data, 0, r->et.key.keyvalue.length,
2205 r->et.key.keyvalue.length);
2207 free_EncryptionKey(&r->et.key);
2209 if (r->canon_client_princ) {
2210 krb5_free_principal(r->context, r->canon_client_princ);
2211 r->canon_client_princ = NULL;
2213 if (r->armor_crypto) {
2214 krb5_crypto_destroy(r->context, r->armor_crypto);
2215 r->armor_crypto = NULL;
2217 if (r->armor_ticket)
2218 krb5_free_ticket(r->context, r->armor_ticket);
2219 if (r->armor_server)
2220 _kdc_free_ent(r->context, r->armor_serverdb, r->armor_server);
2221 if (r->explicit_armor_client)
2222 _kdc_free_ent(r->context,
2223 r->explicit_armor_clientdb,
2224 r->explicit_armor_client);
2225 if (r->explicit_armor_pac)
2226 krb5_pac_free(r->context, r->explicit_armor_pac);
2227 krb5_free_keyblock_contents(r->context, &r->reply_key);
2228 krb5_free_keyblock_contents(r->context, &r->strengthen_key);
2230 if (r->ticket)
2231 krb5_free_ticket(r->context, r->ticket);
2232 if (r->krbtgt)
2233 _kdc_free_ent(r->context, r->krbtgtdb, r->krbtgt);
2235 if (r->client)
2236 _kdc_free_ent(r->context, r->clientdb, r->client);
2237 krb5_free_principal(r->context, r->client_princ);
2238 if (r->server)
2239 _kdc_free_ent(r->context, r->serverdb, r->server);
2240 krb5_free_principal(r->context, r->server_princ);
2241 _kdc_free_fast_state(&r->fast);
2242 krb5_pac_free(r->context, r->pac);
2244 if (auth_data) {
2245 free_AuthorizationData(auth_data);
2246 free(auth_data);
2249 return ret;