r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[tprouty/samba.git] / source / libsmb / smb_signing.c
index 4e9b895a1b32176327ea2bc5db353fa73c2ad80e..3964bfa534d42ffc140b2c38bad789baf0646e87 100644 (file)
@@ -1,12 +1,12 @@
 /* 
    Unix SMB/CIFS implementation.
    SMB Signing Code
-   Copyright (C) Jeremy Allison 2002.
+   Copyright (C) Jeremy Allison 2003.
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2002-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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
+/* Lookup a packet's MID (multiplex id) and figure out it's sequence number */
+struct outstanding_packet_lookup {
+       struct outstanding_packet_lookup *prev, *next;
+       uint16 mid;
+       uint32 reply_seq_num;
+       BOOL can_delete; /* Set to False in trans state. */
+};
+
 struct smb_basic_signing_context {
        DATA_BLOB mac_key;
        uint32 send_seq_num;
-       uint32 reply_seq_num;
+       struct outstanding_packet_lookup *outstanding_packet_list;
 };
 
+static BOOL store_sequence_for_reply(struct outstanding_packet_lookup **list, 
+                                    uint16 mid, uint32 reply_seq_num)
+{
+       struct outstanding_packet_lookup *t;
+
+       /* Ensure we only add a mid once. */
+       for (t = *list; t; t = t->next) {
+               if (t->mid == mid) {
+                       return False;
+               }
+       }
+
+       t = SMB_XMALLOC_P(struct outstanding_packet_lookup);
+       ZERO_STRUCTP(t);
+
+       t->mid = mid;
+       t->reply_seq_num = reply_seq_num;
+       t->can_delete = True;
+
+       /*
+        * Add to the *start* of the list not the end of the list.
+        * This ensures that the *last* send sequence with this mid
+        * is returned by preference.
+        * This can happen if the mid wraps and one of the early
+        * mid numbers didn't get a reply and is still lurking on
+        * the list. JRA. Found by Fran Fabrizio <fran@cis.uab.edu>.
+        */
+
+       DLIST_ADD(*list, t);
+       DEBUG(10,("store_sequence_for_reply: stored seq = %u mid = %u\n",
+                       (unsigned int)reply_seq_num, (unsigned int)mid ));
+       return True;
+}
+
+static BOOL get_sequence_for_reply(struct outstanding_packet_lookup **list,
+                                  uint16 mid, uint32 *reply_seq_num)
+{
+       struct outstanding_packet_lookup *t;
+
+       for (t = *list; t; t = t->next) {
+               if (t->mid == mid) {
+                       *reply_seq_num = t->reply_seq_num;
+                       DEBUG(10,("get_sequence_for_reply: found seq = %u mid = %u\n",
+                               (unsigned int)t->reply_seq_num, (unsigned int)t->mid ));
+                       if (t->can_delete) {
+                               DLIST_REMOVE(*list, t);
+                               SAFE_FREE(t);
+                       }
+                       return True;
+               }
+       }
+       return False;
+}
+
+static BOOL set_sequence_can_delete_flag(struct outstanding_packet_lookup **list, uint16 mid, BOOL can_delete_entry)
+{
+       struct outstanding_packet_lookup *t;
+
+       for (t = *list; t; t = t->next) {
+               if (t->mid == mid) {
+                       t->can_delete = can_delete_entry;
+                       return True;
+               }
+       }
+       return False;
+}
+
 /***********************************************************
  SMB signing - Common code before we set a new signing implementation
 ************************************************************/
 
-static BOOL set_smb_signing_common(struct cli_state *cli) 
+static BOOL cli_set_smb_signing_common(struct cli_state *cli) 
 {
+       if (!cli->sign_info.allow_smb_signing) {
+               return False;
+       }
+
        if (!cli->sign_info.negotiated_smb_signing 
-           && !cli->sign_info.mandetory_signing) {
+           && !cli->sign_info.mandatory_signing) {
                return False;
        }
 
@@ -43,7 +121,7 @@ static BOOL set_smb_signing_common(struct cli_state *cli)
        }
        
        if (cli->sign_info.free_signing_context)
-               cli->sign_info.free_signing_context(cli);
+               cli->sign_info.free_signing_context(&cli->sign_info);
 
        /* These calls are INCOMPATIBLE with SMB signing */
        cli->readbraw_supported = False;
@@ -56,42 +134,122 @@ static BOOL set_smb_signing_common(struct cli_state *cli)
  SMB signing - Common code for 'real' implementations
 ************************************************************/
 
-static BOOL set_smb_signing_real_common(struct cli_state *cli) 
+static BOOL set_smb_signing_real_common(struct smb_sign_info *si)
 {
-       if (cli->sign_info.mandetory_signing) {
+       if (si->mandatory_signing) {
                DEBUG(5, ("Mandatory SMB signing enabled!\n"));
-               cli->sign_info.doing_signing = True;
        }
 
+       si->doing_signing = True;
        DEBUG(5, ("SMB signing enabled!\n"));
 
        return True;
 }
 
-static void mark_packet_signed(struct cli_state *cli) 
+static void mark_packet_signed(char *outbuf)
 {
        uint16 flags2;
-       flags2 = SVAL(cli->outbuf,smb_flg2);
+       flags2 = SVAL(outbuf,smb_flg2);
        flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
-       SSVAL(cli->outbuf,smb_flg2, flags2);
+       SSVAL(outbuf,smb_flg2, flags2);
 }
 
-static BOOL signing_good(struct cli_state *cli, BOOL good) 
+/***********************************************************
+ SMB signing - NULL implementation - calculate a MAC to send.
+************************************************************/
+
+static void null_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
 {
-       DEBUG(10, ("got SMB signature of\n"));
-       dump_data(10,&cli->inbuf[smb_ss_field] , 8);
+       /* we can't zero out the sig, as we might be trying to send a
+          session request - which is NBT-level, not SMB level and doesn't
+          have the field */
+       return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - check a MAC sent by server.
+************************************************************/
+
+static BOOL null_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL must_be_ok)
+{
+       return True;
+}
 
-       if (good && !cli->sign_info.doing_signing) {
-               cli->sign_info.doing_signing = True;
+/***********************************************************
+ SMB signing - NULL implementation - free signing context
+************************************************************/
+
+static void null_free_signing_context(struct smb_sign_info *si)
+{
+       return;
+}
+
+/**
+ SMB signing - NULL implementation - setup the MAC key.
+
+ @note Used as an initialisation only - it will not correctly
+       shut down a real signing mechanism
+*/
+
+static BOOL null_set_signing(struct smb_sign_info *si)
+{
+       si->signing_context = NULL;
+       
+       si->sign_outgoing_message = null_sign_outgoing_message;
+       si->check_incoming_message = null_check_incoming_message;
+       si->free_signing_context = null_free_signing_context;
+
+       return True;
+}
+
+/**
+ * Free the signing context
+ */
+static void free_signing_context(struct smb_sign_info *si)
+{
+       if (si->free_signing_context) {
+               si->free_signing_context(si);
+               si->signing_context = NULL;
        }
 
-       if (!good) {
-               if (cli->sign_info.doing_signing) {
-                       DEBUG(1, ("SMB signature check failed!\n"));
-                       return False;
+       null_set_signing(si);
+}
+
+
+static BOOL signing_good(char *inbuf, struct smb_sign_info *si, BOOL good, uint32 seq, BOOL must_be_ok) 
+{
+       if (good) {
+
+               if (!si->doing_signing) {
+                       si->doing_signing = True;
+               }
+               
+               if (!si->seen_valid) {
+                       si->seen_valid = True;
+               }
+
+       } else {
+               if (!si->mandatory_signing && !si->seen_valid) {
+
+                       if (!must_be_ok) {
+                               return True;
+                       }
+                       /* Non-mandatory signing - just turn off if this is the first bad packet.. */
+                       DEBUG(5, ("srv_check_incoming_message: signing negotiated but not required and peer\n"
+                                 "isn't sending correct signatures. Turning off.\n"));
+                       si->negotiated_smb_signing = False;
+                       si->allow_smb_signing = False;
+                       si->doing_signing = False;
+                       free_signing_context(si);
+                       return True;
+               } else if (!must_be_ok) {
+                       /* This packet is known to be unsigned */
+                       return True;
                } else {
-                       DEBUG(3, ("Server did not sign reply correctly\n"));
-                       cli_free_signing_context(cli);
+                       /* Mandatory signing or bad packet after signing started - fail and disconnect. */
+                       if (seq)
+                               DEBUG(0, ("signing_good: BAD SIG: seq %u\n", (unsigned int)seq));
                        return False;
                }
        }
@@ -99,103 +257,201 @@ static BOOL signing_good(struct cli_state *cli, BOOL good)
 }      
 
 /***********************************************************
- SMB signing - Simple implementation - calculate a MAC to send.
+ SMB signing - Simple implementation - calculate a MAC on the packet
 ************************************************************/
 
-static void cli_simple_sign_outgoing_message(struct cli_state *cli)
+static void simple_packet_signature(struct smb_basic_signing_context *data, 
+                                   const uchar *buf, uint32 seq_number, 
+                                   unsigned char calc_md5_mac[16])
 {
-       unsigned char calc_md5_mac[16];
+       const size_t offset_end_of_sig = (smb_ss_field + 8);
+       unsigned char sequence_buf[8];
        struct MD5Context md5_ctx;
-       struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+#if 0
+        /* JRA - apparently this is incorrect. */
+       unsigned char key_buf[16];
+#endif
 
        /*
         * Firstly put the sequence number into the first 4 bytes.
         * and zero out the next 4 bytes.
+        *
+        * We do this here, to avoid modifying the packet.
         */
-       SIVAL(cli->outbuf, smb_ss_field, 
-             data->send_seq_num);
-       SIVAL(cli->outbuf, smb_ss_field + 4, 0);
 
-       /* mark the packet as signed - BEFORE we sign it...*/
-       mark_packet_signed(cli);
+       DEBUG(10,("simple_packet_signature: sequence number %u\n", seq_number ));
+
+       SIVAL(sequence_buf, 0, seq_number);
+       SIVAL(sequence_buf, 4, 0);
 
-       /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
+       /* Calculate the 16 byte MAC - but don't alter the data in the
+          incoming packet.
+          
+          This makes for a bit of fussing about, but it's not too bad.
+       */
        MD5Init(&md5_ctx);
-       MD5Update(&md5_ctx, data->mac_key.data, 
-                 data->mac_key.length); 
-       MD5Update(&md5_ctx, cli->outbuf + 4, smb_len(cli->outbuf));
+
+       /* intialise with the key */
+       MD5Update(&md5_ctx, data->mac_key.data, data->mac_key.length); 
+#if 0
+       /* JRA - apparently this is incorrect. */
+       /* NB. When making and verifying SMB signatures, Windows apparently
+               zero-pads the key to 128 bits if it isn't long enough.
+               From Nalin Dahyabhai <nalin@redhat.com> */
+       if (data->mac_key.length < sizeof(key_buf)) {
+               memset(key_buf, 0, sizeof(key_buf));
+               MD5Update(&md5_ctx, key_buf, sizeof(key_buf) - data->mac_key.length);
+       }
+#endif
+
+       /* copy in the first bit of the SMB header */
+       MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4);
+
+       /* copy in the sequence number, instead of the signature */
+       MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
+
+       /* copy in the rest of the packet in, skipping the signature */
+       MD5Update(&md5_ctx, buf + offset_end_of_sig, 
+                 smb_len(buf) - (offset_end_of_sig - 4));
+
+       /* calculate the MD5 sig */ 
        MD5Final(calc_md5_mac, &md5_ctx);
+}
+
+
+/***********************************************************
+ SMB signing - Client implementation - send the MAC.
+************************************************************/
+
+static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
+{
+       unsigned char calc_md5_mac[16];
+       struct smb_basic_signing_context *data =
+               (struct smb_basic_signing_context *)si->signing_context;
+
+       if (!si->doing_signing)
+               return;
+
+       /* JRA Paranioa test - we should be able to get rid of this... */
+       if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+               DEBUG(1, ("client_sign_outgoing_message: Logic error. Can't check signature on short packet! smb_len = %u\n",
+                                       smb_len(outbuf) ));
+               abort();
+       }
 
-       DEBUG(10, ("sent SMB signature of\n"));
+       /* mark the packet as signed - BEFORE we sign it...*/
+       mark_packet_signed(outbuf);
+
+       simple_packet_signature(data, (const unsigned char *)outbuf,
+                               data->send_seq_num, calc_md5_mac);
+
+       DEBUG(10, ("client_sign_outgoing_message: sent SMB signature of\n"));
        dump_data(10, calc_md5_mac, 8);
 
-       memcpy(&cli->outbuf[smb_ss_field], calc_md5_mac, 8);
+       memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
 
 /*     cli->outbuf[smb_ss_field+2]=0; 
-       Uncomment this to test if the remote server actually verifies signitures...*/
-       data->send_seq_num++;
-       data->reply_seq_num = data->send_seq_num;
-       data->send_seq_num++;
+       Uncomment this to test if the remote server actually verifies signatures...*/
+
+       /* Instead of re-introducing the trans_info_conect we
+          used to have here, we use the fact that during a
+          SMBtrans/SMBtrans2/SMBnttrans send that the mid stays
+          constant. This means that calling store_sequence_for_reply()
+          will return False for all trans secondaries, as the mid is already
+          on the stored sequence list. As the send_seqence_number must
+          remain constant for all primary+secondary trans sends, we
+          only increment the send sequence number when we successfully
+          add a new entry to the outstanding sequence list. This means
+          I can isolate the fix here rather than re-adding the trans
+          signing on/off calls in libsmb/clitrans2.c JRA.
+        */
+       
+       if (store_sequence_for_reply(&data->outstanding_packet_list, SVAL(outbuf,smb_mid), data->send_seq_num + 1)) {
+               data->send_seq_num += 2;
+       }
 }
 
 /***********************************************************
- SMB signing - Simple implementation - check a MAC sent by server.
+ SMB signing - Client implementation - check a MAC sent by server.
 ************************************************************/
 
-static BOOL cli_simple_check_incoming_message(struct cli_state *cli)
+static BOOL client_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL must_be_ok)
 {
        BOOL good;
+       uint32 reply_seq_number;
        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 = cli->sign_info.signing_context;
-       const size_t offset_end_of_sig = (smb_ss_field + 8);
+       unsigned char *server_sent_mac;
 
-       /*
-        * Firstly put the sequence number into the first 4 bytes.
-        * and zero out the next 4 bytes.
-        */
+       struct smb_basic_signing_context *data =
+               (struct smb_basic_signing_context *)si->signing_context;
 
-       SIVAL(sequence_buf, 0, data->reply_seq_num);
-       SIVAL(sequence_buf, 4, 0);
+       if (!si->doing_signing)
+               return True;
 
-       /* get a copy of the server-sent mac */
-       memcpy(server_sent_mac, &cli->inbuf[smb_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, cli->inbuf + 4, smb_ss_field - 4);
-       MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
-       
-       MD5Update(&md5_ctx, cli->inbuf + offset_end_of_sig, 
-                 smb_len(cli->inbuf) - (offset_end_of_sig - 4));
-       MD5Final(calc_md5_mac, &md5_ctx);
+       if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+               DEBUG(1, ("client_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+               return False;
+       }
+
+       if (!get_sequence_for_reply(&data->outstanding_packet_list, SVAL(inbuf, smb_mid), &reply_seq_number)) {
+               DEBUG(1, ("client_check_incoming_message: received message "
+                       "with mid %u with no matching send record.\n", (unsigned int)SVAL(inbuf, smb_mid) ));
+               return False;
+       }
+
+       simple_packet_signature(data, (const unsigned char *)inbuf,
+                               reply_seq_number, calc_md5_mac);
 
+       server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
        good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
        
        if (!good) {
-               DEBUG(5, ("BAD SIG: wanted SMB signature of\n"));
+               DEBUG(5, ("client_check_incoming_message: BAD SIG: wanted SMB signature of\n"));
                dump_data(5, calc_md5_mac, 8);
                
-               DEBUG(5, ("BAD SIG: got SMB signature of\n"));
+               DEBUG(5, ("client_check_incoming_message: BAD SIG: got SMB signature of\n"));
                dump_data(5, server_sent_mac, 8);
+#if 1 /* JRATEST */
+               {
+                       int i;
+                       for (i = -5; i < 5; i++) {
+                               simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number+i, calc_md5_mac);
+                               if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+                                       DEBUG(0,("client_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number+i, reply_seq_number ));
+                                       break;
+                               }
+                       }
+               }
+#endif /* JRATEST */
+
+       } else {
+               DEBUG(10, ("client_check_incoming_message: seq %u: got good SMB signature of\n", (unsigned int)reply_seq_number));
+               dump_data(10, server_sent_mac, 8);
        }
-       return signing_good(cli, good);
+       return signing_good(inbuf, si, good, reply_seq_number, must_be_ok);
 }
 
 /***********************************************************
  SMB signing - Simple implementation - free signing context
 ************************************************************/
 
-static void cli_simple_free_signing_context(struct cli_state *cli)
+static void simple_free_signing_context(struct smb_sign_info *si)
 {
-       struct smb_basic_signing_context *data = cli->sign_info.signing_context;
+       struct smb_basic_signing_context *data =
+               (struct smb_basic_signing_context *)si->signing_context;
+       struct outstanding_packet_lookup *list;
+       struct outstanding_packet_lookup *next;
+       
+       for (list = data->outstanding_packet_list; list; list = next) {
+               next = list->next;
+               DLIST_REMOVE(data->outstanding_packet_list, list);
+               SAFE_FREE(list);
+       }
 
        data_blob_free(&data->mac_key);
-       SAFE_FREE(cli->sign_info.signing_context);
+
+       SAFE_FREE(si->signing_context);
 
        return;
 }
@@ -204,273 +460,537 @@ static void cli_simple_free_signing_context(struct cli_state *cli)
  SMB signing - Simple implementation - setup the MAC key.
 ************************************************************/
 
-BOOL cli_simple_set_signing(struct cli_state *cli, const uchar user_session_key[16], const DATA_BLOB response)
+BOOL cli_simple_set_signing(struct cli_state *cli,
+                           const DATA_BLOB user_session_key,
+                           const DATA_BLOB response)
 {
        struct smb_basic_signing_context *data;
 
-       if (!set_smb_signing_common(cli)) {
+       if (!user_session_key.length)
+               return False;
+
+       if (!cli_set_smb_signing_common(cli)) {
                return False;
        }
 
-       if (!set_smb_signing_real_common(cli)) {
+       if (!set_smb_signing_real_common(&cli->sign_info)) {
                return False;
        }
 
-       data = smb_xmalloc(sizeof(*data));
+       data = SMB_XMALLOC_P(struct smb_basic_signing_context);
+       memset(data, '\0', sizeof(*data));
+
        cli->sign_info.signing_context = data;
        
-       data->mac_key = data_blob(NULL, MIN(response.length + 16, 40));
+       data->mac_key = data_blob(NULL, response.length + user_session_key.length);
 
-       memcpy(&data->mac_key.data[0], user_session_key, 16);
-       memcpy(&data->mac_key.data[16],response.data, MIN(response.length, 40 - 16));
+       memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+
+       DEBUG(10, ("cli_simple_set_signing: user_session_key\n"));
+       dump_data(10, user_session_key.data, user_session_key.length);
+
+       if (response.length) {
+               memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+               DEBUG(10, ("cli_simple_set_signing: response_data\n"));
+               dump_data(10, response.data, response.length);
+       } else {
+               DEBUG(10, ("cli_simple_set_signing: NULL response_data\n"));
+       }
+
+       dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
 
        /* Initialise the sequence number */
        data->send_seq_num = 0;
 
-       cli->sign_info.sign_outgoing_message = cli_simple_sign_outgoing_message;
-       cli->sign_info.check_incoming_message = cli_simple_check_incoming_message;
-       cli->sign_info.free_signing_context = cli_simple_free_signing_context;
+       /* Initialise the list of outstanding packets */
+       data->outstanding_packet_list = NULL;
+
+       cli->sign_info.sign_outgoing_message = client_sign_outgoing_message;
+       cli->sign_info.check_incoming_message = client_check_incoming_message;
+       cli->sign_info.free_signing_context = simple_free_signing_context;
 
        return True;
 }
 
 /***********************************************************
- SMB signing - NTLMSSP implementation - calculate a MAC to send.
+ SMB signing - TEMP implementation - calculate a MAC to send.
 ************************************************************/
 
-static void cli_ntlmssp_sign_outgoing_message(struct cli_state *cli)
+static void temp_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
 {
-       NTSTATUS nt_status;
-       DATA_BLOB sig;
-       NTLMSSP_CLIENT_STATE *ntlmssp_state = cli->sign_info.signing_context;
-
        /* mark the packet as signed - BEFORE we sign it...*/
-       mark_packet_signed(cli);
-       
-       nt_status = ntlmssp_client_sign_packet(ntlmssp_state, cli->outbuf + 4, 
-                                              smb_len(cli->outbuf), &sig);
-       
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(0, ("NTLMSSP signing failed with %s\n", nt_errstr(nt_status)));
-               return;
-       }
+       mark_packet_signed(outbuf);
 
-       DEBUG(10, ("sent SMB signature of\n"));
-       dump_data(10, sig.data, MIN(sig.length, 8));
-       memcpy(&cli->outbuf[smb_ss_field], sig.data, MIN(sig.length, 8));
-       
-       data_blob_free(&sig);
+       /* I wonder what BSRSPYL stands for - but this is what MS 
+          actually sends! */
+       memcpy(&outbuf[smb_ss_field], "BSRSPYL ", 8);
+       return;
 }
 
 /***********************************************************
- SMB signing - NTLMSSP implementation - check a MAC sent by server.
+ SMB signing - TEMP implementation - check a MAC sent by server.
 ************************************************************/
 
-static BOOL cli_ntlmssp_check_incoming_message(struct cli_state *cli)
+static BOOL temp_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL foo)
 {
-       BOOL good;
-       NTSTATUS nt_status;
-       DATA_BLOB sig = data_blob(&cli->inbuf[smb_ss_field], 8);
+       return True;
+}
 
-       NTLMSSP_CLIENT_STATE *ntlmssp_state = cli->sign_info.signing_context;
+/***********************************************************
+ SMB signing - TEMP implementation - free signing context
+************************************************************/
 
-       nt_status = ntlmssp_client_check_packet(ntlmssp_state, cli->outbuf + 4, 
-                                               smb_len(cli->outbuf), &sig);
-       
-       data_blob_free(&sig);
-       
-       good = NT_STATUS_IS_OK(nt_status);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(5, ("NTLMSSP signing failed with %s\n", nt_errstr(nt_status)));
-       }
+static void temp_free_signing_context(struct smb_sign_info *si)
+{
+       return;
+}
+
+/***********************************************************
+ SMB signing - NULL implementation - setup the MAC key.
+************************************************************/
 
-       return signing_good(cli, good);
+BOOL cli_null_set_signing(struct cli_state *cli)
+{
+       return null_set_signing(&cli->sign_info);
 }
 
 /***********************************************************
- SMB signing - NTLMSSP implementation - free signing context
+ SMB signing - temp implementation - setup the MAC key.
 ************************************************************/
 
-static void cli_ntlmssp_free_signing_context(struct cli_state *cli)
+BOOL cli_temp_set_signing(struct cli_state *cli)
+{
+       if (!cli_set_smb_signing_common(cli)) {
+               return False;
+       }
+
+       cli->sign_info.signing_context = NULL;
+       
+       cli->sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+       cli->sign_info.check_incoming_message = temp_check_incoming_message;
+       cli->sign_info.free_signing_context = temp_free_signing_context;
+
+       return True;
+}
+
+void cli_free_signing_context(struct cli_state *cli)
 {
-       ntlmssp_client_end((NTLMSSP_CLIENT_STATE **)&cli->sign_info.signing_context);
+       free_signing_context(&cli->sign_info);
+}
+
+/**
+ * Sign a packet with the current mechanism
+ */
+void cli_calculate_sign_mac(struct cli_state *cli)
+{
+       cli->sign_info.sign_outgoing_message(cli->outbuf, &cli->sign_info);
+}
+
+/**
+ * Check a packet with the current mechanism
+ * @return False if we had an established signing connection
+ *         which had a bad checksum, True otherwise.
+ */
+BOOL cli_check_sign_mac(struct cli_state *cli) 
+{
+       if (!cli->sign_info.check_incoming_message(cli->inbuf, &cli->sign_info, True)) {
+               free_signing_context(&cli->sign_info);  
+               return False;
+       }
+       return True;
 }
 
 /***********************************************************
SMB signing - NTLMSSP implementation - setup the MAC key.
Enter trans/trans2/nttrans state.
 ************************************************************/
 
-BOOL cli_ntlmssp_set_signing(struct cli_state *cli,
-                            NTLMSSP_CLIENT_STATE *ntlmssp_state)
+BOOL client_set_trans_sign_state_on(struct cli_state *cli, uint16 mid)
 {
-       if (!set_smb_signing_common(cli)) {
+       struct smb_sign_info *si = &cli->sign_info;
+       struct smb_basic_signing_context *data = (struct smb_basic_signing_context *)si->signing_context;
+
+       if (!si->doing_signing) {
+               return True;
+       }
+
+       if (!data) {
                return False;
        }
 
-       if (!NT_STATUS_IS_OK(ntlmssp_client_sign_init(ntlmssp_state))) {
+       if (!set_sequence_can_delete_flag(&data->outstanding_packet_list, mid, False)) {
                return False;
        }
 
-       if (!set_smb_signing_real_common(cli)) {
+       return True;
+}
+
+/***********************************************************
+ Leave trans/trans2/nttrans state.
+************************************************************/
+
+BOOL client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid)
+{
+       uint32 reply_seq_num;
+       struct smb_sign_info *si = &cli->sign_info;
+       struct smb_basic_signing_context *data = (struct smb_basic_signing_context *)si->signing_context;
+
+       if (!si->doing_signing) {
+               return True;
+       }
+
+       if (!data) {
                return False;
        }
 
-       cli->sign_info.signing_context = ntlmssp_state;
-       ntlmssp_state->ref_count++;
+       if (!set_sequence_can_delete_flag(&data->outstanding_packet_list, mid, True)) {
+               return False;
+       }
 
-       cli->sign_info.sign_outgoing_message = cli_ntlmssp_sign_outgoing_message;
-       cli->sign_info.check_incoming_message = cli_ntlmssp_check_incoming_message;
-       cli->sign_info.free_signing_context = cli_ntlmssp_free_signing_context;
+       /* Now delete the stored mid entry. */
+       if (!get_sequence_for_reply(&data->outstanding_packet_list, mid, &reply_seq_num)) {
+               return False;
+       }
 
        return True;
 }
 
 /***********************************************************
- SMB signing - NULL implementation - calculate a MAC to send.
+ Is client signing on ?
 ************************************************************/
 
-static void cli_null_sign_outgoing_message(struct cli_state *cli)
+BOOL client_is_signing_on(struct cli_state *cli)
 {
-       /* we can't zero out the sig, as we might be trying to send a
-          session request - which is NBT-level, not SMB level and doesn't
-          have the field */
-       return;
+       struct smb_sign_info *si = &cli->sign_info;
+       return si->doing_signing;
 }
 
 /***********************************************************
- SMB signing - NULL implementation - check a MAC sent by server.
+ SMB signing - Server implementation - send the MAC.
 ************************************************************/
 
-static BOOL cli_null_check_incoming_message(struct cli_state *cli)
+static void srv_sign_outgoing_message(char *outbuf, struct smb_sign_info *si)
 {
-       return True;
+       unsigned char calc_md5_mac[16];
+       struct smb_basic_signing_context *data =
+               (struct smb_basic_signing_context *)si->signing_context;
+       uint32 send_seq_number = data->send_seq_num-1;
+       uint16 mid;
+
+       if (!si->doing_signing) {
+               return;
+       }
+
+       /* JRA Paranioa test - we should be able to get rid of this... */
+       if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) {
+               DEBUG(1, ("srv_sign_outgoing_message: Logic error. Can't send signature on short packet! smb_len = %u\n",
+                                       smb_len(outbuf) ));
+               abort();
+       }
+
+       /* mark the packet as signed - BEFORE we sign it...*/
+       mark_packet_signed(outbuf);
+
+       mid = SVAL(outbuf, smb_mid);
+
+       /* See if this is a reply for a deferred packet. */
+       get_sequence_for_reply(&data->outstanding_packet_list, mid, &send_seq_number);
+
+       simple_packet_signature(data, (const unsigned char *)outbuf, send_seq_number, calc_md5_mac);
+
+       DEBUG(10, ("srv_sign_outgoing_message: seq %u: sent SMB signature of\n", (unsigned int)send_seq_number));
+       dump_data(10, calc_md5_mac, 8);
+
+       memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8);
+
+/*     cli->outbuf[smb_ss_field+2]=0; 
+       Uncomment this to test if the remote client actually verifies signatures...*/
 }
 
 /***********************************************************
- SMB signing - NULL implementation - free signing context
+ SMB signing - Server implementation - check a MAC sent by server.
 ************************************************************/
 
-static void cli_null_free_signing_context(struct cli_state *cli)
+static BOOL srv_check_incoming_message(char *inbuf, struct smb_sign_info *si, BOOL must_be_ok)
 {
-       return;
-}
+       BOOL good;
+       struct smb_basic_signing_context *data =
+               (struct smb_basic_signing_context *)si->signing_context;
+       uint32 reply_seq_number = data->send_seq_num;
+       uint32 saved_seq;
+       unsigned char calc_md5_mac[16];
+       unsigned char *server_sent_mac;
 
-/**
- SMB signing - NULL implementation - setup the MAC key.
+       if (!si->doing_signing)
+               return True;
 
- @note Used as an initialisation only - it will not correctly
-       shut down a real signing mechanism
-*/
+       if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) {
+               DEBUG(1, ("srv_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf)));
+               return False;
+       }
 
-BOOL cli_null_set_signing(struct cli_state *cli)
-{
-       cli->sign_info.signing_context = NULL;
+       /* We always increment the sequence number. */
+       data->send_seq_num += 2;
+
+       saved_seq = reply_seq_number;
+       simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+
+       server_sent_mac = (unsigned char *)&inbuf[smb_ss_field];
+       good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
        
-       cli->sign_info.sign_outgoing_message = cli_null_sign_outgoing_message;
-       cli->sign_info.check_incoming_message = cli_null_check_incoming_message;
-       cli->sign_info.free_signing_context = cli_null_free_signing_context;
+       if (!good) {
 
-       return True;
+               if (saved_seq) {
+                       DEBUG(0, ("srv_check_incoming_message: BAD SIG: seq %u wanted SMB signature of\n",
+                                       (unsigned int)saved_seq));
+                       dump_data(5, calc_md5_mac, 8);
+
+                       DEBUG(0, ("srv_check_incoming_message: BAD SIG: seq %u got SMB signature of\n",
+                                               (unsigned int)reply_seq_number));
+                       dump_data(5, server_sent_mac, 8);
+               }
+               
+#if 1 /* JRATEST */
+               {
+                       int i;
+                       reply_seq_number -= 5;
+                       for (i = 0; i < 10; i++, reply_seq_number++) {
+                               simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number, calc_md5_mac);
+                               if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) {
+                                       DEBUG(0,("srv_check_incoming_message: out of seq. seq num %u matches. \
+We were expecting seq %u\n", reply_seq_number, saved_seq ));
+                                       break;
+                               }
+                       }
+               }
+#endif /* JRATEST */
+
+       } else {
+               DEBUG(10, ("srv_check_incoming_message: seq %u: (current is %u) got good SMB signature of\n", (unsigned int)reply_seq_number, (unsigned int)data->send_seq_num));
+               dump_data(10, server_sent_mac, 8);
+       }
+
+       return (signing_good(inbuf, si, good, saved_seq, must_be_ok));
 }
 
 /***********************************************************
- SMB signing - TEMP implementation - calculate a MAC to send.
+ SMB signing - server API's.
+************************************************************/
+
+static struct smb_sign_info srv_sign_info = {
+       null_sign_outgoing_message,
+       null_check_incoming_message,
+       null_free_signing_context,
+       NULL,
+       False,
+       False,
+       False,
+       False
+};
+
+/***********************************************************
+ Turn signing off or on for oplock break code.
 ************************************************************/
 
-static void cli_temp_sign_outgoing_message(struct cli_state *cli)
+BOOL srv_oplock_set_signing(BOOL onoff)
 {
-       /* mark the packet as signed - BEFORE we sign it...*/
-       mark_packet_signed(cli);
+       BOOL ret = srv_sign_info.doing_signing;
+       srv_sign_info.doing_signing = onoff;
+       return ret;
+}
 
-       /* I wonder what BSRSPYL stands for - but this is what MS 
-          actually sends! */
-       memcpy(&cli->outbuf[smb_ss_field], "BSRSPYL ", 8);
-       return;
+/***********************************************************
+ Called to validate an incoming packet from the client.
+************************************************************/
+
+BOOL srv_check_sign_mac(char *inbuf, BOOL must_be_ok)
+{
+       /* Check if it's a session keepalive. */
+       if(CVAL(inbuf,0) == SMBkeepalive) {
+               return True;
+       }
+
+       return srv_sign_info.check_incoming_message(inbuf, &srv_sign_info, must_be_ok);
 }
 
 /***********************************************************
SMB signing - TEMP implementation - check a MAC sent by server.
Called to sign an outgoing packet to the client.
 ************************************************************/
 
-static BOOL cli_temp_check_incoming_message(struct cli_state *cli)
+void srv_calculate_sign_mac(char *outbuf)
 {
-       return True;
+       /* Check if it's a session keepalive. */
+       if(CVAL(outbuf,0) == SMBkeepalive) {
+               return;
+       }
+
+       srv_sign_info.sign_outgoing_message(outbuf, &srv_sign_info);
 }
 
 /***********************************************************
- SMB signing - TEMP implementation - free signing context
+ Called by server to defer an outgoing packet.
 ************************************************************/
 
-static void cli_temp_free_signing_context(struct cli_state *cli)
+void srv_defer_sign_response(uint16 mid)
 {
-       return;
+       struct smb_basic_signing_context *data;
+
+       if (!srv_sign_info.doing_signing)
+               return;
+
+       data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+       if (!data)
+               return;
+
+       /*
+        * Ensure we only store this mid reply once...
+        */
+
+       store_sequence_for_reply(&data->outstanding_packet_list, mid,
+                                data->send_seq_num-1);
 }
 
 /***********************************************************
- SMB signing - NULL implementation - setup the MAC key.
+ Called to remove sequence records when a deferred packet is
+ cancelled by mid. This should never find one....
 ************************************************************/
 
-BOOL cli_temp_set_signing(struct cli_state *cli)
+void srv_cancel_sign_response(uint16 mid)
 {
-       if (!set_smb_signing_common(cli)) {
-               return False;
-       }
+       struct smb_basic_signing_context *data;
+       uint32 dummy_seq;
 
-       cli->sign_info.signing_context = NULL;
-       
-       cli->sign_info.sign_outgoing_message = cli_temp_sign_outgoing_message;
-       cli->sign_info.check_incoming_message = cli_temp_check_incoming_message;
-       cli->sign_info.free_signing_context = cli_temp_free_signing_context;
+       if (!srv_sign_info.doing_signing)
+               return;
 
-       return True;
+       data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+
+       if (!data)
+               return;
+
+       DEBUG(10,("srv_cancel_sign_response: for mid %u\n", (unsigned int)mid ));
+
+       while (get_sequence_for_reply(&data->outstanding_packet_list, mid, &dummy_seq))
+               ;
+
+       /* cancel doesn't send a reply so doesn't burn a sequence number. */
+       data->send_seq_num -= 1;
 }
 
-/**
- * Free the signing context
- */
-void cli_free_signing_context(struct cli_state *cli) 
+/***********************************************************
+ Called by server negprot when signing has been negotiated.
+************************************************************/
+
+void srv_set_signing_negotiated(void)
 {
-       if (cli->sign_info.free_signing_context) 
-               cli->sign_info.free_signing_context(cli);
+       srv_sign_info.allow_smb_signing = True;
+       srv_sign_info.negotiated_smb_signing = True;
+       if (lp_server_signing() == Required)
+               srv_sign_info.mandatory_signing = True;
+
+       srv_sign_info.sign_outgoing_message = temp_sign_outgoing_message;
+       srv_sign_info.check_incoming_message = temp_check_incoming_message;
+       srv_sign_info.free_signing_context = temp_free_signing_context;
+}
+
+/***********************************************************
+ Returns whether signing is active. We can't use sendfile or raw
+ reads/writes if it is.
+************************************************************/
 
-       cli_null_set_signing(cli);
+BOOL srv_is_signing_active(void)
+{
+       return srv_sign_info.doing_signing;
 }
 
-/**
- * Sign a packet with the current mechanism
- */
-void cli_caclulate_sign_mac(struct cli_state *cli)
+
+/***********************************************************
+ Returns whether signing is negotiated. We can't use it unless it was
+ in the negprot.  
+************************************************************/
+
+BOOL srv_is_signing_negotiated(void)
 {
-       cli->sign_info.sign_outgoing_message(cli);
+       return srv_sign_info.negotiated_smb_signing;
 }
 
-/**
- * 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_check_sign_mac(struct cli_state *cli) 
+/***********************************************************
+ Returns whether signing is actually happening
+************************************************************/
+
+BOOL srv_signing_started(void)
 {
-       BOOL good;
+       struct smb_basic_signing_context *data;
 
-       if (smb_len(cli->inbuf) < (smb_ss_field + 8 - 4)) {
-               DEBUG(cli->sign_info.doing_signing ? 1 : 10, ("Can't check signature on short packet! smb_len = %u\n", smb_len(cli->inbuf)));
-               good = False;
-       } else {
-               good = cli->sign_info.check_incoming_message(cli);
+       if (!srv_sign_info.doing_signing) {
+               return False;
        }
 
-       if (!good) {
-               if (cli->sign_info.doing_signing) {
-                       return False;
-               } else {
-                       cli_free_signing_context(cli);  
-               }
+       data = (struct smb_basic_signing_context *)srv_sign_info.signing_context;
+       if (!data)
+               return False;
+
+       if (data->send_seq_num == 0) {
+               return False;
        }
 
        return True;
 }
 
+/***********************************************************
+ Turn on signing from this packet onwards. 
+************************************************************/
+
+void srv_set_signing(const DATA_BLOB user_session_key, const DATA_BLOB response)
+{
+       struct smb_basic_signing_context *data;
+
+       if (!user_session_key.length)
+               return;
+
+       if (!srv_sign_info.negotiated_smb_signing && !srv_sign_info.mandatory_signing) {
+               DEBUG(5,("srv_set_signing: signing negotiated = %u, mandatory_signing = %u. Not allowing smb signing.\n",
+                       (unsigned int)srv_sign_info.negotiated_smb_signing,
+                       (unsigned int)srv_sign_info.mandatory_signing ));
+               return;
+       }
+
+       /* Once we've turned on, ignore any more sessionsetups. */
+       if (srv_sign_info.doing_signing) {
+               return;
+       }
+       
+       if (srv_sign_info.free_signing_context)
+               srv_sign_info.free_signing_context(&srv_sign_info);
+       
+       srv_sign_info.doing_signing = True;
+
+       data = SMB_XMALLOC_P(struct smb_basic_signing_context);
+       memset(data, '\0', sizeof(*data));
+
+       srv_sign_info.signing_context = data;
+       
+       data->mac_key = data_blob(NULL, response.length + user_session_key.length);
+
+       memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length);
+       if (response.length)
+               memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
+
+       dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length);
+
+       DEBUG(3,("srv_set_signing: turning on SMB signing: signing negotiated = %s, mandatory_signing = %s.\n",
+                               BOOLSTR(srv_sign_info.negotiated_smb_signing),
+                               BOOLSTR(srv_sign_info.mandatory_signing) ));
+
+       /* Initialise the sequence number */
+       data->send_seq_num = 0;
+
+       /* Initialise the list of outstanding packets */
+       data->outstanding_packet_list = NULL;
+
+       srv_sign_info.sign_outgoing_message = srv_sign_outgoing_message;
+       srv_sign_info.check_incoming_message = srv_check_incoming_message;
+       srv_sign_info.free_signing_context = simple_free_signing_context;
+}