The Samba-Bugzilla – Attachment 9780 Details for
Bug 10344
SessionLogoff on a signed connection with an outstanding notify request crashes smbd.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
git-am patch for 4.0.x. Backport from master.
bug-10344-4.0.patchset (text/plain), 37.17 KB, created by
Jeremy Allison
on 2014-03-17 19:03:18 UTC
(
hide
)
Description:
git-am patch for 4.0.x. Backport from master.
Filename:
MIME Type:
Creator:
Jeremy Allison
Created:
2014-03-17 19:03:18 UTC
Size:
37.17 KB
patch
obsolete
>From a17e10fd5e3510547b2eff4bfe36a08545dd259c Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >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 <metze@samba.org> >Reviewed-by: Volker Lendecke <vl@samba.org> >(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 9985669e858944cb9e5af975a4fdc9a19dea383e Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >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 <jra@samba.org> >--- > source3/Makefile.in | 2 +- > source3/lib/smbd_tevent_queue.c | 83 +++++++++++++++++++++++++++++++++++++++++ > source3/lib/smbd_tevent_queue.h | 34 +++++++++++++++++ > source3/wscript_build | 1 + > 4 files changed, 119 insertions(+), 1 deletion(-) > create mode 100644 source3/lib/smbd_tevent_queue.c > create mode 100644 source3/lib/smbd_tevent_queue.h > >diff --git a/source3/Makefile.in b/source3/Makefile.in >index 96727fc..ad3112c 100644 >--- a/source3/Makefile.in >+++ b/source3/Makefile.in >@@ -495,7 +495,7 @@ LIB_OBJ = $(LIBSAMBAUTIL_OBJ) $(UTIL_OBJ) $(CRYPTO_OBJ) $(LIBTSOCKET_OBJ) \ > lib/fncall.o \ > libads/krb5_errs.o lib/system_smbd.o lib/audit.o $(LIBNDR_OBJ) \ > lib/file_id.o lib/idmap_cache.o \ >- lib/tevent_wait.o \ >+ lib/tevent_wait.o lib/smbd_tevent_queue.o \ > ../libcli/security/dom_sid.o ../libcli/security/security_descriptor.o \ > ../libcli/security/security_token.o ../libcli/security/util_sid.o \ > ../libcli/smb/util.o ../lib/util/idtree.o >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 <http://www.gnu.org/licenses/>. >+*/ >+ >+/* 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 <http://www.gnu.org/licenses/>. >+*/ >+ >+/* 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 099351dba93c63d79ab82751811df93223e47134 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >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 <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(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 658b2c211e7d4b495527295a23e780cccbf09ea0 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >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 <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(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 ea56cdaf4dc62bc97ef5db962dc81a183776d568 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >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 <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(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 2a74fd3ba326c0a131e14070b39fbd336c4d4314 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >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 <metze@samba.org> > >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 506817dfc9d18c2c5c35d60a6e61a82917665e2d) >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > 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 ccab55bbfef95586d6c8ac3f42d35a9a853d91e9 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >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 <metze@samba.org> > >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 195c2d84807a7580e90e288cc813a6c6ca596055) >Signed-off-by: Jeremy Allison <jra@samba.org> >--- > 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 1ac63dd1e72bf6f020f9e1628015d79e67f740f7 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >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 <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(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 0ddaf9d7ee07393c227a8872143cf1341684809b Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >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 <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(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 ec3c71919f45fba58b286c1f5e8611c2284ba11e Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >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 <metze@samba.org> > >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(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 b16825764280407aa00b1c472c0234bdd05815c1 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >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 <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> > >Autobuild-User(master): Jeremy Allison <jra@samba.org> >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 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Flags:
metze
:
review-
Actions:
View
Attachments on
bug 10344
:
9547
|
9619
|
9620
|
9736
|
9761
|
9770
|
9772
|
9773
|
9780
|
9815
|
9816
|
9817
|
9818