def e(s): try: parse_display_name(None, opts, s) except Exception: pass else: raise Exception("parse_display_name should fail for %s" % s)
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 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
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
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)
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()
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
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()
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
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)
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)
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)