The branch, master has been updated via 9c677ff s4: smbtorture: Add a proper change_notify going async followed by tdis test. via 3a727d5 s4: smbtorture: Update the torture_smb2_notify_ulogoff test to demonstrate the problem. via 95f96a8 s3:smb2_tcon: cancel and wait for pending requests on tdis via 7c26475 s3:smb2_sesssetup: cancel and wait for pending requests on logoff via 195c2d8 s3:smb2_tcon: split smbd_smb2_tdis into an async *_send/recv pair. via 506817d s3:smb2_sesssetup: split smbd_smb2_logoff into an async *_send/recv pair. via 4d1d288 s3:smb2_lock: return RANGE_NOT_LOCKED instead of CANCELLED for logoff and tdis via b498937 s3:smb2_lock: fix whitespaces/tabs in smbd_smb2_lock_cancel() via 0e4f239 s4:torture/smb2: accept NT_STATUS_RANGE_NOT_LOCKED after smb2_logoff/tdis from 9c8badc s3-net: add a new "net ads kerberos pac save" tool.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 9c677fff0bb0abc8d19dd67c33b3e044b1a9862e Author: Jeremy Allison <j...@samba.org> Date: Mon Feb 24 10:44:59 2014 -0800 s4: smbtorture: Add a proper change_notify going async followed by tdis test. [Bug 10344] SessionLogoff on a signed connection with an outstanding notify request crashes smbd. https://bugzilla.samba.org/show_bug.cgi?id=10344 Signed-off-by: Jeremy Allison <j...@samba.org> Reviewed-by: Stefan Metzmacher <me...@samba.org> Autobuild-User(master): Jeremy Allison <j...@samba.org> Autobuild-Date(master): Wed Mar 12 20:12:58 CET 2014 on sn-devel-104 commit 3a727d5d39bab05fa7237e32ffe244ddfebb0ee0 Author: Jeremy Allison <j...@samba.org> Date: Tue Jan 28 14:07:26 2014 -0800 s4: smbtorture: Update the torture_smb2_notify_ulogoff test to demonstrate the problem. [Bug 10344] SessionLogoff on a signed connection with an outstanding notify request crashes smbd. https://bugzilla.samba.org/show_bug.cgi?id=10344 Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Jeremy Allison <j...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 95f96a81083be578ee638c0cebd590228d9a4424 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Mar 10 09:53:18 2014 +0100 s3:smb2_tcon: cancel and wait for pending requests on tdis Bug: https://bugzilla.samba.org/show_bug.cgi?id=10344 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 7c26475d58a003888b7ba6f17f649cca6d93f6f3 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Mar 10 09:53:18 2014 +0100 s3:smb2_sesssetup: cancel and wait for pending requests on logoff Bug: https://bugzilla.samba.org/show_bug.cgi?id=10344 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 195c2d84807a7580e90e288cc813a6c6ca596055 Author: Jeremy Allison <j...@samba.org> Date: Mon Mar 10 09:53:18 2014 +0100 s3:smb2_tcon: split smbd_smb2_tdis into an async *_send/recv pair. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10344 Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Jeremy Allison <j...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 506817dfc9d18c2c5c35d60a6e61a82917665e2d Author: Jeremy Allison <j...@samba.org> Date: Mon Mar 10 09:53:18 2014 +0100 s3:smb2_sesssetup: split smbd_smb2_logoff into an async *_send/recv pair. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10344 Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Jeremy Allison <j...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit 4d1d288b89d259f1b69eb3ed643b86d39e03f6bf Author: Stefan Metzmacher <me...@samba.org> Date: Mon Mar 10 09:47:11 2014 +0100 s3:smb2_lock: return RANGE_NOT_LOCKED instead of CANCELLED for logoff and tdis Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit b49893776167460d921822362e1b55abdd5cc751 Author: Stefan Metzmacher <me...@samba.org> Date: Mon Mar 10 09:43:35 2014 +0100 s3:smb2_lock: fix whitespaces/tabs in smbd_smb2_lock_cancel() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 0e4f23991f33bed708e99210e5940abc050e5933 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Mar 7 12:31:19 2014 +0100 s4:torture/smb2: accept NT_STATUS_RANGE_NOT_LOCKED after smb2_logoff/tdis Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: source3/smbd/smb2_lock.c | 42 +++++++--- source3/smbd/smb2_sesssetup.c | 182 +++++++++++++++++++++++++++++++++++++---- source3/smbd/smb2_tcon.c | 179 ++++++++++++++++++++++++++++++++++++---- source4/torture/smb2/lock.c | 24 +++--- source4/torture/smb2/notify.c | 88 ++++++++++++++++++-- 5 files changed, 452 insertions(+), 63 deletions(-) Changeset truncated at 500 lines: diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c index 4af3d61..5da14e9 100644 --- a/source3/smbd/smb2_lock.c +++ b/source3/smbd/smb2_lock.c @@ -368,23 +368,43 @@ static NTSTATUS smbd_smb2_lock_recv(struct tevent_req *req) static bool smbd_smb2_lock_cancel(struct tevent_req *req) { - struct smbd_smb2_request *smb2req = NULL; - struct smbd_smb2_lock_state *state = tevent_req_data(req, - struct smbd_smb2_lock_state); - if (!state) { - return false; - } + struct smbd_smb2_request *smb2req = NULL; + struct smbd_smb2_lock_state *state = tevent_req_data(req, + struct smbd_smb2_lock_state); + if (!state) { + return false; + } - if (!state->smb2req) { - return false; - } + if (!state->smb2req) { + return false; + } - smb2req = state->smb2req; + smb2req = state->smb2req; remove_pending_lock(state, state->blr); tevent_req_defer_callback(req, smb2req->sconn->ev_ctx); + + /* + * If the request is canceled because of logoff, tdis or close + * the status is NT_STATUS_RANGE_NOT_LOCKED instead of + * NT_STATUS_CANCELLED. + * + * Note that the close case is handled in + * cancel_pending_lock_requests_by_fid_smb2(SHUTDOWN_CLOSE) + * for now. + */ + if (!NT_STATUS_IS_OK(smb2req->session->status)) { + tevent_req_nterror(req, NT_STATUS_RANGE_NOT_LOCKED); + return true; + } + + if (!NT_STATUS_IS_OK(smb2req->tcon->status)) { + tevent_req_nterror(req, NT_STATUS_RANGE_NOT_LOCKED); + return true; + } + tevent_req_nterror(req, NT_STATUS_CANCELLED); - return true; + return true; } /**************************************************************** diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 9d7193b..391b100 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -788,44 +788,190 @@ static NTSTATUS smbd_smb2_session_setup_recv(struct tevent_req *req, return status; } +static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req); +static NTSTATUS smbd_smb2_logoff_recv(struct tevent_req *req); +static void smbd_smb2_request_logoff_done(struct tevent_req *subreq); + NTSTATUS smbd_smb2_request_process_logoff(struct smbd_smb2_request *req) { NTSTATUS status; - DATA_BLOB outbody; + struct tevent_req *subreq = NULL; status = smbd_smb2_request_verify_sizes(req, 0x04); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } + subreq = smbd_smb2_logoff_send(req, req->sconn->ev_ctx, req); + if (subreq == NULL) { + return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); + } + tevent_req_set_callback(subreq, smbd_smb2_request_logoff_done, req); + /* - * TODO: cancel all outstanding requests on the session + * Wait a long time before going async on this to allow + * requests we're waiting on to finish. Set timeout to 10 secs. */ - status = smbXsrv_session_logoff(req->session); + return smbd_smb2_request_pending_queue(req, subreq, 10000000); +} + +static void smbd_smb2_request_logoff_done(struct tevent_req *subreq) +{ + struct smbd_smb2_request *smb2req = + tevent_req_callback_data(subreq, + struct smbd_smb2_request); + DATA_BLOB outbody; + NTSTATUS status; + NTSTATUS error; + + status = smbd_smb2_logoff_recv(subreq); + TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("smbd_smb2_request_process_logoff: " - "smbXsrv_session_logoff() failed: %s\n", - nt_errstr(status))); + error = smbd_smb2_request_error(smb2req, status); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } + return; + } + + outbody = smbd_smb2_generate_outbody(smb2req, 0x04); + if (outbody.data == NULL) { + error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } + return; + } + + SSVAL(outbody.data, 0x00, 0x04); /* struct size */ + SSVAL(outbody.data, 0x02, 0); /* reserved */ + + error = smbd_smb2_request_done(smb2req, outbody, NULL); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } +} + +struct smbd_smb2_logout_state { + struct smbd_smb2_request *smb2req; + struct tevent_queue *wait_queue; +}; + +static void smbd_smb2_logoff_wait_done(struct tevent_req *subreq); + +static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req) +{ + struct tevent_req *req; + struct smbd_smb2_logout_state *state; + struct tevent_req *subreq; + struct smbd_smb2_request *preq; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_logout_state); + if (req == NULL) { + return NULL; + } + state->smb2req = smb2req; + + state->wait_queue = tevent_queue_create(state, "logoff_wait_queue"); + if (tevent_req_nomem(state->wait_queue, req)) { + return tevent_req_post(req, ev); + } + + /* + * Make sure that no new request will be able to use this session. + */ + smb2req->session->status = NT_STATUS_USER_SESSION_DELETED; + + for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq == smb2req) { + /* Can't cancel current request. */ + continue; + } + if (preq->session != smb2req->session) { + /* Request on different session. */ + continue; + } + /* - * If we hit this case, there is something completely - * wrong, so we better disconnect the transport connection. + * Never cancel anything in a compound + * request. Way too hard to deal with + * the result. */ - return status; + if (!preq->compound_related && preq->subreq != NULL) { + tevent_req_cancel(preq->subreq); + } + + /* + * Now wait until the request is finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of the request will + * remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } } /* - * we may need to sign the response, so we need to keep - * the session until the response is sent to the wire. + * Now we add our own waiter to the end of the queue, + * this way we get notified when all pending requests are finished + * and send to the socket. */ - talloc_steal(req, req->session); + subreq = tevent_queue_wait_send(state, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbd_smb2_logoff_wait_done, req); - outbody = smbd_smb2_generate_outbody(req, 0x04); - if (outbody.data == NULL) { - return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); + return req; +} + +static void smbd_smb2_logoff_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbd_smb2_logout_state *state = tevent_req_data( + req, struct smbd_smb2_logout_state); + NTSTATUS status; + + tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + + /* + * As we've been awoken, we may have changed + * uid in the meantime. Ensure we're still + * root (SMB2_OP_LOGOFF has .as_root = true). + */ + change_to_root_user(); + + status = smbXsrv_session_logoff(state->smb2req->session); + if (tevent_req_nterror(req, status)) { + return; } - SSVAL(outbody.data, 0x00, 0x04); /* struct size */ - SSVAL(outbody.data, 0x02, 0); /* reserved */ + /* + * we may need to sign the response, so we need to keep + * the session until the response is sent to the wire. + */ + talloc_steal(state->smb2req, state->smb2req->session); + + tevent_req_done(req); +} - return smbd_smb2_request_done(req, outbody, NULL); +static NTSTATUS smbd_smb2_logoff_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index e1f0987..93f62fd 100644 --- a/source3/smbd/smb2_tcon.c +++ b/source3/smbd/smb2_tcon.c @@ -411,40 +411,187 @@ static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req, return NT_STATUS_OK; } +static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req); +static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req); +static void smbd_smb2_request_tdis_done(struct tevent_req *subreq); + NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req) { NTSTATUS status; - DATA_BLOB outbody; + struct tevent_req *subreq = NULL; status = smbd_smb2_request_verify_sizes(req, 0x04); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } + subreq = smbd_smb2_tdis_send(req, req->sconn->ev_ctx, req); + if (subreq == NULL) { + return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); + } + tevent_req_set_callback(subreq, smbd_smb2_request_tdis_done, req); + /* - * TODO: cancel all outstanding requests on the tcon + * Wait a long time before going async on this to allow + * requests we're waiting on to finish. Set timeout to 10 secs. */ - status = smbXsrv_tcon_disconnect(req->tcon, req->tcon->compat->vuid); + return smbd_smb2_request_pending_queue(req, subreq, 10000000); +} + +static void smbd_smb2_request_tdis_done(struct tevent_req *subreq) +{ + struct smbd_smb2_request *smb2req = + tevent_req_callback_data(subreq, + struct smbd_smb2_request); + DATA_BLOB outbody; + NTSTATUS status; + NTSTATUS error; + + status = smbd_smb2_tdis_recv(subreq); + TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("smbd_smb2_request_process_tdis: " - "smbXsrv_tcon_disconnect() failed: %s\n", - nt_errstr(status))); - /* - * If we hit this case, there is something completely - * wrong, so we better disconnect the transport connection. - */ - return status; + error = smbd_smb2_request_error(smb2req, status); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } + return; } - TALLOC_FREE(req->tcon); - - outbody = smbd_smb2_generate_outbody(req, 0x04); + outbody = smbd_smb2_generate_outbody(smb2req, 0x04); if (outbody.data == NULL) { - return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); + error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } + return; } SSVAL(outbody.data, 0x00, 0x04); /* struct size */ SSVAL(outbody.data, 0x02, 0); /* reserved */ - return smbd_smb2_request_done(req, outbody, NULL); + error = smbd_smb2_request_done(smb2req, outbody, NULL); + if (!NT_STATUS_IS_OK(error)) { + smbd_server_connection_terminate(smb2req->sconn, + nt_errstr(error)); + return; + } +} + +struct smbd_smb2_tdis_state { + struct smbd_smb2_request *smb2req; + struct tevent_queue *wait_queue; +}; + +static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq); + +static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req) +{ + struct tevent_req *req; + struct smbd_smb2_tdis_state *state; + struct tevent_req *subreq; + struct smbd_smb2_request *preq; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_tdis_state); + if (req == NULL) { + return NULL; + } + state->smb2req = smb2req; + + state->wait_queue = tevent_queue_create(state, "tdis_wait_queue"); + if (tevent_req_nomem(state->wait_queue, req)) { + return tevent_req_post(req, ev); + } + + /* + * Make sure that no new request will be able to use this tcon. + */ + smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED; + + for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq == smb2req) { + /* Can't cancel current request. */ + continue; + } + if (preq->tcon != smb2req->tcon) { + /* Request on different tcon. */ + continue; + } + + /* + * Never cancel anything in a compound + * request. Way too hard to deal with + * the result. + */ + if (!preq->compound_related && preq->subreq != NULL) { + tevent_req_cancel(preq->subreq); + } + + /* + * Now wait until the request is finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of the request will + * remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } + + /* + * Now we add our own waiter to the end of the queue, + * this way we get notified when all pending requests are finished + * and send to the socket. + */ + subreq = tevent_queue_wait_send(state, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbd_smb2_tdis_wait_done, req); + + return req; +} + +static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbd_smb2_tdis_state *state = tevent_req_data( + req, struct smbd_smb2_tdis_state); + NTSTATUS status; + + tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + + /* + * As we've been awoken, we may have changed + * uid in the meantime. Ensure we're still + * root (SMB2_OP_TDIS has .as_root = true). + */ + change_to_root_user(); + + status = smbXsrv_tcon_disconnect(state->smb2req->tcon, + state->smb2req->tcon->compat->vuid); + if (tevent_req_nterror(req, status)) { + return; + } + + /* We did tear down the tcon. */ + TALLOC_FREE(state->smb2req->tcon); + tevent_req_done(req); +} + +static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c index 9350c13..a27ae90 100644 --- a/source4/torture/smb2/lock.c +++ b/source4/torture/smb2/lock.c @@ -1044,11 +1044,12 @@ static bool test_cancel_tdis(struct torture_context *torture, torture_comment(torture, " Check pending lock reply\n"); status = smb2_lock_recv(req, &lck); - if (torture_setting_bool(torture, "samba4", false)) { - /* saying that this lock succeeded is nonsense - the - * tree is gone!! */ - CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); - } else { + if (!NT_STATUS_EQUAL(status, NT_STATUS_RANGE_NOT_LOCKED)) { + /* + * The status depends on the server internals + * the order in which the files are closed + * by smb2_tdis(). + */ CHECK_STATUS(status, NT_STATUS_OK); -- Samba Shared Repository