Example #1
0
    def _process_cmd(self, msgid, cmd, *args):
        """Executes the requested command in an execution thread.

        This executes a call within a thread executor and returns the results
        of the execution.

        :param msgid: The message identifier.
        :param cmd: The `Message` type indicating the command type.
        :param args: The function, args, and kwargs if a Message.CALL type.
        :return: A tuple of the return status, optional call output, and
                 optional error information.
        """
        if cmd == Message.PING:
            return (Message.PONG.value,)

        try:
            if cmd != Message.CALL:
                raise ProtocolError(_('Unknown privsep cmd: %s') % cmd)

            # Extract the callable and arguments
            name, f_args, f_kwargs = args
            func = importutils.import_class(name)
            if not self.context.is_entrypoint(func):
                msg = _('Invalid privsep function: %s not exported') % name
                raise NameError(msg)

            ret = func(*f_args, **f_kwargs)
            return (Message.RET.value, ret)
        except Exception as e:
            LOG.debug(
                'privsep: Exception during request[%(msgid)s]: '
                '%(err)s', {'msgid': msgid, 'err': e}, exc_info=True)
            cls = e.__class__
            cls_name = '%s.%s' % (cls.__module__, cls.__name__)
            return (Message.ERR.value, cls_name, e.args)
Example #2
0
    def _process_cmd(self, cmd, *args):
        if cmd == Message.PING:
            return (Message.PONG.value, )

        elif cmd == Message.CALL:
            name, f_args, f_kwargs = args
            func = importutils.import_class(name)

            if not self.context.is_entrypoint(func):
                msg = _('Invalid privsep function: %s not exported') % name
                raise NameError(msg)

            ret = func(*f_args, **f_kwargs)
            return (Message.RET.value, ret)

        raise ProtocolError(_('Unknown privsep cmd: %s') % cmd)
Example #3
0
    def _reader_main(self, reader):
        """This thread owns and demuxes the read channel"""
        with self.lock:
            self.running = True
        for msg in reader:
            msgid, data = msg
            if msgid is None:
                self.out_of_band(data)
            else:
                with self.lock:
                    if msgid not in self.outstanding_msgs:
                        raise AssertionError("msgid should in "
                                             "outstanding_msgs.")
                    self.outstanding_msgs[msgid].set_result(data)

        # EOF.  Perhaps the privileged process exited?
        # Send an IOError to any oustanding waiting readers.  Assuming
        # the write direction is also closed, any new writes should
        # get an immediate similar error.
        LOG.debug('EOF on privsep read channel')

        exc = IOError(_('Premature eof waiting for privileged process'))
        with self.lock:
            for mbox in self.outstanding_msgs.values():
                mbox.set_exception(exc)
            self.running = False
Example #4
0
    def _process_cmd(self, cmd, *args):
        if cmd == Message.PING:
            return (Message.PONG.value,)

        elif cmd == Message.CALL:
            name, f_args, f_kwargs = args
            func = importutils.import_class(name)

            if not self.context.is_entrypoint(func):
                msg = _('Invalid privsep function: %s not exported') % name
                raise NameError(msg)

            ret = func(*f_args, **f_kwargs)
            return (Message.RET.value, ret)

        raise ProtocolError(_('Unknown privsep cmd: %s') % cmd)
Example #5
0
 def _read_n(self, n):
     """Read exactly N bytes.  Raises EOFError on premature EOF"""
     data = []
     while n > 0:
         tmp = self.readsock.recv(n)
         if not tmp:
             raise EOFError(_("Premature EOF during deserialization"))
         data.append(tmp)
         n -= len(tmp)
     return b"".join(data)
Example #6
0
def setuid(user_id_or_name):
    try:
        new_uid = int(user_id_or_name)
    except (TypeError, ValueError):
        new_uid = pwd.getpwnam(user_id_or_name).pw_uid
    if new_uid != 0:
        try:
            os.setuid(new_uid)
        except OSError:
            msg = _('Failed to set uid %s') % new_uid
            LOG.critical(msg)
            raise FailedToDropPrivileges(msg)
Example #7
0
def setgid(group_id_or_name):
    try:
        new_gid = int(group_id_or_name)
    except (TypeError, ValueError):
        new_gid = grp.getgrnam(group_id_or_name).gr_gid
    if new_gid != 0:
        try:
            os.setgid(new_gid)
        except OSError:
            msg = _('Failed to set gid %s') % new_gid
            LOG.critical(msg)
            raise FailedToDropPrivileges(msg)
Example #8
0
 def exchange_ping(self):
     try:
         # exchange "ready" messages
         reply = self.send_recv((Message.PING.value, ))
         success = reply[0] == Message.PONG
     except Exception as e:
         LOG.exception('Error while sending initial PING to privsep: %s', e)
         success = False
     if not success:
         msg = _('Privsep daemon failed to start')
         LOG.critical(msg)
         raise FailedToDropPrivileges(msg)
Example #9
0
def setgid(group_id_or_name):
    try:
        new_gid = int(group_id_or_name)
    except (TypeError, ValueError):
        new_gid = grp.getgrnam(group_id_or_name).gr_gid
    if new_gid != 0:
        try:
            os.setgid(new_gid)
        except OSError:
            msg = _('Failed to set gid %s') % new_gid
            LOG.critical(msg)
            raise FailedToDropPrivileges(msg)
Example #10
0
def setuid(user_id_or_name):
    try:
        new_uid = int(user_id_or_name)
    except (TypeError, ValueError):
        new_uid = pwd.getpwnam(user_id_or_name).pw_uid
    if new_uid != 0:
        try:
            os.setuid(new_uid)
        except OSError:
            msg = _('Failed to set uid %s') % new_uid
            LOG.critical(msg)
            raise FailedToDropPrivileges(msg)
Example #11
0
 def exchange_ping(self):
     try:
         # exchange "ready" messages
         reply = self.send_recv((Message.PING.value,))
         success = reply[0] == Message.PONG
     except Exception as e:
         LOG.exception(
             _LE('Error while sending initial PING to privsep: %s'), e)
         success = False
     if not success:
         msg = _('Privsep daemon failed to start')
         LOG.critical(msg)
         raise FailedToDropPrivileges(msg)
Example #12
0
 def remote_call(self, name, args, kwargs):
     result = self.send_recv((Message.CALL.value, name, args, kwargs))
     if result[0] == Message.RET:
         # (RET, return value)
         return result[1]
     elif result[0] == Message.ERR:
         # (ERR, exc_type, args)
         #
         # TODO(gus): see what can be done to preserve traceback
         # (without leaking local values)
         exc_type = importutils.import_class(result[1])
         raise exc_type(*result[2])
     else:
         raise ProtocolError(_('Unexpected response: %r') % result)
Example #13
0
 def remote_call(self, name, args, kwargs):
     result = self.send_recv((Message.CALL.value, name, args, kwargs))
     if result[0] == Message.RET:
         # (RET, return value)
         return result[1]
     elif result[0] == Message.ERR:
         # (ERR, exc_type, args)
         #
         # TODO(gus): see what can be done to preserve traceback
         # (without leaking local values)
         exc_type = importutils.import_class(result[1])
         raise exc_type(*result[2])
     else:
         raise ProtocolError(_('Unexpected response: %r') % result)
Example #14
0
    def _drop_privs(self):
        try:
            # Keep current capabilities across setuid away from root.
            capabilities.set_keepcaps(True)

            if self.group is not None:
                try:
                    os.setgroups([])
                except OSError:
                    msg = _('Failed to remove supplemental groups')
                    LOG.critical(msg)
                    raise FailedToDropPrivileges(msg)

            if self.user is not None:
                setuid(self.user)

            if self.group is not None:
                setgid(self.group)

        finally:
            capabilities.set_keepcaps(False)

        LOG.info(_LI('privsep process running with uid/gid: %(uid)s/%(gid)s'),
                 {
                     'uid': os.getuid(),
                     'gid': os.getgid()
                 })

        capabilities.drop_all_caps_except(self.caps, self.caps, [])

        def fmt_caps(capset):
            if not capset:
                return 'none'
            fc = [capabilities.CAPS_BYVALUE.get(c, str(c)) for c in capset]
            fc.sort()
            return '|'.join(fc)

        eff, prm, inh = capabilities.get_caps()
        LOG.info(
            _LI('privsep process running with capabilities '
                '(eff/prm/inh): %(eff)s/%(prm)s/%(inh)s'), {
                    'eff': fmt_caps(eff),
                    'prm': fmt_caps(prm),
                    'inh': fmt_caps(inh),
                })
Example #15
0
    def _reader_main(self, reader):
        """This thread owns and demuxes the read channel"""
        for msg in reader:
            msgid, data = msg
            with self.lock:
                assert msgid in self.outstanding_msgs
                self.outstanding_msgs[msgid].set_result(data)

        # EOF.  Perhaps the privileged process exited?
        # Send an IOError to any oustanding waiting readers.  Assuming
        # the write direction is also closed, any new writes should
        # get an immediate similar error.
        LOG.debug("EOF on privsep read channel")

        exc = IOError(_("Premature eof waiting for privileged process"))
        with self.lock:
            for mbox in self.outstanding_msgs.values():
                mbox.set_exception(exc)
Example #16
0
    def _drop_privs(self):
        try:
            # Keep current capabilities across setuid away from root.
            capabilities.set_keepcaps(True)

            if self.group is not None:
                try:
                    os.setgroups([])
                except OSError:
                    msg = _('Failed to remove supplemental groups')
                    LOG.critical(msg)
                    raise FailedToDropPrivileges(msg)

            if self.user is not None:
                setuid(self.user)

            if self.group is not None:
                setgid(self.group)

        finally:
            capabilities.set_keepcaps(False)

        LOG.info(_LI('privsep process running with uid/gid: %(uid)s/%(gid)s'),
                 {'uid': os.getuid(), 'gid': os.getgid()})

        capabilities.drop_all_caps_except(self.caps, self.caps, [])

        def fmt_caps(capset):
            if not capset:
                return 'none'
            fc = [capabilities.CAPS_BYVALUE.get(c, str(c))
                  for c in capset]
            fc.sort()
            return '|'.join(fc)

        eff, prm, inh = capabilities.get_caps()
        LOG.info(
            _LI('privsep process running with capabilities '
                '(eff/prm/inh): %(eff)s/%(prm)s/%(inh)s'),
            {
                'eff': fmt_caps(eff),
                'prm': fmt_caps(prm),
                'inh': fmt_caps(inh),
            })
Example #17
0

LOG = logging.getLogger(__name__)


def CapNameOrInt(value):
    value = str(value).strip()
    try:
        return capabilities.CAPS_BYNAME[value]
    except KeyError:
        return int(value)


OPTS = [
    cfg.StrOpt('user',
               help=_('User that the privsep daemon should run as.')),
    cfg.StrOpt('group',
               help=_('Group that the privsep daemon should run as.')),
    cfg.Opt('capabilities',
            type=types.List(CapNameOrInt), default=[],
            help=_('List of Linux capabilities retained by the privsep '
                   'daemon.')),
    cfg.StrOpt('helper_command',
               default=('sudo privsep-helper'
                        # TODO(gus): how do I find a good config path?
                        ' --config-file=/etc/$project/$project.conf'),
               help=_('Command to invoke via sudo/rootwrap to start '
                      'the privsep daemon.')),
]

_ENTRYPOINT_ATTR = 'privsep_entrypoint'
Example #18
0
from oslo_privsep import capabilities
from oslo_privsep import daemon

LOG = logging.getLogger(__name__)


def CapNameOrInt(value):
    value = str(value).strip()
    try:
        return capabilities.CAPS_BYNAME[value]
    except KeyError:
        return int(value)


OPTS = [
    cfg.StrOpt('user', help=_('User that the privsep daemon should run as.')),
    cfg.StrOpt('group',
               help=_('Group that the privsep daemon should run as.')),
    cfg.Opt('capabilities',
            type=types.List(CapNameOrInt),
            default=[],
            help=_('List of Linux capabilities retained by the privsep '
                   'daemon.')),
    cfg.IntOpt('thread_pool_size',
               min=1,
               help=_("The number of threads available for privsep to "
                      "concurrently run processes. Defaults to the number of "
                      "CPU cores in the system."),
               default=multiprocessing.cpu_count(),
               sample_default='multiprocessing.cpu_count()'),
    cfg.StrOpt('helper_command',
Example #19
0
from oslo_privsep._i18n import _, _LW, _LE


LOG = logging.getLogger(__name__)


def CapNameOrInt(value):
    value = str(value).strip()
    try:
        return capabilities.CAPS_BYNAME[value]
    except KeyError:
        return int(value)


OPTS = [
    cfg.StrOpt("user", help=_("User that the privsep daemon should run as.")),
    cfg.StrOpt("group", help=_("Group that the privsep daemon should run as.")),
    cfg.Opt(
        "capabilities",
        type=types.List(CapNameOrInt),
        default=[],
        help=_("List of Linux capabilities retained by the privsep " "daemon."),
    ),
    cfg.StrOpt(
        "helper_command",
        help=_(
            "Command to invoke to start the privsep daemon if "
            'not using the "fork" method. '
            "If not specified, a default is generated using "
            '"sudo privsep-helper" and arguments designed to '
            "recreate the current configuration. "
Example #20
0
from oslo_privsep import daemon
from oslo_privsep._i18n import _, _LW, _LE

LOG = logging.getLogger(__name__)


def CapNameOrInt(value):
    value = str(value).strip()
    try:
        return capabilities.CAPS_BYNAME[value]
    except KeyError:
        return int(value)


OPTS = [
    cfg.StrOpt('user', help=_('User that the privsep daemon should run as.')),
    cfg.StrOpt('group',
               help=_('Group that the privsep daemon should run as.')),
    cfg.Opt('capabilities',
            type=types.List(CapNameOrInt),
            default=[],
            help=_('List of Linux capabilities retained by the privsep '
                   'daemon.')),
    cfg.StrOpt('helper_command',
               help=_('Command to invoke to start the privsep daemon if '
                      'not using the "fork" method. '
                      'If not specified, a default is generated using '
                      '"sudo privsep-helper" and arguments designed to '
                      'recreate the current configuration. '
                      'This command must accept suitable --privsep_context '
                      'and --privsep_sock_path arguments.')),
Example #21
0

LOG = logging.getLogger(__name__)


def CapNameOrInt(value):
    value = str(value).strip()
    try:
        return capabilities.CAPS_BYNAME[value]
    except KeyError:
        return int(value)


OPTS = [
    cfg.StrOpt('user',
               help=_('User that the privsep daemon should run as.')),
    cfg.StrOpt('group',
               help=_('Group that the privsep daemon should run as.')),
    cfg.Opt('capabilities',
            type=types.List(CapNameOrInt), default=[],
            help=_('List of Linux capabilities retained by the privsep '
                   'daemon.')),
    cfg.StrOpt('helper_command',
               help=_('Command to invoke to start the privsep daemon if '
                      'not using the "fork" method. '
                      'If not specified, a default is generated using '
                      '"sudo privsep-helper" and arguments designed to '
                      'recreate the current configuration. '
                      'This command must accept suitable --privsep_context '
                      'and --privsep_sock_path arguments.')),
]