From 046b196babc4e5f18bc8f6870ba8b9272898482c Mon Sep 17 00:00:00 2001 From: Daniel Danner Date: Sun, 11 Jan 2015 14:06:59 +0100 Subject: [PATCH] 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. --- attic/remote.py | 21 ++++++++++++++++++++- attic/testsuite/repository.py | 5 ++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/attic/remote.py b/attic/remote.py index f2a0aed06..5ee91703c 100644 --- a/attic/remote.py +++ b/attic/remote.py @@ -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 @@ def serve(self): 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 @@ def fetch_from_cache(args): 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 diff --git a/attic/testsuite/repository.py b/attic/testsuite/repository.py index 91a822803..e088921cf 100644 --- a/attic/testsuite/repository.py +++ b/attic/testsuite/repository.py @@ -4,7 +4,7 @@ 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):