[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Tue Oct 4 03:49:01 UTC 2022


The branch, master has been updated
       via  37406b9d97f CVE-2007-4559 python: ensure sanity in our tarfiles
       via  6a5d03e2f7b samba-tool: Use authentication file to pass credentials
       via  bff2bc9c7d6 python-drs: Add client-side debug and fallback for GET_ANC
       via  483c48f52d6 s4-libnet: Add messages to object count mismatch failures
       via  b0bbc94d412 selftest: Enable "old Samba" mode regarding GET_ANC/GET_TGT
       via  314bc44fa9b s4-rpc_server:getncchanges Add "old Samba" mode regarding GET_ANC/GET_TGT
       via  7ff743d65dc selftest: Add tests for GetNCChanges GET_ANC using samba-tool drs clone-dc-database
       via  62b426243f4 selftest: Prepare for "old Samba" mode regarding getncchanges GET_ANC/GET_TGT
      from  a91fa70ad56 tevent: Fix flag clearing

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 37406b9d97f123576c811b9fe22b39b02af62f83
Author: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
Date:   Fri Sep 23 12:32:25 2022 +1200

    CVE-2007-4559 python: ensure sanity in our tarfiles
    
    Python's tarfile module is not very careful about paths that step out
    of the target directory. We can be a bit better at little cost.
    
    This was reported in 2007[1], and has recently been publicised [2, for
    example].
    
    We were informed of this bug in December 2021 by Luis Alberto López
    Alvar, but decided then that there were no circumstances under which
    this was a security concern. That is, if you can alter the backup
    files, you can already do worse things. But there is a case to guard
    against an administrator being tricked into trying to restore a file
    that isn't based on a real backup.
    
    [1] https://nvd.nist.gov/vuln/detail/CVE-2007-4559
    [2] https://www.theregister.com/2022/09/22/python_vulnerability_tarfile/
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15185
    
    Signed-off-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Tue Oct  4 03:48:43 UTC 2022 on sn-devel-184

commit 6a5d03e2f7bfa84eea1f1c44604ab70b1257d349
Author: Nikola Radovanovic <nikoladsp at gmail.com>
Date:   Fri Sep 30 09:38:12 2022 +0200

    samba-tool: Use authentication file to pass credentials
    
    In order not to pass credentials in clear-text directly over command line, this is a patch to store username/password/domain in a file and use it during domain join for example.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15031
    
    Signed-off-by: Nikola Radovanovic <radovanovic.extern at univention.de>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>

commit bff2bc9c7d69ec2fbe9339c2353a0a846182f1ea
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Sep 15 17:10:24 2022 +1200

    python-drs: Add client-side debug and fallback for GET_ANC
    
    Samba 4.5 and earlier will fail to do GET_ANC correctly and will not
    replicate non-critical parents of objects with isCriticalSystemObject=TRUE
    when DRSUAPI_DRS_CRITICAL_ONLY is set.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 483c48f52d6ff5e8149ed12bfeb2b6608c946f01
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Tue Sep 20 13:37:30 2022 +1200

    s4-libnet: Add messages to object count mismatch failures
    
    This helps explain these better than WERR_GEN_FAILURE.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit b0bbc94d4124d63b1d5a35ccbc88ffd51d520ba0
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Sep 29 14:54:14 2022 +1300

    selftest: Enable "old Samba" mode regarding GET_ANC/GET_TGT
    
    The chgdcpass server now emulates older verions of Samba that
    fail to implement DRSUAPI_DRS_GET_ANC correctly and totally fails to support
    DRSUAPI_DRS_GET_TGT.
    
    We now show this is in effect by the fact that tests now fail.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 314bc44fa9b8fc99c80bfcfff71f2cec67bbda36
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Sep 29 14:53:38 2022 +1300

    s4-rpc_server:getncchanges Add "old Samba" mode regarding GET_ANC/GET_TGT
    
    This emulates older verions of Samba that fail to implement
    DRSUAPI_DRS_GET_ANC correctly and totally fails to support
    DRSUAPI_DRS_GET_TGT.
    
    This will allow testing of a client-side fallback, allowing migration
    from sites that run very old Samba versions over DRSUAPI (currently
    the only option is to attempt an in-place upgrade).
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 7ff743d65dcf27ffe0c6861720e8ce531bfa378d
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Sep 29 03:05:03 2022 +0000

    selftest: Add tests for GetNCChanges GET_ANC using samba-tool drs clone-dc-database
    
    This test, compared with the direct to RPC tests, will succeed, then fail once the
    server is changed to emulate Samba 4.5 and and again succeed once the python code
    changes to allow skipping the DRSUAPI_DRS_CRITICAL_ONLY step
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

commit 62b426243f4eaa4978c249b6e6ce90d35aeaefe4
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Sep 15 09:36:45 2022 +1200

    selftest: Prepare for "old Samba" mode regarding getncchanges GET_ANC/GET_TGT
    
    The chgdcpass environment will emulate older verions of Samba
    that fail to implement DRSUAPI_DRS_GET_ANC correctly and
    totally fails to support DRSUAPI_DRS_GET_TGT.
    
    This will allow testing of a client-side fallback, allowing migration
    from sites that run very old Samba versions over DRSUAPI (currently
    the only option is to attempt an in-place upgrade).
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15189
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Douglas Bagnall <douglas.bagnall at catalyst.net.nz>

-----------------------------------------------------------------------

Summary of changes:
 docs-xml/manpages/samba-tool.8.xml                 |  2 +
 python/samba/drs_utils.py                          | 47 +++++++++-
 python/samba/getopt.py                             | 10 +++
 python/samba/join.py                               | 54 ++++++++++--
 python/samba/netcmd/domain_backup.py               |  2 +-
 python/samba/ntacls.py                             |  3 +-
 python/samba/safe_tarfile.py                       | 43 ++++++++++
 python/samba/tests/cred_opt.py                     | 99 ++++++++++++++++++++++
 python/samba/tests/domain_backup.py                |  2 +-
 python/samba/tests/domain_backup_offline.py        |  3 +-
 python/samba/tests/safe_tarfile.py                 | 67 +++++++++++++++
 selftest/knownfail.d/samba-4.5-emulation           |  4 +
 selftest/target/Samba4.pm                          | 12 +++
 selftest/tests.py                                  |  1 +
 source4/dsdb/repl/replicated_objects.c             | 11 +++
 source4/rpc_server/drsuapi/getncchanges.c          | 52 ++++++++++--
 source4/selftest/tests.py                          | 23 +++--
 .../torture/drs/python/samba_tool_drs_critical.py  | 98 +++++++++++++++++++++
 18 files changed, 505 insertions(+), 28 deletions(-)
 create mode 100644 python/samba/safe_tarfile.py
 create mode 100644 python/samba/tests/safe_tarfile.py
 create mode 100644 selftest/knownfail.d/samba-4.5-emulation
 create mode 100644 source4/torture/drs/python/samba_tool_drs_critical.py


Changeset truncated at 500 lines:

diff --git a/docs-xml/manpages/samba-tool.8.xml b/docs-xml/manpages/samba-tool.8.xml
index 60f3a5f06ed..965b9d6357c 100644
--- a/docs-xml/manpages/samba-tool.8.xml
+++ b/docs-xml/manpages/samba-tool.8.xml
@@ -62,6 +62,8 @@
 
 	&cmdline.common.credentials.usekrb5ccache;
 
+	&cmdline.common.credentials.authenticationfile;
+
 	<varlistentry>
 	<term>--ipaddress=IPADDRESS</term>
 	<listitem><para>
diff --git a/python/samba/drs_utils.py b/python/samba/drs_utils.py
index a71da6eedd3..6399e5f7fbc 100644
--- a/python/samba/drs_utils.py
+++ b/python/samba/drs_utils.py
@@ -204,6 +204,44 @@ class drs_Replicate(object):
                 supports_ext & DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10 and
                 (req.more_flags & drsuapi.DRSUAPI_DRS_GET_TGT) == 0)
 
+    @staticmethod
+    def _should_calculate_missing_anc_locally(error_code, req):
+        # If the error indicates we fail to resolve the parent object
+        # for a new object, then we assume we are replicating from a
+        # buggy server (Samba 4.5 and earlier) that doesn't really
+        # understand how to implement GET_ANC
+
+        return ((error_code == werror.WERR_DS_DRA_MISSING_PARENT) and
+                (req.replica_flags & drsuapi.DRSUAPI_DRS_GET_ANC) != 0)
+
+
+    def _calculate_missing_anc_locally(self, ctr):
+        self.guids_seen = set()
+
+        # walk objects in ctr, add to guid_seen as we see them
+        # note if an object doesn't have a parent
+
+        object_to_check = ctr.first_object
+
+        while True:
+            if object_to_check is None:
+                break
+
+            self.guids_seen.add(str(object_to_check.object.identifier.guid))
+
+            if object_to_check.parent_object_guid is not None \
+               and object_to_check.parent_object_guid \
+               != misc.GUID("00000000-0000-0000-0000-000000000000") \
+               and str(object_to_check.parent_object_guid) not in self.guids_seen:
+                obj_dn = ldb.Dn(self.samdb, object_to_check.object.identifier.dn)
+                parent_dn = obj_dn.parent()
+                print(f"Object {parent_dn} with "
+                      f"GUID {object_to_check.parent_object_guid} "
+                      "was not sent by the server in this chunk")
+
+            object_to_check = object_to_check.next_object
+
+
     def process_chunk(self, level, ctr, schema, req_level, req, first_chunk):
         '''Processes a single chunk of received replication data'''
         # pass the replication into the py_net.c python bindings for processing
@@ -326,8 +364,13 @@ class drs_Replicate(object):
                     # of causing the DC to restart the replication from scratch)
                     first_chunk = True
                     continue
-                else:
-                    raise e
+
+                if self._should_calculate_missing_anc_locally(e.args[0],
+                                                              req):
+                    print("Missing parent object - calculating missing objects locally")
+
+                    self._calculate_missing_anc_locally(ctr)
+                raise e
 
             first_chunk = False
             num_objects += ctr.object_count
diff --git a/python/samba/getopt.py b/python/samba/getopt.py
index 9e35273381f..ff8aead3f8d 100644
--- a/python/samba/getopt.py
+++ b/python/samba/getopt.py
@@ -192,6 +192,10 @@ class CredentialsOptions(optparse.OptionGroup):
                          action="callback", type=str,
                          help="Kerberos Credentials cache",
                          callback=self._set_krb5_ccache)
+        self._add_option("-A", "--authentication-file", metavar="AUTHFILE",
+                         action="callback", type=str,
+                         help="Authentication file",
+                         callback=self._set_auth_file)
 
         # LEGACY
         self._add_option("-k", "--kerberos", metavar="KERBEROS",
@@ -293,6 +297,12 @@ class CredentialsOptions(optparse.OptionGroup):
         self.creds.set_kerberos_state(MUST_USE_KERBEROS)
         self.creds.set_named_ccache(arg)
 
+    def _set_auth_file(self, option, opt_str, arg, parser):
+        if os.path.exists(arg):
+            self.creds.parse_file(arg)
+            self.ask_for_password = False
+            self.machine_pass = False
+
     def get_credentials(self, lp, fallback_machine=False):
         """Obtain the credentials set on the command-line.
 
diff --git a/python/samba/join.py b/python/samba/join.py
index 97561323f21..650bb5a08ae 100644
--- a/python/samba/join.py
+++ b/python/samba/join.py
@@ -968,17 +968,53 @@ class DCJoinContext(object):
                            destination_dsa_guid, rodc=ctx.RODC,
                            replica_flags=ctx.replica_flags)
             if not ctx.subdomain:
-                # Replicate first the critical object for the basedn
-                if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
-                    print("Replicating critical objects from the base DN of the domain")
-                    ctx.domain_replica_flags |= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
+                # Replicate first the critical objects for the basedn
+
+                # We do this to match Windows.  The default case is to
+                # do a critical objects replication, then a second
+                # with all objects.
+
+                print("Replicating critical objects from the base DN of the domain")
+                try:
                     repl.replicate(ctx.base_dn, source_dsa_invocation_id,
                                    destination_dsa_guid, rodc=ctx.RODC,
-                                   replica_flags=ctx.domain_replica_flags)
-                    ctx.domain_replica_flags ^= drsuapi.DRSUAPI_DRS_CRITICAL_ONLY
-                repl.replicate(ctx.base_dn, source_dsa_invocation_id,
-                               destination_dsa_guid, rodc=ctx.RODC,
-                               replica_flags=ctx.domain_replica_flags)
+                                   replica_flags=ctx.domain_replica_flags | drsuapi.DRSUAPI_DRS_CRITICAL_ONLY)
+                except WERRORError as e:
+
+                    if e.args[0] == werror.WERR_DS_DRA_MISSING_PARENT:
+                        ctx.logger.warning("First pass of replication with "
+                                           "DRSUAPI_DRS_CRITICAL_ONLY "
+                                           "not possible due to a missing parent object.  "
+                                           "This is typical of a Samba "
+                                           "4.5 or earlier server. "
+                                           "We will replicate the all objects instead.")
+                    else:
+                        raise
+
+                # Now replicate all the objects in the domain (unless
+                # we were run with --critical-only).
+                #
+                # Doing the replication of users as a second pass
+                # matches more closely the Windows behaviour, which is
+                # actually to do this on first startup.
+                #
+                # Use --critical-only if you want that (but you don't
+                # really, it is better to see any errors here).
+                if not ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
+                    try:
+                        repl.replicate(ctx.base_dn, source_dsa_invocation_id,
+                                       destination_dsa_guid, rodc=ctx.RODC,
+                                       replica_flags=ctx.domain_replica_flags)
+                    except WERRORError as e:
+
+                        if e.args[0] == werror.WERR_DS_DRA_MISSING_PARENT and \
+                           ctx.domain_replica_flags & drsuapi.DRSUAPI_DRS_CRITICAL_ONLY:
+                            ctx.logger.warning("Replication with DRSUAPI_DRS_CRITICAL_ONLY "
+                                               "failed due to a missing parent object.  "
+                                               "This may be a Samba 4.5 or earlier server "
+                                               "and is not compatible with --critical-only")
+                        raise
+
             print("Done with always replicated NC (base, config, schema)")
 
             # At this point we should already have an entry in the ForestDNS
diff --git a/python/samba/netcmd/domain_backup.py b/python/samba/netcmd/domain_backup.py
index 9c2c9e421f8..9eaba7dea1d 100644
--- a/python/samba/netcmd/domain_backup.py
+++ b/python/samba/netcmd/domain_backup.py
@@ -18,7 +18,6 @@
 import datetime
 import os
 import sys
-import tarfile
 import logging
 import shutil
 import tempfile
@@ -56,6 +55,7 @@ from samba import sites
 from samba.dsdb import _dsdb_load_udv_v2
 from samba.ndr import ndr_pack
 from samba.credentials import SMB_SIGNING_REQUIRED
+from samba import safe_tarfile as tarfile
 
 
 # work out a SID (based on a free RID) to use when the domain gets restored.
diff --git a/python/samba/ntacls.py b/python/samba/ntacls.py
index f35be48c30b..d2fe6ed911e 100644
--- a/python/samba/ntacls.py
+++ b/python/samba/ntacls.py
@@ -19,7 +19,6 @@
 
 
 import os
-import tarfile
 import tempfile
 import shutil
 
@@ -34,6 +33,8 @@ from samba.samba3 import libsmb_samba_internal as libsmb
 from samba.logger import get_samba_logger
 from samba import NTSTATUSError
 from samba.auth_util import system_session_unix
+from samba import safe_tarfile as tarfile
+
 
 # don't include volumes
 SMB_FILE_ATTRIBUTE_FLAGS = libsmb.FILE_ATTRIBUTE_SYSTEM | \
diff --git a/python/samba/safe_tarfile.py b/python/samba/safe_tarfile.py
new file mode 100644
index 00000000000..cc19770d73f
--- /dev/null
+++ b/python/samba/safe_tarfile.py
@@ -0,0 +1,43 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+#
+# 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/>.
+
+
+from tarfile import ExtractError, TarInfo, TarFile as UnsafeTarFile
+
+
+class TarFile(UnsafeTarFile):
+    """This TarFile implementation is trying to ameliorate CVE-2007-4559,
+    where tarfile.TarFiles can step outside of the target directory
+    using '../../'.
+    """
+
+    def extract(self, member, path="", set_attrs=True, *, numeric_owner=False):
+        if isinstance(member, TarInfo):
+            name = member.name
+        else:
+            name = member
+
+        if '../' in name:
+            raise ExtractError(f"'../' is not allowed in path '{name}'")
+
+        if name.startswith('/'):
+            raise ExtractError(f"path '{name}' should not start with '/'")
+
+        super().extract(member, path, set_attrs=set_attrs,
+                        numeric_owner=numeric_owner)
+
+
+open = TarFile.open
diff --git a/python/samba/tests/cred_opt.py b/python/samba/tests/cred_opt.py
index 91ca68085b7..82c6434d9c8 100644
--- a/python/samba/tests/cred_opt.py
+++ b/python/samba/tests/cred_opt.py
@@ -20,6 +20,8 @@
 """
 
 import optparse
+import os
+from contextlib import contextmanager
 from samba.getopt import CredentialsOptions
 import samba.tests
 import setproctitle
@@ -28,6 +30,19 @@ import sys
 password_opt = '--password=super_secret_password'
 clear_password_opt = '--password=xxx'
 
+ at contextmanager
+def auth_fle_opt(auth_file_path, long_opt=True):
+    old_argv = list(sys.argv)
+    try:
+        if long_opt:
+            sys.argv.append('--authentication-file=%s' % auth_file_path)
+        else:
+            sys.argv.append('-A')
+            sys.argv.append(auth_file_path)
+        yield
+    finally:
+        sys.argv = old_argv
+
 class CredentialsOptionsTests(samba.tests.TestCase):
 
     def setUp(self):
@@ -48,3 +63,87 @@ class CredentialsOptionsTests(samba.tests.TestCase):
         super(samba.tests.TestCase, self).tearDown()
         setproctitle.setproctitle(self.old_proctitle)
         sys.argv.pop()
+
+class AuthenticationFileTests(samba.tests.TestCaseInTempDir):
+
+    def setUp(self):
+        super(AuthenticationFileTests, self).setUp()
+
+        self.parser = optparse.OptionParser()
+        self.credopts = CredentialsOptions(self.parser)
+        self.parser.add_option_group(self.credopts)
+
+        self.auth_file_name = os.path.join(self.tempdir, 'auth.txt')
+
+        self.realm = 'realm.example.com'
+        self.domain = 'dom'
+        self.password = 'pass'
+        self.username = 'user'
+
+        auth_file_fd = open(self.auth_file_name, 'x')
+        auth_file_fd.write('realm=%s\n' % self.realm)
+        auth_file_fd.write('domain=%s\n' % self.domain)
+        auth_file_fd.write('username=%s\n' % self.username)
+        auth_file_fd.write('password=%s\n' % self.password)
+        auth_file_fd.close()
+
+    def tearDown(self):
+        super(AuthenticationFileTests, self).tearDown()
+
+        os.unlink(self.auth_file_name)
+
+    def test_long_option_valid_path(self):
+        with auth_fle_opt(self.auth_file_name):
+            self.parser.parse_args()
+            credopts = self.credopts
+            creds = credopts.creds
+
+            self.assertFalse(credopts.ask_for_password)
+            self.assertFalse(credopts.machine_pass)
+
+            self.assertEqual(self.username, creds.get_username())
+            self.assertEqual(self.password, creds.get_password())
+            self.assertEqual(self.domain.upper(), creds.get_domain())
+            self.assertEqual(self.realm.upper(), creds.get_realm())
+
+    def test_long_option_invalid_path(self):
+        with auth_fle_opt(self.auth_file_name + '.dontexist'):
+            self.parser.parse_args()
+            credopts = self.credopts
+            creds = credopts.creds
+
+            self.assertTrue(credopts.ask_for_password)
+            self.assertFalse(credopts.machine_pass)
+
+            self.assertIsNone(creds.get_username())
+            self.assertIsNone(creds.get_password())
+            self.assertIsNone(creds.get_domain())
+            self.assertIsNone(creds.get_realm())
+
+    def test_short_option_valid_path(self):
+        with auth_fle_opt(self.auth_file_name, long_opt=False):
+            self.parser.parse_args()
+            credopts = self.credopts
+            creds = credopts.creds
+
+            self.assertFalse(credopts.ask_for_password)
+            self.assertFalse(credopts.machine_pass)
+
+            self.assertEqual(self.username, creds.get_username())
+            self.assertEqual(self.password, creds.get_password())
+            self.assertEqual(self.domain.upper(), creds.get_domain())
+            self.assertEqual(self.realm.upper(), creds.get_realm())
+
+    def test_short_option_invalid_path(self):
+        with auth_fle_opt(self.auth_file_name + '.dontexist', long_opt=False):
+            self.parser.parse_args()
+            credopts = self.credopts
+            creds = credopts.creds
+
+            self.assertTrue(credopts.ask_for_password)
+            self.assertFalse(credopts.machine_pass)
+
+            self.assertIsNone(creds.get_username())
+            self.assertIsNone(creds.get_password())
+            self.assertIsNone(creds.get_domain())
+            self.assertIsNone(creds.get_realm())
diff --git a/python/samba/tests/domain_backup.py b/python/samba/tests/domain_backup.py
index 2a5df739d2c..b890a1030b7 100644
--- a/python/samba/tests/domain_backup.py
+++ b/python/samba/tests/domain_backup.py
@@ -15,7 +15,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 from samba import provision, param
-import tarfile
 import os
 import shutil
 from samba.tests import (env_loadparm, create_test_ou, BlackboxProcessError,
@@ -28,6 +27,7 @@ from samba.netcmd.fsmo import get_fsmo_roleowner
 import re
 from samba import sites
 from samba.dsdb import _dsdb_load_udv_v2
+from samba import safe_tarfile as tarfile
 
 
 def get_prim_dom(secrets_path, lp):
diff --git a/python/samba/tests/domain_backup_offline.py b/python/samba/tests/domain_backup_offline.py
index ad371680779..b764fcd049b 100644
--- a/python/samba/tests/domain_backup_offline.py
+++ b/python/samba/tests/domain_backup_offline.py
@@ -15,7 +15,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-import tarfile
 import os
 import shutil
 import tempfile
@@ -25,6 +24,8 @@ from samba.param import LoadParm
 from samba.join import join_DC
 from samba.credentials import Credentials
 from samba.logger import get_samba_logger
+from samba import safe_tarfile as tarfile
+
 
 # The backup tests require that a completely clean LoadParm object gets used
 # for the restore. Otherwise the same global LP gets re-used, and the LP
diff --git a/python/samba/tests/safe_tarfile.py b/python/samba/tests/safe_tarfile.py
new file mode 100644
index 00000000000..40aa9e17d4a
--- /dev/null
+++ b/python/samba/tests/safe_tarfile.py
@@ -0,0 +1,67 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Douglas Bagnall <douglas.bagnall at catalyst.net.nz>
+#
+# 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/>.
+import tarfile
+from samba import safe_tarfile
+import tempfile
+
+import os
+from samba.tests import TestCaseInTempDir
+
+
+def filterer(prefix):
+    def f(info):
+        info.name = prefix + info.name
+        return info
+    return f
+
+
+class SafeTarFileTestCase(TestCaseInTempDir):
+
+    def test_dots(self):
+        filename = os.path.join(self.tempdir, 'x')
+        tarname = os.path.join(self.tempdir, 'tar.tar')
+        f = open(filename, 'w')
+        f.write('x')
+        f.close()
+
+        tf = tarfile.open(tarname, 'w')
+        tf.add(filename, filter=filterer('../../'))
+        tf.close()
+
+        stf = safe_tarfile.open(tarname)
+
+        self.assertRaises(tarfile.ExtractError,
+                          stf.extractall,
+                          tarname)
+        self.rm_files('x', 'tar.tar')
+
+    def test_slash(self):
+        filename = os.path.join(self.tempdir, 'x')
+        tarname = os.path.join(self.tempdir, 'tar.tar')
+        f = open(filename, 'w')
+        f.write('x')
+        f.close()
+
+        tf = tarfile.open(tarname, 'w')
+        tf.add(filename, filter=filterer('/'))
+        tf.close()
+
+        stf = safe_tarfile.open(tarname)
+        self.assertRaises(tarfile.ExtractError,
+                          stf.extractall,
+                          tarname)
+
+        self.rm_files('x', 'tar.tar')
diff --git a/selftest/knownfail.d/samba-4.5-emulation b/selftest/knownfail.d/samba-4.5-emulation
new file mode 100644


-- 
Samba Shared Repository



More information about the samba-cvs mailing list