[Patches] The way to remove gensec_update_ev()

Stefan Metzmacher metze at samba.org
Wed May 17 12:12:54 UTC 2017


Hi,

I'm currently working on the removal of gensec_update_ev(),
which relies on nested event loops to be activated.

If we want to have proper support for trusted domains in the
as AD DC, we need to use real async authentication because
we still use a single process model for the rpc server.

So the first step is to make all users of gensec_update_ev()
use gensec_update_send/recv instead.

Once we have that we need to make the low level auth stack async
for NTLMSSP (as a server) and Kerberos (as a client).

Here's the first chunk of patches, they passed a private autobuild.

Please review and push:-)

Thanks!
metze
-------------- next part --------------
From 04a72d2a65d3aea7261b17b00432f29900f08205 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 12 May 2017 09:10:19 +0200
Subject: [PATCH 01/35] auth/spnego: fix gensec_update_ev() argument order for
 the SPNEGO_FALLBACK case

This went unnoticed so long as we don't use -Wc++-compat
and gensec_update_ev() used the sync update() hook for all
NTLMSSP and Kerberos.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12788

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/spnego.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/auth/gensec/spnego.c b/auth/gensec/spnego.c
index 484f491..645c8b2 100644
--- a/auth/gensec/spnego.c
+++ b/auth/gensec/spnego.c
@@ -366,7 +366,7 @@ static NTSTATUS gensec_spnego_server_try_fallback(struct gensec_security *gensec
 			return nt_status;
 		}
 		nt_status = gensec_update_ev(spnego_state->sub_sec_security,
-					  ev, out_mem_ctx, in, out);
+					     out_mem_ctx, ev, in, out);
 		return nt_status;
 	}
 	DEBUG(1, ("Failed to parse SPNEGO request\n"));
@@ -804,8 +804,8 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
 
 	switch (spnego_state->state_position) {
 	case SPNEGO_FALLBACK:
-		return gensec_update_ev(spnego_state->sub_sec_security, ev,
-				     out_mem_ctx, in, out);
+		return gensec_update_ev(spnego_state->sub_sec_security,
+					out_mem_ctx, ev, in, out);
 	case SPNEGO_SERVER_START:
 	{
 		NTSTATUS nt_status;
-- 
1.9.1


From e3eac7c64102ffc95956247eee4d05eab95f5d40 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 13 May 2017 02:25:44 +0200
Subject: [PATCH 02/35] auth/gensec: call gensec_verify_features() also after
 update_recv() in gensec_update_ev()

This is no a real problem until now, because the only backends with update_send()/recv()
are "schannel" (which only supports AUTH_LEVEL_{INTEGRITY,PRIVACY}) and
"naclrpc_as_system" (which doesn't support any protection beside using unix
domain sockets).

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c
index 6cc82e6..23d762b 100644
--- a/auth/gensec/gensec.c
+++ b/auth/gensec/gensec.c
@@ -397,6 +397,19 @@ _PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security,
 		goto fail;
 	}
 	status = ops->update_recv(subreq, out_mem_ctx, out);
+	if (!NT_STATUS_IS_OK(status)) {
+		goto fail;
+	}
+
+	/*
+	 * Because callers using the
+	 * gensec_start_mech_by_auth_type() never call
+	 * gensec_want_feature(), it isn't sensible for them
+	 * to have to call gensec_have_feature() manually, and
+	 * these are not points of negotiation, but are
+	 * asserted by the client
+	 */
+	status = gensec_verify_features(gensec_security);
  fail:
 	TALLOC_FREE(frame);
 	return status;
-- 
1.9.1


From 9ef93071935013fabef3f4cf6d748943d41bfe22 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 10 May 2017 16:17:48 +0200
Subject: [PATCH 03/35] s3:libsmb: don't rely on gensec_session_key() to work
 on an unfinished authentication

If smbXcli_session_is_guest() returns true, we should handle the authentication
as anonymous and don't touch the gensec context anymore.

Note that smbXcli_session_is_guest() always returns false, it signing is
required!

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/libsmb/cliconnect.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index 93f8730..a28a582 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -1070,13 +1070,16 @@ static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq)
 			 * We can't finish the gensec handshake, we don't
 			 * have a negotiated session key.
 			 *
-			 * So just pretend we are completely done.
+			 * So just pretend we are completely done,
+			 * we need to continue as anonymous from this point,
+			 * as we can't get a session key.
 			 *
 			 * Note that smbXcli_session_is_guest()
 			 * always returns false if we require signing.
 			 */
 			state->blob_in = data_blob_null;
 			state->local_ready = true;
+			state->is_anonymous = true;
 		}
 
 		state->remote_ready = true;
-- 
1.9.1


From b6b5470c5a0331c4ac5f3f7bc88e0ee23ebd5b99 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 15:36:31 +0200
Subject: [PATCH 04/35] s4:smb_server: avoid using gensec_update_ev() for the
 negotiate blob

Getting the SPNEGO mech type blob, we don't expect to block for
any network io, so we can also use gensec_update() which creates
a temporary event context.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/smb_server/smb/negprot.c  | 3 ++-
 source4/smb_server/smb2/negprot.c | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/source4/smb_server/smb/negprot.c b/source4/smb_server/smb/negprot.c
index dfcc1a2..79f5069 100644
--- a/source4/smb_server/smb/negprot.c
+++ b/source4/smb_server/smb/negprot.c
@@ -418,7 +418,8 @@ static void reply_nt1(struct smbsrv_request *req, uint16_t choice)
 		
 		if (NT_STATUS_IS_OK(nt_status)) {
 			/* Get and push the proposed OID list into the packets */
-			nt_status = gensec_update_ev(gensec_security, req, req->smb_conn->connection->event.ctx, null_data_blob, &blob);
+			nt_status = gensec_update(gensec_security, req,
+						  null_data_blob, &blob);
 
 			if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 				DEBUG(1, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
diff --git a/source4/smb_server/smb2/negprot.c b/source4/smb_server/smb2/negprot.c
index addd278..4aaaf46 100644
--- a/source4/smb_server/smb2/negprot.c
+++ b/source4/smb_server/smb2/negprot.c
@@ -81,7 +81,8 @@ static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *
 		return nt_status;
 	}
 
-	nt_status = gensec_update_ev(gensec_security, req, req->smb_conn->connection->event.ctx, null_data_blob, &blob);
+	nt_status = gensec_update(gensec_security, req,
+				  null_data_blob, &blob);
 	if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 		DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
 		smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
-- 
1.9.1


From 3d5e685d7c09d4e6744da9d068aef4be4e6ac360 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 20 Dec 2013 08:52:52 +0100
Subject: [PATCH 05/35] s4:auth/gensec: let GENSEC_FEATURE_SESSION_KEY result
 in GSS_C_INTEG_FLAG

This is important to allow the 'new_spnego' with mech_list protection to work
for a SMB session setup.

This is not strictly needed as we always announce GENSEC_FEATURE_SESSION_KEY
in gensec_gssapi_have_feature(), but it's better to send GSS_C_INTEG_FLAG
over the wire.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/gensec/gensec_gssapi.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c
index 2b3c56b..c68469d 100644
--- a/source4/auth/gensec/gensec_gssapi.c
+++ b/source4/auth/gensec/gensec_gssapi.c
@@ -175,6 +175,9 @@ static NTSTATUS gensec_gssapi_start(struct gensec_security *gensec_security)
 		gensec_gssapi_state->gss_want_flags |= GSS_C_SEQUENCE_FLAG;
 	}
 
+	if (gensec_security->want_features & GENSEC_FEATURE_SESSION_KEY) {
+		gensec_gssapi_state->gss_want_flags |= GSS_C_INTEG_FLAG;
+	}
 	if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
 		gensec_gssapi_state->gss_want_flags |= GSS_C_INTEG_FLAG;
 	}
-- 
1.9.1


From c308077ec0882fe22bb3fd9d11d4999da749d53c Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 13 May 2017 08:37:05 +0200
Subject: [PATCH 06/35] selftest: let fl2003dc use "dcesrv:header signing = no"

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/target/Samba4.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 0d75c47..b9367ea 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1507,6 +1507,7 @@ sub provision_fl2003dc($$$)
 
 	print "PROVISIONING DC WITH FOREST LEVEL 2003...\n";
 	my $extra_conf_options = "allow dns updates = nonsecure and secure
+	dcesrv:header signing = no
 	dns forwarder = 127.0.0.$swiface1 127.0.0.$swiface2";
 	my $ret = $self->provision($prefix,
 				   "domain controller",
-- 
1.9.1


From 0252063952ecdb37cbed1336d90d71a58839d114 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 12 May 2017 08:04:33 +0200
Subject: [PATCH 07/35] s3:gse: always announce GENSEC_FEATURE_SIGN_PKT_HEADER
 support.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/librpc/crypto/gse.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index 142627c..972bbe0 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -1106,15 +1106,7 @@ static bool gensec_gse_have_feature(struct gensec_security *gensec_security,
 		return true;
 	}
 	if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
-		if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
-			return true;
-		}
-
-		if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
-			return true;
-		}
-
-		return false;
+		return true;
 	}
 	return false;
 }
-- 
1.9.1


From 79296229ddf696af8b5d18a4e83666730e48068c Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 12 May 2017 08:05:03 +0200
Subject: [PATCH 08/35] s4:gensec_gssapi: always announce
 GENSEC_FEATURE_SIGN_PKT_HEADER

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/gensec/gensec_gssapi.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c
index c68469d..8c93034 100644
--- a/source4/auth/gensec/gensec_gssapi.c
+++ b/source4/auth/gensec/gensec_gssapi.c
@@ -1372,15 +1372,7 @@ static bool gensec_gssapi_have_feature(struct gensec_security *gensec_security,
 		return true;
 	}
 	if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
-		if (gensec_security->want_features & GENSEC_FEATURE_SEAL) {
-			return true;
-		}
-
-		if (gensec_security->want_features & GENSEC_FEATURE_SIGN) {
-			return true;
-		}
-
-		return false;
+		return true;
 	}
 	return false;
 }
-- 
1.9.1


From 920e6c3e3fcb8704e85609e6fb63a66e77fb3f5e Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 12 May 2017 11:05:15 +0200
Subject: [PATCH 09/35] auth/spnego: always announce
 GENSEC_FEATURE_SIGN_PKT_HEADER support.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/spnego.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/auth/gensec/spnego.c b/auth/gensec/spnego.c
index 645c8b2..ed7f3d7 100644
--- a/auth/gensec/spnego.c
+++ b/auth/gensec/spnego.c
@@ -1632,6 +1632,20 @@ static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
 				       uint32_t feature) 
 {
 	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
+
+	if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
+		/*
+		 * All mechs with sub (child) mechs need to provide DCERPC
+		 * header signing! This is required because the negotiation
+		 * of header signing is done before the authentication
+		 * is completed.
+		 *
+		 * Currently all our backends support DCERPC with:
+		 * GENSEC_FEATURE_SIGN_PKT_HEADER.
+		 */
+		return true;
+	}
+
 	if (!spnego_state->sub_sec_security) {
 		return false;
 	}
-- 
1.9.1


From f3085f2681a63931c9a7c8fd2ea6fd5a7a4696e0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 14 May 2017 02:06:08 +0200
Subject: [PATCH 10/35] auth/gensec: add some basic doxygen comments for
 gensec_{want,have}_feature()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.h | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/auth/gensec/gensec.h b/auth/gensec/gensec.h
index e7b9477..9a2bd96 100644
--- a/auth/gensec/gensec.h
+++ b/auth/gensec/gensec.h
@@ -147,8 +147,51 @@ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 				      struct gensec_security *gensec_security,
 				      const DATA_BLOB in);
 NTSTATUS gensec_update_recv(struct tevent_req *req, TALLOC_CTX *out_mem_ctx, DATA_BLOB *out);
+/**
+ * @brief Ask for features for a following authentication
+ *
+ * Typically only one specific feature bit should be passed,
+ * but it also works to ask for more features.
+ *
+ * The features must be requested before starting the
+ * gensec_update*() loop.
+ *
+ * The current expection is GENSEC_FEATURE_SIGN_PKT_HEADER,
+ * it can also be requested once the gensec_update*() loop
+ * returned NT_STATUS_OK.
+ *
+ * The features should not be changed during the gensec_update*()
+ * loop.
+ *
+ * @param[in]  gensec_security The context to be used
+ *
+ * @param[in]  feature         The requested feature[s].
+ *
+ */
 void gensec_want_feature(struct gensec_security *gensec_security,
 			 uint32_t feature);
+/**
+ * @brief Ask for one feature after the finished authentication
+ *
+ * Because the return value is bool, the caller can only
+ * ask for one feature at a time.
+ *
+ * The features must be requested after the finished
+ * gensec_update*() loop.
+ *
+ * The current expection is GENSEC_FEATURE_SIGN_PKT_HEADER,
+ * it can also be requested before the gensec_update*() loop,
+ * as the return value only indicates if the backend supports
+ * dcerpc header signing, not if header signing will be used
+ * between client and server. It will be used only if the caller
+ * also used gensec_want_feature(GENSEC_FEATURE_SIGN_PKT_HEADER).
+ *
+ * @param[in]  gensec_security The context to be used.
+ *
+ * @param[in]  feature         The requested feature.
+ *
+ * @return                     true if the feature is supported, false if not.
+ */
 bool gensec_have_feature(struct gensec_security *gensec_security,
 			 uint32_t feature);
 NTTIME gensec_expire_time(struct gensec_security *gensec_security);
-- 
1.9.1


From 2773d1f4357bf189462c2650230ff4e4aa8dd497 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 14 May 2017 00:28:50 +0200
Subject: [PATCH 11/35] s3:cli_pipe: ask for GENSEC_FEATURE_SIGN_PKT_HEADER
 after the gensec_update() dance

Most features should be added before the update() dance, while
GENSEC_FEATURE_SIGN_PKT_HEADER needs to be after the dance on the client
side.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/rpc_client/cli_pipe.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c
index 14f7fbc..63b6cf4 100644
--- a/source3/rpc_client/cli_pipe.c
+++ b/source3/rpc_client/cli_pipe.c
@@ -1952,6 +1952,14 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq)
 		return;
 	}
 
+	if (pkt->ptype == DCERPC_PKT_BIND_ACK) {
+		if (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
+			if (pauth->client_hdr_signing) {
+				pauth->hdr_signing = true;
+			}
+		}
+	}
+
 	state->cli->max_xmit_frag = pkt->u.bind_ack.max_xmit_frag;
 
 	switch(pauth->auth_type) {
@@ -2017,13 +2025,6 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq)
 	default:
 		gensec_security = pauth->auth_ctx;
 
-		if (pkt->pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
-			if (pauth->client_hdr_signing) {
-				pauth->hdr_signing = true;
-				gensec_want_feature(gensec_security,
-						    GENSEC_FEATURE_SIGN_PKT_HEADER);
-			}
-		}
 
 		status = gensec_update(gensec_security, state,
 				       auth.credentials, &auth_token);
@@ -2032,6 +2033,11 @@ static void rpc_pipe_bind_step_one_done(struct tevent_req *subreq)
 			status = rpc_bind_next_send(req, state,
 							&auth_token);
 		} else if (NT_STATUS_IS_OK(status)) {
+			if (pauth->hdr_signing) {
+				gensec_want_feature(gensec_security,
+						    GENSEC_FEATURE_SIGN_PKT_HEADER);
+			}
+
 			if (auth_token.length == 0) {
 				/* Bind complete. */
 				tevent_req_done(req);
-- 
1.9.1


From 3c268c90881456c69164b552533e56fefc03faeb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 14 May 2017 01:22:32 +0200
Subject: [PATCH 12/35] s3:rpc_server: move gensec_update() out of
 auth_generic_server_authtype_start*()

We let the caller use auth_generic_server_step() instead.
This allows us to request GENSEC_FEATURE_SIGN_PKT_HEADER before
starting the gensec_update() dance.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/rpc_server/dcesrv_auth_generic.c | 14 --------------
 source3/rpc_server/dcesrv_auth_generic.h |  2 --
 source3/rpc_server/srv_pipe.c            | 22 ++++++++++++++--------
 3 files changed, 14 insertions(+), 24 deletions(-)

diff --git a/source3/rpc_server/dcesrv_auth_generic.c b/source3/rpc_server/dcesrv_auth_generic.c
index 1092cd3..28fe76d 100644
--- a/source3/rpc_server/dcesrv_auth_generic.c
+++ b/source3/rpc_server/dcesrv_auth_generic.c
@@ -26,8 +26,6 @@
 
 static NTSTATUS auth_generic_server_authtype_start_as_root(TALLOC_CTX *mem_ctx,
 							   uint8_t auth_type, uint8_t auth_level,
-							   DATA_BLOB *token_in,
-							   DATA_BLOB *token_out,
 							   const struct tsocket_address *remote_address,
 							   const struct tsocket_address *local_address,
 							   const char *service_description,
@@ -55,14 +53,6 @@ static NTSTATUS auth_generic_server_authtype_start_as_root(TALLOC_CTX *mem_ctx,
 		return status;
 	}
 
-	status = gensec_update(gensec_security, mem_ctx, *token_in, token_out);
-	if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-		DEBUG(2, (__location__ ": gensec_update failed: %s\n",
-			  nt_errstr(status)));
-		TALLOC_FREE(gensec_security);
-		return status;
-	}
-
 	/* steal gensec context to the caller */
 	*ctx = talloc_move(mem_ctx, &gensec_security);
 	return status;
@@ -70,8 +60,6 @@ static NTSTATUS auth_generic_server_authtype_start_as_root(TALLOC_CTX *mem_ctx,
 
 NTSTATUS auth_generic_server_authtype_start(TALLOC_CTX *mem_ctx,
 					    uint8_t auth_type, uint8_t auth_level,
-					    DATA_BLOB *token_in,
-					    DATA_BLOB *token_out,
 					    const struct tsocket_address *remote_address,
 					    const struct tsocket_address *local_address,
 					    const char *service_description,
@@ -83,8 +71,6 @@ NTSTATUS auth_generic_server_authtype_start(TALLOC_CTX *mem_ctx,
 	/* this has to be done as root in order to create the messaging socket */
 	status = auth_generic_server_authtype_start_as_root(mem_ctx,
 							    auth_type, auth_level,
-							    token_in,
-							    token_out,
 							    remote_address,
 							    local_address,
 							    service_description,
diff --git a/source3/rpc_server/dcesrv_auth_generic.h b/source3/rpc_server/dcesrv_auth_generic.h
index 4e86eab..f5e186b 100644
--- a/source3/rpc_server/dcesrv_auth_generic.h
+++ b/source3/rpc_server/dcesrv_auth_generic.h
@@ -24,8 +24,6 @@ struct gensec_security;
 
 NTSTATUS auth_generic_server_authtype_start(TALLOC_CTX *mem_ctx,
 					    uint8_t auth_type, uint8_t auth_level,
-					    DATA_BLOB *token_in,
-					    DATA_BLOB *token_out,
 					    const struct tsocket_address *remote_address,
 					    const struct tsocket_address *local_address,
 					    const char *service_description,
diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c
index 251f899..39f5fb4 100644
--- a/source3/rpc_server/srv_pipe.c
+++ b/source3/rpc_server/srv_pipe.c
@@ -527,23 +527,16 @@ static bool pipe_auth_generic_bind(struct pipes_struct *p,
 	status = auth_generic_server_authtype_start(p,
 						    auth_info->auth_type,
 						    auth_info->auth_level,
-						    &auth_info->credentials,
-						    response,
 						    p->remote_address,
 						    p->local_address,
 						    service_description,
 						    &gensec_security);
-	if (!NT_STATUS_IS_OK(status) &&
-	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
-	{
+	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0, (__location__ ": auth_generic_server_authtype_start[%u/%u] failed: %s\n",
 			  auth_info->auth_type, auth_info->auth_level, nt_errstr(status)));
 		return false;
 	}
 
-	/* Make sure data is bound to the memctx, to be freed the caller */
-	talloc_steal(mem_ctx, response->data);
-
 	p->auth.auth_ctx = gensec_security;
 	p->auth.auth_type = auth_info->auth_type;
 	p->auth.auth_level = auth_info->auth_level;
@@ -560,6 +553,19 @@ static bool pipe_auth_generic_bind(struct pipes_struct *p,
 				    GENSEC_FEATURE_SIGN_PKT_HEADER);
 	}
 
+	status = auth_generic_server_step(gensec_security, mem_ctx,
+					  &auth_info->credentials,
+					  response);
+	if (!NT_STATUS_IS_OK(status) &&
+	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED))
+	{
+		DEBUG(2, (__location__ ": "
+			  "auth_generic_server_step[%u/%u] failed: %s\n",
+			  auth_info->auth_type, auth_info->auth_level,
+			  nt_errstr(status)));
+		return false;
+	}
+
 	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 		return true;
 	}
-- 
1.9.1


From 491c100e0fbb82a47261aee620011c17a6671ce4 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sun, 14 May 2017 00:28:50 +0200
Subject: [PATCH 13/35] s4:librpc: ask for GENSEC_FEATURE_SIGN_PKT_HEADER after
 the gensec_update() dance

Most features should be added before the update() dance, while
GENSEC_FEATURE_SIGN_PKT_HEADER needs to be after the dance on the client
side.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/librpc/rpc/dcerpc_auth.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c
index 08578c9..e790a9f 100644
--- a/source4/librpc/rpc/dcerpc_auth.c
+++ b/source4/librpc/rpc/dcerpc_auth.c
@@ -205,6 +205,13 @@ static void bind_auth_next_step(struct composite_context *c)
 
 	if (!composite_is_ok(c)) return;
 
+	if (!more_processing) {
+		if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
+			gensec_want_feature(sec->generic_state,
+					GENSEC_FEATURE_SIGN_PKT_HEADER);
+		}
+	}
+
 	if (state->out_auth_info.credentials.length == 0) {
 		composite_done(c);
 		return;
@@ -271,10 +278,6 @@ static void bind_auth_recv_bindreply(struct tevent_req *subreq)
 	TALLOC_FREE(subreq);
 	if (!composite_is_ok(c)) return;
 
-	if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
-		gensec_want_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER);
-	}
-
 	if (!state->more_processing) {
 		/* The first gensec_update has not requested a second run, so
 		 * we're done here. */
-- 
1.9.1


From e8d6831f12b8f190f92ea75af6878ff432c62161 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 12 May 2017 07:56:47 +0200
Subject: [PATCH 14/35] s4:rpc_server: simplify the
 GENSEC_FEATURE_SIGN_PKT_HEADER logic

We can directly check this after gensec_start_mech_by_authtype(),
the backend either supports it or not. There's nothing.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/rpc_server/dcesrv_auth.c | 61 ++++++++++++++++++----------------------
 1 file changed, 27 insertions(+), 34 deletions(-)

diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c
index efcb586..7b63598 100644
--- a/source4/rpc_server/dcesrv_auth.c
+++ b/source4/rpc_server/dcesrv_auth.c
@@ -43,6 +43,7 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call)
 	struct ncacn_packet *pkt = &call->pkt;
 	struct dcesrv_connection *dce_conn = call->conn;
 	struct dcesrv_auth *auth = &dce_conn->auth_state;
+	bool want_header_signing = false;
 	NTSTATUS status;
 
 	if (pkt->auth_length == 0) {
@@ -213,6 +214,30 @@ bool dcesrv_auth_bind(struct dcesrv_call_state *call)
 		return false;
 	}
 
+	if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
+		auth->client_hdr_signing = true;
+		want_header_signing = true;
+	}
+
+	if (want_header_signing) {
+		want_header_signing = gensec_have_feature(auth->gensec_security,
+						GENSEC_FEATURE_SIGN_PKT_HEADER);
+	}
+
+	if (want_header_signing) {
+		want_header_signing = lpcfg_parm_bool(dce_conn->dce_ctx->lp_ctx,
+						      NULL,
+						      "dcesrv",
+						      "header signing",
+						      true);
+	}
+
+	if (want_header_signing) {
+		gensec_want_feature(auth->gensec_security,
+				    GENSEC_FEATURE_SIGN_PKT_HEADER);
+		auth->hdr_signing = true;
+	}
+
 	return true;
 }
 
@@ -224,7 +249,6 @@ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packe
 {
 	struct dcesrv_connection *dce_conn = call->conn;
 	NTSTATUS status;
-	bool want_header_signing = false;
 
 	dce_conn->allow_alter = true;
 	dce_conn->allow_auth3 = true;
@@ -240,13 +264,8 @@ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packe
 		return NT_STATUS_INTERNAL_ERROR;
 	}
 
-	if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN) {
-		dce_conn->auth_state.client_hdr_signing = true;
-		want_header_signing = true;
-	}
-
-	if (!lpcfg_parm_bool(call->conn->dce_ctx->lp_ctx, NULL, "dcesrv","header signing", true)) {
-		want_header_signing = false;
+	if (dce_conn->auth_state.hdr_signing) {
+		pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
 	}
 
 	call->_out_auth_info = (struct dcerpc_auth) {
@@ -272,36 +291,10 @@ NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packe
 		dce_conn->auth_state.auth_finished = true;
 		dce_conn->allow_request = true;
 
-		if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
-					 GENSEC_FEATURE_SIGN_PKT_HEADER))
-		{
-			want_header_signing = false;
-		}
-
-		if (want_header_signing) {
-			gensec_want_feature(dce_conn->auth_state.gensec_security,
-					    GENSEC_FEATURE_SIGN_PKT_HEADER);
-			dce_conn->auth_state.hdr_signing = true;
-			pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
-		}
-
 		/* Now that we are authenticated, go back to the generic session key... */
 		dce_conn->auth_state.session_key = dcesrv_generic_session_key;
 		return NT_STATUS_OK;
 	} else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-		if (!gensec_have_feature(dce_conn->auth_state.gensec_security,
-					 GENSEC_FEATURE_SIGN_PKT_HEADER))
-		{
-			want_header_signing = false;
-		}
-
-		if (want_header_signing) {
-			gensec_want_feature(dce_conn->auth_state.gensec_security,
-					    GENSEC_FEATURE_SIGN_PKT_HEADER);
-			dce_conn->auth_state.hdr_signing = true;
-			pkt->pfc_flags |= DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN;
-		}
-
 		return NT_STATUS_OK;
 	} else {
 		DEBUG(4, ("GENSEC mech rejected the incoming authentication at bind_ack: %s\n",
-- 
1.9.1


From f2b7b28f67beb7885b9a6b0cb93bb4920da97226 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 00:05:02 +0200
Subject: [PATCH 15/35] auth/gensec: make gensec_start_mech() static

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.h       | 1 -
 auth/gensec/gensec_start.c | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/auth/gensec/gensec.h b/auth/gensec/gensec.h
index 9a2bd96..da3e0fd 100644
--- a/auth/gensec/gensec.h
+++ b/auth/gensec/gensec.h
@@ -268,7 +268,6 @@ NTSTATUS gensec_sign_packet(struct gensec_security *gensec_security,
 			    const uint8_t *data, size_t length,
 			    const uint8_t *whole_pdu, size_t pdu_length,
 			    DATA_BLOB *sig);
-NTSTATUS gensec_start_mech(struct gensec_security *gensec_security);
 NTSTATUS gensec_start_mech_by_authtype(struct gensec_security *gensec_security,
 				       uint8_t auth_type, uint8_t auth_level);
 const char *gensec_get_name_by_authtype(struct gensec_security *gensec_security, uint8_t authtype);
diff --git a/auth/gensec/gensec_start.c b/auth/gensec/gensec_start.c
index 0f7e826..4d33115 100644
--- a/auth/gensec/gensec_start.c
+++ b/auth/gensec/gensec_start.c
@@ -659,7 +659,7 @@ _PUBLIC_ NTSTATUS gensec_server_start(TALLOC_CTX *mem_ctx,
 	return status;
 }
 
-NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
+static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
 {
 	NTSTATUS status;
 
-- 
1.9.1


From b43cb7758911f265f1d578255067e95f693d9960 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 00:05:29 +0200
Subject: [PATCH 16/35] auth/gensec: reset existing context on
 gensec_start_mech()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec_start.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/auth/gensec/gensec_start.c b/auth/gensec/gensec_start.c
index 4d33115..f10f86d 100644
--- a/auth/gensec/gensec_start.c
+++ b/auth/gensec/gensec_start.c
@@ -663,6 +663,14 @@ static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
 {
 	NTSTATUS status;
 
+	/*
+	 * Callers sometimes just reuse a context, we should
+	 * clear the internal state before starting it again.
+	 */
+	talloc_unlink(gensec_security, gensec_security->private_data);
+	gensec_security->private_data = NULL;
+
+
 	if (gensec_security->credentials) {
 		const char *forced_mech = cli_credentials_get_forced_sasl_mech(gensec_security->credentials);
 		if (forced_mech &&
-- 
1.9.1


From 605142e4acce62204345f5d5327c71f9e41873ea Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 30 Dec 2016 17:54:12 +0100
Subject: [PATCH 17/35] auth/gensec: add gensec_child_* helper functions

They will be used to simplify the spnego backend
and maybe of some use for a future negoex backend.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.c          |   9 ++
 auth/gensec/gensec_internal.h |  46 ++++++++++
 auth/gensec/gensec_start.c    |  49 +++++++++++
 auth/gensec/gensec_util.c     | 196 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 300 insertions(+)

diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c
index 23d762b..9b1661e 100644
--- a/auth/gensec/gensec.c
+++ b/auth/gensec/gensec.c
@@ -330,6 +330,10 @@ _PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security,
 	struct tevent_req *subreq = NULL;
 	bool ok;
 
+	if (gensec_security->child_security != NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
 	if (ops->update_send == NULL) {
 
 		if (ev == NULL) {
@@ -480,6 +484,11 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 	state->ops = gensec_security->ops;
 	state->gensec_security = gensec_security;
 
+	if (gensec_security->child_security != NULL) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+		return tevent_req_post(req, ev);
+	}
+
 	if (state->ops->update_send == NULL) {
 		state->in = in;
 		state->im = tevent_create_immediate(state);
diff --git a/auth/gensec/gensec_internal.h b/auth/gensec/gensec_internal.h
index 26c9817..9c160d3 100644
--- a/auth/gensec/gensec_internal.h
+++ b/auth/gensec/gensec_internal.h
@@ -114,6 +114,7 @@ struct gensec_security {
 	 * PAC is found) */
 	struct auth4_context *auth_context;
 
+	struct gensec_security *parent_security;
 	struct gensec_security *child_security;
 };
 
@@ -129,4 +130,49 @@ NTSTATUS gensec_may_reset_crypto(struct gensec_security *gensec_security,
 
 const char *gensec_final_auth_type(struct gensec_security *gensec_security);
 
+NTSTATUS gensec_child_ready(struct gensec_security *parent,
+			    struct gensec_security *child);
+void gensec_child_want_feature(struct gensec_security *gensec_security,
+			       uint32_t feature);
+bool gensec_child_have_feature(struct gensec_security *gensec_security,
+			       uint32_t feature);
+NTSTATUS gensec_child_unseal_packet(struct gensec_security *gensec_security,
+				    uint8_t *data, size_t length,
+				    const uint8_t *whole_pdu, size_t pdu_length,
+				    const DATA_BLOB *sig);
+NTSTATUS gensec_child_check_packet(struct gensec_security *gensec_security,
+				   const uint8_t *data, size_t length,
+				   const uint8_t *whole_pdu, size_t pdu_length,
+				   const DATA_BLOB *sig);
+NTSTATUS gensec_child_seal_packet(struct gensec_security *gensec_security,
+				  TALLOC_CTX *mem_ctx,
+				  uint8_t *data, size_t length,
+				  const uint8_t *whole_pdu, size_t pdu_length,
+				  DATA_BLOB *sig);
+NTSTATUS gensec_child_sign_packet(struct gensec_security *gensec_security,
+				  TALLOC_CTX *mem_ctx,
+				  const uint8_t *data, size_t length,
+				  const uint8_t *whole_pdu, size_t pdu_length,
+				  DATA_BLOB *sig);
+NTSTATUS gensec_child_wrap(struct gensec_security *gensec_security,
+			   TALLOC_CTX *mem_ctx,
+			   const DATA_BLOB *in,
+			   DATA_BLOB *out);
+NTSTATUS gensec_child_unwrap(struct gensec_security *gensec_security,
+			     TALLOC_CTX *mem_ctx,
+			     const DATA_BLOB *in,
+			     DATA_BLOB *out);
+size_t gensec_child_sig_size(struct gensec_security *gensec_security,
+			     size_t data_size);
+size_t gensec_child_max_input_size(struct gensec_security *gensec_security);
+size_t gensec_child_max_wrapped_size(struct gensec_security *gensec_security);
+NTSTATUS gensec_child_session_key(struct gensec_security *gensec_security,
+				  TALLOC_CTX *mem_ctx,
+				  DATA_BLOB *session_key);
+NTSTATUS gensec_child_session_info(struct gensec_security *gensec_security,
+				   TALLOC_CTX *mem_ctx,
+				   struct auth_session_info **session_info);
+NTTIME gensec_child_expire_time(struct gensec_security *gensec_security);
+const char *gensec_child_final_auth_type(struct gensec_security *gensec_security);
+
 #endif /* __GENSEC_H__ */
diff --git a/auth/gensec/gensec_start.c b/auth/gensec/gensec_start.c
index f10f86d..c1affd62 100644
--- a/auth/gensec/gensec_start.c
+++ b/auth/gensec/gensec_start.c
@@ -545,6 +545,25 @@ _PUBLIC_ const char **gensec_security_oids(struct gensec_security *gensec_securi
 	return gensec_security_oids_from_ops(gensec_security, mem_ctx, ops, skip);
 }
 
+static int gensec_security_destructor(struct gensec_security *gctx)
+{
+	if (gctx->parent_security != NULL) {
+		if (gctx->parent_security->child_security == gctx) {
+			gctx->parent_security->child_security = NULL;
+		}
+		gctx->parent_security = NULL;
+	}
+
+	if (gctx->child_security != NULL) {
+		if (gctx->child_security->parent_security == gctx) {
+			gctx->child_security->parent_security = NULL;
+		}
+		gctx->child_security = NULL;
+	}
+
+	return 0;
+}
+
 /**
   Start the GENSEC system, returning a context pointer.
   @param mem_ctx The parent TALLOC memory context.
@@ -571,6 +590,7 @@ static NTSTATUS gensec_start(TALLOC_CTX *mem_ctx,
 	 * from it */
 	(*gensec_security)->auth_context = talloc_reference(*gensec_security, auth_context);
 
+	talloc_set_destructor((*gensec_security), gensec_security_destructor);
 	return NT_STATUS_OK;
 }
 
@@ -586,6 +606,10 @@ _PUBLIC_ NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
 				 struct gensec_security *parent,
 				 struct gensec_security **gensec_security)
 {
+	if (parent->child_security != NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
 	(*gensec_security) = talloc_zero(mem_ctx, struct gensec_security);
 	NT_STATUS_HAVE_NO_MEMORY(*gensec_security);
 
@@ -601,6 +625,23 @@ _PUBLIC_ NTSTATUS gensec_subcontext_start(TALLOC_CTX *mem_ctx,
 	(*gensec_security)->settings = talloc_reference(*gensec_security, parent->settings);
 	(*gensec_security)->auth_context = talloc_reference(*gensec_security, parent->auth_context);
 
+	talloc_set_destructor((*gensec_security), gensec_security_destructor);
+	return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS gensec_child_ready(struct gensec_security *parent,
+				     struct gensec_security *child)
+{
+	if (parent->child_security != NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	if (child->parent_security != NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	parent->child_security = child;
+	child->parent_security = parent;
 	return NT_STATUS_OK;
 }
 
@@ -670,6 +711,14 @@ static NTSTATUS gensec_start_mech(struct gensec_security *gensec_security)
 	talloc_unlink(gensec_security, gensec_security->private_data);
 	gensec_security->private_data = NULL;
 
+	if (gensec_security->child_security != NULL) {
+		/*
+		 * The talloc_unlink(.., gensec_security->private_data)
+		 * should have cleared this via
+		 * gensec_security_destructor().
+		 */
+		return NT_STATUS_INTERNAL_ERROR;
+	}
 
 	if (gensec_security->credentials) {
 		const char *forced_mech = cli_credentials_get_forced_sasl_mech(gensec_security->credentials);
diff --git a/auth/gensec/gensec_util.c b/auth/gensec/gensec_util.c
index da2e2e5..ca5e581 100644
--- a/auth/gensec/gensec_util.c
+++ b/auth/gensec/gensec_util.c
@@ -110,3 +110,199 @@ NTSTATUS gensec_magic_check_krb5_oid(struct gensec_security *unused,
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 }
+
+void gensec_child_want_feature(struct gensec_security *gensec_security,
+			       uint32_t feature)
+{
+	struct gensec_security *child_security = gensec_security->child_security;
+
+	gensec_security->want_features |= feature;
+	if (child_security == NULL) {
+		return;
+	}
+	gensec_want_feature(child_security, feature);
+}
+
+bool gensec_child_have_feature(struct gensec_security *gensec_security,
+			       uint32_t feature)
+{
+	struct gensec_security *child_security = gensec_security->child_security;
+
+	if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
+		/*
+		 * All mechs with sub (child) mechs need to provide DCERPC
+		 * header signing! This is required because the negotiation
+		 * of header signing is done before the authentication
+		 * is completed.
+		 */
+		return true;
+	}
+
+	if (child_security == NULL) {
+		return false;
+	}
+
+	return gensec_have_feature(child_security, feature);
+}
+
+NTSTATUS gensec_child_unseal_packet(struct gensec_security *gensec_security,
+				    uint8_t *data, size_t length,
+				    const uint8_t *whole_pdu, size_t pdu_length,
+				    const DATA_BLOB *sig)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_unseal_packet(gensec_security->child_security,
+				    data, length,
+				    whole_pdu, pdu_length,
+				    sig);
+}
+
+NTSTATUS gensec_child_check_packet(struct gensec_security *gensec_security,
+				   const uint8_t *data, size_t length,
+				   const uint8_t *whole_pdu, size_t pdu_length,
+				   const DATA_BLOB *sig)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_check_packet(gensec_security->child_security,
+				   data, length,
+				   whole_pdu, pdu_length,
+				   sig);
+}
+
+NTSTATUS gensec_child_seal_packet(struct gensec_security *gensec_security,
+				  TALLOC_CTX *mem_ctx,
+				  uint8_t *data, size_t length,
+				  const uint8_t *whole_pdu, size_t pdu_length,
+				  DATA_BLOB *sig)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_seal_packet(gensec_security->child_security,
+				  mem_ctx,
+				  data, length,
+				  whole_pdu, pdu_length,
+				  sig);
+}
+
+NTSTATUS gensec_child_sign_packet(struct gensec_security *gensec_security,
+				  TALLOC_CTX *mem_ctx,
+				  const uint8_t *data, size_t length,
+				  const uint8_t *whole_pdu, size_t pdu_length,
+				  DATA_BLOB *sig)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_sign_packet(gensec_security->child_security,
+				  mem_ctx,
+				  data, length,
+				  whole_pdu, pdu_length,
+				  sig);
+}
+
+NTSTATUS gensec_child_wrap(struct gensec_security *gensec_security,
+			   TALLOC_CTX *mem_ctx,
+			   const DATA_BLOB *in,
+			   DATA_BLOB *out)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_wrap(gensec_security->child_security,
+			   mem_ctx, in, out);
+}
+
+NTSTATUS gensec_child_unwrap(struct gensec_security *gensec_security,
+			     TALLOC_CTX *mem_ctx,
+			     const DATA_BLOB *in,
+			     DATA_BLOB *out)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_unwrap(gensec_security->child_security,
+			     mem_ctx, in, out);
+}
+
+size_t gensec_child_sig_size(struct gensec_security *gensec_security,
+			     size_t data_size)
+{
+	if (gensec_security->child_security == NULL) {
+		return 0;
+	}
+
+	return gensec_sig_size(gensec_security->child_security, data_size);
+}
+
+size_t gensec_child_max_input_size(struct gensec_security *gensec_security)
+{
+	if (gensec_security->child_security == NULL) {
+		return 0;
+	}
+
+	return gensec_max_input_size(gensec_security->child_security);
+}
+
+size_t gensec_child_max_wrapped_size(struct gensec_security *gensec_security)
+{
+	if (gensec_security->child_security == NULL) {
+		return 0;
+	}
+
+	return gensec_max_wrapped_size(gensec_security->child_security);
+}
+
+NTSTATUS gensec_child_session_key(struct gensec_security *gensec_security,
+				  TALLOC_CTX *mem_ctx,
+				  DATA_BLOB *session_key)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_session_key(gensec_security->child_security,
+				  mem_ctx,
+				  session_key);
+}
+
+NTSTATUS gensec_child_session_info(struct gensec_security *gensec_security,
+				   TALLOC_CTX *mem_ctx,
+				   struct auth_session_info **session_info)
+{
+	if (gensec_security->child_security == NULL) {
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	return gensec_session_info(gensec_security->child_security,
+				   mem_ctx,
+				   session_info);
+}
+
+NTTIME gensec_child_expire_time(struct gensec_security *gensec_security)
+{
+	if (gensec_security->child_security == NULL) {
+		return GENSEC_EXPIRE_TIME_INFINITY;
+	}
+
+	return gensec_expire_time(gensec_security->child_security);
+}
+
+const char *gensec_child_final_auth_type(struct gensec_security *gensec_security)
+{
+	if (gensec_security->child_security == NULL) {
+		return "NONE";
+	}
+
+	return gensec_final_auth_type(gensec_security->child_security);
+}
-- 
1.9.1


From 045cbdc91a391c4908ff0a8dc2b4e628005db139 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 30 Dec 2016 18:05:17 +0100
Subject: [PATCH 18/35] auth/spnego: let spnego.c use the new gensec_child_*
 helper functions

This means we no longer allow operations on a half finished authentication,
it's activated by gensec_child_ready().

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/spnego.c | 294 +++++----------------------------------------------
 1 file changed, 26 insertions(+), 268 deletions(-)

diff --git a/auth/gensec/spnego.c b/auth/gensec/spnego.c
index ed7f3d7..db791ce 100644
--- a/auth/gensec/spnego.c
+++ b/auth/gensec/spnego.c
@@ -124,183 +124,6 @@ static NTSTATUS gensec_spnego_server_start(struct gensec_security *gensec_securi
 	return NT_STATUS_OK;
 }
 
-/*
-  wrappers for the spnego_*() functions
-*/
-static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security, 
-					    uint8_t *data, size_t length, 
-					    const uint8_t *whole_pdu, size_t pdu_length, 
-					    const DATA_BLOB *sig)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_unseal_packet(spnego_state->sub_sec_security, 
-				    data, length, 
-				    whole_pdu, pdu_length,
-				    sig); 
-}
-
-static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security, 
-					   const uint8_t *data, size_t length, 
-					   const uint8_t *whole_pdu, size_t pdu_length, 
-					   const DATA_BLOB *sig)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_check_packet(spnego_state->sub_sec_security, 
-				   data, length, 
-				   whole_pdu, pdu_length,
-				   sig);
-}
-
-static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security, 
-					  TALLOC_CTX *mem_ctx, 
-					  uint8_t *data, size_t length, 
-					  const uint8_t *whole_pdu, size_t pdu_length, 
-					  DATA_BLOB *sig)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_seal_packet(spnego_state->sub_sec_security, 
-				  mem_ctx, 
-				  data, length, 
-				  whole_pdu, pdu_length,
-				  sig);
-}
-
-static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security, 
-					  TALLOC_CTX *mem_ctx, 
-					  const uint8_t *data, size_t length, 
-					  const uint8_t *whole_pdu, size_t pdu_length, 
-					  DATA_BLOB *sig)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_sign_packet(spnego_state->sub_sec_security, 
-				  mem_ctx, 
-				  data, length, 
-				  whole_pdu, pdu_length,
-				  sig);
-}
-
-static NTSTATUS gensec_spnego_wrap(struct gensec_security *gensec_security, 
-				   TALLOC_CTX *mem_ctx, 
-				   const DATA_BLOB *in, 
-				   DATA_BLOB *out)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		DEBUG(1, ("gensec_spnego_wrap: wrong state for wrap\n"));
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_wrap(spnego_state->sub_sec_security, 
-			   mem_ctx, in, out);
-}
-
-static NTSTATUS gensec_spnego_unwrap(struct gensec_security *gensec_security, 
-				     TALLOC_CTX *mem_ctx, 
-				     const DATA_BLOB *in, 
-				     DATA_BLOB *out)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		DEBUG(1, ("gensec_spnego_unwrap: wrong state for unwrap\n"));
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_unwrap(spnego_state->sub_sec_security, 
-			     mem_ctx, in, out);
-}
-
-static size_t gensec_spnego_sig_size(struct gensec_security *gensec_security, size_t data_size) 
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return 0;
-	}
-
-	return gensec_sig_size(spnego_state->sub_sec_security, data_size);
-}
-
-static size_t gensec_spnego_max_input_size(struct gensec_security *gensec_security) 
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return 0;
-	}
-
-	return gensec_max_input_size(spnego_state->sub_sec_security);
-}
-
-static size_t gensec_spnego_max_wrapped_size(struct gensec_security *gensec_security) 
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (spnego_state->state_position != SPNEGO_DONE 
-	    && spnego_state->state_position != SPNEGO_FALLBACK) {
-		return 0;
-	}
-
-	return gensec_max_wrapped_size(spnego_state->sub_sec_security);
-}
-
-static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security, 
-					  TALLOC_CTX *mem_ctx,
-					  DATA_BLOB *session_key)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-	if (!spnego_state->sub_sec_security) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_session_key(spnego_state->sub_sec_security, 
-				  mem_ctx,
-				  session_key);
-}
-
-static NTSTATUS gensec_spnego_session_info(struct gensec_security *gensec_security,
-					   TALLOC_CTX *mem_ctx,
-					   struct auth_session_info **session_info)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-	if (!spnego_state->sub_sec_security) {
-		return NT_STATUS_INVALID_PARAMETER;
-	}
-
-	return gensec_session_info(spnego_state->sub_sec_security, 
-				   mem_ctx,
-				   session_info);
-}
-
 /** Fallback to another GENSEC mechanism, based on magic strings 
  *
  * This is the 'fallback' case, where we don't get SPNEGO, and have to
@@ -1513,24 +1336,23 @@ static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security
 
 	*_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;
+		if (spnego_state->out_frag.length > 0) {
+			talloc_steal(out_mem_ctx, _out->data);
+			spnego_state->out_frag = data_blob_null;
+		}
+
+		if (!NT_STATUS_IS_OK(spnego_state->out_status)) {
+			return spnego_state->out_status;
+		}
+
+		return gensec_child_ready(gensec_security,
+					  spnego_state->sub_sec_security);
 	}
 
 	out = spnego_state->out_frag;
@@ -1595,8 +1417,6 @@ static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_secu
 	if (NT_STATUS_IS_OK(status)) {
 		bool reset_full = true;
 
-		gensec_security->child_security = spnego_state->sub_sec_security;
-
 		reset_full = !spnego_state->done_mic_check;
 
 		status = gensec_may_reset_crypto(spnego_state->sub_sec_security,
@@ -1614,68 +1434,6 @@ static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_secu
 					out);
 }
 
-static void gensec_spnego_want_feature(struct gensec_security *gensec_security,
-				       uint32_t feature)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	gensec_security->want_features |= feature;
-	if (!spnego_state || !spnego_state->sub_sec_security) {
-		return;
-	}
-
-	gensec_want_feature(spnego_state->sub_sec_security,
-			    feature);
-}
-
-static bool gensec_spnego_have_feature(struct gensec_security *gensec_security,
-				       uint32_t feature) 
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (feature & GENSEC_FEATURE_SIGN_PKT_HEADER) {
-		/*
-		 * All mechs with sub (child) mechs need to provide DCERPC
-		 * header signing! This is required because the negotiation
-		 * of header signing is done before the authentication
-		 * is completed.
-		 *
-		 * Currently all our backends support DCERPC with:
-		 * GENSEC_FEATURE_SIGN_PKT_HEADER.
-		 */
-		return true;
-	}
-
-	if (!spnego_state->sub_sec_security) {
-		return false;
-	}
-
-	return gensec_have_feature(spnego_state->sub_sec_security, 
-				   feature);
-}
-
-static NTTIME gensec_spnego_expire_time(struct gensec_security *gensec_security)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (!spnego_state->sub_sec_security) {
-		return GENSEC_EXPIRE_TIME_INFINITY;
-	}
-
-	return gensec_expire_time(spnego_state->sub_sec_security);
-}
-
-static const char *gensec_spnego_final_auth_type(struct gensec_security *gensec_security)
-{
-	struct spnego_state *spnego_state = (struct spnego_state *)gensec_security->private_data;
-
-	if (!spnego_state->sub_sec_security) {
-		return "NONE";
-	} else {
-		return gensec_final_auth_type(spnego_state->sub_sec_security);
-	}
-}
-
 static const char *gensec_spnego_oids[] = { 
 	GENSEC_OID_SPNEGO,
 	NULL 
@@ -1689,21 +1447,21 @@ static const struct gensec_security_ops gensec_spnego_security_ops = {
 	.client_start     = gensec_spnego_client_start,
 	.server_start     = gensec_spnego_server_start,
 	.update 	  = gensec_spnego_update_wrapper,
-	.seal_packet	  = gensec_spnego_seal_packet,
-	.sign_packet	  = gensec_spnego_sign_packet,
-	.sig_size	  = gensec_spnego_sig_size,
-	.max_wrapped_size = gensec_spnego_max_wrapped_size,
-	.max_input_size	  = gensec_spnego_max_input_size,
-	.check_packet	  = gensec_spnego_check_packet,
-	.unseal_packet	  = gensec_spnego_unseal_packet,
-	.wrap             = gensec_spnego_wrap,
-	.unwrap           = gensec_spnego_unwrap,
-	.session_key	  = gensec_spnego_session_key,
-	.session_info     = gensec_spnego_session_info,
-	.want_feature     = gensec_spnego_want_feature,
-	.have_feature     = gensec_spnego_have_feature,
-	.expire_time      = gensec_spnego_expire_time,
-	.final_auth_type  = gensec_spnego_final_auth_type,
+	.seal_packet	  = gensec_child_seal_packet,
+	.sign_packet	  = gensec_child_sign_packet,
+	.sig_size	  = gensec_child_sig_size,
+	.max_wrapped_size = gensec_child_max_wrapped_size,
+	.max_input_size	  = gensec_child_max_input_size,
+	.check_packet	  = gensec_child_check_packet,
+	.unseal_packet	  = gensec_child_unseal_packet,
+	.wrap             = gensec_child_wrap,
+	.unwrap           = gensec_child_unwrap,
+	.session_key	  = gensec_child_session_key,
+	.session_info     = gensec_child_session_info,
+	.want_feature     = gensec_child_want_feature,
+	.have_feature     = gensec_child_have_feature,
+	.expire_time      = gensec_child_expire_time,
+	.final_auth_type  = gensec_child_final_auth_type,
 	.enabled          = true,
 	.priority         = GENSEC_SPNEGO
 };
-- 
1.9.1


From c988d0f2e792762636d93111d296fbe3ea862862 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 10 May 2017 17:12:14 +0200
Subject: [PATCH 19/35] auth/spnego: make sure a fatal error or the final
 success make the state as SPNEGO_DONE

This means any further gensec_update() will fail with
NT_STATUS_INVALID_PARAMETER.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/spnego.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/auth/gensec/spnego.c b/auth/gensec/spnego.c
index db791ce..aec4b5e 100644
--- a/auth/gensec/spnego.c
+++ b/auth/gensec/spnego.c
@@ -1351,6 +1351,10 @@ static NTSTATUS gensec_spnego_update_out(struct gensec_security *gensec_security
 			return spnego_state->out_status;
 		}
 
+		/*
+		 * We're completely done, further updates are not allowed.
+		 */
+		spnego_state->state_position = SPNEGO_DONE;
 		return gensec_child_ready(gensec_security,
 					  spnego_state->sub_sec_security);
 	}
@@ -1424,6 +1428,10 @@ static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_secu
 	}
 	if (!NT_STATUS_IS_OK(status) &&
 	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		/*
+		 * A fatal error, further updates are not allowed.
+		 */
+		spnego_state->state_position = SPNEGO_DONE;
 		return status;
 	}
 
-- 
1.9.1


From 805c751128af2105cada6bbe2e3f7c5160f4e7f2 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 20/35] s4:gensec/http_ntlm: add implement
 gensec_http_ntlm_update_send/recv()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/gensec/ntlm.c | 126 +++++++++++++++++++++++++++++++----------
 1 file changed, 95 insertions(+), 31 deletions(-)

diff --git a/source4/lib/http/gensec/ntlm.c b/source4/lib/http/gensec/ntlm.c
index cc4b0f0..7d692ed 100644
--- a/source4/lib/http/gensec/ntlm.c
+++ b/source4/lib/http/gensec/ntlm.c
@@ -20,6 +20,8 @@
 */
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "auth/auth.h"
 #include "auth/gensec/gensec.h"
 #include "auth/gensec/gensec_internal.h"
@@ -50,50 +52,111 @@ static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
 	return gensec_start_mech_by_oid(state->sub, GENSEC_OID_NTLMSSP);
 }
 
-static NTSTATUS gensec_http_ntlm_update(struct gensec_security *gensec_ctx,
-					TALLOC_CTX *mem_ctx,
-					struct tevent_context *ev,
-					const DATA_BLOB in,
-					DATA_BLOB *out)
-{
-	NTSTATUS status;
-	struct gensec_http_ntlm_state *state;
+struct gensec_http_ntlm_update_state {
 	DATA_BLOB ntlm_in;
+	NTSTATUS status;
+	DATA_BLOB out;
+};
+
+static void gensec_http_ntlm_update_done(struct tevent_req *subreq);
 
-	state = talloc_get_type_abort(gensec_ctx->private_data,
+static struct tevent_req *gensec_http_ntlm_update_send(TALLOC_CTX *mem_ctx,
+						    struct tevent_context *ev,
+						    struct gensec_security *gensec_ctx,
+						    const DATA_BLOB in)
+{
+	struct gensec_http_ntlm_state *http_ntlm =
+		talloc_get_type_abort(gensec_ctx->private_data,
 				      struct gensec_http_ntlm_state);
+	struct tevent_req *req = NULL;
+	struct gensec_http_ntlm_update_state *state = NULL;
+	struct tevent_req *subreq = NULL;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_http_ntlm_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
 
 	if (in.length) {
 		if (strncasecmp((char *)in.data, "NTLM ", 5) != 0) {
-			return NT_STATUS_INVALID_PARAMETER;
+			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+			return tevent_req_post(req, ev);
 		}
-		ntlm_in = base64_decode_data_blob_talloc(mem_ctx,
-							 (char *)&in.data[5]);
-	} else {
-		ntlm_in = data_blob_null;
+		state->ntlm_in = base64_decode_data_blob_talloc(state,
+							(char *)&in.data[5]);
 	}
 
-	status = gensec_update_ev(state->sub, mem_ctx, ev, ntlm_in, out);
-	if (NT_STATUS_IS_OK(status) ||
-	    NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-		char *tmp, *b64;
-		b64 = base64_encode_data_blob(mem_ctx, *out);
-		if (b64 == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
+	subreq = gensec_update_send(state, ev,
+				    http_ntlm->sub,
+				    state->ntlm_in);
+	if (tevent_req_nomem(subreq, req)) {
+		return tevent_req_post(req, ev);
+	}
+	tevent_req_set_callback(subreq, gensec_http_ntlm_update_done, req);
 
-		tmp = talloc_asprintf(mem_ctx, "NTLM %s", b64);
-		TALLOC_FREE(b64);
-		if (tmp == NULL) {
-			return NT_STATUS_NO_MEMORY;
-		}
-		*out = data_blob_string_const(tmp);
+	return req;
+}
+
+static void gensec_http_ntlm_update_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req =
+		tevent_req_callback_data(subreq,
+		struct tevent_req);
+	struct gensec_http_ntlm_update_state *state =
+		tevent_req_data(req,
+		struct gensec_http_ntlm_update_state);
+	NTSTATUS status;
+	DATA_BLOB ntlm_out;
+	char *b64 = NULL;
+	char *str = NULL;
+
+	status = gensec_update_recv(subreq, state, &ntlm_out);
+	TALLOC_FREE(subreq);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		status = NT_STATUS_OK;
+	}
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	b64 = base64_encode_data_blob(state, ntlm_out);
+	data_blob_free(&ntlm_out);
+	if (tevent_req_nomem(b64, req)) {
+		return;
 	}
 
-	if (ntlm_in.data) {
-		data_blob_free(&ntlm_in);
+	str = talloc_asprintf(state, "NTLM %s", b64);
+	TALLOC_FREE(b64);
+	if (tevent_req_nomem(str, req)) {
+		return;
+	}
+
+	state->out = data_blob_string_const(str);
+	return;
+}
+
+static NTSTATUS gensec_http_ntlm_update_recv(struct tevent_req *req,
+					     TALLOC_CTX *out_mem_ctx,
+					     DATA_BLOB *out)
+{
+	struct gensec_http_ntlm_update_state *state =
+		tevent_req_data(req,
+		struct gensec_http_ntlm_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
 	}
 
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
 	return status;
 }
 
@@ -101,7 +164,8 @@ static const struct gensec_security_ops gensec_http_ntlm_security_ops = {
 	.name           = "http_ntlm",
 	.auth_type      = 0,
 	.client_start   = gensec_http_ntlm_client_start,
-	.update         = gensec_http_ntlm_update,
+	.update_send    = gensec_http_ntlm_update_send,
+	.update_recv    = gensec_http_ntlm_update_recv,
 	.enabled        = true,
 	.priority       = GENSEC_EXTERNAL,
 };
-- 
1.9.1


From ec73a63ba6033a736dc277d94746ee58c2cd0fc9 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 21/35] s4:gensec/http_basic: add simple
 gensec_http_basic_update_send/recv() wrapper functions

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/gensec/basic.c | 78 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 72 insertions(+), 6 deletions(-)

diff --git a/source4/lib/http/gensec/basic.c b/source4/lib/http/gensec/basic.c
index 6077aca..c7a1bbb 100644
--- a/source4/lib/http/gensec/basic.c
+++ b/source4/lib/http/gensec/basic.c
@@ -20,6 +20,8 @@
 */
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "auth/auth.h"
 #include "auth/gensec/gensec.h"
 #include "auth/gensec/gensec_internal.h"
@@ -51,11 +53,51 @@ static NTSTATUS gensec_http_basic_client_start(struct gensec_security *gensec)
 	return NT_STATUS_OK;
 }
 
-static NTSTATUS gensec_http_basic_update(struct gensec_security *gensec_ctx,
-					 TALLOC_CTX *mem_ctx,
-					 struct tevent_context *ev,
-					 const DATA_BLOB in,
-					 DATA_BLOB *out)
+struct gensec_http_basic_update_state {
+	NTSTATUS status;
+	DATA_BLOB out;
+};
+
+static NTSTATUS gensec_http_basic_update_internal(struct gensec_security *gensec_ctx,
+						  TALLOC_CTX *mem_ctx,
+						  const DATA_BLOB in,
+						  DATA_BLOB *out);
+
+static struct tevent_req *gensec_http_basic_update_send(TALLOC_CTX *mem_ctx,
+						    struct tevent_context *ev,
+						    struct gensec_security *gensec_security,
+						    const DATA_BLOB in)
+{
+	struct tevent_req *req = NULL;
+	struct gensec_http_basic_update_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_http_basic_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	status = gensec_http_basic_update_internal(gensec_security,
+						   state, in,
+						   &state->out);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS gensec_http_basic_update_internal(struct gensec_security *gensec_ctx,
+						  TALLOC_CTX *mem_ctx,
+						  const DATA_BLOB in,
+						  DATA_BLOB *out)
 {
 	struct gensec_http_basic_state *state;
 	struct cli_credentials *creds;
@@ -114,11 +156,35 @@ static NTSTATUS gensec_http_basic_update(struct gensec_security *gensec_ctx,
 	return NT_STATUS_INTERNAL_ERROR;
 }
 
+static NTSTATUS gensec_http_basic_update_recv(struct tevent_req *req,
+					      TALLOC_CTX *out_mem_ctx,
+					      DATA_BLOB *out)
+{
+	struct gensec_http_basic_update_state *state =
+		tevent_req_data(req,
+		struct gensec_http_basic_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
+	return status;
+}
+
 static const struct gensec_security_ops gensec_http_basic_security_ops = {
 	.name           = "http_basic",
 	.auth_type      = 0,
 	.client_start   = gensec_http_basic_client_start,
-	.update         = gensec_http_basic_update,
+	.update_send    = gensec_http_basic_update_send,
+	.update_recv    = gensec_http_basic_update_recv,
 	.enabled        = true,
 	.priority       = GENSEC_EXTERNAL,
 };
-- 
1.9.1


From efc7e6ef7f4544cb71497f7b1421546d3151ded8 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 22/35] s3:gse: add simple gensec_gse_update_send/recv()
 wrapper functions

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/librpc/crypto/gse.c | 86 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 71 insertions(+), 15 deletions(-)

diff --git a/source3/librpc/crypto/gse.c b/source3/librpc/crypto/gse.c
index 972bbe0..bcda8de 100644
--- a/source3/librpc/crypto/gse.c
+++ b/source3/librpc/crypto/gse.c
@@ -22,6 +22,8 @@
 /* We support only GSSAPI/KRB5 here */
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "gse.h"
 #include "libads/kerberos_proto.h"
 #include "auth/common_auth.h"
@@ -807,21 +809,51 @@ static NTSTATUS gensec_gse_server_start(struct gensec_security *gensec_security)
 	return NT_STATUS_OK;
 }
 
-/**
- * Next state function for the GSE GENSEC mechanism
- *
- * @param gensec_gse_state GSE State
- * @param mem_ctx The TALLOC_CTX for *out to be allocated on
- * @param in The request, as a DATA_BLOB
- * @param out The reply, as an talloc()ed DATA_BLOB, on *mem_ctx
- * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
- *                or NT_STATUS_OK if the user is authenticated.
- */
+struct gensec_gse_update_state {
+	NTSTATUS status;
+	DATA_BLOB out;
+};
 
-static NTSTATUS gensec_gse_update(struct gensec_security *gensec_security,
-				  TALLOC_CTX *mem_ctx,
-				  struct tevent_context *ev,
-				  const DATA_BLOB in, DATA_BLOB *out)
+static NTSTATUS gensec_gse_update_internal(struct gensec_security *gensec_security,
+					   TALLOC_CTX *mem_ctx,
+					   const DATA_BLOB in,
+					   DATA_BLOB *out);
+
+static struct tevent_req *gensec_gse_update_send(TALLOC_CTX *mem_ctx,
+						 struct tevent_context *ev,
+						 struct gensec_security *gensec_security,
+						 const DATA_BLOB in)
+{
+	struct tevent_req *req = NULL;
+	struct gensec_gse_update_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_gse_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	status = gensec_gse_update_internal(gensec_security,
+					    state, in,
+					    &state->out);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS gensec_gse_update_internal(struct gensec_security *gensec_security,
+					   TALLOC_CTX *mem_ctx,
+					   const DATA_BLOB in,
+					   DATA_BLOB *out)
 {
 	NTSTATUS status;
 
@@ -844,6 +876,29 @@ static NTSTATUS gensec_gse_update(struct gensec_security *gensec_security,
 	return NT_STATUS_OK;
 }
 
+static NTSTATUS gensec_gse_update_recv(struct tevent_req *req,
+				       TALLOC_CTX *out_mem_ctx,
+				       DATA_BLOB *out)
+{
+	struct gensec_gse_update_state *state =
+		tevent_req_data(req,
+		struct gensec_gse_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
+	return status;
+}
+
 static NTSTATUS gensec_gse_wrap(struct gensec_security *gensec_security,
 				TALLOC_CTX *mem_ctx,
 				const DATA_BLOB *in,
@@ -1295,7 +1350,8 @@ const struct gensec_security_ops gensec_gse_krb5_security_ops = {
 	.client_start   = gensec_gse_client_start,
 	.server_start   = gensec_gse_server_start,
 	.magic  	= gensec_magic_check_krb5_oid,
-	.update 	= gensec_gse_update,
+	.update_send	= gensec_gse_update_send,
+	.update_recv	= gensec_gse_update_recv,
 	.session_key	= gensec_gse_session_key,
 	.session_info	= gensec_gse_session_info,
 	.sig_size	= gensec_gse_sig_size,
-- 
1.9.1


From 4effb74ebbb81500dc00e373d245199d3962b57f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 23/35] s4:gensec_gssapi: add simple
 gensec_gssapi_update_send/recv() wrapper functions

TODO: we still need to make the internal async.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/gensec/gensec_gssapi.c | 90 +++++++++++++++++++++++++++++--------
 1 file changed, 71 insertions(+), 19 deletions(-)

diff --git a/source4/auth/gensec/gensec_gssapi.c b/source4/auth/gensec/gensec_gssapi.c
index 8c93034..8bc5452 100644
--- a/source4/auth/gensec/gensec_gssapi.c
+++ b/source4/auth/gensec/gensec_gssapi.c
@@ -22,6 +22,8 @@
 */
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "lib/events/events.h"
 #include "system/kerberos.h"
 #include "system/gssapi.h"
@@ -419,22 +421,10 @@ static NTSTATUS gensec_gssapi_sasl_client_start(struct gensec_security *gensec_s
 	return nt_status;
 }
 
-
-/**
- * Next state function for the GSSAPI GENSEC mechanism
- * 
- * @param gensec_gssapi_state GSSAPI State
- * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
- * @param in The request, as a DATA_BLOB
- * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
- * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
- *                or NT_STATUS_OK if the user is authenticated. 
- */
-
-static NTSTATUS gensec_gssapi_update(struct gensec_security *gensec_security, 
-				     TALLOC_CTX *out_mem_ctx,
-				     struct tevent_context *ev,
-				     const DATA_BLOB in, DATA_BLOB *out)
+static NTSTATUS gensec_gssapi_update_internal(struct gensec_security *gensec_security,
+					      TALLOC_CTX *out_mem_ctx,
+					      struct tevent_context *ev,
+					      const DATA_BLOB in, DATA_BLOB *out)
 {
 	struct gensec_gssapi_state *gensec_gssapi_state
 		= talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state);
@@ -1043,6 +1033,65 @@ init_sec_context_done:
 	}
 }
 
+struct gensec_gssapi_update_state {
+	NTSTATUS status;
+	DATA_BLOB out;
+};
+
+static struct tevent_req *gensec_gssapi_update_send(TALLOC_CTX *mem_ctx,
+						    struct tevent_context *ev,
+						    struct gensec_security *gensec_security,
+						    const DATA_BLOB in)
+{
+	struct tevent_req *req = NULL;
+	struct gensec_gssapi_update_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_gssapi_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	status = gensec_gssapi_update_internal(gensec_security,
+					       state, ev, in,
+					       &state->out);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS gensec_gssapi_update_recv(struct tevent_req *req,
+					  TALLOC_CTX *out_mem_ctx,
+					  DATA_BLOB *out)
+{
+	struct gensec_gssapi_update_state *state =
+		tevent_req_data(req,
+		struct gensec_gssapi_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
+	return status;
+}
+
 static NTSTATUS gensec_gssapi_wrap(struct gensec_security *gensec_security, 
 				   TALLOC_CTX *mem_ctx, 
 				   const DATA_BLOB *in, 
@@ -1567,7 +1616,8 @@ static const struct gensec_security_ops gensec_gssapi_spnego_security_ops = {
 	.client_start   = gensec_gssapi_client_start,
 	.server_start   = gensec_gssapi_server_start,
 	.magic  	= gensec_magic_check_krb5_oid,
-	.update 	= gensec_gssapi_update,
+	.update_send	= gensec_gssapi_update_send,
+	.update_recv	= gensec_gssapi_update_recv,
 	.session_key	= gensec_gssapi_session_key,
 	.session_info	= gensec_gssapi_session_info,
 	.sign_packet	= gensec_gssapi_sign_packet,
@@ -1594,7 +1644,8 @@ static const struct gensec_security_ops gensec_gssapi_krb5_security_ops = {
 	.client_start   = gensec_gssapi_client_start,
 	.server_start   = gensec_gssapi_server_start,
 	.magic  	= gensec_magic_check_krb5_oid,
-	.update 	= gensec_gssapi_update,
+	.update_send	= gensec_gssapi_update_send,
+	.update_recv	= gensec_gssapi_update_recv,
 	.session_key	= gensec_gssapi_session_key,
 	.session_info	= gensec_gssapi_session_info,
 	.sig_size	= gensec_gssapi_sig_size,
@@ -1620,7 +1671,8 @@ static const struct gensec_security_ops gensec_gssapi_sasl_krb5_security_ops = {
 	.sasl_name        = "GSSAPI",
 	.client_start     = gensec_gssapi_sasl_client_start,
 	.server_start     = gensec_gssapi_sasl_server_start,
-	.update 	  = gensec_gssapi_update,
+	.update_send      = gensec_gssapi_update_send,
+	.update_recv      = gensec_gssapi_update_recv,
 	.session_key	  = gensec_gssapi_session_key,
 	.session_info	  = gensec_gssapi_session_info,
 	.max_input_size	  = gensec_gssapi_max_input_size,
-- 
1.9.1


From 259ba3d33b1e60cf6b97cddaf6ae61da6b84c9fc Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 24/35] s4:gensec_krb5: add simple
 gensec_krb5_update_send/recv() wrapper functions

TODO: we still need to make the internal async.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/gensec/gensec_krb5.c | 86 +++++++++++++++++++++++++++++++--------
 1 file changed, 69 insertions(+), 17 deletions(-)

diff --git a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c
index fdd3823..86ec23f 100644
--- a/source4/auth/gensec/gensec_krb5.c
+++ b/source4/auth/gensec/gensec_krb5.c
@@ -24,6 +24,8 @@
 */
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "system/kerberos.h"
 #include "auth/kerberos/kerberos.h"
 #include "auth/auth.h"
@@ -512,21 +514,10 @@ static bool gensec_gssapi_parse_krb5_wrap(TALLOC_CTX *mem_ctx, const DATA_BLOB *
 	return ret;
 }
 
-/**
- * Next state function for the Krb5 GENSEC mechanism
- * 
- * @param gensec_krb5_state KRB5 State
- * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
- * @param in The request, as a DATA_BLOB
- * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
- * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 
- *                or NT_STATUS_OK if the user is authenticated. 
- */
-
-static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, 
-				   TALLOC_CTX *out_mem_ctx, 
-				   struct tevent_context *ev,
-				   const DATA_BLOB in, DATA_BLOB *out) 
+static NTSTATUS gensec_krb5_update_internal(struct gensec_security *gensec_security,
+					    TALLOC_CTX *out_mem_ctx,
+					    struct tevent_context *ev,
+					    const DATA_BLOB in, DATA_BLOB *out)
 {
 	struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
 	krb5_error_code ret = 0;
@@ -688,6 +679,65 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security,
 	}
 }
 
+struct gensec_krb5_update_state {
+	NTSTATUS status;
+	DATA_BLOB out;
+};
+
+static struct tevent_req *gensec_krb5_update_send(TALLOC_CTX *mem_ctx,
+						  struct tevent_context *ev,
+						  struct gensec_security *gensec_security,
+						  const DATA_BLOB in)
+{
+	struct tevent_req *req = NULL;
+	struct gensec_krb5_update_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_krb5_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	status = gensec_krb5_update_internal(gensec_security,
+					     state, ev, in,
+					     &state->out);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS gensec_krb5_update_recv(struct tevent_req *req,
+					TALLOC_CTX *out_mem_ctx,
+					DATA_BLOB *out)
+{
+	struct gensec_krb5_update_state *state =
+		tevent_req_data(req,
+		struct gensec_krb5_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
+	return status;
+}
+
 static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, 
 					TALLOC_CTX *mem_ctx,
 					DATA_BLOB *session_key) 
@@ -1055,7 +1105,8 @@ static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = {
 	.oid            = gensec_krb5_oids,
 	.client_start   = gensec_fake_gssapi_krb5_client_start,
 	.server_start   = gensec_fake_gssapi_krb5_server_start,
-	.update 	= gensec_krb5_update,
+	.update_send	= gensec_krb5_update_send,
+	.update_recv	= gensec_krb5_update_recv,
 	.magic   	= gensec_magic_check_krb5_oid,
 	.session_key	= gensec_krb5_session_key,
 	.session_info	= gensec_krb5_session_info,
@@ -1070,7 +1121,8 @@ static const struct gensec_security_ops gensec_krb5_security_ops = {
 	.name		= "krb5",
 	.client_start   = gensec_krb5_client_start,
 	.server_start   = gensec_krb5_server_start,
-	.update 	= gensec_krb5_update,
+	.update_send	= gensec_krb5_update_send,
+	.update_recv	= gensec_krb5_update_recv,
 	.session_key	= gensec_krb5_session_key,
 	.session_info	= gensec_krb5_session_info,
 	.have_feature   = gensec_krb5_have_feature,
-- 
1.9.1


From e222fadc4677bd19bdd5fc4ec92fec614986817b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 25/35] auth/ntlmssp: add implement
 gensec_ntlmssp_update_send/recv()

Currently only backend functions are sync functions, but that needs
to change in future.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/ntlmssp/ntlmssp.c         | 86 +++++++++++++++++++++++++++++-------------
 auth/ntlmssp/ntlmssp_private.h |  7 ----
 2 files changed, 59 insertions(+), 34 deletions(-)

diff --git a/auth/ntlmssp/ntlmssp.c b/auth/ntlmssp/ntlmssp.c
index cb8f10b..f00cce9 100644
--- a/auth/ntlmssp/ntlmssp.c
+++ b/auth/ntlmssp/ntlmssp.c
@@ -24,6 +24,8 @@
 struct auth_session_info;
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "auth/ntlmssp/ntlmssp.h"
 #include "auth/ntlmssp/ntlmssp_private.h"
 #include "../libcli/auth/libcli_auth.h"
@@ -143,44 +145,72 @@ static NTSTATUS gensec_ntlmssp_update_find(struct gensec_security *gensec_securi
 	return NT_STATUS_INVALID_PARAMETER;
 }
 
-/**
- * Next state function for the wrapped NTLMSSP state machine
- *
- * @param gensec_security GENSEC state, initialised to NTLMSSP
- * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on
- * @param in The request, as a DATA_BLOB
- * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx
- * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent,
- *                or NT_STATUS_OK if the user is authenticated.
- */
+struct gensec_ntlmssp_update_state {
+	NTSTATUS status;
+	DATA_BLOB out;
+};
 
-NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security,
-			       TALLOC_CTX *out_mem_ctx,
-			       struct tevent_context *ev,
-			       const DATA_BLOB input, DATA_BLOB *out)
+static struct tevent_req *gensec_ntlmssp_update_send(TALLOC_CTX *mem_ctx,
+						     struct tevent_context *ev,
+						     struct gensec_security *gensec_security,
+						     const DATA_BLOB in)
 {
 	struct gensec_ntlmssp_context *gensec_ntlmssp =
 		talloc_get_type_abort(gensec_security->private_data,
 				      struct gensec_ntlmssp_context);
-	struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
+	struct tevent_req *req = NULL;
+	struct gensec_ntlmssp_update_state *state = NULL;
 	NTSTATUS status;
 	uint32_t i;
 
-	*out = data_blob(NULL, 0);
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_ntlmssp_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
 
-	if (!out_mem_ctx) {
-		/* if the caller doesn't want to manage/own the memory,
-		   we can put it on our context */
-		out_mem_ctx = ntlmssp_state;
+	status = gensec_ntlmssp_update_find(gensec_security, gensec_ntlmssp,
+					    in, &i);
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
 	}
 
-	status = gensec_ntlmssp_update_find(gensec_security, gensec_ntlmssp, input, &i);
-	NT_STATUS_NOT_OK_RETURN(status);
+	status = ntlmssp_callbacks[i].sync_fn(gensec_security, state,
+					      in, &state->out);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
 
-	status = ntlmssp_callbacks[i].sync_fn(gensec_security, out_mem_ctx, input, out);
-	NT_STATUS_NOT_OK_RETURN(status);
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
 
-	return NT_STATUS_OK;
+static NTSTATUS gensec_ntlmssp_update_recv(struct tevent_req *req,
+					   TALLOC_CTX *out_mem_ctx,
+					   DATA_BLOB *out)
+{
+	struct gensec_ntlmssp_update_state *state =
+		tevent_req_data(req,
+		struct gensec_ntlmssp_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
+	return status;
 }
 
 static NTSTATUS gensec_ntlmssp_may_reset_crypto(struct gensec_security *gensec_security,
@@ -225,7 +255,8 @@ static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
 	.client_start   = gensec_ntlmssp_client_start,
 	.server_start   = gensec_ntlmssp_server_start,
 	.magic 	        = gensec_ntlmssp_magic,
-	.update 	= gensec_ntlmssp_update,
+	.update_send	= gensec_ntlmssp_update_send,
+	.update_recv	= gensec_ntlmssp_update_recv,
 	.may_reset_crypto= gensec_ntlmssp_may_reset_crypto,
 	.sig_size	= gensec_ntlmssp_sig_size,
 	.sign_packet	= gensec_ntlmssp_sign_packet,
@@ -245,7 +276,8 @@ static const struct gensec_security_ops gensec_ntlmssp_security_ops = {
 static const struct gensec_security_ops gensec_ntlmssp_resume_ccache_ops = {
 	.name		= "ntlmssp_resume_ccache",
 	.client_start   = gensec_ntlmssp_resume_ccache_start,
-	.update 	= gensec_ntlmssp_update,
+	.update_send	= gensec_ntlmssp_update_send,
+	.update_recv	= gensec_ntlmssp_update_recv,
 	.session_key	= gensec_ntlmssp_session_key,
 	.have_feature   = gensec_ntlmssp_have_feature,
 	.enabled        = true,
diff --git a/auth/ntlmssp/ntlmssp_private.h b/auth/ntlmssp/ntlmssp_private.h
index e938e5c..eed48ed 100644
--- a/auth/ntlmssp/ntlmssp_private.h
+++ b/auth/ntlmssp/ntlmssp_private.h
@@ -49,13 +49,6 @@ struct gensec_ntlmssp_context {
 	struct ntlmssp_state *ntlmssp_state;
 };
 
-/* The following definitions come from auth/ntlmssp.c  */
-
-NTSTATUS gensec_ntlmssp_update(struct gensec_security *gensec_security,
-			       TALLOC_CTX *out_mem_ctx,
-			       struct tevent_context *ev,
-			       const DATA_BLOB input, DATA_BLOB *out);
-
 /* The following definitions come from auth/ntlmssp_util.c  */
 
 void debug_ntlmssp_flags(uint32_t neg_flags);
-- 
1.9.1


From 14ef9f4401361678cee83d9d763547bd4b5ff62a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 09:04:02 +0200
Subject: [PATCH 26/35] auth/spnego: add simple
 gensec_spnego_update_send/recv() wrapper functions

TODO: we still need to do the internals async.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/spnego.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/auth/gensec/spnego.c b/auth/gensec/spnego.c
index aec4b5e..9495933 100644
--- a/auth/gensec/spnego.c
+++ b/auth/gensec/spnego.c
@@ -23,6 +23,8 @@
 */
 
 #include "includes.h"
+#include <tevent.h>
+#include "lib/util/tevent_ntstatus.h"
 #include "../libcli/auth/spnego.h"
 #include "librpc/gen_ndr/ndr_dcerpc.h"
 #include "auth/credentials/credentials.h"
@@ -1442,6 +1444,65 @@ static NTSTATUS gensec_spnego_update_wrapper(struct gensec_security *gensec_secu
 					out);
 }
 
+struct gensec_spnego_update_state {
+	NTSTATUS status;
+	DATA_BLOB out;
+};
+
+static struct tevent_req *gensec_spnego_update_send(TALLOC_CTX *mem_ctx,
+						    struct tevent_context *ev,
+						    struct gensec_security *gensec_security,
+						    const DATA_BLOB in)
+{
+	struct tevent_req *req = NULL;
+	struct gensec_spnego_update_state *state = NULL;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct gensec_spnego_update_state);
+	if (req == NULL) {
+		return NULL;
+	}
+
+	status = gensec_spnego_update_wrapper(gensec_security,
+					      state, ev, in,
+					      &state->out);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS gensec_spnego_update_recv(struct tevent_req *req,
+					  TALLOC_CTX *out_mem_ctx,
+					  DATA_BLOB *out)
+{
+	struct gensec_spnego_update_state *state =
+		tevent_req_data(req,
+		struct gensec_spnego_update_state);
+	NTSTATUS status;
+
+	*out = data_blob_null;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		tevent_req_received(req);
+		return status;
+	}
+
+	*out = state->out;
+	talloc_steal(out_mem_ctx, state->out.data);
+	status = state->status;
+	tevent_req_received(req);
+	return status;
+}
+
 static const char *gensec_spnego_oids[] = { 
 	GENSEC_OID_SPNEGO,
 	NULL 
@@ -1454,7 +1515,8 @@ 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_wrapper,
+	.update_send	  = gensec_spnego_update_send,
+	.update_recv	  = gensec_spnego_update_recv,
 	.seal_packet	  = gensec_child_seal_packet,
 	.sign_packet	  = gensec_child_sign_packet,
 	.sig_size	  = gensec_child_sig_size,
-- 
1.9.1


From c40b8716b253470d5d616969947865ee0358372f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 12:48:41 +0200
Subject: [PATCH 27/35] auth/gensec: remove the sync update() hook from
 gensec_security_ops

Some backends still do some nested event context magic,
but that mapping between async and sync is done in these backends
and not in the core gensec code anymore.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.c          | 84 -------------------------------------------
 auth/gensec/gensec_internal.h |  3 --
 2 files changed, 87 deletions(-)

diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c
index 9b1661e..df4ac4e 100644
--- a/auth/gensec/gensec.c
+++ b/auth/gensec/gensec.c
@@ -334,47 +334,6 @@ _PUBLIC_ NTSTATUS gensec_update_ev(struct gensec_security *gensec_security,
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	if (ops->update_send == NULL) {
-
-		if (ev == NULL) {
-			frame = talloc_stackframe();
-
-			ev = samba_tevent_context_init(frame);
-			if (ev == NULL) {
-				status = NT_STATUS_NO_MEMORY;
-				goto fail;
-			}
-
-			/*
-			 * TODO: remove this hack once the backends
-			 * are fixed.
-			 */
-			tevent_loop_allow_nesting(ev);
-		}
-
-		status = ops->update(gensec_security, out_mem_ctx,
-				     ev, in, out);
-		TALLOC_FREE(frame);
-		if (!NT_STATUS_IS_OK(status)) {
-			return status;
-		}
-
-		/*
-		 * Because callers using the
-		 * gensec_start_mech_by_auth_type() never call
-		 * gensec_want_feature(), it isn't sensible for them
-		 * to have to call gensec_have_feature() manually, and
-		 * these are not points of negotiation, but are
-		 * asserted by the client
-		 */
-		status = gensec_verify_features(gensec_security);
-		if (!NT_STATUS_IS_OK(status)) {
-			return status;
-		}
-
-		return NT_STATUS_OK;
-	}
-
 	frame = talloc_stackframe();
 
 	if (ev == NULL) {
@@ -442,18 +401,8 @@ struct gensec_update_state {
 	struct tevent_req *subreq;
 	struct gensec_security *gensec_security;
 	DATA_BLOB out;
-
-	/*
-	 * only for sync backends, we should remove this
-	 * once all backends are async.
-	 */
-	struct tevent_immediate *im;
-	DATA_BLOB in;
 };
 
-static void gensec_update_async_trigger(struct tevent_context *ctx,
-					struct tevent_immediate *im,
-					void *private_data);
 static void gensec_update_subreq_done(struct tevent_req *subreq);
 
 /**
@@ -489,20 +438,6 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	if (state->ops->update_send == NULL) {
-		state->in = in;
-		state->im = tevent_create_immediate(state);
-		if (tevent_req_nomem(state->im, req)) {
-			return tevent_req_post(req, ev);
-		}
-
-		tevent_schedule_immediate(state->im, ev,
-					  gensec_update_async_trigger,
-					  req);
-
-		return req;
-	}
-
 	state->subreq = state->ops->update_send(state, ev, gensec_security, in);
 	if (tevent_req_nomem(state->subreq, req)) {
 		return tevent_req_post(req, ev);
@@ -515,25 +450,6 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 	return req;
 }
 
-static void gensec_update_async_trigger(struct tevent_context *ctx,
-					struct tevent_immediate *im,
-					void *private_data)
-{
-	struct tevent_req *req =
-		talloc_get_type_abort(private_data, struct tevent_req);
-	struct gensec_update_state *state =
-		tevent_req_data(req, struct gensec_update_state);
-	NTSTATUS status;
-
-	status = state->ops->update(state->gensec_security, state, ctx,
-				    state->in, &state->out);
-	if (tevent_req_nterror(req, status)) {
-		return;
-	}
-
-	tevent_req_done(req);
-}
-
 static void gensec_update_subreq_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req =
diff --git a/auth/gensec/gensec_internal.h b/auth/gensec/gensec_internal.h
index 9c160d3..b3a8434 100644
--- a/auth/gensec/gensec_internal.h
+++ b/auth/gensec/gensec_internal.h
@@ -37,9 +37,6 @@ struct gensec_security_ops {
 	*/
 	NTSTATUS (*magic)(struct gensec_security *gensec_security,
 			  const DATA_BLOB *first_packet);
-	NTSTATUS (*update)(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
-			   struct tevent_context *ev,
-			   const DATA_BLOB in, DATA_BLOB *out);
 	struct tevent_req *(*update_send)(TALLOC_CTX *mem_ctx,
 					  struct tevent_context *ev,
 					  struct gensec_security *gensec_security,
-- 
1.9.1


From 2b310fc0dd6f4056510a9b059043ac746f17d0fa Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 13:25:26 +0200
Subject: [PATCH 28/35] auth/gensec: avoid using a state->subreq pointer

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.c | 19 +++++++------------
 1 file changed, 7 insertions(+), 12 deletions(-)

diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c
index df4ac4e..b37f560 100644
--- a/auth/gensec/gensec.c
+++ b/auth/gensec/gensec.c
@@ -398,12 +398,11 @@ _PUBLIC_ NTSTATUS gensec_update(struct gensec_security *gensec_security,
 
 struct gensec_update_state {
 	const struct gensec_security_ops *ops;
-	struct tevent_req *subreq;
 	struct gensec_security *gensec_security;
 	DATA_BLOB out;
 };
 
-static void gensec_update_subreq_done(struct tevent_req *subreq);
+static void gensec_update_done(struct tevent_req *subreq);
 
 /**
  * Next state function for the GENSEC state machine async version
@@ -421,8 +420,9 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 					       struct gensec_security *gensec_security,
 					       const DATA_BLOB in)
 {
-	struct tevent_req *req;
+	struct tevent_req *req = NULL;
 	struct gensec_update_state *state = NULL;
+	struct tevent_req *subreq = NULL;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct gensec_update_state);
@@ -438,19 +438,16 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
-	state->subreq = state->ops->update_send(state, ev, gensec_security, in);
-	if (tevent_req_nomem(state->subreq, req)) {
+	subreq = state->ops->update_send(state, ev, gensec_security, in);
+	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-
-	tevent_req_set_callback(state->subreq,
-				gensec_update_subreq_done,
-				req);
+	tevent_req_set_callback(subreq, gensec_update_done, req);
 
 	return req;
 }
 
-static void gensec_update_subreq_done(struct tevent_req *subreq)
+static void gensec_update_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req =
 		tevent_req_callback_data(subreq,
@@ -460,8 +457,6 @@ static void gensec_update_subreq_done(struct tevent_req *subreq)
 		struct gensec_update_state);
 	NTSTATUS status;
 
-	state->subreq = NULL;
-
 	status = state->ops->update_recv(subreq, state, &state->out);
 	TALLOC_FREE(subreq);
 	if (tevent_req_nterror(req, status)) {
-- 
1.9.1


From b5e1a937151fd19ba85777c17620e55e138cc3bd Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 13:23:07 +0200
Subject: [PATCH 29/35] auth/gensec: improve NT_STATUS_MORE_PROCESSING_REQUIRED
 logic in gensec_update_*()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c
index b37f560..cf00a36 100644
--- a/auth/gensec/gensec.c
+++ b/auth/gensec/gensec.c
@@ -399,6 +399,7 @@ _PUBLIC_ NTSTATUS gensec_update(struct gensec_security *gensec_security,
 struct gensec_update_state {
 	const struct gensec_security_ops *ops;
 	struct gensec_security *gensec_security;
+	NTSTATUS status;
 	DATA_BLOB out;
 };
 
@@ -459,6 +460,11 @@ static void gensec_update_done(struct tevent_req *subreq)
 
 	status = state->ops->update_recv(subreq, state, &state->out);
 	TALLOC_FREE(subreq);
+	state->status = status;
+	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+		tevent_req_done(req);
+		return;
+	}
 	if (tevent_req_nterror(req, status)) {
 		return;
 	}
@@ -496,18 +502,16 @@ _PUBLIC_ NTSTATUS gensec_update_recv(struct tevent_req *req,
 		tevent_req_data(req, struct gensec_update_state);
 	NTSTATUS status;
 
+	*out = data_blob_null;
+
 	if (tevent_req_is_nterror(req, &status)) {
-		if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-			tevent_req_received(req);
-			return status;
-		}
-	} else {
-		status = NT_STATUS_OK;
+		tevent_req_received(req);
+		return status;
 	}
 
 	*out = state->out;
 	talloc_steal(out_mem_ctx, out->data);
-
+	status = state->status;
 	tevent_req_received(req);
 	return status;
 }
-- 
1.9.1


From 4e4fedd918a119d229e8c9a9de5422104812d4cb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 11 May 2017 13:28:10 +0200
Subject: [PATCH 30/35] auth/gensec: make sure there's only one pending
 gensec_update_send() per context

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 auth/gensec/gensec.c          | 29 ++++++++++++++++++++++++++++-
 auth/gensec/gensec_internal.h |  6 ++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/auth/gensec/gensec.c b/auth/gensec/gensec.c
index cf00a36..014516f 100644
--- a/auth/gensec/gensec.c
+++ b/auth/gensec/gensec.c
@@ -403,6 +403,8 @@ struct gensec_update_state {
 	DATA_BLOB out;
 };
 
+static void gensec_update_cleanup(struct tevent_req *req,
+				  enum tevent_req_state req_state);
 static void gensec_update_done(struct tevent_req *subreq);
 
 /**
@@ -430,15 +432,22 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 	if (req == NULL) {
 		return NULL;
 	}
-
 	state->ops = gensec_security->ops;
 	state->gensec_security = gensec_security;
 
+	if (gensec_security->update_busy_ptr != NULL) {
+		tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+		return tevent_req_post(req, ev);
+	}
+
 	if (gensec_security->child_security != NULL) {
 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 		return tevent_req_post(req, ev);
 	}
 
+	gensec_security->update_busy_ptr = &state->gensec_security;
+	tevent_req_set_cleanup_fn(req, gensec_update_cleanup);
+
 	subreq = state->ops->update_send(state, ev, gensec_security, in);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
@@ -448,6 +457,24 @@ _PUBLIC_ struct tevent_req *gensec_update_send(TALLOC_CTX *mem_ctx,
 	return req;
 }
 
+static void gensec_update_cleanup(struct tevent_req *req,
+				  enum tevent_req_state req_state)
+{
+	struct gensec_update_state *state =
+		tevent_req_data(req,
+		struct gensec_update_state);
+
+	if (state->gensec_security == NULL) {
+		return;
+	}
+
+	if (state->gensec_security->update_busy_ptr == &state->gensec_security) {
+		state->gensec_security->update_busy_ptr = NULL;
+	}
+
+	state->gensec_security = NULL;
+}
+
 static void gensec_update_done(struct tevent_req *subreq)
 {
 	struct tevent_req *req =
diff --git a/auth/gensec/gensec_internal.h b/auth/gensec/gensec_internal.h
index b3a8434..c73be11 100644
--- a/auth/gensec/gensec_internal.h
+++ b/auth/gensec/gensec_internal.h
@@ -113,6 +113,12 @@ struct gensec_security {
 
 	struct gensec_security *parent_security;
 	struct gensec_security *child_security;
+
+	/*
+	 * This is used to mark the context as being
+	 * busy in an async gensec_update_send().
+	 */
+	struct gensec_security **update_busy_ptr;
 };
 
 /* this structure is used by backends to determine the size of some critical types */
-- 
1.9.1


From cd41374a989e5d1ace3fc4009f1e69ecc13ba72f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 15 May 2017 07:17:00 +0200
Subject: [PATCH 31/35] s4:auth: split out a
 samba_server_gensec_start_settings() helper function

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/samba_server_gensec.c | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/source4/auth/samba_server_gensec.c b/source4/auth/samba_server_gensec.c
index 7b09aa7..af26f99 100644
--- a/source4/auth/samba_server_gensec.c
+++ b/source4/auth/samba_server_gensec.c
@@ -27,10 +27,11 @@
 #include "auth/gensec/gensec.h"
 #include "param/param.h"
 
-NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
+static NTSTATUS samba_server_gensec_start_settings(TALLOC_CTX *mem_ctx,
 				   struct tevent_context *event_ctx,
 				   struct imessaging_context *msg_ctx,
 				   struct loadparm_context *lp_ctx,
+				   struct gensec_settings *settings,
 				   struct cli_credentials *server_credentials,
 				   const char *target_service,
 				   struct gensec_security **gensec_context)
@@ -57,7 +58,7 @@ NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
 	}
 
 	nt_status = gensec_server_start(tmp_ctx,
-					lpcfg_gensec_settings(mem_ctx, lp_ctx),
+					settings,
 					auth_context,
 					&gensec_ctx);
 	if (!NT_STATUS_IS_OK(nt_status)) {
@@ -75,3 +76,32 @@ NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
 	talloc_free(tmp_ctx);
 	return nt_status;
 }
+
+NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
+				   struct tevent_context *event_ctx,
+				   struct imessaging_context *msg_ctx,
+				   struct loadparm_context *lp_ctx,
+				   struct cli_credentials *server_credentials,
+				   const char *target_service,
+				   struct gensec_security **gensec_context)
+{
+	struct gensec_settings *settings = NULL;
+	NTSTATUS status;
+
+	settings = lpcfg_gensec_settings(mem_ctx, lp_ctx);
+	if (settings == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	status = samba_server_gensec_start_settings(mem_ctx, event_ctx,
+						    msg_ctx, lp_ctx,
+						    settings, server_credentials,
+						    target_service,
+						    gensec_context);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(settings);
+		return status;
+	}
+
+	talloc_reparent(mem_ctx, *gensec_context, settings);
+	return NT_STATUS_OK;
+}
-- 
1.9.1


From d4967084ea9e87b03a553261964439bf28b3b822 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 15 May 2017 07:17:30 +0200
Subject: [PATCH 32/35] s4:auth: add samba_server_gensec_krb5_start()

This will be used by the dns services to only allow
spnego/krb5. This makes sure the accepting backend
doesn't require any RPC or IPC communication for now.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/auth/auth.h                |  7 ++++++
 source4/auth/samba_server_gensec.c | 45 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/source4/auth/auth.h b/source4/auth/auth.h
index de3a8bd..e1b642e 100644
--- a/source4/auth/auth.h
+++ b/source4/auth/auth.h
@@ -187,5 +187,12 @@ NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
 				   struct cli_credentials *server_credentials,
 				   const char *target_service,
 				   struct gensec_security **gensec_context);
+NTSTATUS samba_server_gensec_krb5_start(TALLOC_CTX *mem_ctx,
+					struct tevent_context *event_ctx,
+					struct imessaging_context *msg_ctx,
+					struct loadparm_context *lp_ctx,
+					struct cli_credentials *server_credentials,
+					const char *target_service,
+					struct gensec_security **gensec_context);
 
 #endif /* _SMBAUTH_H_ */
diff --git a/source4/auth/samba_server_gensec.c b/source4/auth/samba_server_gensec.c
index af26f99..ee3396a 100644
--- a/source4/auth/samba_server_gensec.c
+++ b/source4/auth/samba_server_gensec.c
@@ -105,3 +105,48 @@ NTSTATUS samba_server_gensec_start(TALLOC_CTX *mem_ctx,
 	talloc_reparent(mem_ctx, *gensec_context, settings);
 	return NT_STATUS_OK;
 }
+
+NTSTATUS samba_server_gensec_krb5_start(TALLOC_CTX *mem_ctx,
+					struct tevent_context *event_ctx,
+					struct imessaging_context *msg_ctx,
+					struct loadparm_context *lp_ctx,
+					struct cli_credentials *server_credentials,
+					const char *target_service,
+					struct gensec_security **gensec_context)
+{
+	struct gensec_settings *settings = NULL;
+	const struct gensec_security_ops **backends = NULL;
+	size_t idx = 0;
+	NTSTATUS status;
+
+	settings = lpcfg_gensec_settings(mem_ctx, lp_ctx);
+	if (settings == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	backends = talloc_zero_array(settings,
+				     const struct gensec_security_ops *, 3);
+	if (backends == NULL) {
+			TALLOC_FREE(settings);
+		return NT_STATUS_NO_MEMORY;
+	}
+	settings->backends = backends;
+
+	gensec_init();
+
+	backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
+
+	backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
+
+	status = samba_server_gensec_start_settings(mem_ctx, event_ctx,
+						    msg_ctx, lp_ctx,
+						    settings, server_credentials,
+						    target_service,
+						    gensec_context);
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(settings);
+		return status;
+	}
+
+	talloc_steal(*gensec_context, settings);
+	return NT_STATUS_OK;
+}
-- 
1.9.1


From e836925738ed4ea5cc1f9721c58ab8e01c184b61 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 15 May 2017 07:30:14 +0200
Subject: [PATCH 33/35] s4:dns_server: use samba_server_gensec_krb5_start() and
 gensec_update() in dns_query.c

This avoids using gensec_update_ev() with a nested event loop.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dns_server/dns_query.c | 37 ++++++++++++++++++++++++++++---------
 1 file changed, 28 insertions(+), 9 deletions(-)

diff --git a/source4/dns_server/dns_query.c b/source4/dns_server/dns_query.c
index 366696d..b8ecc2e 100644
--- a/source4/dns_server/dns_query.c
+++ b/source4/dns_server/dns_query.c
@@ -725,13 +725,19 @@ static NTSTATUS create_tkey(struct dns_server *dns,
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	status = samba_server_gensec_start(k,
-					   dns->task->event_ctx,
-					   dns->task->msg_ctx,
-					   dns->task->lp_ctx,
-					   dns->server_credentials,
-					   "dns",
-					   &k->gensec);
+	/*
+	 * We only allow SPNEGO/KRB5 currently
+	 * and rely on the backend to be RPC/IPC free.
+	 *
+	 * It allows gensec_update() not to block.
+	 */
+	status = samba_server_gensec_krb5_start(k,
+						dns->task->event_ctx,
+						dns->task->msg_ctx,
+						dns->task->lp_ctx,
+						dns->server_credentials,
+						"dns",
+						&k->gensec);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status)));
 		*tkey = NULL;
@@ -788,8 +794,21 @@ static NTSTATUS accept_gss_ticket(TALLOC_CTX *mem_ctx,
 {
 	NTSTATUS status;
 
-	status = gensec_update_ev(tkey->gensec, mem_ctx, dns->task->event_ctx,
-				  *key, reply);
+	/*
+	 * We use samba_server_gensec_krb5_start(),
+	 * which only allows SPNEGO/KRB5 currently
+	 * and makes sure the backend to be RPC/IPC free.
+	 *
+	 * See gensec_gssapi_update_internal() as
+	 * GENSEC_SERVER.
+	 *
+	 * It allows gensec_update() not to block.
+	 *
+	 * If that changes in future we need to use
+	 * gensec_update_send/recv here!
+	 */
+	status = gensec_update(tkey->gensec, mem_ctx,
+			       *key, reply);
 
 	if (NT_STATUS_EQUAL(NT_STATUS_MORE_PROCESSING_REQUIRED, status)) {
 		*dns_auth_error = DNS_RCODE_OK;
-- 
1.9.1


From 16e84e4e77d11ab9004438a78f9c564140f17733 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 15 May 2017 07:45:47 +0200
Subject: [PATCH 34/35] s4:dlz_bind9: assert SPNEGO/KRB5 and use
 gensec_update()

This avoids using gensec_update_ev() with a nested event loop.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/dns_server/dlz_bind9.c | 42 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 38 insertions(+), 4 deletions(-)

diff --git a/source4/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c
index 4c21a5e..897699a 100644
--- a/source4/dns_server/dlz_bind9.c
+++ b/source4/dns_server/dlz_bind9.c
@@ -1276,6 +1276,9 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
 	struct ldb_result *res;
 	const char * attrs[] = { NULL };
 	uint32_t access_mask;
+	struct gensec_settings *settings = NULL;
+	const struct gensec_security_ops **backends = NULL;
+	size_t idx = 0;
 
 	/* Remove cached credentials, if any */
 	if (state->session_info) {
@@ -1316,8 +1319,27 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
 	}
 	talloc_free(keytab_name);
 
-	nt_status = gensec_server_start(tmp_ctx,
-					lpcfg_gensec_settings(tmp_ctx, state->lp),
+	settings = lpcfg_gensec_settings(tmp_ctx, state->lp);
+	if (settings == NULL) {
+		state->log(ISC_LOG_ERROR, "samba_dlz: lpcfg_gensec_settings failed");
+		talloc_free(tmp_ctx);
+		return ISC_FALSE;
+	}
+	backends = talloc_zero_array(settings,
+				     const struct gensec_security_ops *, 3);
+	if (backends == NULL) {
+		state->log(ISC_LOG_ERROR, "samba_dlz: talloc_zero_array gensec_security_ops failed");
+		talloc_free(tmp_ctx);
+		return ISC_FALSE;
+	}
+	settings->backends = backends;
+
+	gensec_init();
+
+	backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_KERBEROS5);
+	backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
+
+	nt_status = gensec_server_start(tmp_ctx, settings,
 					state->auth_context, &gensec_ctx);
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		state->log(ISC_LOG_ERROR, "samba_dlz: failed to start gensec server");
@@ -1327,14 +1349,26 @@ _PUBLIC_ isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const
 
 	gensec_set_credentials(gensec_ctx, server_credentials);
 
-	nt_status = gensec_start_mech_by_name(gensec_ctx, "spnego");
+	nt_status = gensec_start_mech_by_oid(gensec_ctx, GENSEC_OID_SPNEGO);
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		state->log(ISC_LOG_ERROR, "samba_dlz: failed to start spnego");
 		talloc_free(tmp_ctx);
 		return ISC_FALSE;
 	}
 
-	nt_status = gensec_update_ev(gensec_ctx, tmp_ctx, state->ev_ctx, ap_req, &ap_req);
+	/*
+	 * We only allow SPNEGO/KRB5 and make sure the backend
+	 * to is RPC/IPC free.
+	 *
+	 * See gensec_gssapi_update_internal() as
+	 * GENSEC_SERVER.
+	 *
+	 * It allows gensec_update() not to block.
+	 *
+	 * If that changes in future we need to use
+	 * gensec_update_send/recv here!
+	 */
+	nt_status = gensec_update(gensec_ctx, tmp_ctx, ap_req, &ap_req);
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		state->log(ISC_LOG_ERROR, "samba_dlz: spnego update failed");
 		talloc_free(tmp_ctx);
-- 
1.9.1


From cb900967678af0c93d0da488e2381f3b24e679e8 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 15 May 2017 07:50:31 +0200
Subject: [PATCH 35/35] s4:kdc: make use of gensec_update() in
 kpasswd_process()

This avoids using gensec_update_ev() with a nested event loop.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/kdc/kpasswd-service.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/source4/kdc/kpasswd-service.c b/source4/kdc/kpasswd-service.c
index 9cb1482..b36cf40 100644
--- a/source4/kdc/kpasswd-service.c
+++ b/source4/kdc/kpasswd-service.c
@@ -206,12 +206,20 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
 		goto done;
 	}
 
-	/* Accept the AP-REQ and generate the AP-REP we need for the reply */
-	status = gensec_update_ev(gensec_security,
-				  tmp_ctx,
-				  kdc->task->event_ctx,
-				  ap_req_blob,
-				  &ap_rep_blob);
+	/*
+	 * Accept the AP-REQ and generate the AP-REP we need for the reply
+	 *
+	 * We only allow KRB5 and make sure the backend to is RPC/IPC free.
+	 *
+	 * See gensec_krb5_update_internal() as GENSEC_SERVER.
+	 *
+	 * It allows gensec_update() not to block.
+	 *
+	 * If that changes in future we need to use
+	 * gensec_update_send/recv here!
+	 */
+	status = gensec_update(gensec_security, tmp_ctx,
+			       ap_req_blob, &ap_rep_blob);
 	if (!NT_STATUS_IS_OK(status) &&
 	    !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 		ap_rep_blob = data_blob_null;
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20170517/574fe07a/signature.sig>


More information about the samba-technical mailing list