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_compound_related3(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_handle hd; struct smb2_ioctl io; struct smb2_create cr; struct smb2_close cl; const char *fname = "compound_related3.dat"; struct smb2_request *req[3]; NTSTATUS status; bool ret = false; smb2_util_unlink(tree, fname); ZERO_STRUCT(cr); cr.in.security_flags = 0x00; cr.in.oplock_level = 0; cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; cr.in.create_flags = 0x00000000; cr.in.reserved = 0x00000000; cr.in.desired_access = SEC_RIGHTS_FILE_ALL; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; cr.in.fname = fname; smb2_transport_compound_start(tree->session->transport, 3); req[0] = smb2_create_send(tree, &cr); hd.data[0] = UINT64_MAX; hd.data[1] = UINT64_MAX; smb2_transport_compound_set_related(tree->session->transport, true); ZERO_STRUCT(io); io.in.function = FSCTL_CREATE_OR_GET_OBJECT_ID; io.in.file.handle = hd; io.in.unknown2 = 0; io.in.max_response_size = 64; io.in.flags = 1; req[1] = smb2_ioctl_send(tree, &io); ZERO_STRUCT(cl); cl.in.file.handle = hd; req[2] = smb2_close_send(tree, &cl); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_ioctl_recv(req[1], tree, &io); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_close_recv(req[2], &cl); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_unlink(tree, fname); CHECK_STATUS(status, NT_STATUS_OK); ret = true; done: return ret; }
static bool test_compound_related2(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_handle hd; struct smb2_create cr; NTSTATUS status; const char *fname = "compound_related2.dat"; struct smb2_close cl; bool ret = true; struct smb2_request *req[5]; struct smbXcli_tcon *saved_tcon = tree->smbXcli; struct smbXcli_session *saved_session = tree->session->smbXcli; smb2_transport_credits_ask_num(tree->session->transport, 5); smb2_util_unlink(tree, fname); smb2_transport_credits_ask_num(tree->session->transport, 1); ZERO_STRUCT(cr); cr.in.security_flags = 0x00; cr.in.oplock_level = 0; cr.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; cr.in.create_flags = 0x00000000; cr.in.reserved = 0x00000000; cr.in.desired_access = SEC_RIGHTS_FILE_ALL; cr.in.file_attributes = FILE_ATTRIBUTE_NORMAL; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF; cr.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | 0x00200000; cr.in.fname = fname; smb2_transport_compound_start(tree->session->transport, 5); req[0] = smb2_create_send(tree, &cr); hd.data[0] = UINT64_MAX; hd.data[1] = UINT64_MAX; smb2_transport_compound_set_related(tree->session->transport, true); ZERO_STRUCT(cl); cl.in.file.handle = hd; tree->smbXcli = smbXcli_tcon_create(tree); smb2cli_tcon_set_values(tree->smbXcli, NULL, /* session */ 0xFFFFFFFF, /* tcon_id */ 0, /* type */ 0, /* flags */ 0, /* capabilities */ 0 /* maximal_access */); tree->session->smbXcli = smbXcli_session_copy(tree->session, tree->session->smbXcli); smb2cli_session_set_id_and_flags(tree->session->smbXcli, UINT64_MAX, 0); req[1] = smb2_close_send(tree, &cl); req[2] = smb2_close_send(tree, &cl); req[3] = smb2_close_send(tree, &cl); req[4] = smb2_close_send(tree, &cl); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_close_recv(req[1], &cl); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_close_recv(req[2], &cl); CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); status = smb2_close_recv(req[3], &cl); CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); status = smb2_close_recv(req[4], &cl); CHECK_STATUS(status, NT_STATUS_FILE_CLOSED); TALLOC_FREE(tree->smbXcli); tree->smbXcli = saved_tcon; TALLOC_FREE(tree->session->smbXcli); tree->session->smbXcli = saved_session; smb2_util_unlink(tree, fname); done: 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; }
/* Send a compound request where we expect the middle request (Create, Notify, * GetInfo) to go asynchronous. Against Win7 the sync request succeed while * the async fails. All are returned in the same compound response. */ static bool test_compound_interim2(struct torture_context *tctx, struct smb2_tree *tree) { struct smb2_handle hd; struct smb2_create cr; NTSTATUS status = NT_STATUS_OK; const char *dname = "compound_interim_dir"; struct smb2_getinfo gf; struct smb2_notify nt; bool ret = true; struct smb2_request *req[3]; /* Win7 compound request implementation deviates substantially from the * SMB2 spec as noted in MS-SMB2 <159>, <162>. This, test currently * verifies the Windows behavior, not the general spec behavior. */ smb2_transport_credits_ask_num(tree->session->transport, 5); smb2_deltree(tree, dname); smb2_transport_credits_ask_num(tree->session->transport, 1); ZERO_STRUCT(cr); cr.in.desired_access = SEC_RIGHTS_FILE_ALL; cr.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; cr.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; cr.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; cr.in.create_disposition = NTCREATEX_DISP_CREATE; cr.in.fname = dname; smb2_transport_compound_start(tree->session->transport, 3); req[0] = smb2_create_send(tree, &cr); smb2_transport_compound_set_related(tree->session->transport, true); hd.data[0] = UINT64_MAX; hd.data[1] = UINT64_MAX; ZERO_STRUCT(nt); nt.in.recursive = true; nt.in.buffer_size = 0x1000; nt.in.file.handle = hd; nt.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; nt.in.unknown = 0x00000000; req[1] = smb2_notify_send(tree, &nt); ZERO_STRUCT(gf); gf.in.file.handle = hd; gf.in.info_type = SMB2_GETINFO_FILE; gf.in.info_class = 0x04; /* FILE_BASIC_INFORMATION */ gf.in.output_buffer_length = 0x1000; gf.in.input_buffer_length = 0; req[2] = smb2_getinfo_send(tree, &gf); status = smb2_create_recv(req[0], tree, &cr); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_notify_recv(req[1], tree, &nt); CHECK_STATUS(status, NT_STATUS_INTERNAL_ERROR); status = smb2_getinfo_recv(req[2], tree, &gf); CHECK_STATUS(status, NT_STATUS_OK); smb2_util_close(tree, cr.out.file.handle); smb2_deltree(tree, dname); done: return ret; }
static bool test_smb2_open_multi(struct torture_context *tctx, struct smb2_tree *tree) { const char *fname = "test_oplock.dat"; NTSTATUS status; bool ret = true; union smb_open io; struct smb2_tree **trees; struct smb2_request **requests; union smb_open *ios; int i, num_files = 3; int num_ok = 0; int num_collision = 0; torture_comment(tctx, "Testing SMB2 Open with multiple connections\n"); trees = talloc_array(tctx, struct smb2_tree *, num_files); requests = talloc_array(tctx, struct smb2_request *, num_files); ios = talloc_array(tctx, union smb_open, num_files); if ((tctx->ev == NULL) || (trees == NULL) || (requests == NULL) || (ios == NULL)) { torture_comment(tctx, ("talloc failed\n")); ret = false; goto done; } tree->session->transport->options.request_timeout = 60; for (i=0; i<num_files; i++) { if (!torture_smb2_connection(tctx, &(trees[i]))) { torture_comment(tctx, "Could not open %d'th connection\n", i); ret = false; goto done; } trees[i]->session->transport->options.request_timeout = 60; } /* cleanup */ smb2_util_unlink(tree, fname); /* base ntcreatex parms */ ZERO_STRUCT(io.smb2); io.generic.level = RAW_OPEN_SMB2; io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; 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| NTCREATEX_SHARE_ACCESS_DELETE; 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; io.smb2.in.create_flags = 0; for (i=0; i<num_files; i++) { ios[i] = io; requests[i] = smb2_create_send(trees[i], &(ios[i].smb2)); if (requests[i] == NULL) { torture_comment(tctx, "could not send %d'th request\n", i); ret = false; goto done; } } torture_comment(tctx, "waiting for replies\n"); while (1) { bool unreplied = false; for (i=0; i<num_files; i++) { if (requests[i] == NULL) { continue; } if (requests[i]->state < SMB2_REQUEST_DONE) { unreplied = true; break; } status = smb2_create_recv(requests[i], tctx, &(ios[i].smb2)); torture_comment(tctx, "File %d returned status %s\n", i, nt_errstr(status)); if (NT_STATUS_IS_OK(status)) { num_ok += 1; } if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) { num_collision += 1; } requests[i] = NULL; } if (!unreplied) { break; } if (tevent_loop_once(tctx->ev) != 0) { torture_comment(tctx, "tevent_loop_once failed\n"); ret = false; goto done; } } if ((num_ok != 1) || (num_ok + num_collision != num_files)) { ret = false; } done: smb2_deltree(tree, fname); return ret; }