diff options
-rw-r--r-- | source4/libcli/raw/rawfileinfo.c | 74 | ||||
-rw-r--r-- | source4/libcli/smb2/getinfo.c | 142 | ||||
-rw-r--r-- | source4/libcli/smb2/smb2_calls.h | 30 | ||||
-rw-r--r-- | source4/torture/smb2/config.mk | 4 | ||||
-rw-r--r-- | source4/torture/smb2/connect.c | 23 | ||||
-rw-r--r-- | source4/torture/smb2/getinfo.c | 116 | ||||
-rw-r--r-- | source4/torture/smb2/scan.c | 64 | ||||
-rw-r--r-- | source4/torture/smb2/util.c | 199 | ||||
-rw-r--r-- | source4/torture/torture.c | 1 | ||||
-rw-r--r-- | source4/torture/torture_util.c | 101 |
10 files changed, 626 insertions, 128 deletions
diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c index ede4391824..f631522182 100644 --- a/source4/libcli/raw/rawfileinfo.c +++ b/source4/libcli/raw/rawfileinfo.c @@ -34,6 +34,51 @@ return NT_STATUS_INFO_LENGTH_MISMATCH; \ } +/* + parse a stream information structure +*/ +NTSTATUS smbcli_parse_stream_info(DATA_BLOB blob, TALLOC_CTX *mem_ctx, + struct stream_information *io) +{ + uint32_t ofs = 0; + io->num_streams = 0; + io->streams = NULL; + + while (blob.length - ofs >= 24) { + uint_t n = io->num_streams; + uint32_t nlen, len; + ssize_t size; + void *vstr; + io->streams = + talloc_realloc(mem_ctx, io->streams, struct stream_struct, n+1); + if (!io->streams) { + return NT_STATUS_NO_MEMORY; + } + nlen = IVAL(blob.data, ofs + 0x04); + io->streams[n].size = BVAL(blob.data, ofs + 0x08); + io->streams[n].alloc_size = BVAL(blob.data, ofs + 0x10); + if (nlen > blob.length - (ofs + 24)) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + size = convert_string_talloc(io->streams, CH_UTF16, CH_UNIX, + blob.data+ofs+24, nlen, &vstr); + if (size == -1) { + return NT_STATUS_ILLEGAL_CHARACTER; + } + io->streams[n].stream_name.s = vstr; + io->streams[n].stream_name.private_length = nlen; + io->num_streams++; + len = IVAL(blob.data, ofs); + if (len > blob.length - ofs) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + if (len == 0) break; + ofs += len; + } + + return NT_STATUS_OK; +} + /**************************************************************************** Handle qfileinfo/qpathinfo trans2 backend. ****************************************************************************/ @@ -42,8 +87,6 @@ static NTSTATUS smb_raw_info_backend(struct smbcli_session *session, union smb_fileinfo *parms, DATA_BLOB *blob) { - uint_t len, ofs; - switch (parms->generic.level) { case RAW_FILEINFO_GENERIC: case RAW_FILEINFO_GETATTR: @@ -175,32 +218,7 @@ static NTSTATUS smb_raw_info_backend(struct smbcli_session *session, case RAW_FILEINFO_STREAM_INFO: case RAW_FILEINFO_STREAM_INFORMATION: - ofs = 0; - parms->stream_info.out.num_streams = 0; - parms->stream_info.out.streams = NULL; - - while (blob->length - ofs >= 24) { - uint_t n = parms->stream_info.out.num_streams; - parms->stream_info.out.streams = - talloc_realloc(mem_ctx, - parms->stream_info.out.streams, - struct stream_struct, - n+1); - if (!parms->stream_info.out.streams) { - return NT_STATUS_NO_MEMORY; - } - parms->stream_info.out.streams[n].size = BVAL(blob->data, ofs + 8); - parms->stream_info.out.streams[n].alloc_size = BVAL(blob->data, ofs + 16); - smbcli_blob_pull_string(session, mem_ctx, blob, - &parms->stream_info.out.streams[n].stream_name, - ofs+4, ofs+24, STR_UNICODE); - parms->stream_info.out.num_streams++; - len = IVAL(blob->data, ofs); - if (len > blob->length - ofs) return NT_STATUS_INFO_LENGTH_MISMATCH; - if (len == 0) break; - ofs += len; - } - return NT_STATUS_OK; + return smbcli_parse_stream_info(*blob, mem_ctx, &parms->stream_info.out); case RAW_FILEINFO_INTERNAL_INFORMATION: FINFO_CHECK_SIZE(8); diff --git a/source4/libcli/smb2/getinfo.c b/source4/libcli/smb2/getinfo.c index 9ad2b77310..7a362b24d9 100644 --- a/source4/libcli/smb2/getinfo.c +++ b/source4/libcli/smb2/getinfo.c @@ -113,18 +113,18 @@ NTSTATUS smb2_getinfo_parse(TALLOC_CTX *mem_ctx, if (blob.length != 0x18) { return NT_STATUS_INFO_LENGTH_MISMATCH; } - io->size_info.alloc_size = BVAL(blob.data, 0x00); - io->size_info.size = BVAL(blob.data, 0x08); - io->size_info.nlink = IVAL(blob.data, 0x10); - io->size_info.unknown = IVAL(blob.data, 0x14); + io->size_info.alloc_size = BVAL(blob.data, 0x00); + io->size_info.size = BVAL(blob.data, 0x08); + io->size_info.nlink = IVAL(blob.data, 0x10); + io->size_info.delete_pending = CVAL(blob.data, 0x14); + io->size_info.directory = CVAL(blob.data, 0x15); break; - case SMB2_GETINFO_FILE_06: + case SMB2_GETINFO_FILE_ID: if (blob.length != 0x8) { return NT_STATUS_INFO_LENGTH_MISMATCH; } - io->unknown06.unknown1 = IVAL(blob.data, 0x00); - io->unknown06.unknown2 = IVAL(blob.data, 0x04); + io->file_id.file_id = BVAL(blob.data, 0x00); break; case SMB2_GETINFO_FILE_EA_SIZE: @@ -172,37 +172,105 @@ NTSTATUS smb2_getinfo_parse(TALLOC_CTX *mem_ctx, uint32_t nlen; ssize_t size; void *vstr; - if (blob.length != 0x60) { + if (blob.length < 0x64) { return NT_STATUS_INFO_LENGTH_MISMATCH; } - io->all_info.create_time = smbcli_pull_nttime(blob.data, 0x00); - io->all_info.access_time = smbcli_pull_nttime(blob.data, 0x08); - io->all_info.write_time = smbcli_pull_nttime(blob.data, 0x10); - io->all_info.change_time = smbcli_pull_nttime(blob.data, 0x18); - io->all_info.file_attr = IVAL(blob.data, 0x20); - io->all_info.unknown1 = IVAL(blob.data, 0x24); - io->all_info.alloc_size = BVAL(blob.data, 0x28); - io->all_info.size = BVAL(blob.data, 0x30); - io->all_info.nlink = IVAL(blob.data, 0x38); - io->all_info.unknown2 = IVAL(blob.data, 0x3C); - io->all_info.unknown3 = IVAL(blob.data, 0x40); - io->all_info.unknown4 = IVAL(blob.data, 0x44); - io->all_info.ea_size = IVAL(blob.data, 0x48); - io->all_info.access_mask = IVAL(blob.data, 0x4C); - io->all_info.unknown5 = BVAL(blob.data, 0x50); - io->all_info.unknown6 = BVAL(blob.data, 0x58); - nlen = IVAL(blob.data, 0x5C); - if (nlen > blob.length - 0x60) { + io->all_info.create_time = smbcli_pull_nttime(blob.data, 0x00); + io->all_info.access_time = smbcli_pull_nttime(blob.data, 0x08); + io->all_info.write_time = smbcli_pull_nttime(blob.data, 0x10); + io->all_info.change_time = smbcli_pull_nttime(blob.data, 0x18); + io->all_info.file_attr = IVAL(blob.data, 0x20); + io->all_info.alloc_size = BVAL(blob.data, 0x28); + io->all_info.size = BVAL(blob.data, 0x30); + io->all_info.nlink = IVAL(blob.data, 0x38); + io->all_info.delete_pending = CVAL(blob.data, 0x3C); + io->all_info.directory = CVAL(blob.data, 0x3D); + io->all_info.file_id = BVAL(blob.data, 0x40); + io->all_info.ea_size = IVAL(blob.data, 0x48); + io->all_info.access_mask = IVAL(blob.data, 0x4C); + io->all_info.unknown5 = BVAL(blob.data, 0x50); + io->all_info.unknown6 = BVAL(blob.data, 0x58); + nlen = IVAL(blob.data, 0x60); + if (nlen > blob.length - 0x64) { return NT_STATUS_INFO_LENGTH_MISMATCH; } size = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, - blob.data+0x60, nlen, &vstr); + blob.data+0x64, nlen, &vstr); if (size == -1) { return NT_STATUS_ILLEGAL_CHARACTER; } io->all_info.fname = vstr; break; } + + case SMB2_GETINFO_FILE_SHORT_INFO: { + uint32_t nlen; + ssize_t size; + void *vstr; + if (blob.length < 0x04) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + nlen = IVAL(blob.data, 0x00); + if (nlen > blob.length - 0x04) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + size = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, + blob.data+0x04, nlen, &vstr); + if (size == -1) { + return NT_STATUS_ILLEGAL_CHARACTER; + } + io->short_info.short_name = vstr; + break; + } + + case SMB2_GETINFO_FILE_STREAM_INFO: + return smbcli_parse_stream_info(blob, mem_ctx, &io->stream_info); + + case SMB2_GETINFO_FILE_EOF_INFO: + if (blob.length != 0x10) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + io->eof_info.size = BVAL(blob.data, 0x00); + io->eof_info.unknown = BVAL(blob.data, 0x08); + break; + + case SMB2_GETINFO_FILE_STANDARD_INFO: + if (blob.length != 0x38) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + io->standard_info.create_time = smbcli_pull_nttime(blob.data, 0x00); + io->standard_info.access_time = smbcli_pull_nttime(blob.data, 0x08); + io->standard_info.write_time = smbcli_pull_nttime(blob.data, 0x10); + io->standard_info.change_time = smbcli_pull_nttime(blob.data, 0x18); + io->standard_info.alloc_size = BVAL(blob.data, 0x20); + io->standard_info.size = BVAL(blob.data, 0x28); + io->standard_info.file_attr = IVAL(blob.data, 0x30); + io->standard_info.unknown = IVAL(blob.data, 0x34); + break; + + case SMB2_GETINFO_FILE_ATTRIB_INFO: + if (blob.length != 0x08) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + io->standard_info.file_attr = IVAL(blob.data, 0x00); + io->standard_info.unknown = IVAL(blob.data, 0x04); + break; + + case SMB2_GETINFO_SECURITY: { + struct ndr_pull *ndr; + NTSTATUS status; + ndr = ndr_pull_init_blob(&blob, mem_ctx); + if (!ndr) { + return NT_STATUS_NO_MEMORY; + } + io->security.sd = talloc(mem_ctx, struct security_descriptor); + if (io->security.sd == NULL) { + return NT_STATUS_NO_MEMORY; + } + status = ndr_pull_security_descriptor(ndr, NDR_SCALARS|NDR_BUFFERS, io->security.sd); + talloc_free(ndr); + return status; + } default: return NT_STATUS_INVALID_INFO_CLASS; @@ -230,3 +298,23 @@ NTSTATUS smb2_getinfo_level_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, return status; } +/* + level specific getinfo call +*/ +NTSTATUS smb2_getinfo_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, + struct smb2_handle handle, + uint16_t level, union smb2_fileinfo *io) +{ + struct smb2_getinfo b; + struct smb2_request *req; + + ZERO_STRUCT(b); + b.in.buffer_code = 0x29; + b.in.max_response_size = 0x10000; + b.in.handle = handle; + b.in.level = level; + + req = smb2_getinfo_send(tree, &b); + + return smb2_getinfo_level_recv(req, mem_ctx, level, io); +} diff --git a/source4/libcli/smb2/smb2_calls.h b/source4/libcli/smb2/smb2_calls.h index aa2fb717b2..127a9d229c 100644 --- a/source4/libcli/smb2/smb2_calls.h +++ b/source4/libcli/smb2/smb2_calls.h @@ -170,7 +170,7 @@ struct smb2_close { /* file information levels */ #define SMB2_GETINFO_FILE_BASIC_INFO 0x0401 #define SMB2_GETINFO_FILE_SIZE_INFO 0x0501 -#define SMB2_GETINFO_FILE_06 0x0601 +#define SMB2_GETINFO_FILE_ID 0x0601 #define SMB2_GETINFO_FILE_EA_SIZE 0x0701 #define SMB2_GETINFO_FILE_ACCESS_INFO 0x0801 #define SMB2_GETINFO_FILE_0E 0x0e01 @@ -217,13 +217,13 @@ union smb2_fileinfo { uint64_t alloc_size; uint64_t size; uint32_t nlink; - uint32_t unknown; + uint8_t delete_pending; + uint8_t directory; } size_info; struct { - uint32_t unknown1; - uint32_t unknown2; - } unknown06; + uint64_t file_id; + } file_id; struct { uint32_t ea_size; @@ -256,13 +256,14 @@ union smb2_fileinfo { NTTIME write_time; NTTIME change_time; uint32_t file_attr; - uint32_t unknown1; + /* uint32_t _pad; */ uint64_t alloc_size; uint64_t size; uint32_t nlink; - uint32_t unknown2; - uint32_t unknown3; - uint32_t unknown4; + uint8_t delete_pending; + uint8_t directory; + /* uint16_t _pad; */ + uint64_t file_id; uint32_t ea_size; uint32_t access_mask; uint64_t unknown5; @@ -274,12 +275,7 @@ union smb2_fileinfo { const char *short_name; } short_info; - struct { - uint32_t unknown; - uint64_t size; - uint64_t alloc_size; - const char *stream_name; - } stream_info; + struct stream_information stream_info; struct { uint64_t size; @@ -301,6 +297,10 @@ union smb2_fileinfo { uint32_t file_attr; uint32_t unknown; } attrib_info; + + struct { + struct security_descriptor *sd; + } security; }; diff --git a/source4/torture/smb2/config.mk b/source4/torture/smb2/config.mk index 2c6dfd4fc9..7c7cdeab62 100644 --- a/source4/torture/smb2/config.mk +++ b/source4/torture/smb2/config.mk @@ -4,7 +4,9 @@ [SUBSYSTEM::TORTURE_SMB2] ADD_OBJ_FILES = \ connect.o \ - scan.o + scan.o \ + util.o \ + getinfo.o REQUIRED_SUBSYSTEMS = \ LIBCLI_SMB2 # End SUBSYSTEM TORTURE_SMB2 diff --git a/source4/torture/smb2/connect.c b/source4/torture/smb2/connect.c index 4907aadecb..077c873d08 100644 --- a/source4/torture/smb2/connect.c +++ b/source4/torture/smb2/connect.c @@ -82,12 +82,26 @@ static NTSTATUS torture_smb2_write(struct smb2_tree *tree, struct smb2_handle ha w.in.handle = handle; w.in.data = data; + memset(w.in._pad, 0xff, 16); + + status = smb2_write(tree, &w); + if (!NT_STATUS_IS_OK(status)) { + printf("write failed - %s\n", nt_errstr(status)); + return status; + } + + torture_smb2_all_info(tree, handle); + + memset(w.in._pad, 0xff, 16); + status = smb2_write(tree, &w); if (!NT_STATUS_IS_OK(status)) { printf("write failed - %s\n", nt_errstr(status)); return status; } + torture_smb2_all_info(tree, handle); + ZERO_STRUCT(r); r.in.buffer_code = 0x31; r.in.length = data.length; @@ -168,16 +182,9 @@ BOOL torture_smb2_connect(void) { TALLOC_CTX *mem_ctx = talloc_new(NULL); struct smb2_tree *tree; - const char *host = lp_parm_string(-1, "torture", "host"); - const char *share = lp_parm_string(-1, "torture", "share"); - struct cli_credentials *credentials = cmdline_credentials; struct smb2_handle h1, h2; - NTSTATUS status; - status = smb2_connect(mem_ctx, host, share, credentials, &tree, - event_context_find(mem_ctx)); - if (!NT_STATUS_IS_OK(status)) { - printf("Connection failed - %s\n", nt_errstr(status)); + if (!torture_smb2_connection(mem_ctx, &tree)) { return False; } diff --git a/source4/torture/smb2/getinfo.c b/source4/torture/smb2/getinfo.c new file mode 100644 index 0000000000..843ef5b5d0 --- /dev/null +++ b/source4/torture/smb2/getinfo.c @@ -0,0 +1,116 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 getinfo test suite + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" + +static struct { + const char *name; + uint16_t level; + NTSTATUS fstatus; + NTSTATUS dstatus; + union smb2_fileinfo finfo; + union smb2_fileinfo dinfo; +} levels[] = { +#define LEVEL(x) #x, x + { LEVEL(SMB2_GETINFO_FS_01) }, + { LEVEL(SMB2_GETINFO_FS_03) }, + { LEVEL(SMB2_GETINFO_FS_04) }, + { LEVEL(SMB2_GETINFO_FS_ATTRIB_INFO) }, + { LEVEL(SMB2_GETINFO_FS_06) }, + { LEVEL(SMB2_GETINFO_FS_07) }, + { LEVEL(SMB2_GETINFO_FS_08) }, + { LEVEL(SMB2_GETINFO_SECURITY) }, + { LEVEL(SMB2_GETINFO_FILE_BASIC_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_SIZE_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_ID) }, + { LEVEL(SMB2_GETINFO_FILE_EA_SIZE) }, + { LEVEL(SMB2_GETINFO_FILE_ACCESS_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_0E) }, + { LEVEL(SMB2_GETINFO_FILE_ALL_EAS) }, + { LEVEL(SMB2_GETINFO_FILE_10) }, + { LEVEL(SMB2_GETINFO_FILE_11) }, + { LEVEL(SMB2_GETINFO_FILE_ALL_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_SHORT_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_STREAM_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_EOF_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_STANDARD_INFO) }, + { LEVEL(SMB2_GETINFO_FILE_ATTRIB_INFO) } +}; + +#define FNAME "testsmb2_file.dat" +#define DNAME "testsmb2_dir" + +/* basic testing of all SMB2 getinfo levels +*/ +BOOL torture_smb2_getinfo(void) +{ + TALLOC_CTX *mem_ctx = talloc_new(NULL); + struct smb2_handle hfile, hdir; + struct smb2_tree *tree; + NTSTATUS status; + int i; + + if (!torture_smb2_connection(mem_ctx, &tree)) { + goto failed; + } + + torture_setup_complex_file(FNAME); + torture_setup_complex_file(FNAME ":streamtwo"); + torture_setup_complex_dir(DNAME); + torture_setup_complex_file(DNAME ":streamtwo"); + + status = torture_smb2_testfile(tree, FNAME, &hfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Unable to create test file '%s' - %s\n", FNAME, nt_errstr(status)); + goto failed; + } + + status = torture_smb2_testdir(tree, DNAME, &hdir); + if (!NT_STATUS_IS_OK(status)) { + printf("Unable to create test directory '%s' - %s\n", DNAME, nt_errstr(status)); + goto failed; + } + + torture_smb2_all_info(tree, hfile); + torture_smb2_all_info(tree, hdir); + + for (i=0;i<ARRAY_SIZE(levels);i++) { + levels[i].fstatus = smb2_getinfo_level(tree, mem_ctx, hfile, + levels[i].level, &levels[i].finfo); + if (!NT_STATUS_IS_OK(levels[i].fstatus)) { + printf("%s failed on file - %s\n", levels[i].name, nt_errstr(levels[i].fstatus)); + } + levels[i].dstatus = smb2_getinfo_level(tree, mem_ctx, hdir, + levels[i].level, &levels[i].dinfo); + if (!NT_STATUS_IS_OK(levels[i].dstatus)) { + printf("%s failed on dir - %s\n", levels[i].name, nt_errstr(levels[i].dstatus)); + } + } + + return True; + +failed: + talloc_free(mem_ctx); + return False; +} diff --git a/source4/torture/smb2/scan.c b/source4/torture/smb2/scan.c index 22d17d530c..2f2ab82d0f 100644 --- a/source4/torture/smb2/scan.c +++ b/source4/torture/smb2/scan.c @@ -28,31 +28,6 @@ #include "lib/events/events.h" -/* - create a complex file using the old SMB protocol, to make it easier to - find fields in SMB2 getinfo levels -*/ -static BOOL setup_complex_file(const char *fname) -{ - struct smbcli_state *cli; - int fnum; - - if (!torture_open_connection(&cli)) { - return False; - } - - fnum = create_complex_file(cli, cli, fname); - - if (DEBUGLVL(1)) { - torture_all_info(cli->tree, fname); - } - - talloc_free(cli); - return fnum != -1; -} - - - /* scan for valid SMB2 getinfo levels */ @@ -60,9 +35,6 @@ BOOL torture_smb2_getinfo_scan(void) { TALLOC_CTX *mem_ctx = talloc_new(NULL); struct smb2_tree *tree; - const char *host = lp_parm_string(-1, "torture", "host"); - const char *share = lp_parm_string(-1, "torture", "share"); - struct cli_credentials *credentials = cmdline_credentials; NTSTATUS status; struct smb2_getinfo io; struct smb2_create cr; @@ -70,14 +42,11 @@ BOOL torture_smb2_getinfo_scan(void) int c, i; const char *fname = "scan-getinfo.dat"; - status = smb2_connect(mem_ctx, host, share, credentials, &tree, - event_context_find(mem_ctx)); - if (!NT_STATUS_IS_OK(status)) { - printf("Connection failed - %s\n", nt_errstr(status)); + if (!torture_smb2_connection(mem_ctx, &tree)) { return False; } - if (!setup_complex_file(fname)) { + if (!torture_setup_complex_file(fname)) { printf("Failed to setup complex file '%s'\n", fname); } @@ -108,6 +77,27 @@ BOOL torture_smb2_getinfo_scan(void) io.in.max_response_size = 0xFFFF; io.in.handle = handle; + io.in.max_response_size = 128; + io.in.unknown1 = 0; + io.in.level = SMB2_GETINFO_FILE_ALL_INFO; + status = smb2_getinfo(tree, mem_ctx, &io); + + io.in.max_response_size = 128; + io.in.unknown1 = 64; + io.in.flags = 64; + io.in.unknown3 = 64; + io.in.unknown4 = 64; + io.in.level = SMB2_GETINFO_FILE_ALL_INFO; + status = smb2_getinfo(tree, mem_ctx, &io); + + if (!NT_STATUS_IS_OK(status)) { + printf("level 0x%04x is %d bytes - %s\n", + io.in.level, io.out.blob.length, nt_errstr(status)); + dump_data(1, io.out.blob.data, io.out.blob.length); + } + + return True; + for (c=0;c<5;c++) { for (i=0;i<0x100;i++) { io.in.level = (i<<8) | c; @@ -117,11 +107,9 @@ BOOL torture_smb2_getinfo_scan(void) NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { continue; } - if (NT_STATUS_IS_OK(status)) { - printf("level 0x%04x is %d bytes\n", - io.in.level, io.out.blob.length); - dump_data(1, io.out.blob.data, io.out.blob.length); - } + printf("level 0x%04x is %d bytes - %s\n", + io.in.level, io.out.blob.length, nt_errstr(status)); + dump_data(1, io.out.blob.data, io.out.blob.length); } } diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c new file mode 100644 index 0000000000..7c4c99e777 --- /dev/null +++ b/source4/torture/smb2/util.c @@ -0,0 +1,199 @@ +/* + Unix SMB/CIFS implementation. + + helper functions for SMB2 test suite + + Copyright (C) Andrew Tridgell 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "lib/cmdline/popt_common.h" +#include "lib/events/events.h" + +/* + show lots of information about a file +*/ +void torture_smb2_all_info(struct smb2_tree *tree, struct smb2_handle handle) +{ + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + union smb2_fileinfo io; + + status = smb2_getinfo_level(tree, tmp_ctx, handle, SMB2_GETINFO_FILE_ALL_INFO, &io); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("getinfo failed - %s\n", nt_errstr(status))); + talloc_free(tmp_ctx); + return; + } + + d_printf("\tcreate_time: %s\n", nt_time_string(tmp_ctx, io.all_info.create_time)); + d_printf("\taccess_time: %s\n", nt_time_string(tmp_ctx, io.all_info.access_time)); + d_printf("\twrite_time: %s\n", nt_time_string(tmp_ctx, io.all_info.write_time)); + d_printf("\tchange_time: %s\n", nt_time_string(tmp_ctx, io.all_info.change_time)); + d_printf("\tattrib: 0x%x\n", io.all_info.file_attr); + d_printf("\talloc_size: %llu\n", (uint64_t)io.all_info.alloc_size); + d_printf("\tsize: %llu\n", (uint64_t)io.all_info.size); + d_printf("\tnlink: %u\n", io.all_info.nlink); + d_printf("\tdelete_pending: %u\n", io.all_info.delete_pending); + d_printf("\tdirectory: %u\n", io.all_info.directory); + d_printf("\tfile_id: %llu\n", io.all_info.file_id); + d_printf("\tea_size: %u\n", io.all_info.ea_size); + d_printf("\taccess_mask: 0x%08x\n", io.all_info.access_mask); + d_printf("\tunknown5: 0x%llx\n", io.all_info.unknown5); + d_printf("\tunknown6: 0x%llx\n", io.all_info.unknown6); + d_printf("\tfname: '%s'\n", io.all_info.fname); + + talloc_free(tmp_ctx); +} + + +/* + open a smb2 connection +*/ +BOOL torture_smb2_connection(TALLOC_CTX *mem_ctx, struct smb2_tree **tree) +{ + NTSTATUS status; + const char *host = lp_parm_string(-1, "torture", "host"); + const char *share = lp_parm_string(-1, "torture", "share"); + struct cli_credentials *credentials = cmdline_credentials; + + status = smb2_connect(mem_ctx, host, share, credentials, tree, + event_context_find(mem_ctx)); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect to SMB2 share \\\\%s\\%s - %s\n", + host, share, nt_errstr(status)); + return False; + } + return True; +} + + +/* + create and return a handle to a test file +*/ +NTSTATUS torture_smb2_testfile(struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + struct smb2_create io; + struct smb2_read r; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.buffer_code = 0x39; + io.in.oplock_flags = 0; + io.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.in.fname = fname; + + status = smb2_create(tree, &io); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.handle; + + ZERO_STRUCT(r); + r.in.buffer_code = 0x31; + r.in.length = 5; + r.in.offset = 0; + r.in.handle = *handle; + + smb2_read(tree, tree, &r); + + return NT_STATUS_OK; +} + +/* + create and return a handle to a test directory +*/ +NTSTATUS torture_smb2_testdir(struct smb2_tree *tree, const char *fname, + struct smb2_handle *handle) +{ + struct smb2_create io; + NTSTATUS status; + + ZERO_STRUCT(io); + io.in.buffer_code = 0x39; + io.in.oplock_flags = 0; + io.in.access_mask = SEC_RIGHTS_DIR_ALL; + io.in.file_attr = FILE_ATTRIBUTE_DIRECTORY; + io.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.in.fname = fname; + + status = smb2_create(tree, &io); + NT_STATUS_NOT_OK_RETURN(status); + + *handle = io.out.handle; + + return NT_STATUS_OK; +} + + +/* + create a complex file using the old SMB protocol, to make it easier to + find fields in SMB2 getinfo levels +*/ +BOOL torture_setup_complex_file(const char *fname) +{ + struct smbcli_state *cli; + int fnum; + + if (!torture_open_connection(&cli)) { + return False; + } + + fnum = create_complex_file(cli, cli, fname); + + if (DEBUGLVL(1)) { + torture_all_info(cli->tree, fname); + } + + talloc_free(cli); + return fnum != -1; +} + +/* + create a complex directory using the old SMB protocol, to make it easier to + find fields in SMB2 getinfo levels +*/ +BOOL torture_setup_complex_dir(const char *dname) +{ + struct smbcli_state *cli; + int fnum; + + if (!torture_open_connection(&cli)) { + return False; + } + + fnum = create_complex_dir(cli, cli, dname); + + if (DEBUGLVL(1)) { + torture_all_info(cli->tree, dname); + } + + talloc_free(cli); + return fnum != -1; +} diff --git a/source4/torture/torture.c b/source4/torture/torture.c index ff9584ed5c..3c4c916b0f 100644 --- a/source4/torture/torture.c +++ b/source4/torture/torture.c @@ -2255,6 +2255,7 @@ static struct { {"SMB2-CONNECT", torture_smb2_connect, 0}, {"SMB2-SCAN", torture_smb2_scan, 0}, {"SMB2-SCANGETINFO", torture_smb2_getinfo_scan, 0}, + {"SMB2-GETINFO", torture_smb2_getinfo, 0}, /* protocol scanners */ {"SCAN-TRANS2", torture_trans2_scan, 0}, diff --git a/source4/torture/torture_util.c b/source4/torture/torture_util.c index 59eef81569..4606ec6840 100644 --- a/source4/torture/torture_util.c +++ b/source4/torture/torture_util.c @@ -100,20 +100,99 @@ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const cha smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); - /* setup some EAs */ - setfile.generic.level = RAW_SFILEINFO_EA_SET; + if (strchr(fname, ':') == NULL) { + /* setup some EAs */ + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } + } + + /* make sure all the timestamps aren't the same, and are also + in different DST zones*/ + setfile.generic.level = RAW_SFILEINFO_SETATTRE; setfile.generic.file.fnum = fnum; - setfile.ea_set.in.num_eas = 2; - setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); - setfile.ea_set.in.eas[0].flags = 0; - setfile.ea_set.in.eas[0].name.s = "EAONE"; - setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); - setfile.ea_set.in.eas[1].flags = 0; - setfile.ea_set.in.eas[1].name.s = "SECONDEA"; - setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + + setfile.setattre.in.create_time = t + 9*30*24*60*60; + setfile.setattre.in.access_time = t + 6*30*24*60*60; + setfile.setattre.in.write_time = t + 3*30*24*60*60; + status = smb_raw_setfileinfo(cli->tree, &setfile); if (!NT_STATUS_IS_OK(status)) { - printf("Failed to setup EAs\n"); + printf("Failed to setup file times - %s\n", nt_errstr(status)); + } + + /* make sure all the timestamps aren't the same */ + fileinfo.generic.level = RAW_FILEINFO_GETATTRE; + fileinfo.generic.in.fnum = fnum; + + status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to query file times - %s\n", nt_errstr(status)); + } + + if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) { + printf("create_time not setup correctly\n"); + } + if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) { + printf("access_time not setup correctly\n"); + } + if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) { + printf("write_time not setup correctly\n"); + } + + return fnum; +} + + +/* + sometimes we need a fairly complex directory to work with, so we can test + all possible attributes. +*/ +int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname) +{ + int fnum; + union smb_setfileinfo setfile; + union smb_fileinfo fileinfo; + time_t t = (time(NULL) & ~1); + NTSTATUS status; + + smbcli_deltree(cli->tree, dname); + fnum = smbcli_nt_create_full(cli->tree, dname, 0, + SEC_RIGHTS_DIR_ALL, + FILE_ATTRIBUTE_DIRECTORY, + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE, + NTCREATEX_DISP_OPEN_IF, + NTCREATEX_OPTIONS_DIRECTORY, 0); + if (fnum == -1) return -1; + + if (strchr(dname, ':') == NULL) { + /* setup some EAs */ + setfile.generic.level = RAW_SFILEINFO_EA_SET; + setfile.generic.file.fnum = fnum; + setfile.ea_set.in.num_eas = 2; + setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); + setfile.ea_set.in.eas[0].flags = 0; + setfile.ea_set.in.eas[0].name.s = "EAONE"; + setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); + setfile.ea_set.in.eas[1].flags = 0; + setfile.ea_set.in.eas[1].name.s = "SECONDEA"; + setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); + status = smb_raw_setfileinfo(cli->tree, &setfile); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to setup EAs\n"); + } } /* make sure all the timestamps aren't the same, and are also |