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

openSUSE Build Service is sponsored by