Merge "Validate devices and partitions to avoid directory traversals"
This commit is contained in:
commit
47385a2d8f
@ -32,7 +32,8 @@ import simplejson
|
||||
import swift.common.db
|
||||
from swift.common.db import AccountBroker
|
||||
from swift.common.utils import get_logger, get_param, hash_path, public, \
|
||||
normalize_timestamp, split_path, storage_directory, TRUE_VALUES
|
||||
normalize_timestamp, split_path, storage_directory, TRUE_VALUES, \
|
||||
validate_device_partition
|
||||
from swift.common.constraints import ACCOUNT_LISTING_LIMIT, \
|
||||
check_mount, check_float, check_utf8
|
||||
from swift.common.db_replicator import ReplicatorRpc
|
||||
@ -69,6 +70,7 @@ class AccountController(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
drive, part, account = split_path(unquote(req.path), 3)
|
||||
validate_device_partition(drive, part)
|
||||
except ValueError, err:
|
||||
self.logger.increment('DELETE.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -96,6 +98,7 @@ class AccountController(object):
|
||||
try:
|
||||
drive, part, account, container = split_path(unquote(req.path),
|
||||
3, 4)
|
||||
validate_device_partition(drive, part)
|
||||
except ValueError, err:
|
||||
self.logger.increment('PUT.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -164,6 +167,7 @@ class AccountController(object):
|
||||
try:
|
||||
drive, part, account, container = split_path(unquote(req.path),
|
||||
3, 4)
|
||||
validate_device_partition(drive, part)
|
||||
except ValueError, err:
|
||||
self.logger.increment('HEAD.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -201,6 +205,7 @@ class AccountController(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
drive, part, account = split_path(unquote(req.path), 3)
|
||||
validate_device_partition(drive, part)
|
||||
except ValueError, err:
|
||||
self.logger.increment('GET.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -305,11 +310,12 @@ class AccountController(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
post_args = split_path(unquote(req.path), 3)
|
||||
drive, partition, hash = post_args
|
||||
validate_device_partition(drive, partition)
|
||||
except ValueError, err:
|
||||
self.logger.increment('REPLICATE.errors')
|
||||
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):
|
||||
self.logger.increment('REPLICATE.errors')
|
||||
return HTTPInsufficientStorage(drive=drive, request=req)
|
||||
@ -329,6 +335,7 @@ class AccountController(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
drive, part, account = split_path(unquote(req.path), 3)
|
||||
validate_device_partition(drive, part)
|
||||
except ValueError, err:
|
||||
self.logger.increment('POST.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
|
@ -263,6 +263,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."""
|
||||
|
||||
|
@ -33,7 +33,7 @@ import swift.common.db
|
||||
from swift.common.db import ContainerBroker
|
||||
from swift.common.utils import get_logger, get_param, hash_path, public, \
|
||||
normalize_timestamp, storage_directory, split_path, validate_sync_to, \
|
||||
TRUE_VALUES
|
||||
TRUE_VALUES, validate_device_partition
|
||||
from swift.common.constraints import CONTAINER_LISTING_LIMIT, \
|
||||
check_mount, check_float, check_utf8
|
||||
from swift.common.bufferedhttp import http_connect
|
||||
@ -145,6 +145,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:
|
||||
self.logger.increment('DELETE.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -195,6 +196,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:
|
||||
self.logger.increment('PUT.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -264,6 +266,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:
|
||||
self.logger.increment('HEAD.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -298,6 +301,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:
|
||||
self.logger.increment('GET.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
@ -421,11 +425,12 @@ class ContainerController(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
post_args = split_path(unquote(req.path), 3)
|
||||
drive, partition, hash = post_args
|
||||
validate_device_partition(drive, partition)
|
||||
except ValueError, err:
|
||||
self.logger.increment('REPLICATE.errors')
|
||||
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):
|
||||
self.logger.increment('REPLICATE.errors')
|
||||
return HTTPInsufficientStorage(drive=drive, request=req)
|
||||
@ -445,6 +450,7 @@ class ContainerController(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
drive, part, account, container = split_path(unquote(req.path), 4)
|
||||
validate_device_partition(drive, part)
|
||||
except ValueError, err:
|
||||
self.logger.increment('POST.errors')
|
||||
return HTTPBadRequest(body=str(err), content_type='text/plain',
|
||||
|
@ -38,7 +38,7 @@ from eventlet import sleep, Timeout, tpool
|
||||
from swift.common.utils import mkdirs, normalize_timestamp, public, \
|
||||
storage_directory, hash_path, renamer, fallocate, \
|
||||
split_path, drop_buffer_cache, get_logger, write_pickle, \
|
||||
TRUE_VALUES
|
||||
TRUE_VALUES, validate_device_partition
|
||||
from swift.common.bufferedhttp import http_connect
|
||||
from swift.common.constraints import check_object_creation, check_mount, \
|
||||
check_float, check_utf8
|
||||
@ -494,6 +494,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:
|
||||
self.logger.increment('POST.errors')
|
||||
return HTTPBadRequest(body=str(err), request=request,
|
||||
@ -554,6 +555,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:
|
||||
self.logger.increment('PUT.errors')
|
||||
return HTTPBadRequest(body=str(err), request=request,
|
||||
@ -653,6 +655,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:
|
||||
self.logger.increment('GET.errors')
|
||||
return HTTPBadRequest(body=str(err), request=request,
|
||||
@ -743,6 +746,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:
|
||||
self.logger.increment('HEAD.errors')
|
||||
resp = HTTPBadRequest(request=request)
|
||||
@ -789,6 +793,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:
|
||||
self.logger.increment('DELETE.errors')
|
||||
return HTTPBadRequest(body=str(e), request=request,
|
||||
@ -843,6 +848,7 @@ class ObjectController(object):
|
||||
try:
|
||||
device, partition, suffix = split_path(
|
||||
unquote(request.path), 2, 3, True)
|
||||
validate_device_partition(device, partition)
|
||||
except ValueError, e:
|
||||
self.logger.increment('REPLICATE.errors')
|
||||
return HTTPBadRequest(body=str(e), request=request,
|
||||
|
@ -192,6 +192,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()
|
||||
|
Loading…
Reference in New Issue
Block a user