diff options
Diffstat (limited to 'source4/libcli')
-rw-r--r-- | source4/libcli/auth/gensec_krb5.c | 4 | ||||
-rw-r--r-- | source4/libcli/auth/spnego.c | 43 | ||||
-rw-r--r-- | source4/libcli/raw/clisession.c | 43 | ||||
-rw-r--r-- | source4/libcli/raw/smb_signing.c | 152 |
4 files changed, 141 insertions, 101 deletions
diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c index 8268eb6051..c7c1a18d24 100644 --- a/source4/libcli/auth/gensec_krb5.c +++ b/source4/libcli/auth/gensec_krb5.c @@ -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); diff --git a/source4/libcli/auth/spnego.c b/source4/libcli/auth/spnego.c index d4910eb92f..c16d77dad9 100644 --- a/source4/libcli/auth/spnego.c +++ b/source4/libcli/auth/spnego.c @@ -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: 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; } |