summaryrefslogtreecommitdiff
path: root/source4/libcli/raw/smb_signing.c
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
committerAndrew Tridgell <tridge@samba.org>2003-08-13 01:53:07 +0000
commitef2e26c91b80556af033d3335e55f5dfa6fff31d (patch)
treefaa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/libcli/raw/smb_signing.c
downloadsamba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.gz
first public release of samba4 code
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)
Diffstat (limited to 'source4/libcli/raw/smb_signing.c')
-rw-r--r--source4/libcli/raw/smb_signing.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c
new file mode 100644
index 00000000000..2ab61aa001e
--- /dev/null
+++ b/source4/libcli/raw/smb_signing.c
@@ -0,0 +1,341 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB Signing Code
+ Copyright (C) Jeremy Allison 2002.
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-2003
+ Copyright (C) James J Myers <myersjj@samba.org> 2003
+
+ 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"
+
+struct smb_basic_signing_context {
+ DATA_BLOB mac_key;
+ uint32 next_seq_num;
+};
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+static BOOL set_smb_signing_common(struct cli_transport *transport)
+{
+ if (!(transport->negotiate.sec_mode &
+ (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+ return False;
+ }
+
+ if (transport->negotiate.sign_info.doing_signing) {
+ return False;
+ }
+
+ if (transport->negotiate.sign_info.free_signing_context)
+ transport->negotiate.sign_info.free_signing_context(transport);
+
+ /* These calls are INCOMPATIBLE with SMB signing */
+ transport->negotiate.readbraw_supported = False;
+ transport->negotiate.writebraw_supported = False;
+
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Common code for 'real' implementations
+************************************************************/
+static BOOL set_smb_signing_real_common(struct cli_transport *transport)
+{
+ if (transport->negotiate.sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) {
+ DEBUG(5, ("Mandatory SMB signing enabled!\n"));
+ transport->negotiate.sign_info.doing_signing = True;
+ }
+
+ DEBUG(5, ("SMB signing enabled!\n"));
+
+ return True;
+}
+
+static void mark_packet_signed(struct cli_request *req)
+{
+ uint16 flags2;
+ flags2 = SVAL(req->out.hdr, HDR_FLG2);
+ flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
+ SSVAL(req->out.hdr, HDR_FLG2, flags2);
+}
+
+static BOOL signing_good(struct cli_request *req, BOOL good)
+{
+ if (good && !req->transport->negotiate.sign_info.doing_signing) {
+ req->transport->negotiate.sign_info.doing_signing = True;
+ }
+
+ if (!good) {
+ if (req->transport->negotiate.sign_info.doing_signing) {
+ DEBUG(1, ("SMB signature check failed!\n"));
+ return False;
+ } else {
+ DEBUG(3, ("Server did not sign reply correctly\n"));
+ cli_transport_free_signing_context(req->transport);
+ return False;
+ }
+ }
+ return True;
+}
+
+/***********************************************************
+ SMB signing - Simple implementation - calculate a MAC to send.
+************************************************************/
+static void cli_request_simple_sign_outgoing_message(struct cli_request *req)
+{
+ unsigned char calc_md5_mac[16];
+ struct MD5Context md5_ctx;
+ struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
+
+#if 0
+ /* enable this when packet signing is preventing you working out why valgrind
+ says that data is uninitialised */
+ file_save("pkt.dat", req->out.buffer, req->out.size);
+#endif
+
+ req->seq_num = data->next_seq_num;
+
+ /* some requests (eg. NTcancel) are one way, and the sequence number
+ should be increased by 1 not 2 */
+ if (req->one_way_request) {
+ data->next_seq_num += 1;
+ } else {
+ data->next_seq_num += 2;
+ }
+
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num);
+ SIVAL(req->out.hdr, HDR_SS_FIELD + 4, 0);
+
+ /* mark the packet as signed - BEFORE we sign it...*/
+ mark_packet_signed(req);
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, data->mac_key.data,
+ data->mac_key.length);
+ MD5Update(&md5_ctx,
+ req->out.buffer + NBT_HDR_SIZE,
+ req->out.size - NBT_HDR_SIZE);
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ memcpy(&req->out.hdr[HDR_SS_FIELD], calc_md5_mac, 8);
+
+/* req->out.hdr[HDR_SS_FIELD+2]=0;
+ Uncomment this to test if the remote server actually verifies signitures...*/
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - check a MAC sent by server.
+************************************************************/
+static BOOL cli_request_simple_check_incoming_message(struct cli_request *req)
+{
+ BOOL good;
+ unsigned char calc_md5_mac[16];
+ unsigned char server_sent_mac[8];
+ unsigned char sequence_buf[8];
+ struct MD5Context md5_ctx;
+ struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
+ const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
+ int i;
+ const int sign_range = 0;
+
+ /* its quite bogus to be guessing sequence numbers, but very useful
+ when debugging signing implementations */
+ for (i = 1-sign_range; i <= 1+sign_range; i++) {
+ /*
+ * Firstly put the sequence number into the first 4 bytes.
+ * and zero out the next 4 bytes.
+ */
+ SIVAL(sequence_buf, 0, req->seq_num+i);
+ SIVAL(sequence_buf, 4, 0);
+
+ /* get a copy of the server-sent mac */
+ memcpy(server_sent_mac, &req->in.hdr[HDR_SS_FIELD], sizeof(server_sent_mac));
+
+ /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, data->mac_key.data,
+ data->mac_key.length);
+ MD5Update(&md5_ctx, req->in.hdr, HDR_SS_FIELD);
+ MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+ MD5Update(&md5_ctx, req->in.hdr + offset_end_of_sig,
+ req->in.size - NBT_HDR_SIZE - (offset_end_of_sig));
+ MD5Final(calc_md5_mac, &md5_ctx);
+
+ good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
+ if (good) break;
+ }
+
+ if (good && i != 1) {
+ DEBUG(0,("SIGNING OFFSET %d\n", i));
+ }
+
+ if (!good) {
+ DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
+ dump_data(5, calc_md5_mac, 8);
+
+ DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG: got SMB signature of\n"));
+ dump_data(5, server_sent_mac, 8);
+ }
+ return signing_good(req, good);
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - free signing context
+************************************************************/
+static void cli_transport_simple_free_signing_context(struct cli_transport *transport)
+{
+ struct smb_basic_signing_context *data = transport->negotiate.sign_info.signing_context;
+
+ data_blob_free(&data->mac_key);
+ SAFE_FREE(transport->negotiate.sign_info.signing_context);
+
+ return;
+}
+
+
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+BOOL cli_transport_simple_set_signing(struct cli_transport *transport,
+ const uchar user_transport_key[16], const DATA_BLOB response)
+{
+ struct smb_basic_signing_context *data;
+
+ if (!set_smb_signing_common(transport)) {
+ return False;
+ }
+
+ if (!set_smb_signing_real_common(transport)) {
+ return False;
+ }
+
+ data = smb_xmalloc(sizeof(*data));
+ transport->negotiate.sign_info.signing_context = data;
+
+ data->mac_key = data_blob(NULL, MIN(response.length + 16, 40));
+
+ memcpy(&data->mac_key.data[0], user_transport_key, 16);
+ memcpy(&data->mac_key.data[16],response.data, MIN(response.length, 40 - 16));
+
+ /* Initialise the sequence number */
+ data->next_seq_num = 0;
+
+ transport->negotiate.sign_info.sign_outgoing_message = cli_request_simple_sign_outgoing_message;
+ transport->negotiate.sign_info.check_incoming_message = cli_request_simple_check_incoming_message;
+ transport->negotiate.sign_info.free_signing_context = cli_transport_simple_free_signing_context;
+
+ return True;
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+static void cli_request_null_sign_outgoing_message(struct cli_request *req)
+{
+ /* we can't zero out the sig, as we might be trying to send a
+ transport request - which is NBT-level, not SMB level and doesn't
+ have the field */
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+static BOOL cli_request_null_check_incoming_message(struct cli_request *req)
+{
+ return True;
+}
+
+
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+static void cli_null_free_signing_context(struct cli_transport *transport)
+{
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+ shut down a real signing mechanism
+*/
+BOOL cli_null_set_signing(struct cli_transport *transport)
+{
+ transport->negotiate.sign_info.signing_context = NULL;
+
+ transport->negotiate.sign_info.sign_outgoing_message = cli_request_null_sign_outgoing_message;
+ transport->negotiate.sign_info.check_incoming_message = cli_request_null_check_incoming_message;
+ transport->negotiate.sign_info.free_signing_context = cli_null_free_signing_context;
+
+ return True;
+}
+
+
+/**
+ * Free the signing context
+ */
+void cli_transport_free_signing_context(struct cli_transport *transport)
+{
+ if (transport->negotiate.sign_info.free_signing_context) {
+ transport->negotiate.sign_info.free_signing_context(transport);
+ }
+
+ cli_null_set_signing(transport);
+}
+
+
+/**
+ * Sign a packet with the current mechanism
+ */
+void cli_request_calculate_sign_mac(struct cli_request *req)
+{
+ req->transport->negotiate.sign_info.sign_outgoing_message(req);
+}
+
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ * which had a back checksum, True otherwise
+ */
+BOOL cli_request_check_sign_mac(struct cli_request *req)
+{
+ BOOL good;
+
+ if (req->in.size < (HDR_SS_FIELD + 8)) {
+ good = False;
+ } else {
+ good = req->transport->negotiate.sign_info.check_incoming_message(req);
+ }
+
+ if (!good && req->transport->negotiate.sign_info.doing_signing) {
+ return False;
+ }
+
+ return True;
+}