Esempio n. 1
0
 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
Esempio n. 2
0
 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
Esempio n. 3
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)
Esempio n. 4
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)