fix error message in constrained delegation, from andrew bartlett
[heimdal.git] / kdc / krb5tgs.c
blob358b11276b00c0230e7e91e981afddca4cd9db49
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"
36 RCSID("$Id$");
39 * return the realm of a krbtgt-ticket or NULL
42 static Realm
43 get_krbtgt_realm(const PrincipalName *p)
45 if(p->name_string.len == 2
46 && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
47 return p->name_string.val[1];
48 else
49 return NULL;
53 * The KDC might add a signed path to the ticket authorization data
54 * field. This is to avoid server impersonating clients and the
55 * request constrained delegation.
57 * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
58 * entry of type KRB5SignedPath.
61 static krb5_error_code
62 find_KRB5SignedPath(krb5_context context,
63 const AuthorizationData *ad,
64 krb5_data *data)
66 AuthorizationData child;
67 krb5_error_code ret;
68 int pos;
70 if (ad == NULL || ad->len == 0)
71 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
73 pos = ad->len - 1;
75 if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
76 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
78 ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
79 ad->val[pos].ad_data.length,
80 &child,
81 NULL);
82 if (ret) {
83 krb5_set_error_message(context, ret, "Failed to decode "
84 "IF_RELEVANT with %d", ret);
85 return ret;
88 if (child.len != 1) {
89 free_AuthorizationData(&child);
90 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
93 if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
94 free_AuthorizationData(&child);
95 return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
98 if (data)
99 ret = der_copy_octet_string(&child.val[0].ad_data, data);
100 free_AuthorizationData(&child);
101 return ret;
104 krb5_error_code
105 _kdc_add_KRB5SignedPath(krb5_context context,
106 krb5_kdc_configuration *config,
107 hdb_entry_ex *krbtgt,
108 krb5_enctype enctype,
109 krb5_const_principal server,
110 krb5_principals principals,
111 EncTicketPart *tkt)
113 krb5_error_code ret;
114 KRB5SignedPath sp;
115 krb5_data data;
116 krb5_crypto crypto = NULL;
117 size_t size;
119 if (server && principals) {
120 ret = add_Principals(principals, server);
121 if (ret)
122 return ret;
126 KRB5SignedPathData spd;
128 spd.encticket = *tkt;
129 spd.delegated = principals;
131 ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
132 &spd, &size, ret);
133 if (ret)
134 return ret;
135 if (data.length != size)
136 krb5_abortx(context, "internal asn.1 encoder error");
140 Key *key;
141 ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
142 if (ret == 0)
143 ret = krb5_crypto_init(context, &key->key, 0, &crypto);
144 if (ret) {
145 free(data.data);
146 return ret;
151 * Fill in KRB5SignedPath
154 sp.etype = enctype;
155 sp.delegated = principals;
157 ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
158 data.data, data.length, &sp.cksum);
159 krb5_crypto_destroy(context, crypto);
160 free(data.data);
161 if (ret)
162 return ret;
164 ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
165 free_Checksum(&sp.cksum);
166 if (ret)
167 return ret;
168 if (data.length != size)
169 krb5_abortx(context, "internal asn.1 encoder error");
173 * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
174 * authorization data field.
177 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
178 KRB5_AUTHDATA_SIGNTICKET, &data);
179 krb5_data_free(&data);
181 return ret;
184 static krb5_error_code
185 check_KRB5SignedPath(krb5_context context,
186 krb5_kdc_configuration *config,
187 hdb_entry_ex *krbtgt,
188 EncTicketPart *tkt,
189 krb5_principals *delegated,
190 int *signedpath)
192 krb5_error_code ret;
193 krb5_data data;
194 krb5_crypto crypto = NULL;
196 if (delegated)
197 *delegated = NULL;
199 ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
200 if (ret == 0) {
201 KRB5SignedPathData spd;
202 KRB5SignedPath sp;
203 AuthorizationData *ad;
204 size_t size;
206 ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
207 krb5_data_free(&data);
208 if (ret)
209 return ret;
211 spd.encticket = *tkt;
212 /* the KRB5SignedPath is the last entry */
213 ad = spd.encticket.authorization_data;
214 if (--ad->len == 0)
215 spd.encticket.authorization_data = NULL;
216 spd.delegated = sp.delegated;
218 ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
219 &spd, &size, ret);
220 ad->len++;
221 spd.encticket.authorization_data = ad;
222 if (ret) {
223 free_KRB5SignedPath(&sp);
224 return ret;
226 if (data.length != size)
227 krb5_abortx(context, "internal asn.1 encoder error");
230 Key *key;
231 ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
232 if (ret == 0)
233 ret = krb5_crypto_init(context, &key->key, 0, &crypto);
234 if (ret) {
235 free(data.data);
236 free_KRB5SignedPath(&sp);
237 return ret;
240 ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
241 data.data, data.length,
242 &sp.cksum);
243 krb5_crypto_destroy(context, crypto);
244 free(data.data);
245 if (ret) {
246 free_KRB5SignedPath(&sp);
247 return ret;
250 if (delegated && sp.delegated) {
252 *delegated = malloc(sizeof(*sp.delegated));
253 if (*delegated == NULL) {
254 free_KRB5SignedPath(&sp);
255 return ENOMEM;
258 ret = copy_Principals(*delegated, sp.delegated);
259 if (ret) {
260 free_KRB5SignedPath(&sp);
261 free(*delegated);
262 *delegated = NULL;
263 return ret;
266 free_KRB5SignedPath(&sp);
268 *signedpath = 1;
271 return 0;
278 static krb5_error_code
279 check_PAC(krb5_context context,
280 krb5_kdc_configuration *config,
281 const krb5_principal client_principal,
282 hdb_entry_ex *client,
283 hdb_entry_ex *server,
284 const EncryptionKey *server_key,
285 const EncryptionKey *krbtgt_key,
286 EncTicketPart *tkt,
287 krb5_data *rspac,
288 int *signedpath)
290 AuthorizationData *ad = tkt->authorization_data;
291 unsigned i, j;
292 krb5_error_code ret;
294 if (ad == NULL || ad->len == 0)
295 return 0;
297 for (i = 0; i < ad->len; i++) {
298 AuthorizationData child;
300 if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
301 continue;
303 ret = decode_AuthorizationData(ad->val[i].ad_data.data,
304 ad->val[i].ad_data.length,
305 &child,
306 NULL);
307 if (ret) {
308 krb5_set_error_message(context, ret, "Failed to decode "
309 "IF_RELEVANT with %d", ret);
310 return ret;
312 for (j = 0; j < child.len; j++) {
314 if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
315 krb5_pac pac;
317 /* Found PAC */
318 ret = krb5_pac_parse(context,
319 child.val[j].ad_data.data,
320 child.val[j].ad_data.length,
321 &pac);
322 free_AuthorizationData(&child);
323 if (ret)
324 return ret;
326 ret = krb5_pac_verify(context, pac, tkt->authtime,
327 client_principal,
328 krbtgt_key, NULL);
329 if (ret) {
330 krb5_pac_free(context, pac);
331 return ret;
334 ret = _kdc_pac_verify(context, client_principal,
335 client, server, &pac);
336 if (ret) {
337 krb5_pac_free(context, pac);
338 return ret;
340 *signedpath = 1;
342 ret = _krb5_pac_sign(context, pac, tkt->authtime,
343 client_principal,
344 server_key, krbtgt_key, rspac);
346 krb5_pac_free(context, pac);
348 return ret;
351 free_AuthorizationData(&child);
353 return 0;
360 static krb5_error_code
361 check_tgs_flags(krb5_context context,
362 krb5_kdc_configuration *config,
363 KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
365 KDCOptions f = b->kdc_options;
367 if(f.validate){
368 if(!tgt->flags.invalid || tgt->starttime == NULL){
369 kdc_log(context, config, 0,
370 "Bad request to validate ticket");
371 return KRB5KDC_ERR_BADOPTION;
373 if(*tgt->starttime > kdc_time){
374 kdc_log(context, config, 0,
375 "Early request to validate ticket");
376 return KRB5KRB_AP_ERR_TKT_NYV;
378 /* XXX tkt = tgt */
379 et->flags.invalid = 0;
380 }else if(tgt->flags.invalid){
381 kdc_log(context, config, 0,
382 "Ticket-granting ticket has INVALID flag set");
383 return KRB5KRB_AP_ERR_TKT_INVALID;
386 if(f.forwardable){
387 if(!tgt->flags.forwardable){
388 kdc_log(context, config, 0,
389 "Bad request for forwardable ticket");
390 return KRB5KDC_ERR_BADOPTION;
392 et->flags.forwardable = 1;
394 if(f.forwarded){
395 if(!tgt->flags.forwardable){
396 kdc_log(context, config, 0,
397 "Request to forward non-forwardable ticket");
398 return KRB5KDC_ERR_BADOPTION;
400 et->flags.forwarded = 1;
401 et->caddr = b->addresses;
403 if(tgt->flags.forwarded)
404 et->flags.forwarded = 1;
406 if(f.proxiable){
407 if(!tgt->flags.proxiable){
408 kdc_log(context, config, 0,
409 "Bad request for proxiable ticket");
410 return KRB5KDC_ERR_BADOPTION;
412 et->flags.proxiable = 1;
414 if(f.proxy){
415 if(!tgt->flags.proxiable){
416 kdc_log(context, config, 0,
417 "Request to proxy non-proxiable ticket");
418 return KRB5KDC_ERR_BADOPTION;
420 et->flags.proxy = 1;
421 et->caddr = b->addresses;
423 if(tgt->flags.proxy)
424 et->flags.proxy = 1;
426 if(f.allow_postdate){
427 if(!tgt->flags.may_postdate){
428 kdc_log(context, config, 0,
429 "Bad request for post-datable ticket");
430 return KRB5KDC_ERR_BADOPTION;
432 et->flags.may_postdate = 1;
434 if(f.postdated){
435 if(!tgt->flags.may_postdate){
436 kdc_log(context, config, 0,
437 "Bad request for postdated ticket");
438 return KRB5KDC_ERR_BADOPTION;
440 if(b->from)
441 *et->starttime = *b->from;
442 et->flags.postdated = 1;
443 et->flags.invalid = 1;
444 }else if(b->from && *b->from > kdc_time + context->max_skew){
445 kdc_log(context, config, 0, "Ticket cannot be postdated");
446 return KRB5KDC_ERR_CANNOT_POSTDATE;
449 if(f.renewable){
450 if(!tgt->flags.renewable){
451 kdc_log(context, config, 0,
452 "Bad request for renewable ticket");
453 return KRB5KDC_ERR_BADOPTION;
455 et->flags.renewable = 1;
456 ALLOC(et->renew_till);
457 _kdc_fix_time(&b->rtime);
458 *et->renew_till = *b->rtime;
460 if(f.renew){
461 time_t old_life;
462 if(!tgt->flags.renewable || tgt->renew_till == NULL){
463 kdc_log(context, config, 0,
464 "Request to renew non-renewable ticket");
465 return KRB5KDC_ERR_BADOPTION;
467 old_life = tgt->endtime;
468 if(tgt->starttime)
469 old_life -= *tgt->starttime;
470 else
471 old_life -= tgt->authtime;
472 et->endtime = *et->starttime + old_life;
473 if (et->renew_till != NULL)
474 et->endtime = min(*et->renew_till, et->endtime);
477 #if 0
478 /* checks for excess flags */
479 if(f.request_anonymous && !config->allow_anonymous){
480 kdc_log(context, config, 0,
481 "Request for anonymous ticket");
482 return KRB5KDC_ERR_BADOPTION;
484 #endif
485 return 0;
492 static krb5_error_code
493 check_constrained_delegation(krb5_context context,
494 krb5_kdc_configuration *config,
495 hdb_entry_ex *client,
496 krb5_const_principal server)
498 const HDB_Ext_Constrained_delegation_acl *acl;
499 krb5_error_code ret;
500 int i;
502 ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
503 if (ret) {
504 krb5_clear_error_message(context);
505 return ret;
508 if (acl) {
509 for (i = 0; i < acl->len; i++) {
510 if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
511 return 0;
514 kdc_log(context, config, 0,
515 "Bad request for constrained delegation");
516 return KRB5KDC_ERR_BADOPTION;
523 static krb5_error_code
524 verify_flags (krb5_context context,
525 krb5_kdc_configuration *config,
526 const EncTicketPart *et,
527 const char *pstr)
529 if(et->endtime < kdc_time){
530 kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
531 return KRB5KRB_AP_ERR_TKT_EXPIRED;
533 if(et->flags.invalid){
534 kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
535 return KRB5KRB_AP_ERR_TKT_NYV;
537 return 0;
544 static krb5_error_code
545 fix_transited_encoding(krb5_context context,
546 krb5_kdc_configuration *config,
547 krb5_boolean check_policy,
548 const TransitedEncoding *tr,
549 EncTicketPart *et,
550 const char *client_realm,
551 const char *server_realm,
552 const char *tgt_realm)
554 krb5_error_code ret = 0;
555 char **realms, **tmp;
556 unsigned int num_realms;
557 int i;
559 switch (tr->tr_type) {
560 case DOMAIN_X500_COMPRESS:
561 break;
562 case 0:
564 * Allow empty content of type 0 because that is was Microsoft
565 * generates in their TGT.
567 if (tr->contents.length == 0)
568 break;
569 kdc_log(context, config, 0,
570 "Transited type 0 with non empty content");
571 return KRB5KDC_ERR_TRTYPE_NOSUPP;
572 default:
573 kdc_log(context, config, 0,
574 "Unknown transited type: %u", tr->tr_type);
575 return KRB5KDC_ERR_TRTYPE_NOSUPP;
578 ret = krb5_domain_x500_decode(context,
579 tr->contents,
580 &realms,
581 &num_realms,
582 client_realm,
583 server_realm);
584 if(ret){
585 krb5_warn(context, ret,
586 "Decoding transited encoding");
587 return ret;
589 if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
590 /* not us, so add the previous realm to transited set */
591 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
592 ret = ERANGE;
593 goto free_realms;
595 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
596 if(tmp == NULL){
597 ret = ENOMEM;
598 goto free_realms;
600 realms = tmp;
601 realms[num_realms] = strdup(tgt_realm);
602 if(realms[num_realms] == NULL){
603 ret = ENOMEM;
604 goto free_realms;
606 num_realms++;
608 if(num_realms == 0) {
609 if(strcmp(client_realm, server_realm))
610 kdc_log(context, config, 0,
611 "cross-realm %s -> %s", client_realm, server_realm);
612 } else {
613 size_t l = 0;
614 char *rs;
615 for(i = 0; i < num_realms; i++)
616 l += strlen(realms[i]) + 2;
617 rs = malloc(l);
618 if(rs != NULL) {
619 *rs = '\0';
620 for(i = 0; i < num_realms; i++) {
621 if(i > 0)
622 strlcat(rs, ", ", l);
623 strlcat(rs, realms[i], l);
625 kdc_log(context, config, 0,
626 "cross-realm %s -> %s via [%s]",
627 client_realm, server_realm, rs);
628 free(rs);
631 if(check_policy) {
632 ret = krb5_check_transited(context, client_realm,
633 server_realm,
634 realms, num_realms, NULL);
635 if(ret) {
636 krb5_warn(context, ret, "cross-realm %s -> %s",
637 client_realm, server_realm);
638 goto free_realms;
640 et->flags.transited_policy_checked = 1;
642 et->transited.tr_type = DOMAIN_X500_COMPRESS;
643 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
644 if(ret)
645 krb5_warn(context, ret, "Encoding transited encoding");
646 free_realms:
647 for(i = 0; i < num_realms; i++)
648 free(realms[i]);
649 free(realms);
650 return ret;
654 static krb5_error_code
655 tgs_make_reply(krb5_context context,
656 krb5_kdc_configuration *config,
657 KDC_REQ_BODY *b,
658 krb5_const_principal tgt_name,
659 const EncTicketPart *tgt,
660 const EncryptionKey *serverkey,
661 const krb5_keyblock *sessionkey,
662 krb5_kvno kvno,
663 AuthorizationData *auth_data,
664 hdb_entry_ex *server,
665 krb5_principal server_principal,
666 const char *server_name,
667 hdb_entry_ex *client,
668 krb5_principal client_principal,
669 hdb_entry_ex *krbtgt,
670 krb5_enctype krbtgt_etype,
671 krb5_principals spp,
672 const krb5_data *rspac,
673 const METHOD_DATA *enc_pa_data,
674 const char **e_text,
675 krb5_data *reply)
677 KDC_REP rep;
678 EncKDCRepPart ek;
679 EncTicketPart et;
680 KDCOptions f = b->kdc_options;
681 krb5_error_code ret;
682 int is_weak = 0;
684 memset(&rep, 0, sizeof(rep));
685 memset(&et, 0, sizeof(et));
686 memset(&ek, 0, sizeof(ek));
688 rep.pvno = 5;
689 rep.msg_type = krb_tgs_rep;
691 et.authtime = tgt->authtime;
692 _kdc_fix_time(&b->till);
693 et.endtime = min(tgt->endtime, *b->till);
694 ALLOC(et.starttime);
695 *et.starttime = kdc_time;
697 ret = check_tgs_flags(context, config, b, tgt, &et);
698 if(ret)
699 goto out;
701 /* We should check the transited encoding if:
702 1) the request doesn't ask not to be checked
703 2) globally enforcing a check
704 3) principal requires checking
705 4) we allow non-check per-principal, but principal isn't marked as allowing this
706 5) we don't globally allow this
709 #define GLOBAL_FORCE_TRANSITED_CHECK \
710 (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
711 #define GLOBAL_ALLOW_PER_PRINCIPAL \
712 (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
713 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
714 (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
716 /* these will consult the database in future release */
717 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
718 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
720 ret = fix_transited_encoding(context, config,
721 !f.disable_transited_check ||
722 GLOBAL_FORCE_TRANSITED_CHECK ||
723 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
724 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
725 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
726 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
727 &tgt->transited, &et,
728 krb5_principal_get_realm(context, client_principal),
729 krb5_principal_get_realm(context, server->entry.principal),
730 krb5_principal_get_realm(context, krbtgt->entry.principal));
731 if(ret)
732 goto out;
734 copy_Realm(&server_principal->realm, &rep.ticket.realm);
735 _krb5_principal2principalname(&rep.ticket.sname, server_principal);
736 copy_Realm(&tgt_name->realm, &rep.crealm);
738 if (f.request_anonymous)
739 _kdc_make_anonymous_principalname (&rep.cname);
740 else */
742 copy_PrincipalName(&tgt_name->name, &rep.cname);
743 rep.ticket.tkt_vno = 5;
745 ek.caddr = et.caddr;
746 if(et.caddr == NULL)
747 et.caddr = tgt->caddr;
750 time_t life;
751 life = et.endtime - *et.starttime;
752 if(client && client->entry.max_life)
753 life = min(life, *client->entry.max_life);
754 if(server->entry.max_life)
755 life = min(life, *server->entry.max_life);
756 et.endtime = *et.starttime + life;
758 if(f.renewable_ok && tgt->flags.renewable &&
759 et.renew_till == NULL && et.endtime < *b->till){
760 et.flags.renewable = 1;
761 ALLOC(et.renew_till);
762 *et.renew_till = *b->till;
764 if(et.renew_till){
765 time_t renew;
766 renew = *et.renew_till - et.authtime;
767 if(client && client->entry.max_renew)
768 renew = min(renew, *client->entry.max_renew);
769 if(server->entry.max_renew)
770 renew = min(renew, *server->entry.max_renew);
771 *et.renew_till = et.authtime + renew;
774 if(et.renew_till){
775 *et.renew_till = min(*et.renew_till, *tgt->renew_till);
776 *et.starttime = min(*et.starttime, *et.renew_till);
777 et.endtime = min(et.endtime, *et.renew_till);
780 *et.starttime = min(*et.starttime, et.endtime);
782 if(*et.starttime == et.endtime){
783 ret = KRB5KDC_ERR_NEVER_VALID;
784 goto out;
786 if(et.renew_till && et.endtime == *et.renew_till){
787 free(et.renew_till);
788 et.renew_till = NULL;
789 et.flags.renewable = 0;
792 et.flags.pre_authent = tgt->flags.pre_authent;
793 et.flags.hw_authent = tgt->flags.hw_authent;
794 et.flags.anonymous = tgt->flags.anonymous;
795 et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
797 if (auth_data) {
798 /* XXX Check enc-authorization-data */
799 et.authorization_data = calloc(1, sizeof(*et.authorization_data));
800 if (et.authorization_data == NULL) {
801 ret = ENOMEM;
802 goto out;
804 ret = copy_AuthorizationData(auth_data, et.authorization_data);
805 if (ret)
806 goto out;
808 /* Filter out type KRB5SignedPath */
809 ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
810 if (ret == 0) {
811 if (et.authorization_data->len == 1) {
812 free_AuthorizationData(et.authorization_data);
813 free(et.authorization_data);
814 et.authorization_data = NULL;
815 } else {
816 AuthorizationData *ad = et.authorization_data;
817 free_AuthorizationDataElement(&ad->val[ad->len - 1]);
818 ad->len--;
823 if(rspac->length) {
825 * No not need to filter out the any PAC from the
826 * auth_data since it's signed by the KDC.
828 ret = _kdc_tkt_add_if_relevant_ad(context, &et,
829 KRB5_AUTHDATA_WIN2K_PAC,
830 rspac);
831 if (ret)
832 goto out;
835 ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
836 if (ret)
837 goto out;
838 et.crealm = tgt->crealm;
839 et.cname = tgt_name->name;
841 ek.key = et.key;
842 /* MIT must have at least one last_req */
843 ek.last_req.len = 1;
844 ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
845 if (ek.last_req.val == NULL) {
846 ret = ENOMEM;
847 goto out;
849 ek.nonce = b->nonce;
850 ek.flags = et.flags;
851 ek.authtime = et.authtime;
852 ek.starttime = et.starttime;
853 ek.endtime = et.endtime;
854 ek.renew_till = et.renew_till;
855 ek.srealm = rep.ticket.realm;
856 ek.sname = rep.ticket.sname;
858 _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
859 et.endtime, et.renew_till);
861 /* Don't sign cross realm tickets, they can't be checked anyway */
863 char *r = get_krbtgt_realm(&ek.sname);
865 if (r == NULL || strcmp(r, ek.srealm) == 0) {
866 ret = _kdc_add_KRB5SignedPath(context,
867 config,
868 krbtgt,
869 krbtgt_etype,
870 NULL,
871 spp,
872 &et);
873 if (ret)
874 goto out;
878 if (enc_pa_data->len) {
879 rep.padata = calloc(1, sizeof(*rep.padata));
880 if (rep.padata == NULL) {
881 ret = ENOMEM;
882 goto out;
884 ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
885 if (ret)
886 goto out;
889 if (krb5_enctype_valid(context, et.key.keytype) != 0
890 && _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
892 krb5_enctype_enable(context, et.key.keytype);
893 is_weak = 1;
897 /* It is somewhat unclear where the etype in the following
898 encryption should come from. What we have is a session
899 key in the passed tgt, and a list of preferred etypes
900 *for the new ticket*. Should we pick the best possible
901 etype, given the keytype in the tgt, or should we look
902 at the etype list here as well? What if the tgt
903 session key is DES3 and we want a ticket with a (say)
904 CAST session key. Should the DES3 etype be added to the
905 etype list, even if we don't want a session key with
906 DES3? */
907 ret = _kdc_encode_reply(context, config,
908 &rep, &et, &ek, et.key.keytype,
909 kvno,
910 serverkey, 0, &tgt->key, e_text, reply);
911 if (is_weak)
912 krb5_enctype_disable(context, et.key.keytype);
914 out:
915 free_TGS_REP(&rep);
916 free_TransitedEncoding(&et.transited);
917 if(et.starttime)
918 free(et.starttime);
919 if(et.renew_till)
920 free(et.renew_till);
921 if(et.authorization_data) {
922 free_AuthorizationData(et.authorization_data);
923 free(et.authorization_data);
925 free_LastReq(&ek.last_req);
926 memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
927 free_EncryptionKey(&et.key);
928 return ret;
931 static krb5_error_code
932 tgs_check_authenticator(krb5_context context,
933 krb5_kdc_configuration *config,
934 krb5_auth_context ac,
935 KDC_REQ_BODY *b,
936 const char **e_text,
937 krb5_keyblock *key)
939 krb5_authenticator auth;
940 size_t len;
941 unsigned char *buf;
942 size_t buf_size;
943 krb5_error_code ret;
944 krb5_crypto crypto;
946 krb5_auth_con_getauthenticator(context, ac, &auth);
947 if(auth->cksum == NULL){
948 kdc_log(context, config, 0, "No authenticator in request");
949 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
950 goto out;
953 * according to RFC1510 it doesn't need to be keyed,
954 * but according to the latest draft it needs to.
956 if (
957 #if 0
958 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
960 #endif
961 !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
962 kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
963 auth->cksum->cksumtype);
964 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
965 goto out;
968 /* XXX should not re-encode this */
969 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
970 if(ret){
971 kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
972 krb5_get_err_text(context, ret));
973 goto out;
975 if(buf_size != len) {
976 free(buf);
977 kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
978 *e_text = "KDC internal error";
979 ret = KRB5KRB_ERR_GENERIC;
980 goto out;
982 ret = krb5_crypto_init(context, key, 0, &crypto);
983 if (ret) {
984 free(buf);
985 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
986 krb5_get_err_text(context, ret));
987 goto out;
989 ret = krb5_verify_checksum(context,
990 crypto,
991 KRB5_KU_TGS_REQ_AUTH_CKSUM,
992 buf,
993 len,
994 auth->cksum);
995 free(buf);
996 krb5_crypto_destroy(context, crypto);
997 if(ret){
998 kdc_log(context, config, 0,
999 "Failed to verify authenticator checksum: %s",
1000 krb5_get_err_text(context, ret));
1002 out:
1003 free_Authenticator(auth);
1004 free(auth);
1005 return ret;
1012 static const char *
1013 find_rpath(krb5_context context, Realm crealm, Realm srealm)
1015 const char *new_realm = krb5_config_get_string(context,
1016 NULL,
1017 "capaths",
1018 crealm,
1019 srealm,
1020 NULL);
1021 return new_realm;
1025 static krb5_boolean
1026 need_referral(krb5_context context, krb5_kdc_configuration *config,
1027 const KDCOptions * const options, krb5_principal server,
1028 krb5_realm **realms)
1030 const char *name;
1032 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
1033 return FALSE;
1035 if (server->name.name_string.len == 1)
1036 name = server->name.name_string.val[0];
1037 else if (server->name.name_string.len > 1)
1038 name = server->name.name_string.val[1];
1039 else
1040 return FALSE;
1042 kdc_log(context, config, 0, "Searching referral for %s", name);
1044 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
1047 static krb5_error_code
1048 tgs_parse_request(krb5_context context,
1049 krb5_kdc_configuration *config,
1050 KDC_REQ_BODY *b,
1051 const PA_DATA *tgs_req,
1052 hdb_entry_ex **krbtgt,
1053 krb5_enctype *krbtgt_etype,
1054 krb5_ticket **ticket,
1055 const char **e_text,
1056 const char *from,
1057 const struct sockaddr *from_addr,
1058 time_t **csec,
1059 int **cusec,
1060 AuthorizationData **auth_data)
1062 krb5_ap_req ap_req;
1063 krb5_error_code ret;
1064 krb5_principal princ;
1065 krb5_auth_context ac = NULL;
1066 krb5_flags ap_req_options;
1067 krb5_flags verify_ap_req_flags;
1068 krb5_crypto crypto;
1069 Key *tkey;
1071 *auth_data = NULL;
1072 *csec = NULL;
1073 *cusec = NULL;
1075 memset(&ap_req, 0, sizeof(ap_req));
1076 ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
1077 if(ret){
1078 kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
1079 krb5_get_err_text(context, ret));
1080 goto out;
1083 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1084 /* XXX check for ticket.sname == req.sname */
1085 kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
1086 ret = KRB5KDC_ERR_POLICY; /* ? */
1087 goto out;
1090 _krb5_principalname2krb5_principal(context,
1091 &princ,
1092 ap_req.ticket.sname,
1093 ap_req.ticket.realm);
1095 ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
1097 if(ret) {
1098 char *p;
1099 ret = krb5_unparse_name(context, princ, &p);
1100 if (ret != 0)
1101 p = "<unparse_name failed>";
1102 krb5_free_principal(context, princ);
1103 kdc_log(context, config, 0,
1104 "Ticket-granting ticket not found in database: %s: %s",
1105 p, krb5_get_err_text(context, ret));
1106 if (ret == 0)
1107 free(p);
1108 ret = KRB5KRB_AP_ERR_NOT_US;
1109 goto out;
1112 if(ap_req.ticket.enc_part.kvno &&
1113 *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
1114 char *p;
1116 ret = krb5_unparse_name (context, princ, &p);
1117 krb5_free_principal(context, princ);
1118 if (ret != 0)
1119 p = "<unparse_name failed>";
1120 kdc_log(context, config, 0,
1121 "Ticket kvno = %d, DB kvno = %d (%s)",
1122 *ap_req.ticket.enc_part.kvno,
1123 (*krbtgt)->entry.kvno,
1125 if (ret == 0)
1126 free (p);
1127 ret = KRB5KRB_AP_ERR_BADKEYVER;
1128 goto out;
1131 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1133 ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1134 ap_req.ticket.enc_part.etype, &tkey);
1135 if(ret){
1136 char *str = NULL, *p = NULL;
1138 krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1139 krb5_unparse_name(context, princ, &p);
1140 kdc_log(context, config, 0,
1141 "No server key with enctype %s found for %s",
1142 str ? str : "<unknown enctype>",
1143 p ? p : "<unparse_name failed>");
1144 free(str);
1145 free(p);
1146 ret = KRB5KRB_AP_ERR_BADKEYVER;
1147 goto out;
1150 if (b->kdc_options.validate)
1151 verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1152 else
1153 verify_ap_req_flags = 0;
1155 ret = krb5_verify_ap_req2(context,
1156 &ac,
1157 &ap_req,
1158 princ,
1159 &tkey->key,
1160 verify_ap_req_flags,
1161 &ap_req_options,
1162 ticket,
1163 KRB5_KU_TGS_REQ_AUTH);
1165 krb5_free_principal(context, princ);
1166 if(ret) {
1167 kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
1168 krb5_get_err_text(context, ret));
1169 goto out;
1173 krb5_authenticator auth;
1175 ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1176 if (ret == 0) {
1177 *csec = malloc(sizeof(**csec));
1178 if (*csec == NULL) {
1179 krb5_free_authenticator(context, &auth);
1180 kdc_log(context, config, 0, "malloc failed");
1181 goto out;
1183 **csec = auth->ctime;
1184 *cusec = malloc(sizeof(**cusec));
1185 if (*cusec == NULL) {
1186 krb5_free_authenticator(context, &auth);
1187 kdc_log(context, config, 0, "malloc failed");
1188 goto out;
1190 **cusec = auth->cusec;
1191 krb5_free_authenticator(context, &auth);
1195 ret = tgs_check_authenticator(context, config,
1196 ac, b, e_text, &(*ticket)->ticket.key);
1197 if (ret) {
1198 krb5_auth_con_free(context, ac);
1199 goto out;
1202 if (b->enc_authorization_data) {
1203 unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1204 krb5_keyblock *subkey;
1205 krb5_data ad;
1207 ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
1208 if(ret){
1209 krb5_auth_con_free(context, ac);
1210 kdc_log(context, config, 0, "Failed to get remote subkey: %s",
1211 krb5_get_err_text(context, ret));
1212 goto out;
1214 if(subkey == NULL){
1215 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1216 ret = krb5_auth_con_getkey(context, ac, &subkey);
1217 if(ret) {
1218 krb5_auth_con_free(context, ac);
1219 kdc_log(context, config, 0, "Failed to get session key: %s",
1220 krb5_get_err_text(context, ret));
1221 goto out;
1224 if(subkey == NULL){
1225 krb5_auth_con_free(context, ac);
1226 kdc_log(context, config, 0,
1227 "Failed to get key for enc-authorization-data");
1228 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1229 goto out;
1231 ret = krb5_crypto_init(context, subkey, 0, &crypto);
1232 krb5_free_keyblock(context, subkey);
1233 if (ret) {
1234 krb5_auth_con_free(context, ac);
1235 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1236 krb5_get_err_text(context, ret));
1237 goto out;
1239 ret = krb5_decrypt_EncryptedData (context,
1240 crypto,
1241 usage,
1242 b->enc_authorization_data,
1243 &ad);
1244 krb5_crypto_destroy(context, crypto);
1245 if(ret){
1246 krb5_auth_con_free(context, ac);
1247 kdc_log(context, config, 0,
1248 "Failed to decrypt enc-authorization-data");
1249 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1250 goto out;
1252 ALLOC(*auth_data);
1253 if (*auth_data == NULL) {
1254 krb5_auth_con_free(context, ac);
1255 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1256 goto out;
1258 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1259 if(ret){
1260 krb5_auth_con_free(context, ac);
1261 free(*auth_data);
1262 *auth_data = NULL;
1263 kdc_log(context, config, 0, "Failed to decode authorization data");
1264 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1265 goto out;
1269 krb5_auth_con_free(context, ac);
1271 out:
1272 free_AP_REQ(&ap_req);
1274 return ret;
1277 static krb5_error_code
1278 build_server_referral(krb5_context context,
1279 krb5_kdc_configuration *config,
1280 krb5_crypto session,
1281 krb5_const_realm referred_realm,
1282 const PrincipalName *true_principal_name,
1283 const PrincipalName *requested_principal,
1284 krb5_data *outdata)
1286 PA_ServerReferralData ref;
1287 krb5_error_code ret;
1288 EncryptedData ed;
1289 krb5_data data;
1290 size_t size;
1292 memset(&ref, 0, sizeof(ref));
1294 if (referred_realm) {
1295 ALLOC(ref.referred_realm);
1296 if (ref.referred_realm == NULL)
1297 goto eout;
1298 *ref.referred_realm = strdup(referred_realm);
1299 if (*ref.referred_realm == NULL)
1300 goto eout;
1302 if (true_principal_name) {
1303 ALLOC(ref.true_principal_name);
1304 if (ref.true_principal_name == NULL)
1305 goto eout;
1306 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1307 if (ret)
1308 goto eout;
1310 if (requested_principal) {
1311 ALLOC(ref.requested_principal_name);
1312 if (ref.requested_principal_name == NULL)
1313 goto eout;
1314 ret = copy_PrincipalName(requested_principal,
1315 ref.requested_principal_name);
1316 if (ret)
1317 goto eout;
1320 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1321 data.data, data.length,
1322 &ref, &size, ret);
1323 free_PA_ServerReferralData(&ref);
1324 if (ret)
1325 return ret;
1326 if (data.length != size)
1327 krb5_abortx(context, "internal asn.1 encoder error");
1329 ret = krb5_encrypt_EncryptedData(context, session,
1330 KRB5_KU_PA_SERVER_REFERRAL,
1331 data.data, data.length,
1332 0 /* kvno */, &ed);
1333 free(data.data);
1334 if (ret)
1335 return ret;
1337 ASN1_MALLOC_ENCODE(EncryptedData,
1338 outdata->data, outdata->length,
1339 &ed, &size, ret);
1340 free_EncryptedData(&ed);
1341 if (ret)
1342 return ret;
1343 if (outdata->length != size)
1344 krb5_abortx(context, "internal asn.1 encoder error");
1346 return 0;
1347 eout:
1348 free_PA_ServerReferralData(&ref);
1349 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1350 return ENOMEM;
1353 static krb5_error_code
1354 tgs_build_reply(krb5_context context,
1355 krb5_kdc_configuration *config,
1356 KDC_REQ *req,
1357 KDC_REQ_BODY *b,
1358 hdb_entry_ex *krbtgt,
1359 krb5_enctype krbtgt_etype,
1360 krb5_ticket *ticket,
1361 krb5_data *reply,
1362 const char *from,
1363 const char **e_text,
1364 AuthorizationData **auth_data,
1365 const struct sockaddr *from_addr)
1367 krb5_error_code ret;
1368 krb5_principal cp = NULL, sp = NULL;
1369 krb5_principal client_principal = NULL;
1370 char *spn = NULL, *cpn = NULL;
1371 hdb_entry_ex *server = NULL, *client = NULL;
1372 krb5_realm ref_realm = NULL;
1373 EncTicketPart *tgt = &ticket->ticket;
1374 krb5_principals spp = NULL;
1375 const EncryptionKey *ekey;
1376 krb5_keyblock sessionkey;
1377 krb5_kvno kvno;
1378 krb5_data rspac;
1380 METHOD_DATA enc_pa_data;
1382 PrincipalName *s;
1383 Realm r;
1384 int nloop = 0;
1385 EncTicketPart adtkt;
1386 char opt_str[128];
1387 int signedpath = 0;
1389 Key *tkey;
1391 memset(&sessionkey, 0, sizeof(sessionkey));
1392 memset(&adtkt, 0, sizeof(adtkt));
1393 krb5_data_zero(&rspac);
1394 memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1396 s = b->sname;
1397 r = b->realm;
1399 if(b->kdc_options.enc_tkt_in_skey){
1400 Ticket *t;
1401 hdb_entry_ex *uu;
1402 krb5_principal p;
1403 Key *uukey;
1405 if(b->additional_tickets == NULL ||
1406 b->additional_tickets->len == 0){
1407 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1408 kdc_log(context, config, 0,
1409 "No second ticket present in request");
1410 goto out;
1412 t = &b->additional_tickets->val[0];
1413 if(!get_krbtgt_realm(&t->sname)){
1414 kdc_log(context, config, 0,
1415 "Additional ticket is not a ticket-granting ticket");
1416 ret = KRB5KDC_ERR_POLICY;
1417 goto out;
1419 _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1420 ret = _kdc_db_fetch(context, config, p,
1421 HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
1422 NULL, &uu);
1423 krb5_free_principal(context, p);
1424 if(ret){
1425 if (ret == HDB_ERR_NOENTRY)
1426 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1427 goto out;
1429 ret = hdb_enctype2key(context, &uu->entry,
1430 t->enc_part.etype, &uukey);
1431 if(ret){
1432 _kdc_free_ent(context, uu);
1433 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1434 goto out;
1436 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1437 _kdc_free_ent(context, uu);
1438 if(ret)
1439 goto out;
1441 ret = verify_flags(context, config, &adtkt, spn);
1442 if (ret)
1443 goto out;
1445 s = &adtkt.cname;
1446 r = adtkt.crealm;
1449 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1450 ret = krb5_unparse_name(context, sp, &spn);
1451 if (ret)
1452 goto out;
1453 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1454 ret = krb5_unparse_name(context, cp, &cpn);
1455 if (ret)
1456 goto out;
1457 unparse_flags (KDCOptions2int(b->kdc_options),
1458 asn1_KDCOptions_units(),
1459 opt_str, sizeof(opt_str));
1460 if(*opt_str)
1461 kdc_log(context, config, 0,
1462 "TGS-REQ %s from %s for %s [%s]",
1463 cpn, from, spn, opt_str);
1464 else
1465 kdc_log(context, config, 0,
1466 "TGS-REQ %s from %s for %s", cpn, from, spn);
1469 * Fetch server
1472 server_lookup:
1473 ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | HDB_F_CANON,
1474 NULL, &server);
1476 if(ret){
1477 const char *new_rlm;
1478 Realm req_rlm;
1479 krb5_realm *realms;
1481 if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1482 if(nloop++ < 2) {
1483 new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1484 if(new_rlm) {
1485 kdc_log(context, config, 5, "krbtgt for realm %s "
1486 "not found, trying %s",
1487 req_rlm, new_rlm);
1488 krb5_free_principal(context, sp);
1489 free(spn);
1490 krb5_make_principal(context, &sp, r,
1491 KRB5_TGS_NAME, new_rlm, NULL);
1492 ret = krb5_unparse_name(context, sp, &spn);
1493 if (ret)
1494 goto out;
1496 if (ref_realm)
1497 free(ref_realm);
1498 ref_realm = strdup(new_rlm);
1499 goto server_lookup;
1502 } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1503 if (strcmp(realms[0], sp->realm) != 0) {
1504 kdc_log(context, config, 5,
1505 "Returning a referral to realm %s for "
1506 "server %s that was not found",
1507 realms[0], spn);
1508 krb5_free_principal(context, sp);
1509 free(spn);
1510 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1511 realms[0], NULL);
1512 ret = krb5_unparse_name(context, sp, &spn);
1513 if (ret)
1514 goto out;
1516 if (ref_realm)
1517 free(ref_realm);
1518 ref_realm = strdup(realms[0]);
1520 krb5_free_host_realm(context, realms);
1521 goto server_lookup;
1523 krb5_free_host_realm(context, realms);
1525 kdc_log(context, config, 0,
1526 "Server not found in database: %s: %s", spn,
1527 krb5_get_err_text(context, ret));
1528 if (ret == HDB_ERR_NOENTRY)
1529 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1530 goto out;
1533 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | HDB_F_CANON,
1534 NULL, &client);
1535 if(ret) {
1536 const char *krbtgt_realm;
1539 * If the client belongs to the same realm as our krbtgt, it
1540 * should exist in the local database.
1544 krbtgt_realm =
1545 krb5_principal_get_comp_string(context,
1546 krbtgt->entry.principal, 1);
1548 if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1549 if (ret == HDB_ERR_NOENTRY)
1550 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1551 kdc_log(context, config, 1, "Client no longer in database: %s",
1552 cpn);
1553 goto out;
1556 kdc_log(context, config, 1, "Client not found in database: %s: %s",
1557 cpn, krb5_get_err_text(context, ret));
1561 * Select enctype, return key and kvno.
1565 krb5_enctype etype;
1567 if(b->kdc_options.enc_tkt_in_skey) {
1568 int i;
1569 ekey = &adtkt.key;
1570 for(i = 0; i < b->etype.len; i++)
1571 if (b->etype.val[i] == adtkt.key.keytype)
1572 break;
1573 if(i == b->etype.len) {
1574 kdc_log(context, config, 0,
1575 "Addition ticket have not matching etypes");
1576 krb5_clear_error_message(context);
1577 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1578 goto out;
1580 etype = b->etype.val[i];
1581 kvno = 0;
1582 } else {
1583 Key *skey;
1585 ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
1586 &skey, &etype);
1587 if(ret) {
1588 kdc_log(context, config, 0,
1589 "Server (%s) has no support for etypes", spn);
1590 goto out;
1592 ekey = &skey->key;
1593 kvno = server->entry.kvno;
1596 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1597 if (ret)
1598 goto out;
1602 * Check that service is in the same realm as the krbtgt. If it's
1603 * not the same, it's someone that is using a uni-directional trust
1604 * backward.
1607 if (strcmp(krb5_principal_get_realm(context, sp),
1608 krb5_principal_get_comp_string(context,
1609 krbtgt->entry.principal,
1610 1)) != 0) {
1611 char *tpn;
1612 ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
1613 kdc_log(context, config, 0,
1614 "Request with wrong krbtgt: %s",
1615 (ret == 0) ? tpn : "<unknown>");
1616 if(ret == 0)
1617 free(tpn);
1618 ret = KRB5KRB_AP_ERR_NOT_US;
1619 goto out;
1623 * Validate authoriation data
1626 ret = hdb_enctype2key(context, &krbtgt->entry,
1627 krbtgt_etype, &tkey);
1628 if(ret) {
1629 kdc_log(context, config, 0,
1630 "Failed to find key for krbtgt PAC check");
1631 goto out;
1634 ret = check_PAC(context, config, cp,
1635 client, server, ekey, &tkey->key,
1636 tgt, &rspac, &signedpath);
1637 if (ret) {
1638 kdc_log(context, config, 0,
1639 "Verify PAC failed for %s (%s) from %s with %s",
1640 spn, cpn, from, krb5_get_err_text(context, ret));
1641 goto out;
1644 /* also check the krbtgt for signature */
1645 ret = check_KRB5SignedPath(context,
1646 config,
1647 krbtgt,
1648 tgt,
1649 &spp,
1650 &signedpath);
1651 if (ret) {
1652 kdc_log(context, config, 0,
1653 "KRB5SignedPath check failed for %s (%s) from %s with %s",
1654 spn, cpn, from, krb5_get_err_text(context, ret));
1655 goto out;
1659 * Process request
1662 client_principal = cp;
1664 if (client) {
1665 const PA_DATA *sdata;
1666 int i = 0;
1668 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1669 if (sdata) {
1670 krb5_crypto crypto;
1671 krb5_data datack;
1672 PA_S4U2Self self;
1673 char *selfcpn = NULL;
1674 const char *str;
1676 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1677 sdata->padata_value.length,
1678 &self, NULL);
1679 if (ret) {
1680 kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1681 goto out;
1684 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1685 if (ret)
1686 goto out;
1688 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1689 if (ret) {
1690 free_PA_S4U2Self(&self);
1691 krb5_data_free(&datack);
1692 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1693 krb5_get_err_text(context, ret));
1694 goto out;
1697 ret = krb5_verify_checksum(context,
1698 crypto,
1699 KRB5_KU_OTHER_CKSUM,
1700 datack.data,
1701 datack.length,
1702 &self.cksum);
1703 krb5_data_free(&datack);
1704 krb5_crypto_destroy(context, crypto);
1705 if (ret) {
1706 free_PA_S4U2Self(&self);
1707 kdc_log(context, config, 0,
1708 "krb5_verify_checksum failed for S4U2Self: %s",
1709 krb5_get_err_text(context, ret));
1710 goto out;
1713 ret = _krb5_principalname2krb5_principal(context,
1714 &client_principal,
1715 self.name,
1716 self.realm);
1717 free_PA_S4U2Self(&self);
1718 if (ret)
1719 goto out;
1721 ret = krb5_unparse_name(context, client_principal, &selfcpn);
1722 if (ret)
1723 goto out;
1726 * Check that service doing the impersonating is
1727 * requesting a ticket to it-self.
1729 if (krb5_principal_compare(context, cp, sp) != TRUE) {
1730 kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1731 "to impersonate some other user "
1732 "(tried for user %s to service %s)",
1733 cpn, selfcpn, spn);
1734 free(selfcpn);
1735 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1736 goto out;
1740 * If the service isn't trusted for authentication to
1741 * delegation, remove the forward flag.
1744 if (client->entry.flags.trusted_for_delegation) {
1745 str = "[forwardable]";
1746 } else {
1747 b->kdc_options.forwardable = 0;
1748 str = "";
1750 kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
1751 "service %s %s", cpn, selfcpn, spn, str);
1752 free(selfcpn);
1757 * Constrained delegation
1760 if (client != NULL
1761 && b->additional_tickets != NULL
1762 && b->additional_tickets->len != 0
1763 && b->kdc_options.enc_tkt_in_skey == 0)
1765 int ad_signedpath = 0;
1766 Key *clientkey;
1767 Ticket *t;
1768 char *str;
1771 * Require that the KDC have issued the service's krbtgt (not
1772 * self-issued ticket with kimpersonate(1).
1774 if (!signedpath) {
1775 ret = KRB5KDC_ERR_BADOPTION;
1776 kdc_log(context, config, 0,
1777 "Constrained delegation done on service ticket %s/%s",
1778 cpn, spn);
1779 goto out;
1782 t = &b->additional_tickets->val[0];
1784 ret = hdb_enctype2key(context, &client->entry,
1785 t->enc_part.etype, &clientkey);
1786 if(ret){
1787 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1788 goto out;
1791 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
1792 if (ret) {
1793 kdc_log(context, config, 0,
1794 "failed to decrypt ticket for "
1795 "constrained delegation from %s to %s ", cpn, spn);
1796 goto out;
1799 /* check that ticket is valid */
1800 if (adtkt.flags.forwardable == 0) {
1801 kdc_log(context, config, 0,
1802 "Missing forwardable flag on ticket for "
1803 "constrained delegation from %s to %s ", cpn, spn);
1804 ret = KRB5KDC_ERR_BADOPTION;
1805 goto out;
1808 ret = check_constrained_delegation(context, config, client, sp);
1809 if (ret) {
1810 kdc_log(context, config, 0,
1811 "constrained delegation from %s to %s not allowed",
1812 cpn, spn);
1813 goto out;
1816 ret = _krb5_principalname2krb5_principal(context,
1817 &client_principal,
1818 adtkt.cname,
1819 adtkt.crealm);
1820 if (ret)
1821 goto out;
1823 ret = krb5_unparse_name(context, client_principal, &str);
1824 if (ret)
1825 goto out;
1827 ret = verify_flags(context, config, &adtkt, str);
1828 if (ret) {
1829 free(str);
1830 goto out;
1834 * Check that the KDC issued the user's ticket.
1836 ret = check_KRB5SignedPath(context,
1837 config,
1838 krbtgt,
1839 &adtkt,
1840 NULL,
1841 &ad_signedpath);
1842 if (ret == 0 && !ad_signedpath)
1843 ret = KRB5KDC_ERR_BADOPTION;
1844 if (ret) {
1845 kdc_log(context, config, 0,
1846 "KRB5SignedPath check from service %s failed "
1847 "for delegation to %s for client %s "
1848 "from %s failed with %s",
1849 spn, str, cpn, from, krb5_get_err_text(context, ret));
1850 free(str);
1851 goto out;
1854 kdc_log(context, config, 0, "constrained delegation for %s "
1855 "from %s to %s", str, cpn, spn);
1856 free(str);
1860 * Check flags
1863 ret = kdc_check_flags(context, config,
1864 client, cpn,
1865 server, spn,
1866 FALSE);
1867 if(ret)
1868 goto out;
1870 if((b->kdc_options.validate || b->kdc_options.renew) &&
1871 !krb5_principal_compare(context,
1872 krbtgt->entry.principal,
1873 server->entry.principal)){
1874 kdc_log(context, config, 0, "Inconsistent request.");
1875 ret = KRB5KDC_ERR_SERVER_NOMATCH;
1876 goto out;
1879 /* check for valid set of addresses */
1880 if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
1881 ret = KRB5KRB_AP_ERR_BADADDR;
1882 kdc_log(context, config, 0, "Request from wrong address");
1883 goto out;
1887 * If this is an referral, add server referral data to the
1888 * auth_data reply .
1890 if (ref_realm) {
1891 PA_DATA pa;
1892 krb5_crypto crypto;
1894 kdc_log(context, config, 0,
1895 "Adding server referral to %s", ref_realm);
1897 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
1898 if (ret)
1899 goto out;
1901 ret = build_server_referral(context, config, crypto, ref_realm,
1902 NULL, s, &pa.padata_value);
1903 krb5_crypto_destroy(context, crypto);
1904 if (ret) {
1905 kdc_log(context, config, 0,
1906 "Failed building server referral");
1907 goto out;
1909 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
1911 ret = add_METHOD_DATA(&enc_pa_data, &pa);
1912 krb5_data_free(&pa.padata_value);
1913 if (ret) {
1914 kdc_log(context, config, 0,
1915 "Add server referral METHOD-DATA failed");
1916 goto out;
1924 ret = tgs_make_reply(context,
1925 config,
1927 client_principal,
1928 tgt,
1929 ekey,
1930 &sessionkey,
1931 kvno,
1932 *auth_data,
1933 server,
1935 spn,
1936 client,
1938 krbtgt,
1939 krbtgt_etype,
1940 spp,
1941 &rspac,
1942 &enc_pa_data,
1943 e_text,
1944 reply);
1946 out:
1947 free(spn);
1948 free(cpn);
1950 krb5_data_free(&rspac);
1951 krb5_free_keyblock_contents(context, &sessionkey);
1952 if(server)
1953 _kdc_free_ent(context, server);
1954 if(client)
1955 _kdc_free_ent(context, client);
1957 if (client_principal && client_principal != cp)
1958 krb5_free_principal(context, client_principal);
1959 if (cp)
1960 krb5_free_principal(context, cp);
1961 if (sp)
1962 krb5_free_principal(context, sp);
1963 if (ref_realm)
1964 free(ref_realm);
1965 free_METHOD_DATA(&enc_pa_data);
1967 free_EncTicketPart(&adtkt);
1969 return ret;
1976 krb5_error_code
1977 _kdc_tgs_rep(krb5_context context,
1978 krb5_kdc_configuration *config,
1979 KDC_REQ *req,
1980 krb5_data *data,
1981 const char *from,
1982 struct sockaddr *from_addr,
1983 int datagram_reply)
1985 AuthorizationData *auth_data = NULL;
1986 krb5_error_code ret;
1987 int i = 0;
1988 const PA_DATA *tgs_req;
1990 hdb_entry_ex *krbtgt = NULL;
1991 krb5_ticket *ticket = NULL;
1992 const char *e_text = NULL;
1993 krb5_enctype krbtgt_etype = ETYPE_NULL;
1995 time_t *csec = NULL;
1996 int *cusec = NULL;
1998 if(req->padata == NULL){
1999 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2000 kdc_log(context, config, 0,
2001 "TGS-REQ from %s without PA-DATA", from);
2002 goto out;
2005 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2007 if(tgs_req == NULL){
2008 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2010 kdc_log(context, config, 0,
2011 "TGS-REQ from %s without PA-TGS-REQ", from);
2012 goto out;
2014 ret = tgs_parse_request(context, config,
2015 &req->req_body, tgs_req,
2016 &krbtgt,
2017 &krbtgt_etype,
2018 &ticket,
2019 &e_text,
2020 from, from_addr,
2021 &csec, &cusec,
2022 &auth_data);
2023 if (ret) {
2024 kdc_log(context, config, 0,
2025 "Failed parsing TGS-REQ from %s", from);
2026 goto out;
2029 ret = tgs_build_reply(context,
2030 config,
2031 req,
2032 &req->req_body,
2033 krbtgt,
2034 krbtgt_etype,
2035 ticket,
2036 data,
2037 from,
2038 &e_text,
2039 &auth_data,
2040 from_addr);
2041 if (ret) {
2042 kdc_log(context, config, 0,
2043 "Failed building TGS-REP to %s", from);
2044 goto out;
2047 /* */
2048 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2049 krb5_data_free(data);
2050 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2051 e_text = "Reply packet too large";
2054 out:
2055 if(ret && data->data == NULL){
2056 krb5_mk_error(context,
2057 ret,
2058 NULL,
2059 NULL,
2060 NULL,
2061 NULL,
2062 csec,
2063 cusec,
2064 data);
2066 free(csec);
2067 free(cusec);
2068 if (ticket)
2069 krb5_free_ticket(context, ticket);
2070 if(krbtgt)
2071 _kdc_free_ent(context, krbtgt);
2073 if (auth_data) {
2074 free_AuthorizationData(auth_data);
2075 free(auth_data);
2078 return 0;