File dir-traversals-checks.patch of Package openstack-swift
From 63b9f3bf602dd19712b2ba19046153f5f8495cc5 Mon Sep 17 00:00:00 2001
From: Vincent Untz <vuntz@suse.com>
Date: Tue, 19 Jun 2012 12:11:06 +0200
Subject: [PATCH] Validate devices and partitions to avoid directory
traversals
swift.common.utils.validate_device_partition is a new function to check
that a device and a partition are valid. This means that they don't
contain '/' and are not '.' or '..'.
We use this new function every time we get devices and partitions from a
request.
Fix bug 1005908
Change-Id: Ia545ba8f877e85b4b576d6d7d09d890877ea6d34
---
swift/account/server.py | 11 +++++++++--
swift/common/utils.py | 22 ++++++++++++++++++++++
swift/container/server.py | 11 +++++++++--
swift/obj/server.py | 9 ++++++++-
test/unit/common/test_utils.py | 21 +++++++++++++++++++++
5 files changed, 69 insertions(+), 5 deletions(-)
diff --git a/swift/account/server.py b/swift/account/server.py
index 800b3c0..5353907 100644
--- a/swift/account/server.py
+++ b/swift/account/server.py
@@ -31,7 +31,8 @@ import simplejson
from swift.common.db import AccountBroker
from swift.common.utils import get_logger, get_param, hash_path, \
- normalize_timestamp, split_path, storage_directory
+ normalize_timestamp, split_path, storage_directory, \
+ validate_device_partition
from swift.common.constraints import ACCOUNT_LISTING_LIMIT, \
check_mount, check_float, check_utf8
from swift.common.db_replicator import ReplicatorRpc
@@ -63,6 +64,7 @@ class AccountController(object):
"""Handle HTTP DELETE request."""
try:
drive, part, account = split_path(unquote(req.path), 3)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -83,6 +85,7 @@ class AccountController(object):
try:
drive, part, account, container = split_path(unquote(req.path),
3, 4)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -142,6 +145,7 @@ class AccountController(object):
try:
drive, part, account, container = split_path(unquote(req.path),
3, 4)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -173,6 +177,7 @@ class AccountController(object):
"""Handle HTTP GET request."""
try:
drive, part, account = split_path(unquote(req.path), 3)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -266,10 +271,11 @@ class AccountController(object):
"""
try:
post_args = split_path(unquote(req.path), 3)
+ drive, partition, hash = post_args
+ validate_device_partition(drive, partition)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
- drive, partition, hash = post_args
if self.mount_check and not check_mount(self.root, drive):
return Response(status='507 %s is not mounted' % drive)
try:
@@ -284,6 +290,7 @@ class AccountController(object):
"""Handle HTTP POST request."""
try:
drive, part, account = split_path(unquote(req.path), 3)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
diff --git a/swift/common/utils.py b/swift/common/utils.py
index 47edce8..1e10028 100644
--- a/swift/common/utils.py
+++ b/swift/common/utils.py
@@ -243,6 +243,28 @@ def split_path(path, minsegs=1, maxsegs=None, rest_with_last=False):
return segs
+def validate_device_partition(device, partition):
+ """
+ Validate that a device and a partition are valid and won't lead to
+ directory traversal when used.
+
+ :param device: device to validate
+ :param partition: partition to validate
+ :raises: ValueError if given an invalid device or partition
+ """
+ invalid_device = False
+ invalid_partition = False
+ if not device or '/' in device or device in ['.', '..']:
+ invalid_device = True
+ if not partition or '/' in partition or partition in ['.', '..']:
+ invalid_partition = True
+
+ if invalid_device:
+ raise ValueError('Invalid device: %s' % quote(device or ''))
+ elif invalid_partition:
+ raise ValueError('Invalid partition: %s' % quote(partition or ''))
+
+
class NullLogger():
"""A no-op logger for eventlet wsgi."""
diff --git a/swift/container/server.py b/swift/container/server.py
index 8a18cfd..6278edb 100644
--- a/swift/container/server.py
+++ b/swift/container/server.py
@@ -31,7 +31,8 @@ from webob.exc import HTTPAccepted, HTTPBadRequest, HTTPConflict, \
from swift.common.db import ContainerBroker
from swift.common.utils import get_logger, get_param, hash_path, \
- normalize_timestamp, storage_directory, split_path, validate_sync_to
+ normalize_timestamp, storage_directory, split_path, validate_sync_to, \
+ validate_device_partition
from swift.common.constraints import CONTAINER_LISTING_LIMIT, \
check_mount, check_float, check_utf8
from swift.common.bufferedhttp import http_connect
@@ -136,6 +137,7 @@ class ContainerController(object):
try:
drive, part, account, container, obj = split_path(
unquote(req.path), 4, 5, True)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -176,6 +178,7 @@ class ContainerController(object):
try:
drive, part, account, container, obj = split_path(
unquote(req.path), 4, 5, True)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -235,6 +238,7 @@ class ContainerController(object):
try:
drive, part, account, container, obj = split_path(
unquote(req.path), 4, 5, True)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -263,6 +267,7 @@ class ContainerController(object):
try:
drive, part, account, container, obj = split_path(
unquote(req.path), 4, 5, True)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
@@ -377,10 +382,11 @@ class ContainerController(object):
"""
try:
post_args = split_path(unquote(req.path), 3)
+ drive, partition, hash = post_args
+ validate_device_partition(drive, partition)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
- drive, partition, hash = post_args
if self.mount_check and not check_mount(self.root, drive):
return Response(status='507 %s is not mounted' % drive)
try:
@@ -395,6 +401,7 @@ class ContainerController(object):
"""Handle HTTP POST request."""
try:
drive, part, account, container = split_path(unquote(req.path), 4)
+ validate_device_partition(drive, part)
except ValueError, err:
return HTTPBadRequest(body=str(err), content_type='text/plain',
request=req)
diff --git a/swift/obj/server.py b/swift/obj/server.py
index 9cca16b..612e14c 100644
--- a/swift/obj/server.py
+++ b/swift/obj/server.py
@@ -37,7 +37,8 @@ from eventlet import sleep, Timeout, tpool
from swift.common.utils import mkdirs, normalize_timestamp, \
storage_directory, hash_path, renamer, fallocate, \
- split_path, drop_buffer_cache, get_logger, write_pickle
+ split_path, drop_buffer_cache, get_logger, write_pickle, \
+ validate_device_partition
from swift.common.bufferedhttp import http_connect
from swift.common.constraints import check_object_creation, check_mount, \
check_float, check_utf8
@@ -480,6 +481,7 @@ class ObjectController(object):
try:
device, partition, account, container, obj = \
split_path(unquote(request.path), 5, 5, True)
+ validate_device_partition(device, partition)
except ValueError, err:
return HTTPBadRequest(body=str(err), request=request,
content_type='text/plain')
@@ -532,6 +534,7 @@ class ObjectController(object):
try:
device, partition, account, container, obj = \
split_path(unquote(request.path), 5, 5, True)
+ validate_device_partition(device, partition)
except ValueError, err:
return HTTPBadRequest(body=str(err), request=request,
content_type='text/plain')
@@ -621,6 +624,7 @@ class ObjectController(object):
try:
device, partition, account, container, obj = \
split_path(unquote(request.path), 5, 5, True)
+ validate_device_partition(device, partition)
except ValueError, err:
return HTTPBadRequest(body=str(err), request=request,
content_type='text/plain')
@@ -695,6 +699,7 @@ class ObjectController(object):
try:
device, partition, account, container, obj = \
split_path(unquote(request.path), 5, 5, True)
+ validate_device_partition(device, partition)
except ValueError, err:
resp = HTTPBadRequest(request=request)
resp.content_type = 'text/plain'
@@ -734,6 +739,7 @@ class ObjectController(object):
try:
device, partition, account, container, obj = \
split_path(unquote(request.path), 5, 5, True)
+ validate_device_partition(device, partition)
except ValueError, e:
return HTTPBadRequest(body=str(e), request=request,
content_type='text/plain')
@@ -781,6 +787,7 @@ class ObjectController(object):
try:
device, partition, suffix = split_path(
unquote(request.path), 2, 3, True)
+ validate_device_partition(device, partition)
except ValueError, e:
return HTTPBadRequest(body=str(e), request=request,
content_type='text/plain')
diff --git a/test/unit/common/test_utils.py b/test/unit/common/test_utils.py
index f7e33c4..e897414 100644
--- a/test/unit/common/test_utils.py
+++ b/test/unit/common/test_utils.py
@@ -189,6 +189,27 @@ class TestUtils(unittest.TestCase):
except ValueError, err:
self.assertEquals(str(err), 'Invalid path: o%0An%20e')
+ def test_validate_device_partition(self):
+ """ Test swift.common.utils.validate_device_partition """
+ utils.validate_device_partition('foo', 'bar')
+ self.assertRaises(ValueError, utils.validate_device_partition, '', '')
+ self.assertRaises(ValueError, utils.validate_device_partition, '', 'foo')
+ self.assertRaises(ValueError, utils.validate_device_partition, 'foo', '')
+ self.assertRaises(ValueError, utils.validate_device_partition, 'foo/bar', 'foo')
+ self.assertRaises(ValueError, utils.validate_device_partition, 'foo', 'foo/bar')
+ self.assertRaises(ValueError, utils.validate_device_partition, '.', 'foo')
+ self.assertRaises(ValueError, utils.validate_device_partition, '..', 'foo')
+ self.assertRaises(ValueError, utils.validate_device_partition, 'foo', '.')
+ self.assertRaises(ValueError, utils.validate_device_partition, 'foo', '..')
+ try:
+ utils.validate_device_partition,('o\nn e', 'foo')
+ except ValueError, err:
+ self.assertEquals(str(err), 'Invalid device: o%0An%20e')
+ try:
+ utils.validate_device_partition,('foo', 'o\nn e')
+ except ValueError, err:
+ self.assertEquals(str(err), 'Invalid partition: o%0An%20e')
+
def test_NullLogger(self):
""" Test swift.common.utils.NullLogger """
sio = StringIO()
--
1.7.7