1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-02 09:47:23 +03:00

added server side SMB2 signing

(This used to be commit 8e919dcb0826a5b25d037ee6144af5f7cb21f3ae)
This commit is contained in:
Andrew Tridgell 2008-06-06 22:10:30 -07:00
parent 8e45338c8d
commit e97cf207fa
7 changed files with 103 additions and 71 deletions

View 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);

View File

@ -25,42 +25,14 @@
#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;

View 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);

View 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,

View 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) {

View 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;

View File

@ -376,6 +376,8 @@ struct smbsrv_connection {
struct share_context *share_context;
struct loadparm_context *lp_ctx;
bool doing_signing;
};
struct model_ops;