Skip to content

Commit

Permalink
Move validate_tgs_request() to a separate file
Browse files Browse the repository at this point in the history
  • Loading branch information
tlyu committed Oct 15, 2012
1 parent bce9cb5 commit 6f1a8f8
Show file tree
Hide file tree
Showing 4 changed files with 394 additions and 334 deletions.
6 changes: 4 additions & 2 deletions src/kdc/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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
Expand Down
334 changes: 2 additions & 332 deletions src/kdc/kdc_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions src/kdc/kdc_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__ */

0 comments on commit 6f1a8f8

Please sign in to comment.