[graphite-web/el5] Move back to 0.9.10 to patch for CVE-2013-5093

Jonathan Steffan jsteffan at fedoraproject.org
Fri Sep 20 00:22:10 UTC 2013


commit 203eb4da795f5751df6b3b3e4faefc582ebcfdc1
Author: Jonathan Steffan <jsteffan at fedoraproject.org>
Date:   Thu Sep 19 18:21:01 2013 -0600

    Move back to 0.9.10 to patch for CVE-2013-5093

 graphite-web-0.9.10-CVE-2013-5093.patch  |  207 ++++++++++++++++++++++++++++++
 graphite-web-0.9.10-fhs-thirdparty.patch |  185 ++++++++++++++++++++++++++
 graphite-web-0.9.12-fhs-thirdparty.patch |  162 -----------------------
 graphite-web.spec                        |   25 ++--
 sources                                  |    2 +-
 5 files changed, 405 insertions(+), 176 deletions(-)
---
diff --git a/graphite-web-0.9.10-CVE-2013-5093.patch b/graphite-web-0.9.10-CVE-2013-5093.patch
new file mode 100644
index 0000000..e946568
--- /dev/null
+++ b/graphite-web-0.9.10-CVE-2013-5093.patch
@@ -0,0 +1,207 @@
+diff --git a/webapp/graphite/remote_storage.py b/webapp/graphite/remote_storage.py
+index b33250f..1f8a578 100644
+--- a/webapp/graphite/remote_storage.py
++++ b/webapp/graphite/remote_storage.py
+@@ -5,11 +5,7 @@ from urllib import urlencode
+ from django.core.cache import cache
+ from django.conf import settings
+ from graphite.render.hashing import compactHash
+-
+-try:
+-  import cPickle as pickle
+-except ImportError:
+-  import pickle
++from graphite.util import unpickle
+ 
+ 
+ 
+@@ -79,7 +75,7 @@ class FindRequest:
+       response = self.connection.getresponse()
+       assert response.status == 200, "received error response %s - %s" % (response.status, response.reason)
+       result_data = response.read()
+-      results = pickle.loads(result_data)
++      results = unpickle.loads(result_data)
+ 
+     except:
+       self.store.fail()
+@@ -126,7 +122,7 @@ class RemoteNode:
+     assert response.status == 200, "Failed to retrieve remote data: %d %s" % (response.status, response.reason)
+     rawData = response.read()
+ 
+-    seriesList = pickle.loads(rawData)
++    seriesList = unpickle.loads(rawData)
+     assert len(seriesList) == 1, "Invalid result: seriesList=%s" % str(seriesList)
+     series = seriesList[0]
+ 
+diff --git a/webapp/graphite/render/datalib.py b/webapp/graphite/render/datalib.py
+index b5f7b83..3b697c7 100644
+--- a/webapp/graphite/render/datalib.py
++++ b/webapp/graphite/render/datalib.py
+@@ -19,6 +19,7 @@ from django.conf import settings
+ from graphite.logger import log
+ from graphite.storage import STORE, LOCAL_STORE
+ from graphite.render.hashing import ConsistentHashRing
++from graphite.util import unpickle
+ 
+ try:
+   import cPickle as pickle
+@@ -173,7 +174,7 @@ class CarbonLinkPool:
+     len_prefix = recv_exactly(conn, 4)
+     body_size = struct.unpack("!L", len_prefix)[0]
+     body = recv_exactly(conn, body_size)
+-    return pickle.loads(body)
++    return unpickle.loads(body)
+ 
+ 
+ # Utilities
+diff --git a/webapp/graphite/render/views.py b/webapp/graphite/render/views.py
+index e367f8c..6d10799 100644
+--- a/webapp/graphite/render/views.py
++++ b/webapp/graphite/render/views.py
+@@ -24,7 +24,7 @@ try:
+ except ImportError:
+   import pickle
+ 
+-from graphite.util import getProfileByUsername, json
++from graphite.util import getProfileByUsername, json, unpickle
+ from graphite.remote_storage import HTTPConnectionWithTimeout
+ from graphite.logger import log
+ from graphite.render.evaluator import evaluateTarget
+@@ -303,7 +303,7 @@ def renderLocalView(request):
+     optionsPickle = reqParams.read()
+     reqParams.close()
+     graphClass = GraphTypes[graphType]
+-    options = pickle.loads(optionsPickle)
++    options = unpickle.loads(optionsPickle)
+     image = doImageRender(graphClass, options)
+     log.rendering("Delegated rendering request took %.6f seconds" % (time() -  start))
+     return buildResponse(image)
+diff --git a/webapp/graphite/storage.py b/webapp/graphite/storage.py
+index e765189..5863c2d 100644
+--- a/webapp/graphite/storage.py
++++ b/webapp/graphite/storage.py
+@@ -1,8 +1,10 @@
+ import os, time, fnmatch, socket, errno
++from django.conf import settings
+ from os.path import isdir, isfile, join, exists, splitext, basename, realpath
+ import whisper
+ from graphite.remote_storage import RemoteStore
+ from django.conf import settings
++from graphite.util import unpickle
+ 
+ try:
+   import rrdtool
+@@ -307,7 +309,7 @@ class WhisperFile(Leaf):
+ 
+     if exists(context_path):
+       fh = open(context_path, 'rb')
+-      context_data = pickle.load(fh)
++      context_data = unpickle.load(fh)
+       fh.close()
+     else:
+       context_data = {}
+diff --git a/webapp/graphite/util.py b/webapp/graphite/util.py
+index ada524d..54f425b 100644
+--- a/webapp/graphite/util.py
++++ b/webapp/graphite/util.py
+@@ -12,6 +12,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License."""
+ 
++try:
++  import cPickle as pickle
++  USING_CPICKLE = True
++except:
++  import pickle
++  USING_CPICKLE = False
++
++try:
++  from cStringIO import StringIO
++except ImportError:
++  from StringIO import StringIO
++
+ from django.core.exceptions import ObjectDoesNotExist
+ from django.contrib.auth.models import User
+ from graphite.account.models import Profile
+@@ -65,3 +77,52 @@ except Profile.DoesNotExist:
+   log.info("Default profile does not exist, creating it...")
+   defaultProfile = Profile(user=defaultUser)
+   defaultProfile.save()
++
++# This whole song & dance is due to pickle being insecure
++# The SafeUnpickler classes were largely derived from
++# http://nadiana.com/python-pickle-insecure
++# This code also lives in carbon.util
++if USING_CPICKLE:
++  class SafeUnpickler(object):
++    PICKLE_SAFE = {
++      'copy_reg': set(['_reconstructor']),
++      '__builtin__': set(['object']),
++    }
++
++    @classmethod
++    def find_class(cls, module, name):
++      if not module in cls.PICKLE_SAFE:
++        raise pickle.UnpicklingError('Attempting to unpickle unsafe module %s' % module)
++      __import__(module)
++      mod = sys.modules[module]
++      if not name in cls.PICKLE_SAFE[module]:
++        raise pickle.UnpicklingError('Attempting to unpickle unsafe class %s' % name)
++      return getattr(mod, name)
++
++    @classmethod
++    def loads(cls, pickle_string):
++      pickle_obj = pickle.Unpickler(StringIO(pickle_string))
++      pickle_obj.find_global = cls.find_class
++      return pickle_obj.load()
++
++else:
++  class SafeUnpickler(pickle.Unpickler):
++    PICKLE_SAFE = {
++      'copy_reg': set(['_reconstructor']),
++      '__builtin__': set(['object']),
++    }
++
++    def find_class(self, module, name):
++      if not module in self.PICKLE_SAFE:
++        raise pickle.UnpicklingError('Attempting to unpickle unsafe module %s' % module)
++      __import__(module)
++      mod = sys.modules[module]
++      if not name in self.PICKLE_SAFE[module]:
++        raise pickle.UnpicklingError('Attempting to unpickle unsafe class %s' % name)
++      return getattr(mod, name)
++
++    @classmethod
++    def loads(cls, pickle_string):
++      return cls(StringIO(pickle_string)).load()
++
++unpickle = SafeUnpickler
+diff --git a/webapp/graphite/whitelist/views.py b/webapp/graphite/whitelist/views.py
+index 5baec8d..36374d7 100644
+--- a/webapp/graphite/whitelist/views.py
++++ b/webapp/graphite/whitelist/views.py
+@@ -13,14 +13,12 @@ See the License for the specific language governing permissions and
+ limitations under the License."""
+ 
+ import os
+-try:
+-  import cPickle as pickle
+-except ImportError:
+-  import pickle
+ from random import randint
+ from django.http import HttpResponse
+ from django.conf import settings
+ 
++from graphite.util import unpickle
++
+ 
+ def add(request):
+   metrics = set( request.POST['metrics'].split() )
+@@ -43,7 +41,7 @@ def show(request):
+ 
+ def load_whitelist():
+   fh = open(settings.WHITELIST_FILE, 'rb')
+-  whitelist = pickle.load(fh)
++  whitelist = unpickle.load(fh)
+   fh.close()
+   return whitelist
diff --git a/graphite-web-0.9.10-fhs-thirdparty.patch b/graphite-web-0.9.10-fhs-thirdparty.patch
new file mode 100644
index 0000000..7f0535a
--- /dev/null
+++ b/graphite-web-0.9.10-fhs-thirdparty.patch
@@ -0,0 +1,185 @@
+diff -up ./check-dependencies.py.orig ./check-dependencies.py
+--- ./check-dependencies.py.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./check-dependencies.py	2012-11-24 14:09:50.040701191 -0700
+@@ -58,6 +58,19 @@ except:
+   django = None
+   fatal += 1
+ 
++# Test for pytz
++try:
++  import pytz
++except:
++  print "[FATAL] Unable to import the 'pytz' module, do you have pytz module installed for python %s?\n" % py_version
++  fatal += 1
++
++# Test for pyparsing
++try:
++  import pyparsing
++except:
++  print "[FATAL] Unable to import the 'pyparsing' module, do you have pyparsing module installed for python %s?\n" % py_version
++  fatal += 1
+ 
+ # Test for django-tagging
+ try:
+diff -up ./conf/graphite.wsgi.example.orig ./conf/graphite.wsgi.example
+--- ./conf/graphite.wsgi.example.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./conf/graphite.wsgi.example	2012-11-24 14:10:08.310633299 -0700
+@@ -1,5 +1,5 @@
+ import os, sys
+-sys.path.append('/opt/graphite/webapp')
++sys.path.append('/usr/share/graphite/webapp')
+ os.environ['DJANGO_SETTINGS_MODULE'] = 'graphite.settings'
+ 
+ import django.core.handlers.wsgi
+diff -up ./MANIFEST.in.orig ./MANIFEST.in
+--- ./MANIFEST.in.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./MANIFEST.in	2012-11-24 14:09:50.039701194 -0700
+@@ -5,7 +5,6 @@ include check-dependencies.py
+ include examples/*
+ include conf/*.example
+ include webapp/graphite/local_settings.py.example
+-include webapp/graphite/thirdparty/pytz/pytz-LICENSE.txt
+ recursive-include distro/ *
+ recursive-include webapp/graphite/ *.html
+ recursive-include webapp/content/ *
+diff -up ./setup.cfg.orig ./setup.cfg
+--- ./setup.cfg.orig	2012-11-24 14:09:51.776694909 -0700
++++ ./setup.cfg	2012-11-24 14:10:08.311633295 -0700
+@@ -1,6 +1,7 @@
+ [install]
+-prefix = /opt/graphite
+-install-lib = %(prefix)s/webapp
++#prefix = /opt/graphite
++#install-lib = %(prefix)s/webapp
++install-data = /usr/share/graphite
+ 
+ [bdist_rpm]
+ requires = Django => 1.1.4
+diff -up ./setup.py.orig ./setup.py
+--- ./setup.py.orig	2012-11-24 14:09:51.776694909 -0700
++++ ./setup.py	2012-11-24 14:10:08.312633291 -0700
+@@ -58,6 +58,7 @@ setup(
+   package_data={'graphite' :
+     ['templates/*', 'local_settings.py.example']},
+   scripts=glob('bin/*'),
+-  data_files=webapp_content.items() + storage_dirs + conf_files + examples,
++  #data_files=webapp_content.items() + storage_dirs + conf_files + examples,
++  data_files=webapp_content.items(),
+   **setup_kwargs
+ )
+diff -up ./webapp/graphite/cli/parser.py.orig ./webapp/graphite/cli/parser.py
+--- ./webapp/graphite/cli/parser.py.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./webapp/graphite/cli/parser.py	2012-11-24 14:09:51.777694906 -0700
+@@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ See the License for the specific language governing permissions and
+ limitations under the License."""
+ 
+-from graphite.thirdparty.pyparsing import *
++from pyparsing import *
+ 
+ grammar = Forward()
+ 
+diff -up ./webapp/graphite/local_settings.py.example.orig ./webapp/graphite/local_settings.py.example
+--- ./webapp/graphite/local_settings.py.example.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./webapp/graphite/local_settings.py.example	2012-11-24 14:10:08.313633288 -0700
+@@ -43,29 +43,29 @@
+ #####################################
+ # Change only GRAPHITE_ROOT if your install is merely shifted from /opt/graphite
+ # to somewhere else
+-#GRAPHITE_ROOT = '/opt/graphite'
++GRAPHITE_ROOT = '/usr/share/graphite'
+ 
+ # Most installs done outside of a separate tree such as /opt/graphite will only
+ # need to change these three settings. Note that the default settings for each
+ # of these is relative to GRAPHITE_ROOT
+-#CONF_DIR = '/opt/graphite/conf'
+-#STORAGE_DIR = '/opt/graphite/storage'
+-#CONTENT_DIR = '/opt/graphite/webapp/content'
++CONF_DIR = '/etc/graphite-web'
++STORAGE_DIR = '/var/lib/graphite-web'
++CONTENT_DIR = '/usr/share/graphite/webapp/content'
+ 
+ # To further or fully customize the paths, modify the following. Note that the
+ # default settings for each of these are relative to CONF_DIR and STORAGE_DIR
+ #
+ ## Webapp config files
+-#DASHBOARD_CONF = '/opt/graphite/conf/dashboard.conf'
+-#GRAPHTEMPLATES_CONF = '/opt/graphite/conf/graphTemplates.conf'
++#DASHBOARD_CONF = '/etc/graphite-web/dashboard.conf'
++#GRAPHTEMPLATES_CONF = '/etc/graphite-web/graphTemplates.conf'
+ 
+ ## Data directories
+ # NOTE: If any directory is unreadable in DATA_DIRS it will break metric browsing
+-#WHISPER_DIR = '/opt/graphite/storage/whisper'
+-#RRD_DIR = '/opt/graphite/storage/rrd'
+-#DATA_DIRS = [WHISPER_DIR, RRD_DIR] # Default: set from the above variables
+-#LOG_DIR = '/opt/graphite/storage/log/webapp'
+-#INDEX_FILE = '/opt/graphite/storage/index'  # Search index file
++WHISPER_DIR = '/var/lib/carbon/whisper/'
++RRD_DIR = '/var/lib/carbon/rrd'
++DATA_DIRS = [WHISPER_DIR, RRD_DIR] # Default: set from the above variables
++LOG_DIR = '/var/log/graphite-web/'
++INDEX_FILE = '/var/lib/graphite-web/index'  # Search index file
+ 
+ 
+ #####################################
+diff -up ./webapp/graphite/render/attime.py.orig ./webapp/graphite/render/attime.py
+--- ./webapp/graphite/render/attime.py.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./webapp/graphite/render/attime.py	2012-11-24 14:09:51.777694906 -0700
+@@ -16,11 +16,7 @@ from datetime import datetime,timedelta
+ from time import daylight
+ from django.conf import settings
+ 
+-try: # See if there is a system installation of pytz first
+-  import pytz
+-except ImportError: # Otherwise we fall back to Graphite's bundled version
+-  from graphite.thirdparty import pytz
+-
++import pytz
+ 
+ months = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec']
+ weekdays = ['sun','mon','tue','wed','thu','fri','sat']
+diff -up ./webapp/graphite/render/glyph.py.orig ./webapp/graphite/render/glyph.py
+--- ./webapp/graphite/render/glyph.py.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./webapp/graphite/render/glyph.py	2012-11-24 14:09:51.779694900 -0700
+@@ -22,10 +22,7 @@ from graphite.render.datalib import Time
+ from graphite.util import json
+ 
+ 
+-try: # See if there is a system installation of pytz first
+-  import pytz
+-except ImportError: # Otherwise we fall back to Graphite's bundled version
+-  from graphite.thirdparty import pytz
++import pytz
+ 
+ INFINITY = float('inf')
+ 
+diff -up ./webapp/graphite/render/grammar.py.orig ./webapp/graphite/render/grammar.py
+--- ./webapp/graphite/render/grammar.py.orig	2012-05-31 00:28:54.000000000 -0600
++++ ./webapp/graphite/render/grammar.py	2012-11-24 14:09:51.779694900 -0700
+@@ -1,4 +1,4 @@
+-from graphite.thirdparty.pyparsing import *
++from pyparsing import *
+ 
+ ParserElement.enablePackrat()
+ grammar = Forward()
+diff -up ./webapp/graphite/settings.py.orig ./webapp/graphite/settings.py
+--- ./webapp/graphite/settings.py.orig	2012-05-31 14:30:23.000000000 -0600
++++ ./webapp/graphite/settings.py	2012-11-24 14:09:51.780694897 -0700
+@@ -31,7 +31,6 @@ JAVASCRIPT_DEBUG = False
+ WEB_DIR = dirname( abspath(__file__) )
+ WEBAPP_DIR = dirname(WEB_DIR)
+ GRAPHITE_ROOT = dirname(WEBAPP_DIR)
+-THIRDPARTY_DIR = join(WEB_DIR,'thirdparty')
+ # Initialize additional path variables
+ # Defaults for these are set after local_settings is imported
+ CONTENT_DIR = ''
+@@ -50,8 +49,6 @@ DATA_DIRS = []
+ CLUSTER_SERVERS = []
+ 
+ sys.path.insert(0, WEBAPP_DIR)
+-# Allow local versions of the libs shipped in thirdparty to take precedence
+-sys.path.append(THIRDPARTY_DIR)
+ 
+ # Memcache settings
+ MEMCACHE_HOSTS = []
diff --git a/graphite-web.spec b/graphite-web.spec
index 4e1449d..297625f 100644
--- a/graphite-web.spec
+++ b/graphite-web.spec
@@ -1,19 +1,20 @@
 %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
 
 Name:           graphite-web
-Version:        0.9.12
-Release:        2%{?dist}
+Version:        0.9.10
+Release:        9%{?dist}
 Summary:        A Django webapp for enterprise scalable realtime graphing
 Group:          Applications/Internet
 
 License:        ASL 2.0
 URL:            https://launchpad.net/graphite/
-Source0:        https://github.com/graphite-project/graphite-web/archive/0.9.12.tar.gz#/%{name}-%{version}.tar.gz
+Source0:        https://github.com/downloads/graphite-project/graphite-web/graphite-web-0.9.10.tar.gz
 Source1:        graphite-web-vhost.conf
 Source2:        graphite-web-README.fedora
 Source3:        graphite-web-logrotate.fedora
 Source4:        graphite-web-README.selinux
-Patch0:         graphite-web-0.9.12-fhs-thirdparty.patch
+Patch0:         graphite-web-0.9.10-fhs-thirdparty.patch
+Patch1:         graphite-web-0.9.10-CVE-2013-5093.patch
 BuildRoot:      %{_tmppath}/graphite-web-%{version}-%{release}-root-%(%{__id_u} -n)
 
 BuildArch:      noarch
@@ -22,9 +23,9 @@ Requires:       python-whisper, mod_wsgi, pytz, pyparsing, python-simplejson
 Requires:       dejavu-sans-fonts, dejavu-serif-fonts, pycairo, django-tagging
 
 %if 0%{?fedora} <= 17
-Requires:       python-sqlite2, Django >= 1.3
+Requires:       python-sqlite2, Django
 %else
-Requires:       python-django >= 1.3
+Requires:       python-django
 %endif
 
 
@@ -62,7 +63,9 @@ SELinux labeling for graphite files.
 %setup -q -n graphite-web-%{version}
 # Patch for Filesystem Hierarchy Standard
 # Remove thridparty libs
+# https://github.com/hggh/graphite-web-upstream/commit/47361a2707f904a8b817ca96deeddabcdbaaa534.patch
 %patch0 -p1
+%patch1 -p1
 %{__install} -m 644 %{SOURCE2} README.fedora
 %{__install} -m 644 %{SOURCE4} README.selinux
 
@@ -139,17 +142,13 @@ fi
 %doc README.selinux
 
 %changelog
-* Tue Sep 17 2013 Jonathan Steffan <jsteffan at fedoraproject.org> - 0.9.12-2
+* Thu Sep 19 2013 Jonathan Steffan <jsteffan at fedoraproject.org> - 0.9.10-9
 - Don't ship js/ext/resources/*.swf (RHBZ#1000253)
+- Patch for CVE-2013-5093 (RHBZ#1000060)
 
-* Mon Sep 02 2013 Jonathan Steffan <jsteffan at fedoraproject.org> - 0.9.12-1
-- Update to 0.9.12
-- Require Django >= 1.3
+* Wed Mar 13 2013 Jonathan Steffan <jsteffan at fedoraproject.org> - 0.9.10-8
 - Add EL5 conditional for SELinux policycoreutils
 
-* Sat Aug 03 2013 Fedora Release Engineering <rel-eng at lists.fedoraproject.org> - 0.9.10-8
-- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
-
 * Wed Mar 13 2013 Jonathan Steffan <jsteffan at fedoraproject.org> - 0.9.10-7
 - Update required fonts to actually include fonts (RHBZ#917361)
 
diff --git a/sources b/sources
index 2110667..0fa30cf 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-c09f19fc0076cbadec64039f161bafc0  graphite-web-0.9.12.tar.gz
+f54bf784139c7aef441f5cc1bc66dab4  graphite-web-0.9.10.tar.gz


More information about the scm-commits mailing list