Merge "Validate devices and partitions to avoid directory traversals"

This commit is contained in:
Jenkins 2012-07-09 21:57:08 +00:00 committed by Gerrit Code Review
commit 47385a2d8f
5 changed files with 67 additions and 5 deletions

View File

@ -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',

View File

@ -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."""

View File

@ -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',

View File

@ -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,

View File

@ -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()