static bool test_lease_v2_request(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; struct smb2_lease ls; struct smb2_handle h1, h2, h3, h4, h5; struct smb2_write w; NTSTATUS status; const char *fname = "lease.dat"; const char *dname = "lease.dir"; const char *dnamefname = "lease.dir\\lease.dat"; const char *dnamefname2 = "lease.dir\\lease2.dat"; bool ret = true; smb2_util_unlink(tree, fname); smb2_deltree(tree, dname); tree->session->transport->lease.handler = torture_lease_handler; tree->session->transport->lease.private_data = tree; tree->session->transport->oplock.handler = torture_oplock_handler; tree->session->transport->oplock.private_data = tree; ZERO_STRUCT(break_info); ZERO_STRUCT(io); smb2_lease_v2_create_share(&io, &ls, false, fname, smb2_util_share_access("RWD"), LEASE1, NULL, smb2_util_lease_state("RHW"), 0); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0); ZERO_STRUCT(io); smb2_lease_v2_create_share(&io, &ls, true, dname, smb2_util_share_access("RWD"), LEASE2, NULL, smb2_util_lease_state("RHW"), 0); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0); ZERO_STRUCT(io); smb2_lease_v2_create_share(&io, &ls, false, dnamefname, smb2_util_share_access("RWD"), LEASE3, &LEASE2, smb2_util_lease_state("RHW"), 0); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h3 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE_V2(&io, "RHW", true, LEASE3, SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET); torture_wait_for_lease_break(tctx); CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); ZERO_STRUCT(io); smb2_lease_v2_create_share(&io, &ls, false, dnamefname2, smb2_util_share_access("RWD"), LEASE4, NULL, smb2_util_lease_state("RHW"), 0); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h4 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0); torture_wait_for_lease_break(tctx); torture_wait_for_lease_break(tctx); CHECK_BREAK_INFO("RH", "", LEASE2); torture_wait_for_lease_break(tctx); ZERO_STRUCT(break_info); ZERO_STRUCT(io); smb2_lease_v2_create_share(&io, &ls, true, dname, smb2_util_share_access("RWD"), LEASE2, NULL, smb2_util_lease_state("RHW"), 0); io.in.create_disposition = NTCREATEX_DISP_OPEN; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h5 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY); CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0); smb2_util_close(tree, h5); ZERO_STRUCT(w); w.in.file.handle = h4; w.in.offset = 0; w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); memset(w.in.data.data, 'o', w.in.data.length); status = smb2_write(tree, &w); CHECK_STATUS(status, NT_STATUS_OK); smb_msleep(2000); torture_wait_for_lease_break(tctx); CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); smb2_util_close(tree, h4); torture_wait_for_lease_break(tctx); torture_wait_for_lease_break(tctx); CHECK_BREAK_INFO("RH", "", LEASE2); torture_wait_for_lease_break(tctx); done: smb2_util_close(tree, h1); smb2_util_close(tree, h2); smb2_util_close(tree, h3); smb2_util_close(tree, h4); smb2_util_close(tree, h5); smb2_util_unlink(tree, fname); smb2_deltree(tree, dname); talloc_free(mem_ctx); return ret; }
/** * Test durable request / reconnect with AppInstanceId */ bool test_durable_v2_open_app_instance(struct torture_context *tctx, struct smb2_tree *tree1, struct smb2_tree *tree2) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1, _h2; struct smb2_handle *h1 = NULL, *h2 = NULL; struct smb2_create io1, io2; bool ret = true; struct GUID create_guid_1 = GUID_random(); struct GUID create_guid_2 = GUID_random(); struct GUID app_instance_id = GUID_random(); /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree1, fname); ZERO_STRUCT(break_info); tree1->session->transport->oplock.handler = torture_oplock_handler; tree1->session->transport->oplock.private_data = tree1; smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io1.in.durable_open = false; io1.in.durable_open_v2 = true; io1.in.persistent_open = false; io1.in.create_guid = create_guid_1; io1.in.app_instance_id = &app_instance_id; io1.in.timeout = UINT32_MAX; status = smb2_create(tree1, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OK); _h1 = io1.out.file.handle; h1 = &_h1; CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io1.out.durable_open, false); CHECK_VAL(io1.out.durable_open_v2, true); CHECK_VAL(io1.out.persistent_open, false); CHECK_VAL(io1.out.timeout, io1.in.timeout); /* * try to open the file as durable from a second tree with * a different create guid but the same app_instance_id * while the first handle is still open. */ smb2_oplock_create_share(&io2, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io2.in.durable_open = false; io2.in.durable_open_v2 = true; io2.in.persistent_open = false; io2.in.create_guid = create_guid_2; io2.in.app_instance_id = &app_instance_id; io2.in.timeout = UINT32_MAX; status = smb2_create(tree2, mem_ctx, &io2); CHECK_STATUS(status, NT_STATUS_OK); _h2 = io2.out.file.handle; h2 = &_h2; CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io2.out.durable_open, false); CHECK_VAL(io2.out.durable_open_v2, true); CHECK_VAL(io2.out.persistent_open, false); CHECK_VAL(io2.out.timeout, io2.in.timeout); CHECK_VAL(break_info.count, 0); status = smb2_util_close(tree1, *h1); CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); h1 = NULL; done: if (h1 != NULL) { smb2_util_close(tree1, *h1); } if (h2 != NULL) { smb2_util_close(tree2, *h2); } smb2_util_unlink(tree2, fname); talloc_free(tree1); talloc_free(tree2); talloc_free(mem_ctx); return ret; }
/* test some interesting combinations found by gentest */ static bool test_create_gentest(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_create io; NTSTATUS status; uint32_t access_mask, file_attributes_set; uint32_t ok_mask, not_supported_mask, invalid_parameter_mask; uint32_t not_a_directory_mask, unexpected_mask; union smb_fileinfo q; ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE| NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE; io.in.create_options = 0; io.in.fname = FNAME; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); io.in.create_options = 0xF0000000; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); io.in.create_options = 0; io.in.file_attributes = FILE_ATTRIBUTE_DEVICE; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); io.in.file_attributes = FILE_ATTRIBUTE_VOLUME; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); io.in.create_disposition = NTCREATEX_DISP_OPEN; io.in.file_attributes = FILE_ATTRIBUTE_VOLUME; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); io.in.create_disposition = NTCREATEX_DISP_CREATE; io.in.desired_access = 0x08000000; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); io.in.desired_access = 0x04000000; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); io.in.file_attributes = 0; io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; ok_mask = 0; not_supported_mask = 0; invalid_parameter_mask = 0; not_a_directory_mask = 0; unexpected_mask = 0; { int i; for (i=0;i<32;i++) { io.in.create_options = 1<<i; if (io.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) { continue; } status = smb2_create(tree, tctx, &io); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { not_supported_mask |= 1<<i; } else if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { invalid_parameter_mask |= 1<<i; } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { not_a_directory_mask |= 1<<i; } else if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) { ok_mask |= 1<<i; status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); } else { unexpected_mask |= 1<<i; torture_comment(tctx, "create option 0x%08x returned %s\n", 1<<i, nt_errstr(status)); } } } io.in.create_options = 0; CHECK_EQUAL(ok_mask, 0x00efcf7e); CHECK_EQUAL(not_a_directory_mask, 0x00000001); CHECK_EQUAL(not_supported_mask, 0x00102080); CHECK_EQUAL(invalid_parameter_mask, 0xff000000); CHECK_EQUAL(unexpected_mask, 0x00000000); io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io.in.file_attributes = 0; access_mask = 0; { int i; for (i=0;i<32;i++) { io.in.desired_access = 1<<i; status = smb2_create(tree, tctx, &io); if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(status, NT_STATUS_PRIVILEGE_NOT_HELD)) { access_mask |= io.in.desired_access; } else { CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); } } } if (TARGET_IS_WIN7(tctx)) { CHECK_EQUAL(access_mask, 0x0de0fe00); } else if (torture_setting_bool(tctx, "samba4", false)) { CHECK_EQUAL(access_mask, 0x0cf0fe00); } else { CHECK_EQUAL(access_mask, 0x0df0fe00); } io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; io.in.file_attributes = 0; ok_mask = 0; invalid_parameter_mask = 0; unexpected_mask = 0; file_attributes_set = 0; { int i; for (i=0;i<32;i++) { io.in.file_attributes = 1<<i; if (io.in.file_attributes & FILE_ATTRIBUTE_ENCRYPTED) { continue; } smb2_deltree(tree, FNAME); status = smb2_create(tree, tctx, &io); if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { invalid_parameter_mask |= 1<<i; } else if (NT_STATUS_IS_OK(status)) { uint32_t expected; ok_mask |= 1<<i; expected = (io.in.file_attributes | FILE_ATTRIBUTE_ARCHIVE) & 0x00005127; io.out.file_attr &= ~FILE_ATTRIBUTE_NONINDEXED; CHECK_EQUAL(io.out.file_attr, expected); file_attributes_set |= io.out.file_attr; status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); } else { unexpected_mask |= 1<<i; torture_comment(tctx, "file attribute 0x%08x returned %s\n", 1<<i, nt_errstr(status)); } } } CHECK_EQUAL(ok_mask, 0x00003fb7); CHECK_EQUAL(invalid_parameter_mask, 0xffff8048); CHECK_EQUAL(unexpected_mask, 0x00000000); CHECK_EQUAL(file_attributes_set, 0x00001127); smb2_deltree(tree, FNAME); /* * Standalone servers doesn't support encryption */ io.in.file_attributes = FILE_ATTRIBUTE_ENCRYPTED; status = smb2_create(tree, tctx, &io); if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { torture_comment(tctx, "FILE_ATTRIBUTE_ENCRYPTED returned %s\n", nt_errstr(status)); } else { CHECK_STATUS(status, NT_STATUS_OK); CHECK_EQUAL(io.out.file_attr, (FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_ARCHIVE)); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); } smb2_deltree(tree, FNAME); ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; io.in.file_attributes = 0; io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; io.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE; io.in.create_options = 0; io.in.fname = FNAME ":stream1"; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); io.in.fname = FNAME; io.in.file_attributes = 0x8040; io.in.share_access = NTCREATEX_SHARE_ACCESS_READ; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); io.in.fname = FNAME; io.in.file_attributes = 0; io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA; io.in.query_maximal_access = true; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_EQUAL(io.out.maximal_access, 0x001f01ff); q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; q.access_information.in.file.handle = io.out.file.handle; status = smb2_getinfo_file(tree, tctx, &q); CHECK_STATUS(status, NT_STATUS_OK); CHECK_EQUAL(q.access_information.out.access_flags, io.in.desired_access); io.in.file_attributes = 0; io.in.desired_access = 0; io.in.query_maximal_access = false; io.in.share_access = 0; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); smb2_deltree(tree, FNAME); return true; }
/* try creating with acls */ static bool test_create_acl_ext(struct torture_context *tctx, struct smb2_tree *tree, bool test_dir) { bool ret = true; struct smb2_create io; NTSTATUS status; struct security_ace ace; struct security_descriptor *sd; struct dom_sid *test_sid; union smb_fileinfo q = {}; uint32_t attrib = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | (test_dir ? FILE_ATTRIBUTE_DIRECTORY : 0); NTSTATUS (*delete_func)(struct smb2_tree *, const char *) = test_dir ? smb2_util_rmdir : smb2_util_unlink; smb2_deltree(tree, FNAME); ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.in.create_disposition = NTCREATEX_DISP_CREATE; io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE | NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; io.in.create_options = NTCREATEX_OPTIONS_ASYNC_ALERT | 0x00200000 | (test_dir ? NTCREATEX_OPTIONS_DIRECTORY : (NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)); io.in.fname = FNAME; torture_comment(tctx, "basic create\n"); status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; q.query_secdesc.in.file.handle = io.out.file.handle; q.query_secdesc.in.secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; status = smb2_getinfo_file(tree, tctx, &q); CHECK_STATUS(status, NT_STATUS_OK); sd = q.query_secdesc.out.sd; status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); status = delete_func(tree, FNAME); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "adding a new ACE\n"); test_sid = dom_sid_parse_talloc(tctx, SID_NT_AUTHENTICATED_USERS); ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; ace.flags = 0; ace.access_mask = SEC_STD_ALL; ace.trustee = *test_sid; status = security_descriptor_dacl_add(sd, &ace); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "creating a file with an initial ACL\n"); io.in.sec_desc = sd; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, io.out.file.handle, sd)); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); status = delete_func(tree, FNAME); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "creating with attributes\n"); io.in.sec_desc = NULL; io.in.file_attributes = attrib; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); FAIL_UNLESS(smb2_util_verify_attrib(tctx, tree, io.out.file.handle, attrib)); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); status = delete_func(tree, FNAME); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "creating with attributes and ACL\n"); io.in.sec_desc = sd; io.in.file_attributes = attrib; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, io.out.file.handle, sd)); FAIL_UNLESS(smb2_util_verify_attrib(tctx, tree, io.out.file.handle, attrib)); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); status = delete_func(tree, FNAME); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "creating with attributes, ACL and owner\n"); sd = security_descriptor_dacl_create(tctx, 0, SID_WORLD, SID_BUILTIN_USERS, SID_WORLD, SEC_ACE_TYPE_ACCESS_ALLOWED, SEC_RIGHTS_FILE_READ | SEC_STD_ALL, 0, NULL); io.in.sec_desc = sd; io.in.file_attributes = attrib; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); FAIL_UNLESS(smb2_util_verify_sd(tctx, tree, io.out.file.handle, sd)); FAIL_UNLESS(smb2_util_verify_attrib(tctx, tree, io.out.file.handle, attrib)); done: status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); status = delete_func(tree, FNAME); CHECK_STATUS(status, NT_STATUS_OK); return ret; }
static bool test_compound_break(struct torture_context *tctx, struct smb2_tree *tree) { const char *fname1 = "some-file.pptx"; NTSTATUS status; bool ret = true; union smb_open io1; struct smb2_create io2; struct smb2_getinfo gf; struct smb2_request *req[2]; struct smb2_handle h1; struct smb2_handle h; tree->session->transport->oplock.handler = torture_oplock_handler; tree->session->transport->oplock.private_data = tree; ZERO_STRUCT(break_info); /* base ntcreatex parms */ ZERO_STRUCT(io1.smb2); io1.generic.level = RAW_OPEN_SMB2; io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE| SEC_STD_READ_CONTROL| SEC_FILE_READ_ATTRIBUTE| SEC_FILE_READ_EA| SEC_FILE_READ_DATA); io1.smb2.in.alloc_size = 0; io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io1.smb2.in.create_options = 0; io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; io1.smb2.in.security_flags = 0; io1.smb2.in.fname = fname1; torture_comment(tctx, "TEST2: open a file with an batch " "oplock (share mode: all)\n"); io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; status = smb2_create(tree, tctx, &(io1.smb2)); torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); h1 = io1.smb2.out.file.handle; torture_comment(tctx, "TEST2: Opening second time with compound\n"); ZERO_STRUCT(io2); io2.in.desired_access = (SEC_STD_SYNCHRONIZE| SEC_FILE_READ_ATTRIBUTE| SEC_FILE_READ_EA); io2.in.alloc_size = 0; io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; io2.in.create_disposition = NTCREATEX_DISP_OPEN; io2.in.create_options = 0; io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; io2.in.security_flags = 0; io2.in.fname = fname1; io2.in.oplock_level = 0; smb2_transport_compound_start(tree->session->transport, 2); req[0] = smb2_create_send(tree, &io2); smb2_transport_compound_set_related(tree->session->transport, true); h.data[0] = UINT64_MAX; h.data[1] = UINT64_MAX; ZERO_STRUCT(gf); gf.in.file.handle = h; gf.in.info_type = SMB2_GETINFO_FILE; gf.in.info_class = 0x16; gf.in.output_buffer_length = 0x1000; gf.in.input_buffer_length = 0; req[1] = smb2_getinfo_send(tree, &gf); status = smb2_create_recv(req[0], tree, &io2); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_getinfo_recv(req[1], tree, &gf); CHECK_STATUS(status, NT_STATUS_OK); done: smb2_util_close(tree, h1); smb2_util_unlink(tree, fname1); return ret; }
} /* it seems vista is now fussier about alignment? */ if (strchr(fname, ':') == NULL) { /* setup some EAs */ io.in.eas.num_eas = 2; io.in.eas.eas = talloc_array(tmp_ctx, struct ea_struct, 2); io.in.eas.eas[0].flags = 0; io.in.eas.eas[0].name.s = "EAONE"; io.in.eas.eas[0].value = data_blob_talloc(tmp_ctx, "VALUE1", 6); io.in.eas.eas[1].flags = 0; io.in.eas.eas[1].name.s = "SECONDEA"; io.in.eas.eas[1].value = data_blob_talloc(tmp_ctx, "ValueTwo", 8); } status = smb2_create(tree, tmp_ctx, &io); talloc_free(tmp_ctx); NT_STATUS_NOT_OK_RETURN(status); *handle = io.out.file.handle; if (!dir) { status = smb2_util_write(tree, *handle, buf, 0, sizeof(buf)); NT_STATUS_NOT_OK_RETURN(status); } /* make sure all the timestamps aren't the same, and are also in different DST zones*/ setfile.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; setfile.generic.in.file.handle = *handle;
/* Open, take BRL, disconnect, reconnect. */ bool test_durable_open_lock(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; struct smb2_lease ls; struct smb2_handle h; struct smb2_lock lck; struct smb2_lock_element el[2]; NTSTATUS status; char fname[256]; bool ret = true; uint64_t lease; /* * Choose a random name and random lease in case the state is left a * little funky. */ lease = random(); snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8)); /* Clean slate */ smb2_util_unlink(tree, fname); /* Create with lease */ ZERO_STRUCT(io); io.in.security_flags = 0x00; io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; io.in.create_flags = 0x00000000; io.in.reserved = 0x00000000; io.in.desired_access = SEC_RIGHTS_FILE_ALL; io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; io.in.fname = fname; io.in.durable_open = true; ZERO_STRUCT(ls); ls.lease_key.data[0] = lease; ls.lease_key.data[1] = ~lease; ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE; io.in.lease_request = &ls; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); CHECK_VAL(io.out.lease_response.lease_key.data[0], lease); CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease); CHECK_VAL(io.out.lease_response.lease_state, SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); ZERO_STRUCT(lck); ZERO_STRUCT(el); lck.in.locks = el; lck.in.lock_count = 0x0001; lck.in.reserved = 0x00000000; lck.in.file.handle = h; el[0].offset = 0; el[0].length = 1; el[0].reserved = 0x00000000; el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); /* Disconnect/Reconnect. */ talloc_free(tree); tree = NULL; if (!torture_smb2_connection(tctx, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle = &h; io.in.lease_request = &ls; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; lck.in.file.handle = h; el[0].flags = SMB2_LOCK_FLAG_UNLOCK; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); done: smb2_util_close(tree, h); smb2_util_unlink(tree, fname); return ret; }
/** * testing various create blob combinations. */ bool test_durable_v2_open_create_blob(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h; struct smb2_handle *h = NULL; struct smb2_create io; struct GUID create_guid = GUID_random(); bool ret = true; struct smbcli_options options; options = tree->session->transport->options; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io.in.durable_open = false; io.in.durable_open_v2 = true; io.in.persistent_open = false; io.in.create_guid = create_guid; io.in.timeout = UINT32_MAX; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); _h = io.out.file.handle; h = &_h; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, true); CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.timeout, io.in.timeout); /* disconnect */ TALLOC_FREE(tree); /* create a new session (same client_guid) */ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } /* * check invalid combinations of durable handle * request and reconnect blobs * See MS-SMB2: 3.3.5.9.12 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context */ ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle_v2 = h; /* durable v2 reconnect request */ io.in.durable_open = true; /* durable v1 handle request */ io.in.create_guid = create_guid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle = h; /* durable v1 reconnect request */ io.in.durable_open_v2 = true; /* durable v2 handle request */ io.in.create_guid = create_guid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle = h; /* durable v1 reconnect request */ io.in.durable_handle_v2 = h; /* durable v2 reconnect request */ io.in.create_guid = create_guid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle_v2 = h; /* durable v2 reconnect request */ io.in.durable_open_v2 = true; /* durable v2 handle request */ io.in.create_guid = create_guid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
static bool test_lease_multibreak(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; struct smb2_lease ls; struct smb2_handle h, h2, h3; struct smb2_write w; NTSTATUS status; const char *fname = "lease.dat"; bool ret = true; uint32_t caps; caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); if (!(caps & SMB2_CAP_LEASING)) { torture_skip(tctx, "leases are not supported"); } tree->session->transport->lease.handler = torture_lease_handler; tree->session->transport->lease.private_data = tree; tree->session->transport->oplock.handler = torture_oplock_handler; tree->session->transport->oplock.private_data = tree; smb2_util_unlink(tree, fname); ZERO_STRUCT(break_info); /* Grab lease, upgrade to RHW .. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RH", true, LEASE1); smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RHW", true, LEASE1); /* Contend with LEASE2. */ smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h3 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RH", true, LEASE2); /* Verify that we were only sent one break. */ CHECK_BREAK_INFO("RHW", "RH", LEASE1); /* Drop LEASE1 / LEASE2 */ status = smb2_util_close(tree, h); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, h2); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, h3); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(break_info); /* Grab an R lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "R", true, LEASE1); /* Grab a level-II oplock. */ smb2_oplock_create(&io, fname, smb2_util_oplock_level("s")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s")); break_info.held_oplock_level = io.out.oplock_level; /* Verify no breaks. */ CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); /* Open for truncate, force a break. */ smb2_generic_create(&io, NULL, false, fname, NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h3 = io.out.file.handle; CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("")); break_info.held_oplock_level = io.out.oplock_level; /* Sleep, use a write to clear the recv queue. */ smb_msleep(250); ZERO_STRUCT(w); w.in.file.handle = h3; w.in.offset = 0; w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); memset(w.in.data.data, 'o', w.in.data.length); status = smb2_write(tree, &w); CHECK_STATUS(status, NT_STATUS_OK); /* Verify one oplock break, one lease break. */ CHECK_VAL(break_info.oplock_count, 1); CHECK_VAL(break_info.oplock_failures, 0); CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level("")); CHECK_BREAK_INFO("R", "", LEASE1); done: smb2_util_close(tree, h); smb2_util_close(tree, h2); smb2_util_close(tree, h3); smb2_util_unlink(tree, fname); talloc_free(mem_ctx); return ret; }
/* Open, disconnect, oplock break, reconnect. */ bool test_durable_open_oplock(struct torture_context *tctx, struct smb2_tree *tree1, struct smb2_tree *tree2) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io1, io2; struct smb2_handle h1, h2; NTSTATUS status; char fname[256]; bool ret = true; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8)); /* Clean slate */ smb2_util_unlink(tree1, fname); /* Create with batch oplock */ ZERO_STRUCT(io1); io1.in.security_flags = 0x00; io1.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; io1.in.create_flags = 0x00000000; io1.in.reserved = 0x00000000; io1.in.desired_access = SEC_RIGHTS_FILE_ALL; io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; io1.in.fname = fname; io1.in.durable_open = true; io2 = io1; io2.in.create_disposition = NTCREATEX_DISP_OPEN; status = smb2_create(tree1, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OK); h1 = io1.out.file.handle; CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); /* Disconnect after getting the batch */ talloc_free(tree1); tree1 = NULL; /* * Windows7 (build 7000) will break a batch oplock immediately if the * original client is gone. (ZML: This seems like a bug. It should give * some time for the client to reconnect!) */ status = smb2_create(tree2, mem_ctx, &io2); CHECK_STATUS(status, NT_STATUS_OK); h2 = io2.out.file.handle; CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); /* What if tree1 tries to come back and reclaim? */ if (!torture_smb2_connection(tctx, &tree1)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } ZERO_STRUCT(io1); io1.in.fname = fname; io1.in.durable_handle = &h1; status = smb2_create(tree1, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); done: smb2_util_close(tree2, h2); smb2_util_unlink(tree2, fname); return ret; }
static bool test_lease_oplock(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; struct smb2_lease ls; struct smb2_handle h, h2; NTSTATUS status; const char *fname = "lease.dat"; bool ret = true; int i; uint32_t caps; caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); if (!(caps & SMB2_CAP_LEASING)) { torture_skip(tctx, "leases are not supported"); } tree->session->transport->lease.handler = torture_lease_handler; tree->session->transport->lease.private_data = tree; tree->session->transport->oplock.handler = torture_oplock_handler; tree->session->transport->oplock.private_data = tree; smb2_util_unlink(tree, fname); for (i = 0; i < NOPLOCK_RESULTS; i++) { const char *held = oplock_results[i][0]; const char *contend = oplock_results[i][1]; const char *brokento = oplock_results[i][2]; const char *granted = oplock_results[i][3]; torture_comment(tctx, "Hold %s(%x), requesting %s(%x), " "expecting break to %s(%x) and grant of %s(%x)\n", held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend), brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted)); ZERO_STRUCT(break_info); /* Grab lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, held, true, LEASE1); /* Does an oplock contend the lease? */ smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted)); break_info.held_oplock_level = io.out.oplock_level; if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) { CHECK_BREAK_INFO(held, brokento, LEASE1); } else { CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); } smb2_util_close(tree, h); smb2_util_close(tree, h2); status = smb2_util_unlink(tree, fname); CHECK_STATUS(status, NT_STATUS_OK); } for (i = 0; i < NOPLOCK_RESULTS; i++) { const char *held = oplock_results_2[i][0]; const char *contend = oplock_results_2[i][1]; const char *brokento = oplock_results_2[i][2]; const char *granted = oplock_results_2[i][3]; torture_comment(tctx, "Hold %s(%x), requesting %s(%x), " "expecting break to %s(%x) and grant of %s(%x)\n", held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend), brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted)); ZERO_STRUCT(break_info); /* Grab an oplock. */ smb2_oplock_create(&io, fname, smb2_util_oplock_level(held)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held)); break_info.held_oplock_level = io.out.oplock_level; /* Grab lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, granted, true, LEASE1); if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) { CHECK_VAL(break_info.oplock_count, 1); CHECK_VAL(break_info.oplock_failures, 0); CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento)); break_info.held_oplock_level = break_info.oplock_level; } else { CHECK_VAL(break_info.oplock_count, 0); CHECK_VAL(break_info.oplock_failures, 0); } smb2_util_close(tree, h); smb2_util_close(tree, h2); status = smb2_util_unlink(tree, fname); CHECK_STATUS(status, NT_STATUS_OK); } done: smb2_util_close(tree, h); smb2_util_close(tree, h2); smb2_util_unlink(tree, fname); talloc_free(mem_ctx); return ret; }
static bool test_lease_upgrade3(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_handle h, h2, hnew; NTSTATUS status; struct smb2_create io; struct smb2_lease ls; const char *fname = "upgrade3.dat"; bool ret = true; int i; uint32_t caps; caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); if (!(caps & SMB2_CAP_LEASING)) { torture_skip(tctx, "leases are not supported"); } tree->session->transport->lease.handler = torture_lease_handler; tree->session->transport->lease.private_data = tree; smb2_util_unlink(tree, fname); for (i = 0; i < NUM_UPGRADE3_TESTS; i++) { struct lease_upgrade3_test t = lease_upgrade3_tests[i]; smb2_util_unlink(tree, fname); ZERO_STRUCT(break_info); /* grab first lease */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.held1)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, t.held1, true, LEASE1); h = io.out.file.handle; /* grab second lease */ smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(t.held2)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, t.held2, true, LEASE2); h2 = io.out.file.handle; /* no break has happened */ CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); /* try to upgrade lease1 */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, t.upgraded_to, true, LEASE1); hnew = io.out.file.handle; /* no break has happened */ CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); smb2_util_close(tree, hnew); smb2_util_close(tree, h); smb2_util_close(tree, h2); } done: smb2_util_close(tree, h); smb2_util_close(tree, hnew); smb2_util_close(tree, h2); smb2_util_unlink(tree, fname); talloc_free(mem_ctx); return ret; }
static bool test_lease_upgrade(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; struct smb2_lease ls; struct smb2_handle h, hnew; NTSTATUS status; const char *fname = "lease.dat"; bool ret = true; uint32_t caps; caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); if (!(caps & SMB2_CAP_LEASING)) { torture_skip(tctx, "leases are not supported"); } smb2_util_unlink(tree, fname); /* Grab a RH lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RH", true, LEASE1); h = io.out.file.handle; /* Upgrades (sidegrades?) to RW leave us with an RH. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RH", true, LEASE1); hnew = io.out.file.handle; smb2_util_close(tree, hnew); /* Upgrade to RHW lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RHW", true, LEASE1); hnew = io.out.file.handle; smb2_util_close(tree, h); h = hnew; /* Attempt to downgrade - original lease state is maintained. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RHW", true, LEASE1); hnew = io.out.file.handle; smb2_util_close(tree, hnew); done: smb2_util_close(tree, h); smb2_util_close(tree, hnew); smb2_util_unlink(tree, fname); talloc_free(mem_ctx); return ret; }
/** * Basic test for doing a durable open * and do a session reconnect while the first * session is still active and the handle is * still open in the client. * This closes the original session and a * durable reconnect on the new session succeeds. */ bool test_durable_v2_open_reopen1a(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h; struct smb2_handle *h = NULL; struct smb2_create io, io2; struct GUID create_guid = GUID_random(); bool ret = true; struct smb2_tree *tree2 = NULL; uint64_t previous_session_id; struct smbcli_options options; options = tree->session->transport->options; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io.in.durable_open = false; io.in.durable_open_v2 = true; io.in.persistent_open = false; io.in.create_guid = create_guid; io.in.timeout = UINT32_MAX; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); _h = io.out.file.handle; h = &_h; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, true); CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.timeout, io.in.timeout); /* * a session reconnect on a second tcp connection */ previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); if (!torture_smb2_connection_ext(tctx, previous_session_id, &options, &tree2)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } /* * check that this has deleted the old session */ ZERO_STRUCT(io); io.in.fname = ""; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); /* * but a durable reconnect on the new session succeeds: */ ZERO_STRUCT(io2); io2.in.fname = ""; io2.in.durable_handle_v2 = h; io2.in.create_guid = create_guid; status = smb2_create(tree2, mem_ctx, &io2); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io2.out.durable_open, false); CHECK_VAL(io2.out.durable_open_v2, false); /* no dh2q response blob */ CHECK_VAL(io2.out.persistent_open, false); CHECK_VAL(io2.out.timeout, io.in.timeout); _h = io2.out.file.handle; h = &_h; done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
/* Open, disconnect, open in another tree, reconnect. This test actually demonstrates a minimum level of respect for the durable open in the face of another open. As long as this test shows an inability to reconnect after an open, the oplock/lease tests above will certainly demonstrate an error on reconnect. */ bool test_durable_open_open(struct torture_context *tctx, struct smb2_tree *tree1, struct smb2_tree *tree2) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io1, io2; struct smb2_lease ls; struct smb2_handle h1, h2; NTSTATUS status; char fname[256]; bool ret = true; uint64_t lease; /* * Choose a random name and random lease in case the state is left a * little funky. */ lease = random(); snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8)); /* Clean slate */ smb2_util_unlink(tree1, fname); /* Create with lease */ ZERO_STRUCT(io1); io1.in.security_flags = 0x00; io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; io1.in.create_flags = 0x00000000; io1.in.reserved = 0x00000000; io1.in.desired_access = SEC_RIGHTS_FILE_ALL; io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io1.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; io1.in.fname = fname; io1.in.durable_open = true; io2 = io1; io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; io2.in.durable_open = false; ZERO_STRUCT(ls); ls.lease_key.data[0] = lease; ls.lease_key.data[1] = ~lease; ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE; io1.in.lease_request = &ls; status = smb2_create(tree1, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OK); h1 = io1.out.file.handle; CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease); CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease); CHECK_VAL(io1.out.lease_response.lease_state, SMB2_LEASE_READ|SMB2_LEASE_HANDLE); /* Disconnect */ talloc_free(tree1); tree1 = NULL; /* Open the file in tree2 */ status = smb2_create(tree2, mem_ctx, &io2); CHECK_STATUS(status, NT_STATUS_OK); h2 = io2.out.file.handle; CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); /* Reconnect */ if (!torture_smb2_connection(tctx, &tree1)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } ZERO_STRUCT(io1); io1.in.fname = fname; io1.in.durable_handle = &h1; io1.in.lease_request = &ls; /* * Windows7 (build 7000) will give away an open immediately if the * original client is gone. (ZML: This seems like a bug. It should give * some time for the client to reconnect!) */ status = smb2_create(tree1, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); h1 = io1.out.file.handle; done: smb2_util_close(tree2, h2); smb2_util_unlink(tree2, fname); smb2_util_close(tree1, h1); smb2_util_unlink(tree1, fname); return ret; }
/** * basic test for doing a durable open * tcp disconnect, reconnect, do a durable reopen (succeeds) */ bool test_durable_v2_open_reopen2(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h; struct smb2_handle *h = NULL; struct smb2_create io; struct GUID create_guid = GUID_random(); struct GUID create_guid_invalid = GUID_random(); bool ret = true; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io.in.durable_open = false; io.in.durable_open_v2 = true; io.in.persistent_open = false; io.in.create_guid = create_guid; io.in.timeout = UINT32_MAX; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); _h = io.out.file.handle; h = &_h; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, true); CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.timeout, io.in.timeout); /* disconnect, leaving the durable open */ TALLOC_FREE(tree); if (!torture_smb2_connection(tctx, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } /* * first a few failure cases */ ZERO_STRUCT(io); io.in.fname = ""; io.in.durable_handle_v2 = h; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); ZERO_STRUCT(io); io.in.fname = "__non_existing_fname__"; io.in.durable_handle_v2 = h; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle_v2 = h; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); /* a non-zero but non-matching create_guid does not change it: */ ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid_invalid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); /* * now success: * The important difference is that the create_guid is provided. */ ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_open_v2 = false; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; h = NULL; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); _h = io.out.file.handle; h = &_h; /* disconnect one more time */ TALLOC_FREE(tree); if (!torture_smb2_connection(tctx, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } ZERO_STRUCT(io); /* These are completely ignored by the server */ io.in.security_flags = 0x78; io.in.oplock_level = 0x78; io.in.impersonation_level = 0x12345678; io.in.create_flags = 0x12345678; io.in.reserved = 0x12345678; io.in.desired_access = 0x12345678; io.in.file_attributes = 0x12345678; io.in.share_access = 0x12345678; io.in.create_disposition = 0x12345678; io.in.create_options = 0x12345678; io.in.fname = "__non_existing_fname__"; /* * only io.in.durable_handle_v2 and * io.in.create_guid are checked */ io.in.durable_open_v2 = false; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; h = NULL; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); _h = io.out.file.handle; h = &_h; done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
/* basic testing of SMB2 durable opens regarding the position information on the handle */ bool test_durable_open_file_position(struct torture_context *tctx, struct smb2_tree *tree1, struct smb2_tree *tree2) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_handle h1, h2; struct smb2_create io1, io2; NTSTATUS status; const char *fname = "durable_open_position.dat"; union smb_fileinfo qfinfo; union smb_setfileinfo sfinfo; bool ret = true; uint64_t pos; smb2_util_unlink(tree1, fname); ZERO_STRUCT(io1); io1.in.security_flags = 0x00; io1.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; io1.in.create_flags = 0x00000000; io1.in.reserved = 0x00000000; io1.in.desired_access = SEC_RIGHTS_FILE_ALL; io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF; io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; io1.in.durable_open = true; io1.in.fname = fname; status = smb2_create(tree1, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OK); h1 = io1.out.file.handle; CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); /* TODO: check extra blob content */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = h1; status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VAL(qfinfo.position_information.out.position, 0); pos = qfinfo.position_information.out.position; torture_comment(tctx, "position: %llu\n", (unsigned long long)pos); ZERO_STRUCT(sfinfo); sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION; sfinfo.generic.in.file.handle = h1; sfinfo.position_information.in.position = 0x1000; status = smb2_setinfo_file(tree1, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = h1; status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VAL(qfinfo.position_information.out.position, 0x1000); pos = qfinfo.position_information.out.position; torture_comment(tctx, "position: %llu\n", (unsigned long long)pos); talloc_free(tree1); tree1 = NULL; ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = h1; status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); ZERO_STRUCT(io2); io2.in.fname = fname; io2.in.durable_handle = &h1; status = smb2_create(tree2, mem_ctx, &io2); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); CHECK_VAL(io2.out.reserved, 0x00); CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED); CHECK_VAL(io2.out.alloc_size, 0); CHECK_VAL(io2.out.size, 0); CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io2.out.reserved2, 0); h2 = io2.out.file.handle; ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = h2; status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); CHECK_VAL(qfinfo.position_information.out.position, 0x1000); pos = qfinfo.position_information.out.position; torture_comment(tctx, "position: %llu\n", (unsigned long long)pos); smb2_util_close(tree2, h2); talloc_free(mem_ctx); smb2_util_unlink(tree2, fname); done: return ret; }
/** * durable reconnect test: * connect with v1, reconnect with v2 : fails (no create_guid...) */ bool test_durable_v2_open_reopen2c(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h; struct smb2_handle *h = NULL; struct smb2_create io; struct GUID create_guid = GUID_random(); bool ret = true; struct smbcli_options options; options = tree->session->transport->options; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io.in.durable_open = true; io.in.durable_open_v2 = false; io.in.persistent_open = false; io.in.create_guid = create_guid; io.in.timeout = UINT32_MAX; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); _h = io.out.file.handle; h = &_h; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); CHECK_VAL(io.out.durable_open, true); CHECK_VAL(io.out.durable_open_v2, false); CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.timeout, 0); /* disconnect, leaving the durable open */ TALLOC_FREE(tree); if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle_v2 = h; /* durable v2 reconnect */ io.in.create_guid = create_guid; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
static bool test_smb2_open_for_delete(struct torture_context *tctx, struct smb2_tree *tree) { union smb_open io; union smb_fileinfo finfo; const char *fname = DNAME "\\torture_open_for_delete.txt"; NTSTATUS status; struct smb2_handle h, h1; bool ret = true; torture_comment(tctx, "Checking SMB2_OPEN for delete on a readonly file.\n"); smb2_util_unlink(tree, fname); smb2_deltree(tree, fname); status = torture_smb2_testdir(tree, DNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); /* reasonable default parameters */ ZERO_STRUCT(io.smb2); io.generic.level = RAW_OPEN_SMB2; io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; io.smb2.in.alloc_size = 0; io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; io.smb2.in.file_attributes = FILE_ATTRIBUTE_READONLY; io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; io.smb2.in.create_options = 0; io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; io.smb2.in.security_flags = 0; io.smb2.in.fname = fname; /* Create the readonly file. */ status = smb2_create(tree, tctx, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.smb2.out.file.handle; CHECK_VAL(io.smb2.out.oplock_level, 0); io.smb2.in.create_options = 0; CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED); CHECK_ALL_INFO(io.smb2.out.file_attr, attrib); smb2_util_close(tree, h1); /* Now try and open for delete only - should succeed. */ io.smb2.in.desired_access = SEC_STD_DELETE; io.smb2.in.file_attributes = 0; io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; status = smb2_create(tree, tctx, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_unlink(tree, fname); smb2_util_close(tree, h1); smb2_util_unlink(tree, fname); smb2_deltree(tree, DNAME); return ret; }
static bool test_compound_padding(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_handle h; struct smb2_create cr; struct smb2_read r; const char *fname = "compound_read.dat"; const char *sname = "compound_read.dat:foo"; struct smb2_request *req[3]; NTSTATUS status; bool ret = false; smb2_util_unlink(tree, fname); /* Write file */ ZERO_STRUCT(cr); cr.in.desired_access = SEC_FILE_WRITE_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_CREATE; cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.fname = fname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; status = smb2_create(tree, tctx, &cr); CHECK_STATUS(status, NT_STATUS_OK); h = cr.out.file.handle; status = smb2_util_write(tree, h, "123", 0, 3); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); /* Write stream */ ZERO_STRUCT(cr); cr.in.desired_access = SEC_FILE_WRITE_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_CREATE; cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.fname = sname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; status = smb2_create(tree, tctx, &cr); CHECK_STATUS(status, NT_STATUS_OK); h = cr.out.file.handle; status = smb2_util_write(tree, h, "456", 0, 3); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); /* Check compound read from basefile */ smb2_transport_compound_start(tree->session->transport, 2); ZERO_STRUCT(cr); cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.desired_access = SEC_FILE_READ_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_OPEN; cr.in.fname = fname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; req[0] = smb2_create_send(tree, &cr); smb2_transport_compound_set_related(tree->session->transport, true); ZERO_STRUCT(r); h.data[0] = UINT64_MAX; h.data[1] = UINT64_MAX; r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[1] = smb2_read_send(tree, &r); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[1]) || !smb2_request_is_ok(req[1])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 24: 16 byte read response header plus 3 * requested bytes padded to an 8 byte boundary. */ CHECK_VALUE(req[1]->in.body_size, 24); status = smb2_read_recv(req[1], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, cr.out.file.handle); /* Check compound read from stream */ smb2_transport_compound_start(tree->session->transport, 2); ZERO_STRUCT(cr); cr.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; cr.in.desired_access = SEC_FILE_READ_DATA; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.create_disposition = NTCREATEX_DISP_OPEN; cr.in.fname = sname; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE| NTCREATEX_SHARE_ACCESS_DELETE; req[0] = smb2_create_send(tree, &cr); smb2_transport_compound_set_related(tree->session->transport, true); ZERO_STRUCT(r); h.data[0] = UINT64_MAX; h.data[1] = UINT64_MAX; r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[1] = smb2_read_send(tree, &r); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[1]) || !smb2_request_is_ok(req[1])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 24: 16 byte read response header plus 3 * requested bytes padded to an 8 byte boundary. */ CHECK_VALUE(req[1]->in.body_size, 24); status = smb2_read_recv(req[1], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); h = cr.out.file.handle; /* Check 2 compound (unrelateated) reads from existing stream handle */ smb2_transport_compound_start(tree->session->transport, 2); ZERO_STRUCT(r); r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[0] = smb2_read_send(tree, &r); req[1] = smb2_read_send(tree, &r); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[0]) || !smb2_request_is_ok(req[0])) { torture_fail(tctx, "failed to receive read request"); } if (!smb2_request_receive(req[1]) || !smb2_request_is_ok(req[1])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 24: 16 byte read response header plus 3 * requested bytes padded to an 8 byte boundary. */ CHECK_VALUE(req[0]->in.body_size, 24); CHECK_VALUE(req[1]->in.body_size, 24); status = smb2_read_recv(req[0], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_read_recv(req[1], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); /* * now try a single read from the stream and verify there's no padding */ ZERO_STRUCT(r); r.in.file.handle = h; r.in.length = 3; r.in.offset = 0; r.in.min_count = 1; req[0] = smb2_read_send(tree, &r); /* * We must do a manual smb2_request_receive() in order to be * able to check the transport layer info, as smb2_read_recv() * will destroy the req. smb2_read_recv() will call * smb2_request_receive() again, but that's ok. */ if (!smb2_request_receive(req[0]) || !smb2_request_is_ok(req[0])) { torture_fail(tctx, "failed to receive read request"); } /* * size must be 19: 16 byte read response header plus 3 * requested bytes without padding. */ CHECK_VALUE(req[0]->in.body_size, 19); status = smb2_read_recv(req[0], tree, &r); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h); status = smb2_util_unlink(tree, fname); CHECK_STATUS(status, NT_STATUS_OK); ret = true; done: return ret; }
static bool test_smb2_open_brlocked(struct torture_context *tctx, struct smb2_tree *tree) { union smb_open io, io1; union smb_lock io2; struct smb2_lock_element lock[1]; const char *fname = DNAME "\\torture_ntcreatex.txt"; NTSTATUS status; bool ret = true; struct smb2_handle h; char b = 42; torture_comment(tctx, "Testing SMB2 open with a byte range locked file\n"); smb2_util_unlink(tree, fname); status = torture_smb2_testdir(tree, DNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(io.smb2); io.generic.level = RAW_OPEN_SMB2; io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; io.smb2.in.desired_access = 0x2019f; io.smb2.in.alloc_size = 0; io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; io.smb2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; io.smb2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; io.smb2.in.security_flags = SMB2_SECURITY_DYNAMIC_TRACKING; io.smb2.in.fname = fname; status = smb2_create(tree, tctx, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_write(tree, io.smb2.out.file.handle, &b, 0, 1); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(io2.smb2); io2.smb2.level = RAW_LOCK_SMB2; io2.smb2.in.file.handle = io.smb2.out.file.handle; io2.smb2.in.lock_count = 1; ZERO_STRUCT(lock); lock[0].offset = 0; lock[0].length = 1; lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; io2.smb2.in.locks = &lock[0]; status = smb2_lock(tree, &(io2.smb2)); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(io1.smb2); io1.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; io1.smb2.in.desired_access = 0x20196; io1.smb2.in.alloc_size = 0; io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; io1.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; io1.smb2.in.create_options = 0; io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION; io1.smb2.in.security_flags = SMB2_SECURITY_DYNAMIC_TRACKING; io1.smb2.in.fname = fname; status = smb2_create(tree, tctx, &(io1.smb2)); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, io.smb2.out.file.handle); smb2_util_close(tree, io1.smb2.out.file.handle); smb2_util_unlink(tree, fname); smb2_deltree(tree, DNAME); return ret; }
/* * Test creating a file with a NULL DACL. */ static bool test_create_null_dacl(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; struct smb2_create io; const char *fname = "nulldacl.txt"; bool ret = true; struct smb2_handle handle; union smb_fileinfo q; union smb_setfileinfo s; struct security_descriptor *sd = security_descriptor_initialise(tctx); struct security_acl dacl; torture_comment(tctx, "TESTING SEC_DESC WITH A NULL DACL\n"); smb2_util_unlink(tree, fname); ZERO_STRUCT(io); io.level = RAW_OPEN_SMB2; io.in.create_flags = 0; io.in.desired_access = SEC_STD_READ_CONTROL | SEC_STD_WRITE_DAC | SEC_STD_WRITE_OWNER; io.in.create_options = 0; io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; io.in.alloc_size = 0; io.in.create_disposition = NTCREATEX_DISP_CREATE; io.in.impersonation_level = NTCREATEX_IMPERSONATION_ANONYMOUS; io.in.security_flags = 0; io.in.fname = fname; io.in.sec_desc = sd; /* XXX create_options ? */ io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; torture_comment(tctx, "creating a file with a empty sd\n"); status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); handle = io.out.file.handle; torture_comment(tctx, "get the original sd\n"); q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; q.query_secdesc.in.file.handle = handle; q.query_secdesc.in.secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; status = smb2_getinfo_file(tree, tctx, &q); CHECK_STATUS(status, NT_STATUS_OK); /* * Testing the created DACL, * the server should add the inherited DACL * when SEC_DESC_DACL_PRESENT isn't specified */ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { ret = false; torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); } if (q.query_secdesc.out.sd->dacl == NULL) { ret = false; torture_fail_goto(tctx, done, "no DACL has been created on the server!\n"); } torture_comment(tctx, "set NULL DACL\n"); sd->type |= SEC_DESC_DACL_PRESENT; s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; s.set_secdesc.in.file.handle = handle; s.set_secdesc.in.secinfo_flags = SECINFO_DACL; s.set_secdesc.in.sd = sd; status = smb2_setinfo_file(tree, &s); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "get the sd\n"); q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; q.query_secdesc.in.file.handle = handle; q.query_secdesc.in.secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; status = smb2_getinfo_file(tree, tctx, &q); CHECK_STATUS(status, NT_STATUS_OK); /* Testing the modified DACL */ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { ret = false; torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); } if (q.query_secdesc.out.sd->dacl != NULL) { ret = false; torture_fail_goto(tctx, done, "DACL has been created on the server!\n"); } io.in.create_disposition = NTCREATEX_DISP_OPEN; torture_comment(tctx, "try open for read control\n"); io.in.desired_access = SEC_STD_READ_CONTROL; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_STD_READ_CONTROL); smb2_util_close(tree, io.out.file.handle); torture_comment(tctx, "try open for write\n"); io.in.desired_access = SEC_FILE_WRITE_DATA; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_FILE_WRITE_DATA); smb2_util_close(tree, io.out.file.handle); torture_comment(tctx, "try open for read\n"); io.in.desired_access = SEC_FILE_READ_DATA; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_FILE_READ_DATA); smb2_util_close(tree, io.out.file.handle); torture_comment(tctx, "try open for generic write\n"); io.in.desired_access = SEC_GENERIC_WRITE; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_RIGHTS_FILE_WRITE); smb2_util_close(tree, io.out.file.handle); torture_comment(tctx, "try open for generic read\n"); io.in.desired_access = SEC_GENERIC_READ; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_RIGHTS_FILE_READ); smb2_util_close(tree, io.out.file.handle); torture_comment(tctx, "set DACL with 0 aces\n"); ZERO_STRUCT(dacl); dacl.revision = SECURITY_ACL_REVISION_NT4; dacl.num_aces = 0; sd->dacl = &dacl; s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; s.set_secdesc.in.file.handle = handle; s.set_secdesc.in.secinfo_flags = SECINFO_DACL; s.set_secdesc.in.sd = sd; status = smb2_setinfo_file(tree, &s); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "get the sd\n"); q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; q.query_secdesc.in.file.handle = handle; q.query_secdesc.in.secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; status = smb2_getinfo_file(tree, tctx, &q); CHECK_STATUS(status, NT_STATUS_OK); /* Testing the modified DACL */ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { ret = false; torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); } if (q.query_secdesc.out.sd->dacl == NULL) { ret = false; torture_fail_goto(tctx, done, "no DACL has been created on the server!\n"); } if (q.query_secdesc.out.sd->dacl->num_aces != 0) { torture_result(tctx, TORTURE_FAIL, "DACL has %u aces!\n", q.query_secdesc.out.sd->dacl->num_aces); ret = false; goto done; } torture_comment(tctx, "try open for read control\n"); io.in.desired_access = SEC_STD_READ_CONTROL; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_ACCESS_FLAGS(io.out.file.handle, SEC_STD_READ_CONTROL); smb2_util_close(tree, io.out.file.handle); torture_comment(tctx, "try open for write => access_denied\n"); io.in.desired_access = SEC_FILE_WRITE_DATA; status = smb2_create(tree, tctx, &io); if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); } else { CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); } torture_comment(tctx, "try open for read => access_denied\n"); io.in.desired_access = SEC_FILE_READ_DATA; status = smb2_create(tree, tctx, &io); if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); } else { CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); } torture_comment(tctx, "try open for generic write => access_denied\n"); io.in.desired_access = SEC_GENERIC_WRITE; status = smb2_create(tree, tctx, &io); if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); } else { CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); } torture_comment(tctx, "try open for generic read => access_denied\n"); io.in.desired_access = SEC_GENERIC_READ; status = smb2_create(tree, tctx, &io); if (torture_setting_bool(tctx, "hide_on_access_denied", false)) { CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); } else { CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); } torture_comment(tctx, "set empty sd\n"); sd->type &= ~SEC_DESC_DACL_PRESENT; sd->dacl = NULL; s.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; s.set_secdesc.in.file.handle = handle; s.set_secdesc.in.secinfo_flags = SECINFO_DACL; s.set_secdesc.in.sd = sd; status = smb2_setinfo_file(tree, &s); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "get the sd\n"); q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; q.query_secdesc.in.file.handle = handle; q.query_secdesc.in.secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; status = smb2_getinfo_file(tree, tctx, &q); CHECK_STATUS(status, NT_STATUS_OK); /* Testing the modified DACL */ if (!(q.query_secdesc.out.sd->type & SEC_DESC_DACL_PRESENT)) { ret = false; torture_fail_goto(tctx, done, "DACL_PRESENT flag not set by the server!\n"); } if (q.query_secdesc.out.sd->dacl != NULL) { ret = false; torture_fail_goto(tctx, done, "DACL has been created on the server!\n"); } done: smb2_util_close(tree, handle); smb2_util_unlink(tree, fname); smb2_tdis(tree); smb2_logoff(tree->session); return ret; }
static bool test_fixed(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create create; struct smb2_handle h, h2; struct smb2_find f; union smb_search_data *d; struct file_elem files[NFILES] = {}; NTSTATUS status; bool ret = true; unsigned int count; int i; status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h); ZERO_STRUCT(create); create.in.desired_access = SEC_RIGHTS_DIR_ALL; create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; create.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; create.in.create_disposition = NTCREATEX_DISP_OPEN; create.in.fname = DNAME; status = smb2_create(tree, mem_ctx, &create); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); h2 = create.out.file.handle; ZERO_STRUCT(f); f.in.file.handle = h; f.in.pattern = "*"; f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; f.in.max_response_size = 0x100; f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; /* Start enumeration on h, then delete all from h2 */ status = smb2_find_level(tree, tree, &f, &count, &d); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); f.in.file.handle = h2; do { status = smb2_find_level(tree, tree, &f, &count, &d); if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) break; torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); for (i = 0; i < count; i++) { const char *found = d[i].both_directory_info.name.s; char *path = talloc_asprintf(mem_ctx, "%s\\%s", DNAME, found); if (!strcmp(found, ".") || !strcmp(found, "..")) continue; status = smb2_util_unlink(tree, path); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); talloc_free(path); } f.in.continue_flags = 0; f.in.max_response_size = 4096; } while (count != 0); /* Now finish h enumeration. */ f.in.file.handle = h; do { status = smb2_find_level(tree, tree, &f, &count, &d); if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) break; torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ""); for (i = 0; i < count; i++) { const char *found = d[i].both_directory_info.name.s; if (!strcmp(found, ".") || !strcmp(found, "..")) continue; torture_result(tctx, TORTURE_FAIL, "(%s): didn't expect %s\n", __location__, found); ret = false; goto done; } f.in.continue_flags = 0; f.in.max_response_size = 4096; } while (count != 0); done: smb2_util_close(tree, h); smb2_util_close(tree, h2); smb2_deltree(tree, DNAME); talloc_free(mem_ctx); return ret; }
/* try the various request blobs */ static bool test_create_blob(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_create io; NTSTATUS status; smb2_deltree(tree, FNAME); ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; io.in.share_access = NTCREATEX_SHARE_ACCESS_DELETE| NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE; io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; io.in.fname = FNAME; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "Testing alloc size\n"); io.in.alloc_size = 4096; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_EQUAL(io.out.alloc_size, io.in.alloc_size); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "Testing durable open\n"); io.in.durable_open = true; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "Testing query maximal access\n"); io.in.query_maximal_access = true; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_EQUAL(io.out.maximal_access, 0x001f01ff); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "Testing timewarp\n"); io.in.timewarp = 10000; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); io.in.timewarp = 0; torture_comment(tctx, "Testing query_on_disk\n"); io.in.query_on_disk_id = true; status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "Testing unknown tag\n"); status = smb2_create_blob_add(tctx, &io.in.blobs, "FooO", data_blob(NULL, 0)); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "Testing bad tag length\n"); status = smb2_create_blob_add(tctx, &io.in.blobs, "xxx", data_blob(NULL, 0)); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_create(tree, tctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); smb2_deltree(tree, FNAME); return true; }
/** * lease_v2 variant of reopen2 * basic test for doing a durable open * tcp disconnect, reconnect, do a durable reopen (succeeds) */ bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h; struct smb2_handle *h = NULL; struct smb2_create io; struct GUID create_guid = GUID_random(); struct smb2_lease ls; uint64_t lease_key; bool ret = true; struct smbcli_options options; uint32_t caps; caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); if (!(caps & SMB2_CAP_LEASING)) { torture_skip(tctx, "leases are not supported"); } options = tree->session->transport->options; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); lease_key = random(); smb2_lease_v2_create(&io, &ls, false /* dir */, fname, lease_key, 0, /* parent lease key */ smb2_util_lease_state("RWH"), 0 /* lease epoch */); io.in.durable_open = false; io.in.durable_open_v2 = true; io.in.persistent_open = false; io.in.create_guid = create_guid; io.in.timeout = UINT32_MAX; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); _h = io.out.file.handle; h = &_h; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, true); CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.timeout, io.in.timeout); CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); /* disconnect, reconnect and then do durable reopen */ TALLOC_FREE(tree); if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } /* a few failure tests: */ /* * several attempts without lease attached: * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND * irrespective of file name provided */ ZERO_STRUCT(io); io.in.fname = ""; io.in.durable_handle_v2 = h; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); ZERO_STRUCT(io); io.in.fname = "__non_existing_fname__"; io.in.durable_handle_v2 = h; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_handle_v2 = h; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); /* * attempt with lease provided, but * with a changed lease key. => fails */ ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_open_v2 = false; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; io.in.lease_request_v2 = &ls; io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; /* a wrong lease key lets the request fail */ ls.lease_key.data[0]++; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); /* restore the correct lease key */ ls.lease_key.data[0]--; /* * this last failing attempt is almost correct: * only problem is: we use the wrong filename... * Note that this gives INVALID_PARAMETER. * This is different from oplocks! */ ZERO_STRUCT(io); io.in.fname = "__non_existing_fname__"; io.in.durable_open_v2 = false; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; io.in.lease_request_v2 = &ls; io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); /* * Now for a succeeding reconnect: */ ZERO_STRUCT(io); io.in.fname = fname; io.in.durable_open_v2 = false; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; io.in.lease_request_v2 = &ls; io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; /* the requested lease state is irrelevant */ ls.lease_state = smb2_util_lease_state(""); h = NULL; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); CHECK_VAL(io.out.lease_response_v2.lease_state, smb2_util_lease_state("RWH")); CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); _h = io.out.file.handle; h = &_h; /* disconnect one more time */ TALLOC_FREE(tree); if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { torture_warning(tctx, "couldn't reconnect, bailing\n"); ret = false; goto done; } /* * demonstrate that various parameters are ignored * in the reconnect */ ZERO_STRUCT(io); /* * These are completely ignored by the server */ io.in.security_flags = 0x78; io.in.oplock_level = 0x78; io.in.impersonation_level = 0x12345678; io.in.create_flags = 0x12345678; io.in.reserved = 0x12345678; io.in.desired_access = 0x12345678; io.in.file_attributes = 0x12345678; io.in.share_access = 0x12345678; io.in.create_disposition = 0x12345678; io.in.create_options = 0x12345678; io.in.fname = "__non_existing_fname__"; /* * only these are checked: * - io.in.fname * - io.in.durable_handle_v2, * - io.in.create_guid * - io.in.lease_request_v2->lease_key */ io.in.fname = fname; io.in.durable_open_v2 = false; io.in.durable_handle_v2 = h; io.in.create_guid = create_guid; io.in.lease_request_v2 = &ls; /* the requested lease state is irrelevant */ ls.lease_state = smb2_util_lease_state(""); h = NULL; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io.out.durable_open, false); CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ CHECK_VAL(io.out.persistent_open, false); CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key); CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key); CHECK_VAL(io.out.lease_response_v2.lease_state, smb2_util_lease_state("RWH")); CHECK_VAL(io.out.lease_response_v2.lease_flags, 0); CHECK_VAL(io.out.lease_response_v2.lease_duration, 0); _h = io.out.file.handle; h = &_h; done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
/* test SMB2 open */ static bool test_smb2_open(struct torture_context *tctx, struct smb2_tree *tree) { union smb_open io; union smb_fileinfo finfo; const char *fname = DNAME "\\torture_ntcreatex.txt"; const char *dname = DNAME "\\torture_ntcreatex.dir"; NTSTATUS status; struct smb2_handle h, h1; bool ret = true; int i; struct { uint32_t create_disp; bool with_file; NTSTATUS correct_status; } open_funcs[] = { { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_OK }, { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_OK }, { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK }, { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK }, { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK }, { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK }, { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_OK }, { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_OK }, { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_OK }, { 6, true, NT_STATUS_INVALID_PARAMETER }, { 6, false, NT_STATUS_INVALID_PARAMETER }, }; torture_comment(tctx, "Checking SMB2 Open\n"); smb2_util_unlink(tree, fname); smb2_util_rmdir(tree, dname); status = torture_smb2_testdir(tree, DNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(io.smb2); /* reasonable default parameters */ io.generic.level = RAW_OPEN_SMB2; io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; io.smb2.in.alloc_size = 1024*1024; io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; io.smb2.in.create_options = 0; io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; io.smb2.in.security_flags = 0; io.smb2.in.fname = fname; /* test the create disposition */ for (i=0; i<ARRAY_SIZE(open_funcs); i++) { if (open_funcs[i].with_file) { io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; status= smb2_create(tree, tctx, &(io.smb2)); if (!NT_STATUS_IS_OK(status)) { torture_comment(tctx, "Failed to create file %s status %s %d\n", fname, nt_errstr(status), i); ret = false; goto done; } smb2_util_close(tree, io.smb2.out.file.handle); } io.smb2.in.create_disposition = open_funcs[i].create_disp; status = smb2_create(tree, tctx, &(io.smb2)); if (!NT_STATUS_EQUAL(status, open_funcs[i].correct_status)) { torture_comment(tctx, "(%s) incorrect status %s should be %s (i=%d " "with_file=%d open_disp=%d)\n", __location__, nt_errstr(status), nt_errstr(open_funcs[i].correct_status), i, (int)open_funcs[i].with_file, (int)open_funcs[i].create_disp); ret = false; goto done; } if (NT_STATUS_IS_OK(status) || open_funcs[i].with_file) { smb2_util_close(tree, io.smb2.out.file.handle); smb2_util_unlink(tree, fname); } } /* basic field testing */ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; status = smb2_create(tree, tctx, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.smb2.out.file.handle; CHECK_VAL(io.smb2.out.oplock_level, 0); CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED); CHECK_NTTIME(io.smb2.out.create_time, create_time); CHECK_NTTIME(io.smb2.out.access_time, access_time); CHECK_NTTIME(io.smb2.out.write_time, write_time); CHECK_NTTIME(io.smb2.out.change_time, change_time); CHECK_ALL_INFO(io.smb2.out.file_attr, attrib); CHECK_ALL_INFO(io.smb2.out.alloc_size, alloc_size); CHECK_ALL_INFO(io.smb2.out.size, size); /* check fields when the file already existed */ smb2_util_close(tree, h1); smb2_util_unlink(tree, fname); status = smb2_create_complex_file(tree, fname, &h1); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, h1); io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; status = smb2_create(tree, tctx, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.smb2.out.file.handle; CHECK_VAL(io.smb2.out.oplock_level, 0); CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_EXISTED); CHECK_NTTIME(io.smb2.out.create_time, create_time); CHECK_NTTIME(io.smb2.out.access_time, access_time); CHECK_NTTIME(io.smb2.out.write_time, write_time); CHECK_NTTIME(io.smb2.out.change_time, change_time); CHECK_ALL_INFO(io.smb2.out.file_attr, attrib); CHECK_ALL_INFO(io.smb2.out.alloc_size, alloc_size); CHECK_ALL_INFO(io.smb2.out.size, size); smb2_util_close(tree, h1); smb2_util_unlink(tree, fname); /* create a directory */ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; io.smb2.in.alloc_size = 0; io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; io.smb2.in.create_options = 0; io.smb2.in.fname = dname; fname = dname; smb2_util_rmdir(tree, fname); smb2_util_unlink(tree, fname); io.smb2.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; 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; status = smb2_create(tree, tctx, &(io.smb2)); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.smb2.out.file.handle; CHECK_VAL(io.smb2.out.oplock_level, 0); CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED); CHECK_NTTIME(io.smb2.out.create_time, create_time); CHECK_NTTIME(io.smb2.out.access_time, access_time); CHECK_NTTIME(io.smb2.out.write_time, write_time); CHECK_NTTIME(io.smb2.out.change_time, change_time); CHECK_ALL_INFO(io.smb2.out.file_attr, attrib); CHECK_VAL(io.smb2.out.file_attr & ~FILE_ATTRIBUTE_NONINDEXED, FILE_ATTRIBUTE_DIRECTORY); CHECK_ALL_INFO(io.smb2.out.alloc_size, alloc_size); CHECK_ALL_INFO(io.smb2.out.size, size); CHECK_VAL(io.smb2.out.size, 0); CHECK_VAL(io.smb2.out.alloc_size, 0); smb2_util_unlink(tree, fname); done: smb2_util_close(tree, h1); smb2_util_unlink(tree, fname); smb2_deltree(tree, DNAME); return ret; }
static bool test_lease_request(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; struct smb2_lease ls; struct smb2_handle h1, h2; NTSTATUS status; const char *fname = "lease.dat"; const char *fname2 = "lease2.dat"; const char *sname = "lease.dat:stream"; const char *dname = "lease.dir"; bool ret = true; int i; uint32_t caps; caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); if (!(caps & SMB2_CAP_LEASING)) { torture_skip(tctx, "leases are not supported"); } smb2_util_unlink(tree, fname); smb2_util_unlink(tree, fname2); smb2_util_rmdir(tree, dname); /* Win7 is happy to grant RHW leases on files. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RHW", true, LEASE1); /* But will reject leases on directories. */ smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); CHECK_LEASE(&io, "", false, 0); smb2_util_close(tree, io.out.file.handle); /* Also rejects multiple files leased under the same key. */ smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); /* And grants leases on streams (with separate leasekey). */ smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); h2 = io.out.file.handle; CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, "RHW", true, LEASE2); smb2_util_close(tree, h2); smb2_util_close(tree, h1); /* Now see what combos are actually granted. */ for (i = 0; i < NREQUEST_RESULTS; i++) { torture_comment(tctx, "Requesting lease type %s(%x)," " expecting %s(%x)\n", request_results[i][0], smb2_util_lease_state(request_results[i][0]), request_results[i][1], smb2_util_lease_state(request_results[i][1])); smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(request_results[i][0])); status = smb2_create(tree, mem_ctx, &io); h2 = io.out.file.handle; CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, request_results[i][1], true, LEASE1); smb2_util_close(tree, io.out.file.handle); } done: smb2_util_close(tree, h1); smb2_util_close(tree, h2); smb2_util_unlink(tree, fname); smb2_util_unlink(tree, fname2); smb2_util_rmdir(tree, dname); talloc_free(mem_ctx); return ret; }