From 6af15943c6f868d08945d0ca993506c17107fbd4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 9 Mar 2009 09:50:36 +0100 Subject: s3:libsmb: use new simplified smb_signing code for the client side We store the seqnum/mid mapping in the cli_request structure for async requests and in the cli_state structure for sync calls. We skip the signing check for oplock requests while waiting for async requests coming in. metze --- source3/include/async_smb.h | 2 + source3/include/client.h | 10 +- source3/include/proto.h | 15 +- source3/include/smb.h | 15 - source3/libsmb/async_smb.c | 43 ++- source3/libsmb/cliconnect.c | 39 ++- source3/libsmb/clientgen.c | 104 ++++++- source3/libsmb/clisigning.c | 650 +++----------------------------------------- source3/libsmb/clitrans.c | 54 ++-- 9 files changed, 224 insertions(+), 708 deletions(-) (limited to 'source3') diff --git a/source3/include/async_smb.h b/source3/include/async_smb.h index 7fc4ff7d27..2ac1101a1e 100644 --- a/source3/include/async_smb.h +++ b/source3/include/async_smb.h @@ -63,6 +63,8 @@ struct cli_request { */ uint16_t mid; + uint32_t seqnum; + /** * The bytes we have to ship to the server */ diff --git a/source3/include/client.h b/source3/include/client.h index 320a90e66b..db19f34a9d 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -166,6 +166,13 @@ struct smb_trans_enc_state { } s; }; +struct cli_state_seqnum { + struct cli_state_seqnum *prev, *next; + uint16_t mid; + uint32_t seqnum; + bool persistent; +}; + struct cli_state { /** * A list of subsidiary connections for DFS. @@ -217,6 +224,7 @@ struct cli_state { size_t max_xmit; size_t max_mux; char *outbuf; + struct cli_state_seqnum *seqnum; char *inbuf; unsigned int bufsize; int initialised; @@ -231,7 +239,7 @@ struct cli_state { TALLOC_CTX *call_mem_ctx; #endif - smb_sign_info sign_info; + struct smb_signing_state *signing_state; struct smb_trans_enc_state *trans_enc_state; /* Setup if we're encrypting SMB's. */ diff --git a/source3/include/proto.h b/source3/include/proto.h index 356eb4935f..d93c4a5125 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -2410,6 +2410,10 @@ bool receive_getdc_response(TALLOC_CTX *mem_ctx, int cli_set_message(char *buf,int num_words,int num_bytes,bool zero); unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout); void cli_set_port(struct cli_state *cli, int port); +bool cli_state_seqnum_persistent(struct cli_state *cli, + uint16_t mid); +bool cli_state_seqnum_remove(struct cli_state *cli, + uint16_t mid); bool cli_receive_smb(struct cli_state *cli); ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len); bool cli_receive_smb_readX_header(struct cli_state *cli); @@ -3186,14 +3190,13 @@ NTSTATUS cli_encrypt_message(struct cli_state *cli, char *buf, char **buf_out); bool cli_simple_set_signing(struct cli_state *cli, const DATA_BLOB user_session_key, const DATA_BLOB response); -bool cli_null_set_signing(struct cli_state *cli); bool cli_temp_set_signing(struct cli_state *cli); -void cli_free_signing_context(struct cli_state *cli); -void cli_calculate_sign_mac(struct cli_state *cli, char *buf); -bool cli_check_sign_mac(struct cli_state *cli, char *buf); -bool client_set_trans_sign_state_on(struct cli_state *cli, uint16 mid); -bool client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid); +void cli_calculate_sign_mac(struct cli_state *cli, char *buf, uint32_t *seqnum); +bool cli_check_sign_mac(struct cli_state *cli, const char *buf, uint32_t seqnum); bool client_is_signing_on(struct cli_state *cli); +bool client_is_signing_allowed(struct cli_state *cli); +bool client_is_signing_mandatory(struct cli_state *cli); +void cli_set_signing_negotiated(struct cli_state *cli); /* The following definitions come from smbd/signing.c */ diff --git a/source3/include/smb.h b/source3/include/smb.h index 215adba1b4..84aa36a364 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -1849,21 +1849,6 @@ struct ip_service { /* Special name type used to cause a _kerberos DNS lookup. */ #define KDC_NAME_TYPE 0xDCDC -/* Used by the SMB signing functions. */ - -typedef struct smb_sign_info { - void (*sign_outgoing_message)(char *outbuf, struct smb_sign_info *si); - bool (*check_incoming_message)(const char *inbuf, struct smb_sign_info *si, bool must_be_ok); - void (*free_signing_context)(struct smb_sign_info *si); - void *signing_context; - - bool negotiated_smb_signing; - bool allow_smb_signing; - bool doing_signing; - bool mandatory_signing; - bool seen_valid; /* Have I ever seen a validly signed packet? */ -} smb_sign_info; - struct ea_struct { uint8 flags; char *name; diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 066ac7bdb8..86fd5c8bef 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -550,7 +550,7 @@ void cli_chain_uncork(struct cli_state *cli) _smb_setlen_large(((char *)req->outbuf), smblen); } - cli_calculate_sign_mac(cli, (char *)req->outbuf); + cli_calculate_sign_mac(cli, (char *)req->outbuf, &req->seqnum); if (cli_encryption_on(cli)) { NTSTATUS status; @@ -811,9 +811,16 @@ NTSTATUS cli_pull_reply(struct async_req *req, */ -static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu) +static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu, + struct cli_request **_req, + uint16_t *_mid) { NTSTATUS status; + struct cli_request *req = NULL; + uint16_t mid; + + *_req = NULL; + *_mid = 0; if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ { @@ -846,11 +853,27 @@ static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu) } } - if (!cli_check_sign_mac(cli, pdu)) { + mid = SVAL(pdu, smb_mid); + + for (req = cli->outstanding_requests; req; req = req->next) { + if (req->mid == mid) { + break; + } + } + + if (!req) { + /* oplock breaks are not signed */ + goto done; + } + + if (!cli_check_sign_mac(cli, pdu, req->seqnum+1)) { DEBUG(10, ("cli_check_sign_mac failed\n")); return NT_STATUS_ACCESS_DENIED; } +done: + *_req = req; + *_mid = mid; return NT_STATUS_OK; } @@ -863,7 +886,7 @@ static void handle_incoming_pdu(struct cli_state *cli) { struct cli_request *req, *next; uint16_t mid; - size_t raw_pdu_len, buf_len, pdu_len, rest_len; + size_t raw_pdu_len, buf_len, rest_len; char *pdu; int i; NTSTATUS status; @@ -923,23 +946,13 @@ static void handle_incoming_pdu(struct cli_state *cli) } } - status = validate_smb_crypto(cli, pdu); + status = validate_smb_crypto(cli, pdu, &req, &mid); if (!NT_STATUS_IS_OK(status)) { goto invalidate_requests; } - mid = SVAL(pdu, smb_mid); - DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid)); - for (req = cli->outstanding_requests; req; req = req->next) { - if (req->mid == mid) { - break; - } - } - - pdu_len = smb_len(pdu) + 4; - if (req == NULL) { DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid)); diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 525c361beb..aa1ca595a7 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -505,11 +505,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user, ok = cli_simple_set_signing(cli, session_key, nt_response); #endif if (ok) { - /* 'resign' the last message, so we get the right sequence numbers - for checking the first reply from the server */ - cli_calculate_sign_mac(cli, cli->outbuf); - - if (!cli_check_sign_mac(cli, cli->inbuf)) { + if (!cli_check_sign_mac(cli, cli->inbuf, 1)) { result = NT_STATUS_ACCESS_DENIED; goto end; } @@ -747,11 +743,7 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char * if (cli_simple_set_signing( cli, session_key_krb5, data_blob_null)) { - /* 'resign' the last message, so we get the right sequence numbers - for checking the first reply from the server */ - cli_calculate_sign_mac(cli, cli->outbuf); - - if (!cli_check_sign_mac(cli, cli->inbuf)) { + if (!cli_check_sign_mac(cli, cli->inbuf, 1)) { nt_status = NT_STATUS_ACCESS_DENIED; goto nt_error; } @@ -873,11 +865,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use if (cli_simple_set_signing( cli, ntlmssp_state->session_key, data_blob_null)) { - /* 'resign' the last message, so we get the right sequence numbers - for checking the first reply from the server */ - cli_calculate_sign_mac(cli, cli->outbuf); - - if (!cli_check_sign_mac(cli, cli->inbuf)) { + if (!cli_check_sign_mac(cli, cli->inbuf, 1)) { nt_status = NT_STATUS_ACCESS_DENIED; } } @@ -1540,13 +1528,16 @@ NTSTATUS cli_negprot_recv(struct async_req *req) cli->protocol = prots[protnum].prot; - if ((cli->protocol < PROTOCOL_NT1) && cli->sign_info.mandatory_signing) { + if ((cli->protocol < PROTOCOL_NT1) && + client_is_signing_mandatory(cli)) { DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n")); return NT_STATUS_ACCESS_DENIED; } if (cli->protocol >= PROTOCOL_NT1) { struct timespec ts; + bool negotiated_smb_signing = false; + /* NT protocol */ cli->sec_mode = CVAL(vwv + 1, 0); cli->max_mux = SVAL(vwv + 1, 1); @@ -1579,22 +1570,24 @@ NTSTATUS cli_negprot_recv(struct async_req *req) if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_REQUIRED) { /* Fail if server says signing is mandatory and we don't want to support it. */ - if (!cli->sign_info.allow_smb_signing) { + if (!client_is_signing_allowed(cli)) { DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n")); return NT_STATUS_ACCESS_DENIED; } - cli->sign_info.negotiated_smb_signing = True; - cli->sign_info.mandatory_signing = True; - } else if (cli->sign_info.mandatory_signing && cli->sign_info.allow_smb_signing) { + negotiated_smb_signing = true; + } else if (client_is_signing_mandatory(cli) && client_is_signing_allowed(cli)) { /* Fail if client says signing is mandatory and the server doesn't support it. */ if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) { DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n")); return NT_STATUS_ACCESS_DENIED; } - cli->sign_info.negotiated_smb_signing = True; - cli->sign_info.mandatory_signing = True; + negotiated_smb_signing = true; } else if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) { - cli->sign_info.negotiated_smb_signing = True; + negotiated_smb_signing = true; + } + + if (negotiated_smb_signing) { + cli_set_signing_negotiated(cli); } if (cli->capabilities & (CAP_LARGE_READX|CAP_LARGE_WRITEX)) { diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index c1ba4e5c4f..6186387076 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -135,6 +135,79 @@ static ssize_t client_receive_smb(struct cli_state *cli, size_t maxlen) return len; } +static bool cli_state_set_seqnum(struct cli_state *cli, uint16_t mid, uint32_t seqnum) +{ + struct cli_state_seqnum *c; + + for (c = cli->seqnum; c; c = c->next) { + if (c->mid == mid) { + c->seqnum = seqnum; + return true; + } + } + + c = talloc_zero(cli, struct cli_state_seqnum); + if (!c) { + return false; + } + + c->mid = mid; + c->seqnum = seqnum; + c->persistent = false; + DLIST_ADD_END(cli->seqnum, c, struct cli_state_seqnum *); + + return true; +} + +bool cli_state_seqnum_persistent(struct cli_state *cli, + uint16_t mid) +{ + struct cli_state_seqnum *c; + + for (c = cli->seqnum; c; c = c->next) { + if (c->mid == mid) { + c->persistent = true; + return true; + } + } + + return false; +} + +bool cli_state_seqnum_remove(struct cli_state *cli, + uint16_t mid) +{ + struct cli_state_seqnum *c; + + for (c = cli->seqnum; c; c = c->next) { + if (c->mid == mid) { + DLIST_REMOVE(cli->seqnum, c); + TALLOC_FREE(c); + return true; + } + } + + return false; +} + +static uint32_t cli_state_get_seqnum(struct cli_state *cli, uint16_t mid) +{ + struct cli_state_seqnum *c; + + for (c = cli->seqnum; c; c = c->next) { + if (c->mid == mid) { + uint32_t seqnum = c->seqnum; + if (!c->persistent) { + DLIST_REMOVE(cli->seqnum, c); + TALLOC_FREE(c); + } + return seqnum; + } + } + + return 0; +} + /**************************************************************************** Recv an smb. ****************************************************************************/ @@ -142,6 +215,8 @@ static ssize_t client_receive_smb(struct cli_state *cli, size_t maxlen) bool cli_receive_smb(struct cli_state *cli) { ssize_t len; + uint16_t mid; + uint32_t seqnum; /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ if (cli->fd == -1) @@ -177,7 +252,10 @@ bool cli_receive_smb(struct cli_state *cli) return false; } - if (!cli_check_sign_mac(cli, cli->inbuf)) { + mid = SVAL(cli->inbuf,smb_mid); + seqnum = cli_state_get_seqnum(cli, mid); + + if (!cli_check_sign_mac(cli, cli->inbuf, seqnum+1)) { /* * If we get a signature failure in sessionsetup, then * the server sometimes just reflects the sent signature @@ -264,12 +342,20 @@ bool cli_send_smb(struct cli_state *cli) ssize_t ret; char *buf_out = cli->outbuf; bool enc_on = cli_encryption_on(cli); + uint32_t seqnum; /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ if (cli->fd == -1) return false; - cli_calculate_sign_mac(cli, cli->outbuf); + cli_calculate_sign_mac(cli, cli->outbuf, &seqnum); + + if (!cli_state_set_seqnum(cli, cli->mid, seqnum)) { + DEBUG(0,("Failed to store mid[%u]/seqnum[%u]\n", + (unsigned int)cli->mid, + (unsigned int)seqnum)); + return false; + } if (enc_on) { NTSTATUS status = cli_encrypt_message(cli, cli->outbuf, @@ -506,6 +592,7 @@ struct cli_state *cli_initialise_ex(int signing_state) cli->bufsize = CLI_BUFFER_SIZE+4; cli->max_xmit = cli->bufsize; cli->outbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN); + cli->seqnum = 0; cli->inbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN); cli->oplock_handler = cli_oplock_ack; cli->case_sensitive = false; @@ -556,9 +643,12 @@ struct cli_state *cli_initialise_ex(int signing_state) #endif /* initialise signing */ - cli->sign_info.allow_smb_signing = allow_smb_signing; - cli->sign_info.mandatory_signing = mandatory_signing; - cli_null_set_signing(cli); + cli->signing_state = smb_signing_init(cli, + allow_smb_signing, + mandatory_signing); + if (!cli->signing_state) { + goto error; + } cli->initialised = 1; @@ -641,7 +731,6 @@ void cli_shutdown(struct cli_state *cli) SAFE_FREE(cli->outbuf); SAFE_FREE(cli->inbuf); - cli_free_signing_context(cli); data_blob_free(&cli->secblob); data_blob_free(&cli->user_session_key); @@ -740,7 +829,6 @@ static void cli_echo_recv_helper(struct async_req *req) cli_req->data.echo.num_echos -= 1; if (cli_req->data.echo.num_echos == 0) { - client_set_trans_sign_state_off(cli_req->cli, cli_req->mid); async_req_done(req); return; } @@ -782,8 +870,6 @@ struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, } req = talloc_get_type_abort(result->private_data, struct cli_request); - client_set_trans_sign_state_on(cli, req->mid); - req->data.echo.num_echos = num_echos; req->data.echo.data.data = talloc_move(req, &data_copy); req->data.echo.data.length = data.length; diff --git a/source3/libsmb/clisigning.c b/source3/libsmb/clisigning.c index 6644bc0d60..0d0e926e6c 100644 --- a/source3/libsmb/clisigning.c +++ b/source3/libsmb/clisigning.c @@ -1,8 +1,9 @@ -/* +/* Unix SMB/CIFS implementation. SMB Signing Code Copyright (C) Jeremy Allison 2003. Copyright (C) Andrew Bartlett 2002-2003 + Copyright (C) Stefan Metzmacher 2009 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 @@ -20,654 +21,67 @@ #include "includes.h" -/* Lookup a packet's MID (multiplex id) and figure out it's sequence number */ -struct outstanding_packet_lookup { - struct outstanding_packet_lookup *prev, *next; - uint16 mid; - uint32 reply_seq_num; - bool can_delete; /* Set to False in trans state. */ -}; - -struct smb_basic_signing_context { - DATA_BLOB mac_key; - uint32 send_seq_num; - struct outstanding_packet_lookup *outstanding_packet_list; -}; - -static bool store_sequence_for_reply(struct outstanding_packet_lookup **list, - uint16 mid, uint32 reply_seq_num) -{ - struct outstanding_packet_lookup *t; - - /* Ensure we only add a mid once. */ - for (t = *list; t; t = t->next) { - if (t->mid == mid) { - return False; - } - } - - t = SMB_XMALLOC_P(struct outstanding_packet_lookup); - ZERO_STRUCTP(t); - - t->mid = mid; - t->reply_seq_num = reply_seq_num; - t->can_delete = True; - - /* - * Add to the *start* of the list not the end of the list. - * This ensures that the *last* send sequence with this mid - * is returned by preference. - * This can happen if the mid wraps and one of the early - * mid numbers didn't get a reply and is still lurking on - * the list. JRA. Found by Fran Fabrizio . - */ - - DLIST_ADD(*list, t); - DEBUG(10,("store_sequence_for_reply: stored seq = %u mid = %u\n", - (unsigned int)reply_seq_num, (unsigned int)mid )); - return True; -} - -static bool get_sequence_for_reply(struct outstanding_packet_lookup **list, - uint16 mid, uint32 *reply_seq_num) -{ - struct outstanding_packet_lookup *t; - - for (t = *list; t; t = t->next) { - if (t->mid == mid) { - *reply_seq_num = t->reply_seq_num; - DEBUG(10,("get_sequence_for_reply: found seq = %u mid = %u\n", - (unsigned int)t->reply_seq_num, (unsigned int)t->mid )); - if (t->can_delete) { - DLIST_REMOVE(*list, t); - SAFE_FREE(t); - } - return True; - } - } - return False; -} - -static bool set_sequence_can_delete_flag(struct outstanding_packet_lookup **list, uint16 mid, bool can_delete_entry) -{ - struct outstanding_packet_lookup *t; - - for (t = *list; t; t = t->next) { - if (t->mid == mid) { - t->can_delete = can_delete_entry; - return True; - } - } - return False; -} - -/*********************************************************** - SMB signing - Common code before we set a new signing implementation -************************************************************/ - -static bool cli_set_smb_signing_common(struct cli_state *cli) -{ - if (!cli->sign_info.allow_smb_signing) { - return False; - } - - if (!cli->sign_info.negotiated_smb_signing - && !cli->sign_info.mandatory_signing) { - return False; - } - - if (cli->sign_info.doing_signing) { - return False; - } - - if (cli->sign_info.free_signing_context) - cli->sign_info.free_signing_context(&cli->sign_info); - - /* These calls are INCOMPATIBLE with SMB signing */ - cli->readbraw_supported = False; - cli->writebraw_supported = False; - - return True; -} - -/*********************************************************** - SMB signing - Common code for 'real' implementations -************************************************************/ - -static bool set_smb_signing_real_common(struct smb_sign_info *si) -{ - if (si->mandatory_signing) { - DEBUG(5, ("Mandatory SMB signing enabled!\n")); - } - - si->doing_signing = True; - DEBUG(5, ("SMB signing enabled!\n")); - - return True; -} - -static void mark_packet_signed(char *outbuf) -{ - uint16 flags2; - flags2 = SVAL(outbuf,smb_flg2); - flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; - SSVAL(outbuf,smb_flg2, flags2); -} - -/*********************************************************** - SMB signing - NULL implementation - calculate a MAC to send. -************************************************************/ - -static void null_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) -{ - /* we can't zero out the sig, as we might be trying to send a - session request - which is NBT-level, not SMB level and doesn't - have the field */ - return; -} - -/*********************************************************** - SMB signing - NULL implementation - check a MAC sent by server. -************************************************************/ - -static bool null_check_incoming_message(const char *inbuf, - struct smb_sign_info *si, - bool must_be_ok) -{ - return True; -} - -/*********************************************************** - SMB signing - NULL implementation - free signing context -************************************************************/ - -static void null_free_signing_context(struct smb_sign_info *si) -{ - return; -} - -/** - SMB signing - NULL implementation - setup the MAC key. - - @note Used as an initialisation only - it will not correctly - shut down a real signing mechanism -*/ - -static bool null_set_signing(struct smb_sign_info *si) -{ - si->signing_context = NULL; - - si->sign_outgoing_message = null_sign_outgoing_message; - si->check_incoming_message = null_check_incoming_message; - si->free_signing_context = null_free_signing_context; - - return True; -} - -/** - * Free the signing context - */ - -static void free_signing_context(struct smb_sign_info *si) -{ - if (si->free_signing_context) { - si->free_signing_context(si); - si->signing_context = NULL; - } - - null_set_signing(si); -} - - -static bool signing_good(const char *inbuf, struct smb_sign_info *si, - bool good, uint32 seq, bool must_be_ok) -{ - if (good) { - - if (!si->doing_signing) { - si->doing_signing = True; - } - - if (!si->seen_valid) { - si->seen_valid = True; - } - - } else { - if (!si->mandatory_signing && !si->seen_valid) { - - if (!must_be_ok) { - return True; - } - /* Non-mandatory signing - just turn off if this is the first bad packet.. */ - DEBUG(5, ("srv_check_incoming_message: signing negotiated but not required and peer\n" - "isn't sending correct signatures. Turning off.\n")); - si->negotiated_smb_signing = False; - si->allow_smb_signing = False; - si->doing_signing = False; - free_signing_context(si); - return True; - } else if (!must_be_ok) { - /* This packet is known to be unsigned */ - 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)); - return False; - } - } - return True; -} - -/*********************************************************** - SMB signing - Simple implementation - calculate a MAC on the packet -************************************************************/ - -static void simple_packet_signature(struct smb_basic_signing_context *data, - const uchar *buf, uint32 seq_number, - unsigned char calc_md5_mac[16]) -{ - const size_t offset_end_of_sig = (smb_ss_field + 8); - unsigned char sequence_buf[8]; - struct MD5Context md5_ctx; -#if 0 - /* JRA - apparently this is incorrect. */ - unsigned char key_buf[16]; -#endif - - /* - * Firstly put the sequence number into the first 4 bytes. - * and zero out the next 4 bytes. - * - * We do this here, to avoid modifying the packet. - */ - - DEBUG(10,("simple_packet_signature: sequence number %u\n", seq_number )); - - SIVAL(sequence_buf, 0, seq_number); - SIVAL(sequence_buf, 4, 0); - - /* Calculate the 16 byte MAC - but don't alter the data in the - incoming packet. - - This makes for a bit of fussing about, but it's not too bad. - */ - MD5Init(&md5_ctx); - - /* intialise with the key */ - MD5Update(&md5_ctx, data->mac_key.data, data->mac_key.length); -#if 0 - /* JRA - apparently this is incorrect. */ - /* NB. When making and verifying SMB signatures, Windows apparently - zero-pads the key to 128 bits if it isn't long enough. - From Nalin Dahyabhai */ - if (data->mac_key.length < sizeof(key_buf)) { - memset(key_buf, 0, sizeof(key_buf)); - MD5Update(&md5_ctx, key_buf, sizeof(key_buf) - data->mac_key.length); - } -#endif - - /* copy in the first bit of the SMB header */ - MD5Update(&md5_ctx, buf + 4, smb_ss_field - 4); - - /* copy in the sequence number, instead of the signature */ - MD5Update(&md5_ctx, sequence_buf, sizeof(sequence_buf)); - - /* copy in the rest of the packet in, skipping the signature */ - MD5Update(&md5_ctx, buf + offset_end_of_sig, - smb_len(buf) - (offset_end_of_sig - 4)); - - /* calculate the MD5 sig */ - MD5Final(calc_md5_mac, &md5_ctx); -} - - -/*********************************************************** - SMB signing - Client implementation - send the MAC. -************************************************************/ - -static void client_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) -{ - unsigned char calc_md5_mac[16]; - struct smb_basic_signing_context *data = - (struct smb_basic_signing_context *)si->signing_context; - - if (!si->doing_signing) - return; - - /* JRA Paranioa test - we should be able to get rid of this... */ - if (smb_len(outbuf) < (smb_ss_field + 8 - 4)) { - DEBUG(1, ("client_sign_outgoing_message: Logic error. Can't check signature on short packet! smb_len = %u\n", - smb_len(outbuf) )); - abort(); - } - - /* mark the packet as signed - BEFORE we sign it...*/ - mark_packet_signed(outbuf); - - simple_packet_signature(data, (const unsigned char *)outbuf, - data->send_seq_num, calc_md5_mac); - - DEBUG(10, ("client_sign_outgoing_message: sent SMB signature of\n")); - dump_data(10, calc_md5_mac, 8); - - memcpy(&outbuf[smb_ss_field], calc_md5_mac, 8); - -/* cli->outbuf[smb_ss_field+2]=0; - Uncomment this to test if the remote server actually verifies signatures...*/ - - /* Instead of re-introducing the trans_info_conect we - used to have here, we use the fact that during a - SMBtrans/SMBtrans2/SMBnttrans send that the mid stays - constant. This means that calling store_sequence_for_reply() - will return False for all trans secondaries, as the mid is already - on the stored sequence list. As the send_seqence_number must - remain constant for all primary+secondary trans sends, we - only increment the send sequence number when we successfully - add a new entry to the outstanding sequence list. This means - I can isolate the fix here rather than re-adding the trans - signing on/off calls in libsmb/clitrans2.c JRA. - */ - - if (store_sequence_for_reply(&data->outstanding_packet_list, SVAL(outbuf,smb_mid), data->send_seq_num + 1)) { - data->send_seq_num += 2; - } -} - -/*********************************************************** - SMB signing - Client implementation - check a MAC sent by server. -************************************************************/ - -static bool client_check_incoming_message(const char *inbuf, - struct smb_sign_info *si, - bool must_be_ok) -{ - bool good; - uint32 reply_seq_number; - unsigned char calc_md5_mac[16]; - unsigned char *server_sent_mac; - - struct smb_basic_signing_context *data = - (struct smb_basic_signing_context *)si->signing_context; - - if (!si->doing_signing) - return True; - - if (smb_len(inbuf) < (smb_ss_field + 8 - 4)) { - DEBUG(1, ("client_check_incoming_message: Can't check signature on short packet! smb_len = %u\n", smb_len(inbuf))); - return False; - } - - if (!get_sequence_for_reply(&data->outstanding_packet_list, SVAL(inbuf, smb_mid), &reply_seq_number)) { - DEBUG(1, ("client_check_incoming_message: received message " - "with mid %u with no matching send record.\n", (unsigned int)SVAL(inbuf, smb_mid) )); - return False; - } - - simple_packet_signature(data, (const unsigned char *)inbuf, - reply_seq_number, calc_md5_mac); - - server_sent_mac = (unsigned char *)&inbuf[smb_ss_field]; - good = (memcmp(server_sent_mac, calc_md5_mac, 8) == 0); - - if (!good) { - DEBUG(5, ("client_check_incoming_message: BAD SIG: wanted SMB signature of\n")); - dump_data(5, calc_md5_mac, 8); - - DEBUG(5, ("client_check_incoming_message: BAD SIG: got SMB signature of\n")); - dump_data(5, server_sent_mac, 8); -#if 1 /* JRATEST */ - { - int i; - for (i = -5; i < 5; i++) { - simple_packet_signature(data, (const unsigned char *)inbuf, reply_seq_number+i, calc_md5_mac); - if (memcmp(server_sent_mac, calc_md5_mac, 8) == 0) { - DEBUG(0,("client_check_incoming_message: out of seq. seq num %u matches. \ -We were expecting seq %u\n", reply_seq_number+i, reply_seq_number )); - break; - } - } - } -#endif /* JRATEST */ - - } else { - DEBUG(10, ("client_check_incoming_message: seq %u: got good SMB signature of\n", (unsigned int)reply_seq_number)); - dump_data(10, server_sent_mac, 8); - } - return signing_good(inbuf, si, good, reply_seq_number, must_be_ok); -} - -/*********************************************************** - SMB signing - Simple implementation - free signing context -************************************************************/ - -static void simple_free_signing_context(struct smb_sign_info *si) -{ - struct smb_basic_signing_context *data = - (struct smb_basic_signing_context *)si->signing_context; - struct outstanding_packet_lookup *list; - struct outstanding_packet_lookup *next; - - for (list = data->outstanding_packet_list; list; list = next) { - next = list->next; - DLIST_REMOVE(data->outstanding_packet_list, list); - SAFE_FREE(list); - } - - data_blob_free(&data->mac_key); - - SAFE_FREE(si->signing_context); - - return; -} - -/*********************************************************** - SMB signing - Simple implementation - setup the MAC key. -************************************************************/ - bool cli_simple_set_signing(struct cli_state *cli, const DATA_BLOB user_session_key, const DATA_BLOB response) { - struct smb_basic_signing_context *data; - - if (!user_session_key.length) - return False; - - if (!cli_set_smb_signing_common(cli)) { - return False; - } - - if (!set_smb_signing_real_common(&cli->sign_info)) { - return False; - } - - data = SMB_XMALLOC_P(struct smb_basic_signing_context); - memset(data, '\0', sizeof(*data)); - - cli->sign_info.signing_context = data; - - data->mac_key = data_blob(NULL, response.length + user_session_key.length); - - memcpy(&data->mac_key.data[0], user_session_key.data, user_session_key.length); - - DEBUG(10, ("cli_simple_set_signing: user_session_key\n")); - dump_data(10, user_session_key.data, user_session_key.length); + bool ok; - if (response.length) { - memcpy(&data->mac_key.data[user_session_key.length],response.data, response.length); - DEBUG(10, ("cli_simple_set_signing: response_data\n")); - dump_data(10, response.data, response.length); - } else { - DEBUG(10, ("cli_simple_set_signing: NULL response_data\n")); + ok = smb_signing_activate(cli->signing_state, + user_session_key, + response); + if (!ok) { + return false; } - dump_data_pw("MAC ssession key is:\n", data->mac_key.data, data->mac_key.length); + cli->readbraw_supported = false; + cli->writebraw_supported = false; - /* Initialise the sequence number */ - data->send_seq_num = 0; - - /* Initialise the list of outstanding packets */ - data->outstanding_packet_list = NULL; - - cli->sign_info.sign_outgoing_message = client_sign_outgoing_message; - cli->sign_info.check_incoming_message = client_check_incoming_message; - cli->sign_info.free_signing_context = simple_free_signing_context; - - return True; -} - -/*********************************************************** - SMB signing - TEMP implementation - calculate a MAC to send. -************************************************************/ - -static void temp_sign_outgoing_message(char *outbuf, struct smb_sign_info *si) -{ - /* mark the packet as signed - BEFORE we sign it...*/ - mark_packet_signed(outbuf); - - /* I wonder what BSRSPYL stands for - but this is what MS - actually sends! */ - memcpy(&outbuf[smb_ss_field], "BSRSPYL ", 8); - return; + return true; } -/*********************************************************** - SMB signing - TEMP implementation - check a MAC sent by server. -************************************************************/ - -static bool temp_check_incoming_message(const char *inbuf, - struct smb_sign_info *si, bool foo) +bool cli_temp_set_signing(struct cli_state *cli) { - return True; + return smb_signing_set_bsrspyl(cli->signing_state); } -/*********************************************************** - SMB signing - TEMP implementation - free signing context -************************************************************/ - -static void temp_free_signing_context(struct smb_sign_info *si) +void cli_calculate_sign_mac(struct cli_state *cli, char *buf, uint32_t *seqnum) { - return; + *seqnum = smb_signing_next_seqnum(cli->signing_state, false); + smb_signing_sign_pdu(cli->signing_state, (uint8_t *)buf, *seqnum); } -/*********************************************************** - SMB signing - NULL implementation - setup the MAC key. -************************************************************/ - -bool cli_null_set_signing(struct cli_state *cli) +bool cli_check_sign_mac(struct cli_state *cli, const char *buf, uint32_t seqnum) { - return null_set_signing(&cli->sign_info); -} + bool ok; -/*********************************************************** - SMB signing - temp implementation - setup the MAC key. -************************************************************/ + ok = smb_signing_check_pdu(cli->signing_state, + (const uint8_t *)buf, + seqnum); -bool cli_temp_set_signing(struct cli_state *cli) -{ - if (!cli_set_smb_signing_common(cli)) { - return False; + if (!ok) { + return false; } - cli->sign_info.signing_context = NULL; - - cli->sign_info.sign_outgoing_message = temp_sign_outgoing_message; - cli->sign_info.check_incoming_message = temp_check_incoming_message; - cli->sign_info.free_signing_context = temp_free_signing_context; - - return True; -} - -void cli_free_signing_context(struct cli_state *cli) -{ - free_signing_context(&cli->sign_info); + return true; } -/** - * Sign a packet with the current mechanism - */ - -void cli_calculate_sign_mac(struct cli_state *cli, char *buf) +void cli_set_signing_negotiated(struct cli_state *cli) { - cli->sign_info.sign_outgoing_message(buf, &cli->sign_info); + smb_signing_set_negotiated(cli->signing_state); } -/** - * Check a packet with the current mechanism - * @return False if we had an established signing connection - * which had a bad checksum, True otherwise. - */ - -bool cli_check_sign_mac(struct cli_state *cli, char *buf) -{ - if (!cli->sign_info.check_incoming_message(buf, &cli->sign_info, True)) { - free_signing_context(&cli->sign_info); - return False; - } - return True; -} - -/*********************************************************** - Enter trans/trans2/nttrans state. -************************************************************/ - -bool client_set_trans_sign_state_on(struct cli_state *cli, uint16 mid) +bool client_is_signing_on(struct cli_state *cli) { - struct smb_sign_info *si = &cli->sign_info; - struct smb_basic_signing_context *data = (struct smb_basic_signing_context *)si->signing_context; - - if (!si->doing_signing) { - return True; - } - - if (!data) { - return False; - } - - if (!set_sequence_can_delete_flag(&data->outstanding_packet_list, mid, False)) { - return False; - } - - return True; + return smb_signing_is_active(cli->signing_state); } -/*********************************************************** - Leave trans/trans2/nttrans state. -************************************************************/ - -bool client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid) +bool client_is_signing_allowed(struct cli_state *cli) { - uint32 reply_seq_num; - struct smb_sign_info *si = &cli->sign_info; - struct smb_basic_signing_context *data = (struct smb_basic_signing_context *)si->signing_context; - - if (!si->doing_signing) { - return True; - } - - if (!data) { - return False; - } - - if (!set_sequence_can_delete_flag(&data->outstanding_packet_list, mid, True)) { - return False; - } - - /* Now delete the stored mid entry. */ - if (!get_sequence_for_reply(&data->outstanding_packet_list, mid, &reply_seq_num)) { - return False; - } - - return True; + return smb_signing_is_allowed(cli->signing_state); } -/*********************************************************** - Is client signing on ? -************************************************************/ - -bool client_is_signing_on(struct cli_state *cli) +bool client_is_signing_mandatory(struct cli_state *cli) { - struct smb_sign_info *si = &cli->sign_info; - return si->doing_signing; + return smb_signing_is_mandatory(cli->signing_state); } diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c index 0266c0307e..c566972b21 100644 --- a/source3/libsmb/clitrans.c +++ b/source3/libsmb/clitrans.c @@ -94,14 +94,12 @@ bool cli_send_trans(struct cli_state *cli, int trans, return False; } - /* Note we're in a trans state. Save the sequence - * numbers for replies. */ - client_set_trans_sign_state_on(cli, mid); + cli_state_seqnum_persistent(cli, mid); if (this_ldata < ldata || this_lparam < lparam) { /* receive interim response */ if (!cli_receive_smb(cli) || cli_is_error(cli)) { - client_set_trans_sign_state_off(cli, mid); + cli_state_seqnum_remove(cli, mid); return(False); } @@ -137,12 +135,11 @@ bool cli_send_trans(struct cli_state *cli, int trans, show_msg(cli->outbuf); - client_set_trans_sign_state_off(cli, mid); cli->mid = mid; if (!cli_send_smb(cli)) { + cli_state_seqnum_remove(cli, mid); return False; } - client_set_trans_sign_state_on(cli, mid); tot_data += this_ldata; tot_param += this_lparam; @@ -165,10 +162,14 @@ bool cli_receive_trans(struct cli_state *cli,int trans, unsigned int this_data,this_param; NTSTATUS status; bool ret = False; + uint16_t mid; *data_len = *param_len = 0; + mid = SVAL(cli->inbuf,smb_mid); + if (!cli_receive_smb(cli)) { + cli_state_seqnum_remove(cli, mid); return False; } @@ -179,6 +180,7 @@ bool cli_receive_trans(struct cli_state *cli,int trans, DEBUG(0,("Expected %s response, got command 0x%02x\n", trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(cli->inbuf,smb_com))); + cli_state_seqnum_remove(cli, mid); return False; } @@ -331,6 +333,8 @@ bool cli_receive_trans(struct cli_state *cli,int trans, out: + cli_state_seqnum_remove(cli, mid); + if (ret) { /* Ensure the last 2 bytes of param and data are 2 null * bytes. These are malloc'ed, but not included in any @@ -344,7 +348,6 @@ bool cli_receive_trans(struct cli_state *cli,int trans, } } - client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid)); return ret; } @@ -412,14 +415,12 @@ bool cli_send_nt_trans(struct cli_state *cli, return False; } - /* Note we're in a trans state. Save the sequence - * numbers for replies. */ - client_set_trans_sign_state_on(cli, mid); + cli_state_seqnum_persistent(cli, mid); if (this_ldata < ldata || this_lparam < lparam) { /* receive interim response */ if (!cli_receive_smb(cli) || cli_is_error(cli)) { - client_set_trans_sign_state_off(cli, mid); + cli_state_seqnum_remove(cli, mid); return(False); } @@ -454,12 +455,11 @@ bool cli_send_nt_trans(struct cli_state *cli, show_msg(cli->outbuf); - client_set_trans_sign_state_off(cli, mid); cli->mid = mid; if (!cli_send_smb(cli)) { + cli_state_seqnum_remove(cli, mid); return False; } - client_set_trans_sign_state_on(cli, mid); tot_data += this_ldata; tot_param += this_lparam; @@ -483,10 +483,14 @@ bool cli_receive_nt_trans(struct cli_state *cli, uint8 eclass; uint32 ecode; bool ret = False; + uint16_t mid; *data_len = *param_len = 0; + mid = SVAL(cli->inbuf,smb_mid); + if (!cli_receive_smb(cli)) { + cli_state_seqnum_remove(cli, mid); return False; } @@ -496,6 +500,7 @@ bool cli_receive_nt_trans(struct cli_state *cli, if (CVAL(cli->inbuf,smb_com) != SMBnttrans) { DEBUG(0,("Expected SMBnttrans response, got command 0x%02x\n", CVAL(cli->inbuf,smb_com))); + cli_state_seqnum_remove(cli, mid); return(False); } @@ -669,6 +674,8 @@ bool cli_receive_nt_trans(struct cli_state *cli, out: + cli_state_seqnum_remove(cli, mid); + if (ret) { /* Ensure the last 2 bytes of param and data are 2 null * bytes. These are malloc'ed, but not included in any @@ -682,7 +689,6 @@ bool cli_receive_nt_trans(struct cli_state *cli, } } - client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid)); return ret; } @@ -696,6 +702,7 @@ struct cli_trans_state { struct event_context *ev; uint8_t cmd; uint16_t mid; + uint32_t seqnum; const char *pipe_name; uint16_t fid; uint16_t function; @@ -919,6 +926,7 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, cli_req = talloc_get_type_abort(result->private_data, struct cli_request); state->mid = cli_req->mid; + state->seqnum = cli_req->seqnum; } else { uint16_t num_bytes = talloc_get_size(bytes); /* @@ -939,12 +947,10 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, cli_req->recv_helper.fn = cli_trans_recv_helper; cli_req->recv_helper.priv = state; cli_req->mid = state->mid; - client_set_trans_sign_state_off(state->cli, state->mid); cli_chain_uncork(state->cli); + state->seqnum = cli_req->seqnum; } - client_set_trans_sign_state_on(state->cli, state->mid); - fail: TALLOC_FREE(frame); return result; @@ -953,6 +959,8 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, static void cli_trans_ship_rest(struct async_req *req, struct cli_trans_state *state) { + struct cli_request *cli_req; + state->secondary_request_ctx = talloc_new(state); if (state->secondary_request_ctx == NULL) { async_req_nterror(req, NT_STATUS_NO_MEMORY); @@ -961,14 +969,19 @@ static void cli_trans_ship_rest(struct async_req *req, while ((state->param_sent < state->num_param) || (state->data_sent < state->num_data)) { - struct async_req *cli_req; + struct async_req *subreq; - cli_req = cli_ship_trans(state->secondary_request_ctx, state); - if (cli_req == NULL) { + subreq = cli_ship_trans(state->secondary_request_ctx, state); + if (subreq == NULL) { async_req_nterror(req, NT_STATUS_NO_MEMORY); return; } } + + cli_req = talloc_get_type_abort(req->private_data, + struct cli_request); + + cli_req->seqnum = state->seqnum; } static NTSTATUS cli_pull_trans(struct async_req *req, @@ -1174,7 +1187,6 @@ static void cli_trans_recv_helper(struct async_req *req) if ((state->rparam.total == state->rparam.received) && (state->rdata.total == state->rdata.received)) { - client_set_trans_sign_state_off(state->cli, state->mid); async_req_done(req); } } -- cgit