From a17e10fd5e3510547b2eff4bfe36a08545dd259c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 11 Jan 2014 08:58:05 +0100 Subject: [PATCH 01/11] tevent: fix crash bug in tevent_queue_immediate_trigger() Assume we we have a queue with 2 entries (A and B with triggerA() and triggerB()). If triggerA() removes itself tevent_queue_entry_destructor() will be called for A, this schedules the immediate event to call triggerB(). If triggerA() then also removes B by an explicit of implizit talloc_free(), q->list is NULL, but the immediate event is still scheduled and can't be unscheduled. Signed-off-by: Stefan Metzmacher Reviewed-by: Volker Lendecke (cherry picked from commit 7fe5584e2a59584431cb2ddf8a4da22bfb924454) --- lib/tevent/tevent_queue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/tevent/tevent_queue.c b/lib/tevent/tevent_queue.c index 4750675..eeb922f 100644 --- a/lib/tevent/tevent_queue.c +++ b/lib/tevent/tevent_queue.c @@ -140,6 +140,10 @@ static void tevent_queue_immediate_trigger(struct tevent_context *ev, return; } + if (!q->list) { + return; + } + q->list->triggered = true; q->list->trigger(q->list->req, q->list->private_data); } -- 1.9.0.279.gdc9e3eb From e7853e46079b3eae9976bef2715729317c187b26 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 13 Mar 2014 10:06:48 -0700 Subject: [PATCH 02/11] s3: lib: Back-port tevent_queue_wait_send/recv -> smbd_tevent_queue_wait_send/recv Required for bugfix: [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 --- source3/lib/smbd_tevent_queue.c | 83 +++++++++++++++++++++++++++++++++++++++++ source3/lib/smbd_tevent_queue.h | 34 +++++++++++++++++ source3/wscript_build | 1 + 3 files changed, 118 insertions(+) create mode 100644 source3/lib/smbd_tevent_queue.c create mode 100644 source3/lib/smbd_tevent_queue.h diff --git a/source3/lib/smbd_tevent_queue.c b/source3/lib/smbd_tevent_queue.c new file mode 100644 index 0000000..c7b93bb --- /dev/null +++ b/source3/lib/smbd_tevent_queue.c @@ -0,0 +1,83 @@ +/* + Unix SMB/CIFS implementation. + Infrastructure for async requests + Copyright (C) Volker Lendecke 2008 + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* Back-port of missing API from 4.2.x for tevent_queues. */ + +#include "replace.h" +#include "tevent.h" +#include "tevent_internal.h" +#include "tevent_util.h" +#include "source3/lib/smbd_tevent_queue.h" + +struct tevent_queue_wait_state { + uint8_t dummy; +}; + +static void tevent_queue_wait_trigger(struct tevent_req *req, + void *private_data); + +struct tevent_req *smbd_tevent_queue_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tevent_queue *queue) +{ + struct tevent_req *req; + struct tevent_queue_wait_state *state; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct tevent_queue_wait_state); + if (req == NULL) { + return NULL; + } + + ok = tevent_queue_add(queue, ev, req, + tevent_queue_wait_trigger, + NULL); + if (!ok) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + + return req; +} + +static void tevent_queue_wait_trigger(struct tevent_req *req, + void *private_data) +{ + tevent_req_done(req); +} + +bool smbd_tevent_queue_wait_recv(struct tevent_req *req) +{ + enum tevent_req_state state; + uint64_t err; + + if (tevent_req_is_error(req, &state, &err)) { + tevent_req_received(req); + return false; + } + + tevent_req_received(req); + return true; +} diff --git a/source3/lib/smbd_tevent_queue.h b/source3/lib/smbd_tevent_queue.h new file mode 100644 index 0000000..c919958 --- /dev/null +++ b/source3/lib/smbd_tevent_queue.h @@ -0,0 +1,34 @@ +/* + Unix SMB/CIFS implementation. + Infrastructure for async requests + Copyright (C) Volker Lendecke 2008 + Copyright (C) Stefan Metzmacher 2009 + + ** NOTE! The following LGPL license applies to the tevent + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* Back-port of missing API from 4.2 for tevent_queues. */ + +#ifndef _SMBD_TEVENT_QUEUE_H_ +#define _SMBD_TEVENT_QUEUE_H_ +struct tevent_req *smbd_tevent_queue_wait_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tevent_queue *queue); + +bool smbd_tevent_queue_wait_recv(struct tevent_req *req); +#endif diff --git a/source3/wscript_build b/source3/wscript_build index 924bde7..3e05fd6 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -67,6 +67,7 @@ LIB_SRC = ''' lib/fncall.c libads/krb5_errs.c lib/system_smbd.c lib/audit.c lib/tevent_wait.c + lib/smbd_tevent_queue.c lib/idmap_cache.c''' LIB_UTIL_SRC = ''' -- 1.9.0.279.gdc9e3eb From 286d203b63edfdce8e83788d584174d37a0a3e0d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 7 Mar 2014 12:31:19 +0100 Subject: [PATCH 03/11] s4:torture/smb2: accept NT_STATUS_RANGE_NOT_LOCKED after smb2_logoff/tdis Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 0e4f23991f33bed708e99210e5940abc050e5933) --- source4/torture/smb2/lock.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) 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); } @@ -1087,7 +1088,7 @@ static bool test_cancel_logoff(struct torture_context *torture, struct smb2_lock_element el[2]; struct smb2_request *req = NULL; - const char *fname = BASEDIR "\\cancel_tdis.txt"; + const char *fname = BASEDIR "\\cancel_logoff.txt"; status = torture_smb2_testdir(tree, BASEDIR, &h); CHECK_STATUS(status, NT_STATUS_OK); @@ -1130,11 +1131,12 @@ static bool test_cancel_logoff(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)) { - /* another bogus 'success' code from windows. The lock - * cannot have succeeded, as we are now logged off */ - 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_logoff(). + */ CHECK_STATUS(status, NT_STATUS_OK); } -- 1.9.0.279.gdc9e3eb From cbbf999de314b28704cc8fba93f1325c6c3536b3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 10 Mar 2014 09:43:35 +0100 Subject: [PATCH 04/11] s3:smb2_lock: fix whitespaces/tabs in smbd_smb2_lock_cancel() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit b49893776167460d921822362e1b55abdd5cc751) --- source3/smbd/smb2_lock.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c index 671cd6f..56c594b 100644 --- a/source3/smbd/smb2_lock.c +++ b/source3/smbd/smb2_lock.c @@ -368,23 +368,23 @@ 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); tevent_req_nterror(req, NT_STATUS_CANCELLED); - return true; + return true; } /**************************************************************** -- 1.9.0.279.gdc9e3eb From 540d1d4a1508606126f48a21bfc8055242637cc6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 10 Mar 2014 09:47:11 +0100 Subject: [PATCH 05/11] s3:smb2_lock: return RANGE_NOT_LOCKED instead of CANCELLED for logoff and tdis Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 4d1d288b89d259f1b69eb3ed643b86d39e03f6bf) --- source3/smbd/smb2_lock.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c index 56c594b..2ee7afa 100644 --- a/source3/smbd/smb2_lock.c +++ b/source3/smbd/smb2_lock.c @@ -383,6 +383,26 @@ static bool smbd_smb2_lock_cancel(struct tevent_req *req) 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; } -- 1.9.0.279.gdc9e3eb From 45c724e255073a73bf37db6c9628b39cf40e00fb Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 10 Mar 2014 09:53:18 +0100 Subject: [PATCH 06/11] 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 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (cherry picked from commit 506817dfc9d18c2c5c35d60a6e61a82917665e2d) Signed-off-by: Jeremy Allison --- source3/smbd/smb2_sesssetup.c | 112 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 20 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 265f802..f1e10ed 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -751,44 +751,116 @@ 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))); - /* - * 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; + } + + outbody = data_blob_talloc(smb2req->out.vector, NULL, 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; +}; + +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; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_logout_state); + if (req == NULL) { + return NULL; + } + state->smb2req = smb2req; + + /* + * TODO: cancel all outstanding requests on the session + */ + status = smbXsrv_session_logoff(state->smb2req->session); + if (tevent_req_nterror(req, status)) { + 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. */ - talloc_steal(req, req->session); - - outbody = data_blob_talloc(req->out.vector, NULL, 0x04); - if (outbody.data == NULL) { - return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); - } + talloc_steal(state->smb2req, state->smb2req->session); - SSVAL(outbody.data, 0x00, 0x04); /* struct size */ - SSVAL(outbody.data, 0x02, 0); /* reserved */ + tevent_req_done(req); + return tevent_req_post(req, ev); +} - 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); } -- 1.9.0.279.gdc9e3eb From 7d8213cc5985fb8d4eccd042b6052155b1480607 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 10 Mar 2014 09:53:18 +0100 Subject: [PATCH 07/11] 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 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (cherry picked from commit 195c2d84807a7580e90e288cc813a6c6ca596055) Signed-off-by: Jeremy Allison --- source3/smbd/smb2_tcon.c | 105 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index ca67461..b1e1fc6 100644 --- a/source3/smbd/smb2_tcon.c +++ b/source3/smbd/smb2_tcon.c @@ -411,40 +411,113 @@ 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 = data_blob_talloc(req->out.vector, NULL, 0x04); + outbody = data_blob_talloc(smb2req->out.vector, NULL, 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; +}; + +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; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_tdis_state); + if (req == NULL) { + return NULL; + } + state->smb2req = smb2req; + + /* + * TODO: cancel all outstanding requests on the tcon + */ + status = smbXsrv_tcon_disconnect(state->smb2req->tcon, + state->smb2req->tcon->compat->vuid); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + /* We did tear down the tcon. */ + TALLOC_FREE(state->smb2req->tcon); + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } -- 1.9.0.279.gdc9e3eb From 2640d7667e0d415b3e40453715b2e5e8b9e4a3a9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 10 Mar 2014 09:53:18 +0100 Subject: [PATCH 08/11] 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 Reviewed-by: Jeremy Allison (cherry picked from commit 7c26475d58a003888b7ba6f17f649cca6d93f6f3) --- source3/smbd/smb2_sesssetup.c | 83 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index f1e10ed..e911945 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -28,6 +28,7 @@ #include "../lib/tsocket/tsocket.h" #include "../libcli/security/security.h" #include "../lib/util/tevent_ntstatus.h" +#include "lib/smbd_tevent_queue.h" static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -825,15 +826,19 @@ static void smbd_smb2_request_logoff_done(struct tevent_req *subreq) 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; - NTSTATUS status; + struct tevent_req *subreq; + struct smbd_smb2_request *preq; req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_logout_state); @@ -842,12 +847,83 @@ static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, } 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; + } + + /* + * 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 = smbd_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 = smbd_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); + + 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; + + smbd_tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + /* - * TODO: cancel all outstanding requests on the session + * 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 tevent_req_post(req, ev); + return; } /* @@ -857,7 +933,6 @@ static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, talloc_steal(state->smb2req, state->smb2req->session); tevent_req_done(req); - return tevent_req_post(req, ev); } static NTSTATUS smbd_smb2_logoff_recv(struct tevent_req *req) -- 1.9.0.279.gdc9e3eb From b0c24890ecf5bb40e77b4ac224a2f5a5881dbfcf Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 10 Mar 2014 09:53:18 +0100 Subject: [PATCH 09/11] 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 Reviewed-by: Jeremy Allison (cherry picked from commit 95f96a81083be578ee638c0cebd590228d9a4424) --- source3/smbd/smb2_tcon.c | 83 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index b1e1fc6..ef2e318 100644 --- a/source3/smbd/smb2_tcon.c +++ b/source3/smbd/smb2_tcon.c @@ -26,6 +26,7 @@ #include "auth.h" #include "lib/param/loadparm.h" #include "../lib/util/tevent_ntstatus.h" +#include "lib/smbd_tevent_queue.h" static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -485,15 +486,19 @@ static void smbd_smb2_request_tdis_done(struct tevent_req *subreq) 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; - NTSTATUS status; + struct tevent_req *subreq; + struct smbd_smb2_request *preq; req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_tdis_state); @@ -502,19 +507,89 @@ static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, } 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 = smbd_tevent_queue_wait_send(preq, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } + /* - * TODO: cancel all outstanding requests on the tcon + * 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 = smbd_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; + + smbd_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 tevent_req_post(req, ev); + return; } /* We did tear down the tcon. */ TALLOC_FREE(state->smb2req->tcon); tevent_req_done(req); - return tevent_req_post(req, ev); } static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req) -- 1.9.0.279.gdc9e3eb From 77468db92e0b1cd46c61234b6406aff9ec5e8a5f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 28 Jan 2014 14:07:26 -0800 Subject: [PATCH 10/11] 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 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (cherry picked from commit 3a727d5d39bab05fa7237e32ffe244ddfebb0ee0) --- source4/torture/smb2/notify.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c index e83b099..83ed7cb 100644 --- a/source4/torture/smb2/notify.c +++ b/source4/torture/smb2/notify.c @@ -69,6 +69,13 @@ goto done; \ }} while (0) +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(torture->ev) != 0) { \ + break; \ + } \ + } + #define BASEDIR "test_notify" #define FNAME "smb2-notify01.dat" @@ -1244,8 +1251,7 @@ done: */ static bool torture_smb2_notify_ulogoff(struct torture_context *torture, - struct smb2_tree *tree1, - struct smb2_tree *tree2) + struct smb2_tree *tree1) { bool ret = true; NTSTATUS status; @@ -1276,11 +1282,11 @@ static bool torture_smb2_notify_ulogoff(struct torture_context *torture, io.smb2.in.security_flags = 0; io.smb2.in.fname = BASEDIR; - status = smb2_create(tree2, torture, &(io.smb2)); + status = smb2_create(tree1, torture, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; - status = smb2_create(tree2, torture, &(io.smb2)); + status = smb2_create(tree1, torture, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.smb2.out.file.handle; @@ -1295,7 +1301,9 @@ static bool torture_smb2_notify_ulogoff(struct torture_context *torture, req = smb2_notify_send(tree1, &(notify.smb2)); - status = smb2_logoff(tree2->session); + WAIT_FOR_ASYNC_RESPONSE(req); + + status = smb2_logoff(tree1->session); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_notify_recv(req, torture, &(notify.smb2)); @@ -1992,7 +2000,7 @@ struct torture_suite *torture_smb2_notify_init(void) torture_suite_add_2smb2_test(suite, "mask", torture_smb2_notify_mask); torture_suite_add_1smb2_test(suite, "tdis", torture_smb2_notify_tree_disconnect); torture_suite_add_2smb2_test(suite, "mask-change", torture_smb2_notify_mask_change); - torture_suite_add_2smb2_test(suite, "logoff", torture_smb2_notify_ulogoff); + torture_suite_add_1smb2_test(suite, "logoff", torture_smb2_notify_ulogoff); torture_suite_add_1smb2_test(suite, "tree", torture_smb2_notify_tree); torture_suite_add_2smb2_test(suite, "basedir", torture_smb2_notify_basedir); torture_suite_add_2smb2_test(suite, "double", torture_smb2_notify_double); -- 1.9.0.279.gdc9e3eb From 5350fd1a36699b202872c59b3b7871d5e96a895f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 24 Feb 2014 10:44:59 -0800 Subject: [PATCH 11/11] 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 Reviewed-by: Stefan Metzmacher Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed Mar 12 20:12:58 CET 2014 on sn-devel-104 (cherry picked from commit 9c677fff0bb0abc8d19dd67c33b3e044b1a9862e) --- source4/torture/smb2/notify.c | 68 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c index 83ed7cb..a737661 100644 --- a/source4/torture/smb2/notify.c +++ b/source4/torture/smb2/notify.c @@ -1195,7 +1195,7 @@ static bool torture_smb2_notify_tree_disconnect( smb2_deltree(tree, BASEDIR); smb2_util_rmdir(tree, BASEDIR); - torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY " + torture_comment(torture, "TESTING CHANGE NOTIFY+CANCEL FOLLOWED BY " "TREE-DISCONNECT\n"); /* @@ -1247,6 +1247,71 @@ done: } /* + testing of change notifies followed by a tdis - no cancel +*/ + +static bool torture_smb2_notify_tree_disconnect_1( + struct torture_context *torture, + struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + + smb2_deltree(tree, BASEDIR); + smb2_util_rmdir(tree, BASEDIR); + + torture_comment(torture, "TESTING CHANGE NOTIFY ASYNC FOLLOWED BY " + "TREE-DISCONNECT\n"); + + /* + get a handle on the directory + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_ALL; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR; + + status = smb2_create(tree, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify.smb2); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = h1; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree, &(notify.smb2)); + WAIT_FOR_ASYNC_RESPONSE(req); + + status = smb2_tdis(tree); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_VAL(notify.smb2.out.num_changes, 0); + +done: + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* basic testing of change notifies followed by a ulogoff */ @@ -1999,6 +2064,7 @@ struct torture_suite *torture_smb2_notify_init(void) torture_suite_add_2smb2_test(suite, "dir", torture_smb2_notify_dir); torture_suite_add_2smb2_test(suite, "mask", torture_smb2_notify_mask); torture_suite_add_1smb2_test(suite, "tdis", torture_smb2_notify_tree_disconnect); + torture_suite_add_1smb2_test(suite, "tdis1", torture_smb2_notify_tree_disconnect_1); torture_suite_add_2smb2_test(suite, "mask-change", torture_smb2_notify_mask_change); torture_suite_add_1smb2_test(suite, "logoff", torture_smb2_notify_ulogoff); torture_suite_add_1smb2_test(suite, "tree", torture_smb2_notify_tree); -- 1.9.0.279.gdc9e3eb