fixed mandatory signing
[kai/samba.git] / source4 / smb_server / smb2 / receive.c
index 6a36d2f3658a5e6fd54fff22e6cd498790fc61c2..2f4e9df2b6ab9cb4c8692290db1348841709e324 100644 (file)
 #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 */
+void smb2srv_setup_bufinfo(struct smb2srv_request *req)
+{
+       req->in.bufinfo.mem_ctx    = req;
+       req->in.bufinfo.flags      = BUFINFO_FLAG_UNICODE | BUFINFO_FLAG_SMB2;
+       req->in.bufinfo.align_base = req->in.buffer;
+       if (req->in.dynamic) {
+               req->in.bufinfo.data       = req->in.dynamic;
+               req->in.bufinfo.data_size  = req->in.body_size - req->in.body_fixed;
+       } else {
+               req->in.bufinfo.data       = NULL;
+               req->in.bufinfo.data_size  = 0;
+       }
+}
 
 static int smb2srv_request_destructor(struct smb2srv_request *req)
 {
@@ -59,7 +77,7 @@ struct smb2srv_request *smb2srv_init_request(struct smbsrv_connection *smb_conn)
 }
 
 NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_size,
-                            BOOL body_dynamic_present, uint32_t body_dynamic_size)
+                            bool body_dynamic_present, uint32_t body_dynamic_size)
 {
        uint32_t flags = 0x00000001;
        uint32_t pid = IVAL(req->in.hdr, SMB2_HDR_PID);
@@ -82,7 +100,8 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si
        req->out.size           = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
 
        req->out.allocated      = req->out.size + body_dynamic_size;
-       req->out.buffer         = talloc_size(req, req->out.allocated);
+       req->out.buffer         = talloc_array(req, uint8_t, 
+                                              req->out.allocated);
        NT_STATUS_HAVE_NO_MEMORY(req->out.buffer);
 
        req->out.hdr            = req->out.buffer       + NBT_HDR_SIZE;
@@ -93,17 +112,17 @@ NTSTATUS smb2srv_setup_reply(struct smb2srv_request *req, uint16_t body_fixed_si
 
        SIVAL(req->out.hdr, 0,                          SMB2_MAGIC);
        SSVAL(req->out.hdr, SMB2_HDR_LENGTH,            SMB2_HDR_BODY);
-       SSVAL(req->out.hdr, SMB2_HDR_PAD1,              0);
+       SSVAL(req->out.hdr, SMB2_HDR_EPOCH,             0);
        SIVAL(req->out.hdr, SMB2_HDR_STATUS,            NT_STATUS_V(req->status));
        SSVAL(req->out.hdr, SMB2_HDR_OPCODE,            SVAL(req->in.hdr, SMB2_HDR_OPCODE));
-       SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,          0x0001);
+       SSVAL(req->out.hdr, SMB2_HDR_CREDIT,            0x0001);
        SIVAL(req->out.hdr, SMB2_HDR_FLAGS,             flags);
-       SIVAL(req->out.hdr, SMB2_HDR_CHAIN_OFFSET,      0);
-       SBVAL(req->out.hdr, SMB2_HDR_SEQNUM,            req->seqnum);
+       SIVAL(req->out.hdr, SMB2_HDR_NEXT_COMMAND,      0);
+       SBVAL(req->out.hdr, SMB2_HDR_MESSAGE_ID,        req->seqnum);
        SIVAL(req->out.hdr, SMB2_HDR_PID,               pid);
        SIVAL(req->out.hdr, SMB2_HDR_TID,               tid);
-       SBVAL(req->out.hdr, SMB2_HDR_UID,               BVAL(req->in.hdr, SMB2_HDR_UID));
-       memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
+       SBVAL(req->out.hdr, SMB2_HDR_SESSION_ID,        BVAL(req->in.hdr, SMB2_HDR_SESSION_ID));
+       memset(req->out.hdr+SMB2_HDR_SIGNATURE, 0, 16);
 
        /* set the length of the fixed body part and +1 if there's a dynamic part also */
        SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
@@ -179,6 +198,8 @@ static void smb2srv_chain_reply(struct smb2srv_request *p_req)
                }
        }
 
+       smb2srv_setup_bufinfo(req);
+
        if (p_req->chained_file_handle) {
                memcpy(req->_chained_file_handle,
                       p_req->_chained_file_handle,
@@ -214,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)) {
@@ -236,7 +271,7 @@ void smb2srv_send_error(struct smb2srv_request *req, NTSTATUS error)
                return;
        }
 
-       status = smb2srv_setup_reply(req, 8, True, 0);
+       status = smb2srv_setup_reply(req, 8, true, 0);
        if (!NT_STATUS_IS_OK(status)) {
                smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
                talloc_free(req);
@@ -256,18 +291,42 @@ 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_CHAIN_OFFSET);
-       req->seqnum             = BVAL(req->in.hdr, SMB2_HDR_SEQNUM);
+       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_UID);
+       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;                    
+               }
+       } else if (req->smb_conn->doing_signing && req->session != NULL) {
+               /* we require signing and this request was not signed */
+               smb2srv_send_error(req, NT_STATUS_ACCESS_DENIED);
+               return NT_STATUS_OK;                                    
+       }
+
        /* TODO: check the seqnum */
 
        switch (opcode) {
@@ -429,6 +488,8 @@ NTSTATUS smbsrv_recv_smb2_request(void *private, DATA_BLOB blob)
                }
        }
 
+       smb2srv_setup_bufinfo(req);
+
        /* 
         * TODO: - make sure the length field is 64
         *       - make sure it's a request
@@ -515,12 +576,12 @@ NTSTATUS smbsrv_init_smb2_connection(struct smbsrv_connection *smb_conn)
 
        /* this is the size that w2k uses, and it appears to be important for
           good performance */
-       smb_conn->negotiate.max_recv = lp_max_xmit();
+       smb_conn->negotiate.max_recv = lp_max_xmit(smb_conn->lp_ctx);
 
        smb_conn->negotiate.zone_offset = get_time_zone(time(NULL));
 
        smb_conn->config.security = SEC_USER;
-       smb_conn->config.nt_status_support = True;
+       smb_conn->config.nt_status_support = true;
 
        status = smbsrv_init_sessions(smb_conn, UINT64_MAX);
        NT_STATUS_NOT_OK_RETURN(status);