diff options
Diffstat (limited to 'src/kdc')
| -rw-r--r-- | src/kdc/deps | 40 | ||||
| -rw-r--r-- | src/kdc/dispatch.c | 19 | ||||
| -rw-r--r-- | src/kdc/do_as_req.c | 90 | ||||
| -rw-r--r-- | src/kdc/do_tgs_req.c | 70 | ||||
| -rw-r--r-- | src/kdc/extern.c | 4 | ||||
| -rw-r--r-- | src/kdc/fast_util.c | 4 | ||||
| -rw-r--r-- | src/kdc/kdc_log.c | 29 | ||||
| -rw-r--r-- | src/kdc/kdc_preauth.c | 35 | ||||
| -rw-r--r-- | src/kdc/kdc_preauth_ec.c | 41 | ||||
| -rw-r--r-- | src/kdc/kdc_preauth_encts.c | 9 | ||||
| -rw-r--r-- | src/kdc/kdc_util.c | 57 | ||||
| -rw-r--r-- | src/kdc/kdc_util.h | 21 | ||||
| -rw-r--r-- | src/kdc/main.c | 8 | ||||
| -rw-r--r-- | src/kdc/policy.c | 265 | ||||
| -rw-r--r-- | src/kdc/policy.h | 19 | ||||
| -rw-r--r-- | src/kdc/replay.c | 2 | ||||
| -rwxr-xr-x | src/kdc/t_emptytgt.py | 5 | ||||
| -rw-r--r-- | src/kdc/t_replay.c | 13 | ||||
| -rw-r--r-- | src/kdc/tgs_policy.c | 13 |
19 files changed, 475 insertions, 269 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; + } + + *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); } -#endif - return 0; /* not against policy */ +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: - */ + 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; - if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) { - *status = "FORWARD POLICY"; - return KRB5KDC_ERR_POLICY; + 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; } |
