[Python-modules-commits] r26255 - in packages/scipy/branches/wheezy/debian (3 files)

jtaylor-guest at users.alioth.debian.org jtaylor-guest at users.alioth.debian.org
Tue Oct 22 22:54:13 UTC 2013


    Date: Tuesday, October 22, 2013 @ 22:54:11
  Author: jtaylor-guest
Revision: 26255

temporary-directory-usage.patch:
fix insecure temporary directory usage of weave module. (Closes: #726093)
Thanks to Tomas Tomecek for the patch.

Added:
  packages/scipy/branches/wheezy/debian/patches/temporary-directory-usage.patch
Modified:
  packages/scipy/branches/wheezy/debian/changelog
  packages/scipy/branches/wheezy/debian/patches/series

Modified: packages/scipy/branches/wheezy/debian/changelog
===================================================================
--- packages/scipy/branches/wheezy/debian/changelog	2013-10-22 22:40:59 UTC (rev 26254)
+++ packages/scipy/branches/wheezy/debian/changelog	2013-10-22 22:54:11 UTC (rev 26255)
@@ -1,3 +1,11 @@
+python-scipy (0.10.1+dfsg2-1+deb7u1) stable-proposed-updates; urgency=low
+
+  * temporary-directory-usage.patch:
+    fix insecure temporary directory usage of weave module. (Closes: #726093)
+    Thanks to Tomas Tomecek for the patch.
+
+ -- Julian Taylor <jtaylor.debian at googlemail.com>  Tue, 22 Oct 2013 23:44:47 +0200
+
 python-scipy (0.10.1+dfsg2-1) unstable; urgency=low
 
   * add missing cython and swig sources from git tag (Closes: #589731)

Modified: packages/scipy/branches/wheezy/debian/patches/series
===================================================================
--- packages/scipy/branches/wheezy/debian/patches/series	2013-10-22 22:40:59 UTC (rev 26254)
+++ packages/scipy/branches/wheezy/debian/patches/series	2013-10-22 22:54:11 UTC (rev 26255)
@@ -5,3 +5,4 @@
 blitz++.patch
 up_minpack_ints.diff
 interpnd-generator.patch
+temporary-directory-usage.patch

Added: packages/scipy/branches/wheezy/debian/patches/temporary-directory-usage.patch
===================================================================
--- packages/scipy/branches/wheezy/debian/patches/temporary-directory-usage.patch	                        (rev 0)
+++ packages/scipy/branches/wheezy/debian/patches/temporary-directory-usage.patch	2013-10-22 22:54:11 UTC (rev 26255)
@@ -0,0 +1,601 @@
+Description: weave: fix insecure temporary directory usage
+Date: Sun, 1 Sep 2013 16:40:18 +0300
+Applied-Upstream: 0.12.1
+Origin: b94d76ce234cc07e456976e8f6edc2981431242f
+Author: Tomas Tomecek <ttomecek at redhat.com>
+Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=726093
+
+--- a/scipy/weave/catalog.py
++++ b/scipy/weave/catalog.py
+@@ -33,6 +33,7 @@
+ 
+ import os
+ import sys
++import stat
+ import pickle
+ import socket
+ import tempfile
+@@ -114,20 +115,6 @@ def unique_file(d,expr):
+             break
+     return os.path.join(d,fname)
+ 
+-def create_dir(p):
+-    """ Create a directory and any necessary intermediate directories."""
+-    if not os.path.exists(p):
+-        try:
+-            os.mkdir(p)
+-        except OSError:
+-            # perhaps one or more intermediate path components don't exist
+-            # try to create them
+-            base,dir = os.path.split(p)
+-            create_dir(base)
+-            # don't enclose this one in try/except - we want the user to
+-            # get failure info
+-            os.mkdir(p)
+-
+ def is_writable(dir):
+     """Determine whether a given directory is writable in a portable manner.
+ 
+@@ -141,6 +128,8 @@ def is_writable(dir):
+     res : bool
+         True or False.
+     """
++    if not os.path.isdir(dir):
++        return False
+ 
+     # Do NOT use a hardcoded name here due to the danger from race conditions
+     # on NFS when multiple processes are accessing the same base directory in
+@@ -161,6 +150,88 @@ def whoami():
+     """return a string identifying the user."""
+     return os.environ.get("USER") or os.environ.get("USERNAME") or "unknown"
+ 
++
++def _create_dirs(path):
++    """ create provided path, ignore errors """
++    try:
++        os.makedirs(path, mode=0o700)
++    except OSError:
++        pass
++
++
++def default_dir_posix(tmp_dir=None):
++    """
++    Create or find default catalog store for posix systems
++
++    purpose of 'tmp_dir' is to enable way how to test this function easily
++    """
++    path_candidates = []
++    python_name = "python%d%d_compiled" % tuple(sys.version_info[:2])
++
++    if tmp_dir:
++        home_dir = tmp_dir
++    else:
++        home_dir = os.path.expanduser('~')
++    tmp_dir = tmp_dir or tempfile.gettempdir()
++
++    home_temp_dir_name = '.' + python_name
++    home_temp_dir = os.path.join(home_dir, home_temp_dir_name)
++    path_candidates.append(home_temp_dir)
++
++    temp_dir_name = repr(os.getuid()) + '_' + python_name
++    temp_dir_path = os.path.join(tmp_dir, temp_dir_name)
++    path_candidates.append(temp_dir_path)
++
++    for path in path_candidates:
++        _create_dirs(path)
++        if check_dir(path):
++            return path
++
++    # since we got here, both dirs are not useful
++    tmp_dir_path = find_valid_temp_dir(temp_dir_name, tmp_dir)
++    if not tmp_dir_path:
++        tmp_dir_path = create_temp_dir(temp_dir_name, tmp_dir=tmp_dir)
++    return tmp_dir_path
++
++
++def default_dir_win(tmp_dir=None):
++    """
++    Create or find default catalog store for Windows systems
++
++    purpose of 'tmp_dir' is to enable way how to test this function easily
++    """
++    def create_win_temp_dir(prefix, inner_dir=None, tmp_dir=None):
++        """
++        create temp dir starting with 'prefix' in 'tmp_dir' or
++        'tempfile.gettempdir'; if 'inner_dir' is specified, it should be
++        created inside
++        """
++        tmp_dir_path = find_valid_temp_dir(prefix, tmp_dir)
++        if tmp_dir_path:
++            if inner_dir:
++                tmp_dir_path = os.path.join(tmp_dir_path, inner_dir)
++                if not os.path.isdir(tmp_dir_path):
++                    os.mkdir(tmp_dir_path, 0o700)
++        else:
++            tmp_dir_path = create_temp_dir(prefix, inner_dir, tmp_dir)
++        return tmp_dir_path
++
++    python_name = "python%d%d_compiled" % tuple(sys.version_info[:2])
++    tmp_dir = tmp_dir or tempfile.gettempdir()
++
++    temp_dir_name = "%s" % whoami()
++    temp_root_dir = os.path.join(tmp_dir, temp_dir_name)
++    temp_dir_path = os.path.join(temp_root_dir, python_name)
++    _create_dirs(temp_dir_path)
++    if check_dir(temp_dir_path) and check_dir(temp_root_dir):
++        return temp_dir_path
++    else:
++        if check_dir(temp_root_dir):
++            return create_win_temp_dir(python_name, tmp_dir=temp_root_dir)
++        else:
++            return create_win_temp_dir(temp_dir_name, python_name, tmp_dir)
++
++
+ def default_dir():
+     """ Return a default location to store compiled files and catalogs.
+ 
+@@ -175,43 +246,18 @@ def default_dir():
+         in the user's home, /tmp/<uid>_pythonXX_compiled is used.  If it
+         doesn't exist, it is created.  The directory is marked rwx------
+         to try and keep people from being able to sneak a bad module
+-        in on you.
+-
++        in on you. If the directory already exists in /tmp/ and is not
++        secure, new one is created.
+     """
+-
+     # Use a cached value for fast return if possible
+-    if hasattr(default_dir,"cached_path") and \
+-       os.path.exists(default_dir.cached_path) and \
+-       os.access(default_dir.cached_path, os.W_OK):
++    if hasattr(default_dir, "cached_path") and \
++       check_dir(default_dir.cached_path):
+         return default_dir.cached_path
+ 
+-    python_name = "python%d%d_compiled" % tuple(sys.version_info[:2])
+-    path_candidates = []
+-    if sys.platform != 'win32':
+-        try:
+-            path_candidates.append(os.path.join(os.environ['HOME'],
+-                                                '.' + python_name))
+-        except KeyError:
+-            pass
+-
+-        temp_dir = `os.getuid()` + '_' + python_name
+-        path_candidates.append(os.path.join(tempfile.gettempdir(), temp_dir))
++    if sys.platform == 'win32':
++        path = default_dir_win()
+     else:
+-        path_candidates.append(os.path.join(tempfile.gettempdir(),
+-                                           "%s" % whoami(), python_name))
+-
+-    writable = False
+-    for path in path_candidates:
+-        if not os.path.exists(path):
+-            create_dir(path)
+-            os.chmod(path, 0700) # make it only accessible by this user.
+-        if is_writable(path):
+-            writable = True
+-            break
+-
+-    if not writable:
+-        print 'warning: default directory is not write accessible.'
+-        print 'default:', path
++        path = default_dir_posix()
+ 
+     # Cache the default dir path so that this function returns quickly after
+     # being called once (nothing in it should change after the first call)
+@@ -219,21 +265,137 @@ def default_dir():
+ 
+     return path
+ 
+-def intermediate_dir():
+-    """ Location in temp dir for storing .cpp and .o  files during
+-        builds.
++
++def check_dir(im_dir):
+     """
+-    python_name = "python%d%d_intermediate" % tuple(sys.version_info[:2])
+-    path = os.path.join(tempfile.gettempdir(),"%s"%whoami(),python_name)
+-    if not os.path.exists(path):
+-        create_dir(path)
+-    return path
++    Check if dir is safe; if it is, return True.
++    These checks make sense only on posix:
++     * directory has correct owner
++     * directory has correct permissions (0700)
++     * directory is not a symlink
++    """
++    def check_is_dir():
++        return os.path.isdir(im_dir)
++
++    def check_permissions():
++        """ If on posix, permissions should be 0700. """
++        writable = is_writable(im_dir)
++        if sys.platform != 'win32':
++            try:
++                im_dir_stat = os.stat(im_dir)
++            except OSError:
++                return False
++            writable &= stat.S_IMODE(im_dir_stat.st_mode) == 0o0700
++        return writable
++
++    def check_ownership():
++        """ Intermediate dir owner should be same as owner of process. """
++        if sys.platform != 'win32':
++            try:
++                im_dir_stat = os.stat(im_dir)
++            except OSError:
++                return False
++            proc_uid = os.getuid()
++            return proc_uid == im_dir_stat.st_uid
++        return True
++
++    def check_is_symlink():
++        """ Check if intermediate dir is symlink. """
++        try:
++            return not os.path.islink(im_dir)
++        except OSError:
++            return False
++
++    checks = [check_is_dir, check_permissions,
++              check_ownership, check_is_symlink]
++
++    for check in checks:
++        if not check():
++            return False
++
++    return True
++
++
++def create_temp_dir(prefix, inner_dir=None, tmp_dir=None):
++    """
++    Create intermediate dirs <tmp>/<prefix+random suffix>/<inner_dir>/
++
++    argument 'tmp_dir' is used in unit tests
++    """
++    if not tmp_dir:
++        tmp_dir_path = tempfile.mkdtemp(prefix=prefix)
++    else:
++        tmp_dir_path = tempfile.mkdtemp(prefix=prefix, dir=tmp_dir)
++    if inner_dir:
++        tmp_dir_path = os.path.join(tmp_dir_path, inner_dir)
++        os.mkdir(tmp_dir_path, 0o700)
++    return tmp_dir_path
++
++
++def intermediate_dir_prefix():
++    """ Prefix of root intermediate dir (<tmp>/<root_im_dir>). """
++    return "%s-%s-" % ("scipy", whoami())
++
++
++def find_temp_dir(prefix, tmp_dir=None):
++    """ Find temp dirs in 'tmp_dir' starting with 'prefix'"""
++    matches = []
++    tmp_dir = tmp_dir or tempfile.gettempdir()
++    for tmp_file in os.listdir(tmp_dir):
++        if tmp_file.startswith(prefix):
++            matches.append(os.path.join(tmp_dir, tmp_file))
++    return matches
++
++
++def find_valid_temp_dir(prefix, tmp_dir=None):
++    """
++    Try to look for existing temp dirs.
++    If there is one suitable found, return it, otherwise return None.
++    """
++    matches = find_temp_dir(prefix, tmp_dir)
++    for match in matches:
++        if check_dir(match):
++            # as soon as we find correct dir, we can stop searching
++            return match
++
++
++def py_intermediate_dir():
++    """
++    Name of intermediate dir for current python interpreter:
++    <temp dir>/<name>/pythonXY_intermediate/
++    """
++    name = "python%d%d_intermediate" % tuple(sys.version_info[:2])
++    return name
++
++
++def create_intermediate_dir(tmp_dir=None):
++    py_im_dir = py_intermediate_dir()
++    return create_temp_dir(intermediate_dir_prefix(), py_im_dir, tmp_dir)
++
++
++def intermediate_dir(tmp_dir=None):
++    """
++    Temporary directory for storing .cpp and .o files during builds.
++
++    First, try to find the dir and if it exists, verify it is safe.
++    Otherwise, create it.
++    """
++    im_dir = find_valid_temp_dir(intermediate_dir_prefix(), tmp_dir)
++    py_im_dir = py_intermediate_dir()
++    if im_dir is None:
++        py_im_dir = py_intermediate_dir()
++        im_dir = create_intermediate_dir(tmp_dir)
++    else:
++        im_dir = os.path.join(im_dir, py_im_dir)
++        if not os.path.isdir(im_dir):
++            os.mkdir(im_dir, 0o700)
++    return im_dir
++
+ 
+ def default_temp_dir():
+     path = os.path.join(default_dir(),'temp')
+     if not os.path.exists(path):
+-        create_dir(path)
+-        os.chmod(path,0700) # make it only accessible by this user.
++        os.makedirs(path, mode=0o700)
+     if not is_writable(path):
+         print 'warning: default directory is not write accessible.'
+         print 'default:', path
+diff --git a/scipy/weave/tests/test_catalog.py b/scipy/weave/tests/test_catalog.py
+index 8b1f324..e972c06 100644
+--- a/scipy/weave/tests/test_catalog.py
++++ b/scipy/weave/tests/test_catalog.py
+@@ -1,15 +1,233 @@
+ import sys
+ import os
++import stat
++import tempfile
++
++from distutils.dir_util import remove_tree
+ 
+ from numpy.testing import TestCase, assert_
++from numpy.testing.noseclasses import KnownFailureTest
+ 
+ from scipy.weave import catalog
+ from weave_test_utils import clear_temp_catalog, restore_temp_catalog, \
+         empty_temp_dir, cleanup_temp_dir
+ 
+ 
++class TestIntermediateDir(TestCase):
++    """
++    Tests for intermediate dir (store of .cpp and .o during builds).
++    These tests test whether intermediate dir is safe. If it's not,
++    new one should be created.
++    """
++    def dirs_are_valid(self, wrong_dir, tmpdir):
++        """ test if new dir is created and is consistent """
++        new_im_dir = catalog.intermediate_dir(tmpdir)
++        assert_(not os.path.samefile(new_im_dir, wrong_dir))
++        new_im_dir2 = catalog.intermediate_dir(tmpdir)
++        assert_(os.path.samefile(new_im_dir, new_im_dir2))
++
++    def test_ownership(self):
++        """ test if intermediate dir is owned by correct user """
++        if sys.platform != 'win32':
++            im_dir = catalog.intermediate_dir()
++            im_dir_stat = os.stat(im_dir)
++            proc_uid = os.getuid()
++            assert_(proc_uid == im_dir_stat.st_uid)
++            r_im_dir_stat = os.stat(os.path.dirname(im_dir))
++            assert_(proc_uid == r_im_dir_stat.st_uid)
++
++    def test_incorrect_ownership(self):
++        """
++        test if new intermediate dir is created when there is only one
++        im dir owned by improper user
++        """
++        if sys.platform != 'win32':
++            import pwd
++            tmpdir = tempfile.mkdtemp()
++            try:
++                im_dir = catalog.create_intermediate_dir(tmpdir)
++                root_im_dir = os.path.dirname(im_dir)
++                nobody = pwd.getpwnam('nobody')[2]
++                nobody_g = pwd.getpwnam('nobody')[3]
++                try:
++                    os.chown(root_im_dir, nobody, nobody_g)
++                except OSError:
++                    raise KnownFailureTest("Can't change owner.")
++                else:
++                    self.dirs_are_valid(im_dir, tmpdir)
++            finally:
++                remove_tree(tmpdir)
++
++    def test_permissions(self):
++        """ im dir should have permissions 0700 """
++        if sys.platform != 'win32':
++            im_dir = catalog.intermediate_dir()
++            im_dir_stat = os.stat(im_dir)
++            assert_(stat.S_IMODE(im_dir_stat.st_mode) == 0o0700)
++            r_im_dir_stat = os.stat(os.path.dirname(im_dir))
++            assert_(stat.S_IMODE(r_im_dir_stat.st_mode) == 0o0700)
++
++    def test_incorrect_permissions(self):
++        """
++        if permissions on existing im dir are not correct,
++        new one should be created
++        """
++        if sys.platform != 'win32':
++            tmpdir = tempfile.mkdtemp()
++            try:
++                im_dir = catalog.create_intermediate_dir(tmpdir)
++                root_im_dir = os.path.dirname(im_dir)
++                try:
++                    os.chmod(root_im_dir, 0o777)
++                except OSError:
++                    raise KnownFailureTest("Can't set file permissions.")
++                else:
++                    self.dirs_are_valid(im_dir, tmpdir)
++            finally:
++                remove_tree(tmpdir)
++
++    def test_symlink(self):
++        """ im dir shouldn't be a symlink """
++        if sys.platform != 'win32':
++            r_im_dir = os.path.dirname(catalog.intermediate_dir())
++            assert_(os.path.islink(r_im_dir) is False)
++
++    def test_symlink_raise(self):
++        """ if existing im dir is a symlink, new one should be created """
++        if sys.platform != 'win32':
++            tmpdir = tempfile.mkdtemp()
++            try:
++                im_dir = catalog.create_intermediate_dir(tmpdir)
++                root_im_dir = os.path.dirname(im_dir)
++
++                tempdir = tempfile.mkdtemp(prefix='scipy-test', dir=tmpdir)
++                try:
++                    os.rename(root_im_dir, tempdir)
++                except OSError:
++                    raise KnownFailureTest("Can't move intermediate dir.")
++
++                try:
++                    os.symlink(tempdir, root_im_dir)
++                except OSError:
++                    raise KnownFailureTest(
++                        "Can't create symlink to intermediate dir.")
++                else:
++                    self.dirs_are_valid(im_dir, tmpdir)
++            finally:
++                remove_tree(tmpdir)
++
++
+ class TestDefaultDir(TestCase):
++    """
++    Tests for 'catalog.default_dir()'.
++    These should verified posix and win default_dir function.
++    """
++    def test_win(self):
++        """
++        test if default_dir for Windows platform is accessible
++
++        since default_dir_win() does not have any Windows specific code,
++        let's test it everywhere
++        """
++        d = catalog.default_dir_win()
++        assert_(catalog.is_writable(d))
++
++    def test_win_inaccessible_root(self):
++        """
++        there should be a new root dir created if existing one is not accessible
++        """
++        tmpdir = tempfile.mkdtemp()
++        try:
++            d_dir = catalog.default_dir_win(tmpdir)
++            root_ddir = os.path.dirname(d_dir)
++
++            try:
++                os.chmod(root_ddir, stat.S_IREAD | stat.S_IEXEC)
++            except OSError:
++                raise KnownFailureTest("Can't change permissions of root default_dir.")
++
++            new_ddir = catalog.default_dir_win(tmpdir)
++            assert_(not os.path.samefile(new_ddir, d_dir))
++            new_ddir2 = catalog.default_dir_win(tmpdir)
++            assert_(os.path.samefile(new_ddir, new_ddir2))
++        finally:
++            os.chmod(root_ddir, 0o700)
++            remove_tree(tmpdir)
++
++    def test_win_inaccessible_ddir(self):
++        """
++        create new defualt_dir if current one is not accessible
++        """
++        tmpdir = tempfile.mkdtemp()
++        try:
++            d_dir = catalog.default_dir_win(tmpdir)
++
++            try:
++                os.chmod(d_dir, stat.S_IREAD | stat.S_IEXEC)
++            except OSError:
++                raise KnownFailureTest("Can't change permissions of default_dir.")
++
++            new_ddir = catalog.default_dir_win(tmpdir)
++            assert_(not os.path.samefile(new_ddir, d_dir))
++            new_ddir2 = catalog.default_dir_win(tmpdir)
++            assert_(os.path.samefile(new_ddir, new_ddir2))
++        finally:
++            os.chmod(d_dir, 0o700)
++            remove_tree(tmpdir)
++
++    def test_posix(self):
++        """ test if posix default_dir is writable """
++        d = catalog.default_dir_posix()
++        assert_(catalog.is_writable(d))
++
++    def test_posix_home_inaccessible(self):
++        """ what happens when home catalog dir is innaccessible """
++        tmpdir = tempfile.mkdtemp()
++        try:
++            d_dir = catalog.default_dir_posix(tmpdir)
++
++            try:
++                os.chmod(d_dir, 0o000)
++            except OSError:
++                raise KnownFailureTest("Can't change permissions of default_dir.")
++
++            new_ddir = catalog.default_dir_posix(tmpdir)
++            assert_(not os.path.samefile(new_ddir, d_dir))
++            new_ddir2 = catalog.default_dir_posix(tmpdir)
++            assert_(os.path.samefile(new_ddir, new_ddir2))
++        finally:
++            os.chmod(d_dir, 0o700)
++            remove_tree(tmpdir)
++
++    def test_posix_dirs_inaccessible(self):
++        """ test if new dir is created if both implicit dirs are not valid"""
++        tmpdir = tempfile.mkdtemp()
++        try:
++            d_dir = catalog.default_dir_posix(tmpdir)
++
++            try:
++                os.chmod(d_dir, 0o000)
++            except OSError:
++                raise KnownFailureTest("Can't change permissions of default_dir.")
++
++            d_dir2 = catalog.default_dir_posix(tmpdir)
++
++            try:
++                os.chmod(d_dir2, 0o000)
++            except OSError:
++                raise KnownFailureTest("Can't change permissions of default_dir.")
++
++            new_ddir = catalog.default_dir_posix(tmpdir)
++            assert_(not (os.path.samefile(new_ddir, d_dir) or os.path.samefile(new_ddir, d_dir2)))
++            new_ddir2 = catalog.default_dir_posix(tmpdir)
++            assert_(os.path.samefile(new_ddir, new_ddir2))
++        finally:
++            os.chmod(d_dir, 0o700)
++            os.chmod(d_dir2, 0o700)
++            remove_tree(tmpdir)
++
+     def test_is_writable(self):
++        """ default_dir has to be writable """
+         path = catalog.default_dir()
+         name = os.path.join(path,'dummy_catalog')
+         test_file = open(name,'w')
+@@ -91,14 +309,10 @@ class TestGetCatalog(TestCase):
+                 os.remove(cat_file)
+         return pardir
+ 
+-    def remove_dir(self,d):
+-        import distutils.dir_util
+-        distutils.dir_util.remove_tree(d)
+-
+     def test_nonexistent_catalog_is_none(self):
+         pardir = self.get_test_dir(erase=1)
+         cat = catalog.get_catalog(pardir,'r')
+-        self.remove_dir(pardir)
++        remove_tree(pardir)
+         assert_(cat is None)
+ 
+     def test_create_catalog(self):
+@@ -106,7 +320,7 @@ class TestGetCatalog(TestCase):
+         cat = catalog.get_catalog(pardir,'c')
+         assert_(cat is not None)
+         cat.close()
+-        self.remove_dir(pardir)
++        remove_tree(pardir)
+ 
+ 
+ class TestCatalog(TestCase):
+-- 
+1.8.3.2
+




More information about the Python-modules-commits mailing list