[SCM] Samba Shared Repository - branch master updated

Stefan Metzmacher metze at samba.org
Thu Jan 12 06:48:03 MST 2012


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 <metze at 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 <metze at samba.org>
    Autobuild-Date: Thu Jan 12 14:47:05 CET 2012 on sn-devel-104

commit 891318ee4cc77077525e698d21398c6db82f0a1a
Author: Stefan Metzmacher <metze at 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 <metze at 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 <metze at 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


More information about the samba-cvs mailing list