From 02c37ff845fbf3bea4cb14738064af88d3ad7e41 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:04:55 +0200 Subject: [PATCH 01/18] s3:smbd: add a smbd_notify_cancel_by_map() helper function Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 2c47fb16089602a42f62124520e58bdcd8c7d053) --- source3/smbd/notify.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c index ac1a55c..0a0ae83 100644 --- a/source3/smbd/notify.c +++ b/source3/smbd/notify.c @@ -374,6 +374,17 @@ static void change_notify_remove_request(struct smbd_server_connection *sconn, TALLOC_FREE(req); } +static void smbd_notify_cancel_by_map(struct notify_mid_map *map) +{ + struct smb_request *smbreq = map->req->req; + struct smbd_server_connection *sconn = smbreq->sconn; + NTSTATUS notify_status = NT_STATUS_CANCELLED; + + change_notify_reply(smbreq, notify_status, + 0, NULL, map->req->reply_fn); + change_notify_remove_request(sconn, map->req); +} + /**************************************************************************** Delete entries by mid from the change notify pending queue. Always send reply. *****************************************************************************/ @@ -393,9 +404,7 @@ void remove_pending_change_notify_requests_by_mid( return; } - change_notify_reply(map->req->req, - NT_STATUS_CANCELLED, 0, NULL, map->req->reply_fn); - change_notify_remove_request(sconn, map->req); + smbd_notify_cancel_by_map(map); } void smbd_notify_cancel_by_smbreq(const struct smb_request *smbreq) @@ -413,9 +422,7 @@ void smbd_notify_cancel_by_smbreq(const struct smb_request *smbreq) return; } - change_notify_reply(map->req->req, - NT_STATUS_CANCELLED, 0, NULL, map->req->reply_fn); - change_notify_remove_request(sconn, map->req); + smbd_notify_cancel_by_map(map); } /**************************************************************************** -- 1.9.1 From 5691b0db272a1e2ec8c3094259ba25efe1790cd8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:02:38 +0200 Subject: [PATCH 02/18] s3:smbd: use STATUS_NOTIFY_CLEANUP when closing a smb2 directory handle Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit a5981d137461e5715c92a4fb4cdeaa650f34e999) --- selftest/knownfail | 1 - source3/smbd/close.c | 15 +++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/selftest/knownfail b/selftest/knownfail index 2c35a58..febbd2e 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -188,7 +188,6 @@ ^samba3.smb2.create.blob ^samba3.smb2.create.open ^samba3.smb2.notify.valid-req -^samba3.smb2.notify.dir ^samba3.smb2.notify.rec ^samba3.smb2.durable-open.delete_on_close2 ^samba3.smb2.durable-v2-open.app-instance diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 4fbd442f..fc1d380 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -1050,6 +1050,13 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, NTSTATUS status1 = NT_STATUS_OK; const struct security_token *del_nt_token = NULL; const struct security_unix_token *del_token = NULL; + NTSTATUS notify_status; + + if (fsp->conn->sconn->using_smb2) { + notify_status = STATUS_NOTIFY_CLEANUP; + } else { + notify_status = NT_STATUS_OK; + } /* * NT can set delete_on_close of the last open @@ -1159,8 +1166,8 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, * now fail as the directory has been deleted. */ - if(NT_STATUS_IS_OK(status)) { - remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING); + if (NT_STATUS_IS_OK(status)) { + notify_status = NT_STATUS_DELETE_PENDING; } } else { if (!del_share_mode(lck, fsp)) { @@ -1169,10 +1176,10 @@ static NTSTATUS close_directory(struct smb_request *req, files_struct *fsp, } TALLOC_FREE(lck); - remove_pending_change_notify_requests_by_fid( - fsp, NT_STATUS_OK); } + remove_pending_change_notify_requests_by_fid(fsp, notify_status); + status1 = fd_close(fsp); if (!NT_STATUS_IS_OK(status1)) { -- 1.9.1 From 97be5bc01bc54ebdb15960c7ebc31653c4ed0a15 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:02:38 +0200 Subject: [PATCH 03/18] s3:smbd: use STATUS_NOTIFY_CLEANUP on smb2 logoff (explicit and implicit) and tdis Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 5169e9b20c69092d04b596f48ca0e69a46af438f) --- source3/smbd/notify.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c index 0a0ae83..0e4969d 100644 --- a/source3/smbd/notify.c +++ b/source3/smbd/notify.c @@ -378,8 +378,22 @@ static void smbd_notify_cancel_by_map(struct notify_mid_map *map) { struct smb_request *smbreq = map->req->req; struct smbd_server_connection *sconn = smbreq->sconn; + struct smbd_smb2_request *smb2req = smbreq->smb2req; NTSTATUS notify_status = NT_STATUS_CANCELLED; + if (smb2req != NULL) { + if (smb2req->session == NULL) { + notify_status = STATUS_NOTIFY_CLEANUP; + } else if (!NT_STATUS_IS_OK(smb2req->session->status)) { + notify_status = STATUS_NOTIFY_CLEANUP; + } + if (smb2req->tcon == NULL) { + notify_status = STATUS_NOTIFY_CLEANUP; + } else if (!NT_STATUS_IS_OK(smb2req->tcon->status)) { + notify_status = STATUS_NOTIFY_CLEANUP; + } + } + change_notify_reply(smbreq, notify_status, 0, NULL, map->req->reply_fn); change_notify_remove_request(sconn, map->req); -- 1.9.1 From 9444e35a1dba683fdbbeea8c1dcdcbef38f0e370 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:19:42 +0200 Subject: [PATCH 04/18] s4:torture/smb2: verify STATUS_NOTIFY_CLEANUP return value Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 4bfa6b024e530694741c7c07171fa09762578389) --- source4/torture/smb2/notify.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c index c721d5f..827f5d5 100644 --- a/source4/torture/smb2/notify.c +++ b/source4/torture/smb2/notify.c @@ -1304,6 +1304,7 @@ static bool torture_smb2_notify_tree_disconnect_1( CHECK_STATUS(status, NT_STATUS_OK); status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, STATUS_NOTIFY_CLEANUP); CHECK_VAL(notify.smb2.out.num_changes, 0); done: @@ -1372,6 +1373,7 @@ static bool torture_smb2_notify_ulogoff(struct torture_context *torture, CHECK_STATUS(status, NT_STATUS_OK); status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, STATUS_NOTIFY_CLEANUP); CHECK_VAL(notify.smb2.out.num_changes, 0); done: -- 1.9.1 From e1a52244de571c8996429e4d7047cf1bd038ee39 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:20:50 +0200 Subject: [PATCH 05/18] s4:torture/smb2: add smb2.notify.close test Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 44f9e1052de81a0a3052997e7e19a01813fbec43) --- source4/torture/smb2/notify.c | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c index 827f5d5..e9cfeda 100644 --- a/source4/torture/smb2/notify.c +++ b/source4/torture/smb2/notify.c @@ -1313,6 +1313,75 @@ done: } /* + basic testing of change notifies followed by a close +*/ + +static bool torture_smb2_notify_close(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY ULOGOFF\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(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree1, 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(tree1, &(notify.smb2)); + + WAIT_FOR_ASYNC_RESPONSE(req); + + status = smb2_util_close(tree1, h1); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, STATUS_NOTIFY_CLEANUP); + CHECK_VAL(notify.smb2.out.num_changes, 0); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* basic testing of change notifies followed by a ulogoff */ @@ -2026,6 +2095,7 @@ struct torture_suite *torture_smb2_notify_init(void) 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, "close", torture_smb2_notify_close); 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); -- 1.9.1 From b072b09e5751a178f243c30a0c0c60a034125584 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:20:50 +0200 Subject: [PATCH 06/18] s4:torture/smb2: add smb2.notify.invalid-reauth test An invalid reauth closes the session. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit a8ec77e86b17213eeb6a51a835639d79e9486223) --- source4/torture/smb2/notify.c | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c index e9cfeda..fe496c8 100644 --- a/source4/torture/smb2/notify.c +++ b/source4/torture/smb2/notify.c @@ -1450,6 +1450,87 @@ done: return ret; } +/* + basic testing of change notifies followed by an invalid reauth +*/ + +static bool torture_smb2_notify_invalid_reauth(struct torture_context *torture, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + struct cli_credentials *invalid_creds; + + smb2_deltree(tree2, BASEDIR); + smb2_util_rmdir(tree2, BASEDIR); + + torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY invalid REAUTH\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(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree1, 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(tree1, &(notify.smb2)); + + WAIT_FOR_ASYNC_RESPONSE(req); + + invalid_creds = cli_credentials_init(torture); + torture_assert(torture, (invalid_creds != NULL), "talloc error"); + cli_credentials_set_username(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_domain(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_password(invalid_creds, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_realm(invalid_creds, NULL, CRED_SPECIFIED); + cli_credentials_set_workstation(invalid_creds, "", CRED_UNINITIALISED); + + status = smb2_session_setup_spnego(tree1->session, + invalid_creds, + 0 /* previous_session_id */); + CHECK_STATUS(status, NT_STATUS_LOGON_FAILURE); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, STATUS_NOTIFY_CLEANUP); + CHECK_VAL(notify.smb2.out.num_changes, 0); + +done: + smb2_deltree(tree2, BASEDIR); + return ret; +} + static void tcp_dis_handler(struct smb2_transport *t, void *p) { struct smb2_tree *tree = (struct smb2_tree *)p; @@ -2097,6 +2178,7 @@ struct torture_suite *torture_smb2_notify_init(void) torture_suite_add_2smb2_test(suite, "mask-change", torture_smb2_notify_mask_change); torture_suite_add_1smb2_test(suite, "close", torture_smb2_notify_close); torture_suite_add_1smb2_test(suite, "logoff", torture_smb2_notify_ulogoff); + torture_suite_add_2smb2_test(suite, "invalid-reauth", torture_smb2_notify_invalid_reauth); 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.1 From a4e0624bb3e8c65481f2890c648abae652291e35 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 09:57:03 +0200 Subject: [PATCH 07/18] s4:torture/smb2: add smb2.notify.session-reconnect test Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit f435c89d61385272bf5b79f82f6e1373908d2b94) --- source4/torture/smb2/notify.c | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c index fe496c8..5589559 100644 --- a/source4/torture/smb2/notify.c +++ b/source4/torture/smb2/notify.c @@ -1451,6 +1451,86 @@ done: } /* + basic testing of change notifies followed by a session reconnect +*/ + +static bool torture_smb2_notify_session_reconnect(struct torture_context *torture, + struct smb2_tree *tree1) +{ + bool ret = true; + NTSTATUS status; + union smb_notify notify; + union smb_open io; + struct smb2_handle h1; + struct smb2_request *req; + uint64_t previous_session_id = 0; + struct smb2_session *session2 = NULL; + + smb2_deltree(tree1, BASEDIR); + smb2_util_rmdir(tree1, BASEDIR); + + torture_comment(torture, "TESTING CHANGE NOTIFY FOLLOWED BY SESSION RECONNECT\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(tree1, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree1, 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(tree1, &(notify.smb2)); + + WAIT_FOR_ASYNC_RESPONSE(req); + + previous_session_id = smb2cli_session_current_id(tree1->session->smbXcli); + torture_assert(torture, torture_smb2_session_setup(torture, + tree1->session->transport, + previous_session_id, + torture, &session2), + "session setup with previous_session_id failed"); + + status = smb2_notify_recv(req, torture, &(notify.smb2)); + CHECK_STATUS(status, STATUS_NOTIFY_CLEANUP); + CHECK_VAL(notify.smb2.out.num_changes, 0); + + status = smb2_logoff(tree1->session); + CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); + + status = smb2_logoff(session2); + CHECK_STATUS(status, NT_STATUS_OK); +done: + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* basic testing of change notifies followed by an invalid reauth */ @@ -2178,6 +2258,7 @@ struct torture_suite *torture_smb2_notify_init(void) torture_suite_add_2smb2_test(suite, "mask-change", torture_smb2_notify_mask_change); torture_suite_add_1smb2_test(suite, "close", torture_smb2_notify_close); torture_suite_add_1smb2_test(suite, "logoff", torture_smb2_notify_ulogoff); + torture_suite_add_1smb2_test(suite, "session-reconnect", torture_smb2_notify_session_reconnect); torture_suite_add_2smb2_test(suite, "invalid-reauth", torture_smb2_notify_invalid_reauth); torture_suite_add_1smb2_test(suite, "tree", torture_smb2_notify_tree); torture_suite_add_2smb2_test(suite, "basedir", torture_smb2_notify_basedir); -- 1.9.1 From 4f002b94bccda20685fc19bb5f5e97c0236fec07 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:09:40 +0200 Subject: [PATCH 08/18] s3:smbXsrv_session: clear smb2req->session of pending requests in smbXsrv_session_destructor() This won't be needed typically needed as the caller is supposted to cancel the requests already, but this makes sure we don't keep dangling pointers. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 7fea42110596e8e9da0155d726aaa72223107fbd) --- source3/smbd/smbXsrv_session.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index 7eca968..14457cf 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -1060,6 +1060,29 @@ NTSTATUS smb2srv_session_close_previous_recv(struct tevent_req *req) static int smbXsrv_session_destructor(struct smbXsrv_session *session) { NTSTATUS status; + struct smbXsrv_connection *xconn = NULL; + + if (session->client != NULL) { + xconn = session->client->connections; + } + + for (; xconn != NULL; xconn = xconn->next) { + struct smbd_smb2_request *preq; + + for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq->session != session) { + continue; + } + + 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; + } + } status = smbXsrv_session_logoff(session); if (!NT_STATUS_IS_OK(status)) { -- 1.9.1 From 20a51d58f1881a099ea7ad109de0893341e34b40 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:17:34 +0200 Subject: [PATCH 09/18] s3:smbXsrv_session: clear smb2req->session of pending requests in smbXsrv_session_logoff_all_callback() smbXsrv_session_logoff_all_callback() is called when the last transport connection is gone, which means we won't need to sign any response... Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit b6c34a07760141bda3e78624d62eb556bb70da65) --- source3/smbd/smbXsrv_session.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index 14457cf..f38f950 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -1497,6 +1497,7 @@ static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec, TDB_DATA val; void *ptr = NULL; struct smbXsrv_session *session = NULL; + struct smbXsrv_connection *xconn = NULL; NTSTATUS status; val = dbwrap_record_get_value(local_rec); @@ -1513,6 +1514,28 @@ static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec, session = talloc_get_type_abort(ptr, struct smbXsrv_session); session->db_rec = local_rec; + + if (session->client != NULL) { + xconn = session->client->connections; + } + for (; xconn != NULL; xconn = xconn->next) { + struct smbd_smb2_request *preq; + + for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq->session != session) { + continue; + } + + 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; + } + } + status = smbXsrv_session_logoff(session); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_IS_OK(state->first_status)) { -- 1.9.1 From 6e9c22532c9fc97fe919eb8b247c6d155361adc5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 09:57:03 +0200 Subject: [PATCH 10/18] s3:smbXsrv_session: add smb2srv_session_shutdown_send/recv helper functions Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 4fceb4531ccd6bb1fd6ebd7b6eb5b894959bc010) --- source3/smbd/globals.h | 5 ++ source3/smbd/smbXsrv_session.c | 121 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index eacc5b3..2aed98e 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -535,6 +535,11 @@ struct smbXsrv_channel_global0; NTSTATUS smbXsrv_session_find_channel(const struct smbXsrv_session *session, const struct smbXsrv_connection *conn, struct smbXsrv_channel_global0 **_c); +struct tevent_req *smb2srv_session_shutdown_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXsrv_session *session, + struct smbd_smb2_request *current_req); +NTSTATUS smb2srv_session_shutdown_recv(struct tevent_req *req); NTSTATUS smbXsrv_session_logoff(struct smbXsrv_session *session); NTSTATUS smbXsrv_session_logoff_all(struct smbXsrv_connection *conn); NTSTATUS smb1srv_session_table_init(struct smbXsrv_connection *conn); diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index f38f950..375d8a2 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -1321,6 +1321,127 @@ NTSTATUS smbXsrv_session_find_channel(const struct smbXsrv_session *session, return NT_STATUS_USER_SESSION_DELETED; } +struct smb2srv_session_shutdown_state { + struct tevent_queue *wait_queue; +}; + +static void smb2srv_session_shutdown_wait_done(struct tevent_req *subreq); + +struct tevent_req *smb2srv_session_shutdown_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXsrv_session *session, + struct smbd_smb2_request *current_req) +{ + struct tevent_req *req; + struct smb2srv_session_shutdown_state *state; + struct tevent_req *subreq; + struct smbXsrv_connection *xconn = NULL; + size_t len = 0; + + /* + * Make sure that no new request will be able to use this session. + */ + session->status = NT_STATUS_USER_SESSION_DELETED; + + req = tevent_req_create(mem_ctx, &state, + struct smb2srv_session_shutdown_state); + if (req == NULL) { + return NULL; + } + + state->wait_queue = tevent_queue_create(state, "smb2srv_session_shutdown_queue"); + if (tevent_req_nomem(state->wait_queue, req)) { + return tevent_req_post(req, ev); + } + + for (xconn = session->client->connections; xconn != NULL; xconn = xconn->next) { + struct smbd_smb2_request *preq; + + for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq == current_req) { + /* Can't cancel current request. */ + continue; + } + if (preq->session != session) { + /* Request on different session. */ + continue; + } + + if (!NT_STATUS_IS_OK(xconn->transport.status)) { + 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; + + if (preq->subreq != NULL) { + tevent_req_cancel(preq->subreq); + } + continue; + } + + /* + * Never cancel anything in a compound + * request. Way too hard to deal with + * the result. + */ + if (!preq->compound_related && preq->subreq != NULL) { + tevent_req_cancel(preq->subreq); + } + + /* + * Now wait until the request is finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of the request will + * remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } + } + + len = tevent_queue_length(state->wait_queue); + if (len == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + /* + * Now we add our own waiter to the end of the queue, + * this way we get notified when all pending requests are finished + * and send to the socket. + */ + subreq = tevent_queue_wait_send(state, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smb2srv_session_shutdown_wait_done, req); + + return req; +} + +static void smb2srv_session_shutdown_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + + tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + + tevent_req_done(req); +} + +NTSTATUS smb2srv_session_shutdown_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + NTSTATUS smbXsrv_session_logoff(struct smbXsrv_session *session) { struct smbXsrv_session_table *table; -- 1.9.1 From 0e67689ba0fe8d865893503b38b5137d03f92f27 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:13:27 +0200 Subject: [PATCH 11/18] s3:smbXsrv_session: cancel pending requests when we logoff a previous session Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit cc9d52e10f4f1b192171e03674061d4e8e6bcc84) --- source3/smbd/smbXsrv_session.c | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index 375d8a2..d968008 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -222,6 +222,8 @@ static NTSTATUS smbXsrv_session_table_init(struct smbXsrv_connection *conn, return NT_STATUS_OK; } +static void smbXsrv_session_close_shutdown_done(struct tevent_req *subreq); + static void smbXsrv_session_close_loop(struct tevent_req *subreq) { struct smbXsrv_client *client = @@ -326,20 +328,22 @@ static void smbXsrv_session_close_loop(struct tevent_req *subreq) goto next; } - /* - * TODO: cancel all outstanding requests on the session - */ - status = smbXsrv_session_logoff(session); - if (!NT_STATUS_IS_OK(status)) { + subreq = smb2srv_session_shutdown_send(session, client->ev_ctx, + session, NULL); + if (subreq == NULL) { + status = NT_STATUS_NO_MEMORY; DEBUG(0, ("smbXsrv_session_close_loop: " - "smbXsrv_session_logoff(%llu) failed: %s\n", + "smb2srv_session_shutdown_send(%llu) failed: %s\n", (unsigned long long)session->global->session_wire_id, nt_errstr(status))); if (DEBUGLVL(1)) { NDR_PRINT_DEBUG(smbXsrv_session_closeB, &close_blob); } + goto next; } - TALLOC_FREE(session); + tevent_req_set_callback(subreq, + smbXsrv_session_close_shutdown_done, + session); next: TALLOC_FREE(rec); @@ -355,6 +359,33 @@ next: tevent_req_set_callback(subreq, smbXsrv_session_close_loop, client); } +static void smbXsrv_session_close_shutdown_done(struct tevent_req *subreq) +{ + struct smbXsrv_session *session = + tevent_req_callback_data(subreq, + struct smbXsrv_session); + NTSTATUS status; + + status = smb2srv_session_shutdown_recv(subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smbXsrv_session_close_loop: " + "smb2srv_session_shutdown_recv(%llu) failed: %s\n", + (unsigned long long)session->global->session_wire_id, + nt_errstr(status))); + } + + status = smbXsrv_session_logoff(session); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("smbXsrv_session_close_loop: " + "smbXsrv_session_logoff(%llu) failed: %s\n", + (unsigned long long)session->global->session_wire_id, + nt_errstr(status))); + } + + TALLOC_FREE(session); +} + struct smb1srv_session_local_allocate_state { const uint32_t lowest_id; const uint32_t highest_id; -- 1.9.1 From 545046f02eeb855f84cb3fd8d7e87cf18f457281 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:20:06 +0200 Subject: [PATCH 12/18] s3:smb2_sesssetup: let smbd_smb2_logoff_* use smbXsrv_session_shutdown_* Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 95057fe375348b918cb2ca58109f4c110a4a5f77) --- source3/smbd/smb2_sesssetup.c | 75 +++++++++---------------------------------- 1 file changed, 15 insertions(+), 60 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 57f623a..b52ed7d0 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -855,95 +855,50 @@ static void smbd_smb2_request_logoff_done(struct tevent_req *subreq) } } -struct smbd_smb2_logout_state { +struct smbd_smb2_logoff_state { struct smbd_smb2_request *smb2req; - struct tevent_queue *wait_queue; }; -static void smbd_smb2_logoff_wait_done(struct tevent_req *subreq); +static void smbd_smb2_logoff_shutdown_done(struct tevent_req *subreq); static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req) { struct tevent_req *req; - struct smbd_smb2_logout_state *state; + struct smbd_smb2_logoff_state *state; struct tevent_req *subreq; - struct smbd_smb2_request *preq; - struct smbXsrv_connection *xconn = smb2req->xconn; req = tevent_req_create(mem_ctx, &state, - struct smbd_smb2_logout_state); + struct smbd_smb2_logoff_state); if (req == NULL) { return NULL; } state->smb2req = smb2req; - state->wait_queue = tevent_queue_create(state, "logoff_wait_queue"); - if (tevent_req_nomem(state->wait_queue, req)) { - return tevent_req_post(req, ev); - } - - /* - * Make sure that no new request will be able to use this session. - */ - smb2req->session->status = NT_STATUS_USER_SESSION_DELETED; - - for (preq = xconn->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 = tevent_queue_wait_send(preq, ev, state->wait_queue); - if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); - } - } - - /* - * Now we add our own waiter to the end of the queue, - * this way we get notified when all pending requests are finished - * and send to the socket. - */ - subreq = tevent_queue_wait_send(state, ev, state->wait_queue); + subreq = smb2srv_session_shutdown_send(state, ev, + smb2req->session, + smb2req); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } - tevent_req_set_callback(subreq, smbd_smb2_logoff_wait_done, req); + tevent_req_set_callback(subreq, smbd_smb2_logoff_shutdown_done, req); return req; } -static void smbd_smb2_logoff_wait_done(struct tevent_req *subreq) +static void smbd_smb2_logoff_shutdown_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); + struct smbd_smb2_logoff_state *state = tevent_req_data( + req, struct smbd_smb2_logoff_state); NTSTATUS status; - tevent_queue_wait_recv(subreq); + status = smb2srv_session_shutdown_recv(subreq); + if (tevent_req_nterror(req, status)) { + return; + } TALLOC_FREE(subreq); /* -- 1.9.1 From f6cbfb41f80c1258057c5cb5fbf21fec18c15755 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:27:26 +0200 Subject: [PATCH 13/18] s3:smb2_sesssetup: always assign smb2req->session when a session was created. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 8f0d4d1132b74615dc6198ab736590dec52effda) --- source3/smbd/smb2_sesssetup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index b52ed7d0..b1ba29e 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -363,7 +363,6 @@ static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, * we attach the session to the request * so that the response can be signed */ - smb2req->session = session; if (!guest) { smb2req->do_signing = true; } @@ -582,6 +581,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, if (tevent_req_nterror(req, status)) { return tevent_req_post(req, ev); } + smb2req->session = state->session; } else { if (smb2req->session == NULL) { tevent_req_nterror(req, NT_STATUS_USER_SESSION_DELETED); -- 1.9.1 From 2e16aef0ec4abfbd3a7bacf8e2e30ee7c8fbe0e5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:21:25 +0200 Subject: [PATCH 14/18] s3:smb2_sesssetup: add smbd_smb2_session_setup_wrap_send/recv() The wrapper calls smbXsrv_session_shutdown_send/recv() in case of an error, this makes sure a failing reauth shuts down the session like an explicit logoff. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 50aeb6b38b14d6c26229834ece3c32eb50f9e56a) --- source3/smbd/smb2_sesssetup.c | 186 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 171 insertions(+), 15 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index b1ba29e..c5eec57 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -29,7 +29,7 @@ #include "../libcli/security/security.h" #include "../lib/util/tevent_ntstatus.h" -static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, +static struct tevent_req *smbd_smb2_session_setup_wrap_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req, uint64_t in_session_id, @@ -37,7 +37,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, uint8_t in_security_mode, uint64_t in_previous_session_id, DATA_BLOB in_security_buffer); -static NTSTATUS smbd_smb2_session_setup_recv(struct tevent_req *req, +static NTSTATUS smbd_smb2_session_setup_wrap_recv(struct tevent_req *req, uint16_t *out_session_flags, TALLOC_CTX *mem_ctx, DATA_BLOB *out_security_buffer, @@ -87,14 +87,14 @@ NTSTATUS smbd_smb2_request_process_sesssetup(struct smbd_smb2_request *smb2req) in_security_buffer.data = SMBD_SMB2_IN_DYN_PTR(smb2req); in_security_buffer.length = in_security_length; - subreq = smbd_smb2_session_setup_send(smb2req, - smb2req->sconn->ev_ctx, - smb2req, - in_session_id, - in_flags, - in_security_mode, - in_previous_session_id, - in_security_buffer); + subreq = smbd_smb2_session_setup_wrap_send(smb2req, + smb2req->sconn->ev_ctx, + smb2req, + in_session_id, + in_flags, + in_security_mode, + in_previous_session_id, + in_security_buffer); if (subreq == NULL) { return smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); } @@ -118,11 +118,11 @@ static void smbd_smb2_request_sesssetup_done(struct tevent_req *subreq) NTSTATUS status; NTSTATUS error; /* transport error */ - status = smbd_smb2_session_setup_recv(subreq, - &out_session_flags, - smb2req, - &out_security_buffer, - &out_session_id); + status = smbd_smb2_session_setup_wrap_recv(subreq, + &out_session_flags, + smb2req, + &out_security_buffer, + &out_session_id); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { @@ -783,6 +783,162 @@ static NTSTATUS smbd_smb2_session_setup_recv(struct tevent_req *req, return status; } +struct smbd_smb2_session_setup_wrap_state { + struct tevent_context *ev; + struct smbd_smb2_request *smb2req; + uint64_t in_session_id; + uint8_t in_flags; + uint8_t in_security_mode; + uint64_t in_previous_session_id; + DATA_BLOB in_security_buffer; + uint16_t out_session_flags; + DATA_BLOB out_security_buffer; + uint64_t out_session_id; + NTSTATUS error; +}; + +static void smbd_smb2_session_setup_wrap_setup_done(struct tevent_req *subreq); +static void smbd_smb2_session_setup_wrap_shutdown_done(struct tevent_req *subreq); + +static struct tevent_req *smbd_smb2_session_setup_wrap_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbd_smb2_request *smb2req, + uint64_t in_session_id, + uint8_t in_flags, + uint8_t in_security_mode, + uint64_t in_previous_session_id, + DATA_BLOB in_security_buffer) +{ + struct tevent_req *req; + struct smbd_smb2_session_setup_wrap_state *state; + struct tevent_req *subreq; + + req = tevent_req_create(mem_ctx, &state, + struct smbd_smb2_session_setup_wrap_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->smb2req = smb2req; + state->in_session_id = in_session_id; + state->in_flags = in_flags; + state->in_security_mode = in_security_mode; + state->in_previous_session_id = in_previous_session_id; + state->in_security_buffer = in_security_buffer; + + subreq = smbd_smb2_session_setup_send(state, state->ev, + state->smb2req, + state->in_session_id, + state->in_flags, + state->in_security_mode, + state->in_previous_session_id, + state->in_security_buffer); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + smbd_smb2_session_setup_wrap_setup_done, req); + + return req; +} + +static void smbd_smb2_session_setup_wrap_setup_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct smbd_smb2_session_setup_wrap_state *state = + tevent_req_data(req, + struct smbd_smb2_session_setup_wrap_state); + NTSTATUS status; + + status = smbd_smb2_session_setup_recv(subreq, + &state->out_session_flags, + state, + &state->out_security_buffer, + &state->out_session_id); + TALLOC_FREE(subreq); + if (NT_STATUS_IS_OK(status)) { + tevent_req_done(req); + return; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return; + } + + if (state->smb2req->session == NULL) { + tevent_req_nterror(req, status); + return; + } + + state->error = status; + + subreq = smb2srv_session_shutdown_send(state, state->ev, + state->smb2req->session, + state->smb2req); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + smbd_smb2_session_setup_wrap_shutdown_done, + req); +} + +static void smbd_smb2_session_setup_wrap_shutdown_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct smbd_smb2_session_setup_wrap_state *state = + tevent_req_data(req, + struct smbd_smb2_session_setup_wrap_state); + NTSTATUS status; + + status = smb2srv_session_shutdown_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* + * we may need to sign the response, so we need to keep + * the session until the response is sent to the wire. + */ + talloc_steal(state->smb2req, state->smb2req->session); + + tevent_req_nterror(req, state->error); +} + +static NTSTATUS smbd_smb2_session_setup_wrap_recv(struct tevent_req *req, + uint16_t *out_session_flags, + TALLOC_CTX *mem_ctx, + DATA_BLOB *out_security_buffer, + uint64_t *out_session_id) +{ + struct smbd_smb2_session_setup_wrap_state *state = + tevent_req_data(req, + struct smbd_smb2_session_setup_wrap_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_received(req); + return nt_status_squash(status); + } + } else { + status = NT_STATUS_OK; + } + + *out_session_flags = state->out_session_flags; + *out_security_buffer = state->out_security_buffer; + *out_session_id = state->out_session_id; + + talloc_steal(mem_ctx, out_security_buffer->data); + tevent_req_received(req); + return status; +} + static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req); -- 1.9.1 From e18aa36d8b5d879241b79a39d78ed6745dcb642c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 2 May 2015 16:29:03 +0200 Subject: [PATCH 15/18] s3:smb2_sesssetup: remove unused smbd_smb2_session_setup_* destructors The cleanup of a failing session setup is now handled in smbd_smb2_session_setup_wrap_*(). Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 5871d3da871349cba058bb91218ae58107cf05c8) --- source3/smbd/smb2_sesssetup.c | 98 ------------------------------------------- 1 file changed, 98 deletions(-) diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index c5eec57..85f8a9a 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -443,94 +443,12 @@ struct smbd_smb2_session_setup_state { uint16_t out_session_flags; DATA_BLOB out_security_buffer; uint64_t out_session_id; - /* The following pointer is owned by state->session. */ - struct smbd_smb2_session_setup_state **pp_self_ref; }; -static int pp_self_ref_destructor(struct smbd_smb2_session_setup_state **pp_state) -{ - (*pp_state)->session = NULL; - /* - * To make things clearer, ensure the pp_self_ref - * pointer is nulled out. We're never going to - * access this again. - */ - (*pp_state)->pp_self_ref = NULL; - return 0; -} - -static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_setup_state *state) -{ - struct smbXsrv_connection *xconn; - struct smbd_smb2_request *preq; - - /* - * If state->session is not NULL, - * we move the session from the session table to the request on failure - * so that the error response can be correctly signed, but the session - * is then really deleted when the request is done. - */ - - if (state->session == NULL) { - return 0; - } - - state->session->status = NT_STATUS_USER_SESSION_DELETED; - state->smb2req->session = talloc_move(state->smb2req, &state->session); - - /* - * We own the session now - we don't need the - * tag talloced on session that keeps track of session independently. - */ - TALLOC_FREE(state->pp_self_ref); - - /* - * We've made this session owned by the current request. - * Ensure that any outstanding requests don't also refer - * to it. - */ - xconn = state->smb2req->xconn; - - for (preq = xconn->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; - } - } - - return 0; -} - static void smbd_smb2_session_setup_gensec_done(struct tevent_req *subreq); static void smbd_smb2_session_setup_previous_done(struct tevent_req *subreq); static void smbd_smb2_session_setup_auth_return(struct tevent_req *req); -/************************************************************************ - We have to tag the state->session pointer with memory talloc'ed - on it to ensure it gets NULL'ed out if the underlying struct smbXsrv_session - is deleted by shutdown whilst this request is in flight. -************************************************************************/ - -static NTSTATUS tag_state_session_ptr(struct smbd_smb2_session_setup_state *state) -{ - state->pp_self_ref = talloc_zero(state->session, - struct smbd_smb2_session_setup_state *); - if (state->pp_self_ref == NULL) { - return NT_STATUS_NO_MEMORY; - } - *state->pp_self_ref = state; - talloc_set_destructor(state->pp_self_ref, pp_self_ref_destructor); - return NT_STATUS_OK; -} - static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbd_smb2_request *smb2req, @@ -572,8 +490,6 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } - talloc_set_destructor(state, smbd_smb2_session_setup_state_destructor); - if (state->in_session_id == 0) { /* create a new session */ status = smbXsrv_session_create(state->smb2req->xconn, @@ -604,11 +520,6 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, } } - status = tag_state_session_ptr(state); - if (tevent_req_nterror(req, status)) { - return tevent_req_post(req, ev); - } - if (state->session->gensec == NULL) { status = auth_generic_prepare(state->session, state->smb2req->xconn->remote_address, @@ -663,9 +574,6 @@ static void smbd_smb2_session_setup_gensec_done(struct tevent_req *subreq) if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { state->out_session_id = state->session->global->session_wire_id; - /* we want to keep the session */ - state->session = NULL; - TALLOC_FREE(state->pp_self_ref); tevent_req_nterror(req, status); return; } @@ -730,9 +638,6 @@ static void smbd_smb2_session_setup_auth_return(struct tevent_req *req) if (tevent_req_nterror(req, status)) { return; } - /* we want to keep the session */ - state->session = NULL; - TALLOC_FREE(state->pp_self_ref); tevent_req_done(req); return; } @@ -747,9 +652,6 @@ static void smbd_smb2_session_setup_auth_return(struct tevent_req *req) return; } - /* we want to keep the session */ - state->session = NULL; - TALLOC_FREE(state->pp_self_ref); tevent_req_done(req); return; } -- 1.9.1 From eeb39ea1895b50b8f524a2f968a6778474a5c16e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 16:50:55 +0200 Subject: [PATCH 16/18] s3:smb2_tcon: cancel pending requests on all connections on tdis Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit f71941491bbba20f394bd2f44425d7c21e90ba92) --- source3/smbd/smb2_tcon.c | 66 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c index 278fb6f..8a6d339 100644 --- a/source3/smbd/smb2_tcon.c +++ b/source3/smbd/smb2_tcon.c @@ -497,8 +497,7 @@ static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, struct tevent_req *req; struct smbd_smb2_tdis_state *state; struct tevent_req *subreq; - struct smbd_smb2_request *preq; - struct smbXsrv_connection *xconn = smb2req->xconn; + struct smbXsrv_connection *xconn = NULL; req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_tdis_state); @@ -517,35 +516,40 @@ static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, */ smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED; - for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { - if (preq == smb2req) { - /* Can't cancel current request. */ - continue; - } - if (preq->tcon != smb2req->tcon) { - /* Request on different tcon. */ - continue; - } - - /* - * Never cancel anything in a compound - * request. Way too hard to deal with - * the result. - */ - if (!preq->compound_related && preq->subreq != NULL) { - tevent_req_cancel(preq->subreq); - } - - /* - * Now wait until the request is finished. - * - * We don't set a callback, as we just want to block the - * wait queue and the talloc_free() of the request will - * remove the item from the wait queue. - */ - subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); - if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); + xconn = smb2req->xconn->client->connections; + for (; xconn != NULL; xconn = xconn->next) { + struct smbd_smb2_request *preq; + + for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { + if (preq == smb2req) { + /* Can't cancel current request. */ + continue; + } + if (preq->tcon != smb2req->tcon) { + /* Request on different tcon. */ + continue; + } + + /* + * Never cancel anything in a compound + * request. Way too hard to deal with + * the result. + */ + if (!preq->compound_related && preq->subreq != NULL) { + tevent_req_cancel(preq->subreq); + } + + /* + * Now wait until the request is finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of the request will + * remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } } } -- 1.9.1 From df4c2a4cd8b5ef7ed686360bd9c43567d490417c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 1 May 2015 20:26:41 +0200 Subject: [PATCH 17/18] s3:selftest: run smb2.notify with --signing=required This reproduces a bug withe implicit canceled requests. Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Signed-off-by: Stefan Metzmacher --- source3/selftest/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 40599c3..476652e 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -372,6 +372,9 @@ for t in tests: elif t == "vfs.fruit": plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --option=torture:share1=vfs_fruit --option=torture:share2=tmp --option=torture:localdir=$SELFTEST_PREFIX/s3dc/share') plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --option=torture:share1=vfs_fruit --option=torture:share2=tmp --option=torture:localdir=$SELFTEST_PREFIX/plugin_s4_dc/share') + elif t == "smb2.notify": + plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --signing=required') + plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD --signing=required') else: plansmbtorture4testsuite(t, "s3dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD') plansmbtorture4testsuite(t, "plugin_s4_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD') -- 1.9.1 From 40c2fe7af2c529d36dbb706eff26ba58297fb311 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Wed, 6 May 2015 17:20:55 +0200 Subject: [PATCH 18/18] s3:smbXsrv: refactor duplicate code into smbXsrv_session_clear_and_logoff() This replaces code in smbXsrv_session_logoff_all_callback() and smbXsrv_session_clear_and_logoff(). Signed-off-by: Michael Adam Reviewed-by: Stefan Metzmacher Bug: https://bugzilla.samba.org/show_bug.cgi?id=11182 Autobuild-User(master): Michael Adam Autobuild-Date(master): Thu May 7 10:43:29 CEST 2015 on sn-devel-104 (cherry picked from commit c7fcab7999c763acbc0f9dadb7fe05b47a257c7a) --- source3/smbd/smbXsrv_session.c | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index d968008..9ec118d 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -1088,7 +1088,7 @@ NTSTATUS smb2srv_session_close_previous_recv(struct tevent_req *req) return NT_STATUS_OK; } -static int smbXsrv_session_destructor(struct smbXsrv_session *session) +static NTSTATUS smbXsrv_session_clear_and_logoff(struct smbXsrv_session *session) { NTSTATUS status; struct smbXsrv_connection *xconn = NULL; @@ -1116,6 +1116,14 @@ static int smbXsrv_session_destructor(struct smbXsrv_session *session) } status = smbXsrv_session_logoff(session); + return status; +} + +static int smbXsrv_session_destructor(struct smbXsrv_session *session) +{ + NTSTATUS status; + + status = smbXsrv_session_clear_and_logoff(session); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("smbXsrv_session_destructor: " "smbXsrv_session_logoff() failed: %s\n", @@ -1649,7 +1657,6 @@ static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec, TDB_DATA val; void *ptr = NULL; struct smbXsrv_session *session = NULL; - struct smbXsrv_connection *xconn = NULL; NTSTATUS status; val = dbwrap_record_get_value(local_rec); @@ -1667,28 +1674,7 @@ static int smbXsrv_session_logoff_all_callback(struct db_record *local_rec, session->db_rec = local_rec; - if (session->client != NULL) { - xconn = session->client->connections; - } - for (; xconn != NULL; xconn = xconn->next) { - struct smbd_smb2_request *preq; - - for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { - if (preq->session != session) { - continue; - } - - 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; - } - } - - status = smbXsrv_session_logoff(session); + status = smbXsrv_session_clear_and_logoff(session); if (!NT_STATUS_IS_OK(status)) { if (NT_STATUS_IS_OK(state->first_status)) { state->first_status = status; -- 1.9.1