r23792: convert Samba4 to GPLv3
[samba.git] / source4 / libcli / raw / smb_signing.c
index c1fad1eaf8d1fcb5b12324bb24b8aebfbe771754..99044d23aec4fad126c860619133350ce6977687 100644 (file)
@@ -7,7 +7,7 @@
    
    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"
+#include "smb.h"
+#include "libcli/raw/libcliraw.h"
+#include "lib/crypto/crypto.h"
 
 /***********************************************************
  SMB signing - Common code before we set a new signing implementation
 ************************************************************/
-static BOOL set_smb_signing_common(struct smbcli_transport *transport)
+BOOL set_smb_signing_common(struct smb_signing_context *sign_info)
 {
-       if (!(transport->negotiate.sec_mode & 
-             (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+       if (sign_info->doing_signing) {
+               DEBUG(5, ("SMB Signing already in progress, so we don't start it again\n"));
                return False;
        }
 
-       if (transport->negotiate.sign_info.doing_signing) {
+       if (!sign_info->allow_smb_signing) {
+               DEBUG(5, ("SMB Signing has been locally disabled\n"));
                return False;
        }
 
-       if (!transport->negotiate.sign_info.allow_smb_signing) {
+       return True;
+}
+
+/***********************************************************
+ SMB signing - Common code before we set a new signing implementation
+************************************************************/
+static BOOL smbcli_set_smb_signing_common(struct smbcli_transport *transport)
+{
+       if (!set_smb_signing_common(&transport->negotiate.sign_info)) {
                return False;
        }
 
-       if (transport->negotiate.sign_info.free_signing_context)
-               transport->negotiate.sign_info
-                       .free_signing_context(&transport->negotiate.sign_info);
+       if (!(transport->negotiate.sec_mode & 
+             (NEGOTIATE_SECURITY_SIGNATURES_REQUIRED|NEGOTIATE_SECURITY_SIGNATURES_ENABLED))) {
+               DEBUG(5, ("SMB Signing is not negotiated by the peer\n"));
+               return False;
+       }
 
        /* These calls are INCOMPATIBLE with SMB signing */
        transport->negotiate.readbraw_supported = False;
@@ -51,7 +64,7 @@ static BOOL set_smb_signing_common(struct smbcli_transport *transport)
        return True;
 }
 
-static void mark_packet_signed(struct request_buffer *out) 
+void mark_packet_signed(struct request_buffer *out) 
 {
        uint16_t flags2;
        flags2 = SVAL(out->hdr, HDR_FLG2);
@@ -59,14 +72,16 @@ static void mark_packet_signed(struct request_buffer *out)
        SSVAL(out->hdr, HDR_FLG2, flags2);
 }
 
-static BOOL signing_good(struct smb_signing_context *sign_info, 
+BOOL signing_good(struct smb_signing_context *sign_info, 
                         unsigned int seq, BOOL good) 
 {
        if (good) {
                if (!sign_info->doing_signing) {
+                       DEBUG(5, ("Seen valid packet, so turning signing on\n"));
                        sign_info->doing_signing = True;
                }
                if (!sign_info->seen_valid) {
+                       DEBUG(5, ("Seen valid packet, so marking signing as 'seen valid'\n"));
                        sign_info->seen_valid = True;
                }
        } else {
@@ -74,12 +89,7 @@ static BOOL signing_good(struct smb_signing_context *sign_info,
                        /* If we have never seen a good packet, just turn it off */
                        DEBUG(5, ("signing_good: signing negotiated but not required and peer\n"
                                  "isn't sending correct signatures. Turning off.\n"));
-                       sign_info->negotiated_smb_signing = False;
-                       sign_info->allow_smb_signing = False;
-                       sign_info->doing_signing = False;
-                       if (sign_info->free_signing_context)
-                               sign_info->free_signing_context(sign_info);
-                       smbcli_null_set_signing(sign_info);
+                       smbcli_set_signing_off(sign_info);
                        return True;
                } else {
                        /* bad packet after signing started - fail and disconnect. */
@@ -90,10 +100,11 @@ static BOOL signing_good(struct smb_signing_context *sign_info,
        return True;
 }
 
-void sign_outgoing_message(struct request_buffer *out, DATA_BLOB *mac_key, uint_t seq_num) 
+void sign_outgoing_message(struct request_buffer *out, DATA_BLOB *mac_key, unsigned int seq_num) 
 {
        uint8_t calc_md5_mac[16];
        struct MD5Context md5_ctx;
+
        /*
         * Firstly put the sequence number into the first 4 bytes.
         * and zero out the next 4 bytes.
@@ -106,8 +117,7 @@ void sign_outgoing_message(struct request_buffer *out, DATA_BLOB *mac_key, uint_
 
        /* Calculate the 16 byte MAC and place first 8 bytes into the field. */
        MD5Init(&md5_ctx);
-       MD5Update(&md5_ctx, mac_key->data, 
-                 mac_key->length); 
+        MD5Update(&md5_ctx, mac_key->data, mac_key->length);
        MD5Update(&md5_ctx, 
                  out->buffer + NBT_HDR_SIZE, 
                  out->size - NBT_HDR_SIZE);
@@ -138,6 +148,11 @@ BOOL check_signed_incoming_message(struct request_buffer *in, DATA_BLOB *mac_key
                return False;
        }
 
+       if (!mac_key->length) {
+               /* NO key yet */
+               return False;
+       }
+
        /* its quite bogus to be guessing sequence numbers, but very useful
           when debugging signing implementations */
        for (i = 0-sign_range; i <= 0+sign_range; i++) {
@@ -183,274 +198,196 @@ BOOL check_signed_incoming_message(struct request_buffer *in, DATA_BLOB *mac_key
        if (good && i != 0) {
                DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, seq_num));
        }
+
        return good;
 }
 
-/***********************************************************
- SMB signing - Simple implementation - calculate a MAC to send.
-************************************************************/
-static void smbcli_request_simple_sign_outgoing_message(struct smbcli_request *req)
+static void smbcli_req_allocate_seq_num(struct smbcli_request *req) 
 {
-       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;
+       req->seq_num = req->transport->negotiate.sign_info.next_seq_num;
        
        /* some requests (eg. NTcancel) are one way, and the sequence number
           should be increased by 1 not 2 */
        if (req->sign_single_increment) {
-               data->next_seq_num += 1;
+               req->transport->negotiate.sign_info.next_seq_num += 1;
        } else {
-               data->next_seq_num += 2;
+               req->transport->negotiate.sign_info.next_seq_num += 2;
        }
-       
-       sign_outgoing_message(&req->out, &data->mac_key, req->seq_num);
 }
 
-
 /***********************************************************
- SMB signing - Simple implementation - check a MAC sent by server.
+ SMB signing - Simple implementation - calculate a MAC to send.
 ************************************************************/
-static BOOL smbcli_request_simple_check_incoming_message(struct smbcli_request *req)
+void smbcli_request_calculate_sign_mac(struct smbcli_request *req)
 {
-       struct smb_basic_signing_context *data 
-               = req->transport->negotiate.sign_info.signing_context;
-
-       BOOL good = check_signed_incoming_message(&req->in, 
-                                                 &data->mac_key, 
-                                                 req->seq_num+1);
-                                                 
-       return signing_good(&req->transport->negotiate.sign_info, 
-                           req->seq_num+1, good);
-}
-
+#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
 
-/***********************************************************
- SMB signing - Simple implementation - free signing context
-************************************************************/
-static void smbcli_transport_simple_free_signing_context(struct smb_signing_context *sign_info)
-{
-       struct smb_basic_signing_context *data = sign_info->signing_context;
+       switch (req->transport->negotiate.sign_info.signing_state) {
+       case SMB_SIGNING_ENGINE_OFF:
+               break;
 
-       data_blob_free(&data->mac_key);
-       SAFE_FREE(sign_info->signing_context);
+       case SMB_SIGNING_ENGINE_BSRSPYL:
+               /* mark the packet as signed - BEFORE we sign it...*/
+               mark_packet_signed(&req->out);
+               
+               /* I wonder what BSRSPYL stands for - but this is what MS 
+                  actually sends! */
+               memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
+               break;
 
+       case SMB_SIGNING_ENGINE_ON:
+                       
+               smbcli_req_allocate_seq_num(req);
+               sign_outgoing_message(&req->out, 
+                                     &req->transport->negotiate.sign_info.mac_key, 
+                                     req->seq_num);
+               break;
+       }
        return;
 }
 
-/***********************************************************
- SMB signing - Simple implementation - setup the MAC key.
-************************************************************/
-BOOL smbcli_simple_set_signing(struct smb_signing_context *sign_info,
-                              const DATA_BLOB user_session_key, 
-                              const DATA_BLOB response)
-{
-       struct smb_basic_signing_context *data;
-
-       if (sign_info->mandatory_signing) {
-               DEBUG(5, ("Mandatory SMB signing enabled!\n"));
-       }
-
-       DEBUG(5, ("SMB signing enabled!\n"));
-
-       data = smb_xmalloc(sizeof(*data));
-       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("Started Signing with key:\n", data->mac_key.data, data->mac_key.length);
-
-       /* Initialise the sequence number */
-       data->next_seq_num = 0;
 
-       sign_info->sign_outgoing_message = smbcli_request_simple_sign_outgoing_message;
-       sign_info->check_incoming_message = smbcli_request_simple_check_incoming_message;
-       sign_info->free_signing_context = smbcli_transport_simple_free_signing_context;
+/**
+ SMB signing - NULL implementation
 
+ @note Used as an initialisation only - it will not correctly
+       shut down a real signing mechanism
+*/
+BOOL smbcli_set_signing_off(struct smb_signing_context *sign_info)
+{
+       DEBUG(5, ("Shutdown SMB signing\n"));
+       sign_info->doing_signing = False;
+       sign_info->next_seq_num = 0;
+       data_blob_free(&sign_info->mac_key);
+       sign_info->signing_state = SMB_SIGNING_ENGINE_OFF;
        return True;
 }
 
+/**
+ SMB signing - TEMP implementation - setup the MAC key.
 
-/***********************************************************
- SMB signing - Simple implementation - setup the MAC key.
-************************************************************/
-BOOL smbcli_transport_simple_set_signing(struct smbcli_transport *transport,
-                                     const DATA_BLOB user_session_key, 
-                                     const DATA_BLOB response)
+*/
+BOOL smbcli_temp_set_signing(struct smbcli_transport *transport)
 {
-       if (!set_smb_signing_common(transport)) {
+       if (!smbcli_set_smb_signing_common(transport)) {
                return False;
        }
+       DEBUG(5, ("BSRSPYL SMB signing enabled\n"));
+       smbcli_set_signing_off(&transport->negotiate.sign_info);
 
-       return smbcli_simple_set_signing(&transport->negotiate.sign_info,
-                                        user_session_key,
-                                        response);
-}
-
-
-/***********************************************************
- SMB signing - NULL implementation - calculate a MAC to send.
-************************************************************/
-static void smbcli_request_null_sign_outgoing_message(struct smbcli_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 */
-}
-
+       transport->negotiate.sign_info.mac_key = data_blob(NULL, 0);
+       transport->negotiate.sign_info.signing_state = SMB_SIGNING_ENGINE_BSRSPYL;
 
-/***********************************************************
- SMB signing - NULL implementation - check a MAC sent by server.
-************************************************************/
-static BOOL smbcli_request_null_check_incoming_message(struct smbcli_request *req)
-{
        return True;
 }
 
-
 /***********************************************************
- SMB signing - NULL implementation - free signing context
+ SMB signing - Simple implementation - check a MAC sent by server.
 ************************************************************/
-static void smbcli_null_free_signing_context(struct smb_signing_context *sign_info)
-{
-}
-
 /**
- 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 smbcli_null_set_signing(struct smb_signing_context *sign_info)
-{
-       sign_info->signing_context = NULL;
-       
-       sign_info->sign_outgoing_message = smbcli_request_null_sign_outgoing_message;
-       sign_info->check_incoming_message = smbcli_request_null_check_incoming_message;
-       sign_info->free_signing_context = smbcli_null_free_signing_context;
-
-       return True;
-}
-
-/***********************************************************
- SMB signing - TEMP implementation - calculate a MAC to send.
-************************************************************/
-static void smbcli_request_temp_sign_outgoing_message(struct smbcli_request *req)
+ * Check a packet supplied by the server.
+ * @return False if we had an established signing connection
+ *         which had a back checksum, True otherwise
+ */
+BOOL smbcli_request_check_sign_mac(struct smbcli_request *req) 
 {
-       /* mark the packet as signed - BEFORE we sign it...*/
-       mark_packet_signed(&req->out);
-
-       /* I wonder what BSRSPYL stands for - but this is what MS 
-          actually sends! */
-       memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
+       BOOL good;
 
-       return;
+       switch (req->transport->negotiate.sign_info.signing_state) 
+       {
+       case SMB_SIGNING_ENGINE_OFF:
+               return True;
+       case SMB_SIGNING_ENGINE_BSRSPYL:
+       case SMB_SIGNING_ENGINE_ON:
+       {                       
+               if (req->in.size < (HDR_SS_FIELD + 8)) {
+                       return False;
+               } else {
+                       good = check_signed_incoming_message(&req->in, 
+                                                            &req->transport->negotiate.sign_info.mac_key, 
+                                                            req->seq_num+1);
+                       
+                       return signing_good(&req->transport->negotiate.sign_info, 
+                                           req->seq_num+1, good);
+               }
+       }
+       }
+       return False;
 }
 
-/***********************************************************
- SMB signing - TEMP implementation - check a MAC sent by server.
-************************************************************/
-static BOOL smbcli_request_temp_check_incoming_message(struct smbcli_request *req)
-{
-       return True;
-}
 
 /***********************************************************
- SMB signing - NULL implementation - free signing context
+ SMB signing - Simple implementation - setup the MAC key.
 ************************************************************/
-static void smbcli_temp_free_signing_context(struct smb_signing_context *sign_info)
-{
-       return;
-}
-
-/**
- SMB signing - TEMP implementation - setup the MAC key.
-
- @note Used as an initialisation only - it will not correctly
-       shut down a real signing mechanism
-*/
-BOOL smbcli_temp_set_signing(struct smbcli_transport *transport)
+BOOL smbcli_simple_set_signing(TALLOC_CTX *mem_ctx,
+                              struct smb_signing_context *sign_info,
+                              const DATA_BLOB *user_session_key, 
+                              const DATA_BLOB *response)
 {
-       if (!set_smb_signing_common(transport)) {
-               return False;
+       if (sign_info->mandatory_signing) {
+               DEBUG(5, ("Mandatory SMB signing enabled!\n"));
        }
 
-       transport->negotiate.sign_info.signing_context = NULL;
-       
-       transport->negotiate.sign_info.sign_outgoing_message = smbcli_request_temp_sign_outgoing_message;
-       transport->negotiate.sign_info.check_incoming_message = smbcli_request_temp_check_incoming_message;
-       transport->negotiate.sign_info.free_signing_context = smbcli_temp_free_signing_context;
+       DEBUG(5, ("SMB signing enabled!\n"));
 
-       return True;
-}
+       if (response && response->length) {
+               sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, response->length + user_session_key->length);
+       } else {
+               sign_info->mac_key = data_blob_talloc(mem_ctx, NULL, user_session_key->length);
+       }
+               
+       memcpy(&sign_info->mac_key.data[0], user_session_key->data, user_session_key->length);
 
-/**
- * Free the signing context
- */
-void smbcli_transport_free_signing_context(struct smb_signing_context *sign_info) 
-{
-       if (sign_info->free_signing_context) {
-               sign_info->free_signing_context(sign_info);
+       if (response && response->length) {
+               memcpy(&sign_info->mac_key.data[user_session_key->length],response->data, response->length);
        }
 
-       smbcli_null_set_signing(sign_info);
-}
+       dump_data_pw("Started Signing with key:\n", sign_info->mac_key.data, sign_info->mac_key.length);
 
+       /* Initialise the sequence number */
+       sign_info->next_seq_num = 0;
 
-/**
- * Sign a packet with the current mechanism
- */
-void smbcli_request_calculate_sign_mac(struct smbcli_request *req)
-{
-       req->transport->negotiate.sign_info.sign_outgoing_message(req);
+       sign_info->signing_state = SMB_SIGNING_ENGINE_ON;
+
+       return True;
 }
 
 
-/**
- * Check a packet with the current mechanism
- * @return False if we had an established signing connection
- *         which had a back checksum, True otherwise
- */
-BOOL smbcli_request_check_sign_mac(struct smbcli_request *req) 
+/***********************************************************
+ SMB signing - Simple implementation - setup the MAC key.
+************************************************************/
+BOOL smbcli_transport_simple_set_signing(struct smbcli_transport *transport,
+                                        const DATA_BLOB user_session_key, 
+                                        const DATA_BLOB response)
 {
-       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) {
+       if (!smbcli_set_smb_signing_common(transport)) {
                return False;
        }
 
-       return True;
+       return smbcli_simple_set_signing(transport,
+                                        &transport->negotiate.sign_info,
+                                        &user_session_key,
+                                        &response);
 }
 
 
 BOOL smbcli_init_signing(struct smbcli_transport *transport) 
 {
-       if (!smbcli_null_set_signing(&transport->negotiate.sign_info)) {
+       transport->negotiate.sign_info.mac_key = data_blob(NULL, 0);
+       if (!smbcli_set_signing_off(&transport->negotiate.sign_info)) {
                return False;
        }
        
        switch (lp_client_signing()) {
        case SMB_SIGNING_OFF:
-       transport->negotiate.sign_info.allow_smb_signing = False;
+               transport->negotiate.sign_info.allow_smb_signing = False;
                break;
        case SMB_SIGNING_SUPPORTED:
+       case SMB_SIGNING_AUTO:
                transport->negotiate.sign_info.allow_smb_signing = True;
                break;
        case SMB_SIGNING_REQUIRED: