Ejemplo n.º 1
0
    def loop(self):
        """Main body of daemon request loop"""
        LOG.info(_LI('privsep daemon running as pid %s'), os.getpid())

        # We *are* this context now - any calls through it should be
        # executed locally.
        self.context.set_client_mode(False)

        for msgid, msg in self.channel:
            LOG.debug('privsep: request[%(msgid)s]: %(req)s',
                      {'msgid': msgid, 'req': msg})
            try:
                reply = self._process_cmd(*msg)
            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__)
                reply = (Message.ERR.value, cls_name, e.args)

            try:
                LOG.debug('privsep: reply[%(msgid)s]: %(reply)s',
                          {'msgid': msgid, 'reply': reply})
                self.channel.send((msgid, reply))
            except IOError as e:
                if e.errno == errno.EPIPE:
                    # Write stream closed, exit loop
                    break
                raise

        LOG.debug('Socket closed, shutting down privsep daemon')
Ejemplo n.º 2
0
    def __init__(self, context):
        """Start privsep daemon using exec()

        Uses sudo/rootwrap to gain privileges.
        """

        listen_sock = socket.socket(socket.AF_UNIX)

        # Note we listen() on the unprivileged side, and connect to it
        # from the privileged process.  This means there is no exposed
        # attack point on the privileged side.

        # NB: Permissions on sockets are not checked on some (BSD) Unices
        # so create socket in a private directory for safety.  Privsep
        # daemon will (initially) be running as root, so will still be
        # able to connect to sock path.
        tmpdir = tempfile.mkdtemp()  # NB: created with 0700 perms

        try:
            sockpath = os.path.join(tmpdir, 'privsep.sock')
            listen_sock.bind(sockpath)
            listen_sock.listen(1)

            cmd = self._helper_command(context, sockpath)
            LOG.info(_LI('Running privsep helper: %s'), cmd)
            proc = subprocess.Popen(cmd, shell=False, stderr=_fd_logger())
            if proc.wait() != 0:
                msg = (_LE('privsep helper command exited non-zero (%s)') %
                       proc.returncode)
                LOG.critical(msg)
                raise FailedToDropPrivileges(msg)
            LOG.info(_LI('Spawned new privsep daemon via rootwrap'))

            sock, _addr = listen_sock.accept()
            LOG.debug('Accepted privsep connection to %s', sockpath)

        finally:
            # Don't need listen_sock anymore, so clean up.
            listen_sock.close()
            try:
                os.unlink(sockpath)
            except OSError as e:
                if e.errno != errno.ENOENT:
                    raise
            os.rmdir(tmpdir)

        super(RootwrapClientChannel, self).__init__(sock)
Ejemplo n.º 3
0
    def __init__(self, context):
        """Start privsep daemon using exec()

        Uses sudo/rootwrap to gain privileges.
        """

        listen_sock = socket.socket(socket.AF_UNIX)

        # Note we listen() on the unprivileged side, and connect to it
        # from the privileged process.  This means there is no exposed
        # attack point on the privileged side.

        # NB: Permissions on sockets are not checked on some (BSD) Unices
        # so create socket in a private directory for safety.  Privsep
        # daemon will (initially) be running as root, so will still be
        # able to connect to sock path.
        tmpdir = tempfile.mkdtemp()  # NB: created with 0700 perms

        try:
            sockpath = os.path.join(tmpdir, 'privsep.sock')
            listen_sock.bind(sockpath)
            listen_sock.listen(1)

            cmd = context.helper_command(sockpath)
            LOG.info(_LI('Running privsep helper: %s'), cmd)
            proc = subprocess.Popen(cmd, shell=False, stderr=_fd_logger())
            if proc.wait() != 0:
                msg = (_LE('privsep helper command exited non-zero (%s)') %
                       proc.returncode)
                LOG.critical(msg)
                raise FailedToDropPrivileges(msg)
            LOG.info(_LI('Spawned new privsep daemon via rootwrap'))

            sock, _addr = listen_sock.accept()
            LOG.debug('Accepted privsep connection to %s', sockpath)

        finally:
            # Don't need listen_sock anymore, so clean up.
            listen_sock.close()
            try:
                os.unlink(sockpath)
            except OSError as e:
                if e.errno != errno.ENOENT:
                    raise
            os.rmdir(tmpdir)

        super(RootwrapClientChannel, self).__init__(sock)
Ejemplo n.º 4
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),
                })
Ejemplo n.º 5
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),
            })
Ejemplo n.º 6
0
def helper_main():
    """Start privileged process, serving requests over a Unix socket."""

    cfg.CONF.register_cli_opts([
        cfg.StrOpt('privsep_context', required=True),
        cfg.StrOpt('privsep_sock_path', required=True),
    ])

    logging.register_options(cfg.CONF)

    cfg.CONF(args=sys.argv[1:], project='privsep')
    logging.setup(cfg.CONF, 'privsep')

    # We always log to stderr.  Replace the root logger we just set up.
    replace_logging(pylogging.StreamHandler(sys.stderr))

    LOG.info(_LI('privsep daemon starting'))

    context = importutils.import_class(cfg.CONF.privsep_context)
    from oslo_privsep import priv_context  # Avoid circular import
    if not isinstance(context, priv_context.PrivContext):
        LOG.fatal(
            _LE('--privsep_context must be the (python) name of a '
                'PrivContext object'))

    sock = socket.socket(socket.AF_UNIX)
    sock.connect(cfg.CONF.privsep_sock_path)
    set_cloexec(sock)
    channel = comm.ServerChannel(sock)

    # Channel is set up, so fork off daemon "in the background" and exit
    if os.fork() != 0:
        # parent
        return

    # child

    # Note we don't move into a new process group/session like a
    # regular daemon might, since we _want_ to remain associated with
    # the originating (unprivileged) process.

    try:
        Daemon(channel, context).run()
    except Exception as e:
        LOG.exception(e)
        sys.exit(str(e))

    LOG.debug('privsep daemon exiting')
    sys.exit(0)
Ejemplo n.º 7
0
def helper_main():
    """Start privileged process, serving requests over a Unix socket."""

    cfg.CONF.register_cli_opts([
        cfg.StrOpt('privsep_context', required=True),
        cfg.StrOpt('privsep_sock_path', required=True),
    ])

    logging.register_options(cfg.CONF)

    cfg.CONF(args=sys.argv[1:], project='privsep')
    logging.setup(cfg.CONF, 'privsep')

    # We always log to stderr.  Replace the root logger we just set up.
    replace_logging(pylogging.StreamHandler(sys.stderr))

    LOG.info(_LI('privsep daemon starting'))

    context = importutils.import_class(cfg.CONF.privsep_context)
    from oslo_privsep import priv_context   # Avoid circular import
    if not isinstance(context, priv_context.PrivContext):
        LOG.fatal(_LE('--privsep_context must be the (python) name of a '
                      'PrivContext object'))

    sock = socket.socket(socket.AF_UNIX)
    sock.connect(cfg.CONF.privsep_sock_path)
    set_cloexec(sock)
    channel = comm.ServerChannel(sock)

    # Channel is set up, so fork off daemon "in the background" and exit
    if os.fork() != 0:
        # parent
        return

    # child

    # Note we don't move into a new process group/session like a
    # regular daemon might, since we _want_ to remain associated with
    # the originating (unprivileged) process.

    try:
        Daemon(channel, context).run()
    except Exception as e:
        LOG.exception(e)
        sys.exit(str(e))

    LOG.debug('privsep daemon exiting')
    sys.exit(0)
Ejemplo n.º 8
0
    def loop(self):
        """Main body of daemon request loop"""
        LOG.info(_LI('privsep daemon running as pid %s'), os.getpid())

        # We *are* this context now - any calls through it should be
        # executed locally.
        self.context.set_client_mode(False)

        for msgid, msg in self.channel:
            LOG.debug('privsep: request[%(msgid)s]: %(req)s', {
                'msgid': msgid,
                'req': msg
            })
            try:
                reply = self._process_cmd(*msg)
            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__)
                reply = (Message.ERR.value, cls_name, e.args)

            try:
                LOG.debug('privsep: reply[%(msgid)s]: %(reply)s', {
                    'msgid': msgid,
                    'reply': reply
                })
                self.channel.send((msgid, reply))
            except IOError as e:
                if e.errno == errno.EPIPE:
                    # Write stream closed, exit loop
                    break
                raise

        LOG.debug('Socket closed, shutting down privsep daemon')
Ejemplo n.º 9
0
    def __init__(self, context):
        """Start privsep daemon using exec()

        Uses sudo/rootwrap to gain privileges.
        """

        # We need to be able to reconstruct the context object in the new
        # python process we'll get after rootwrap/sudo.  This means we
        # need to construct the context object and store it somewhere
        # globally accessible, and then use that python name to find it
        # again in the new python interpreter.  Yes, it's all a bit
        # clumsy, and none of it is required when using the fork-based
        # alternative above.
        # These asserts here are just attempts to catch errors earlier.
        # TODO(gus): Consider replacing with setuptools entry_points.
        assert context.pypath is not None, (
            'RootwrapClientChannel requires priv_context '
            'pypath to be specified')
        assert importutils.import_class(context.pypath) is context, (
            'RootwrapClientChannel requires priv_context pypath '
            'for context object')

        listen_sock = socket.socket(socket.AF_UNIX)

        # Note we listen() on the unprivileged side, and connect to it
        # from the privileged process.  This means there is no exposed
        # attack point on the privileged side.

        # NB: Permissions on sockets are not checked on some (BSD) Unices
        # so create socket in a private directory for safety.  Privsep
        # daemon will (initially) be running as root, so will still be
        # able to connect to sock path.
        tmpdir = tempfile.mkdtemp()  # NB: created with 0700 perms

        try:
            sockpath = os.path.join(tmpdir, 'privsep.sock')
            listen_sock.bind(sockpath)
            listen_sock.listen(1)

            cmd = shlex.split(context.conf.helper_command) + [
                '--privsep_context', context.pypath,
                '--privsep_sock_path', sockpath]
            LOG.info(_LI('Running privsep helper: %s'), cmd)
            proc = subprocess.Popen(cmd, shell=False, stderr=_fd_logger())
            if proc.wait() != 0:
                msg = (_LE('privsep helper command exited non-zero (%s)') %
                       proc.returncode)
                LOG.critical(msg)
                raise FailedToDropPrivileges(msg)
            LOG.info(_LI('Spawned new privsep daemon via rootwrap'))

            sock, _addr = listen_sock.accept()
            LOG.debug('Accepted privsep connection to %s', sockpath)

        finally:
            # Don't need listen_sock anymore, so clean up.
            listen_sock.close()
            try:
                os.unlink(sockpath)
            except OSError as e:
                if e.errno != errno.ENOENT:
                    raise
            os.rmdir(tmpdir)

        super(RootwrapClientChannel, self).__init__(sock)