diff options
Diffstat (limited to 'src/leap/bitmask/vpn/privilege.py')
| -rw-r--r-- | src/leap/bitmask/vpn/privilege.py | 210 | 
1 files changed, 210 insertions, 0 deletions
diff --git a/src/leap/bitmask/vpn/privilege.py b/src/leap/bitmask/vpn/privilege.py new file mode 100644 index 00000000..e8ed5576 --- /dev/null +++ b/src/leap/bitmask/vpn/privilege.py @@ -0,0 +1,210 @@ +# -*- coding: utf-8 -*- +# privilege_policies.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +""" +Helpers to determine if the needed policies for privilege escalation +are operative under this client run. +""" + +import commands +import os +import subprocess +import platform +import time + +from abc import ABCMeta, abstractmethod + +from twisted.logger import Logger +from twisted.python.procutils import which + +logger = Logger() + + +flags_STANDALONE = False + + +class NoPolkitAuthAgentAvailable(Exception): +    pass + + +class NoPkexecAvailable(Exception): +    pass + + +def is_missing_policy_permissions(): +    """ +    Returns True if we do not have implemented a policy checker for this +    platform, or if the policy checker exists but it cannot find the +    appropriate policy mechanisms in place. + +    :rtype: bool +    """ +    _system = platform.system() +    platform_checker = _system + "PolicyChecker" +    policy_checker = globals().get(platform_checker, None) +    if not policy_checker: +        # it is true that we miss permission to escalate +        # privileges without asking for password each time. +        logger.debug("we could not find a policy checker implementation " +                     "for %s" % (_system,)) +        return True +    return policy_checker().is_missing_policy_permissions() + + +class PolicyChecker: +    """ +    Abstract PolicyChecker class +    """ + +    __metaclass__ = ABCMeta + +    @abstractmethod +    def is_missing_policy_permissions(self): +        """ +        Returns True if we could not find any policy mechanisms that +        are defined to be in used for this particular platform. + +        :rtype: bool +        """ +        return True + + +class LinuxPolicyChecker(PolicyChecker): +    """ +    PolicyChecker for Linux +    """ +    LINUX_POLKIT_FILE = ("/usr/share/polkit-1/actions/" +                         "se.leap.bitmask.policy") +    LINUX_POLKIT_FILE_BUNDLE = ("/usr/share/polkit-1/actions/" +                                "se.leap.bitmask.bundle.policy") +    PKEXEC_BIN = 'pkexec' + +    @classmethod +    def get_polkit_path(self): +        """ +        Returns the polkit file path. + +        :rtype: str +        """ +        return (self.LINUX_POLKIT_FILE_BUNDLE if flags_STANDALONE +                else self.LINUX_POLKIT_FILE) + +    def is_missing_policy_permissions(self): +        # FIXME this name is quite confusing, it does not have anything to do +        # with file permissions. +        """ +        Returns True if we could not find the appropriate policykit file +        in place + +        :rtype: bool +        """ +        path = self.get_polkit_path() +        return not os.path.isfile(path) + +    @classmethod +    def maybe_pkexec(self): +        """ +        Checks whether pkexec is available in the system, and +        returns the path if found. + +        Might raise: +            NoPkexecAvailable, +            NoPolkitAuthAgentAvailable. + +        :returns: a list of the paths where pkexec is to be found +        :rtype: list +        """ +        if self._is_pkexec_in_system(): +            if not self.is_up(): +                self.launch() +                time.sleep(2) +            if self.is_up(): +                pkexec_possibilities = which(self.PKEXEC_BIN) +                # leap_assert(len(pkexec_possibilities) > 0, +                #             "We couldn't find pkexec") +                if not pkexec_possibilities: +                    logger.error("We couldn't find pkexec") +                    raise Exception("We couldn't find pkexec") +                return pkexec_possibilities +            else: +                logger.warning("No polkit auth agent found. pkexec " + +                               "will use its own auth agent.") +                raise NoPolkitAuthAgentAvailable() +        else: +            logger.warning("System has no pkexec") +            raise NoPkexecAvailable() + +    @classmethod +    def launch(self): +        """ +        Tries to launch policykit +        """ +        env = None +        if flags_STANDALONE: +            # This allows us to send to subprocess the environment configs that +            # works for the standalone bundle (like the PYTHONPATH) +            env = dict(os.environ) +            # The LD_LIBRARY_PATH is set on the launcher but not forwarded to +            # subprocess unless we do so explicitly. +            env["LD_LIBRARY_PATH"] = os.path.abspath("./lib/") +        try: +            # We need to quote the command because subprocess call +            # will do "sh -c 'foo'", so if we do not quoute it we'll end +            # up with a invocation to the python interpreter. And that +            # is bad. +            logger.debug("Trying to launch polkit agent") +            subprocess.call(["python -m leap.bitmask.util.polkit_agent"], +                            shell=True, env=env) +        except Exception as exc: +            logger.exception(exc) + +    @classmethod +    def is_up(self): +        """ +        Checks if a polkit daemon is running. + +        :return: True if it's running, False if it's not. +        :rtype: boolean +        """ +        # Note that gnome-shell does not uses a separate process for the +        # polkit-agent, it uses a polkit-agent within its own process so we +        # can't ps-grep a polkit process, we can ps-grep gnome-shell itself. + +        # the [x] thing is to avoid grep match itself +        polkit_options = [ +            'ps aux | grep "polkit-[g]nome-authentication-agent-1"', +            'ps aux | grep "polkit-[k]de-authentication-agent-1"', +            'ps aux | grep "polkit-[m]ate-authentication-agent-1"', +            'ps aux | grep "[l]xpolkit"', +            'ps aux | grep "[l]xsession"', +            'ps aux | grep "[g]nome-shell"', +            'ps aux | grep "[f]ingerprint-polkit-agent"', +            'ps aux | grep "[x]fce-polkit"', +        ] +        is_running = [commands.getoutput(cmd) for cmd in polkit_options] + +        return any(is_running) + +    @classmethod +    def _is_pkexec_in_system(self): +        """ +        Checks the existence of the pkexec binary in system. +        """ +        pkexec_path = which('pkexec') +        if len(pkexec_path) == 0: +            return False +        return True  | 
