From 1ea0a5d0cdaf9cbc1d1a5c0720ae466f07cea816 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 28 Feb 2008 14:41:25 +0100 Subject: Add infrastructure to support async SMB requests (This used to be commit e2153301351559f30f2714345f4c1ca6c5f1a45f) --- source3/libsmb/async_smb.c | 483 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 483 insertions(+) create mode 100644 source3/libsmb/async_smb.c (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c new file mode 100644 index 0000000000..21bcd5b9b1 --- /dev/null +++ b/source3/libsmb/async_smb.c @@ -0,0 +1,483 @@ +/* + Unix SMB/CIFS implementation. + Infrastructure for async SMB client requests + Copyright (C) Volker Lendecke 2008 + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" + +/* + * Fetch an error out of a NBT packet + */ + +NTSTATUS cli_pull_error(char *buf) +{ + uint32_t flags2 = SVAL(buf, smb_flg2); + + if (flags2 & FLAGS2_32_BIT_ERROR_CODES) { + return NT_STATUS(IVAL(buf, smb_rcls)); + } + + return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err)); +} + +/* + * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf + */ + +void cli_set_error(struct cli_state *cli, NTSTATUS status) +{ + uint32_t flags2 = SVAL(cli->inbuf, smb_flg2); + + if (NT_STATUS_IS_DOS(status)) { + SSVAL(cli->inbuf, smb_flg2, + flags2 & ~FLAGS2_32_BIT_ERROR_CODES); + SCVAL(cli->inbuf, smb_rcls, NT_STATUS_DOS_CLASS(status)); + SSVAL(cli->inbuf, smb_err, NT_STATUS_DOS_CODE(status)); + return; + } + + SSVAL(cli->inbuf, smb_flg2, flags2 | FLAGS2_32_BIT_ERROR_CODES); + SIVAL(cli->inbuf, smb_rcls, NT_STATUS_V(status)); + return; +} + +/* + * Allocate a new mid + */ + +static uint16_t cli_new_mid(struct cli_state *cli) +{ + uint16_t result; + struct cli_request *req; + + while (true) { + result = cli->mid++; + if (result == 0) { + continue; + } + + for (req = cli->outstanding_requests; req; req = req->next) { + if (result == req->mid) { + break; + } + } + + if (req == NULL) { + return result; + } + } +} + +static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req) +{ + char *result = async_req_print(mem_ctx, req); + struct cli_request *cli_req = cli_request_get(req); + + if (result == NULL) { + return NULL; + } + + return talloc_asprintf_append_buffer( + result, "mid=%d\n", cli_req->mid); +} + +static int cli_request_destructor(struct cli_request *req) +{ + if (req->enc_state != NULL) { + common_free_enc_buffer(req->enc_state, req->outbuf); + } + DLIST_REMOVE(req->cli->outstanding_requests, req); + return 0; +} + +/* + * Create a fresh async smb request + */ + +struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint8_t num_words, size_t num_bytes, + struct cli_request **preq) +{ + struct async_req *result; + struct cli_request *cli_req; + size_t bufsize = smb_size + num_words * 2 + num_bytes; + + result = async_req_new(mem_ctx, ev); + if (result == NULL) { + return NULL; + } + + cli_req = (struct cli_request *)talloc_size( + result, sizeof(*cli_req) + bufsize); + if (cli_req == NULL) { + TALLOC_FREE(result); + return NULL; + } + talloc_set_name_const(cli_req, "struct cli_request"); + result->private_data = cli_req; + result->print = cli_request_print; + + cli_req->async = result; + cli_req->cli = cli; + cli_req->outbuf = ((char *)cli_req + sizeof(*cli_req)); + cli_req->sent = 0; + cli_req->mid = cli_new_mid(cli); + cli_req->inbuf = NULL; + cli_req->enc_state = NULL; + + SCVAL(cli_req->outbuf, smb_wct, num_words); + SSVAL(cli_req->outbuf, smb_vwv + num_words * 2, num_bytes); + + DLIST_ADD_END(cli->outstanding_requests, cli_req, + struct cli_request *); + talloc_set_destructor(cli_req, cli_request_destructor); + + DEBUG(10, ("cli_request_new: mid=%d\n", cli_req->mid)); + + *preq = cli_req; + return result; +} + +/* + * Convenience function to get the SMB part out of an async_req + */ + +struct cli_request *cli_request_get(struct async_req *req) +{ + if (req == NULL) { + return NULL; + } + return talloc_get_type_abort(req->private_data, struct cli_request); +} + +/* + * A PDU has arrived on cli->evt_inbuf + */ + +static void handle_incoming_pdu(struct cli_state *cli) +{ + struct cli_request *req; + uint16_t mid; + size_t raw_pdu_len, buf_len, pdu_len; + size_t rest_len; + NTSTATUS status; + + /* + * The encrypted PDU len might differ from the unencrypted one + */ + raw_pdu_len = smb_len(cli->evt_inbuf) + 4; + + /* + * TODO: Handle oplock break requests + */ + + if (cli_encryption_on(cli) && CVAL(cli->evt_inbuf, 0) == 0) { + uint16_t enc_ctx_num; + + status = get_enc_ctx_num((uint8_t *)cli->evt_inbuf, + &enc_ctx_num); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("get_enc_ctx_num returned %s\n", + nt_errstr(status))); + goto invalidate_requests; + } + + if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) { + DEBUG(10, ("wrong enc_ctx %d, expected %d\n", + enc_ctx_num, + cli->trans_enc_state->enc_ctx_num)); + status = NT_STATUS_INVALID_HANDLE; + goto invalidate_requests; + } + + status = common_decrypt_buffer(cli->trans_enc_state, + cli->evt_inbuf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("common_decrypt_buffer returned %s\n", + nt_errstr(status))); + goto invalidate_requests; + } + } + + if (!cli_check_sign_mac(cli, cli->evt_inbuf)) { + DEBUG(10, ("cli_check_sign_mac failed\n")); + status = NT_STATUS_ACCESS_DENIED; + goto invalidate_requests; + } + + mid = SVAL(cli->evt_inbuf, 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; + } + } + + buf_len = talloc_get_size(cli->evt_inbuf); + pdu_len = smb_len(cli->evt_inbuf) + 4; + rest_len = buf_len - raw_pdu_len; + + if (req == NULL) { + DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid)); + + memmove(cli->evt_inbuf, cli->evt_inbuf + raw_pdu_len, + buf_len - raw_pdu_len); + + cli->evt_inbuf = TALLOC_REALLOC_ARRAY(NULL, cli->evt_inbuf, + char, rest_len); + return; + } + + if (buf_len == pdu_len) { + /* + * Optimal case: Exactly one PDU was in the socket buffer + */ + req->inbuf = talloc_move(req, &cli->evt_inbuf); + goto done; + } + + DEBUG(11, ("buf_len = %d, pdu_len = %d, splitting buffer\n", + (int)buf_len, (int)pdu_len)); + + if (pdu_len < rest_len) { + /* + * The PDU is shorter, talloc_memdup that one. + */ + req->inbuf = (char *)talloc_memdup( + req, cli->evt_inbuf, pdu_len); + + memmove(cli->evt_inbuf, + cli->evt_inbuf + raw_pdu_len, + buf_len - raw_pdu_len); + + cli->evt_inbuf = TALLOC_REALLOC_ARRAY( + NULL, cli->evt_inbuf, char, rest_len); + } + else { + /* + * The PDU is larger than the rest, + * talloc_memdup the rest + */ + req->inbuf = talloc_move(req, &cli->evt_inbuf); + + cli->evt_inbuf = (char *)talloc_memdup( + cli, req->inbuf + raw_pdu_len, + rest_len); + } + + if ((req->inbuf == NULL) || (cli->evt_inbuf == NULL)) { + status = NT_STATUS_NO_MEMORY; + goto invalidate_requests; + } + + done: + async_req_done(req->async); + return; + + invalidate_requests: + + DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n", + nt_errstr(status))); + + for (req = cli->outstanding_requests; req; req = req->next) { + async_req_error(req->async, status); + } + return; +} + +/* + * fd event callback. This is the basic connection to the socket + */ + +static void cli_state_handler(struct event_context *event_ctx, + struct fd_event *event, uint16 flags, void *p) +{ + struct cli_state *cli = (struct cli_state *)p; + struct cli_request *req; + + DEBUG(11, ("cli_state_handler called with flags %d\n", flags)); + + if (flags & EVENT_FD_READ) { + int res, available; + size_t old_size, new_size; + char *tmp; + + res = ioctl(cli->fd, FIONREAD, &available); + if (res == -1) { + DEBUG(10, ("ioctl(FIONREAD) failed: %s\n", + strerror(errno))); + goto sock_error; + } + + if (available == 0) { + /* EOF */ + goto sock_error; + } + + old_size = talloc_get_size(cli->evt_inbuf); + new_size = old_size + available; + + if (new_size < old_size) { + /* wrap */ + goto sock_error; + } + + tmp = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf, char, + new_size); + if (tmp == NULL) { + /* nomem */ + goto sock_error; + } + cli->evt_inbuf = tmp; + + res = recv(cli->fd, cli->evt_inbuf + old_size, available, 0); + if (res == -1) { + DEBUG(10, ("recv failed: %s\n", strerror(errno))); + goto sock_error; + } + + DEBUG(11, ("cli_state_handler: received %d bytes, " + "smb_len(evt_inbuf) = %d\n", (int)res, + smb_len(cli->evt_inbuf))); + + /* recv *might* have returned less than announced */ + new_size = old_size + res; + + /* shrink, so I don't expect errors here */ + cli->evt_inbuf = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf, + char, new_size); + + while ((cli->evt_inbuf != NULL) + && ((smb_len(cli->evt_inbuf) + 4) <= new_size)) { + /* + * we've got a complete NBT level PDU in evt_inbuf + */ + handle_incoming_pdu(cli); + new_size = talloc_get_size(cli->evt_inbuf); + } + } + + if (flags & EVENT_FD_WRITE) { + size_t to_send; + ssize_t sent; + + for (req = cli->outstanding_requests; req; req = req->next) { + to_send = smb_len(req->outbuf)+4; + if (to_send > req->sent) { + break; + } + } + + if (req == NULL) { + event_fd_set_not_writeable(event); + return; + } + + sent = send(cli->fd, req->outbuf + req->sent, + to_send - req->sent, 0); + + if (sent < 0) { + goto sock_error; + } + + req->sent += sent; + + if (req->sent == to_send) { + return; + } + } + return; + + sock_error: + for (req = cli->outstanding_requests; req; req = req->next) { + req->async->state = ASYNC_REQ_ERROR; + req->async->status = map_nt_error_from_unix(errno); + } + TALLOC_FREE(cli->fd_event); + close(cli->fd); + cli->fd = -1; +} + +/* + * Holder for a talloc_destructor, we need to zero out the pointers in cli + * when deleting + */ +struct cli_tmp_event { + struct cli_state *cli; +}; + +static int cli_tmp_event_destructor(struct cli_tmp_event *e) +{ + TALLOC_FREE(e->cli->fd_event); + TALLOC_FREE(e->cli->event_ctx); + return 0; +} + +/* + * Create a temporary event context for use in the sync helper functions + */ + +struct cli_tmp_event *cli_tmp_event_ctx(TALLOC_CTX *mem_ctx, + struct cli_state *cli) +{ + struct cli_tmp_event *state; + + if (cli->event_ctx != NULL) { + return NULL; + } + + state = talloc(mem_ctx, struct cli_tmp_event); + if (state == NULL) { + return NULL; + } + state->cli = cli; + talloc_set_destructor(state, cli_tmp_event_destructor); + + cli->event_ctx = event_context_init(state); + if (cli->event_ctx == NULL) { + TALLOC_FREE(state); + return NULL; + } + + cli->fd_event = event_add_fd(cli->event_ctx, state, cli->fd, + EVENT_FD_READ, cli_state_handler, cli); + if (cli->fd_event == NULL) { + TALLOC_FREE(state); + return NULL; + } + return state; +} + +/* + * Attach an event context permanently to a cli_struct + */ + +NTSTATUS cli_add_event_ctx(struct cli_state *cli, + struct event_context *event_ctx) +{ + cli->event_ctx = event_ctx; + cli->fd_event = event_add_fd(event_ctx, cli, cli->fd, EVENT_FD_READ, + cli_state_handler, cli); + if (cli->fd_event == NULL) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} -- cgit From 464f74df2f6f50525c3701025860fe0435d9c91c Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 7 Mar 2008 15:03:20 +0100 Subject: Move inbuf handling to before the PDU handling In an error case, correctly discard the offending PDU (This used to be commit 0aa195b5d623e1f26f2a1b9e91323a5ddd3ff282) --- source3/libsmb/async_smb.c | 117 +++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 58 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 21bcd5b9b1..04c22a9d17 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -174,24 +174,72 @@ static void handle_incoming_pdu(struct cli_state *cli) { struct cli_request *req; uint16_t mid; - size_t raw_pdu_len, buf_len, pdu_len; - size_t rest_len; + size_t raw_pdu_len, buf_len, pdu_len, rest_len; + char *pdu; NTSTATUS status; /* * The encrypted PDU len might differ from the unencrypted one */ raw_pdu_len = smb_len(cli->evt_inbuf) + 4; + buf_len = talloc_get_size(cli->evt_inbuf); + rest_len = buf_len - raw_pdu_len; + + if (buf_len == raw_pdu_len) { + /* + * Optimal case: Exactly one PDU was in the socket buffer + */ + pdu = cli->evt_inbuf; + cli->evt_inbuf = NULL; + } + else { + DEBUG(11, ("buf_len = %d, raw_pdu_len = %d, splitting " + "buffer\n", (int)buf_len, (int)raw_pdu_len)); + + if (raw_pdu_len < rest_len) { + /* + * The PDU is shorter, talloc_memdup that one. + */ + pdu = (char *)talloc_memdup( + cli, cli->evt_inbuf, raw_pdu_len); + + memmove(cli->evt_inbuf, cli->evt_inbuf + raw_pdu_len, + buf_len - raw_pdu_len); + + cli->evt_inbuf = TALLOC_REALLOC_ARRAY( + NULL, cli->evt_inbuf, char, rest_len); + + if (pdu == NULL) { + status = NT_STATUS_NO_MEMORY; + goto invalidate_requests; + } + } + else { + /* + * The PDU is larger than the rest, talloc_memdup the + * rest + */ + pdu = cli->evt_inbuf; + + cli->evt_inbuf = (char *)talloc_memdup( + cli, pdu + raw_pdu_len, rest_len); + + if (cli->evt_inbuf == NULL) { + status = NT_STATUS_NO_MEMORY; + goto invalidate_requests; + } + } + + } /* * TODO: Handle oplock break requests */ - if (cli_encryption_on(cli) && CVAL(cli->evt_inbuf, 0) == 0) { + if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) { uint16_t enc_ctx_num; - status = get_enc_ctx_num((uint8_t *)cli->evt_inbuf, - &enc_ctx_num); + status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("get_enc_ctx_num returned %s\n", nt_errstr(status))); @@ -207,7 +255,7 @@ static void handle_incoming_pdu(struct cli_state *cli) } status = common_decrypt_buffer(cli->trans_enc_state, - cli->evt_inbuf); + pdu); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("common_decrypt_buffer returned %s\n", nt_errstr(status))); @@ -215,13 +263,13 @@ static void handle_incoming_pdu(struct cli_state *cli) } } - if (!cli_check_sign_mac(cli, cli->evt_inbuf)) { + if (!cli_check_sign_mac(cli, pdu)) { DEBUG(10, ("cli_check_sign_mac failed\n")); status = NT_STATUS_ACCESS_DENIED; goto invalidate_requests; } - mid = SVAL(cli->evt_inbuf, smb_mid); + mid = SVAL(pdu, smb_mid); DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid)); @@ -231,64 +279,17 @@ static void handle_incoming_pdu(struct cli_state *cli) } } - buf_len = talloc_get_size(cli->evt_inbuf); - pdu_len = smb_len(cli->evt_inbuf) + 4; - rest_len = buf_len - raw_pdu_len; + pdu_len = smb_len(pdu) + 4; if (req == NULL) { DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid)); - memmove(cli->evt_inbuf, cli->evt_inbuf + raw_pdu_len, - buf_len - raw_pdu_len); - - cli->evt_inbuf = TALLOC_REALLOC_ARRAY(NULL, cli->evt_inbuf, - char, rest_len); + TALLOC_FREE(pdu); return; } - if (buf_len == pdu_len) { - /* - * Optimal case: Exactly one PDU was in the socket buffer - */ - req->inbuf = talloc_move(req, &cli->evt_inbuf); - goto done; - } - - DEBUG(11, ("buf_len = %d, pdu_len = %d, splitting buffer\n", - (int)buf_len, (int)pdu_len)); - - if (pdu_len < rest_len) { - /* - * The PDU is shorter, talloc_memdup that one. - */ - req->inbuf = (char *)talloc_memdup( - req, cli->evt_inbuf, pdu_len); - - memmove(cli->evt_inbuf, - cli->evt_inbuf + raw_pdu_len, - buf_len - raw_pdu_len); - - cli->evt_inbuf = TALLOC_REALLOC_ARRAY( - NULL, cli->evt_inbuf, char, rest_len); - } - else { - /* - * The PDU is larger than the rest, - * talloc_memdup the rest - */ - req->inbuf = talloc_move(req, &cli->evt_inbuf); - - cli->evt_inbuf = (char *)talloc_memdup( - cli, req->inbuf + raw_pdu_len, - rest_len); - } - - if ((req->inbuf == NULL) || (cli->evt_inbuf == NULL)) { - status = NT_STATUS_NO_MEMORY; - goto invalidate_requests; - } + req->inbuf = talloc_move(req, &pdu); - done: async_req_done(req->async); return; -- cgit From b48ba00dc67cc7235a135615b0ee25245ac8474b Mon Sep 17 00:00:00 2001 From: Jim McDonough Date: Wed, 13 Aug 2008 18:03:51 -0400 Subject: Prevent NT_STATUS 0xF1000000 errors from appearing when dos errors are used and there is no error. It should be mapped directly to NT_STATUS_OK. smbclient to older servers didn't work. (This used to be commit 78f009b7ef1f1d63b21df9ba6da7fcca01c12109) --- source3/libsmb/async_smb.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 04c22a9d17..58bba2bfc7 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -31,6 +31,12 @@ NTSTATUS cli_pull_error(char *buf) return NT_STATUS(IVAL(buf, smb_rcls)); } + /* if the client uses dos errors, but there is no error, + we should return no error here, otherwise it looks + like an unknown bad NT_STATUS. jmcd */ + if (CVAL(buf, smb_rcls) == 0) + return NT_STATUS_OK; + return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err)); } -- cgit From 58aa97c0d9db06588d1aba4f06a3c98ed2098d8f Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 1 Aug 2008 23:14:51 +0200 Subject: Refactoring: Add the routine cli_request_send() cli_request_send() is supposed to bundle all generic SMB-header handling. This makes cli_request_new static to async_smb.c. (This used to be commit 7e73dd4e7622db64d30d48ba106892e0895fc188) --- source3/libsmb/async_smb.c | 62 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 5 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 58bba2bfc7..454bd8169b 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -114,11 +114,11 @@ static int cli_request_destructor(struct cli_request *req) * Create a fresh async smb request */ -struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint8_t num_words, size_t num_bytes, - struct cli_request **preq) +static struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint8_t num_words, size_t num_bytes, + struct cli_request **preq) { struct async_req *result; struct cli_request *cli_req; @@ -160,6 +160,58 @@ struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, return result; } +/* + * Ship a new smb request to the server + */ +struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint8_t wct, const uint16_t *vwv, + uint16_t num_bytes, const uint8_t *bytes) +{ + struct async_req *result; + struct cli_request *req; + + result = cli_request_new(mem_ctx, cli->event_ctx, cli, wct, num_bytes, + &req); + if (result == NULL) { + DEBUG(0, ("cli_request_new failed\n")); + return NULL; + } + + cli_set_message(req->outbuf, wct, num_bytes, false); + SCVAL(req->outbuf, smb_com, smb_command); + SSVAL(req->outbuf, smb_tid, cli->cnum); + cli_setup_packet_buf(cli, req->outbuf); + + memcpy(req->outbuf + smb_vwv0, vwv, sizeof(uint16_t) * wct); + memcpy(smb_buf(req->outbuf), bytes, num_bytes); + SSVAL(req->outbuf, smb_mid, req->mid); + SCVAL(cli->outbuf, smb_flg, + CVAL(cli->outbuf,smb_flg) | additional_flags); + + cli_calculate_sign_mac(cli, req->outbuf); + + if (cli_encryption_on(cli)) { + NTSTATUS status; + char *enc_buf; + + status = cli_encrypt_message(cli, req->outbuf, &enc_buf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Error in encrypting client message. " + "Error %s\n", nt_errstr(status))); + TALLOC_FREE(req); + return NULL; + } + req->outbuf = enc_buf; + req->enc_state = cli->trans_enc_state; + } + + event_fd_set_writeable(cli->fd_event); + + return result; +} + /* * Convenience function to get the SMB part out of an async_req */ -- cgit From 2650207d4adbfd68974fc2b342dd2af079a2552c Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 24 Aug 2008 14:17:43 +0200 Subject: Remove cli->event_ctx, pass it explicitly Storing the event_context as permanent state in struct cli_state creates more complex code than necessary IMO. (This used to be commit debb37f703075008e5ea7d34d214cfa4d0f8f916) --- source3/libsmb/async_smb.c | 94 ++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 70 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 454bd8169b..e58b753da2 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -19,6 +19,9 @@ #include "includes.h" +static void cli_state_handler(struct event_context *event_ctx, + struct fd_event *event, uint16 flags, void *p); + /* * Fetch an error out of a NBT packet */ @@ -107,6 +110,9 @@ static int cli_request_destructor(struct cli_request *req) common_free_enc_buffer(req->enc_state, req->outbuf); } DLIST_REMOVE(req->cli->outstanding_requests, req); + if (req->cli->outstanding_requests == NULL) { + TALLOC_FREE(req->cli->fd_event); + } return 0; } @@ -163,7 +169,9 @@ static struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, /* * Ship a new smb request to the server */ -struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, +struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint8_t smb_command, uint8_t additional_flags, uint8_t wct, const uint16_t *vwv, @@ -172,10 +180,20 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, struct async_req *result; struct cli_request *req; - result = cli_request_new(mem_ctx, cli->event_ctx, cli, wct, num_bytes, - &req); + if (cli->fd_event == NULL) { + SMB_ASSERT(cli->outstanding_requests == NULL); + cli->fd_event = event_add_fd(ev, cli, cli->fd, + EVENT_FD_READ, + cli_state_handler, cli); + if (cli->fd_event == NULL) { + return NULL; + } + } + + result = cli_request_new(mem_ctx, ev, cli, wct, num_bytes, &req); if (result == NULL) { DEBUG(0, ("cli_request_new failed\n")); + TALLOC_FREE(cli->fd_event); return NULL; } @@ -446,7 +464,9 @@ static void cli_state_handler(struct event_context *event_ctx, } if (req == NULL) { - event_fd_set_not_writeable(event); + if (cli->fd_event != NULL) { + event_fd_set_not_writeable(cli->fd_event); + } return; } @@ -474,69 +494,3 @@ static void cli_state_handler(struct event_context *event_ctx, close(cli->fd); cli->fd = -1; } - -/* - * Holder for a talloc_destructor, we need to zero out the pointers in cli - * when deleting - */ -struct cli_tmp_event { - struct cli_state *cli; -}; - -static int cli_tmp_event_destructor(struct cli_tmp_event *e) -{ - TALLOC_FREE(e->cli->fd_event); - TALLOC_FREE(e->cli->event_ctx); - return 0; -} - -/* - * Create a temporary event context for use in the sync helper functions - */ - -struct cli_tmp_event *cli_tmp_event_ctx(TALLOC_CTX *mem_ctx, - struct cli_state *cli) -{ - struct cli_tmp_event *state; - - if (cli->event_ctx != NULL) { - return NULL; - } - - state = talloc(mem_ctx, struct cli_tmp_event); - if (state == NULL) { - return NULL; - } - state->cli = cli; - talloc_set_destructor(state, cli_tmp_event_destructor); - - cli->event_ctx = event_context_init(state); - if (cli->event_ctx == NULL) { - TALLOC_FREE(state); - return NULL; - } - - cli->fd_event = event_add_fd(cli->event_ctx, state, cli->fd, - EVENT_FD_READ, cli_state_handler, cli); - if (cli->fd_event == NULL) { - TALLOC_FREE(state); - return NULL; - } - return state; -} - -/* - * Attach an event context permanently to a cli_struct - */ - -NTSTATUS cli_add_event_ctx(struct cli_state *cli, - struct event_context *event_ctx) -{ - cli->event_ctx = event_ctx; - cli->fd_event = event_add_fd(event_ctx, cli, cli->fd, EVENT_FD_READ, - cli_state_handler, cli); - if (cli->fd_event == NULL) { - return NT_STATUS_NO_MEMORY; - } - return NT_STATUS_OK; -} -- cgit From 128524930d490fb5ea637d99bffb36157c80869b Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 25 Aug 2008 13:33:41 +0200 Subject: Add cli_pull_reply Along the lines of cli_request_send this abstracts away the smb-level buffer handling when parsing replies we got from the server. (This used to be commit 253134d3aaa359fdfb665709dd5686f69af7f8fd) --- source3/libsmb/async_smb.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index e58b753da2..32f0e8abd6 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -230,6 +230,60 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, return result; } +/** + * @brief Pull reply data out of a request + * @param[in] req The request that we just received a reply for + * @param[out] pwct How many words did the server send? + * @param[out] pvwv The words themselves + * @param[out] pnum_bytes How many bytes did the server send? + * @param[out] pbytes The bytes themselves + * @retval Was the reply formally correct? + */ + +NTSTATUS cli_pull_reply(struct async_req *req, + uint8_t *pwct, uint16_t **pvwv, + uint16_t *pnum_bytes, uint8_t **pbytes) +{ + struct cli_request *cli_req = cli_request_get(req); + uint8_t wct, cmd; + uint16_t num_bytes; + size_t wct_ofs, bytes_offset; + NTSTATUS status; + + status = cli_pull_error(cli_req->inbuf); + + if (NT_STATUS_IS_ERR(status)) { + cli_set_error(cli_req->cli, status); + return status; + } + + cmd = CVAL(cli_req->inbuf, smb_com); + wct_ofs = smb_wct; + + wct = CVAL(cli_req->inbuf, wct_ofs); + + bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t); + num_bytes = SVAL(cli_req->inbuf, bytes_offset); + + /* + * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes + * is a 16-bit value. So bytes_offset being size_t should be far from + * wrapping. + */ + + if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf)) + || (bytes_offset > 0xffff)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + *pwct = wct; + *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1); + *pnum_bytes = num_bytes; + *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2; + + return NT_STATUS_OK; +} + /* * Convenience function to get the SMB part out of an async_req */ -- cgit From 77d1b29e25512982a1f912adac46bb954ee3d19f Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 25 Aug 2008 14:40:15 +0200 Subject: Move "struct cli_request" from client.h to async_smb.h Also add some comments (This used to be commit 2ecc311f785317caf5b60051147dcd085c80d64f) --- source3/libsmb/async_smb.c | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 32f0e8abd6..e36e514a16 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -22,8 +22,10 @@ static void cli_state_handler(struct event_context *event_ctx, struct fd_event *event, uint16 flags, void *p); -/* +/** * Fetch an error out of a NBT packet + * @param[in] buf The SMB packet + * @retval The error, converted to NTSTATUS */ NTSTATUS cli_pull_error(char *buf) @@ -43,8 +45,10 @@ NTSTATUS cli_pull_error(char *buf) return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err)); } -/* +/** * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf + * @param[in] cli The client connection that just received an error + * @param[in] status The error to set on "cli" */ void cli_set_error(struct cli_state *cli, NTSTATUS status) @@ -64,8 +68,10 @@ void cli_set_error(struct cli_state *cli, NTSTATUS status) return; } -/* +/** * Allocate a new mid + * @param[in] cli The client connection + * @retval The new, unused mid */ static uint16_t cli_new_mid(struct cli_state *cli) @@ -91,6 +97,13 @@ static uint16_t cli_new_mid(struct cli_state *cli) } } +/** + * Print an async req that happens to be a cli_request + * @param[in] mem_ctx The TALLOC_CTX to put the result on + * @param[in] req The request to print + * @retval The string representation of "req" + */ + static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req) { char *result = async_req_print(mem_ctx, req); @@ -104,6 +117,12 @@ static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req) result, "mid=%d\n", cli_req->mid); } +/** + * Destroy a cli_request + * @param[in] req The cli_request to kill + * @retval Can't fail + */ + static int cli_request_destructor(struct cli_request *req) { if (req->enc_state != NULL) { @@ -284,8 +303,10 @@ NTSTATUS cli_pull_reply(struct async_req *req, return NT_STATUS_OK; } -/* +/** * Convenience function to get the SMB part out of an async_req + * @param[in] req The request to look at + * @retval The private_data as struct cli_request */ struct cli_request *cli_request_get(struct async_req *req) @@ -296,8 +317,9 @@ struct cli_request *cli_request_get(struct async_req *req) return talloc_get_type_abort(req->private_data, struct cli_request); } -/* +/** * A PDU has arrived on cli->evt_inbuf + * @param[in] cli The cli_state that received something */ static void handle_incoming_pdu(struct cli_state *cli) @@ -434,8 +456,12 @@ static void handle_incoming_pdu(struct cli_state *cli) return; } -/* +/** * fd event callback. This is the basic connection to the socket + * @param[in] event_ctx The event context that called us + * @param[in] event The event that fired + * @param[in] flags EVENT_FD_READ | EVENT_FD_WRITE + * @param[in] p private_data, in this case the cli_state */ static void cli_state_handler(struct event_context *event_ctx, -- cgit From 65dcdf9c32e8ac43344b23db528206c02fcb967c Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 25 Aug 2008 15:56:26 +0200 Subject: This adds the code to allow chained requests in libsmb/ This is not compiled yet, but it makes the patches much easier to read if it is add in bulk. (This used to be commit b4c539ba041bab8856c83816f08a35b5f5b21740) --- source3/libsmb/async_smb.c | 534 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 534 insertions(+) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index e36e514a16..e764147432 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -135,6 +135,540 @@ static int cli_request_destructor(struct cli_request *req) return 0; } +#if 0 + +/** + * Is the SMB command able to hold an AND_X successor + * @param[in] cmd The SMB command in question + * @retval Can we add a chained request after "cmd"? + */ + +static bool is_andx_req(uint8_t cmd) +{ + switch (cmd) { + case SMBtconX: + case SMBlockingX: + case SMBopenX: + case SMBreadX: + case SMBwriteX: + case SMBsesssetupX: + case SMBulogoffX: + case SMBntcreateX: + return true; + break; + default: + break; + } + + return false; +} + +/** + * @brief Find the smb_cmd offset of the last command pushed + * @param[in] buf The buffer we're building up + * @retval Where can we put our next andx cmd? + * + * While chaining requests, the "next" request we're looking at needs to put + * its SMB_Command before the data the previous request already built up added + * to the chain. Find the offset to the place where we have to put our cmd. + */ + +static bool find_andx_cmd_ofs(char *buf, size_t *pofs) +{ + uint8_t cmd; + size_t ofs; + + cmd = CVAL(buf, smb_com); + + SMB_ASSERT(is_andx_req(cmd)); + + ofs = smb_vwv0; + + while (CVAL(buf, ofs) != 0xff) { + + if (!is_andx_req(CVAL(buf, ofs))) { + return false; + } + + /* + * ofs is from start of smb header, so add the 4 length + * bytes. The next cmd is right after the wct field. + */ + ofs = SVAL(buf, ofs+2) + 4 + 1; + + SMB_ASSERT(ofs+4 < talloc_get_size(buf)); + } + + *pofs = ofs; + return true; +} + +/** + * @brief Destroy an async_req that is the visible part of a cli_request + * @param[in] req The request to kill + * @retval Return 0 to make talloc happy + * + * This destructor is a bit tricky: Because a cli_request can host more than + * one async_req for chained requests, we need to make sure that the + * "cli_request" that we were part of is correctly destroyed at the right + * time. This is done by NULLing out ourself from the "async" member of our + * "cli_request". If there is none left, then also TALLOC_FREE() the + * cli_request, which was a talloc child of the client connection cli_state. + */ + +static int cli_async_req_destructor(struct async_req *req) +{ + struct cli_request *cli_req = cli_request_get(req); + int i, pending; + bool found = false; + + pending = 0; + + for (i=0; inum_async; i++) { + if (cli_req->async[i] == req) { + cli_req->async[i] = NULL; + found = true; + } + if (cli_req->async[i] != NULL) { + pending += 1; + } + } + + SMB_ASSERT(found); + + if (pending == 0) { + TALLOC_FREE(cli_req); + } + + return 0; +} + +/** + * @brief Chain up a request + * @param[in] mem_ctx The TALLOC_CTX for the result + * @param[in] ev The event context that will call us back + * @param[in] cli The cli_state we queue the request up for + * @param[in] smb_command The command that we want to issue + * @param[in] additional_flags open_and_x wants to add oplock header flags + * @param[in] wct How many words? + * @param[in] vwv The words, already in network order + * @param[in] num_bytes How many bytes? + * @param[in] bytes The data the request ships + * + * cli_request_chain() is the core of the SMB request marshalling routine. It + * will create a new async_req structure in the cli->chain_accumulator->async + * array and marshall the smb_cmd, the vwv array and the bytes into + * cli->chain_accumulator->outbuf. + */ + +static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint8_t wct, const uint16_t *vwv, + uint16_t num_bytes, + const uint8_t *bytes) +{ + struct async_req **tmp_reqs; + char *tmp_buf; + struct cli_request *req; + size_t old_size, new_size; + size_t ofs; + + req = cli->chain_accumulator; + + tmp_reqs = TALLOC_REALLOC_ARRAY(req, req->async, struct async_req *, + req->num_async + 1); + if (tmp_reqs == NULL) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + req->async = tmp_reqs; + req->num_async += 1; + + req->async[req->num_async-1] = async_req_new(mem_ctx, ev); + if (req->async[req->num_async-1] == NULL) { + DEBUG(0, ("async_req_new failed\n")); + req->num_async -= 1; + return NULL; + } + req->async[req->num_async-1]->private_data = req; + req->async[req->num_async-1]->print = cli_request_print; + talloc_set_destructor(req->async[req->num_async-1], + cli_async_req_destructor); + + old_size = talloc_get_size(req->outbuf); + + /* + * We need space for the wct field, the words, the byte count field + * and the bytes themselves. + */ + new_size = old_size + 1 + wct * sizeof(uint16_t) + 2 + num_bytes; + + if (new_size > 0xffff) { + DEBUG(1, ("cli_request_chain: %u bytes won't fit\n", + (unsigned)new_size)); + goto fail; + } + + tmp_buf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, char, new_size); + if (tmp_buf == NULL) { + DEBUG(0, ("talloc failed\n")); + goto fail; + } + req->outbuf = tmp_buf; + + if (old_size == smb_wct) { + SCVAL(req->outbuf, smb_com, smb_command); + } else { + size_t andx_cmd_ofs; + if (!find_andx_cmd_ofs(req->outbuf, &andx_cmd_ofs)) { + DEBUG(1, ("invalid command chain\n")); + goto fail; + } + SCVAL(req->outbuf, andx_cmd_ofs, smb_command); + SSVAL(req->outbuf, andx_cmd_ofs + 2, old_size - 4); + } + + ofs = old_size; + + SCVAL(req->outbuf, ofs, wct); + ofs += 1; + + memcpy(req->outbuf + ofs, vwv, sizeof(uint16_t) * wct); + ofs += sizeof(uint16_t) * wct; + + SSVAL(req->outbuf, ofs, num_bytes); + ofs += sizeof(uint16_t); + + memcpy(req->outbuf + ofs, bytes, num_bytes); + + return req->async[req->num_async-1]; + + fail: + TALLOC_FREE(req->async[req->num_async-1]); + req->num_async -= 1; + return NULL; +} + +/** + * @brief prepare a cli_state to accept a chain of requests + * @param[in] cli The cli_state we want to queue up in + * @param[in] ev The event_context that will call us back for the socket + * @param[in] size_hint How many bytes are expected, just an optimization + * @retval Did we have enough memory? + * + * cli_chain_cork() sets up a new cli_request in cli->chain_accumulator. If + * cli is used in an async fashion, i.e. if we have outstanding requests, then + * we do not have to create a fd event. If cli is used only with the sync + * helpers, we need to create the fd_event here. + * + * If you want to issue a chained request to the server, do a + * cli_chain_cork(), then do you cli_open_send(), cli_read_and_x_send(), + * cli_close_send() and so on. The async requests that come out of + * cli_xxx_send() are normal async requests with the difference that they + * won't be shipped individually. But the event_context will still trigger the + * req->async.fn to be called on every single request. + * + * You have to take care yourself that you only issue chainable requests in + * the middle of the chain. + */ + +bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, + size_t size_hint) +{ + struct cli_request *req = NULL; + + SMB_ASSERT(cli->chain_accumulator == NULL); + + if (cli->fd_event == NULL) { + SMB_ASSERT(cli->outstanding_requests == NULL); + cli->fd_event = event_add_fd(ev, cli, cli->fd, + EVENT_FD_READ, + cli_state_handler, cli); + if (cli->fd_event == NULL) { + return false; + } + } + + req = talloc(cli, struct cli_request); + if (req == NULL) { + goto fail; + } + req->cli = cli; + + if (size_hint == 0) { + size_hint = 100; + } + req->outbuf = talloc_array(req, char, smb_wct + size_hint); + if (req->outbuf == NULL) { + goto fail; + } + req->outbuf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, char, smb_wct); + + req->num_async = 0; + req->async = NULL; + + req->enc_state = NULL; + + SSVAL(req->outbuf, smb_tid, cli->cnum); + cli_setup_packet_buf(cli, req->outbuf); + + req->mid = cli_new_mid(cli); + SSVAL(req->outbuf, smb_mid, req->mid); + + cli->chain_accumulator = req; + + DEBUG(10, ("cli_chain_cork: mid=%d\n", req->mid)); + + return true; + fail: + TALLOC_FREE(req); + if (cli->outstanding_requests == NULL) { + TALLOC_FREE(cli->fd_event); + } + return false; +} + +/** + * Ship a request queued up via cli_request_chain() + * @param[in] cl The connection + */ + +void cli_chain_uncork(struct cli_state *cli) +{ + struct cli_request *req = cli->chain_accumulator; + + SMB_ASSERT(req != NULL); + + DLIST_ADD_END(cli->outstanding_requests, req, struct cli_request *); + talloc_set_destructor(req, cli_request_destructor); + + cli->chain_accumulator = NULL; + + smb_setlen(req->outbuf, talloc_get_size(req->outbuf) - 4); + + cli_calculate_sign_mac(cli, req->outbuf); + + if (cli_encryption_on(cli)) { + NTSTATUS status; + char *enc_buf; + + status = cli_encrypt_message(cli, req->outbuf, &enc_buf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Error in encrypting client message. " + "Error %s\n", nt_errstr(status))); + TALLOC_FREE(req); + return; + } + req->outbuf = enc_buf; + req->enc_state = cli->trans_enc_state; + } + + req->sent = 0; + + event_fd_set_writeable(cli->fd_event); +} + +/** + * @brief Send a request to the server + * @param[in] mem_ctx The TALLOC_CTX for the result + * @param[in] ev The event context that will call us back + * @param[in] cli The cli_state we queue the request up for + * @param[in] smb_command The command that we want to issue + * @param[in] additional_flags open_and_x wants to add oplock header flags + * @param[in] wct How many words? + * @param[in] vwv The words, already in network order + * @param[in] num_bytes How many bytes? + * @param[in] bytes The data the request ships + * + * This is the generic routine to be used by the cli_xxx_send routines. + */ + +struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint8_t wct, const uint16_t *vwv, + uint16_t num_bytes, const uint8_t *bytes) +{ + struct async_req *result; + bool uncork = false; + + if (cli->chain_accumulator == NULL) { + if (!cli_chain_cork(cli, ev, + wct * sizeof(uint16_t) + num_bytes + 3)) { + DEBUG(1, ("cli_chain_cork failed\n")); + return NULL; + } + uncork = true; + } + + result = cli_request_chain(mem_ctx, ev, cli, smb_command, + additional_flags, wct, vwv, + num_bytes, bytes); + + if (result == NULL) { + DEBUG(1, ("cli_request_chain failed\n")); + } + + if (uncork) { + cli_chain_uncork(cli); + } + + return result; +} + +/** + * Figure out if there is an andx command behind the current one + * @param[in] buf The smb buffer to look at + * @param[in] ofs The offset to the wct field that is followed by the cmd + * @retval Is there a command following? + */ + +static bool have_andx_command(const char *buf, uint16_t ofs) +{ + uint8_t wct; + size_t buflen = talloc_get_size(buf); + + if ((ofs == buflen-1) || (ofs == buflen)) { + return false; + } + + wct = CVAL(buf, ofs); + if (wct < 2) { + /* + * Not enough space for the command and a following pointer + */ + return false; + } + return (CVAL(buf, ofs+1) != 0xff); +} + +/** + * @brief Pull reply data out of a request + * @param[in] req The request that we just received a reply for + * @param[out] pwct How many words did the server send? + * @param[out] pvwv The words themselves + * @param[out] pnum_bytes How many bytes did the server send? + * @param[out] pbytes The bytes themselves + * @retval Was the reply formally correct? + */ + +NTSTATUS cli_pull_reply(struct async_req *req, + uint8_t *pwct, uint16_t **pvwv, + uint16_t *pnum_bytes, uint8_t **pbytes) +{ + struct cli_request *cli_req = cli_request_get(req); + uint8_t wct, cmd; + uint16_t num_bytes; + size_t wct_ofs, bytes_offset; + int i, j; + NTSTATUS status; + + for (i = 0; i < cli_req->num_async; i++) { + if (req == cli_req->async[i]) { + break; + } + } + + if (i == cli_req->num_async) { + cli_set_error(cli_req->cli, NT_STATUS_INVALID_PARAMETER); + return NT_STATUS_INVALID_PARAMETER; + } + + /** + * The status we pull here is only relevant for the last reply in the + * chain. + */ + + status = cli_pull_error(cli_req->inbuf); + + if (i == 0) { + if (NT_STATUS_IS_ERR(status) + && !have_andx_command(cli_req->inbuf, smb_wct)) { + cli_set_error(cli_req->cli, status); + return status; + } + wct_ofs = smb_wct; + goto done; + } + + cmd = CVAL(cli_req->inbuf, smb_com); + wct_ofs = smb_wct; + + for (j = 0; j < i; j++) { + if (j < i-1) { + if (cmd == 0xff) { + return NT_STATUS_REQUEST_ABORTED; + } + if (!is_andx_req(cmd)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + } + + if (!have_andx_command(cli_req->inbuf, wct_ofs)) { + /* + * This request was not completed because a previous + * request in the chain had received an error. + */ + return NT_STATUS_REQUEST_ABORTED; + } + + wct_ofs = SVAL(cli_req->inbuf, wct_ofs + 3); + + /* + * Skip the all-present length field. No overflow, we've just + * put a 16-bit value into a size_t. + */ + wct_ofs += 4; + + if (wct_ofs+2 > talloc_get_size(cli_req->inbuf)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + cmd = CVAL(cli_req->inbuf, wct_ofs + 1); + } + + if (!have_andx_command(cli_req->inbuf, wct_ofs) + && NT_STATUS_IS_ERR(status)) { + /* + * The last command takes the error code. All further commands + * down the requested chain will get a + * NT_STATUS_REQUEST_ABORTED. + */ + return status; + } + + done: + wct = CVAL(cli_req->inbuf, wct_ofs); + + bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t); + num_bytes = SVAL(cli_req->inbuf, bytes_offset); + + /* + * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes + * is a 16-bit value. So bytes_offset being size_t should be far from + * wrapping. + */ + + if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf)) + || (bytes_offset > 0xffff)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + *pwct = wct; + *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1); + *pnum_bytes = num_bytes; + *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2; + + return NT_STATUS_OK; +} + +#endif + /* * Create a fresh async smb request */ -- cgit From b054f14111337c826548d7728dc2b0a66ab5beae Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 25 Aug 2008 15:59:36 +0200 Subject: Activate code to enable chained requests Add the CHAIN1 torture test (This used to be commit 82992d74a99b056bbfe90e1b79190e0b7c0bf2bd) --- source3/libsmb/async_smb.c | 203 ++++++--------------------------------------- 1 file changed, 27 insertions(+), 176 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index e764147432..4d6c32edfa 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -135,8 +135,6 @@ static int cli_request_destructor(struct cli_request *req) return 0; } -#if 0 - /** * Is the SMB command able to hold an AND_X successor * @param[in] cmd The SMB command in question @@ -667,176 +665,6 @@ NTSTATUS cli_pull_reply(struct async_req *req, return NT_STATUS_OK; } -#endif - -/* - * Create a fresh async smb request - */ - -static struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint8_t num_words, size_t num_bytes, - struct cli_request **preq) -{ - struct async_req *result; - struct cli_request *cli_req; - size_t bufsize = smb_size + num_words * 2 + num_bytes; - - result = async_req_new(mem_ctx, ev); - if (result == NULL) { - return NULL; - } - - cli_req = (struct cli_request *)talloc_size( - result, sizeof(*cli_req) + bufsize); - if (cli_req == NULL) { - TALLOC_FREE(result); - return NULL; - } - talloc_set_name_const(cli_req, "struct cli_request"); - result->private_data = cli_req; - result->print = cli_request_print; - - cli_req->async = result; - cli_req->cli = cli; - cli_req->outbuf = ((char *)cli_req + sizeof(*cli_req)); - cli_req->sent = 0; - cli_req->mid = cli_new_mid(cli); - cli_req->inbuf = NULL; - cli_req->enc_state = NULL; - - SCVAL(cli_req->outbuf, smb_wct, num_words); - SSVAL(cli_req->outbuf, smb_vwv + num_words * 2, num_bytes); - - DLIST_ADD_END(cli->outstanding_requests, cli_req, - struct cli_request *); - talloc_set_destructor(cli_req, cli_request_destructor); - - DEBUG(10, ("cli_request_new: mid=%d\n", cli_req->mid)); - - *preq = cli_req; - return result; -} - -/* - * Ship a new smb request to the server - */ -struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint8_t smb_command, - uint8_t additional_flags, - uint8_t wct, const uint16_t *vwv, - uint16_t num_bytes, const uint8_t *bytes) -{ - struct async_req *result; - struct cli_request *req; - - if (cli->fd_event == NULL) { - SMB_ASSERT(cli->outstanding_requests == NULL); - cli->fd_event = event_add_fd(ev, cli, cli->fd, - EVENT_FD_READ, - cli_state_handler, cli); - if (cli->fd_event == NULL) { - return NULL; - } - } - - result = cli_request_new(mem_ctx, ev, cli, wct, num_bytes, &req); - if (result == NULL) { - DEBUG(0, ("cli_request_new failed\n")); - TALLOC_FREE(cli->fd_event); - return NULL; - } - - cli_set_message(req->outbuf, wct, num_bytes, false); - SCVAL(req->outbuf, smb_com, smb_command); - SSVAL(req->outbuf, smb_tid, cli->cnum); - cli_setup_packet_buf(cli, req->outbuf); - - memcpy(req->outbuf + smb_vwv0, vwv, sizeof(uint16_t) * wct); - memcpy(smb_buf(req->outbuf), bytes, num_bytes); - SSVAL(req->outbuf, smb_mid, req->mid); - SCVAL(cli->outbuf, smb_flg, - CVAL(cli->outbuf,smb_flg) | additional_flags); - - cli_calculate_sign_mac(cli, req->outbuf); - - if (cli_encryption_on(cli)) { - NTSTATUS status; - char *enc_buf; - - status = cli_encrypt_message(cli, req->outbuf, &enc_buf); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Error in encrypting client message. " - "Error %s\n", nt_errstr(status))); - TALLOC_FREE(req); - return NULL; - } - req->outbuf = enc_buf; - req->enc_state = cli->trans_enc_state; - } - - event_fd_set_writeable(cli->fd_event); - - return result; -} - -/** - * @brief Pull reply data out of a request - * @param[in] req The request that we just received a reply for - * @param[out] pwct How many words did the server send? - * @param[out] pvwv The words themselves - * @param[out] pnum_bytes How many bytes did the server send? - * @param[out] pbytes The bytes themselves - * @retval Was the reply formally correct? - */ - -NTSTATUS cli_pull_reply(struct async_req *req, - uint8_t *pwct, uint16_t **pvwv, - uint16_t *pnum_bytes, uint8_t **pbytes) -{ - struct cli_request *cli_req = cli_request_get(req); - uint8_t wct, cmd; - uint16_t num_bytes; - size_t wct_ofs, bytes_offset; - NTSTATUS status; - - status = cli_pull_error(cli_req->inbuf); - - if (NT_STATUS_IS_ERR(status)) { - cli_set_error(cli_req->cli, status); - return status; - } - - cmd = CVAL(cli_req->inbuf, smb_com); - wct_ofs = smb_wct; - - wct = CVAL(cli_req->inbuf, wct_ofs); - - bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t); - num_bytes = SVAL(cli_req->inbuf, bytes_offset); - - /* - * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes - * is a 16-bit value. So bytes_offset being size_t should be far from - * wrapping. - */ - - if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf)) - || (bytes_offset > 0xffff)) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - *pwct = wct; - *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1); - *pnum_bytes = num_bytes; - *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2; - - return NT_STATUS_OK; -} - /** * Convenience function to get the SMB part out of an async_req * @param[in] req The request to look at @@ -862,8 +690,11 @@ static void handle_incoming_pdu(struct cli_state *cli) uint16_t mid; size_t raw_pdu_len, buf_len, pdu_len, rest_len; char *pdu; + int i; NTSTATUS status; + int num_async; + /* * The encrypted PDU len might differ from the unencrypted one */ @@ -976,7 +807,24 @@ static void handle_incoming_pdu(struct cli_state *cli) req->inbuf = talloc_move(req, &pdu); - async_req_done(req->async); + /* + * Freeing the last async_req will free the req (see + * cli_async_req_destructor). So make a copy of req->num_async, we + * can't reference it in the last round. + */ + + num_async = req->num_async; + + for (i=0; iasync via its + * destructor cli_async_req_destructor(). + */ + if (req->async[i] != NULL) { + async_req_done(req->async[i]); + } + } return; invalidate_requests: @@ -985,7 +833,7 @@ static void handle_incoming_pdu(struct cli_state *cli) nt_errstr(status))); for (req = cli->outstanding_requests; req; req = req->next) { - async_req_error(req->async, status); + async_req_error(req->async[0], status); } return; } @@ -1101,8 +949,11 @@ static void cli_state_handler(struct event_context *event_ctx, sock_error: for (req = cli->outstanding_requests; req; req = req->next) { - req->async->state = ASYNC_REQ_ERROR; - req->async->status = map_nt_error_from_unix(errno); + int i; + for (i=0; inum_async; i++) { + req->async[i]->state = ASYNC_REQ_ERROR; + req->async[i]->status = map_nt_error_from_unix(errno); + } } TALLOC_FREE(cli->fd_event); close(cli->fd); -- cgit From bb0fc9cfceab7e961eaa9049d111121609ff8174 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 27 Aug 2008 19:26:40 +0200 Subject: Add cli_request->recv_helper Necessary for requests with multiple replies (This used to be commit cb2e338eb33dfb4627f9b43456af0c86d7d268c6) --- source3/libsmb/async_smb.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 4d6c32edfa..b5fa9c44b1 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -409,6 +409,7 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, req->async = NULL; req->enc_state = NULL; + req->recv_helper.fn = NULL; SSVAL(req->outbuf, smb_tid, cli->cnum); cli_setup_packet_buf(cli, req->outbuf); @@ -822,7 +823,11 @@ static void handle_incoming_pdu(struct cli_state *cli) * destructor cli_async_req_destructor(). */ if (req->async[i] != NULL) { - async_req_done(req->async[i]); + if (req->recv_helper.fn != NULL) { + req->recv_helper.fn(req->async[i]); + } else { + async_req_done(req->async[i]); + } } } return; -- cgit From f294f51bf0d136208fee1be343684ea890a499d0 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 28 Aug 2008 15:44:14 +0200 Subject: Remove cli_request_get() req->private_data==NULL at this point is definitely a bug. (This used to be commit ce3dc9f616cafc1289a94ac7cae0beca967d836e) --- source3/libsmb/async_smb.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index b5fa9c44b1..79a924b9db 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -107,7 +107,8 @@ static uint16_t cli_new_mid(struct cli_state *cli) static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req) { char *result = async_req_print(mem_ctx, req); - struct cli_request *cli_req = cli_request_get(req); + struct cli_request *cli_req = talloc_get_type_abort( + req->private_data, struct cli_request); if (result == NULL) { return NULL; @@ -216,7 +217,8 @@ static bool find_andx_cmd_ofs(char *buf, size_t *pofs) static int cli_async_req_destructor(struct async_req *req) { - struct cli_request *cli_req = cli_request_get(req); + struct cli_request *cli_req = talloc_get_type_abort( + req->private_data, struct cli_request); int i, pending; bool found = false; @@ -560,7 +562,8 @@ NTSTATUS cli_pull_reply(struct async_req *req, uint8_t *pwct, uint16_t **pvwv, uint16_t *pnum_bytes, uint8_t **pbytes) { - struct cli_request *cli_req = cli_request_get(req); + struct cli_request *cli_req = talloc_get_type_abort( + req->private_data, struct cli_request); uint8_t wct, cmd; uint16_t num_bytes; size_t wct_ofs, bytes_offset; @@ -666,20 +669,6 @@ NTSTATUS cli_pull_reply(struct async_req *req, return NT_STATUS_OK; } -/** - * Convenience function to get the SMB part out of an async_req - * @param[in] req The request to look at - * @retval The private_data as struct cli_request - */ - -struct cli_request *cli_request_get(struct async_req *req) -{ - if (req == NULL) { - return NULL; - } - return talloc_get_type_abort(req->private_data, struct cli_request); -} - /** * A PDU has arrived on cli->evt_inbuf * @param[in] cli The cli_state that received something -- cgit From f912ac962a0e0a79496178227e2a6614f700dde3 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 5 Sep 2008 11:48:36 +0200 Subject: Do proper error handling if the socket is closed (This used to be commit e5a27773f97d7017cfa345799c6803fd82c8e797) --- source3/libsmb/async_smb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 79a924b9db..a4c58aa5c7 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -845,6 +845,7 @@ static void cli_state_handler(struct event_context *event_ctx, { struct cli_state *cli = (struct cli_state *)p; struct cli_request *req; + NTSTATUS status; DEBUG(11, ("cli_state_handler called with flags %d\n", flags)); @@ -857,11 +858,13 @@ static void cli_state_handler(struct event_context *event_ctx, if (res == -1) { DEBUG(10, ("ioctl(FIONREAD) failed: %s\n", strerror(errno))); + status = map_nt_error_from_unix(errno); goto sock_error; } if (available == 0) { /* EOF */ + status = NT_STATUS_END_OF_FILE; goto sock_error; } @@ -870,6 +873,7 @@ static void cli_state_handler(struct event_context *event_ctx, if (new_size < old_size) { /* wrap */ + status = NT_STATUS_UNEXPECTED_IO_ERROR; goto sock_error; } @@ -877,6 +881,7 @@ static void cli_state_handler(struct event_context *event_ctx, new_size); if (tmp == NULL) { /* nomem */ + status = NT_STATUS_NO_MEMORY; goto sock_error; } cli->evt_inbuf = tmp; @@ -884,6 +889,7 @@ static void cli_state_handler(struct event_context *event_ctx, res = recv(cli->fd, cli->evt_inbuf + old_size, available, 0); if (res == -1) { DEBUG(10, ("recv failed: %s\n", strerror(errno))); + status = map_nt_error_from_unix(errno); goto sock_error; } @@ -930,6 +936,7 @@ static void cli_state_handler(struct event_context *event_ctx, to_send - req->sent, 0); if (sent < 0) { + status = map_nt_error_from_unix(errno); goto sock_error; } @@ -945,8 +952,7 @@ static void cli_state_handler(struct event_context *event_ctx, for (req = cli->outstanding_requests; req; req = req->next) { int i; for (i=0; inum_async; i++) { - req->async[i]->state = ASYNC_REQ_ERROR; - req->async[i]->status = map_nt_error_from_unix(errno); + async_req_error(req->async[i], status); } } TALLOC_FREE(cli->fd_event); -- cgit From 60255c3b2e557a233011ea02c761faee0dd3bc7f Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 5 Sep 2008 11:47:48 +0200 Subject: Protect against a closed socket (This used to be commit d6cb5fdafbddb08d32b788674eff509cae9525c6) --- source3/libsmb/async_smb.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index a4c58aa5c7..435c8c1cb9 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -382,6 +382,11 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, SMB_ASSERT(cli->chain_accumulator == NULL); + if (cli->fd == -1) { + DEBUG(10, ("cli->fd closed\n")); + return false; + } + if (cli->fd_event == NULL) { SMB_ASSERT(cli->outstanding_requests == NULL); cli->fd_event = event_add_fd(ev, cli, cli->fd, -- cgit From 6ba8c105c541636ee79e20eb7c5547ed6d8117e2 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 9 Sep 2008 14:37:17 +0200 Subject: Add utility function cli_in_chain() This gives a hint whether a function is called from within the middle of a chain. In particular the trans calls don't really like this. (This used to be commit 4252b32db5ef7483f2c5c52312b6e6dc68d1d687) --- source3/libsmb/async_smb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 435c8c1cb9..c875acbe48 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -136,6 +136,21 @@ static int cli_request_destructor(struct cli_request *req) return 0; } +/** + * Are there already requests waiting in the chain_accumulator? + * @param[in] cli The cli_state we want to check + * @retval reply :-) + */ + +bool cli_in_chain(struct cli_state *cli) +{ + if (cli->chain_accumulator == NULL) { + return false; + } + + return (cli->chain_accumulator->num_async != 0); +} + /** * Is the SMB command able to hold an AND_X successor * @param[in] cmd The SMB command in question -- cgit From 2a934529942feb1b9411b5351e27cb0b5718ea34 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 9 Sep 2008 14:39:45 +0200 Subject: Move setting the mid field in req->outbuf from _cork to _uncork The async trans calls need this, as for secondary trans calls they have to modify the MID from what cli_request_chain() gave us. (This used to be commit c85de4b7b5db8b54b8bf0f91acbd6d08d1b0bc9d) --- source3/libsmb/async_smb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index c875acbe48..eedc7d4481 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -437,7 +437,6 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, cli_setup_packet_buf(cli, req->outbuf); req->mid = cli_new_mid(cli); - SSVAL(req->outbuf, smb_mid, req->mid); cli->chain_accumulator = req; @@ -468,6 +467,7 @@ void cli_chain_uncork(struct cli_state *cli) cli->chain_accumulator = NULL; + SSVAL(req->outbuf, smb_mid, req->mid); smb_setlen(req->outbuf, talloc_get_size(req->outbuf) - 4); cli_calculate_sign_mac(cli, req->outbuf); -- cgit From f8f1679bc46a99462daf4336be5155db37482361 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Sep 2008 21:19:37 +0200 Subject: Add a paranoia check on incoming PDUs (This used to be commit 8b81b85200b7ca18cf81fdbbc3254d8578b35f43) --- source3/libsmb/async_smb.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index eedc7d4481..b4d93ff968 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -759,6 +759,13 @@ static void handle_incoming_pdu(struct cli_state *cli) } + if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ + && (IVAL(pdu, 4) != 0x424d45ff)) /* 0xFF"EMB" */ { + DEBUG(10, ("Got non-SMB PDU\n")); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto invalidate_requests; + } + /* * TODO: Handle oplock break requests */ -- cgit From d892b1c886e6fbdca485807aaac522312e0a6937 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Sep 2008 21:20:02 +0200 Subject: remove a pointless empty line (This used to be commit fba250ece45f6632c7d89b0ea28baab047e41a8f) --- source3/libsmb/async_smb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index b4d93ff968..79999b5654 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -756,7 +756,6 @@ static void handle_incoming_pdu(struct cli_state *cli) goto invalidate_requests; } } - } if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ -- cgit From ced409b5f754623652580b708943ead6d98adb6d Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Sep 2008 22:26:10 +0200 Subject: Fix "make test" -- gna... (This used to be commit c1d3ae80b5f5f07c5efcd7f3ee301d5c3090f3c6) --- source3/libsmb/async_smb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 79999b5654..c8d010a3fe 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -759,7 +759,7 @@ static void handle_incoming_pdu(struct cli_state *cli) } if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ - && (IVAL(pdu, 4) != 0x424d45ff)) /* 0xFF"EMB" */ { + && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ { DEBUG(10, ("Got non-SMB PDU\n")); status = NT_STATUS_INVALID_NETWORK_RESPONSE; goto invalidate_requests; -- cgit From ffbc38abe116fb6edc1739c6440207edc8230de9 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Sep 2008 23:05:51 +0200 Subject: Factor out validate_smb_crypto (This used to be commit 37fcc9dc462dfb006fdac294e49c0dae7588c103) --- source3/libsmb/async_smb.c | 94 ++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 41 deletions(-) (limited to 'source3/libsmb/async_smb.c') diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index c8d010a3fe..d5eac07b48 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -689,6 +689,57 @@ NTSTATUS cli_pull_reply(struct async_req *req, return NT_STATUS_OK; } +/** + * Decrypt a PDU, check the signature + * @param[in] cli The cli_state that received something + * @param[in] pdu The incoming bytes + * @retval error code + */ + + +static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu) +{ + NTSTATUS status; + + if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ + && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ { + DEBUG(10, ("Got non-SMB PDU\n")); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) { + uint16_t enc_ctx_num; + + status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("get_enc_ctx_num returned %s\n", + nt_errstr(status))); + return status; + } + + if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) { + DEBUG(10, ("wrong enc_ctx %d, expected %d\n", + enc_ctx_num, + cli->trans_enc_state->enc_ctx_num)); + return NT_STATUS_INVALID_HANDLE; + } + + status = common_decrypt_buffer(cli->trans_enc_state, pdu); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("common_decrypt_buffer returned %s\n", + nt_errstr(status))); + return status; + } + } + + if (!cli_check_sign_mac(cli, pdu)) { + DEBUG(10, ("cli_check_sign_mac failed\n")); + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} + /** * A PDU has arrived on cli->evt_inbuf * @param[in] cli The cli_state that received something @@ -758,47 +809,8 @@ static void handle_incoming_pdu(struct cli_state *cli) } } - if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ - && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ { - DEBUG(10, ("Got non-SMB PDU\n")); - status = NT_STATUS_INVALID_NETWORK_RESPONSE; - goto invalidate_requests; - } - - /* - * TODO: Handle oplock break requests - */ - - if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) { - uint16_t enc_ctx_num; - - status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("get_enc_ctx_num returned %s\n", - nt_errstr(status))); - goto invalidate_requests; - } - - if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) { - DEBUG(10, ("wrong enc_ctx %d, expected %d\n", - enc_ctx_num, - cli->trans_enc_state->enc_ctx_num)); - status = NT_STATUS_INVALID_HANDLE; - goto invalidate_requests; - } - - status = common_decrypt_buffer(cli->trans_enc_state, - pdu); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("common_decrypt_buffer returned %s\n", - nt_errstr(status))); - goto invalidate_requests; - } - } - - if (!cli_check_sign_mac(cli, pdu)) { - DEBUG(10, ("cli_check_sign_mac failed\n")); - status = NT_STATUS_ACCESS_DENIED; + status = validate_smb_crypto(cli, pdu); + if (!NT_STATUS_IS_OK(status)) { goto invalidate_requests; } -- cgit