From c5e11daa8bb00665efabbf7939062e7e60112ced Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sat, 22 May 2004 11:16:21 +0000 Subject: r818: added server side SMB signing to Samba4 (This used to be commit 8e5ddf5e8eb74f667897f90baa2d00f02ca5818b) --- source4/smb_server/config.m4 | 3 +- source4/smb_server/negprot.c | 16 ++++- source4/smb_server/reply.c | 7 +- source4/smb_server/request.c | 19 ++++- source4/smb_server/sesssetup.c | 2 + source4/smb_server/signing.c | 149 ++++++++++++++++++++++++++++++++++++++++ source4/smb_server/smb_server.c | 8 ++- 7 files changed, 196 insertions(+), 8 deletions(-) create mode 100644 source4/smb_server/signing.c (limited to 'source4/smb_server') 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); -- cgit