krb5/Fix-KDC-null-dereference-on-large-TGS-replies.patch
2018-04-24 11:19:31 -04:00

225 lines
8.1 KiB
Diff

From d3697aa9a653bc9aaf11f3c9e985ba544c23a9c3 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Fri, 20 Apr 2018 16:16:02 -0400
Subject: [PATCH] Fix KDC null dereference on large TGS replies
For TGS requests, dispatch() doesn't set state->active_realm, which
leads to a NULL dereference in finish_dispatch() if the reply is too
big for UDP. Prior to commit 0a2f14f752c32a24200363cc6b6ae64a92f81379
the active realm was a global and was set when process_tgs_req()
called setup_server_realm().
Move TGS decoding out of process_tgs_req() so that we can set
state->active_realm before any errors requiring response. Add a test
case.
[ghudson@mit.edu: edited commit message; added test case; reduced code
duplication; removed server handle from process_tgs_req() parameters]
ticket: 8666
tags: pullup
target_version: 1.16-next
target_version: 1.15-next
(cherry picked from commit 6afa8b4abf8f7c5774d03e6b15ee7288ad68d725)
---
src/kdc/Makefile.in | 1 +
src/kdc/dispatch.c | 50 ++++++++++++++++++++++++-------------------
src/kdc/do_tgs_req.c | 24 ++++++---------------
src/kdc/kdc_util.h | 5 ++---
src/kdc/t_bigreply.py | 19 ++++++++++++++++
5 files changed, 56 insertions(+), 43 deletions(-)
create mode 100644 src/kdc/t_bigreply.py
diff --git a/src/kdc/Makefile.in b/src/kdc/Makefile.in
index 61a3dbc6f..117a8f561 100644
--- a/src/kdc/Makefile.in
+++ b/src/kdc/Makefile.in
@@ -85,6 +85,7 @@ check-cmocka: t_replay
check-pytests:
$(RUNPYTEST) $(srcdir)/t_workers.py $(PYTESTFLAGS)
$(RUNPYTEST) $(srcdir)/t_emptytgt.py $(PYTESTFLAGS)
+ $(RUNPYTEST) $(srcdir)/t_bigreply.py $(PYTESTFLAGS)
install:
$(INSTALL_PROGRAM) krb5kdc ${DESTDIR}$(SERVER_BINDIR)/krb5kdc
diff --git a/src/kdc/dispatch.c b/src/kdc/dispatch.c
index 3867ff952..3ed5176a8 100644
--- a/src/kdc/dispatch.c
+++ b/src/kdc/dispatch.c
@@ -124,7 +124,7 @@ dispatch(void *cb, const krb5_fulladdr *local_addr,
verto_ctx *vctx, loop_respond_fn respond, void *arg)
{
krb5_error_code retval;
- krb5_kdc_req *as_req;
+ krb5_kdc_req *req = NULL;
krb5_data *response = NULL;
struct dispatch_state *state;
struct server_handle *handle = cb;
@@ -176,29 +176,35 @@ dispatch(void *cb, const krb5_fulladdr *local_addr,
/* try TGS_REQ first; they are more common! */
- if (krb5_is_tgs_req(pkt)) {
- 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))) {
- /*
- * setup_server_realm() sets up the global realm-specific data
- * pointer.
- * process_as_req frees the request if it is called
- */
- state->active_realm = setup_server_realm(handle, as_req->server);
- if (state->active_realm != NULL) {
- process_as_req(as_req, pkt, local_addr, remote_addr,
- state->active_realm, vctx,
- finish_dispatch_cache, state);
- return;
- } else {
- retval = KRB5KDC_ERR_WRONG_REALM;
- krb5_free_kdc_req(kdc_err_context, as_req);
- }
- }
- } else
+ if (krb5_is_tgs_req(pkt))
+ retval = decode_krb5_tgs_req(pkt, &req);
+ else if (krb5_is_as_req(pkt))
+ retval = decode_krb5_as_req(pkt, &req);
+ else
retval = KRB5KRB_AP_ERR_MSG_TYPE;
+ if (retval)
+ goto done;
+ state->active_realm = setup_server_realm(handle, req->server);
+ if (state->active_realm == NULL) {
+ retval = KRB5KDC_ERR_WRONG_REALM;
+ goto done;
+ }
+
+ if (krb5_is_tgs_req(pkt)) {
+ /* process_tgs_req frees the request */
+ retval = process_tgs_req(req, pkt, remote_addr, state->active_realm,
+ &response);
+ req = NULL;
+ } else if (krb5_is_as_req(pkt)) {
+ /* process_as_req frees the request and calls finish_dispatch_cache. */
+ process_as_req(req, pkt, local_addr, remote_addr, state->active_realm,
+ vctx, finish_dispatch_cache, state);
+ return;
+ }
+
+done:
+ krb5_free_kdc_req(kdc_err_context, req);
finish_dispatch_cache(state, retval, response);
}
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index cc5a69236..61051bafa 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -98,12 +98,12 @@ search_sprinc(kdc_realm_t *, krb5_kdc_req *, krb5_flags,
/*ARGSUSED*/
krb5_error_code
-process_tgs_req(struct server_handle *handle, krb5_data *pkt,
- const krb5_fulladdr *from, krb5_data **response)
+process_tgs_req(krb5_kdc_req *request, krb5_data *pkt,
+ const krb5_fulladdr *from, kdc_realm_t *kdc_active_realm,
+ krb5_data **response)
{
krb5_keyblock * subkey = 0;
krb5_keyblock *header_key = NULL;
- krb5_kdc_req *request = 0;
krb5_db_entry *server = NULL;
krb5_db_entry *stkt_server = NULL;
krb5_kdc_rep reply;
@@ -136,7 +136,6 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
krb5_pa_data *pa_tgs_req; /*points into request*/
krb5_data scratch;
krb5_pa_data **e_data = NULL;
- kdc_realm_t *kdc_active_realm = NULL;
krb5_audit_state *au_state = NULL;
krb5_data **auth_indicators = NULL;
@@ -146,36 +145,25 @@ process_tgs_req(struct server_handle *handle, krb5_data *pkt,
memset(&enc_tkt_reply, 0, sizeof(enc_tkt_reply));
session_key.contents = NULL;
- retval = decode_krb5_tgs_req(pkt, &request);
- if (retval)
- return retval;
/* Save pointer to client-requested service principal, in case of
* errors before a successful call to search_sprinc(). */
sprinc = request->server;
if (request->msg_type != KRB5_TGS_REQ) {
- krb5_free_kdc_req(handle->kdc_err_context, request);
+ krb5_free_kdc_req(kdc_context, request);
return KRB5_BADMSGTYPE;
}
- /*
- * setup_server_realm() sets up the global realm-specific data pointer.
- */
- kdc_active_realm = setup_server_realm(handle, request->server);
- if (kdc_active_realm == NULL) {
- krb5_free_kdc_req(handle->kdc_err_context, request);
- return KRB5KDC_ERR_WRONG_REALM;
- }
errcode = kdc_make_rstate(kdc_active_realm, &state);
if (errcode !=0) {
- krb5_free_kdc_req(handle->kdc_err_context, request);
+ krb5_free_kdc_req(kdc_context, request);
return errcode;
}
/* Initialize audit state. */
errcode = kau_init_kdc_req(kdc_context, request, from, &au_state);
if (errcode) {
- krb5_free_kdc_req(handle->kdc_err_context, request);
+ krb5_free_kdc_req(kdc_context, request);
return errcode;
}
/* Seed the audit trail with the request ID and basic information. */
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index a63af2503..1885c9f80 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -145,9 +145,8 @@ process_as_req (krb5_kdc_req *, krb5_data *,
/* do_tgs_req.c */
krb5_error_code
-process_tgs_req (struct server_handle *, krb5_data *,
- const krb5_fulladdr *,
- krb5_data ** );
+process_tgs_req (krb5_kdc_req *, krb5_data *, const krb5_fulladdr *,
+ kdc_realm_t *, krb5_data ** );
/* dispatch.c */
void
dispatch (void *,
diff --git a/src/kdc/t_bigreply.py b/src/kdc/t_bigreply.py
new file mode 100644
index 000000000..6bc9a8fe0
--- /dev/null
+++ b/src/kdc/t_bigreply.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+from k5test import *
+
+# Set the maximum UDP reply size very low, so that all replies go
+# through the RESPONSE_TOO_BIG path.
+kdc_conf = {'kdcdefaults': {'kdc_max_dgram_reply_size': '10'}}
+realm = K5Realm(kdc_conf=kdc_conf, get_creds=False)
+
+msgs = ('Sending initial UDP request',
+ 'Received answer',
+ 'Request or response is too big for UDP; retrying with TCP',
+ ' to KRBTEST.COM (tcp only)',
+ 'Initiating TCP connection',
+ 'Sending TCP request',
+ 'Terminating TCP connection')
+realm.kinit(realm.user_princ, password('user'), expected_trace=msgs)
+realm.run([kvno, realm.host_princ], expected_trace=msgs)
+
+success('Large KDC replies')