X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source3%2Flibsmb%2Fclientgen.c;h=1122bbbaa507373960c6de2797a78b542b85c128;hb=ad0a07c531fadd1639c5298951cfaf5cfe0cb10e;hp=c1ba4e5c4f6909f325a10d73048573a3c06877c0;hpb=27c6eca04c4c1bb40ff36f3a08748e2f45770aa8;p=garming%2Fsamba-autobuild%2F.git diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index c1ba4e5c4f6..1122bbbaa50 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -19,6 +19,10 @@ */ #include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "smb_signing.h" +#include "async_smb.h" /******************************************************************* Setup the word count and byte count for a client smb message. @@ -64,298 +68,35 @@ bool cli_ucs2(struct cli_state *cli) return ((cli->capabilities & CAP_UNICODE) != 0); } - -/**************************************************************************** - Read an smb from a fd ignoring all keepalive packets. - The timeout is in milliseconds - - This is exactly the same as receive_smb except that it never returns - a session keepalive packet (just as receive_smb used to do). - receive_smb was changed to return keepalives as the oplock processing means this call - should never go into a blocking read. -****************************************************************************/ - -static ssize_t client_receive_smb(struct cli_state *cli, size_t maxlen) +bool cli_state_seqnum_persistent(struct cli_state *cli, + uint16_t mid) { - size_t len; - - for(;;) { - NTSTATUS status; - - set_smb_read_error(&cli->smb_rw_error, SMB_READ_OK); - - status = receive_smb_raw(cli->fd, cli->inbuf, cli->bufsize, - cli->timeout, maxlen, &len); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("client_receive_smb failed\n")); - show_msg(cli->inbuf); - - if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { - set_smb_read_error(&cli->smb_rw_error, - SMB_READ_EOF); - return -1; - } - - if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { - set_smb_read_error(&cli->smb_rw_error, - SMB_READ_TIMEOUT); - return -1; - } - - set_smb_read_error(&cli->smb_rw_error, SMB_READ_ERROR); - return -1; - } + struct cli_state_seqnum *c; - /* - * I don't believe len can be < 0 with NT_STATUS_OK - * returned above, but this check doesn't hurt. JRA. - */ - - if ((ssize_t)len < 0) { - return len; - } - - /* Ignore session keepalive packets. */ - if(CVAL(cli->inbuf,0) != SMBkeepalive) { - break; - } - } - - if (cli_encryption_on(cli)) { - NTSTATUS status = cli_decrypt_message(cli); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("SMB decryption failed on incoming packet! Error %s\n", - nt_errstr(status))); - cli->smb_rw_error = SMB_READ_BAD_DECRYPT; - return -1; - } - } - - show_msg(cli->inbuf); - return len; -} - -/**************************************************************************** - Recv an smb. -****************************************************************************/ - -bool cli_receive_smb(struct cli_state *cli) -{ - ssize_t len; - - /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ - if (cli->fd == -1) - return false; - - again: - len = client_receive_smb(cli, 0); - - if (len > 0) { - /* it might be an oplock break request */ - if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) && - CVAL(cli->inbuf,smb_com) == SMBlockingX && - SVAL(cli->inbuf,smb_vwv6) == 0 && - SVAL(cli->inbuf,smb_vwv7) == 0) { - if (cli->oplock_handler) { - int fnum = SVAL(cli->inbuf,smb_vwv2); - unsigned char level = CVAL(cli->inbuf,smb_vwv3+1); - if (!cli->oplock_handler(cli, fnum, level)) { - return false; - } - } - /* try to prevent loops */ - SCVAL(cli->inbuf,smb_com,0xFF); - goto again; - } - } - - /* If the server is not responding, note that now */ - if (len < 0) { - DEBUG(0, ("Receiving SMB: Server stopped responding\n")); - close(cli->fd); - cli->fd = -1; - return false; - } - - if (!cli_check_sign_mac(cli, cli->inbuf)) { - /* - * If we get a signature failure in sessionsetup, then - * the server sometimes just reflects the sent signature - * back to us. Detect this and allow the upper layer to - * retrieve the correct Windows error message. - */ - if (CVAL(cli->outbuf,smb_com) == SMBsesssetupX && - (smb_len(cli->inbuf) > (smb_ss_field + 8 - 4)) && - (SVAL(cli->inbuf,smb_flg2) & FLAGS2_SMB_SECURITY_SIGNATURES) && - memcmp(&cli->outbuf[smb_ss_field],&cli->inbuf[smb_ss_field],8) == 0 && - cli_is_error(cli)) { - - /* - * Reflected signature on login error. - * Set bad sig but don't close fd. - */ - cli->smb_rw_error = SMB_READ_BAD_SIG; + for (c = cli->seqnum; c; c = c->next) { + if (c->mid == mid) { + c->persistent = true; return true; } - - DEBUG(0, ("SMB Signature verification failed on incoming packet!\n")); - cli->smb_rw_error = SMB_READ_BAD_SIG; - close(cli->fd); - cli->fd = -1; - return false; - }; - return true; -} - -/**************************************************************************** - Read the data portion of a readX smb. - The timeout is in milliseconds -****************************************************************************/ - -ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len) -{ - NTSTATUS status; - - set_smb_read_error(&cli->smb_rw_error, SMB_READ_OK); - - status = read_socket_with_timeout( - cli->fd, buffer, len, len, cli->timeout, NULL); - if (NT_STATUS_IS_OK(status)) { - return len; - } - - if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { - set_smb_read_error(&cli->smb_rw_error, SMB_READ_EOF); - return -1; } - if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { - set_smb_read_error(&cli->smb_rw_error, SMB_READ_TIMEOUT); - return -1; - } - - set_smb_read_error(&cli->smb_rw_error, SMB_READ_ERROR); - return -1; + return false; } -static ssize_t write_socket(int fd, const char *buf, size_t len) +bool cli_state_seqnum_remove(struct cli_state *cli, + uint16_t mid) { - ssize_t ret=0; + struct cli_state_seqnum *c; - DEBUG(6,("write_socket(%d,%d)\n",fd,(int)len)); - ret = write_data(fd,buf,len); - - DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,(int)len,(int)ret)); - if(ret <= 0) - DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n", - (int)len, fd, strerror(errno) )); - - return(ret); -} - -/**************************************************************************** - Send an smb to a fd. -****************************************************************************/ - -bool cli_send_smb(struct cli_state *cli) -{ - size_t len; - size_t nwritten=0; - ssize_t ret; - char *buf_out = cli->outbuf; - bool enc_on = cli_encryption_on(cli); - - /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ - if (cli->fd == -1) - return false; - - cli_calculate_sign_mac(cli, cli->outbuf); - - if (enc_on) { - NTSTATUS status = cli_encrypt_message(cli, cli->outbuf, - &buf_out); - if (!NT_STATUS_IS_OK(status)) { - close(cli->fd); - cli->fd = -1; - cli->smb_rw_error = SMB_WRITE_ERROR; - DEBUG(0,("Error in encrypting client message. Error %s\n", - nt_errstr(status) )); - return false; - } - } - - len = smb_len(buf_out) + 4; - - while (nwritten < len) { - ret = write_socket(cli->fd,buf_out+nwritten,len - nwritten); - if (ret <= 0) { - if (enc_on) { - cli_free_enc_buffer(cli, buf_out); - } - close(cli->fd); - cli->fd = -1; - cli->smb_rw_error = SMB_WRITE_ERROR; - DEBUG(0,("Error writing %d bytes to client. %d (%s)\n", - (int)len,(int)ret, strerror(errno) )); - return false; + for (c = cli->seqnum; c; c = c->next) { + if (c->mid == mid) { + DLIST_REMOVE(cli->seqnum, c); + TALLOC_FREE(c); + return true; } - nwritten += ret; - } - - if (enc_on) { - cli_free_enc_buffer(cli, buf_out); - } - - /* Increment the mid so we can tell between responses. */ - cli->mid++; - if (!cli->mid) - cli->mid++; - return true; -} - -/**************************************************************************** - Send a "direct" writeX smb to a fd. -****************************************************************************/ - -bool cli_send_smb_direct_writeX(struct cli_state *cli, - const char *p, - size_t extradata) -{ - /* First length to send is the offset to the data. */ - size_t len = SVAL(cli->outbuf,smb_vwv11) + 4; - size_t nwritten=0; - struct iovec iov[2]; - - /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */ - if (cli->fd == -1) { - return false; } - if (client_is_signing_on(cli)) { - DEBUG(0,("cli_send_smb_large: cannot send signed packet.\n")); - return false; - } - - iov[0].iov_base = cli->outbuf; - iov[0].iov_len = len; - iov[1].iov_base = CONST_DISCARD(char *, p); - iov[1].iov_len = extradata; - - nwritten = write_data_iov(cli->fd, iov, 2); - if (nwritten < (len + extradata)) { - close(cli->fd); - cli->fd = -1; - cli->smb_rw_error = SMB_WRITE_ERROR; - DEBUG(0,("Error writing %d bytes to client. (%s)\n", - (int)(len+extradata), strerror(errno))); - return false; - } - - /* Increment the mid so we can tell between responses. */ - cli->mid++; - if (!cli->mid) - cli->mid++; - return true; + return false; } /**************************************************************************** @@ -394,20 +135,6 @@ void cli_setup_packet_buf(struct cli_state *cli, char *buf) SSVAL(buf,smb_flg2, flags2); } -void cli_setup_packet(struct cli_state *cli) -{ - cli_setup_packet_buf(cli, cli->outbuf); -} - -/**************************************************************************** - Setup the bcc length of the packet from a pointer to the end of the data. -****************************************************************************/ - -void cli_setup_bcc(struct cli_state *cli, void *p) -{ - set_message_bcc(cli->outbuf, PTR_DIFF(p, smb_buf(cli->outbuf))); -} - /**************************************************************************** Initialize Domain, user or password. ****************************************************************************/ @@ -486,7 +213,7 @@ struct cli_state *cli_initialise_ex(int signing_state) return NULL; } - cli = TALLOC_ZERO_P(NULL, struct cli_state); + cli = talloc_zero(NULL, struct cli_state); if (!cli) { return NULL; } @@ -506,6 +233,7 @@ struct cli_state *cli_initialise_ex(int signing_state) cli->bufsize = CLI_BUFFER_SIZE+4; cli->max_xmit = cli->bufsize; cli->outbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN); + cli->seqnum = 0; cli->inbuf = (char *)SMB_MALLOC(cli->bufsize+SAFETY_MARGIN); cli->oplock_handler = cli_oplock_ack; cli->case_sensitive = false; @@ -548,17 +276,19 @@ struct cli_state *cli_initialise_ex(int signing_state) memset(cli->outbuf, 0, cli->bufsize); memset(cli->inbuf, 0, cli->bufsize); - -#if defined(DEVELOPER) - /* just because we over-allocate, doesn't mean it's right to use it */ - clobber_region(FUNCTION_MACRO, __LINE__, cli->outbuf+cli->bufsize, SAFETY_MARGIN); - clobber_region(FUNCTION_MACRO, __LINE__, cli->inbuf+cli->bufsize, SAFETY_MARGIN); -#endif - /* initialise signing */ - cli->sign_info.allow_smb_signing = allow_smb_signing; - cli->sign_info.mandatory_signing = mandatory_signing; - cli_null_set_signing(cli); + cli->signing_state = smb_signing_init(cli, + allow_smb_signing, + mandatory_signing); + if (!cli->signing_state) { + goto error; + } + + cli->outgoing = tevent_queue_create(cli, "cli_outgoing"); + if (cli->outgoing == NULL) { + goto error; + } + cli->pending = NULL; cli->initialised = 1; @@ -597,29 +327,8 @@ void cli_nt_pipes_close(struct cli_state *cli) Shutdown a client structure. ****************************************************************************/ -void cli_shutdown(struct cli_state *cli) +static void _cli_shutdown(struct cli_state *cli) { - if (cli->prev == NULL) { - /* - * Possible head of a DFS list, - * shutdown all subsidiary DFS - * connections. - */ - struct cli_state *p, *next; - - for (p = cli->next; p; p = next) { - next = p->next; - cli_shutdown(p); - } - } else { - /* - * We're a subsidiary connection. - * Just remove ourselves from the - * DFS list. - */ - DLIST_REMOVE(cli->prev, cli); - } - cli_nt_pipes_close(cli); /* @@ -641,7 +350,6 @@ void cli_shutdown(struct cli_state *cli) SAFE_FREE(cli->outbuf); SAFE_FREE(cli->inbuf); - cli_free_signing_context(cli); data_blob_free(&cli->secblob); data_blob_free(&cli->user_session_key); @@ -651,9 +359,41 @@ void cli_shutdown(struct cli_state *cli) cli->fd = -1; cli->smb_rw_error = SMB_READ_OK; + /* + * Need to free pending first, they remove themselves + */ + while (cli->pending) { + talloc_free(cli->pending[0]); + } TALLOC_FREE(cli); } +void cli_shutdown(struct cli_state *cli) +{ + struct cli_state *cli_head; + if (cli == NULL) { + return; + } + DLIST_HEAD(cli, cli_head); + if (cli_head == cli) { + /* + * head of a DFS list, shutdown all subsidiary DFS + * connections. + */ + struct cli_state *p, *next; + + for (p = cli_head->next; p; p = next) { + next = p->next; + DLIST_REMOVE(cli_head, p); + _cli_shutdown(p); + } + } else { + DLIST_REMOVE(cli_head, cli); + } + + _cli_shutdown(cli); +} + /**************************************************************************** Set socket options on a open connection. ****************************************************************************/ @@ -685,112 +425,74 @@ bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive) return ret; } -/**************************************************************************** -Send a keepalive packet to the server -****************************************************************************/ +struct cli_echo_state { + uint16_t vwv[1]; + DATA_BLOB data; + int num_echos; +}; -bool cli_send_keepalive(struct cli_state *cli) +static void cli_echo_done(struct tevent_req *subreq); + +struct tevent_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint16_t num_echos, + DATA_BLOB data) { - if (cli->fd == -1) { - DEBUG(3, ("cli_send_keepalive: fd == -1\n")); - return false; - } - if (!send_keepalive(cli->fd)) { - close(cli->fd); - cli->fd = -1; - DEBUG(0,("Error sending keepalive packet to client.\n")); - return false; - } - return true; -} + struct tevent_req *req, *subreq; + struct cli_echo_state *state; -/** - * @brief: Collect a echo reply - * @param[in] req The corresponding async request - * - * There might be more than one echo reply. This helper pulls the reply out of - * the data stream. If all expected replies have arrived, declare the - * async_req done. - */ + req = tevent_req_create(mem_ctx, &state, struct cli_echo_state); + if (req == NULL) { + return NULL; + } + SSVAL(state->vwv, 0, num_echos); + state->data = data; + state->num_echos = num_echos; + + subreq = cli_smb_send(state, ev, cli, SMBecho, 0, 1, state->vwv, + data.length, data.data); + if (subreq == NULL) { + goto fail; + } + tevent_req_set_callback(subreq, cli_echo_done, req); + return req; + fail: + TALLOC_FREE(req); + return NULL; +} -static void cli_echo_recv_helper(struct async_req *req) +static void cli_echo_done(struct tevent_req *subreq) { - struct cli_request *cli_req; - uint8_t wct; - uint16_t *vwv; - uint16_t num_bytes; - uint8_t *bytes; + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_echo_state *state = tevent_req_data( + req, struct cli_echo_state); NTSTATUS status; + uint32_t num_bytes; + uint8_t *bytes; + uint8_t *inbuf; - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + status = cli_smb_recv(subreq, state, &inbuf, 0, NULL, NULL, + &num_bytes, &bytes); if (!NT_STATUS_IS_OK(status)) { - async_req_nterror(req, status); + tevent_req_nterror(req, status); return; } - - cli_req = talloc_get_type_abort(req->private_data, struct cli_request); - - if ((num_bytes != cli_req->data.echo.data.length) - || (memcmp(cli_req->data.echo.data.data, bytes, - num_bytes) != 0)) { - async_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + if ((num_bytes != state->data.length) + || (memcmp(bytes, state->data.data, num_bytes) != 0)) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); return; } - cli_req->data.echo.num_echos -= 1; - - if (cli_req->data.echo.num_echos == 0) { - client_set_trans_sign_state_off(cli_req->cli, cli_req->mid); - async_req_done(req); + state->num_echos -=1; + if (state->num_echos == 0) { + tevent_req_done(req); return; } - return; -} - -/** - * @brief Send SMBEcho requests - * @param[in] mem_ctx The memory context to put the async_req on - * @param[in] ev The event context that will call us back - * @param[in] cli The connection to send the echo to - * @param[in] num_echos How many times do we want to get the reply? - * @param[in] data The data we want to get back - * @retval The async request - */ - -struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, - struct cli_state *cli, uint16_t num_echos, - DATA_BLOB data) -{ - uint16_t vwv[1]; - uint8_t *data_copy; - struct async_req *result; - struct cli_request *req; - - SSVAL(vwv, 0, num_echos); - - data_copy = (uint8_t *)talloc_memdup(mem_ctx, data.data, data.length); - if (data_copy == NULL) { - return NULL; - } - - result = cli_request_send(mem_ctx, ev, cli, SMBecho, 0, 1, vwv, 0, - data.length, data.data); - if (result == NULL) { - TALLOC_FREE(data_copy); - return NULL; + if (!cli_smb_req_set_pending(subreq)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; } - req = talloc_get_type_abort(result->private_data, struct cli_request); - - client_set_trans_sign_state_on(cli, req->mid); - - req->data.echo.num_echos = num_echos; - req->data.echo.data.data = talloc_move(req, &data_copy); - req->data.echo.data.length = data.length; - - req->recv_helper.fn = cli_echo_recv_helper; - - return result; } /** @@ -799,9 +501,9 @@ struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, * @retval Did the server reply correctly? */ -NTSTATUS cli_echo_recv(struct async_req *req) +NTSTATUS cli_echo_recv(struct tevent_req *req) { - return async_req_simple_recv_ntstatus(req); + return tevent_req_simple_recv_ntstatus(req); } /** @@ -818,35 +520,40 @@ NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data) { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status = NT_STATUS_NO_MEMORY; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + status = NT_STATUS_INVALID_PARAMETER; goto fail; } ev = event_context_init(frame); if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } req = cli_echo_send(frame, ev, cli, num_echos, data); if (req == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_echo_recv(req); - fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } @@ -874,3 +581,40 @@ bool is_andx_req(uint8_t cmd) return false; } + +NTSTATUS cli_smb(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t smb_command, uint8_t additional_flags, + uint8_t wct, uint16_t *vwv, + uint32_t num_bytes, const uint8_t *bytes, + struct tevent_req **result_parent, + uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv, + uint32_t *pnum_bytes, uint8_t **pbytes) +{ + struct tevent_context *ev; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (cli_has_async_calls(cli)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = tevent_context_init(mem_ctx); + if (ev == NULL) { + goto fail; + } + req = cli_smb_send(mem_ctx, ev, cli, smb_command, additional_flags, + wct, vwv, num_bytes, bytes); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb_recv(req, NULL, NULL, min_wct, pwct, pvwv, + pnum_bytes, pbytes); +fail: + TALLOC_FREE(ev); + if (NT_STATUS_IS_OK(status) && (result_parent != NULL)) { + *result_parent = req; + } + return status; +}