[SCM] Samba Shared Repository - branch v4-0-test updated - release-4-0-0alpha3-1972-gb788096

Andrew Tridgell tridge at samba.org
Fri May 30 07:04:14 GMT 2008


The branch, v4-0-test has been updated
       via  b788096add3586d7277efcd3bf5ca7f3a604cb7a (commit)
      from  db6122ec104e80ee2e02b1170ff808b6456b780b (commit)

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v4-0-test


- Log -----------------------------------------------------------------
commit b788096add3586d7277efcd3bf5ca7f3a604cb7a
Author: Andrew Tridgell <tridge at samba.org>
Date:   Fri May 30 17:03:54 2008 +1000

    implemented client side SMB2 signing
    
    This doessn't work against Windows yet, and I've submitted a WSPP
    request for clarification of the docs to try and find out
    why. Meanwhile this is no worse than what we had, as it only gets used
    when the server demands signing, and we didn't work then anyway.

-----------------------------------------------------------------------

Summary of changes:
 source/libcli/smb2/cancel.c        |    6 +-
 source/libcli/smb2/config.mk       |    2 +-
 source/libcli/smb2/connect.c       |   54 +++++++++++-
 source/libcli/smb2/notify.c        |    6 +-
 source/libcli/smb2/session.c       |   17 +++-
 source/libcli/smb2/signing.c       |  165 ++++++++++++++++++++++++++++++++++++
 source/libcli/smb2/smb2.h          |   22 ++++-
 source/libcli/smb2/transport.c     |   49 +++++++----
 source/librpc/rpc/dcerpc_connect.c |    6 +-
 source/librpc/rpc/dcerpc_smb2.c    |    2 +-
 source/ntvfs/smb2/vfs_smb2.c       |    6 +-
 source/torture/gentest.c           |    7 +-
 source/torture/smb2/read.c         |    1 -
 source/torture/smb2/scan.c         |   11 ++-
 source/torture/smb2/util.c         |    5 +-
 15 files changed, 308 insertions(+), 51 deletions(-)
 create mode 100644 source/libcli/smb2/signing.c


Changeset truncated at 500 lines:

diff --git a/source/libcli/smb2/cancel.c b/source/libcli/smb2/cancel.c
index 80127fe..65f0218 100644
--- a/source/libcli/smb2/cancel.c
+++ b/source/libcli/smb2/cancel.c
@@ -61,10 +61,10 @@ NTSTATUS smb2_cancel(struct smb2_request *r)
 
 	SSVAL(c->out.body, 0x02, 0);
 
-	old_timeout = c->transport->options.timeout;
-	c->transport->options.timeout = 0;
+	old_timeout = c->transport->options.request_timeout;
+	c->transport->options.request_timeout = 0;
 	smb2_transport_send(c);
-	c->transport->options.timeout = old_timeout;
+	c->transport->options.request_timeout = old_timeout;
 
 	if (c->state == SMB2_REQUEST_ERROR) {
 		status = c->status;
diff --git a/source/libcli/smb2/config.mk b/source/libcli/smb2/config.mk
index 00b6305..322bca1 100644
--- a/source/libcli/smb2/config.mk
+++ b/source/libcli/smb2/config.mk
@@ -5,6 +5,6 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \
 	transport.o request.o negprot.o session.o tcon.o \
 	create.o close.o connect.o getinfo.o write.o read.o \
 	setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \
-	lock.o notify.o cancel.o keepalive.o break.o util.o)
+	lock.o notify.o cancel.o keepalive.o break.o util.o signing.o)
 
 $(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c)))
diff --git a/source/libcli/smb2/connect.c b/source/libcli/smb2/connect.c
index eabfa41..cdb5e3b 100644
--- a/source/libcli/smb2/connect.c
+++ b/source/libcli/smb2/connect.c
@@ -33,6 +33,7 @@ struct smb2_connect_state {
 	struct resolve_context *resolve_ctx;
 	const char *host;
 	const char *share;
+	struct smbcli_options options;
 	struct smb2_negprot negprot;
 	struct smb2_tree_connect tcon;
 	struct smb2_session *session;
@@ -103,6 +104,34 @@ static void continue_negprot(struct smb2_request *req)
 
 	transport->negotiate.system_time = state->negprot.out.system_time;
 	transport->negotiate.server_start_time = state->negprot.out.server_start_time;
+	transport->negotiate.security_mode = state->negprot.out.security_mode;
+
+	switch (transport->options.signing) {
+	case SMB_SIGNING_OFF:
+		if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+			composite_error(c, NT_STATUS_ACCESS_DENIED);
+			return;
+		}
+		transport->signing.doing_signing = false;
+		break;
+	case SMB_SIGNING_SUPPORTED:
+	case SMB_SIGNING_AUTO:
+		if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+			transport->signing.doing_signing = true;
+		} else {
+			transport->signing.doing_signing = false;
+		}
+		break;
+	case SMB_SIGNING_REQUIRED:
+		if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) {
+			transport->signing.doing_signing = true;
+		} else {
+			composite_error(c, NT_STATUS_ACCESS_DENIED);
+			return;
+		}
+		break;
+	}
+	
 
 	state->session = smb2_session_init(transport, global_loadparm, state, true);
 	if (composite_nomem(state->session, c)) return;
@@ -129,12 +158,24 @@ static void continue_socket(struct composite_context *creq)
 	c->status = smbcli_sock_connect_recv(creq, state, &sock);
 	if (!composite_is_ok(c)) return;
 
-	transport = smb2_transport_init(sock, state);
+	transport = smb2_transport_init(sock, state, &state->options);
 	if (composite_nomem(transport, c)) return;
 
 	ZERO_STRUCT(state->negprot);
 	state->negprot.in.dialect_count = 2;
-	state->negprot.in.security_mode = 0;
+	switch (transport->options.signing) {
+	case SMB_SIGNING_OFF:
+		state->negprot.in.security_mode = 0;
+		break;
+	case SMB_SIGNING_SUPPORTED:
+	case SMB_SIGNING_AUTO:
+		state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+		break;
+	case SMB_SIGNING_REQUIRED:
+		state->negprot.in.security_mode = 
+			SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+		break;
+	}
 	state->negprot.in.capabilities  = 0;
 	unix_to_nt_time(&state->negprot.in.start_time, time(NULL));
 	dialects[0] = 0;
@@ -178,7 +219,8 @@ struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
 					    const char *share,
 					    struct resolve_context *resolve_ctx,
 					    struct cli_credentials *credentials,
-					    struct event_context *ev)
+					    struct event_context *ev,
+					    struct smbcli_options *options)
 {
 	struct composite_context *c;
 	struct smb2_connect_state *state;
@@ -193,6 +235,7 @@ struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
 	c->private_data = state;
 
 	state->credentials = credentials;
+	state->options = *options;
 	state->host = talloc_strdup(c, host);
 	if (composite_nomem(state->host, c)) return c;
 	state->share = talloc_strdup(c, share);
@@ -232,10 +275,11 @@ NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
 		      struct resolve_context *resolve_ctx,
 		      struct cli_credentials *credentials,
 		      struct smb2_tree **tree,
-		      struct event_context *ev)
+		      struct event_context *ev,
+		      struct smbcli_options *options)
 {
 	struct composite_context *c = smb2_connect_send(mem_ctx, host, share, 
 							resolve_ctx,
-							credentials, ev);
+							credentials, ev, options);
 	return smb2_connect_recv(c, mem_ctx, tree);
 }
diff --git a/source/libcli/smb2/notify.c b/source/libcli/smb2/notify.c
index 096d790..ef7341c 100644
--- a/source/libcli/smb2/notify.c
+++ b/source/libcli/smb2/notify.c
@@ -44,10 +44,10 @@ struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify
 	SIVAL(req->out.body, 0x18, io->in.completion_filter);
 	SIVAL(req->out.body, 0x1C, io->in.unknown);
 
-	old_timeout = req->transport->options.timeout;
-	req->transport->options.timeout = 0;
+	old_timeout = req->transport->options.request_timeout;
+	req->transport->options.request_timeout = 0;
 	smb2_transport_send(req);
-	req->transport->options.timeout = old_timeout;
+	req->transport->options.request_timeout = old_timeout;
 
 	return req;
 }
diff --git a/source/libcli/smb2/session.c b/source/libcli/smb2/session.c
index 29af665..54915d8 100644
--- a/source/libcli/smb2/session.c
+++ b/source/libcli/smb2/session.c
@@ -164,8 +164,8 @@ static void session_request_handler(struct smb2_request *req)
 
 		session_key_err = gensec_session_key(session->gensec, &session_key);
 		if (NT_STATUS_IS_OK(session_key_err)) {
-			session->session_key = session_key;
-		}
+			session->transport->signing.session_key = session_key;
+		}		
 	}
 
 	session->uid = state->io.out.uid;
@@ -187,6 +187,14 @@ static void session_request_handler(struct smb2_request *req)
 		return;
 	}
 
+	if (session->transport->signing.doing_signing) {
+		c->status = smb2_start_signing(session->transport);
+		if (!NT_STATUS_IS_OK(c->status)) {
+			composite_error(c, c->status);
+			return;
+		}
+	}
+
 	composite_done(c);
 }
 
@@ -208,7 +216,10 @@ struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *se
 
 	ZERO_STRUCT(state->io);
 	state->io.in.vc_number          = 0;
-	state->io.in.security_mode      = 0;
+	if (session->transport->signing.doing_signing) {
+		state->io.in.security_mode = 
+			SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+	}
 	state->io.in.capabilities       = 0;
 	state->io.in.channel            = 0;
 	state->io.in.previous_sessionid = 0;
diff --git a/source/libcli/smb2/signing.c b/source/libcli/smb2/signing.c
new file mode 100644
index 0000000..01f7576
--- /dev/null
+++ b/source/libcli/smb2/signing.c
@@ -0,0 +1,165 @@
+/* 
+   Unix SMB/CIFS implementation.
+
+   SMB2 Signing Code
+
+   Copyright (C) Andrew Tridgell <tridge at samba.org> 2008
+
+   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 "libcli/raw/libcliraw.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "heimdal/lib/hcrypto/sha.h"
+
+/*
+  NOTE: this code does not yet interoperate with the windows SMB2
+  implementation. We are waiting on feedback on the docs to find out
+  why
+ */
+
+
+/*
+  setup signing on a transport
+ */
+NTSTATUS smb2_start_signing(struct smb2_transport *transport)
+{
+	if (transport->signing.session_key.length != 16) {
+		DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+			 (unsigned)transport->signing.session_key.length));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	transport->signing.signing_started = true;
+	return NT_STATUS_OK;
+}
+
+/*
+  sign an outgoing message
+ */
+NTSTATUS smb2_sign_message(struct smb2_request *req)
+{
+	struct smb2_request_buffer *buf = &req->out;
+	uint64_t session_id;
+	SHA256_CTX m;
+	uint8_t res[32];
+
+	if (!req->transport->signing.doing_signing ||
+	    !req->transport->signing.signing_started) {
+		return NT_STATUS_OK;
+	}
+
+	if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+		/* can't sign non-SMB2 messages */
+		return NT_STATUS_OK;
+	}
+
+	session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
+	if (session_id == 0) {
+		/* we don't sign messages with a zero session_id. See
+		   MS-SMB2 3.2.4.1.1 */
+		return NT_STATUS_OK;		
+	}
+
+	if (req->transport->signing.session_key.length != 16) {
+		DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+			 (unsigned)req->transport->signing.session_key.length));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
+
+	SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
+
+	ZERO_STRUCT(m);
+	SHA256_Init(&m);
+	SHA256_Update(&m, req->transport->signing.session_key.data, 
+		      req->transport->signing.session_key.length);
+	SHA256_Update(&m, buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE);
+	SHA256_Final(res, &m);
+
+	DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE));
+
+	memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16);
+
+	if (DEBUGLVL(5)) {
+		/* check our own signature */
+		smb2_check_signature(req->transport, buf->buffer, buf->size);
+	}
+
+	return NT_STATUS_OK;	
+}
+
+/*
+  check an incoming signature
+ */
+NTSTATUS smb2_check_signature(struct smb2_transport *transport,
+			      uint8_t *buffer, uint_t length)
+{
+	uint64_t session_id;
+	SHA256_CTX m;
+	uint8_t res[SHA256_DIGEST_LENGTH];
+	uint8_t sig[16];
+
+	if (!transport->signing.signing_started ||
+	    !transport->signing.doing_signing) {
+		return NT_STATUS_OK;
+	}
+
+	if (length < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+		/* can't check non-SMB2 messages */
+		return NT_STATUS_OK;
+	}
+
+	session_id = BVAL(buffer+NBT_HDR_SIZE, SMB2_HDR_SESSION_ID);
+	if (session_id == 0) {
+		/* don't sign messages with a zero session_id. See
+		   MS-SMB2 3.2.4.1.1 */
+		return NT_STATUS_OK;		
+	}
+
+	if (transport->signing.session_key.length == 0) {
+		/* we don't have the session key yet */
+		return NT_STATUS_OK;
+	}
+
+	if (transport->signing.session_key.length != 16) {
+		DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+			 (unsigned)transport->signing.session_key.length));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	memcpy(sig, buffer+NBT_HDR_SIZE+SMB2_HDR_SIGNATURE, 16);
+
+	memset(buffer + NBT_HDR_SIZE + SMB2_HDR_SIGNATURE, 0, 16);
+
+	ZERO_STRUCT(m);
+	SHA256_Init(&m);
+	SHA256_Update(&m, transport->signing.session_key.data,    16);
+	SHA256_Update(&m, buffer+NBT_HDR_SIZE, length-NBT_HDR_SIZE);
+	SHA256_Final(res, &m);
+
+	memcpy(buffer+NBT_HDR_SIZE+SMB2_HDR_SIGNATURE, sig, 16);
+
+	if (memcmp(res, sig, 16) != 0) {
+		DEBUG(0,("Bad SMB2 signature for message of size %u\n", length));
+		dump_data(0, sig, 16);
+		dump_data(0, res, 16);
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	return NT_STATUS_OK;	
+}
diff --git a/source/libcli/smb2/smb2.h b/source/libcli/smb2/smb2.h
index b55da05..0903509 100644
--- a/source/libcli/smb2/smb2.h
+++ b/source/libcli/smb2/smb2.h
@@ -23,20 +23,24 @@
 #define __LIBCLI_SMB2_SMB2_H__
 
 #include "libcli/raw/request.h"
+#include "libcli/raw/libcliraw.h"
 
 struct smb2_handle;
 
-struct smb2_options {
-	uint32_t timeout;
+struct smb2_signing_context {
+	bool doing_signing;
+	bool signing_started;
+	DATA_BLOB session_key;
 };
 
 /*
-  information returned from the negotiate response
+  information returned from the negotiate process
 */
 struct smb2_negotiate {
 	DATA_BLOB secblob;
 	NTTIME system_time;
 	NTTIME server_start_time;
+	uint16_t security_mode;
 };
 
 /* this is the context for the smb2 transport layer */
@@ -44,7 +48,6 @@ struct smb2_transport {
 	/* socket level info */
 	struct smbcli_socket *socket;
 
-	struct smb2_options options;
 	struct smb2_negotiate negotiate;
 
 	/* next seqnum to allocate */
@@ -74,6 +77,9 @@ struct smb2_transport {
 		/* private data passed to the oplock handler */
 		void *private_data;
 	} oplock;
+
+	struct smbcli_options options;
+	struct smb2_signing_context signing;
 };
 
 
@@ -92,7 +98,6 @@ struct smb2_session {
 	struct smb2_transport *transport;
 	struct gensec_security *gensec;
 	uint64_t uid;
-	DATA_BLOB session_key;
 };
 
 
@@ -193,6 +198,13 @@ struct smb2_request {
 #define SMB2_HDR_SIGNATURE	0x30 /* 16 bytes */
 #define SMB2_HDR_BODY		0x40
 
+/* header flags */
+#define SMB2_HDR_FLAG_REDIRECT  0x01
+#define SMB2_HDR_FLAG_ASYNC     0x02
+#define SMB2_HDR_FLAG_CHAINED   0x04
+#define SMB2_HDR_FLAG_SIGNED    0x08
+#define SMB2_HDR_FLAG_DFS       0x10000000
+
 /* SMB2 opcodes */
 #define SMB2_OP_NEGPROT   0x00
 #define SMB2_OP_SESSSETUP 0x01
diff --git a/source/libcli/smb2/transport.c b/source/libcli/smb2/transport.c
index 8eb60a0..561b6e5 100644
--- a/source/libcli/smb2/transport.c
+++ b/source/libcli/smb2/transport.c
@@ -74,7 +74,8 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob);
   create a transport structure based on an established socket
 */
 struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
-					   TALLOC_CTX *parent_ctx)
+					   TALLOC_CTX *parent_ctx,
+					   struct smbcli_options *options)
 {
 	struct smb2_transport *transport;
 
@@ -82,6 +83,7 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 	if (!transport) return NULL;
 
 	transport->socket = talloc_steal(transport, sock);
+	transport->options = *options;
 
 	/* setup the stream -> packet parser */
 	transport->packet = packet_init(transport);
@@ -112,8 +114,6 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 
 	talloc_set_destructor(transport, transport_destructor);
 
-	transport->options.timeout = 30;
-
 	return transport;
 }
 
@@ -140,27 +140,24 @@ void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
 	}
 }
 
-static bool smb2_handle_oplock_break(struct smb2_transport *transport,
-				     const DATA_BLOB *blob)
+static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
+					 const DATA_BLOB *blob)
 {
 	uint8_t *hdr;
 	uint16_t opcode;
-	uint64_t seqnum;
 
 	hdr = blob->data+NBT_HDR_SIZE;
 
 	if (blob->length < (SMB2_MIN_SIZE+0x18)) {
 		DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
-			 blob->length));
-		return false;
+			 (unsigned)blob->length));
+		return NT_STATUS_INVALID_NETWORK_RESPONSE;
 	}
 
 	opcode	= SVAL(hdr, SMB2_HDR_OPCODE);
-	seqnum	= BVAL(hdr, SMB2_HDR_MESSAGE_ID);
 
-	if ((opcode != SMB2_OP_BREAK) ||
-	    (seqnum != UINT64_MAX)) {
-		return false;
+	if (opcode != SMB2_OP_BREAK) {


-- 
Samba Shared Repository


More information about the samba-cvs mailing list