CVE-2016-2115: s3:libsmb: add signing constant SMB_SIGNING_IPC_DEFAULT
[kai/samba-autobuild/.git] / source4 / smb_server / smb2 / negprot.c
index c666b3d761978db0a4739406099f2b61bc2c8ca7..addd278eb4c2068a3448274139975a564abbc0b2 100644 (file)
@@ -6,7 +6,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 "auth/credentials/credentials.h"
+#include "auth/auth.h"
 #include "auth/gensec/gensec.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
 #include "libcli/smb2/smb2.h"
 #include "libcli/smb2/smb2_calls.h"
 #include "smb_server/smb_server.h"
-#include "smb_server/service_smb_proto.h"
 #include "smb_server/smb2/smb2_server.h"
 #include "smbd/service_stream.h"
+#include "param/param.h"
 
 static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *_blob)
 {
@@ -37,32 +39,37 @@ static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *
        NTSTATUS nt_status;
        struct cli_credentials *server_credentials;
 
-       nt_status = gensec_server_start(req,
-                                       req->smb_conn->connection->event.ctx,
-                                       req->smb_conn->connection->msg_ctx,
-                                       &gensec_security);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
-               smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
-               return nt_status;
-       }
-
        server_credentials = cli_credentials_init(req);
        if (!server_credentials) {
                smbsrv_terminate_connection(req->smb_conn, "Failed to init server credentials\n");
                return NT_STATUS_NO_MEMORY;
        }
 
-       cli_credentials_set_conf(server_credentials);
-       nt_status = cli_credentials_set_machine_account(server_credentials);
+       cli_credentials_set_conf(server_credentials, req->smb_conn->lp_ctx);
+       nt_status = cli_credentials_set_machine_account(server_credentials, req->smb_conn->lp_ctx);
        if (!NT_STATUS_IS_OK(nt_status)) {
                DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(nt_status)));
-               talloc_free(server_credentials);
-               server_credentials = NULL;
+               /*
+                * We keep the server_credentials as anonymous
+                * this is required for the spoolss.notify test
+                */
        }
 
        req->smb_conn->negotiate.server_credentials = talloc_steal(req->smb_conn, server_credentials);
 
+       nt_status = samba_server_gensec_start(req,
+                                             req->smb_conn->connection->event.ctx,
+                                             req->smb_conn->connection->msg_ctx,
+                                             req->smb_conn->lp_ctx,
+                                             server_credentials,
+                                             "cifs",
+                                             &gensec_security);
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               DEBUG(0, ("Failed to start GENSEC: %s\n", nt_errstr(nt_status)));
+               smbsrv_terminate_connection(req->smb_conn, "Failed to start GENSEC\n");
+               return nt_status;
+       }
+
        gensec_set_target_service(gensec_security, "cifs");
 
        gensec_set_credentials(gensec_security, server_credentials);
@@ -74,7 +81,7 @@ static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *
                return nt_status;
        }
 
-       nt_status = gensec_update(gensec_security, req, null_data_blob, &blob);
+       nt_status = gensec_update_ev(gensec_security, req, req->smb_conn->connection->event.ctx, null_data_blob, &blob);
        if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                DEBUG(0, ("Failed to get SPNEGO to give us the first token: %s\n", nt_errstr(nt_status)));
                smbsrv_terminate_connection(req->smb_conn, "Failed to start SPNEGO - no first token\n");
@@ -90,25 +97,85 @@ static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2
        NTSTATUS status;
        struct timeval current_time;
        struct timeval boot_time;
+       uint16_t i;
+       uint16_t dialect = 0;
+       enum smb_signing_setting signing_setting;
+       struct loadparm_context *lp_ctx = req->smb_conn->lp_ctx;
+
+       /* we only do one dialect for now */
+       if (io->in.dialect_count < 1) {
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+       for (i=0; i < io->in.dialect_count; i++) {
+               dialect = io->in.dialects[i];
+               if (dialect == SMB2_DIALECT_REVISION_202) {
+                       break;
+               }
+       }
+       if (dialect != SMB2_DIALECT_REVISION_202) {
+               DEBUG(0,("Got unexpected SMB2 dialect %u\n", dialect));
+               return NT_STATUS_NOT_SUPPORTED;
+       }
 
-       req->smb_conn->negotiate.protocol = PROTOCOL_SMB2;
+       req->smb_conn->negotiate.protocol = PROTOCOL_SMB2_02;
 
        current_time = timeval_current(); /* TODO: handle timezone?! */
        boot_time = timeval_current(); /* TODO: fix me */
 
-       io->out._pad            = 0;
-       io->out.unknown2        = 0x06;
-       ZERO_STRUCT(io->out.sessid);
-       io->out.unknown3        = 0x0d;
-       io->out.unknown4        = 0x00;
-       io->out.unknown5        = 0x01;
-       io->out.unknown6        = 0x01;
-       io->out.unknown7        = 0x01;
-       io->out.current_time    = timeval_to_nttime(&current_time);
-       io->out.boot_time       = timeval_to_nttime(&boot_time);
+       ZERO_STRUCT(io->out);
+
+       signing_setting = lpcfg_server_signing(lp_ctx);
+       if (signing_setting == SMB_SIGNING_DEFAULT) {
+               /*
+                * If we are a domain controller, SMB signing is
+                * really important, as it can prevent a number of
+                * attacks on communications between us and the
+                * clients
+                *
+                * However, it really sucks (no sendfile, CPU
+                * overhead) performance-wise when used on a
+                * file server, so disable it by default
+                * on non-DCs
+                */
+
+               if (lpcfg_server_role(lp_ctx) >= ROLE_ACTIVE_DIRECTORY_DC) {
+                       signing_setting = SMB_SIGNING_REQUIRED;
+               } else {
+                       signing_setting = SMB_SIGNING_OFF;
+               }
+       }
+
+       switch (signing_setting) {
+       case SMB_SIGNING_DEFAULT:
+       case SMB_SIGNING_IPC_DEFAULT:
+               smb_panic(__location__);
+               break;
+       case SMB_SIGNING_OFF:
+               io->out.security_mode = 0;
+               break;
+       case SMB_SIGNING_DESIRED:
+       case SMB_SIGNING_IF_REQUIRED:
+               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;
+               /* force signing on immediately */
+               req->smb_conn->smb2_signing_required = true;
+               break;
+       }
+       io->out.dialect_revision   = dialect;
+       io->out.capabilities       = 0;
+       io->out.max_transact_size  = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+                                                  "smb2", "max transaction size", 0x10000);
+       io->out.max_read_size      = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+                                                  "smb2", "max read size", 0x10000);
+       io->out.max_write_size     = lpcfg_parm_ulong(req->smb_conn->lp_ctx, NULL,
+                                                  "smb2", "max write size", 0x10000);
+       io->out.system_time        = timeval_to_nttime(&current_time);
+       io->out.server_start_time  = timeval_to_nttime(&boot_time);
+       io->out.reserved2          = 0;
        status = smb2srv_negprot_secblob(req, &io->out.secblob);
        NT_STATUS_NOT_OK_RETURN(status);
-       io->out.unknown9        = 0x204d4c20;
 
        return NT_STATUS_OK;
 }
@@ -122,23 +189,29 @@ static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negpro
                return;
        }
 
-       status = smb2srv_setup_reply(req, 0x40, True, io->out.secblob.length);
+       status = smb2srv_setup_reply(req, 0x40, true, io->out.secblob.length);
        if (!NT_STATUS_IS_OK(status)) {
                smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
                talloc_free(req);
                return;
        }
 
-       SSVAL(req->out.body, 0x02, io->out._pad);
-       SIVAL(req->out.body, 0x04, io->out.unknown2);
-       memcpy(req->out.body+0x08, io->out.sessid, 16);
-       SIVAL(req->out.body, 0x18, io->out.unknown3);
-       SSVAL(req->out.body, 0x1C, io->out.unknown4);
-       SIVAL(req->out.body, 0x1E, io->out.unknown5);
-       SIVAL(req->out.body, 0x22, io->out.unknown6);
-       SSVAL(req->out.body, 0x26, io->out.unknown7);
-       push_nttime(req->out.body, 0x28, io->out.current_time);
-       push_nttime(req->out.body, 0x30, io->out.boot_time);
+       SSVAL(req->out.body, 0x02, io->out.security_mode);
+       SIVAL(req->out.body, 0x04, io->out.dialect_revision);
+       SIVAL(req->out.body, 0x06, io->out.reserved);
+       status = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid);
+       if (!NT_STATUS_IS_OK(status)) {
+               smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
+               talloc_free(req);
+               return;
+       }
+       SIVAL(req->out.body, 0x18, io->out.capabilities);
+       SIVAL(req->out.body, 0x1C, io->out.max_transact_size);
+       SIVAL(req->out.body, 0x20, io->out.max_read_size);
+       SIVAL(req->out.body, 0x24, io->out.max_write_size);
+       push_nttime(req->out.body, 0x28, io->out.system_time);
+       push_nttime(req->out.body, 0x30, io->out.server_start_time);
+       SIVAL(req->out.body, 0x3C, io->out.reserved2);
        status = smb2_push_o16s16_blob(&req->out, 0x38, io->out.secblob);
        if (!NT_STATUS_IS_OK(status)) {
                smbsrv_terminate_connection(req->smb_conn, nt_errstr(status));
@@ -146,17 +219,16 @@ static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negpro
                return;
        }
 
-       SIVAL(req->out.body, 0x3C, io->out.unknown9);
-
        smb2srv_send_reply(req);
 }
 
 void smb2srv_negprot_recv(struct smb2srv_request *req)
 {
        struct smb2_negprot *io;
+       int i;
 
        if (req->in.body_size < 0x26) {
-               smb2srv_send_error(req,  NT_STATUS_FOOBAR);
+               smbsrv_terminate_connection(req->smb_conn, "Bad body size in SMB2 negprot");
                return;
        }
 
@@ -167,9 +239,27 @@ void smb2srv_negprot_recv(struct smb2srv_request *req)
                return;
        }
 
-       io->in.unknown1 = SVAL(req->in.body, 0x02);
-       memcpy(io->in.unknown2, req->in.body + 0x04, 0x20);
-       io->in.unknown3 = SVAL(req->in.body, 0x24);
+       io->in.dialect_count = SVAL(req->in.body, 0x02);
+       io->in.security_mode = SVAL(req->in.body, 0x04);
+       io->in.reserved      = SVAL(req->in.body, 0x06);
+       io->in.capabilities  = IVAL(req->in.body, 0x08);
+       req->status = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid);
+       if (!NT_STATUS_IS_OK(req->status)) {
+               smbsrv_terminate_connection(req->smb_conn, "Bad GUID in SMB2 negprot");
+               talloc_free(req);
+               return;
+       }
+       io->in.start_time = smbcli_pull_nttime(req->in.body, 0x1C);
+
+       io->in.dialects = talloc_array(req, uint16_t, io->in.dialect_count);
+       if (io->in.dialects == NULL) {
+               smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_NO_MEMORY));
+               talloc_free(req);
+               return;
+       }
+       for (i=0;i<io->in.dialect_count;i++) {
+               io->in.dialects[i] = SVAL(req->in.body, 0x24+i*2);
+       }
 
        req->status = smb2srv_negprot_backend(req, io);
 
@@ -181,14 +271,13 @@ void smb2srv_negprot_recv(struct smb2srv_request *req)
 }
 
 /*
- * reply to a SMB negprot request with dialect "SMB 2.001"
+ * reply to a SMB negprot request with dialect "SMB 2.002"
  */
 void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
 {
        struct smb2srv_request *req;
        uint32_t body_fixed_size = 0x26;
 
-       /* create a fake SMB2 negprot request */
        req = talloc_zero(smb_req->smb_conn, struct smb2srv_request);
        if (!req) goto nomem;
        req->smb_conn           = smb_req->smb_conn;
@@ -197,33 +286,35 @@ void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
 
        req->in.size      = NBT_HDR_SIZE+SMB2_HDR_BODY+body_fixed_size;
        req->in.allocated = req->in.size;
-       req->in.buffer    = talloc_size(req, req->in.allocated);
+       req->in.buffer    = talloc_array(req, uint8_t, req->in.allocated);
        if (!req->in.buffer) goto nomem;
        req->in.hdr       = req->in.buffer + NBT_HDR_SIZE;
        req->in.body      = req->in.hdr + SMB2_HDR_BODY;
        req->in.body_size = body_fixed_size;
        req->in.dynamic   = NULL;
 
+       smb2srv_setup_bufinfo(req);
+
        SIVAL(req->in.hdr, 0,                           SMB2_MAGIC);
        SSVAL(req->in.hdr, SMB2_HDR_LENGTH,             SMB2_HDR_BODY);
-       SSVAL(req->in.hdr, SMB2_HDR_PAD1,               0);
+       SSVAL(req->in.hdr, SMB2_HDR_EPOCH,              0);
        SIVAL(req->in.hdr, SMB2_HDR_STATUS,             0);
        SSVAL(req->in.hdr, SMB2_HDR_OPCODE,             SMB2_OP_NEGPROT);
-       SSVAL(req->in.hdr, SMB2_HDR_UNKNOWN1,           0);
+       SSVAL(req->in.hdr, SMB2_HDR_CREDIT,             0);
        SIVAL(req->in.hdr, SMB2_HDR_FLAGS,              0);
-       SIVAL(req->in.hdr, SMB2_HDR_CHAIN_OFFSET,       0);
-       SBVAL(req->in.hdr, SMB2_HDR_SEQNUM,             0);
+       SIVAL(req->in.hdr, SMB2_HDR_NEXT_COMMAND,       0);
+       SBVAL(req->in.hdr, SMB2_HDR_MESSAGE_ID,         0);
        SIVAL(req->in.hdr, SMB2_HDR_PID,                0);
        SIVAL(req->in.hdr, SMB2_HDR_TID,                0);
-       SBVAL(req->in.hdr, SMB2_HDR_UID,                0);
-       memset(req->in.hdr+SMB2_HDR_SIG, 0, 16);
+       SBVAL(req->in.hdr, SMB2_HDR_SESSION_ID,         0);
+       memset(req->in.hdr+SMB2_HDR_SIGNATURE, 0, 16);
 
        /* this seems to be a bug, they use 0x24 but the length is 0x26 */
        SSVAL(req->in.body, 0x00, 0x24);
 
        SSVAL(req->in.body, 0x02, 1);
        memset(req->in.body+0x04, 0, 32);
-       SSVAL(req->in.body, 0x24, 0);
+       SSVAL(req->in.body, 0x24, SMB2_DIALECT_REVISION_202);
 
        smb2srv_negprot_recv(req);
        return;