/* 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
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
struct dcesrv_context dcesrv;
+ struct signing_context signing;
+
/* the pid of the process handling this session */
pid_t pid;
#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 */
BOOL bLanmanAuth;
BOOL bNTLMAuth;
BOOL bUseSpnego;
+ BOOL server_signing;
BOOL bClientLanManAuth;
BOOL bClientNTLMv2Auth;
BOOL bHostMSDfs;
{-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
{"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},
Globals.bUseSpnego = True;
+ Globals.server_signing = False;
+
string_set(&Globals.smb_ports, SMB_PORTS);
}
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 */
smb_server/session.o
smb_server/sesssetup.o
smb_server/srvtime.o
- smb_server/trans2.o])
+ smb_server/trans2.o
+ smb_server/signing.o])
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;
#endif
}
- req_send_reply(req);
+ req_send_reply_nosign(req);
}
/* these are the protocol lists used for auto architecture detection:
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);
}
req->out.buffer = buf;
req->out.size = 4;
- req_send_reply(req);
+ req_send_reply_nosign(req);
return;
case 0x89: /* session keepalive request
SCVAL(buf, 3, 0);
req->out.buffer = buf;
req->out.size = 4;
- req_send_reply(req);
+ req_send_reply_nosign(req);
return;
case SMBkeepalive:
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);
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);
+}
+
/*
&sess->nt1.out.lanman,
&sess->nt1.out.domain);
+ srv_setup_signing(req->smb, &session_key, &sess->nt1.in.password2);
+
return NT_STATUS_OK;
}
--- /dev/null
+/*
+ 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;
+}
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",
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);
}
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);