From 07055331b52644bba0df67faa0f2f52bd48f3ed4 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Thu, 15 Jul 2010 10:56:01 +0200 Subject: [PATCH] [entropy.db.skel,entropy.client.interfaces.db] disallow unprivileged repository update --- client/text_repositories.py | 5 ++-- libraries/entropy/client/interfaces/db.py | 27 +++++++++++++++---- .../entropy/client/interfaces/repository.py | 10 ++++--- libraries/entropy/db/skel.py | 4 +++ 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/client/text_repositories.py b/client/text_repositories.py index e80326924..d34c712a8 100644 --- a/client/text_repositories.py +++ b/client/text_repositories.py @@ -48,9 +48,8 @@ def repositories(options): try: if options[0] == "update": # check if I am root - er_txt = darkred(_("You must be either root or in this group:")) + \ - " " + etpConst['sysgroup'] - if not entropy.tools.is_user_in_entropy_group(): + er_txt = darkred(_("You must be root")) + if not entropy.tools.is_root(): print_error(er_txt) return 1 diff --git a/libraries/entropy/client/interfaces/db.py b/libraries/entropy/client/interfaces/db.py index cae73e11c..32af81891 100644 --- a/libraries/entropy/client/interfaces/db.py +++ b/libraries/entropy/client/interfaces/db.py @@ -25,7 +25,7 @@ from entropy.dump import dumpobj from entropy.cache import EntropyCacher from entropy.db import EntropyRepository from entropy.exceptions import RepositoryError, SystemDatabaseError, \ - ConnectionError + ConnectionError, PermissionDenied from entropy.security import Repository as RepositorySecurity from entropy.misc import TimeScheduled from entropy.i18n import _ @@ -695,7 +695,7 @@ class AvailablePackagesRepositoryUpdater(object): if self._entropy.installed_repository() is not None: try: # client db can be absent self._entropy.installed_repository().createAllIndexes() - except (OperationalError, IntegrityError,): + except (DatabaseError, OperationalError, IntegrityError,): pass const_set_nice_level(old_prio) @@ -838,7 +838,7 @@ class AvailablePackagesRepositoryUpdater(object): rc = fetcher.download() if rc in ("-1", "-2", "-3", "-4"): return False - const_setup_file(filepath, etpConst['entropygid'], 0o664) + const_setup_file(filepath, etpConst['entropygid'], 0o644) return True def _is_repository_unlocked(self): @@ -1016,6 +1016,8 @@ class AvailablePackagesRepositoryUpdater(object): continue continue + const_setup_file(to_mypath, etpConst['entropygid'], 0o644) + finally: shutil.rmtree(tmpdir, True) @@ -1758,6 +1760,11 @@ class AvailablePackagesRepositoryUpdater(object): def update(self): + # disallow unprivileged update + if not entropy.tools.is_root(): + raise PermissionDenied( + "cannot update repository as unprivileged user") + self.__show_repository_information() # this calls writes self._last_rev which is used to write back @@ -1949,8 +1956,13 @@ class AvailablePackagesRepositoryUpdater(object): continue return EntropyRepositoryBase.REPOSITORY_GENERIC_ERROR - if os.path.isfile(dbfile) and os.access(dbfile, os.W_OK): - const_setup_file(dbfile, etpConst['entropygid'], 0o664) + # make sure that all the repository files are stored with proper + # permissions to avoid possible XSS and trust boundary problems. + downloaded_files.append(dbfile) + for downloaded_file in sorted(set(downloaded_files)): + if os.path.isfile(downloaded_file) and \ + os.access(downloaded_file, os.W_OK | os.R_OK): + const_setup_file(downloaded_file, etpConst['entropygid'], 0o644) # remove garbage left around for path in files_to_remove: @@ -1992,6 +2004,11 @@ class AvailablePackagesRepository(EntropyRepository): subclass of EntropyRepository. It implements the update() method in order to make possible to update the repository. """ + def __init__(self, *args, **kwargs): + EntropyRepository.__init__(self, *args, **kwargs) + # ensure proper repository file permissions + if entropy.tools.is_root() and os.path.isfile(self._db_path): + const_setup_file(self._db_path, etpConst['entropygid'], 0o644) @staticmethod def update(entropy_client, repository_id, force, gpg): diff --git a/libraries/entropy/client/interfaces/repository.py b/libraries/entropy/client/interfaces/repository.py index 2d1f5e5cc..5133f9165 100644 --- a/libraries/entropy/client/interfaces/repository.py +++ b/libraries/entropy/client/interfaces/repository.py @@ -16,7 +16,7 @@ import subprocess from entropy.i18n import _ from entropy.const import etpConst, const_debug_write -from entropy.exceptions import RepositoryError +from entropy.exceptions import RepositoryError, PermissionDenied from entropy.output import blue, darkred, red, darkgreen, bold from entropy.db.exceptions import Error @@ -67,8 +67,12 @@ class Repository: for repo in self.repo_ids: # handle - status = self._entropy.get_repository(repo).update(self._entropy, - repo, self.force, self._gpg_feature) + try: + status = self._entropy.get_repository(repo).update(self._entropy, + repo, self.force, self._gpg_feature) + except PermissionDenied: + status = EntropyRepositoryBase.REPOSITORY_PERMISSION_DENIED_ERROR + if status == EntropyRepositoryBase.REPOSITORY_ALREADY_UPTODATE: self.already_updated = True elif status == EntropyRepositoryBase.REPOSITORY_NOT_AVAILABLE: diff --git a/libraries/entropy/db/skel.py b/libraries/entropy/db/skel.py index e8f67330f..59baba514 100644 --- a/libraries/entropy/db/skel.py +++ b/libraries/entropy/db/skel.py @@ -3512,6 +3512,7 @@ class EntropyRepositoryBase(TextInterface, EntropyRepositoryPluginStore, object) REPOSITORY_NOT_AVAILABLE = -2 REPOSITORY_GENERIC_ERROR = -3 REPOSITORY_CHECKSUM_ERROR = -4 + REPOSITORY_PERMISSION_DENIED_ERROR = -5 REPOSITORY_UPDATED_OK = 0 @staticmethod @@ -3527,6 +3528,9 @@ class EntropyRepositoryBase(TextInterface, EntropyRepositoryPluginStore, object) EntropyRepositoryBase.REPOSITORY_UPDATED_OK If your repository is not supposed to be remotely updated, just ignore this method. + Otherwise, if you intend to implement this method, make sure that + any unprivileged call raises entropy.exceptions.PermissionDenied(). + Only superuser should call this method. @param entropy_client: Entropy Client based object @type entropy_client: entropy.client.interfaces.Client