From b3c46674a670ea51607d5c2a73271dff531ae7d6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 16 Jul 2004 02:54:57 +0000 Subject: 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 35b83eb329482ac1b3bc67285854cc47844ff353) --- source4/libcli/raw/clisession.c | 43 +++++++---- source4/libcli/raw/smb_signing.c | 152 ++++++++++++++++++++++----------------- 2 files changed, 115 insertions(+), 80 deletions(-) (limited to 'source4/libcli/raw') diff --git a/source4/libcli/raw/clisession.c b/source4/libcli/raw/clisession.c index e31cf07bf8..c6d7f800a4 100644 --- a/source4/libcli/raw/clisession.c +++ b/source4/libcli/raw/clisession.c @@ -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); diff --git a/source4/libcli/raw/smb_signing.c b/source4/libcli/raw/smb_signing.c index fc10413108..20f8921acb 100644 --- a/source4/libcli/raw/smb_signing.c +++ b/source4/libcli/raw/smb_signing.c @@ -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,119 +99,97 @@ 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; } -/*********************************************************** - SMB signing - Simple implementation - calculate a MAC to send. -************************************************************/ -static void cli_request_simple_sign_outgoing_message(struct cli_request *req) +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; - struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context; - -#if 0 - /* enable this when packet signing is preventing you working out why valgrind - says that data is uninitialised */ - file_save("pkt.dat", req->out.buffer, req->out.size); -#endif - - req->seq_num = data->next_seq_num; - - /* some requests (eg. NTcancel) are one way, and the sequence number - should be increased by 1 not 2 */ - if (req->one_way_request) { - data->next_seq_num += 1; - } 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); + 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(req); + mark_packet_signed(out); /* 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, mac_key->data, + mac_key->length); MD5Update(&md5_ctx, - req->out.buffer + NBT_HDR_SIZE, - req->out.size - NBT_HDR_SIZE); + out->buffer + NBT_HDR_SIZE, + out->size - NBT_HDR_SIZE); MD5Final(calc_md5_mac, &md5_ctx); - memcpy(&req->out.hdr[HDR_SS_FIELD], calc_md5_mac, 8); + memcpy(&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)); + 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...*/ } - -/*********************************************************** - SMB signing - Simple implementation - check a MAC sent by server. -************************************************************/ -static BOOL cli_request_simple_check_incoming_message(struct cli_request *req) +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; - 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; + /* 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 = 1-sign_range; i <= 1+sign_range; i++) { + 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, req->seq_num+i); + SIVAL(sequence_buf, 0, 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)); + 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, data->mac_key.data, - data->mac_key.length); - MD5Update(&md5_ctx, req->in.hdr, HDR_SS_FIELD); + 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, req->in.hdr + offset_end_of_sig, - req->in.size - NBT_HDR_SIZE - (offset_end_of_sig)); + 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 == 1) { + if (i == 0) { if (!good) { - DEBUG(5, ("cli_request_simple_check_incoming_message: BAD SIG (seq: %d): wanted SMB signature of\n", req->seq_num + i)); + 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, ("cli_request_simple_check_incoming_message: BAD SIG (seq: %d): got SMB signature of\n", req->seq_num + i)); + 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, ("cli_request_simple_check_incoming_message: GOOD SIG (seq: %d): got SMB signature of\n", req->seq_num + i)); + 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); } } @@ -220,10 +197,51 @@ static BOOL cli_request_simple_check_incoming_message(struct cli_request *req) if (good) break; } - if (good && i != 1) { - DEBUG(0,("SIGNING OFFSET %d (should be %d)\n", i, req->seq_num+1)); + 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) +{ + struct smb_basic_signing_context *data = req->transport->negotiate.sign_info.signing_context; + +#if 0 + /* enable this when packet signing is preventing you working out why valgrind + says that data is uninitialised */ + file_save("pkt.dat", req->out.buffer, req->out.size); +#endif + + req->seq_num = data->next_seq_num; + + /* some requests (eg. NTcancel) are one way, and the sequence number + should be increased by 1 not 2 */ + if (req->one_way_request) { + data->next_seq_num += 1; + } else { + data->next_seq_num += 2; } + + sign_outgoing_message(&req->out, &data->mac_key, req->seq_num); +} + +/*********************************************************** + SMB signing - Simple implementation - check a MAC sent by server. +************************************************************/ +static BOOL cli_request_simple_check_incoming_message(struct cli_request *req) +{ + 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; } -- cgit