CVE-2014-0012: Permissions, Privileges, and Access Controls (CWE-264)
value: FileSystemBytecodeCache in Jinja2 2.7.2 does not properly create temporary directories, which allows local users to gain privileges by pre-creating a temporary directory with a user's uid.  NOTE: this vulnerability exists because of an incomplete fix for CVE-2014-1402.
CWE description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
Published: 2014-05-19T14:55Z
Repository: https://github.com/mitsuhiko/jinja2
Language: Other
Files changed: CHANGES, jinja2/bccache.py
Commit date: 2014-01-10 10:40:51 +0000
Commit message: Fixed a security issue with temporary files on the filesystem cache on UNIX.

Diff: diff --git a/CHANGES b/CHANGES
index 6425de5..17e44ef 100644
--- a/CHANGES
+++ b/CHANGES
@@ -5,10 +5,15 @@ Version 2.7.2
 -------------
 (bugfix release, release date to be decided)
 
 - Prefix loader was not forwarding the locals properly to
   inner loaders.  This is now fixed.
+- Security issue: Changed the default folder for the filesystem cache to be
+  user specific and read and write protected on UNIX systems.  See `Debian bug
+  734747`_ for more information.
+
+.. _Debian bug 734747: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=734747
 
 Version 2.7.1
 -------------
 (bugfix release, released on August 7th 2013)
 
diff --git a/jinja2/bccache.py b/jinja2/bccache.py
index f2f9db6..433a480 100644
--- a/jinja2/bccache.py
+++ b/jinja2/bccache.py
@@ -13,11 +13,13 @@
 
     :copyright: (c) 2010 by the Jinja Team.
     :license: BSD.
 """
 from os import path, listdir
+import os
 import sys
+import errno
 import marshal
 import tempfile
 import fnmatch
 from hashlib import sha1
 from jinja2.utils import open_if_exists
@@ -187,11 +189,13 @@ class BytecodeCache(object):
 class FileSystemBytecodeCache(BytecodeCache):
     """A bytecode cache that stores bytecode on the filesystem.  It accepts
     two arguments: The directory where the cache items are stored and a
     pattern string that is used to build the filename.
 
-    If no directory is specified the system temporary items folder is used.
+    If no directory is specified a default cache directory is selected.  On
+    Windows the user's temp directory is used, on UNIX systems a directory
+    is created for the user in the system temp directory.
 
     The pattern can be used to have multiple separate caches operate on the
     same directory.  The default pattern is ``'__jinja2_%s.cache'``.  ``%s``
     is replaced with the cache key.
 
@@ -200,14 +204,35 @@ class FileSystemBytecodeCache(BytecodeCache):
     This bytecode cache supports clearing of the cache using the clear method.
     """
 
     def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
         if directory is None:
-            directory = tempfile.gettempdir()
+            directory = self._get_default_cache_dir()
         self.directory = directory
         self.pattern = pattern
 
+    def _get_default_cache_dir(self):
+        tmpdir = tempfile.gettempdir()
+
+        # On windows the temporary directory is used specific unless
+        # explicitly forced otherwise.  We can just use that.
+        if os.name == 'n':
+            return tmpdir
+        if not hasattr(os, 'getuid'):
+            raise RuntimeError('Cannot determine safe temp directory.  You '
+                               'need to explicitly provide one.')
+
+        dirname = '_jinja2-cache-%d' % os.getuid()
+        actual_dir = os.path.join(tmpdir, dirname)
+        try:
+            os.mkdir(actual_dir, 0700)
+        except OSError as e:
+            if e.errno != errno.EEXIST:
+                raise
+
+        return actual_dir
+
     def _get_cache_filename(self, bucket):
         return path.join(self.directory, self.pattern % bucket.key)
 
     def load_bytecode(self, bucket):
         f = open_if_exists(self._get_cache_filename(bucket), 'rb')

Vulnerable code:     If no directory is specified the system temporary items folder is used.
            directory = tempfile.gettempdir()
Fixed code: - Security issue: Changed the default folder for the filesystem cache to be
  user specific and read and write protected on UNIX systems.  See `Debian bug
  734747`_ for more information.

.. _Debian bug 734747: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=734747
import os
import errno
    If no directory is specified a default cache directory is selected.  On
    Windows the user's temp directory is used, on UNIX systems a directory
    is created for the user in the system temp directory.
            directory = self._get_default_cache_dir()
    def _get_default_cache_dir(self):
        tmpdir = tempfile.gettempdir()

        # On windows the temporary directory is used specific unless
        # explicitly forced otherwise.  We can just use that.
        if os.name == 'n':
            return tmpdir
        if not hasattr(os, 'getuid'):
            raise RuntimeError('Cannot determine safe temp directory.  You '
                               'need to explicitly provide one.')

        dirname = '_jinja2-cache-%d' % os.getuid()
        actual_dir = os.path.join(tmpdir, dirname)
        try:
            os.mkdir(actual_dir, 0700)
        except OSError as e:
            if e.errno != errno.EEXIST:
                raise

        return actual_dir