From f6aaa61a47d343144e9e10500bd4606d39d3ce68 Mon Sep 17 00:00:00 2001 From: Peter Somogyi Date: Tue, 14 Jun 2016 17:48:59 +0200 Subject: [PATCH] [SIGNED-OFF] torture: recreate a share security issue for samba-4.0 Signed-off-by: Peter Somogyi --- source4/torture/rpc/samba3rpc.c | 369 ++++++++++++++++++++++++++++++++++++++- 1 files changed, 367 insertions(+), 2 deletions(-) diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c index 9da6b54..6141489 100644 --- a/source4/torture/rpc/samba3rpc.c +++ b/source4/torture/rpc/samba3rpc.c @@ -2176,13 +2176,18 @@ static struct security_descriptor *get_sharesec(struct torture_context *tctx, struct srvsvc_NetShareGetInfo r; union srvsvc_NetShareInfo info; struct security_descriptor *result; + char *path_ipc; if (!(tmp_ctx = talloc_new(mem_ctx))) { torture_comment(tctx, "talloc_new failed\n"); return NULL; } - if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, "IPC$", &tree))) { + /* Win 2012 works only when host or IP given as a prefix */ + path_ipc = talloc_asprintf(tctx, "\\\\%s\\IPC$", + torture_setting_string(tctx, "host", NULL)); + if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, + sess, path_ipc, &tree))) { torture_comment(tctx, "secondary_tcon failed\n"); talloc_free(tmp_ctx); return NULL; @@ -2241,6 +2246,7 @@ static NTSTATUS set_sharesec(struct torture_context *tctx, struct sec_desc_buf i; struct srvsvc_NetShareSetInfo r; union srvsvc_NetShareInfo info; + char *path_ipc; uint32_t error = 0; if (!(tmp_ctx = talloc_new(mem_ctx))) { @@ -2248,7 +2254,10 @@ static NTSTATUS set_sharesec(struct torture_context *tctx, return NT_STATUS_NO_MEMORY; } - if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, "IPC$", &tree))) { + path_ipc = talloc_asprintf(tctx, "\\\\%s\\IPC$", + torture_setting_string(tctx, "host", NULL)); + + if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, path_ipc, &tree))) { torture_comment(tctx, "secondary_tcon failed\n"); talloc_free(tmp_ctx); return NT_STATUS_UNSUCCESSFUL; @@ -2517,6 +2526,361 @@ static bool torture_samba3_rpc_sharesec(struct torture_context *torture) return true; } + +static NTSTATUS torture_open_connection2(struct torture_context *tctx, + struct smbcli_state **cli, + struct cli_credentials *credentials) +{ + NTSTATUS status; + struct smbcli_options options; + struct smbcli_session_options session_options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options); + + status = smbcli_full_connection(tctx, + cli, + torture_setting_string(tctx, "host", NULL), + lpcfg_smb_ports(tctx->lp_ctx), + torture_setting_string(tctx, "share", NULL), + NULL, + lpcfg_socket_options(tctx->lp_ctx), + credentials, + lpcfg_resolve_context(tctx->lp_ctx), + tctx->ev, &options, &session_options, + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open connection - %s\n", nt_errstr(status)); + } + return status; +} + +static NTSTATUS torture_open_connection_smb2(struct torture_context *tctx, + struct smb2_tree **tree, + struct cli_credentials *credentials) +{ + struct smbcli_options options; + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + + return smb2_connect(tctx, + torture_setting_string(tctx, "host", NULL), + lpcfg_smb_ports(tctx->lp_ctx), + torture_setting_string(tctx, "share", NULL), + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); +} + +static NTSTATUS set_sharesec2(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smbcli_state *cli_admin, + const char *username, + uint32_t access_mask) +{ + struct security_descriptor *sd = NULL; + + sd = security_descriptor_dacl_create( + mem_ctx, 0, NULL, NULL, SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, access_mask, 0, NULL); + if (sd == NULL) { + torture_comment(tctx, "security_descriptor_dacl_create " + "failed\n"); + return NT_STATUS_NO_MEMORY; + } + + return set_sharesec(tctx, mem_ctx, + cli_admin->session, + torture_setting_string(tctx, "share", NULL), + sd); +} + +static bool try_sharesec_setacl(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + struct smb2_handle handle, + bool expect_setacl) +{ + union smb_setfileinfo set; + NTSTATUS status; + + torture_comment(tctx, "try to set ACL allow everything" + "for users\n"); + + set.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + set.set_secdesc.in.file.handle = handle; + set.set_secdesc.in.secinfo_flags = SECINFO_DACL; + + set.set_secdesc.in.sd = security_descriptor_dacl_create(mem_ctx, + 0, NULL, NULL, + SID_NT_AUTHENTICATED_USERS, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_GENERIC_ALL, + 0, + NULL); + + status = smb2_setinfo_file(tree, &set); + if (expect_setacl) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, + "expected setacl to succeed but " + "failed with %s\n", + nt_errstr(status)); + } + } else { + if (!NT_STATUS_EQUAL(status, NT_STATUS_MEDIA_WRITE_PROTECTED) + && !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + torture_comment(tctx, + "expected setacl to fail but succeeded!\n"); + return false; + } + } + + return true; +} + +static bool try_sharesec_createfile_setacl(struct torture_context *tctx, + TALLOC_CTX *mem_ctx, + struct smb2_tree *tree, + const char *fname, + uint32_t desired_access, + bool expect_open, + bool try_setacl, bool expect_setacl) +{ + struct smb2_create io; + struct smb2_handle handle; + NTSTATUS status; + bool result = true; + + ZERO_STRUCT(io); + io.level = RAW_OPEN_SMB2; + io.in.create_flags = 0; + io.in.desired_access = desired_access; + io.in.create_options = 0; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = 0; + io.in.alloc_size = 0; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.in.security_flags = 0; + io.in.fname = fname; + + torture_comment(tctx, "create(%s, 0x%.8x)\n", fname, desired_access); + status = smb2_create(tree, mem_ctx, &io); + if (expect_open) { + torture_assert_ntstatus_ok(tctx, status, "try smb2_create"); + handle = io.out.file.handle; + + if (try_setacl) { + result = try_sharesec_setacl(tctx, mem_ctx, tree, + handle, expect_setacl); + } + + smb2_util_close(tree, handle); + + } else { + if (!NT_STATUS_EQUAL(status, NT_STATUS_MEDIA_WRITE_PROTECTED) + && !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) + { + torture_comment(tctx, "expected open to be denied " + "but result is %s !\n", + nt_errstr(status)); + return false; + } + + if (try_setacl) { + torture_comment(tctx, "invalid scene\n"); + return false; + } + } + + + return result; +} + +/* + * This scenario tests whether a share-ACL is effectively denying setting ACLs + * found as a samba-4.0 only bug (till todays samba-4.3 present) + */ +static bool torture_samba3_rpc_sharesec2(struct torture_context *tctx) +{ + struct smbcli_state *cli_admin = NULL; + struct smb2_tree *tree_testuser = NULL; + struct cli_credentials *test_credentials = NULL; + const char *test_username = NULL; + const char *test_password = NULL; + const char *fname = "testfile.txt"; + int fail_cnt = 0; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct security_descriptor *sd_orig = NULL; + bool result = false; + uint32_t desired_access; + + torture_comment(tctx, "Init...\n"); + + if (!(mem_ctx = talloc_new(mem_ctx))) { + torture_comment(tctx, "talloc_new failed\n"); + return false; + } + + test_username = torture_setting_string(tctx, "extra_user1", NULL); + torture_assert(tctx, (test_username != NULL), + "extra_user1 torture param missing"); + test_password = torture_setting_string(tctx, "extra_password1", NULL); + torture_assert(tctx, (test_password != NULL), + "extra_password1 torture param missing"); + + test_credentials = cli_credentials_init(tctx); + cli_credentials_set_workstation(test_credentials, + "localhost", CRED_SPECIFIED); + cli_credentials_set_domain(test_credentials, + lpcfg_workgroup(tctx->lp_ctx), + CRED_SPECIFIED); + cli_credentials_set_username(test_credentials, test_username, + CRED_SPECIFIED); + cli_credentials_set_password(test_credentials, test_password, + CRED_SPECIFIED); + + status = torture_open_connection2(tctx, &cli_admin, + cmdline_credentials); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "connection#admin failed"); + + status = smbcli_unlink(cli_admin->tree, fname); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND) && + !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + torture_comment(tctx, "failed to cleanup %s (%s)\n", + fname, nt_errstr(status)); + goto done; + } + + sd_orig = get_sharesec(tctx, mem_ctx, cli_admin->session, + torture_setting_string(tctx, "share", NULL)); + if (sd_orig == NULL) { + torture_comment(tctx, "failed to get original sd\n"); + goto done; + } + + torture_comment(tctx, "Set sharesec to \"FULL\"\n"); + status = set_sharesec2(tctx, mem_ctx, cli_admin, test_username, + SEC_RIGHTS_FILE_ALL); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "set_sharesec failed!"); + + torture_comment(tctx, "Test that user can open file as RW" + " + can setacl - while sharesec is \"FULL\"\n"); + + status = torture_open_connection_smb2(tctx, &tree_testuser, + test_credentials); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "open connection for testuser#1"); + + if (!try_sharesec_createfile_setacl(tctx, mem_ctx, tree_testuser, + fname, + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_DATA | + SEC_STD_WRITE_DAC, + true, + true, true)) { + torture_comment(tctx, "RW test FAILED\n"); + fail_cnt++; + } + + talloc_free(tree_testuser); + + torture_comment(tctx, "Set sharesec to \"READ\"\n"); + status = set_sharesec2(tctx, mem_ctx, cli_admin, test_username, + SEC_RIGHTS_DIR_READ|SEC_DIR_TRAVERSE); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "set_sharesec failed!"); + + status = torture_open_connection_smb2(tctx, &tree_testuser, + test_credentials); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "open connection for testuser#2"); + + torture_comment(tctx, "Test that user can't write\n"); + if (!try_sharesec_createfile_setacl(tctx, mem_ctx, tree_testuser, + fname, + SEC_FILE_WRITE_DATA, false, + false, false)) { + torture_comment(tctx, "RO test FAILED\n"); + fail_cnt++; + } + + talloc_free(tree_testuser); + + torture_comment(tctx, "Set sharesec to \"CHANGE\"" + " (= no setacl allowed)\n"); + status = set_sharesec2(tctx, mem_ctx, + cli_admin, + test_username, + SEC_RIGHTS_DIR_READ|SEC_STD_DELETE| + SEC_RIGHTS_DIR_WRITE|SEC_DIR_TRAVERSE); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "set_sharesec failed!"); + + status = torture_open_connection_smb2(tctx, &tree_testuser, + test_credentials); + torture_assert_ntstatus_ok_goto(tctx, status, result, done, + "open connection for testuser"); + + torture_comment(tctx, "Test that user can't set acl\n"); + if (!try_sharesec_createfile_setacl(tctx, mem_ctx, tree_testuser, + fname, + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_DATA | + SEC_STD_WRITE_DAC, + false, + false, false)) { + torture_comment(tctx, "WRITE_DAC#1 test FAILED\n"); + fail_cnt++; + } + + torture_comment(tctx, "Test that user can'tctx set acl after " + "opened without desired flags\n"); + if (!try_sharesec_createfile_setacl(tctx, mem_ctx, tree_testuser, + fname, + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_DATA, + true, + true, false)) { + torture_comment(tctx, "WRITE_DAC#2 test FAILED\n"); + fail_cnt++; + } + + torture_comment(tctx, "Fail counter: %d\n", fail_cnt); + result = (fail_cnt==0) ? true : false; + +done: + if (cli_admin!=NULL) { + if (sd_orig != NULL) { + status = set_sharesec(tctx, mem_ctx, + cli_admin->session, + torture_setting_string(tctx, + "share", + NULL), + sd_orig); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, + "failed to restore sd_orig\n"); + } + } + talloc_free(cli_admin); + } + talloc_free(tree_testuser); + talloc_free(mem_ctx); + + return result; +} + static bool torture_samba3_rpc_lsa(struct torture_context *torture) { struct dcerpc_pipe *p; @@ -4544,6 +4908,7 @@ struct torture_suite *torture_rpc_samba3(TALLOC_CTX *mem_ctx) torture_suite_add_simple_test(suite, "sessionkey", torture_samba3_sessionkey); torture_suite_add_simple_test(suite, "srvsvc", torture_samba3_rpc_srvsvc); torture_suite_add_simple_test(suite, "sharesec", torture_samba3_rpc_sharesec); + torture_suite_add_simple_test(suite, "sharesec2", torture_samba3_rpc_sharesec2); torture_suite_add_simple_test(suite, "getusername", torture_samba3_rpc_getusername); torture_suite_add_simple_test(suite, "randomauth2", torture_samba3_rpc_randomauth2); torture_suite_add_simple_test(suite, "lsa", torture_samba3_rpc_lsa); -- 1.7.1