The branch, v4-18-stable has been updated via 273696dc5d4 VERSION: Disable GIT_SNAPSHOT for the 4.18.4 release. via bcdc186bb67 WHATSNEW: Add release notes for Samba 4.18.4. via d4b86186d29 selftest:Samba3: use the correct NSS_WRAPPER_HOSTNAME via d35c773e969 pidl: avoid py compile issues with --pidl-developer via 29c08b5891d s3:utils: smbget fix a memory leak via 694829f1872 smbd: Don't mask open error if fstatat() fails via 5d040beed2c tests: Show smbd returns wrong error code when creating on r/o fs via 14cbe1b8671 error_inject: Enable returning EROFS for O_CREAT via 3350e196a7e error_inject: map EROFS via ccb235c2f4e vfs_gpfs: Register smbd process with GPFS via 2492a18d762 gpfswrap: Add wrapper for gpfs_register_cifs_export via fe17ff2cfc4 s3:winbind: Fix talloc parent in find_dc() leading to a segfault via a6edfaa4985 python:safe_tarfile: Improve safe extract() via b7cad429a52 python:safe_tarfile: Implement safer extractall() via eff4e88d2cc python:safe_tarfile: Set extraction_filter for pythons providing it via 4a79ee44c31 python:tests: Adopt safe_tarfile for extraction_filter raises via d2c86925f62 s3/utils: avoid erronous NO MEMORY detection via c7e3c042fbc smbcacls/smbcquotas: check for valid UNC path via c4968128b7f smbclient: Fix fd leak with "showacls;ls" via d49f9f4be7d smbd: remove comments about deprecated 'write cache size' via 3662ddaadc9 libsmb: Fix directory listing against old servers via ad3f78b4ab6 tests: Show that we 100% loop in cli_list_old_recv() via 6f0d17e1210 tests: Make timelimit available to test scripts via af4d536ad20 s4:dnsserver: Rename dns_name_equal() to samba_dns_name_equal() via fcf2e89167f VERSION: Bump version up to Samba 4.18.4... from 897e67a7cf8 VERSION: Disable GIT_SNAPSHOT for the 4.18.3 release.
https://git.samba.org/?p=samba.git;a=shortlog;h=v4-18-stable - Log ----------------------------------------------------------------- ----------------------------------------------------------------------- Summary of changes: VERSION | 2 +- WHATSNEW.txt | 73 ++++++++++++++++++++++++++++- docs-xml/smbdotconf/locking/smb2leases.xml | 2 - docs-xml/smbdotconf/tuning/aioreadsize.xml | 2 +- docs-xml/smbdotconf/tuning/aiowritesize.xml | 2 +- lib/util/gpfswrap.c | 12 +++++ lib/util/gpfswrap.h | 1 + pidl/lib/Parse/Pidl/Samba4/Python.pm | 8 ++-- python/samba/safe_tarfile.py | 73 ++++++++++++++++++++++++----- python/samba/tests/safe_tarfile.py | 27 ++++++++--- selftest/selftesthelpers.py | 1 + selftest/target/Samba3.pm | 4 +- source3/client/client.c | 1 + source3/libsmb/clilist.c | 6 +++ source3/modules/vfs_error_inject.c | 20 ++++++++ source3/modules/vfs_gpfs.c | 6 +++ source3/script/tests/test_old_dirlisting.sh | 28 +++++++++++ source3/script/tests/test_rofs.sh | 34 ++++++++++++++ source3/selftest/tests.py | 13 +++++ source3/smbd/open.c | 9 ++++ source3/smbd/smb2_read.c | 1 - source3/utils/net_ads.c | 10 ++-- source3/utils/smbcacls.c | 5 ++ source3/utils/smbcquotas.c | 5 ++ source3/utils/smbget.c | 1 + source3/winbindd/winbindd_cm.c | 2 +- source4/dns_server/dns_crypto.c | 2 +- source4/dns_server/dns_update.c | 4 +- source4/dns_server/dnsserver_common.c | 21 +++++---- source4/dns_server/dnsserver_common.h | 2 +- source4/rpc_server/dnsserver/dnsutils.c | 2 +- source4/torture/dns/dlz_bind9.c | 8 ++-- 32 files changed, 334 insertions(+), 53 deletions(-) create mode 100755 source3/script/tests/test_old_dirlisting.sh create mode 100755 source3/script/tests/test_rofs.sh Changeset truncated at 500 lines: diff --git a/VERSION b/VERSION index 93ebe2430aa..35e338ed4d5 100644 --- a/VERSION +++ b/VERSION @@ -25,7 +25,7 @@ ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=18 -SAMBA_VERSION_RELEASE=3 +SAMBA_VERSION_RELEASE=4 ######################################################## # If a official release has a serious bug # diff --git a/WHATSNEW.txt b/WHATSNEW.txt index 01c121a8e48..c5dbc985f28 100644 --- a/WHATSNEW.txt +++ b/WHATSNEW.txt @@ -1,3 +1,73 @@ + ============================== + Release Notes for Samba 4.18.4 + July 05, 2023 + ============================== + + +This is the latest stable release of the Samba 4.18 release series. + + +Changes since 4.18.3 +-------------------- + +o Douglas Bagnall <douglas.bagn...@catalyst.net.nz> + * BUG 15404: Backport --pidl-developer fixes. + +o Samuel Cabrero <scabr...@samba.org> + * BUG 14030: Named crashes on DLZ zone update. + +o Björn Jacke <b...@sernet.de> + * BUG 2312: smbcacls and smbcquotas do not check // before the server. + +o Volker Lendecke <v...@samba.org> + * BUG 15382: cli_list loops 100% CPU against pre-lanman2 servers. + * BUG 15391: smbclient leaks fds with showacls. + * BUG 15402: smbd returns NOT_FOUND when creating files on a r/o filesystem. + +o Stefan Metzmacher <me...@samba.org> + * BUG 15355: NSS_WRAPPER_HOSTNAME doesn't match NSS_WRAPPER_HOSTS entry and + causes test timeouts. + +o Noel Power <noel.po...@suse.com> + * BUG 15384: net ads lookup (with unspecified realm) fails. + +o Christof Schmitt <c...@samba.org> + * BUG 15381: Register Samba processes with GPFS. + +o Andreas Schneider <a...@samba.org> + * BUG 15390: Python tarfile extraction needs change to avoid a warning + (CVE-2007-4559 mitigation). + * BUG 15398: The winbind child segfaults when listing users with `winbind + scan trusted domains = yes`. + +o Jones Syue <joness...@qnap.com> + * BUG 15383: Remove comments about deprecated 'write cache size'. + * BUG 15403: smbget memory leak if failed to download files recursively. + + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical:matrix.org matrix room, or +#samba-technical IRC channel on irc.libera.chat. + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. All bug reports should +be filed under the Samba 4.1 and newer product in the project's Bugzilla +database (https://bugzilla.samba.org/). + + +====================================================================== +== Our Code, Our Bugs, Our Responsibility. +== The Samba Team +====================================================================== + + +Release notes for older releases follow: +---------------------------------------- ============================== Release Notes for Samba 4.18.3 May 31, 2023 @@ -58,8 +128,7 @@ database (https://bugzilla.samba.org/). ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- ============================== Release Notes for Samba 4.18.2 April 19, 2023 diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml index 5a490875af7..89ff307d8f9 100644 --- a/docs-xml/smbdotconf/locking/smb2leases.xml +++ b/docs-xml/smbdotconf/locking/smb2leases.xml @@ -15,8 +15,6 @@ and <smbconfoption name="kernel oplocks">no</smbconfoption>. </para> - <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para> - </description> <related>oplocks</related> diff --git a/docs-xml/smbdotconf/tuning/aioreadsize.xml b/docs-xml/smbdotconf/tuning/aioreadsize.xml index 71120a80388..5218f34a6d6 100644 --- a/docs-xml/smbdotconf/tuning/aioreadsize.xml +++ b/docs-xml/smbdotconf/tuning/aioreadsize.xml @@ -6,7 +6,7 @@ <para>If this integer parameter is set to a non-zero value, Samba will read from files asynchronously when the request size is bigger than this value. Note that it happens only for non-chained and non-chaining - reads and when not using write cache.</para> + reads.</para> <para>The only reasonable values for this parameter are 0 (no async I/O) and 1 (always do async I/O).</para> <related>aio write size</related> diff --git a/docs-xml/smbdotconf/tuning/aiowritesize.xml b/docs-xml/smbdotconf/tuning/aiowritesize.xml index cdc079d13dc..029e1d135c0 100644 --- a/docs-xml/smbdotconf/tuning/aiowritesize.xml +++ b/docs-xml/smbdotconf/tuning/aiowritesize.xml @@ -6,7 +6,7 @@ <para>If this integer parameter is set to a non-zero value, Samba will write to files asynchronously when the request size is bigger than this value. Note that it happens only for non-chained and non-chaining - reads and when not using write cache.</para> + writes.</para> <para>The only reasonable values for this parameter are 0 (no async I/O) and 1 (always do async I/O).</para> <para>Compared to <smbconfoption name="aio read size"/> this parameter has diff --git a/lib/util/gpfswrap.c b/lib/util/gpfswrap.c index d05358e141e..2f15bf452cf 100644 --- a/lib/util/gpfswrap.c +++ b/lib/util/gpfswrap.c @@ -28,6 +28,7 @@ static int (*gpfs_putacl_fn)(const char *pathname, int flags, void *acl); static int (*gpfs_get_realfilename_path_fn)(const char *pathname, char *filenamep, int *len); +static int (*gpfs_register_cifs_export_fn)(void); static int (*gpfs_set_winattrs_path_fn)(const char *pathname, int flags, struct gpfs_winattr *attrs); @@ -71,6 +72,7 @@ int gpfswrap_init(void) gpfs_fgetacl_fn = dlsym(l, "gpfs_getacl_fd"); gpfs_putacl_fn = dlsym(l, "gpfs_putacl"); gpfs_get_realfilename_path_fn = dlsym(l, "gpfs_get_realfilename_path"); + gpfs_register_cifs_export_fn = dlsym(l, "gpfs_register_cifs_export"); gpfs_set_winattrs_path_fn = dlsym(l, "gpfs_set_winattrs_path"); gpfs_set_winattrs_fn = dlsym(l, "gpfs_set_winattrs"); gpfs_get_winattrs_fn = dlsym(l, "gpfs_get_winattrs"); @@ -141,6 +143,16 @@ int gpfswrap_get_realfilename_path(const char *pathname, return gpfs_get_realfilename_path_fn(pathname, filenamep, len); } +int gpfswrap_register_cifs_export(void) +{ + if (gpfs_register_cifs_export_fn == NULL) { + errno = ENOSYS; + return -1; + } + + return gpfs_register_cifs_export_fn(); +} + int gpfswrap_set_winattrs_path(const char *pathname, int flags, struct gpfs_winattr *attrs) diff --git a/lib/util/gpfswrap.h b/lib/util/gpfswrap.h index 1e74496c060..e387a56446b 100644 --- a/lib/util/gpfswrap.h +++ b/lib/util/gpfswrap.h @@ -34,6 +34,7 @@ int gpfswrap_putacl(const char *pathname, int flags, void *acl); int gpfswrap_get_realfilename_path(const char *pathname, char *filenamep, int *len); +int gpfswrap_register_cifs_export(void); int gpfswrap_set_winattrs_path(const char *pathname, int flags, struct gpfs_winattr *attrs); diff --git a/pidl/lib/Parse/Pidl/Samba4/Python.pm b/pidl/lib/Parse/Pidl/Samba4/Python.pm index 5c4d3e8e087..03a901cc24d 100644 --- a/pidl/lib/Parse/Pidl/Samba4/Python.pm +++ b/pidl/lib/Parse/Pidl/Samba4/Python.pm @@ -1747,7 +1747,7 @@ sub ConvertObjectFromPythonData($$$$$$;$$) $self->pidl("}"); $self->pidl("if (test_var > uint_max) {"); $self->indent; - $self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s within range 0 - %llu, got %llu\",\\"); + $self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s within range 0 - %llu, got %llu\","); $self->pidl(" PyLong_Type.tp_name, uint_max, test_var);"); $self->pidl($fail); $self->deindent; @@ -1756,7 +1756,7 @@ sub ConvertObjectFromPythonData($$$$$$;$$) $self->deindent; $self->pidl("} else {"); $self->indent; - $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s\",\\"); + $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s\","); $self->pidl(" PyLong_Type.tp_name);"); $self->pidl($fail); $self->deindent; @@ -1786,7 +1786,7 @@ sub ConvertObjectFromPythonData($$$$$$;$$) $self->pidl("}"); $self->pidl("if (test_var < int_min || test_var > int_max) {"); $self->indent; - $self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s within range %lld - %lld, got %lld\",\\"); + $self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s within range %lld - %lld, got %lld\","); $self->pidl(" PyLong_Type.tp_name, int_min, int_max, test_var);"); $self->pidl($fail); $self->deindent; @@ -1795,7 +1795,7 @@ sub ConvertObjectFromPythonData($$$$$$;$$) $self->deindent; $self->pidl("} else {"); $self->indent; - $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s\",\\"); + $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s\","); $self->pidl(" PyLong_Type.tp_name);"); $self->pidl($fail); $self->deindent; diff --git a/python/samba/safe_tarfile.py b/python/samba/safe_tarfile.py index cc19770d73f..7a2b0382a79 100644 --- a/python/samba/safe_tarfile.py +++ b/python/samba/safe_tarfile.py @@ -15,6 +15,9 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. +import os +import tarfile +from pathlib import Path from tarfile import ExtractError, TarInfo, TarFile as UnsafeTarFile @@ -24,20 +27,68 @@ class TarFile(UnsafeTarFile): using '../../'. """ - def extract(self, member, path="", set_attrs=True, *, numeric_owner=False): - if isinstance(member, TarInfo): - name = member.name - else: - name = member + try: + # New in version 3.11.4 (also has been backported) + # https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extraction_filter + # https://peps.python.org/pep-0706/ + extraction_filter = staticmethod(tarfile.data_filter) + except AttributeError: + def extract(self, member, path="", set_attrs=True, *, + numeric_owner=False): + self._safetarfile_check() + super().extract(member, path, set_attrs=set_attrs, + numeric_owner=numeric_owner) - if '../' in name: - raise ExtractError(f"'../' is not allowed in path '{name}'") + def extractall(self, path, members=None, *, numeric_owner=False): + self._safetarfile_check() + super().extractall(path, members, + numeric_owner=numeric_owner) - if name.startswith('/'): - raise ExtractError(f"path '{name}' should not start with '/'") + def _safetarfile_check(self): + for tarinfo in self.__iter__(): + if self._is_traversal_attempt(tarinfo=tarinfo): + raise ExtractError( + "Attempted directory traversal for " + f"member: {tarinfo.name}") + if self._is_unsafe_symlink(tarinfo=tarinfo): + raise ExtractError( + "Attempted directory traversal via symlink for " + f"member: {tarinfo.linkname}") + if self._is_unsafe_link(tarinfo=tarinfo): + raise ExtractError( + "Attempted directory traversal via link for " + f"member: {tarinfo.linkname}") - super().extract(member, path, set_attrs=set_attrs, - numeric_owner=numeric_owner) + def _resolve_path(self, path): + return os.path.realpath(os.path.abspath(path)) + + def _is_path_in_dir(self, path, basedir): + return self._resolve_path(os.path.join(basedir, + path)).startswith(basedir) + + def _is_traversal_attempt(self, tarinfo): + if (tarinfo.name.startswith(os.sep) + or ".." + os.sep in tarinfo.name): + return True + return False + + def _is_unsafe_symlink(self, tarinfo): + if tarinfo.issym(): + symlink_file = Path( + os.path.normpath(os.path.join(os.getcwd(), + tarinfo.linkname))) + if not self._is_path_in_dir(symlink_file, os.getcwd()): + return True + return False + + def _is_unsafe_link(self, tarinfo): + if tarinfo.islnk(): + link_file = Path( + os.path.normpath(os.path.join(os.getcwd(), + tarinfo.linkname))) + if not self._is_path_in_dir(link_file, os.getcwd()): + return True + return False open = TarFile.open diff --git a/python/samba/tests/safe_tarfile.py b/python/samba/tests/safe_tarfile.py index 40aa9e17d4a..cf9b725afcb 100644 --- a/python/samba/tests/safe_tarfile.py +++ b/python/samba/tests/safe_tarfile.py @@ -43,9 +43,16 @@ class SafeTarFileTestCase(TestCaseInTempDir): stf = safe_tarfile.open(tarname) - self.assertRaises(tarfile.ExtractError, - stf.extractall, - tarname) + # We we have data_filter, we have a patched python to address + # CVE-2007-4559. + if hasattr(tarfile, "data_filter"): + self.assertRaises(tarfile.OutsideDestinationError, + stf.extractall, + tarname) + else: + self.assertRaises(tarfile.ExtractError, + stf.extractall, + tarname) self.rm_files('x', 'tar.tar') def test_slash(self): @@ -60,8 +67,16 @@ class SafeTarFileTestCase(TestCaseInTempDir): tf.close() stf = safe_tarfile.open(tarname) - self.assertRaises(tarfile.ExtractError, - stf.extractall, - tarname) + + # We we have data_filter, we have a patched python to address + # CVE-2007-4559. + if hasattr(tarfile, "data_filter"): + self.assertRaises(NotADirectoryError, + stf.extractall, + tarname) + else: + self.assertRaises(tarfile.ExtractError, + stf.extractall, + tarname) self.rm_files('x', 'tar.tar') diff --git a/selftest/selftesthelpers.py b/selftest/selftesthelpers.py index 0320008faf9..1af8f5f837c 100644 --- a/selftest/selftesthelpers.py +++ b/selftest/selftesthelpers.py @@ -226,3 +226,4 @@ rpcclient = binpath('rpcclient') smbcacls = binpath('smbcacls') smbcontrol = binpath('smbcontrol') smbstatus = binpath('smbstatus') +timelimit = binpath('timelimit') diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 0556efd4741..ba3268f147c 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -3678,7 +3678,7 @@ jacknomappergroup:x:$gid_jacknomapper:jacknomapper $createuser_env{NSS_WRAPPER_PASSWD} = $nss_wrapper_passwd; $createuser_env{NSS_WRAPPER_GROUP} = $nss_wrapper_group; $createuser_env{NSS_WRAPPER_HOSTS} = $nss_wrapper_hosts; - $createuser_env{NSS_WRAPPER_HOSTNAME} = "${hostname}.samba.example.com"; + $createuser_env{NSS_WRAPPER_HOSTNAME} = "${hostname}.${dns_domain}"; if ($ENV{SAMBA_DNS_FAKING}) { $createuser_env{RESOLV_WRAPPER_HOSTS} = $dns_host_file; } else { @@ -3732,7 +3732,7 @@ jacknomappergroup:x:$gid_jacknomapper:jacknomapper $ret{NSS_WRAPPER_PASSWD} = $nss_wrapper_passwd; $ret{NSS_WRAPPER_GROUP} = $nss_wrapper_group; $ret{NSS_WRAPPER_HOSTS} = $nss_wrapper_hosts; - $ret{NSS_WRAPPER_HOSTNAME} = "${hostname}.samba.example.com"; + $ret{NSS_WRAPPER_HOSTNAME} = "${hostname}.${dns_domain}"; $ret{NSS_WRAPPER_MODULE_SO_PATH} = Samba::nss_wrapper_winbind_so_path($self); $ret{NSS_WRAPPER_MODULE_FN_PREFIX} = "winbind"; if ($ENV{SAMBA_DNS_FAKING}) { diff --git a/source3/client/client.c b/source3/client/client.c index 8e29224918d..c4d309958cd 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -626,6 +626,7 @@ static NTSTATUS display_finfo(struct cli_state *cli_state, struct file_info *fin display_sec_desc(sd); } TALLOC_FREE(sd); + cli_close(targetcli, fnum); } TALLOC_FREE(afname); } diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c index 903c5d22777..58a8fe1a0c6 100644 --- a/source3/libsmb/clilist.c +++ b/source3/libsmb/clilist.c @@ -544,6 +544,11 @@ static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, return status; } + if (state->dirlist == NULL) { + *pfinfo = NULL; + return NT_STATUS_OK; + } + num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE; finfo = talloc_array(mem_ctx, struct file_info, num_received); @@ -570,6 +575,7 @@ static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, return status; } } + TALLOC_FREE(state->dirlist); *pfinfo = finfo; return NT_STATUS_OK; } diff --git a/source3/modules/vfs_error_inject.c b/source3/modules/vfs_error_inject.c index 1a327097b30..edb7c64a92a 100644 --- a/source3/modules/vfs_error_inject.c +++ b/source3/modules/vfs_error_inject.c @@ -31,6 +31,7 @@ struct unix_error_map { { "EBADF", EBADF }, { "EINTR", EINTR }, { "EACCES", EACCES }, + { "EROFS", EROFS }, }; static int find_unix_error_from_string(const char *err_str) @@ -115,6 +116,7 @@ static int vfs_error_inject_openat(struct vfs_handle_struct *handle, const struct vfs_open_how *how) { int error = inject_unix_error("openat", handle); + int create_error = inject_unix_error("openat_create", handle); int dirfsp_flags = (O_NOFOLLOW|O_DIRECTORY); bool return_error; @@ -126,6 +128,24 @@ static int vfs_error_inject_openat(struct vfs_handle_struct *handle, #endif #endif + if ((create_error != 0) && (how->flags & O_CREAT)) { + struct stat_ex st = { + .st_ex_nlink = 0, + }; + int ret; + + ret = SMB_VFS_FSTATAT(handle->conn, + dirfsp, + smb_fname, + &st, + AT_SYMLINK_NOFOLLOW); + + if ((ret == -1) && (errno == ENOENT)) { + errno = create_error; + return -1; + } + } + return_error = (error != 0); return_error &= !fsp->fsp_flags.is_pathref; return_error &= ((how->flags & dirfsp_flags) != dirfsp_flags); diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c index 969e7744fce..93748eab54c 100644 --- a/source3/modules/vfs_gpfs.c +++ b/source3/modules/vfs_gpfs.c @@ -2043,6 +2043,12 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle, gpfswrap_lib_init(0); + ret = gpfswrap_register_cifs_export(); + if (ret < 0) { + DBG_ERR("Failed to register with GPFS: %s\n", strerror(errno)); + return ret; + } + config = talloc_zero(handle->conn, struct gpfs_config_data); if (!config) { DEBUG(0, ("talloc_zero() failed\n")); diff --git a/source3/script/tests/test_old_dirlisting.sh b/source3/script/tests/test_old_dirlisting.sh new file mode 100755 index 00000000000..f50a4742b1a --- /dev/null -- Samba Shared Repository