From 4ea44818f9cb446dc6a82c628b6e34c7cb093eec Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 29 Apr 2019 14:53:13 +0200 Subject: [PATCH 1/5] s4:torture/smb2: add smb2.stream.names3 test BUG: https://bugzilla.samba.org/show_bug.cgi?id=13919 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit b5c4fdbf99caa3a9e7c6446cfbc4f1b23b84b3c8) --- selftest/knownfail.d/smb2-streams | 1 + source4/torture/smb2/streams.c | 115 ++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 selftest/knownfail.d/smb2-streams diff --git a/selftest/knownfail.d/smb2-streams b/selftest/knownfail.d/smb2-streams new file mode 100644 index 00000000000..e2550e90f26 --- /dev/null +++ b/selftest/knownfail.d/smb2-streams @@ -0,0 +1 @@ +^samba3.smb2.streams.*names3 diff --git a/source4/torture/smb2/streams.c b/source4/torture/smb2/streams.c index b39d96d4924..c0516b7cc80 100644 --- a/source4/torture/smb2/streams.c +++ b/source4/torture/smb2/streams.c @@ -1214,6 +1214,120 @@ done: return ret; } +/* + test case insensitive stream names +*/ +static bool test_stream_names3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_fsinfo info; + const char *fname = DNAME "\\stream_names3.txt"; + const char *sname = NULL; + const char *snamel = NULL; + const char *snameu = NULL; + const char *sdname = NULL; + const char *sdnamel = NULL; + const char *sdnameu = NULL; + bool ret = true; + struct smb2_handle h = {{0}}; + struct smb2_handle hf = {{0}}; + struct smb2_handle hs = {{0}}; + struct smb2_handle hsl = {{0}}; + struct smb2_handle hsu = {{0}}; + struct smb2_handle hsd = {{0}}; + struct smb2_handle hsdl = {{0}}; + struct smb2_handle hsdu = {{0}}; + const char *streams[] = { "::$DATA", ":StreamName:$DATA", }; + + smb2_deltree(tree, DNAME); + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(info); + info.generic.level = RAW_QFS_ATTRIBUTE_INFORMATION; + info.generic.handle = h; + status = smb2_getinfo_fs(tree, tree, &info); + CHECK_STATUS(status, NT_STATUS_OK); + if (!(info.attribute_info.out.fs_attr & FILE_CASE_SENSITIVE_SEARCH)) { + torture_skip(tctx, "No FILE_CASE_SENSITIVE_SEARCH supported"); + } + + /* + * We create the following file: + * + * teststreams\\stream_names3.txt + * + * and add a stream named 'StreamName' + * + * Then we try to open the stream using the following names: + * + * teststreams\\stream_names3.txt:StreamName + * teststreams\\stream_names3.txt:streamname + * teststreams\\stream_names3.txt:STREAMNAME + * teststreams\\stream_names3.txt:StreamName:$dAtA + * teststreams\\stream_names3.txt:streamname:$data + * teststreams\\stream_names3.txt:STREAMNAME:$DATA + */ + sname = talloc_asprintf(tctx, "%s:StreamName", fname); + torture_assert_not_null(tctx, sname, __location__); + snamel = strlower_talloc(tctx, sname); + torture_assert_not_null(tctx, snamel, __location__); + snameu = strupper_talloc(tctx, sname); + torture_assert_not_null(tctx, snameu, __location__); + + sdname = talloc_asprintf(tctx, "%s:$dAtA", sname); + torture_assert_not_null(tctx, sdname, __location__); + sdnamel = strlower_talloc(tctx, sdname); + torture_assert_not_null(tctx, sdnamel, __location__); + sdnameu = strupper_talloc(tctx, sdname); + torture_assert_not_null(tctx, sdnameu, __location__); + + torture_comment(tctx, "(%s) testing case insensitive stream names\n", + __location__); + status = torture_smb2_testfile(tree, fname, &hf); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_testfile(tree, sname, &hs); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, hs); + + torture_assert(tctx, + check_stream_list(tree, tctx, fname, + ARRAY_SIZE(streams), + streams, + hf), + "streams"); + + status = torture_smb2_open(tree, sname, SEC_RIGHTS_FILE_ALL, &hs); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, snamel, SEC_RIGHTS_FILE_ALL, &hsl); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, snameu, SEC_RIGHTS_FILE_ALL, &hsu); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, sdname, SEC_RIGHTS_FILE_ALL, &hsd); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, sdnamel, SEC_RIGHTS_FILE_ALL, &hsdl); + CHECK_STATUS(status, NT_STATUS_OK); + status = torture_smb2_open(tree, sdnameu, SEC_RIGHTS_FILE_ALL, &hsdu); + CHECK_STATUS(status, NT_STATUS_OK); + +done: + smb2_util_close(tree, hsdu); + smb2_util_close(tree, hsdl); + smb2_util_close(tree, hsd); + smb2_util_close(tree, hsu); + smb2_util_close(tree, hsl); + smb2_util_close(tree, hs); + smb2_util_close(tree, hf); + smb2_util_close(tree, h); + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + #define CHECK_CALL_HANDLE(call, rightstatus) do { \ sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ sfinfo.generic.in.file.handle = h1; \ @@ -1924,6 +2038,7 @@ struct torture_suite *torture_smb2_streams_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "sharemodes", test_stream_sharemodes); torture_suite_add_1smb2_test(suite, "names", test_stream_names); torture_suite_add_1smb2_test(suite, "names2", test_stream_names2); + torture_suite_add_1smb2_test(suite, "names3", test_stream_names3); torture_suite_add_1smb2_test(suite, "rename", test_stream_rename); torture_suite_add_1smb2_test(suite, "rename2", test_stream_rename2); torture_suite_add_1smb2_test(suite, "create-disposition", test_stream_create_disposition); -- 2.21.0.593.g511ec345e18-goog From 69eeed5f4f041ee823b41b5981efa1b10be62b0d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 25 Apr 2019 17:30:43 +0200 Subject: [PATCH 2/5] smbd: allow case insensitive opens of named streams BUG: https://bugzilla.samba.org/show_bug.cgi?id=13919 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 2a69c0915586fb9fb2148239965d06bf9f93c803) --- selftest/knownfail.d/smb2-streams | 1 - source3/smbd/filename.c | 72 ++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) delete mode 100644 selftest/knownfail.d/smb2-streams diff --git a/selftest/knownfail.d/smb2-streams b/selftest/knownfail.d/smb2-streams deleted file mode 100644 index e2550e90f26..00000000000 --- a/selftest/knownfail.d/smb2-streams +++ /dev/null @@ -1 +0,0 @@ -^samba3.smb2.streams.*names3 diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index 568bef897a0..5e29e7c18a8 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -1374,6 +1374,76 @@ static bool fname_equal(const char *name1, const char *name2, return(strequal(name1,name2)); } +static bool sname_equal(const char *name1, const char *name2, + bool case_sensitive) +{ + bool match; + const char *s1 = NULL; + const char *s2 = NULL; + size_t n1; + size_t n2; + const char *e1 = NULL; + const char *e2 = NULL; + char *c1 = NULL; + char *c2 = NULL; + + match = fname_equal(name1, name2, case_sensitive); + if (match) { + return true; + } + + if (name1[0] != ':') { + return false; + } + if (name2[0] != ':') { + return false; + } + s1 = &name1[1]; + e1 = strchr(s1, ':'); + if (e1 == NULL) { + n1 = strlen(s1); + } else { + n1 = PTR_DIFF(e1, s1); + } + s2 = &name2[1]; + e2 = strchr(s2, ':'); + if (e2 == NULL) { + n2 = strlen(s2); + } else { + n2 = PTR_DIFF(e2, s2); + } + + /* Normal filename handling */ + if (case_sensitive) { + return (strncmp(s1, s2, n1) == 0); + } + + /* + * We can't use strnequal() here + * as it takes the number of codepoints + * and not the number of bytes. + * + * So we make a copy before calling + * strequal(). + * + * Note that we TALLOC_FREE() in reverse order + * in order to avoid memory fragmentation. + */ + + c1 = talloc_strndup(talloc_tos(), s1, n1); + c2 = talloc_strndup(talloc_tos(), s2, n2); + if (c1 == NULL || c2 == NULL) { + TALLOC_FREE(c2); + TALLOC_FREE(c1); + return (strncmp(s1, s2, n1) == 0); + } + + match = strequal(c1, c2); + TALLOC_FREE(c2); + TALLOC_FREE(c1); + return match; +} + /**************************************************************************** Scan a directory to find a filename, matching without case sensitivity. If the name looks like a mangled name then try via the mangling functions @@ -1570,7 +1640,7 @@ static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx, for (i=0; istream_name, streams[i].name)); - if (fname_equal(smb_fname->stream_name, streams[i].name, + if (sname_equal(smb_fname->stream_name, streams[i].name, conn->case_sensitive)) { DEBUGADD(10, ("equal\n")); break; -- 2.21.0.593.g511ec345e18-goog From 263a89ff49a144697bf63813b01e79fcdc0dcb78 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 25 Apr 2019 12:12:34 +0200 Subject: [PATCH 3/5] s4:libcli/raw: add RAW_FILEINFO_NORMALIZED_NAME_INFORMATION support This is supported over the wire in SMB 3.1.1 on starting with Windows 10 1803. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13919 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 0c602319194bda6b2a0efdd7c186078583f79264) --- source4/libcli/raw/interfaces.h | 16 ++++------------ source4/libcli/raw/rawfileinfo.c | 11 +++++++++++ source4/libcli/raw/trans2.h | 1 + source4/ntvfs/ntvfs_generic.c | 2 ++ source4/ntvfs/posix/pvfs_qfileinfo.c | 2 ++ source4/torture/gentest.c | 4 ++++ 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index 43a53f834df..e57d276d033 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -461,6 +461,7 @@ enum smb_fileinfo_level { RAW_FILEINFO_COMPRESSION_INFORMATION = SMB_QFILEINFO_COMPRESSION_INFORMATION, RAW_FILEINFO_NETWORK_OPEN_INFORMATION = SMB_QFILEINFO_NETWORK_OPEN_INFORMATION, RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION = SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION, + RAW_FILEINFO_NORMALIZED_NAME_INFORMATION= SMB_QFILEINFO_NORMALIZED_NAME_INFORMATION, /* SMB2 specific levels */ RAW_FILEINFO_SMB2_ALL_EAS = 0x0f01, RAW_FILEINFO_SMB2_ALL_INFORMATION = 0x1201 @@ -643,6 +644,8 @@ union smb_fileinfo { } ea_info; /* RAW_FILEINFO_NAME_INFO and RAW_FILEINFO_NAME_INFORMATION interfaces */ + /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */ + /* RAW_FILEINFO_NORMALIZED_NAME_INFORMATION interface */ struct { enum smb_fileinfo_level level; struct { @@ -651,7 +654,7 @@ union smb_fileinfo { struct { struct smb_wire_string fname; } out; - } name_info; + } name_info, alt_name_info, normalized_name_info; /* RAW_FILEINFO_ALL_INFO and RAW_FILEINFO_ALL_INFORMATION interfaces */ struct { @@ -704,17 +707,6 @@ union smb_fileinfo { } out; } all_info2; - /* RAW_FILEINFO_ALT_NAME_INFO and RAW_FILEINFO_ALT_NAME_INFORMATION interfaces */ - struct { - enum smb_fileinfo_level level; - struct { - union smb_handle_or_path file; - } in; - struct { - struct smb_wire_string fname; - } out; - } alt_name_info; - /* RAW_FILEINFO_STREAM_INFO and RAW_FILEINFO_STREAM_INFORMATION interfaces */ struct { enum smb_fileinfo_level level; diff --git a/source4/libcli/raw/rawfileinfo.c b/source4/libcli/raw/rawfileinfo.c index 50a6731ba41..a2599085ab7 100644 --- a/source4/libcli/raw/rawfileinfo.c +++ b/source4/libcli/raw/rawfileinfo.c @@ -219,6 +219,13 @@ NTSTATUS smb_raw_fileinfo_passthru_parse(const DATA_BLOB *blob, TALLOC_CTX *mem_ parms->attribute_tag_information.out.reparse_tag = IVAL(blob->data, 4); return NT_STATUS_OK; + case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION: + FINFO_CHECK_MIN_SIZE(4); + smbcli_blob_pull_string(NULL, mem_ctx, blob, + &parms->normalized_name_info.out.fname, + 0, 4, STR_UNICODE); + return NT_STATUS_OK; + case RAW_FILEINFO_SMB2_ALL_EAS: FINFO_CHECK_MIN_SIZE(4); return ea_pull_list_chained(blob, mem_ctx, @@ -443,6 +450,10 @@ static NTSTATUS smb_raw_info_backend(struct smbcli_session *session, return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, parms); + case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION: + return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, + RAW_FILEINFO_NORMALIZED_NAME_INFORMATION, parms); + case RAW_FILEINFO_SMB2_ALL_INFORMATION: return smb_raw_fileinfo_passthru_parse(blob, mem_ctx, RAW_FILEINFO_SMB2_ALL_INFORMATION, parms); diff --git a/source4/libcli/raw/trans2.h b/source4/libcli/raw/trans2.h index b7cfc6d0e8b..f93b1f1d35b 100644 --- a/source4/libcli/raw/trans2.h +++ b/source4/libcli/raw/trans2.h @@ -139,6 +139,7 @@ Found 8 aliased levels #define SMB_QFILEINFO_COMPRESSION_INFORMATION 1028 #define SMB_QFILEINFO_NETWORK_OPEN_INFORMATION 1034 #define SMB_QFILEINFO_ATTRIBUTE_TAG_INFORMATION 1035 +#define SMB_QFILEINFO_NORMALIZED_NAME_INFORMATION 1048 diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c index fe68b4132bc..69e046c9143 100644 --- a/source4/ntvfs/ntvfs_generic.c +++ b/source4/ntvfs/ntvfs_generic.c @@ -960,6 +960,8 @@ NTSTATUS ntvfs_map_fileinfo(TALLOC_CTX *mem_ctx, case RAW_FILEINFO_SMB2_ALL_EAS: case RAW_FILEINFO_SMB2_ALL_INFORMATION: return NT_STATUS_INVALID_LEVEL; + case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION: + return NT_STATUS_NOT_SUPPORTED; } return NT_STATUS_INVALID_LEVEL; diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c index 33ff9ce3cba..53cde69b1b7 100644 --- a/source4/ntvfs/posix/pvfs_qfileinfo.c +++ b/source4/ntvfs/posix/pvfs_qfileinfo.c @@ -342,6 +342,8 @@ static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs, case RAW_FILEINFO_UNIX_INFO2: case RAW_FILEINFO_UNIX_LINK: return NT_STATUS_INVALID_LEVEL; + case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION: + return NT_STATUS_NOT_SUPPORTED; } return NT_STATUS_INVALID_LEVEL; diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c index 33de6cc0423..24228ad65d4 100644 --- a/source4/torture/gentest.c +++ b/source4/torture/gentest.c @@ -1697,6 +1697,10 @@ static bool cmp_fileinfo(int instance, CHECK_EQUAL(attribute_tag_information.out.reparse_tag); break; + case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION: + CHECK_WSTR_EQUAL(normalized_name_info.out.fname); + break; + case RAW_FILEINFO_SMB2_ALL_INFORMATION: CHECK_NTTIMES_EQUAL(all_info2.out.create_time); CHECK_NTTIMES_EQUAL(all_info2.out.access_time); -- 2.21.0.593.g511ec345e18-goog From 84afc6071e1c2ca7eeabe68a5ded44165f689520 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 25 Apr 2019 14:57:02 +0200 Subject: [PATCH 4/5] s4:torture/smb2: add smb2.getinfo.normalized test BUG: https://bugzilla.samba.org/show_bug.cgi?id=13919 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 8a5828de2bdd95223e5f30996d0490fef53742dd) --- selftest/knownfail.d/smb2-getinfo | 1 + source4/torture/smb2/getinfo.c | 311 ++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+) create mode 100644 selftest/knownfail.d/smb2-getinfo diff --git a/selftest/knownfail.d/smb2-getinfo b/selftest/knownfail.d/smb2-getinfo new file mode 100644 index 00000000000..10ee423e55a --- /dev/null +++ b/selftest/knownfail.d/smb2-getinfo @@ -0,0 +1 @@ +^samba3.smb2.getinfo.normalized diff --git a/source4/torture/smb2/getinfo.c b/source4/torture/smb2/getinfo.c index f6ef0a55103..e0152f8afff 100644 --- a/source4/torture/smb2/getinfo.c +++ b/source4/torture/smb2/getinfo.c @@ -170,6 +170,315 @@ static bool torture_smb2_fileinfo_grant_read(struct torture_context *tctx) return true; } +static bool torture_smb2_fileinfo_normalized(struct torture_context *tctx) +{ + struct smb2_tree *tree = NULL; + bool ret; + struct smb2_handle hroot; + const char *d1 = NULL, *d1l = NULL, *d1u = NULL; + struct smb2_handle hd1, hd1l, hd1u; + const char *d2 = NULL, *d2l = NULL, *d2u = NULL; + struct smb2_handle hd2, hd2l, hd2u; + const char *d3 = NULL, *d3l = NULL, *d3u = NULL; + struct smb2_handle hd3, hd3l, hd3u; + const char *d3s = NULL, *d3sl = NULL, *d3su = NULL, *d3sd = NULL; + struct smb2_handle hd3s, hd3sl, hd3su, hd3sd; + const char *f4 = NULL, *f4l = NULL, *f4u = NULL, *f4d = NULL; + struct smb2_handle hf4, hf4l, hf4u, hf4d; + const char *f4s = NULL, *f4sl = NULL, *f4su = NULL, *f4sd = NULL; + struct smb2_handle hf4s, hf4sl, hf4su, hf4sd; + union smb_fileinfo info = { + .normalized_name_info = { + .level = RAW_FILEINFO_NORMALIZED_NAME_INFORMATION, + }, + }; + NTSTATUS status; + enum protocol_types protocol; + struct smb2_tree *tree_3_0 = NULL; + struct smbcli_options options3_0; + struct smb2_handle hroot_3_0; + + ret = torture_smb2_connection(tctx, &tree); + torture_assert(tctx, ret, "connection failed"); + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + + d1 = talloc_asprintf(tctx, "torture_dIr1N"); + torture_assert_not_null(tctx, d1, "d1"); + d1l = strlower_talloc(tctx, d1); + torture_assert_not_null(tctx, d1l, "d1l"); + d1u = strupper_talloc(tctx, d1); + torture_assert_not_null(tctx, d1u, "d1u"); + + d2 = talloc_asprintf(tctx, "%s\\dIr2Na", d1); + torture_assert_not_null(tctx, d2, "d2"); + d2l = strlower_talloc(tctx, d2); + torture_assert_not_null(tctx, d2l, "d2l"); + d2u = strupper_talloc(tctx, d2); + torture_assert_not_null(tctx, d2u, "d2u"); + + d3 = talloc_asprintf(tctx, "%s\\dIr3NaM", d2); + torture_assert_not_null(tctx, d3, "d3"); + d3l = strlower_talloc(tctx, d3); + torture_assert_not_null(tctx, d3l, "d3l"); + d3u = strupper_talloc(tctx, d3); + torture_assert_not_null(tctx, d3u, "d3u"); + + d3s = talloc_asprintf(tctx, "%s:sTrEaM3", d3); + torture_assert_not_null(tctx, d3s, "d3s"); + d3sl = strlower_talloc(tctx, d3s); + torture_assert_not_null(tctx, d3sl, "d3sl"); + d3su = strupper_talloc(tctx, d3s); + torture_assert_not_null(tctx, d3su, "d3su"); + d3sd = talloc_asprintf(tctx, "%s:$DaTa", d3s); + torture_assert_not_null(tctx, d3sd, "d3sd"); + + f4 = talloc_asprintf(tctx, "%s\\fIlE4NaMe", d3); + torture_assert_not_null(tctx, f4, "f4"); + f4l = strlower_talloc(tctx, f4); + torture_assert_not_null(tctx, f4l, "f4l"); + f4u = strupper_talloc(tctx, f4); + torture_assert_not_null(tctx, f4u, "f4u"); + f4d = talloc_asprintf(tctx, "%s::$dAtA", f4); + torture_assert_not_null(tctx, f4d, "f4d"); + + f4s = talloc_asprintf(tctx, "%s:StReAm4", f4); + torture_assert_not_null(tctx, f4s, "f4s"); + f4sl = strlower_talloc(tctx, f4s); + torture_assert_not_null(tctx, f4sl, "f4sl"); + f4su = strupper_talloc(tctx, f4s); + torture_assert_not_null(tctx, f4su, "f4su"); + f4sd = talloc_asprintf(tctx, "%s:$dAtA", f4s); + torture_assert_not_null(tctx, f4sd, "f4sd"); + + status = smb2_util_roothandle(tree, &hroot); + torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle"); + + info.normalized_name_info.in.file.handle = hroot; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + if (protocol < PROTOCOL_SMB3_11) { + /* + * Only SMB 3.1.1 and above should offer this. + */ + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_NOT_SUPPORTED, + "getinfo hroot"); + torture_skip(tctx, "SMB 3.1.1 not supported"); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + /* + * Not all servers support this. + * (only Windows 10 1803 and higher) + */ + torture_skip(tctx, "NORMALIZED_NAME_INFORMATION not supported"); + } + torture_assert_ntstatus_ok(tctx, status, "getinfo hroot"); + torture_assert(tctx, info.normalized_name_info.out.fname.s == NULL, + "getinfo hroot should be empty"); + + smb2_deltree(tree, d1); + + status = torture_smb2_testdir(tree, d1, &hd1); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd1"); + status = torture_smb2_open(tree, d1l, SEC_RIGHTS_FILE_ALL, &hd1l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd1l"); + status = torture_smb2_open(tree, d1u, SEC_RIGHTS_FILE_ALL, &hd1u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd1u"); + + status = torture_smb2_testdir(tree, d2, &hd2); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd2"); + status = torture_smb2_open(tree, d2l, SEC_RIGHTS_FILE_ALL, &hd2l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd2l"); + status = torture_smb2_open(tree, d2u, SEC_RIGHTS_FILE_ALL, &hd2u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd2u"); + + status = torture_smb2_testdir(tree, d3, &hd3); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd3"); + status = torture_smb2_open(tree, d3l, SEC_RIGHTS_FILE_ALL, &hd3l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3l"); + status = torture_smb2_open(tree, d3u, SEC_RIGHTS_FILE_ALL, &hd3u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3u"); + + status = torture_smb2_testfile(tree, d3s, &hd3s); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hd3s"); + status = torture_smb2_open(tree, d3sl, SEC_RIGHTS_FILE_ALL, &hd3sl); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3sl"); + status = torture_smb2_open(tree, d3su, SEC_RIGHTS_FILE_ALL, &hd3su); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3su"); + status = torture_smb2_open(tree, d3sd, SEC_RIGHTS_FILE_ALL, &hd3sd); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hd3sd"); + + status = torture_smb2_testfile(tree, f4, &hf4); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hf4"); + status = torture_smb2_open(tree, f4l, SEC_RIGHTS_FILE_ALL, &hf4l); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4l"); + status = torture_smb2_open(tree, f4u, SEC_RIGHTS_FILE_ALL, &hf4u); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4u"); + status = torture_smb2_open(tree, f4d, SEC_RIGHTS_FILE_ALL, &hf4d); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4d"); + + status = torture_smb2_testfile(tree, f4s, &hf4s); + torture_assert_ntstatus_ok(tctx, status, "Unable to create hf4s"); + status = torture_smb2_open(tree, f4sl, SEC_RIGHTS_FILE_ALL, &hf4sl); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4sl"); + status = torture_smb2_open(tree, f4su, SEC_RIGHTS_FILE_ALL, &hf4su); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4su"); + status = torture_smb2_open(tree, f4sd, SEC_RIGHTS_FILE_ALL, &hf4sd); + torture_assert_ntstatus_ok(tctx, status, "Unable to open hf4sd"); + + info.normalized_name_info.in.file.handle = hd1; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd1"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d1, "getinfo hd1"); + info.normalized_name_info.in.file.handle = hd1l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd1l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d1, "getinfo hd1l"); + info.normalized_name_info.in.file.handle = hd1u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd1u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d1, "getinfo hd1u"); + + info.normalized_name_info.in.file.handle = hd2; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd2"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d2, "getinfo hd2"); + info.normalized_name_info.in.file.handle = hd2l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd2l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d2, "getinfo hd2l"); + info.normalized_name_info.in.file.handle = hd2u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd2u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d2, "getinfo hd2u"); + + info.normalized_name_info.in.file.handle = hd3; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3, "getinfo hd3"); + info.normalized_name_info.in.file.handle = hd3l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3, "getinfo hd3l"); + info.normalized_name_info.in.file.handle = hd3u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3, "getinfo hd3u"); + + info.normalized_name_info.in.file.handle = hd3s; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3s"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3s"); + info.normalized_name_info.in.file.handle = hd3sl; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3sl"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3sl"); + info.normalized_name_info.in.file.handle = hd3su; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3su"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3su"); + info.normalized_name_info.in.file.handle = hd3sd; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hd3sd"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + d3s, "getinfo hd3sd"); + + info.normalized_name_info.in.file.handle = hf4; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4"); + info.normalized_name_info.in.file.handle = hf4l; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4l"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4l"); + info.normalized_name_info.in.file.handle = hf4u; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4u"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4u"); + info.normalized_name_info.in.file.handle = hf4d; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4d"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4, "getinfo hf4d"); + + info.normalized_name_info.in.file.handle = hf4s; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4s"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4s"); + info.normalized_name_info.in.file.handle = hf4sl; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4sl"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4sl"); + info.normalized_name_info.in.file.handle = hf4su; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4su"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4su"); + info.normalized_name_info.in.file.handle = hf4sd; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree, tree, &info); + torture_assert_ntstatus_ok(tctx, status, "getinfo hf4sd"); + torture_assert_str_equal(tctx, info.normalized_name_info.out.fname.s, + f4s, "getinfo hf4sd"); + + /* Set max protocol to SMB 3.0.2 */ + options3_0 = tree->session->transport->options; + options3_0.max_protocol = PROTOCOL_SMB3_02; + options3_0.client_guid = GUID_zero(); + ret = torture_smb2_connection_ext(tctx, 0, &options3_0, &tree_3_0); + torture_assert(tctx, ret, "connection with SMB < 3.1.1 failed"); + + status = smb2_util_roothandle(tree_3_0, &hroot_3_0); + torture_assert_ntstatus_ok(tctx, status, "Unable to create root handle 3_0"); + + info.normalized_name_info.in.file.handle = hroot_3_0; + ZERO_STRUCT(info.normalized_name_info.out); + status = smb2_getinfo_file(tree_3_0, tree_3_0, &info); + torture_assert_ntstatus_equal(tctx, status, + NT_STATUS_NOT_SUPPORTED, + "getinfo hroot"); + + return true; +} + /* test fsinfo levels */ @@ -489,5 +798,7 @@ struct torture_suite *torture_smb2_getinfo_init(TALLOC_CTX *ctx) torture_smb2_qsec_buffercheck); torture_suite_add_simple_test(suite, "granted", torture_smb2_fileinfo_grant_read); + torture_suite_add_simple_test(suite, "normalized", + torture_smb2_fileinfo_normalized); return suite; } -- 2.21.0.593.g511ec345e18-goog From 3765ccfce0b89db657807394d1774cee09e5f598 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 25 Apr 2019 14:57:33 +0200 Subject: [PATCH 5/5] smbd: implement SMB_FILE_NORMALIZED_NAME_INFORMATION handling Windows 10 (1803 and higher) support and use SMB_FILE_NORMALIZED_NAME_INFORMATION calls over the network. As a fallback (in case the server don't support it) the client traverses all path components, which is very expensive. Implementing SMB_FILE_NORMALIZED_NAME_INFORMATION is very cheap for us as the open already went through unix_convert() and we have the information the client is asking for. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13919 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed May 1 18:33:00 UTC 2019 on sn-devel-184 (cherry picked from commit b20fd15e04ce9292f90a7f70f4184e43034b4b9d) --- selftest/knownfail.d/smb2-getinfo | 1 - source3/smbd/smb2_getinfo.c | 9 +++++ source3/smbd/trans2.c | 57 +++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) delete mode 100644 selftest/knownfail.d/smb2-getinfo diff --git a/selftest/knownfail.d/smb2-getinfo b/selftest/knownfail.d/smb2-getinfo deleted file mode 100644 index 10ee423e55a..00000000000 --- a/selftest/knownfail.d/smb2-getinfo +++ /dev/null @@ -1 +0,0 @@ -^samba3.smb2.getinfo.normalized diff --git a/source3/smbd/smb2_getinfo.c b/source3/smbd/smb2_getinfo.c index 29625fc1ba7..3d4e0c00d6c 100644 --- a/source3/smbd/smb2_getinfo.c +++ b/source3/smbd/smb2_getinfo.c @@ -318,6 +318,15 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, break; } + switch (file_info_level) { + case SMB_FILE_NORMALIZED_NAME_INFORMATION: + if (smb2req->xconn->protocol < PROTOCOL_SMB3_11) { + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + break; + } + if (fsp->fake_file_handle) { /* * This is actually for the QUOTA_FAKE_FILE --metze diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index e68bf6dd69b..5fbc6db3925 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -5214,6 +5214,63 @@ NTSTATUS smbd_do_qfilepathinfo(connection_struct *conn, break; } + case SMB_FILE_NORMALIZED_NAME_INFORMATION: + { + char *nfname = NULL; + + if (!fsp->conn->sconn->using_smb2) { + return NT_STATUS_INVALID_LEVEL; + } + + nfname = talloc_strdup(mem_ctx, smb_fname->base_name); + if (nfname == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (ISDOT(nfname)) { + nfname[0] = '\0'; + } + string_replace(nfname, '/', '\\'); + + if (smb_fname->stream_name != NULL) { + const char *s = smb_fname->stream_name; + const char *e = NULL; + size_t n; + + SMB_ASSERT(s[0] != '\0'); + + /* + * smb_fname->stream_name is in form + * of ':StrEam:$DATA', but we should only + * append ':StrEam' here. + */ + + e = strchr(&s[1], ':'); + if (e == NULL) { + n = strlen(s); + } else { + n = PTR_DIFF(e, s); + } + nfname = talloc_strndup_append(nfname, s, n); + if (nfname == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + status = srvstr_push(dstart, flags2, + pdata+4, nfname, + PTR_DIFF(dend, pdata+4), + STR_UNICODE, &len); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_NORMALIZED_NAME_INFORMATION\n")); + data_size = 4 + len; + SIVAL(pdata,0,len); + *fixed_portion = 8; + break; + } + case SMB_FILE_ALLOCATION_INFORMATION: case SMB_QUERY_FILE_ALLOCATION_INFO: DEBUG(10,("smbd_do_qfilepathinfo: SMB_FILE_ALLOCATION_INFORMATION\n")); -- 2.21.0.593.g511ec345e18-goog