mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
cb166028c4
Separate concerns of conversion and pulling off the wire. Needed soon for smb311 pidl generated parsing. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
4285 lines
98 KiB
C
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(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);
|
|
}
|