Graphite patch for http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=2013-5093
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment