added server side SMB2 signing
authorAndrew Tridgell <tridge@samba.org>
Sat, 7 Jun 2008 05:10:30 +0000 (22:10 -0700)
committerAndrew Tridgell <tridge@samba.org>
Sat, 7 Jun 2008 05:10:30 +0000 (22:10 -0700)
(This used to be commit 8e919dcb0826a5b25d037ee6144af5f7cb21f3ae)

source4/libcli/smb2/session.c
source4/libcli/smb2/signing.c
source4/libcli/smb2/transport.c
source4/smb_server/smb2/negprot.c
source4/smb_server/smb2/receive.c
source4/smb_server/smb2/sesssetup.c
source4/smb_server/smb_server.h

index 54915d85353f065b897f0f94eb0c9fdf3f16d5dc..42fd4840a1ba82268e23620606635028ea379715 100644 (file)
@@ -188,11 +188,13 @@ static void session_request_handler(struct smb2_request *req)
        }
 
        if (session->transport->signing.doing_signing) {
-               c->status = smb2_start_signing(session->transport);
-               if (!NT_STATUS_IS_OK(c->status)) {
-                       composite_error(c, c->status);
+               if (session->transport->signing.session_key.length != 16) {
+                       DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
+                                (unsigned)session->transport->signing.session_key.length));
+                       composite_error(c, NT_STATUS_ACCESS_DENIED);
                        return;
                }
+               session->transport->signing.signing_started = true;
        }
 
        composite_done(c);
index 16c0ff99c1bab4a243d5fa541ce07038e16c557e..fb2c22db4e2a26e37b413759eef8366029bbef17 100644 (file)
 #include "libcli/smb2/smb2_calls.h"
 #include "lib/crypto/crypto.h"
 
-/*
-  NOTE: this code does not yet interoperate with the windows SMB2
-  implementation. We are waiting on feedback on the docs to find out
-  why
- */
-
-
-/*
-  setup signing on a transport
- */
-NTSTATUS smb2_start_signing(struct smb2_transport *transport)
-{
-       if (transport->signing.session_key.length != 16) {
-               DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
-                        (unsigned)transport->signing.session_key.length));
-               return NT_STATUS_ACCESS_DENIED;
-       }
-
-       transport->signing.signing_started = true;
-       return NT_STATUS_OK;
-}
-
 /*
   sign an outgoing message
  */
-NTSTATUS smb2_sign_message(struct smb2_request *req)
+NTSTATUS smb2_sign_message(struct smb2_request_buffer *buf, DATA_BLOB session_key)
 {
-       struct smb2_request_buffer *buf = &req->out;
-       uint64_t session_id;
        struct HMACSHA256Context m;
        uint8_t res[32];
-
-       if (!req->transport->signing.doing_signing ||
-           !req->transport->signing.signing_started) {
-               return NT_STATUS_OK;
-       }
+       uint64_t session_id;
 
        if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
                /* can't sign non-SMB2 messages */
@@ -74,9 +46,9 @@ NTSTATUS smb2_sign_message(struct smb2_request *req)
                return NT_STATUS_OK;            
        }
 
-       if (req->transport->signing.session_key.length != 16) {
+       if (session_key.length != 16) {
                DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
-                        (unsigned)req->transport->signing.session_key.length));
+                        (unsigned)session_key.length));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -85,7 +57,7 @@ NTSTATUS smb2_sign_message(struct smb2_request *req)
        SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED);
 
        ZERO_STRUCT(m);
-       hmac_sha256_init(req->transport->signing.session_key.data, 16, &m);
+       hmac_sha256_init(session_key.data, 16, &m);
        hmac_sha256_update(buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE, &m);
        hmac_sha256_final(res, &m);
 
@@ -93,66 +65,56 @@ NTSTATUS smb2_sign_message(struct smb2_request *req)
 
        memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16);
 
-       if (DEBUGLVL(5)) {
-               /* check our own signature */
-               smb2_check_signature(req->transport, buf->buffer, buf->size);
-       }
-
        return NT_STATUS_OK;    
 }
 
 /*
   check an incoming signature
  */
-NTSTATUS smb2_check_signature(struct smb2_transport *transport,
-                             uint8_t *buffer, uint_t length)
+NTSTATUS smb2_check_signature(struct smb2_request_buffer *buf, DATA_BLOB session_key)
 {
        uint64_t session_id;
        struct HMACSHA256Context m;
        uint8_t res[SHA256_DIGEST_LENGTH];
        uint8_t sig[16];
 
-       if (!transport->signing.signing_started ||
-           !transport->signing.doing_signing) {
-               return NT_STATUS_OK;
-       }
-
-       if (length < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
+       if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) {
                /* can't check non-SMB2 messages */
                return NT_STATUS_OK;
        }
 
-       session_id = BVAL(buffer+NBT_HDR_SIZE, SMB2_HDR_SESSION_ID);
+       session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID);
        if (session_id == 0) {
                /* don't sign messages with a zero session_id. See
                   MS-SMB2 3.2.4.1.1 */
                return NT_STATUS_OK;            
        }
 
-       if (transport->signing.session_key.length == 0) {
+       if (session_key.length == 0) {
                /* we don't have the session key yet */
                return NT_STATUS_OK;
        }
 
-       if (transport->signing.session_key.length != 16) {
+       if (session_key.length != 16) {
                DEBUG(2,("Wrong session key length %u for SMB2 signing\n",
-                        (unsigned)transport->signing.session_key.length));
+                        (unsigned)session_key.length));
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       memcpy(sig, buffer+NBT_HDR_SIZE+SMB2_HDR_SIGNATURE, 16);
+       memcpy(sig, buf->hdr+SMB2_HDR_SIGNATURE, 16);
 
-       memset(buffer + NBT_HDR_SIZE + SMB2_HDR_SIGNATURE, 0, 16);
+       memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16);
 
        ZERO_STRUCT(m);
-       hmac_sha256_init(transport->signing.session_key.data, 16, &m);
-       hmac_sha256_update(buffer+NBT_HDR_SIZE, length-NBT_HDR_SIZE, &m);
+       hmac_sha256_init(session_key.data, 16, &m);
+       hmac_sha256_update(buf->hdr, buf->size-NBT_HDR_SIZE, &m);
        hmac_sha256_final(res, &m);
 
-       memcpy(buffer+NBT_HDR_SIZE+SMB2_HDR_SIGNATURE, sig, 16);
+       memcpy(buf->hdr+SMB2_HDR_SIGNATURE, sig, 16);
 
        if (memcmp(res, sig, 16) != 0) {
-               DEBUG(0,("Bad SMB2 signature for message of size %u\n", length));
+               DEBUG(0,("Bad SMB2 signature for message of size %u\n", 
+                        (unsigned)buf->size-NBT_HDR_SIZE));
                dump_data(0, sig, 16);
                dump_data(0, res, 16);
                return NT_STATUS_ACCESS_DENIED;
index 561b6e528eb60ddfb16dda2df3378c28875a330c..a9a9efb3aac3bb51f5f3a969567ccbf071b0b7a5 100644 (file)
@@ -205,12 +205,6 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
                goto error;
        }
 
-       status = smb2_check_signature(transport, buffer, len);
-       if (!NT_STATUS_IS_OK(status)) {
-               talloc_free(buffer);
-               return status;
-       }
-       
        flags   = IVAL(hdr, SMB2_HDR_FLAGS);
        seqnum  = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
 
@@ -241,6 +235,18 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
        req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
        req->status       = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
 
+       if (transport->signing.signing_started &&
+           transport->signing.doing_signing) {
+               status = smb2_check_signature(&req->in, 
+                                             transport->signing.session_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       /* the spec says to ignore packets with a bad signature */
+                       talloc_free(buffer);
+                       return status;
+               }
+       }
+       
+
        if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
                if (flags & 0x00000002) {
                        req->cancel.can_cancel = true;
@@ -346,11 +352,15 @@ void smb2_transport_send(struct smb2_request *req)
                return;
        }
 
-       status = smb2_sign_message(req);
-       if (!NT_STATUS_IS_OK(status)) {
-               req->state = SMB2_REQUEST_ERROR;
-               req->status = status;
-               return;
+       /* possibly sign the message */
+       if (req->transport->signing.doing_signing &&
+           req->transport->signing.signing_started) {
+               status = smb2_sign_message(&req->out, req->transport->signing.session_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       req->state = SMB2_REQUEST_ERROR;
+                       req->status = status;
+                       return;
+               }
        }
        
        blob = data_blob_const(req->out.buffer, req->out.size);
index 4479ae2da1d7ff9d234acbc3ffed6118193980f2..2da39001ab1f4045d2c1566f1b8b5ee07d2efd9f 100644 (file)
@@ -111,7 +111,18 @@ static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2
        boot_time = timeval_current(); /* TODO: fix me */
 
        ZERO_STRUCT(io->out);
-       io->out.security_mode      = 0; /* no signing yet */
+       switch (lp_server_signing(req->smb_conn->lp_ctx)) {
+       case SMB_SIGNING_OFF:
+               io->out.security_mode = 0;
+               break;
+       case SMB_SIGNING_SUPPORTED:
+       case SMB_SIGNING_AUTO:
+               io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED;
+               break;
+       case SMB_SIGNING_REQUIRED:
+               io->out.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED;
+               break;
+       }
        io->out.dialect_revision   = SMB2_DIALECT_REVISION;
        io->out.capabilities       = 0;
        io->out.max_transact_size  = lp_parm_ulong(req->smb_conn->lp_ctx, NULL, 
index dea7c9e79ea25ae3d7ee049969c7e5bc9db84bc9..3def8fe56380d1fcef545a0a4b0b8fb77906f1fb 100644 (file)
@@ -29,6 +29,8 @@
 #include "lib/stream/packet.h"
 #include "ntvfs/ntvfs.h"
 #include "param/param.h"
+#include "auth/gensec/gensec.h"
+#include "auth/auth.h"
 
 
 /* fill in the bufinfo */
@@ -233,6 +235,20 @@ void smb2srv_send_reply(struct smb2srv_request *req)
                _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
        }
 
+       /* if the request was signed or doing_signing is true, then we
+          must sign the reply */
+       if (req->session &&
+           (req->smb_conn->doing_signing ||
+            (IVAL(req->in.hdr, SMB2_HDR_FLAGS) & SMB2_HDR_FLAG_SIGNED))) {
+               status = smb2_sign_message(&req->out, 
+                                          req->session->session_info->session_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+                       return;
+               }               
+       }
+
+
        blob = data_blob_const(req->out.buffer, req->out.size);
        status = packet_send(req->smb_conn->packet, blob);
        if (!NT_STATUS_IS_OK(status)) {
@@ -275,18 +291,38 @@ static NTSTATUS smb2srv_reply(struct smb2srv_request *req)
        uint16_t opcode;
        uint32_t tid;
        uint64_t uid;
+       uint32_t flags;
 
        opcode                  = SVAL(req->in.hdr, SMB2_HDR_OPCODE);
        req->chain_offset       = IVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND);
        req->seqnum             = BVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID);
        tid                     = IVAL(req->in.hdr, SMB2_HDR_TID);
        uid                     = BVAL(req->in.hdr, SMB2_HDR_SESSION_ID);
+       flags                   = IVAL(req->in.hdr, SMB2_HDR_FLAGS);
 
        req->session    = smbsrv_session_find(req->smb_conn, uid, req->request_time);
        req->tcon       = smbsrv_smb2_tcon_find(req->session, tid, req->request_time);
 
        errno = 0;
 
+       /* supporting signing is mandatory in SMB2, and is per-packet. So we 
+          should check the signature on any incoming packet that is signed, and 
+          should give a signed reply to any signed request */
+       if (flags & SMB2_HDR_FLAG_SIGNED) {
+               NTSTATUS status;
+               if (req->session == NULL) {
+                       /* we can't check signing with no session */
+                       smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED);
+                       return NT_STATUS_OK;                    
+               }
+               status = smb2_check_signature(&req->in, 
+                                             req->session->session_info->session_key);
+               if (!NT_STATUS_IS_OK(status)) {
+                       smb2srv_send_error(req, status);
+                       return NT_STATUS_OK;                    
+               }
+       }
+
        /* TODO: check the seqnum */
 
        switch (opcode) {
index d386bfc72d1c23c4bc28fc0f917e2f845f1e012c..482dd181c27a2fc3b2ba10b7a20ba639a1d3cf67 100644 (file)
@@ -177,6 +177,15 @@ static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_ses
 
        gensec_update_send(smb_sess->gensec_ctx, io->smb2.in.secblob,
                           smb2srv_sesssetup_callback, callback_ctx);
+
+       /* note that we ignore SMB2_NEGOTIATE_SIGNING_ENABLED from the client.
+          This is deliberate as windows does not set it even when it does 
+          set SMB2_NEGOTIATE_SIGNING_REQUIRED */
+       if ((io->smb2.in.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) ||
+           lp_server_signing(req->smb_conn->lp_ctx) == SMB_SIGNING_REQUIRED) {
+               req->smb_conn->doing_signing = true;
+       }
+
        return;
 nomem:
        status = NT_STATUS_NO_MEMORY;
index 776fe1b71b1a50881a64c4f8ab448d88b0b1af62..ac3e0f3bd3d48da003d436768d18c487dbcd6cc4 100644 (file)
@@ -376,6 +376,8 @@ struct smbsrv_connection {
        struct share_context *share_context;
 
        struct loadparm_context *lp_ctx;
+
+       bool doing_signing;
 };
 
 struct model_ops;