From b529a1e98723c30f965f71fb1e9577edb23219d1 Mon Sep 17 00:00:00 2001 From: Aravind Srinivasan Date: Thu, 1 Oct 2009 16:13:37 -0700 Subject: s4/torture: Add two new SMB RAW-OPEN tests * Add chained NTCREATEX_READX test which first tries to open/read a non-existant file failing on the open, then attempts the same operation on a file that does exist, opening and reading successfully. * Add test for open_dispositions on directories. --- source4/libcli/raw/interfaces.h | 52 ++++++++++++ source4/libcli/raw/rawfile.c | 74 +++++++++++++++++ source4/torture/raw/open.c | 171 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 296 insertions(+), 1 deletion(-) diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index 13217158cb..f7d64d04cc 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -1356,6 +1356,7 @@ enum smb_open_level { RAW_OPEN_T2OPEN, RAW_OPEN_NTTRANS_CREATE, RAW_OPEN_OPENX_READX, + RAW_OPEN_NTCREATEX_READX, RAW_OPEN_SMB2 }; @@ -1400,6 +1401,9 @@ union smb_open { case RAW_OPEN_OPENX_READX: \ file = &op->openxreadx.out.file; \ break; \ + case RAW_OPEN_NTCREATEX_READX: \ + file = &op->ntcreatexreadx.out.file; \ + break; \ case RAW_OPEN_SMB2: \ file = &op->smb2.out.file; \ break; \ @@ -1619,6 +1623,54 @@ union smb_open { } out; } openxreadx; + /* chained NTCreateX/ReadX interface */ + struct { + enum smb_open_level level; + struct { + uint32_t flags; + uint32_t root_fid; + uint32_t access_mask; + uint64_t alloc_size; + uint32_t file_attr; + uint32_t share_access; + uint32_t open_disposition; + uint32_t create_options; + uint32_t impersonation; + uint8_t security_flags; + /* NOTE: fname can also be a pointer to a + uint64_t file_id if create_options has the + NTCREATEX_OPTIONS_OPEN_BY_FILE_ID flag set */ + const char *fname; + + /* readx part */ + uint64_t offset; + uint16_t mincnt; + uint32_t maxcnt; + uint16_t remaining; + } in; + struct { + union smb_handle file; + uint8_t oplock_level; + uint32_t create_action; + NTTIME create_time; + NTTIME access_time; + NTTIME write_time; + NTTIME change_time; + uint32_t attrib; + uint64_t alloc_size; + uint64_t size; + uint16_t file_type; + uint16_t ipc_state; + uint8_t is_directory; + + /* readx part */ + uint8_t *data; + uint16_t remaining; + uint16_t compaction_mode; + uint16_t nread; + } out; + } ntcreatexreadx; + #define SMB2_CREATE_FLAG_REQUEST_OPLOCK 0x0100 #define SMB2_CREATE_FLAG_REQUEST_EXCLUSIVE_OPLOCK 0x0800 #define SMB2_CREATE_FLAG_GRANT_OPLOCK 0x0001 diff --git a/source4/libcli/raw/rawfile.c b/source4/libcli/raw/rawfile.c index 35d6b75c4d..ea254a5bf2 100644 --- a/source4/libcli/raw/rawfile.c +++ b/source4/libcli/raw/rawfile.c @@ -616,6 +616,45 @@ _PUBLIC_ struct smbcli_request *smb_raw_open_send(struct smbcli_tree *tree, unio SIVAL(req->out.vwv, VWV(10),parms->openxreadx.in.offset>>32); } break; + + case RAW_OPEN_NTCREATEX_READX: + SETUP_REQUEST(SMBntcreateX, 24, 0); + SSVAL(req->out.vwv, VWV(0),SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1),0); + SCVAL(req->out.vwv, VWV(2),0); /* padding */ + SIVAL(req->out.vwv, 7, parms->ntcreatexreadx.in.flags); + SIVAL(req->out.vwv, 11, parms->ntcreatexreadx.in.root_fid); + SIVAL(req->out.vwv, 15, parms->ntcreatexreadx.in.access_mask); + SBVAL(req->out.vwv, 19, parms->ntcreatexreadx.in.alloc_size); + SIVAL(req->out.vwv, 27, parms->ntcreatexreadx.in.file_attr); + SIVAL(req->out.vwv, 31, parms->ntcreatexreadx.in.share_access); + SIVAL(req->out.vwv, 35, parms->ntcreatexreadx.in.open_disposition); + SIVAL(req->out.vwv, 39, parms->ntcreatexreadx.in.create_options); + SIVAL(req->out.vwv, 43, parms->ntcreatexreadx.in.impersonation); + SCVAL(req->out.vwv, 47, parms->ntcreatexreadx.in.security_flags); + + smbcli_req_append_string_len(req, parms->ntcreatexreadx.in.fname, STR_TERMINATE, &len); + SSVAL(req->out.vwv, 5, len); + + if (tree->session->transport->negotiate.capabilities & CAP_LARGE_FILES) { + bigoffset = true; + } + + smbcli_chained_request_setup(req, SMBreadX, bigoffset ? 12 : 10, 0); + + SSVAL(req->out.vwv, VWV(0), SMB_CHAIN_NONE); + SSVAL(req->out.vwv, VWV(1), 0); + SSVAL(req->out.vwv, VWV(2), 0); + SIVAL(req->out.vwv, VWV(3), parms->ntcreatexreadx.in.offset); + SSVAL(req->out.vwv, VWV(5), parms->ntcreatexreadx.in.maxcnt & 0xFFFF); + SSVAL(req->out.vwv, VWV(6), parms->ntcreatexreadx.in.mincnt); + SIVAL(req->out.vwv, VWV(7), parms->ntcreatexreadx.in.maxcnt >> 16); + SSVAL(req->out.vwv, VWV(9), parms->ntcreatexreadx.in.remaining); + if (bigoffset) { + SIVAL(req->out.vwv, VWV(10),parms->ntcreatexreadx.in.offset>>32); + } + break; + case RAW_OPEN_SMB2: return NULL; } @@ -753,6 +792,41 @@ _PUBLIC_ NTSTATUS smb_raw_open_recv(struct smbcli_request *req, TALLOC_CTX *mem_ req->status = NT_STATUS_BUFFER_TOO_SMALL; } break; + + case RAW_OPEN_NTCREATEX_READX: + SMBCLI_CHECK_MIN_WCT(req, 34); + parms->ntcreatexreadx.out.oplock_level = CVAL(req->in.vwv, 4); + parms->ntcreatexreadx.out.file.fnum = SVAL(req->in.vwv, 5); + parms->ntcreatexreadx.out.create_action = IVAL(req->in.vwv, 7); + parms->ntcreatexreadx.out.create_time = smbcli_pull_nttime(req->in.vwv, 11); + parms->ntcreatexreadx.out.access_time = smbcli_pull_nttime(req->in.vwv, 19); + parms->ntcreatexreadx.out.write_time = smbcli_pull_nttime(req->in.vwv, 27); + parms->ntcreatexreadx.out.change_time = smbcli_pull_nttime(req->in.vwv, 35); + parms->ntcreatexreadx.out.attrib = IVAL(req->in.vwv, 43); + parms->ntcreatexreadx.out.alloc_size = BVAL(req->in.vwv, 47); + parms->ntcreatexreadx.out.size = BVAL(req->in.vwv, 55); + parms->ntcreatexreadx.out.file_type = SVAL(req->in.vwv, 63); + parms->ntcreatexreadx.out.ipc_state = SVAL(req->in.vwv, 65); + parms->ntcreatexreadx.out.is_directory = CVAL(req->in.vwv, 67); + + status = smbcli_chained_advance(req); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + SMBCLI_CHECK_WCT(req, 12); + parms->ntcreatexreadx.out.remaining = SVAL(req->in.vwv, VWV(2)); + parms->ntcreatexreadx.out.compaction_mode = SVAL(req->in.vwv, VWV(3)); + parms->ntcreatexreadx.out.nread = SVAL(req->in.vwv, VWV(5)); + if (parms->ntcreatexreadx.out.nread > + MAX(parms->openxreadx.in.mincnt, parms->openxreadx.in.maxcnt) || + !smbcli_raw_pull_data(&req->in.bufinfo, req->in.hdr + SVAL(req->in.vwv, VWV(6)), + parms->ntcreatexreadx.out.nread, + parms->ntcreatexreadx.out.data)) { + req->status = NT_STATUS_BUFFER_TOO_SMALL; + } + break; + case RAW_OPEN_SMB2: req->status = NT_STATUS_INTERNAL_ERROR; break; diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c index b52c1d1f73..49540d8b53 100644 --- a/source4/torture/raw/open.c +++ b/source4/torture/raw/open.c @@ -1692,8 +1692,175 @@ done: return ret; } +/* + test chained RAW_OPEN_NTCREATEX_READX + Send chained NTCREATEX_READX on a file that doesn't exist, then create + the file and try again. +*/ +static bool test_chained_ntcreatex_readx(struct smbcli_state *cli, struct torture_context *tctx) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + union smb_open io; + const char *fname = BASEDIR "\\torture_chained.txt"; + NTSTATUS status; + int fnum = -1; + bool ret = true; + const char *buf = "test"; + char buf2[4]; + + torture_comment(tctx, "Checking RAW_NTCREATEX_READX chained on " + "non-existant file \n"); + smbcli_unlink(cli->tree, fname); + + /* ntcreatex parameters */ + io.generic.level = RAW_OPEN_NTCREATEX_READX; + io.ntcreatexreadx.in.flags = 0; + io.ntcreatexreadx.in.root_fid = 0; + io.ntcreatexreadx.in.access_mask = SEC_FILE_READ_DATA; + io.ntcreatexreadx.in.alloc_size = 0; + io.ntcreatexreadx.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatexreadx.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; + io.ntcreatexreadx.in.open_disposition = NTCREATEX_DISP_OPEN; + io.ntcreatexreadx.in.create_options = 0; + io.ntcreatexreadx.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.ntcreatexreadx.in.security_flags = 0; + io.ntcreatexreadx.in.fname = fname; + + /* readx parameters */ + io.ntcreatexreadx.in.offset = 0; + io.ntcreatexreadx.in.mincnt = sizeof(buf); + io.ntcreatexreadx.in.maxcnt = sizeof(buf); + io.ntcreatexreadx.in.remaining = 0; + io.ntcreatexreadx.out.data = (uint8_t *)buf2; + + /* try to open the non-existant file */ + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + fnum = io.ntcreatexreadx.out.file.fnum; + + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + + torture_comment(tctx, "Checking RAW_NTCREATEX_READX chained on " + "existing file \n"); + + fnum = create_complex_file(cli, mem_ctx, fname); + smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); + smbcli_close(cli->tree, fnum); + + status = smb_raw_open(cli->tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + fnum = io.ntcreatexreadx.out.file.fnum; + + if (memcmp(buf, buf2, sizeof(buf)) != 0) { + torture_result(tctx, TORTURE_FAIL, + "(%s): wrong data in reply buffer\n", __location__); + ret = false; + } + +done: + smbcli_close(cli->tree, fnum); + smbcli_unlink(cli->tree, fname); + talloc_free(mem_ctx); + + return ret; +} + +#define FILL_NTCREATEX(_struct, _init...) \ + do { \ + (_struct)->generic.level = RAW_OPEN_NTCREATEX; \ + (_struct)->ntcreatex.in \ + = (typeof((_struct)->ntcreatex.in)) {_init};\ + } while (0) + +static bool test_ntcreatex_opendisp_dir(struct smbcli_state *cli, + struct torture_context *tctx) +{ + union smb_open io; + const char *dname = BASEDIR "\\torture_ntcreatex_opendisp_dir"; + NTSTATUS status; + bool ret = true; + int i; + struct { + uint32_t open_disp; + bool dir_exists; + NTSTATUS correct_status; + } open_funcs_dir[] = { + { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_INVALID_PARAMETER }, + { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_INVALID_PARAMETER }, + { 6, true, NT_STATUS_INVALID_PARAMETER }, + { 6, false, NT_STATUS_INVALID_PARAMETER }, + }; + + if (!torture_setup_dir(cli, BASEDIR)) { + return false; + } + + FILL_NTCREATEX(&io, + .flags = NTCREATEX_FLAGS_EXTENDED, + .access_mask = SEC_FLAG_MAXIMUM_ALLOWED, + .file_attr = FILE_ATTRIBUTE_DIRECTORY, + .share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE, + .create_options = NTCREATEX_OPTIONS_DIRECTORY, + .fname = dname, + ); + + smbcli_rmdir(cli->tree, dname); + smbcli_unlink(cli->tree, dname); + + /* test the open disposition for directories */ + torture_comment(tctx, "Testing open dispositions for directories...\n"); + + for (i=0; itree, dname); + if (!NT_STATUS_IS_OK(status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s): Failed to make directory " + "%s - %s\n", __location__, dname, + smbcli_errstr(cli->tree)); + ret = false; + goto done; + } + } + + io.ntcreatex.in.open_disposition = open_funcs_dir[i].open_disp; + status = smb_raw_open(cli->tree, tctx, &io); + if (!NT_STATUS_EQUAL(status, open_funcs_dir[i].correct_status)) { + torture_result(tctx, TORTURE_FAIL, + "(%s) incorrect status %s should be %s " + "(i=%d dir_exists=%d open_disp=%d)\n", + __location__, nt_errstr(status), + nt_errstr(open_funcs_dir[i].correct_status), + i, (int)open_funcs_dir[i].dir_exists, + (int)open_funcs_dir[i].open_disp); + ret = false; + } + if (NT_STATUS_IS_OK(status) || open_funcs_dir[i].dir_exists) { + smbcli_close(cli->tree, io.ntcreatex.out.file.fnum); + smbcli_rmdir(cli->tree, dname); + } + } + +done: + smbcli_rmdir(cli->tree, dname); + smbcli_deltree(cli->tree, BASEDIR); + return ret; +} + -/* basic testing of all RAW_OPEN_* calls +/* basic testing of all RAW_OPEN_* calls */ bool torture_raw_open(struct torture_context *torture, struct smbcli_state *cli) { @@ -1714,9 +1881,11 @@ bool torture_raw_open(struct torture_context *torture, struct smbcli_state *cli) ret &= test_create(cli, torture); ret &= test_ctemp(cli, torture); ret &= test_chained(cli, torture); + ret &= test_chained_ntcreatex_readx(cli, torture); ret &= test_no_leading_slash(cli, torture); ret &= test_openx_over_dir(cli, torture); ret &= test_open_for_delete(cli, torture); + ret &= test_ntcreatex_opendisp_dir(cli, torture); smb_raw_exit(cli->session); smbcli_deltree(cli->tree, BASEDIR); -- cgit