summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/smbd/globals.h7
-rw-r--r--source3/smbd/smb2_negprot.c243
-rw-r--r--source3/smbd/smb2_server.c28
3 files changed, 274 insertions, 4 deletions
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 22cf5d60890..3ddafafa49b 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -344,6 +344,10 @@ bool push_deferred_open_message_smb2(struct smbd_smb2_request *smb2req,
struct smbXsrv_client;
+struct smbXsrv_preauth {
+ uint8_t sha512_value[64];
+};
+
struct smbXsrv_connection {
struct smbXsrv_connection *prev, *next;
@@ -516,6 +520,8 @@ struct smbXsrv_connection {
uint16_t cipher;
} server;
+ struct smbXsrv_preauth preauth;
+
struct smbd_smb2_request *requests;
} smb2;
};
@@ -662,6 +668,7 @@ struct smbd_smb2_request {
* request/response of a compound chain
*/
DATA_BLOB last_key;
+ struct smbXsrv_preauth *preauth;
struct timeval request_time;
diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c
index ae2f3f7d53e..199dc147ab6 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -22,6 +22,7 @@
#include "smbd/smbd.h"
#include "smbd/globals.h"
#include "../libcli/smb/smb_common.h"
+#include "../libcli/smb/smb2_negotiate_context.h"
#include "../lib/tsocket/tsocket.h"
#include "../librpc/ndr/libndr.h"
@@ -140,6 +141,13 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
uint32_t in_capabilities;
DATA_BLOB in_guid_blob;
struct GUID in_guid;
+ struct smb2_negotiate_contexts in_c = { .num_contexts = 0, };
+ struct smb2_negotiate_context *in_preauth = NULL;
+ struct smb2_negotiate_context *in_cipher = NULL;
+ struct smb2_negotiate_contexts out_c = { .num_contexts = 0, };
+ DATA_BLOB out_negotiate_context_blob = data_blob_null;
+ uint32_t out_negotiate_context_offset = 0;
+ uint16_t out_negotiate_context_count = 0;
uint16_t dialect = 0;
uint32_t capabilities;
DATA_BLOB out_guid_blob;
@@ -201,6 +209,53 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED);
}
+ if (protocol >= PROTOCOL_SMB3_10) {
+ uint32_t in_negotiate_context_offset = 0;
+ uint16_t in_negotiate_context_count = 0;
+ DATA_BLOB in_negotiate_context_blob = data_blob_null;
+ size_t ofs;
+
+ in_negotiate_context_offset = IVAL(inbody, 0x1C);
+ in_negotiate_context_count = SVAL(inbody, 0x20);
+
+ ofs = SMB2_HDR_BODY;
+ ofs += SMBD_SMB2_IN_BODY_LEN(req);
+ ofs += expected_dyn_size;
+ if ((ofs % 8) != 0) {
+ ofs += 8 - (ofs % 8);
+ }
+
+ if (in_negotiate_context_offset != ofs) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ ofs -= SMB2_HDR_BODY;
+ ofs -= SMBD_SMB2_IN_BODY_LEN(req);
+
+ if (SMBD_SMB2_IN_DYN_LEN(req) < ofs) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ in_negotiate_context_blob = data_blob_const(indyn,
+ SMBD_SMB2_IN_DYN_LEN(req));
+
+ in_negotiate_context_blob.data += ofs;
+ in_negotiate_context_blob.length -= ofs;
+
+ status = smb2_negotiate_context_parse(req,
+ in_negotiate_context_blob, &in_c);
+ if (!NT_STATUS_IS_OK(status)) {
+ return smbd_smb2_request_error(req, status);
+ }
+
+ if (in_negotiate_context_count != in_c.num_contexts) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+ }
+
if (get_remote_arch() != RA_SAMBA) {
set_remote_arch(RA_VISTA);
}
@@ -211,6 +266,14 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
reload_services(req->sconn, conn_snum_used, true);
DEBUG(3,("Selected protocol %s\n", remote_proto));
+ in_preauth = smb2_negotiate_context_find(&in_c,
+ SMB2_PREAUTH_INTEGRITY_CAPABILITIES);
+ if (protocol >= PROTOCOL_SMB3_10 && in_preauth == NULL) {
+ return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+ }
+ in_cipher = smb2_negotiate_context_find(&in_c,
+ SMB2_ENCRYPTION_CAPABILITIES);
+
/* negprot_spnego() returns a the server guid in the first 16 bytes */
negprot_spnego_blob = negprot_spnego(req, xconn);
if (negprot_spnego_blob.data == NULL) {
@@ -285,6 +348,131 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
max_read = MIN(max_limit, lp_smb2_max_read());
max_write = MIN(max_limit, lp_smb2_max_write());
+ if (in_preauth != NULL) {
+ size_t needed = 4;
+ uint16_t hash_count;
+ uint16_t salt_length;
+ uint16_t selected_preauth = 0;
+ const uint8_t *p;
+ uint8_t buf[38];
+ DATA_BLOB b;
+ size_t i;
+
+ if (in_preauth->data.length < needed) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ hash_count = SVAL(in_preauth->data.data, 0);
+ salt_length = SVAL(in_preauth->data.data, 2);
+
+ if (hash_count == 0) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ p = in_preauth->data.data + needed;
+ needed += hash_count * 2;
+ needed += salt_length;
+
+ if (in_preauth->data.length < needed) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ for (i=0; i < hash_count; i++) {
+ uint16_t v;
+
+ v = SVAL(p, 0);
+ p += 2;
+
+ if (v == SMB2_PREAUTH_INTEGRITY_SHA512) {
+ selected_preauth = v;
+ break;
+ }
+ }
+
+ if (selected_preauth == 0) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP);
+ }
+
+ SSVAL(buf, 0, 1); /* HashAlgorithmCount */
+ SSVAL(buf, 2, 32); /* SaltLength */
+ SSVAL(buf, 4, selected_preauth);
+ generate_random_buffer(buf + 6, 32);
+
+ b = data_blob_const(buf, sizeof(buf));
+ status = smb2_negotiate_context_add(req, &out_c,
+ SMB2_PREAUTH_INTEGRITY_CAPABILITIES, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return smbd_smb2_request_error(req, status);
+ }
+
+ req->preauth = &req->xconn->smb2.preauth;
+ }
+
+ if (!(capabilities & SMB2_CAP_ENCRYPTION)) {
+ in_cipher = NULL;
+ }
+
+ if (in_cipher != NULL) {
+ size_t needed = 2;
+ uint16_t cipher_count;
+ const uint8_t *p;
+ uint8_t buf[4];
+ DATA_BLOB b;
+ size_t i;
+
+ capabilities &= ~SMB2_CAP_ENCRYPTION;
+
+ if (in_cipher->data.length < needed) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ cipher_count = SVAL(in_cipher->data.data, 0);
+
+ if (cipher_count == 0) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ p = in_cipher->data.data + needed;
+ needed += cipher_count * 2;
+
+ if (in_cipher->data.length < needed) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_INVALID_PARAMETER);
+ }
+
+ for (i=0; i < cipher_count; i++) {
+ uint16_t v;
+
+ v = SVAL(p, 0);
+ p += 2;
+
+ if (v == SMB2_ENCRYPTION_AES128_GCM) {
+ xconn->smb2.server.cipher = v;
+ break;
+ }
+ if (v == SMB2_ENCRYPTION_AES128_CCM) {
+ xconn->smb2.server.cipher = v;
+ break;
+ }
+ }
+
+ SSVAL(buf, 0, 1); /* ChiperCount */
+ SSVAL(buf, 2, xconn->smb2.server.cipher);
+
+ b = data_blob_const(buf, sizeof(buf));
+ status = smb2_negotiate_context_add(req, &out_c,
+ SMB2_ENCRYPTION_CAPABILITIES, b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return smbd_smb2_request_error(req, status);
+ }
+ }
+
if (capabilities & SMB2_CAP_ENCRYPTION) {
xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM;
}
@@ -300,6 +488,53 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
security_buffer = data_blob_const(NULL, 0);
#endif
+ if (out_c.num_contexts != 0) {
+ status = smb2_negotiate_context_push(req,
+ &out_negotiate_context_blob,
+ out_c);
+ if (!NT_STATUS_IS_OK(status)) {
+ return smbd_smb2_request_error(req, status);
+ }
+ }
+
+ if (out_negotiate_context_blob.length != 0) {
+ static const uint8_t zeros[8];
+ size_t pad = 0;
+ size_t ofs;
+ bool ok;
+
+ outdyn = data_blob_dup_talloc(req, security_buffer);
+ if (outdyn.length != security_buffer.length) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_NO_MEMORY);
+ }
+
+ ofs = security_offset + security_buffer.length;
+ if ((ofs % 8) != 0) {
+ pad = 8 - (ofs % 8);
+ }
+ ofs += pad;
+
+ ok = data_blob_append(req, &outdyn, zeros, pad);
+ if (!ok) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_NO_MEMORY);
+ }
+
+ ok = data_blob_append(req, &outdyn,
+ out_negotiate_context_blob.data,
+ out_negotiate_context_blob.length);
+ if (!ok) {
+ return smbd_smb2_request_error(req,
+ NT_STATUS_NO_MEMORY);
+ }
+
+ out_negotiate_context_offset = ofs;
+ out_negotiate_context_count = out_c.num_contexts;
+ } else {
+ outdyn = security_buffer;
+ }
+
out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16);
status = GUID_from_ndr_blob(&out_guid_blob, &out_guid);
if (!NT_STATUS_IS_OK(status)) {
@@ -315,7 +550,8 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
SSVAL(outbody.data, 0x02,
security_mode); /* security mode */
SSVAL(outbody.data, 0x04, dialect); /* dialect revision */
- SSVAL(outbody.data, 0x06, 0); /* reserved */
+ SSVAL(outbody.data, 0x06,
+ out_negotiate_context_count); /* reserved/NegotiateContextCount */
memcpy(outbody.data + 0x08,
out_guid_blob.data, 16); /* server guid */
SIVAL(outbody.data, 0x18,
@@ -329,9 +565,8 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
security_offset); /* security buffer offset */
SSVAL(outbody.data, 0x3A,
security_buffer.length); /* security buffer length */
- SIVAL(outbody.data, 0x3C, 0); /* reserved */
-
- outdyn = security_buffer;
+ SIVAL(outbody.data, 0x3C,
+ out_negotiate_context_offset); /* reserved/NegotiateContextOffset */
req->sconn->using_smb2 = true;
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 9658534a062..9e5eff7cb60 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -30,6 +30,7 @@
#include "../librpc/gen_ndr/krb5pac.h"
#include "lib/util/iov_buf.h"
#include "auth.h"
+#include "lib/crypto/sha512.h"
static void smbd_smb2_connection_handler(struct tevent_context *ev,
struct tevent_fd *fde,
@@ -2523,6 +2524,33 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req)
data_blob_clear_free(&req->first_key);
}
+ if (req->preauth != NULL) {
+ struct hc_sha512state sctx;
+ int i;
+
+ samba_SHA512_Init(&sctx);
+ samba_SHA512_Update(&sctx, req->preauth->sha512_value,
+ sizeof(req->preauth->sha512_value));
+ for (i = 1; i < req->in.vector_count; i++) {
+ samba_SHA512_Update(&sctx,
+ req->in.vector[i].iov_base,
+ req->in.vector[i].iov_len);
+ }
+ samba_SHA512_Final(req->preauth->sha512_value, &sctx);
+
+ samba_SHA512_Init(&sctx);
+ samba_SHA512_Update(&sctx, req->preauth->sha512_value,
+ sizeof(req->preauth->sha512_value));
+ for (i = 1; i < req->out.vector_count; i++) {
+ samba_SHA512_Update(&sctx,
+ req->out.vector[i].iov_base,
+ req->out.vector[i].iov_len);
+ }
+ samba_SHA512_Final(req->preauth->sha512_value, &sctx);
+
+ req->preauth = NULL;
+ }
+
/* I am a sick, sick man... :-). Sendfile hack ... JRA. */
if (req->out.vector_count < (2*SMBD_SMB2_NUM_IOV_PER_REQ) &&
outdyn->iov_base == NULL && outdyn->iov_len != 0) {