From ac3b08b33934c247345673bb8c746ffda17b5a60 Mon Sep 17 00:00:00 2001 From: Necoro <> Date: Mon, 15 Oct 2007 08:54:54 +0000 Subject: use anonymous pipe instead of socket -- fixes security issue --- portato.py | 52 +++++++++++++++++++++++++++++---------------------- portato/constants.py | 9 ++++----- portato/plistener.py | 53 +++++++++++++++++----------------------------------- 3 files changed, 51 insertions(+), 63 deletions(-) diff --git a/portato.py b/portato.py index 765e070..93fc88e 100755 --- a/portato.py +++ b/portato.py @@ -14,7 +14,7 @@ from __future__ import with_statement, absolute_import -import sys, os +import sys, os, subprocess import gettext, locale from optparse import OptionParser @@ -47,6 +47,9 @@ def main (): parser.add_option("-e", "--ebuild", action = "store", dest = "ebuild", help = _("opens the ebuild viewer instead of launching Portato")) + parser.add_option("-p", "--pipe", action = "store", dest = "pipe", + help = _("file descriptor to use to communicate with the listener (internal use only)")) + parser.add_option("-x", "--validate", action = "store", dest = "validate", metavar="PLUGIN", help = _("validates the given plugin xml instead of launching Portato")) @@ -58,7 +61,7 @@ def main (): # evaluate parser's results if options.check: # run pychecker - os.environ['PYCHECKER'] = "--limit 50" + os.environ['PYCHECKER'] = "--limit 100" import pychecker.checker if len(args): # additional arguments overwrite given frontend @@ -75,9 +78,9 @@ def main (): print _("'%(frontend)s' should be installed, but cannot be imported. This is definitly a bug. (%(error)s)") % {"frontend": options.frontend, "error": e[0]} sys.exit(1) - if options.ebuild: + if options.ebuild: # show ebuild show_ebuild(options.ebuild) - elif options.validate: + elif options.validate: # validate a plugin from lxml import etree try: etree.XMLSchema(file = XSD_LOCATION).assertValid(etree.parse(options.validate)) @@ -90,25 +93,30 @@ def main (): else: print _("Validation succeeded.") return - elif options.nolistener or os.getuid() == 0: - listener.set_send() + + elif options.nolistener or os.getuid() == 0: # start GUI + if options.pipe: + listener.set_send(int(options.pipe)) + else: + listener.set_send() + run() - else: # start listener and start us again in root modus - pid = os.fork() - if pid == 0: # start portato in child - additional = [] - if options.check: - additional.append("-c") - if options.frontend: - additional.extend(["-f", options.frontend]) - - cmd = SU_COMMAND.split() - env = os.environ.copy() - env.update(DBUS_SESSION_BUS_ADDRESS="") - os.execvpe(cmd[0], cmd+["%s --no-listener %s" % (sys.argv[0], " ".join(additional))], env = env) - - else: # start listener - listener.set_recv() + + else: # start us again in root modus and launch listener + read, write = os.pipe() + additional = [] + if options.check: + additional.append("--check") + if options.frontend: + additional.extend(["--frontend", options.frontend]) + + # set DBUS_SESSION_BUS_ADDRESS to "" to make dbus work as root ;) + env = os.environ.copy() + env.update(DBUS_SESSION_BUS_ADDRESS="") + cmd = SU_COMMAND.split() + subprocess.Popen(cmd+["%s --no-listener --pipe %d %s" % (sys.argv[0], write, " ".join(additional))], env = env, close_fds = False) + + listener.set_recv(read) if __name__ == "__main__": main() diff --git a/portato/constants.py b/portato/constants.py index d871e78..3aea05d 100644 --- a/portato/constants.py +++ b/portato/constants.py @@ -40,8 +40,6 @@ These should be set during the installation. @type STD_FRONTEND: string @var SU_COMMAND: command to execute to "su" @type SU_COMMAND: string -@var SOCKET: path to socket for communication between listener and GUI -@type SOCKET: string """ import os from os.path import join as pjoin @@ -56,6 +54,8 @@ SETTINGS_DIR = pjoin(HOME, "."+APP) CONFIG_DIR = "/etc/portato/" CONFIG_LOCATION = pjoin(CONFIG_DIR, "portato.cfg") +VAR_DIR = "/var/portato/" + DATA_DIR = "portato/gui/templates/" PLUGIN_DIR = "plugins/" @@ -67,10 +67,9 @@ APP_ICON = pjoin(ICON_DIR, "portato-icon.png") LOCALE_DIR = "i18n/" -FRONTENDS = ["gtk" ,"qt"] +FRONTENDS = ["gtk"] STD_FRONTEND = "gtk" SU_COMMAND = "gksu -D 'Portato'" -SOCKET = "/tmp/portato.socket" -USE_CATAPULT = False +USE_CATAPULT = True diff --git a/portato/plistener.py b/portato/plistener.py index 452bee6..a24a262 100644 --- a/portato/plistener.py +++ b/portato/plistener.py @@ -21,7 +21,7 @@ try: except ImportError: pynotify = None -from .constants import SOCKET, APP +from .constants import APP from .helper import debug, warning class PListener (object): @@ -29,29 +29,17 @@ class PListener (object): This listener starts programs as the user while the GUI runs as root. @ivar _recv: listener socket - @type _recv: socket.socket + @type _recv: int @ivar _send: sender socket - @type _send: socket.socket""" + @type _send: int""" - def set_recv (self): - self._recv = socket.socket(socket.AF_UNIX) + def set_recv (self, pipe): + self._recv = pipe - try: - self._recv.bind(SOCKET) - except socket.error, e: - if int(e[0]) == 98: # already existing - delete - os.unlink(SOCKET) - self._recv.bind(SOCKET) - else: - raise - - self._recv.listen(1) - con, addr = self._recv.accept() - while True: try: - len = con.recv(4) - string = con.recv(int(len)) + len = os.read(self._recv, 4) + string = os.read(self._recv, int(len)) data = string.split("\0") @@ -62,10 +50,9 @@ class PListener (object): elif data[0] == "close": break except KeyboardInterrupt: - pass + break - con.close() - self._recv.close() + os.close(self._recv) def do_cmd (self, cmdlist): """Starts a command as the user. @@ -87,20 +74,15 @@ class PListener (object): n.set_urgency(int(urgency)) n.show() - def set_send (self): - self._send = socket.socket(socket.AF_UNIX) - try: - self._send.connect(SOCKET) - except socket.error, e: - if e[0] in [111, 2]: # can't connect - warning(_("Listener has not been started.")) - self._send = None - else: - raise + def set_send (self, pipe = None): + if pipe is None: + warning(_("Listener has not been started.")) + + self._send = pipe def __send (self, string): - self._send.sendall("%4d" % len(string)) - self._send.sendall(string) + os.write(self._send, "%4d" % len(string)) + os.write(self._send, string) def send_notify (self, base = "", descr = "", icon = "", urgency = None): if self._send is None: @@ -124,5 +106,4 @@ class PListener (object): def close (self): if self._send is not None: self.__send("close") - self._send.close() - os.unlink(SOCKET) + os.close(self._send) -- cgit v1.2.3-70-g09d2