summaryrefslogtreecommitdiffstats
path: root/source3/torture/test_smb1_dfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/torture/test_smb1_dfs.c')
-rw-r--r--source3/torture/test_smb1_dfs.c4284
1 files changed, 4284 insertions, 0 deletions
diff --git a/source3/torture/test_smb1_dfs.c b/source3/torture/test_smb1_dfs.c
new file mode 100644
index 0000000..4cd75c9
--- /dev/null
+++ b/source3/torture/test_smb1_dfs.c
@@ -0,0 +1,4284 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB1 DFS tests.
+ Copyright (C) Jeremy Allison 2022.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "torture/proto.h"
+#include "client.h"
+#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libcli/security/security.h"
+#include "libsmb/proto.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+#include "auth_generic.h"
+#include "../librpc/ndr/libndr.h"
+#include "libsmb/clirap.h"
+#include "async_smb.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "lib/util/time_basic.h"
+
+extern fstring host, workgroup, share, password, username, myname;
+extern struct cli_credentials *torture_creds;
+
+/*
+ * Open an SMB1 file readonly and return the create time.
+ */
+static NTSTATUS get_smb1_crtime(struct cli_state *cli,
+ const char *pathname,
+ struct timespec *pcrtime)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0;
+ struct timespec crtime = {0};
+
+ /*
+ * Open the file.
+ */
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ pathname,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Get the create time. Note - we can use
+ * a higher-level cli_XXX function here
+ * for SMB1 as cli_qfileinfo_basic()
+ * doesn't use any pathnames, only fnums
+ * so it isn't affected by DFS pathnames.
+ */
+ status = cli_qfileinfo_basic(cli,
+ fnum,
+ NULL, /* attr */
+ NULL, /* size */
+ &crtime, /* create_time */
+ NULL, /* access_time */
+ NULL, /* write_time */
+ NULL, /* change_time */
+ NULL);
+ if (NT_STATUS_IS_OK(status)) {
+ *pcrtime = crtime;
+ }
+
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return status;
+}
+
+/*
+ * Check a crtime matches a given SMB1 path.
+ */
+static bool smb1_crtime_matches(struct cli_state *cli,
+ const char *match_pathname,
+ struct timespec crtime_tomatch,
+ const char *test_pathname)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+ bool equal = false;
+
+ status = get_smb1_crtime(cli,
+ test_pathname,
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s: Failed to get crtime "
+ "for %s, (%s)\n",
+ __func__,
+ test_pathname,
+ nt_errstr(status));
+ return false;
+ }
+ equal = (timespec_compare(&test_crtime, &crtime_tomatch) == 0);
+ if (!equal) {
+ struct timeval_buf test_buf;
+ struct timeval_buf tomatch_buf;
+ printf("%s: crtime mismatch "
+ "%s:crtime_tomatch=%s, %s:test_crtime = %s\n",
+ __func__,
+ match_pathname,
+ timespec_string_buf(&crtime_tomatch,
+ true,
+ &tomatch_buf),
+ test_pathname,
+ timespec_string_buf(&test_crtime,
+ true,
+ &test_buf));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Delete an SMB1 file on a DFS share.
+ */
+static NTSTATUS smb1_dfs_delete(struct cli_state *cli,
+ const char *pathname)
+{
+ NTSTATUS status;
+ uint16_t fnum = 0;
+
+ /*
+ * Open the file.
+ */
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ pathname,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ * Set delete on close. Note - we can use
+ * a higher-level cli_XXX function here
+ * for SMB1 as cli_nt_delete_on_close()
+ * doesn't use any pathnames, only fnums
+ * so it isn't affected by DFS pathnames.
+ */
+ /*
+ */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+}
+
+static void smb1_mv_done(struct tevent_req *subreq);
+
+struct smb1_mv_state {
+ uint16_t vwv[1];
+};
+
+static struct tevent_req *smb1_mv_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ uint8_t *bytes = NULL;
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_mv_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_mv_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(state->vwv,
+ 0,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ src_dfs_name,
+ strlen(src_dfs_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes = talloc_realloc(state,
+ bytes,
+ uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ target_name,
+ strlen(target_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state,
+ ev,
+ cli,
+ SMBmv,
+ 0, /* additional_flags */
+ 0, /* additional_flags2 */
+ 1,
+ state->vwv,
+ talloc_get_size(bytes),
+ bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_mv_done, req);
+ return req;
+}
+
+static void smb1_mv_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(subreq,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq,
+ status);
+}
+
+static NTSTATUS smb1_mv_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename an SMB1 file on a DFS share. SMBmv version.
+ */
+static NTSTATUS smb1_mv(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_mv_send(frame,
+ ev,
+ cli,
+ src_dfs_name,
+ target_name);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_mv_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool test_smb1_mv(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_timespec = { 0 };
+ NTSTATUS status;
+
+ status = smb1_mv(cli,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMBmv of %s -> %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_timespec);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Put it back. */
+ status = smb1_mv(cli,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMBmv of %s -> %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did put it back. */
+ status = get_smb1_crtime(cli,
+ src_dfs_name,
+ &test_timespec);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ status = smb1_mv(cli,
+ src_dfs_name,
+ "renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMBmv of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ status = smb1_mv(cli,
+ src_dfs_name,
+ "BAD\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMBmv of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static void smb1_setpathinfo_done(struct tevent_req *subreq);
+
+struct smb1_setpathinfo_state {
+ uint16_t setup;
+ uint8_t *param;
+ uint8_t *data;
+};
+
+static struct tevent_req *smb1_setpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t info_level)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_setpathinfo_state *state = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ bool ok = false;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_setpathinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETPATHINFO);
+
+ state->param = talloc_zero_array(state, uint8_t, 6);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+ PUSH_LE_U16(state->param, 0, info_level);
+
+ state->param = trans2_bytes_push_str(state->param,
+ smbXcli_conn_use_unicode(cli->conn),
+ src_dfs_name,
+ strlen(src_dfs_name)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ ok = push_ucs2_talloc(state,
+ &converted_str,
+ target_name,
+ &converted_size_bytes);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * W2K8 insists the dest name is not null
+ * terminated. Remove the last 2 zero bytes
+ * and reduce the name length.
+ */
+
+ if (converted_size_bytes < 2) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ converted_size_bytes -= 2;
+
+ state->data = talloc_zero_array(state,
+ uint8_t,
+ 12 + converted_size_bytes);
+ if (tevent_req_nomem(state->data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SIVAL(state->data, 8, converted_size_bytes);
+ memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev,/* event ctx. */
+ cli,/* cli_state. */
+ 0,/* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL,/* pipe name. */
+ -1,/* fid. */
+ 0,/* function. */
+ 0,/* flags. */
+ &state->setup,/* setup. */
+ 1,/* num setup uint16_t words. */
+ 0,/* max returned setup. */
+ state->param,/* param. */
+ talloc_get_size(state->param),/* num param. */
+ 2,/* max returned param. */
+ state->data,/* data. */
+ talloc_get_size(state->data),/* num data. */
+ 0);/* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_setpathinfo_done, req);
+ return req;
+}
+
+static void smb1_setpathinfo_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(subreq,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq,
+ status);
+}
+
+static NTSTATUS smb1_setpathinfo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename or hardlink an SMB1 file on a DFS share. SMB1 setpathinfo
+ * (pathnames only) version.
+ */
+static NTSTATUS smb1_setpathinfo(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t info_level)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_setpathinfo_send(frame,
+ ev,
+ cli,
+ src_dfs_name,
+ target_name,
+ info_level);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_setpathinfo_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS smb1_setpathinfo_rename(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_setpathinfo(cli,
+ src_dfs_name,
+ target_name,
+ SMB_FILE_RENAME_INFORMATION);
+}
+
+static bool test_smb1_setpathinfo_rename(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+ const char *putback_path = NULL;
+
+ /*
+ * On Windows, setpathinfo rename where the target contains
+ * any directory separator returns STATUS_NOT_SUPPORTED.
+ *
+ * MS-SMB behavior note: <133> Section 3.3.5.10.6:
+ *
+ * "If the file name pointed to by the FileName parameter of the
+ * FILE_RENAME_INFORMATION structure contains a separator character,
+ * then the request fails with STATUS_NOT_SUPPORTED."
+ */
+ status = smb1_setpathinfo_rename(cli,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("%s:%d SMB1 setpathinfo rename of %s -> %s should get "
+ "NT_STATUS_NOT_SUPPORTED got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ status = smb1_setpathinfo_rename(cli,
+ src_dfs_name,
+ "renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * To put it back we need to reverse the DFS-ness of src
+ * and destination paths.
+ */
+ putback_path = strrchr(src_dfs_name, '\\');
+ if (putback_path == NULL) {
+ printf("%s:%d non DFS path %s passed. Internal error\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name);
+ return false;
+ }
+ /* Walk past the last '\\' */
+ putback_path++;
+
+ /* Put it back. */
+ status = smb1_setpathinfo_rename(cli,
+ "BAD\\BAD\\renamed_file",
+ putback_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ putback_path,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ src_dfs_name,
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS smb1_setpathinfo_hardlink(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_setpathinfo(cli,
+ src_dfs_name,
+ target_name,
+ SMB_FILE_LINK_INFORMATION);
+}
+
+static bool test_smb1_setpathinfo_hardlink(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ NTSTATUS status;
+
+ /*
+ * On Windows, setpathinfo rename where the target contains
+ * any directory separator returns STATUS_NOT_SUPPORTED.
+ *
+ * MS-SMB behavior note: <133> Section 3.3.5.10.6:
+ *
+ * "If the file name pointed to by the FileName parameter of the
+ * FILE_RENAME_INFORMATION structure contains a separator character,
+ * then the request fails with STATUS_NOT_SUPPORTED."
+ *
+ * setpathinfo info level SMB_FILE_LINK_INFORMATION
+ * seems to do the same, but this could be an artifact
+ * of the Windows version tested (Win2K8). I will
+ * revisit this when I'm able to test against
+ * a later Windows version with a DFS server.
+ */
+ status = smb1_setpathinfo_hardlink(cli,
+ src_dfs_name,
+ "BAD\\BAD\\hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
+ "NT_STATUS_NOT_SUPPORTED got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Try with a non-DFS name. */
+ /*
+ * At least on Windows 2008 this also fails with
+ * NT_STATUS_NOT_SUPPORTED, leading me to believe
+ * setting hardlinks is only supported via NTrename
+ * in SMB1.
+ */
+ status = smb1_setpathinfo_hardlink(cli,
+ src_dfs_name,
+ "hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
+ "NT_STATUS_NOT_SUPPORTED got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static void smb1_ntrename_done(struct tevent_req *subreq);
+
+struct smb1_ntrename_state {
+ uint16_t vwv[4];
+};
+
+static struct tevent_req *smb1_ntrename_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t rename_flag)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_ntrename_state *state = NULL;
+ uint8_t *bytes = NULL;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_ntrename_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(state->vwv,
+ 0,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY);
+ PUSH_LE_U16(state->vwv, 2, rename_flag);
+
+ bytes = talloc_array(state, uint8_t, 1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ src_dfs_name,
+ strlen(src_dfs_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = talloc_realloc(state,
+ bytes,
+ uint8_t,
+ talloc_get_size(bytes)+1);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ bytes[talloc_get_size(bytes)-1] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ target_name,
+ strlen(target_name)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = cli_smb_send(state,
+ ev,
+ cli,
+ SMBntrename,
+ 0, /* additional_flags */
+ 0, /* additional_flags2 */
+ 4,
+ state->vwv,
+ talloc_get_size(bytes),
+ bytes);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_ntrename_done, req);
+ return req;
+}
+
+static void smb1_ntrename_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb_recv(subreq,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS smb1_ntrename_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename or hardlink an SMB1 file on a DFS share. SMB1 ntrename version.
+ * (pathnames only).
+ */
+static NTSTATUS smb1_ntrename(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name,
+ uint16_t rename_flag)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_ntrename_send(frame,
+ ev,
+ cli,
+ src_dfs_name,
+ target_name,
+ rename_flag);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_ntrename_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+/*
+ * Rename an SMB1 file on a DFS share. SMB1 ntrename version.
+ */
+static NTSTATUS smb1_ntrename_rename(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_ntrename(cli,
+ src_dfs_name,
+ target_name,
+ RENAME_FLAG_RENAME);
+}
+
+
+static bool test_smb1_ntrename_rename(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+
+ /* Try with a non-DFS name. */
+ status = smb1_ntrename_rename(cli,
+ src_dfs_name,
+ "renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMB1 ntrename rename of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb1_ntrename_rename(cli,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 ntrename rename of %s -> %s should "
+ "succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\renamed_file",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Put it back. */
+ status = smb1_ntrename_rename(cli,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 ntrename rename of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\renamed_file",
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Ensure we did rename. */
+ status = get_smb1_crtime(cli,
+ src_dfs_name,
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Hard link an SMB1 file on a DFS share. SMB1 ntrename version.
+ */
+static NTSTATUS smb1_ntrename_hardlink(struct cli_state *cli,
+ const char *src_dfs_name,
+ const char *target_name)
+{
+ return smb1_ntrename(cli,
+ src_dfs_name,
+ target_name,
+ RENAME_FLAG_HARD_LINK);
+}
+
+static bool test_smb1_ntrename_hardlink(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Try with a non-DFS name. */
+ status = smb1_ntrename_hardlink(cli,
+ src_dfs_name,
+ "hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* Fails I think as target becomes "" on server. */
+ printf("%s:%d SMB1 ntrename of %s -> %s should get "
+ "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = smb1_ntrename_hardlink(cli,
+ src_dfs_name,
+ "BAD\\BAD\\hlink");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1 ntrename hardlink of %s -> %s "
+ "should succeed got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Ensure we did hardlink. */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\hlink",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime "
+ "for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ retval = smb1_crtime_matches(cli,
+ "BAD\\BAD\\hlink",
+ test_crtime,
+ src_dfs_name);
+ if (!retval) {
+ printf("%s:%d smb1_crtime_matches failed for "
+ "%s %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink");
+ goto out;
+ }
+
+ out:
+
+ /* Remove the hardlink to clean up. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+static void smb1_setfileinfo_done(struct tevent_req *subreq);
+
+struct smb1_setfileinfo_state {
+ uint16_t setup;
+ uint8_t param[6];
+ uint8_t *data;
+};
+
+static struct tevent_req *smb1_setfileinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name,
+ uint16_t info_level)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct smb1_setfileinfo_state *state = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ bool ok = false;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct smb1_setfileinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETPATHINFO);
+
+ PUSH_LE_U16(state->param, 0, fnum);
+ PUSH_LE_U16(state->param, 2, info_level);
+
+ ok = push_ucs2_talloc(state,
+ &converted_str,
+ target_name,
+ &converted_size_bytes);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * W2K8 insists the dest name is not null
+ * terminated. Remove the last 2 zero bytes
+ * and reduce the name length.
+ */
+
+ if (converted_size_bytes < 2) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ converted_size_bytes -= 2;
+
+ state->data = talloc_zero_array(state,
+ uint8_t,
+ 12 + converted_size_bytes);
+ if (tevent_req_nomem(state->data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ SIVAL(state->data, 8, converted_size_bytes);
+ memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+ subreq = cli_trans_send(state, /* mem ctx. */
+ ev,/* event ctx. */
+ cli,/* cli_state. */
+ 0,/* additional_flags2 */
+ SMBtrans2, /* cmd. */
+ NULL,/* pipe name. */
+ -1,/* fid. */
+ 0,/* function. */
+ 0,/* flags. */
+ &state->setup,/* setup. */
+ 1,/* num setup uint16_t words. */
+ 0,/* max returned setup. */
+ state->param,/* param. */
+ 6,/* num param. */
+ 2,/* max returned param. */
+ state->data,/* data. */
+ talloc_get_size(state->data),/* num data. */
+ 0);/* max returned data. */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_setfileinfo_done, req);
+ return req;
+}
+
+static void smb1_setfileinfo_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_trans_recv(subreq,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ NULL,
+ 0,
+ NULL);
+ tevent_req_simple_finish_ntstatus(subreq,
+ status);
+}
+
+static NTSTATUS smb1_setfileinfo_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/*
+ * Rename or hardlink an SMB1 file on a DFS share.
+ * setfileinfo (file handle + target pathname) version.
+ */
+static NTSTATUS smb1_setfileinfo(struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name,
+ uint16_t info_level)
+{
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status;
+
+ frame = talloc_stackframe();
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ req = smb1_setfileinfo_send(frame,
+ ev,
+ cli,
+ fnum,
+ target_name,
+ info_level);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_setfileinfo_recv(req);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS smb1_setfileinfo_rename(struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name)
+{
+ return smb1_setfileinfo(cli,
+ fnum,
+ target_name,
+ SMB_FILE_RENAME_INFORMATION);
+}
+
+/*
+ * On Windows, rename using a file handle as source
+ * is not supported.
+ */
+
+static bool test_smb1_setfileinfo_rename(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ bool retval = false;
+
+ /* First open the source file. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ src_dfs_name,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to open %s, %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * On Windows rename given a file handle returns
+ * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
+ */
+
+ status = smb1_setfileinfo_rename(cli,
+ fnum,
+ "BAD\\BAD\\renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
+ status = smb1_setfileinfo_rename(cli,
+ fnum,
+ "renamed_file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ retval = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ return retval;
+}
+
+
+static NTSTATUS smb1_setfileinfo_hardlink(struct cli_state *cli,
+ uint16_t fnum,
+ const char *target_name)
+{
+ return smb1_setfileinfo(cli,
+ fnum,
+ target_name,
+ SMB_FILE_LINK_INFORMATION);
+}
+
+/*
+ * On Windows, hardlink using a file handle as source
+ * is not supported.
+ */
+
+static bool test_smb1_setfileinfo_hardlink(struct cli_state *cli,
+ const char *src_dfs_name)
+{
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ bool retval = false;
+
+ /* First open the source file. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ src_dfs_name,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_RIGHTS_FILE_READ, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to open %s, %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ nt_errstr(status));
+ goto out;
+ }
+
+ /*
+ * On Windows hardlink given a file handle returns
+ * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
+ */
+
+ status = smb1_setfileinfo_hardlink(cli,
+ fnum,
+ "BAD\\BAD\\hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "BAD\\BAD\\hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
+ status = smb1_setfileinfo_hardlink(cli,
+ fnum,
+ "hlink");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
+ printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
+ "NT_STATUS_UNSUCCESSFUL got %s\n",
+ __FILE__,
+ __LINE__,
+ src_dfs_name,
+ "hlink",
+ nt_errstr(status));
+ goto out;
+ }
+
+ retval = true;
+
+ out:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+/*
+ * According to:
+
+ * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/dc9978d7-6299-4c5a-a22d-a039cdc716ea
+ *
+ * (Characters " \ / [ ] : | < > + = ; , * ?,
+ * and control characters in range 0x00 through
+ * 0x1F, inclusive, are illegal in a share name)
+ *
+ * But Windows server only checks in DFS sharenames ':'. All other
+ * share names are allowed.
+ */
+
+static bool test_smb1_dfs_sharenames(struct cli_state *cli,
+ const char *dfs_root_share_name,
+ struct timespec root_crtime)
+{
+ char test_path[20];
+ const char *test_str = "/[]:|<>+=;,*?";
+ const char *p;
+ unsigned int i;
+ bool crtime_matched = false;
+
+ /* Setup template pathname. */
+ memcpy(test_path, "\\SERVER\\X", 10);
+
+ /* Test invalid control characters. */
+ for (i = 1; i < 0x20; i++) {
+ test_path[8] = i;
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ test_path);
+ if (!crtime_matched) {
+ return false;
+ }
+ }
+
+ /* Test explicit invalid characters. */
+ for (p = test_str; *p != '\0'; p++) {
+ test_path[8] = *p;
+ if (*p == ':') {
+ /*
+ * Only ':' is treated as an INVALID sharename
+ * for a DFS SERVER\\SHARE path.
+ */
+ struct timespec test_crtime = { 0 };
+ NTSTATUS status = get_smb1_crtime(cli,
+ test_path,
+ &test_crtime);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_INVALID)) {
+ printf("%s:%d Open of %s should get "
+ "NT_STATUS_OBJECT_NAME_INVALID, got %s\n",
+ __FILE__,
+ __LINE__,
+ test_path,
+ nt_errstr(status));
+ return false;
+ }
+ } else {
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ test_path);
+ if (!crtime_matched) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*
+ * "Raw" test of SMB1 paths to a DFS share.
+ * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb1_dfs_paths(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ char *dfs_root_share_name = NULL;
+ struct timespec root_crtime = { 0 };
+ struct timespec test_crtime = { 0 };
+ bool crtime_matched = false;
+ bool retval = false;
+ bool ok = false;
+ bool equal = false;
+ unsigned int i;
+ uint16_t fnum = (uint16_t)-1;
+
+ printf("Starting SMB1-DFS-PATHS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ /* Start with an empty share. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\BAD");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+
+ /*
+ * Create the "official" DFS share root name.
+ */
+ dfs_root_share_name = talloc_asprintf(talloc_tos(),
+ "\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ if (dfs_root_share_name == NULL) {
+ printf("Out of memory\n");
+ return false;
+ }
+
+ /* Get the share root crtime. */
+ status = get_smb1_crtime(cli,
+ dfs_root_share_name,
+ &root_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime for share root %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ dfs_root_share_name,
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Test the Windows algorithm for parsing DFS names.
+ */
+ /*
+ * A single "SERVER" element should open and match the share root.
+ */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ smbXcli_conn_remote_name(cli->conn));
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+
+ /* An "" (empty) server name should open and match the share root. */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "");
+ return false;
+ }
+
+ /*
+ * For SMB1 the server just strips off any number of leading '\\'
+ * characters. Show this is the case.
+ */
+ for (i = 0; i < 10; i++) {
+ char leading_backslash_name[20];
+ leading_backslash_name[i] = '\\';
+ memcpy(&leading_backslash_name[i+1],
+ "SERVER",
+ strlen("SERVER")+1);
+
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ leading_backslash_name);
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ leading_backslash_name);
+ return false;
+ }
+ }
+
+ /* A "BAD" server name should open and match the share root. */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "BAD");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD");
+ return false;
+ }
+ /*
+ * A "BAD\\BAD" server and share name should open
+ * and match the share root.
+ */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "BAD\\BAD");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD");
+ return false;
+ }
+ /*
+ * Trying to open "BAD\\BAD\\BAD" should get
+ * NT_STATUS_OBJECT_NAME_NOT_FOUND.
+ */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\BAD",
+ &test_crtime);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\BAD",
+ nt_errstr(status));
+ return false;
+ }
+ /*
+ * Trying to open "BAD\\BAD\\BAD\\BAD" should get
+ * NT_STATUS_OBJECT_PATH_NOT_FOUND.
+ */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\BAD\\BAD",
+ &test_crtime);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
+ printf("%s:%d Open of %s should get "
+ "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\BAD\\BAD",
+ nt_errstr(status));
+ return false;
+ }
+ /*
+ * Test for invalid pathname characters in the servername.
+ * They are ignored, and it still opens the share root.
+ */
+ crtime_matched = smb1_crtime_matches(cli,
+ dfs_root_share_name,
+ root_crtime,
+ "::::");
+ if (!crtime_matched) {
+ printf("%s:%d Failed to match crtime for %s\n",
+ __FILE__,
+ __LINE__,
+ "::::");
+ return false;
+ }
+
+ /*
+ * Test for invalid pathname characters in the sharename.
+ * Invalid sharename characters should still be flagged as
+ * NT_STATUS_OBJECT_NAME_INVALID. It turns out only ':'
+ * is considered an invalid sharename character.
+ */
+ ok = test_smb1_dfs_sharenames(cli,
+ dfs_root_share_name,
+ root_crtime);
+ if (!ok) {
+ return false;
+ }
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ "BAD\\BAD\\file",
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ fnum = (uint16_t)-1;
+
+ /*
+ * Trying to open "BAD\\BAD\\file" should now get
+ * a valid crtime.
+ */
+ status = get_smb1_crtime(cli,
+ "BAD\\BAD\\file",
+ &test_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Open of %s should succeed "
+ "got %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * This crtime must be different from the root_crtime.
+ * This checks we're actually correctly reading crtimes
+ * from the filesystem.
+ */
+ equal = (timespec_compare(&test_crtime, &root_crtime) == 0);
+ if (equal) {
+ printf("%s:%d Error. crtime of %s must differ from "
+ "root_crtime\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file");
+ goto err;
+ }
+
+ /*
+ * Test different SMB1 renames
+ * and hard links.
+ */
+
+ /* SMBmv only does rename. */
+ ok = test_smb1_mv(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setpathinfo_rename(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setpathinfo_hardlink(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setfileinfo_rename(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setfileinfo_hardlink(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ntrename_rename(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ntrename_hardlink(cli,
+ "BAD\\BAD\\file");
+ if (!ok) {
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ /* Delete anything we made. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\BAD");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\renamed_file");
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\hlink");
+ return retval;
+}
+
+/*
+ * SMB1 Findfirst. This is a minimal implementation
+ * that expects all filename returns in one packet.
+ * We're only using this to test the search DFS pathname
+ * parsing.
+ */
+
+/****************************************************************************
+ Calculate a safe next_entry_offset.
+****************************************************************************/
+
+static size_t calc_next_entry_offset(const uint8_t *base,
+ const uint8_t *pdata_end)
+{
+ size_t next_entry_offset = (size_t)PULL_LE_U32(base,0);
+
+ if (next_entry_offset == 0 ||
+ base + next_entry_offset < base ||
+ base + next_entry_offset > pdata_end) {
+ next_entry_offset = pdata_end - base;
+ }
+ return next_entry_offset;
+}
+
+static size_t get_filename(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const uint8_t *base_ptr,
+ uint16_t recv_flags2,
+ const uint8_t *p,
+ const uint8_t *pdata_end,
+ struct file_info *finfo)
+{
+ size_t ret = 0;
+ const uint8_t *base = p;
+ size_t namelen = 0;
+ size_t slen = 0;
+
+ ZERO_STRUCTP(finfo);
+
+ if (pdata_end - base < 94) {
+ return pdata_end - base;
+ }
+ p += 4; /* next entry offset */
+ p += 4; /* fileindex */
+ /* Offset zero is "create time", not "change time". */
+ p += 8;
+ finfo->atime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->mtime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->ctime_ts = interpret_long_date(BVAL(p, 0));
+ p += 8;
+ finfo->size = PULL_LE_U64(p, 0);
+ p += 8;
+ p += 8; /* alloc size */
+ finfo->attr = PULL_LE_U32(p, 0);
+ p += 4;
+ namelen = PULL_LE_U32(p, 0);
+ p += 4;
+ p += 4; /* EA size */
+ slen = PULL_LE_U8(p, 0);
+ if (slen > 24) {
+ /* Bad short name length. */
+ return pdata_end - base;
+ }
+ p += 2;
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->short_name,
+ p,
+ slen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ p += 24; /* short name */
+ if (p + namelen < p || p + namelen > pdata_end) {
+ return pdata_end - base;
+ }
+ ret = pull_string_talloc(ctx,
+ base_ptr,
+ recv_flags2,
+ &finfo->name,
+ p,
+ namelen,
+ 0);
+ if (ret == (size_t)-1) {
+ return pdata_end - base;
+ }
+ return calc_next_entry_offset(base, pdata_end);
+}
+
+/* Single shot SMB1 TRANS2 FindFirst. */
+
+static NTSTATUS smb1_findfirst(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ const char *search_name,
+ struct file_info **names,
+ size_t *num_names)
+{
+ NTSTATUS status;
+ uint16_t setup[1];
+ uint8_t *param = NULL;
+ uint16_t recv_flags2 = 0;
+ uint8_t *rparam = NULL;
+ uint32_t num_rparam = 0;
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata = 0;
+ uint16_t num_names_returned = 0;
+ struct file_info *finfo = NULL;
+ uint8_t *p2 = NULL;
+ uint8_t *data_end = NULL;
+ uint16_t i = 0;
+
+ PUSH_LE_U16(&setup[0], 0, TRANSACT2_FINDFIRST);
+
+ param = talloc_array(mem_ctx, uint8_t, 12);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ PUSH_LE_U16(param, 0, FILE_ATTRIBUTE_DIRECTORY |
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN);
+ PUSH_LE_U16(param, 2, 1366); /* max_matches */
+ PUSH_LE_U16(param, 4, FLAG_TRANS2_FIND_CLOSE_IF_END);
+ PUSH_LE_U16(param, 6, SMB_FIND_FILE_BOTH_DIRECTORY_INFO); /* info_level */
+
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ search_name,
+ strlen(search_name)+1,
+ NULL);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * A one shot SMB1 findfirst will be enough to
+ * return ".", "..", and "file".
+ */
+ status = cli_trans(mem_ctx,
+ cli,
+ SMBtrans2, /* cmd */
+ NULL, /* pipe_name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ &setup[0],
+ 1, /* num_setup uint16_t words */
+ 0, /* max returned setup */
+ param,
+ talloc_get_size(param), /* num_param */
+ 10, /* max returned param */
+ NULL, /* data */
+ 0, /* num_data */
+ SMB_BUFFER_SIZE_MAX, /* max returned data */
+ /* Return values from here on.. */
+ &recv_flags2, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min returned rsetup */
+ NULL, /* num_rsetup */
+ &rparam,
+ 6, /* min returned rparam */
+ &num_rparam, /* number of returned rparam */
+ &rdata,
+ 0, /* min returned rdata */
+ &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ num_names_returned = PULL_LE_U16(rparam, 2);
+
+ finfo = talloc_array(mem_ctx, struct file_info, num_names_returned);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p2 = rdata;
+ data_end = rdata + num_rdata;
+
+ for (i = 0; i < num_names_returned; i++) {
+ if (p2 >= data_end) {
+ break;
+ }
+ if (i == num_names_returned - 1) {
+ /* Last entry - fixup the last offset length. */
+ PUSH_LE_U32(p2, 0, PTR_DIFF((rdata + num_rdata), p2));
+ }
+
+ p2 += get_filename(mem_ctx,
+ cli,
+ rdata,
+ recv_flags2,
+ p2,
+ data_end,
+ &finfo[i]);
+
+ if (finfo->name == NULL) {
+ printf("%s:%d Unable to parse name from listing "
+ "of %s, position %u\n",
+ __FILE__,
+ __LINE__,
+ search_name,
+ (unsigned int)i);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ *num_names = i;
+ *names = finfo;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Test a specific SMB1 findfirst path to see if it
+ * matches a given file array.
+ */
+static bool test_smb1_findfirst_path(struct cli_state *cli,
+ const char *search_path,
+ struct file_info *root_finfo,
+ size_t num_root_finfo)
+{
+ size_t i = 0;
+ size_t num_finfo = 0;
+ struct file_info *finfo = NULL;
+ NTSTATUS status;
+
+ status = smb1_findfirst(talloc_tos(),
+ cli,
+ search_path,
+ &finfo,
+ &num_finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1findfirst on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ search_path,
+ nt_errstr(status));
+ return false;
+ }
+
+ if (num_finfo != num_root_finfo) {
+ printf("%s:%d On %s, num_finfo = %zu, num_root_finfo = %zu\n",
+ __FILE__,
+ __LINE__,
+ search_path,
+ num_finfo,
+ num_root_finfo);
+ return false;
+ }
+ for (i = 0; i < num_finfo; i++) {
+ bool match = strequal_m(finfo[i].name,
+ root_finfo[i].name);
+ if (!match) {
+ printf("%s:%d Mismatch. For %s, at position %zu, "
+ "finfo[i].name = %s, "
+ "root_finfo[i].name = %s\n",
+ __FILE__,
+ __LINE__,
+ search_path,
+ i,
+ finfo[i].name,
+ root_finfo[i].name);
+ return false;
+ }
+ }
+ TALLOC_FREE(finfo);
+ return true;
+}
+
+/*
+ * "Raw" test of doing a SMB1 findfirst to a DFS share.
+ * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb1_dfs_search_paths(int dummy)
+{
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+ bool dfs_supported = false;
+ struct file_info *root_finfo = NULL;
+ size_t num_root_finfo = 0;
+ bool retval = false;
+ bool ok = false;
+ uint16_t fnum = (uint16_t)-1;
+
+ printf("Starting SMB1-DFS-SEARCH-PATHS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+
+ /* Create a test file to search for. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ "BAD\\BAD\\file",
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "BAD\\BAD\\file",
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ fnum = (uint16_t)-1;
+
+ /* Get the list of files in the share. */
+ status = smb1_findfirst(talloc_tos(),
+ cli,
+ "SERVER\\SHARE\\*",
+ &root_finfo,
+ &num_root_finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1findfirst on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "SERVER\\SHARE\\*",
+ nt_errstr(status));
+ return false;
+ }
+
+ /*
+ * Try different search names. They should
+ * all match the root directory list.
+ */
+ ok = test_smb1_findfirst_path(cli,
+ "\\SERVER\\SHARE\\*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_findfirst_path(cli,
+ "*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+ ok = test_smb1_findfirst_path(cli,
+ "\\*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+ ok = test_smb1_findfirst_path(cli,
+ "\\SERVER\\*",
+ root_finfo,
+ num_root_finfo);
+ if (!ok) {
+ goto err;
+ }
+ retval = true;
+
+ err:
+
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+
+ /* Delete anything we made. */
+ (void)smb1_dfs_delete(cli, "BAD\\BAD\\file");
+ return retval;
+}
+
+static bool smb1_create_testfile(struct cli_state *cli,
+ const char *path)
+{
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+
+ /* Create a test file. */
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ path,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ path,
+ nt_errstr(status));
+ return false;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return true;
+}
+
+static NTSTATUS smb1_unlink(struct cli_state *cli,
+ const char *path)
+{
+ uint16_t vwv[1];
+ uint8_t *bytes = NULL;
+
+ PUSH_LE_U16(vwv, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN);
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return cli_smb(talloc_tos(),
+ cli,
+ SMBunlink, /* command. */
+ 0, /* additional_flags. */
+ 1, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+}
+
+static bool test_smb1_unlink(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\file");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\file");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\file");
+ goto err;
+ }
+
+ status = smb1_unlink(cli, "file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1unlink of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "file",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_unlink(cli, "\\BAD\\file");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1unlink of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_unlink(cli, "\\BAD\\BAD\\file");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1unlink on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\file",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\file");
+ return retval;
+}
+
+static NTSTATUS smb1_mkdir(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *bytes = NULL;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return cli_smb(talloc_tos(),
+ cli,
+ SMBmkdir, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+}
+
+static bool test_smb1_mkdir(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+
+ status = smb1_mkdir(cli, "dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1mkdir of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_mkdir(cli, "\\BAD\\dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1mkdir of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_mkdir(cli, "\\BAD\\BAD\\dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1mkdir on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+ return retval;
+}
+
+static NTSTATUS smb1_rmdir(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *bytes = NULL;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return cli_smb(talloc_tos(),
+ cli,
+ SMBrmdir, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+}
+
+static bool test_smb1_rmdir(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+
+ status = smb1_mkdir(cli, "\\BAD\\BAD\\dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1rmdir on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = smb1_rmdir(cli, "dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1rmdir of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_rmdir(cli, "\\BAD\\dir");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1rmdir of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_rmdir(cli, "\\BAD\\BAD\\dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1rmdir on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\dir");
+ return retval;
+}
+
+static NTSTATUS smb1_ntcreatex(struct cli_state *cli,
+ const char *path)
+{
+ NTSTATUS status;
+ uint16_t fnum = (uint16_t)-1;
+
+ status = smb1cli_ntcreatex(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ path,
+ OPLOCK_NONE, /* CreatFlags */
+ 0, /* RootDirectoryFid */
+ SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE, /* DesiredAccess */
+ 0, /* AllocationSize */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* ShareAccess */
+ FILE_CREATE, /* CreateDisposition */
+ 0, /* CreateOptions */
+ 2, /* ImpersonationLevel */
+ 0, /* SecurityFlags */
+ &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return NT_STATUS_OK;
+}
+
+static bool test_smb1_ntcreatex(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ntcreateXfile");
+
+ status = smb1_ntcreatex(cli, "ntcreateXfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1ntcreateX of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "ntcreateXfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ntcreatex(cli, "\\BAD\\ntcreateXfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1ntcreateX of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\ntcreateXfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ntcreatex(cli, "\\BAD\\BAD\\ntcreateXfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1ntcreateX on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\ntcreateXfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ntcreateXfile");
+ return retval;
+}
+
+static NTSTATUS smb1_nttrans_create(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *param = NULL;
+ size_t converted_len = 0;
+ uint8_t *rparam = NULL;
+ uint32_t num_rparam = 0;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+
+ param = talloc_zero_array(talloc_tos(), uint8_t, 53);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path),
+ &converted_len);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ PUSH_LE_U32(param, 8, SEC_STD_SYNCHRONIZE|
+ SEC_STD_DELETE |
+ SEC_FILE_READ_DATA|
+ SEC_FILE_READ_ATTRIBUTE); /* DesiredAccess */
+ PUSH_LE_U32(param, 20, FILE_ATTRIBUTE_NORMAL);
+ PUSH_LE_U32(param, 24, FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE); /* ShareAccess */
+ PUSH_LE_U32(param, 28, FILE_CREATE);
+ PUSH_LE_U32(param, 44, converted_len);
+ PUSH_LE_U32(param, 48, 0x02); /* ImpersonationLevel */
+
+ status = cli_trans(talloc_tos(),
+ cli,
+ SMBnttrans, /* trans cmd */
+ NULL, /* pipe_name */
+ 0, /* fid */
+ NT_TRANSACT_CREATE, /* function */
+ 0, /* flags */
+ NULL, /* setup */
+ 0, /* num_setup */
+ 0, /* max_setup */
+ param, /* param */
+ talloc_get_size(param), /* num_param */
+ 128, /* max_param */
+ NULL, /* data */
+ 0, /* num_data */
+ 0, /* max_data */
+ NULL, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min_rsetup */
+ NULL, /* num_rsetup */
+ &rparam, /* rparam */
+ 69, /* min_rparam */
+ &num_rparam, /* num_rparam */
+ NULL, /* rdata */
+ 0, /* min_rdata */
+ NULL); /* num_rdata */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ fnum = PULL_LE_U16(param, 2);
+ /* Close "file" handle. */
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ return NT_STATUS_OK;
+}
+
+static bool test_smb1_nttrans_create(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\nttransfile");
+
+ status = smb1_nttrans_create(cli, "nttransfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1trans NT_TRANSACT_CREATE of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "nttransfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_nttrans_create(cli, "\\BAD\\nttransfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("%s:%d SMB1trans NT_TRANSACT_CREATE of %s should get "
+ "NT_STATUS_OBJECT_NAME_COLLISION, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\nttransfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_nttrans_create(cli, "\\BAD\\BAD\\nttransfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1trans NT_TRANSACT_CREATE on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\nttransfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\nttransfile");
+ return retval;
+}
+
+struct smb1_openx_state {
+ const char *fname;
+ uint16_t vwv[15];
+ uint16_t fnum;
+ struct iovec bytes;
+};
+
+static void smb1_openx_done(struct tevent_req *subreq);
+
+static struct tevent_req *smb1_openx_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *path)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ uint16_t accessmode = 0;
+ struct smb1_openx_state *state = NULL;
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state, struct smb1_openx_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ accessmode = (DENY_NONE<<4);
+ accessmode |= DOS_OPEN_RDONLY;
+
+ PUSH_LE_U8(state->vwv + 0, 0, 0xFF);
+ PUSH_LE_U16(state->vwv + 3, 0, accessmode);
+ PUSH_LE_U16(state->vwv + 4, 0,
+ FILE_ATTRIBUTE_SYSTEM |
+ FILE_ATTRIBUTE_HIDDEN |
+ FILE_ATTRIBUTE_DIRECTORY);
+ PUSH_LE_U16(state->vwv + 8,
+ 0,
+ OPENX_FILE_CREATE_IF_NOT_EXIST| OPENX_FILE_EXISTS_FAIL);
+
+ bytes = talloc_array(state, uint8_t, 0);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (tevent_req_nomem(bytes, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->bytes.iov_base = (void *)bytes;
+ state->bytes.iov_len = talloc_get_size(bytes);
+ subreq = cli_smb_req_create(state,
+ ev,
+ cli,
+ SMBopenX, /* cmd */
+ 0, /* additional_flags */
+ 0, /* additional_flags2 */
+ 15, /* num_vwv */
+ state->vwv, /* vwv */
+ 1, /* iovcount */
+ &state->bytes); /* iovec */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, smb1_openx_done, req);
+
+ status = smb1cli_req_chain_submit(&subreq, 1);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void smb1_openx_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct smb1_openx_state *state = tevent_req_data(
+ req, struct smb1_openx_state);
+ uint8_t wct = 0;
+ uint16_t *vwv = NULL;
+ NTSTATUS status;
+
+ status = cli_smb_recv(subreq,
+ state,
+ NULL, /* pinbuf */
+ 3, /* min_wct */
+ &wct, /* wct */
+ &vwv, /* vwv */
+ NULL, /* num_rbytes */
+ NULL); /* rbytes */
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ state->fnum = PULL_LE_U16(vwv+2, 0);
+ tevent_req_done(req);
+}
+
+static NTSTATUS smb1_openx_recv(struct tevent_req *req, uint16_t *pfnum)
+{
+ struct smb1_openx_state *state = tevent_req_data(
+ req, struct smb1_openx_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfnum = state->fnum;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS smb1_openx(struct cli_state *cli, const char *path)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = smb1_openx_send(frame,
+ ev,
+ cli,
+ path);
+ if (req == NULL) {
+ goto fail;
+ }
+
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+
+ status = smb1_openx_recv(req, &fnum);
+ fail:
+
+ /* Close "file" handle. */
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static bool test_smb1_openx(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openxfile");
+
+ status = smb1_openx(cli, "openxfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1openx of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "openxfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_openx(cli, "\\BAD\\openxfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1openx of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\openxfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_openx(cli, "\\BAD\\BAD\\openxfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1openx on %s returned %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openxfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openxfile");
+ return retval;
+}
+
+static NTSTATUS smb1_open(struct cli_state *cli,
+ const char *path,
+ uint16_t *pfnum)
+{
+ uint16_t vwv[2] = { 0, 0};
+ uint8_t *bytes = NULL;
+ uint16_t accessmode = 0;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ NTSTATUS status;
+
+ accessmode = (DENY_NONE<<4);
+ accessmode |= DOS_OPEN_RDONLY;
+
+ PUSH_LE_U16(vwv + 0, 0, accessmode);
+ PUSH_LE_U16(vwv + 1, 0, FILE_ATTRIBUTE_NORMAL);
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBopen, /* command. */
+ 0, /* additional_flags. */
+ 2, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 7, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pfnum = PULL_LE_U16(return_words, 0);
+ return status;
+}
+
+static bool test_smb1_open(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ bool equal = false;
+ uint16_t fnum = (uint16_t)-1;
+ struct timespec testfile_crtime = { 0 };
+ struct timespec open_crtime = { 0 };
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\openfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile");
+ goto err;
+ }
+
+ /* Get the test file crtime number. */
+ status = get_smb1_crtime(cli,
+ "\\BAD\\BAD\\openfile",
+ &testfile_crtime);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to get crtime for %s, (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = smb1_open(cli, "openfile", &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1open of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_open(cli, "\\BAD\\openfile", &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1open of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_open(cli, "\\BAD\\BAD\\openfile", &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to open test file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = cli_qfileinfo_basic(cli,
+ fnum,
+ NULL, /* attr */
+ NULL, /* size */
+ &open_crtime, /* create_time */
+ NULL, /* access_time */
+ NULL, /* write_time */
+ NULL, /* change_time */
+ NULL); /* ino */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to get crtime of test file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ equal = (timespec_compare(&testfile_crtime, &open_crtime) == 0);
+ if (!equal) {
+ printf("%s:%d crtime mismatch of test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\openfile");
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ /* Close "openfile" handle. */
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\openfile");
+ return retval;
+}
+
+static NTSTATUS smb1_create(struct cli_state *cli,
+ const char *path,
+ uint16_t smb1_operation,
+ uint16_t *pfnum)
+{
+ uint16_t vwv[3] = { 0, 0, 0};
+ uint8_t *bytes = NULL;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ NTSTATUS status;
+
+ PUSH_LE_U16(vwv + 0, 0, FILE_ATTRIBUTE_NORMAL);
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ smb1_operation, /* command. */
+ 0, /* additional_flags. */
+ 3, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 1, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pfnum = PULL_LE_U16(return_words, 0);
+ return status;
+}
+
+static bool test_smb1_create(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ uint16_t fnum = (uint16_t)-1;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\createfile");
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\mknewfile");
+
+ status = smb1_create(cli, "createfile", SMBcreate, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1create of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "createfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\createfile", SMBcreate, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1open of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\openfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\BAD\\createfile", SMBcreate, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to create file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\createfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+
+ fnum = (uint16_t)-1;
+
+ /* Now do the same with SMBmknew */
+ status = smb1_create(cli, "mknewfile", SMBmknew, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1mknew of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "mknewfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\mknewfile", SMBmknew, &fnum);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1mknew of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\mknewfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_create(cli, "\\BAD\\BAD\\mknewfile", SMBmknew, &fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d failed to create file %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\mknewfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+
+ fnum = (uint16_t)-1;
+
+ retval = true;
+
+ err:
+
+ /* Close "openfile" handle. */
+ if (fnum != (uint16_t)-1) {
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ }
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\createfile");
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\mknewfile");
+ return retval;
+}
+
+static NTSTATUS smb1_getatr(struct cli_state *cli,
+ const char *path,
+ uint16_t *pattr)
+{
+ uint8_t *bytes = NULL;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ NTSTATUS status;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBgetatr, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 10, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pattr = PULL_LE_U16(return_words, 0);
+ return status;
+}
+
+static bool test_smb1_getatr(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ uint16_t attrs = 0;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\getatrfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\getatrfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\getatrfile");
+ goto err;
+ }
+
+ /*
+ * We expect this to succeed, but get attributes of
+ * the root directory.
+ */
+ status = smb1_getatr(cli, "getatrfile", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "getatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d error expected SMB1getatr of file %s "
+ "to return directory attributes. Got 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "getatrfile",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /*
+ * We expect this to succeed, but get attributes of
+ * the root directory.
+ */
+ status = smb1_getatr(cli, "\\BAD\\getatrfile", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\getatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d error expected SMB1getatr of file %s "
+ "to return directory attributes. Got 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\getatrfile",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /*
+ * We expect this to succeed, and get attributes of
+ * the testfile.
+ */
+ status = smb1_getatr(cli, "\\BAD\\BAD\\getatrfile", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\getatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
+ printf("%s:%d error expected SMB1getatr of file %s "
+ "to return non-directory attributes. Got 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\getatrfile",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\getatrfile");
+ return retval;
+}
+
+static NTSTATUS smb1_setatr(struct cli_state *cli,
+ const char *path,
+ uint16_t attr)
+{
+ uint16_t vwv[8] = { 0 };
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+
+ PUSH_LE_U16(vwv, 0, attr);
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBsetatr, /* command. */
+ 0, /* additional_flags. */
+ 8, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return status;
+}
+
+static bool test_smb1_setatr(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ uint16_t file_attrs = 0;
+ uint16_t orig_file_attrs = 0;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\setatrfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\setatrfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile");
+ goto err;
+ }
+ /* Get it's original attributes. */
+ status = smb1_getatr(cli, "\\BAD\\BAD\\setatrfile", &orig_file_attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ if (orig_file_attrs & FILE_ATTRIBUTE_SYSTEM) {
+ printf("%s:%d orig_file_attrs of %s already has SYSTEM. "
+ "Test cannot proceed.\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile");
+ goto err;
+ }
+
+ /*
+ * Seems we can't set attrs on the root of a share,
+ * even as Administrator.
+ */
+ status = smb1_setatr(cli, "setatrfile", FILE_ATTRIBUTE_SYSTEM);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1setatr of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Seems we can't set attrs on the root of a share,
+ * even as Administrator.
+ */
+ status = smb1_setatr(cli, "\\BAD\\setatrfile", FILE_ATTRIBUTE_SYSTEM);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("%s:%d SMB1setatr of %s should get "
+ "NT_STATUS_ACCESS_DENIED, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ status = smb1_setatr(cli,
+ "\\BAD\\BAD\\setatrfile",
+ FILE_ATTRIBUTE_SYSTEM);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1setatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_getatr(cli, "\\BAD\\BAD\\setatrfile", &file_attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1getatr of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ if (file_attrs != FILE_ATTRIBUTE_SYSTEM) {
+ printf("%s:%d Failed to set SYSTEM attr on %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\setatrfile");
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\setatrfile");
+ return retval;
+}
+
+static NTSTATUS smb1_chkpath(struct cli_state *cli,
+ const char *path)
+{
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBcheckpath, /* command. */
+ 0, /* additional_flags. */
+ 0, /* wct. */
+ NULL, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 0, /* min_wct. */
+ NULL, /* return wcount. */
+ NULL, /* return wvw. */
+ NULL, /* return byte count. */
+ NULL); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return status;
+}
+
+static bool test_smb1_chkpath(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\chkpathfile");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\chkpathfile");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\chkpathfile");
+ goto err;
+ }
+ /*
+ * Should succeed - "chkpathfile" maps to
+ * directory "".
+ */
+ status = smb1_chkpath(cli, "chkpathfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1chkpath of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "chkpathfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Should succeed - "\\BAD\\chkpathfile" maps to
+ * directory "".
+ */
+ status = smb1_chkpath(cli, "\\BAD\\chkpathfile");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1chkpath of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\chkpathfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Should fail - "\\BAD\\BAD\\chkpathfile" maps to the
+ * "\\BAD\\BAD\\chkpathfile", not a directory.
+ */
+ status = smb1_chkpath(cli, "\\BAD\\BAD\\chkpathfile");
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ printf("%s:%d SMB1chkpath of %s should get "
+ "NT_STATUS_NOT_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\chkpathfile",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\chkpathfile");
+ return retval;
+}
+
+/*
+ * Test BUG: https://bugzilla.samba.org/show_bug.cgi?id=15419
+ */
+
+static bool test_smb1_chkpath_bad(struct cli_state *cli)
+{
+ NTSTATUS status;
+
+ status = smb1_chkpath(cli, "\\x//\\/");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d SMB1chkpath of %s failed (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\x//\\/",
+ nt_errstr(status));
+ return false;
+ }
+ return true;
+}
+
+static NTSTATUS smb1_ctemp(struct cli_state *cli,
+ const char *path,
+ char **tmp_path)
+{
+ uint16_t vwv[3] = { 0 };
+ uint8_t *bytes = NULL;
+ NTSTATUS status;
+ uint16_t *return_words = NULL;
+ uint8_t return_wcount = 0;
+ uint32_t return_bytecount = 0;
+ uint8_t *return_bytes = NULL;
+ size_t sret = 0;
+ uint16_t fnum = (uint16_t)-1;
+
+ bytes = talloc_array(talloc_tos(), uint8_t, 1);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ bytes[0] = 4;
+ bytes = smb_bytes_push_str(bytes,
+ smbXcli_conn_use_unicode(cli->conn),
+ path,
+ strlen(path)+1,
+ NULL);
+ if (bytes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = cli_smb(talloc_tos(),
+ cli,
+ SMBctemp, /* command. */
+ 0, /* additional_flags. */
+ 3, /* wct. */
+ vwv, /* vwv. */
+ talloc_get_size(bytes), /* num_bytes. */
+ bytes, /* bytes. */
+ NULL, /* result parent. */
+ 1, /* min_wct. */
+ &return_wcount, /* return wcount. */
+ &return_words, /* return wvw. */
+ &return_bytecount, /* return byte count. */
+ &return_bytes); /* return bytes. */
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (return_wcount != 1) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ fnum = PULL_LE_U16(return_words, 0);
+
+ /* Delete the file by fnum. */
+ status = cli_nt_delete_on_close(cli, fnum, 1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ (void)smb1cli_close(cli->conn,
+ cli->timeout,
+ cli->smb1.pid,
+ cli->smb1.tcon,
+ cli->smb1.session,
+ fnum,
+ 0); /* last_modified */
+ fnum = (uint16_t)-1;
+
+ if (return_bytecount < 2) {
+ return NT_STATUS_DATA_ERROR;
+ }
+
+ sret = pull_string_talloc(talloc_tos(),
+ NULL,
+ 0,
+ tmp_path,
+ return_bytes,
+ return_bytecount,
+ STR_ASCII);
+ if (sret == 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return status;
+}
+
+static bool test_smb1_ctemp(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ char *retpath = NULL;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ctemp_dir");
+
+ status = smb1_mkdir(cli, "\\BAD\\BAD\\ctemp_dir");
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d Failed to create %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ /*
+ * Windows returns NT_STATUS_FILE_IS_A_DIRECTORY
+ * for all SMBctemp calls on a DFS share, no
+ * matter what we put in the pathname.
+ */
+
+ /*
+ * When we fix smbd we'll need to detect running
+ * in smbtorture3 against smbd here and modify
+ * the expected behavior. Windows is simply
+ * broken here.
+ */
+ status = smb1_ctemp(cli, "ctemp_dir", &retpath);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1ctemp of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ctemp(cli, "\\BAD\\ctemp_dir", &retpath);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1ctemp of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+ status = smb1_ctemp(cli, "\\BAD\\BAD\\ctemp_dir", &retpath);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ printf("%s:%d SMB1ctemp of %s should get "
+ "NT_STATUS_FILE_IS_A_DIRECTORY, got %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\ctemp_dir",
+ nt_errstr(status));
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\ctemp_dir");
+ return retval;
+}
+
+static NTSTATUS smb1_qpathinfo(struct cli_state *cli,
+ const char *fname,
+ uint32_t *pattrs)
+{
+ NTSTATUS status;
+ uint8_t *param = NULL;
+ uint16_t setup[1] = { 0 };
+ uint8_t *rdata = NULL;
+ uint32_t num_rdata = 0;
+
+ PUSH_LE_U16(setup, 0, TRANSACT2_QPATHINFO);
+
+ param = talloc_zero_array(talloc_tos(), uint8_t, 6);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ PUSH_LE_U16(param, 0, SMB_QUERY_FILE_BASIC_INFO);
+
+ param = trans2_bytes_push_str(param,
+ smbXcli_conn_use_unicode(cli->conn),
+ fname,
+ strlen(fname)+1,
+ NULL);
+ if (param == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_trans(talloc_tos(),
+ cli,
+ SMBtrans2, /* cmd */
+ NULL, /* pipe_name */
+ 0, /* fid */
+ 0, /* function */
+ 0, /* flags */
+ &setup[0],
+ 1, /* num_setup uint16_t words */
+ 0, /* max returned setup */
+ param,
+ talloc_get_size(param), /* num_param */
+ 2, /* max returned param */
+ NULL, /* data */
+ 0, /* num_data */
+ SMB_BUFFER_SIZE_MAX, /* max returned data */
+ /* Return values from here on.. */
+ NULL, /* recv_flags2 */
+ NULL, /* rsetup */
+ 0, /* min returned rsetup */
+ NULL, /* num_rsetup */
+ NULL,
+ 0, /* min returned rparam */
+ NULL, /* number of returned rparam */
+ &rdata,
+ 36, /* min returned rdata */
+ &num_rdata);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *pattrs = PULL_LE_U32(rdata, 32);
+ return NT_STATUS_OK;
+}
+
+static bool test_smb1_qpathinfo(struct cli_state *cli)
+{
+ NTSTATUS status;
+ bool retval = false;
+ bool ok = false;
+ uint32_t attrs;
+
+ /* Start clean. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\qpathinfo_file");
+
+ /* Create a test file. */
+ ok = smb1_create_testfile(cli, "\\BAD\\BAD\\qpathinfo_file");
+ if (!ok) {
+ printf("%s:%d failed to create test file %s\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\qpathinfo_file");
+ goto err;
+ }
+
+ /* Should get root dir attrs. */
+ status = smb1_qpathinfo(cli, "qpathinfo_file", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1_qpathinfo failed %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "qpathinfo_file",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d expected FILE_ATTRIBUTE_DIRECTORY on %s "
+ "got attribute 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "qpathinfo_file",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /* Should get root dir attrs. */
+ status = smb1_qpathinfo(cli, "\\BAD\\qpathinfo_file", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1_qpathinfo failed %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\qpathinfo_file",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ printf("%s:%d expected FILE_ATTRIBUTE_DIRECTORY on %s "
+ "got attribute 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\qpathinfo_file",
+ (unsigned int)attrs);
+ goto err;
+ }
+
+ /* Should get file attrs. */
+ status = smb1_qpathinfo(cli, "\\BAD\\BAD\\qpathinfo_file", &attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("%s:%d smb1_qpathinfo failed %s (%s)\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\qpathinfo_file",
+ nt_errstr(status));
+ goto err;
+ }
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ printf("%s:%d expected not FILE_ATTRIBUTE_DIRECTORY on %s "
+ "got attribute 0x%x\n",
+ __FILE__,
+ __LINE__,
+ "\\BAD\\BAD\\qpathinfo_file",
+ (unsigned int)attrs);
+ }
+
+ retval = true;
+
+ err:
+
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\qpathinfo_file");
+ return retval;
+}
+
+/*
+ * "Raw" test of different SMB1 operations to a DFS share.
+ * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
+ * not the cli_XXX() ones here as the ultimate goal is to fix our
+ * cli_XXX() interfaces to work transparently over DFS.
+ *
+ * So here, we're testing the server code, not the client code.
+ *
+ * Passes cleanly against Windows.
+ */
+
+bool run_smb1_dfs_operations(int dummy)
+{
+ struct cli_state *cli = NULL;
+ bool dfs_supported = false;
+ bool retval = false;
+ bool ok = false;
+
+ printf("Starting SMB1-DFS-OPS\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ ok = test_smb1_unlink(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_mkdir(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_rmdir(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ntcreatex(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_nttrans_create(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_openx(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_open(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_create(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_getatr(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_setatr(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_chkpath(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_ctemp(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ ok = test_smb1_qpathinfo(cli);
+ if (!ok) {
+ goto err;
+ }
+
+ retval = true;
+
+ err:
+
+ /* Delete anything we made. */
+ (void)smb1_dfs_delete(cli, "\\BAD\\BAD\\file");
+ return retval;
+}
+
+/*
+ * Test BUG: https://bugzilla.samba.org/show_bug.cgi?id=15419
+ */
+
+bool run_smb1_dfs_check_badpath(int dummy)
+{
+ struct cli_state *cli = NULL;
+ bool dfs_supported = false;
+
+ printf("Starting SMB1-DFS-CHECK-BADPATH\n");
+
+ if (!torture_init_connection(&cli)) {
+ return false;
+ }
+
+ if (!torture_open_connection(&cli, 0)) {
+ return false;
+ }
+
+ /* Ensure this is a DFS share. */
+ dfs_supported = smbXcli_conn_dfs_supported(cli->conn);
+ if (!dfs_supported) {
+ printf("Server %s does not support DFS\n",
+ smbXcli_conn_remote_name(cli->conn));
+ return false;
+ }
+ dfs_supported = smbXcli_tcon_is_dfs_share(cli->smb1.tcon);
+ if (!dfs_supported) {
+ printf("Share %s does not support DFS\n",
+ cli->share);
+ return false;
+ }
+
+ return test_smb1_chkpath_bad(cli);
+}