From 6f1a8f8c42cfc92fce8745e9228badb080bac892 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Thu, 20 Sep 2012 14:13:44 -0400 Subject: [PATCH] Move validate_tgs_request() to a separate file --- src/kdc/Makefile.in | 6 +- src/kdc/kdc_util.c | 334 +------------------------------------ src/kdc/kdc_util.h | 7 + src/kdc/tgs_policy.c | 381 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+), 334 deletions(-) create mode 100644 src/kdc/tgs_policy.c diff --git a/src/kdc/Makefile.in b/src/kdc/Makefile.in index 52e65d6776..acc7dbb0eb 100644 --- a/src/kdc/Makefile.in +++ b/src/kdc/Makefile.in @@ -27,7 +27,8 @@ SRCS= \ $(srcdir)/extern.c \ $(srcdir)/replay.c \ $(srcdir)/kdc_authdata.c \ - $(srcdir)/kdc_transit.c + $(srcdir)/kdc_transit.c \ + $(srcdir)/tgs_policy.c OBJS= \ kdc5_err.o \ @@ -44,7 +45,8 @@ OBJS= \ extern.o \ replay.o \ kdc_authdata.o \ - kdc_transit.o + kdc_transit.o \ + tgs_policy.o RT_OBJS= rtest.o \ kdc_transit.o diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index 0162f790e4..10ed38314d 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -551,7 +551,7 @@ check_hot_list(krb5_ticket *ticket) /* Convert an API error code to a protocol error code. */ -static int +int errcode_to_protocol(krb5_error_code code) { int protcode; @@ -562,7 +562,7 @@ errcode_to_protocol(krb5_error_code code) /* Return -1 if the AS or TGS request is disallowed due to KDC policy on * anonymous tickets. */ -static int +int check_anon(kdc_realm_t *kdc_active_realm, krb5_principal client, krb5_principal server) { @@ -874,336 +874,6 @@ fetch_asn1_field(unsigned char *astream, unsigned int level, return(-1); } -/* - * Routines that validate a TGS request; checks a lot of things. :-) - * - * Returns a Kerberos protocol error number, which is _not_ the same - * as a com_err error number! - */ - -struct tgsflagrule { - krb5_flags reqflags; /* Flag(s) in TGS-REQ */ - krb5_flags checkflag; /* Flags to check against */ - char *status; /* Status string */ - int err; /* Protocol error code */ -}; - -/* Service principal TGS policy checking functions */ -typedef int (check_tgs_svc_pol_fn)(krb5_kdc_req *, krb5_db_entry, - krb5_ticket *, const char **); - -static check_tgs_svc_pol_fn check_tgs_svc_deny_opts; -static check_tgs_svc_pol_fn check_tgs_svc_deny_all; -static check_tgs_svc_pol_fn check_tgs_svc_reqd_flags; - -static const struct tgsflagrule tgsflagrules[] = { - { (KDC_OPT_FORWARDED | KDC_OPT_FORWARDABLE), TKT_FLG_FORWARDABLE, - "TGT NOT FORWARDABLE", KDC_ERR_BADOPTION }, - { (KDC_OPT_PROXY | KDC_OPT_PROXIABLE), TKT_FLG_PROXIABLE, - "TGT NOT PROXIABLE", KDC_ERR_BADOPTION }, - { (KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED), TKT_FLG_MAY_POSTDATE, - "TGT NOT POSTDATABLE", KDC_ERR_BADOPTION }, - { KDC_OPT_VALIDATE, TKT_FLG_INVALID, - "VALIDATE VALID TICKET", KDC_ERR_BADOPTION }, - { (KDC_OPT_RENEW | KDC_OPT_RENEWABLE), TKT_FLG_RENEWABLE, - "TICKET NOT RENEWABLE", KDC_ERR_BADOPTION } -}; - -/* - * Check that TGS-REQ options are consistent with the ticket flags. - */ -static int -check_tgs_opts(krb5_kdc_req *req, krb5_ticket *tkt, const char **status) -{ - size_t i; - size_t nrules = sizeof(tgsflagrules) / sizeof(tgsflagrules[0]); - const struct tgsflagrule *r; - - for (i = 0; i < nrules; i++) { - r = &tgsflagrules[i]; - if (!(r->reqflags & req->kdc_options)) - continue; - if (!(r->checkflag & tkt->enc_part2->flags)) { - *status = r->status; - return r->err; - } - } - return 0; -} - -static const struct tgsflagrule svcdenyrules[] = { - { KDC_OPT_FORWARDABLE, KRB5_KDB_DISALLOW_FORWARDABLE, - "NON-FORWARDABLE TICKET", KDC_ERR_POLICY }, - { KDC_OPT_RENEWABLE, KRB5_KDB_DISALLOW_RENEWABLE, - "NON-RENEWABLE TICKET", KDC_ERR_POLICY }, - { KDC_OPT_PROXIABLE, KRB5_KDB_DISALLOW_PROXIABLE, - "NON-PROXIABLE TICKET", KDC_ERR_POLICY }, - { KDC_OPT_ALLOW_POSTDATE, KRB5_KDB_DISALLOW_POSTDATED, - "NON-POSTDATABLE TICKET", KDC_ERR_CANNOT_POSTDATE }, - { KDC_OPT_ENC_TKT_IN_SKEY, KRB5_KDB_DISALLOW_DUP_SKEY, - "DUP_SKEY DISALLOWED", KDC_ERR_POLICY } -}; - -/* - * A service principal can forbid some TGS-REQ options. - */ -static int -check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry server, - krb5_ticket *tkt, const char **status) -{ - size_t i; - size_t nrules = sizeof(svcdenyrules) / sizeof(svcdenyrules[0]); - const struct tgsflagrule *r; - - for (i = 0; i < nrules; i++) { - r = &svcdenyrules[i]; - if (!(r->reqflags & req->kdc_options)) - continue; - if (r->checkflag & server.attributes) { - *status = r->status; - return r->err; - } - } - return 0; -} - -static int -check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry server, - krb5_ticket *tkt, const char **status) -{ - if (server.attributes & KRB5_KDB_DISALLOW_ALL_TIX) { - *status = "SERVER LOCKED OUT"; - return KDC_ERR_S_PRINCIPAL_UNKNOWN; - } - if (server.attributes & KRB5_KDB_DISALLOW_SVR) { - *status = "SERVER NOT ALLOWED"; - return KDC_ERR_MUST_USE_USER2USER; - } - return 0; -} - -/* - * A service principal can require certain TGT flags. - */ -static int -check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry server, - krb5_ticket *tkt, const char **status) -{ - if (server.attributes & KRB5_KDB_REQUIRES_HW_AUTH && - !(tkt->enc_part2->flags & TKT_FLG_HW_AUTH)) { - *status = "NO HW PREAUTH"; - return KRB_ERR_GENERIC; - } - if (server.attributes & KRB5_KDB_REQUIRES_PRE_AUTH && - !(tkt->enc_part2->flags & TKT_FLG_PRE_AUTH)) { - *status = "NO PREAUTH"; - return KRB_ERR_GENERIC; - } - return 0; -} - -static check_tgs_svc_pol_fn *svc_pol_fns[] = { - check_tgs_svc_deny_opts, check_tgs_svc_deny_all, check_tgs_svc_reqd_flags -}; - -static int -check_tgs_svc_policy(krb5_kdc_req *req, krb5_db_entry server, - krb5_ticket *tkt, const char **status) -{ - int errcode; - size_t i; - size_t nfns = sizeof(svc_pol_fns) / sizeof(svc_pol_fns[0]); - - for (i = 0; i < nfns; i++) { - errcode = svc_pol_fns[i](req, server, tkt, status); - if (errcode != 0) - return errcode; - } - return 0; -} - -/* - * Check some timestamps in the TGS-REQ. - */ -static int -check_tgs_times(krb5_kdc_req *req, krb5_db_entry server, krb5_ticket *tkt, - krb5_timestamp kdc_time, const char **status) -{ - - /* Check to see if service principal has expired. */ - if (server.expiration && server.expiration < kdc_time) { - *status = "SERVICE EXPIRED"; - return KDC_ERR_SERVICE_EXP; - } - /* For validating a postdated ticket, check the start time vs. the - KDC time. */ - if (req->kdc_options & KDC_OPT_VALIDATE) { - if (tkt->enc_part2->times.starttime > kdc_time) { - *status = "NOT_YET_VALID"; - return KRB_AP_ERR_TKT_NYV; - } - } - /* - * Check the renew_till time. The endtime was already - * been checked in the initial authentication check. - */ - if ((req->kdc_options & KDC_OPT_RENEW) && - (tkt->enc_part2->times.renew_till < kdc_time)) { - *status = "TKT_EXPIRED"; - return KRB_AP_ERR_TKT_EXPIRED; - } - return 0; -} - -/* - * Check second ticket, if required by TGS-REQ options. - */ -static int -check_2nd_tkt(kdc_realm_t *kdc_active_realm, - krb5_kdc_req *req, const char **status) -{ - /* user-to-user */ - if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { - /* Check that second ticket is in request. */ - if (!req->second_ticket || !req->second_ticket[0]) { - *status = "NO_2ND_TKT"; - return KDC_ERR_BADOPTION; - } - /* Check that second ticket is a TGT. */ - if (!krb5_principal_compare(kdc_context, - req->second_ticket[0]->server, - tgs_server)) { - *status = "2ND_TKT_NOT_TGS"; - return KDC_ERR_POLICY; - } - } - /* S4U2Proxy */ - if (req->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) { - /* Check that second ticket is in request. */ - if (!req->second_ticket || !req->second_ticket[0]) { - *status = "NO_2ND_TKT"; - return KDC_ERR_BADOPTION; - } - } - return 0; -} - -/* - * Some TGS-REQ options allow for a non-TGS principal in the ticket. Do some - * checks that are peculiar to these cases. (e.g., ticket service principal - * matches requested service principal) - */ -static int -check_tgs_nontgt(kdc_realm_t *kdc_active_realm, - krb5_kdc_req *req, krb5_ticket *tkt, const char **status) -{ - - if (!krb5_principal_compare(kdc_context, tkt->server, req->server)) { - *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; - return KDC_ERR_SERVER_NOMATCH; - } - /* Cannot proxy ticket granting tickets. */ - if ((req->kdc_options & KDC_OPT_PROXY) && - krb5_is_tgs_principal(req->server)) { - *status = "CAN'T PROXY TGT"; - return KDC_ERR_BADOPTION; - } - return 0; -} - -/* - * Do some checks for a normal TGS-REQ (where the ticket service must be a TGS - * principal). - */ -static int -check_tgs_tgt(kdc_realm_t *kdc_active_realm, - krb5_kdc_req *req, krb5_db_entry server, - krb5_ticket *tkt, const char **status) -{ - /* Make sure it's a TGS principal. */ - if (!krb5_is_tgs_principal(tkt->server)) { - *status = "BAD TGS SERVER NAME"; - return KRB_AP_ERR_NOT_US; - } - /* TGS principal second component must match service realm. */ - if (!data_eq(*krb5_princ_component(kdc_context, tkt->server, 1), - *krb5_princ_realm(kdc_context, req->server))) { - *status = "BAD TGS SERVER INSTANCE"; - return KRB_AP_ERR_NOT_US; - } - /* Server must allow TGS based issuances */ - if (server.attributes & KRB5_KDB_DISALLOW_TGT_BASED) { - *status = "TGT BASED NOT ALLOWED"; - return KDC_ERR_POLICY; - } - return 0; -} - -/* TGS-REQ options where the service can be a non-TGS principal */ -#define NON_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \ - KDC_OPT_VALIDATE) - -int -validate_tgs_request(kdc_realm_t *kdc_active_realm, - register krb5_kdc_req *request, krb5_db_entry server, - krb5_ticket *ticket, krb5_timestamp kdc_time, - const char **status, krb5_pa_data ***e_data) -{ - int errcode; - krb5_error_code ret; - - errcode = check_tgs_times(request, server, ticket, kdc_time, status); - if (errcode != 0) - return errcode; - - errcode = check_tgs_opts(request, ticket, status); - if (errcode != 0) - return errcode; - - errcode = check_tgs_svc_policy(request, server, ticket, status); - if (errcode != 0) - return errcode; - - if (request->kdc_options & NON_TGT_OPTION) - errcode = check_tgs_nontgt(kdc_active_realm, request, ticket, status); - else - errcode = check_tgs_tgt(kdc_active_realm, request, server, ticket, - status); - if (errcode != 0) - return errcode; - - /* Check the hot list */ - if (check_hot_list(ticket)) { - *status = "HOT_LIST"; - return(KRB_AP_ERR_REPEAT); - } - - errcode = check_2nd_tkt(kdc_active_realm, request, status); - if (errcode != 0) - return errcode; - - if (check_anon(kdc_active_realm, ticket->enc_part2->client, - request->server) != 0) { - *status = "ANONYMOUS NOT ALLOWED"; - return(KDC_ERR_POLICY); - } - - /* Perform KDB module policy checks. */ - ret = krb5_db_check_policy_tgs(kdc_context, request, &server, - ticket, status, e_data); - if (ret && ret != KRB5_PLUGIN_OP_NOTSUPP) - return errcode_to_protocol(ret); - - /* Check local policy. */ - errcode = against_local_policy_tgs(request, server, ticket, - status, e_data); - if (errcode) - return errcode; - - - return 0; -} - /* Return true if we believe server can support enctype as a session key. */ static krb5_boolean dbentry_supports_enctype(kdc_realm_t *kdc_active_realm, krb5_db_entry *server, diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index 2f215a7413..b89bd99c73 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -392,5 +392,12 @@ struct krb5_kdcpreauth_rock_st { /* RFC 4120: KRB5KDC_ERR_KEY_TOO_WEAK * RFC 4556: KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED */ #define KRB5KDC_ERR_KEY_TOO_WEAK KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED +/* TGS-REQ options where the service can be a non-TGS principal */ + +#define NON_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \ + KDC_OPT_VALIDATE) +int check_anon(kdc_realm_t *kdc_active_realm, + krb5_principal client, krb5_principal server); +int errcode_to_protocol(krb5_error_code code); #endif /* __KRB5_KDC_UTIL__ */ diff --git a/src/kdc/tgs_policy.c b/src/kdc/tgs_policy.c new file mode 100644 index 0000000000..0650c23f02 --- /dev/null +++ b/src/kdc/tgs_policy.c @@ -0,0 +1,381 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kdc/tgs_policy.c */ +/* + * Copyright (C) 2012 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "kdc_util.h" + +/* + * Routines that validate a TGS request; checks a lot of things. :-) + * + * Returns a Kerberos protocol error number, which is _not_ the same + * as a com_err error number! + */ + +struct tgsflagrule { + krb5_flags reqflags; /* Flag(s) in TGS-REQ */ + krb5_flags checkflag; /* Flags to check against */ + char *status; /* Status string */ + int err; /* Protocol error code */ +}; + +/* Service principal TGS policy checking functions */ +typedef int (check_tgs_svc_pol_fn)(krb5_kdc_req *, krb5_db_entry, + krb5_ticket *, krb5_timestamp, + const char **); + +static check_tgs_svc_pol_fn check_tgs_svc_deny_opts; +static check_tgs_svc_pol_fn check_tgs_svc_deny_all; +static check_tgs_svc_pol_fn check_tgs_svc_reqd_flags; +static check_tgs_svc_pol_fn check_tgs_svc_time; + +static check_tgs_svc_pol_fn * const svc_pol_fns[] = { + check_tgs_svc_deny_opts, check_tgs_svc_deny_all, check_tgs_svc_reqd_flags, + check_tgs_svc_time +}; + +static const struct tgsflagrule tgsflagrules[] = { + { (KDC_OPT_FORWARDED | KDC_OPT_FORWARDABLE), TKT_FLG_FORWARDABLE, + "TGT NOT FORWARDABLE", KDC_ERR_BADOPTION }, + { (KDC_OPT_PROXY | KDC_OPT_PROXIABLE), TKT_FLG_PROXIABLE, + "TGT NOT PROXIABLE", KDC_ERR_BADOPTION }, + { (KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED), TKT_FLG_MAY_POSTDATE, + "TGT NOT POSTDATABLE", KDC_ERR_BADOPTION }, + { KDC_OPT_VALIDATE, TKT_FLG_INVALID, + "VALIDATE VALID TICKET", KDC_ERR_BADOPTION }, + { (KDC_OPT_RENEW | KDC_OPT_RENEWABLE), TKT_FLG_RENEWABLE, + "TICKET NOT RENEWABLE", KDC_ERR_BADOPTION } +}; + +/* + * Some TGS-REQ options require that the ticket have corresponding flags set. + */ +static int +check_tgs_opts(krb5_kdc_req *req, krb5_ticket *tkt, const char **status) +{ + size_t i; + size_t nrules = sizeof(tgsflagrules) / sizeof(tgsflagrules[0]); + const struct tgsflagrule *r; + + for (i = 0; i < nrules; i++) { + r = &tgsflagrules[i]; + if (r->reqflags & req->kdc_options) { + if (!(r->checkflag & tkt->enc_part2->flags)) { + *status = r->status; + return r->err; + } + } + } + return 0; +} + +static const struct tgsflagrule svcdenyrules[] = { + { KDC_OPT_FORWARDABLE, KRB5_KDB_DISALLOW_FORWARDABLE, + "NON-FORWARDABLE TICKET", KDC_ERR_POLICY }, + { KDC_OPT_RENEWABLE, KRB5_KDB_DISALLOW_RENEWABLE, + "NON-RENEWABLE TICKET", KDC_ERR_POLICY }, + { KDC_OPT_PROXIABLE, KRB5_KDB_DISALLOW_PROXIABLE, + "NON-PROXIABLE TICKET", KDC_ERR_POLICY }, + { KDC_OPT_ALLOW_POSTDATE, KRB5_KDB_DISALLOW_POSTDATED, + "NON-POSTDATABLE TICKET", KDC_ERR_CANNOT_POSTDATE }, + { KDC_OPT_ENC_TKT_IN_SKEY, KRB5_KDB_DISALLOW_DUP_SKEY, + "DUP_SKEY DISALLOWED", KDC_ERR_POLICY } +}; + +/* + * A service principal can forbid some TGS-REQ options. + */ +static int +check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry server, + krb5_ticket *tkt, krb5_timestamp kdc_time, + const char **status) +{ + size_t i; + size_t nrules = sizeof(svcdenyrules) / sizeof(svcdenyrules[0]); + const struct tgsflagrule *r; + + for (i = 0; i < nrules; i++) { + r = &svcdenyrules[i]; + if (!(r->reqflags & req->kdc_options)) + continue; + if (r->checkflag & server.attributes) { + *status = r->status; + return r->err; + } + } + return 0; +} + +/* + * A service principal can deny all TGS-REQs for it. + */ +static int +check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry server, + krb5_ticket *tkt, krb5_timestamp kdc_time, + const char **status) +{ + if (server.attributes & KRB5_KDB_DISALLOW_ALL_TIX) { + *status = "SERVER LOCKED OUT"; + return KDC_ERR_S_PRINCIPAL_UNKNOWN; + } + if (server.attributes & KRB5_KDB_DISALLOW_SVR) { + *status = "SERVER NOT ALLOWED"; + return KDC_ERR_MUST_USE_USER2USER; + } + if (server.attributes & KRB5_KDB_DISALLOW_TGT_BASED) { + if (krb5_is_tgs_principal(tkt->server)) { + *status = "TGT BASED NOT ALLOWED"; + return KDC_ERR_POLICY; + } + } + return 0; +} + +/* + * A service principal can require certain TGT flags. + */ +static int +check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry server, + krb5_ticket *tkt, + krb5_timestamp kdc_time, const char **status) +{ + if (server.attributes & KRB5_KDB_REQUIRES_HW_AUTH) { + if (!(tkt->enc_part2->flags & TKT_FLG_HW_AUTH)) { + *status = "NO HW PREAUTH"; + return KRB_ERR_GENERIC; + } + } + if (server.attributes & KRB5_KDB_REQUIRES_PRE_AUTH) { + if (!(tkt->enc_part2->flags & TKT_FLG_PRE_AUTH)) { + *status = "NO PREAUTH"; + return KRB_ERR_GENERIC; + } + } + return 0; +} + +static int +check_tgs_svc_time(krb5_kdc_req *req, krb5_db_entry server, krb5_ticket *tkt, + krb5_timestamp kdc_time, const char **status) +{ + if (server.expiration && server.expiration < kdc_time) { + *status = "SERVICE EXPIRED"; + return KDC_ERR_SERVICE_EXP; + } + return 0; +} + +static int +check_tgs_svc_policy(krb5_kdc_req *req, krb5_db_entry server, krb5_ticket *tkt, + krb5_timestamp kdc_time, const char **status) +{ + int errcode; + size_t i; + size_t nfns = sizeof(svc_pol_fns) / sizeof(svc_pol_fns[0]); + + for (i = 0; i < nfns; i++) { + errcode = svc_pol_fns[i](req, server, tkt, kdc_time, status); + if (errcode != 0) + return errcode; + } + return 0; +} + +/* + * Check some timestamps in the TGS-REQ. + */ +static int +check_tgs_times(krb5_kdc_req *req, krb5_ticket *tkt, + krb5_timestamp kdc_time, const char **status) +{ + /* For validating a postdated ticket, check the start time vs. the + KDC time. */ + if (req->kdc_options & KDC_OPT_VALIDATE) { + if (tkt->enc_part2->times.starttime > kdc_time) { + *status = "NOT_YET_VALID"; + return KRB_AP_ERR_TKT_NYV; + } + } + /* + * Check the renew_till time. The endtime was already + * been checked in the initial authentication check. + */ + if ((req->kdc_options & KDC_OPT_RENEW) && + (tkt->enc_part2->times.renew_till < kdc_time)) { + *status = "TKT_EXPIRED"; + return KRB_AP_ERR_TKT_EXPIRED; + } + return 0; +} + +static int +check_tgs_s4u2proxy(kdc_realm_t *kdc_active_realm, + krb5_kdc_req *req, const char **status) +{ + if (req->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT) { + /* Check that second ticket is in request. */ + if (!req->second_ticket || !req->second_ticket[0]) { + *status = "NO_2ND_TKT"; + return KDC_ERR_BADOPTION; + } + } + return 0; +} + +static int +check_tgs_u2u(kdc_realm_t *kdc_active_realm, + krb5_kdc_req *req, const char **status) +{ + if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { + /* Check that second ticket is in request. */ + if (!req->second_ticket || !req->second_ticket[0]) { + *status = "NO_2ND_TKT"; + return KDC_ERR_BADOPTION; + } + /* Check that second ticket is a TGT. */ + if (!krb5_principal_compare(kdc_context, + req->second_ticket[0]->server, + tgs_server)) { + *status = "2ND_TKT_NOT_TGS"; + return KDC_ERR_POLICY; + } + } + return 0; +} + +/* + * Some TGS-REQ options allow for a non-TGS principal in the ticket. Do some + * checks that are peculiar to these cases. (e.g., ticket service principal + * matches requested service principal) + */ +static int +check_tgs_nontgt(kdc_realm_t *kdc_active_realm, + krb5_kdc_req *req, krb5_ticket *tkt, const char **status) +{ + if (!krb5_principal_compare(kdc_context, tkt->server, req->server)) { + *status = "SERVER DIDN'T MATCH TICKET FOR RENEW/FORWARD/ETC"; + return KDC_ERR_SERVER_NOMATCH; + } + /* Cannot proxy ticket granting tickets. */ + if ((req->kdc_options & KDC_OPT_PROXY) && + krb5_is_tgs_principal(req->server)) { + *status = "CAN'T PROXY TGT"; + return KDC_ERR_BADOPTION; + } + return 0; +} + +/* + * Do some checks for a normal TGS-REQ (where the ticket service must be a TGS + * principal). + */ +static int +check_tgs_tgt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req, + krb5_ticket *tkt, const char **status) +{ + /* Make sure it's a TGS principal. */ + if (!krb5_is_tgs_principal(tkt->server)) { + *status = "BAD TGS SERVER NAME"; + return KRB_AP_ERR_NOT_US; + } + /* TGS principal second component must match service realm. */ + if (!data_eq(*krb5_princ_component(kdc_context, tkt->server, 1), + *krb5_princ_realm(kdc_context, req->server))) { + *status = "BAD TGS SERVER INSTANCE"; + return KRB_AP_ERR_NOT_US; + } + return 0; +} + +int +validate_tgs_request(kdc_realm_t *kdc_active_realm, + register krb5_kdc_req *request, krb5_db_entry server, + krb5_ticket *ticket, krb5_timestamp kdc_time, + const char **status, krb5_pa_data ***e_data) +{ + int errcode; + krb5_error_code ret; + + /* Depends only on request and ticket. */ + errcode = check_tgs_opts(request, ticket, status); + if (errcode != 0) + return errcode; + + /* Depends only on request, ticket, and time. */ + errcode = check_tgs_times(request, ticket, kdc_time, status); + if (errcode != 0) + return errcode; + + errcode = check_tgs_svc_policy(request, server, ticket, kdc_time, status); + if (errcode != 0) + return errcode; + + if (request->kdc_options & NON_TGT_OPTION) + errcode = check_tgs_nontgt(kdc_active_realm, request, ticket, status); + else + errcode = check_tgs_tgt(kdc_active_realm, request, ticket, status); + if (errcode != 0) + return errcode; + + /* Check the hot list */ + if (check_hot_list(ticket)) { + *status = "HOT_LIST"; + return(KRB_AP_ERR_REPEAT); + } + + errcode = check_tgs_u2u(kdc_active_realm, request, status); + if (errcode != 0) + return errcode; + + errcode = check_tgs_s4u2proxy(kdc_active_realm, request, status); + if (errcode != 0) + return errcode; + + if (check_anon(kdc_active_realm, ticket->enc_part2->client, + request->server) != 0) { + *status = "ANONYMOUS NOT ALLOWED"; + return(KDC_ERR_POLICY); + } + + /* Perform KDB module policy checks. */ + ret = krb5_db_check_policy_tgs(kdc_context, request, &server, + ticket, status, e_data); + if (ret && ret != KRB5_PLUGIN_OP_NOTSUPP) + return errcode_to_protocol(ret); + + /* Check local policy. */ + errcode = against_local_policy_tgs(request, server, ticket, + status, e_data); + if (errcode) + return errcode; + + return 0; +} -- 2.47.3