From 6e77c70db2beede61103ca24fb87ede4d3e9fc9a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 5 Mar 2014 14:07:11 -0800 Subject: [PATCH 1/4] s3: smbd: SMB2 server. Add a generic wait_queue mechanism. NTSTATUS smbd_smb2_wait_for_request_to_complete( TALLOC_CTX *mem_ctx, struct smbd_smb2_request *smb2req_to_wait_for, struct tevent_context *ev, void (*callback_fn)(struct tevent_req *), void *priv_ptr) Parameters are: TALLOC_CTX *mem_ctx - Context for wait struct (Usually waiting SMB2 request struct). struct smbd_smb2_request *smb2req_to_wait_for - SMB2 request to wait for. struct tevent_context *ev - Event context. void (*callback_fn)(struct tevent_req *) - Callback Function to call once request has completed. void *priv_ptr - Private pointer passed to callback. Allows an SMB2 request to suspend itself via registering a wait request using tevent_wait_send() on another SMB2 request. The suspending request will be awoken only when the second SMB2 request completes and sends its reply back to the client. Signed-off-by: Jeremy Allison --- source3/smbd/globals.h | 45 +++++++++++++++++++ source3/smbd/smb2_server.c | 105 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 3baa048..f25667f 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -254,6 +254,27 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req); size_t smbd_smb2_unread_bytes(struct smbd_smb2_request *req); void remove_smb2_chained_fsp(files_struct *fsp); +/************************************************************* + Add a tevent request to the wait queue associated with an + SMB2 request. Will get notified once the SMB2 request completes. + + Parameters are: + + TALLOC_CTX *mem_ctx - Context for wait queue. + struct smbd_smb2_request *smb2req_to_wait_for - SMB2 request to wait for. + struct tevent_context *ev, - Event context. + void (*callback_fn)(struct tevent_req *), - Callback Function to call + once request has completed. + void *priv_ptr - Private pointer passed to + callback. +*************************************************************/ + +NTSTATUS smbd_smb2_wait_for_request_to_complete( + TALLOC_CTX *mem_ctx, + struct smbd_smb2_request *smb2req_to_wait_for, + struct tevent_context *ev, + void (*callback_fn)(struct tevent_req *), + void *priv_ptr); NTSTATUS smbd_smb2_request_verify_creditcharge(struct smbd_smb2_request *req, uint32_t data_length); @@ -480,6 +501,23 @@ struct smbd_smb2_send_queue { TALLOC_CTX *mem_ctx; }; +struct smb2_request_wait_list { + struct smb2_request_wait_list *prev, *next; + + /* + * Back pointer to smbd_smb2_request we're + * waiting on so we can remove ourselves + * from the list. + */ + struct smbd_smb2_request *wait_smb2req; + /* + * Waiting event to trigger once the enclosing + * struct smbd_smb2_request has sent out a reply. + * This pointer is created by tevent_wait_send(). + */ + struct tevent_req *wait_req; +}; + struct smbd_smb2_request { struct smbd_smb2_request *prev, *next; @@ -487,6 +525,13 @@ struct smbd_smb2_request { struct smbd_smb2_send_queue queue_entry; + /* + * A list of wait events that represent + * other SMB2 requests waiting for this + * request to complete. + */ + struct smb2_request_wait_list *wait_list; + /* the session the request operates on, maybe NULL */ struct smbXsrv_session *session; uint64_t last_session_id; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 3c46efd..1809254 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -29,6 +29,7 @@ #include "../lib/util/bitmap.h" #include "../librpc/gen_ndr/krb5pac.h" #include "auth.h" +#include "lib/tevent_wait.h" static void smbd_smb2_connection_handler(struct tevent_context *ev, struct tevent_fd *fde, @@ -1161,6 +1162,13 @@ static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *re newreq->do_signing = req->do_signing; newreq->current_idx = req->current_idx; + /* + * Note we never duplicate the wait list of + * SMB2 requests to awaken. The wait list + * is associated with the SMB2 request left + * on the request queue. + */ + outvec = talloc_zero_array(newreq, struct iovec, count); if (!outvec) { TALLOC_FREE(newreq); @@ -1606,6 +1614,99 @@ static void smbd_smb2_request_pending_timer(struct tevent_context *ev, } } +/************************************************************* + Destructor for a wait list entry. + Ensures we are removed from any linked list + associated with an SMB2 request we may still be on. + Only matters if the request to be awoken finishes + before the request doing the wake-up. +*************************************************************/ + +static int smbd_smb2_cleanup_wait_list_entry(struct smb2_request_wait_list *pwe) +{ + if (pwe->wait_smb2req) { + /* + * This is only the case when the SMB2 request to + * be awakened has finished and is being freed before + * the SMB2 request that should awaken us. This should + * be a very rare occurrence. + */ + DLIST_REMOVE(pwe->wait_smb2req->wait_list, pwe); + } + return 0; +} + +/************************************************************* + Add a tevent request to the wait queue associated with an + SMB2 request. Will get notified once the SMB2 request completes. + + Parameters are: + + TALLOC_CTX *mem_ctx - Context for wait queue. + struct smbd_smb2_request *smb2req_to_wait_for - SMB2 request to wait for. + struct tevent_context *ev, - Event context. + void (*callback_fn)(struct tevent_req *), - Callback Function to call + once request has completed. + void *priv_ptr - Private pointer passed to + callback. +*************************************************************/ + +NTSTATUS smbd_smb2_wait_for_request_to_complete( + TALLOC_CTX *mem_ctx, + struct smbd_smb2_request *smb2req_to_wait_for, + struct tevent_context *ev, + void (*callback_fn)(struct tevent_req *), + void *priv_ptr) +{ + struct smb2_request_wait_list *pwe = talloc_zero(mem_ctx, + struct smb2_request_wait_list); + if (pwe == NULL) { + return NT_STATUS_NO_MEMORY; + } + + pwe->wait_req = tevent_wait_send(pwe, ev); + if (pwe->wait_req == NULL) { + TALLOC_FREE(pwe); + return NT_STATUS_NO_MEMORY; + } + tevent_req_set_callback(pwe->wait_req, + callback_fn, + priv_ptr); + pwe->wait_smb2req = smb2req_to_wait_for; + DLIST_ADD_END(smb2req_to_wait_for->wait_list, pwe, + struct smb2_request_wait_list *); + + /* + * The following is needed in case the SMB2 request + * to_awaken finishes and is deleted before the + * request we want to awaken us. + */ + talloc_set_destructor(pwe, smbd_smb2_cleanup_wait_list_entry); + return NT_STATUS_OK; +} + +/************************************************************* + Awaken all tevent requests that were waiting for this + SMB2 request to complete. +*************************************************************/ + +static void smbd_smb2_awaken_waiting_requests(struct smbd_smb2_request *req) +{ + struct smb2_request_wait_list *pwl_next; + struct smb2_request_wait_list *pwl; + + for (pwl = req->wait_list; pwl; pwl = pwl_next) { + pwl_next = pwl->next; + + tevent_wait_done(pwl->wait_req); + + DLIST_REMOVE(req->wait_list, pwl); + /* Esnure the destructor is safe to call. */ + pwl->wait_smb2req = NULL; + pwl->next = pwl->prev = NULL; + } +} + static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) { struct smbd_server_connection *sconn = req->sconn; @@ -1627,6 +1728,8 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) * cancel requests never have a response */ DLIST_REMOVE(req->sconn->smb2.requests, req); + /* Wake all other requests waiting for completion. */ + smbd_smb2_awaken_waiting_requests(req); TALLOC_FREE(req); for (cur = sconn->smb2.requests; cur; cur = cur->next) { @@ -2483,6 +2586,8 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) * move it off the "being processed" queue. */ DLIST_REMOVE(req->sconn->smb2.requests, req); + /* Wake all other requests waiting for completion. */ + smbd_smb2_awaken_waiting_requests(req); req->queue_entry.mem_ctx = req; req->queue_entry.vector = req->out.vector; -- 1.9.0.279.gdc9e3eb From 502d33a9d12e926fd649e263dd28d12473c8b0c4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 13 Jan 2014 14:12:18 -0800 Subject: [PATCH 2/4] s3: SMB2 logoff - factor code out into a function remove_outstanding_session_refs(). We will be reusing this. [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/smbd/smb2_sesssetup.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 786bfe8..8bf7322 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -455,10 +455,30 @@ static int pp_self_ref_destructor(struct smbd_smb2_session_setup_state **pp_stat return 0; } -static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_setup_state *state) +static void remove_outstanding_session_refs(struct smbd_smb2_request *smb2req) { struct smbd_smb2_request *preq; + for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq == smb2req) { + /* Don't remove the session from the current + request in flight. */ + continue; + } + if (preq->session == smb2req->session) { + preq->session = NULL; + /* + * If we no longer have a session we can't + * sign or encrypt replies. + */ + preq->do_signing = false; + preq->do_encryption = false; + } + } +} + +static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_setup_state *state) +{ /* * If state->session is not NULL, * we move the session from the session table to the request on failure @@ -479,21 +499,7 @@ static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_set * to it. */ - for (preq = state->smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { - if (preq == state->smb2req) { - continue; - } - if (preq->session == state->smb2req->session) { - preq->session = NULL; - /* - * If we no longer have a session we can't - * sign or encrypt replies. - */ - preq->do_signing = false; - preq->do_encryption = false; - } - } - + remove_outstanding_session_refs(state->smb2req); return 0; } -- 1.9.0.279.gdc9e3eb From bf486b178222582d416dc017a5e04b30b7913e37 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 13 Jan 2014 14:31:31 -0800 Subject: [PATCH 3/4] s3: smbd: Make SMB2 ulogoff asnyc by waiting for outstanding requests. [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/smbd/smb2_sesssetup.c | 196 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 191 insertions(+), 5 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 8bf7322..e5f35ec 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/tevent_wait.h" static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -868,8 +869,13 @@ static void smbd_smb2_request_logoff_done(struct tevent_req *subreq) struct smbd_smb2_logout_state { struct smbd_smb2_request *smb2req; + struct tevent_context *ev; }; +static NTSTATUS wait_for_outstanding_requests(struct smbd_smb2_logout_state *state, + struct tevent_req *req); +static NTSTATUS try_to_tear_down_session(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) @@ -878,29 +884,209 @@ static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, struct smbd_smb2_logout_state *state; NTSTATUS status; + /* + * Prevent new users of this session. + */ + smb2req->session->status = NT_STATUS_USER_SESSION_DELETED; + req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_logout_state); if (req == NULL) { return NULL; } state->smb2req = smb2req; + state->ev = ev; + + status = wait_for_outstanding_requests(state, req); /* - * TODO: cancel all outstanding requests on the session + * NT_STATUS_RETRY means we're waiting + * for a cancelled request to complete. */ - status = smbXsrv_session_logoff(state->smb2req->session); + + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + return req; + } + + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("waiting for requests failed: %s\n", + nt_errstr(status))); + return tevent_req_post(req, ev); + } + + status = try_to_tear_down_session(state->smb2req); if (tevent_req_nterror(req, status)) { + DEBUG(0, ("tearing down session failed: %s\n", + nt_errstr(status))); return tevent_req_post(req, ev); } + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +/******************************************************** + Walk the outstanding SMB2 requests and return the + first one (not including the current request being processed) + on this session. Return NULL if there are no outstanding + requests on this session. +********************************************************/ + +static struct smbd_smb2_request *find_outstanding_session_request( + const struct smbd_smb2_request *smb2req) +{ + struct smbd_smb2_request *preq; + + if (smb2req->session == NULL) { + return NULL; + } + 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 get here we've found a request + * request we're going to cancel. + */ + break; + } + return preq; +} + +static void smbd_smb2_logoff_do(struct tevent_req *subreq); + +/*************************************************************** + Utility function to wait for outstanding SMB2 requests on + this session. + If we return NT_STATUS_RETRY we have set ourselves to wait + for another request to complete. +***************************************************************/ + +static NTSTATUS wait_for_outstanding_requests(struct smbd_smb2_logout_state *state, + struct tevent_req *req) +{ + struct smbd_smb2_request *smb2req = state->smb2req; + struct smbd_smb2_request *smb2_wait_req; + NTSTATUS status; + + /* + * Try and find a request to cancel. If we can, + * cancel it and tell ourselves to wait unti it's finished. + */ + smb2_wait_req = find_outstanding_session_request(smb2req); + if (smb2_wait_req != NULL) { + + if (!smb2_wait_req->compound_related && + smb2_wait_req->subreq != NULL) { + /* + * Only cancel non-compound requests. + * Way too hard to deal with the result + * otherwise. We still wait for them + * though. + */ + tevent_req_cancel(smb2_wait_req->subreq); + } + + status = smbd_smb2_wait_for_request_to_complete( + smb2req, /* Talloc context. */ + smb2_wait_req, /* Request we're waiting on. */ + state->ev, /* Event context. */ + smbd_smb2_logoff_do, /* callback. */ + req); /* Private pointer. */ + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("logoff wait failed: %s\n", + nt_errstr(status))); + return status; + } + /* We're waiting on another SMB2 request. */ + return NT_STATUS_RETRY; + } + + return NT_STATUS_OK; +} + +/*************************************************************** + Utility function to do the work of tearing down a session. +***************************************************************/ + +static NTSTATUS try_to_tear_down_session(struct smbd_smb2_request *smb2req) +{ + NTSTATUS status = smbXsrv_session_logoff(smb2req->session); + if (!NT_STATUS_IS_OK(status)) { + return status; + } /* - * we may need to sign the response, so we need to keep + * 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); + talloc_steal(smb2req, smb2req->session); + /* + * The return from smbXsrv_session_logoff() + * leaves all outstanding pointers to + * smb2req->session not on this request in an + * undefined state, ensure we don't try and + * access any req->session pointers once + * we return. + */ + remove_outstanding_session_refs(smb2req); + return NT_STATUS_OK; +} + +static void smbd_smb2_logoff_do(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; + int ret; + ret = tevent_wait_recv(subreq); + TALLOC_FREE(subreq); + if (ret != 0) { + DEBUG(10, ("tevent_wait_recv returned %s\n", + strerror(ret))); + /* + * Continue anyway, this should never happen + */ + } + + /* Should we still wait ? */ + status = wait_for_outstanding_requests(state, req); + + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + /* + * NT_STATUS_RETRY means we're waiting + * for another cancelled request to complete. + */ + return; + } + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("waiting for requetsts failed: %s\n", + nt_errstr(status))); + return; + } + + /* + * 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 = try_to_tear_down_session(state->smb2req); + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("tearing down session failed: %s\n", + nt_errstr(status))); + return; + } 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 1e7d8d6ad85ea1db490e590a93cbc5661d316fd4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 6 Mar 2014 15:10:19 -0800 Subject: [PATCH 4/4] s3: smbd: Make SMB2 tdis asnyc by waiting for outstanding requests. [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/smbd/smb2_tcon.c | 194 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 189 insertions(+), 5 deletions(-) diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index 0e9c6f6..bf5d9cc 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/tevent_wait.h" static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -485,8 +486,13 @@ static void smbd_smb2_request_tdis_done(struct tevent_req *subreq) struct smbd_smb2_tdis_state { struct smbd_smb2_request *smb2req; + struct tevent_context *ev; }; +static NTSTATUS wait_for_outstanding_requests(struct smbd_smb2_tdis_state *state, + struct tevent_req *req); +static NTSTATUS try_to_tear_down_tcon(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) @@ -495,28 +501,206 @@ static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, struct smbd_smb2_tdis_state *state; NTSTATUS status; + /* + * Make sure that no new request will be able to use this tcon. + */ + smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED; + req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_tdis_state); if (req == NULL) { return NULL; } state->smb2req = smb2req; + state->ev = ev; + + status = wait_for_outstanding_requests(state, req); /* - * TODO: cancel all outstanding requests on the tcon + * NT_STATUS_RETRY means we're waiting + * for a cancelled request to complete. */ - status = smbXsrv_tcon_disconnect(state->smb2req->tcon, - state->smb2req->tcon->compat->vuid); + + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + return req; + } + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("waiting for requests failed: %s\n", + nt_errstr(status))); return tevent_req_post(req, ev); } - /* We did tear down the tcon. */ - TALLOC_FREE(state->smb2req->tcon); + status = try_to_tear_down_tcon(state->smb2req); + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("tearing down the tcon failed: %s\n", + nt_errstr(status))); + return tevent_req_post(req, ev); + } + /* Either way, we're done. */ tevent_req_done(req); return tevent_req_post(req, ev); } +/******************************************************** + Walk the outstanding SMB2 requests and return the + first one (not including the current request being processed) + on this tcon. Return NULL if there are no outstanding + requests on this session. +********************************************************/ + +static struct smbd_smb2_request *find_outstanding_tcon_request( + const struct smbd_smb2_request *smb2req) +{ + struct smbd_smb2_request *preq; + + if (smb2req->tcon == NULL) { + return NULL; + } + 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; + } + /* + * If we get here we've found a request + * we're going to cancel. + */ + break; + } + return preq; +} + +static void smbd_smb2_tdis_do(struct tevent_req *subreq); + +/*************************************************************** + Utility function to wait for outstanding SMB2 requests on + this tcon. + If we return NT_STATUS_RETRY we have set ourselves to wait + for another request to complete. +***************************************************************/ + +static NTSTATUS wait_for_outstanding_requests(struct smbd_smb2_tdis_state *state, + struct tevent_req *req) +{ + struct smbd_smb2_request *smb2req = state->smb2req; + struct smbd_smb2_request *smb2_wait_req; + NTSTATUS status; + + /* + * Try and find a request to cancel. If we can, + * cancel it and tell ourselves to wait unti it's finished. + */ + smb2_wait_req = find_outstanding_tcon_request(smb2req); + if (smb2_wait_req != NULL) { + + if (!smb2_wait_req->compound_related && + smb2_wait_req->subreq != NULL) { + /* + * Only cancel non-compound requests. + * Way too hard to deal with the result + * otherwise. We still wait for them + * though. + */ + tevent_req_cancel(smb2_wait_req->subreq); + } + + status = smbd_smb2_wait_for_request_to_complete( + smb2req, /* Talloc context. */ + smb2_wait_req, /* Request we're waiting on. */ + state->ev, /* Event context. */ + smbd_smb2_tdis_do, /* callback. */ + req); /* Private pointer. */ + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("tdis wait failed: %s\n", + nt_errstr(status))); + return status; + } + /* We're waiting on another SMB2 request. */ + return NT_STATUS_RETRY; + } + return NT_STATUS_OK; +} + +/*************************************************************** + Utility function to do the work of tearing down a connection. +***************************************************************/ + +static NTSTATUS try_to_tear_down_tcon(struct smbd_smb2_request *smb2req) +{ + NTSTATUS status = smbXsrv_tcon_disconnect(smb2req->tcon, smb2req->tcon->compat->vuid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smbXsrv_tcon_disconnect() failed: %s\n", + nt_errstr(status))); + return status; + } + + TALLOC_FREE(smb2req->tcon); + return NT_STATUS_OK; +} + +/*************************************************************** + Wait callback function called when we were woken up by an + SMB2 request we were waiting on completing. Checks to see + if we should still wait, and goes back to sleep if so, + otherwise tears down the tcon and finishes the request. +***************************************************************/ + +static void smbd_smb2_tdis_do(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; + int ret; + + ret = tevent_wait_recv(subreq); + TALLOC_FREE(subreq); + if (ret != 0) { + DEBUG(10, ("tevent_wait_recv returned %s\n", + strerror(ret))); + /* + * Continue anyway, this should never happen + */ + } + + status = wait_for_outstanding_requests(state, req); + + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + /* + * NT_STATUS_RETRY means we're waiting + * for another cancelled request to complete. + */ + return; + } + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("waiting for requests failed: %s\n", + nt_errstr(status))); + return; + } + + /* + * 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 = try_to_tear_down_tcon(state->smb2req); + if (tevent_req_nterror(req, status)) { + DEBUG(0, ("tearing down the tcon failed: %s\n", + nt_errstr(status))); + return; + } + /* Either way we're done. */ + tevent_req_done(req); +} + static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req) { return tevent_req_simple_recv_ntstatus(req); -- 1.9.0.279.gdc9e3eb