mirror of
https://github.com/samba-team/samba.git
synced 2024-12-24 21:34:56 +03:00
r1521: Updates to our SMB signing code.
- This causes our client and server code to use the same core code,
with the same debugs etc.
- In turn, this will allow the 'mandetory/fallback' signing algorithms
to be shared, and only written once.
Updates to the SPNEGO code
- Don't wrap an empty token to the server, if we are actually already finished.
Andrew Bartlett
(This used to be commit 35b83eb329
)
This commit is contained in:
parent
526d687cbb
commit
b3c46674a6
@ -215,6 +215,7 @@ struct cli_tree {
|
||||
char *fs_type;
|
||||
};
|
||||
|
||||
|
||||
/* the context for a single SMB request. This is passed to any request-context
|
||||
* functions (similar to context.h, the server version).
|
||||
* This will allow requests to be multi-threaded. */
|
||||
@ -249,37 +250,8 @@ struct cli_request {
|
||||
/* the mid of this packet - used to match replies */
|
||||
uint16_t mid;
|
||||
|
||||
struct {
|
||||
/* the raw SMB buffer, including the 4 byte length header */
|
||||
char *buffer;
|
||||
|
||||
/* the size of the raw buffer, including 4 byte header */
|
||||
uint_t size;
|
||||
|
||||
/* how much has been allocated - on reply the buffer is over-allocated to
|
||||
prevent too many realloc() calls
|
||||
*/
|
||||
uint_t allocated;
|
||||
|
||||
/* the start of the SMB header - this is always buffer+4 */
|
||||
char *hdr;
|
||||
|
||||
/* the command words and command word count. vwv points
|
||||
into the raw buffer */
|
||||
char *vwv;
|
||||
uint_t wct;
|
||||
|
||||
/* the data buffer and size. data points into the raw buffer */
|
||||
char *data;
|
||||
uint_t data_size;
|
||||
|
||||
/* ptr is used as a moving pointer into the data area
|
||||
* of the packet. The reason its here and not a local
|
||||
* variable in each function is that when a realloc of
|
||||
* a send packet is done we need to move this
|
||||
* pointer */
|
||||
char *ptr;
|
||||
} in, out;
|
||||
struct request_buffer in;
|
||||
struct request_buffer out;
|
||||
|
||||
/* information on what to do with a reply when it is received
|
||||
asyncronously. If this is not setup when a reply is received then
|
||||
|
@ -671,6 +671,7 @@ extern int errno;
|
||||
#include "smbd/server.h"
|
||||
#include "smbd/service.h"
|
||||
#include "rpc_server/dcerpc_server.h"
|
||||
#include "request.h"
|
||||
#include "smb_server/smb_server.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "cli_context.h"
|
||||
|
@ -304,6 +304,8 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
|
||||
DATA_BLOB unwrapped_in;
|
||||
|
||||
if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
|
||||
DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
|
||||
dump_data_pw("Mutual authentication message:\n", in.data, in.length);
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
/* TODO: check the tok_id */
|
||||
@ -316,7 +318,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
|
||||
if (ret) {
|
||||
DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
|
||||
error_message(ret)));
|
||||
dump_data_pw("Mutual authentication message:\n", in.data, in.length);
|
||||
dump_data_pw("Mutual authentication message:\n", inbuf.data, inbuf.length);
|
||||
nt_status = NT_STATUS_ACCESS_DENIED;
|
||||
} else {
|
||||
*out = data_blob(NULL, 0);
|
||||
|
@ -41,7 +41,6 @@ struct spnego_state {
|
||||
uint_t ref_count;
|
||||
enum spnego_message_type expected_packet;
|
||||
enum spnego_state_position state_position;
|
||||
enum spnego_negResult result;
|
||||
struct gensec_security *sub_sec_security;
|
||||
};
|
||||
|
||||
@ -60,7 +59,6 @@ static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_securi
|
||||
|
||||
spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
|
||||
spnego_state->state_position = SPNEGO_CLIENT_START;
|
||||
spnego_state->result = SPNEGO_ACCEPT_INCOMPLETE;
|
||||
spnego_state->mem_ctx = mem_ctx;
|
||||
spnego_state->sub_sec_security = NULL;
|
||||
|
||||
@ -140,8 +138,7 @@ static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_securit
|
||||
DATA_BLOB *session_key)
|
||||
{
|
||||
struct spnego_state *spnego_state = gensec_security->private_data;
|
||||
if (spnego_state->state_position != SPNEGO_DONE
|
||||
&& spnego_state->state_position != SPNEGO_FALLBACK) {
|
||||
if (!spnego_state->sub_sec_security) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
@ -450,7 +447,6 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
spnego.negTokenTarg.responseToken,
|
||||
&unwrapped_out);
|
||||
|
||||
spnego_state->result = spnego.negTokenTarg.negResult;
|
||||
spnego_free_data(&spnego);
|
||||
|
||||
/* compose reply */
|
||||
@ -514,38 +510,45 @@ static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TA
|
||||
spnego.negTokenTarg.responseToken,
|
||||
&unwrapped_out);
|
||||
|
||||
if (NT_STATUS_IS_OK(nt_status)
|
||||
&& (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
|
||||
|
||||
if ((spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED)
|
||||
&& !NT_STATUS_IS_OK(nt_status)) {
|
||||
DEBUG(1,("gensec_update ok but not accepted\n"));
|
||||
nt_status = NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
spnego_state->result = spnego.negTokenTarg.negResult;
|
||||
spnego_free_data(&spnego);
|
||||
|
||||
spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
|
||||
spnego_out.negTokenTarg.supportedMech = NULL;
|
||||
spnego_out.negTokenTarg.responseToken = unwrapped_out;
|
||||
spnego_out.negTokenTarg.mechListMIC = null_data_blob;
|
||||
|
||||
if (unwrapped_out.length) {
|
||||
spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
|
||||
spnego_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
|
||||
spnego_out.negTokenTarg.supportedMech = NULL;
|
||||
spnego_out.negTokenTarg.responseToken = unwrapped_out;
|
||||
spnego_out.negTokenTarg.mechListMIC = null_data_blob;
|
||||
|
||||
if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
|
||||
DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
} else {
|
||||
*out = null_data_blob;
|
||||
}
|
||||
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
/* compose reply */
|
||||
|
||||
|
||||
spnego_state->state_position = SPNEGO_CLIENT_TARG;
|
||||
} else if (NT_STATUS_IS_OK(nt_status)) {
|
||||
/* all done - server has accepted, and we agree */
|
||||
spnego_state->state_position = SPNEGO_DONE;
|
||||
return NT_STATUS_OK;
|
||||
} else {
|
||||
DEBUG(1, ("SPNEGO(%s) login failed: %s\n",
|
||||
spnego_state->sub_sec_security->ops->name,
|
||||
nt_errstr(nt_status)));
|
||||
return nt_status;
|
||||
}
|
||||
if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
|
||||
DEBUG(1, ("Failed to write SPNEGO reply to NEG_TOKEN_TARG\n"));
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return nt_status;
|
||||
}
|
||||
case SPNEGO_DONE:
|
||||
|
@ -260,7 +260,7 @@ static void use_nt1_session_keys(struct cli_session *session,
|
||||
E_md4hash(password, nt_hash);
|
||||
SMBsesskeygen_ntv1(nt_hash, session_key.data);
|
||||
|
||||
cli_transport_simple_set_signing(transport, session_key, *nt_response, 0);
|
||||
cli_transport_simple_set_signing(transport, session_key, *nt_response);
|
||||
|
||||
cli_session_set_user_session_key(session, &session_key);
|
||||
data_blob_free(&session_key);
|
||||
@ -380,6 +380,7 @@ static NTSTATUS smb_raw_session_setup_generic_spnego(struct cli_session *session
|
||||
union smb_sesssetup *parms)
|
||||
{
|
||||
NTSTATUS status;
|
||||
NTSTATUS session_key_err = NT_STATUS_NO_USER_SESSION_KEY;
|
||||
union smb_sesssetup s2;
|
||||
DATA_BLOB session_key = data_blob(NULL, 0);
|
||||
DATA_BLOB null_data_blob = data_blob(NULL, 0);
|
||||
@ -443,15 +444,20 @@ static NTSTATUS smb_raw_session_setup_generic_spnego(struct cli_session *session
|
||||
&s2.spnego.in.secblob);
|
||||
|
||||
while(1) {
|
||||
if (NT_STATUS_IS_OK(status) && s2.spnego.in.secblob.length == 0) {
|
||||
break;
|
||||
}
|
||||
if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
|
||||
break;
|
||||
}
|
||||
|
||||
status = gensec_session_key(session->gensec, &session_key);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
cli_transport_simple_set_signing(session->transport, session_key, null_data_blob, 0);
|
||||
if (!NT_STATUS_IS_OK(session_key_err)) {
|
||||
session_key_err = gensec_session_key(session->gensec, &session_key);
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(session_key_err)) {
|
||||
cli_transport_simple_set_signing(session->transport, session_key, null_data_blob);
|
||||
}
|
||||
|
||||
session->vuid = s2.spnego.out.vuid;
|
||||
status = smb_raw_session_setup(session, mem_ctx, &s2);
|
||||
session->vuid = UID_FIELD_INVALID;
|
||||
@ -464,19 +470,14 @@ static NTSTATUS smb_raw_session_setup_generic_spnego(struct cli_session *session
|
||||
s2.spnego.out.secblob,
|
||||
&s2.spnego.in.secblob);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
status = gensec_session_key(session->gensec, &session_key);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
if (!NT_STATUS_IS_OK(session_key_err)) {
|
||||
DEBUG(1, ("Failed to get user session key: %s\n", nt_errstr(session_key_err)));
|
||||
return session_key_err;
|
||||
}
|
||||
|
||||
cli_transport_simple_set_signing(session->transport, session_key, null_data_blob, 2 /* two legs on last packet */);
|
||||
|
||||
cli_session_set_user_session_key(session, &session_key);
|
||||
|
||||
@ -484,6 +485,9 @@ done:
|
||||
parms->generic.out.os = s2.spnego.out.os;
|
||||
parms->generic.out.lanman = s2.spnego.out.lanman;
|
||||
parms->generic.out.domain = s2.spnego.out.domain;
|
||||
} else {
|
||||
DEBUG(1, ("Failed to login with SPNEGO: %s\n", nt_errstr(status)));
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -528,7 +532,18 @@ NTSTATUS smb_raw_session_setup(struct cli_session *session, TALLOC_CTX *mem_ctx,
|
||||
struct cli_request *req;
|
||||
|
||||
if (parms->generic.level == RAW_SESSSETUP_GENERIC) {
|
||||
return smb_raw_session_setup_generic(session, mem_ctx, parms);
|
||||
NTSTATUS ret = smb_raw_session_setup_generic(session, mem_ctx, parms);
|
||||
|
||||
if (NT_STATUS_IS_OK(ret)
|
||||
&& parms->generic.in.user
|
||||
&& *parms->generic.in.user) {
|
||||
if (!session->transport->negotiate.sign_info.doing_signing
|
||||
&& session->transport->negotiate.sign_info.mandatory_signing) {
|
||||
DEBUG(0, ("SMB signing required, but server does not support it\n"));
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
req = smb_raw_session_setup_send(session, parms);
|
||||
|
@ -69,15 +69,15 @@ static BOOL set_smb_signing_real_common(struct cli_transport *transport)
|
||||
return True;
|
||||
}
|
||||
|
||||
static void mark_packet_signed(struct cli_request *req)
|
||||
static void mark_packet_signed(struct request_buffer *out)
|
||||
{
|
||||
uint16_t flags2;
|
||||
flags2 = SVAL(req->out.hdr, HDR_FLG2);
|
||||
flags2 = SVAL(out->hdr, HDR_FLG2);
|
||||
flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
|
||||
SSVAL(req->out.hdr, HDR_FLG2, flags2);
|
||||
SSVAL(out->hdr, HDR_FLG2, flags2);
|
||||
}
|
||||
|
||||
static BOOL signing_good(struct cli_request *req, int seq, BOOL good)
|
||||
static BOOL signing_good(struct cli_request *req, unsigned int seq, BOOL good)
|
||||
{
|
||||
if (good) {
|
||||
if (!req->transport->negotiate.sign_info.doing_signing) {
|
||||
@ -87,9 +87,8 @@ static BOOL signing_good(struct cli_request *req, int seq, BOOL good)
|
||||
req->transport->negotiate.sign_info.seen_valid = True;
|
||||
}
|
||||
} else {
|
||||
if (!req->transport->negotiate.sign_info.mandatory_signing && !req->transport->negotiate.sign_info.seen_valid) {
|
||||
|
||||
/* Non-mandatory signing - just turn off if this is the first bad packet.. */
|
||||
if (!req->transport->negotiate.sign_info.seen_valid) {
|
||||
/* If we have never seen a good packet, just turn it off */
|
||||
DEBUG(5, ("signing_good: signing negotiated but not required and peer\n"
|
||||
"isn't sending correct signatures. Turning off.\n"));
|
||||
req->transport->negotiate.sign_info.negotiated_smb_signing = False;
|
||||
@ -100,22 +99,115 @@ static BOOL signing_good(struct cli_request *req, int seq, BOOL good)
|
||||
cli_null_set_signing(req->transport);
|
||||
return True;
|
||||
} else {
|
||||
/* Mandatory signing or bad packet after signing started - fail and disconnect. */
|
||||
if (seq)
|
||||
DEBUG(0, ("signing_good: BAD SIG: seq %u\n", (unsigned int)seq));
|
||||
/* bad packet after signing started - fail and disconnect. */
|
||||
DEBUG(0, ("signing_good: BAD SIG: seq %u\n", seq));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
void sign_outgoing_message(struct request_buffer *out, DATA_BLOB *mac_key, uint_t seq_num)
|
||||
{
|
||||
uint8_t calc_md5_mac[16];
|
||||
struct MD5Context md5_ctx;
|
||||
/*
|
||||
* Firstly put the sequence number into the first 4 bytes.
|
||||
* and zero out the next 4 bytes.
|
||||
*/
|
||||
SIVAL(out->hdr, HDR_SS_FIELD, seq_num);
|
||||
SIVAL(out->hdr, HDR_SS_FIELD + 4, 0);
|
||||
|
||||
/* mark the packet as signed - BEFORE we sign it...*/
|
||||
mark_packet_signed(out);
|
||||
|
||||
/* Calculate the 16 byte MAC and place first 8 bytes into the field. */
|
||||
MD5Init(&md5_ctx);
|
||||
MD5Update(&md5_ctx, mac_key->data,
|
||||
mac_key->length);
|
||||
MD5Update(&md5_ctx,
|
||||
out->buffer + NBT_HDR_SIZE,
|
||||
out->size - NBT_HDR_SIZE);
|
||||
MD5Final(calc_md5_mac, &md5_ctx);
|
||||
|
||||
memcpy(&out->hdr[HDR_SS_FIELD], calc_md5_mac, 8);
|
||||
|
||||
DEBUG(5, ("sign_outgoing_message: SENT SIG (seq: %d): sent SMB signature of\n",
|
||||
seq_num));
|
||||
dump_data(5, calc_md5_mac, 8);
|
||||
/* req->out.hdr[HDR_SS_FIELD+2]=0;
|
||||
Uncomment this to test if the remote server actually verifies signitures...*/
|
||||
}
|
||||
|
||||
BOOL check_signed_incoming_message(struct request_buffer *in, DATA_BLOB *mac_key, uint_t seq_num)
|
||||
{
|
||||
BOOL good;
|
||||
uint8_t calc_md5_mac[16];
|
||||
uint8_t server_sent_mac[8];
|
||||
uint8_t sequence_buf[8];
|
||||
struct MD5Context md5_ctx;
|
||||
const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
|
||||
int i;
|
||||
const int sign_range = 0;
|
||||
|
||||
/* room enough for the signature? */
|
||||
if (in->size < NBT_HDR_SIZE + HDR_SS_FIELD + 8) {
|
||||
return False;
|
||||
}
|
||||
|
||||
/* its quite bogus to be guessing sequence numbers, but very useful
|
||||
when debugging signing implementations */
|
||||
for (i = 0-sign_range; i <= 0+sign_range; i++) {
|
||||
/*
|
||||
* Firstly put the sequence number into the first 4 bytes.
|
||||
* and zero out the next 4 bytes.
|
||||
*/
|
||||
SIVAL(sequence_buf, 0, seq_num + i);
|
||||
SIVAL(sequence_buf, 4, 0);
|
||||
|
||||
/* get a copy of the server-sent mac */
|
||||
memcpy(server_sent_mac, &in->hdr[HDR_SS_FIELD], sizeof(server_sent_mac));
|
||||
|
||||
/* Calculate the 16 byte MAC and place first 8 bytes into the field. */
|
||||
MD5Init(&md5_ctx);
|
||||
MD5Update(&md5_ctx, mac_key->data,
|
||||
mac_key->length);
|
||||
MD5Update(&md5_ctx, in->hdr, HDR_SS_FIELD);
|
||||
MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
|
||||
|
||||
MD5Update(&md5_ctx, in->hdr + offset_end_of_sig,
|
||||
in->size - NBT_HDR_SIZE - (offset_end_of_sig));
|
||||
MD5Final(calc_md5_mac, &md5_ctx);
|
||||
|
||||
good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
|
||||
|
||||
if (i == 0) {
|
||||
if (!good) {
|
||||
DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): wanted SMB signature of\n", seq_num + i));
|
||||
dump_data(5, calc_md5_mac, 8);
|
||||
|
||||
DEBUG(5, ("check_signed_incoming_message: BAD SIG (seq: %d): got SMB signature of\n", seq_num + i));
|
||||
dump_data(5, server_sent_mac, 8);
|
||||
} else {
|
||||
DEBUG(15, ("check_signed_incoming_message: GOOD SIG (seq: %d): got SMB signature of\n", seq_num + i));
|
||||
dump_data(5, server_sent_mac, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (good) break;
|
||||
}
|
||||
|
||||
if (good && i != 0) {
|
||||
DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, seq_num));
|
||||
}
|
||||
return good;
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
SMB signing - Simple implementation - calculate a MAC to send.
|
||||
************************************************************/
|
||||
static void cli_request_simple_sign_outgoing_message(struct cli_request *req)
|
||||
{
|
||||
uint8_t calc_md5_mac[16];
|
||||
struct MD5Context md5_ctx;
|
||||
struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
|
||||
|
||||
#if 0
|
||||
@ -133,33 +225,8 @@ static void cli_request_simple_sign_outgoing_message(struct cli_request *req)
|
||||
} else {
|
||||
data->next_seq_num += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Firstly put the sequence number into the first 4 bytes.
|
||||
* and zero out the next 4 bytes.
|
||||
*/
|
||||
SIVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num);
|
||||
SIVAL(req->out.hdr, HDR_SS_FIELD + 4, 0);
|
||||
|
||||
/* mark the packet as signed - BEFORE we sign it...*/
|
||||
mark_packet_signed(req);
|
||||
|
||||
/* Calculate the 16 byte MAC and place first 8 bytes into the field. */
|
||||
MD5Init(&md5_ctx);
|
||||
MD5Update(&md5_ctx, data->mac_key.data,
|
||||
data->mac_key.length);
|
||||
MD5Update(&md5_ctx,
|
||||
req->out.buffer + NBT_HDR_SIZE,
|
||||
req->out.size - NBT_HDR_SIZE);
|
||||
MD5Final(calc_md5_mac, &md5_ctx);
|
||||
|
||||
memcpy(&req->out.hdr[HDR_SS_FIELD], calc_md5_mac, 8);
|
||||
|
||||
DEBUG(5, ("cli_request_simple_sign_outgoing_message: SENT SIG (seq: %d, next %d): sent SMB signature of\n",
|
||||
req->seq_num, data->next_seq_num));
|
||||
dump_data(5, calc_md5_mac, 8);
|
||||
/* req->out.hdr[HDR_SS_FIELD+2]=0;
|
||||
Uncomment this to test if the remote server actually verifies signitures...*/
|
||||
|
||||
sign_outgoing_message(&req->out, &data->mac_key, req->seq_num);
|
||||
}
|
||||
|
||||
|
||||
@ -168,62 +235,13 @@ static void cli_request_simple_sign_outgoing_message(struct cli_request *req)
|
||||
************************************************************/
|
||||
static BOOL cli_request_simple_check_incoming_message(struct cli_request *req)
|
||||
{
|
||||
BOOL good;
|
||||
uint8_t calc_md5_mac[16];
|
||||
uint8_t server_sent_mac[8];
|
||||
uint8_t sequence_buf[8];
|
||||
struct MD5Context md5_ctx;
|
||||
struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context;
|
||||
const size_t offset_end_of_sig = (HDR_SS_FIELD + 8);
|
||||
int i;
|
||||
const int sign_range = 0;
|
||||
|
||||
/* its quite bogus to be guessing sequence numbers, but very useful
|
||||
when debugging signing implementations */
|
||||
for (i = 1-sign_range; i <= 1+sign_range; i++) {
|
||||
/*
|
||||
* Firstly put the sequence number into the first 4 bytes.
|
||||
* and zero out the next 4 bytes.
|
||||
*/
|
||||
SIVAL(sequence_buf, 0, req->seq_num+i);
|
||||
SIVAL(sequence_buf, 4, 0);
|
||||
|
||||
/* get a copy of the server-sent mac */
|
||||
memcpy(server_sent_mac, &req->in.hdr[HDR_SS_FIELD], sizeof(server_sent_mac));
|
||||
|
||||
/* Calculate the 16 byte MAC and place first 8 bytes into the field. */
|
||||
MD5Init(&md5_ctx);
|
||||
MD5Update(&md5_ctx, data->mac_key.data,
|
||||
data->mac_key.length);
|
||||
MD5Update(&md5_ctx, req->in.hdr, HDR_SS_FIELD);
|
||||
MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf));
|
||||
|
||||
MD5Update(&md5_ctx, req->in.hdr + offset_end_of_sig,
|
||||
req->in.size - NBT_HDR_SIZE - (offset_end_of_sig));
|
||||
MD5Final(calc_md5_mac, &md5_ctx);
|
||||
|
||||
good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0);
|
||||
|
||||
if (i == 1) {
|
||||
if (!good) {
|
||||
DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG (seq: %d): wanted SMB signature of\n", req->seq_num + i));
|
||||
dump_data(5, calc_md5_mac, 8);
|
||||
|
||||
DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG (seq: %d): got SMB signature of\n", req->seq_num + i));
|
||||
dump_data(5, server_sent_mac, 8);
|
||||
} else {
|
||||
DEBUG(15, ("cli_request_simple_check_incoming_message: GOOD SIG (seq: %d): got SMB signature of\n", req->seq_num + i));
|
||||
dump_data(5, server_sent_mac, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (good) break;
|
||||
}
|
||||
|
||||
if (good && i != 1) {
|
||||
DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, req->seq_num+1));
|
||||
}
|
||||
struct smb_basic_signing_context *data
|
||||
= req->transport->negotiate.sign_info.signing_context;
|
||||
|
||||
BOOL good = check_signed_incoming_message(&req->in,
|
||||
&data->mac_key,
|
||||
req->seq_num+1);
|
||||
|
||||
return signing_good(req, req->seq_num+1, good);
|
||||
}
|
||||
|
||||
@ -247,8 +265,7 @@ static void cli_transport_simple_free_signing_context(struct cli_transport *tran
|
||||
************************************************************/
|
||||
BOOL cli_transport_simple_set_signing(struct cli_transport *transport,
|
||||
const DATA_BLOB user_session_key,
|
||||
const DATA_BLOB response,
|
||||
int seq_num)
|
||||
const DATA_BLOB response)
|
||||
{
|
||||
struct smb_basic_signing_context *data;
|
||||
|
||||
@ -271,8 +288,10 @@ BOOL cli_transport_simple_set_signing(struct cli_transport *transport,
|
||||
memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length);
|
||||
}
|
||||
|
||||
dump_data_pw("Started Signing with key:\n", data->mac_key.data, data->mac_key.length);
|
||||
|
||||
/* Initialise the sequence number */
|
||||
data->next_seq_num = seq_num;
|
||||
data->next_seq_num = 0;
|
||||
|
||||
transport->negotiate.sign_info.sign_outgoing_message = cli_request_simple_sign_outgoing_message;
|
||||
transport->negotiate.sign_info.check_incoming_message = cli_request_simple_check_incoming_message;
|
||||
@ -332,11 +351,12 @@ BOOL cli_null_set_signing(struct cli_transport *transport)
|
||||
static void cli_request_temp_sign_outgoing_message(struct cli_request *req)
|
||||
{
|
||||
/* mark the packet as signed - BEFORE we sign it...*/
|
||||
mark_packet_signed(req);
|
||||
mark_packet_signed(&req->out);
|
||||
|
||||
/* I wonder what BSRSPYL stands for - but this is what MS
|
||||
actually sends! */
|
||||
memcpy((req->out.hdr + HDR_SS_FIELD), "BSRSPYL ", 8);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -20,34 +20,6 @@
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/*
|
||||
mark the flags2 field in a packet as signed
|
||||
*/
|
||||
static void mark_packet_signed(struct smbsrv_request *req)
|
||||
{
|
||||
uint16_t flags2;
|
||||
flags2 = SVAL(req->out.hdr, HDR_FLG2);
|
||||
flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES;
|
||||
SSVAL(req->out.hdr, HDR_FLG2, flags2);
|
||||
}
|
||||
|
||||
/*
|
||||
calculate the signature for a message
|
||||
*/
|
||||
static void calc_signature(uint8_t *buffer, size_t length,
|
||||
DATA_BLOB *mac_key, uint8_t signature[8])
|
||||
{
|
||||
uint8_t calc_md5_mac[16];
|
||||
struct MD5Context md5_ctx;
|
||||
|
||||
MD5Init(&md5_ctx);
|
||||
MD5Update(&md5_ctx, mac_key->data, mac_key->length);
|
||||
MD5Update(&md5_ctx, buffer, length);
|
||||
MD5Final(calc_md5_mac, &md5_ctx);
|
||||
memcpy(signature, calc_md5_mac, 8);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
sign an outgoing packet
|
||||
*/
|
||||
@ -57,14 +29,9 @@ void req_sign_packet(struct smbsrv_request *req)
|
||||
if (req->smb_conn->signing.signing_state != SMB_SIGNING_REQUIRED) {
|
||||
return;
|
||||
}
|
||||
|
||||
SBVAL(req->out.hdr, HDR_SS_FIELD, req->seq_num+1);
|
||||
|
||||
mark_packet_signed(req);
|
||||
|
||||
calc_signature(req->out.hdr, req->out.size - NBT_HDR_SIZE,
|
||||
&req->smb_conn->signing.mac_key,
|
||||
&req->out.hdr[HDR_SS_FIELD]);
|
||||
sign_outgoing_message(&req->out,
|
||||
&req->smb_conn->signing.mac_key,
|
||||
req->seq_num+1);
|
||||
}
|
||||
|
||||
|
||||
@ -127,23 +94,8 @@ BOOL req_signing_check_incoming(struct smbsrv_request *req)
|
||||
return True;
|
||||
}
|
||||
|
||||
/* room enough for the signature? */
|
||||
if (req->in.size < NBT_HDR_SIZE + HDR_SS_FIELD + 8) {
|
||||
return False;
|
||||
}
|
||||
return check_signed_incoming_message(&req->in,
|
||||
&req->smb_conn->signing.mac_key,
|
||||
req->seq_num);
|
||||
|
||||
memcpy(client_md5_mac, req->in.hdr + HDR_SS_FIELD, 8);
|
||||
|
||||
SBVAL(req->in.hdr, HDR_SS_FIELD, req->seq_num);
|
||||
|
||||
calc_signature(req->in.hdr, req->in.size - NBT_HDR_SIZE,
|
||||
&req->smb_conn->signing.mac_key,
|
||||
signature);
|
||||
|
||||
if (memcmp(client_md5_mac, signature, 8) != 0) {
|
||||
DEBUG(2,("Bad SMB signature seq_num=%d\n", (int)req->seq_num));
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
@ -141,37 +141,8 @@ struct smbsrv_request {
|
||||
NTSTATUS status;
|
||||
} async;
|
||||
|
||||
struct {
|
||||
/* the raw SMB buffer, including the 4 byte length header */
|
||||
char *buffer;
|
||||
|
||||
/* the size of the raw buffer, including 4 byte header */
|
||||
unsigned size;
|
||||
|
||||
/* how much has been allocated - on reply the buffer is over-allocated to
|
||||
prevent too many realloc() calls
|
||||
*/
|
||||
unsigned allocated;
|
||||
|
||||
/* the start of the SMB header - this is always buffer+4 */
|
||||
char *hdr;
|
||||
|
||||
/* the command words and command word count. vwv points
|
||||
into the raw buffer */
|
||||
char *vwv;
|
||||
unsigned wct;
|
||||
|
||||
/* the data buffer and size. data points into the raw buffer */
|
||||
char *data;
|
||||
unsigned data_size;
|
||||
|
||||
/* ptr is used as a moving pointer into the data area
|
||||
* of the packet. The reason its here and not a local
|
||||
* variable in each function is that when a realloc of
|
||||
* a reply packet is done we need to move this
|
||||
* pointer */
|
||||
char *ptr;
|
||||
} in, out;
|
||||
struct request_buffer in;
|
||||
struct request_buffer out;
|
||||
};
|
||||
|
||||
/* this contains variables that should be used in % substitutions for
|
||||
|
Loading…
Reference in New Issue
Block a user