r818: added server side SMB signing to Samba4
authorAndrew Tridgell <tridge@samba.org>
Sat, 22 May 2004 11:16:21 +0000 (11:16 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:53:52 +0000 (12:53 -0500)
(This used to be commit 8e5ddf5e8eb74f667897f90baa2d00f02ca5818b)

source4/include/context.h
source4/include/smb.h
source4/param/loadparm.c
source4/smb_server/config.m4
source4/smb_server/negprot.c
source4/smb_server/reply.c
source4/smb_server/request.c
source4/smb_server/sesssetup.c
source4/smb_server/signing.c [new file with mode: 0644]
source4/smb_server/smb_server.c

index 17664c50f860f740fece3aa7fe6ce135e94a0046..e533c6d243788d846cbdd71f85803eb3cb59ed4b 100644 (file)
@@ -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;
        
index 911aa9bd413c4935e23f28002571676fb63d7fba..0881469af32f1ac043eff6660bceaa306a4c594b 100644 (file)
@@ -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 */
index 3cd6b0b9ef285d20524b11255ea8b52e63064e34..f357703013923688e2ccdbb5113e5e0c40854ae0 100644 (file)
@@ -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 */
 
index e94dbf1444c903832a07afd94a7a5325f540fed2..4722faf8bbc015ecc10af6e615608fc988a1e193 100644 (file)
@@ -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])
index d9d65d4cf2a38e6e795a1af4be44c45749b697ca..253dc1d7a4465475d12d092c0700403687e974ce 100644 (file)
@@ -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:
index 8b9bb4fb5efc07da722a3690214c3031e0a21e19..073ee956ca7fd9358b61d9f6d4f93d152f45f90d 100644 (file)
@@ -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: 
index 964f4a2d706fabe070bdc2aa7c527c5b33c828c6..5c1d14a9d07f32ab14060d271638ec92dcced39e 100644 (file)
@@ -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);
+}
+
 
 
 /* 
index f42efcb7ec91e8d496777254c355b718ff300158..fdcc1d298a6f2e2915c94ae69ef5064bc6c01084 100644 (file)
@@ -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 (file)
index 0000000..a3779e1
--- /dev/null
@@ -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;
+}
index aceae08ad8581990e7457e3d866b3362bcc017f4..ce50df04c3a80331e70ed27272620839d51b618a 100644 (file)
@@ -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);