diff options
author | Simo Sorce <idra@samba.org> | 2008-09-03 11:52:54 -0400 |
---|---|---|
committer | Simo Sorce <idra@samba.org> | 2008-09-03 11:52:54 -0400 |
commit | c5894e14771562bccd153a98610722632ca3956a (patch) | |
tree | 5ea00141df4ffa12bad3475fdafc525ffa4d92af /source3/libsmb | |
parent | a1de4e988d7780f687bb7ed2288faf3dfbb9da71 (diff) | |
parent | 84fca380f2040c53d20fff41972d2f4102183766 (diff) | |
download | samba-c5894e14771562bccd153a98610722632ca3956a.tar.gz samba-c5894e14771562bccd153a98610722632ca3956a.tar.bz2 samba-c5894e14771562bccd153a98610722632ca3956a.zip |
Merge branch 'v3-devel' of ssh://git.samba.org/data/git/samba into v3-devel
(This used to be commit 8e4dca3b9416d9b5e535bda5e4befc073bfc1641)
Diffstat (limited to 'source3/libsmb')
-rw-r--r-- | source3/libsmb/async_smb.c | 697 | ||||
-rw-r--r-- | source3/libsmb/clientgen.c | 166 | ||||
-rw-r--r-- | source3/libsmb/clifile.c | 235 | ||||
-rw-r--r-- | source3/libsmb/clikrb5.c | 42 | ||||
-rw-r--r-- | source3/libsmb/clireadwrite.c | 129 | ||||
-rw-r--r-- | source3/libsmb/doserr.c | 2 | ||||
-rw-r--r-- | source3/libsmb/passchange.c | 22 | ||||
-rw-r--r-- | source3/libsmb/samlogon_cache.c | 54 |
8 files changed, 1013 insertions, 334 deletions
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 58bba2bfc7..79a924b9db 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -19,8 +19,13 @@ #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 + * @param[in] buf The SMB packet + * @retval The error, converted to NTSTATUS */ NTSTATUS cli_pull_error(char *buf) @@ -40,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) @@ -61,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) @@ -88,10 +97,18 @@ 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); - 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; @@ -101,79 +118,560 @@ 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) { 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; } -/* - * Create a fresh async smb request +/** + * 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"? */ -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 bool is_andx_req(uint8_t cmd) { - struct async_req *result; - struct cli_request *cli_req; - size_t bufsize = smb_size + num_words * 2 + num_bytes; + switch (cmd) { + case SMBtconX: + case SMBlockingX: + case SMBopenX: + case SMBreadX: + case SMBwriteX: + case SMBsesssetupX: + case SMBulogoffX: + case SMBntcreateX: + return true; + break; + default: + break; + } - result = async_req_new(mem_ctx, ev); - if (result == NULL) { + 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 = talloc_get_type_abort( + req->private_data, struct cli_request); + int i, pending; + bool found = false; + + pending = 0; + + for (i=0; i<cli_req->num_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; - cli_req = (struct cli_request *)talloc_size( - result, sizeof(*cli_req) + bufsize); - if (cli_req == NULL) { - TALLOC_FREE(result); + 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; } - talloc_set_name_const(cli_req, "struct cli_request"); - result->private_data = cli_req; - result->print = cli_request_print; + 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); - 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; + old_size = talloc_get_size(req->outbuf); - SCVAL(cli_req->outbuf, smb_wct, num_words); - SSVAL(cli_req->outbuf, smb_vwv + num_words * 2, num_bytes); + /* + * 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; - DLIST_ADD_END(cli->outstanding_requests, cli_req, - struct cli_request *); - talloc_set_destructor(cli_req, cli_request_destructor); + if (new_size > 0xffff) { + DEBUG(1, ("cli_request_chain: %u bytes won't fit\n", + (unsigned)new_size)); + goto fail; + } - DEBUG(10, ("cli_request_new: mid=%d\n", cli_req->mid)); + 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); + } - *preq = cli_req; - return result; + 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; } -/* - * Convenience function to get the SMB part out of an async_req +/** + * @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. */ -struct cli_request *cli_request_get(struct async_req *req) +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) { - return 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; + req->recv_helper.fn = 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 talloc_get_type_abort(req->private_data, struct cli_request); + 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 = talloc_get_type_abort( + req->private_data, struct cli_request); + 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; +} + +/** * 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) @@ -182,8 +680,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 */ @@ -296,7 +797,28 @@ 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; i<num_async; i++) { + /** + * A request might have been talloc_free()'ed before we arrive + * here. It will have removed itself from req->async via its + * destructor cli_async_req_destructor(). + */ + if (req->async[i] != NULL) { + if (req->recv_helper.fn != NULL) { + req->recv_helper.fn(req->async[i]); + } else { + async_req_done(req->async[i]); + } + } + } return; invalidate_requests: @@ -305,13 +827,17 @@ 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; } -/* +/** * 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, @@ -394,7 +920,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; } @@ -415,76 +943,13 @@ 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; i<req->num_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); 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; -} diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index 2c0950de03..9d65fb4e94 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -637,41 +637,153 @@ bool cli_send_keepalive(struct cli_state *cli) return true; } -/**************************************************************************** - Send/receive a SMBecho command: ping the server -****************************************************************************/ +/** + * @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. + */ + +static void cli_echo_recv_helper(struct async_req *req) +{ + struct cli_request *cli_req; + uint8_t wct; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + + status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + if (!NT_STATUS_IS_OK(status)) { + async_req_error(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_error(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + cli_req->data.echo.num_echos -= 1; -bool cli_echo(struct cli_state *cli, uint16 num_echos, - unsigned char *data, size_t length) + if (cli_req->data.echo.num_echos == 0) { + client_set_trans_sign_state_off(cli_req->cli, cli_req->mid); + async_req_done(req); + return; + } + + 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) { - char *p; - int i; + uint16_t vwv[1]; + uint8_t *data_copy; + struct async_req *result; + struct cli_request *req; - SMB_ASSERT(length < 1024); + SSVAL(vwv, 0, num_echos); - memset(cli->outbuf,'\0',smb_size); - cli_set_message(cli->outbuf,1,length,true); - SCVAL(cli->outbuf,smb_com,SMBecho); - SSVAL(cli->outbuf,smb_tid,65535); - SSVAL(cli->outbuf,smb_vwv0,num_echos); - cli_setup_packet(cli); - p = smb_buf(cli->outbuf); - memcpy(p, data, length); - p += length; + data_copy = (uint8_t *)talloc_memdup(mem_ctx, data.data, data.length); + if (data_copy == NULL) { + return NULL; + } - cli_setup_bcc(cli, p); + result = cli_request_send(mem_ctx, ev, cli, SMBecho, 0, 1, vwv, + data.length, data.data); + if (result == NULL) { + TALLOC_FREE(data_copy); + return NULL; + } + req = talloc_get_type_abort(result->private_data, struct cli_request); - cli_send_smb(cli); + client_set_trans_sign_state_on(cli, req->mid); - for (i=0; i<num_echos; i++) { - if (!cli_receive_smb(cli)) { - return false; - } + req->data.echo.num_echos = num_echos; + req->data.echo.data.data = talloc_move(req, &data_copy); + req->data.echo.data.length = data.length; - if (cli_is_error(cli)) { - return false; - } + req->recv_helper.fn = cli_echo_recv_helper; + + return result; +} + +/** + * Get the result out from an echo request + * @param[in] req The async_req from cli_echo_send + * @retval Did the server reply correctly? + */ + +NTSTATUS cli_echo_recv(struct async_req *req) +{ + SMB_ASSERT(req->state >= ASYNC_REQ_DONE); + if (req->state == ASYNC_REQ_ERROR) { + return req->status; } - return true; + return NT_STATUS_OK; +} + +/** + * @brief Send/Receive 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 Did the server reply correctly? + */ + +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; + + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_echo_send(frame, ev, cli, num_echos, data); + if (req == NULL) { + goto fail; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + status = cli_echo_recv(req); + + fail: + TALLOC_FREE(frame); + return status; } diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 12b10ba0a0..a8b3440513 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -781,19 +781,62 @@ int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0); } +static uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str) +{ + size_t buflen = talloc_get_size(buf); + char *converted; + size_t converted_size; + + /* + * We're pushing into an SMB buffer, align odd + */ + if (ucs2 && (buflen % 2 == 0)) { + buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, buflen + 1); + if (buf == NULL) { + return NULL; + } + buf[buflen] = '\0'; + buflen += 1; + } + + if (!convert_string_allocate(talloc_tos(), CH_UNIX, + ucs2 ? CH_UTF16LE : CH_DOS, + str, strlen(str)+1, &converted, + &converted_size, true)) { + return NULL; + } + + buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t, + buflen + converted_size); + if (buf == NULL) { + return NULL; + } + + memcpy(buf + buflen, converted, converted_size); + + TALLOC_FREE(converted); + return buf; +} + /**************************************************************************** Open a file WARNING: if you open with O_WRONLY then getattrE won't work! ****************************************************************************/ -int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode) +struct async_req *cli_open_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, + const char *fname, int flags, int share_mode) { - char *p; - unsigned openfn=0; - unsigned accessmode=0; + unsigned openfn = 0; + unsigned accessmode = 0; + uint8_t additional_flags = 0; + uint8_t *bytes; + uint16_t vwv[15]; + struct async_req *result; - if (flags & O_CREAT) + if (flags & O_CREAT) { openfn |= (1<<4); + } if (!(flags & O_EXCL)) { if (flags & O_TRUNC) openfn |= (1<<1); @@ -819,74 +862,172 @@ int cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode accessmode = 0xFF; } - memset(cli->outbuf,'\0',smb_size); - memset(cli->inbuf,'\0',smb_size); - - cli_set_message(cli->outbuf,15,0, true); - - SCVAL(cli->outbuf,smb_com,SMBopenX); - SSVAL(cli->outbuf,smb_tid,cli->cnum); - cli_setup_packet(cli); - - SSVAL(cli->outbuf,smb_vwv0,0xFF); - SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */ - SSVAL(cli->outbuf,smb_vwv3,accessmode); - SSVAL(cli->outbuf,smb_vwv4,aSYSTEM | aHIDDEN); - SSVAL(cli->outbuf,smb_vwv5,0); - SSVAL(cli->outbuf,smb_vwv8,openfn); + SCVAL(vwv + 0, 0, 0xFF); + SCVAL(vwv + 0, 1, 0); + SSVAL(vwv + 1, 0, 0); + SSVAL(vwv + 2, 0, 0); /* no additional info */ + SSVAL(vwv + 3, 0, accessmode); + SSVAL(vwv + 4, 0, aSYSTEM | aHIDDEN); + SSVAL(vwv + 5, 0, 0); + SIVAL(vwv + 6, 0, 0); + SSVAL(vwv + 8, 0, openfn); + SIVAL(vwv + 9, 0, 0); + SIVAL(vwv + 11, 0, 0); + SIVAL(vwv + 13, 0, 0); if (cli->use_oplocks) { /* if using oplocks then ask for a batch oplock via core and extended methods */ - SCVAL(cli->outbuf,smb_flg, CVAL(cli->outbuf,smb_flg)| - FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK); - SSVAL(cli->outbuf,smb_vwv2,SVAL(cli->outbuf,smb_vwv2) | 6); + additional_flags = + FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK; + SSVAL(vwv+2, 0, SVAL(vwv+2, 0) | 6); } - p = smb_buf(cli->outbuf); - p += clistr_push(cli, p, fname, - cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_TERMINATE); + bytes = talloc_array(talloc_tos(), uint8_t, 0); + if (bytes == NULL) { + return NULL; + } - cli_setup_bcc(cli, p); + bytes = smb_bytes_push_str( + bytes, (cli->capabilities & CAP_UNICODE) != 0, fname); + if (bytes == NULL) { + return NULL; + } - cli_send_smb(cli); - if (!cli_receive_smb(cli)) { - return -1; + result = cli_request_send(mem_ctx, ev, cli, SMBopenX, additional_flags, + 15, vwv, talloc_get_size(bytes), bytes); + TALLOC_FREE(bytes); + return result; +} + +NTSTATUS cli_open_recv(struct async_req *req, int *fnum) +{ + uint8_t wct; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + + SMB_ASSERT(req->state >= ASYNC_REQ_DONE); + if (req->state == ASYNC_REQ_ERROR) { + return req->status; } - if (cli_is_error(cli)) { - return -1; + status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (wct < 3) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; } - return SVAL(cli->inbuf,smb_vwv2); + *fnum = SVAL(vwv+2, 0); + + return NT_STATUS_OK; +} + +int cli_open(struct cli_state *cli, const char *fname, int flags, + int share_mode) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct async_req *req; + int result = -1; + + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_open_send(frame, ev, cli, fname, flags, share_mode); + if (req == NULL) { + goto fail; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + cli_open_recv(req, &result); + fail: + TALLOC_FREE(frame); + return result; } /**************************************************************************** Close a file. ****************************************************************************/ -bool cli_close(struct cli_state *cli, int fnum) +struct async_req *cli_close_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, int fnum) { - memset(cli->outbuf,'\0',smb_size); - memset(cli->inbuf,'\0',smb_size); + uint16_t vwv[3]; - cli_set_message(cli->outbuf,3,0,True); + SSVAL(vwv+0, 0, fnum); + SIVALS(vwv+1, 0, -1); - SCVAL(cli->outbuf,smb_com,SMBclose); - SSVAL(cli->outbuf,smb_tid,cli->cnum); - cli_setup_packet(cli); + return cli_request_send(mem_ctx, ev, cli, SMBclose, 0, 3, vwv, + 0, NULL); +} - SSVAL(cli->outbuf,smb_vwv0,fnum); - SIVALS(cli->outbuf,smb_vwv1,-1); +NTSTATUS cli_close_recv(struct async_req *req) +{ + uint8_t wct; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; - cli_send_smb(cli); - if (!cli_receive_smb(cli)) { - return False; + SMB_ASSERT(req->state >= ASYNC_REQ_DONE); + if (req->state == ASYNC_REQ_ERROR) { + return req->status; } - return !cli_is_error(cli); + return cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); } +bool cli_close(struct cli_state *cli, int fnum) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct async_req *req; + bool result = false; + + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_close_send(frame, ev, cli, fnum); + if (req == NULL) { + goto fail; + } + + while (req->state < ASYNC_REQ_DONE) { + event_loop_once(ev); + } + + result = NT_STATUS_IS_OK(cli_close_recv(req)); + fail: + TALLOC_FREE(frame); + return result; +} /**************************************************************************** Truncate a file to a specified size @@ -1751,7 +1892,7 @@ bool cli_set_ea_fnum(struct cli_state *cli, int fnum, const char *ea_name, const } /********************************************************* - Get an extended attribute list tility fn. + Get an extended attribute list utility fn. *********************************************************/ static bool cli_get_ea_list(struct cli_state *cli, diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index fa21ad3467..f940081072 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -27,18 +27,6 @@ #ifdef HAVE_KRB5 -#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE /* Heimdal */ -#define KRB5_KEY_TYPE(k) ((k)->keytype) -#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length) -#define KRB5_KEY_DATA(k) ((k)->keyvalue.data) -#define KRB5_KEY_DATA_CAST void -#else /* MIT */ -#define KRB5_KEY_TYPE(k) ((k)->enctype) -#define KRB5_KEY_LENGTH(k) ((k)->length) -#define KRB5_KEY_DATA(k) ((k)->contents) -#define KRB5_KEY_DATA_CAST krb5_octet -#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */ - #define GSSAPI_CHECKSUM 0x8003 /* Checksum type value for Kerberos */ #define GSSAPI_BNDLENGTH 16 /* Bind Length (rfc-1964 pg.3) */ #define GSSAPI_CHECKSUM_SIZE (12+GSSAPI_BNDLENGTH) @@ -761,16 +749,10 @@ static krb5_error_code ads_krb5_mk_req(krb5_context context, ccache, &in_data ); if (retval) { - DEBUG( 1, ("ads_krb5_get_fwd_ticket failed (%s)\n", error_message( retval ) ) ); - goto cleanup_creds; - } - - if (retval) { - DEBUG( 1, ("krb5_auth_con_set_req_cksumtype failed (%s)\n", - error_message( retval ) ) ); + DEBUG( 1, ("ads_krb5_get_fwd_ticket failed (%s)\n", + error_message( retval ) ) ); goto cleanup_creds; } - } #endif @@ -1057,6 +1039,7 @@ get_key_from_keytab(krb5_context context, krb5_error_code ret; krb5_keytab keytab; char *name = NULL; + krb5_keyblock *keyp; /* We have to open a new keytab handle here, as MIT does an implicit open/getnext/close on krb5_kt_get_entry. We @@ -1089,14 +1072,9 @@ get_key_from_keytab(krb5_context context, goto out; } -#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */ - ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); -#elif defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) /* MIT */ - ret = krb5_copy_keyblock(context, &entry.key, out_key); -#else -#error UNKNOWN_KRB5_KEYTAB_ENTRY_FORMAT -#endif + keyp = KRB5_KT_KEY(&entry); + ret = krb5_copy_keyblock(context, keyp, out_key); if (ret) { DEBUG(0,("get_key_from_keytab: failed to copy key: %s\n", error_message(ret))); goto out; @@ -1584,15 +1562,9 @@ done: #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */ } - krb5_enctype smb_get_enctype_from_kt_entry(const krb5_keytab_entry *kt_entry) + krb5_enctype smb_get_enctype_from_kt_entry(krb5_keytab_entry *kt_entry) { -#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY /* MIT */ - return kt_entry->key.enctype; -#elif defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK) /* Heimdal */ - return kt_entry->keyblock.keytype; -#else -#error UNKNOWN_KRB5_KEYTAB_ENTRY_KEYBLOCK_FORMAT -#endif + return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry)); } diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index a57f1e0785..ec63281630 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -41,13 +41,16 @@ static size_t cli_read_max_bufsize(struct cli_state *cli) */ struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, struct cli_state *cli, int fnum, off_t offset, size_t size) { struct async_req *result; struct cli_request *req; bool bigoffset = False; - char *enc_buf; + + uint16_t vwv[12]; + uint8_t wct = 10; if (size > cli_read_max_bufsize(cli)) { DEBUG(0, ("cli_read_andx_send got size=%d, can only handle " @@ -56,60 +59,37 @@ struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, return NULL; } - result = cli_request_new(mem_ctx, cli->event_ctx, cli, 12, 0, &req); + SCVAL(vwv + 0, 0, 0xFF); + SCVAL(vwv + 0, 1, 0); + SSVAL(vwv + 1, 0, 0); + SSVAL(vwv + 2, 0, fnum); + SIVAL(vwv + 3, 0, offset); + SSVAL(vwv + 5, 0, size); + SSVAL(vwv + 6, 0, size); + SSVAL(vwv + 7, 0, (size >> 16)); + SSVAL(vwv + 8, 0, 0); + SSVAL(vwv + 9, 0, 0); + + if ((SMB_BIG_UINT)offset >> 32) { + bigoffset = True; + SIVAL(vwv + 10, 0, + (((SMB_BIG_UINT)offset)>>32) & 0xffffffff); + wct += 2; + } + + result = cli_request_send(mem_ctx, ev, cli, SMBreadX, 0, wct, vwv, + 0, NULL); if (result == NULL) { - DEBUG(0, ("cli_request_new failed\n")); return NULL; } + req = talloc_get_type_abort(result->private_data, struct cli_request); + req->data.read.ofs = offset; req->data.read.size = size; req->data.read.received = 0; req->data.read.rcvbuf = NULL; - if ((SMB_BIG_UINT)offset >> 32) - bigoffset = True; - - cli_set_message(req->outbuf, bigoffset ? 12 : 10, 0, False); - - SCVAL(req->outbuf,smb_com,SMBreadX); - SSVAL(req->outbuf,smb_tid,cli->cnum); - cli_setup_packet_buf(cli, req->outbuf); - - SCVAL(req->outbuf,smb_vwv0,0xFF); - SCVAL(req->outbuf,smb_vwv0+1,0); - SSVAL(req->outbuf,smb_vwv1,0); - SSVAL(req->outbuf,smb_vwv2,fnum); - SIVAL(req->outbuf,smb_vwv3,offset); - SSVAL(req->outbuf,smb_vwv5,size); - SSVAL(req->outbuf,smb_vwv6,size); - SSVAL(req->outbuf,smb_vwv7,(size >> 16)); - SSVAL(req->outbuf,smb_vwv8,0); - SSVAL(req->outbuf,smb_vwv9,0); - SSVAL(req->outbuf,smb_mid,req->mid); - - if (bigoffset) { - SIVAL(req->outbuf, smb_vwv10, - (((SMB_BIG_UINT)offset)>>32) & 0xffffffff); - } - - cli_calculate_sign_mac(cli, req->outbuf); - - event_fd_set_writeable(cli->fd_event); - - if (cli_encryption_on(cli)) { - NTSTATUS status; - 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; - } - return result; } @@ -123,7 +103,12 @@ struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, uint8_t **rcvbuf) { - 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; + uint16_t *vwv; + uint16_t num_bytes; + uint8_t *bytes; NTSTATUS status; size_t size; @@ -132,24 +117,27 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, return req->status; } - status = cli_pull_error(cli_req->inbuf); + status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); if (NT_STATUS_IS_ERR(status)) { return status; } + if (wct < 12) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + /* size is the number of bytes the server returned. * Might be zero. */ - size = SVAL(cli_req->inbuf, smb_vwv5); - size |= (((unsigned int)(SVAL(cli_req->inbuf, smb_vwv7))) << 16); + size = SVAL(vwv + 5, 0); + size |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); if (size > cli_req->data.read.size) { DEBUG(5,("server returned more than we wanted!\n")); return NT_STATUS_UNEXPECTED_IO_ERROR; } - *rcvbuf = (uint8_t *) - (smb_base(cli_req->inbuf) + SVAL(cli_req->inbuf, smb_vwv6)); + *rcvbuf = (uint8_t *)(smb_base(cli_req->inbuf) + SVAL(vwv + 6, 0)); *received = size; return NT_STATUS_OK; } @@ -165,6 +153,7 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, struct cli_pull_state { struct async_req *req; + struct event_context *ev; struct cli_state *cli; uint16_t fnum; off_t start_offset; @@ -223,7 +212,9 @@ static void cli_pull_read_done(struct async_req *read_req); * Prepare an async pull request */ -struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, +struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, off_t start_offset, SMB_OFF_T size, size_t window_size, NTSTATUS (*sink)(char *buf, size_t n, @@ -234,7 +225,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, struct cli_pull_state *state; int i; - result = async_req_new(mem_ctx, cli->event_ctx); + result = async_req_new(mem_ctx, ev); if (result == NULL) { goto failed; } @@ -247,6 +238,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, state->req = result; state->cli = cli; + state->ev = ev; state->fnum = fnum; state->start_offset = start_offset; state->size = size; @@ -289,7 +281,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, struct cli_state *cli, request_thistime = MIN(size_left, state->chunk_size); state->reqs[i] = cli_read_andx_send( - state->reqs, cli, fnum, + state->reqs, ev, cli, fnum, state->start_offset + state->requested, request_thistime); @@ -320,7 +312,8 @@ static void cli_pull_read_done(struct async_req *read_req) read_req->async.priv, struct async_req); struct cli_pull_state *state = talloc_get_type_abort( pull_req->private_data, struct cli_pull_state); - struct cli_request *read_state = cli_request_get(read_req); + struct cli_request *read_state = talloc_get_type_abort( + read_req->private_data, struct cli_request); NTSTATUS status; status = cli_read_andx_recv(read_req, &read_state->data.read.received, @@ -351,7 +344,9 @@ static void cli_pull_read_done(struct async_req *read_req) return; } - top_read = cli_request_get(state->reqs[state->top_req]); + top_read = talloc_get_type_abort( + state->reqs[state->top_req]->private_data, + struct cli_request); DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already " "pushed\n", (int)top_read->data.read.received, @@ -384,7 +379,8 @@ static void cli_pull_read_done(struct async_req *read_req) state->top_req)); new_req = cli_read_andx_send( - state->reqs, state->cli, state->fnum, + state->reqs, state->ev, state->cli, + state->fnum, state->start_offset + state->requested, request_thistime); @@ -424,21 +420,30 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, void *priv, SMB_OFF_T *received) { TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; struct async_req *req; NTSTATUS result = NT_STATUS_NO_MEMORY; - if (cli_tmp_event_ctx(frame, cli) == NULL) { + if (cli->fd_event != NULL) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + ev = event_context_init(frame); + if (ev == NULL) { goto nomem; } - req = cli_pull_send(frame, cli, fnum, start_offset, size, window_size, - sink, priv); + req = cli_pull_send(frame, ev, cli, fnum, start_offset, size, + window_size, sink, priv); if (req == NULL) { goto nomem; } while (req->state < ASYNC_REQ_DONE) { - event_loop_once(cli->event_ctx); + event_loop_once(ev); } result = cli_pull_recv(req, received); diff --git a/source3/libsmb/doserr.c b/source3/libsmb/doserr.c index 50b5b2238c..c62918e214 100644 --- a/source3/libsmb/doserr.c +++ b/source3/libsmb/doserr.c @@ -91,6 +91,7 @@ werror_code_struct dos_errs[] = { "WERR_DEFAULT_JOIN_REQUIRED", WERR_DEFAULT_JOIN_REQUIRED }, { "WERR_DEVICE_NOT_AVAILABLE", WERR_DEVICE_NOT_AVAILABLE }, { "WERR_LOGON_FAILURE", WERR_LOGON_FAILURE }, + { "WERR_WRONG_PASSWORD", WERR_WRONG_PASSWORD }, { "WERR_PASSWORD_RESTRICTION", WERR_PASSWORD_RESTRICTION }, { "WERR_NO_SUCH_DOMAIN", WERR_NO_SUCH_DOMAIN }, { "WERR_NONE_MAPPED", WERR_NONE_MAPPED }, @@ -150,6 +151,7 @@ werror_str_struct dos_err_strs[] = { { WERR_GROUP_EXISTS, "Group already exists" }, { WERR_DS_DRA_BAD_DN, "An invalid distinguished name was specified for this replication" }, { WERR_DS_DRA_BAD_NC, "An invalid naming context was specified for this replication operation" }, + { WERR_WRONG_PASSWORD, "The current password is incorrect" } }; /***************************************************************************** diff --git a/source3/libsmb/passchange.c b/source3/libsmb/passchange.c index c8a4406949..4c76234e0c 100644 --- a/source3/libsmb/passchange.c +++ b/source3/libsmb/passchange.c @@ -42,7 +42,7 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam "%s.\n", remote_machine); return NT_STATUS_UNSUCCESSFUL; } - + cli = cli_initialise(); if (!cli) { return NT_STATUS_NO_MEMORY; @@ -56,10 +56,10 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam cli_shutdown(cli); return result; } - + make_nmb_name(&calling, global_myname() , 0x0); make_nmb_name(&called , remote_machine, 0x20); - + if (!cli_session_request(cli, &calling, &called)) { asprintf(err_str, "machine %s rejected the session setup. " "Error was : %s.\n", @@ -68,7 +68,7 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam cli_shutdown(cli); return result; } - + cli->protocol = PROTOCOL_NT1; if (!cli_negprot(cli)) { @@ -79,7 +79,7 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam cli_shutdown(cli); return result; } - + /* Given things like SMB signing, restrict anonymous and the like, try an authenticated connection first */ result = cli_session_setup(cli, user_name, @@ -188,7 +188,7 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { /* it failed, but for reasons such as wrong password, too short etc ... */ - + asprintf(err_str, "machine %s rejected the password change: " "Error was : %s.\n", remote_machine, get_friendly_nt_error_msg(result)); @@ -198,12 +198,12 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam /* OK, that failed, so try again... */ TALLOC_FREE(pipe_hnd); - + /* Try anonymous NTLMSSP... */ cli_init_creds(cli, "", "", NULL); - + result = NT_STATUS_UNSUCCESSFUL; - + /* OK, this is ugly, but... try an anonymous pipe. */ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id, &pipe_hnd); @@ -227,10 +227,10 @@ NTSTATUS remote_password_change(const char *remote_machine, const char *user_nam cli_shutdown(cli); return result; } - + /* We have failed to change the user's password, and we think the server just might not support SAMR password changes, so fall back */ - + if (lp_client_lanman_auth()) { /* Use the old RAP method. */ if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c index 2d2588f70c..4abe5bb6de 100644 --- a/source3/libsmb/samlogon_cache.c +++ b/source3/libsmb/samlogon_cache.c @@ -59,48 +59,30 @@ bool netsamlogon_cache_shutdown(void) Clear cache getpwnam and getgroups entries from the winbindd cache ***********************************************************************/ -void netsamlogon_clear_cached_user(TDB_CONTEXT *tdb, struct netr_SamInfo3 *info3) +void netsamlogon_clear_cached_user(struct netr_SamInfo3 *info3) { - bool got_tdb = false; - DOM_SID sid; - fstring key_str, sid_string; - - /* We may need to call this function from smbd which will not have - winbindd_cache.tdb open. Open the tdb if a NULL is passed. */ - - if (!tdb) { - tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), - WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, - TDB_DEFAULT, O_RDWR, 0600); - if (!tdb) { - DEBUG(5, ("netsamlogon_clear_cached_user: failed to open cache\n")); - return; - } - got_tdb = true; - } - - sid_copy(&sid, info3->base.domain_sid); - sid_append_rid(&sid, info3->base.rid); - - /* Clear U/SID cache entry */ - - fstr_sprintf(key_str, "U/%s", sid_to_fstring(sid_string, &sid)); - - DEBUG(10, ("netsamlogon_clear_cached_user: clearing %s\n", key_str)); - - tdb_delete(tdb, string_tdb_data(key_str)); + DOM_SID user_sid; + fstring keystr, tmp; - /* Clear UG/SID cache entry */ + if (!info3) { + return; + } - fstr_sprintf(key_str, "UG/%s", sid_to_fstring(sid_string, &sid)); + if (!netsamlogon_cache_init()) { + DEBUG(0,("netsamlogon_clear_cached_user: cannot open " + "%s for write!\n", + NETSAMLOGON_TDB)); + return; + } + sid_copy(&user_sid, info3->base.domain_sid); + sid_append_rid(&user_sid, info3->base.rid); - DEBUG(10, ("netsamlogon_clear_cached_user: clearing %s\n", key_str)); + /* Prepare key as DOMAIN-SID/USER-RID string */ + slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, &user_sid)); - tdb_delete(tdb, string_tdb_data(key_str)); + DEBUG(10,("netsamlogon_clear_cached_user: SID [%s]\n", keystr)); - if (got_tdb) { - tdb_close(tdb); - } + tdb_delete_bystring(netsamlogon_tdb, keystr); } /*********************************************************************** |