summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/include/context.h12
-rw-r--r--source4/include/smb.h2
-rw-r--r--source4/param/loadparm.c26
-rw-r--r--source4/smb_server/config.m43
-rw-r--r--source4/smb_server/negprot.c16
-rw-r--r--source4/smb_server/reply.c7
-rw-r--r--source4/smb_server/request.c19
-rw-r--r--source4/smb_server/sesssetup.c2
-rw-r--r--source4/smb_server/signing.c149
-rw-r--r--source4/smb_server/smb_server.c8
10 files changed, 236 insertions, 8 deletions
diff --git a/source4/include/context.h b/source4/include/context.h
index 17664c50f8..e533c6d243 100644
--- a/source4/include/context.h
+++ b/source4/include/context.h
@@ -114,6 +114,9 @@ struct request_context {
/* how far through the chain of SMB commands have we gone? */
unsigned chain_count;
+ /* the sequence number for signing */
+ large_t seq_num;
+
/* the async structure allows backend functions to delay
replying to requests. To use this, the front end must set
async.send_fn to a function to be called by the backend
@@ -329,6 +332,13 @@ struct timers_context {
time_t last_smb_conf_reload;
};
+
+struct signing_context {
+ DATA_BLOB mac_key;
+ large_t next_seq_num;
+ enum smb_signing_state signing_state;
+};
+
#include "smbd/process_model.h"
/* smb context structure. This should contain all the state
@@ -355,6 +365,8 @@ struct server_context {
struct dcesrv_context dcesrv;
+ struct signing_context signing;
+
/* the pid of the process handling this session */
pid_t pid;
diff --git a/source4/include/smb.h b/source4/include/smb.h
index 911aa9bd41..0881469af3 100644
--- a/source4/include/smb.h
+++ b/source4/include/smb.h
@@ -37,6 +37,8 @@
#define True (1)
#define Auto (2)
+enum smb_signing_state {SMB_SIGNING_OFF, SMB_SIGNING_SUPPORTED, SMB_SIGNING_REQUIRED};
+
#ifndef _BOOL
typedef int BOOL;
#define _BOOL /* So we don't typedef BOOL again in vfs.h */
diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c
index 3cd6b0b9ef..f357703013 100644
--- a/source4/param/loadparm.c
+++ b/source4/param/loadparm.c
@@ -211,6 +211,7 @@ typedef struct
BOOL bLanmanAuth;
BOOL bNTLMAuth;
BOOL bUseSpnego;
+ BOOL server_signing;
BOOL bClientLanManAuth;
BOOL bClientNTLMv2Auth;
BOOL bHostMSDfs;
@@ -487,6 +488,27 @@ static const struct enum_list enum_csc_policy[] = {
{-1, NULL}
};
+/* SMB signing types. */
+static const struct enum_list enum_smb_signing_vals[] = {
+ {SMB_SIGNING_OFF, "No"},
+ {SMB_SIGNING_OFF, "False"},
+ {SMB_SIGNING_OFF, "0"},
+ {SMB_SIGNING_OFF, "Off"},
+ {SMB_SIGNING_OFF, "disabled"},
+ {SMB_SIGNING_SUPPORTED, "Yes"},
+ {SMB_SIGNING_SUPPORTED, "True"},
+ {SMB_SIGNING_SUPPORTED, "1"},
+ {SMB_SIGNING_SUPPORTED, "On"},
+ {SMB_SIGNING_SUPPORTED, "enabled"},
+ {SMB_SIGNING_SUPPORTED, "auto"},
+ {SMB_SIGNING_REQUIRED, "required"},
+ {SMB_SIGNING_REQUIRED, "mandatory"},
+ {SMB_SIGNING_REQUIRED, "force"},
+ {SMB_SIGNING_REQUIRED, "forced"},
+ {SMB_SIGNING_REQUIRED, "enforced"},
+ {-1, NULL}
+};
+
/*
Do you want session setups at user level security with a invalid
password to be rejected or allowed in as guest? WinNT rejects them
@@ -631,6 +653,7 @@ static struct parm_struct parm_table[] = {
{"time server", P_BOOL, P_GLOBAL, &Globals.bTimeServer, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"unix extensions", P_BOOL, P_GLOBAL, &Globals.bUnixExtensions, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
{"use spnego", P_BOOL, P_GLOBAL, &Globals.bUseSpnego, NULL, NULL, FLAG_DEVELOPER},
+ {"server signing", P_ENUM, P_GLOBAL, &Globals.server_signing, NULL, enum_smb_signing_vals, FLAG_ADVANCED},
{"rpc big endian", P_BOOL, P_GLOBAL, &Globals.bRpcBigEndian, NULL, NULL, FLAG_DEVELOPER},
{"Tuning Options", P_SEP, P_SEPARATOR},
@@ -1083,6 +1106,8 @@ static void init_globals(void)
Globals.bUseSpnego = True;
+ Globals.server_signing = False;
+
string_set(&Globals.smb_ports, SMB_PORTS);
}
@@ -1352,6 +1377,7 @@ FN_GLOBAL_INTEGER(lp_winbind_cache_time, &Globals.winbind_cache_time)
FN_GLOBAL_BOOL(lp_hide_local_users, &Globals.bHideLocalUsers)
FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase)
FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout)
+FN_GLOBAL_INTEGER(lp_server_signing, &Globals.server_signing)
/* local prototypes */
diff --git a/source4/smb_server/config.m4 b/source4/smb_server/config.m4
index e94dbf1444..4722faf8bb 100644
--- a/source4/smb_server/config.m4
+++ b/source4/smb_server/config.m4
@@ -13,4 +13,5 @@ SMB_SUBSYSTEM(SMB,smb_server/smb_server.o,
smb_server/session.o
smb_server/sesssetup.o
smb_server/srvtime.o
- smb_server/trans2.o])
+ smb_server/trans2.o
+ smb_server/signing.o])
diff --git a/source4/smb_server/negprot.c b/source4/smb_server/negprot.c
index d9d65d4cf2..253dc1d7a4 100644
--- a/source4/smb_server/negprot.c
+++ b/source4/smb_server/negprot.c
@@ -288,6 +288,20 @@ static void reply_nt1(struct request_context *req, uint16 choice)
if (req->smb->negotiate.encrypted_passwords) {
secword |= NEGOTIATE_SECURITY_CHALLENGE_RESPONSE;
}
+
+ req->smb->signing.signing_state = lp_server_signing();
+
+ switch (req->smb->signing.signing_state) {
+ case SMB_SIGNING_OFF:
+ break;
+ case SMB_SIGNING_SUPPORTED:
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED;
+ break;
+ case SMB_SIGNING_REQUIRED:
+ secword |= NEGOTIATE_SECURITY_SIGNATURES_ENABLED |
+ NEGOTIATE_SECURITY_SIGNATURES_REQUIRED;
+ break;
+ }
req->smb->negotiate.protocol = PROTOCOL_NT1;
@@ -334,7 +348,7 @@ static void reply_nt1(struct request_context *req, uint16 choice)
#endif
}
- req_send_reply(req);
+ req_send_reply_nosign(req);
}
/* these are the protocol lists used for auto architecture detection:
diff --git a/source4/smb_server/reply.c b/source4/smb_server/reply.c
index 8b9bb4fb5e..073ee956ca 100644
--- a/source4/smb_server/reply.c
+++ b/source4/smb_server/reply.c
@@ -710,7 +710,8 @@ failed:
req->out.size = 4;
req->out.buffer = talloc(req->mem_ctx, req->out.size);
SIVAL(req->out.buffer, 0, 0); /* init NBT header */
- req_send_reply(req);
+
+ req_send_reply_nosign(req);
}
@@ -2335,7 +2336,7 @@ void reply_special(struct request_context *req)
req->out.buffer = buf;
req->out.size = 4;
- req_send_reply(req);
+ req_send_reply_nosign(req);
return;
case 0x89: /* session keepalive request
@@ -2344,7 +2345,7 @@ void reply_special(struct request_context *req)
SCVAL(buf, 3, 0);
req->out.buffer = buf;
req->out.size = 4;
- req_send_reply(req);
+ req_send_reply_nosign(req);
return;
case SMBkeepalive:
diff --git a/source4/smb_server/request.c b/source4/smb_server/request.c
index 964f4a2d70..5c1d14a9d0 100644
--- a/source4/smb_server/request.c
+++ b/source4/smb_server/request.c
@@ -256,7 +256,7 @@ void req_grow_data(struct request_context *req, unsigned new_size)
note that this only looks at req->out.buffer and req->out.size, allowing manually
constructed packets to be sent
*/
-void req_send_reply(struct request_context *req)
+void req_send_reply_nosign(struct request_context *req)
{
if (req->out.size > NBT_HDR_SIZE) {
_smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
@@ -269,6 +269,23 @@ void req_send_reply(struct request_context *req)
req_destroy(req);
}
+/*
+ possibly sign a message then send a reply and destroy the request buffer
+
+ note that this only looks at req->out.buffer and req->out.size, allowing manually
+ constructed packets to be sent
+*/
+void req_send_reply(struct request_context *req)
+{
+ if (req->out.size > NBT_HDR_SIZE) {
+ _smb_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
+ }
+
+ req_sign_packet(req);
+
+ req_send_reply_nosign(req);
+}
+
/*
diff --git a/source4/smb_server/sesssetup.c b/source4/smb_server/sesssetup.c
index f42efcb7ec..fdcc1d298a 100644
--- a/source4/smb_server/sesssetup.c
+++ b/source4/smb_server/sesssetup.c
@@ -127,6 +127,8 @@ static NTSTATUS sesssetup_nt1(struct request_context *req, union smb_sesssetup *
&sess->nt1.out.lanman,
&sess->nt1.out.domain);
+ srv_setup_signing(req->smb, &session_key, &sess->nt1.in.password2);
+
return NT_STATUS_OK;
}
diff --git a/source4/smb_server/signing.c b/source4/smb_server/signing.c
new file mode 100644
index 0000000000..a3779e17cf
--- /dev/null
+++ b/source4/smb_server/signing.c
@@ -0,0 +1,149 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ mark the flags2 field in a packet as signed
+*/
+static void mark_packet_signed(struct request_context *req)
+{
+ uint16 flags2;
+ flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(req->out.hdr, HDR_FLG2, flags2);
+}
+
+/*
+ calculate the signature for a message
+*/
+static void calc_signature(uint8 *buffer, size_t length,
+ DATA_BLOB *mac_key, uint8 signature[8])
+{
+ unsigned char calc_md5_mac[16];
+ struct MD5Context md5_ctx;
+
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, mac_key->data, mac_key->length);
+ MD5Update(&md5_ctx, buffer, length);
+ MD5Final(calc_md5_mac, &md5_ctx);
+ memcpy(signature, calc_md5_mac, 8);
+}
+
+
+/*
+ sign an outgoing packet
+*/
+void req_sign_packet(struct request_context *req)
+{
+ /* check if we are doing signing on this connection */
+ if (req->smb->signing.signing_state != SMB_SIGNING_REQUIRED) {
+ return;
+ }
+
+ SBVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num+1);
+
+ mark_packet_signed(req);
+
+ calc_signature(req->out.hdr, req->out.size - NBT_HDR_SIZE,
+ &req->smb->signing.mac_key,
+ &req->out.hdr[HDR_SS_FIELD]);
+}
+
+
+/*
+ setup the signing key for a connection. Called after authentication succeeds
+ in a session setup
+*/
+void srv_setup_signing(struct server_context *smb,
+ DATA_BLOB *session_key,
+ DATA_BLOB *session_response)
+{
+ smb->signing.mac_key = data_blob(NULL,
+ session_key->length + session_response->length);
+ memcpy(smb->signing.mac_key.data, session_key->data, session_key->length);
+ if (session_response->length != 0) {
+ memcpy(&smb->signing.mac_key.data[session_key->length],
+ session_response->data,
+ session_response->length);
+ }
+}
+
+
+/*
+ allocate a sequence number to a request
+*/
+static void req_signing_alloc_seq_num(struct request_context *req)
+{
+ req->seq_num = req->smb->signing.next_seq_num;
+
+ /* TODO: we need to handle one-way requests like NTcancel, which
+ only increment the sequence number by 1 */
+ if (req->smb->signing.signing_state != SMB_SIGNING_OFF) {
+ req->smb->signing.next_seq_num += 2;
+ }
+}
+
+/*
+ check the signature of an incoming packet
+*/
+BOOL req_signing_check_incoming(struct request_context *req)
+{
+ unsigned char client_md5_mac[8], signature[8];
+
+ switch (req->smb->signing.signing_state) {
+ case SMB_SIGNING_OFF:
+ return True;
+ case SMB_SIGNING_SUPPORTED:
+ if (req->flags2 & FLAGS2_SMB_SECURITY_SIGNATURES) {
+ req->smb->signing.signing_state = SMB_SIGNING_REQUIRED;
+ }
+ return True;
+ case SMB_SIGNING_REQUIRED:
+ break;
+ }
+
+ req_signing_alloc_seq_num(req);
+
+ /* the first packet isn't checked as the key hasn't been established */
+ if (req->seq_num == 0) {
+ return True;
+ }
+
+ /* room enough for the signature? */
+ if (req->in.size < NBT_HDR_SIZE + HDR_SS_FIELD + 8) {
+ return False;
+ }
+
+ memcpy(client_md5_mac, req->in.hdr + HDR_SS_FIELD, 8);
+
+ SBVAL(req->in.hdr, HDR_SS_FIELD, req->seq_num);
+
+ calc_signature(req->in.hdr, req->in.size - NBT_HDR_SIZE,
+ &req->smb->signing.mac_key,
+ signature);
+
+ if (memcmp(client_md5_mac, signature, 8) != 0) {
+ DEBUG(2,("Bad SMB signature seq_num=%d\n", (int)req->seq_num));
+ return False;
+ }
+
+ return True;
+}
diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c
index aceae08ad8..ce50df04c3 100644
--- a/source4/smb_server/smb_server.c
+++ b/source4/smb_server/smb_server.c
@@ -558,7 +558,6 @@ static void construct_reply(struct request_context *req)
return;
}
-
/* Make sure this is an SMB packet */
if (memcmp(req->in.hdr,"\377SMB",4) != 0) {
DEBUG(2,("Non-SMB packet of length %d. Terminating connection\n",
@@ -584,6 +583,11 @@ static void construct_reply(struct request_context *req)
req->flags = CVAL(req->in.hdr, HDR_FLG);
req->flags2 = SVAL(req->in.hdr, HDR_FLG2);
+ if (!req_signing_check_incoming(req)) {
+ req_reply_error(req, NT_STATUS_ACCESS_DENIED);
+ return;
+ }
+
switch_message(type, req);
}
@@ -733,7 +737,7 @@ void init_smbsession(struct event_context *ev, struct model_ops *model_ops, int
mem_ctx = talloc_init("server_context");
- smb = (struct server_context *)talloc(mem_ctx, sizeof(*smb));
+ smb = talloc_p(mem_ctx, struct server_context);
if (!smb) return;
ZERO_STRUCTP(smb);