[Patches] improve ncacn_http handling and add HttpAuthOption=negotiate support

Stefan Metzmacher metze at samba.org
Thu Jul 20 22:36:01 UTC 2017


Hi,

while converting http_ntlm to gensec_update_send/recv
I improved a few things and implemented HttpAuthOption=negotiate

It's tested against Windows 2008R2 using (all in one line...):
bin/smbtorture -W BLA --realm=BLA.BASE -s /dev/null -Uadministrator%A1b2C3d4
ncacn_http:w2k8r2-219[593,RpcProxy=w2k8r2-219.bla.base,HttpUseTls=false,HttpAuthOption=negotiate]
rpc.epmapper.epmapper.Lookup_simple

It also still works with HttpAuthOption=ntlm and HttpAuthOption=basic

I'm currently running some autobuilds with this.

Please start reviewing:-)

Note that this depends on the gensec_update_ev() removal changes
and the fix for the missing tevent_req_done() in
 gensec_http_ntlm_update_done().

Thanks!
metze
-------------- next part --------------
From 7d3223474bf101808dbd8bce34628e331bcd707a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 18:13:28 +0200
Subject: [PATCH 01/13] s4:lib/http: lower HTTP_MAX_HEADER_SIZE from UINT_MAX
 to 0x1FFFF

We don't need every large headers, the largest ones are
"Authorization" or "WWW-Authenticate", but 128k should be
more than enough for all headers.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/http.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/lib/http/http.h b/source4/lib/http/http.h
index a601a03..35fe8a7 100644
--- a/source4/lib/http/http.h
+++ b/source4/lib/http/http.h
@@ -43,7 +43,7 @@
 #define HTTP_NOTIMPLEMENTED	501	/* not implemented */
 #define HTTP_SERVUNAVAIL	503	/* server is not available */
 
-#define HTTP_MAX_HEADER_SIZE 	UINT_MAX
+#define HTTP_MAX_HEADER_SIZE	0x1FFFF
 
 struct cli_credentials;
 struct loadparm_ctx;
-- 
1.9.1


From e065e2acabce71f13d16c1ea5e88885c694d7e20 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 18:12:27 +0200
Subject: [PATCH 02/13] s4:lib/http: let http_read_response_send/recv() also
 the body if it fits into a max value

We need to consume full HTTP responses from the socket
during the authentication exchanges, otherwise our HTTP
parser gets out of sync for the next requests.

This will important for gensec mechs which use an even number
of authentication packets.

I guess this should be done just based on the Content-Length
value and not based on the response code.

So far I saw bodies with 200 and 401 codes.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/http.c                     | 63 ++++++++++++++++++++++-------
 source4/lib/http/http.h                     |  4 +-
 source4/lib/http/http_auth.c                | 25 +++++++++++-
 source4/lib/http/http_internal.h            |  1 +
 source4/librpc/rpc/dcerpc_roh_channel_out.c |  3 +-
 5 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/source4/lib/http/http.c b/source4/lib/http/http.c
index c6976ee..8fac33f 100644
--- a/source4/lib/http/http.c
+++ b/source4/lib/http/http.c
@@ -30,19 +30,39 @@
 
 /**
  * Determines if a response should have a body.
- * Follows the rules in RFC 2616 section 4.3.
  * @return 1 if the response MUST have a body; 0 if the response MUST NOT have
  *     a body. Returns -1 on error.
  */
 static int http_response_needs_body(struct http_request *req)
 {
+	struct http_header *h = NULL;
+
 	if (!req) return -1;
 
-	/* If response code is 503, the body contains the error description
-	 * (2.1.2.1.3)
-	 */
-	if (req->response_code == 503)
-		return 1;
+	for (h = req->headers; h != NULL; h = h->next) {
+		int cmp;
+		int n;
+		char c;
+		unsigned long long v;
+
+		cmp = strcasecmp(h->key, "Content-Length");
+		if (cmp != 0) {
+			continue;
+		}
+
+		n = sscanf(h->value, "%llu%c", &v, &c);
+		if (n != 1) {
+			return -1;
+		}
+
+		req->remaining_content_length = v;
+
+		if (v != 0) {
+			return 1;
+		}
+
+		return 0;
+	}
 
 	return 0;
 }
@@ -92,15 +112,18 @@ static enum http_read_status http_parse_headers(struct http_read_response_state
 
 		ret = http_response_needs_body(state->response);
 		switch (ret) {
+		case 1:
+			if (state->response->remaining_content_length <= state->max_content_length) {
+				DEBUG(11, ("%s: Start of read body\n", __func__));
+				state->parser_state = HTTP_READING_BODY;
+				break;
+			}
+			/* fall through */
 		case 0:
 			DEBUG(11, ("%s: Skipping body for code %d\n", __func__,
 				   state->response->response_code));
 			state->parser_state = HTTP_READING_DONE;
 			break;
-		case 1:
-			DEBUG(11, ("%s: Start of read body\n", __func__));
-			state->parser_state = HTTP_READING_BODY;
-			break;
 		case -1:
 			DEBUG(0, ("%s_: Error in http_response_needs_body\n", __func__));
 			TALLOC_FREE(line);
@@ -256,9 +279,19 @@ static enum http_read_status http_parse_firstline(struct http_read_response_stat
 
 static enum http_read_status http_read_body(struct http_read_response_state *state)
 {
-	enum http_read_status status = HTTP_DATA_CORRUPTED;
-	/* TODO */
-	return status;
+	struct http_request *resp = state->response;
+
+	if (state->buffer.length < resp->remaining_content_length) {
+		return HTTP_MORE_DATA_EXPECTED;
+	}
+
+	resp->body = state->buffer;
+	state->buffer = data_blob_null;
+	talloc_steal(resp, resp->body.data);
+	resp->remaining_content_length = 0;
+
+	state->parser_state = HTTP_READING_DONE;
+	return HTTP_ALL_DATA_READ;
 }
 
 static enum http_read_status http_read_trailer(struct http_read_response_state *state)
@@ -519,7 +552,8 @@ static int http_read_response_next_vector(struct tstream_context *stream,
 static void http_read_response_done(struct tevent_req *);
 struct tevent_req *http_read_response_send(TALLOC_CTX *mem_ctx,
 					   struct tevent_context *ev,
-					   struct tstream_context *stream)
+					   struct tstream_context *stream,
+					   size_t max_content_length)
 {
 	struct tevent_req		*req;
 	struct tevent_req		*subreq;
@@ -539,6 +573,7 @@ struct tevent_req *http_read_response_send(TALLOC_CTX *mem_ctx,
 	}
 
 	state->max_headers_size = HTTP_MAX_HEADER_SIZE;
+	state->max_content_length = (uint64_t)max_content_length;
 	state->parser_state = HTTP_READING_FIRSTLINE;
 	state->response = talloc_zero(state, struct http_request);
 	if (tevent_req_nomem(state->response, req)) {
diff --git a/source4/lib/http/http.h b/source4/lib/http/http.h
index 35fe8a7..a871462 100644
--- a/source4/lib/http/http.h
+++ b/source4/lib/http/http.h
@@ -82,6 +82,7 @@ struct http_request {
 	size_t			headers_size;
 	unsigned int		response_code;		/* HTTP response code */
 	char			*response_code_line;	/* Readable response */
+	uint64_t		remaining_content_length; /* data not represent in body */
 	DATA_BLOB		body;
 };
 
@@ -101,7 +102,8 @@ NTSTATUS http_send_request_recv(struct tevent_req *);
 /* HTTP response */
 struct tevent_req *http_read_response_send(TALLOC_CTX *,
 					   struct tevent_context *,
-					   struct tstream_context *);
+					   struct tstream_context *,
+					   size_t max_content_length);
 NTSTATUS http_read_response_recv(struct tevent_req *,
 			    TALLOC_CTX *,
 			    struct http_request **);
diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c
index 3d2148e..d134bd6 100644
--- a/source4/lib/http/http_auth.c
+++ b/source4/lib/http/http_auth.c
@@ -272,9 +272,16 @@ static void http_send_auth_request_http_req_done(struct tevent_req *subreq)
 		return;
 	}
 
-	/* If more processing required, read the response from server */
+	/*
+	 * If more processing required, read the response from server
+	 *
+	 * We may get an empty RPCH Echo packet from the server
+	 * on the "RPC_OUT_DATA" path. We need to consume this
+	 * from the socket, but for now we just ignore the bytes.
+	 */
 	subreq = http_read_response_send(state, state->ev,
-					 state->stream);
+					 state->stream,
+					 UINT16_MAX);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
@@ -301,6 +308,20 @@ static void http_send_auth_request_http_rep_done(struct tevent_req *subreq)
 		return;
 	}
 
+	/*
+	 * We we asked for up to UINT16_MAX bytes of
+	 * content, we don't expect
+	 * state->auth_response->remaining_content_length
+	 * to be set.
+	 *
+	 * For now we just ignore any bytes in
+	 * state->auth_response->body.
+	 */
+	if (state->auth_response->remaining_content_length != 0) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+		return;
+	}
+
 	status = http_parse_auth_response(state->auth,
 					  state->auth_response,
 					  &gensec_in);
diff --git a/source4/lib/http/http_internal.h b/source4/lib/http/http_internal.h
index 72ea9df..040466d 100644
--- a/source4/lib/http/http_internal.h
+++ b/source4/lib/http/http_internal.h
@@ -54,6 +54,7 @@ struct http_send_request_state {
 struct http_read_response_state {
 	enum http_parser_state	parser_state;
 	size_t			max_headers_size;
+	uint64_t		max_content_length;
 	DATA_BLOB		buffer;
 	struct http_request	*response;
 };
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_out.c b/source4/librpc/rpc/dcerpc_roh_channel_out.c
index b370e56..8a337ad 100644
--- a/source4/librpc/rpc/dcerpc_roh_channel_out.c
+++ b/source4/librpc/rpc/dcerpc_roh_channel_out.c
@@ -482,7 +482,8 @@ struct tevent_req *roh_recv_out_channel_response_send(TALLOC_CTX *mem_ctx,
 	}
 
 	subreq = http_read_response_send(state, ev,
-					 roh->default_channel_out->streams.active);
+					 roh->default_channel_out->streams.active,
+					 0); /* we'll get the content later */
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-- 
1.9.1


From 35e4348c97f55361ff1f6c5bc74ddcbfcad4aa65 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 14:44:51 +0200
Subject: [PATCH 03/13] s4:lib/http: remove indentation level from
 http_parse_auth_response()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/http_auth.c | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c
index d134bd6..701d9b3 100644
--- a/source4/lib/http/http_auth.c
+++ b/source4/lib/http/http_auth.c
@@ -61,17 +61,19 @@ static NTSTATUS http_parse_auth_response(enum http_auth_method auth,
 	struct http_header *h;
 
 	for (h = auth_response->headers; h != NULL; h = h->next) {
-		if (strncasecmp(h->key, "WWW-Authenticate", 16) == 0) {
-			switch (auth) {
-			case HTTP_AUTH_NTLM:
-				if (strncasecmp(h->value, "NTLM ", 5) == 0) {
-					*in = data_blob_string_const(h->value);
-					return NT_STATUS_OK;
-				}
-				break;
-			default:
-				break;
+		if (strncasecmp(h->key, "WWW-Authenticate", 16) != 0) {
+			continue;
+		}
+
+		switch (auth) {
+		case HTTP_AUTH_NTLM:
+			if (strncasecmp(h->value, "NTLM ", 5) == 0) {
+				*in = data_blob_string_const(h->value);
+				return NT_STATUS_OK;
 			}
+			break;
+		default:
+			break;
 		}
 	}
 
-- 
1.9.1


From 22f3f9d934effba11a04240ef64f58c133a4e877 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 15:46:38 +0200
Subject: [PATCH 04/13] s4:lib/http: use strcasecmp(h->key, "WWW-Authenticate")
 instead of strncasecmp()

The key is already normalized and should match completely.

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

diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c
index 701d9b3..6541833 100644
--- a/source4/lib/http/http_auth.c
+++ b/source4/lib/http/http_auth.c
@@ -61,7 +61,10 @@ static NTSTATUS http_parse_auth_response(enum http_auth_method auth,
 	struct http_header *h;
 
 	for (h = auth_response->headers; h != NULL; h = h->next) {
-		if (strncasecmp(h->key, "WWW-Authenticate", 16) != 0) {
+		int cmp;
+
+		cmp = strcasecmp(h->key, "WWW-Authenticate");
+		if (cmp != 0) {
 			continue;
 		}
 
-- 
1.9.1


From 3192001453a6d885754ee833a4f95fdd384871ba Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 15:48:35 +0200
Subject: [PATCH 05/13] s4:lib/http: pass a generic prefix blob to
 http_parse_auth_response()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/http_auth.c | 24 +++++++++++++-----------
 1 file changed, 13 insertions(+), 11 deletions(-)

diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c
index 6541833..288a664 100644
--- a/source4/lib/http/http_auth.c
+++ b/source4/lib/http/http_auth.c
@@ -54,7 +54,7 @@ static NTSTATUS http_copy_header(const struct http_request *src,
  * Retrieve the WWW-Authenticate header from server response based on the
  * authentication scheme being used.
  */
-static NTSTATUS http_parse_auth_response(enum http_auth_method auth,
+static NTSTATUS http_parse_auth_response(const DATA_BLOB prefix,
 					 struct http_request *auth_response,
 					 DATA_BLOB *in)
 {
@@ -68,16 +68,15 @@ static NTSTATUS http_parse_auth_response(enum http_auth_method auth,
 			continue;
 		}
 
-		switch (auth) {
-		case HTTP_AUTH_NTLM:
-			if (strncasecmp(h->value, "NTLM ", 5) == 0) {
-				*in = data_blob_string_const(h->value);
-				return NT_STATUS_OK;
-			}
-			break;
-		default:
-			break;
+		cmp = strncasecmp(h->value,
+				  (const char *)prefix.data,
+				  prefix.length);
+		if (cmp != 0) {
+			continue;
 		}
+
+		*in = data_blob_string_const(h->value);
+		return NT_STATUS_OK;
 	}
 
 	return NT_STATUS_NOT_SUPPORTED;
@@ -90,6 +89,7 @@ struct http_auth_state {
 	struct tevent_queue *send_queue;
 
 	enum http_auth_method auth;
+	DATA_BLOB prefix;
 
 	struct gensec_security *gensec_ctx;
 	NTSTATUS gensec_status;
@@ -149,9 +149,11 @@ struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
 	switch (state->auth) {
 	case HTTP_AUTH_BASIC:
 		mech_name = "http_basic";
+		state->prefix = data_blob_string_const("Basic");
 		break;
 	case HTTP_AUTH_NTLM:
 		mech_name = "http_ntlm";
+		state->prefix = data_blob_string_const("NTLM");
 		break;
 	default:
 		tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
@@ -327,7 +329,7 @@ static void http_send_auth_request_http_rep_done(struct tevent_req *subreq)
 		return;
 	}
 
-	status = http_parse_auth_response(state->auth,
+	status = http_parse_auth_response(state->prefix,
 					  state->auth_response,
 					  &gensec_in);
 	if (tevent_req_nterror(req, status)) {
-- 
1.9.1


From 35c2a9525ea606cdf8f5d4eef2dbb6b70f1fd18a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 23:05:53 +0200
Subject: [PATCH 06/13] s4:librpc/rpc: remember the target_hostname on
 ncacn_http connections

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

diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c
index 8ed1257..bec7022 100644
--- a/source4/librpc/rpc/dcerpc_connect.c
+++ b/source4/librpc/rpc/dcerpc_connect.c
@@ -422,6 +422,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp_recv(struct composite_context *
 struct pipe_http_state {
 	struct dcerpc_pipe_connect io;
 	const char *localaddr;
+	const char *target_hostname;
 	const char *rpc_server;
 	uint32_t rpc_server_port;
 	char *rpc_proxy;
@@ -457,6 +458,8 @@ static void continue_pipe_open_ncacn_http(struct tevent_req *subreq)
 	s->io.conn->transport.stream = stream;
 	s->io.conn->transport.write_queue = queue;
 	s->io.conn->transport.pending_reads = 0;
+	s->io.conn->server_name = strupper_talloc(s->io.conn,
+						  s->target_hostname);
 
 	composite_done(c);
 }
@@ -493,6 +496,8 @@ static struct composite_context* dcerpc_pipe_connect_ncacn_http_send(
 							"localaddress");
 	/* RPC server and port (the endpoint) */
 	s->rpc_server = dcerpc_binding_get_string_option(io->binding, "host");
+	s->target_hostname = dcerpc_binding_get_string_option(io->binding,
+							      "target_hostname");
 	endpoint = dcerpc_binding_get_string_option(io->binding, "endpoint");
 	if (endpoint == NULL) {
 		composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
-- 
1.9.1


From 68152b5e954cb0a05cd8489033f1d7d3b391959b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 13:05:39 +0200
Subject: [PATCH 07/13] s4:librpc/rpc: pass down HTTP_AUTH_* values directly to
 dcerpc_pipe_open_roh_send()

They get passed to http_send_auth_request_send() unmodified.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/librpc/rpc/dcerpc_connect.c         | 11 ++++++-----
 source4/librpc/rpc/dcerpc_roh.c             | 10 +++++-----
 source4/librpc/rpc/dcerpc_roh_channel_in.c  |  5 ++---
 source4/librpc/rpc/dcerpc_roh_channel_out.c |  5 ++---
 4 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c
index bec7022..2206bef 100644
--- a/source4/librpc/rpc/dcerpc_connect.c
+++ b/source4/librpc/rpc/dcerpc_connect.c
@@ -35,6 +35,7 @@
 #include "auth/credentials/credentials.h"
 #include "param/param.h"
 #include "libcli/resolve/resolve.h"
+#include "lib/http/http.h"
 
 struct dcerpc_pipe_connect {
 	struct dcecli_connection *conn;
@@ -431,7 +432,7 @@ struct pipe_http_state {
 	uint32_t http_proxy_port;
 	bool use_tls;
 	bool use_proxy;
-	bool use_ntlm;
+	enum http_auth_method http_auth;
 	struct loadparm_context *lp_ctx;
 };
 
@@ -565,15 +566,15 @@ static struct composite_context* dcerpc_pipe_connect_ncacn_http_send(
 	opt = dcerpc_binding_get_string_option(io->binding, "HttpAuthOption");
 	if (opt) {
 		if (strcasecmp(opt, "basic") == 0) {
-			s->use_ntlm = false;
+			s->http_auth = HTTP_AUTH_BASIC;
 		} else if (strcasecmp(opt, "ntlm") == 0) {
-			s->use_ntlm = true;
+			s->http_auth = HTTP_AUTH_NTLM;
 		} else {
 			composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
 			return c;
 		}
 	} else {
-		s->use_ntlm = true;
+		s->http_auth = HTTP_AUTH_NTLM;
 	}
 
 	subreq = dcerpc_pipe_open_roh_send(s->io.conn, s->localaddr,
@@ -582,7 +583,7 @@ static struct composite_context* dcerpc_pipe_connect_ncacn_http_send(
 	                                   s->http_proxy, s->http_proxy_port,
 	                                   s->use_tls, s->use_proxy,
 	                                   s->io.creds, io->resolve_ctx,
-	                                   s->lp_ctx, s->use_ntlm);
+	                                   s->lp_ctx, s->http_auth);
 	if (composite_nomem(subreq, c)) return c;
 
 	tevent_req_set_callback(subreq, continue_pipe_open_ncacn_http, c);
diff --git a/source4/librpc/rpc/dcerpc_roh.c b/source4/librpc/rpc/dcerpc_roh.c
index 6da2978..f7e0ef8 100644
--- a/source4/librpc/rpc/dcerpc_roh.c
+++ b/source4/librpc/rpc/dcerpc_roh.c
@@ -89,7 +89,7 @@ struct roh_open_connection_state {
 	struct roh_connection		*roh;
 	struct tstream_tls_params	*tls_params;
 	struct loadparm_context		*lp_ctx;
-	bool				use_ntlm;
+	uint8_t				http_auth;
 };
 
 NTSTATUS dcerpc_pipe_open_roh_recv(struct tevent_req *req,
@@ -143,7 +143,7 @@ struct tevent_req *dcerpc_pipe_open_roh_send(struct dcecli_connection *conn,
 					     struct cli_credentials *credentials,
 					     struct resolve_context *resolve_ctx,
 					     struct loadparm_context *lp_ctx,
-					     bool use_ntlm)
+					     uint8_t http_auth)
 {
 	NTSTATUS				status;
 	struct tevent_req			*req;
@@ -170,7 +170,7 @@ struct tevent_req *dcerpc_pipe_open_roh_send(struct dcecli_connection *conn,
 	state->rpc_server_port = rpc_server_port;
 	state->rpc_proxy = talloc_strdup(state, rpc_proxy);
 	state->rpc_proxy_port = rpc_proxy_port;
-	state->use_ntlm = use_ntlm;
+	state->http_auth = http_auth;
 
 	state->roh = talloc_zero(state, struct roh_connection);
 	state->roh->protocol_version = ROH_V2;
@@ -313,7 +313,7 @@ static void roh_connect_channel_out_done(struct tevent_req *subreq)
 					   state->rpc_server,
 					   state->rpc_server_port,
 					   state->rpc_proxy,
-					   state->use_ntlm);
+					   state->http_auth);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
@@ -344,7 +344,7 @@ static void roh_send_RPC_DATA_IN_done(struct tevent_req *subreq)
 					    state->rpc_server,
 					    state->rpc_server_port,
 					    state->rpc_proxy,
-					    state->use_ntlm);
+					    state->http_auth);
 	if (tevent_req_nomem(subreq, req)) {
 		return;
 	}
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_in.c b/source4/librpc/rpc/dcerpc_roh_channel_in.c
index 887a57b..d6c37a3 100644
--- a/source4/librpc/rpc/dcerpc_roh_channel_in.c
+++ b/source4/librpc/rpc/dcerpc_roh_channel_in.c
@@ -227,7 +227,7 @@ struct tevent_req *roh_send_RPC_DATA_IN_send(TALLOC_CTX *mem_ctx,
 					     const char *rpc_server,
 					     uint32_t rpc_server_port,
 					     const char *rpc_proxy,
-					     bool use_ntlm)
+					     uint8_t http_auth)
 {
 	struct tevent_req		*req;
 	struct tevent_req		*subreq;
@@ -299,8 +299,7 @@ struct tevent_req *roh_send_RPC_DATA_IN_send(TALLOC_CTX *mem_ctx,
 					state->request,
 					credentials,
 					lp_ctx,
-					use_ntlm ? HTTP_AUTH_NTLM :
-						   HTTP_AUTH_BASIC);
+					http_auth);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
diff --git a/source4/librpc/rpc/dcerpc_roh_channel_out.c b/source4/librpc/rpc/dcerpc_roh_channel_out.c
index 8a337ad..4e1c934 100644
--- a/source4/librpc/rpc/dcerpc_roh_channel_out.c
+++ b/source4/librpc/rpc/dcerpc_roh_channel_out.c
@@ -227,7 +227,7 @@ struct tevent_req *roh_send_RPC_DATA_OUT_send(TALLOC_CTX *mem_ctx,
 					      const char *rpc_server,
 					      uint32_t rpc_server_port,
 					      const char *rpc_proxy,
-					      bool use_ntlm)
+					      uint8_t http_auth)
 {
 	struct tevent_req		*req;
 	struct tevent_req		*subreq;
@@ -299,8 +299,7 @@ struct tevent_req *roh_send_RPC_DATA_OUT_send(TALLOC_CTX *mem_ctx,
 					state->request,
 					credentials,
 					lp_ctx,
-					use_ntlm ? HTTP_AUTH_NTLM :
-						   HTTP_AUTH_BASIC);
+					http_auth);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-- 
1.9.1


From 65427e6198b9d2bacaf76ed726c2e0bb4a5e7163 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 12:07:18 +0200
Subject: [PATCH 08/13] s4:http/gensec: rename ntlm.c to generic.c

Check with git show -C

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/gensec/generic.c | 185 ++++++++++++++++++++++++++++++++++++++
 source4/lib/http/gensec/ntlm.c    | 185 --------------------------------------
 source4/lib/http/wscript_build    |   6 +-
 3 files changed, 188 insertions(+), 188 deletions(-)
 create mode 100644 source4/lib/http/gensec/generic.c
 delete mode 100644 source4/lib/http/gensec/ntlm.c

diff --git a/source4/lib/http/gensec/generic.c b/source4/lib/http/gensec/generic.c
new file mode 100644
index 0000000..92993e8
--- /dev/null
+++ b/source4/lib/http/gensec/generic.c
@@ -0,0 +1,185 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   HTTP library - NTLM authentication mechanism gensec module
+
+   Copyright (C) 2014 Samuel Cabrero <samuelcabrero at kernevil.me>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#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"
+#include "lib/util/base64.h"
+
+_PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *);
+
+struct gensec_http_ntlm_state {
+	struct gensec_security *sub;
+};
+
+static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
+{
+	NTSTATUS status;
+	struct gensec_http_ntlm_state *state;
+
+	state = talloc_zero(gensec, struct gensec_http_ntlm_state);
+	if (state == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	gensec->private_data = state;
+
+	status = gensec_subcontext_start(state, gensec, &state->sub);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	return gensec_start_mech_by_oid(state->sub, GENSEC_OID_NTLMSSP);
+}
+
+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);
+
+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) {
+			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+			return tevent_req_post(req, ev);
+		}
+		state->ntlm_in = base64_decode_data_blob_talloc(state,
+							(char *)&in.data[5]);
+	}
+
+	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);
+
+	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;
+	}
+
+	str = talloc_asprintf(state, "NTLM %s", b64);
+	TALLOC_FREE(b64);
+	if (tevent_req_nomem(str, req)) {
+		return;
+	}
+
+	state->out = data_blob_string_const(str);
+	tevent_req_done(req);
+}
+
+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;
+}
+
+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_send    = gensec_http_ntlm_update_send,
+	.update_recv    = gensec_http_ntlm_update_recv,
+	.enabled        = true,
+	.priority       = GENSEC_EXTERNAL,
+};
+
+_PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *ctx)
+{
+	NTSTATUS status;
+
+	status = gensec_register(ctx, &gensec_http_ntlm_security_ops);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0, ("Failed to register '%s' gensec backend!\n",
+				gensec_http_ntlm_security_ops.name));
+		return status;
+	}
+
+	return status;
+}
diff --git a/source4/lib/http/gensec/ntlm.c b/source4/lib/http/gensec/ntlm.c
deleted file mode 100644
index 788fb7d..0000000
--- a/source4/lib/http/gensec/ntlm.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
-   Unix SMB/CIFS implementation.
-
-   HTTP library - NTLM authentication mechanism gensec module
-
-   Copyright (C) 2014 Samuel Cabrero <samuelcabrero at kernevil.me>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#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"
-#include "lib/util/base64.h"
-
-_PUBLIC_ NTSTATUS gensec_http_ntlm_init(TALLOC_CTX *);
-
-struct gensec_http_ntlm_state {
-	struct gensec_security *sub;
-};
-
-static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
-{
-	NTSTATUS status;
-	struct gensec_http_ntlm_state *state;
-
-	state = talloc_zero(gensec, struct gensec_http_ntlm_state);
-	if (state == NULL) {
-		return NT_STATUS_NO_MEMORY;
-	}
-	gensec->private_data = state;
-
-	status = gensec_subcontext_start(state, gensec, &state->sub);
-	if (!NT_STATUS_IS_OK(status)) {
-		return status;
-	}
-
-	return gensec_start_mech_by_oid(state->sub, GENSEC_OID_NTLMSSP);
-}
-
-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);
-
-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) {
-			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-			return tevent_req_post(req, ev);
-		}
-		state->ntlm_in = base64_decode_data_blob_talloc(state,
-							(char *)&in.data[5]);
-	}
-
-	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);
-
-	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;
-	}
-
-	str = talloc_asprintf(state, "NTLM %s", b64);
-	TALLOC_FREE(b64);
-	if (tevent_req_nomem(str, req)) {
-		return;
-	}
-
-	state->out = data_blob_string_const(str);
-	tevent_req_done(req);
-}
-
-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;
-}
-
-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_send    = gensec_http_ntlm_update_send,
-	.update_recv    = gensec_http_ntlm_update_recv,
-	.enabled        = true,
-	.priority       = GENSEC_EXTERNAL,
-};
-
-_PUBLIC_ NTSTATUS gensec_http_ntlm_init(TALLOC_CTX *ctx)
-{
-	NTSTATUS status;
-
-	status = gensec_register(ctx, &gensec_http_ntlm_security_ops);
-	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(0, ("Failed to register '%s' gensec backend!\n",
-				gensec_http_ntlm_security_ops.name));
-		return status;
-	}
-
-	return status;
-}
diff --git a/source4/lib/http/wscript_build b/source4/lib/http/wscript_build
index e18d853..e77a699 100644
--- a/source4/lib/http/wscript_build
+++ b/source4/lib/http/wscript_build
@@ -13,9 +13,9 @@ bld.SAMBA_MODULE('gensec_http_basic',
 	deps='samba-util auth_session'
 )
 
-bld.SAMBA_MODULE('gensec_http_ntlm',
-	source='gensec/ntlm.c',
+bld.SAMBA_MODULE('gensec_http_generic',
+	source='gensec/generic.c',
 	subsystem='gensec',
-	init_function='gensec_http_ntlm_init',
+	init_function='gensec_http_generic_init',
 	deps='samba-util auth_session'
 )
-- 
1.9.1


From b8b26ffdcc5c41fb59ec25376044d18fa0f83673 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 13:00:27 +0200
Subject: [PATCH 09/13] s4:http/gensec: make the "NTLM" base64 wrapping more
 generic

We only need to know the prefix "NTLM" and the submech oid GENSEC_OID_NTLMSSP
everything else can be generic.

This should allow us to implement "Negotiate" with GENSEC_OID_SPNEGO
trivial.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/gensec/generic.c | 139 +++++++++++++++++++++++++++++---------
 1 file changed, 107 insertions(+), 32 deletions(-)

diff --git a/source4/lib/http/gensec/generic.c b/source4/lib/http/gensec/generic.c
index 92993e8..1d7ae47 100644
--- a/source4/lib/http/gensec/generic.c
+++ b/source4/lib/http/gensec/generic.c
@@ -29,89 +29,157 @@
 
 _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *);
 
-struct gensec_http_ntlm_state {
+struct gensec_http_generic_state {
 	struct gensec_security *sub;
+	DATA_BLOB prefix;
 };
 
-static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
+static NTSTATUS gensec_http_generic_client_start(struct gensec_security *gensec,
+						 const char *prefix_str,
+						 const char *mech_oid)
 {
 	NTSTATUS status;
-	struct gensec_http_ntlm_state *state;
+	struct gensec_http_generic_state *state;
 
-	state = talloc_zero(gensec, struct gensec_http_ntlm_state);
+	state = talloc_zero(gensec, struct gensec_http_generic_state);
 	if (state == NULL) {
 		return NT_STATUS_NO_MEMORY;
 	}
 	gensec->private_data = state;
 
+	state->prefix = data_blob_string_const(prefix_str);
+
 	status = gensec_subcontext_start(state, gensec, &state->sub);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
 
-	return gensec_start_mech_by_oid(state->sub, GENSEC_OID_NTLMSSP);
+	return gensec_start_mech_by_oid(state->sub, mech_oid);
+}
+
+static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
+{
+	return gensec_http_generic_client_start(gensec, "NTLM",
+						GENSEC_OID_NTLMSSP);
 }
 
-struct gensec_http_ntlm_update_state {
-	DATA_BLOB ntlm_in;
+struct gensec_http_generic_update_state {
+	struct gensec_security *gensec;
+	DATA_BLOB sub_in;
 	NTSTATUS status;
 	DATA_BLOB out;
 };
 
-static void gensec_http_ntlm_update_done(struct tevent_req *subreq);
+static void gensec_http_generic_update_done(struct tevent_req *subreq);
 
-static struct tevent_req *gensec_http_ntlm_update_send(TALLOC_CTX *mem_ctx,
+static struct tevent_req *gensec_http_generic_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 =
+	struct gensec_http_generic_state *http_generic =
 		talloc_get_type_abort(gensec_ctx->private_data,
-				      struct gensec_http_ntlm_state);
+				      struct gensec_http_generic_state);
 	struct tevent_req *req = NULL;
-	struct gensec_http_ntlm_update_state *state = NULL;
+	struct gensec_http_generic_update_state *state = NULL;
 	struct tevent_req *subreq = NULL;
 
 	req = tevent_req_create(mem_ctx, &state,
-				struct gensec_http_ntlm_update_state);
+				struct gensec_http_generic_update_state);
 	if (req == NULL) {
 		return NULL;
 	}
+	state->gensec = gensec_ctx;
 
 	if (in.length) {
-		if (strncasecmp((char *)in.data, "NTLM ", 5) != 0) {
+		int cmp;
+		DATA_BLOB b64b;
+		size_t skip = 0;
+
+		if (in.length < http_generic->prefix.length) {
+			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+			return tevent_req_post(req, ev);
+		}
+
+		cmp = strncasecmp((const char *)in.data,
+				  (const char *)http_generic->prefix.data,
+				  http_generic->prefix.length);
+		if (cmp != 0) {
 			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 			return tevent_req_post(req, ev);
 		}
-		state->ntlm_in = base64_decode_data_blob_talloc(state,
-							(char *)&in.data[5]);
+
+		if (in.length == http_generic->prefix.length) {
+			/*
+			 * We expect more data, but the
+			 * server just sent the prefix without
+			 * a space prefixing base64 data.
+			 *
+			 * It means the server rejects
+			 * the request with.
+			 */
+			tevent_req_nterror(req, NT_STATUS_LOGON_FAILURE);
+			return tevent_req_post(req, ev);
+		}
+
+		if (in.data[http_generic->prefix.length] != ' ') {
+			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+			return tevent_req_post(req, ev);
+		}
+		skip = http_generic->prefix.length + 1;
+
+		b64b = data_blob_const(in.data + skip, in.length - skip);
+		if (b64b.length != 0) {
+			char *b64 = NULL;
+
+			/*
+			 * ensure it's terminated with \0' before
+			 * passing to base64_decode_data_blob_talloc().
+			 */
+			b64 = talloc_strndup(state, (const char *)b64b.data,
+					     b64b.length);
+			if (tevent_req_nomem(b64, req)) {
+				return tevent_req_post(req, ev);
+			}
+
+			state->sub_in = base64_decode_data_blob_talloc(state,
+								       b64);
+			TALLOC_FREE(b64);
+			if (tevent_req_nomem(state->sub_in.data, req)) {
+				return tevent_req_post(req, ev);
+			}
+		}
 	}
 
 	subreq = gensec_update_send(state, ev,
-				    http_ntlm->sub,
-				    state->ntlm_in);
+				    http_generic->sub,
+				    state->sub_in);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
-	tevent_req_set_callback(subreq, gensec_http_ntlm_update_done, req);
+	tevent_req_set_callback(subreq, gensec_http_generic_update_done, req);
 
 	return req;
 }
 
-static void gensec_http_ntlm_update_done(struct tevent_req *subreq)
+static void gensec_http_generic_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 =
+	struct gensec_http_generic_update_state *state =
 		tevent_req_data(req,
-		struct gensec_http_ntlm_update_state);
+		struct gensec_http_generic_update_state);
+	struct gensec_http_generic_state *http_generic =
+		talloc_get_type_abort(state->gensec->private_data,
+		struct gensec_http_generic_state);
 	NTSTATUS status;
-	DATA_BLOB ntlm_out;
+	DATA_BLOB sub_out = data_blob_null;
 	char *b64 = NULL;
 	char *str = NULL;
+	int prefix_length;
 
-	status = gensec_update_recv(subreq, state, &ntlm_out);
+	status = gensec_update_recv(subreq, state, &sub_out);
 	TALLOC_FREE(subreq);
 	state->status = status;
 	if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
@@ -121,13 +189,20 @@ static void gensec_http_ntlm_update_done(struct tevent_req *subreq)
 		return;
 	}
 
-	b64 = base64_encode_data_blob(state, ntlm_out);
-	data_blob_free(&ntlm_out);
+	if (sub_out.length == 0) {
+		tevent_req_done(req);
+		return;
+	}
+
+	b64 = base64_encode_data_blob(state, sub_out);
+	data_blob_free(&sub_out);
 	if (tevent_req_nomem(b64, req)) {
 		return;
 	}
 
-	str = talloc_asprintf(state, "NTLM %s", b64);
+	prefix_length = http_generic->prefix.length;
+	str = talloc_asprintf(state, "%*.*s %s", prefix_length, prefix_length,
+			      (const char *)http_generic->prefix.data, b64);
 	TALLOC_FREE(b64);
 	if (tevent_req_nomem(str, req)) {
 		return;
@@ -137,13 +212,13 @@ static void gensec_http_ntlm_update_done(struct tevent_req *subreq)
 	tevent_req_done(req);
 }
 
-static NTSTATUS gensec_http_ntlm_update_recv(struct tevent_req *req,
+static NTSTATUS gensec_http_generic_update_recv(struct tevent_req *req,
 					     TALLOC_CTX *out_mem_ctx,
 					     DATA_BLOB *out)
 {
-	struct gensec_http_ntlm_update_state *state =
+	struct gensec_http_generic_update_state *state =
 		tevent_req_data(req,
-		struct gensec_http_ntlm_update_state);
+		struct gensec_http_generic_update_state);
 	NTSTATUS status;
 
 	*out = data_blob_null;
@@ -164,8 +239,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_send    = gensec_http_ntlm_update_send,
-	.update_recv    = gensec_http_ntlm_update_recv,
+	.update_send    = gensec_http_generic_update_send,
+	.update_recv    = gensec_http_generic_update_recv,
 	.enabled        = true,
 	.priority       = GENSEC_EXTERNAL,
 };
-- 
1.9.1


From 7633f88cef6c0b7f7860d6ef5eff42c7e91a3e55 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 13:03:03 +0200
Subject: [PATCH 10/13] s4:http/gensec: implement "http_negotiate" using
 GENSEC_OID_SPNEGO

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

diff --git a/source4/lib/http/gensec/generic.c b/source4/lib/http/gensec/generic.c
index 1d7ae47..5bafb83 100644
--- a/source4/lib/http/gensec/generic.c
+++ b/source4/lib/http/gensec/generic.c
@@ -63,6 +63,12 @@ static NTSTATUS gensec_http_ntlm_client_start(struct gensec_security *gensec)
 						GENSEC_OID_NTLMSSP);
 }
 
+static NTSTATUS gensec_http_negotiate_client_start(struct gensec_security *gensec)
+{
+	return gensec_http_generic_client_start(gensec, "Negotiate",
+						GENSEC_OID_SPNEGO);
+}
+
 struct gensec_http_generic_update_state {
 	struct gensec_security *gensec;
 	DATA_BLOB sub_in;
@@ -245,6 +251,17 @@ static const struct gensec_security_ops gensec_http_ntlm_security_ops = {
 	.priority       = GENSEC_EXTERNAL,
 };
 
+static const struct gensec_security_ops gensec_http_negotiate_security_ops = {
+	.name           = "http_negotiate",
+	.auth_type      = 0,
+	.client_start   = gensec_http_negotiate_client_start,
+	.update_send    = gensec_http_generic_update_send,
+	.update_recv    = gensec_http_generic_update_recv,
+	.enabled        = true,
+	.priority       = GENSEC_EXTERNAL,
+	.glue           = true,
+};
+
 _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *ctx)
 {
 	NTSTATUS status;
@@ -256,5 +273,12 @@ _PUBLIC_ NTSTATUS gensec_http_generic_init(TALLOC_CTX *ctx)
 		return status;
 	}
 
+	status = gensec_register(ctx, &gensec_http_negotiate_security_ops);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(0, ("Failed to register '%s' gensec backend!\n",
+			  gensec_http_negotiate_security_ops.name));
+		return status;
+	}
+
 	return status;
 }
-- 
1.9.1


From 8ceeed268439b78d4c4637907b163c138d89fb60 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 13:03:40 +0200
Subject: [PATCH 11/13] s4:lib/http: add HTTP_AUTH_NEGOTIATE which maps to the
 "http_negotiate" gensec backend

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/http.h      | 1 +
 source4/lib/http/http_auth.c | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/source4/lib/http/http.h b/source4/lib/http/http.h
index a871462..0fa65ca 100644
--- a/source4/lib/http/http.h
+++ b/source4/lib/http/http.h
@@ -65,6 +65,7 @@ enum http_cmd_type {
 enum http_auth_method {
 	HTTP_AUTH_BASIC=1,
 	HTTP_AUTH_NTLM,
+	HTTP_AUTH_NEGOTIATE,
 };
 
 struct http_header {
diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c
index 288a664..31f6a2b 100644
--- a/source4/lib/http/http_auth.c
+++ b/source4/lib/http/http_auth.c
@@ -155,6 +155,10 @@ struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
 		mech_name = "http_ntlm";
 		state->prefix = data_blob_string_const("NTLM");
 		break;
+	case HTTP_AUTH_NEGOTIATE:
+		mech_name = "http_negotiate";
+		state->prefix = data_blob_string_const("Negotiate");
+		break;
 	default:
 		tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
 		return tevent_req_post(req, ev);
-- 
1.9.1


From a01875a2b85688ec0e66fcfb25be1cf6ed693518 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 16:11:48 +0200
Subject: [PATCH 12/13] s4:lib/http: pass down the target service/hostname to
 gensec

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/lib/http/http_auth.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/source4/lib/http/http_auth.c b/source4/lib/http/http_auth.c
index 31f6a2b..f561c49 100644
--- a/source4/lib/http/http_auth.c
+++ b/source4/lib/http/http_auth.c
@@ -118,6 +118,7 @@ struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
 	struct tevent_req *subreq = NULL;
 	DATA_BLOB gensec_in = data_blob_null;
 	NTSTATUS status;
+	struct http_header *h = NULL;
 	const char *mech_name = NULL;
 
 	req = tevent_req_create(mem_ctx, &state, struct http_auth_state);
@@ -146,6 +147,26 @@ struct tevent_req *http_send_auth_request_send(TALLOC_CTX *mem_ctx,
 		return tevent_req_post(req, ev);
 	}
 
+	for (h = original_request->headers; h != NULL; h = h->next) {
+		int cmp;
+
+		cmp = strcasecmp(h->key, "Host");
+		if (cmp != 0) {
+			continue;
+		}
+
+		status = gensec_set_target_service(state->gensec_ctx, "http");
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
+
+		status = gensec_set_target_hostname(state->gensec_ctx, h->value);
+		if (tevent_req_nterror(req, status)) {
+			return tevent_req_post(req, ev);
+		}
+		break;
+	}
+
 	switch (state->auth) {
 	case HTTP_AUTH_BASIC:
 		mech_name = "http_basic";
-- 
1.9.1


From dc34f7ca6c3deb04c239e73f84d22e137ea493c0 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 20 Jul 2017 13:06:58 +0200
Subject: [PATCH 13/13] s4:librpc/rpc: add support for HttpAuthOption=negotiate

Note that rpcproxy.dll on Windows doesn't support kerberos,
it allways downgrades the connection to NTLMSSP.

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

diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c
index 2206bef..1252e74 100644
--- a/source4/librpc/rpc/dcerpc_connect.c
+++ b/source4/librpc/rpc/dcerpc_connect.c
@@ -569,6 +569,8 @@ static struct composite_context* dcerpc_pipe_connect_ncacn_http_send(
 			s->http_auth = HTTP_AUTH_BASIC;
 		} else if (strcasecmp(opt, "ntlm") == 0) {
 			s->http_auth = HTTP_AUTH_NTLM;
+		} else if (strcasecmp(opt, "negotiate") == 0) {
+			s->http_auth = HTTP_AUTH_NEGOTIATE;
 		} else {
 			composite_error(c, NT_STATUS_INVALID_PARAMETER_MIX);
 			return c;
-- 
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/20170721/a32d68e5/signature.sig>


More information about the samba-technical mailing list