drop 45 library
[heimdal.git] / kdc / krb5tgs.c
blobc3b0aaa89e23cc88998938b65aaca1f0a9a793af
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_principal client,
110 krb5_const_principal server,
111 krb5_principals principals,
112 EncTicketPart *tkt)
114 krb5_error_code ret;
115 KRB5SignedPath sp;
116 krb5_data data;
117 krb5_crypto crypto = NULL;
118 size_t size;
120 if (server && principals) {
121 ret = add_Principals(principals, server);
122 if (ret)
123 return ret;
127 KRB5SignedPathData spd;
129 spd.client = client;
130 spd.authtime = tkt->authtime;
131 spd.delegated = principals;
132 spd.method_data = NULL;
134 ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
135 &spd, &size, ret);
136 if (ret)
137 return ret;
138 if (data.length != size)
139 krb5_abortx(context, "internal asn.1 encoder error");
143 Key *key;
144 ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
145 if (ret == 0)
146 ret = krb5_crypto_init(context, &key->key, 0, &crypto);
147 if (ret) {
148 free(data.data);
149 return ret;
154 * Fill in KRB5SignedPath
157 sp.etype = enctype;
158 sp.delegated = principals;
159 sp.method_data = NULL;
161 ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
162 data.data, data.length, &sp.cksum);
163 krb5_crypto_destroy(context, crypto);
164 free(data.data);
165 if (ret)
166 return ret;
168 ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
169 free_Checksum(&sp.cksum);
170 if (ret)
171 return ret;
172 if (data.length != size)
173 krb5_abortx(context, "internal asn.1 encoder error");
177 * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
178 * authorization data field.
181 ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
182 KRB5_AUTHDATA_SIGNTICKET, &data);
183 krb5_data_free(&data);
185 return ret;
188 static krb5_error_code
189 check_KRB5SignedPath(krb5_context context,
190 krb5_kdc_configuration *config,
191 hdb_entry_ex *krbtgt,
192 krb5_principal cp,
193 EncTicketPart *tkt,
194 krb5_principals *delegated,
195 int *signedpath)
197 krb5_error_code ret;
198 krb5_data data;
199 krb5_crypto crypto = NULL;
201 if (delegated)
202 *delegated = NULL;
204 ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
205 if (ret == 0) {
206 KRB5SignedPathData spd;
207 KRB5SignedPath sp;
208 size_t size;
210 ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
211 krb5_data_free(&data);
212 if (ret)
213 return ret;
215 spd.client = cp;
216 spd.authtime = tkt->authtime;
217 spd.delegated = sp.delegated;
218 spd.method_data = sp.method_data;
220 ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
221 &spd, &size, ret);
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 kdc_log(context, config, 5,
248 "KRB5SignedPath not signed correctly, not marking as signed");
249 return 0;
252 if (delegated && sp.delegated) {
254 *delegated = malloc(sizeof(*sp.delegated));
255 if (*delegated == NULL) {
256 free_KRB5SignedPath(&sp);
257 return ENOMEM;
260 ret = copy_Principals(*delegated, sp.delegated);
261 if (ret) {
262 free_KRB5SignedPath(&sp);
263 free(*delegated);
264 *delegated = NULL;
265 return ret;
268 free_KRB5SignedPath(&sp);
270 *signedpath = 1;
273 return 0;
280 static krb5_error_code
281 check_PAC(krb5_context context,
282 krb5_kdc_configuration *config,
283 const krb5_principal client_principal,
284 hdb_entry_ex *client,
285 hdb_entry_ex *server,
286 const EncryptionKey *server_key,
287 const EncryptionKey *krbtgt_key,
288 EncTicketPart *tkt,
289 krb5_data *rspac,
290 int *signedpath)
292 AuthorizationData *ad = tkt->authorization_data;
293 unsigned i, j;
294 krb5_error_code ret;
296 if (ad == NULL || ad->len == 0)
297 return 0;
299 for (i = 0; i < ad->len; i++) {
300 AuthorizationData child;
302 if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
303 continue;
305 ret = decode_AuthorizationData(ad->val[i].ad_data.data,
306 ad->val[i].ad_data.length,
307 &child,
308 NULL);
309 if (ret) {
310 krb5_set_error_message(context, ret, "Failed to decode "
311 "IF_RELEVANT with %d", ret);
312 return ret;
314 for (j = 0; j < child.len; j++) {
316 if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
317 krb5_pac pac;
319 /* Found PAC */
320 ret = krb5_pac_parse(context,
321 child.val[j].ad_data.data,
322 child.val[j].ad_data.length,
323 &pac);
324 free_AuthorizationData(&child);
325 if (ret)
326 return ret;
328 ret = krb5_pac_verify(context, pac, tkt->authtime,
329 client_principal,
330 krbtgt_key, NULL);
331 if (ret) {
332 krb5_pac_free(context, pac);
333 return ret;
336 ret = _kdc_pac_verify(context, client_principal,
337 client, server, &pac);
338 if (ret) {
339 krb5_pac_free(context, pac);
340 return ret;
342 *signedpath = 1;
344 ret = _krb5_pac_sign(context, pac, tkt->authtime,
345 client_principal,
346 server_key, krbtgt_key, rspac);
348 krb5_pac_free(context, pac);
350 return ret;
353 free_AuthorizationData(&child);
355 return 0;
362 static krb5_error_code
363 check_tgs_flags(krb5_context context,
364 krb5_kdc_configuration *config,
365 KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
367 KDCOptions f = b->kdc_options;
369 if(f.validate){
370 if(!tgt->flags.invalid || tgt->starttime == NULL){
371 kdc_log(context, config, 0,
372 "Bad request to validate ticket");
373 return KRB5KDC_ERR_BADOPTION;
375 if(*tgt->starttime > kdc_time){
376 kdc_log(context, config, 0,
377 "Early request to validate ticket");
378 return KRB5KRB_AP_ERR_TKT_NYV;
380 /* XXX tkt = tgt */
381 et->flags.invalid = 0;
382 }else if(tgt->flags.invalid){
383 kdc_log(context, config, 0,
384 "Ticket-granting ticket has INVALID flag set");
385 return KRB5KRB_AP_ERR_TKT_INVALID;
388 if(f.forwardable){
389 if(!tgt->flags.forwardable){
390 kdc_log(context, config, 0,
391 "Bad request for forwardable ticket");
392 return KRB5KDC_ERR_BADOPTION;
394 et->flags.forwardable = 1;
396 if(f.forwarded){
397 if(!tgt->flags.forwardable){
398 kdc_log(context, config, 0,
399 "Request to forward non-forwardable ticket");
400 return KRB5KDC_ERR_BADOPTION;
402 et->flags.forwarded = 1;
403 et->caddr = b->addresses;
405 if(tgt->flags.forwarded)
406 et->flags.forwarded = 1;
408 if(f.proxiable){
409 if(!tgt->flags.proxiable){
410 kdc_log(context, config, 0,
411 "Bad request for proxiable ticket");
412 return KRB5KDC_ERR_BADOPTION;
414 et->flags.proxiable = 1;
416 if(f.proxy){
417 if(!tgt->flags.proxiable){
418 kdc_log(context, config, 0,
419 "Request to proxy non-proxiable ticket");
420 return KRB5KDC_ERR_BADOPTION;
422 et->flags.proxy = 1;
423 et->caddr = b->addresses;
425 if(tgt->flags.proxy)
426 et->flags.proxy = 1;
428 if(f.allow_postdate){
429 if(!tgt->flags.may_postdate){
430 kdc_log(context, config, 0,
431 "Bad request for post-datable ticket");
432 return KRB5KDC_ERR_BADOPTION;
434 et->flags.may_postdate = 1;
436 if(f.postdated){
437 if(!tgt->flags.may_postdate){
438 kdc_log(context, config, 0,
439 "Bad request for postdated ticket");
440 return KRB5KDC_ERR_BADOPTION;
442 if(b->from)
443 *et->starttime = *b->from;
444 et->flags.postdated = 1;
445 et->flags.invalid = 1;
446 }else if(b->from && *b->from > kdc_time + context->max_skew){
447 kdc_log(context, config, 0, "Ticket cannot be postdated");
448 return KRB5KDC_ERR_CANNOT_POSTDATE;
451 if(f.renewable){
452 if(!tgt->flags.renewable){
453 kdc_log(context, config, 0,
454 "Bad request for renewable ticket");
455 return KRB5KDC_ERR_BADOPTION;
457 et->flags.renewable = 1;
458 ALLOC(et->renew_till);
459 _kdc_fix_time(&b->rtime);
460 *et->renew_till = *b->rtime;
462 if(f.renew){
463 time_t old_life;
464 if(!tgt->flags.renewable || tgt->renew_till == NULL){
465 kdc_log(context, config, 0,
466 "Request to renew non-renewable ticket");
467 return KRB5KDC_ERR_BADOPTION;
469 old_life = tgt->endtime;
470 if(tgt->starttime)
471 old_life -= *tgt->starttime;
472 else
473 old_life -= tgt->authtime;
474 et->endtime = *et->starttime + old_life;
475 if (et->renew_till != NULL)
476 et->endtime = min(*et->renew_till, et->endtime);
479 #if 0
480 /* checks for excess flags */
481 if(f.request_anonymous && !config->allow_anonymous){
482 kdc_log(context, config, 0,
483 "Request for anonymous ticket");
484 return KRB5KDC_ERR_BADOPTION;
486 #endif
487 return 0;
494 static krb5_error_code
495 check_constrained_delegation(krb5_context context,
496 krb5_kdc_configuration *config,
497 HDB *clientdb,
498 hdb_entry_ex *client,
499 krb5_const_principal server)
501 const HDB_Ext_Constrained_delegation_acl *acl;
502 krb5_error_code ret;
503 int i;
505 /* if client delegates to itself, that ok */
506 if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
507 return 0;
509 if (clientdb->hdb_check_constrained_delegation) {
510 ret = clientdb->hdb_check_constrained_delegation(context, clientdb, client, server);
511 if (ret == 0)
512 return 0;
513 } else {
514 ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
515 if (ret) {
516 krb5_clear_error_message(context);
517 return ret;
520 if (acl) {
521 for (i = 0; i < acl->len; i++) {
522 if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
523 return 0;
526 ret = KRB5KDC_ERR_BADOPTION;
528 kdc_log(context, config, 0,
529 "Bad request for constrained delegation");
530 return ret;
537 static krb5_error_code
538 verify_flags (krb5_context context,
539 krb5_kdc_configuration *config,
540 const EncTicketPart *et,
541 const char *pstr)
543 if(et->endtime < kdc_time){
544 kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
545 return KRB5KRB_AP_ERR_TKT_EXPIRED;
547 if(et->flags.invalid){
548 kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
549 return KRB5KRB_AP_ERR_TKT_NYV;
551 return 0;
558 static krb5_error_code
559 fix_transited_encoding(krb5_context context,
560 krb5_kdc_configuration *config,
561 krb5_boolean check_policy,
562 const TransitedEncoding *tr,
563 EncTicketPart *et,
564 const char *client_realm,
565 const char *server_realm,
566 const char *tgt_realm)
568 krb5_error_code ret = 0;
569 char **realms, **tmp;
570 unsigned int num_realms;
571 int i;
573 switch (tr->tr_type) {
574 case DOMAIN_X500_COMPRESS:
575 break;
576 case 0:
578 * Allow empty content of type 0 because that is was Microsoft
579 * generates in their TGT.
581 if (tr->contents.length == 0)
582 break;
583 kdc_log(context, config, 0,
584 "Transited type 0 with non empty content");
585 return KRB5KDC_ERR_TRTYPE_NOSUPP;
586 default:
587 kdc_log(context, config, 0,
588 "Unknown transited type: %u", tr->tr_type);
589 return KRB5KDC_ERR_TRTYPE_NOSUPP;
592 ret = krb5_domain_x500_decode(context,
593 tr->contents,
594 &realms,
595 &num_realms,
596 client_realm,
597 server_realm);
598 if(ret){
599 krb5_warn(context, ret,
600 "Decoding transited encoding");
601 return ret;
603 if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
604 /* not us, so add the previous realm to transited set */
605 if (num_realms + 1 > UINT_MAX/sizeof(*realms)) {
606 ret = ERANGE;
607 goto free_realms;
609 tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
610 if(tmp == NULL){
611 ret = ENOMEM;
612 goto free_realms;
614 realms = tmp;
615 realms[num_realms] = strdup(tgt_realm);
616 if(realms[num_realms] == NULL){
617 ret = ENOMEM;
618 goto free_realms;
620 num_realms++;
622 if(num_realms == 0) {
623 if(strcmp(client_realm, server_realm))
624 kdc_log(context, config, 0,
625 "cross-realm %s -> %s", client_realm, server_realm);
626 } else {
627 size_t l = 0;
628 char *rs;
629 for(i = 0; i < num_realms; i++)
630 l += strlen(realms[i]) + 2;
631 rs = malloc(l);
632 if(rs != NULL) {
633 *rs = '\0';
634 for(i = 0; i < num_realms; i++) {
635 if(i > 0)
636 strlcat(rs, ", ", l);
637 strlcat(rs, realms[i], l);
639 kdc_log(context, config, 0,
640 "cross-realm %s -> %s via [%s]",
641 client_realm, server_realm, rs);
642 free(rs);
645 if(check_policy) {
646 ret = krb5_check_transited(context, client_realm,
647 server_realm,
648 realms, num_realms, NULL);
649 if(ret) {
650 krb5_warn(context, ret, "cross-realm %s -> %s",
651 client_realm, server_realm);
652 goto free_realms;
654 et->flags.transited_policy_checked = 1;
656 et->transited.tr_type = DOMAIN_X500_COMPRESS;
657 ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
658 if(ret)
659 krb5_warn(context, ret, "Encoding transited encoding");
660 free_realms:
661 for(i = 0; i < num_realms; i++)
662 free(realms[i]);
663 free(realms);
664 return ret;
668 static krb5_error_code
669 tgs_make_reply(krb5_context context,
670 krb5_kdc_configuration *config,
671 KDC_REQ_BODY *b,
672 krb5_const_principal tgt_name,
673 const EncTicketPart *tgt,
674 const EncryptionKey *serverkey,
675 const krb5_keyblock *sessionkey,
676 krb5_kvno kvno,
677 AuthorizationData *auth_data,
678 hdb_entry_ex *server,
679 krb5_principal server_principal,
680 const char *server_name,
681 hdb_entry_ex *client,
682 krb5_principal client_principal,
683 hdb_entry_ex *krbtgt,
684 krb5_enctype krbtgt_etype,
685 krb5_principals spp,
686 const krb5_data *rspac,
687 const METHOD_DATA *enc_pa_data,
688 const char **e_text,
689 krb5_data *reply)
691 KDC_REP rep;
692 EncKDCRepPart ek;
693 EncTicketPart et;
694 KDCOptions f = b->kdc_options;
695 krb5_error_code ret;
696 int is_weak = 0;
698 memset(&rep, 0, sizeof(rep));
699 memset(&et, 0, sizeof(et));
700 memset(&ek, 0, sizeof(ek));
702 rep.pvno = 5;
703 rep.msg_type = krb_tgs_rep;
705 et.authtime = tgt->authtime;
706 _kdc_fix_time(&b->till);
707 et.endtime = min(tgt->endtime, *b->till);
708 ALLOC(et.starttime);
709 *et.starttime = kdc_time;
711 ret = check_tgs_flags(context, config, b, tgt, &et);
712 if(ret)
713 goto out;
715 /* We should check the transited encoding if:
716 1) the request doesn't ask not to be checked
717 2) globally enforcing a check
718 3) principal requires checking
719 4) we allow non-check per-principal, but principal isn't marked as allowing this
720 5) we don't globally allow this
723 #define GLOBAL_FORCE_TRANSITED_CHECK \
724 (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
725 #define GLOBAL_ALLOW_PER_PRINCIPAL \
726 (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
727 #define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
728 (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
730 /* these will consult the database in future release */
731 #define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
732 #define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
734 ret = fix_transited_encoding(context, config,
735 !f.disable_transited_check ||
736 GLOBAL_FORCE_TRANSITED_CHECK ||
737 PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
738 !((GLOBAL_ALLOW_PER_PRINCIPAL &&
739 PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
740 GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
741 &tgt->transited, &et,
742 krb5_principal_get_realm(context, client_principal),
743 krb5_principal_get_realm(context, server->entry.principal),
744 krb5_principal_get_realm(context, krbtgt->entry.principal));
745 if(ret)
746 goto out;
748 copy_Realm(&server_principal->realm, &rep.ticket.realm);
749 _krb5_principal2principalname(&rep.ticket.sname, server_principal);
750 copy_Realm(&tgt_name->realm, &rep.crealm);
752 if (f.request_anonymous)
753 _kdc_make_anonymous_principalname (&rep.cname);
754 else */
756 copy_PrincipalName(&tgt_name->name, &rep.cname);
757 rep.ticket.tkt_vno = 5;
759 ek.caddr = et.caddr;
760 if(et.caddr == NULL)
761 et.caddr = tgt->caddr;
764 time_t life;
765 life = et.endtime - *et.starttime;
766 if(client && client->entry.max_life)
767 life = min(life, *client->entry.max_life);
768 if(server->entry.max_life)
769 life = min(life, *server->entry.max_life);
770 et.endtime = *et.starttime + life;
772 if(f.renewable_ok && tgt->flags.renewable &&
773 et.renew_till == NULL && et.endtime < *b->till){
774 et.flags.renewable = 1;
775 ALLOC(et.renew_till);
776 *et.renew_till = *b->till;
778 if(et.renew_till){
779 time_t renew;
780 renew = *et.renew_till - et.authtime;
781 if(client && client->entry.max_renew)
782 renew = min(renew, *client->entry.max_renew);
783 if(server->entry.max_renew)
784 renew = min(renew, *server->entry.max_renew);
785 *et.renew_till = et.authtime + renew;
788 if(et.renew_till){
789 *et.renew_till = min(*et.renew_till, *tgt->renew_till);
790 *et.starttime = min(*et.starttime, *et.renew_till);
791 et.endtime = min(et.endtime, *et.renew_till);
794 *et.starttime = min(*et.starttime, et.endtime);
796 if(*et.starttime == et.endtime){
797 ret = KRB5KDC_ERR_NEVER_VALID;
798 goto out;
800 if(et.renew_till && et.endtime == *et.renew_till){
801 free(et.renew_till);
802 et.renew_till = NULL;
803 et.flags.renewable = 0;
806 et.flags.pre_authent = tgt->flags.pre_authent;
807 et.flags.hw_authent = tgt->flags.hw_authent;
808 et.flags.anonymous = tgt->flags.anonymous;
809 et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
811 if(rspac->length) {
813 * No not need to filter out the any PAC from the
814 * auth_data since it's signed by the KDC.
816 ret = _kdc_tkt_add_if_relevant_ad(context, &et,
817 KRB5_AUTHDATA_WIN2K_PAC, rspac);
818 if (ret)
819 goto out;
822 if (auth_data) {
823 unsigned int i = 0;
825 /* XXX check authdata */
826 if (et.authorization_data == NULL) {
827 ret = ENOMEM;
828 krb5_set_error_message(context, ret, "malloc: out of memory");
829 goto out;
831 for(i = 0; i < auth_data->len ; i++) {
832 ret = add_AuthorizationData(et.authorization_data, &auth_data->val[i]);
833 if (ret) {
834 krb5_set_error_message(context, ret, "malloc: out of memory");
835 goto out;
839 /* Filter out type KRB5SignedPath */
840 ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
841 if (ret == 0) {
842 if (et.authorization_data->len == 1) {
843 free_AuthorizationData(et.authorization_data);
844 free(et.authorization_data);
845 et.authorization_data = NULL;
846 } else {
847 AuthorizationData *ad = et.authorization_data;
848 free_AuthorizationDataElement(&ad->val[ad->len - 1]);
849 ad->len--;
854 ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
855 if (ret)
856 goto out;
857 et.crealm = tgt->crealm;
858 et.cname = tgt_name->name;
860 ek.key = et.key;
861 /* MIT must have at least one last_req */
862 ek.last_req.len = 1;
863 ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
864 if (ek.last_req.val == NULL) {
865 ret = ENOMEM;
866 goto out;
868 ek.nonce = b->nonce;
869 ek.flags = et.flags;
870 ek.authtime = et.authtime;
871 ek.starttime = et.starttime;
872 ek.endtime = et.endtime;
873 ek.renew_till = et.renew_till;
874 ek.srealm = rep.ticket.realm;
875 ek.sname = rep.ticket.sname;
877 _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
878 et.endtime, et.renew_till);
880 /* Don't sign cross realm tickets, they can't be checked anyway */
882 char *r = get_krbtgt_realm(&ek.sname);
884 if (r == NULL || strcmp(r, ek.srealm) == 0) {
885 ret = _kdc_add_KRB5SignedPath(context,
886 config,
887 krbtgt,
888 krbtgt_etype,
889 client_principal,
890 NULL,
891 spp,
892 &et);
893 if (ret)
894 goto out;
898 if (enc_pa_data->len) {
899 rep.padata = calloc(1, sizeof(*rep.padata));
900 if (rep.padata == NULL) {
901 ret = ENOMEM;
902 goto out;
904 ret = copy_METHOD_DATA(enc_pa_data, rep.padata);
905 if (ret)
906 goto out;
909 if (krb5_enctype_valid(context, et.key.keytype) != 0
910 && _kdc_is_weak_exception(server->entry.principal, et.key.keytype))
912 krb5_enctype_enable(context, et.key.keytype);
913 is_weak = 1;
917 /* It is somewhat unclear where the etype in the following
918 encryption should come from. What we have is a session
919 key in the passed tgt, and a list of preferred etypes
920 *for the new ticket*. Should we pick the best possible
921 etype, given the keytype in the tgt, or should we look
922 at the etype list here as well? What if the tgt
923 session key is DES3 and we want a ticket with a (say)
924 CAST session key. Should the DES3 etype be added to the
925 etype list, even if we don't want a session key with
926 DES3? */
927 ret = _kdc_encode_reply(context, config,
928 &rep, &et, &ek, et.key.keytype,
929 kvno,
930 serverkey, 0, &tgt->key, e_text, reply);
931 if (is_weak)
932 krb5_enctype_disable(context, et.key.keytype);
934 out:
935 free_TGS_REP(&rep);
936 free_TransitedEncoding(&et.transited);
937 if(et.starttime)
938 free(et.starttime);
939 if(et.renew_till)
940 free(et.renew_till);
941 if(et.authorization_data) {
942 free_AuthorizationData(et.authorization_data);
943 free(et.authorization_data);
945 free_LastReq(&ek.last_req);
946 memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
947 free_EncryptionKey(&et.key);
948 return ret;
951 static krb5_error_code
952 tgs_check_authenticator(krb5_context context,
953 krb5_kdc_configuration *config,
954 krb5_auth_context ac,
955 KDC_REQ_BODY *b,
956 const char **e_text,
957 krb5_keyblock *key)
959 krb5_authenticator auth;
960 size_t len;
961 unsigned char *buf;
962 size_t buf_size;
963 krb5_error_code ret;
964 krb5_crypto crypto;
966 krb5_auth_con_getauthenticator(context, ac, &auth);
967 if(auth->cksum == NULL){
968 kdc_log(context, config, 0, "No authenticator in request");
969 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
970 goto out;
973 * according to RFC1510 it doesn't need to be keyed,
974 * but according to the latest draft it needs to.
976 if (
977 #if 0
978 !krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
980 #endif
981 !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
982 kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
983 auth->cksum->cksumtype);
984 ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
985 goto out;
988 /* XXX should not re-encode this */
989 ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
990 if(ret){
991 kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
992 krb5_get_err_text(context, ret));
993 goto out;
995 if(buf_size != len) {
996 free(buf);
997 kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
998 *e_text = "KDC internal error";
999 ret = KRB5KRB_ERR_GENERIC;
1000 goto out;
1002 ret = krb5_crypto_init(context, key, 0, &crypto);
1003 if (ret) {
1004 free(buf);
1005 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1006 krb5_get_err_text(context, ret));
1007 goto out;
1009 ret = krb5_verify_checksum(context,
1010 crypto,
1011 KRB5_KU_TGS_REQ_AUTH_CKSUM,
1012 buf,
1013 len,
1014 auth->cksum);
1015 free(buf);
1016 krb5_crypto_destroy(context, crypto);
1017 if(ret){
1018 kdc_log(context, config, 0,
1019 "Failed to verify authenticator checksum: %s",
1020 krb5_get_err_text(context, ret));
1022 out:
1023 free_Authenticator(auth);
1024 free(auth);
1025 return ret;
1032 static const char *
1033 find_rpath(krb5_context context, Realm crealm, Realm srealm)
1035 const char *new_realm = krb5_config_get_string(context,
1036 NULL,
1037 "capaths",
1038 crealm,
1039 srealm,
1040 NULL);
1041 return new_realm;
1045 static krb5_boolean
1046 need_referral(krb5_context context, krb5_kdc_configuration *config,
1047 const KDCOptions * const options, krb5_principal server,
1048 krb5_realm **realms)
1050 const char *name;
1052 if(!options->canonicalize && server->name.name_type != KRB5_NT_SRV_INST)
1053 return FALSE;
1055 if (server->name.name_string.len == 1)
1056 name = server->name.name_string.val[0];
1057 else if (server->name.name_string.len > 1)
1058 name = server->name.name_string.val[1];
1059 else
1060 return FALSE;
1062 kdc_log(context, config, 0, "Searching referral for %s", name);
1064 return _krb5_get_host_realm_int(context, name, FALSE, realms) == 0;
1067 static krb5_error_code
1068 tgs_parse_request(krb5_context context,
1069 krb5_kdc_configuration *config,
1070 KDC_REQ_BODY *b,
1071 const PA_DATA *tgs_req,
1072 hdb_entry_ex **krbtgt,
1073 krb5_enctype *krbtgt_etype,
1074 krb5_ticket **ticket,
1075 const char **e_text,
1076 const char *from,
1077 const struct sockaddr *from_addr,
1078 time_t **csec,
1079 int **cusec,
1080 AuthorizationData **auth_data)
1082 krb5_ap_req ap_req;
1083 krb5_error_code ret;
1084 krb5_principal princ;
1085 krb5_auth_context ac = NULL;
1086 krb5_flags ap_req_options;
1087 krb5_flags verify_ap_req_flags;
1088 krb5_crypto crypto;
1089 Key *tkey;
1091 *auth_data = NULL;
1092 *csec = NULL;
1093 *cusec = NULL;
1095 memset(&ap_req, 0, sizeof(ap_req));
1096 ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
1097 if(ret){
1098 kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
1099 krb5_get_err_text(context, ret));
1100 goto out;
1103 if(!get_krbtgt_realm(&ap_req.ticket.sname)){
1104 /* XXX check for ticket.sname == req.sname */
1105 kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
1106 ret = KRB5KDC_ERR_POLICY; /* ? */
1107 goto out;
1110 _krb5_principalname2krb5_principal(context,
1111 &princ,
1112 ap_req.ticket.sname,
1113 ap_req.ticket.realm);
1115 ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
1117 if(ret) {
1118 char *p;
1119 ret = krb5_unparse_name(context, princ, &p);
1120 if (ret != 0)
1121 p = "<unparse_name failed>";
1122 krb5_free_principal(context, princ);
1123 kdc_log(context, config, 0,
1124 "Ticket-granting ticket not found in database: %s: %s",
1125 p, krb5_get_err_text(context, ret));
1126 if (ret == 0)
1127 free(p);
1128 ret = KRB5KRB_AP_ERR_NOT_US;
1129 goto out;
1132 if(ap_req.ticket.enc_part.kvno &&
1133 *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
1134 char *p;
1136 ret = krb5_unparse_name (context, princ, &p);
1137 krb5_free_principal(context, princ);
1138 if (ret != 0)
1139 p = "<unparse_name failed>";
1140 kdc_log(context, config, 0,
1141 "Ticket kvno = %d, DB kvno = %d (%s)",
1142 *ap_req.ticket.enc_part.kvno,
1143 (*krbtgt)->entry.kvno,
1145 if (ret == 0)
1146 free (p);
1147 ret = KRB5KRB_AP_ERR_BADKEYVER;
1148 goto out;
1151 *krbtgt_etype = ap_req.ticket.enc_part.etype;
1153 ret = hdb_enctype2key(context, &(*krbtgt)->entry,
1154 ap_req.ticket.enc_part.etype, &tkey);
1155 if(ret){
1156 char *str = NULL, *p = NULL;
1158 krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
1159 krb5_unparse_name(context, princ, &p);
1160 kdc_log(context, config, 0,
1161 "No server key with enctype %s found for %s",
1162 str ? str : "<unknown enctype>",
1163 p ? p : "<unparse_name failed>");
1164 free(str);
1165 free(p);
1166 ret = KRB5KRB_AP_ERR_BADKEYVER;
1167 goto out;
1170 if (b->kdc_options.validate)
1171 verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
1172 else
1173 verify_ap_req_flags = 0;
1175 ret = krb5_verify_ap_req2(context,
1176 &ac,
1177 &ap_req,
1178 princ,
1179 &tkey->key,
1180 verify_ap_req_flags,
1181 &ap_req_options,
1182 ticket,
1183 KRB5_KU_TGS_REQ_AUTH);
1185 krb5_free_principal(context, princ);
1186 if(ret) {
1187 kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
1188 krb5_get_err_text(context, ret));
1189 goto out;
1193 krb5_authenticator auth;
1195 ret = krb5_auth_con_getauthenticator(context, ac, &auth);
1196 if (ret == 0) {
1197 *csec = malloc(sizeof(**csec));
1198 if (*csec == NULL) {
1199 krb5_free_authenticator(context, &auth);
1200 kdc_log(context, config, 0, "malloc failed");
1201 goto out;
1203 **csec = auth->ctime;
1204 *cusec = malloc(sizeof(**cusec));
1205 if (*cusec == NULL) {
1206 krb5_free_authenticator(context, &auth);
1207 kdc_log(context, config, 0, "malloc failed");
1208 goto out;
1210 **cusec = auth->cusec;
1211 krb5_free_authenticator(context, &auth);
1215 ret = tgs_check_authenticator(context, config,
1216 ac, b, e_text, &(*ticket)->ticket.key);
1217 if (ret) {
1218 krb5_auth_con_free(context, ac);
1219 goto out;
1222 if (b->enc_authorization_data) {
1223 unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
1224 krb5_keyblock *subkey;
1225 krb5_data ad;
1227 ret = krb5_auth_con_getremotesubkey(context, ac, &subkey);
1228 if(ret){
1229 krb5_auth_con_free(context, ac);
1230 kdc_log(context, config, 0, "Failed to get remote subkey: %s",
1231 krb5_get_err_text(context, ret));
1232 goto out;
1234 if(subkey == NULL){
1235 usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
1236 ret = krb5_auth_con_getkey(context, ac, &subkey);
1237 if(ret) {
1238 krb5_auth_con_free(context, ac);
1239 kdc_log(context, config, 0, "Failed to get session key: %s",
1240 krb5_get_err_text(context, ret));
1241 goto out;
1244 if(subkey == NULL){
1245 krb5_auth_con_free(context, ac);
1246 kdc_log(context, config, 0,
1247 "Failed to get key for enc-authorization-data");
1248 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1249 goto out;
1251 ret = krb5_crypto_init(context, subkey, 0, &crypto);
1252 krb5_free_keyblock(context, subkey);
1253 if (ret) {
1254 krb5_auth_con_free(context, ac);
1255 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1256 krb5_get_err_text(context, ret));
1257 goto out;
1259 ret = krb5_decrypt_EncryptedData (context,
1260 crypto,
1261 usage,
1262 b->enc_authorization_data,
1263 &ad);
1264 krb5_crypto_destroy(context, crypto);
1265 if(ret){
1266 krb5_auth_con_free(context, ac);
1267 kdc_log(context, config, 0,
1268 "Failed to decrypt enc-authorization-data");
1269 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1270 goto out;
1272 ALLOC(*auth_data);
1273 if (*auth_data == NULL) {
1274 krb5_auth_con_free(context, ac);
1275 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1276 goto out;
1278 ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
1279 if(ret){
1280 krb5_auth_con_free(context, ac);
1281 free(*auth_data);
1282 *auth_data = NULL;
1283 kdc_log(context, config, 0, "Failed to decode authorization data");
1284 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
1285 goto out;
1289 krb5_auth_con_free(context, ac);
1291 out:
1292 free_AP_REQ(&ap_req);
1294 return ret;
1297 static krb5_error_code
1298 build_server_referral(krb5_context context,
1299 krb5_kdc_configuration *config,
1300 krb5_crypto session,
1301 krb5_const_realm referred_realm,
1302 const PrincipalName *true_principal_name,
1303 const PrincipalName *requested_principal,
1304 krb5_data *outdata)
1306 PA_ServerReferralData ref;
1307 krb5_error_code ret;
1308 EncryptedData ed;
1309 krb5_data data;
1310 size_t size;
1312 memset(&ref, 0, sizeof(ref));
1314 if (referred_realm) {
1315 ALLOC(ref.referred_realm);
1316 if (ref.referred_realm == NULL)
1317 goto eout;
1318 *ref.referred_realm = strdup(referred_realm);
1319 if (*ref.referred_realm == NULL)
1320 goto eout;
1322 if (true_principal_name) {
1323 ALLOC(ref.true_principal_name);
1324 if (ref.true_principal_name == NULL)
1325 goto eout;
1326 ret = copy_PrincipalName(true_principal_name, ref.true_principal_name);
1327 if (ret)
1328 goto eout;
1330 if (requested_principal) {
1331 ALLOC(ref.requested_principal_name);
1332 if (ref.requested_principal_name == NULL)
1333 goto eout;
1334 ret = copy_PrincipalName(requested_principal,
1335 ref.requested_principal_name);
1336 if (ret)
1337 goto eout;
1340 ASN1_MALLOC_ENCODE(PA_ServerReferralData,
1341 data.data, data.length,
1342 &ref, &size, ret);
1343 free_PA_ServerReferralData(&ref);
1344 if (ret)
1345 return ret;
1346 if (data.length != size)
1347 krb5_abortx(context, "internal asn.1 encoder error");
1349 ret = krb5_encrypt_EncryptedData(context, session,
1350 KRB5_KU_PA_SERVER_REFERRAL,
1351 data.data, data.length,
1352 0 /* kvno */, &ed);
1353 free(data.data);
1354 if (ret)
1355 return ret;
1357 ASN1_MALLOC_ENCODE(EncryptedData,
1358 outdata->data, outdata->length,
1359 &ed, &size, ret);
1360 free_EncryptedData(&ed);
1361 if (ret)
1362 return ret;
1363 if (outdata->length != size)
1364 krb5_abortx(context, "internal asn.1 encoder error");
1366 return 0;
1367 eout:
1368 free_PA_ServerReferralData(&ref);
1369 krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
1370 return ENOMEM;
1373 static krb5_error_code
1374 tgs_build_reply(krb5_context context,
1375 krb5_kdc_configuration *config,
1376 KDC_REQ *req,
1377 KDC_REQ_BODY *b,
1378 hdb_entry_ex *krbtgt,
1379 krb5_enctype krbtgt_etype,
1380 krb5_ticket *ticket,
1381 krb5_data *reply,
1382 const char *from,
1383 const char **e_text,
1384 AuthorizationData **auth_data,
1385 const struct sockaddr *from_addr)
1387 krb5_error_code ret;
1388 krb5_principal cp = NULL, sp = NULL;
1389 krb5_principal client_principal = NULL;
1390 char *spn = NULL, *cpn = NULL;
1391 hdb_entry_ex *server = NULL, *client = NULL;
1392 HDB *clientdb;
1393 krb5_realm ref_realm = NULL;
1394 EncTicketPart *tgt = &ticket->ticket;
1395 krb5_principals spp = NULL;
1396 const EncryptionKey *ekey;
1397 krb5_keyblock sessionkey;
1398 krb5_kvno kvno;
1399 krb5_data rspac;
1401 METHOD_DATA enc_pa_data;
1403 PrincipalName *s;
1404 Realm r;
1405 int nloop = 0;
1406 EncTicketPart adtkt;
1407 char opt_str[128];
1408 int signedpath = 0;
1410 Key *tkey;
1412 memset(&sessionkey, 0, sizeof(sessionkey));
1413 memset(&adtkt, 0, sizeof(adtkt));
1414 krb5_data_zero(&rspac);
1415 memset(&enc_pa_data, 0, sizeof(enc_pa_data));
1417 s = b->sname;
1418 r = b->realm;
1420 if(b->kdc_options.enc_tkt_in_skey){
1421 Ticket *t;
1422 hdb_entry_ex *uu;
1423 krb5_principal p;
1424 Key *uukey;
1426 if(b->additional_tickets == NULL ||
1427 b->additional_tickets->len == 0){
1428 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1429 kdc_log(context, config, 0,
1430 "No second ticket present in request");
1431 goto out;
1433 t = &b->additional_tickets->val[0];
1434 if(!get_krbtgt_realm(&t->sname)){
1435 kdc_log(context, config, 0,
1436 "Additional ticket is not a ticket-granting ticket");
1437 ret = KRB5KDC_ERR_POLICY;
1438 goto out;
1440 _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
1441 ret = _kdc_db_fetch(context, config, p,
1442 HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
1443 NULL, &uu);
1444 krb5_free_principal(context, p);
1445 if(ret){
1446 if (ret == HDB_ERR_NOENTRY)
1447 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1448 goto out;
1450 ret = hdb_enctype2key(context, &uu->entry,
1451 t->enc_part.etype, &uukey);
1452 if(ret){
1453 _kdc_free_ent(context, uu);
1454 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1455 goto out;
1457 ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
1458 _kdc_free_ent(context, uu);
1459 if(ret)
1460 goto out;
1462 ret = verify_flags(context, config, &adtkt, spn);
1463 if (ret)
1464 goto out;
1466 s = &adtkt.cname;
1467 r = adtkt.crealm;
1470 _krb5_principalname2krb5_principal(context, &sp, *s, r);
1471 ret = krb5_unparse_name(context, sp, &spn);
1472 if (ret)
1473 goto out;
1474 _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
1475 ret = krb5_unparse_name(context, cp, &cpn);
1476 if (ret)
1477 goto out;
1478 unparse_flags (KDCOptions2int(b->kdc_options),
1479 asn1_KDCOptions_units(),
1480 opt_str, sizeof(opt_str));
1481 if(*opt_str)
1482 kdc_log(context, config, 0,
1483 "TGS-REQ %s from %s for %s [%s]",
1484 cpn, from, spn, opt_str);
1485 else
1486 kdc_log(context, config, 0,
1487 "TGS-REQ %s from %s for %s", cpn, from, spn);
1490 * Fetch server
1493 server_lookup:
1494 ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | HDB_F_CANON,
1495 NULL, &server);
1497 if(ret){
1498 const char *new_rlm;
1499 Realm req_rlm;
1500 krb5_realm *realms;
1502 if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
1503 if(nloop++ < 2) {
1504 new_rlm = find_rpath(context, tgt->crealm, req_rlm);
1505 if(new_rlm) {
1506 kdc_log(context, config, 5, "krbtgt for realm %s "
1507 "not found, trying %s",
1508 req_rlm, new_rlm);
1509 krb5_free_principal(context, sp);
1510 free(spn);
1511 krb5_make_principal(context, &sp, r,
1512 KRB5_TGS_NAME, new_rlm, NULL);
1513 ret = krb5_unparse_name(context, sp, &spn);
1514 if (ret)
1515 goto out;
1517 if (ref_realm)
1518 free(ref_realm);
1519 ref_realm = strdup(new_rlm);
1520 goto server_lookup;
1523 } else if(need_referral(context, config, &b->kdc_options, sp, &realms)) {
1524 if (strcmp(realms[0], sp->realm) != 0) {
1525 kdc_log(context, config, 5,
1526 "Returning a referral to realm %s for "
1527 "server %s that was not found",
1528 realms[0], spn);
1529 krb5_free_principal(context, sp);
1530 free(spn);
1531 krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
1532 realms[0], NULL);
1533 ret = krb5_unparse_name(context, sp, &spn);
1534 if (ret)
1535 goto out;
1537 if (ref_realm)
1538 free(ref_realm);
1539 ref_realm = strdup(realms[0]);
1541 krb5_free_host_realm(context, realms);
1542 goto server_lookup;
1544 krb5_free_host_realm(context, realms);
1546 kdc_log(context, config, 0,
1547 "Server not found in database: %s: %s", spn,
1548 krb5_get_err_text(context, ret));
1549 if (ret == HDB_ERR_NOENTRY)
1550 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1551 goto out;
1554 ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | HDB_F_CANON,
1555 &clientdb, &client);
1556 if(ret) {
1557 const char *krbtgt_realm;
1560 * If the client belongs to the same realm as our krbtgt, it
1561 * should exist in the local database.
1565 krbtgt_realm =
1566 krb5_principal_get_comp_string(context,
1567 krbtgt->entry.principal, 1);
1569 if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
1570 if (ret == HDB_ERR_NOENTRY)
1571 ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
1572 kdc_log(context, config, 1, "Client no longer in database: %s",
1573 cpn);
1574 goto out;
1577 kdc_log(context, config, 1, "Client not found in database: %s: %s",
1578 cpn, krb5_get_err_text(context, ret));
1582 * Select enctype, return key and kvno.
1586 krb5_enctype etype;
1588 if(b->kdc_options.enc_tkt_in_skey) {
1589 int i;
1590 ekey = &adtkt.key;
1591 for(i = 0; i < b->etype.len; i++)
1592 if (b->etype.val[i] == adtkt.key.keytype)
1593 break;
1594 if(i == b->etype.len) {
1595 kdc_log(context, config, 0,
1596 "Addition ticket have not matching etypes");
1597 krb5_clear_error_message(context);
1598 ret = KRB5KDC_ERR_ETYPE_NOSUPP;
1599 goto out;
1601 etype = b->etype.val[i];
1602 kvno = 0;
1603 } else {
1604 Key *skey;
1606 ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
1607 &skey, &etype);
1608 if(ret) {
1609 kdc_log(context, config, 0,
1610 "Server (%s) has no support for etypes", spn);
1611 goto out;
1613 ekey = &skey->key;
1614 kvno = server->entry.kvno;
1617 ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
1618 if (ret)
1619 goto out;
1623 * Check that service is in the same realm as the krbtgt. If it's
1624 * not the same, it's someone that is using a uni-directional trust
1625 * backward.
1628 if (strcmp(krb5_principal_get_realm(context, sp),
1629 krb5_principal_get_comp_string(context,
1630 krbtgt->entry.principal,
1631 1)) != 0) {
1632 char *tpn;
1633 ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
1634 kdc_log(context, config, 0,
1635 "Request with wrong krbtgt: %s",
1636 (ret == 0) ? tpn : "<unknown>");
1637 if(ret == 0)
1638 free(tpn);
1639 ret = KRB5KRB_AP_ERR_NOT_US;
1640 goto out;
1644 * Validate authoriation data
1647 ret = hdb_enctype2key(context, &krbtgt->entry,
1648 krbtgt_etype, &tkey);
1649 if(ret) {
1650 kdc_log(context, config, 0,
1651 "Failed to find key for krbtgt PAC check");
1652 goto out;
1655 ret = check_PAC(context, config, cp,
1656 client, server, ekey, &tkey->key,
1657 tgt, &rspac, &signedpath);
1658 if (ret) {
1659 kdc_log(context, config, 0,
1660 "Verify PAC failed for %s (%s) from %s with %s",
1661 spn, cpn, from, krb5_get_err_text(context, ret));
1662 goto out;
1665 /* also check the krbtgt for signature */
1666 ret = check_KRB5SignedPath(context,
1667 config,
1668 krbtgt,
1670 tgt,
1671 &spp,
1672 &signedpath);
1673 if (ret) {
1674 kdc_log(context, config, 0,
1675 "KRB5SignedPath check failed for %s (%s) from %s with %s",
1676 spn, cpn, from, krb5_get_err_text(context, ret));
1677 goto out;
1681 * Process request
1684 client_principal = cp;
1686 if (client) {
1687 const PA_DATA *sdata;
1688 int i = 0;
1690 sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER);
1691 if (sdata) {
1692 krb5_crypto crypto;
1693 krb5_data datack;
1694 PA_S4U2Self self;
1695 char *selfcpn = NULL;
1696 const char *str;
1698 ret = decode_PA_S4U2Self(sdata->padata_value.data,
1699 sdata->padata_value.length,
1700 &self, NULL);
1701 if (ret) {
1702 kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
1703 goto out;
1706 ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
1707 if (ret)
1708 goto out;
1710 ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
1711 if (ret) {
1712 free_PA_S4U2Self(&self);
1713 krb5_data_free(&datack);
1714 kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
1715 krb5_get_err_text(context, ret));
1716 goto out;
1719 ret = krb5_verify_checksum(context,
1720 crypto,
1721 KRB5_KU_OTHER_CKSUM,
1722 datack.data,
1723 datack.length,
1724 &self.cksum);
1725 krb5_data_free(&datack);
1726 krb5_crypto_destroy(context, crypto);
1727 if (ret) {
1728 free_PA_S4U2Self(&self);
1729 kdc_log(context, config, 0,
1730 "krb5_verify_checksum failed for S4U2Self: %s",
1731 krb5_get_err_text(context, ret));
1732 goto out;
1735 ret = _krb5_principalname2krb5_principal(context,
1736 &client_principal,
1737 self.name,
1738 self.realm);
1739 free_PA_S4U2Self(&self);
1740 if (ret)
1741 goto out;
1743 ret = krb5_unparse_name(context, client_principal, &selfcpn);
1744 if (ret)
1745 goto out;
1748 * Check that service doing the impersonating is
1749 * requesting a ticket to it-self.
1751 if (krb5_principal_compare(context, cp, sp) != TRUE) {
1752 kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
1753 "to impersonate some other user "
1754 "(tried for user %s to service %s)",
1755 cpn, selfcpn, spn);
1756 free(selfcpn);
1757 ret = KRB5KDC_ERR_BADOPTION; /* ? */
1758 goto out;
1762 * If the service isn't trusted for authentication to
1763 * delegation, remove the forward flag.
1766 if (client->entry.flags.trusted_for_delegation) {
1767 str = "[forwardable]";
1768 } else {
1769 b->kdc_options.forwardable = 0;
1770 str = "";
1772 kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
1773 "service %s %s", cpn, selfcpn, spn, str);
1774 free(selfcpn);
1779 * Constrained delegation
1782 if (client != NULL
1783 && b->additional_tickets != NULL
1784 && b->additional_tickets->len != 0
1785 && b->kdc_options.enc_tkt_in_skey == 0)
1787 int ad_signedpath = 0;
1788 Key *clientkey;
1789 Ticket *t;
1790 char *str;
1793 * Require that the KDC have issued the service's krbtgt (not
1794 * self-issued ticket with kimpersonate(1).
1796 if (!signedpath) {
1797 ret = KRB5KDC_ERR_BADOPTION;
1798 kdc_log(context, config, 0,
1799 "Constrained delegation done on service ticket %s/%s",
1800 cpn, spn);
1801 goto out;
1804 t = &b->additional_tickets->val[0];
1806 ret = hdb_enctype2key(context, &client->entry,
1807 t->enc_part.etype, &clientkey);
1808 if(ret){
1809 ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
1810 goto out;
1813 ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
1814 if (ret) {
1815 kdc_log(context, config, 0,
1816 "failed to decrypt ticket for "
1817 "constrained delegation from %s to %s ", cpn, spn);
1818 goto out;
1821 /* check that ticket is valid */
1822 if (adtkt.flags.forwardable == 0) {
1823 kdc_log(context, config, 0,
1824 "Missing forwardable flag on ticket for "
1825 "constrained delegation from %s to %s ", cpn, spn);
1826 ret = KRB5KDC_ERR_BADOPTION;
1827 goto out;
1830 ret = check_constrained_delegation(context, config, clientdb,
1831 client, sp);
1832 if (ret) {
1833 kdc_log(context, config, 0,
1834 "constrained delegation from %s to %s not allowed",
1835 cpn, spn);
1836 goto out;
1839 ret = _krb5_principalname2krb5_principal(context,
1840 &client_principal,
1841 adtkt.cname,
1842 adtkt.crealm);
1843 if (ret)
1844 goto out;
1846 ret = krb5_unparse_name(context, client_principal, &str);
1847 if (ret)
1848 goto out;
1850 ret = verify_flags(context, config, &adtkt, str);
1851 if (ret) {
1852 free(str);
1853 goto out;
1857 * Check that the KDC issued the user's ticket.
1859 ret = check_KRB5SignedPath(context,
1860 config,
1861 krbtgt,
1863 &adtkt,
1864 NULL,
1865 &ad_signedpath);
1866 if (ret == 0 && !ad_signedpath)
1867 ret = KRB5KDC_ERR_BADOPTION;
1868 if (ret) {
1869 kdc_log(context, config, 0,
1870 "KRB5SignedPath check from service %s failed "
1871 "for delegation to %s for client %s "
1872 "from %s failed with %s",
1873 spn, str, cpn, from, krb5_get_err_text(context, ret));
1874 free(str);
1875 goto out;
1878 kdc_log(context, config, 0, "constrained delegation for %s "
1879 "from %s to %s", str, cpn, spn);
1880 free(str);
1884 * Check flags
1887 ret = kdc_check_flags(context, config,
1888 client, cpn,
1889 server, spn,
1890 FALSE);
1891 if(ret)
1892 goto out;
1894 if((b->kdc_options.validate || b->kdc_options.renew) &&
1895 !krb5_principal_compare(context,
1896 krbtgt->entry.principal,
1897 server->entry.principal)){
1898 kdc_log(context, config, 0, "Inconsistent request.");
1899 ret = KRB5KDC_ERR_SERVER_NOMATCH;
1900 goto out;
1903 /* check for valid set of addresses */
1904 if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
1905 ret = KRB5KRB_AP_ERR_BADADDR;
1906 kdc_log(context, config, 0, "Request from wrong address");
1907 goto out;
1911 * If this is an referral, add server referral data to the
1912 * auth_data reply .
1914 if (ref_realm) {
1915 PA_DATA pa;
1916 krb5_crypto crypto;
1918 kdc_log(context, config, 0,
1919 "Adding server referral to %s", ref_realm);
1921 ret = krb5_crypto_init(context, &sessionkey, 0, &crypto);
1922 if (ret)
1923 goto out;
1925 ret = build_server_referral(context, config, crypto, ref_realm,
1926 NULL, s, &pa.padata_value);
1927 krb5_crypto_destroy(context, crypto);
1928 if (ret) {
1929 kdc_log(context, config, 0,
1930 "Failed building server referral");
1931 goto out;
1933 pa.padata_type = KRB5_PADATA_SERVER_REFERRAL;
1935 ret = add_METHOD_DATA(&enc_pa_data, &pa);
1936 krb5_data_free(&pa.padata_value);
1937 if (ret) {
1938 kdc_log(context, config, 0,
1939 "Add server referral METHOD-DATA failed");
1940 goto out;
1948 ret = tgs_make_reply(context,
1949 config,
1951 client_principal,
1952 tgt,
1953 ekey,
1954 &sessionkey,
1955 kvno,
1956 *auth_data,
1957 server,
1959 spn,
1960 client,
1962 krbtgt,
1963 krbtgt_etype,
1964 spp,
1965 &rspac,
1966 &enc_pa_data,
1967 e_text,
1968 reply);
1970 out:
1971 free(spn);
1972 free(cpn);
1974 krb5_data_free(&rspac);
1975 krb5_free_keyblock_contents(context, &sessionkey);
1976 if(server)
1977 _kdc_free_ent(context, server);
1978 if(client)
1979 _kdc_free_ent(context, client);
1981 if (client_principal && client_principal != cp)
1982 krb5_free_principal(context, client_principal);
1983 if (cp)
1984 krb5_free_principal(context, cp);
1985 if (sp)
1986 krb5_free_principal(context, sp);
1987 if (ref_realm)
1988 free(ref_realm);
1989 free_METHOD_DATA(&enc_pa_data);
1991 free_EncTicketPart(&adtkt);
1993 return ret;
2000 krb5_error_code
2001 _kdc_tgs_rep(krb5_context context,
2002 krb5_kdc_configuration *config,
2003 KDC_REQ *req,
2004 krb5_data *data,
2005 const char *from,
2006 struct sockaddr *from_addr,
2007 int datagram_reply)
2009 AuthorizationData *auth_data = NULL;
2010 krb5_error_code ret;
2011 int i = 0;
2012 const PA_DATA *tgs_req;
2014 hdb_entry_ex *krbtgt = NULL;
2015 krb5_ticket *ticket = NULL;
2016 const char *e_text = NULL;
2017 krb5_enctype krbtgt_etype = ETYPE_NULL;
2019 time_t *csec = NULL;
2020 int *cusec = NULL;
2022 if(req->padata == NULL){
2023 ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
2024 kdc_log(context, config, 0,
2025 "TGS-REQ from %s without PA-DATA", from);
2026 goto out;
2029 tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
2031 if(tgs_req == NULL){
2032 ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
2034 kdc_log(context, config, 0,
2035 "TGS-REQ from %s without PA-TGS-REQ", from);
2036 goto out;
2038 ret = tgs_parse_request(context, config,
2039 &req->req_body, tgs_req,
2040 &krbtgt,
2041 &krbtgt_etype,
2042 &ticket,
2043 &e_text,
2044 from, from_addr,
2045 &csec, &cusec,
2046 &auth_data);
2047 if (ret) {
2048 kdc_log(context, config, 0,
2049 "Failed parsing TGS-REQ from %s", from);
2050 goto out;
2053 ret = tgs_build_reply(context,
2054 config,
2055 req,
2056 &req->req_body,
2057 krbtgt,
2058 krbtgt_etype,
2059 ticket,
2060 data,
2061 from,
2062 &e_text,
2063 &auth_data,
2064 from_addr);
2065 if (ret) {
2066 kdc_log(context, config, 0,
2067 "Failed building TGS-REP to %s", from);
2068 goto out;
2071 /* */
2072 if (datagram_reply && data->length > config->max_datagram_reply_length) {
2073 krb5_data_free(data);
2074 ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
2075 e_text = "Reply packet too large";
2078 out:
2079 if(ret && data->data == NULL){
2080 krb5_mk_error(context,
2081 ret,
2082 NULL,
2083 NULL,
2084 NULL,
2085 NULL,
2086 csec,
2087 cusec,
2088 data);
2090 free(csec);
2091 free(cusec);
2092 if (ticket)
2093 krb5_free_ticket(context, ticket);
2094 if(krbtgt)
2095 _kdc_free_ent(context, krbtgt);
2097 if (auth_data) {
2098 free_AuthorizationData(auth_data);
2099 free(auth_data);
2102 return 0;