Fix smbclient mget crashes

- Upstream bug #14517
- rhbz#1892745, rhbz#1900232
This commit is contained in:
Alexander Bokovoy 2020-11-25 12:41:21 +02:00
parent 061477b3e7
commit 13eed773b0
2 changed files with 436 additions and 2 deletions

View File

@ -0,0 +1,430 @@
From 52ddfacead1ba50da0fc706b54e90e7a0cadb8e9 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Mon, 28 Sep 2020 14:11:13 +0200
Subject: [PATCH 1/4] smbclient: Remove the "abort_mget" variable
This was never set to true anywhere in the code
Bug: https://bugzilla.samba.org/show_bug.cgi?id=14517
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
(cherry picked from commit 8fa451d2b052223a11b24ffc2a956b80d03aaa7c)
---
source3/client/client.c | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/source3/client/client.c b/source3/client/client.c
index f65293849d0..5bed37fc2a2 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -87,8 +87,6 @@ static char dest_ss_str[INET6_ADDRSTRLEN];
#define SEPARATORS " \t\n\r"
-static bool abort_mget = true;
-
/* timing globals */
uint64_t get_total_size = 0;
unsigned int get_total_time_ms = 0;
@@ -1217,11 +1215,6 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
if (strequal(finfo->name,".") || strequal(finfo->name,".."))
return NT_STATUS_OK;
- if (abort_mget) {
- d_printf("mget aborted\n");
- return NT_STATUS_UNSUCCESSFUL;
- }
-
if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
if (asprintf(&quest,
"Get directory %s? ",finfo->name) < 0) {
@@ -1419,8 +1412,6 @@ static int cmd_mget(void)
attribute |= FILE_ATTRIBUTE_DIRECTORY;
}
- abort_mget = false;
-
while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
mget_mask = talloc_strdup(ctx, client_get_cur_dir());
--
2.20.1
From 159a03a9067f7aeddb29080dc34e37b567a02479 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Mon, 28 Sep 2020 14:21:24 +0200
Subject: [PATCH 2/4] smbclient: Slightly simplify do_mget()
Put the prompt query into a separate if-statement, move the "quest"
variable closer to its use
Bug: https://bugzilla.samba.org/show_bug.cgi?id=14517
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
(cherry picked from commit 71bc4d4b8d94458ac2e40d659f06110d434fd5c9)
---
source3/client/client.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/source3/client/client.c b/source3/client/client.c
index 5bed37fc2a2..5901419f427 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -1203,7 +1203,6 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
TALLOC_CTX *ctx = talloc_tos();
NTSTATUS status = NT_STATUS_OK;
char *rname = NULL;
- char *quest = NULL;
char *saved_curdir = NULL;
char *mget_mask = NULL;
char *new_cd = NULL;
@@ -1215,23 +1214,24 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
if (strequal(finfo->name,".") || strequal(finfo->name,".."))
return NT_STATUS_OK;
- if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
- if (asprintf(&quest,
- "Get directory %s? ",finfo->name) < 0) {
- return NT_STATUS_NO_MEMORY;
- }
- } else {
- if (asprintf(&quest,
- "Get file %s? ",finfo->name) < 0) {
+ if (prompt) {
+ const char *object = (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) ?
+ "directory" : "file";
+ char *quest = NULL;
+ bool ok;
+
+ quest = talloc_asprintf(
+ ctx, "Get %s %s? ", object, finfo->name);
+ if (quest == NULL) {
return NT_STATUS_NO_MEMORY;
}
- }
- if (prompt && !yesno(quest)) {
- SAFE_FREE(quest);
- return NT_STATUS_OK;
+ ok = yesno(quest);
+ TALLOC_FREE(quest);
+ if (!ok) {
+ return NT_STATUS_OK;
+ }
}
- SAFE_FREE(quest);
if (!(finfo->attr & FILE_ATTRIBUTE_DIRECTORY)) {
rname = talloc_asprintf(ctx,
--
2.20.1
From 523ccc98d2c6a9ddc0714084b5e19cee2a80bf27 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Mon, 28 Sep 2020 16:29:27 +0200
Subject: [PATCH 3/4] test3: Add a test showing that smbclient recursive mget
is broken
Bug: https://bugzilla.samba.org/show_bug.cgi?id=14517
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
(cherry picked from commit 254a5b034e5a081c9d3f28717a4b54d2af0180fc)
---
selftest/knownfail.d/smbclient_mget | 1 +
source3/script/tests/test_smbclient_mget.sh | 39 +++++++++++++++++++++
source3/selftest/tests.py | 10 ++++++
3 files changed, 50 insertions(+)
create mode 100644 selftest/knownfail.d/smbclient_mget
create mode 100755 source3/script/tests/test_smbclient_mget.sh
diff --git a/selftest/knownfail.d/smbclient_mget b/selftest/knownfail.d/smbclient_mget
new file mode 100644
index 00000000000..64407a8c5d4
--- /dev/null
+++ b/selftest/knownfail.d/smbclient_mget
@@ -0,0 +1 @@
+^samba3.blackbox.smbclient-mget.smbclient\ mget\(fileserver\)
\ No newline at end of file
diff --git a/source3/script/tests/test_smbclient_mget.sh b/source3/script/tests/test_smbclient_mget.sh
new file mode 100755
index 00000000000..45f62f15d4d
--- /dev/null
+++ b/source3/script/tests/test_smbclient_mget.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+if [ $# -lt 6 ]; then
+cat <<EOF
+Usage: $0 smbclient3 server share user password directory
+EOF
+exit 1;
+fi
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+failed=0
+
+SMBCLIENT3="$1"; shift
+SERVER="$1"; shift
+SHARE="$1"; shift
+USERNAME="$1"; shift
+PASSWORD="$1"; shift
+DIRECTORY="$1"; shift
+
+# Can't use "testit" here -- it somehow breaks the -c command passed
+# to smbclient into two, spoiling the "mget"
+
+name="smbclient mget"
+subunit_start_test "$name"
+output=$("$SMBCLIENT3" //"$SERVER"/"$SHARE" \
+ -U"$USERNAME"%"$PASSWORD" -c "recurse;prompt;mget $DIRECTORY")
+status=$?
+if [ x$status = x0 ]; then
+ subunit_pass_test "$name"
+else
+ echo "$output" | subunit_fail_test "$name"
+fi
+
+testit "rm foo" rm "$DIRECTORY"/foo || failed=`expr $failed + 1`
+testit "rmdir $DIRECTORY" rmdir "$DIRECTORY" || failed=`expr $failed + 1`
+
+testok $0 $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index d05de6bd08c..f9202f3f93a 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -1098,6 +1098,16 @@ for env in ["ad_member_idmap_rid:local", "maptoguest:local"]:
plantestsuite("samba3.blackbox.itime", "ad_dc", [os.path.join(samba3srcdir, "script/tests/test_itime.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', smbclient3, 'xattr'])
+plantestsuite("samba3.blackbox.smbclient-mget",
+ "fileserver",
+ [os.path.join(samba3srcdir, "script/tests/test_smbclient_mget.sh"),
+ smbclient3,
+ "$SERVER",
+ "tmp",
+ "$USERNAME",
+ "$PASSWORD",
+ "valid_users"])
+
t = "readdir-timestamp"
plantestsuite(
"samba3.smbtorture_s3.plain.%s" % t,
--
2.20.1
From 8cf00e6d64b098c8c21656e9f56d389758503dcd Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl@samba.org>
Date: Mon, 28 Sep 2020 15:03:41 +0200
Subject: [PATCH 4/4] smbclient: Fix recursive mget
Make do_mget rely on do_list() already doing the recursion in a
breadth-first manner. The previous code called do_list() from within
its callback. Unfortunately the recent simplifications of do_list()
broke this, leading to recursive mget to segfault. Instead of figuring
out how this worked before the simplifications in do_list() (I did
spend a few hours on this) and fixing it, I chose to restructure
do_mget() to not recursively call do_list() anymore but instead rely
on do_list() to do the recursion. Saves quite a few lines of code and
complexity.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=14517
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Wed Sep 30 17:23:45 UTC 2020 on sn-devel-184
(cherry picked from commit 9f24b5098f796f364a3f403ad4e9ae28b3c0935a)
---
selftest/knownfail.d/smbclient_mget | 1 -
source3/client/client.c | 121 ++++++++--------------------
2 files changed, 33 insertions(+), 89 deletions(-)
delete mode 100644 selftest/knownfail.d/smbclient_mget
diff --git a/selftest/knownfail.d/smbclient_mget b/selftest/knownfail.d/smbclient_mget
deleted file mode 100644
index 64407a8c5d4..00000000000
--- a/selftest/knownfail.d/smbclient_mget
+++ /dev/null
@@ -1 +0,0 @@
-^samba3.blackbox.smbclient-mget.smbclient\ mget\(fileserver\)
\ No newline at end of file
diff --git a/source3/client/client.c b/source3/client/client.c
index 5901419f427..8c7ceb644aa 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -1201,11 +1201,10 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
const char *dir)
{
TALLOC_CTX *ctx = talloc_tos();
- NTSTATUS status = NT_STATUS_OK;
- char *rname = NULL;
- char *saved_curdir = NULL;
- char *mget_mask = NULL;
- char *new_cd = NULL;
+ const char *client_cwd = NULL;
+ size_t client_cwd_len;
+ char *path = NULL;
+ char *local_path = NULL;
if (!finfo->name) {
return NT_STATUS_OK;
@@ -1214,6 +1213,10 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
if (strequal(finfo->name,".") || strequal(finfo->name,".."))
return NT_STATUS_OK;
+ if ((finfo->attr & FILE_ATTRIBUTE_DIRECTORY) && !recurse) {
+ return NT_STATUS_OK;
+ }
+
if (prompt) {
const char *object = (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) ?
"directory" : "file";
@@ -1233,98 +1236,40 @@ static NTSTATUS do_mget(struct cli_state *cli_state, struct file_info *finfo,
}
}
- if (!(finfo->attr & FILE_ATTRIBUTE_DIRECTORY)) {
- rname = talloc_asprintf(ctx,
- "%s%s",
- client_get_cur_dir(),
- finfo->name);
- 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;
- }
-
- /* handle directories */
- saved_curdir = talloc_strdup(ctx, client_get_cur_dir());
- if (!saved_curdir) {
+ path = talloc_asprintf(
+ ctx, "%s%c%s", dir, CLI_DIRSEP_CHAR, finfo->name);
+ if (path == NULL) {
return NT_STATUS_NO_MEMORY;
}
-
- new_cd = talloc_asprintf(ctx,
- "%s%s%s",
- client_get_cur_dir(),
- finfo->name,
- CLI_DIRSEP_STR);
- if (!new_cd) {
- return NT_STATUS_NO_MEMORY;
- }
- new_cd = client_clean_name(ctx, new_cd);
- if (new_cd == NULL) {
+ path = client_clean_name(ctx, path);
+ if (path == NULL) {
return NT_STATUS_NO_MEMORY;
}
- client_set_cur_dir(new_cd);
-
- string_replace(finfo->name,'\\','/');
- if (lowercase) {
- if (!strlower_m(finfo->name)) {
- return NT_STATUS_INVALID_PARAMETER;
- }
- }
-
- if (!directory_exist(finfo->name) &&
- mkdir(finfo->name,0777) != 0) {
- d_printf("failed to create directory %s\n",finfo->name);
- client_set_cur_dir(saved_curdir);
- return map_nt_error_from_unix(errno);
- }
-
- if (chdir(finfo->name) != 0) {
- d_printf("failed to chdir to directory %s\n",finfo->name);
- client_set_cur_dir(saved_curdir);
- return map_nt_error_from_unix(errno);
- }
- mget_mask = talloc_asprintf(ctx,
- "%s*",
- client_get_cur_dir());
+ /*
+ * Skip the path prefix if we've done a remote "cd" when
+ * creating the local path
+ */
+ client_cwd = client_get_cur_dir();
+ client_cwd_len = strlen(client_cwd);
- if (!mget_mask) {
+ local_path = talloc_strdup(ctx, path + client_cwd_len);
+ if (local_path == NULL) {
+ TALLOC_FREE(path);
return NT_STATUS_NO_MEMORY;
}
+ string_replace(local_path, CLI_DIRSEP_CHAR, '/');
- 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
- | FILE_ATTRIBUTE_DIRECTORY),
- do_mget, false, true);
- if (!NT_STATUS_IS_OK(status)
- && !NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
- /*
- * Ignore access denied errors to ensure all permitted files are
- * pulled down.
- */
- return status;
- }
+ if (finfo->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ int ret = mkdir(local_path, 0777);
- if (chdir("..") == -1) {
- d_printf("do_mget: failed to chdir to .. (error %s)\n",
- strerror(errno) );
- return map_nt_error_from_unix(errno);
+ if ((ret == -1) && (errno != EEXIST)) {
+ return map_nt_error_from_unix(errno);
+ }
+ } else {
+ do_get(path, local_path, false);
}
- client_set_cur_dir(saved_curdir);
- TALLOC_FREE(mget_mask);
- TALLOC_FREE(saved_curdir);
- TALLOC_FREE(new_cd);
+
return NT_STATUS_OK;
}
@@ -1431,7 +1376,7 @@ static int cmd_mget(void)
if (mget_mask == NULL) {
return 1;
}
- status = do_list(mget_mask, attribute, do_mget, false, true);
+ status = do_list(mget_mask, attribute, do_mget, recurse, true);
if (!NT_STATUS_IS_OK(status)) {
return 1;
}
@@ -1453,7 +1398,7 @@ static int cmd_mget(void)
if (mget_mask == NULL) {
return 1;
}
- status = do_list(mget_mask, attribute, do_mget, false, true);
+ status = do_list(mget_mask, attribute, do_mget, recurse, true);
if (!NT_STATUS_IS_OK(status)) {
return 1;
}
--
2.20.1

View File

@ -108,7 +108,7 @@
%define samba_requires_eq() %(LC_ALL="C" echo '%*' | xargs -r rpm -q --qf 'Requires: %%{name} = %%{epoch}:%%{version}\\n' | sed -e 's/ (none):/ /' -e 's/ 0:/ /' | grep -v "is not")
%global main_release 1
%global main_release 2
%global samba_version 4.13.2
%global talloc_version 2.3.1
@ -177,7 +177,8 @@ Source14: samba.pamd
Source201: README.downgrade
Patch1: samba-s4u.patch
Patch2: samba-gc-lookup_unix_user_name-allow-lookup-for-own-realm.patch
Patch2: samba-4.13-redhat.patch
Patch3: samba-smbclient-mget-bug-14517.patch
Requires(pre): /usr/sbin/groupadd
Requires(post): systemd
@ -3786,6 +3787,9 @@ fi
%endif
%changelog
* Wed Nov 25 2020 Alexander Bokovoy <abokovoy@redhat.com> - 4.13.2-2
- rhbz#1892745, rhbz#1900232: smbclient mget crashes (upstream bug 14517)
* Tue Nov 03 2020 Andreas Schneider <asn@redhat.com> - 4.13.2-1
- Create a python3-samba-devel package to avoid unnessary dependencies