summaryrefslogtreecommitdiff
path: root/source4/smb_server
diff options
context:
space:
mode:
Diffstat (limited to 'source4/smb_server')
-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
7 files changed, 196 insertions, 8 deletions
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);