1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/torture/test_smb1_dfs.c
Jeremy Allison 2aa9ffa2f0 s3: torture: Add test to show an SMB1 DFS path of "\\x//\\/" crashes smbd.
Adds knownfail.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15419

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
2023-07-27 09:59:29 +00:00

4285 lines
98 KiB
C

/*
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((const char *)p);
p += 8;
finfo->mtime_ts = interpret_long_date((const char *)p);
p += 8;
finfo->ctime_ts = interpret_long_date((const char *)p);
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);
}