From 082bcc965fb5ab44aca32202071847f4e685a912 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 21 Oct 2017 00:08:08 +0000 Subject: [PATCH 1/3] s3: client: Add new utility function client_clean_name(). Correctly canonicalizes a remote pathname removing '..' elements before sending to a remote server. '..' elements work in SMB1 pathnames, but not in SMB2. Not yet used. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13093 Signed-off-by: Jeremy Allison Reviewed-by: Andreas Schneider (cherry picked from commit d4d9d1941bdac9993968c34cf928c645e4152fd3) --- source3/client/client.c | 31 +++++++++++++++++++++++++++++++ source3/client/client_proto.h | 1 + 2 files changed, 32 insertions(+) diff --git a/source3/client/client.c b/source3/client/client.c index 5ef9ad52151..dd008081df9 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -345,6 +345,37 @@ static void normalize_name(char *newdir) } } +/**************************************************************************** + Local name cleanup before sending to server. SMB1 allows relative pathnames, + but SMB2 does not, so we need to resolve them locally. +****************************************************************************/ + +char *client_clean_name(TALLOC_CTX *ctx, const char *name) +{ + char *newname = NULL; + if (name == NULL) { + return NULL; + } + + /* First ensure any path separators are correct. */ + newname = talloc_strdup(ctx, name); + if (newname == NULL) { + return NULL; + } + normalize_name(newname); + + /* Now remove any relative (..) path components. */ + if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + newname = unix_clean_name(ctx, newname); + } else { + newname = clean_name(ctx, newname); + } + if (newname == NULL) { + return NULL; + } + return newname; +} + /**************************************************************************** Change directory - inner section. ****************************************************************************/ diff --git a/source3/client/client_proto.h b/source3/client/client_proto.h index d3d40363f20..38f13aa0adc 100644 --- a/source3/client/client_proto.h +++ b/source3/client/client_proto.h @@ -35,6 +35,7 @@ enum { const char *client_get_cur_dir(void); const char *client_set_cur_dir(const char *newdir); +char *client_clean_name(TALLOC_CTX *ctx, const char *name); NTSTATUS do_list(const char *mask, uint16_t attribute, NTSTATUS (*fn)(struct cli_state *cli_state, struct file_info *, -- 2.15.0.rc0.271.g36b669edcc-goog From eaf9f015df5d2c3bacf8a1db8c1ad8a0e29c7506 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 20 Oct 2017 15:09:38 -0700 Subject: [PATCH 2/3] s3: smbclient: Ensure we call client_clean_name() before all operations on remote pathnames. This allows names containing .. components to be resolved on the client side before being sent to the server. Relative names work in SMB1 but not in SMB2. Fix both client.c and clitar.c BUG: https://bugzilla.samba.org/show_bug.cgi?id=13093 Signed-off-by: Jeremy Allison Reviewed-by: Andreas Schneider (cherry picked from commit f81c34c296f87127c6d1e4dd6ea74aa75660885d) --- source3/client/client.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++-- source3/client/clitar.c | 31 ++++++++ 2 files changed, 222 insertions(+), 8 deletions(-) diff --git a/source3/client/client.c b/source3/client/client.c index dd008081df9..b4a6c7d0389 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -430,7 +430,7 @@ static int do_cd(const char *new_dir) } client_set_cur_dir(new_cd); - new_cd = clean_name(ctx, new_cd); + new_cd = client_clean_name(ctx, new_cd); client_set_cur_dir(new_cd); status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), @@ -474,7 +474,7 @@ static int do_cd(const char *new_dir) client_set_cur_dir(saved_dir); goto out; } - targetpath = clean_name(ctx, targetpath); + targetpath = client_clean_name(ctx, targetpath); if (!targetpath) { client_set_cur_dir(saved_dir); goto out; @@ -984,6 +984,11 @@ static int cmd_dir(void) return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } + if (showacls) { /* cwd is only used if showacls is on */ client_set_cwd(client_get_cur_dir()); @@ -1036,6 +1041,14 @@ static int cmd_du(void) } else { mask = talloc_strdup(ctx, "*"); } + if (!mask) { + return 1; + } + + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } status = do_list(mask, attribute, do_du, recurse, true); if (!NT_STATUS_IS_OK(status)) { @@ -1232,7 +1245,7 @@ static int cmd_get(void) if (!rname) { return 1; } - rname = clean_name(ctx, rname); + rname = client_clean_name(ctx, rname); if (!rname) { return 1; } @@ -1298,6 +1311,10 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo, if (!rname) { return NT_STATUS_NO_MEMORY; } + rname = client_clean_name(ctx, rname); + if (rname == NULL) { + return NT_STATUS_NO_MEMORY; + } do_get(rname, finfo->name, false); TALLOC_FREE(rname); return NT_STATUS_OK; @@ -1317,6 +1334,10 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo, if (!new_cd) { return NT_STATUS_NO_MEMORY; } + new_cd = client_clean_name(ctx, new_cd); + if (new_cd == NULL) { + return NT_STATUS_NO_MEMORY; + } client_set_cur_dir(new_cd); string_replace(finfo->name,'\\','/'); @@ -1347,6 +1368,10 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo, return NT_STATUS_NO_MEMORY; } + mget_mask = client_clean_name(ctx, mget_mask); + if (mget_mask == NULL) { + return NT_STATUS_NO_MEMORY; + } status = do_list(mget_mask, (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN @@ -1416,7 +1441,7 @@ static int cmd_more(void) if (!rname) { return 1; } - rname = clean_name(ctx,rname); + rname = client_clean_name(ctx,rname); if (!rname) { return 1; } @@ -1474,6 +1499,10 @@ static int cmd_mget(void) if (!mget_mask) { return 1; } + mget_mask = client_clean_name(ctx, mget_mask); + if (mget_mask == NULL) { + return 1; + } status = do_list(mget_mask, attribute, do_mget, false, true); if (!NT_STATUS_IS_OK(status)) { return 1; @@ -1492,6 +1521,10 @@ static int cmd_mget(void) if (!mget_mask) { return 1; } + mget_mask = client_clean_name(ctx, mget_mask); + if (mget_mask == NULL) { + return 1; + } status = do_list(mget_mask, attribute, do_mget, false, true); if (!NT_STATUS_IS_OK(status)) { return 1; @@ -1588,6 +1621,10 @@ static int cmd_mkdir(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } if (recurse) { char *ddir = NULL; @@ -1659,6 +1696,10 @@ static int cmd_altname(void) if (!name) { return 1; } + name = client_clean_name(ctx, name); + if (name == NULL) { + return 1; + } do_altname(name); return 0; } @@ -1889,7 +1930,10 @@ static int cmd_allinfo(void) if (!name) { return 1; } - + name = client_clean_name(ctx, name); + if (name == NULL) { + return 1; + } do_allinfo(name); return 0; @@ -2052,7 +2096,7 @@ static int cmd_put(void) return 1; } - rname = clean_name(ctx, rname); + rname = client_clean_name(ctx, rname); if (!rname) { return 1; } @@ -2261,6 +2305,19 @@ static int cmd_mput(void) break; } normalize_name(rname); + { + char *tmp_rname = + client_clean_name(ctx, rname); + if (tmp_rname == NULL) { + break; + } + SAFE_FREE(rname); + rname = smb_xstrdup(tmp_rname); + TALLOC_FREE(tmp_rname); + if (rname == NULL) { + break; + } + } if (!NT_STATUS_IS_OK(cli_chkpath(cli, rname)) && !do_mkdir(rname)) { DEBUG (0, ("Unable to make dir, skipping...")); @@ -2291,6 +2348,18 @@ static int cmd_mput(void) normalize_name(rname); + { + char *tmp_rname = client_clean_name(ctx, rname); + if (tmp_rname == NULL) { + break; + } + SAFE_FREE(rname); + rname = smb_xstrdup(tmp_rname); + TALLOC_FREE(tmp_rname); + if (rname == NULL) { + break; + } + } do_put(rname, lname, false); } free_file_list(file_list); @@ -2461,6 +2530,10 @@ static int cmd_del(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } status = do_list(mask,attribute,do_del,false,false); if (!NT_STATUS_IS_OK(status)) { @@ -2577,6 +2650,10 @@ static int cmd_deltree(void) if (mask == NULL) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } deltree_list_head = NULL; @@ -2678,6 +2755,10 @@ static int cmd_wdel(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, mask, &targetcli, &targetname); @@ -2719,6 +2800,11 @@ static int cmd_open(void) return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } + status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, mask, &targetcli, &targetname); if (!NT_STATUS_IS_OK(status)) { @@ -2834,6 +2920,10 @@ static int cmd_posix_open(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) { d_printf("posix_open 0\n"); @@ -2889,6 +2979,10 @@ static int cmd_posix_mkdir(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) { d_printf("posix_mkdir 0\n"); @@ -2933,6 +3027,10 @@ static int cmd_posix_unlink(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, mask, &targetcli, &targetname); @@ -2972,6 +3070,10 @@ static int cmd_posix_rmdir(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, mask, &targetcli, &targetname); @@ -3274,6 +3376,10 @@ static int cmd_rmdir(void) if (!mask) { return 1; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, mask, &targetcli, &targetname); @@ -3318,6 +3424,10 @@ static int cmd_link(void) if (!oldname) { return 1; } + oldname = client_clean_name(ctx, oldname); + if (oldname == NULL) { + return 1; + } newname = talloc_asprintf(ctx, "%s%s", client_get_cur_dir(), @@ -3325,6 +3435,10 @@ static int cmd_link(void) if (!newname) { return 1; } + newname = client_clean_name(ctx, newname); + if (newname == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, oldname, &targetcli, &targetname); @@ -3372,6 +3486,10 @@ static int cmd_readlink(void) if (!name) { return 1; } + name = client_clean_name(ctx, name); + if (name == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, name, &targetcli, &targetname); @@ -3426,6 +3544,10 @@ static int cmd_symlink(void) if (!newname) { return 1; } + newname = client_clean_name(ctx, newname); + if (newname == NULL) { + return 1; + } /* New name must be present in share namespace. */ status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, newname, @@ -3477,6 +3599,10 @@ static int cmd_chmod(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } mode = (mode_t)strtol(buf, NULL, 8); @@ -3636,6 +3762,10 @@ static int cmd_getfacl(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetname); @@ -3804,6 +3934,10 @@ static int cmd_geteas(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetname); @@ -3861,6 +3995,10 @@ static int cmd_setea(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetname); @@ -3907,6 +4045,10 @@ static int cmd_stat(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetname); @@ -4016,6 +4158,10 @@ static int cmd_chown(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetname); if (!NT_STATUS_IS_OK(status)) { @@ -4066,6 +4212,10 @@ static int cmd_rename(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } dest = talloc_asprintf(ctx, "%s%s", @@ -4074,6 +4224,10 @@ static int cmd_rename(void) if (!dest) { return 1; } + dest = client_clean_name(ctx, dest); + if (dest == NULL) { + return 1; + } if (next_token_talloc(ctx, &cmd_ptr, &buf, NULL) && strcsequal(buf, "-f")) { @@ -4159,6 +4313,10 @@ static int cmd_scopy(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } dest = talloc_asprintf(ctx, "%s%s", @@ -4167,6 +4325,10 @@ static int cmd_scopy(void) if (!dest) { return 1; } + dest = client_clean_name(ctx, dest); + if (dest == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetsrc); @@ -4289,6 +4451,10 @@ static int cmd_hardlink(void) if (!src) { return 1; } + src = client_clean_name(ctx, src); + if (src == NULL) { + return 1; + } dest = talloc_asprintf(ctx, "%s%s", @@ -4297,6 +4463,10 @@ static int cmd_hardlink(void) if (!dest) { return 1; } + dest = client_clean_name(ctx, dest); + if (dest == NULL) { + return 1; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, src, &targetcli, &targetname); @@ -4376,6 +4546,10 @@ static int cmd_notify(void) if (name == NULL) { goto fail; } + name = client_clean_name(talloc_tos(), name); + if (name == NULL) { + return 1; + } status = cli_ntcreate( cli, name, 0, FILE_READ_DATA, 0, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, @@ -4550,7 +4724,7 @@ static int cmd_reget(void) if (!remote_name) { return 1; } - remote_name = clean_name(ctx,remote_name); + remote_name = client_clean_name(ctx,remote_name); if (!remote_name) { return 1; } @@ -4602,7 +4776,7 @@ static int cmd_reput(void) return 1; } - remote_name = clean_name(ctx, remote_name); + remote_name = client_clean_name(ctx, remote_name); if (!remote_name) { return 1; } @@ -5041,6 +5215,11 @@ int cmd_setmode(void) err = 1; goto out; } + fname = client_clean_name(ctx, fname); + if (fname == NULL) { + err = 1; + goto out; + } while (next_token_talloc(ctx, &cmd_ptr, &buf, NULL)) { const char *s = buf; @@ -5549,6 +5728,10 @@ static char **remote_completion(const char *text, int len) if (!dirmask) { goto cleanup; } + dirmask = client_clean_name(ctx, dirmask); + if (dirmask == NULL) { + goto cleanup; + } status = cli_resolve_path(ctx, "", popt_get_cmdline_auth_info(), cli, dirmask, &targetcli, &targetpath); diff --git a/source3/client/clitar.c b/source3/client/clitar.c index eff79698315..b8009c92cb6 100644 --- a/source3/client/clitar.c +++ b/source3/client/clitar.c @@ -699,6 +699,11 @@ static int tar_create(struct tar* t) err = 1; goto out_close; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + err = 1; + goto out_close; + } DBG(5, ("tar_process do_list with mask: %s\n", mask)); status = do_list(mask, TAR_DO_LIST_ATTR, get_file_callback, false, true); if (!NT_STATUS_IS_OK(status)) { @@ -764,6 +769,11 @@ static int tar_create_from_list(struct tar *t) err = 1; goto out; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + err = 1; + goto out; + } DBG(5, ("incl. path='%s', base='%s', mask='%s'\n", path, base ? base : "NULL", mask)); @@ -775,6 +785,12 @@ static int tar_create_from_list(struct tar *t) err = 1; goto out; } + base = client_clean_name(ctx, base); + if (base == NULL) { + err = 1; + goto out; + } + DBG(5, ("cd '%s' before do_list\n", base)); client_set_cur_dir(base); } @@ -820,6 +836,11 @@ static NTSTATUS get_file_callback(struct cli_state *cli, status = NT_STATUS_NO_MEMORY; goto out; } + remote_name = client_clean_name(ctx, remote_name); + if (remote_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } if (strequal(finfo->name, "..") || strequal(finfo->name, ".")) { goto out; @@ -853,6 +874,11 @@ static NTSTATUS get_file_callback(struct cli_state *cli, status = NT_STATUS_NO_MEMORY; goto out; } + mask = client_clean_name(ctx, mask); + if (mask == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } rc = tar_get_file(&tar_ctx, remote_name, finfo); if (rc != 0) { @@ -1112,6 +1138,11 @@ static int tar_send_file(struct tar *t, struct archive_entry *entry) err = 1; goto out; } + full_path = client_clean_name(ctx, full_path); + if (full_path == NULL) { + err = 1; + goto out; + } if (mode != AE_IFREG && mode != AE_IFDIR) { DBG(0, ("Skipping non-dir & non-regular file %s\n", full_path)); -- 2.15.0.rc0.271.g36b669edcc-goog From 705374e634bf03cf12ceefb313672268f73cd9f2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 23 Oct 2017 15:40:04 -0700 Subject: [PATCH 3/3] s3: smbclient: Test we can rename with a name containing. Samba always allowed this anyway, but it's a good place to ensure we don't regress. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13093 Signed-off-by: Jeremy Allison Reviewed-by: Andreas Schneider Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Tue Oct 24 23:32:58 CEST 2017 on sn-devel-144 (cherry picked from commit 7abe56ccfa4aba75c5e166a7bd0bb8141c3f258b) --- source3/script/tests/test_smbclient_s3.sh | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/source3/script/tests/test_smbclient_s3.sh b/source3/script/tests/test_smbclient_s3.sh index 5d05a1a587f..db598a35326 100755 --- a/source3/script/tests/test_smbclient_s3.sh +++ b/source3/script/tests/test_smbclient_s3.sh @@ -1414,6 +1414,50 @@ EOF fi } +# Test smbclient renames with pathnames containing '..' +test_rename_dotdot() +{ + tmpfile=$PREFIX/smbclient_interactive_prompt_commands + +cat > $tmpfile <