Only allow whitelisted RPC calls in server mode

Without this check, the client is able to call any method of
RepositoryServer and Repository, potentially circumventing
restrict_to_paths or even run arbitrary code.
This commit is contained in:
Daniel Danner 2015-01-11 14:06:59 +01:00
parent 4ab4ecc7af
commit 046b196bab
2 changed files with 24 additions and 2 deletions

View file

@ -22,8 +22,23 @@ class ConnectionClosed(Error):
class PathNotAllowed(Error):
"""Repository path not allowed"""
class InvalidRPCMethod(Error):
"""RPC method is not valid"""
class RepositoryServer(object):
rpc_methods = (
'__len__',
'check',
'commit',
'delete',
'get',
'list',
'negotiate',
'open',
'put',
'repair',
'rollback',
)
def __init__(self, restrict_to_paths):
self.repository = None
@ -47,6 +62,8 @@ class RepositoryServer(object):
for type, msgid, method, args in unpacker:
method = method.decode('ascii')
try:
if not method in self.rpc_methods:
raise InvalidRPCMethod(method)
try:
f = getattr(self, method)
except AttributeError:
@ -155,8 +172,10 @@ class RemoteRepository(object):
raise IntegrityError(res)
elif error == b'PathNotAllowed':
raise PathNotAllowed(*res)
if error == b'ObjectNotFound':
elif error == b'ObjectNotFound':
raise Repository.ObjectNotFound(res[0], self.location.orig)
elif error == b'InvalidRPCMethod':
raise InvalidRPCMethod(*res)
raise self.RPCError(error)
else:
yield res

View file

@ -4,7 +4,7 @@ import tempfile
from attic.testsuite.mock import patch
from attic.hashindex import NSIndex
from attic.helpers import Location, IntegrityError, UpgradableLock
from attic.remote import RemoteRepository
from attic.remote import RemoteRepository, InvalidRPCMethod
from attic.repository import Repository
from attic.testsuite import AtticTestCase
@ -319,6 +319,9 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
def open(self, create=False):
return RemoteRepository(Location('__testsuite__:' + os.path.join(self.tmppath, 'repository')), create=create)
def test_invalid_rpc(self):
self.assert_raises(InvalidRPCMethod, lambda: self.repository.call('__init__', None))
class RemoteRepositoryCheckTestCase(RepositoryCheckTestCase):