CVE-2016-2115: s3:libsmb: add signing constant SMB_SIGNING_IPC_DEFAULT
[kai/samba-autobuild/.git] / source4 / smb_server / smb2 / negprot.c
index 043534d7771baa27211a0e4b3bdc90c7fb3e68bb..addd278eb4c2068a3448274139975a564abbc0b2 100644 (file)
 
 #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"
@@ -38,17 +39,6 @@ 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->lp_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");
@@ -59,12 +49,27 @@ static NTSTATUS smb2srv_negprot_secblob(struct smb2srv_request *req, DATA_BLOB *
        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);
@@ -76,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");
@@ -92,26 +97,80 @@ static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2
        NTSTATUS status;
        struct timeval current_time;
        struct timeval boot_time;
-
-       /* we only do dialect 0 for now */
-       if (io->in.dialect_count < 1 ||
-           io->in.dialects[0] != 0) {
-               DEBUG(0,("Got unexpected SMB2 dialect %u\n", io->in.dialects[0]));
+       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 */
 
        ZERO_STRUCT(io->out);
-       io->out.security_mode      = 0; /* no signing yet */
-        /* choose the first dialect offered for now */
-       io->out.dialect_revision   = io->in.dialects[0];
+
+       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  = 0x10000;
-       io->out.max_read_size      = 0x10000;
-       io->out.max_write_size     = 0x10000;
+       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;
@@ -124,7 +183,6 @@ static NTSTATUS smb2srv_negprot_backend(struct smb2srv_request *req, struct smb2
 static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negprot *io)
 {
        NTSTATUS status;
-       enum ndr_err_code ndr_err;
 
        if (NT_STATUS_IS_ERR(req->status)) {
                smb2srv_send_error(req, req->status); /* TODO: is this correct? */
@@ -141,8 +199,8 @@ static void smb2srv_negprot_send(struct smb2srv_request *req, struct smb2_negpro
        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);
-       ndr_err = smbcli_push_guid(req->out.body, 0x08, &io->out.server_guid);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+       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;
@@ -168,10 +226,9 @@ void smb2srv_negprot_recv(struct smb2srv_request *req)
 {
        struct smb2_negprot *io;
        int i;
-       enum ndr_err_code ndr_err;
 
        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;
        }
 
@@ -186,9 +243,9 @@ void smb2srv_negprot_recv(struct smb2srv_request *req)
        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);
-       ndr_err = smbcli_pull_guid(req->in.body, 0xC, &io->in.client_guid);
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               smbsrv_terminate_connection(req->smb_conn, nt_errstr(NT_STATUS_FOOBAR));
+       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;
        }
@@ -236,26 +293,28 @@ void smb2srv_reply_smb_negprot(struct smbsrv_request *smb_req)
        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;