def start_new_session(self, username, uid, gid, new_session_dict={}, displays=()): log("start_new_session%s", (username, uid, gid, new_session_dict, displays)) sns = typedict(new_session_dict) if WIN32: return self.start_win32_shadow(username, new_session_dict) mode = sns.get("mode", "start") assert mode in ("start", "start-desktop", "shadow"), "invalid start-new-session mode '%s'" % mode display = sns.get("display") if display in displays: raise Exception("display %s is already active!" % display) log("starting new server subprocess: mode=%s, display=%s", mode, display) args = [] if display: args = [display] #allow the client to override some options: opts = make_defaults_struct(username=username, uid=uid, gid=gid) for k,v in sns.items(): k = bytestostr(k) if k in ("mode", "display"): continue #those special attributes have been consumed already if k not in PROXY_START_OVERRIDABLE_OPTIONS: log.warn("Warning: ignoring invalid start override") log.warn(" %s=%s", k, v) continue try: vt = OPTION_TYPES[k] if vt==str: v = bytestostr(v) elif vt==bool: v = parse_bool(k, v) elif vt==int: v = int(v) elif vt==list: v = list(bytestostr(x) for x in v) except ValueError: log("start_new_session: override option %s", k, exc_info=True) log.warn("Warning: ignoring invalid value %s for %s (%s)", v, k, vt) continue log("start override: %s=%s", k, v) if v is not None: fn = k.replace("-", "_") setattr(opts, fn, v) opts.attach = False opts.start_via_proxy = False env = self.get_proxy_env() cwd = None if uid>0: cwd = get_home_for_uid(uid) or None if not cwd or not os.path.exists(cwd): import tempfile cwd = tempfile.gettempdir() log("starting new server subprocess: options=%s", opts) log("env=%s", env) log("args=%s", args) log("cwd=%s", cwd) proc, socket_path, display = start_server_subprocess(sys.argv[0], args, mode, opts, username, uid, gid, env, cwd) if proc: self.child_reaper.add_process(proc, "server-%s" % (display or socket_path), "xpra %s" % mode, True, True) log("start_new_session(..) pid=%s, socket_path=%s, display=%s, ", proc.pid, socket_path, display) return proc, socket_path, display
def start_new_session(self, username, uid, gid, new_session_dict={}, displays=()): log("start_new_session%s", (username, uid, gid, new_session_dict, displays)) sns = typedict(new_session_dict) if WIN32: return self.start_win32_shadow(username, new_session_dict) mode = sns.get("mode", "start") assert mode in ("start", "start-desktop", "shadow"), "invalid start-new-session mode '%s'" % mode display = sns.get("display") if display in displays: raise Exception("display %s is already active!" % display) log("starting new server subprocess: mode=%s, display=%s", mode, display) args = [] if display: args = [display] #allow the client to override some options: opts = make_defaults_struct() for k, v in sns.items(): if k in ("mode", "display"): continue #those special attributes have been consumed already if k not in PROXY_START_OVERRIDABLE_OPTIONS: log.warn("Warning: ignoring invalid start override") log.warn(" %s=%s", k, v) continue log("start override: %s=%s", k, v) if v is not None: fn = k.replace("-", "_") setattr(opts, fn, v) opts.attach = False opts.start_via_proxy = False env = self.get_proxy_env() cwd = None if uid > 0: cwd = get_home_for_uid(uid) or None log("starting new server subprocess: options=%s", opts) log("env=%s", env) log("args=%s", args) log("cwd=%s", cwd) proc, socket_path, display = start_server_subprocess( sys.argv[0], args, mode, opts, uid, gid, env, cwd) if proc: self.child_reaper.add_process( proc, "server-%s" % (display or socket_path), "xpra %s" % mode, True, True) log("start_new_session(..)=%s, %s", display, proc) return proc, socket_path, display
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)