heimdal:kdc: Do not generate extra PAC buffers for S4U2Self service ticket
[Samba.git] / source4 / heimdal / kdc / krb5tgs.c
blob38dba8493ae1cab2873f6ce81613681a4515d340
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;
54 static krb5_error_code
55 check_PAC(krb5_context context,
56 krb5_kdc_configuration *config,
57 const krb5_principal client_principal,
58 const krb5_principal delegated_proxy_principal,
59 hdb_entry_ex *client,
60 hdb_entry_ex *server,
61 hdb_entry_ex *krbtgt,
62 hdb_entry_ex *ticket_server,
63 const EncryptionKey *server_check_key,
64 const EncryptionKey *krbtgt_check_key,
65 EncTicketPart *tkt,
66 krb5_boolean *kdc_issued,
67 krb5_pac *ppac)
69 krb5_pac pac = NULL;
70 krb5_error_code ret;
71 krb5_boolean signedticket;
73 *kdc_issued = FALSE;
74 *ppac = NULL;
76 ret = _krb5_kdc_pac_ticket_parse(context, tkt, &signedticket, &pac);
77 if (ret)
78 return ret;
80 if (pac == NULL)
81 return KRB5KDC_ERR_TGT_REVOKED;
83 /* Verify the server signature. */
84 ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal,
85 server_check_key, NULL);
86 if (ret) {
87 krb5_pac_free(context, pac);
88 return ret;
91 /* Verify the KDC signatures. */
92 ret = _kdc_pac_verify(context, client_principal, delegated_proxy_principal,
93 client, server, krbtgt, &pac);
94 if (ret == KRB5_PLUGIN_NO_HANDLE) {
96 * We can't verify the KDC signatures if the ticket was issued by
97 * another realm's KDC.
99 if (krb5_realm_compare(context, server->entry.principal,
100 ticket_server->entry.principal)) {
101 ret = krb5_pac_verify(context, pac, 0, NULL, NULL,
102 krbtgt_check_key);
103 if (ret) {
104 krb5_pac_free(context, pac);
105 return ret;
108 /* Discard the PAC if the plugin didn't handle it */
109 krb5_pac_free(context, pac);
110 ret = krb5_pac_init(context, &pac);
111 if (ret)
112 return ret;
113 } else if (ret) {
114 krb5_pac_free(context, pac);
115 return ret;
118 *kdc_issued = signedticket ||
119 krb5_principal_is_krbtgt(context,
120 ticket_server->entry.principal);
121 *ppac = pac;
123 return 0;
130 static krb5_error_code
131 check_tgs_flags(krb5_context context,
132 krb5_kdc_configuration *config,
133 KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
135 KDCOptions f = b->kdc_options;
137 if(f.validate){
138 if(!tgt->flags.invalid || tgt->starttime == NULL){
139 kdc_log(context, config, 0,
140 "Bad request to validate ticket");
141 return KRB5KDC_ERR_BADOPTION;
143 if(*tgt->starttime > kdc_time){
144 kdc_log(context, config, 0,
145 "Early request to validate ticket");
146 return KRB5KRB_AP_ERR_TKT_NYV;
148 /* XXX tkt = tgt */
149 et->flags.invalid = 0;
150 }else if(tgt->flags.invalid){
151 kdc_log(context, config, 0,
152 "Ticket-granting ticket has INVALID flag set");
153 return KRB5KRB_AP_ERR_TKT_INVALID;
156 if(f.forwardable){
157 if(!tgt->flags.forwardable){
158 kdc_log(context, config, 0,
159 "Bad request for forwardable ticket");
160 return KRB5KDC_ERR_BADOPTION;
162 et->flags.forwardable = 1;
164 if(f.forwarded){
165 if(!tgt->flags.forwardable){
166 kdc_log(context, config, 0,
167 "Request to forward non-forwardable ticket");
168 return KRB5KDC_ERR_BADOPTION;
170 et->flags.forwarded = 1;
171 et->caddr = b->addresses;
173 if(tgt->flags.forwarded)
174 et->flags.forwarded = 1;
176 if(f.proxiable){
177 if(!tgt->flags.proxiable){
178 kdc_log(context, config, 0,
179 "Bad request for proxiable ticket");
180 return KRB5KDC_ERR_BADOPTION;
182 et->flags.proxiable = 1;
184 if(f.proxy){
185 if(!tgt->flags.proxiable){
186 kdc_log(context, config, 0,
187 "Request to proxy non-proxiable ticket");
188 return KRB5KDC_ERR_BADOPTION;
190 et->flags.proxy = 1;
191 et->caddr = b->addresses;
193 if(tgt->flags.proxy)
194 et->flags.proxy = 1;
196 if(f.allow_postdate){
197 if(!tgt->flags.may_postdate){
198 kdc_log(context, config, 0,
199 "Bad request for post-datable ticket");
200 return KRB5KDC_ERR_BADOPTION;
202 et->flags.may_postdate = 1;
204 if(f.postdated){
205 if(!tgt->flags.may_postdate){
206 kdc_log(context, config, 0,
207 "Bad request for postdated ticket");
208 return KRB5KDC_ERR_BADOPTION;
210 if(b->from)
211 *et->starttime = *b->from;
212 et->flags.postdated = 1;
213 et->flags.invalid = 1;
214 }else if(b->from && *b->from > kdc_time + context->max_skew){
215 kdc_log(context, config, 0, "Ticket cannot be postdated");
216 return KRB5KDC_ERR_CANNOT_POSTDATE;
219 if(f.renewable){
220 if(!tgt->flags.renewable || tgt->renew_till == NULL){
221 kdc_log(context, config, 0,
222 "Bad request for renewable ticket");
223 return KRB5KDC_ERR_BADOPTION;
225 et->flags.renewable = 1;
226 ALLOC(et->renew_till);
227 _kdc_fix_time(&b->rtime);
228 *et->renew_till = *b->rtime;
230 if(f.renew){
231 time_t old_life;
232 if(!tgt->flags.renewable || tgt->renew_till == NULL){
233 kdc_log(context, config, 0,
234 "Request to renew non-renewable ticket");
235 return KRB5KDC_ERR_BADOPTION;
237 old_life = tgt->endtime;
238 if(tgt->starttime)
239 old_life -= *tgt->starttime;
240 else
241 old_life -= tgt->authtime;
242 et->endtime = *et->starttime + old_life;
243 if (et->renew_till != NULL)
244 et->endtime = min(*et->renew_till, et->endtime);
247 #if 0
248 /* checks for excess flags */
249 if(f.request_anonymous && !config->allow_anonymous){
250 kdc_log(context, config, 0,
251 "Request for anonymous ticket");
252 return KRB5KDC_ERR_BADOPTION;
254 #endif
255 return 0;
259 * Determine if constrained delegation is allowed from this client to this server
262 static krb5_error_code
263 check_constrained_delegation(krb5_context context,
264 krb5_kdc_configuration *config,
265 HDB *clientdb,
266 hdb_entry_ex *client,
267 hdb_entry_ex *server,
268 krb5_const_principal target)
270 const HDB_Ext_Constrained_delegation_acl *acl;
271 krb5_error_code ret;
272 size_t i;
275 * constrained_delegation (S4U2Proxy) only works within
276 * the same realm. We use the already canonicalized version
277 * of the principals here, while "target" is the principal
278 * provided by the client.
280 if(!krb5_realm_compare(context, client->entry.principal, server->entry.principal)) {
281 ret = KRB5KDC_ERR_BADOPTION;
282 kdc_log(context, config, 0,
283 "Bad request for constrained delegation");
284 return ret;
287 if (clientdb->hdb_check_constrained_delegation) {
288 ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, target);
289 if (ret == 0)
290 return 0;
291 } else {
292 /* if client delegates to itself, that ok */
293 if (krb5_principal_compare(context, client->entry.principal, server->entry.principal) == TRUE)
294 return 0;
296 ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
297 if (ret) {
298 krb5_clear_error_message(context);
299 return ret;
302 if (acl) {
303 for (i = 0; i < acl->len; i++) {
304 if (krb5_principal_compare(context, target, &acl->val[i]) == TRUE)
305 return 0;
308 ret = KRB5KDC_ERR_BADOPTION;
310 kdc_log(context, config, 0,
311 "Bad request for constrained delegation");
312 return ret;
316 * Determine if s4u2self is allowed from this client to this server
318 * For example, regardless of the principal being impersonated, if the
319 * 'client' and 'server' (target) are the same, then it's safe.
322 static krb5_error_code
323 check_s4u2self(krb5_context context,
324 krb5_kdc_configuration *config,
325 HDB *clientdb,
326 hdb_entry_ex *client,
327 hdb_entry_ex *target_server,
328 krb5_const_principal target_server_principal)
330 krb5_error_code ret;
333 * Always allow the plugin to check, this might be faster, allow a
334 * policy or audit check and can look into the DB records
335 * directly
337 if (clientdb->hdb_check_s4u2self) {
338 ret = clientdb->hdb_check_s4u2self(context,
339 clientdb,
340 client,
341 target_server);
342 if (ret == 0)
343 return 0;
344 } else if (krb5_principal_compare(context,
345 client->entry.principal,
346 target_server_principal) == TRUE) {
347 /* if client does a s4u2self to itself, and there is no plugin, that is ok */
348 return 0;
349 } else {
350 ret = KRB5KDC_ERR_BADOPTION;
352 return ret;
359 static krb5_error_code
360 verify_flags (krb5_context context,
361 krb5_kdc_configuration *config,
362 const EncTicketPart *et,
363 const char *pstr)
365 if(et->endtime < kdc_time){
366 kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
367 return KRB5KRB_AP_ERR_TKT_EXPIRED;
369 if(et->flags.invalid){
370 kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
371 return KRB5KRB_AP_ERR_TKT_NYV;
373 return 0;
380 static krb5_error_code
381 fix_transited_encoding(krb5_context context,
382 krb5_kdc_configuration *config,
383 krb5_boolean check_policy,
384 const TransitedEncoding *tr,
385 EncTicketPart *et,
386 const char *client_realm,
387 const char *server_realm,
388 const char *tgt_realm)
390 krb5_error_code ret = 0;
391 char **realms, **tmp;
392 unsigned int num_realms;
393 size_t i;
395 switch (tr->tr_type) {
396 case DOMAIN_X500_COMPRESS:
397 break;
398 case 0:
400 * Allow empty content of type 0 because that is was Microsoft
401 * generates in their TGT.
403 if (tr->contents.length == 0)
404 break;
405 kdc_log(context, config, 0,
406 "Transited type 0 with non empty content");
407 return KRB5KDC_ERR_TRTYPE_NOSUPP;
408 default:
409 kdc_log(context, config, 0,
410 "Unknown transited type: %u", tr->tr_type);
411 return KRB5KDC_ERR_TRTYPE_NOSUPP;
414 ret = krb5_domain_x500_decode(context,
415 tr->contents,
416 &realms,
417 &num_realms,
418 client_realm,
419 server_realm);
420 if(ret){
421 krb5_warn(context, ret,
422 "Decoding transited encoding");
423 return ret;
427 * If the realm of the presented tgt is neither the client nor the server
428 * realm, it is a transit realm and must be added to transited set.
430 if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
431 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
432 ret = ERANGE;
433 goto free_realms;
435 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
436 if(tmp == NULL){
437 ret = ENOMEM;
438 goto free_realms;
440 realms = tmp;
441 realms[num_realms] = strdup(tgt_realm);
442 if(realms[num_realms] == NULL){
443 ret = ENOMEM;
444 goto free_realms;
446 num_realms++;
448 if(num_realms == 0) {
449 if(strcmp(client_realm, server_realm))
450 kdc_log(context, config, 0,
451 "cross-realm %s -> %s", client_realm, server_realm);
452 } else {
453 size_t l = 0;
454 char *rs;
455 for(i = 0; i < num_realms; i++)
456 l += strlen(realms[i]) + 2;
457 rs = malloc(l);
458 if(rs != NULL) {
459 *rs = '\0';
460 for(i = 0; i < num_realms; i++) {
461 if(i > 0)
462 strlcat(rs, ", ", l);
463 strlcat(rs, realms[i], l);
465 kdc_log(context, config, 0,
466 "cross-realm %s -> %s via [%s]",
467 client_realm, server_realm, rs);
468 free(rs);
471 if(check_policy) {
472 ret = krb5_check_transited(context, client_realm,
473 server_realm,
474 realms, num_realms, NULL);
475 if(ret) {
476 krb5_warn(context, ret, "cross-realm %s -> %s",
477 client_realm, server_realm);
478 goto free_realms;
480 et->flags.transited_policy_checked = 1;
482 et->transited.tr_type = DOMAIN_X500_COMPRESS;
483 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
484 if(ret)
485 krb5_warn(context, ret, "Encoding transited encoding");
486 free_realms:
487 for(i = 0; i < num_realms; i++)
488 free(realms[i]);
489 free(realms);
490 return ret;
494 static krb5_error_code
495 tgs_make_reply(krb5_context context,
496 krb5_kdc_configuration *config,
497 KDC_REQ_BODY *b,
498 krb5_principal tgt_name,
499 const EncTicketPart *tgt,
500 const krb5_keyblock *replykey,
501 int rk_is_subkey,
502 const EncryptionKey *serverkey,
503 const EncryptionKey *krbtgtkey,
504 const krb5_keyblock *sessionkey,
505 krb5_kvno kvno,
506 AuthorizationData *auth_data,
507 hdb_entry_ex *server,
508 krb5_principal server_principal,
509 const char *server_name,
510 hdb_entry_ex *client,
511 krb5_principal client_principal,
512 const char *tgt_realm,
513 hdb_entry_ex *krbtgt,
514 krb5_pac mspac,
515 uint16_t rodc_id,
516 krb5_boolean add_ticket_sig,
517 const METHOD_DATA *enc_pa_data,
518 const char **e_text,
519 krb5_data *reply)
521 KDC_REP rep;
522 EncKDCRepPart ek;
523 EncTicketPart et;
524 KDCOptions f = b->kdc_options;
525 krb5_error_code ret;
526 int is_weak = 0;
528 memset(&rep, 0, sizeof(rep));
529 memset(&et, 0, sizeof(et));
530 memset(&ek, 0, sizeof(ek));
532 rep.pvno = 5;
533 rep.msg_type = krb_tgs_rep;
535 et.authtime = tgt->authtime;
536 _kdc_fix_time(&b->till);
537 et.endtime = min(tgt->endtime, *b->till);
538 ALLOC(et.starttime);
539 *et.starttime = kdc_time;
541 ret = check_tgs_flags(context, config, b, tgt, &et);
542 if(ret)
543 goto out;
545 /* We should check the transited encoding if:
546 1) the request doesn't ask not to be checked
547 2) globally enforcing a check
548 3) principal requires checking
549 4) we allow non-check per-principal, but principal isn't marked as allowing this
550 5) we don't globally allow this
553 #define GLOBAL_FORCE_TRANSITED_CHECK \
554 (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
555 #define GLOBAL_ALLOW_PER_PRINCIPAL \
556 (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
557 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
558 (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
560 /* these will consult the database in future release */
561 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
562 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
564 ret = fix_transited_encoding(context, config,
565 !f.disable_transited_check ||
566 GLOBAL_FORCE_TRANSITED_CHECK ||
567 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
568 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
569 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
570 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
571 &tgt->transited, &et,
572 krb5_principal_get_realm(context, client_principal),
573 krb5_principal_get_realm(context, server->entry.principal),
574 tgt_realm);
575 if(ret)
576 goto out;
578 copy_Realm(&server_principal->realm, &rep.ticket.realm);
579 _krb5_principal2principalname(&rep.ticket.sname, server_principal);
580 copy_Realm(&tgt_name->realm, &rep.crealm);
582 if (f.request_anonymous)
583 _kdc_make_anonymous_principalname (&rep.cname);
584 else */
586 copy_PrincipalName(&tgt_name->name, &rep.cname);
587 rep.ticket.tkt_vno = 5;
589 ek.caddr = et.caddr;
590 if(et.caddr == NULL)
591 et.caddr = tgt->caddr;
594 time_t life;
595 life = et.endtime - *et.starttime;
596 if(client && client->entry.max_life)
597 life = min(life, *client->entry.max_life);
598 if(server->entry.max_life)
599 life = min(life, *server->entry.max_life);
600 et.endtime = *et.starttime + life;
602 if(f.renewable_ok && tgt->flags.renewable &&
603 et.renew_till == NULL && et.endtime < *b->till &&
604 tgt->renew_till != NULL)
606 et.flags.renewable = 1;
607 ALLOC(et.renew_till);
608 *et.renew_till = *b->till;
610 if(et.renew_till){
611 time_t renew;
612 renew = *et.renew_till - et.authtime;
613 if(client && client->entry.max_renew)
614 renew = min(renew, *client->entry.max_renew);
615 if(server->entry.max_renew)
616 renew = min(renew, *server->entry.max_renew);
617 *et.renew_till = et.authtime + renew;
620 if(et.renew_till){
621 *et.renew_till = min(*et.renew_till, *tgt->renew_till);
622 *et.starttime = min(*et.starttime, *et.renew_till);
623 et.endtime = min(et.endtime, *et.renew_till);
626 *et.starttime = min(*et.starttime, et.endtime);
628 if(*et.starttime == et.endtime){
629 ret = KRB5KDC_ERR_NEVER_VALID;
630 goto out;
632 if(et.renew_till && et.endtime == *et.renew_till){
633 free(et.renew_till);
634 et.renew_till = NULL;
635 et.flags.renewable = 0;
638 et.flags.pre_authent = tgt->flags.pre_authent;
639 et.flags.hw_authent = tgt->flags.hw_authent;
640 et.flags.anonymous = tgt->flags.anonymous;
641 et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
643 /* See MS-KILE 3.3.5.1 */
644 if (!server->entry.flags.forwardable)
645 et.flags.forwardable = 0;
646 if (!server->entry.flags.proxiable)
647 et.flags.proxiable = 0;
649 if (auth_data) {
650 unsigned int i = 0;
652 /* XXX check authdata */
654 if (et.authorization_data == NULL) {
655 et.authorization_data = calloc(1, sizeof(*et.authorization_data));
656 if (et.authorization_data == NULL) {
657 ret = ENOMEM;
658 krb5_set_error_message(context, ret, "malloc: out of memory");
659 goto out;
662 for(i = 0; i < auth_data->len ; i++) {
663 ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
664 if (ret) {
665 krb5_set_error_message(context, ret, "malloc: out of memory");
666 goto out;
671 ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
672 if (ret)
673 goto out;
674 et.crealm = tgt_name->realm;
675 et.cname = tgt_name->name;
677 ek.key = et.key;
678 /* MIT must have at least one last_req */
679 ek.last_req.len = 1;
680 ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
681 if (ek.last_req.val == NULL) {
682 ret = ENOMEM;
683 goto out;
685 ek.nonce = b->nonce;
686 ek.flags = et.flags;
687 ek.authtime = et.authtime;
688 ek.starttime = et.starttime;
689 ek.endtime = et.endtime;
690 ek.renew_till = et.renew_till;
691 ek.srealm = rep.ticket.realm;
692 ek.sname = rep.ticket.sname;
694 _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
695 et.endtime, et.renew_till);
697 if (enc_pa_data->len) {
698 rep.padata = calloc(1, sizeof(*rep.padata));
699 if (rep.padata == NULL) {
700 ret = ENOMEM;
701 goto out;
703 ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
704 if (ret)
705 goto out;
708 if (krb5_enctype_valid(context, et.key.keytype) != 0
709 && _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
711 krb5_enctype_enable(context, et.key.keytype);
712 is_weak = 1;
715 /* The PAC should be the last change to the ticket. */
716 if (mspac != NULL) {
717 ret = _krb5_kdc_pac_sign_ticket(context, mspac, tgt_name, serverkey,
718 krbtgtkey, rodc_id, add_ticket_sig, &et);
719 if (ret)
720 goto out;
723 /* It is somewhat unclear where the etype in the following
724 encryption should come from. What we have is a session
725 key in the passed tgt, and a list of preferred etypes
726 *for the new ticket*. Should we pick the best possible
727 etype, given the keytype in the tgt, or should we look
728 at the etype list here as well? What if the tgt
729 session key is DES3 and we want a ticket with a (say)
730 CAST session key. Should the DES3 etype be added to the
731 etype list, even if we don't want a session key with
732 DES3? */
733 ret = _kdc_encode_reply(context, config,
734 &rep, &et, &ek, et.key.keytype,
735 kvno,
736 serverkey, 0, replykey, rk_is_subkey,
737 e_text, reply);
738 if (is_weak)
739 krb5_enctype_disable(context, et.key.keytype);
741 out:
742 free_TGS_REP(&rep);
743 free_TransitedEncoding(&et.transited);
744 if(et.starttime)
745 free(et.starttime);
746 if(et.renew_till)
747 free(et.renew_till);
748 if(et.authorization_data) {
749 free_AuthorizationData(et.authorization_data);
750 free(et.authorization_data);
752 free_LastReq(&ek.last_req);
753 memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
754 free_EncryptionKey(&et.key);
755 return ret;
758 static krb5_error_code
759 tgs_check_authenticator(krb5_context context,
760 krb5_kdc_configuration *config,
761 krb5_auth_context ac,
762 KDC_REQ_BODY *b,
763 const char **e_text,
764 krb5_keyblock *key)
766 krb5_authenticator auth;
767 size_t len = 0;
768 unsigned char *buf;
769 size_t buf_size;
770 krb5_error_code ret;
771 krb5_crypto crypto;
773 krb5_auth_con_getauthenticator(context, ac, &auth);
774 if(auth->cksum == NULL){
775 kdc_log(context, config, 0, "No authenticator in request");
776 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
777 goto out;
780 * according to RFC1510 it doesn't need to be keyed,
781 * but according to the latest draft it needs to.
783 if (
784 #if 0
785 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
787 #endif
788 !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
789 kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
790 auth->cksum->cksumtype);
791 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
792 goto out;
795 /* XXX should not re-encode this */
796 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
797 if(ret){
798 const char *msg = krb5_get_error_message(context, ret);
799 kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s", msg);
800 krb5_free_error_message(context, msg);
801 goto out;
803 if(buf_size != len) {
804 free(buf);
805 kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
806 *e_text = "KDC internal error";
807 ret = KRB5KRB_ERR_GENERIC;
808 goto out;
810 ret = krb5_crypto_init(context, key, 0, &crypto);
811 if (ret) {
812 const char *msg = krb5_get_error_message(context, ret);
813 free(buf);
814 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
815 krb5_free_error_message(context, msg);
816 goto out;
818 ret = krb5_verify_checksum(context,
819 crypto,
820 KRB5_KU_TGS_REQ_AUTH_CKSUM,
821 buf,
822 len,
823 auth->cksum);
824 free(buf);
825 krb5_crypto_destroy(context, crypto);
826 if(ret){
827 const char *msg = krb5_get_error_message(context, ret);
828 kdc_log(context, config, 0,
829 "Failed to verify authenticator checksum: %s", msg);
830 krb5_free_error_message(context, msg);
832 out:
833 free_Authenticator(auth);
834 free(auth);
835 return ret;
842 static const char *
843 find_rpath(krb5_context context, Realm crealm, Realm srealm)
845 const char *new_realm = krb5_config_get_string(context,
846 NULL,
847 "capaths",
848 crealm,
849 srealm,
850 NULL);
851 return new_realm;
855 static krb5_boolean
856 need_referral(krb5_context context, krb5_kdc_configuration *config,
857 const KDCOptions * const options, krb5_principal server,
858 krb5_realm **realms)
860 const char *name;
862 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
863 return FALSE;
865 if (server->name.name_string.len == 1)
866 name = server->name.name_string.val[0];
867 else if (server->name.name_string.len == 3) {
869 This is used to give referrals for the
870 E3514235-4B06-11D1-AB04-00C04FC2DCD2/NTDSGUID/DNSDOMAIN
871 SPN form, which is used for inter-domain communication in AD
873 name = server->name.name_string.val[2];
874 kdc_log(context, config, 0, "Giving 3 part referral for %s", name);
875 *realms = malloc(sizeof(char *)*2);
876 if (*realms == NULL) {
877 krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
878 return FALSE;
880 (*realms)[0] = strdup(name);
881 (*realms)[1] = NULL;
882 return TRUE;
883 } else if (server->name.name_string.len > 1)
884 name = server->name.name_string.val[1];
885 else
886 return FALSE;
888 kdc_log(context, config, 0, "Searching referral for %s", name);
890 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
893 static krb5_error_code
894 tgs_parse_request(krb5_context context,
895 krb5_kdc_configuration *config,
896 KDC_REQ_BODY *b,
897 const PA_DATA *tgs_req,
898 hdb_entry_ex **krbtgt,
899 krb5_enctype *krbtgt_etype,
900 krb5_ticket **ticket,
901 const char **e_text,
902 const char *from,
903 const struct sockaddr *from_addr,
904 time_t **csec,
905 int **cusec,
906 AuthorizationData **auth_data,
907 krb5_keyblock **replykey,
908 Key **header_key,
909 int *rk_is_subkey)
911 static char failed[] = "<unparse_name failed>";
912 krb5_ap_req ap_req;
913 krb5_error_code ret;
914 krb5_principal princ;
915 krb5_auth_context ac = NULL;
916 krb5_flags ap_req_options;
917 krb5_flags verify_ap_req_flags;
918 krb5_crypto crypto;
919 Key *tkey;
920 krb5_keyblock *subkey = NULL;
921 unsigned usage;
922 krb5uint32 kvno = 0;
923 krb5uint32 *kvno_ptr = NULL;
925 *auth_data = NULL;
926 *csec = NULL;
927 *cusec = NULL;
928 *replykey = NULL;
930 memset(&ap_req, 0, sizeof(ap_req));
931 ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
932 if(ret){
933 const char *msg = krb5_get_error_message(context, ret);
934 kdc_log(context, config, 0, "Failed to decode AP-REQ: %s", msg);
935 krb5_free_error_message(context, msg);
936 goto out;
939 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
940 /* XXX check for ticket.sname == req.sname */
941 kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
942 ret = KRB5KDC_ERR_POLICY; /* ? */
943 goto out;
946 _krb5_principalname2krb5_principal(context,
947 &princ,
948 ap_req.ticket.sname,
949 ap_req.ticket.realm);
951 if (ap_req.ticket.enc_part.kvno) {
952 kvno = *ap_req.ticket.enc_part.kvno;
953 kvno_ptr = &kvno;
955 ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, kvno_ptr,
956 NULL, krbtgt);
958 if(ret == HDB_ERR_NOT_FOUND_HERE) {
959 char *p;
960 ret = krb5_unparse_name(context, princ, &p);
961 if (ret != 0)
962 p = failed;
963 krb5_free_principal(context, princ);
964 kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p);
965 if (ret == 0)
966 free(p);
967 ret = HDB_ERR_NOT_FOUND_HERE;
968 goto out;
969 } else if(ret){
970 const char *msg = krb5_get_error_message(context, ret);
971 char *p;
972 ret = krb5_unparse_name(context, princ, &p);
973 if (ret != 0)
974 p = failed;
975 krb5_free_principal(context, princ);
976 kdc_log(context, config, 0,
977 "Ticket-granting ticket not found in database: %s", msg);
978 krb5_free_error_message(context, msg);
979 if (ret == 0)
980 free(p);
981 ret = KRB5KRB_AP_ERR_NOT_US;
982 goto out;
985 if(ap_req.ticket.enc_part.kvno &&
986 *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
987 char *p;
989 ret = krb5_unparse_name (context, princ, &p);
990 krb5_free_principal(context, princ);
991 if (ret != 0)
992 p = failed;
993 kdc_log(context, config, 0,
994 "Ticket kvno = %d, DB kvno = %d (%s)",
995 *ap_req.ticket.enc_part.kvno,
996 (*krbtgt)->entry.kvno,
998 if (ret == 0)
999 free (p);
1000 ret = KRB5KRB_AP_ERR_BADKEYVER;
1001 goto out;
1004 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1006 ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1007 ap_req.ticket.enc_part.etype, &tkey);
1008 if(ret){
1009 char *str = NULL, *p = NULL;
1011 krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1012 krb5_unparse_name(context, princ, &p);
1013 kdc_log(context, config, 0,
1014 "No server key with enctype %s found for %s",
1015 str ? str : "<unknown enctype>",
1016 p ? p : "<unparse_name failed>");
1017 free(str);
1018 free(p);
1019 ret = KRB5KRB_AP_ERR_BADKEYVER;
1020 goto out;
1023 if (b->kdc_options.validate)
1024 verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1025 else
1026 verify_ap_req_flags = 0;
1028 ret = krb5_verify_ap_req2(context,
1029 &ac,
1030 &ap_req,
1031 princ,
1032 &tkey->key,
1033 verify_ap_req_flags,
1034 &ap_req_options,
1035 ticket,
1036 KRB5_KU_TGS_REQ_AUTH);
1038 krb5_free_principal(context, princ);
1039 if(ret) {
1040 const char *msg = krb5_get_error_message(context, ret);
1041 kdc_log(context, config, 0, "Failed to verify AP-REQ: %s", msg);
1042 krb5_free_error_message(context, msg);
1043 goto out;
1046 *header_key = tkey;
1049 krb5_authenticator auth;
1051 ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1052 if (ret == 0) {
1053 *csec = malloc(sizeof(**csec));
1054 if (*csec == NULL) {
1055 krb5_free_authenticator(context, &auth);
1056 kdc_log(context, config, 0, "malloc failed");
1057 goto out;
1059 **csec = auth->ctime;
1060 *cusec = malloc(sizeof(**cusec));
1061 if (*cusec == NULL) {
1062 krb5_free_authenticator(context, &auth);
1063 kdc_log(context, config, 0, "malloc failed");
1064 goto out;
1066 **cusec = auth->cusec;
1067 krb5_free_authenticator(context, &auth);
1071 ret = tgs_check_authenticator(context, config,
1072 ac, b, e_text, &(*ticket)->ticket.key);
1073 if (ret) {
1074 krb5_auth_con_free(context, ac);
1075 goto out;
1078 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1079 *rk_is_subkey = 1;
1081 ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
1082 if(ret){
1083 const char *msg = krb5_get_error_message(context, ret);
1084 krb5_auth_con_free(context, ac);
1085 kdc_log(context, config, 0, "Failed to get remote subkey: %s", msg);
1086 krb5_free_error_message(context, msg);
1087 goto out;
1089 if(subkey == NULL){
1090 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1091 *rk_is_subkey = 0;
1093 ret = krb5_auth_con_getkey(context, ac, &subkey);
1094 if(ret) {
1095 const char *msg = krb5_get_error_message(context, ret);
1096 krb5_auth_con_free(context, ac);
1097 kdc_log(context, config, 0, "Failed to get session key: %s", msg);
1098 krb5_free_error_message(context, msg);
1099 goto out;
1102 if(subkey == NULL){
1103 krb5_auth_con_free(context, ac);
1104 kdc_log(context, config, 0,
1105 "Failed to get key for enc-authorization-data");
1106 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1107 goto out;
1110 *replykey = subkey;
1112 if (b->enc_authorization_data) {
1113 krb5_data ad;
1115 ret = krb5_crypto_init(context, subkey, 0, &crypto);
1116 if (ret) {
1117 const char *msg = krb5_get_error_message(context, ret);
1118 krb5_auth_con_free(context, ac);
1119 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1120 krb5_free_error_message(context, msg);
1121 goto out;
1123 ret = krb5_decrypt_EncryptedData (context,
1124 crypto,
1125 usage,
1126 b->enc_authorization_data,
1127 &ad);
1128 krb5_crypto_destroy(context, crypto);
1129 if(ret){
1130 krb5_auth_con_free(context, ac);
1131 kdc_log(context, config, 0,
1132 "Failed to decrypt enc-authorization-data");
1133 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1134 goto out;
1136 ALLOC(*auth_data);
1137 if (*auth_data == NULL) {
1138 krb5_auth_con_free(context, ac);
1139 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1140 goto out;
1142 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1143 if(ret){
1144 krb5_auth_con_free(context, ac);
1145 free(*auth_data);
1146 *auth_data = NULL;
1147 kdc_log(context, config, 0, "Failed to decode authorization data");
1148 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1149 goto out;
1153 krb5_auth_con_free(context, ac);
1155 out:
1156 free_AP_REQ(&ap_req);
1158 return ret;
1161 static krb5_error_code
1162 build_server_referral(krb5_context context,
1163 krb5_kdc_configuration *config,
1164 krb5_crypto session,
1165 krb5_const_realm referred_realm,
1166 const PrincipalName *true_principal_name,
1167 const PrincipalName *requested_principal,
1168 krb5_data *outdata)
1170 PA_ServerReferralData ref;
1171 krb5_error_code ret;
1172 EncryptedData ed;
1173 krb5_data data;
1174 size_t size = 0;
1176 memset(&ref, 0, sizeof(ref));
1178 if (referred_realm) {
1179 ALLOC(ref.referred_realm);
1180 if (ref.referred_realm == NULL)
1181 goto eout;
1182 *ref.referred_realm = strdup(referred_realm);
1183 if (*ref.referred_realm == NULL)
1184 goto eout;
1186 if (true_principal_name) {
1187 ALLOC(ref.true_principal_name);
1188 if (ref.true_principal_name == NULL)
1189 goto eout;
1190 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1191 if (ret)
1192 goto eout;
1194 if (requested_principal) {
1195 ALLOC(ref.requested_principal_name);
1196 if (ref.requested_principal_name == NULL)
1197 goto eout;
1198 ret = copy_PrincipalName(requested_principal,
1199 ref.requested_principal_name);
1200 if (ret)
1201 goto eout;
1204 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1205 data.data, data.length,
1206 &ref, &size, ret);
1207 free_PA_ServerReferralData(&ref);
1208 if (ret)
1209 return ret;
1210 if (data.length != size)
1211 krb5_abortx(context, "internal asn.1 encoder error");
1213 ret = krb5_encrypt_EncryptedData(context, session,
1214 KRB5_KU_PA_SERVER_REFERRAL,
1215 data.data, data.length,
1216 0 /* kvno */, &ed);
1217 free(data.data);
1218 if (ret)
1219 return ret;
1221 ASN1_MALLOC_ENCODE(EncryptedData,
1222 outdata->data, outdata->length,
1223 &ed, &size, ret);
1224 free_EncryptedData(&ed);
1225 if (ret)
1226 return ret;
1227 if (outdata->length != size)
1228 krb5_abortx(context, "internal asn.1 encoder error");
1230 return 0;
1231 eout:
1232 free_PA_ServerReferralData(&ref);
1233 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1234 return ENOMEM;
1237 static krb5_error_code
1238 db_fetch_client(krb5_context context,
1239 krb5_kdc_configuration *config,
1240 int flags,
1241 krb5_principal cp,
1242 const char *cpn,
1243 const char *krbtgt_realm,
1244 HDB **clientdb,
1245 hdb_entry_ex **client_out)
1247 krb5_error_code ret;
1248 hdb_entry_ex *client = NULL;
1250 *client_out = NULL;
1252 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | flags,
1253 NULL, clientdb, &client);
1254 if (ret == HDB_ERR_NOT_FOUND_HERE) {
1256 * This is OK, we are just trying to find out if they have
1257 * been disabled or deleted in the meantime; missing secrets
1258 * are OK.
1260 } else if (ret) {
1262 * If the client belongs to the same realm as our TGS, it
1263 * should exist in the local database.
1265 const char *msg;
1267 if (strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1268 if (ret == HDB_ERR_NOENTRY)
1269 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1270 kdc_log(context, config, 4, "Client no longer in database: %s", cpn);
1271 return ret;
1274 msg = krb5_get_error_message(context, ret);
1275 kdc_log(context, config, 4, "Client not found in database: %s", msg);
1276 krb5_free_error_message(context, msg);
1277 } else if (client->entry.flags.invalid || !client->entry.flags.client) {
1278 kdc_log(context, config, 4, "Client has invalid bit set");
1279 _kdc_free_ent(context, client);
1280 return KRB5KDC_ERR_POLICY;
1283 *client_out = client;
1285 return 0;
1288 static krb5_error_code
1289 tgs_build_reply(krb5_context context,
1290 krb5_kdc_configuration *config,
1291 KDC_REQ *req,
1292 KDC_REQ_BODY *b,
1293 hdb_entry_ex *krbtgt,
1294 krb5_enctype krbtgt_etype,
1295 Key *tkey_check,
1296 const krb5_keyblock *replykey,
1297 int rk_is_subkey,
1298 krb5_ticket *ticket,
1299 krb5_data *reply,
1300 const char *from,
1301 const char **e_text,
1302 AuthorizationData **auth_data,
1303 const struct sockaddr *from_addr)
1305 krb5_error_code ret;
1306 krb5_principal cp = NULL, sp = NULL, tp = NULL, dp = NULL;
1307 krb5_principal krbtgt_principal = NULL;
1308 krb5_principal user2user_princ = NULL;
1309 char *spn = NULL, *cpn = NULL, *tpn = NULL, *dpn = NULL;
1310 char *user2user_name = NULL;
1311 hdb_entry_ex *server = NULL, *client = NULL, *s4u2self_impersonated_client = NULL;
1312 hdb_entry_ex *user2user_krbtgt = NULL;
1313 HDB *clientdb, *s4u2self_impersonated_clientdb;
1314 HDB *serverdb = NULL;
1315 krb5_realm ref_realm = NULL;
1316 EncTicketPart *tgt = &ticket->ticket;
1317 const char *tgt_realm = /* Realm of TGT issuer */
1318 krb5_principal_get_realm(context, krbtgt->entry.principal);
1319 const EncryptionKey *ekey;
1320 krb5_keyblock sessionkey;
1321 krb5_kvno kvno;
1322 krb5_pac mspac = NULL;
1323 krb5_pac user2user_pac = NULL;
1324 uint16_t rodc_id;
1325 krb5_boolean add_ticket_sig = FALSE;
1326 hdb_entry_ex *krbtgt_out = NULL;
1328 METHOD_DATA enc_pa_data;
1330 PrincipalName *s;
1331 Realm r;
1332 int nloop = 0;
1333 EncTicketPart adtkt;
1334 char opt_str[128];
1335 krb5_boolean kdc_issued = FALSE;
1337 Key *tkey_sign;
1338 int flags = HDB_F_FOR_TGS_REQ;
1340 memset(&sessionkey, 0, sizeof(sessionkey));
1341 memset(&adtkt, 0, sizeof(adtkt));
1342 memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1344 s = b->sname;
1345 r = b->realm;
1347 if (b->kdc_options.canonicalize)
1348 flags |= HDB_F_CANON;
1350 if (s == NULL) {
1351 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1352 krb5_set_error_message(context, ret, "No server in request");
1353 goto out;
1356 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1357 ret = krb5_unparse_name(context, sp, &spn);
1358 if (ret)
1359 goto out;
1360 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1361 ret = krb5_unparse_name(context, cp, &cpn);
1362 if (ret)
1363 goto out;
1364 unparse_flags (KDCOptions2int(b->kdc_options),
1365 asn1_KDCOptions_units(),
1366 opt_str, sizeof(opt_str));
1367 if(*opt_str)
1368 kdc_log(context, config, 0,
1369 "TGS-REQ %s from %s for %s [%s]",
1370 cpn, from, spn, opt_str);
1371 else
1372 kdc_log(context, config, 0,
1373 "TGS-REQ %s from %s for %s", cpn, from, spn);
1376 * Fetch server
1379 server_lookup:
1380 ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | flags,
1381 NULL, &serverdb, &server);
1383 if(ret == HDB_ERR_NOT_FOUND_HERE) {
1384 kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", sp);
1385 goto out;
1386 } else if (ret == HDB_ERR_WRONG_REALM) {
1387 if (ref_realm)
1388 free(ref_realm);
1389 ref_realm = strdup(server->entry.principal->realm);
1390 if (ref_realm == NULL) {
1391 ret = ENOMEM;
1392 goto out;
1395 kdc_log(context, config, 5,
1396 "Returning a referral to realm %s for "
1397 "server %s.",
1398 ref_realm, spn);
1399 krb5_free_principal(context, sp);
1400 sp = NULL;
1401 free(spn);
1402 spn = NULL;
1403 ret = krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1404 ref_realm, NULL);
1405 if (ret)
1406 goto out;
1407 ret = krb5_unparse_name(context, sp, &spn);
1408 if (ret)
1409 goto out;
1411 goto server_lookup;
1412 } else if(ret){
1413 const char *new_rlm, *msg;
1414 Realm req_rlm;
1415 krb5_realm *realms;
1417 if (!config->autodetect_referrals) {
1418 /* noop */
1419 } else if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1420 if(nloop++ < 2) {
1421 new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1422 if(new_rlm) {
1423 kdc_log(context, config, 5, "krbtgt for realm %s "
1424 "not found, trying %s",
1425 req_rlm, new_rlm);
1426 krb5_free_principal(context, sp);
1427 free(spn);
1428 krb5_make_principal(context, &sp, r,
1429 KRB5_TGS_NAME, new_rlm, NULL);
1430 ret = krb5_unparse_name(context, sp, &spn);
1431 if (ret)
1432 goto out;
1434 if (ref_realm)
1435 free(ref_realm);
1436 ref_realm = strdup(new_rlm);
1437 goto server_lookup;
1440 } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1441 if (strcmp(realms[0], sp->realm) != 0) {
1442 kdc_log(context, config, 5,
1443 "Returning a referral to realm %s for "
1444 "server %s that was not found",
1445 realms[0], spn);
1446 krb5_free_principal(context, sp);
1447 free(spn);
1448 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1449 realms[0], NULL);
1450 ret = krb5_unparse_name(context, sp, &spn);
1451 if (ret)
1452 goto out;
1454 if (ref_realm)
1455 free(ref_realm);
1456 ref_realm = strdup(realms[0]);
1458 krb5_free_host_realm(context, realms);
1459 goto server_lookup;
1461 krb5_free_host_realm(context, realms);
1463 msg = krb5_get_error_message(context, ret);
1464 kdc_log(context, config, 0,
1465 "Server not found in database: %s: %s", spn, msg);
1466 krb5_free_error_message(context, msg);
1467 if (ret == HDB_ERR_NOENTRY)
1468 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1469 goto out;
1472 /* Now refetch the primary krbtgt, and get the current kvno (the
1473 * sign check may have been on an old kvno, and the server may
1474 * have been an incoming trust) */
1475 ret = krb5_make_principal(context, &krbtgt_principal,
1476 krb5_principal_get_comp_string(context,
1477 krbtgt->entry.principal,
1479 KRB5_TGS_NAME,
1480 krb5_principal_get_comp_string(context,
1481 krbtgt->entry.principal,
1482 1), NULL);
1483 if(ret) {
1484 kdc_log(context, config, 0,
1485 "Failed to generate krbtgt principal");
1486 goto out;
1489 ret = _kdc_db_fetch(context, config, krbtgt_principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out);
1490 krb5_free_principal(context, krbtgt_principal);
1491 if (ret) {
1492 krb5_error_code ret2;
1493 char *ktpn, *ktpn2;
1494 ret = krb5_unparse_name(context, krbtgt->entry.principal, &ktpn);
1495 ret2 = krb5_unparse_name(context, krbtgt_principal, &ktpn2);
1496 kdc_log(context, config, 0,
1497 "Request with wrong krbtgt: %s, %s not found in our database",
1498 (ret == 0) ? ktpn : "<unknown>", (ret2 == 0) ? ktpn2 : "<unknown>");
1499 if(ret == 0)
1500 free(ktpn);
1501 if(ret2 == 0)
1502 free(ktpn2);
1503 ret = KRB5KRB_AP_ERR_NOT_US;
1504 goto out;
1508 * Select enctype, return key and kvno.
1512 krb5_enctype etype;
1514 if(b->kdc_options.enc_tkt_in_skey) {
1515 Ticket *t;
1516 krb5_principal p;
1517 Key *uukey;
1518 krb5uint32 second_kvno = 0;
1519 krb5uint32 *kvno_ptr = NULL;
1520 size_t i;
1521 hdb_entry_ex *user2user_client = NULL;
1522 krb5_boolean user2user_kdc_issued = FALSE;
1524 if(b->additional_tickets == NULL ||
1525 b->additional_tickets->len == 0){
1526 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1527 kdc_log(context, config, 0,
1528 "No second ticket present in request");
1529 goto out;
1531 t = &b->additional_tickets->val[0];
1532 if(!get_krbtgt_realm(&t->sname)){
1533 kdc_log(context, config, 0,
1534 "Additional ticket is not a ticket-granting ticket");
1535 ret = KRB5KDC_ERR_POLICY;
1536 goto out;
1538 ret = _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1539 if (ret) {
1540 goto out;
1542 if(t->enc_part.kvno){
1543 second_kvno = *t->enc_part.kvno;
1544 kvno_ptr = &second_kvno;
1546 ret = _kdc_db_fetch(context, config, p,
1547 HDB_F_GET_KRBTGT, kvno_ptr,
1548 NULL, &user2user_krbtgt);
1549 krb5_free_principal(context, p);
1550 if(ret){
1551 if (ret == HDB_ERR_NOENTRY)
1552 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1553 goto out;
1555 ret = hdb_enctype2key(context, &user2user_krbtgt->entry,
1556 t->enc_part.etype, &uukey);
1557 if(ret){
1558 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1559 goto out;
1561 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1562 if(ret)
1563 goto out;
1565 ret = verify_flags(context, config, &adtkt, spn);
1566 if (ret)
1567 goto out;
1569 /* Fetch the name from the TGT. */
1570 ret = _krb5_principalname2krb5_principal(context, &user2user_princ,
1571 adtkt.cname, adtkt.crealm);
1572 if (ret) {
1573 goto out;
1576 ret = krb5_unparse_name(context, user2user_princ, &user2user_name);
1577 if (ret) {
1578 goto out;
1581 /* Look up the name given in the TGT in the database. */
1582 ret = db_fetch_client(context, config, flags, user2user_princ, user2user_name,
1583 krb5_principal_get_realm(context, krbtgt_out->entry.principal),
1584 NULL, &user2user_client);
1585 if (ret) {
1586 goto out;
1589 if (user2user_client != NULL) {
1591 * If the account is present in the database, check the account
1592 * flags.
1594 ret = kdc_check_flags(context, config,
1595 user2user_client, user2user_name,
1596 NULL, NULL,
1597 FALSE);
1598 if (ret) {
1599 _kdc_free_ent(context, user2user_client);
1600 goto out;
1604 * Also check that the account is the same one specified in the
1605 * request.
1607 ret = check_s4u2self(context, config, serverdb, server, user2user_client, user2user_princ);
1608 if (ret) {
1609 _kdc_free_ent(context, user2user_client);
1610 goto out;
1614 /* Verify the PAC of the TGT. */
1615 ret = check_PAC(context, config, user2user_princ, NULL,
1616 user2user_client, user2user_krbtgt, user2user_krbtgt, user2user_krbtgt,
1617 &uukey->key, &tkey_check->key, &adtkt, &user2user_kdc_issued, &user2user_pac);
1618 _kdc_free_ent(context, user2user_client);
1619 if (ret) {
1620 const char *msg = krb5_get_error_message(context, ret);
1621 kdc_log(context, config, 0,
1622 "Verify PAC failed for %s (%s) from %s with %s",
1623 spn, user2user_name, from, msg);
1624 krb5_free_error_message(context, msg);
1625 goto out;
1628 if (user2user_pac == NULL || !user2user_kdc_issued) {
1629 ret = KRB5KDC_ERR_BADOPTION;
1630 kdc_log(context, config, 0,
1631 "Ticket not signed with PAC; user-to-user failed (%s).",
1632 user2user_pac ? "Ticket unsigned" : "No PAC");
1633 goto out;
1636 ekey = &adtkt.key;
1637 for(i = 0; i < b->etype.len; i++)
1638 if (b->etype.val[i] == adtkt.key.keytype)
1639 break;
1640 if(i == b->etype.len) {
1641 kdc_log(context, config, 0,
1642 "Addition ticket have not matching etypes");
1643 krb5_clear_error_message(context);
1644 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1645 goto out;
1647 etype = b->etype.val[i];
1648 kvno = 0;
1649 } else {
1650 Key *skey;
1652 ret = _kdc_find_etype(context,
1653 config->tgs_use_strongest_session_key, FALSE,
1654 server, b->etype.val, b->etype.len, NULL,
1655 &skey);
1656 if(ret) {
1657 kdc_log(context, config, 0,
1658 "Server (%s) has no support for etypes", spn);
1659 goto out;
1661 ekey = &skey->key;
1662 etype = skey->key.keytype;
1663 kvno = server->entry.kvno;
1666 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1667 if (ret)
1668 goto out;
1672 * Check that service is in the same realm as the krbtgt. If it's
1673 * not the same, it's someone that is using a uni-directional trust
1674 * backward.
1677 /* The first realm is the realm of the service, the second is
1678 * krbtgt/<this>/@REALM component of the krbtgt DN the request was
1679 * encrypted to. The redirection via the krbtgt_out entry allows
1680 * the DB to possibly correct the case of the realm (Samba4 does
1681 * this) before the strcmp() */
1682 if (strcmp(krb5_principal_get_realm(context, server->entry.principal),
1683 krb5_principal_get_realm(context, krbtgt_out->entry.principal)) != 0) {
1684 char *ktpn;
1685 ret = krb5_unparse_name(context, krbtgt_out->entry.principal, &ktpn);
1686 kdc_log(context, config, 0,
1687 "Request with wrong krbtgt: %s",
1688 (ret == 0) ? ktpn : "<unknown>");
1689 if(ret == 0)
1690 free(ktpn);
1691 ret = KRB5KRB_AP_ERR_NOT_US;
1694 ret = hdb_enctype2key(context, &krbtgt_out->entry,
1695 krbtgt_etype, &tkey_sign);
1696 if(ret) {
1697 kdc_log(context, config, 0,
1698 "Failed to find key for krbtgt PAC signature");
1699 goto out;
1702 ret = db_fetch_client(context, config, flags, cp, cpn,
1703 krb5_principal_get_realm(context, krbtgt_out->entry.principal),
1704 &clientdb, &client);
1705 if (ret)
1706 goto out;
1708 ret = check_PAC(context, config, cp, NULL, client, server, krbtgt, krbtgt,
1709 &tkey_check->key, &tkey_check->key, tgt, &kdc_issued, &mspac);
1710 if (ret) {
1711 const char *msg = krb5_get_error_message(context, ret);
1712 kdc_log(context, config, 0,
1713 "Verify PAC failed for %s (%s) from %s with %s",
1714 spn, cpn, from, msg);
1715 krb5_free_error_message(context, msg);
1716 goto out;
1720 * Process request
1723 /* by default the tgt principal matches the client principal */
1724 tp = cp;
1725 tpn = cpn;
1727 if (client) {
1728 const PA_DATA *sdata;
1729 int i = 0;
1731 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1732 if (sdata) {
1733 krb5_crypto crypto;
1734 krb5_data datack;
1735 PA_S4U2Self self;
1736 const char *str;
1738 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1739 sdata->padata_value.length,
1740 &self, NULL);
1741 if (ret) {
1742 kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1743 goto out;
1746 if (!krb5_checksum_is_keyed(context, self.cksum.cksumtype)) {
1747 free_PA_S4U2Self(&self);
1748 kdc_log(context, config, 0, "Reject PA-S4U2Self with unkeyed checksum");
1749 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
1750 goto out;
1753 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1754 if (ret)
1755 goto out;
1757 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1758 if (ret) {
1759 const char *msg = krb5_get_error_message(context, ret);
1760 free_PA_S4U2Self(&self);
1761 krb5_data_free(&datack);
1762 kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg);
1763 krb5_free_error_message(context, msg);
1764 goto out;
1767 /* Allow HMAC_MD5 checksum with any key type */
1768 if (self.cksum.cksumtype == CKSUMTYPE_HMAC_MD5) {
1769 unsigned char csdata[16];
1770 Checksum cs;
1772 cs.checksum.length = sizeof(csdata);
1773 cs.checksum.data = &csdata;
1775 ret = _krb5_HMAC_MD5_checksum(context, &crypto->key,
1776 datack.data, datack.length,
1777 KRB5_KU_OTHER_CKSUM, &cs);
1778 if (ret == 0 &&
1779 krb5_data_ct_cmp(&cs.checksum, &self.cksum.checksum) != 0)
1780 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1782 else {
1783 ret = krb5_verify_checksum(context,
1784 crypto,
1785 KRB5_KU_OTHER_CKSUM,
1786 datack.data,
1787 datack.length,
1788 &self.cksum);
1790 krb5_data_free(&datack);
1791 krb5_crypto_destroy(context, crypto);
1792 if (ret) {
1793 const char *msg = krb5_get_error_message(context, ret);
1794 free_PA_S4U2Self(&self);
1795 kdc_log(context, config, 0,
1796 "krb5_verify_checksum failed for S4U2Self: %s", msg);
1797 krb5_free_error_message(context, msg);
1798 goto out;
1801 ret = _krb5_principalname2krb5_principal(context,
1802 &tp,
1803 self.name,
1804 self.realm);
1805 free_PA_S4U2Self(&self);
1806 if (ret)
1807 goto out;
1809 ret = krb5_unparse_name(context, tp, &tpn);
1810 if (ret)
1811 goto out;
1813 ret = _kdc_db_fetch(context, config, tp, HDB_F_GET_CLIENT | flags,
1814 NULL, &s4u2self_impersonated_clientdb,
1815 &s4u2self_impersonated_client);
1816 if (ret) {
1817 const char *msg;
1820 * If the client belongs to the same realm as our krbtgt, it
1821 * should exist in the local database.
1825 if (ret == HDB_ERR_NOENTRY)
1826 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1827 msg = krb5_get_error_message(context, ret);
1828 kdc_log(context, config, 1,
1829 "S2U4Self principal to impersonate %s not found in database: %s",
1830 tpn, msg);
1831 krb5_free_error_message(context, msg);
1832 goto out;
1835 /* Ignore pw_end attributes (as Windows does),
1836 * since S4U2Self is not password authentication. */
1837 free(s4u2self_impersonated_client->entry.pw_end);
1838 s4u2self_impersonated_client->entry.pw_end = NULL;
1840 ret = kdc_check_flags(context, config, s4u2self_impersonated_client, tpn,
1841 NULL, NULL, FALSE);
1842 if (ret)
1843 goto out;
1845 /* If we were about to put a PAC into the ticket, we better fix it to be the right PAC */
1846 if (mspac) {
1847 krb5_pac_free(context, mspac);
1848 mspac = NULL;
1851 ret = _kdc_pac_generate(context, s4u2self_impersonated_client, server,
1852 NULL, NULL, &mspac);
1853 if (ret) {
1854 kdc_log(context, config, 0, "PAC generation failed for -- %s",
1855 tpn);
1856 goto out;
1860 * Check that service doing the impersonating is
1861 * requesting a ticket to it-self.
1863 ret = check_s4u2self(context, config, clientdb, client, server, sp);
1864 if (ret) {
1865 kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1866 "to impersonate to service "
1867 "(tried for user %s to service %s)",
1868 cpn, tpn, spn);
1869 goto out;
1873 * If the service isn't trusted for authentication to
1874 * delegation or if the impersonate client is disallowed
1875 * forwardable, remove the forwardable flag.
1878 if (client->entry.flags.trusted_for_delegation &&
1879 s4u2self_impersonated_client->entry.flags.forwardable) {
1880 str = "[forwardable]";
1881 } else {
1882 b->kdc_options.forwardable = 0;
1883 str = "";
1885 kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
1886 "service %s %s", cpn, tpn, spn, str);
1891 * Constrained delegation
1894 if (client != NULL
1895 && b->additional_tickets != NULL
1896 && b->additional_tickets->len != 0
1897 && b->kdc_options.enc_tkt_in_skey == 0)
1899 hdb_entry_ex *adclient = NULL;
1900 krb5_boolean ad_kdc_issued = FALSE;
1901 Key *clientkey;
1902 Ticket *t;
1905 * We require that the service's krbtgt has a PAC.
1907 if (mspac == NULL) {
1908 ret = KRB5KDC_ERR_BADOPTION;
1909 kdc_log(context, config, 0,
1910 "Constrained delegation without PAC %s/%s",
1911 cpn, spn);
1912 goto out;
1915 krb5_pac_free(context, mspac);
1916 mspac = NULL;
1918 t = &b->additional_tickets->val[0];
1920 ret = hdb_enctype2key(context, &client->entry,
1921 t->enc_part.etype, &clientkey);
1922 if(ret){
1923 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1924 goto out;
1927 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
1928 if (ret) {
1929 kdc_log(context, config, 0,
1930 "failed to decrypt ticket for "
1931 "constrained delegation from %s to %s ", cpn, spn);
1932 goto out;
1935 ret = _krb5_principalname2krb5_principal(context,
1936 &tp,
1937 adtkt.cname,
1938 adtkt.crealm);
1939 if (ret)
1940 goto out;
1942 ret = krb5_unparse_name(context, tp, &tpn);
1943 if (ret)
1944 goto out;
1946 ret = _krb5_principalname2krb5_principal(context,
1947 &dp,
1948 t->sname,
1949 t->realm);
1950 if (ret)
1951 goto out;
1953 ret = krb5_unparse_name(context, dp, &dpn);
1954 if (ret)
1955 goto out;
1957 /* check that ticket is valid */
1958 if (adtkt.flags.forwardable == 0) {
1959 kdc_log(context, config, 0,
1960 "Missing forwardable flag on ticket for "
1961 "constrained delegation from %s (%s) as %s to %s ",
1962 cpn, dpn, tpn, spn);
1963 ret = KRB5KDC_ERR_BADOPTION;
1964 goto out;
1967 ret = check_constrained_delegation(context, config, clientdb,
1968 client, server, sp);
1969 if (ret) {
1970 kdc_log(context, config, 0,
1971 "constrained delegation from %s (%s) as %s to %s not allowed",
1972 cpn, dpn, tpn, spn);
1973 goto out;
1976 ret = verify_flags(context, config, &adtkt, tpn);
1977 if (ret) {
1978 goto out;
1981 /* Try lookup the delegated client in DB */
1982 ret = db_fetch_client(context, config, flags, tp, tpn,
1983 krb5_principal_get_realm(context, krbtgt_out->entry.principal),
1984 NULL, &adclient);
1985 if (ret)
1986 goto out;
1988 if (adclient != NULL) {
1989 ret = kdc_check_flags(context, config,
1990 adclient, tpn,
1991 server, spn,
1992 FALSE);
1993 if (ret) {
1994 _kdc_free_ent(context, adclient);
1995 goto out;
2000 * TODO: pass in t->sname and t->realm and build
2001 * a S4U_DELEGATION_INFO blob to the PAC.
2003 ret = check_PAC(context, config, tp, dp, adclient, server, krbtgt, client,
2004 &clientkey->key, &tkey_check->key, &adtkt, &ad_kdc_issued, &mspac);
2005 if (adclient)
2006 _kdc_free_ent(context, adclient);
2007 if (ret) {
2008 const char *msg = krb5_get_error_message(context, ret);
2009 kdc_log(context, config, 0,
2010 "Verify delegated PAC failed to %s for client"
2011 "%s (%s) as %s from %s with %s",
2012 spn, cpn, dpn, tpn, from, msg);
2013 krb5_free_error_message(context, msg);
2014 goto out;
2017 if (mspac == NULL || !ad_kdc_issued) {
2018 ret = KRB5KDC_ERR_BADOPTION;
2019 kdc_log(context, config, 0,
2020 "Ticket not signed with PAC; service %s failed for "
2021 "for delegation to %s for client %s (%s) from %s; (%s).",
2022 spn, tpn, dpn, cpn, from, mspac ? "Ticket unsigned" : "No PAC");
2023 goto out;
2026 kdc_log(context, config, 0, "constrained delegation for %s "
2027 "from %s (%s) to %s", tpn, cpn, dpn, spn);
2031 * Check flags
2034 ret = kdc_check_flags(context, config,
2035 client, cpn,
2036 server, spn,
2037 FALSE);
2038 if(ret)
2039 goto out;
2041 if((b->kdc_options.validate || b->kdc_options.renew) &&
2042 !krb5_principal_compare(context,
2043 krbtgt->entry.principal,
2044 server->entry.principal)){
2045 kdc_log(context, config, 0, "Inconsistent request.");
2046 ret = KRB5KDC_ERR_SERVER_NOMATCH;
2047 goto out;
2050 /* check for valid set of addresses */
2051 if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
2052 ret = KRB5KRB_AP_ERR_BADADDR;
2053 kdc_log(context, config, 0, "Request from wrong address");
2054 goto out;
2058 * If this is an referral, add server referral data to the
2059 * auth_data reply .
2061 if (ref_realm) {
2062 PA_DATA pa;
2063 krb5_crypto crypto;
2065 kdc_log(context, config, 0,
2066 "Adding server referral to %s", ref_realm);
2068 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
2069 if (ret)
2070 goto out;
2072 ret = build_server_referral(context, config, crypto, ref_realm,
2073 NULL, s, &pa.padata_value);
2074 krb5_crypto_destroy(context, crypto);
2075 if (ret) {
2076 kdc_log(context, config, 0,
2077 "Failed building server referral");
2078 goto out;
2080 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
2082 ret = add_METHOD_DATA(&enc_pa_data, &pa);
2083 krb5_data_free(&pa.padata_value);
2084 if (ret) {
2085 kdc_log(context, config, 0,
2086 "Add server referral METHOD-DATA failed");
2087 goto out;
2092 * Only add ticket signature if the requested server is not krbtgt, and
2093 * either the header server is krbtgt or, in the case of renewal/validation
2094 * if it was signed with PAC ticket signature and we verified it.
2095 * Currently Heimdal only allows renewal of krbtgt anyway but that might
2096 * change one day (see issue #763) so make sure to check for it.
2099 if (kdc_issued &&
2100 !krb5_principal_is_krbtgt(context, server->entry.principal))
2101 add_ticket_sig = TRUE;
2104 * Active-Directory implementations use the high part of the kvno as the
2105 * read-only-dc identifier, we need to embed it in the PAC KDC signatures.
2108 rodc_id = krbtgt_out->entry.kvno >> 16;
2114 ret = tgs_make_reply(context,
2115 config,
2118 tgt,
2119 replykey,
2120 rk_is_subkey,
2121 ekey,
2122 &tkey_sign->key,
2123 &sessionkey,
2124 kvno,
2125 *auth_data,
2126 server,
2127 server->entry.principal,
2128 spn,
2129 client,
2131 tgt_realm,
2132 krbtgt_out,
2133 mspac,
2134 rodc_id,
2135 add_ticket_sig,
2136 &enc_pa_data,
2137 e_text,
2138 reply);
2140 out:
2141 free(user2user_name);
2142 if (tpn != cpn)
2143 free(tpn);
2144 free(spn);
2145 free(cpn);
2146 if (dpn)
2147 free(dpn);
2149 krb5_free_keyblock_contents(context, &sessionkey);
2150 if(krbtgt_out)
2151 _kdc_free_ent(context, krbtgt_out);
2152 if(server)
2153 _kdc_free_ent(context, server);
2154 if(client)
2155 _kdc_free_ent(context, client);
2156 if(s4u2self_impersonated_client)
2157 _kdc_free_ent(context, s4u2self_impersonated_client);
2158 if (user2user_krbtgt)
2159 _kdc_free_ent(context, user2user_krbtgt);
2161 if (user2user_princ)
2162 krb5_free_principal(context, user2user_princ);
2163 if (tp && tp != cp)
2164 krb5_free_principal(context, tp);
2165 if (cp)
2166 krb5_free_principal(context, cp);
2167 if (dp)
2168 krb5_free_principal(context, dp);
2169 if (sp)
2170 krb5_free_principal(context, sp);
2171 if (ref_realm)
2172 free(ref_realm);
2173 free_METHOD_DATA(&enc_pa_data);
2175 free_EncTicketPart(&adtkt);
2177 krb5_pac_free(context, mspac);
2178 krb5_pac_free(context, user2user_pac);
2180 return ret;
2187 krb5_error_code
2188 _kdc_tgs_rep(krb5_context context,
2189 krb5_kdc_configuration *config,
2190 KDC_REQ *req,
2191 krb5_data *data,
2192 const char *from,
2193 struct sockaddr *from_addr,
2194 int datagram_reply)
2196 AuthorizationData *auth_data = NULL;
2197 krb5_error_code ret;
2198 int i = 0;
2199 const PA_DATA *tgs_req;
2200 Key *header_key = NULL;
2202 hdb_entry_ex *krbtgt = NULL;
2203 krb5_ticket *ticket = NULL;
2204 const char *e_text = NULL;
2205 krb5_enctype krbtgt_etype = ETYPE_NULL;
2207 krb5_keyblock *replykey = NULL;
2208 int rk_is_subkey = 0;
2209 time_t *csec = NULL;
2210 int *cusec = NULL;
2212 if(req->padata == NULL){
2213 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2214 kdc_log(context, config, 0,
2215 "TGS-REQ from %s without PA-DATA", from);
2216 goto out;
2219 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2221 if(tgs_req == NULL){
2222 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2224 kdc_log(context, config, 0,
2225 "TGS-REQ from %s without PA-TGS-REQ", from);
2226 goto out;
2228 ret = tgs_parse_request(context, config,
2229 &req->req_body, tgs_req,
2230 &krbtgt,
2231 &krbtgt_etype,
2232 &ticket,
2233 &e_text,
2234 from, from_addr,
2235 &csec, &cusec,
2236 &auth_data,
2237 &replykey,
2238 &header_key,
2239 &rk_is_subkey);
2240 if (ret == HDB_ERR_NOT_FOUND_HERE) {
2241 /* kdc_log() is called in tgs_parse_request() */
2242 goto out;
2244 if (ret) {
2245 kdc_log(context, config, 0,
2246 "Failed parsing TGS-REQ from %s", from);
2247 goto out;
2250 ret = tgs_build_reply(context,
2251 config,
2252 req,
2253 &req->req_body,
2254 krbtgt,
2255 krbtgt_etype,
2256 header_key,
2257 replykey,
2258 rk_is_subkey,
2259 ticket,
2260 data,
2261 from,
2262 &e_text,
2263 &auth_data,
2264 from_addr);
2265 if (ret) {
2266 kdc_log(context, config, 0,
2267 "Failed building TGS-REP to %s", from);
2268 goto out;
2271 /* */
2272 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2273 krb5_data_free(data);
2274 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2275 e_text = "Reply packet too large";
2278 out:
2279 if (replykey)
2280 krb5_free_keyblock(context, replykey);
2281 if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){
2282 krb5_mk_error(context,
2283 ret,
2284 NULL,
2285 NULL,
2286 NULL,
2287 NULL,
2288 csec,
2289 cusec,
2290 data);
2291 ret = 0;
2293 free(csec);
2294 free(cusec);
2295 if (ticket)
2296 krb5_free_ticket(context, ticket);
2297 if(krbtgt)
2298 _kdc_free_ent(context, krbtgt);
2300 if (auth_data) {
2301 free_AuthorizationData(auth_data);
2302 free(auth_data);
2305 return ret;