#include "../lib/util/tevent_ntstatus.h"
#include "async_smb.h"
#include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/****************************************************************************
+ Check if a returned directory name is safe.
+****************************************************************************/
+
+static NTSTATUS is_bad_name(bool windows_names, const char *name)
+{
+ const char *bad_name_p = NULL;
+
+ bad_name_p = strchr(name, '/');
+ if (bad_name_p != NULL) {
+ /*
+ * Windows and POSIX names can't have '/'.
+ * Server is attacking us.
+ */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (windows_names) {
+ bad_name_p = strchr(name, '\\');
+ if (bad_name_p != NULL) {
+ /*
+ * Windows names can't have '\\'.
+ * Server is attacking us.
+ */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Check if a returned directory name is safe. Disconnect if server is
+ sending bad names.
+****************************************************************************/
+
+NTSTATUS is_bad_finfo_name(const struct cli_state *cli,
+ const struct file_info *finfo)
+{
+ NTSTATUS status = NT_STATUS_OK;
+ bool windows_names = true;
+
+ if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
+ windows_names = false;
+ }
+ if (finfo->name != NULL) {
+ status = is_bad_name(windows_names, finfo->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("bad finfo->name\n");
+ return status;
+ }
+ }
+ if (finfo->short_name != NULL) {
+ status = is_bad_name(windows_names, finfo->short_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("bad finfo->short_name\n");
+ return status;
+ }
+ }
+ return NT_STATUS_OK;
+}
/****************************************************************************
Calculate a safe next_entry_offset.
const char *p,
const char *pdata_end,
struct file_info *finfo,
- uint32 *p_resume_key,
+ uint32_t *p_resume_key,
DATA_BLOB *p_last_name_raw)
{
int len;
if (pdata_end - base < 27) {
return pdata_end - base;
}
+ /*
+ * What we're returning here as ctime_ts is
+ * actually the server create time.
+ */
+ finfo->btime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+4,
+ smb1cli_conn_server_time_zone(
+ cli->conn)));
finfo->ctime_ts = convert_time_t_to_timespec(
- make_unix_date2(p+4, cli->serverzone));
+ make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
finfo->atime_ts = convert_time_t_to_timespec(
- make_unix_date2(p+8, cli->serverzone));
+ make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
finfo->mtime_ts = convert_time_t_to_timespec(
- make_unix_date2(p+12, cli->serverzone));
+ make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
finfo->size = IVAL(p,16);
finfo->mode = CVAL(p,24);
len = CVAL(p, 26);
p += 27;
- p += align_string(base_ptr, p, 0);
+ if (recv_flags2 & FLAGS2_UNICODE_STRINGS) {
+ p += ucs2_align(base_ptr, p, STR_UNICODE);
+ }
/* We can safely use len here (which is required by OS/2)
* and the NAS-BASIC server instead of +2 or +1 as the
if (pdata_end - base < 31) {
return pdata_end - base;
}
+ /*
+ * What we're returning here as ctime_ts is
+ * actually the server create time.
+ */
+ finfo->btime_ts = convert_time_t_to_timespec(
+ make_unix_date2(p+4,
+ smb1cli_conn_server_time_zone(
+ cli->conn)));
finfo->ctime_ts = convert_time_t_to_timespec(
- make_unix_date2(p+4, cli->serverzone));
+ make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn)));
finfo->atime_ts = convert_time_t_to_timespec(
- make_unix_date2(p+8, cli->serverzone));
+ make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
finfo->mtime_ts = convert_time_t_to_timespec(
- make_unix_date2(p+12, cli->serverzone));
+ make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
finfo->size = IVAL(p,16);
finfo->mode = CVAL(p,24);
len = CVAL(p, 30);
namelen = IVAL(p,0);
p += 4;
p += 4; /* EA size */
- slen = SVAL(p, 0);
+ slen = CVAL(p, 0);
if (slen > 24) {
/* Bad short name length. */
return pdata_end - base;
}
p += 2;
- {
- /* stupid NT bugs. grr */
- int flags = 0;
- if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
- clistr_pull(base_ptr, finfo->short_name, p,
- sizeof(finfo->short_name),
- slen, flags);
+ ret = clistr_pull_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 calc_next_entry_offset(base, pdata_end);
}
+ case SMB_FIND_FILE_UNIX_INFO2:
+ {
+ SMB_STRUCT_STAT *sbuf = &finfo->posix_sbuf;
+ size_t namelen;
+
+ if (pdata_end - base < 128) {
+ return pdata_end - base;
+ }
+
+ p += 4; /* next entry offset */
+
+ if (p_resume_key) {
+ *p_resume_key = IVAL(p,0);
+ }
+ p += 4; /* fileindex */
+
+ fetch_file_unix_basic_info2((const uint8_t *)p, sbuf);
+ p += 116;
+
+ finfo->mode = S_ISDIR(sbuf->st_ex_mode) ?
+ FILE_ATTRIBUTE_DIRECTORY :
+ FILE_ATTRIBUTE_NORMAL;
+ if (sbuf->st_ex_mode & S_IXUSR) {
+ finfo->mode |= FILE_ATTRIBUTE_ARCHIVE;
+ }
+
+ finfo->size = sbuf->st_ex_size;
+ finfo->allocated_size =
+ sbuf->st_ex_blksize * sbuf->st_ex_blocks;
+ finfo->uid = sbuf->st_ex_uid;
+ finfo->gid = sbuf->st_ex_gid;
+ finfo->ino = sbuf->st_ex_ino;
+ finfo->btime_ts = sbuf->st_ex_btime;
+ finfo->mtime_ts = sbuf->st_ex_mtime;
+ finfo->atime_ts = sbuf->st_ex_atime;
+ finfo->ctime_ts = sbuf->st_ex_ctime;
+
+ namelen = IVAL(p, 0);
+ p += 4;
+
+ if (namelen > (pdata_end - p)) {
+ return pdata_end - base;
+ }
+
+ ret = clistr_pull_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);
+ }
}
DEBUG(1,("Unknown long filename format %d\n",level));
finfo->mode = CVAL(p,21);
+ /* We don't get birth time. */
+ finfo->btime_ts.tv_sec = 0;
+ finfo->btime_ts.tv_nsec = 0;
/* this date is converted to GMT by make_unix_date */
- finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
+ finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn));
finfo->ctime_ts.tv_nsec = 0;
finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
finfo->size = IVAL(p,26);
ret = clistr_pull_talloc(ctx,
- cli->inbuf,
- SVAL(cli->inbuf, smb_flg2),
+ NULL,
+ 0,
&finfo->name,
p+30,
12,
}
if (finfo->name) {
- strlcpy(finfo->short_name,
- finfo->name,
- sizeof(finfo->short_name));
+ finfo->short_name = talloc_strdup(ctx, finfo->name);
+ if (finfo->short_name == NULL) {
+ return false;
+ }
}
return true;
}
struct cli_list_old_state *state;
uint8_t *bytes;
static const uint16_t zero = 0;
+ uint32_t usable_space;
req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
if (req == NULL) {
if (tevent_req_nomem(state->mask, req)) {
return tevent_req_post(req, ev);
}
- state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
+ usable_space = cli_state_available_size(cli, 100);
+ state->num_asked = usable_space / DIR_STRUCT_SIZE;
SSVAL(state->vwv + 0, 0, state->num_asked);
SSVAL(state->vwv + 1, 0, state->attribute);
return tevent_req_post(req, ev);
}
bytes[0] = 4;
- bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), mask,
strlen(mask)+1, NULL);
bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
return tevent_req_post(req, ev);
}
- subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
- 0, 2, state->vwv, talloc_get_size(bytes), bytes);
+ subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0,
+ 2, state->vwv, talloc_get_size(bytes), bytes);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
dirlist_len = talloc_get_size(state->dirlist);
- tmp = TALLOC_REALLOC_ARRAY(
+ tmp = talloc_realloc(
state, state->dirlist, uint8_t,
dirlist_len + received * DIR_STRUCT_SIZE);
if (tevent_req_nomem(tmp, req)) {
return;
}
bytes[0] = 4;
- bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
+ bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "",
1, NULL);
bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
sizeof(state->search_status));
if (tevent_req_nomem(bytes, req)) {
return;
}
- subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
+ subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0,
2, state->vwv, talloc_get_size(bytes), bytes);
if (tevent_req_nomem(subreq, req)) {
return;
num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
- finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
+ finfo = talloc_array(mem_ctx, struct file_info, num_received);
if (finfo == NULL) {
return NT_STATUS_NO_MEMORY;
}
TALLOC_FREE(finfo);
return NT_STATUS_NO_MEMORY;
}
+
+ status = is_bad_finfo_name(state->cli, finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbXcli_conn_disconnect(state->cli->conn, status);
+ TALLOC_FREE(finfo);
+ return status;
+ }
}
*pfinfo = finfo;
return NT_STATUS_OK;
}
NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
- uint16 attribute,
+ uint16_t attribute,
NTSTATUS (*fn)(const char *, struct file_info *,
const char *, void *), void *state)
{
TALLOC_CTX *frame = talloc_stackframe();
- struct event_context *ev;
+ struct tevent_context *ev;
struct tevent_req *req;
NTSTATUS status = NT_STATUS_NO_MEMORY;
- struct file_info *finfo;
+ struct file_info *finfo = NULL;
size_t i, num_finfo;
- if (cli_has_async_calls(cli)) {
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
* Can't use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
- ev = event_context_init(frame);
+ ev = samba_tevent_context_init(frame);
if (ev == NULL) {
goto fail;
}
if (req == NULL) {
goto fail;
}
- if (!tevent_req_poll(req, ev)) {
- status = map_nt_error_from_unix(errno);
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
status = cli_list_old_recv(req, frame, &finfo);
{
struct tevent_req *req, *subreq;
struct cli_list_trans_state *state;
- size_t nlen, param_len;
- char *p;
+ size_t param_len;
+ uint16_t additional_flags2 = 0;
req = tevent_req_create(mem_ctx, &state,
struct cli_list_trans_state);
state->max_matches = 1366; /* Match W2k */
- state->setup[0] = TRANSACT2_FINDFIRST;
+ SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
- nlen = 2*(strlen(mask)+1);
- state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
+ state->param = talloc_array(state, uint8_t, 12);
if (tevent_req_nomem(state->param, req)) {
return tevent_req_post(req, ev);
}
SSVAL(state->param, 2, state->max_matches);
SSVAL(state->param, 4,
FLAG_TRANS2_FIND_REQUIRE_RESUME
- |FLAG_TRANS2_FIND_CLOSE_IF_END);
+ |FLAG_TRANS2_FIND_CLOSE_IF_END
+ |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
SSVAL(state->param, 6, state->info_level);
SIVAL(state->param, 8, 0);
- p = ((char *)state->param)+12;
- p += clistr_push(state->cli, p, state->mask, nlen,
- STR_TERMINATE);
- param_len = PTR_DIFF(p, state->param);
+ state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn),
+ state->mask, strlen(state->mask)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
+
+ param_len = talloc_get_size(state->param);
- subreq = cli_trans_send(state, state->ev, state->cli,
+ subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
SMBtrans2, NULL, -1, 0, 0,
state->setup, 1, 0,
state->param, param_len, 10,
- NULL, 0, cli->max_xmit);
+ NULL, 0, CLI_BUFFER_SIZE);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
int i;
DATA_BLOB last_name_raw;
struct file_info *finfo = NULL;
- size_t nlen, param_len;
+ size_t param_len;
+ uint16_t additional_flags2 = 0;
min_param = (state->first ? 6 : 4);
old_num_finfo = talloc_array_length(state->finfo);
- tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
+ tmp = talloc_realloc(state, state->finfo, struct file_info,
old_num_finfo + ff_searchcount);
if (tevent_req_nomem(tmp, req)) {
return;
ff_eos = true;
break;
}
+
+ status = is_bad_finfo_name(state->cli, finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbXcli_conn_disconnect(state->cli->conn, status);
+ tevent_req_nterror(req, status);
+ return;
+ }
+
if (!state->first && (state->mask[0] != '\0') &&
strcsequal(finfo->name, state->mask)) {
DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
/*
* Shrink state->finfo to the real length we received
*/
- tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
+ tmp = talloc_realloc(state, state->finfo, struct file_info,
old_num_finfo + i);
if (tevent_req_nomem(tmp, req)) {
return;
return;
}
- state->setup[0] = TRANSACT2_FINDNEXT;
-
- nlen = 2*(strlen(state->mask) + 1);
+ SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
- param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
- 12 + nlen + last_name_raw.length + 2);
+ param = talloc_realloc(state, state->param, uint8_t, 12);
if (tevent_req_nomem(param, req)) {
return;
}
* continue instead. JRA
*/
SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
- |FLAG_TRANS2_FIND_CLOSE_IF_END));
- p = ((char *)param)+12;
+ |FLAG_TRANS2_FIND_CLOSE_IF_END
+ |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
if (last_name_raw.length) {
- memcpy(p, last_name_raw.data, last_name_raw.length);
- p += last_name_raw.length;
+ state->param = trans2_bytes_push_bytes(state->param,
+ last_name_raw.data,
+ last_name_raw.length);
+ if (tevent_req_nomem(state->param, req)) {
+ return;
+ }
data_blob_free(&last_name_raw);
} else {
- p += clistr_push(state->cli, p, state->mask, nlen,
- STR_TERMINATE);
+ state->param = trans2_bytes_push_str(state->param,
+ smbXcli_conn_use_unicode(state->cli->conn),
+ state->mask,
+ strlen(state->mask)+1,
+ NULL);
+ if (tevent_req_nomem(state->param, req)) {
+ return;
+ }
}
+ param_len = talloc_get_size(state->param);
- param_len = PTR_DIFF(p, param);
+ if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) {
+ additional_flags2 = FLAGS2_REPARSE_PATH;
+ }
- subreq = cli_trans_send(state, state->ev, state->cli,
+ subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2,
SMBtrans2, NULL, -1, 0, 0,
state->setup, 1, 0,
state->param, param_len, 10,
- NULL, 0, state->cli->max_xmit);
+ NULL, 0, CLI_BUFFER_SIZE);
if (tevent_req_nomem(subreq, req)) {
return;
}
void *private_data)
{
TALLOC_CTX *frame = talloc_stackframe();
- struct event_context *ev;
+ struct tevent_context *ev;
struct tevent_req *req;
int i, num_finfo;
struct file_info *finfo = NULL;
NTSTATUS status = NT_STATUS_NO_MEMORY;
- if (cli_has_async_calls(cli)) {
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
* Can't use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
- ev = event_context_init(frame);
+ ev = samba_tevent_context_init(frame);
if (ev == NULL) {
goto fail;
}
return NULL;
}
- if (cli->protocol <= PROTOCOL_LANMAN1) {
+ if (smbXcli_conn_protocol(cli->conn) <= PROTOCOL_LANMAN1) {
subreq = cli_list_old_send(state, ev, cli, mask, attribute);
state->recv_fn = cli_list_old_recv;
} else {
return NT_STATUS_OK;
}
-NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
+NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16_t attribute,
NTSTATUS (*fn)(const char *, struct file_info *, const char *,
void *), void *state)
{
- TALLOC_CTX *frame = talloc_stackframe();
- struct event_context *ev;
+ TALLOC_CTX *frame = NULL;
+ struct tevent_context *ev;
struct tevent_req *req;
NTSTATUS status = NT_STATUS_NO_MEMORY;
struct file_info *finfo;
- size_t i, num_finfo;
+ size_t i, num_finfo = 0;
+ uint32_t caps;
uint16_t info_level;
- if (cli_has_async_calls(cli)) {
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_list(cli, mask, attribute, fn, state);
+ }
+
+ frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
* Can't use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER;
goto fail;
}
- ev = event_context_init(frame);
+ ev = samba_tevent_context_init(frame);
if (ev == NULL) {
goto fail;
}
- info_level = (cli->capabilities & CAP_NT_SMBS)
- ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
+ caps = smb1cli_conn_capabilities(cli->conn);
+
+ if (caps & CAP_UNIX) {
+ info_level = SMB_FIND_FILE_UNIX_INFO2;
+ } else if (caps & CAP_NT_SMBS) {
+ info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
+ } else {
+ info_level = SMB_FIND_INFO_STANDARD;
+ }
req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
if (req == NULL) {
goto fail;
}
- if (!tevent_req_poll(req, ev)) {
- status = map_nt_error_from_unix(errno);
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}