aboutsummaryrefslogtreecommitdiffstats
path: root/src/kdc
diff options
context:
space:
mode:
Diffstat (limited to 'src/kdc')
-rw-r--r--src/kdc/deps40
-rw-r--r--src/kdc/dispatch.c19
-rw-r--r--src/kdc/do_as_req.c90
-rw-r--r--src/kdc/do_tgs_req.c70
-rw-r--r--src/kdc/extern.c4
-rw-r--r--src/kdc/fast_util.c4
-rw-r--r--src/kdc/kdc_log.c29
-rw-r--r--src/kdc/kdc_preauth.c35
-rw-r--r--src/kdc/kdc_preauth_ec.c41
-rw-r--r--src/kdc/kdc_preauth_encts.c9
-rw-r--r--src/kdc/kdc_util.c57
-rw-r--r--src/kdc/kdc_util.h21
-rw-r--r--src/kdc/main.c8
-rw-r--r--src/kdc/policy.c267
-rw-r--r--src/kdc/policy.h19
-rw-r--r--src/kdc/replay.c2
-rwxr-xr-xsrc/kdc/t_emptytgt.py5
-rw-r--r--src/kdc/t_replay.c13
-rw-r--r--src/kdc/tgs_policy.c13
19 files changed, 476 insertions, 270 deletions
diff --git a/src/kdc/deps b/src/kdc/deps
index b5257c715f88..6bc00f7a938c 100644
--- a/src/kdc/deps
+++ b/src/kdc/deps
@@ -171,20 +171,22 @@ $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \
$(top_srcdir)/include/socket-utils.h extern.h kdc5_err.h \
- kdc_audit.h kdc_util.h main.c realm_data.h reqstate.h
+ kdc_audit.h kdc_util.h main.c policy.h realm_data.h \
+ reqstate.h
$(OUTPRE)policy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
- $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
- $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
- $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/kdcpolicy_plugin.h \
$(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
$(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \
$(top_srcdir)/include/socket-utils.h extern.h kdc_util.h \
- policy.c realm_data.h reqstate.h
+ policy.c policy.h realm_data.h reqstate.h
$(OUTPRE)extern.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
@@ -279,14 +281,14 @@ $(OUTPRE)kdc_log.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(OUTPRE)t_replay.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
- $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
- $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
- $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/kdcpreauth_plugin.h \
- $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
- $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
- extern.h kdc_util.h realm_data.h replay.c reqstate.h \
- t_replay.c
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-cmocka.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-queue.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/kdcpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h extern.h kdc_util.h \
+ realm_data.h replay.c reqstate.h t_replay.c
diff --git a/src/kdc/dispatch.c b/src/kdc/dispatch.c
index 3a169ebc7f40..3867ff952e49 100644
--- a/src/kdc/dispatch.c
+++ b/src/kdc/dispatch.c
@@ -94,8 +94,8 @@ static void
reseed_random(krb5_context kdc_err_context)
{
krb5_error_code retval;
- krb5_int32 now, now_usec;
- krb5_int32 usec_difference;
+ krb5_timestamp now;
+ krb5_int32 now_usec, usec_difference;
krb5_data data;
retval = krb5_crypto_us_timeofday(&now, &now_usec);
@@ -104,7 +104,7 @@ reseed_random(krb5_context kdc_err_context)
if (last_os_random == 0)
last_os_random = now;
/* Grab random data from OS every hour*/
- if (now-last_os_random >= 60 * 60) {
+ if (ts_delta(now, last_os_random) >= 60 * 60) {
krb5_c_random_os_entropy(kdc_err_context, 0, NULL);
last_os_random = now;
}
@@ -119,8 +119,8 @@ reseed_random(krb5_context kdc_err_context)
}
void
-dispatch(void *cb, struct sockaddr *local_saddr,
- const krb5_fulladdr *from, krb5_data *pkt, int is_tcp,
+dispatch(void *cb, const krb5_fulladdr *local_addr,
+ const krb5_fulladdr *remote_addr, krb5_data *pkt, int is_tcp,
verto_ctx *vctx, loop_respond_fn respond, void *arg)
{
krb5_error_code retval;
@@ -150,8 +150,8 @@ dispatch(void *cb, struct sockaddr *local_saddr,
const char *name = 0;
char buf[46];
- name = inet_ntop (ADDRTYPE2FAMILY (from->address->addrtype),
- from->address->contents, buf, sizeof (buf));
+ name = inet_ntop(ADDRTYPE2FAMILY(remote_addr->address->addrtype),
+ remote_addr->address->contents, buf, sizeof(buf));
if (name == 0)
name = "[unknown address type]";
if (response)
@@ -177,7 +177,7 @@ dispatch(void *cb, struct sockaddr *local_saddr,
/* try TGS_REQ first; they are more common! */
if (krb5_is_tgs_req(pkt)) {
- retval = process_tgs_req(handle, pkt, from, &response);
+ retval = process_tgs_req(handle, pkt, remote_addr, &response);
} else if (krb5_is_as_req(pkt)) {
if (!(retval = decode_krb5_as_req(pkt, &as_req))) {
/*
@@ -187,7 +187,8 @@ dispatch(void *cb, struct sockaddr *local_saddr,
*/
state->active_realm = setup_server_realm(handle, as_req->server);
if (state->active_realm != NULL) {
- process_as_req(as_req, pkt, from, state->active_realm, vctx,
+ process_as_req(as_req, pkt, local_addr, remote_addr,
+ state->active_realm, vctx,
finish_dispatch_cache, state);
return;
} else {
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 712ccb794680..7c8da63e1861 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -87,7 +87,7 @@ get_key_exp(krb5_db_entry *entry)
return entry->pw_expiration;
if (entry->pw_expiration == 0)
return entry->expiration;
- return min(entry->expiration, entry->pw_expiration);
+ return ts_min(entry->expiration, entry->pw_expiration);
}
/*
@@ -160,7 +160,8 @@ struct as_req_state {
struct kdc_request_state *rstate;
char *sname, *cname;
void *pa_context;
- const krb5_fulladdr *from;
+ const krb5_fulladdr *local_addr;
+ const krb5_fulladdr *remote_addr;
krb5_data **auth_indicators;
krb5_error_code preauth_err;
@@ -207,6 +208,13 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
state->ticket_reply.enc_part2 = &state->enc_tkt_reply;
+ errcode = check_kdcpolicy_as(kdc_context, state->request, state->client,
+ state->server, state->auth_indicators,
+ state->kdc_time, &state->enc_tkt_reply.times,
+ &state->status);
+ if (errcode)
+ goto egress;
+
/*
* Find the server key
*/
@@ -239,10 +247,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
state->reply.ticket = &state->ticket_reply;
state->reply_encpart.session = &state->session_key;
if ((errcode = fetch_last_req_info(state->client,
- &state->reply_encpart.last_req))) {
- state->status = "FETCH_LAST_REQ";
+ &state->reply_encpart.last_req)))
goto egress;
- }
state->reply_encpart.nonce = state->request->nonce;
state->reply_encpart.key_exp = get_key_exp(state->client);
state->reply_encpart.flags = state->enc_tkt_reply.flags;
@@ -300,27 +306,21 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
errcode = krb5_encrypt_tkt_part(kdc_context, &state->server_keyblock,
&state->ticket_reply);
- if (errcode) {
- state->status = "ENCRYPT_TICKET";
+ if (errcode)
goto egress;
- }
errcode = kau_make_tkt_id(kdc_context, &state->ticket_reply,
&au_state->tkt_out_id);
- if (errcode) {
- state->status = "GENERATE_TICKET_ID";
+ if (errcode)
goto egress;
- }
state->ticket_reply.enc_part.kvno = server_key->key_data_kvno;
errcode = kdc_fast_response_handle_padata(state->rstate,
state->request,
&state->reply,
state->client_keyblock.enctype);
- if (errcode) {
- state->status = "MAKE_FAST_RESPONSE";
+ if (errcode)
goto egress;
- }
/* now encode/encrypt the response */
@@ -328,10 +328,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
errcode = kdc_fast_handle_reply_key(state->rstate, &state->client_keyblock,
&as_encrypting_key);
- if (errcode) {
- state->status = "MAKE_FAST_REPLY_KEY";
+ if (errcode)
goto egress;
- }
errcode = return_enc_padata(kdc_context, state->req_pkt, state->request,
as_encrypting_key, state->server,
&state->reply_encpart, FALSE);
@@ -348,10 +346,8 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
&state->reply, &response);
if (state->client_key != NULL)
state->reply.enc_part.kvno = state->client_key->key_data_kvno;
- if (errcode) {
- state->status = "ENCODE_KDC_REP";
+ if (errcode)
goto egress;
- }
/* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
can use them in raw form if needed. But, we don't... */
@@ -359,14 +355,14 @@ finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
state->reply.enc_part.ciphertext.length);
free(state->reply.enc_part.ciphertext.data);
- log_as_req(kdc_context, state->from, state->request, &state->reply,
- state->client, state->cname, state->server,
- state->sname, state->authtime, 0, 0, 0);
+ log_as_req(kdc_context, state->local_addr, state->remote_addr,
+ state->request, &state->reply, state->client, state->cname,
+ state->server, state->sname, state->authtime, 0, 0, 0);
did_log = 1;
egress:
- if (errcode != 0)
- assert (state->status != 0);
+ if (errcode != 0 && state->status == NULL)
+ state->status = "UNKNOWN_REASON";
au_state->status = state->status;
au_state->reply = &state->reply;
@@ -381,8 +377,8 @@ egress:
emsg = krb5_get_error_message(kdc_context, errcode);
if (state->status) {
- log_as_req(kdc_context,
- state->from, state->request, &state->reply, state->client,
+ log_as_req(kdc_context, state->local_addr, state->remote_addr,
+ state->request, &state->reply, state->client,
state->cname, state->server, state->sname, state->authtime,
state->status, errcode, emsg);
did_log = 1;
@@ -492,7 +488,8 @@ finish_preauth(void *arg, krb5_error_code code)
/*ARGSUSED*/
void
process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
- const krb5_fulladdr *from, kdc_realm_t *kdc_active_realm,
+ const krb5_fulladdr *local_addr,
+ const krb5_fulladdr *remote_addr, kdc_realm_t *kdc_active_realm,
verto_ctx *vctx, loop_respond_fn respond, void *arg)
{
krb5_error_code errcode;
@@ -511,7 +508,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
state->arg = arg;
state->request = request;
state->req_pkt = req_pkt;
- state->from = from;
+ state->local_addr = local_addr;
+ state->remote_addr = remote_addr;
state->active_realm = kdc_active_realm;
errcode = kdc_make_rstate(kdc_active_realm, &state->rstate);
@@ -522,7 +520,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
}
/* Initialize audit state. */
- errcode = kau_init_kdc_req(kdc_context, state->request, from, &au_state);
+ errcode = kau_init_kdc_req(kdc_context, state->request, remote_addr,
+ &au_state);
if (errcode) {
(*respond)(arg, errcode, NULL);
kdc_free_rstate(state->rstate);
@@ -543,7 +542,6 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
if (fetch_asn1_field((unsigned char *) req_pkt->data,
1, 4, &encoded_req_body) != 0) {
errcode = ASN1_BAD_ID;
- state->status = "FETCH_REQ_BODY";
goto errout;
}
errcode = kdc_find_fast(&state->request, &encoded_req_body, NULL, NULL,
@@ -556,10 +554,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
/* Not a FAST request; copy the encoded request body. */
errcode = krb5_copy_data(kdc_context, &encoded_req_body,
&state->inner_body);
- if (errcode) {
- state->status = "COPY_REQ_BODY";
+ if (errcode)
goto errout;
- }
}
au_state->request = state->request;
state->rock.request = state->request;
@@ -574,10 +570,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
}
if ((errcode = krb5_unparse_name(kdc_context,
state->request->client,
- &state->cname))) {
- state->status = "UNPARSE_CLIENT";
+ &state->cname)))
goto errout;
- }
limit_string(state->cname);
if (!state->request->server) {
@@ -587,10 +581,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
}
if ((errcode = krb5_unparse_name(kdc_context,
state->request->server,
- &state->sname))) {
- state->status = "UNPARSE_SERVER";
+ &state->sname)))
goto errout;
- }
limit_string(state->sname);
/*
@@ -670,18 +662,14 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
au_state->stage = VALIDATE_POL;
- if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time))) {
- state->status = "TIMEOFDAY";
+ if ((errcode = krb5_timeofday(kdc_context, &state->kdc_time)))
goto errout;
- }
state->authtime = state->kdc_time; /* for audit_as_request() */
if ((errcode = validate_as_request(kdc_active_realm,
state->request, *state->client,
*state->server, state->kdc_time,
&state->status, &state->e_data))) {
- if (!state->status)
- state->status = "UNKNOWN_REASON";
errcode += ERROR_TABLE_BASE_krb5;
goto errout;
}
@@ -701,10 +689,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
}
if ((errcode = krb5_c_make_random_key(kdc_context, useenctype,
- &state->session_key))) {
- state->status = "MAKE_RANDOM_KEY";
+ &state->session_key)))
goto errout;
- }
/*
* Canonicalization is only effective if we are issuing a TGT
@@ -785,10 +771,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
state->request->client = NULL;
errcode = krb5_copy_principal(kdc_context, krb5_anonymous_principal(),
&state->request->client);
- if (errcode) {
- state->status = "COPY_ANONYMOUS_PRINCIPAL";
+ if (errcode)
goto errout;
- }
state->enc_tkt_reply.client = state->request->client;
setflag(state->client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH);
}
@@ -841,6 +825,8 @@ prepare_error_as(struct kdc_request_state *rstate, krb5_kdc_req *request,
kdc_realm_t *kdc_active_realm = rstate->realm_data;
size_t count;
+ errpkt.magic = KV5M_ERROR;
+
if (e_data_in != NULL) {
/* Add a PA-FX-COOKIE to e_data_in. e_data is a shallow copy
* containing aliases. */
@@ -854,7 +840,7 @@ prepare_error_as(struct kdc_request_state *rstate, krb5_kdc_req *request,
e_data[count] = cookie;
}
- errpkt.ctime = request->nonce;
+ errpkt.ctime = 0;
errpkt.cusec = 0;
retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 547a41441767..cc5a6923629a 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -195,15 +195,12 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
if (!header_ticket) {
errcode = KRB5_NO_TKT_SUPPLIED; /* XXX? */
- status="UNEXPECTED NULL in header_ticket";
goto cleanup;
}
errcode = kau_make_tkt_id(kdc_context, header_ticket,
&au_state->tkt_in_id);
- if (errcode) {
- status = "GENERATE_TICKET_ID";
+ if (errcode)
goto cleanup;
- }
scratch.length = pa_tgs_req->length;
scratch.data = (char *) pa_tgs_req->contents;
@@ -264,16 +261,12 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
au_state->stage = VALIDATE_POL;
- if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
- status = "TIME_OF_DAY";
+ if ((errcode = krb5_timeofday(kdc_context, &kdc_time)))
goto cleanup;
- }
if ((retval = validate_tgs_request(kdc_active_realm,
request, *server, header_ticket,
kdc_time, &status, &e_data))) {
- if (!status)
- status = "UNKNOWN_REASON";
if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION)
au_state->violation = PROT_CONSTRAINT;
errcode = retval + ERROR_TABLE_BASE_krb5;
@@ -340,7 +333,6 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
retval = kau_make_tkt_id(kdc_context, request->second_ticket[st_idx],
&au_state->evid_tkt_id);
if (retval) {
- status = "GENERATE_TICKET_ID";
errcode = retval;
goto cleanup;
}
@@ -500,12 +492,12 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
old_starttime = enc_tkt_reply.times.starttime ?
enc_tkt_reply.times.starttime : enc_tkt_reply.times.authtime;
- old_life = enc_tkt_reply.times.endtime - old_starttime;
+ old_life = ts_delta(enc_tkt_reply.times.endtime, old_starttime);
enc_tkt_reply.times.starttime = kdc_time;
enc_tkt_reply.times.endtime =
- min(header_ticket->enc_part2->times.renew_till,
- kdc_time + old_life);
+ ts_min(header_ticket->enc_part2->times.renew_till,
+ ts_incr(kdc_time, old_life));
} else {
/* not a renew request */
enc_tkt_reply.times.starttime = kdc_time;
@@ -518,6 +510,12 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
kdc_get_ticket_renewtime(kdc_active_realm, request, header_enc_tkt, client,
server, &enc_tkt_reply);
+ errcode = check_kdcpolicy_tgs(kdc_context, request, server, header_ticket,
+ auth_indicators, kdc_time,
+ &enc_tkt_reply.times, &status);
+ if (errcode)
+ goto cleanup;
+
/*
* Set authtime to be the same as header or evidence ticket's
*/
@@ -723,10 +721,8 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
&ticket_reply);
if (!isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY))
krb5_free_keyblock_contents(kdc_context, &encrypting_key);
- if (errcode) {
- status = "ENCRYPT_TICKET";
+ if (errcode)
goto cleanup;
- }
ticket_reply.enc_part.kvno = ticket_kvno;
/* Start assembling the response */
au_state->stage = ENCR_REP;
@@ -740,10 +736,8 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
s4u_x509_user,
&reply,
&reply_encpart);
- if (errcode) {
- status = "MAKE_S4U2SELF_PADATA";
+ if (errcode)
au_state->status = status;
- }
kau_s4u2self(kdc_context, errcode ? FALSE : TRUE, au_state);
if (errcode)
goto cleanup;
@@ -775,16 +769,12 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
header_ticket->enc_part2->session->enctype;
errcode = kdc_fast_response_handle_padata(state, request, &reply,
subkey ? subkey->enctype : header_ticket->enc_part2->session->enctype);
- if (errcode !=0 ) {
- status = "MAKE_FAST_RESPONSE";
+ if (errcode)
goto cleanup;
- }
errcode =kdc_fast_handle_reply_key(state,
subkey?subkey:header_ticket->enc_part2->session, &reply_key);
- if (errcode) {
- status = "MAKE_FAST_REPLY_KEY";
+ if (errcode)
goto cleanup;
- }
errcode = return_enc_padata(kdc_context, pkt, request,
reply_key, server, &reply_encpart,
is_referral &&
@@ -796,10 +786,8 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
}
errcode = kau_make_tkt_id(kdc_context, &ticket_reply, &au_state->tkt_out_id);
- if (errcode) {
- status = "GENERATE_TICKET_ID";
+ if (errcode)
goto cleanup;
- }
if (kdc_fast_hide_client(state))
reply.client = (krb5_principal)krb5_anonymous_principal();
@@ -807,11 +795,8 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
subkey ? 1 : 0,
reply_key,
&reply, response);
- if (errcode) {
- status = "ENCODE_KDC_REP";
- } else {
+ if (!errcode)
status = "ISSUE";
- }
memset(ticket_reply.enc_part.ciphertext.data, 0,
ticket_reply.enc_part.ciphertext.length);
@@ -823,7 +808,8 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
free(reply.enc_part.ciphertext.data);
cleanup:
- assert(status != NULL);
+ if (status == NULL)
+ status = "UNKNOWN_REASON";
if (reply_key)
krb5_free_keyblock(kdc_context, reply_key);
if (errcode)
@@ -909,7 +895,8 @@ prepare_error_tgs (struct kdc_request_state *state,
krb5_data *scratch, *e_data_asn1 = NULL, *fast_edata = NULL;
kdc_realm_t *kdc_active_realm = state->realm_data;
- errpkt.ctime = request->nonce;
+ errpkt.magic = KV5M_ERROR;
+ errpkt.ctime = 0;
errpkt.cusec = 0;
if ((retval = krb5_us_timeofday(kdc_context, &errpkt.stime,
@@ -1052,7 +1039,7 @@ gen_session_key(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
retval = get_2ndtkt_enctype(kdc_active_realm, req, &useenctype,
status);
if (retval != 0)
- goto cleanup;
+ return retval;
}
if (useenctype == 0) {
useenctype = select_session_keytype(kdc_active_realm, server,
@@ -1062,17 +1049,10 @@ gen_session_key(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
if (useenctype == 0) {
/* unsupported ktype */
*status = "BAD_ENCRYPTION_TYPE";
- retval = KRB5KDC_ERR_ETYPE_NOSUPP;
- goto cleanup;
- }
- retval = krb5_c_make_random_key(kdc_context, useenctype, skey);
- if (retval != 0) {
- /* random key failed */
- *status = "MAKE_RANDOM_KEY";
- goto cleanup;
+ return KRB5KDC_ERR_ETYPE_NOSUPP;
}
-cleanup:
- return retval;
+
+ return krb5_c_make_random_key(kdc_context, useenctype, skey);
}
/*
diff --git a/src/kdc/extern.c b/src/kdc/extern.c
index fe627494b8d3..84b5c6ad5d1c 100644
--- a/src/kdc/extern.c
+++ b/src/kdc/extern.c
@@ -37,6 +37,8 @@
kdc_realm_t **kdc_realmlist = (kdc_realm_t **) NULL;
int kdc_numrealms = 0;
krb5_data empty_string = {0, 0, ""};
-krb5_timestamp kdc_infinity = KRB5_INT32_MAX; /* XXX */
krb5_keyblock psr_key;
krb5_int32 max_dgram_reply_size = MAX_DGRAM_SIZE;
+
+/* With ts_after(), this is the largest timestamp value. */
+krb5_timestamp kdc_infinity = -1;
diff --git a/src/kdc/fast_util.c b/src/kdc/fast_util.c
index 9df940219cd8..e05107ef328d 100644
--- a/src/kdc/fast_util.c
+++ b/src/kdc/fast_util.c
@@ -607,7 +607,7 @@ kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
ret = krb5_timeofday(context, &now);
if (ret)
goto cleanup;
- if (now - COOKIE_LIFETIME > cookie->time) {
+ if (ts2tt(now) > cookie->time + COOKIE_LIFETIME) {
/* Don't accept the cookie contents. Only return an error if the
* cookie is relevant to the request. */
if (is_relevant(cookie->data, req->padata))
@@ -700,7 +700,7 @@ kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
ret = krb5_timeofday(context, &now);
if (ret)
goto cleanup;
- cookie.time = now;
+ cookie.time = ts2tt(now);
cookie.data = contents;
ret = encode_krb5_secure_cookie(&cookie, &der_cookie);
if (ret)
diff --git a/src/kdc/kdc_log.c b/src/kdc/kdc_log.c
index 94a2a1c87c91..7e8733980a41 100644
--- a/src/kdc/kdc_log.c
+++ b/src/kdc/kdc_log.c
@@ -54,7 +54,9 @@
/* Someday, pass local address/port as well. */
/* Currently no info about name canonicalization is logged. */
void
-log_as_req(krb5_context context, const krb5_fulladdr *from,
+log_as_req(krb5_context context,
+ const krb5_fulladdr *local_addr,
+ const krb5_fulladdr *remote_addr,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_db_entry *client, const char *cname,
krb5_db_entry *server, const char *sname,
@@ -67,8 +69,8 @@ log_as_req(krb5_context context, const krb5_fulladdr *from,
const char *cname2 = cname ? cname : "<unknown client>";
const char *sname2 = sname ? sname : "<unknown server>";
- fromstring = inet_ntop(ADDRTYPE2FAMILY (from->address->addrtype),
- from->address->contents,
+ fromstring = inet_ntop(ADDRTYPE2FAMILY(remote_addr->address->addrtype),
+ remote_addr->address->contents,
fromstringbuf, sizeof(fromstringbuf));
if (!fromstring)
fromstring = "<unknown>";
@@ -79,9 +81,9 @@ log_as_req(krb5_context context, const krb5_fulladdr *from,
/* success */
char rep_etypestr[128];
rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), reply);
- krb5_klog_syslog(LOG_INFO, _("AS_REQ (%s) %s: ISSUE: authtime %d, %s, "
+ krb5_klog_syslog(LOG_INFO, _("AS_REQ (%s) %s: ISSUE: authtime %u, %s, "
"%s for %s"),
- ktypestr, fromstring, authtime,
+ ktypestr, fromstring, (unsigned int)authtime,
rep_etypestr, cname2, sname2);
} else {
/* fail */
@@ -89,14 +91,15 @@ log_as_req(krb5_context context, const krb5_fulladdr *from,
ktypestr, fromstring, status,
cname2, sname2, emsg ? ", " : "", emsg ? emsg : "");
}
- krb5_db_audit_as_req(context, request, client, server, authtime,
- errcode);
+ krb5_db_audit_as_req(context, request,
+ local_addr->address, remote_addr->address,
+ client, server, authtime, errcode);
#if 0
/* Sun (OpenSolaris) version would probably something like this.
The client and server names passed can be null, unlike in the
logging routines used above. Note that a struct in_addr is
used, but the real address could be an IPv6 address. */
- audit_krb5kdc_as_req(some in_addr *, (in_port_t)from->port, 0,
+ audit_krb5kdc_as_req(some in_addr *, (in_port_t)remote_addr->port, 0,
cname, sname, errcode);
#endif
}
@@ -156,10 +159,10 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from,
name (useful), and doesn't log ktypestr (probably not
important). */
if (errcode != KRB5KDC_ERR_SERVER_NOMATCH) {
- krb5_klog_syslog(LOG_INFO, _("TGS_REQ (%s) %s: %s: authtime %d, %s%s "
+ krb5_klog_syslog(LOG_INFO, _("TGS_REQ (%s) %s: %s: authtime %u, %s%s "
"%s for %s%s%s"),
- ktypestr, fromstring, status, authtime, rep_etypestr,
- !errcode ? "," : "", logcname, logsname,
+ ktypestr, fromstring, status, (unsigned int)authtime,
+ rep_etypestr, !errcode ? "," : "", logcname, logsname,
errcode ? ", " : "", errcode ? emsg : "");
if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
krb5_klog_syslog(LOG_INFO,
@@ -171,9 +174,9 @@ log_tgs_req(krb5_context ctx, const krb5_fulladdr *from,
logaltcname);
} else
- krb5_klog_syslog(LOG_INFO, _("TGS_REQ %s: %s: authtime %d, %s for %s, "
+ krb5_klog_syslog(LOG_INFO, _("TGS_REQ %s: %s: authtime %u, %s for %s, "
"2nd tkt client %s"),
- fromstring, status, authtime,
+ fromstring, status, (unsigned int)authtime,
logcname, logsname, logaltcname);
/* OpenSolaris: audit_krb5kdc_tgs_req(...) or
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index 605fcb7addc6..81d0b8cffd39 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -568,8 +568,37 @@ set_cookie(krb5_context context, krb5_kdcpreauth_rock rock,
return kdc_fast_set_cookie(rock->rstate, pa_type, data);
}
+static krb5_boolean
+match_client(krb5_context context, krb5_kdcpreauth_rock rock,
+ krb5_principal princ)
+{
+ krb5_db_entry *ent;
+ krb5_boolean match = FALSE;
+ krb5_principal req_client = rock->request->client;
+ krb5_principal client = rock->client->princ;
+
+ /* Check for a direct match against the request principal or
+ * the post-canon client principal. */
+ if (krb5_principal_compare_flags(context, princ, req_client,
+ KRB5_PRINCIPAL_COMPARE_ENTERPRISE) ||
+ krb5_principal_compare(context, princ, client))
+ return TRUE;
+
+ if (krb5_db_get_principal(context, princ, KRB5_KDB_FLAG_ALIAS_OK, &ent))
+ return FALSE;
+ match = krb5_principal_compare(context, ent->princ, client);
+ krb5_db_free_principal(context, ent);
+ return match;
+}
+
+static krb5_principal
+client_name(krb5_context context, krb5_kdcpreauth_rock rock)
+{
+ return rock->client->princ;
+}
+
static struct krb5_kdcpreauth_callbacks_st callbacks = {
- 3,
+ 4,
max_time_skew,
client_keys,
free_keys,
@@ -583,7 +612,9 @@ static struct krb5_kdcpreauth_callbacks_st callbacks = {
client_keyblock,
add_auth_indicator,
get_cookie,
- set_cookie
+ set_cookie,
+ match_client,
+ client_name
};
static krb5_error_code
diff --git a/src/kdc/kdc_preauth_ec.c b/src/kdc/kdc_preauth_ec.c
index feef3683141c..7e636b3f9fed 100644
--- a/src/kdc/kdc_preauth_ec.c
+++ b/src/kdc/kdc_preauth_ec.c
@@ -56,7 +56,6 @@ ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_kdcpreauth_verify_respond_fn respond, void *arg)
{
krb5_error_code retval = 0;
- krb5_timestamp now;
krb5_enc_data *enc = NULL;
krb5_data scratch, plain;
krb5_keyblock *armor_key = cb->fast_armor(context, rock);
@@ -66,6 +65,8 @@ ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_keyblock *kdc_challenge_key;
krb5_kdcpreauth_modreq modreq = NULL;
int i = 0;
+ char *ai = NULL, *realmstr = NULL;
+ krb5_data realm = request->server->realm;
plain.data = NULL;
@@ -84,6 +85,15 @@ ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
if (plain.data == NULL)
retval = ENOMEM;
}
+
+ /* Check for a configured FAST ec auth indicator. */
+ realmstr = k5memdup0(realm.data, realm.length, &retval);
+ if (realmstr != NULL)
+ retval = profile_get_string(context->profile, KRB5_CONF_REALMS,
+ realmstr,
+ KRB5_CONF_ENCRYPTED_CHALLENGE_INDICATOR,
+ NULL, &ai);
+
if (retval == 0)
retval = cb->client_keys(context, rock, &client_keys);
if (retval == 0) {
@@ -113,21 +123,20 @@ ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
if (retval == 0)
retval = decode_krb5_pa_enc_ts(&plain, &ts);
if (retval == 0)
- retval = krb5_timeofday(context, &now);
+ retval = krb5_check_clockskew(context, ts->patimestamp);
if (retval == 0) {
- if (labs(now-ts->patimestamp) < context->clockskew) {
- enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
- /*
- * If this fails, we won't generate a reply to the client. That
- * may cause the client to fail, but at this point the KDC has
- * considered this a success, so the return value is ignored.
- */
- if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor",
- &client_keys[i], "challengelongterm",
- &kdc_challenge_key) == 0)
- modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key;
- } else { /*skew*/
- retval = KRB5KRB_AP_ERR_SKEW;
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ /*
+ * If this fails, we won't generate a reply to the client. That may
+ * cause the client to fail, but at this point the KDC has considered
+ * this a success, so the return value is ignored.
+ */
+ if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor",
+ &client_keys[i], "challengelongterm",
+ &kdc_challenge_key) == 0) {
+ modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key;
+ if (ai != NULL)
+ cb->add_auth_indicator(context, rock, ai);
}
}
cb->free_keys(context, rock, client_keys);
@@ -137,6 +146,8 @@ ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_free_enc_data(context, enc);
if (ts)
krb5_free_pa_enc_ts(context, ts);
+ free(realmstr);
+ free(ai);
(*respond)(arg, retval, modreq, NULL, NULL);
}
diff --git a/src/kdc/kdc_preauth_encts.c b/src/kdc/kdc_preauth_encts.c
index e80dc12a8cf3..25fc784576ef 100644
--- a/src/kdc/kdc_preauth_encts.c
+++ b/src/kdc/kdc_preauth_encts.c
@@ -58,7 +58,6 @@ enc_ts_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_keyblock key;
krb5_key_data * client_key;
krb5_int32 start;
- krb5_timestamp timenow;
scratch.data = (char *)pa->contents;
scratch.length = pa->length;
@@ -95,14 +94,10 @@ enc_ts_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
goto cleanup;
- if ((retval = krb5_timeofday(context, &timenow)) != 0)
+ retval = krb5_check_clockskew(context, pa_enc->patimestamp);
+ if (retval)
goto cleanup;
- if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
- retval = KRB5KRB_AP_ERR_SKEW;
- goto cleanup;
- }
-
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
retval = 0;
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 29f9dbbf07eb..754570c01310 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -642,7 +642,6 @@ validate_as_request(kdc_realm_t *kdc_active_realm,
krb5_db_entry server, krb5_timestamp kdc_time,
const char **status, krb5_pa_data ***e_data)
{
- int errcode;
krb5_error_code ret;
/*
@@ -654,7 +653,7 @@ validate_as_request(kdc_realm_t *kdc_active_realm,
}
/* The client must not be expired */
- if (client.expiration && client.expiration < kdc_time) {
+ if (client.expiration && ts_after(kdc_time, client.expiration)) {
*status = "CLIENT EXPIRED";
if (vague_errors)
return(KRB_ERR_GENERIC);
@@ -664,7 +663,7 @@ validate_as_request(kdc_realm_t *kdc_active_realm,
/* The client's password must not be expired, unless the server is
a KRB5_KDC_PWCHANGE_SERVICE. */
- if (client.pw_expiration && client.pw_expiration < kdc_time &&
+ if (client.pw_expiration && ts_after(kdc_time, client.pw_expiration) &&
!isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) {
*status = "CLIENT KEY EXPIRED";
if (vague_errors)
@@ -674,7 +673,7 @@ validate_as_request(kdc_realm_t *kdc_active_realm,
}
/* The server must not be expired */
- if (server.expiration && server.expiration < kdc_time) {
+ if (server.expiration && ts_after(kdc_time, server.expiration)) {
*status = "SERVICE EXPIRED";
return(KDC_ERR_SERVICE_EXP);
}
@@ -750,12 +749,6 @@ validate_as_request(kdc_realm_t *kdc_active_realm,
if (ret && ret != KRB5_PLUGIN_OP_NOTSUPP)
return errcode_to_protocol(ret);
- /* Check against local policy. */
- errcode = against_local_policy_as(request, client, server,
- kdc_time, status, e_data);
- if (errcode)
- return errcode;
-
return 0;
}
@@ -1220,8 +1213,10 @@ kdc_process_for_user(kdc_realm_t *kdc_active_realm,
req_data.data = (char *)pa_data->contents;
code = decode_krb5_pa_for_user(&req_data, &for_user);
- if (code)
+ if (code) {
+ *status = "DECODE_PA_FOR_USER";
return code;
+ }
code = verify_for_user_checksum(kdc_context, tgs_session, for_user);
if (code) {
@@ -1320,8 +1315,10 @@ kdc_process_s4u_x509_user(krb5_context context,
req_data.data = (char *)pa_data->contents;
code = decode_krb5_pa_s4u_x509_user(&req_data, s4u_x509_user);
- if (code)
+ if (code) {
+ *status = "DECODE_PA_S4U_X509_USER";
return code;
+ }
code = verify_s4u_x509_user_checksum(context,
tgs_subkey ? tgs_subkey :
@@ -1624,6 +1621,7 @@ kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm,
* that is validated previously in validate_tgs_request().
*/
if (request->kdc_options & (NON_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) {
+ *status = "INVALID_S4U2PROXY_OPTIONS";
return KRB5KDC_ERR_BADOPTION;
}
@@ -1631,6 +1629,7 @@ kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm,
if (!krb5_principal_compare(kdc_context,
server->princ, /* after canon */
server_princ)) {
+ *status = "EVIDENCE_TICKET_MISMATCH";
return KRB5KDC_ERR_SERVER_NOMATCH;
}
@@ -1760,14 +1759,19 @@ kdc_get_ticket_endtime(kdc_realm_t *kdc_active_realm,
krb5_db_entry *server,
krb5_timestamp *out_endtime)
{
- krb5_timestamp until, life;
+ krb5_timestamp until;
+ krb5_deltat life;
if (till == 0)
till = kdc_infinity;
- until = min(till, endtime);
+ until = ts_min(till, endtime);
- life = until - starttime;
+ /* Determine the requested lifetime, capped at the maximum valid time
+ * interval. */
+ life = ts_delta(until, starttime);
+ if (ts_after(until, starttime) && life < 0)
+ life = INT32_MAX;
if (client != NULL && client->max_life != 0)
life = min(life, client->max_life);
@@ -1776,7 +1780,7 @@ kdc_get_ticket_endtime(kdc_realm_t *kdc_active_realm,
if (kdc_active_realm->realm_maxlife != 0)
life = min(life, kdc_active_realm->realm_maxlife);
- *out_endtime = starttime + life;
+ *out_endtime = ts_incr(starttime, life);
}
/*
@@ -1791,6 +1795,7 @@ kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request,
{
krb5_timestamp rtime, max_rlife;
+ clear(tkt->flags, TKT_FLG_RENEWABLE);
tkt->times.renew_till = 0;
/* Don't issue renewable tickets if the client or server don't allow it,
@@ -1806,25 +1811,27 @@ kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request,
if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE))
rtime = request->rtime ? request->rtime : kdc_infinity;
else if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
- tkt->times.endtime < request->till)
+ ts_after(request->till, tkt->times.endtime))
rtime = request->till;
else
return;
/* Truncate it to the allowable renewable time. */
if (tgt != NULL)
- rtime = min(rtime, tgt->times.renew_till);
+ rtime = ts_min(rtime, tgt->times.renew_till);
max_rlife = min(server->max_renewable_life, realm->realm_maxrlife);
if (client != NULL)
max_rlife = min(max_rlife, client->max_renewable_life);
- rtime = min(rtime, tkt->times.starttime + max_rlife);
+ rtime = ts_min(rtime, ts_incr(tkt->times.starttime, max_rlife));
- /* Make the ticket renewable if the truncated requested time is larger than
- * the ticket end time. */
- if (rtime > tkt->times.endtime) {
- setflag(tkt->flags, TKT_FLG_RENEWABLE);
- tkt->times.renew_till = rtime;
- }
+ /* If the client only specified renewable-ok, don't issue a renewable
+ * ticket unless the truncated renew time exceeds the ticket end time. */
+ if (!isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
+ !ts_after(rtime, tkt->times.endtime))
+ return;
+
+ setflag(tkt->flags, TKT_FLG_RENEWABLE);
+ tkt->times.renew_till = rtime;
}
/**
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index bcf05fc2779f..f99efcf505d1 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -140,7 +140,7 @@ cammac_check_kdcver(krb5_context context, krb5_cammac *cammac,
/* do_as_req.c */
void
process_as_req (krb5_kdc_req *, krb5_data *,
- const krb5_fulladdr *, kdc_realm_t *,
+ const krb5_fulladdr *, const krb5_fulladdr *, kdc_realm_t *,
verto_ctx *, loop_respond_fn, void *);
/* do_tgs_req.c */
@@ -151,7 +151,7 @@ process_tgs_req (struct server_handle *, krb5_data *,
/* dispatch.c */
void
dispatch (void *,
- struct sockaddr *,
+ const krb5_fulladdr *,
const krb5_fulladdr *,
krb5_data *,
int,
@@ -166,17 +166,6 @@ kdc_err(krb5_context call_context, errcode_t code, const char *fmt, ...)
#endif
;
-/* policy.c */
-int
-against_local_policy_as (krb5_kdc_req *, krb5_db_entry,
- krb5_db_entry, krb5_timestamp,
- const char **, krb5_pa_data ***);
-
-int
-against_local_policy_tgs (krb5_kdc_req *, krb5_db_entry,
- krb5_ticket *, const char **,
- krb5_pa_data ***);
-
/* kdc_preauth.c */
krb5_boolean
enctype_requires_etype_info_2(krb5_enctype enctype);
@@ -346,7 +335,9 @@ kdc_get_ticket_renewtime(kdc_realm_t *realm, krb5_kdc_req *request,
krb5_db_entry *server, krb5_enc_tkt_part *tkt);
void
-log_as_req(krb5_context context, const krb5_fulladdr *from,
+log_as_req(krb5_context context,
+ const krb5_fulladdr *local_addr,
+ const krb5_fulladdr *remote_addr,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_db_entry *client, const char *cname,
krb5_db_entry *server, const char *sname,
@@ -452,6 +443,8 @@ struct krb5_kdcpreauth_rock_st {
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
+#define ts_min(a, b) (ts_after(a, b) ? (b) : (a))
+
#define ADDRTYPE2FAMILY(X) \
((X) == ADDRTYPE_INET6 ? AF_INET6 : (X) == ADDRTYPE_INET ? AF_INET : -1)
diff --git a/src/kdc/main.c b/src/kdc/main.c
index ebc852bba2e9..f2226da2597f 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -31,6 +31,7 @@
#include "kdc_util.h"
#include "kdc_audit.h"
#include "extern.h"
+#include "policy.h"
#include "kdc5_err.h"
#include "kdb_kt.h"
#include "net-server.h"
@@ -986,6 +987,12 @@ int main(int argc, char **argv)
load_preauth_plugins(&shandle, kcontext, ctx);
load_authdata_plugins(kcontext);
+ retval = load_kdcpolicy_plugins(kcontext);
+ if (retval) {
+ kdc_err(kcontext, retval, _("while loading KDC policy plugin"));
+ finish_realms();
+ return 1;
+ }
retval = setup_sam();
if (retval) {
@@ -1068,6 +1075,7 @@ int main(int argc, char **argv)
krb5_klog_syslog(LOG_INFO, _("shutting down"));
unload_preauth_plugins(kcontext);
unload_authdata_plugins(kcontext);
+ unload_kdcpolicy_plugins(kcontext);
unload_audit_modules(kcontext);
krb5_klog_close(kcontext);
finish_realms();
diff --git a/src/kdc/policy.c b/src/kdc/policy.c
index 6cba4303f81d..26c16f97cb53 100644
--- a/src/kdc/policy.c
+++ b/src/kdc/policy.c
@@ -1,67 +1,246 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* kdc/policy.c - Policy decision routines for KDC */
/*
- * Copyright 1990 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2017 by Red Hat, Inc.
+ * All rights reserved.
*
- * Export of this software from the United States of America may
- * require a specific license from the United States Government.
- * It is the responsibility of any person or organization contemplating
- * export to obtain such a license before exporting.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission. Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose. It is provided "as is" without express
- * or implied warranty.
+ * * 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"
#include "extern.h"
+#include "policy.h"
+#include "adm_proto.h"
+#include <krb5/kdcpolicy_plugin.h>
+#include <syslog.h>
+
+typedef struct kdcpolicy_handle_st {
+ struct krb5_kdcpolicy_vtable_st vt;
+ krb5_kdcpolicy_moddata moddata;
+} *kdcpolicy_handle;
+
+static kdcpolicy_handle *handles;
+
+static void
+free_indicators(char **ais)
+{
+ size_t i;
-int
-against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client,
- krb5_db_entry server, krb5_timestamp kdc_time,
- const char **status, krb5_pa_data ***e_data)
+ if (ais == NULL)
+ return;
+ for (i = 0; ais[i] != NULL; i++)
+ free(ais[i]);
+ free(ais);
+}
+
+/* Convert inds to a null-terminated list of C strings. */
+static krb5_error_code
+authind_strings(krb5_data *const *inds, char ***strs_out)
{
-#if 0
- /* An AS request must include the addresses field */
- if (request->addresses == 0) {
- *status = "NO ADDRESS";
- return KRB5KDC_ERR_POLICY;
+ krb5_error_code ret;
+ char **list = NULL;
+ size_t i, count;
+
+ *strs_out = NULL;
+
+ for (count = 0; inds != NULL && inds[count] != NULL; count++);
+ list = k5calloc(count + 1, sizeof(*list), &ret);
+ if (list == NULL)
+ goto error;
+
+ for (i = 0; i < count; i++) {
+ list[i] = k5memdup0(inds[i]->data, inds[i]->length, &ret);
+ if (list[i] == NULL)
+ goto error;
}
-#endif
- return 0; /* not against policy */
+ *strs_out = list;
+ return 0;
+
+error:
+ free_indicators(list);
+ return ret;
+}
+
+/* Constrain times->endtime to life and times->renew_till to rlife, relative to
+ * now. */
+static void
+update_ticket_times(krb5_ticket_times *times, krb5_timestamp now,
+ krb5_deltat life, krb5_deltat rlife)
+{
+ if (life)
+ times->endtime = ts_min(ts_incr(now, life), times->endtime);
+ if (rlife)
+ times->renew_till = ts_min(ts_incr(now, rlife), times->renew_till);
+}
+
+/* Check an AS request against kdcpolicy modules, updating times with any
+ * module endtime constraints. Set an appropriate status string on error. */
+krb5_error_code
+check_kdcpolicy_as(krb5_context context, const krb5_kdc_req *request,
+ const krb5_db_entry *client, const krb5_db_entry *server,
+ krb5_data *const *auth_indicators, krb5_timestamp kdc_time,
+ krb5_ticket_times *times, const char **status)
+{
+ krb5_deltat life, rlife;
+ krb5_error_code ret;
+ kdcpolicy_handle *hp, h;
+ char **ais = NULL;
+
+ *status = NULL;
+
+ ret = authind_strings(auth_indicators, &ais);
+ if (ret)
+ goto done;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ h = *hp;
+ if (h->vt.check_as == NULL)
+ continue;
+
+ ret = h->vt.check_as(context, h->moddata, request, client, server,
+ (const char **)ais, status, &life, &rlife);
+ if (ret)
+ goto done;
+
+ update_ticket_times(times, kdc_time, life, rlife);
+ }
+
+done:
+ free_indicators(ais);
+ return ret;
}
/*
- * This is where local policy restrictions for the TGS should placed.
+ * Check the TGS request against the local TGS policy. Accepts an
+ * authentication indicator for the module policy decisions. Returns 0 and a
+ * NULL status string on success.
*/
krb5_error_code
-against_local_policy_tgs(register krb5_kdc_req *request, krb5_db_entry server,
- krb5_ticket *ticket, const char **status,
- krb5_pa_data ***e_data)
+check_kdcpolicy_tgs(krb5_context context, const krb5_kdc_req *request,
+ const krb5_db_entry *server, const krb5_ticket *ticket,
+ krb5_data *const *auth_indicators, krb5_timestamp kdc_time,
+ krb5_ticket_times *times, const char **status)
{
-#if 0
- /*
- * For example, if your site wants to disallow ticket forwarding,
- * you might do something like this:
- */
-
- if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
- *status = "FORWARD POLICY";
- return KRB5KDC_ERR_POLICY;
+ krb5_deltat life, rlife;
+ krb5_error_code ret;
+ kdcpolicy_handle *hp, h;
+ char **ais = NULL;
+
+ *status = NULL;
+
+ ret = authind_strings(auth_indicators, &ais);
+ if (ret)
+ goto done;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ h = *hp;
+ if (h->vt.check_tgs == NULL)
+ continue;
+
+ ret = h->vt.check_tgs(context, h->moddata, request, server, ticket,
+ (const char **)ais, status, &life, &rlife);
+ if (ret)
+ goto done;
+
+ update_ticket_times(times, kdc_time, life, rlife);
}
-#endif
- return 0; /* not against policy */
+done:
+ free_indicators(ais);
+ return ret;
+}
+
+void
+unload_kdcpolicy_plugins(krb5_context context)
+{
+ kdcpolicy_handle *hp, h;
+
+ for (hp = handles; *hp != NULL; hp++) {
+ h = *hp;
+ if (h->vt.fini != NULL)
+ h->vt.fini(context, h->moddata);
+ free(h);
+ }
+ free(handles);
+ handles = NULL;
+}
+
+krb5_error_code
+load_kdcpolicy_plugins(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_plugin_initvt_fn *modules = NULL, *mod;
+ kdcpolicy_handle h;
+ size_t count;
+
+ ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KDCPOLICY, &modules);
+ if (ret)
+ goto cleanup;
+
+ for (count = 0; modules[count] != NULL; count++);
+ handles = k5calloc(count + 1, sizeof(*handles), &ret);
+ if (handles == NULL)
+ goto cleanup;
+
+ count = 0;
+ for (mod = modules; *mod != NULL; mod++) {
+ h = k5calloc(1, sizeof(*h), &ret);
+ if (h == NULL)
+ goto cleanup;
+
+ ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt);
+ if (ret) { /* Version mismatch. */
+ TRACE_KDCPOLICY_VTINIT_FAIL(context, ret);
+ free(h);
+ continue;
+ }
+ if (h->vt.init != NULL) {
+ ret = h->vt.init(context, &h->moddata);
+ if (ret == KRB5_PLUGIN_NO_HANDLE) {
+ TRACE_KDCPOLICY_INIT_SKIP(context, h->vt.name);
+ free(h);
+ continue;
+ }
+ if (ret) {
+ kdc_err(context, ret, _("while loading policy module %s"),
+ h->vt.name);
+ free(h);
+ goto cleanup;
+ }
+ }
+ handles[count++] = h;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (ret)
+ unload_kdcpolicy_plugins(context);
+ k5_plugin_free_modules(context, modules);
+ return ret;
}
diff --git a/src/kdc/policy.h b/src/kdc/policy.h
index 6b000dc90346..2a57b0a01683 100644
--- a/src/kdc/policy.h
+++ b/src/kdc/policy.h
@@ -26,11 +26,22 @@
#ifndef __KRB5_KDC_POLICY__
#define __KRB5_KDC_POLICY__
-extern int against_postdate_policy (krb5_timestamp);
+krb5_error_code
+load_kdcpolicy_plugins(krb5_context context);
-extern int against_flag_policy_as (const krb5_kdc_req *);
+void
+unload_kdcpolicy_plugins(krb5_context context);
-extern int against_flag_policy_tgs (const krb5_kdc_req *,
- const krb5_ticket *);
+krb5_error_code
+check_kdcpolicy_as(krb5_context context, const krb5_kdc_req *request,
+ const krb5_db_entry *client, const krb5_db_entry *server,
+ krb5_data *const *auth_indicators, krb5_timestamp kdc_time,
+ krb5_ticket_times *times, const char **status);
+
+krb5_error_code
+check_kdcpolicy_tgs(krb5_context context, const krb5_kdc_req *request,
+ const krb5_db_entry *server, const krb5_ticket *ticket,
+ krb5_data *const *auth_indicators, krb5_timestamp kdc_time,
+ krb5_ticket_times *times, const char **status);
#endif /* __KRB5_KDC_POLICY__ */
diff --git a/src/kdc/replay.c b/src/kdc/replay.c
index 8da7ac19aef3..caca783bf1ab 100644
--- a/src/kdc/replay.c
+++ b/src/kdc/replay.c
@@ -61,7 +61,7 @@ static size_t total_size = 0;
static krb5_ui_4 seed;
#define STALE_TIME (2*60) /* two minutes */
-#define STALE(ptr, now) (abs((ptr)->timein - (now)) >= STALE_TIME)
+#define STALE(ptr, now) (ts_after(now, ts_incr((ptr)->timein, STALE_TIME)))
/* Return x rotated to the left by r bits. */
static inline krb5_ui_4
diff --git a/src/kdc/t_emptytgt.py b/src/kdc/t_emptytgt.py
index 8f7717a011c2..2d0432e338ac 100755
--- a/src/kdc/t_emptytgt.py
+++ b/src/kdc/t_emptytgt.py
@@ -2,7 +2,6 @@
from k5test import *
realm = K5Realm(create_host=False)
-output = realm.run([kvno, 'krbtgt/'], expected_code=1)
-if 'not found in Kerberos database' not in output:
- fail('TGT lookup for empty realm failed in unexpected way')
+realm.run([kvno, 'krbtgt/'], expected_code=1,
+ expected_msg='not found in Kerberos database')
success('Empty tgt lookup.')
diff --git a/src/kdc/t_replay.c b/src/kdc/t_replay.c
index 1442e0e8ceee..00bb39012680 100644
--- a/src/kdc/t_replay.c
+++ b/src/kdc/t_replay.c
@@ -36,10 +36,7 @@
#ifndef NOCACHE
-#include <stdarg.h>
-#include <stddef.h>
-#include <setjmp.h>
-#include <cmocka.h>
+#include "k5-cmocka.h"
/* For wrapping functions */
#include "k5-int.h"
@@ -623,6 +620,8 @@ test_kdc_check_lookaside_hit(void **state)
assert_true(data_eq(rep, *result_data));
assert_int_equal(hits, 1);
assert_int_equal(e->num_hits, 1);
+
+ krb5_free_data(context, result_data);
}
static void
@@ -700,6 +699,8 @@ test_kdc_check_lookaside_hit_multiple(void **state)
assert_int_equal(e1->num_hits, 1);
assert_int_equal(e2->num_hits, 0);
+ krb5_free_data(context, result_data);
+
/* Set result_data so we can verify that it is reset to NULL. */
result_data = &req1;
result = kdc_check_lookaside(context, &req2, &result_data);
@@ -733,6 +734,8 @@ test_kdc_check_lookaside_hit_hash_collision(void **state)
assert_int_equal(e1->num_hits, 1);
assert_int_equal(e2->num_hits, 0);
+ krb5_free_data(context, result_data);
+
/* Set result_data so we can verify that it is reset to NULL. */
result_data = &req1;
result = kdc_check_lookaside(context, &req2, &result_data);
@@ -903,7 +906,7 @@ test_kdc_insert_lookaside_cache_expire(void **state)
assert_non_null(e);
e->num_hits = 5;
- time_return(STALE_TIME, 0);
+ time_return(STALE_TIME + 1, 0);
kdc_insert_lookaside(context, &req2, NULL);
assert_null(K5_LIST_FIRST(&hash_table[req_hash1]));
diff --git a/src/kdc/tgs_policy.c b/src/kdc/tgs_policy.c
index a30cacc665a1..33cfbcd8184f 100644
--- a/src/kdc/tgs_policy.c
+++ b/src/kdc/tgs_policy.c
@@ -186,7 +186,7 @@ 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) {
+ if (server.expiration && ts_after(kdc_time, server.expiration)) {
*status = "SERVICE EXPIRED";
return KDC_ERR_SERVICE_EXP;
}
@@ -222,7 +222,7 @@ check_tgs_times(krb5_kdc_req *req, krb5_ticket_times *times,
KDC time. */
if (req->kdc_options & KDC_OPT_VALIDATE) {
starttime = times->starttime ? times->starttime : times->authtime;
- if (starttime > kdc_time) {
+ if (ts_after(starttime, kdc_time)) {
*status = "NOT_YET_VALID";
return KRB_AP_ERR_TKT_NYV;
}
@@ -231,7 +231,8 @@ check_tgs_times(krb5_kdc_req *req, krb5_ticket_times *times,
* Check the renew_till time. The endtime was already
* been checked in the initial authentication check.
*/
- if ((req->kdc_options & KDC_OPT_RENEW) && times->renew_till < kdc_time) {
+ if ((req->kdc_options & KDC_OPT_RENEW) &&
+ ts_after(kdc_time, times->renew_till)) {
*status = "TKT_EXPIRED";
return KRB_AP_ERR_TKT_EXPIRED;
}
@@ -374,11 +375,5 @@ validate_tgs_request(kdc_realm_t *kdc_active_realm,
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;
}