Example #1
0
 def get_challenge(self):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_hex_uuid()+get_hex_uuid()
     #we need the raw password, so tell the client to use "xor":
     return self.salt, "xor"
Example #2
0
 def get_challenge(self):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_hex_uuid()+get_hex_uuid()
     #this authenticator can use the safer "hmac" digest:
     return self.salt, "hmac"
Example #3
0
 def get_challenge(self):
     if self.salt is not None:
         log.error("challenge already sent!")
         return None
     self.salt = get_hex_uuid()+get_hex_uuid()
     #we need the raw password, so tell the client to use "xor":
     return self.salt, "xor"
Example #4
0
 def _process_challenge(self, packet):
     authlog("processing challenge: %s", packet[1:])
     def warn_server_and_exit(code, message, server_message="authentication failed"):
         authlog.error("Error: authentication failed:")
         authlog.error(" %s", message)
         self.disconnect_and_quit(code, server_message)
     if not self.password_file and not os.environ.get('XPRA_PASSWORD'):
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "this server requires authentication, please provide a password", "no password available")
         return
     password = self.load_password()
     if not password:
         warn_server_and_exit(EXIT_PASSWORD_FILE_ERROR, "failed to load password from file %s" % self.password_file, "no password available")
         return
     salt = packet[1]
     if self.encryption:
         assert len(packet)>=3, "challenge does not contain encryption details to use for the response"
         server_cipher = typedict(packet[2])
         key = self.get_encryption_key()
         if key is None:
             warn_server_and_exit(EXIT_ENCRYPTION, "the server does not use any encryption", "client requires encryption")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     #all server versions support a client salt,
     #they also tell us which digest to use:
     digest = packet[3]
     client_salt = get_hex_uuid()+get_hex_uuid()
     #TODO: use some key stretching algorigthm? (meh)
     try:
         from xpra.codecs.xor.cyxor import xor_str           #@UnresolvedImport
         salt = xor_str(salt, client_salt)
     except:
         salt = xor(salt, client_salt)
     if digest==b"hmac":
         import hmac, hashlib
         def s(v):
             try:
                 return v.encode()
             except:
                 return str(v)
         password = s(password)
         salt = s(salt)
         challenge_response = hmac.HMAC(password, salt, digestmod=hashlib.md5).hexdigest()
     elif digest==b"xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             warn_server_and_exit(EXIT_ENCRYPTION, "server requested digest %s, cowardly refusing to use it without encryption" % digest, "invalid digest")
             return
         challenge_response = xor(password, salt)
     else:
         warn_server_and_exit(EXIT_PASSWORD_REQUIRED, "server requested an unsupported digest: %s" % digest, "invalid digest")
         return
     if digest:
         authlog("%s(%s, %s)=%s", digest, binascii.hexlify(password), binascii.hexlify(salt), challenge_response)
     self.password_sent = True
     self.remove_packet_handlers("challenge")
     self.send_hello(challenge_response, client_salt)
Example #5
0
 def _process_challenge(self, packet):
     log("processing challenge: %s", packet[1:])
     if not self.password_file:
         self.warn_and_quit(
             EXIT_PASSWORD_REQUIRED,
             "server requires authentication, please provide a password")
         return
     password = self.load_password()
     if not password:
         self.warn_and_quit(
             EXIT_PASSWORD_FILE_ERROR,
             "failed to load password from file %s" % self.password_file)
         return
     salt = packet[1]
     if self.encryption:
         assert len(
             packet
         ) >= 3, "challenge does not contain encryption details to use for the response"
         server_cipher = packet[2]
         key = self.get_encryption_key()
         if key is None:
             self.warn_and_quit(EXIT_ENCRYPTION,
                                "encryption key is missing")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     digest = "hmac"
     client_can_salt = len(packet) >= 4
     client_salt = None
     if client_can_salt:
         #server supports client salt, and tells us which digest to use:
         digest = packet[3]
         client_salt = get_hex_uuid() + get_hex_uuid()
         #TODO: use some key stretching algorigthm? (meh)
         salt = xor(salt, client_salt)
     if digest == "hmac":
         import hmac
         challenge_response = hmac.HMAC(password, salt).hexdigest()
     elif digest == "xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             self.warn_and_quit(
                 EXIT_ENCRYPTION,
                 "server requested digest %s, cowardly refusing to use it without encryption"
                 % digest)
             return
         challenge_response = xor(password, salt)
     else:
         self.warn_and_quit(
             EXIT_PASSWORD_REQUIRED,
             "server requested an unsupported digest: %s" % digest)
         return
     if digest:
         log("%s(%s, %s)=%s", digest, password, salt, challenge_response)
     self.password_sent = True
     self.send_hello(challenge_response, client_salt)
Example #6
0
def new_cipher_caps(proto, cipher, encryption_key):
    iv = get_hex_uuid()[:16]
    key_salt = get_hex_uuid() + get_hex_uuid()
    iterations = 1000
    proto.set_cipher_in(cipher, iv, encryption_key, key_salt, iterations)
    return {
        "cipher": cipher,
        "cipher.iv": iv,
        "cipher.key_salt": key_salt,
        "cipher.key_stretch_iterations": iterations
    }
Example #7
0
def new_cipher_caps(proto, cipher, encryption_key):
    iv = get_hex_uuid()[:16]
    key_salt = get_hex_uuid()+get_hex_uuid()
    iterations = 1000
    proto.set_cipher_in(cipher, iv, encryption_key, key_salt, iterations)
    return {
                 "cipher"           : cipher,
                 "cipher.iv"        : iv,
                 "cipher.key_salt"  : key_salt,
                 "cipher.key_stretch_iterations" : iterations
                 }
Example #8
0
    def make_hello_base(self):
        capabilities = get_network_caps()
        capabilities.update({
            "version": local_version,
            "encoding.generic": True,
            "namespace": True,
            "file-transfer": self.file_transfer,
            "file-size-limit": self.file_size_limit,
            "printing": self.printing,
            "hostname": socket.gethostname(),
            "uuid": self.uuid,
            "username": self.username,
            "name": get_name(),
            "client_type": self.client_type(),
            "python.version": sys.version_info[:3],
            "compression_level": self.compression_level,
        })
        if self.display:
            capabilities["display"] = self.display

        def up(prefix, d):
            updict(capabilities, prefix, d)

        up("platform", get_platform_info())
        up("build", get_version_info())
        mid = get_machine_id()
        if mid:
            capabilities["machine_id"] = mid

        if self.encryption:
            assert self.encryption in ENCRYPTION_CIPHERS
            iv = get_hex_uuid()[:16]
            key_salt = get_hex_uuid() + get_hex_uuid()
            iterations = 1000
            capabilities.update({
                "cipher": self.encryption,
                "cipher.iv": iv,
                "cipher.key_salt": key_salt,
                "cipher.key_stretch_iterations": iterations,
            })
            key = self.get_encryption_key()
            if key is None:
                self.warn_and_quit(EXIT_ENCRYPTION,
                                   "encryption key is missing")
                return
            self._protocol.set_cipher_in(self.encryption, iv, key, key_salt,
                                         iterations)
            log("encryption capabilities: %s",
                [(k, v)
                 for k, v in capabilities.items() if k.startswith("cipher")])
        return capabilities
Example #9
0
 def _process_challenge(self, packet):
     log("processing challenge: %s", packet[1:])
     if not self.password_file and not os.environ.get('XPRA_PASSWORD'):
         self.warn_and_quit(EXIT_PASSWORD_REQUIRED, "server requires authentication, please provide a password")
         return
     password = self.load_password()
     if not password:
         self.warn_and_quit(EXIT_PASSWORD_FILE_ERROR, "failed to load password from file %s" % self.password_file)
         return
     salt = packet[1]
     if self.encryption:
         assert len(packet)>=3, "challenge does not contain encryption details to use for the response"
         server_cipher = packet[2]
         key = self.get_encryption_key()
         if key is None:
             self.warn_and_quit(EXIT_ENCRYPTION, "encryption key is missing")
             return
         if not self.set_server_encryption(server_cipher, key):
             return
     digest = "hmac"
     client_can_salt = len(packet)>=4
     client_salt = None
     if client_can_salt:
         #server supports client salt, and tells us which digest to use:
         digest = packet[3]
         client_salt = get_hex_uuid()+get_hex_uuid()
         #TODO: use some key stretching algorigthm? (meh)
         salt = xor(salt, client_salt)
     if digest=="hmac":
         import hmac
         challenge_response = hmac.HMAC(password, salt).hexdigest()
     elif digest=="xor":
         #don't send XORed password unencrypted:
         if not self._protocol.cipher_out and not ALLOW_UNENCRYPTED_PASSWORDS:
             self.warn_and_quit(EXIT_ENCRYPTION, "server requested digest %s, cowardly refusing to use it without encryption" % digest)
             return
         challenge_response = xor(password, salt)
     else:
         self.warn_and_quit(EXIT_PASSWORD_REQUIRED, "server requested an unsupported digest: %s" % digest)
         return
     if digest:
         log("%s(%s, %s)=%s", digest, password, salt, challenge_response)
     self.password_sent = True
     for d in (self._packet_handlers, self._ui_packet_handlers):
         try:
             del d["challenge"]
         except:
             pass
     self.send_hello(challenge_response, client_salt)
Example #10
0
 def __init__(self, session_options, video_encoder_modules, pings,
              disp_desc, cipher, encryption_key, caps):
     self.session_options = session_options
     self.video_encoder_modules = video_encoder_modules
     self.pings = pings
     self.disp_desc = disp_desc
     self.cipher = cipher
     self.encryption_key = encryption_key
     self.caps = caps
     log("ProxyInstance%s",
         (session_options, video_encoder_modules, disp_desc, cipher,
          encryption_key, "%s: %s.." % (type(caps), ellipsizer(caps))))
     self.uuid = get_hex_uuid()
     self.client_protocol = None
     self.server_protocol = None
     #ping handling:
     self.client_last_ping = 0
     self.server_last_ping = 0
     self.client_last_ping_echo = 0
     self.server_last_ping_echo = 0
     self.client_last_ping_latency = 0
     self.server_last_ping_latency = 0
     self.client_ping_timer = None
     self.server_ping_timer = None
     self.client_challenge_packet = None
     self.exit = False
     self.lost_windows = None
     self.encode_queue = None  #holds draw packets to encode
     self.encode_thread = None
     self.video_encoding_defs = None
     self.video_encoders = None
     self.video_encoders_last_used_time = None
     self.video_encoder_types = None
     self.video_helper = None
Example #11
0
 def make_hello(self, challenge_response=None):
     capabilities = {}
     add_version_info(capabilities)
     capabilities["python.version"] = sys.version_info[:3]
     if challenge_response:
         assert self.password
         capabilities["challenge_response"] = challenge_response
     if self.encryption:
         assert self.encryption in ENCRYPTION_CIPHERS
         capabilities["cipher"] = self.encryption
         iv = get_hex_uuid()[:16]
         capabilities["cipher.iv"] = iv
         key_salt = get_hex_uuid()
         capabilities["cipher.key_salt"] = key_salt
         iterations = 1000
         capabilities["cipher.key_stretch_iterations"] = iterations
         self._protocol.set_cipher_in(self.encryption, iv,
                                      self.get_password(), key_salt,
                                      iterations)
         log("encryption capabilities: %s",
             [(k, v)
              for k, v in capabilities.items() if k.startswith("cipher")])
     capabilities["platform"] = sys.platform
     capabilities["platform.release"] = python_platform.release()
     capabilities["platform.machine"] = python_platform.machine()
     capabilities["platform.processor"] = python_platform.processor()
     capabilities["client_type"] = self.client_type()
     capabilities["namespace"] = True
     capabilities["raw_packets"] = True
     capabilities["chunked_compression"] = True
     capabilities["bencode"] = True
     capabilities["rencode"] = has_rencode
     if has_rencode:
         capabilities["rencode.version"] = rencode_version
     capabilities["hostname"] = socket.gethostname()
     capabilities["uuid"] = self.uuid
     try:
         from xpra.platform.info import get_username, get_name
         capabilities["username"] = get_username()
         capabilities["name"] = get_name()
     except:
         log.error("failed to get username/name", exc_info=True)
     capabilities["randr_notify"] = False  #only client.py cares about this
     capabilities["windows"] = False  #only client.py cares about this
     if self._reverse_aliases:
         capabilities["aliases"] = self._reverse_aliases
     return capabilities
Example #12
0
    def make_hello_base(self):
        capabilities = get_network_caps()
        capabilities.update({
                "version"               : local_version,
                "encoding.generic"      : True,
                "namespace"             : True,
                "file-transfer"         : self.file_transfer,
                "file-size-limit"       : self.file_size_limit,
                "printing"              : self.printing,
                "hostname"              : socket.gethostname(),
                "uuid"                  : self.uuid,
                "username"              : self.username,
                "name"                  : get_name(),
                "client_type"           : self.client_type(),
                "python.version"        : sys.version_info[:3],
                "compression_level"     : self.compression_level,
                })
        if self.display:
            capabilities["display"] = self.display
        def up(prefix, d):
            updict(capabilities, prefix, d)
        up("platform",  get_platform_info())
        up("build",     get_version_info())
        mid = get_machine_id()
        if mid:
            capabilities["machine_id"] = mid

        if self.encryption:
            assert self.encryption in ENCRYPTION_CIPHERS
            iv = get_hex_uuid()[:16]
            key_salt = get_hex_uuid()+get_hex_uuid()
            iterations = 1000
            capabilities.update({
                        "cipher"                       : self.encryption,
                        "cipher.iv"                    : iv,
                        "cipher.key_salt"              : key_salt,
                        "cipher.key_stretch_iterations": iterations,
                        })
            key = self.get_encryption_key()
            if key is None:
                self.warn_and_quit(EXIT_ENCRYPTION, "encryption key is missing")
                return
            self._protocol.set_cipher_in(self.encryption, iv, key, key_salt, iterations)
            log("encryption capabilities: %s", [(k,v) for k,v in capabilities.items() if k.startswith("cipher")])
        return capabilities
Example #13
0
 def test_file(self):
     from xpra.os_util import get_hex_uuid
     password = get_hex_uuid()
     f = self._temp_file(strtobytes(password))
     self._test_auth("file", "", EXIT_PASSWORD_REQUIRED)
     self._test_auth("file:filename=%s" % f.name, "", EXIT_PASSWORD_REQUIRED)
     self._test_auth("file:filename=%s" % f.name, "", EXIT_OK, password)
     self._test_auth("file:filename=%s" % f.name, "", EXIT_FAILURE, password+"A")
     f.close()
Example #14
0
	def test_file(self):
		from xpra.os_util import get_hex_uuid
		password = get_hex_uuid()
		f = self._temp_file(password)
		self._test_auth("file", "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("file:filename=%s" % f.name, "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("file:filename=%s" % f.name, "", EXIT_OK, password)
		self._test_auth("file:filename=%s" % f.name, "", EXIT_FAILURE, password+"A")
		f.close()
Example #15
0
 def make_hello(self, challenge_response=None):
     capabilities = {}
     add_version_info(capabilities)
     capabilities["python.version"] = sys.version_info[:3]
     if challenge_response:
         assert self.password
         capabilities["challenge_response"] = challenge_response
     if self.encryption:
         assert self.encryption in ENCRYPTION_CIPHERS
         capabilities["cipher"] = self.encryption
         iv = get_hex_uuid()[:16]
         capabilities["cipher.iv"] = iv
         key_salt = get_hex_uuid()
         capabilities["cipher.key_salt"] = key_salt
         iterations = 1000
         capabilities["cipher.key_stretch_iterations"] = iterations
         self._protocol.set_cipher_in(self.encryption, iv, self.get_password(), key_salt, iterations)
         log("encryption capabilities: %s", [(k,v) for k,v in capabilities.items() if k.startswith("cipher")])
     capabilities["platform"] = sys.platform
     capabilities["platform.release"] = python_platform.release()
     capabilities["platform.machine"] = python_platform.machine()
     capabilities["platform.processor"] = python_platform.processor()
     capabilities["client_type"] = self.client_type()
     capabilities["namespace"] = True
     capabilities["raw_packets"] = True
     capabilities["chunked_compression"] = True
     capabilities["bencode"] = True
     capabilities["rencode"] = has_rencode
     if has_rencode:
         capabilities["rencode.version"] = rencode_version
     capabilities["hostname"] = socket.gethostname()
     capabilities["uuid"] = self.uuid
     try:
         from xpra.platform.info import get_username, get_name
         capabilities["username"] = get_username()
         capabilities["name"] = get_name()
     except:
         log.error("failed to get username/name", exc_info=True)
     capabilities["randr_notify"] = False    #only client.py cares about this
     capabilities["windows"] = False         #only client.py cares about this
     if self._reverse_aliases:
         capabilities["aliases"] = self._reverse_aliases
     return capabilities
Example #16
0
 def make_hello(self):
     self.value = get_hex_uuid()
     capabilities = GObjectXpraClient.make_hello(self)
     capabilities.update({
         "connect_test_request": self.value,
         #older servers don't know about connect-test,
         #pretend that we're interested in info:
         "info_request": True,
         "info-namespace": True,
     })
     return capabilities
Example #17
0
 def do_run_client(self, client_display, *args):
     from xpra.x11.vfb_util import xauth_add
     xauth_data = get_hex_uuid()
     xauth_add(self.default_env["XAUTHORITY"], client_display, xauth_data,
               os.getuid(), os.getgid())
     env = self.get_run_env()
     env["DISPLAY"] = client_display
     global uq
     env["XPRA_LOG_PREFIX"] = "client %i: " % uq
     uq += 1
     log("starting test client on Xvfb %s", client_display)
     return self.run_xpra(["attach"] + list(args), env)
Example #18
0
	def test_multifile(self):
		from xpra.platform.info import get_username
		username = get_username()
		from xpra.os_util import get_hex_uuid
		password = get_hex_uuid()
		displays = ""
		data = "%s|%s|%i|%i|%s||" % (username, password, os.getuid(), os.getgid(), displays)
		f = self._temp_file(data)
		self._test_auth("multifile", "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("multifile:filename=%s" % f.name, "", EXIT_PASSWORD_REQUIRED)
		self._test_auth("multifile:filename=%s" % f.name, "", EXIT_OK, password)
		self._test_auth("multifile:filename=%s" % f.name, "", EXIT_FAILURE, password+"A")
		f.close()
Example #19
0
 def do_run_client(self, client_display, *args):
     from xpra.x11.vfb_util import xauth_add
     xauth_data = get_hex_uuid()
     xauthority = self.default_env.get("XAUTHORITY",
                                       osexpand("~/.Xauthority"))
     xauth_add(xauthority, client_display, xauth_data, os.getuid(),
               os.getgid())
     env = self.get_run_env()
     env["DISPLAY"] = client_display
     env["XPRA_LOG_PREFIX"] = "client %i: " % X11ClientTestUtil.uq
     X11ClientTestUtil.uq += 1
     log("starting test client on Xvfb %s", client_display)
     return self.run_xpra(["attach"] + list(args), env=env)
Example #20
0
 def test_multifile(self):
     from xpra.platform.info import get_username
     username = get_username()
     from xpra.os_util import get_hex_uuid
     password = get_hex_uuid()
     displays = ""
     data = "%s|%s|%i|%i|%s||" % (username, password, os.getuid(), os.getgid(), displays)
     f = self._temp_file(strtobytes(data))
     self._test_auth("multifile", "", EXIT_PASSWORD_REQUIRED)
     self._test_auth("multifile:filename=%s" % f.name, "", EXIT_PASSWORD_REQUIRED)
     self._test_auth("multifile:filename=%s" % f.name, "", EXIT_OK, password)
     self._test_auth("multifile:filename=%s" % f.name, "", EXIT_FAILURE, password+"A")
     f.close()
Example #21
0
 def __init__(self, *args, **kwargs):
     CommandConnectClient.__init__(self, *args)
     self.value = get_hex_uuid()
     self.hello_extra.update({
         "connect_test_request": self.value,
         #tells proxy servers we don't want to connect to the real / new instance:
         "connect": False,
         #older servers don't know about connect-test,
         #pretend that we're interested in info:
         "info_request": True,
         "info-namespace": True,
     })
     self.hello_extra.update(kwargs)
Example #22
0
	def test_networkstate(self):
		opts = AdHocStruct()
		opts.pings = True
		opts.bandwidth_limit = 0
		opts.bandwidth_detection = True
		self._test_mixin_class(NetworkState, opts, {"start_time" : time.time()})
		x = self.mixin
		if x.server_start_time<x.start_time:
			raise Exception("invalid time: server=%s vs start=%s" % (x.server_start_time, x.start_time))
		x.uuid = get_hex_uuid()
		x.send_info_request()
		packet = ["info-response", {"foo" : "bar"}]
		self.handle_packet(packet)
		assert x.server_last_info.get("foo")=="bar"
Example #23
0
	def copy_and_verify(self, display1, display2, synced=True, wait=1, selection="clipboard"):
		log("copy_and_verify%s", (display1, display2, synced, wait, selection))
		value = get_hex_uuid()
		self.set_clipboard_value(display1, value)
		#wait for synchronization to occur:
		time.sleep(wait)
		new_value = self.get_clipboard_value(display2)
		if synced:
			assert new_value==value, "clipboard contents for %s do not match, expected '%s' but got '%s'" % (selection, value, new_value)
		else:
			assert new_value!=value, "clipboard contents for %s match but synchronization was not expected: value='%s'" % (selection, value)
		if SANITY_CHECKS and display2!=display1:
			#verify that the value has not changed on the original display:
			new_value = self.get_clipboard_value(display1)
		return value
	def copy_and_verify(self, display1, display2, synced=True, wait=1, selection="clipboard"):
		log("copy_and_verify%s", (display1, display2, synced, wait, selection))
		value = get_hex_uuid()
		self.set_clipboard_value(display1, value)
		#wait for synchronization to occur:
		time.sleep(wait)
		new_value = self.get_clipboard_value(display2)
		if synced:
			assert new_value==value, "clipboard contents do not match, expected '%s' but got '%s'" % (value, new_value)
		else:
			assert new_value!=value, "clipboard contents match but synchronization was not expected: value='%s'" % value
		if SANITY_CHECKS and display2!=display1:
			#verify that the value has not changed on the original display:
			new_value = self.get_clipboard_value(display1)
		return value
Example #25
0
 def test_networkstate(self):
     x = NetworkState()
     fake_protocol = AdHocStruct()
     fake_protocol.get_info = lambda: {}
     x._protocol = fake_protocol
     opts = AdHocStruct()
     opts.pings = True
     opts.bandwidth_limit = 0
     opts.bandwidth_detection = True
     x.init(opts)
     assert x.get_caps() is not None
     x.server_capabilities = typedict({"start_time": time.time()})
     x.parse_server_capabilities()
     assert x.server_start_time >= x.start_time, "server_start_time=%s vs start_time=%s" % (
         x.server_start_time, x.start_time)
     x.uuid = get_hex_uuid()
     x.send_info_request()
     packet = ["info-response", {"foo": "bar"}]
     x._process_info_response(packet)
     assert x.server_last_info.get("foo") == "bar"
Example #26
0
    def test_file(self):
        def genfiledata(_a):
            password = uuid.uuid4().hex
            return password, password

        self._test_file_auth("file", genfiledata)
        #no digest -> no challenge
        a = self._init_auth("file", filename="foo")
        assert a.requires_challenge()
        try:
            a.get_challenge(["not-a-valid-digest"])
        except ValueError:
            pass
        a.password_filename = "./this-path-should-not-exist"
        assert a.load_password_file() is None
        assert a.stat_password_filetime() == 0
        #inaccessible:
        if POSIX:
            filename = "./test-file-auth-%s-%s" % (get_hex_uuid(), os.getpid())
            with open(filename, 'wb') as f:
                os.fchmod(f.fileno(), 0o200)  #write-only
            a.password_filename = filename
            a.load_password_file()
Example #27
0
def start_Xvfb(xvfb_str, display_name):
    # We need to set up a new server environment
    xauthority = os.environ.get("XAUTHORITY", os.path.expanduser("~/.Xauthority"))
    if not os.path.exists(xauthority):
        try:
            open(xauthority, 'wa').close()
        except Exception as e:
            #trying to continue anyway!
            sys.stderr.write("Error trying to create XAUTHORITY file %s: %s\n" % (xauthority, e))

    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")
    #identify logfile argument if it exists and if we may have to rename it:
    xvfb_cmd = xvfb_str.split()
    if '-logfile' in xvfb_cmd and display_name[0]=='S':
        xvfb_cmd = xvfb_str.split()
        logfile_argindex = xvfb_cmd.index('-logfile') + 1
        assert logfile_argindex<len(xvfb_cmd), "invalid xvfb command string: -logfile should not be last"
        xorg_log_file = xvfb_cmd[logfile_argindex]
    else:
        xorg_log_file = None

    #apply string substitutions:
    subs = {"XAUTHORITY"    : xauthority,
            "USER"          : os.environ.get("USER", "unknown-user"),
            "HOME"          : os.environ.get("HOME", os.getcwd()),
            "DISPLAY"       : display_name}
    xvfb_str = shellsub(xvfb_str, subs)

    def setsid():
        #run in a new session
        if os.name=="posix":
            os.setsid()

    xvfb_cmd = xvfb_str.split()
    if not xvfb_cmd:
        raise Exception("cannot start Xvfb, command definition is missing!")
    xvfb_executable = xvfb_cmd[0]
    if display_name[0]=='S':
        # 'S' means that we allocate the display automatically
        r_pipe, w_pipe = os.pipe()
        xvfb_cmd += ["-displayfd", str(w_pipe)]
        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
        def preexec():
            setsid()
            #duplicate python's _close_fds() function
            #(taking care to exclude the pipe)
            try:
                MAXFD = os.sysconf("SC_OPEN_MAX")
            except:
                MAXFD = 256
            for i in range(3, MAXFD):
                if i not in (r_pipe, w_pipe):
                    try:
                        os.close(i)
                    except:
                        pass
        xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=False,
                                stdin=subprocess.PIPE, preexec_fn=preexec)
        # Read the display number from the pipe we gave to Xvfb
        # waiting up to 10 seconds for it to show up
        limit = time.time()+10
        buf = ""
        while time.time()<limit and len(buf)<8:
            r, _, _ = select.select([r_pipe], [], [], max(0, limit-time.time()))
            if r_pipe in r:
                buf += os.read(r_pipe, 8)
                if buf[-1] == '\n':
                    break
        os.close(r_pipe)
        os.close(w_pipe)
        if len(buf) == 0:
            raise OSError("%s did not provide a display number using -displayfd" % xvfb_executable)
        if buf[-1] != '\n':
            raise OSError("%s output not terminated by newline: %s" % (xvfb_executable, buf))
        try:
            n = int(buf[:-1])
        except:
            raise OSError("%s display number is not a valid number: %s" % (xvfb_executable, buf[:-1]))
        if n<0 or n>=2**16:
            raise OSError("%s provided an invalid display number: %s" % (xvfb_executable, n))
        new_display_name = ":%s" % n
        sys.stdout.write("Using display number provided by %s: %s\n" % (xvfb_executable, new_display_name))
        if xorg_log_file != None:
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
            f0 = shellsub(xorg_log_file, subs)
            subs["DISPLAY"] = new_display_name
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
            f1 = shellsub(xorg_log_file, subs)
            if f0 != f1:
                try:
                    os.rename(f0, f1)
                except Exception as e:
                    sys.stderr.write("failed to rename Xorg log file from '%s' to '%s'\n" % (f0, f1))
                    sys.stderr.write(" %s\n" % 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)
        xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=True,
                                stdin=subprocess.PIPE, preexec_fn=setsid)

    from xpra.os_util import get_hex_uuid
    xauth_cmd = ["xauth", "add", display_name, "MIT-MAGIC-COOKIE-1", get_hex_uuid()]
    try:
        code = subprocess.call(xauth_cmd)
        if code != 0:
            raise OSError("non-zero exit code: %s" % code)
    except OSError as e:
        #trying to continue anyway!
        sys.stderr.write("Error running \"%s\": %s\n" % (" ".join(xauth_cmd), e))
    return xvfb, display_name
Example #28
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
Example #29
0
                    return False                
                if proto.authenticator:
                    challenge = proto.authenticator.get_challenge()
                    if challenge is None:
                        auth_failed("invalid state: unexpected challenge response")
                        return False
                    salt, digest = challenge
                    log.info("Authentication required, %s sending challenge for '%s' using digest %s", proto.authenticator, username, digest)
                    if digest not in self.digest_modes:
                        auth_failed("cannot proceed without %s digest support" % digest)
                        return False
                else:
                    log.warn("Warning: client expects a challenge but this connection is unauthenticated")
                    #fake challenge so the client will send the real hello:
                    from xpra.os_util import get_hex_uuid
                    salt = get_hex_uuid()+get_hex_uuid()
                    digest = "hmac"
                proto.challenge_sent = True
                proto.send_now(("challenge", salt, auth_caps or "", digest))
                return False

            if not proto.authenticator.authenticate(challenge_response, client_salt):
                auth_failed("invalid challenge response")
                return False
            log("authentication challenge passed")
        else:
            #did the client expect a challenge?
            if c.boolget("challenge"):
                log.warn("this server does not require authentication (client supplied a challenge)")
        return auth_caps
Example #30
0
 def init_uuid(self):
     self._loop_uuid = LOOP_PREFIX + get_hex_uuid()
     log("init_uuid() %s uuid=%s", self._selection, self._loop_uuid)
Example #31
0
 def get_challenge(self):
     return get_hex_uuid(), "hmac"
Example #32
0
def do_run_server(error_cb,
                  opts,
                  mode,
                  xpra_file,
                  extra_args,
                  desktop_display=None):
    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)

    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 shadowing and is_Wayland():
        warn("shadow servers do not support Wayland, switch to X11")

    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 shadowing:
        #there should already be one running
        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 = ":0"
        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 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 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 = 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

    # 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 = osexpand(
                select_log_file(log_dir, opts.log_file, display_name),
                username, uid, gid)
            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 IOError:
                #we tried our best, logging another error won't help
                pass

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

    log = get_util_logger()
    sockets, mdns_recs, wrap_socket_fn = create_sockets(opts, error_cb)

    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 KeyError:
            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, starting_desktop)
        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:
        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):
                os.environ["XAUTHORITY"] = xauthority

        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 IOError:
            pass

    kill_display = None
    if not proxying:
        add_cleanup(close_gtk_display)
    if not proxying and not shadowing:

        def kill_display():
            if xvfb_pid:
                kill_xvfb(xvfb_pid)

        add_cleanup(kill_display)

    if opts.daemon:

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

        log_filename1 = osexpand(
            select_log_file(log_dir, opts.log_file, display_name), username,
            uid, gid)
        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
        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:
        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:
    from xpra.server.socket_util import get_network_logger, setup_local_sockets
    netlog = get_network_logger()
    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)
    ssh_port = get_ssh_port()
    ssh_access = ssh_port > 0 and opts.ssh.lower().strip() not in FALSE_OPTIONS
    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 and ssh_access:
            netlog("ssh %s:%s : %s", "", ssh_port, socket)
            add_mdns(mdns_recs, "ssh", "", ssh_port)

    def b(v):
        return str(v).lower() not in FALSE_OPTIONS

    #turn off some server mixins:
    from xpra.server import server_features
    impwarned = []

    def impcheck(*modules):
        for mod in modules:
            try:
                __import__("xpra.%s" % mod, {}, {}, [])
            except ImportError:
                if mod not in impwarned:
                    impwarned.append(mod)
                    log = get_util_logger()
                    log.warn("Warning: missing %s module", mod)
                return False
        return True

    server_features.notifications = opts.notifications and impcheck(
        "notifications")
    server_features.webcam = b(opts.webcam) and impcheck("codecs")
    server_features.clipboard = b(opts.clipboard) and impcheck("clipboard")
    server_features.audio = (b(opts.speaker)
                             or b(opts.microphone)) and impcheck("sound")
    server_features.av_sync = server_features.audio and b(opts.av_sync)
    server_features.fileprint = b(opts.printing) or b(opts.file_transfer)
    server_features.mmap = b(opts.mmap)
    server_features.input_devices = not opts.readonly and impcheck("keyboard")
    server_features.commands = impcheck("server.control_command")
    server_features.dbus = opts.dbus_proxy and impcheck("dbus")
    server_features.encoding = impcheck("codecs")
    server_features.logging = b(opts.remote_logging)
    #server_features.network_state   = ??
    server_features.display = opts.windows
    server_features.windows = opts.windows and impcheck("codecs")
    server_features.rfb = b(opts.rfb_upgrade) and impcheck("server.rfb")

    kill_dbus = None
    if shadowing:
        app = make_shadow_server()
    elif proxying:
        app = make_proxy_server()
    else:
        if not check_xvfb():
            return 1
        assert starting or starting_desktop or upgrading
        from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source, close_gdk_display_source
        init_gdk_display_source()
        insert_cleanup(close_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)
            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)

        if POSIX:
            #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_XDG_OPEN_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(" ssh connections will not be available")

        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.gtk_x11.wm_check import wm_check
                if not wm_check(display, opts.wm_name, upgrading):
                    return 1
            log("XShape=%s", X11Window.displayHasXShape())
            app = make_server(clobber)
        else:
            assert starting_desktop
            app = make_desktop_server()
        app.init_virtual_devices(devices)

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

    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.setup()
    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

    #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": app.uuid,
            "platform": sys.platform,
            "type": app.session_type,
        }
        MDNS_EXPOSE_NAME = envbool("XPRA_MDNS_EXPOSE_NAME", True)
        if MDNS_EXPOSE_NAME and app.session_name:
            mdns_info["name"] = app.session_name
        for mode, listen_on in mdns_recs.items():
            mdns_publish(display_name, mode, listen_on, mdns_info)

    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")
        return 0
    except Exception:
        log.error("server error", exc_info=True)
        return -128
    else:
        if r > 0:
            # Upgrading/exiting, so leave X and dbus servers running
            if kill_display:
                _cleanups.remove(kill_display)
            if kill_dbus:
                _cleanups.remove(kill_dbus)
            from xpra.server import EXITING_CODE
            if r == EXITING_CODE:
                log.info("exiting: not cleaning up Xvfb")
            else:
                log.info("upgrading: not cleaning up Xvfb")
            r = 0
    return r
Example #33
0
                    salt, digest = challenge
                    log.info(
                        "Authentication required, %s sending challenge for '%s' using digest %s",
                        proto.authenticator, username, digest)
                    if digest not in self.digest_modes:
                        auth_failed(
                            "cannot proceed without %s digest support" %
                            digest)
                        return False
                else:
                    log.warn(
                        "Warning: client expects a challenge but this connection is unauthenticated"
                    )
                    #fake challenge so the client will send the real hello:
                    from xpra.os_util import get_hex_uuid
                    salt = get_hex_uuid() + get_hex_uuid()
                    digest = "hmac"
                proto.challenge_sent = True
                proto.send_now(("challenge", salt, auth_caps or "", digest))
                return False

            if not proto.authenticator.authenticate(challenge_response,
                                                    client_salt):
                auth_failed("invalid challenge response")
                return False
            log("authentication challenge passed")
        else:
            #did the client expect a challenge?
            if c.boolget("challenge"):
                log.warn(
                    "this server does not require authentication (client supplied a challenge)"
Example #34
0
    def verify_hello(self, proto, c):
        remote_version = c.strget("version")
        verr = version_compat_check(remote_version)
        if verr is not None:
            self.disconnect_client(proto, VERSION_ERROR, "incompatible version: %s" % verr)
            proto.close()
            return False

        def auth_failed(msg):
            log.warn("Warning: authentication failed: %s", msg)
            self.timeout_add(1000, self.disconnect_client, proto, msg)

        # authenticator:
        username = c.strget("username")
        if proto.authenticator is None and proto.auth_class:
            try:
                proto.authenticator = proto.auth_class(username)
            except Exception as e:
                log.warn("error instantiating %s: %s", proto.auth_class, e)
                auth_failed("authentication failed")
                return False
        self.digest_modes = c.get("digest", ("hmac",))

        # client may have requested encryption:
        cipher = c.strget("cipher")
        cipher_iv = c.strget("cipher.iv")
        key_salt = c.strget("cipher.key_salt")
        iterations = c.intget("cipher.key_stretch_iterations")
        auth_caps = {}
        if cipher and cipher_iv:
            if cipher not in ENCRYPTION_CIPHERS:
                log.warn("unsupported cipher: %s", cipher)
                auth_failed("unsupported cipher")
                return False
            encryption_key = self.get_encryption_key(proto.authenticator)
            if encryption_key is None:
                auth_failed("encryption key is missing")
                return False
            proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt, iterations)
            # use the same cipher as used by the client:
            auth_caps = new_cipher_caps(proto, cipher, encryption_key)
            log("server cipher=%s", auth_caps)
        else:
            auth_caps = None

        # verify authentication if required:
        if (proto.authenticator and proto.authenticator.requires_challenge()) or c.get("challenge") is not None:
            challenge_response = c.strget("challenge_response")
            client_salt = c.strget("challenge_client_salt")
            log(
                "processing authentication with %s, response=%s, client_salt=%s, challenge_sent=%s",
                proto.authenticator,
                challenge_response,
                binascii.hexlify(client_salt or ""),
                proto.challenge_sent,
            )
            # send challenge if this is not a response:
            if not challenge_response:
                if proto.challenge_sent:
                    auth_failed("invalid state, challenge already sent - no response!")
                    return False
                if proto.authenticator:
                    challenge = proto.authenticator.get_challenge()
                    if challenge is None:
                        auth_failed("invalid state: unexpected challenge response")
                        return False
                    salt, digest = challenge
                    log.info(
                        "Authentication required, %s sending challenge for '%s' using digest %s",
                        proto.authenticator,
                        username,
                        digest,
                    )
                    if digest not in self.digest_modes:
                        auth_failed("cannot proceed without %s digest support" % digest)
                        return False
                else:
                    log.warn("Warning: client expects a challenge but this connection is unauthenticated")
                    # fake challenge so the client will send the real hello:
                    from xpra.os_util import get_hex_uuid

                    salt = get_hex_uuid() + get_hex_uuid()
                    digest = "hmac"
                proto.challenge_sent = True
                proto.send_now(("challenge", salt, auth_caps or "", digest))
                return False

            if not proto.authenticator.authenticate(challenge_response, client_salt):
                auth_failed("invalid challenge response")
                return False
            log("authentication challenge passed")
        else:
            # did the client expect a challenge?
            if c.boolget("challenge"):
                log.warn("this server does not require authentication (client supplied a challenge)")
        return auth_caps
Example #35
0
    xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)

    def setsid():
        #run in a new session
        if os.name == "posix":
            os.setsid()

    xvfb = subprocess.Popen(xvfb_cmd,
                            executable=xvfb_executable,
                            close_fds=True,
                            stdin=subprocess.PIPE,
                            preexec_fn=setsid)
    from xpra.os_util import get_hex_uuid
    xauth_cmd = [
        "xauth", "add", display_name, "MIT-MAGIC-COOKIE-1",
        get_hex_uuid()
    ]
    try:
        code = subprocess.call(xauth_cmd)
        if code != 0:
            raise OSError("non-zero exit code: %s" % code)
    except OSError, e:
        #trying to continue anyway!
        sys.stderr.write("Error running \"%s\": %s\n" %
                         (" ".join(xauth_cmd), e))
    return xvfb


def check_xvfb_process(xvfb=None):
    if xvfb is None:
        #we don't have a process to check
Example #36
0
            "HOME"          : os.environ.get("HOME", os.getcwd()),
            "DISPLAY"       : display_name}
    for var,value in subs.items():
        xvfb_str = xvfb_str.replace("$%s" % var, value)
        xvfb_str = xvfb_str.replace("${%s}" % var, value)
    xvfb_cmd = xvfb_str.split()+[display_name]
    xvfb_executable = xvfb_cmd[0]
    xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
    def setsid():
        #run in a new session
        if os.name=="posix":
            os.setsid()
    xvfb = subprocess.Popen(xvfb_cmd, executable=xvfb_executable, close_fds=True,
                                stdin=subprocess.PIPE, preexec_fn=setsid)
    from xpra.os_util import get_hex_uuid
    xauth_cmd = ["xauth", "add", display_name, "MIT-MAGIC-COOKIE-1", get_hex_uuid()]
    try:
        code = subprocess.call(xauth_cmd)
        if code != 0:
            raise OSError("non-zero exit code: %s" % code)
    except OSError, e:
        #trying to continue anyway!
        sys.stderr.write("Error running \"%s\": %s\n" % (" ".join(xauth_cmd), e))
    return xvfb

def check_xvfb_process(xvfb=None):
    if xvfb is None:
        #we don't have a process to check
        return True
    if xvfb.poll() is None:
        #process is running
Example #37
0
 def test_uuid(self):
     assert len(get_hex_uuid()) == 32
     assert isinstance(get_int_uuid(), int)
     assert get_int_uuid() != 0
     assert get_user_uuid() != 0
Example #38
0
def start_Xvfb(xvfb_str, display_name):
    # We need to set up a new server environment
    xauthority = os.environ.get("XAUTHORITY",
                                os.path.expanduser("~/.Xauthority"))
    if not os.path.exists(xauthority):
        try:
            open(xauthority, 'wa').close()
        except Exception as e:
            #trying to continue anyway!
            sys.stderr.write(
                "Error trying to create XAUTHORITY file %s: %s\n" %
                (xauthority, e))

    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")
    #identify logfile argument if it exists and if we may have to rename it:
    xvfb_cmd = xvfb_str.split()
    if '-logfile' in xvfb_cmd and display_name[0] == 'S':
        xvfb_cmd = xvfb_str.split()
        logfile_argindex = xvfb_cmd.index('-logfile') + 1
        assert logfile_argindex < len(
            xvfb_cmd
        ), "invalid xvfb command string: -logfile should not be last"
        xorg_log_file = xvfb_cmd[logfile_argindex]
    else:
        xorg_log_file = None

    #apply string substitutions:
    subs = {
        "XAUTHORITY": xauthority,
        "USER": os.environ.get("USER", "unknown-user"),
        "HOME": os.environ.get("HOME", os.getcwd()),
        "DISPLAY": display_name
    }
    xvfb_str = shellsub(xvfb_str, subs)

    def setsid():
        #run in a new session
        if os.name == "posix":
            os.setsid()

    xvfb_cmd = xvfb_str.split()
    if not xvfb_cmd:
        raise InitException(
            "cannot start Xvfb, the command definition is missing!")
    xvfb_executable = xvfb_cmd[0]
    if display_name[0] == 'S':
        # 'S' means that we allocate the display automatically
        r_pipe, w_pipe = os.pipe()
        xvfb_cmd += ["-displayfd", str(w_pipe)]
        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)

        def preexec():
            setsid()
            #duplicate python's _close_fds() function
            #(taking care to exclude the pipe)
            try:
                MAXFD = os.sysconf("SC_OPEN_MAX")
            except:
                MAXFD = 256
            for i in range(3, MAXFD):
                if i not in (r_pipe, w_pipe):
                    try:
                        os.close(i)
                    except:
                        pass

        xvfb = subprocess.Popen(xvfb_cmd,
                                executable=xvfb_executable,
                                close_fds=False,
                                stdin=subprocess.PIPE,
                                preexec_fn=preexec)
        # Read the display number from the pipe we gave to Xvfb
        # waiting up to 10 seconds for it to show up
        limit = time.time() + 10
        buf = ""
        while time.time() < limit and len(buf) < 8:
            r, _, _ = select.select([r_pipe], [], [],
                                    max(0, limit - time.time()))
            if r_pipe in r:
                buf += os.read(r_pipe, 8)
                if buf[-1] == '\n':
                    break
        os.close(r_pipe)
        os.close(w_pipe)
        if len(buf) == 0:
            raise OSError(
                "%s did not provide a display number using -displayfd" %
                xvfb_executable)
        if buf[-1] != '\n':
            raise OSError("%s output not terminated by newline: %s" %
                          (xvfb_executable, buf))
        try:
            n = int(buf[:-1])
        except:
            raise OSError("%s display number is not a valid number: %s" %
                          (xvfb_executable, buf[:-1]))
        if n < 0 or n >= 2**16:
            raise OSError("%s provided an invalid display number: %s" %
                          (xvfb_executable, n))
        new_display_name = ":%s" % n
        sys.stdout.write("Using display number provided by %s: %s\n" %
                         (xvfb_executable, new_display_name))
        if xorg_log_file != None:
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
            f0 = shellsub(xorg_log_file, subs)
            subs["DISPLAY"] = new_display_name
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
            f1 = shellsub(xorg_log_file, subs)
            if f0 != f1:
                try:
                    os.rename(f0, f1)
                except Exception as e:
                    sys.stderr.write(
                        "failed to rename Xorg log file from '%s' to '%s'\n" %
                        (f0, f1))
                    sys.stderr.write(" %s\n" % 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)
        xvfb = subprocess.Popen(xvfb_cmd,
                                executable=xvfb_executable,
                                close_fds=True,
                                stdin=subprocess.PIPE,
                                preexec_fn=setsid)

    from xpra.os_util import get_hex_uuid
    xauth_cmd = [
        "xauth", "add", display_name, "MIT-MAGIC-COOKIE-1",
        get_hex_uuid()
    ]
    try:
        code = subprocess.call(xauth_cmd)
        if code != 0:
            raise OSError("non-zero exit code: %s" % code)
    except OSError as e:
        #trying to continue anyway!
        sys.stderr.write("Error running \"%s\": %s\n" %
                         (" ".join(xauth_cmd), e))
    return xvfb, display_name
Example #39
0
 def init_uuid(self):
     self._loop_uuid = LOOP_PREFIX+get_hex_uuid()
     log("init_uuid() %s uuid=%s", self._selection, self._loop_uuid)
     set_string(self._clipboard, self._loop_uuid)
Example #40
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
Example #41
0
def init_client_mmap(mmap_group=None,
                     socket_filename=None,
                     size=128 * 1024 * 1024,
                     filename=None):
    """
        Initializes an mmap area, writes the token in it and returns:
            (success flag, mmap_area, mmap_size, temp_file, mmap_filename)
        The caller must keep hold of temp_file to ensure it does not get deleted!
        This is used by the client.
    """
    def rerr():
        return False, False, None, 0, None, None

    log("init_mmap%s", (mmap_group, socket_filename, size, filename))
    mmap_filename = filename
    mmap_temp_file = None
    delete = True

    def validate_size(size):
        assert size >= 64 * 1024 * 1024, "mmap size is too small: %sB (minimum is 64MB)" % std_unit(
            size)
        assert size <= 4 * 1024 * 1024 * 1024, "mmap is too big: %sB (maximum is 4GB)" % std_unit(
            size)

    try:
        import mmap
        unit = max(4096, mmap.PAGESIZE)
        #add 8 bytes for the mmap area control header zone:
        mmap_size = roundup(size + 8, unit)
        if WIN32:
            validate_size(mmap_size)
            if not filename:
                from xpra.os_util import get_hex_uuid
                filename = "xpra-%s" % get_hex_uuid()
            mmap_filename = filename
            mmap_area = mmap.mmap(0, mmap_size, filename)
            #not a real file:
            delete = False
            mmap_temp_file = None
        else:
            assert POSIX
            if filename:
                if os.path.exists(filename):
                    fd = os.open(filename, os.O_EXCL | os.O_RDWR)
                    mmap_size = os.path.getsize(mmap_filename)
                    validate_size(mmap_size)
                    #mmap_size = 4*1024*1024    #size restriction needed with ivshmem
                    delete = False
                    log.info("Using existing mmap file '%s': %sMB",
                             mmap_filename, mmap_size // 1024 // 1024)
                else:
                    validate_size(mmap_size)
                    import errno
                    flags = os.O_CREAT | os.O_EXCL | os.O_RDWR
                    try:
                        fd = os.open(filename, flags)
                        mmap_temp_file = None  #os.fdopen(fd, 'w')
                        mmap_filename = filename
                    except OSError as e:
                        if e.errno == errno.EEXIST:
                            log.error(
                                "Error: the mmap file '%s' already exists",
                                filename)
                            return rerr()
                        raise
            else:
                validate_size(mmap_size)
                import tempfile
                from xpra.platform.paths import get_mmap_dir
                mmap_dir = get_mmap_dir()
                subs = os.environ.copy()
                subs.update({
                    "UID": os.getuid(),
                    "GID": os.getgid(),
                    "PID": os.getpid(),
                })
                mmap_dir = shellsub(mmap_dir, subs)
                if mmap_dir and not os.path.exists(mmap_dir):
                    os.mkdir(mmap_dir, 0o700)
                if not mmap_dir or not os.path.exists(mmap_dir):
                    raise Exception("mmap directory %s does not exist!" %
                                    mmap_dir)
                #create the mmap file, the mkstemp that is called via NamedTemporaryFile ensures
                #that the file is readable and writable only by the creating user ID
                try:
                    temp = tempfile.NamedTemporaryFile(prefix="xpra.",
                                                       suffix=".mmap",
                                                       dir=mmap_dir)
                except OSError as e:
                    log.error("Error: cannot create mmap file:")
                    log.error(" %s", e)
                    return rerr()
                #keep a reference to it so it does not disappear!
                mmap_temp_file = temp
                mmap_filename = temp.name
                fd = temp.file.fileno()
            #set the group permissions and gid if the mmap-group option is specified
            mmap_group = (mmap_group or "")
            if POSIX and mmap_group and mmap_group not in FALSE_OPTIONS:
                group_id = None
                if mmap_group == "SOCKET":
                    group_id = get_socket_group(socket_filename)
                elif mmap_group.lower() == "auto":
                    group_id = xpra_group()
                    if not group_id and socket_filename:
                        group_id = get_socket_group(socket_filename)
                elif mmap_group.lower() in TRUE_OPTIONS:
                    log.info("parsing legacy mmap-group value '%s' as 'auto'",
                             mmap_group)
                    log.info(" please update your configuration")
                    group_id = xpra_group() or get_socket_group(
                        socket_filename)
                else:
                    group_id = get_group_id(mmap_group)
                if group_id > 0:
                    log("setting mmap file %s to group id=%i", mmap_filename,
                        group_id)
                    try:
                        os.fchown(fd, -1, group_id)
                    except OSError as e:
                        log("fchown(%i, %i, %i) on %s",
                            fd,
                            -1,
                            group_id,
                            mmap_filename,
                            exc_info=True)
                        log.error(
                            "Error: failed to change group ownership of mmap file to '%s':",
                            mmap_group)
                        log.error(" %s", e)
                    from stat import S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP
                    os.fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
            log("using mmap file %s, fd=%s, size=%s", mmap_filename, fd,
                mmap_size)
            os.lseek(fd, mmap_size - 1, os.SEEK_SET)
            assert os.write(fd, b'\x00')
            os.lseek(fd, 0, os.SEEK_SET)
            mmap_area = mmap.mmap(fd, length=mmap_size)
        return True, delete, mmap_area, mmap_size, mmap_temp_file, mmap_filename
    except Exception as e:
        log("failed to setup mmap: %s", e, exc_info=True)
        log.error("Error: mmap setup failed:")
        log.error(" %s", e)
        clean_mmap(mmap_filename)
        return rerr()
Example #42
0
    def verify_hello(self, proto, c):
        remote_version = c.strget("version")
        verr = version_compat_check(remote_version)
        if verr is not None:
            self.disconnect_client(proto, VERSION_ERROR,
                                   "incompatible version: %s" % verr)
            proto.close()
            return False

        def auth_failed(msg):
            log.warn("Warning: authentication failed: %s", msg)
            self.timeout_add(1000, self.disconnect_client, proto, msg)

        #authenticator:
        username = c.strget("username")
        if proto.authenticator is None and proto.auth_class:
            try:
                proto.authenticator = proto.auth_class(username)
            except Exception as e:
                log.warn("error instantiating %s: %s", proto.auth_class, e)
                auth_failed("authentication failed")
                return False
        self.digest_modes = c.get("digest", ("hmac", ))

        #client may have requested encryption:
        cipher = c.strget("cipher")
        cipher_iv = c.strget("cipher.iv")
        key_salt = c.strget("cipher.key_salt")
        iterations = c.intget("cipher.key_stretch_iterations")
        auth_caps = {}
        if cipher and cipher_iv:
            if cipher not in ENCRYPTION_CIPHERS:
                log.warn("unsupported cipher: %s", cipher)
                auth_failed("unsupported cipher")
                return False
            encryption_key = self.get_encryption_key(proto.authenticator)
            if encryption_key is None:
                auth_failed("encryption key is missing")
                return False
            proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt,
                                 iterations)
            #use the same cipher as used by the client:
            auth_caps = new_cipher_caps(proto, cipher, encryption_key)
            log("server cipher=%s", auth_caps)
        else:
            auth_caps = None

        #verify authentication if required:
        if (proto.authenticator and proto.authenticator.requires_challenge()
            ) or c.get("challenge") is not None:
            challenge_response = c.strget("challenge_response")
            client_salt = c.strget("challenge_client_salt")
            log(
                "processing authentication with %s, response=%s, client_salt=%s, challenge_sent=%s",
                proto.authenticator, challenge_response,
                binascii.hexlify(client_salt or ""), proto.challenge_sent)
            #send challenge if this is not a response:
            if not challenge_response:
                if proto.challenge_sent:
                    auth_failed(
                        "invalid state, challenge already sent - no response!")
                    return False
                if proto.authenticator:
                    challenge = proto.authenticator.get_challenge()
                    if challenge is None:
                        auth_failed(
                            "invalid state: unexpected challenge response")
                        return False
                    salt, digest = challenge
                    log.info(
                        "Authentication required, %s sending challenge for '%s' using digest %s",
                        proto.authenticator, username, digest)
                    if digest not in self.digest_modes:
                        auth_failed(
                            "cannot proceed without %s digest support" %
                            digest)
                        return False
                else:
                    log.warn(
                        "Warning: client expects a challenge but this connection is unauthenticated"
                    )
                    #fake challenge so the client will send the real hello:
                    from xpra.os_util import get_hex_uuid
                    salt = get_hex_uuid() + get_hex_uuid()
                    digest = "hmac"
                proto.challenge_sent = True
                proto.send_now(("challenge", salt, auth_caps or "", digest))
                return False

            if not proto.authenticator.authenticate(challenge_response,
                                                    client_salt):
                auth_failed("invalid challenge response")
                return False
            log("authentication challenge passed")
        else:
            #did the client expect a challenge?
            if c.boolget("challenge"):
                log.warn(
                    "this server does not require authentication (client supplied a challenge)"
                )
        return auth_caps
Example #43
0
def get_iv():
    IV = None
    #IV = "0000000000000000"
    return IV or get_hex_uuid()[:16]