Exemplo n.º 1
0
 def __init__(self, **kwargs):
     self.app_id = kwargs.pop("app_id", APP_ID)
     key_hexstring = kwargs.pop("public_key", "")
     super().__init__(**kwargs)
     self.public_keys = {}
     key_strs = {}
     if key_hexstring:
         log("u2f_auth: public key from configuration=%s", key_hexstring)
         key_strs["command-option"] = key_hexstring
     #try to load public keys from the user conf dir(s):
     if getuid() == 0 and POSIX:
         #root: use the uid of the username specified:
         uid = self.get_uid()
     else:
         uid = getuid()
     conf_dirs = get_user_conf_dirs(uid)
     log("u2f: will try to load public keys from %s", csv(conf_dirs))
     #load public keys:
     for d in conf_dirs:
         ed = osexpand(d)
         if os.path.exists(ed) and os.path.isdir(ed):
             pub_keyfiles = glob.glob(os.path.join(ed, "u2f*-pub.hex"))
             log("u2f: keyfiles(%s)=%s", ed, pub_keyfiles)
             for f in sorted(pub_keyfiles):
                 key_hexstring = load_binary_file(f)
                 if key_hexstring:
                     key_hexstring = key_hexstring.rstrip(b" \n\r")
                     key_strs[f] = key_hexstring
                     log("u2f_auth: loaded public key from file '%s': %s",
                         f, key_hexstring)
     #parse public key data:
     #pylint: disable=import-outside-toplevel
     from cryptography.hazmat.primitives.serialization import load_der_public_key
     from cryptography.hazmat.backends import default_backend
     for origin, key_hexstring in key_strs.items():
         try:
             key = binascii.unhexlify(key_hexstring)
         except Exception as e:
             log("unhexlify(%s)", key_hexstring, exc_info=True)
             log.warn("Warning: failed to parse key '%s'", origin)
             log.warn(" %s", e)
             continue
         log("u2f: trying to load DER public key %s", repr(key))
         if not key.startswith(PUB_KEY_DER_PREFIX):
             key = PUB_KEY_DER_PREFIX + key
         try:
             k = load_der_public_key(key, default_backend())
         except Exception as e:
             log("load_der_public_key(%r)", key, exc_info=True)
             log.warn("Warning: failed to parse key '%s'", origin)
             log.warn(" %s", e)
             continue
         self.public_keys[origin] = k
     if not self.public_keys:
         raise Exception(
             "u2f authenticator requires at least one public key")
Exemplo n.º 2
0
    def run(self):
        register_SIGUSR_signals(self.idle_add)
        client_protocol_class = get_client_protocol_class(self.client_conn.socktype)
        server_protocol_class = get_server_protocol_class(self.server_conn.socktype)
        self.client_protocol = client_protocol_class(self, self.client_conn,
                                                     self.process_client_packet, self.get_client_packet)
        self.client_protocol.restore_state(self.client_state)
        self.server_protocol = server_protocol_class(self, self.server_conn,
                                                     self.process_server_packet, self.get_server_packet)
        self.log_start()

        log("ProxyProcessProcess.run() pid=%s, uid=%s, gid=%s", os.getpid(), getuid(), getgid())
        set_proc_title("Xpra Proxy Instance for %s" % self.server_conn)
        if POSIX and (getuid()!=self.uid or getgid()!=self.gid):
            #do we need a valid XDG_RUNTIME_DIR for the socket-dir?
            username = get_username_for_uid(self.uid)
            socket_dir = osexpand(self.socket_dir, username, self.uid, self.gid)
            if not os.path.exists(socket_dir):
                log("the socket directory '%s' does not exist, checking for $XDG_RUNTIME_DIR path", socket_dir)
                for prefix in ("/run/user/", "/var/run/user/"):
                    if socket_dir.startswith(prefix):
                        from xpra.scripts.server import create_runtime_dir
                        xrd = os.path.join(prefix, str(self.uid))   #ie: /run/user/99
                        log("creating XDG_RUNTIME_DIR=%s for uid=%i, gid=%i", xrd, self.uid, self.gid)
                        create_runtime_dir(xrd, self.uid, self.gid)
                        break
            #change uid or gid:
            setuidgid(self.uid, self.gid)
        if self.env_options:
            #TODO: whitelist env update?
            os.environ.update(self.env_options)

        signal.signal(signal.SIGTERM, self.signal_quit)
        signal.signal(signal.SIGINT, self.signal_quit)
        log("registered signal handler %s", self.signal_quit)

        start_thread(self.server_message_queue, "server message queue")

        if not self.create_control_socket():
            return
        self.control_socket_thread = start_thread(self.control_socket_loop, "control", daemon=True)

        self.main_queue = Queue()

        ProxyInstance.run(self)

        try:
            QueueScheduler.run(self)
        except KeyboardInterrupt as e:
            self.stop(None, str(e))
        finally:
            log("ProxyProcess.run() ending %s", os.getpid())
Exemplo n.º 3
0
    def test_posix_wrappers(self):
        if not POSIX:
            return
        assert isinstance(getuid(), int)
        assert isinstance(getgid(), int)

        def isstr(v):
            assert v
            assert isinstance(v, str)

        isstr(get_shell_for_uid(getuid()))
        isstr(get_username_for_uid(getuid()))
        isstr(get_home_for_uid(getuid()))
        assert not get_shell_for_uid(999999999)
Exemplo n.º 4
0
def handle_socket_error(sockpath, e):
    log = get_network_logger()
    log("socket creation error", exc_info=True)
    if sockpath.startswith("/var/run/xpra") or sockpath.startswith(
            "/run/xpra"):
        log.warn("Warning: cannot create socket '%s'", sockpath)
        log.warn(" %s", e)
        dirname = sockpath[:sockpath.find("xpra") + len("xpra")]
        if not os.path.exists(dirname):
            log.warn(" %s does not exist", dirname)
        if POSIX:
            uid = getuid()
            username = get_username_for_uid(uid)
            groups = get_groups(username)
            log.warn(" user '%s' is a member of groups: %s", username,
                     csv(groups) or "no groups!")
            if "xpra" not in groups:
                log.warn("  missing 'xpra' group membership?")
            for x in path_permission_info(dirname):
                log.warn("  %s", x)
    elif sockpath.startswith("/var/run/user") or sockpath.startswith(
            "/run/user"):
        log.warn("Warning: cannot create socket '%s':", sockpath)
        log.warn(" %s", e)
        run_user = sockpath.split("/user")[0] + "/user"
        if not os.path.exists(run_user):
            log.warn(" %s does not exist", run_user)
        else:
            log.warn(" ($XDG_RUNTIME_DIR has not been created?)")
    else:
        log.error("Error: failed to create socket '%s':", sockpath)
        log.error(" %s", e)
        raise InitException("failed to create socket %s" % sockpath)
Exemplo n.º 5
0
    def run(self):
        log("ProxyProcess.run() pid=%s, uid=%s, gid=%s", os.getpid(), getuid(), getgid())
        setuidgid(self.uid, self.gid)
        if self.env_options:
            #TODO: whitelist env update?
            os.environ.update(self.env_options)
        self.video_init()

        log.info("new proxy instance started")
        log.info(" for client %s", self.client_conn)
        log.info(" and server %s", self.server_conn)

        signal.signal(signal.SIGTERM, self.signal_quit)
        signal.signal(signal.SIGINT, self.signal_quit)
        log("registered signal handler %s", self.signal_quit)

        start_thread(self.server_message_queue, "server message queue")

        if not self.create_control_socket():
            #TODO: should send a message to the client
            return
        self.control_socket_thread = start_thread(self.control_socket_loop, "control")

        self.main_queue = Queue()
        #setup protocol wrappers:
        self.server_packets = Queue(PROXY_QUEUE_SIZE)
        self.client_packets = Queue(PROXY_QUEUE_SIZE)
        self.client_protocol = Protocol(self, self.client_conn, self.process_client_packet, self.get_client_packet)
        self.client_protocol.restore_state(self.client_state)
        self.server_protocol = Protocol(self, self.server_conn, self.process_server_packet, self.get_server_packet)
        #server connection tweaks:
        self.server_protocol.large_packets.append("draw")
        self.server_protocol.large_packets.append("window-icon")
        self.server_protocol.large_packets.append("keymap-changed")
        self.server_protocol.large_packets.append("server-settings")
        if self.caps.boolget("file-transfer"):
            self.client_protocol.large_packets.append("send-file")
            self.client_protocol.large_packets.append("send-file-chunk")
            self.server_protocol.large_packets.append("send-file")
            self.server_protocol.large_packets.append("send-file-chunk")
        self.server_protocol.set_compression_level(self.session_options.get("compression_level", 0))
        self.server_protocol.enable_default_encoder()

        self.lost_windows = set()
        self.encode_queue = Queue()
        self.encode_thread = start_thread(self.encode_loop, "encode")

        log("starting network threads")
        self.server_protocol.start()
        self.client_protocol.start()

        self.send_hello()
        self.timeout_add(VIDEO_TIMEOUT*1000, self.timeout_video_encoders)

        try:
            self.run_queue()
        except KeyboardInterrupt as e:
            self.stop(str(e))
        finally:
            log("ProxyProcess.run() ending %s", os.getpid())
Exemplo n.º 6
0
def handle_socket_error(sockpath, sperms, e):
    log = get_network_logger()
    log("socket creation error", exc_info=True)
    if sockpath.startswith("/var/run/xpra") or sockpath.startswith("/run/xpra"):
        log.info("cannot create group socket '%s'", sockpath)
        log.info(" %s", e)
        dirname = sockpath[:sockpath.find("xpra")+len("xpra")]
        if not os.path.exists(dirname):
            log.info(" %s does not exist", dirname)
        #only show extra information if the socket permissions
        #would have been accessible by the group:
        elif POSIX and (sperms & 0o40):
            uid = getuid()
            username = get_username_for_uid(uid)
            groups = get_groups(username)
            log.info(" user '%s' is a member of groups: %s", username, csv(groups) or "no groups!")
            if "xpra" not in groups:
                log.info("  add 'xpra' group membership to enable group socket sharing")
            for x in path_permission_info(dirname):
                log.info("  %s", x)
    elif sockpath.startswith("/var/run/user") or sockpath.startswith("/run/user"):
        log.warn("Warning: cannot create socket '%s':", sockpath)
        log.warn(" %s", e)
        run_user = sockpath.split("/user")[0]+"/user"
        if not os.path.exists(run_user):
            log.warn(" %s does not exist", run_user)
        else:
            log.warn(" ($XDG_RUNTIME_DIR has not been created?)")
    else:
        log.error("Error: failed to create socket '%s':", sockpath)
        log.error(" %s", e)
        raise InitExit(EXIT_SOCKET_CREATION_ERROR,
                       "failed to create socket %s" % sockpath)
Exemplo n.º 7
0
def create_runtime_dir(xrd, uid, gid):
    if not POSIX or OSX or getuid() != 0 or (uid == 0 and gid == 0):
        return
    #workarounds:
    #* some distros don't set a correct value,
    #* or they don't create the directory for us,
    #* or pam_open is going to create the directory but needs time to do so..
    if xrd and xrd.endswith("/user/0"):
        #don't keep root's directory, as this would not work:
        xrd = None
    if not xrd:
        #find the "/run/user" directory:
        run_user = "******"
        if not os.path.exists(run_user):
            run_user = "******"
        if os.path.exists(run_user):
            xrd = os.path.join(run_user, str(uid))
    if not xrd:
        return None
    if not os.path.exists(xrd):
        os.mkdir(xrd, 0o700)
        os.lchown(xrd, uid, gid)
    xpra_dir = os.path.join(xrd, "xpra")
    if not os.path.exists(xpra_dir):
        os.mkdir(xpra_dir, 0o700)
        os.lchown(xpra_dir, uid, gid)
    return xrd
Exemplo n.º 8
0
 def check_auth_publickey(self, username, key):
     log("check_auth_publickey(%s, %r) pubkey_auth=%s", username, key,
         self.pubkey_auth)
     if not self.pubkey_auth:
         return paramiko.AUTH_FAILED
     if not POSIX or getuid() != 0:
         import getpass
         sysusername = getpass.getuser()
         if sysusername != username:
             log.warn("Warning: ssh password authentication failed,")
             log.warn(" username does not match:")
             log.warn(" expected '%s', got '%s'", sysusername, username)
             return paramiko.AUTH_FAILED
     authorized_keys_filename = osexpand(AUTHORIZED_KEYS)
     if not os.path.exists(authorized_keys_filename) or not os.path.isfile(
             authorized_keys_filename):
         log("file '%s' does not exist", authorized_keys_filename)
         return paramiko.AUTH_FAILED
     import base64
     import binascii
     fingerprint = key.get_fingerprint()
     hex_fingerprint = binascii.hexlify(fingerprint)
     log("looking for key fingerprint '%s' in '%s'", hex_fingerprint,
         authorized_keys_filename)
     count = 0
     with open(authorized_keys_filename, "rb") as f:
         for line in f:
             if line.startswith("#"):
                 continue
             line = line.strip("\n\r")
             try:
                 key = base64.b64decode(
                     line.strip().split()[1].encode('ascii'))
             except Exception as e:
                 log("ignoring line '%s': %s", line, e)
                 continue
             import hashlib
             for hash_algo in AUTHORIZED_KEYS_HASHES:
                 hash_instance = None
                 try:
                     hash_class = getattr(hashlib,
                                          hash_algo)  #ie: hashlib.md5
                     hash_instance = hash_class(
                         key
                     )  #can raise ValueError (ie: on FIPS compliant systems)
                 except ValueError:
                     hash_instance = None
                 if not hash_instance:
                     if first_time("hash-%s-missing" % hash_algo):
                         log.warn("Warning: unsupported hash '%s'",
                                  hash_algo)
                     continue
                 fp_plain = hash_instance.hexdigest()
                 log("%s(%s)=%s", hash_algo, line, fp_plain)
                 if fp_plain == hex_fingerprint:
                     return paramiko.OPEN_SUCCEEDED
             count += 1
     log("no match in %i keys from '%s'", count, authorized_keys_filename)
     return paramiko.AUTH_FAILED
Exemplo n.º 9
0
def load_categories_to_type():
    categories_to_type = {}
    for d in get_system_conf_dirs():
        v = load_content_categories_dir(os.path.join(d, "content-categories"))
        categories_to_type.update(v)
    if not POSIX or getuid() > 0:
        for d in get_user_conf_dirs():
            load_content_categories_dir(os.path.join(d, "content-categories"))
            categories_to_type.update(v)
    return categories_to_type
Exemplo n.º 10
0
def add_user(filename,
             username,
             password,
             uid=getuid(),
             gid=getgid(),
             displays="",
             env_options="",
             session_options=""):
    sql = "INSERT INTO users(username, password, uid, gid, displays, env_options, session_options) VALUES(?, ?, ?, ?, ?, ?, ?)"
    return exec_database_sql_script(
        None, filename, sql,
        (username, password, uid, gid, displays, env_options, session_options))
Exemplo n.º 11
0
def load_content_type_defs() -> dict:
    global content_type_defs
    if content_type_defs is None:
        content_type_defs = {}
        for d in get_system_conf_dirs():
            load_content_type_dir(os.path.join(d, "content-type"))
        if not POSIX or getuid() > 0:
            for d in get_user_conf_dirs():
                load_content_type_dir(os.path.join(d, "content-type"))
        for e in CONTENT_TYPE_DEFS.split(","):
            if not process_content_type_entry(e):
                log.warn(" invalid entry in environment variable")
    return content_type_defs
Exemplo n.º 12
0
 def add_user(self,
              username,
              password,
              uid=getuid(),
              gid=getgid(),
              displays="",
              env_options="",
              session_options=""):
     sql = "INSERT INTO users(username, password, uid, gid, displays, env_options, session_options) "+\
           "VALUES(%s, %s, %s, %s, %s, %s, %s)" % ((self.param,)*7)
     self.exec_database_sql_script(None, sql,
                                   (username, password, uid, gid, displays,
                                    env_options, session_options))
Exemplo n.º 13
0
def create_unix_domain_socket(sockpath, socket_permissions=0o600):
    #convert this to a umask!
    umask = 0o777 - socket_permissions
    listener = socket.socket(socket.AF_UNIX)
    listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    #bind the socket, using umask to set the correct permissions
    orig_umask = os.umask(umask)
    try:
        listener.bind(sockpath)
    finally:
        os.umask(orig_umask)
    try:
        inode = os.stat(sockpath).st_ino
    except:
        inode = -1
    #set to the "xpra" group if we are a member of it, or if running as root:
    uid = getuid()
    username = get_username_for_uid(uid)
    groups = get_groups(username)
    if uid == 0 or GROUP in groups:
        group_id = get_group_id(GROUP)
        if group_id >= 0:
            try:
                os.lchown(sockpath, -1, group_id)
            except Exception as e:
                log = get_network_logger()
                log.warn("Warning: failed to set '%s' group ownership", GROUP)
                log.warn(" on socket '%s':", sockpath)
                log.warn(" %s", e)
            #don't know why this doesn't work:
            #os.fchown(listener.fileno(), -1, group_id)
    def cleanup_socket():
        log = get_network_logger()
        try:
            cur_inode = os.stat(sockpath).st_ino
        except:
            log.info("socket '%s' already deleted", sockpath)
            return
        delpath = sockpath
        log("cleanup_socket '%s', original inode=%s, new inode=%s", sockpath,
            inode, cur_inode)
        if cur_inode == inode:
            log.info("removing socket %s", delpath)
            try:
                os.unlink(delpath)
            except:
                pass

    return listener, cleanup_socket
Exemplo n.º 14
0
    def create_control_socket(self):
        assert self.socket_dir

        def stop(msg):
            self.stop(None, "cannot create the proxy control socket: %s" % msg)

        username = get_username_for_uid(self.uid)
        dotxpra = DotXpra(self.socket_dir,
                          actual_username=username,
                          uid=self.uid,
                          gid=self.gid)
        sockname = ":proxy-%s" % os.getpid()
        sockpath = dotxpra.socket_path(sockname)
        log("%s.socket_path(%s)=%s", dotxpra, sockname, sockpath)
        state = dotxpra.get_server_state(sockpath)
        log(
            "create_control_socket: socket path='%s', uid=%i, gid=%i, state=%s",
            sockpath, getuid(), getgid(), state)
        if state in (DotXpra.LIVE, DotXpra.UNKNOWN, DotXpra.INACCESSIBLE):
            log.error("Error: you already have a proxy server running at '%s'",
                      sockpath)
            log.error(" the control socket will not be created")
            stop("socket already exists")
            return False
        d = os.path.dirname(sockpath)
        try:
            dotxpra.mksockdir(d, SOCKET_DIR_MODE)
        except Exception as e:
            log.warn("Warning: failed to create socket directory '%s'", d)
            log.warn(" %s", e)
        try:
            sock, self.control_socket_cleanup = create_unix_domain_socket(
                sockpath, 0o600)
            sock.listen(5)
        except Exception as e:
            log("create_unix_domain_socket failed for '%s'",
                sockpath,
                exc_info=True)
            log.error("Error: failed to setup control socket '%s':", sockpath)
            handle_socket_error(sockpath, 0o600, e)
            stop(e)
            return False
        self.control_socket = sock
        self.control_socket_path = sockpath
        log.info("proxy instance now also available using unix domain socket:")
        log.info(" %s", self.control_socket_path)
        return True
Exemplo n.º 15
0
def find_log_dir(username="", uid=0, gid=0):
    from xpra.platform.paths import get_default_log_dirs
    errs  = []
    for x in get_default_log_dirs():
        v = osexpand(x, username, uid, gid)
        if not os.path.exists(v):
            if getuid()==0 and uid!=0:
                continue
            try:
                os.mkdir(v, 0o700)
            except Exception as e:
                errs.append((v, e))
                continue
        return v
    for d, e in errs:
        sys.stderr.write("Error: cannot create log directory '%s':" % d)
        sys.stderr.write(" %s\n" % e)
    return None
Exemplo n.º 16
0
 def check_auth_publickey(self, username, key):
     log("check_auth_publickey(%s, %r) pubkey_auth=%s", username, key,
         self.pubkey_auth)
     if not self.pubkey_auth:
         return paramiko.AUTH_FAILED
     if not POSIX or getuid() != 0:
         import getpass
         sysusername = getpass.getuser()
         if sysusername != username:
             log.warn("Warning: ssh password authentication failed,")
             log.warn(" username does not match:")
             log.warn(" expected '%s', got '%s'", sysusername, username)
             return paramiko.AUTH_FAILED
     authorized_keys_filename = osexpand(AUTHORIZED_KEYS)
     if not os.path.exists(authorized_keys_filename) or not os.path.isfile(
             authorized_keys_filename):
         log("file '%s' does not exist", authorized_keys_filename)
         return paramiko.AUTH_FAILED
     import base64
     import binascii
     fingerprint = key.get_fingerprint()
     hex_fingerprint = binascii.hexlify(fingerprint)
     log("looking for key fingerprint '%s' in '%s'", hex_fingerprint,
         authorized_keys_filename)
     count = 0
     with open(authorized_keys_filename, "rb") as f:
         for line in f:
             if line.startswith("#"):
                 continue
             line = line.strip("\n\r")
             try:
                 key = base64.b64decode(
                     line.strip().split()[1].encode('ascii'))
             except Exception as e:
                 log("ignoring line '%s': %s", line, e)
                 continue
             import hashlib
             fp_plain = hashlib.md5(key).hexdigest()
             log("key(%s)=%s", line, fp_plain)
             if fp_plain == hex_fingerprint:
                 return paramiko.OPEN_SUCCEEDED
             count += 1
     log("no match in %i keys from '%s'", count, authorized_keys_filename)
     return paramiko.AUTH_FAILED
Exemplo n.º 17
0
def write_pidfile(pidfile, uid, gid):
    from xpra.log import Logger
    log = Logger("server")
    pidstr = str(os.getpid())
    try:
        with open(pidfile, "w") as f:
            os.fchmod(f.fileno(), 0o600)
            f.write("%s\n" % pidstr)
            try:
                inode = os.fstat(f.fileno()).st_ino
            except:
                inode = -1
            if POSIX and uid != getuid() or gid != getgid():
                try:
                    os.fchown(f.fileno(), uid, gid)
                except:
                    pass
        log.info("wrote pid %s to '%s'", pidstr, pidfile)

        def cleanuppidfile():
            #verify this is the right file!
            log("cleanuppidfile: inode=%i", inode)
            if inode > 0:
                try:
                    i = os.stat(pidfile).st_ino
                    log("cleanuppidfile: current inode=%i", i)
                    if i != inode:
                        return
                except:
                    pass
            try:
                os.unlink(pidfile)
            except:
                pass

        add_cleanup(cleanuppidfile)
    except Exception as e:
        log.error("Error: failed to write pid %i to pidfile '%s':",
                  os.getpid(), pidfile)
        log.error(" %s", e)
Exemplo n.º 18
0
 def get_info(self, proto, *_args):
     authenticated = proto and proto.authenticators
     if not authenticated:
         info = super().get_server_info()
     else:
         #only show more info if we have authenticated
         #as the user running the proxy server process:
         info = super().get_info(proto)
         sessions = ()
         for authenticator in proto.authenticators:
             auth_sessions = authenticator.get_sessions()
             if auth_sessions:
                 sessions = auth_sessions
                 break
         if sessions:
             uid, gid = sessions[:2]
             if not POSIX or (uid == getuid() and gid == getgid()):
                 self.reap()
                 i = 0
                 instances = dict(self.instances)
                 instances_info = {}
                 for proxy_instance, v in instances.items():
                     isprocess, d, _ = v
                     iinfo = {
                         "display": d,
                         "live": proxy_instance.is_alive(),
                     }
                     if isprocess:
                         iinfo.update({
                             "pid": proxy_instance.pid,
                         })
                     else:
                         iinfo.update(proxy_instance.get_info())
                     instances_info[i] = iinfo
                     i += 1
                 info["instances"] = instances_info
                 info["proxies"] = len(instances)
     info.setdefault("server", {})["type"] = "Python/GLib/proxy"
     return info
Exemplo n.º 19
0
 def create_control_socket(self):
     assert self.socket_dir
     username = get_username_for_uid(self.uid)
     dotxpra = DotXpra(self.socket_dir,
                       actual_username=username,
                       uid=self.uid,
                       gid=self.gid)
     sockpath = dotxpra.socket_path(":proxy-%s" % os.getpid())
     state = dotxpra.get_server_state(sockpath)
     log(
         "create_control_socket: socket path='%s', uid=%i, gid=%i, state=%s",
         sockpath, getuid(), getgid(), state)
     if state in (DotXpra.LIVE, DotXpra.UNKNOWN):
         log.error("Error: you already have a proxy server running at '%s'",
                   sockpath)
         log.error(" the control socket will not be created")
         return False
     d = os.path.dirname(sockpath)
     try:
         dotxpra.mksockdir(d)
     except Exception as e:
         log.warn("Warning: failed to create socket directory '%s'", d)
         log.warn(" %s", e)
     try:
         sock, self.control_socket_cleanup = create_unix_domain_socket(
             sockpath, None, 0o600)
         sock.listen(5)
     except Exception as e:
         log("create_unix_domain_socket failed for '%s'",
             sockpath,
             exc_info=True)
         log.error("Error: failed to setup control socket '%s':", sockpath)
         log.error(" %s", e)
         return False
     self.control_socket = sock
     self.control_socket_path = sockpath
     log.info("proxy instance now also available using unix domain socket:")
     log.info(" %s", self.control_socket_path)
     return True
Exemplo n.º 20
0
 def create_control_socket(self):
     assert self.socket_dir
     dotxpra = DotXpra(self.socket_dir)
     sockpath = dotxpra.socket_path(":proxy-%s" % os.getpid())
     state = dotxpra.get_server_state(sockpath)
     if state in (DotXpra.LIVE, DotXpra.UNKNOWN):
         log.error("Error: you already have a proxy server running at '%s'", sockpath)
         log.error(" the control socket will not be created")
         return False
     log("create_control_socket: socket path='%s', uid=%i, gid=%i", sockpath, getuid(), getgid())
     try:
         sock, self.control_socket_cleanup = create_unix_domain_socket(sockpath, None, 0o600)
         sock.listen(5)
     except Exception as e:
         log("create_unix_domain_socket failed for '%s'", sockpath, exc_info=True)
         log.error("Error: failed to setup control socket '%s':", sockpath)
         log.error(" %s", e)
         return False
     self.control_socket = sock
     self.control_socket_path = sockpath
     log.info("proxy instance now also available using unix domain socket:")
     log.info(" %s", self.control_socket_path)
     return True
Exemplo n.º 21
0
 def check_peercred(self, connection, uids="", gids="", allow_owner=False):
     allow_uids = allow_gids = None
     if uids or allow_owner:
         allow_uids = []
         if allow_owner:
             allow_uids.append(getuid())
         for x in uids.split(":"):
             if not x.strip():
                 continue
             x = osexpand(x.strip())
             try:
                 allow_uids.append(int(x))
             except ValueError:
                 import pwd  #pylint: disable=import-outside-toplevel
                 try:
                     pw = pwd.getpwnam(x)
                     allow_uids.append(pw.pw_uid)
                 except KeyError:
                     log.warn("Warning: unknown username '%s'", x)
         log("peercred: allow_uids(%s)=%s", uids, allow_uids)
     if gids:
         allow_gids = []
         for x in gids.split(":"):
             if not x.strip():
                 continue
             x = osexpand(x.strip())
             try:
                 allow_gids.append(int(x))
             except ValueError:
                 gid = get_group_id(x)
                 if gid >= 0:
                     allow_gids.append(gid)
                 else:
                     log.warn("Warning: unknown group '%s'", x)
         log("peercred: allow_gids(%s)=%s", gids, allow_gids)
     self.do_check_peercred(connection, allow_uids, allow_gids)
Exemplo n.º 22
0
class Authenticator(SysAuthenticator):
    CLIENT_USERNAME = getuid() == 0

    def __init__(self, **kwargs):
        self.service = kwargs.pop("service", PAM_AUTH_SERVICE)
        self.check_account = parse_bool(
            "check-account", kwargs.pop("check-account", PAM_CHECK_ACCOUNT),
            False)
        super().__init__(**kwargs)

    def check(self, password) -> bool:
        log("pam.check(..) pw=%s", self.pw)
        if self.pw is None:
            return False
        return check(self.username, password, self.service, self.check_account)

    def get_challenge(self, digests):
        if "xor" not in digests:
            log.error("Error: pam authentication requires the 'xor' digest")
            return None
        return super().get_challenge(["xor"])

    def __repr__(self):
        return "PAM"
Exemplo n.º 23
0
 def preexec():
     setsid()
     if getuid() == 0 and uid:
         setuidgid(uid, gid)
     close_fds([0, 1, 2, r_pipe, w_pipe])
Exemplo n.º 24
0
def start_Xvfb(xvfb_str,
               pixel_depth,
               display_name,
               cwd,
               uid,
               gid,
               username,
               xauth_data,
               uinput_uuid=None):
    if not POSIX:
        raise InitException("starting an Xvfb is not supported on %s" %
                            os.name)
    if OSX:
        raise InitException("starting an Xvfb is not supported on MacOS")
    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")

    cleanups = []
    log = get_vfb_logger()
    log("start_Xvfb%s", (xvfb_str, pixel_depth, display_name, cwd, uid, gid,
                         username, xauth_data, uinput_uuid))
    xauthority = get_xauthority_path(display_name, username, uid, gid)
    os.environ["XAUTHORITY"] = xauthority
    if not os.path.exists(xauthority):
        log("creating XAUTHORITY=%s with data=%s", xauthority, xauth_data)
        try:
            with open(xauthority, 'wa') as f:
                if getuid() == 0 and (uid != 0 or gid != 0):
                    os.fchown(f.fileno(), uid, gid)
        except Exception as e:
            #trying to continue anyway!
            log.error("Error trying to create XAUTHORITY file %s:", xauthority)
            log.error(" %s", e)
    else:
        log("found existing XAUTHORITY file '%s'", xauthority)
    use_display_fd = display_name[0] == 'S'

    subs = {}

    def pathexpand(s):
        return osexpand(s,
                        actual_username=username,
                        uid=uid,
                        gid=gid,
                        subs=subs)

    subs.update({
        "DISPLAY": display_name,
        "XPRA_LOG_DIR": pathexpand(os.environ.get("XPRA_LOG_DIR")),
    })

    #identify logfile argument if it exists,
    #as we may have to rename it, or create the directory for it:
    import shlex
    xvfb_cmd = shlex.split(xvfb_str)
    if not xvfb_cmd:
        raise InitException(
            "cannot start Xvfb, the command definition is missing!")
    #make sure all path values are expanded:
    xvfb_cmd = [pathexpand(s) for s in xvfb_cmd]

    try:
        logfile_argindex = xvfb_cmd.index('-logfile')
        assert logfile_argindex + 1 < len(
            xvfb_cmd
        ), "invalid xvfb command string: -logfile should not be last (found at index %i)" % logfile_argindex
        xorg_log_file = xvfb_cmd[logfile_argindex + 1]
    except ValueError:
        xorg_log_file = None
    tmp_xorg_log_file = None
    if xorg_log_file:
        if use_display_fd:
            #keep track of it so we can rename it later:
            tmp_xorg_log_file = xorg_log_file
        #make sure the Xorg log directory exists:
        xorg_log_dir = os.path.dirname(xorg_log_file)
        if not os.path.exists(xorg_log_dir):
            try:
                log("creating Xorg log dir '%s'", xorg_log_dir)
                os.mkdir(xorg_log_dir, 0o700)
                if POSIX and uid != getuid() or gid != getgid():
                    try:
                        os.lchown(xorg_log_dir, uid, gid)
                    except:
                        pass
            except OSError as e:
                raise InitException(
                    "failed to create the Xorg log directory '%s': %s" %
                    (xorg_log_dir, e))

    if uinput_uuid:
        #use uinput:
        #identify -config xorg.conf argument and replace it with the uinput one:
        try:
            config_argindex = xvfb_cmd.index("-config")
        except ValueError as e:
            log.warn("Warning: cannot use uinput")
            log.warn(" '-config' argument not found in the xvfb command")
        else:
            assert config_argindex + 1 < len(
                xvfb_cmd
            ), "invalid xvfb command string: -config should not be last (found at index %i)" % config_argindex
            xorg_conf = xvfb_cmd[config_argindex + 1]
            if xorg_conf.endswith("xorg.conf"):
                xorg_conf = xorg_conf.replace("xorg.conf", "xorg-uinput.conf")
                if os.path.exists(xorg_conf):
                    xvfb_cmd[config_argindex + 1] = xorg_conf
            #create uinput device definition files:
            #(we have to assume that Xorg is configured to use this path..)
            xorg_conf_dir = pathexpand(get_Xdummy_confdir())
            cleanups = create_xorg_device_configs(xorg_conf_dir, uinput_uuid,
                                                  uid, gid)

    xvfb_executable = xvfb_cmd[0]
    if (xvfb_executable.endswith("Xorg")
            or xvfb_executable.endswith("Xdummy")) and pixel_depth > 0:
        xvfb_cmd.append("-depth")
        xvfb_cmd.append(str(pixel_depth))
    xvfb = None
    try:
        if use_display_fd:

            def displayfd_err(msg):
                raise InitException("%s: %s" % (xvfb_executable, msg))

            r_pipe, w_pipe = os.pipe()
            try:
                if PYTHON3:
                    os.set_inheritable(w_pipe, True)  #@UndefinedVariable
                xvfb_cmd += ["-displayfd", str(w_pipe)]
                xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable,
                                                  display_name)

                def preexec():
                    setsid()
                    if getuid() == 0 and uid:
                        setuidgid(uid, gid)
                    close_fds([0, 1, 2, r_pipe, w_pipe])

                try:
                    xvfb = subprocess.Popen(xvfb_cmd,
                                            executable=xvfb_executable,
                                            close_fds=False,
                                            stdin=subprocess.PIPE,
                                            preexec_fn=preexec,
                                            cwd=cwd)
                except OSError as e:
                    log("Popen%s", (xvfb_cmd, xvfb_executable, cwd),
                        exc_info=True)
                    raise InitException(
                        "failed to execute xvfb command %s: %s" %
                        (xvfb_cmd, e))
                assert xvfb.poll() is None, "xvfb command failed"
                # Read the display number from the pipe we gave to Xvfb
                try:
                    buf = read_displayfd(r_pipe)
                except Exception as e:
                    log("read_displayfd(%s)", r_pipe, exc_info=True)
                    displayfd_err("failed to read displayfd pipe %s: %s" %
                                  (r_pipe, e))
            finally:
                osclose(r_pipe)
                osclose(w_pipe)
            n = parse_displayfd(buf, displayfd_err)
            new_display_name = ":%s" % n
            log("Using display number provided by %s: %s", xvfb_executable,
                new_display_name)
            if tmp_xorg_log_file != None:
                #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
                f0 = shellsub(tmp_xorg_log_file, subs)
                subs["DISPLAY"] = new_display_name
                #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
                f1 = shellsub(tmp_xorg_log_file, subs)
                if f0 != f1:
                    try:
                        os.rename(f0, f1)
                    except Exception as e:
                        log.warn("Warning: failed to rename Xorg log file,")
                        log.warn(" from '%s' to '%s'" % (f0, f1))
                        log.warn(" %s" % e)
            display_name = new_display_name
        else:
            # use display specified
            xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
            xvfb_cmd.append(display_name)

            def preexec():
                if getuid() == 0 and (uid != 0 or gid != 0):
                    setuidgid(uid, gid)
                else:
                    setsid()

            log("xvfb_cmd=%s", xvfb_cmd)
            xvfb = subprocess.Popen(xvfb_cmd,
                                    executable=xvfb_executable,
                                    close_fds=True,
                                    stdin=subprocess.PIPE,
                                    preexec_fn=preexec)

        xauth_add(xauthority, display_name, xauth_data, uid, gid)
    except Exception as e:
        if xvfb and xvfb.poll() is None:
            log.error(" stopping vfb process with pid %i", xvfb.pid)
            xvfb.terminate()
        raise
    log("xvfb process=%s", xvfb)
    log("display_name=%s", display_name)
    return xvfb, display_name, cleanups
Exemplo n.º 25
0
def run_server(error_cb,
               opts,
               mode,
               xpra_file,
               extra_args,
               desktop_display=None):
    try:
        cwd = os.getcwd()
    except:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding == "help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    from xpra.server.socket_util import parse_bind_ip, parse_bind_vsock, get_network_logger
    bind_tcp = parse_bind_ip(opts.bind_tcp)
    bind_udp = parse_bind_ip(opts.bind_udp)
    bind_ssl = parse_bind_ip(opts.bind_ssl)
    bind_ws = parse_bind_ip(opts.bind_ws)
    bind_wss = parse_bind_ip(opts.bind_wss)
    bind_rfb = parse_bind_ip(opts.bind_rfb, 5900)
    bind_vsock = parse_bind_vsock(opts.bind_vsock)

    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
    starting = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    shadowing = mode == "shadow"
    proxying = mode == "proxy"
    clobber = upgrading or opts.use_display
    start_vfb = not shadowing and not proxying and not clobber

    if upgrading or shadowing:
        #there should already be one running
        opts.pulseaudio = False

    #get the display name:
    if shadowing and len(extra_args) == 0:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = ":0"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra)
    elif upgrading and len(extra_args) == 0:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb(
                "too many extra arguments (%i): only expected a display number"
                % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not proxying and not opts.use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb(
                        "you must specify a free virtual display name to use with the proxy server"
                    )
            elif opts.use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir,
                                                  opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child:
        error_cb(
            "--exit-with-children specified without any children to spawn; exiting immediately"
        )

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import xpra_runner_shell_script, write_runner_shell_scripts, write_pidfile, find_log_dir, create_input_devices
    script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()
    ROOT = POSIX and getuid() == 0

    protected_fds = []
    protected_env = {}
    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = os.path.abspath(opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    displayfd = 0
    if POSIX and opts.displayfd:
        try:
            displayfd = int(opts.displayfd)
            if displayfd > 0:
                protected_fds.append(displayfd)
        except ValueError as e:
            stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd)
            stderr.write(" %s\n" % e)
            del e

    # if pam is present, try to create a new session:
    pam = None
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid != 0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session  #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            del e
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                #"XDG_SEAT"               : "seat1",
                #"XDG_VTNR"               : "0",
                "XDG_SESSION_TYPE": "x11",
                #"XDG_SESSION_CLASS"      : "user",
                "XDG_SESSION_DESKTOP": "xpra",
            }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds += fdc.get_new_fds()

    #get XDG_RUNTIME_DIR from env options,
    #which may not be have updated os.environ yet when running as root with "--uid="
    xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", ""))
    if ROOT and (uid > 0 or gid > 0):
        #we're going to chown the directory if we create it,
        #ensure this cannot be abused, only use "safe" paths:
        if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp")
                   if xrd.startswith(x)):
            xrd = ""
        #these paths could cause problems if we were to create and chown them:
        if xrd.startswith("/tmp/.X11-unix") or xrd.startswith(
                "/tmp/.XIM-unix"):
            xrd = ""
    if not xrd:
        xrd = os.environ.get("XDG_RUNTIME_DIR")
    xrd = create_runtime_dir(xrd, uid, gid)
    if xrd:
        #this may override the value we get from pam
        #with the value supplied by the user:
        protected_env["XDG_RUNTIME_DIR"] = xrd

    if opts.pidfile:
        write_pidfile(opts.pidfile, uid, gid)

    if POSIX and not ROOT:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    if start_vfb or opts.daemon:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower() == "auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException(
                    "cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

        if opts.daemon:
            from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
            log_filename0 = select_log_file(log_dir, opts.log_file,
                                            display_name)
            logfd = open_log_file(log_filename0)
            if ROOT and (uid > 0 or gid > 0):
                try:
                    os.fchown(logfd, uid, gid)
                except:
                    pass
            stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
            try:
                stderr.write("Entering daemon mode; " +
                             "any further errors will be reported to:\n" +
                             ("  %s\n" % log_filename0))
            except:
                #we tried our best, logging another error won't help
                pass

    #warn early about this:
    if (starting or starting_desktop) and desktop_display:
        print_DE_warnings(desktop_display, opts.pulseaudio, opts.notifications,
                          opts.dbus_launch)

    log = get_util_logger()
    netlog = get_network_logger()

    mdns_recs = {}
    sockets = []

    #SSL sockets:
    wrap_socket_fn = None
    need_ssl = False
    ssl_opt = opts.ssl.lower()
    if ssl_opt in TRUE_OPTIONS or bind_ssl or bind_wss:
        need_ssl = True
    if opts.bind_tcp or opts.bind_ws:
        if ssl_opt == "auto" and opts.ssl_cert:
            need_ssl = True
        elif ssl_opt == "tcp" and opts.bind_tcp:
            need_ssl = True
        elif ssl_opt == "www":
            need_ssl = True
    if need_ssl:
        from xpra.scripts.main import ssl_wrap_socket_fn
        try:
            wrap_socket_fn = ssl_wrap_socket_fn(opts, server_side=True)
            netlog("wrap_socket_fn=%s", wrap_socket_fn)
        except Exception as e:
            netlog("SSL error", exc_info=True)
            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key)
                         if x)
            raise InitException(
                "cannot create SSL socket, check your certificate paths (%s): %s"
                % (cpaths, e))

    from xpra.server.socket_util import setup_tcp_socket, setup_udp_socket, setup_vsock_socket, setup_local_sockets, has_dual_stack
    min_port = int(opts.min_port)

    def hosts(host_str):
        if host_str == "*":
            if has_dual_stack():
                #IPv6 will also listen for IPv4:
                return ["::"]
            #no dual stack, so we have to listen on both IPv4 and IPv6 explicitly:
            return ["0.0.0.0", "::"]
        return [host_str]

    def add_mdns(socktype, host_str, port):
        recs = mdns_recs.setdefault(socktype.lower(), [])
        for host in hosts(host_str):
            rec = (host, port)
            if rec not in recs:
                recs.append(rec)

    def add_tcp_socket(socktype, host_str, iport):
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        for host in hosts(host_str):
            socket = setup_tcp_socket(host, iport, socktype)
            sockets.append(socket)
            add_mdns(socktype, host, iport)

    def add_udp_socket(socktype, host_str, iport):
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        for host in hosts(host_str):
            socket = setup_udp_socket(host, iport, socktype)
            sockets.append(socket)
            add_mdns(socktype, host, iport)

    # Initialize the TCP sockets before the display,
    # That way, errors won't make us kill the Xvfb
    # (which may not be ours to kill at that point)
    netlog("setting up SSL sockets: %s", csv(bind_ssl))
    for host, iport in bind_ssl:
        add_tcp_socket("ssl", host, iport)
    netlog("setting up https / wss (secure websockets): %s", csv(bind_wss))
    for host, iport in bind_wss:
        add_tcp_socket("wss", host, iport)
    tcp_ssl = ssl_opt in TRUE_OPTIONS or (ssl_opt == "auto" and opts.ssl_cert)
    netlog("setting up TCP sockets: %s", csv(bind_tcp))
    for host, iport in bind_tcp:
        add_tcp_socket("tcp", host, iport)
        if tcp_ssl:
            add_mdns("ssl", host, iport)
    netlog("setting up UDP sockets: %s", csv(bind_udp))
    for host, iport in bind_udp:
        add_udp_socket("udp", host, iport)
    netlog("setting up http / ws (websockets): %s", csv(bind_ws))
    for host, iport in bind_ws:
        add_tcp_socket("ws", host, iport)
        if tcp_ssl:
            add_mdns("wss", host, iport)
    if bind_rfb and (proxying or starting):
        log.warn("Warning: bind-rfb sockets cannot be used with '%s' mode" %
                 mode)
    else:
        netlog("setting up rfb sockets: %s", csv(bind_rfb))
        for host, iport in bind_rfb:
            add_tcp_socket("rfb", host, iport)
    netlog("setting up vsock sockets: %s", csv(bind_vsock))
    for cid, iport in bind_vsock:
        socket = setup_vsock_socket(cid, iport)
        sockets.append(socket)
        #add_mdns("vsock", str(cid), iport)

    # systemd socket activation:
    try:
        from xpra.platform.xposix.sd_listen import get_sd_listen_sockets
    except ImportError:
        pass
    else:
        sd_sockets = get_sd_listen_sockets()
        netlog("systemd sockets: %s", sd_sockets)
        for stype, socket, addr in sd_sockets:
            sockets.append((stype, socket, addr))
            netlog("%s : %s", (stype, [addr]), socket)
            if stype == "tcp":
                host, iport = addr
                add_mdns("tcp", host, iport)

    sanitize_env()
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
    else:
        try:
            del os.environ["DISPLAY"]
        except:
            pass
    os.environ.update(protected_env)
    log("env=%s", os.environ)

    UINPUT_UUID_LEN = 12
    UINPUT_UUID_MIN_LEN = 12
    UINPUT_UUID_MAX_LEN = 32
    # Start the Xvfb server first to get the display_name if needed
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    uinput_uuid = None
    if start_vfb:
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth)
        from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process
        from xpra.server.server_util import has_uinput
        uinput_uuid = None
        if has_uinput() and opts.input_devices.lower() in (
                "uinput", "auto") and not shadowing:
            from xpra.os_util import get_rand_chars
            uinput_uuid = get_rand_chars(UINPUT_UUID_LEN)
        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth,
                                                  display_name, cwd, uid, gid,
                                                  username, xauth_data,
                                                  uinput_uuid)
        for f in cleanups:
            add_cleanup(f)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name != odisplay_name and pam:
            pam.set_items({"XDISPLAY": display_name})

        def check_xvfb():
            return check_xvfb_process(xvfb)
    else:

        def check_xvfb():
            return True

    if POSIX and not OSX and displayfd > 0:
        from xpra.platform.displayfd import write_displayfd
        try:
            display = display_name[1:]
            log("writing display='%s' to displayfd=%i", display, displayfd)
            assert write_displayfd(displayfd, display), "timeout"
        except Exception as e:
            log.error("write_displayfd failed", exc_info=True)
            log.error("Error: failed to write '%s' to fd=%s", display_name,
                      displayfd)
            log.error(" %s", str(e) or type(e))
            del e
        try:
            os.close(displayfd)
        except:
            pass

    if not proxying:

        def close_display():
            close_gtk_display()
            kill_xvfb(xvfb_pid)

        add_cleanup(close_display)
    else:
        close_display = None

    if opts.daemon:

        def noerr(fn, *args):
            try:
                fn(*args)
            except:
                pass

        log_filename1 = select_log_file(log_dir, opts.log_file, display_name)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            os.rename(log_filename0, log_filename1)
            if odisplay_name != display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write,
                  "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb():
        #xvfb problem: exit now
        return 1

    #create devices for vfb if needed:
    devices = {}
    if not start_vfb and not proxying and not shadowing:
        #try to find the existing uinput uuid:
        #use a subprocess to avoid polluting our current process
        #with X11 connections before we get a chance to change uid
        cmd = ["xprop", "-display", display_name, "-root", "_XPRA_UINPUT_ID"]
        try:
            code, out, err = get_status_output(cmd)
        except Exception as e:
            log("failed to get existing uinput id: %s", e)
            del e
        else:
            log("Popen(%s)=%s", cmd, (code, out, err))
            if code == 0 and out.find("=") > 0:
                uinput_uuid = out.split("=", 1)[1]
                log("raw uinput uuid=%s", uinput_uuid)
                uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ '))
                if uinput_uuid:
                    if len(uinput_uuid) > UINPUT_UUID_MAX_LEN or len(
                            uinput_uuid) < UINPUT_UUID_MIN_LEN:
                        log.warn("Warning: ignoring invalid uinput id:")
                        log.warn(" '%s'", uinput_uuid)
                        uinput_uuid = None
                    else:
                        log.info("retrieved existing uinput id: %s",
                                 bytestostr(uinput_uuid))
    if uinput_uuid:
        devices = create_input_devices(uinput_uuid, uid)

    if ROOT and (uid != 0 or gid != 0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME": home,
            "USER": username,
            "LOGNAME": username,
        })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        #now we've changed uid, it is safe to honour all the env updates:
        configure_env(opts.env)
        os.environ.update(protected_env)

    if opts.chdir:
        os.chdir(opts.chdir)

    display = None
    if not proxying:
        no_gtk()
        if POSIX and not OSX and (starting or starting_desktop or shadowing):
            #check that we can access the X11 display:
            from xpra.x11.vfb_util import verify_display_ready
            if not verify_display_ready(xvfb, display_name, shadowing):
                return 1
            if not PYTHON3:
                from xpra.x11.gtk2.gdk_display_util import verify_gdk_display  #@UnusedImport
            else:
                from xpra.x11.gtk3.gdk_display_util import verify_gdk_display  #@Reimport
            display = verify_gdk_display(display_name)
            if not display:
                return 1
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        gui_init()

    #setup unix domain socket:
    if not opts.socket_dir and not opts.socket_dirs:
        #we always need at least one valid socket dir
        from xpra.platform.paths import get_socket_dirs
        opts.socket_dirs = get_socket_dirs()
    local_sockets = setup_local_sockets(opts.bind, opts.socket_dir,
                                        opts.socket_dirs, display_name,
                                        clobber, opts.mmap_group,
                                        opts.socket_permissions, username, uid,
                                        gid)
    netlog("setting up local sockets: %s", local_sockets)
    for rec, cleanup_socket in local_sockets:
        socktype, socket, sockpath = rec
        #ie: ("unix-domain", sock, sockpath), cleanup_socket
        sockets.append(rec)
        netlog("%s %s : %s", socktype, sockpath, socket)
        add_cleanup(cleanup_socket)
        if opts.mdns:
            ssh_port = get_ssh_port()
            netlog("ssh %s:%s : %s", "", ssh_port, socket)
            if ssh_port:
                add_mdns("ssh", "", ssh_port)

    kill_dbus = None
    if shadowing:
        from xpra.platform.shadow_server import ShadowServer
        app = ShadowServer()
    elif proxying:
        from xpra.server.proxy.proxy_server import ProxyServer
        app = ProxyServer()
    else:
        if not check_xvfb():
            return 1
        assert starting or starting_desktop or upgrading
        from xpra.x11.gtk2.gdk_display_source import init_gdk_display_source
        init_gdk_display_source()
        #(now we can access the X11 server)

        #make sure the pid we save is the real one:
        if not check_xvfb():
            return 1
        if xvfb_pid is not None:
            #save the new pid (we should have one):
            save_xvfb_pid(xvfb_pid)

        if POSIX:
            save_uinput_id(uinput_uuid or "")
            dbus_pid = -1
            dbus_env = {}
            if clobber:
                #get the saved pids and env
                dbus_pid = get_dbus_pid()
                dbus_env = get_dbus_env()
                log("retrieved existing dbus attributes")
            else:
                assert starting or starting_desktop
                if xvfb_pid is not None:
                    #save the new pid (we should have one):
                    save_xvfb_pid(xvfb_pid)
                bus_address = protected_env.get("DBUS_SESSION_BUS_ADDRESS")
                log("dbus_launch=%s, current DBUS_SESSION_BUS_ADDRESS=%s",
                    opts.dbus_launch, bus_address)
                if opts.dbus_launch and not bus_address:
                    #start a dbus server:
                    def kill_dbus():
                        log("kill_dbus: dbus_pid=%s" % dbus_pid)
                        if dbus_pid <= 0:
                            return
                        try:
                            os.kill(dbus_pid, signal.SIGINT)
                        except Exception as e:
                            log.warn(
                                "Warning: error trying to stop dbus with pid %i:",
                                dbus_pid)
                            log.warn(" %s", e)

                    add_cleanup(kill_dbus)
                    #this also updates os.environ with the dbus attributes:
                    dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
                    if dbus_pid > 0:
                        save_dbus_pid(dbus_pid)
                    if dbus_env:
                        save_dbus_env(dbus_env)
            log("dbus attributes: pid=%s, env=%s", dbus_pid, dbus_env)
            if dbus_env:
                os.environ.update(dbus_env)
                os.environ.update(protected_env)

        log("env=%s", os.environ)
        try:
            # This import is delayed because the module depends on gtk:
            from xpra.x11.bindings.window_bindings import X11WindowBindings
            X11Window = X11WindowBindings()
            if (starting or
                    starting_desktop) and not clobber and opts.resize_display:
                from xpra.x11.vfb_util import set_initial_resolution
                set_initial_resolution(starting_desktop)
        except ImportError as e:
            log.error(
                "Failed to load Xpra server components, check your installation: %s"
                % e)
            return 1
        if starting or upgrading:
            if not X11Window.displayHasXComposite():
                log.error(
                    "Xpra 'start' subcommand runs as a compositing manager")
                log.error(
                    " it cannot use a display which lacks the XComposite extension!"
                )
                return 1
            if starting:
                #check for an existing window manager:
                from xpra.x11.gtk2.wm import wm_check
                if not wm_check(display, opts.wm_name, upgrading):
                    return 1
            log("XShape=%s", X11Window.displayHasXShape())
            from xpra.x11.server import XpraServer
            app = XpraServer(clobber)
        else:
            assert starting_desktop
            from xpra.x11.desktop_server import XpraDesktopServer
            app = XpraDesktopServer()
        app.init_virtual_devices(devices)

    #publish mdns records:
    if opts.mdns:
        from xpra.platform.info import get_username
        from xpra.server.socket_util import mdns_publish
        mdns_info = {
            "display": display_name,
            "username": get_username(),
            "uuid": strtobytes(app.uuid),
            "platform": sys.platform,
            "type": app.session_type,
        }
        if opts.session_name:
            mdns_info["session"] = opts.session_name
        for mode, listen_on in mdns_recs.items():
            mdns_publish(display_name, mode, listen_on, mdns_info)

    try:
        app._ssl_wrap_socket = wrap_socket_fn
        app.original_desktop_display = desktop_display
        app.exec_cwd = opts.chdir or cwd
        app.init(opts)
        app.init_components(opts)
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server",
                  app.session_type,
                  exc_info=True)
        log.error(str(e))
        log.info("")
        return 1

    #honour start child, html webserver, and setup child reaper
    if not proxying and not upgrading:
        if opts.exit_with_children:
            assert opts.start_child, "exit-with-children was specified but start-child is missing!"
        app.start_commands = opts.start
        app.start_child_commands = opts.start_child
        app.start_after_connect = opts.start_after_connect
        app.start_child_after_connect = opts.start_child_after_connect
        app.start_on_connect = opts.start_on_connect
        app.start_child_on_connect = opts.start_child_on_connect
        app.exec_start_commands()
    del opts

    log("%s(%s)", app.init_sockets, sockets)
    app.init_sockets(sockets)
    log("%s(%s)", app.init_when_ready, _when_ready)
    app.init_when_ready(_when_ready)

    try:
        #from here on, we own the vfb, even if we inherited one:
        if (starting or starting_desktop or upgrading) and clobber:
            #and it will be killed if exit cleanly:
            xvfb_pid = get_xvfb_pid()

        log("running %s", app.run)
        r = app.run()
        log("%s()=%s", app.run, r)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        r = 0
    except Exception:
        log.error("server error", exc_info=True)
        r = -128
    if r > 0:
        # Upgrading/exiting, so leave X and dbus servers running
        if close_display:
            _cleanups.remove(close_display)
        if kill_dbus:
            _cleanups.remove(kill_dbus)
        from xpra.server.server_core import ServerCore
        if r == ServerCore.EXITING_CODE:
            log.info("exiting: not cleaning up Xvfb")
        else:
            log.info("upgrading: not cleaning up Xvfb")
        log("cleanups=%s", _cleanups)
        r = 0
    return r
Exemplo n.º 26
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)
Exemplo n.º 27
0
def create_unix_domain_socket(sockpath,
                              mmap_group=False,
                              socket_permissions="600"):
    if mmap_group:
        #when using the mmap group option, use '660'
        umask = 0o117
    else:
        #parse octal mode given as config option:
        try:
            if type(socket_permissions) == int:
                sperms = socket_permissions
            else:
                #assume octal string:
                sperms = int(socket_permissions, 8)
            assert sperms >= 0 and sperms <= 0o777
        except ValueError:
            raise ValueError(
                "invalid socket permissions (must be an octal number): '%s'" %
                socket_permissions)
        #now convert this to a umask!
        umask = 0o777 - sperms
    listener = socket.socket(socket.AF_UNIX)
    listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    #bind the socket, using umask to set the correct permissions
    orig_umask = os.umask(umask)
    try:
        listener.bind(sockpath)
    finally:
        os.umask(orig_umask)
    try:
        inode = os.stat(sockpath).st_ino
    except:
        inode = -1
    #set to the "xpra" group if we are a member of it, or if running as root:
    uid = getuid()
    username = get_username_for_uid(uid)
    groups = get_groups(username)
    if uid == 0 or GROUP in groups:
        group_id = get_group_id(GROUP)
        if group_id >= 0:
            try:
                os.lchown(sockpath, -1, group_id)
            except Exception as e:
                log = get_network_logger()
                log.warn("Warning: failed to set '%s' group ownership", GROUP)
                log.warn(" on socket '%s':", sockpath)
                log.warn(" %s", e)
            #don't know why this doesn't work:
            #os.fchown(listener.fileno(), -1, group_id)
    def cleanup_socket():
        log = get_network_logger()
        try:
            cur_inode = os.stat(sockpath).st_ino
        except:
            log.info("socket '%s' already deleted", sockpath)
            return
        delpath = sockpath
        log("cleanup_socket '%s', original inode=%s, new inode=%s", sockpath,
            inode, cur_inode)
        if cur_inode == inode:
            log.info("removing socket %s", delpath)
            try:
                os.unlink(delpath)
            except:
                pass

    return listener, cleanup_socket
Exemplo n.º 28
0
 def preexec():
     if getuid() == 0 and (uid != 0 or gid != 0):
         setuidgid(uid, gid)
     else:
         setsid()
Exemplo n.º 29
0
from xpra.scripts.config import make_defaults_struct, PROXY_START_OVERRIDABLE_OPTIONS, OPTION_TYPES
from xpra.scripts.main import parse_display_name, connect_to, start_server_subprocess
from xpra.make_thread import start_thread
from xpra.log import Logger

log = Logger("proxy")
authlog = Logger("proxy", "auth")

glib = import_glib()

freeze_support()

PROXY_SOCKET_TIMEOUT = envfloat("XPRA_PROXY_SOCKET_TIMEOUT", "0.1")
PROXY_WS_TIMEOUT = envfloat("XPRA_PROXY_WS_TIMEOUT", "1.0")
assert PROXY_SOCKET_TIMEOUT > 0, "invalid proxy socket timeout"
CAN_STOP_PROXY = envbool("XPRA_CAN_STOP_PROXY", getuid() != 0)
STOP_PROXY_SOCKET_TYPES = os.environ.get("XPRA_STOP_PROXY_SOCKET_TYPES",
                                         "unix-domain,named-pipe").split(",")
PROXY_INSTANCE_THREADED = envbool("XPRA_PROXY_INSTANCE_THREADED", False)
PROXY_CLEANUP_GRACE_PERIOD = envfloat("XPRA_PROXY_CLEANUP_GRACE_PERIOD", "0.5")

MAX_CONCURRENT_CONNECTIONS = envint("XPRA_PROXY_MAX_CONCURRENT_CONNECTIONS",
                                    200)
if WIN32:
    #DEFAULT_ENV_WHITELIST = "ALLUSERSPROFILE,APPDATA,COMMONPROGRAMFILES,COMMONPROGRAMFILES(X86),COMMONPROGRAMW6432,COMPUTERNAME,COMSPEC,FP_NO_HOST_CHECK,LOCALAPPDATA,NUMBER_OF_PROCESSORS,OS,PATH,PATHEXT,PROCESSOR_ARCHITECTURE,PROCESSOR_ARCHITECTURE,PROCESSOR_IDENTIFIER,PROCESSOR_LEVEL,PROCESSOR_REVISION,PROGRAMDATA,PROGRAMFILES,PROGRAMFILES(X86),PROGRAMW6432,PSMODULEPATH,PUBLIC,SYSTEMDRIVE,SYSTEMROOT,TEMP,TMP,USERDOMAIN,WORKGROUP,USERNAME,USERPROFILE,WINDIR,XPRA_REDIRECT_OUTPUT,XPRA_LOG_FILENAME,XPRA_ALL_DEBUG"
    DEFAULT_ENV_WHITELIST = "*"
else:
    DEFAULT_ENV_WHITELIST = "LANG,HOSTNAME,PWD,TERM,SHELL,SHLVL,PATH"
ENV_WHITELIST = os.environ.get("XPRA_PROXY_ENV_WHITELIST",
                               DEFAULT_ENV_WHITELIST).split(",")
Exemplo n.º 30
0
def do_run_server(error_cb,
                  opts,
                  mode,
                  xpra_file,
                  extra_args,
                  desktop_display=None,
                  progress_cb=None):
    assert mode in (
        "start",
        "start-desktop",
        "upgrade",
        "upgrade-desktop",
        "shadow",
        "proxy",
    )

    def _progress(i, msg):
        if progress_cb:
            progress_cb(i, msg)

    progress = _progress

    progress(10, "initializing environment")
    try:
        cwd = os.getcwd()
    except OSError:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding == "help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    #remove anything pointing to dbus from the current env
    #(so we only detect a dbus instance started by pam,
    # and override everything else)
    for k in tuple(os.environ.keys()):
        if k.startswith("DBUS_"):
            del os.environ[k]

    use_display = parse_bool("use-display", opts.use_display)
    starting = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    upgrading_desktop = mode == "upgrade-desktop"
    shadowing = mode == "shadow"
    proxying = mode == "proxy"

    if not proxying and POSIX and not OSX:
        #we don't support wayland servers,
        #so make sure GDK will use the X11 backend:
        from xpra.os_util import saved_env
        saved_env["GDK_BACKEND"] = "x11"
        os.environ["GDK_BACKEND"] = "x11"

    has_child_arg = (opts.start_child or opts.start_child_on_connect
                     or opts.start_child_after_connect
                     or opts.start_child_on_last_client_exit)
    if proxying or upgrading or upgrading_desktop:
        #when proxying or upgrading, don't exec any plain start commands:
        opts.start = opts.start_child = []
    elif opts.exit_with_children:
        assert has_child_arg, "exit-with-children was specified but start-child* is missing!"
    elif opts.start_child:
        warn("Warning: the 'start-child' option is used,")
        warn(" but 'exit-with-children' is not enabled,")
        warn(" use 'start' instead")

    if opts.bind_rfb and (proxying or starting):
        get_util_logger().warn(
            "Warning: bind-rfb sockets cannot be used with '%s' mode" % mode)
        opts.bind_rfb = []

    if not shadowing and not starting_desktop:
        opts.rfb_upgrade = 0

    if upgrading or upgrading_desktop or shadowing:
        #there should already be one running
        #so change None ('auto') to False
        if opts.pulseaudio is None:
            opts.pulseaudio = False

    #get the display name:
    if shadowing and not extra_args:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = "Main"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra, desktop_display)
    elif (upgrading or upgrading_desktop) and not extra_args:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb(
                "too many extra arguments (%i): only expected a display number"
                % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not upgrading and not use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb(
                        "you must specify a free virtual display name to use with the proxy server"
                    )
            elif use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir,
                                                  opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not (shadowing or proxying or upgrading or upgrading_desktop) and \
    opts.exit_with_children and not has_child_arg:
        error_cb(
            "--exit-with-children specified without any children to spawn; exiting immediately"
        )

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import (
        xpra_runner_shell_script,
        write_runner_shell_scripts,
        find_log_dir,
        create_input_devices,
        source_env,
    )
    script = None
    if POSIX and getuid() != 0:
        script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    ROOT = POSIX and getuid() == 0

    protected_fds = []
    protected_env = {}
    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = tuple(
                os.path.abspath(x) for x in opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    displayfd = 0
    if POSIX and opts.displayfd:
        try:
            displayfd = int(opts.displayfd)
            if displayfd > 0:
                protected_fds.append(displayfd)
        except ValueError as e:
            stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd)
            stderr.write(" %s\n" % e)
            del e

    clobber = int(upgrading or upgrading_desktop) * CLOBBER_UPGRADE | int(
        use_display or 0) * CLOBBER_USE_DISPLAY
    start_vfb = not (shadowing or proxying or clobber)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()

    # if pam is present, try to create a new session:
    pam = None
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid != 0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session  #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            del e
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                #"XDG_SEAT"               : "seat1",
                #"XDG_VTNR"               : "0",
                "XDG_SESSION_TYPE": "x11",
                #"XDG_SESSION_CLASS"      : "user",
                "XDG_SESSION_DESKTOP": "xpra",
            }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds += fdc.get_new_fds()

    #get XDG_RUNTIME_DIR from env options,
    #which may not be have updated os.environ yet when running as root with "--uid="
    xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", ""))
    if ROOT and (uid > 0 or gid > 0):
        #we're going to chown the directory if we create it,
        #ensure this cannot be abused, only use "safe" paths:
        if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp")
                   if xrd.startswith(x)):
            xrd = ""
        #these paths could cause problems if we were to create and chown them:
        if xrd.startswith("/tmp/.X11-unix") or xrd.startswith(
                "/tmp/.XIM-unix"):
            xrd = ""
    if not xrd:
        xrd = os.environ.get("XDG_RUNTIME_DIR")
    xrd = create_runtime_dir(xrd, uid, gid)
    if xrd:
        #this may override the value we get from pam
        #with the value supplied by the user:
        protected_env["XDG_RUNTIME_DIR"] = xrd

    if script:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    import datetime
    extra_expand = {
        "TIMESTAMP": datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    }
    log_to_file = opts.daemon or os.environ.get("XPRA_LOG_TO_FILE", "") == "1"
    if start_vfb or log_to_file:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower() == "auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException(
                    "cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

    if log_to_file:
        from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
        log_filename0 = osexpand(
            select_log_file(log_dir, opts.log_file, display_name), username,
            uid, gid, extra_expand)
        if os.path.exists(log_filename0) and not display_name.startswith("S"):
            #don't overwrite the log file just yet,
            #as we may still fail to start
            log_filename0 += ".new"
        logfd = open_log_file(log_filename0)
        if POSIX and ROOT and (uid > 0 or gid > 0):
            try:
                os.fchown(logfd, uid, gid)
            except OSError as e:
                noerr(stderr.write,
                      "failed to chown the log file '%s'\n" % log_filename0)
                noerr(stderr.flush)
        stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
        noerr(
            stderr.write, "Entering daemon mode; " +
            "any further errors will be reported to:\n" +
            ("  %s\n" % log_filename0))
        noerr(stderr.flush)
        os.environ["XPRA_SERVER_LOG"] = log_filename0
    else:
        #server log does not exist:
        os.environ.pop("XPRA_SERVER_LOG", None)

    #warn early about this:
    if (starting or starting_desktop
        ) and desktop_display and opts.notifications and not opts.dbus_launch:
        print_DE_warnings()

    if start_vfb and opts.xvfb.find("Xephyr") >= 0 and opts.sync_xvfb <= 0:
        warn("Warning: using Xephyr as vfb")
        warn(" you should also enable the sync-xvfb option")
        warn(" to keep the Xephyr window updated")

    progress(10, "creating sockets")
    from xpra.net.socket_util import get_network_logger, setup_local_sockets, create_sockets
    sockets = create_sockets(opts, error_cb)

    sanitize_env()
    os.environ.update(source_env(opts.source))
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        if not OSX:
            os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        if POSIX:
            os.environ["CKCON_X11_DISPLAY"] = display_name
    elif not start_vfb or opts.xvfb.find("Xephyr") < 0:
        os.environ.pop("DISPLAY", None)
    os.environ.update(protected_env)
    from xpra.log import Logger
    log = Logger("server")
    log("env=%s", os.environ)

    UINPUT_UUID_LEN = 12
    UINPUT_UUID_MIN_LEN = 12
    UINPUT_UUID_MAX_LEN = 32
    # Start the Xvfb server first to get the display_name if needed
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    uinput_uuid = None
    if start_vfb and use_display is None:
        #use-display='auto' so we have to figure out
        #if we have to start the vfb or not:
        if not display_name:
            use_display = False
        else:
            progress(20, "connecting to the display")
            start_vfb = verify_display(
                None, display_name, log_errors=False, timeout=1) != 0
    if start_vfb:
        progress(20, "starting a virtual display")
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth, starting_desktop)
        from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process, parse_resolution
        from xpra.server.server_util import has_uinput
        uinput_uuid = None
        if has_uinput() and opts.input_devices.lower() in (
                "uinput", "auto") and not shadowing:
            from xpra.os_util import get_rand_chars
            uinput_uuid = get_rand_chars(UINPUT_UUID_LEN)
        vfb_geom = ""
        try:
            vfb_geom = parse_resolution(opts.resize_display)
        except Exception:
            pass
        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, vfb_geom,
                                                  pixel_depth, display_name,
                                                  cwd, uid, gid, username,
                                                  xauth_data, uinput_uuid)
        for f in cleanups:
            add_cleanup(f)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name != odisplay_name and pam:
            pam.set_items({"XDISPLAY": display_name})

        def check_xvfb(timeout=0):
            return check_xvfb_process(xvfb, timeout=timeout, command=opts.xvfb)
    else:
        if POSIX and clobber:
            #if we're meant to be using a private XAUTHORITY file,
            #make sure to point to it:
            from xpra.x11.vfb_util import get_xauthority_path
            xauthority = get_xauthority_path(display_name, username, uid, gid)
            if os.path.exists(xauthority):
                log("found XAUTHORITY=%s", xauthority)
                os.environ["XAUTHORITY"] = xauthority

        def check_xvfb(timeout=0):  #pylint: disable=unused-argument
            return True

    if POSIX and not OSX and displayfd > 0:
        from xpra.platform.displayfd import write_displayfd
        try:
            display_no = display_name[1:]
            #ensure it is a string containing the number:
            display_no = str(int(display_no))
            log("writing display_no='%s' to displayfd=%i", display_no,
                displayfd)
            assert write_displayfd(displayfd, display_no), "timeout"
        except Exception as e:
            log.error("write_displayfd failed", exc_info=True)
            log.error("Error: failed to write '%s' to fd=%s", display_name,
                      displayfd)
            log.error(" %s", str(e) or type(e))
            del e

    if not check_xvfb(1):
        noerr(stderr.write, "vfb failed to start, exiting\n")
        return EXIT_VFB_ERROR

    if WIN32 and os.environ.get("XPRA_LOG_FILENAME"):
        os.environ["XPRA_SERVER_LOG"] = os.environ["XPRA_LOG_FILENAME"]
    if opts.daemon:
        log_filename1 = osexpand(
            select_log_file(log_dir, opts.log_file, display_name), username,
            uid, gid, extra_expand)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            try:
                os.rename(log_filename0, log_filename1)
            except (OSError, IOError):
                pass
            else:
                os.environ["XPRA_SERVER_LOG"] = log_filename1
            if odisplay_name != display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write,
                  "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb():
        noerr(stderr.write, "vfb failed to start, exiting\n")
        return EXIT_VFB_ERROR

    #create devices for vfb if needed:
    devices = {}
    if not start_vfb and not proxying and not shadowing and envbool(
            "XPRA_UINPUT", True):
        #try to find the existing uinput uuid:
        #use a subprocess to avoid polluting our current process
        #with X11 connections before we get a chance to change uid
        prop = "_XPRA_UINPUT_ID"
        cmd = ["xprop", "-display", display_name, "-root", prop]
        log("looking for '%s' on display '%s' with XAUTHORITY='%s'", prop,
            display_name, os.environ.get("XAUTHORITY"))
        try:
            code, out, err = get_status_output(cmd)
        except Exception as e:
            log("failed to get existing uinput id: %s", e)
            del e
        else:
            log("Popen(%s)=%s", cmd, (code, out, err))
            if code == 0 and out.find("=") > 0:
                uinput_uuid = out.split("=", 1)[1]
                log("raw uinput uuid=%s", uinput_uuid)
                uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ '))
                if uinput_uuid:
                    if len(uinput_uuid) > UINPUT_UUID_MAX_LEN or len(
                            uinput_uuid) < UINPUT_UUID_MIN_LEN:
                        log.warn("Warning: ignoring invalid uinput id:")
                        log.warn(" '%s'", uinput_uuid)
                        uinput_uuid = None
                    else:
                        log.info("retrieved existing uinput id: %s",
                                 bytestostr(uinput_uuid))
    if uinput_uuid:
        devices = create_input_devices(uinput_uuid, uid)

    if ROOT and (uid != 0 or gid != 0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME": home,
            "USER": username,
            "LOGNAME": username,
        })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        #now we've changed uid, it is safe to honour all the env updates:
        configure_env(opts.env)
        os.environ.update(protected_env)

    if opts.chdir:
        log("chdir(%s)", opts.chdir)
        os.chdir(opts.chdir)

    dbus_pid, dbus_env = 0, {}
    if not shadowing and POSIX and not OSX and not clobber:
        no_gtk()
        assert starting or starting_desktop or proxying
        try:
            from xpra.server.dbus.dbus_start import start_dbus
        except ImportError as e:
            log("dbus components are not installed: %s", e)
        else:
            dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
            if dbus_env:
                os.environ.update(dbus_env)

    if not proxying:
        if POSIX and not OSX:
            no_gtk()
            if starting or starting_desktop or shadowing:
                r = verify_display(xvfb, display_name, shadowing)
                if r:
                    return r
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        log("gui_init()")
        gui_init()

    progress(50, "creating local sockets")
    #setup unix domain socket:
    netlog = get_network_logger()
    local_sockets = setup_local_sockets(opts.bind, opts.socket_dir,
                                        opts.socket_dirs, display_name,
                                        clobber, opts.mmap_group,
                                        opts.socket_permissions, username, uid,
                                        gid)
    netlog("setting up local sockets: %s", local_sockets)
    sockets.update(local_sockets)
    if POSIX and (starting or upgrading or starting_desktop
                  or upgrading_desktop):
        #all unix domain sockets:
        ud_paths = [
            sockpath for stype, _, sockpath, _ in local_sockets
            if stype == "unix-domain"
        ]
        if ud_paths:
            #choose one so our xdg-open override script can use to talk back to us:
            if opts.forward_xdg_open:
                for x in ("/usr/libexec/xpra", "/usr/lib/xpra"):
                    xdg_override = os.path.join(x, "xdg-open")
                    if os.path.exists(xdg_override):
                        os.environ["PATH"] = x + os.pathsep + os.environ.get(
                            "PATH", "")
                        os.environ["XPRA_SERVER_SOCKET"] = ud_paths[0]
                        break
        else:
            log.warn("Warning: no local server sockets,")
            if opts.forward_xdg_open:
                log.warn(" forward-xdg-open cannot be enabled")
            log.warn(" non-embedded ssh connections will not be available")

    set_server_features(opts)

    if not proxying and POSIX and not OSX:
        if not check_xvfb():
            return 1
        from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source
        if os.environ.get("NO_AT_BRIDGE") is None:
            os.environ["NO_AT_BRIDGE"] = "1"
        init_gdk_display_source()
        #(now we can access the X11 server)
        if uinput_uuid:
            save_uinput_id(uinput_uuid)

    progress(60, "initializing server")
    if shadowing:
        app = make_shadow_server()
    elif proxying:
        app = make_proxy_server()
    else:
        if starting or upgrading:
            app = make_server(clobber)
        else:
            assert starting_desktop or upgrading_desktop
            app = make_desktop_server(clobber)
        app.init_virtual_devices(devices)

    try:
        app.exec_cwd = opts.chdir or cwd
        app.display_name = display_name
        app.init(opts)
        progress(70, "initializing sockets")
        app.init_sockets(sockets)
        app.init_dbus(dbus_pid, dbus_env)
        if not shadowing and not proxying:
            app.init_display_pid(xvfb_pid)
        app.original_desktop_display = desktop_display
        del opts
        if not app.server_ready():
            return 1
        progress(80, "finalizing")
        app.server_init()
        app.setup()
        app.init_when_ready(_when_ready)
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        app.cleanup()
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server",
                  app.session_type,
                  exc_info=True)
        log.error(str(e))
        log.info("")
        if upgrading or upgrading_desktop:
            #something abnormal occurred,
            #don't kill the vfb on exit:
            from xpra.server import EXITING_CODE
            app._upgrading = EXITING_CODE
        app.cleanup()
        return 1

    try:
        progress(100, "running")
        log("running %s", app.run)
        r = app.run()
        log("%s()=%s", app.run, r)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        app.cleanup()
        return EXIT_OK
    except Exception:
        log.error("server error", exc_info=True)
        app.cleanup()
        return -128
    else:
        if r > 0:
            r = 0
    return r
Exemplo n.º 31
0
 def preexec():
     setsid()
     if getuid() == 0 and uid:
         setuidgid(uid, gid)
Exemplo n.º 32
0
    def run(self):
        log("ProxyProcess.run() pid=%s, uid=%s, gid=%s", os.getpid(), getuid(), getgid())
        setuidgid(self.uid, self.gid)
        if self.env_options:
            #TODO: whitelist env update?
            os.environ.update(self.env_options)
        self.video_init()

        log.info("new proxy instance started")
        log.info(" for client %s", self.client_conn)
        log.info(" and server %s", self.server_conn)

        signal.signal(signal.SIGTERM, self.signal_quit)
        signal.signal(signal.SIGINT, self.signal_quit)
        log("registered signal handler %s", self.signal_quit)

        start_thread(self.server_message_queue, "server message queue")

        if not self.create_control_socket():
            #TODO: should send a message to the client
            return
        self.control_socket_thread = start_thread(self.control_socket_loop, "control")

        self.main_queue = Queue()
        #setup protocol wrappers:
        self.server_packets = Queue(PROXY_QUEUE_SIZE)
        self.client_packets = Queue(PROXY_QUEUE_SIZE)
        self.client_protocol = Protocol(self, self.client_conn, self.process_client_packet, self.get_client_packet)
        self.client_protocol.restore_state(self.client_state)
        self.server_protocol = Protocol(self, self.server_conn, self.process_server_packet, self.get_server_packet)
        #server connection tweaks:
        self.server_protocol.large_packets.append("input-devices")
        self.server_protocol.large_packets.append("draw")
        self.server_protocol.large_packets.append("window-icon")
        self.server_protocol.large_packets.append("keymap-changed")
        self.server_protocol.large_packets.append("server-settings")
        if self.caps.boolget("file-transfer"):
            self.client_protocol.large_packets.append("send-file")
            self.client_protocol.large_packets.append("send-file-chunk")
            self.server_protocol.large_packets.append("send-file")
            self.server_protocol.large_packets.append("send-file-chunk")
        self.server_protocol.set_compression_level(self.session_options.get("compression_level", 0))
        self.server_protocol.enable_default_encoder()

        self.lost_windows = set()
        self.encode_queue = Queue()
        self.encode_thread = start_thread(self.encode_loop, "encode")

        log("starting network threads")
        self.server_protocol.start()
        self.client_protocol.start()

        self.send_hello()
        self.timeout_add(VIDEO_TIMEOUT*1000, self.timeout_video_encoders)

        try:
            self.run_queue()
        except KeyboardInterrupt as e:
            self.stop(str(e))
        finally:
            log("ProxyProcess.run() ending %s", os.getpid())