The branch, master has been updated via 0f14ac4 s4:pygensec/tests: add test for gensec_set_max_update_size() via 891318e s4:auth/gensec/spnego: add support for fragmented spnego messages via b3f8f7e s4:pygensec: add set_max_update_size() and max_update_size() functions via 6eea2c3 auth/gensec: add gensec_*max_update_size() from 1798609 s3: Split a line with 1 statements
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 0f14ac40a29bb23bb0a417df4bbeee009400f33e Author: Stefan Metzmacher <me...@samba.org> Date: Wed Jan 11 16:00:59 2012 +0100 s4:pygensec/tests: add test for gensec_set_max_update_size() metze Autobuild-User: Stefan Metzmacher <me...@samba.org> Autobuild-Date: Thu Jan 12 14:47:05 CET 2012 on sn-devel-104 commit 891318ee4cc77077525e698d21398c6db82f0a1a Author: Stefan Metzmacher <me...@samba.org> Date: Sat Dec 24 00:27:45 2011 +0100 s4:auth/gensec/spnego: add support for fragmented spnego messages metze commit b3f8f7e8a3c28bc74f252534b1c45c9ed52d8ebe Author: Stefan Metzmacher <me...@samba.org> Date: Wed Jan 11 14:53:52 2012 +0100 s4:pygensec: add set_max_update_size() and max_update_size() functions metze commit 6eea2c33c797065f7b189d32648d2cfde5d2e3b9 Author: Stefan Metzmacher <me...@samba.org> Date: Sat Dec 24 01:14:26 2011 +0100 auth/gensec: add gensec_*max_update_size() This is only a hint for the backend, which may want to fragment update tokens. metze ----------------------------------------------------------------------- Summary of changes: auth/gensec/gensec.c | 15 ++ auth/gensec/gensec.h | 4 + auth/gensec/gensec_start.c | 3 + source4/auth/gensec/pygensec.c | 25 +++ source4/auth/gensec/spnego.c | 208 +++++++++++++++++++++++- source4/scripting/python/samba/tests/gensec.py | 54 ++++++ 6 files changed, 306 insertions(+), 3 deletions(-) Changeset truncated at 500 lines: diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c index ec104a7..d1dcc75 100644 --- a/auth/gensec/gensec.c +++ b/auth/gensec/gensec.c @@ -185,6 +185,21 @@ _PUBLIC_ NTSTATUS gensec_session_info(struct gensec_security *gensec_security, return gensec_security->ops->session_info(gensec_security, mem_ctx, session_info); } +void gensec_set_max_update_size(struct gensec_security *gensec_security, + uint32_t max_update_size) +{ + gensec_security->max_update_size = max_update_size; +} + +size_t gensec_max_update_size(struct gensec_security *gensec_security) +{ + if (gensec_security->max_update_size == 0) { + return UINT32_MAX; + } + + return gensec_security->max_update_size; +} + /** * Next state function for the GENSEC state machine * diff --git a/auth/gensec/gensec.h b/auth/gensec/gensec.h index a1ae634..9982718 100644 --- a/auth/gensec/gensec.h +++ b/auth/gensec/gensec.h @@ -167,6 +167,7 @@ struct gensec_security { enum gensec_role gensec_role; bool subcontext; uint32_t want_features; + uint32_t max_update_size; uint8_t dcerpc_auth_level; struct tsocket_address *local_addr, *remote_addr; struct gensec_settings *settings; @@ -223,6 +224,9 @@ NTSTATUS gensec_start_mech_by_ops(struct gensec_security *gensec_security, const struct gensec_security_ops *ops); NTSTATUS gensec_start_mech_by_sasl_list(struct gensec_security *gensec_security, const char **sasl_names); +void gensec_set_max_update_size(struct gensec_security *gensec_security, + uint32_t max_update_size); +size_t gensec_max_update_size(struct gensec_security *gensec_security); NTSTATUS gensec_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out); diff --git a/auth/gensec/gensec_start.c b/auth/gensec/gensec_start.c index 9576e53..016967a 100644 --- a/auth/gensec/gensec_start.c +++ b/auth/gensec/gensec_start.c @@ -518,6 +518,8 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx, (*gensec_security) = talloc_zero(mem_ctx, struct gensec_security); NT_STATUS_HAVE_NO_MEMORY(*gensec_security); + (*gensec_security)->max_update_size = 0; + SMB_ASSERT(settings->lp_ctx != NULL); (*gensec_security)->settings = talloc_reference(*gensec_security, settings); @@ -550,6 +552,7 @@ _PUBLIC_ NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx, (*gensec_security)->subcontext = true; (*gensec_security)->want_features = parent->want_features; + (*gensec_security)->max_update_size = parent->max_update_size; (*gensec_security)->dcerpc_auth_level = parent->dcerpc_auth_level; (*gensec_security)->auth_context = talloc_reference(*gensec_security, parent->auth_context); (*gensec_security)->settings = talloc_reference(*gensec_security, parent->settings); diff --git a/source4/auth/gensec/pygensec.c b/source4/auth/gensec/pygensec.c index a683daf..acbad5f 100644 --- a/source4/auth/gensec/pygensec.c +++ b/source4/auth/gensec/pygensec.c @@ -371,6 +371,27 @@ static PyObject *py_gensec_have_feature(PyObject *self, PyObject *args) return Py_False; } +static PyObject *py_gensec_set_max_update_size(PyObject *self, PyObject *args) +{ + struct gensec_security *security = pytalloc_get_type(self, struct gensec_security); + unsigned int max_update_size = 0; + + if (!PyArg_ParseTuple(args, "I", &max_update_size)) + return NULL; + + gensec_set_max_update_size(security, max_update_size); + + Py_RETURN_NONE; +} + +static PyObject *py_gensec_max_update_size(PyObject *self) +{ + struct gensec_security *security = pytalloc_get_type(self, struct gensec_security); + unsigned int max_update_size = gensec_max_update_size(security); + + return PyInt_FromLong(max_update_size); +} + static PyObject *py_gensec_update(PyObject *self, PyObject *args) { NTSTATUS status; @@ -512,6 +533,10 @@ static PyMethodDef py_gensec_security_methods[] = { "S.want_feature(feature)\n Request that GENSEC negotiate a particular feature." }, { "have_feature", (PyCFunction)py_gensec_have_feature, METH_VARARGS, "S.have_feature()\n Return True if GENSEC negotiated a particular feature." }, + { "set_max_update_size", (PyCFunction)py_gensec_set_max_update_size, METH_VARARGS, + "S.set_max_update_size(max_size) \n Some mechs can fragment update packets, needs to be use before the mech is started." }, + { "max_update_size", (PyCFunction)py_gensec_max_update_size, 0, + "S.max_update_size() \n Return the current max_update_size." }, { "update", (PyCFunction)py_gensec_update, METH_VARARGS, "S.update(blob_in) -> (finished, blob_out)\nPerform one step in a GENSEC dance. Repeat with new packets until finished is true or exception." }, { "wrap", (PyCFunction)py_gensec_wrap, METH_VARARGS, diff --git a/source4/auth/gensec/spnego.c b/source4/auth/gensec/spnego.c index fae32d8..fa20c45 100644 --- a/source4/auth/gensec/spnego.c +++ b/source4/auth/gensec/spnego.c @@ -30,6 +30,7 @@ #include "auth/gensec/gensec_proto.h" #include "auth/gensec/gensec_toplevel_proto.h" #include "param/param.h" +#include "lib/util/asn1.h" _PUBLIC_ NTSTATUS gensec_spnego_init(void); @@ -51,6 +52,16 @@ struct spnego_state { const char *neg_oid; DATA_BLOB mech_types; + + /* + * The following is used to implement + * the update token fragmentation + */ + size_t in_needed; + DATA_BLOB in_frag; + size_t out_max_length; + DATA_BLOB out_frag; + NTSTATUS out_status; }; @@ -58,7 +69,7 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi { struct spnego_state *spnego_state; - spnego_state = talloc(gensec_security, struct spnego_state); + spnego_state = talloc_zero(gensec_security, struct spnego_state); if (!spnego_state) { return NT_STATUS_NO_MEMORY; } @@ -68,6 +79,8 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi spnego_state->sub_sec_security = NULL; spnego_state->no_response_expected = false; spnego_state->mech_types = data_blob(NULL, 0); + spnego_state->out_max_length = gensec_max_update_size(gensec_security); + spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED; gensec_security->private_data = spnego_state; return NT_STATUS_OK; @@ -77,7 +90,7 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi { struct spnego_state *spnego_state; - spnego_state = talloc(gensec_security, struct spnego_state); + spnego_state = talloc_zero(gensec_security, struct spnego_state); if (!spnego_state) { return NT_STATUS_NO_MEMORY; } @@ -87,6 +100,8 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi spnego_state->sub_sec_security = NULL; spnego_state->no_response_expected = false; spnego_state->mech_types = data_blob(NULL, 0); + spnego_state->out_max_length = gensec_max_update_size(gensec_security); + spnego_state->out_status = NT_STATUS_MORE_PROCESSING_REQUIRED; gensec_security->private_data = spnego_state; return NT_STATUS_OK; @@ -1130,6 +1145,193 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA return NT_STATUS_INVALID_PARAMETER; } +static NTSTATUS gensec_spnego_update_in(struct gensec_security *gensec_security, + const DATA_BLOB in, DATA_BLOB *full_in) +{ + struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data; + size_t expected; + uint8_t *buf; + NTSTATUS status; + bool ok; + + *full_in = data_blob_null; + + if (spnego_state->in_needed == 0) { + size_t size = 0; + + /* + * try to work out the size of the full + * input token, it might be fragmented + */ + status = asn1_peek_full_tag(in, ASN1_APPLICATION(0), &size); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + status = asn1_peek_full_tag(in, ASN1_CONTEXT(1), &size); + } + + if (NT_STATUS_IS_OK(status) || + NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + spnego_state->in_needed = size; + } else { + /* + * If it is not an asn1 message + * just call the next layer. + */ + spnego_state->in_needed = in.length; + } + } + + if (spnego_state->in_needed > UINT16_MAX) { + /* + * limit the incoming message to 0xFFFF + * to avoid DoS attacks. + */ + return NT_STATUS_INVALID_BUFFER_SIZE; + } + + if ((spnego_state->in_needed > 0) && (in.length == 0)) { + /* + * If we reach this, we know we got at least + * part of an asn1 message, getting 0 means + * the remote peer wants us to spin. + */ + return NT_STATUS_INVALID_PARAMETER; + } + + expected = spnego_state->in_needed - spnego_state->in_frag.length; + if (in.length > expected) { + /* + * we got more than expected + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (in.length == spnego_state->in_needed) { + /* + * if the in.length contains the full blob + * we are done. + * + * Note: this implies spnego_state->in_frag.length == 0, + * but we do not need to check this explicitly + * because we already know that we did not get + * more than expected. + */ + *full_in = in; + return NT_STATUS_OK; + } + + ok = data_blob_append(spnego_state, &spnego_state->in_frag, + in.data, in.length); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + + if (spnego_state->in_needed > spnego_state->in_frag.length) { + return NT_STATUS_MORE_PROCESSING_REQUIRED; + } + + *full_in = spnego_state->in_frag; + return NT_STATUS_OK; +} + +static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security, + TALLOC_CTX *out_mem_ctx, + DATA_BLOB *_out) +{ + struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data; + size_t new_length; + uint8_t *buf; + DATA_BLOB out = data_blob_null; + + *_out = data_blob_null; + + if (spnego_state->out_frag.length == 0) { + return spnego_state->out_status; + } + + /* + * There is still more data to be delivered + * to the remote peer. + */ + + if (spnego_state->out_frag.length <= spnego_state->out_max_length) { + /* + * Fast path, we can deliver everything + */ + + *_out = spnego_state->out_frag; + talloc_steal(out_mem_ctx, _out->data); + spnego_state->out_frag = data_blob_null; + return spnego_state->out_status; + } + + out = spnego_state->out_frag; + + /* + * copy the remaining bytes + */ + spnego_state->out_frag = data_blob_talloc(spnego_state, + out.data + spnego_state->out_max_length, + out.length - spnego_state->out_max_length); + if (spnego_state->out_frag.data == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* + * truncate the buffer + */ + data_blob_realloc(spnego_state, &out, spnego_state->out_max_length); + + talloc_steal(out_mem_ctx, out.data); + *_out = out; + return NT_STATUS_MORE_PROCESSING_REQUIRED; +} + +static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_security, + TALLOC_CTX *out_mem_ctx, + struct tevent_context *ev, + const DATA_BLOB in, DATA_BLOB *out) +{ + struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data; + DATA_BLOB full_in = data_blob_null; + NTSTATUS status; + + *out = data_blob_null; + + if (spnego_state->out_frag.length > 0) { + if (in.length > 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + return gensec_spnego_update_out(gensec_security, + out_mem_ctx, + out); + } + + status = gensec_spnego_update_in(gensec_security, + in, &full_in); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = gensec_spnego_update(gensec_security, + spnego_state, ev, + full_in, + &spnego_state->out_frag); + data_blob_free(&spnego_state->in_frag); + spnego_state->in_needed = 0; + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + return status; + } + + spnego_state->out_status = status; + + return gensec_spnego_update_out(gensec_security, + out_mem_ctx, + out); +} + static void gensec_spnego_want_feature(struct gensec_security *gensec_security, uint32_t feature) { @@ -1168,7 +1370,7 @@ static const struct gensec_security_ops gensec_spnego_security_ops = { .oid = gensec_spnego_oids, .client_start = gensec_spnego_client_start, .server_start = gensec_spnego_server_start, - .update = gensec_spnego_update, + .update = gensec_spnego_update_wrapper, .seal_packet = gensec_spnego_seal_packet, .sign_packet = gensec_spnego_sign_packet, .sig_size = gensec_spnego_sig_size, diff --git a/source4/scripting/python/samba/tests/gensec.py b/source4/scripting/python/samba/tests/gensec.py index ab38d18..d08022e 100644 --- a/source4/scripting/python/samba/tests/gensec.py +++ b/source4/scripting/python/samba/tests/gensec.py @@ -92,3 +92,57 @@ class GensecTests(samba.tests.TestCase): client_session_key = self.gensec_client.session_key() server_session_key = self.gensec_server.session_key() self.assertEqual(client_session_key, server_session_key) + + def test_max_update_size(self): + """Test GENSEC by doing an exchange with ourselves using GSSAPI against a KDC""" + + """Start up a client and server GENSEC instance to test things with""" + + self.gensec_client = gensec.Security.start_client(self.settings) + self.gensec_client.set_credentials(self.get_credentials()) + self.gensec_client.want_feature(gensec.FEATURE_SIGN) + self.gensec_client.set_max_update_size(5) + self.gensec_client.start_mech_by_name("spnego") + + self.gensec_server = gensec.Security.start_server(settings=self.settings, + auth_context=auth.AuthContext(lp_ctx=self.lp_ctx)) + creds = Credentials() + creds.guess(self.lp_ctx) + creds.set_machine_account(self.lp_ctx) + self.gensec_server.set_credentials(creds) + self.gensec_server.want_feature(gensec.FEATURE_SIGN) + self.gensec_server.set_max_update_size(5) + self.gensec_server.start_mech_by_name("spnego") + + client_finished = False + server_finished = False + server_to_client = "" + + """Run the actual call loop""" + i = 0 + while client_finished == False or server_finished == False: + i += 1 + if not client_finished: + print "running client gensec_update: %d: %r" % (len(server_to_client), server_to_client) + (client_finished, client_to_server) = self.gensec_client.update(server_to_client) + if not server_finished: + print "running server gensec_update: %d: %r" % (len(client_to_server), client_to_server) + (server_finished, server_to_client) = self.gensec_server.update(client_to_server) + + """Here we expect a lot more than the typical 1 or 2 roundtrips""" + self.assertTrue(i > 10) + + session_info = self.gensec_server.session_info() + + test_string = "Hello Server" + test_wrapped = self.gensec_client.wrap(test_string) + test_unwrapped = self.gensec_server.unwrap(test_wrapped) + self.assertEqual(test_string, test_unwrapped) + test_string = "Hello Client" + test_wrapped = self.gensec_server.wrap(test_string) + test_unwrapped = self.gensec_client.unwrap(test_wrapped) + self.assertEqual(test_string, test_unwrapped) + + client_session_key = self.gensec_client.session_key() + server_session_key = self.gensec_server.session_key() + self.assertEqual(client_session_key, server_session_key) -- Samba Shared Repository