static bool test_lease_upgrade2(struct torture_context *tctx, struct smb2_tree *tree) { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_handle h, hnew; NTSTATUS status; struct smb2_create io; struct smb2_lease ls; 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"); } for (i = 0; i < NUM_UPGRADE_TESTS; i++) { struct lease_upgrade2_test t = lease_upgrade2_tests[i]; smb2_util_unlink(tree, fname); /* Grab a lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial)); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE(&io, t.initial, true, LEASE1); h = io.out.file.handle; /* Upgrade. */ 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.expected, true, LEASE1); hnew = io.out.file.handle; smb2_util_close(tree, hnew); smb2_util_close(tree, h); } 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 session reconnect on one connection */ bool test_session_reconnect2(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; uint64_t previous_session_id; bool ret = true; struct smb2_session *session2; union smb_fileinfo qfinfo; /* Add some random component to the file name. */ snprintf(fname, 256, "session_reconnect_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; status = smb2_create(tree, 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")); /* disconnect, reconnect and then do durable reopen */ previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); torture_assert(tctx, torture_smb2_session_setup(tctx, tree->session->transport, previous_session_id, tctx, &session2), "session reconnect (on the same connection) failed"); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); h1 = NULL; done: if (h1 != NULL) { smb2_util_close(tree, *h1); } talloc_free(tree); talloc_free(session2); talloc_free(mem_ctx); return ret; }
/** * basic test for doing a durable open * and do a durable reopen on the same connection * while the first open is still active (fails) */ bool test_durable_v2_open_reopen1(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; /* Choose a random name in case the state is left a little funky. */ snprintf(fname, 256, "durable_v2_open_reopen1_%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); /* try a durable reconnect while the file is still open */ 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_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_one_durable_v2_open_lease(struct torture_context *tctx, struct smb2_tree *tree, const char *fname, bool request_persistent, struct durable_open_vs_lease test) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_handle _h; struct smb2_handle *h = NULL; bool ret = true; struct smb2_create io; struct smb2_lease ls; uint64_t lease; smb2_util_unlink(tree, fname); lease = random(); smb2_lease_create_share(&io, &ls, false /* dir */, fname, smb2_util_share_access(test.share_mode), lease, smb2_util_lease_state(test.type)); io.in.durable_open = false; io.in.durable_open_v2 = true; io.in.persistent_open = request_persistent; io.in.create_guid = GUID_random(); 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, test.durable); CHECK_VAL(io.out.persistent_open, test.persistent); 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_util_lease_state(test.type)); done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(mem_ctx); return ret; }
static bool test_one_durable_v2_open_oplock(struct torture_context *tctx, struct smb2_tree *tree, const char *fname, bool request_persistent, struct durable_open_vs_oplock test) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_handle _h; struct smb2_handle *h = NULL; bool ret = true; struct smb2_create io; smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io, fname, smb2_util_share_access(test.share_mode), smb2_util_oplock_level(test.level)); io.in.durable_open = false; io.in.durable_open_v2 = true; io.in.persistent_open = request_persistent; io.in.create_guid = GUID_random(); 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, test.durable); CHECK_VAL(io.out.persistent_open, test.persistent); CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level)); done: if (h != NULL) { smb2_util_close(tree, *h); } smb2_util_unlink(tree, fname); talloc_free(mem_ctx); return ret; }
bool test_session_reauth2(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; bool ret = true; union smb_fileinfo qfinfo; struct cli_credentials *anon_creds = NULL; /* Add some random component to the file name. */ snprintf(fname, sizeof(fname), "session_reauth2_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, mem_ctx, &io1); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); _h1 = io1.out.file.handle; h1 = &_h1; CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); torture_assert_int_equal(tctx, io1.out.oplock_level, smb2_util_oplock_level("b"), "oplock_level incorrect"); /* re-authenticate as anonymous */ anon_creds = cli_credentials_init_anon(mem_ctx); torture_assert(tctx, (anon_creds != NULL), "talloc error"); status = smb2_session_setup_spnego(tree->session, anon_creds, 0 /* previous_session_id */); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_session_setup_spnego failed"); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file failed"); /* re-authenticate as original user again */ status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_session_setup_spnego failed"); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file failed"); done: if (h1 != NULL) { smb2_util_close(tree, *h1); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
bool test_session_bind1(struct torture_context *tctx, struct smb2_tree *tree1) { const char *host = torture_setting_string(tctx, "host", NULL); const char *share = torture_setting_string(tctx, "share", NULL); struct cli_credentials *credentials = cmdline_credentials; NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; union smb_fileinfo qfinfo; bool ret = false; struct smb2_tree *tree2 = NULL; struct smb2_transport *transport1 = tree1->session->transport; struct smb2_transport *transport2 = NULL; struct smb2_session *session1_1 = tree1->session; struct smb2_session *session1_2 = NULL; struct smb2_session *session2_1 = NULL; struct smb2_session *session2_2 = NULL; uint32_t caps; caps = smb2cli_conn_server_capabilities(transport1->conn); if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); } /* Add some random component to the file name. */ snprintf(fname, sizeof(fname), "session_bind1_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree1, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree1, mem_ctx, &io1); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_create failed"); _h1 = io1.out.file.handle; h1 = &_h1; CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); torture_assert_int_equal(tctx, io1.out.oplock_level, smb2_util_oplock_level("b"), "oplock_level incorrect"); status = smb2_connect(tctx, host, lpcfg_smb_ports(tctx->lp_ctx), share, lpcfg_resolve_context(tctx->lp_ctx), credentials, &tree2, tctx->ev, &transport1->options, lpcfg_socket_options(tctx->lp_ctx), lpcfg_gensec_settings(tctx, tctx->lp_ctx) ); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_connect failed"); session2_2 = tree2->session; transport2 = tree2->session->transport; /* * Now bind the 2nd transport connection to the 1st session */ session1_2 = smb2_session_channel(transport2, lpcfg_gensec_settings(tctx, tctx->lp_ctx), tree2, session1_1); torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); status = smb2_session_setup_spnego(session1_2, cmdline_credentials, 0 /* previous_session_id */); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_session_setup_spnego failed"); /* use the 1st connection, 1st session */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; tree1->session = session1_1; status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file failed"); /* use the 2nd connection, 1st session */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; tree1->session = session1_2; status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_getinfo_file failed"); tree1->session = session1_1; status = smb2_util_close(tree1, *h1); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_close failed"); h1 = NULL; /* * Now bind the 1st transport connection to the 2nd session */ session2_1 = smb2_session_channel(transport1, lpcfg_gensec_settings(tctx, tctx->lp_ctx), tree1, session2_2); torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); status = smb2_session_setup_spnego(session2_1, cmdline_credentials, 0 /* previous_session_id */); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_session_setup_spnego failed"); tree2->session = session2_1; status = smb2_util_unlink(tree2, fname); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_util_unlink failed"); ret = true; done: talloc_free(tree2); tree1->session = session1_1; if (h1 != NULL) { smb2_util_close(tree1, *h1); } smb2_util_unlink(tree1, fname); talloc_free(tree1); talloc_free(mem_ctx); 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; }
/** * 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_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; }
/** * test renaming after reauth. * compare security descriptors before and after rename/reauth */ bool test_session_reauth5(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char dname[256]; char fname[256]; char fname2[256]; struct smb2_handle _dh1; struct smb2_handle *dh1 = NULL; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; bool ret = true; bool ok; union smb_fileinfo qfinfo; union smb_setfileinfo sfinfo; struct cli_credentials *anon_creds = NULL; uint32_t secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL; struct security_descriptor *f_sd1; struct security_descriptor *d_sd1 = NULL; struct security_ace ace; struct dom_sid *extra_sid; /* Add some random component to the file name. */ snprintf(dname, 256, "session_reauth5_%s.d", generate_random_str(tctx, 8)); snprintf(fname, 256, "%s\\file.dat", dname); ok = smb2_util_setup_dir(tctx, tree, dname); CHECK_VAL(ok, true); status = torture_smb2_testdir(tree, dname, &_dh1); CHECK_STATUS(status, NT_STATUS_OK); dh1 = &_dh1; smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, 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")); /* get the security descriptor */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); f_sd1 = qfinfo.query_secdesc.out.sd; /* re-authenticate as anonymous */ anon_creds = cli_credentials_init_anon(mem_ctx); torture_assert(tctx, (anon_creds != NULL), "talloc error"); status = smb2_session_setup_spnego(tree->session, anon_creds, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* try to rename the file: fails */ snprintf(fname2, 256, "%s\\file2.dat", dname); smb2_util_unlink(tree, fname2); ZERO_STRUCT(sfinfo); sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; sfinfo.rename_information.in.file.handle = _h1; sfinfo.rename_information.in.overwrite = true; sfinfo.rename_information.in.new_name = fname2; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); /* re-authenticate as original user again */ status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* give full access on the file to anonymous */ extra_sid = dom_sid_parse_talloc(tctx, SID_NT_ANONYMOUS); ZERO_STRUCT(ace); ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; ace.flags = 0; ace.access_mask = SEC_RIGHTS_FILE_ALL; ace.trustee = *extra_sid; status = security_descriptor_dacl_add(f_sd1, &ace); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(sfinfo); sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; sfinfo.set_secdesc.in.file.handle = _h1; sfinfo.set_secdesc.in.secinfo_flags = secinfo_flags; sfinfo.set_secdesc.in.sd = f_sd1; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); /* re-get the security descriptor */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); /* re-authenticate as anonymous - again */ anon_creds = cli_credentials_init_anon(mem_ctx); torture_assert(tctx, (anon_creds != NULL), "talloc error"); status = smb2_session_setup_spnego(tree->session, anon_creds, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* try to rename the file: fails */ ZERO_STRUCT(sfinfo); sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; sfinfo.rename_information.in.file.handle = _h1; sfinfo.rename_information.in.overwrite = true; sfinfo.rename_information.in.new_name = fname2; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); /* give full access on the parent dir to anonymous */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _dh1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); d_sd1 = qfinfo.query_secdesc.out.sd; ZERO_STRUCT(ace); ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; ace.flags = 0; ace.access_mask = SEC_RIGHTS_FILE_ALL; ace.trustee = *extra_sid; status = security_descriptor_dacl_add(d_sd1, &ace); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(sfinfo); sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; sfinfo.set_secdesc.in.file.handle = _dh1; sfinfo.set_secdesc.in.secinfo_flags = secinfo_flags; sfinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; sfinfo.set_secdesc.in.sd = d_sd1; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _dh1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, _dh1); dh1 = NULL; /* try to rename the file: still fails */ ZERO_STRUCT(sfinfo); sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; sfinfo.rename_information.in.file.handle = _h1; sfinfo.rename_information.in.overwrite = true; sfinfo.rename_information.in.new_name = fname2; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); /* re-authenticate as original user - again */ status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* rename the file - for verification that it works */ ZERO_STRUCT(sfinfo); sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; sfinfo.rename_information.in.file.handle = _h1; sfinfo.rename_information.in.overwrite = true; sfinfo.rename_information.in.new_name = fname2; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); /* closs the file, check it is gone and reopen under the new name */ smb2_util_close(tree, _h1); ZERO_STRUCT(io1); smb2_generic_create_share(&io1, NULL /* lease */, false /* dir */, fname, NTCREATEX_DISP_OPEN, smb2_util_share_access(""), smb2_util_oplock_level("b"), 0 /* leasekey */, 0 /* leasestate */); status = smb2_create(tree, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); ZERO_STRUCT(io1); smb2_generic_create_share(&io1, NULL /* lease */, false /* dir */, fname2, NTCREATEX_DISP_OPEN, smb2_util_share_access(""), smb2_util_oplock_level("b"), 0 /* leasekey */, 0 /* leasestate */); status = smb2_create(tree, mem_ctx, &io1); CHECK_STATUS(status, NT_STATUS_OK); _h1 = io1.out.file.handle; h1 = &_h1; CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b")); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); done: if (dh1 != NULL) { smb2_util_close(tree, *dh1); } if (h1 != NULL) { smb2_util_close(tree, *h1); } smb2_deltree(tree, dname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
/** * test getting security descriptor after reauth */ bool test_session_reauth3(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; bool ret = true; union smb_fileinfo qfinfo; struct cli_credentials *anon_creds = NULL; uint32_t secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL; /* Add some random component to the file name. */ snprintf(fname, 256, "session_reauth3_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, 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")); /* get the security descriptor */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); /* re-authenticate as anonymous */ anon_creds = cli_credentials_init_anon(mem_ctx); torture_assert(tctx, (anon_creds != NULL), "talloc error"); status = smb2_session_setup_spnego(tree->session, anon_creds, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); /* re-authenticate as original user again */ status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); done: if (h1 != NULL) { smb2_util_close(tree, *h1); } smb2_util_unlink(tree, fname); talloc_free(tree); 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; }
/** * 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; }
/* 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; }
/* 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; }
/* 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; }
/* 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_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; }
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; }
bool test_session_reauth1(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; bool ret = true; union smb_fileinfo qfinfo; /* Add some random component to the file name. */ snprintf(fname, 256, "session_reauth1_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, 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")); status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); done: if (h1 != NULL) { smb2_util_close(tree, *h1); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
/** * do reauth with wrong credentials, * hence triggering the error path in reauth. * The invalid reauth deletes the session. */ bool test_session_reauth6(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; bool ret = true; char *corrupted_password; struct cli_credentials *broken_creds; bool ok; bool encrypted; NTSTATUS expected; enum credentials_use_kerberos krb_state; krb_state = cli_credentials_get_kerberos_state(cmdline_credentials); if (krb_state == CRED_MUST_USE_KERBEROS) { torture_skip(tctx, "Can't test failing session setup with kerberos."); } encrypted = smb2cli_tcon_is_encryption_on(tree->smbXcli); /* Add some random component to the file name. */ snprintf(fname, 256, "session_reauth1_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; status = smb2_create(tree, 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")); /* * reauthentication with invalid credentials: */ broken_creds = cli_credentials_shallow_copy(mem_ctx, cmdline_credentials); torture_assert(tctx, (broken_creds != NULL), "talloc error"); corrupted_password = talloc_asprintf(mem_ctx, "%s%s", cli_credentials_get_password(broken_creds), "corrupt"); torture_assert(tctx, (corrupted_password != NULL), "talloc error"); ok = cli_credentials_set_password(broken_creds, corrupted_password, CRED_SPECIFIED); CHECK_VAL(ok, true); status = smb2_session_setup_spnego(tree->session, broken_creds, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_LOGON_FAILURE); torture_comment(tctx, "did failed reauth\n"); /* * now verify that the invalid session reauth has closed our session */ if (encrypted) { expected = NT_STATUS_CONNECTION_DISCONNECTED; } else { expected = NT_STATUS_USER_SESSION_DELETED; } smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, mem_ctx, &io1); CHECK_STATUS(status, expected); done: if (h1 != NULL) { smb2_util_close(tree, *h1); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
/** * test setting security descriptor after reauth. */ bool test_session_reauth4(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; bool ret = true; union smb_fileinfo qfinfo; union smb_setfileinfo sfinfo; struct cli_credentials *anon_creds = NULL; uint32_t secinfo_flags = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL | SECINFO_PROTECTED_DACL | SECINFO_UNPROTECTED_DACL; struct security_descriptor *sd1; struct security_ace ace; struct dom_sid *extra_sid; /* Add some random component to the file name. */ snprintf(fname, 256, "session_reauth4_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, 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")); /* get the security descriptor */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); sd1 = qfinfo.query_secdesc.out.sd; /* re-authenticate as anonymous */ anon_creds = cli_credentials_init_anon(mem_ctx); torture_assert(tctx, (anon_creds != NULL), "talloc error"); status = smb2_session_setup_spnego(tree->session, anon_creds, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* give full access on the file to anonymous */ extra_sid = dom_sid_parse_talloc(tctx, SID_NT_ANONYMOUS); ZERO_STRUCT(ace); ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; ace.flags = 0; ace.access_mask = SEC_STD_ALL | SEC_FILE_ALL; ace.trustee = *extra_sid; status = security_descriptor_dacl_add(sd1, &ace); CHECK_STATUS(status, NT_STATUS_OK); ZERO_STRUCT(sfinfo); sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; sfinfo.set_secdesc.in.file.handle = _h1; sfinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; sfinfo.set_secdesc.in.sd = sd1; status = smb2_setinfo_file(tree, &sfinfo); CHECK_STATUS(status, NT_STATUS_OK); /* re-authenticate as original user again */ status = smb2_session_setup_spnego(tree->session, cmdline_credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); /* re-get the security descriptor */ ZERO_STRUCT(qfinfo); qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; qfinfo.query_secdesc.in.file.handle = _h1; qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); ret = true; done: if (h1 != NULL) { smb2_util_close(tree, *h1); } smb2_util_unlink(tree, fname); talloc_free(tree); talloc_free(mem_ctx); return ret; }
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; }
/** * basic test for doing a session reconnect */ bool test_session_reconnect1(struct torture_context *tctx, struct smb2_tree *tree) { NTSTATUS status; TALLOC_CTX *mem_ctx = talloc_new(tctx); char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_handle _h2; struct smb2_handle *h2 = NULL; struct smb2_create io1, io2; uint64_t previous_session_id; bool ret = true; struct smb2_tree *tree2; union smb_fileinfo qfinfo; /* Add some random component to the file name. */ snprintf(fname, 256, "session_reconnect_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); status = smb2_create(tree, 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")); /* disconnect, reconnect and then do durable reopen */ previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree->session->transport->options, &tree2)) { torture_warning(tctx, "session reconnect failed\n"); ret = false; goto done; } /* try to access the file via the old handle */ ZERO_STRUCT(qfinfo); qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; qfinfo.generic.in.file.handle = _h1; status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED); h1 = NULL; smb2_oplock_create_share(&io2, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); 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")); _h2 = io2.out.file.handle; h2 = &_h2; done: if (h1 != NULL) { smb2_util_close(tree, *h1); } if (h2 != NULL) { smb2_util_close(tree2, *h2); } smb2_util_unlink(tree2, fname); talloc_free(tree); talloc_free(tree2); 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_session_expire1(struct torture_context *tctx) { NTSTATUS status; bool ret = false; struct smbcli_options options; const char *host = torture_setting_string(tctx, "host", NULL); const char *share = torture_setting_string(tctx, "share", NULL); struct cli_credentials *credentials = cmdline_credentials; struct smb2_tree *tree = NULL; enum credentials_use_kerberos use_kerberos; char fname[256]; struct smb2_handle _h1; struct smb2_handle *h1 = NULL; struct smb2_create io1; union smb_fileinfo qfinfo; size_t i; use_kerberos = cli_credentials_get_kerberos_state(credentials); if (use_kerberos != CRED_MUST_USE_KERBEROS) { torture_warning(tctx, "smb2.session.expire1 requires -k yes!"); torture_skip(tctx, "smb2.session.expire1 requires -k yes!"); } torture_assert_int_equal(tctx, use_kerberos, CRED_MUST_USE_KERBEROS, "please use -k yes"); lpcfg_set_option(tctx->lp_ctx, "gensec_gssapi:requested_life_time=4"); lpcfg_smbcli_options(tctx->lp_ctx, &options); status = smb2_connect(tctx, host, lpcfg_smb_ports(tctx->lp_ctx), share, lpcfg_resolve_context(tctx->lp_ctx), credentials, &tree, tctx->ev, &options, lpcfg_socket_options(tctx->lp_ctx), lpcfg_gensec_settings(tctx, tctx->lp_ctx) ); torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "smb2_connect failed"); /* Add some random component to the file name. */ snprintf(fname, 256, "session_expire1_%s.dat", generate_random_str(tctx, 8)); smb2_util_unlink(tree, fname); smb2_oplock_create_share(&io1, fname, smb2_util_share_access(""), smb2_util_oplock_level("b")); io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; status = smb2_create(tree, tctx, &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")); /* get the security descriptor */ ZERO_STRUCT(qfinfo); qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; qfinfo.access_information.in.file.handle = _h1; for (i=0; i < 2; i++) { torture_comment(tctx, "query info => OK\n"); ZERO_STRUCT(qfinfo.access_information.out); status = smb2_getinfo_file(tree, tctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); torture_comment(tctx, "sleep 5 seconds\n"); smb_msleep(5*1000); torture_comment(tctx, "query info => EXPIRED\n"); ZERO_STRUCT(qfinfo.access_information.out); status = smb2_getinfo_file(tree, tctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_NETWORK_SESSION_EXPIRED); /* * the krb5 library may not handle expired creds * well, lets start with an empty ccache. */ cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); torture_comment(tctx, "reauth => OK\n"); status = smb2_session_setup_spnego(tree->session, credentials, 0 /* previous_session_id */); CHECK_STATUS(status, NT_STATUS_OK); } ZERO_STRUCT(qfinfo.access_information.out); status = smb2_getinfo_file(tree, tctx, &qfinfo); CHECK_STATUS(status, NT_STATUS_OK); ret = true; done: if (h1 != NULL) { smb2_util_close(tree, *h1); } talloc_free(tree); lpcfg_set_option(tctx->lp_ctx, "gensec_gssapi:requested_life_time=0"); 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; }
/** * 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; }
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; }