Example #1
0
 def e(s):
     try:
         parse_display_name(None, opts, s)
     except Exception:
         pass
     else:
         raise Exception("parse_display_name should fail for %s" % s)
Example #2
0
 def t(s, e):
     r = parse_display_name(None, opts, s)
     if e:
         for k, v in e.items():
             actual = r.get(k)
             assert actual == v, "expected %s but got %s from parse_display_name(%s)=%s" % (
                 v, actual, s, r)
Example #3
0
    def start_proxy(self, client_proto, c, auth_caps):
        assert client_proto.authenticator is not None

        #find the target server session:
        def disconnect(msg):
            self.send_disconnect(client_proto, msg)

        sessions = client_proto.authenticator.get_sessions()
        if sessions is None:
            disconnect("no sessions found")
            return
        debug("start_proxy(%s, {..}, %s) found sessions: %s", client_proto,
              auth_caps, sessions)
        uid, gid, displays, env_options, session_options = sessions
        #debug("unused options: %s, %s", env_options, session_options)
        if len(displays) == 0:
            disconnect("no displays found")
            return
        display = c.strget("display")
        proxy_virtual_display = os.environ["DISPLAY"]
        #ensure we don't loop back to the proxy:
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        if display == proxy_virtual_display:
            disconnect("invalid display")
            return
        if display:
            if display not in displays:
                disconnect("display not found")
                return
        else:
            if len(displays) != 1:
                disconnect(
                    "please specify a display (more than one available)")
                return
            display = displays[0]

        debug("start_proxy(%s, {..}, %s) using server display at: %s",
              client_proto, auth_caps, display)

        def parse_error(*args):
            disconnect("invalid display string")
            log.warn("parse error on %s: %s", display, args)
            raise Exception("parse error on %s: %s" % (display, args))

        opts = make_defaults_struct()
        opts.username = c.strget("username")
        disp_desc = parse_display_name(parse_error, opts, display)
        debug("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc)
        except Exception, e:
            log.error("cannot start proxy connection to %s: %s", disp_desc, e)
            disconnect("failed to connect to display")
            return
Example #4
0
    def start_proxy(self, client_proto, c, auth_caps):
        assert client_proto.authenticator is not None
        #find the target server session:
        def disconnect(msg):
            self.send_disconnect(client_proto, msg)
        sessions = client_proto.authenticator.get_sessions()
        if sessions is None:
            disconnect("no sessions found")
            return
        debug("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions)
        uid, gid, displays, env_options, session_options = sessions
        #debug("unused options: %s, %s", env_options, session_options)
        if len(displays)==0:
            disconnect("no displays found")
            return
        display = c.strget("display")
        proxy_virtual_display = os.environ["DISPLAY"]
        #ensure we don't loop back to the proxy:
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        if display==proxy_virtual_display:
            disconnect("invalid display")
            return
        if display:
            if display not in displays:
                disconnect("display not found")
                return
        else:
            if len(displays)!=1:
                disconnect("please specify a display (more than one available)")
                return
            display = displays[0]

        debug("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display)
        def parse_error(*args):
            disconnect("invalid display string")
            log.warn("parse error on %s: %s", display, args)
            raise Exception("parse error on %s: %s" % (display, args))
        opts = make_defaults_struct()
        opts.username = c.strget("username")
        disp_desc = parse_display_name(parse_error, opts, display)
        debug("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc)
        except Exception, e:
            log.error("cannot start proxy connection to %s: %s", disp_desc, e)
            disconnect("failed to connect to display")
            return
Example #5
0
    def proxy_session(self, client_proto, c, auth_caps, sessions):
        def disconnect(reason, *extras):
            log("disconnect(%s, %s)", reason, extras)
            self.send_disconnect(client_proto, reason, *extras)
        uid, gid, displays, env_options, session_options = sessions
        if POSIX:
            if getuid()==0:
                if uid==0 or gid==0:
                    log.error("Error: proxy instances cannot run as root")
                    log.error(" use a different uid and gid (ie: nobody)")
                    disconnect(AUTHENTICATION_ERROR, "cannot run proxy instances as root")
                    return
            else:
                uid = getuid()
                gid = getgid()
            username = get_username_for_uid(uid)
            groups = get_groups(username)
            log("username(%i)=%s, groups=%s", uid, username, groups)
        else:
            #the auth module recorded the username we authenticate against
            assert client_proto.authenticators
            for authenticator in client_proto.authenticators:
                username = getattr(authenticator, "username", "")
                if username:
                    break
        #ensure we don't loop back to the proxy:
        proxy_virtual_display = os.environ.get("DISPLAY")
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        #remove proxy instance virtual displays:
        displays = [x for x in displays if not x.startswith(":proxy-")]
        #log("unused options: %s, %s", env_options, session_options)
        proc = None
        socket_path = None
        display = None
        sns = c.dictget("start-new-session")
        authlog("proxy_session: displays=%s, start_sessions=%s, start-new-session=%s", displays, self._start_sessions, sns)
        if len(displays)==0 or sns:
            if not self._start_sessions:
                disconnect(SESSION_NOT_FOUND, "no displays found")
                return
            try:
                proc, socket_path, display = self.start_new_session(username, uid, gid, sns, displays)
                log("start_new_session%s=%s", (username, uid, gid, sns, displays), (proc, socket_path, display))
            except Exception as e:
                log("start_server_subprocess failed", exc_info=True)
                log.error("Error: failed to start server subprocess:")
                log.error(" %s", e)
                disconnect(SERVER_ERROR, "failed to start a new session")
                return
        if display is None:
            display = c.strget("display")
            authlog("proxy_session: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s", proxy_virtual_display, display, displays)
            if display==proxy_virtual_display:
                disconnect(SESSION_NOT_FOUND, "invalid display")
                return
            if display:
                if display not in displays:
                    disconnect(SESSION_NOT_FOUND, "display '%s' not found" % display)
                    return
            else:
                if len(displays)!=1:
                    disconnect(SESSION_NOT_FOUND, "please specify a display, more than one is available: %s" % csv(displays))
                    return
                display = displays[0]

        connect = c.boolget("connect", True)
        #ConnectTestXpraClient doesn't want to connect to the real session either:
        ctr = c.strget("connect_test_request")
        log("connect=%s, connect_test_request=%s", connect, ctr)
        if not connect or ctr:
            log("proxy_session: not connecting to the session")
            hello = {"display" : display}
            if socket_path:
                hello["socket-path"] = socket_path
            #echo mode if present:
            mode = sns.get("mode")
            if mode:
                hello["mode"] = mode
            client_proto.send_now(("hello", hello))
            return

        def stop_server_subprocess():
            log("stop_server_subprocess() proc=%s", proc)
            if proc and proc.poll() is None:
                proc.terminate()

        log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display)
        def parse_error(*args):
            stop_server_subprocess()
            disconnect(SESSION_NOT_FOUND, "invalid display string")
            log.warn("Error: parsing failed for display string '%s':", display)
            for arg in args:
                log.warn(" %s", arg)
            raise Exception("parse error on %s: %s" % (display, args))
        opts = make_defaults_struct(username=username, uid=uid, gid=gid)
        opts.username = username
        disp_desc = parse_display_name(parse_error, opts, display)
        if uid or gid:
            disp_desc["uid"] = uid
            disp_desc["gid"] = gid
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc, opts)
        except Exception as e:
            log("cannot connect", exc_info=True)
            log.error("Error: cannot start proxy connection:")
            for x in str(e).splitlines():
                log.error(" %s", x)
            log.error(" connection definition:")
            print_nested_dict(disp_desc, prefix=" ", lchar="*", pad=20, print_fn=log.error)
            disconnect(SESSION_NOT_FOUND, "failed to connect to display")
            stop_server_subprocess()
            return
        log("server connection=%s", server_conn)

        #no other packets should be arriving until the proxy instance responds to the initial hello packet
        def unexpected_packet(packet):
            if packet:
                log.warn("Warning: received an unexpected packet on the proxy connection %s:", client_proto)
                log.warn(" %s", repr_ellipsized(packet))
        client_conn = client_proto.steal_connection(unexpected_packet)
        client_state = client_proto.save_state()
        cipher = None
        encryption_key = None
        if auth_caps:
            cipher = auth_caps.get("cipher")
            if cipher:
                encryption_key = self.get_encryption_key(client_proto.authenticators, client_proto.keyfile)
        log("start_proxy(..) client connection=%s", client_conn)
        log("start_proxy(..) client state=%s", client_state)

        #this may block, so run it in a thread:
        def do_start_proxy():
            log("do_start_proxy()")
            message_queue = MQueue()
            try:
                ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout)
                if not ioe:
                    log.error("Error: some network IO threads have failed to terminate")
                    return
                client_conn.set_active(True)
                process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir,
                                               self.video_encoders, self.csc_modules,
                                               client_conn, disp_desc, client_state, cipher, encryption_key, server_conn, c, message_queue)
                log("starting %s from pid=%s", process, os.getpid())
                self.processes[process] = (display, message_queue)
                process.start()
                log("process started")
                popen = process._popen
                assert popen
                #when this process dies, run reap to update our list of proxy processes:
                self.child_reaper.add_process(popen, "xpra-proxy-%s" % display, "xpra-proxy-instance", True, True, self.reap)
            finally:
                #now we can close our handle on the connection:
                client_conn.close()
                server_conn.close()
                message_queue.put("socket-handover-complete")
        start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
Example #6
0
    def start_proxy(self, client_proto, c, auth_caps):
        assert client_proto.authenticator is not None
        #find the target server session:
        def disconnect(reason, *extras):
            self.send_disconnect(client_proto, reason, *extras)
        try:
            sessions = client_proto.authenticator.get_sessions()
        except Exception as e:
            log.error("failed to get the list of sessions: %s", e)
            disconnect(AUTHENTICATION_ERROR)
            return
        if sessions is None:
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions)
        uid, gid, displays, env_options, session_options = sessions
        #log("unused options: %s, %s", env_options, session_options)
        if len(displays)==0:
            disconnect(SESSION_NOT_FOUND, "no displays found")
            return
        display = c.strget("display")
        proxy_virtual_display = os.environ.get("DISPLAY")
        #ensure we don't loop back to the proxy:
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        if display==proxy_virtual_display:
            disconnect(SESSION_NOT_FOUND, "invalid display")
            return
        if display:
            if display not in displays:
                disconnect(SESSION_NOT_FOUND, "display not found")
                return
        else:
            if len(displays)!=1:
                disconnect(SESSION_NOT_FOUND, "please specify a display (more than one available)")
                return
            display = displays[0]

        log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display)
        def parse_error(*args):
            disconnect(SESSION_NOT_FOUND, "invalid display string")
            log.warn("parse error on %s: %s", display, args)
            raise Exception("parse error on %s: %s" % (display, args))
        opts = make_defaults_struct()
        opts.username = client_proto.authenticator.username
        disp_desc = parse_display_name(parse_error, opts, display)
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc)
        except Exception as e:
            log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True)
            disconnect(SESSION_NOT_FOUND, "failed to connect to display")
            return
        log("server connection=%s", server_conn)

        #no other packets should be arriving until the proxy instance responds to the initial hello packet
        def unexpected_packet(packet):
            if packet:
                log.warn("received an unexpected packet on the proxy connection: %s", repr_ellipsized(packet))
        client_conn = client_proto.steal_connection(unexpected_packet)
        client_state = client_proto.save_state()
        cipher = None
        encryption_key = None
        if auth_caps:
            cipher = auth_caps.get("cipher")
            if cipher:
                encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile)
        log("start_proxy(..) client connection=%s", client_conn)
        log("start_proxy(..) client state=%s", client_state)

        #this may block, so run it in a thread:
        def do_start_proxy():
            log("do_start_proxy()")
            message_queue = MQueue()
            try:
                ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout)
                if not ioe:
                    log.error("some network IO threads have failed to terminate!")
                    return
                client_conn.set_active(True)
                assert uid!=0 and gid!=0
                process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir,
                                               self.video_encoders, self.csc_modules,
                                               client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue)
                log("starting %s from pid=%s", process, os.getpid())
                self.processes[process] = (display, message_queue)
                process.start()
                log("process started")
            finally:
                #now we can close our handle on the connection:
                client_conn.close()
                server_conn.close()
                message_queue.put("socket-handover-complete")
            #FIXME: remove processes that have terminated
        make_thread(do_start_proxy, "start_proxy(%s)" % client_conn).start()
Example #7
0
class ProxyServer(ServerCore):
    """
        This is the proxy server you can launch with "xpra proxy",
        once authenticated, it will dispatch the connection
        to the session found using the authenticator's
        get_sessions() function.
    """
    def __init__(self):
        log("ProxyServer.__init__()")
        ServerCore.__init__(self)
        self._max_connections = MAX_CONCURRENT_CONNECTIONS
        self.main_loop = None
        #keep track of the proxy process instances
        #the display they're on and the message queue we can
        # use to communicate with them
        self.processes = {}
        self.idle_add = gobject.idle_add
        self.timeout_add = gobject.timeout_add
        self.source_remove = gobject.source_remove
        self._socket_timeout = PROXY_SOCKET_TIMEOUT
        self._socket_dir = None
        self.control_commands = ["hello", "stop"]
        #ensure we cache the platform info before intercepting SIGCHLD
        #as this will cause a fork and SIGCHLD to be emitted:
        from xpra.version_util import get_platform_info
        get_platform_info()
        signal.signal(signal.SIGCHLD, self.sigchld)

    def init(self, opts):
        log("ProxyServer.init(%s)", opts)
        if not opts.auth:
            raise Exception(
                "The proxy server requires an authentication mode (use 'none' to disable authentication)"
            )
        self._socket_dir = opts.socket_dir
        self.video_encoders = opts.video_encoders
        self.csc_modules = opts.csc_modules
        ServerCore.init(self, opts)

    def get_server_mode(self):
        return "proxy"

    def init_aliases(self):
        pass

    def do_run(self):
        self.main_loop = gobject.MainLoop()
        self.main_loop.run()

    def do_handle_command_request(self, proto, command, args):
        if command in ("help", "hello"):
            return ServerCore.do_handle_command_request(
                self, proto, command, args)
        assert command == "stop"
        if len(args) != 1:
            return ServerCore.control_command_response(
                self, proto, command, 4,
                "invalid number of arguments, usage: 'xpra control stop DISPLAY'"
            )
        display = args[0]
        log("stop command: will try to find proxy process for display %s",
            display)
        for process, v in list(self.processes.items()):
            disp, mq = v
            if disp == display:
                pid = process.pid
                log.info(
                    "stop command: found process %s with pid %s for display %s, sending it 'stop' request",
                    process, pid, display)
                mq.put("stop")
                return self.control_command_response(
                    proto, command, 0,
                    "stopped proxy process with pid %s" % pid)
        return self.control_command_response(
            proto, command, 14, "no proxy found for display %s" % display)

    def stop_all_proxies(self):
        processes = self.processes
        self.processes = {}
        log("stop_all_proxies() will stop proxy processes: %s", processes)
        for process, v in processes.items():
            if not process.is_alive():
                continue
            disp, mq = v
            log("stop_all_proxies() stopping process %s for display %s",
                process, disp)
            mq.put("stop")
        log("stop_all_proxies() done")

    def cleanup(self):
        self.stop_all_proxies()
        ServerCore.cleanup(self)

    def do_quit(self):
        self.main_loop.quit()
        log.info("Proxy Server process ended")

    def add_listen_socket(self, socktype, sock):
        sock.listen(5)
        gobject.io_add_watch(sock, gobject.IO_IN, self._new_connection, sock)
        self.socket_types[sock] = socktype

    def verify_connection_accepted(self, protocol):
        #if we start a proxy, the protocol will be closed
        #(a new one is created in the proxy process)
        if not protocol._closed:
            self.send_disconnect(protocol, "connection timeout")

    def hello_oked(self, proto, packet, c, auth_caps):
        if c.boolget("stop_request"):
            self.clean_quit()
            return
        self.accept_client(proto, c)
        self.start_proxy(proto, c, auth_caps)

    def start_proxy(self, client_proto, c, auth_caps):
        assert client_proto.authenticator is not None

        #find the target server session:
        def disconnect(msg):
            self.send_disconnect(client_proto, msg)

        try:
            sessions = client_proto.authenticator.get_sessions()
        except Exception, e:
            log.error("failed to get the list of sessions: %s", e)
            disconnect("authentication error")
            return
        if sessions is None:
            disconnect("no sessions found")
            return
        log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto,
            auth_caps, sessions)
        uid, gid, displays, env_options, session_options = sessions
        #log("unused options: %s, %s", env_options, session_options)
        if len(displays) == 0:
            disconnect("no displays found")
            return
        display = c.strget("display")
        proxy_virtual_display = os.environ.get("DISPLAY")
        #ensure we don't loop back to the proxy:
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        if display == proxy_virtual_display:
            disconnect("invalid display")
            return
        if display:
            if display not in displays:
                disconnect("display not found")
                return
        else:
            if len(displays) != 1:
                disconnect(
                    "please specify a display (more than one available)")
                return
            display = displays[0]

        log("start_proxy(%s, {..}, %s) using server display at: %s",
            client_proto, auth_caps, display)

        def parse_error(*args):
            disconnect("invalid display string")
            log.warn("parse error on %s: %s", display, args)
            raise Exception("parse error on %s: %s" % (display, args))

        opts = make_defaults_struct()
        opts.username = client_proto.authenticator.username
        disp_desc = parse_display_name(parse_error, opts, display)
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc)
        except Exception, e:
            log.error("cannot start proxy connection to %s: %s",
                      disp_desc,
                      e,
                      exc_info=True)
            disconnect("failed to connect to display")
            return
Example #8
0
    def start_proxy(self, client_proto, c, auth_caps):
        assert client_proto.authenticator is not None
        #find the target server session:
        def disconnect(reason, *extras):
            self.send_disconnect(client_proto, reason, *extras)
        try:
            sessions = client_proto.authenticator.get_sessions()
        except Exception as e:
            log.error("failed to get the list of sessions: %s", e)
            disconnect(AUTHENTICATION_ERROR)
            return
        if sessions is None:
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions)
        uid, gid, displays, env_options, session_options = sessions
        #log("unused options: %s, %s", env_options, session_options)
        if len(displays)==0:
            disconnect(SESSION_NOT_FOUND, "no displays found")
            return
        display = c.strget("display")
        proxy_virtual_display = os.environ.get("DISPLAY")
        #ensure we don't loop back to the proxy:
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        if display==proxy_virtual_display:
            disconnect(SESSION_NOT_FOUND, "invalid display")
            return
        if display:
            if display not in displays:
                disconnect(SESSION_NOT_FOUND, "display not found")
                return
        else:
            if len(displays)!=1:
                disconnect(SESSION_NOT_FOUND, "please specify a display (more than one available)")
                return
            display = displays[0]

        log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display)
        def parse_error(*args):
            disconnect(SESSION_NOT_FOUND, "invalid display string")
            log.warn("parse error on %s: %s", display, args)
            raise Exception("parse error on %s: %s" % (display, args))
        opts = make_defaults_struct()
        opts.username = client_proto.authenticator.username
        disp_desc = parse_display_name(parse_error, opts, display)
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc)
        except Exception as e:
            log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True)
            disconnect(SESSION_NOT_FOUND, "failed to connect to display")
            return
        log("server connection=%s", server_conn)

        #no other packets should be arriving until the proxy instance responds to the initial hello packet
        def unexpected_packet(packet):
            if packet:
                log.warn("received an unexpected packet on the proxy connection: %s", repr_ellipsized(packet))
        client_conn = client_proto.steal_connection(unexpected_packet)
        client_state = client_proto.save_state()
        cipher = None
        encryption_key = None
        if auth_caps:
            cipher = auth_caps.get("cipher")
            if cipher:
                encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile)
        log("start_proxy(..) client connection=%s", client_conn)
        log("start_proxy(..) client state=%s", client_state)

        #this may block, so run it in a thread:
        def do_start_proxy():
            log("do_start_proxy()")
            message_queue = MQueue()
            try:
                ioe = client_proto.wait_for_io_threads_exit(0.5+self._socket_timeout)
                if not ioe:
                    log.error("some network IO threads have failed to terminate!")
                    return
                client_conn.set_active(True)
                assert uid!=0 and gid!=0
                process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir,
                                               self.video_encoders, self.csc_modules,
                                               client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue)
                log("starting %s from pid=%s", process, os.getpid())
                self.processes[process] = (display, message_queue)
                process.start()
                log("process started")
            finally:
                #now we can close our handle on the connection:
                client_conn.close()
                server_conn.close()
                message_queue.put("socket-handover-complete")
            #FIXME: remove processes that have terminated
        make_thread(do_start_proxy, "start_proxy(%s)" % client_conn).start()
Example #9
0
    def test_parse_display_name(self):
        opts = AdHocStruct()
        opts.socket_dirs = ["/tmp"]
        opts.socket_dir = "/tmp"
        opts.exit_ssh = False
        opts.ssh = "ssh -v "
        opts.remote_xpra = "run-xpra"
        if WIN32:
            fd = parse_display_name(None, opts,
                                    "named-pipe://FOO")["named-pipe"]
            sd = parse_display_name(None, opts, "FOO")["named-pipe"]
        else:
            fd = parse_display_name(None, opts, "socket:///FOO")
            sd = parse_display_name(None, opts, "/FOO")
        assert sd == fd, "expected %s but got %s" % (fd, sd)

        def t(s, e):
            r = parse_display_name(None, opts, s)
            if e:
                for k, v in e.items():
                    actual = r.get(k)
                    assert actual == v, "expected %s but got %s from parse_display_name(%s)=%s" % (
                        v, actual, s, r)

        def e(s):
            try:
                parse_display_name(None, opts, s)
            except Exception:
                pass
            else:
                raise Exception("parse_display_name should fail for %s" % s)

        if POSIX:
            e("ZZZZZZ")
            t("10", {
                "display_name": "10",
                "local": True,
                "type": "unix-domain"
            })
            t("/tmp/thesocket", {"display_name": "socket:///tmp/thesocket"})
            t("socket:/tmp/thesocket",
              {"display_name": "socket:/tmp/thesocket"})
        e("tcp://*****:*****@host/", {"username": "******", "password": None})
        for socktype in ("tcp", "udp", "ws", "wss", "ssl", "ssh"):
            #e(socktype+"://a/b/c/d")
            t(
                socktype +
                "://username:password@host:10000/DISPLAY?key1=value1", {
                    "type": socktype,
                    "display": "DISPLAY",
                    "key1": "value1",
                    "username": "******",
                    "password": "******",
                    "port": 10000,
                    "host": "host",
                    "local": False,
                })
        t("tcp://fe80::c1:ac45:7351:ea69:14500", {
            "host": "fe80::c1:ac45:7351:ea69",
            "port": 14500
        })
        t("tcp://fe80::c1:ac45:7351:ea69%eth1:14500", {
            "host": "fe80::c1:ac45:7351:ea69%eth1",
            "port": 14500
        })
        t("tcp://[fe80::c1:ac45:7351:ea69]:14500", {
            "host": "fe80::c1:ac45:7351:ea69",
            "port": 14500
        })
        t("tcp://host/100,key1=value1", {"key1": "value1"})
        t("tcp://host/key1=value1", {"key1": "value1"})
        try:
            from xpra.net.vsock import CID_ANY, PORT_ANY  #@UnresolvedImport
            t("vsock://any:any/", {"vsock": (CID_ANY, PORT_ANY)})
            t("vsock://10:2000/", {"vsock": (10, 2000)})
        except ImportError:
            pass
Example #10
0
    def start_proxy(self, client_proto, c, auth_caps):
        def disconnect(reason, *extras):
            log("disconnect(%s, %s)", reason, extras)
            self.send_disconnect(client_proto, reason, *extras)

        #find the target server session:
        if not client_proto.authenticator:
            log.error("Error: the proxy server requires an authentication mode,")
            try:
                log.error(" client connection '%s' does not specify one", client_proto._conn.socktype)
            except:
                pass
            log.error(" use 'none' to disable authentication")
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        try:
            sessions = client_proto.authenticator.get_sessions()
        except Exception as e:
            authlog("failed to get the list of sessions", exc_info=True)
            authlog.error("Error: failed to get the list of sessions using '%s' authenticator", client_proto.authenticator)
            authlog.error(" %s", e)
            disconnect(AUTHENTICATION_ERROR, "cannot access sessions")
            return
        authlog("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions)
        if sessions is None:
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        uid, gid, displays, env_options, session_options = sessions
        if os.name=="posix":
            if uid==0 or gid==0:
                log.error("Error: proxy instances should not run as root")
                log.error(" use a different uid and gid (ie: nobody)")
                disconnect(AUTHENTICATION_ERROR, "cannot run proxy instances as root")
                return
            username = get_username_for_uid(uid)
            groups = get_groups(username)
            if "xpra" not in groups:
                log.error("Error: user '%s' (uid=%i) is not in the xpra group", username, uid)
                log.error(" it belongs to: %s", csv(groups) or None)
                disconnect(PERMISSION_ERROR, "user missing 'xpra' group membership")
                return
        #ensure we don't loop back to the proxy:
        proxy_virtual_display = os.environ.get("DISPLAY")
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        #remove proxy instance virtual displays:
        displays = [x for x in displays if not x.startswith(":proxy-")]
        #log("unused options: %s, %s", env_options, session_options)
        opts = make_defaults_struct()
        display = None
        proc = None
        sns = c.dictget("start-new-session")
        authlog("start_proxy: displays=%s, start-new-session=%s", displays, bool(sns))
        if len(displays)==0 or sns:
            if self._start_sessions:
                #start a new session
                mode = sns.get("mode", "start")
                assert mode in ("start", "start-desktop", "shadow"), "invalid start-new-session mode '%s'" % mode
                sns = typedict(sns)
                display = sns.get("display")
                args = []
                if display:
                    args = [display]
                start = sns.strlistget("start")
                start_child = sns.strlistget("start-child")
                exit_with_children = sns.boolget("exit-with-children")
                exit_with_client = sns.boolget("exit-with-client")
                log("starting new server subprocess: mode=%s, socket-dir=%s, socket-dirs=%s, start=%s, start-child=%s, exit-with-children=%s, exit-with-client=%s, uid=%s, gid=%s",
                    mode, opts.socket_dir, opts.socket_dirs, start, start_child, exit_with_children, exit_with_client, uid, gid)
                try:
                    proc, socket_path = start_server_subprocess(sys.argv[0], args, mode, opts,
                                                                opts.socket_dir, opts.socket_dirs,
                                                                start, start_child,
                                                                exit_with_children, exit_with_client,
                                                                uid=uid, gid=gid)
                    display = "socket:%s" % socket_path
                except Exception as e:
                    log("start_server_subprocess failed", exc_info=True)
                    log.error("Error: failed to start server subprocess:")
                    log.error(" %s", e)
                    disconnect(SERVER_ERROR, "failed to start a new session")
                    return
                if proc:
                    self.child_reaper.add_process(proc, "server-%s" % display, "xpra start", True, True)
            else:
                disconnect(SESSION_NOT_FOUND, "no displays found")
                return
        if display is None:
            display = c.strget("display")
            authlog("start_proxy: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s", proxy_virtual_display, display, displays)
            if display==proxy_virtual_display:
                disconnect(SESSION_NOT_FOUND, "invalid display")
                return
            if display:
                if display not in displays:
                    disconnect(SESSION_NOT_FOUND, "display '%s' not found" % display)
                    return
            else:
                if len(displays)!=1:
                    disconnect(SESSION_NOT_FOUND, "please specify a display, more than one is available: %s" % csv(displays))
                    return
                display = displays[0]

        def stop_server_subprocess():
            if proc:
                proc.terminate()

        log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display)
        def parse_error(*args):
            stop_server_subprocess()
            disconnect(SESSION_NOT_FOUND, "invalid display string")
            log.warn("Error: parsing failed for display string '%s':", display)
            for arg in args:
                log.warn(" %s", arg)
            raise Exception("parse error on %s: %s" % (display, args))
        opts.username = client_proto.authenticator.username
        disp_desc = parse_display_name(parse_error, opts, display)
        if uid or gid:
            disp_desc["uid"] = uid
            disp_desc["gid"] = gid
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc, opts)
        except Exception as e:
            log("cannot connect", exc_info=True)
            log.error("Error: cannot start proxy connection:")
            log.error(" %s", e)
            log.error(" connection definition:")
            print_nested_dict(disp_desc, prefix=" ", lchar="*", pad=20, print_fn=log.error)
            disconnect(SESSION_NOT_FOUND, "failed to connect to display")
            stop_server_subprocess()
            return
        log("server connection=%s", server_conn)

        #no other packets should be arriving until the proxy instance responds to the initial hello packet
        def unexpected_packet(packet):
            if packet:
                log.warn("Warning: received an unexpected packet on the proxy connection %s:", client_proto)
                log.warn(" %s", repr_ellipsized(packet))
        client_conn = client_proto.steal_connection(unexpected_packet)
        client_state = client_proto.save_state()
        cipher = None
        encryption_key = None
        if auth_caps:
            cipher = auth_caps.get("cipher")
            if cipher:
                encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile)
        log("start_proxy(..) client connection=%s", client_conn)
        log("start_proxy(..) client state=%s", client_state)

        #this may block, so run it in a thread:
        def do_start_proxy():
            log("do_start_proxy()")
            message_queue = MQueue()
            try:
                ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout)
                if not ioe:
                    log.error("Error: some network IO threads have failed to terminate")
                    return
                client_conn.set_active(True)
                process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir,
                                               self.video_encoders, self.csc_modules,
                                               client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue)
                log("starting %s from pid=%s", process, os.getpid())
                self.processes[process] = (display, message_queue)
                process.start()
                log("process started")
                popen = process._popen
                assert popen
                #when this process dies, run reap to update our list of proxy processes:
                self.child_reaper.add_process(popen, "xpra-proxy-%s" % display, "xpra-proxy-instance", True, True, self.reap)
            finally:
                #now we can close our handle on the connection:
                client_conn.close()
                server_conn.close()
                message_queue.put("socket-handover-complete")
        start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
Example #11
0
    def start_proxy(self, client_proto, c, auth_caps):
        def disconnect(reason, *extras):
            log("disconnect(%s, %s)", reason, extras)
            self.send_disconnect(client_proto, reason, *extras)

        #find the target server session:
        if not client_proto.authenticator:
            log.error(
                "Error: the proxy server requires an authentication mode,")
            try:
                log.error(" client connection '%s' does not specify one",
                          client_proto._conn.socktype)
            except:
                pass
            log.error(" use 'none' to disable authentication")
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        try:
            sessions = client_proto.authenticator.get_sessions()
        except Exception as e:
            authlog("failed to get the list of sessions", exc_info=True)
            authlog.error(
                "Error: failed to get the list of sessions using '%s' authenticator",
                client_proto.authenticator)
            authlog.error(" %s", e)
            disconnect(AUTHENTICATION_ERROR, "cannot access sessions")
            return
        authlog("start_proxy(%s, {..}, %s) found sessions: %s", client_proto,
                auth_caps, sessions)
        if sessions is None:
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        uid, gid, displays, env_options, session_options = sessions
        if os.name == "posix":
            if uid == 0 or gid == 0:
                log.error("Error: proxy instances should not run as root")
                log.error(" use a different uid and gid (ie: nobody)")
                disconnect(AUTHENTICATION_ERROR,
                           "cannot run proxy instances as root")
                return
            username = get_username_for_uid(uid)
            groups = get_groups(username)
            if "xpra" not in groups:
                log("user '%s' (uid=%i) is not in the xpra group", username,
                    uid)
                log(" it belongs to: %s", csv(groups) or None)
        #ensure we don't loop back to the proxy:
        proxy_virtual_display = os.environ.get("DISPLAY")
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        #remove proxy instance virtual displays:
        displays = [x for x in displays if not x.startswith(":proxy-")]
        #log("unused options: %s, %s", env_options, session_options)
        opts = make_defaults_struct()
        display = None
        proc = None
        sns = c.dictget("start-new-session")
        authlog("start_proxy: displays=%s, start-new-session=%s", displays,
                bool(sns))
        if len(displays) == 0 or sns:
            if self._start_sessions:
                #start a new session
                mode = sns.get("mode", "start")
                assert mode in (
                    "start", "start-desktop",
                    "shadow"), "invalid start-new-session mode '%s'" % mode
                sns = typedict(sns)
                display = sns.get("display")
                args = []
                if display:
                    args = [display]
                start = sns.strlistget("start")
                start_child = sns.strlistget("start-child")
                exit_with_children = sns.boolget("exit-with-children")
                exit_with_client = sns.boolget("exit-with-client")
                log(
                    "starting new server subprocess: mode=%s, socket-dir=%s, socket-dirs=%s, start=%s, start-child=%s, exit-with-children=%s, exit-with-client=%s, uid=%s, gid=%s",
                    mode, opts.socket_dir, opts.socket_dirs, start,
                    start_child, exit_with_children, exit_with_client, uid,
                    gid)
                try:
                    proc, socket_path = start_server_subprocess(
                        sys.argv[0],
                        args,
                        mode,
                        opts,
                        opts.socket_dir,
                        opts.socket_dirs,
                        start,
                        start_child,
                        exit_with_children,
                        exit_with_client,
                        uid=uid,
                        gid=gid)
                    display = "socket:%s" % socket_path
                except Exception as e:
                    log("start_server_subprocess failed", exc_info=True)
                    log.error("Error: failed to start server subprocess:")
                    log.error(" %s", e)
                    disconnect(SERVER_ERROR, "failed to start a new session")
                    return
                if proc:
                    self.child_reaper.add_process(proc, "server-%s" % display,
                                                  "xpra start", True, True)
            else:
                disconnect(SESSION_NOT_FOUND, "no displays found")
                return
        if display is None:
            display = c.strget("display")
            authlog(
                "start_proxy: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s",
                proxy_virtual_display, display, displays)
            if display == proxy_virtual_display:
                disconnect(SESSION_NOT_FOUND, "invalid display")
                return
            if display:
                if display not in displays:
                    disconnect(SESSION_NOT_FOUND,
                               "display '%s' not found" % display)
                    return
            else:
                if len(displays) != 1:
                    disconnect(
                        SESSION_NOT_FOUND,
                        "please specify a display, more than one is available: %s"
                        % csv(displays))
                    return
                display = displays[0]

        def stop_server_subprocess():
            if proc:
                proc.terminate()

        log("start_proxy(%s, {..}, %s) using server display at: %s",
            client_proto, auth_caps, display)

        def parse_error(*args):
            stop_server_subprocess()
            disconnect(SESSION_NOT_FOUND, "invalid display string")
            log.warn("Error: parsing failed for display string '%s':", display)
            for arg in args:
                log.warn(" %s", arg)
            raise Exception("parse error on %s: %s" % (display, args))

        opts.username = client_proto.authenticator.username
        disp_desc = parse_display_name(parse_error, opts, display)
        if uid or gid:
            disp_desc["uid"] = uid
            disp_desc["gid"] = gid
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc, opts)
        except Exception as e:
            log("cannot connect", exc_info=True)
            log.error("Error: cannot start proxy connection:")
            log.error(" %s", e)
            log.error(" connection definition:")
            print_nested_dict(disp_desc,
                              prefix=" ",
                              lchar="*",
                              pad=20,
                              print_fn=log.error)
            disconnect(SESSION_NOT_FOUND, "failed to connect to display")
            stop_server_subprocess()
            return
        log("server connection=%s", server_conn)

        #no other packets should be arriving until the proxy instance responds to the initial hello packet
        def unexpected_packet(packet):
            if packet:
                log.warn(
                    "Warning: received an unexpected packet on the proxy connection %s:",
                    client_proto)
                log.warn(" %s", repr_ellipsized(packet))

        client_conn = client_proto.steal_connection(unexpected_packet)
        client_state = client_proto.save_state()
        cipher = None
        encryption_key = None
        if auth_caps:
            cipher = auth_caps.get("cipher")
            if cipher:
                encryption_key = self.get_encryption_key(
                    client_proto.authenticator, client_proto.keyfile)
        log("start_proxy(..) client connection=%s", client_conn)
        log("start_proxy(..) client state=%s", client_state)

        #this may block, so run it in a thread:
        def do_start_proxy():
            log("do_start_proxy()")
            message_queue = MQueue()
            try:
                ioe = client_proto.wait_for_io_threads_exit(
                    5 + self._socket_timeout)
                if not ioe:
                    log.error(
                        "Error: some network IO threads have failed to terminate"
                    )
                    return
                client_conn.set_active(True)
                process = ProxyInstanceProcess(
                    uid, gid, env_options, session_options, self._socket_dir,
                    self.video_encoders, self.csc_modules, client_conn,
                    client_state, cipher, encryption_key, server_conn, c,
                    message_queue)
                log("starting %s from pid=%s", process, os.getpid())
                self.processes[process] = (display, message_queue)
                process.start()
                log("process started")
                popen = process._popen
                assert popen
                #when this process dies, run reap to update our list of proxy processes:
                self.child_reaper.add_process(popen, "xpra-proxy-%s" % display,
                                              "xpra-proxy-instance", True,
                                              True, self.reap)
            finally:
                #now we can close our handle on the connection:
                client_conn.close()
                server_conn.close()
                message_queue.put("socket-handover-complete")

        start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)
Example #12
0
    def start_proxy(self, client_proto, c, auth_caps):
        def disconnect(reason, *extras):
            log("disconnect(%s, %s)", reason, extras)
            self.send_disconnect(client_proto, reason, *extras)

        #find the target server session:
        if not client_proto.authenticator:
            log.error(
                "Error: the proxy server requires an authentication mode,")
            try:
                log.error(" client connection '%s' does not specify one",
                          client_proto._conn.socktype)
            except:
                pass
            log.error(" use 'none' to disable authentication")
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        try:
            sessions = client_proto.authenticator.get_sessions()
        except Exception as e:
            authlog("failed to get the list of sessions", exc_info=True)
            authlog.error(
                "Error: failed to get the list of sessions using '%s' authenticator",
                client_proto.authenticator)
            authlog.error(" %s", e)
            disconnect(AUTHENTICATION_ERROR)
            return
        if sessions is None:
            disconnect(SESSION_NOT_FOUND, "no sessions found")
            return
        authlog("start_proxy(%s, {..}, %s) found sessions: %s", client_proto,
                auth_caps, sessions)
        uid, gid, displays, env_options, session_options = sessions
        #log("unused options: %s, %s", env_options, session_options)
        if len(displays) == 0:
            disconnect(SESSION_NOT_FOUND, "no displays found")
            return
        display = c.strget("display")
        proxy_virtual_display = os.environ.get("DISPLAY")
        #ensure we don't loop back to the proxy:
        if proxy_virtual_display in displays:
            displays.remove(proxy_virtual_display)
        authlog(
            "start_proxy: proxy-virtual-display=%s (ignored), user specified display=%s, found displays=%s",
            proxy_virtual_display, display, displays)
        if display == proxy_virtual_display:
            disconnect(SESSION_NOT_FOUND, "invalid display")
            return
        if display:
            if display not in displays:
                disconnect(SESSION_NOT_FOUND,
                           "display '%s' not found" % display)
                return
        else:
            if len(displays) != 1:
                disconnect(
                    SESSION_NOT_FOUND,
                    "please specify a display, more than one is available: %s"
                    % csv(displays))
                return
            display = displays[0]

        log("start_proxy(%s, {..}, %s) using server display at: %s",
            client_proto, auth_caps, display)

        def parse_error(*args):
            disconnect(SESSION_NOT_FOUND, "invalid display string")
            log.warn("Error: parsing failed for display string '%s':", display)
            for arg in args:
                log.warn(" %s", arg)
            raise Exception("parse error on %s: %s" % (display, args))

        opts = make_defaults_struct()
        opts.username = client_proto.authenticator.username
        disp_desc = parse_display_name(parse_error, opts, display)
        log("display description(%s) = %s", display, disp_desc)
        try:
            server_conn = connect_to(disp_desc, opts)
        except Exception as e:
            log("cannot connect", exc_info=True)
            log.error("Error: cannot start proxy connection:")
            log.error(" %s", e)
            log.error(" connection definition:")
            print_nested_dict(disp_desc,
                              prefix=" ",
                              lchar="*",
                              pad=20,
                              print_fn=log.error)
            disconnect(SESSION_NOT_FOUND, "failed to connect to display")
            return
        log("server connection=%s", server_conn)

        #no other packets should be arriving until the proxy instance responds to the initial hello packet
        def unexpected_packet(packet):
            if packet:
                log.warn(
                    "Warning: received an unexpected packet on the proxy connection %s:",
                    client_proto)
                log.warn(" %s", repr_ellipsized(packet))

        client_conn = client_proto.steal_connection(unexpected_packet)
        client_state = client_proto.save_state()
        cipher = None
        encryption_key = None
        if auth_caps:
            cipher = auth_caps.get("cipher")
            if cipher:
                encryption_key = self.get_encryption_key(
                    client_proto.authenticator, client_proto.keyfile)
        log("start_proxy(..) client connection=%s", client_conn)
        log("start_proxy(..) client state=%s", client_state)

        #this may block, so run it in a thread:
        def do_start_proxy():
            log("do_start_proxy()")
            message_queue = MQueue()
            try:
                ioe = client_proto.wait_for_io_threads_exit(
                    5 + self._socket_timeout)
                if not ioe:
                    log.error(
                        "Error: some network IO threads have failed to terminate"
                    )
                    return
                client_conn.set_active(True)
                assert uid != 0 and gid != 0
                process = ProxyInstanceProcess(
                    uid, gid, env_options, session_options, self._socket_dir,
                    self.video_encoders, self.csc_modules, client_conn,
                    client_state, cipher, encryption_key, server_conn, c,
                    message_queue)
                log("starting %s from pid=%s", process, os.getpid())
                self.processes[process] = (display, message_queue)
                process.start()
                log("process started")
                popen = process._popen
                assert popen
                #when this process dies, run reap to update our list of proxy processes:
                self.child_reaper.add_process(popen, "xpra-proxy-%s" % display,
                                              "xpra-proxy-instance", True,
                                              True, self.reap)
            finally:
                #now we can close our handle on the connection:
                client_conn.close()
                server_conn.close()
                message_queue.put("socket-handover-complete")
            #FIXME: remove processes that have terminated

        start_thread(do_start_proxy, "start_proxy(%s)" % client_conn)