def parse_server_capabilities(self, capabilities): def get(key, default=None): return self.capsget(capabilities, key, default) self._remote_version = get("version") self._remote_revision = get("revision") self._remote_revision = get("build.revision", self._remote_revision) self._remote_platform = get("platform") self._remote_platform_release = get("platform.release") self._remote_platform_platform = get("platform.platform") self._remote_platform_linux_distribution = get( "platform.linux_distribution") verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit( EXIT_INCOMPATIBLE_VERSION, "incompatible remote version %s: %s" % (self._remote_version, verr)) return False if capabilities.get("rencode") and use_rencode: self._protocol.enable_rencode() if self.encryption: #server uses a new cipher after second hello: self.set_server_encryption(capabilities) self._protocol.chunked_compression = get("chunked_compression", False) self._protocol.aliases = get("aliases", {}) if self.pings: self.timeout_add(1000, self.send_ping) else: self.timeout_add(10 * 1000, self.send_ping) return True
def parse_hello(self, c: typedict): self.ui_client = c.boolget("ui_client", True) self.wants_encodings = c.boolget("wants_encodings", self.ui_client) self.wants_display = c.boolget("wants_display", self.ui_client) self.wants_events = c.boolget("wants_events", False) self.wants_aliases = c.boolget("wants_aliases", True) self.wants_versions = c.boolget("wants_versions", True) self.wants_features = c.boolget("wants_features", True) self.wants_default_cursor = c.boolget("wants_default_cursor", False) for bc in CC_BASES: log("%s.parse_client_caps(..)", bc) bc.parse_client_caps(self, c) #log client info: cinfo = self.get_connect_info() for i, ci in enumerate(cinfo): log.info("%s%s", ["", " "][int(i > 0)], ci) if self.client_proxy: from xpra.version_util import version_compat_check msg = version_compat_check(self.proxy_version) if msg: proxylog = Logger("proxy") proxylog.warn( "Warning: proxy version may not be compatible: %s", msg)
def parse_server_capabilities(self, c): self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("version") self._remote_revision = c.strget("revision") self._remote_revision = c.strget("build.revision", self._remote_revision) self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") self._remote_platform_linux_distribution = c.get("platform.linux_distribution") verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit(EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False self._protocol.chunked_compression = c.boolget("chunked_compression") if use_rencode and c.boolget("rencode"): self._protocol.enable_rencode() if use_lz4 and c.boolget("lz4") and self._protocol.chunked_compression and self.compression_level==1: self._protocol.enable_lz4() if self.encryption: #server uses a new cipher after second hello: key = self.get_encryption_key() assert key, "encryption key is missing" if not self.set_server_encryption(c, key): return False self._protocol.aliases = c.dictget("aliases", {}) if self.pings: self.timeout_add(1000, self.send_ping) else: self.timeout_add(10*1000, self.send_ping) return True
def parse_server_capabilities(self, capabilities): def get(key, default=None): return self.capsget(capabilities, key, default) self._remote_version = get("version") self._remote_revision = get("revision") self._remote_revision = get("build.revision", self._remote_revision) self._remote_platform = get("platform") self._remote_platform_release = get("platform.release") self._remote_platform_platform = get("platform.platform") self._remote_platform_linux_distribution = get("platform.linux_distribution") verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit(EXIT_INCOMPATIBLE_VERSION, "incompatible remote version %s: %s" % (self._remote_version, verr)) return False if capabilities.get("rencode") and use_rencode: self._protocol.enable_rencode() if self.encryption: #server uses a new cipher after second hello: self.set_server_encryption(capabilities) self._protocol.chunked_compression = get("chunked_compression", False) self._protocol.aliases = get("aliases", {}) if self.pings: self.timeout_add(1000, self.send_ping) else: self.timeout_add(10*1000, self.send_ping) return True
def parse_version_capabilities(self): c = self.server_capabilities self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("build.version", c.strget("version")) self._remote_revision = c.strget("build.revision", c.strget("revision")) self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") # linux distribution is a tuple of different types, ie: ('Linux Fedora' , 20, 'Heisenbug') pld = c.listget("platform.linux_distribution") if pld and len(pld) == 3: def san(v): if type(v) == int: return v return bytestostr(v) self._remote_platform_linux_distribution = [san(x) for x in pld] verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit( EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr) ) return False return True
def parse_version_capabilities(self): c = self.server_capabilities self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("build.version", c.strget("version")) self._remote_revision = c.strget("build.revision", c.strget("revision")) self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") #linux distribution is a tuple of different types, ie: ('Linux Fedora' , 20, 'Heisenbug') pld = c.listget("platform.linux_distribution") if pld and len(pld) == 3: def san(v): if type(v) == int: return v return bytestostr(v) self._remote_platform_linux_distribution = [san(x) for x in pld] verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit( EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False return True
def parse_hello(self, c): self.ui_client = c.boolget("ui_client", True) self.wants_encodings = c.boolget("wants_encodings", self.ui_client) self.wants_display = c.boolget("wants_display", self.ui_client) self.wants_events = c.boolget("wants_events", False) self.wants_aliases = c.boolget("wants_aliases", True) self.wants_versions = c.boolget("wants_versions", True) self.wants_features = c.boolget("wants_features", True) self.wants_default_cursor = c.boolget("wants_default_cursor", False) for mixin in CC_BASES: mixin.parse_client_caps(self, c) #general features: self.info_namespace = c.boolget("info-namespace") self.send_notifications = c.boolget("notifications") self.send_notifications_actions = c.boolget("notifications.actions") log("notifications=%s, actions=%s", self.send_notifications, self.send_notifications_actions) self.share = c.boolget("share") self.lock = c.boolget("lock") self.control_commands = c.strlistget("control_commands") bandwidth_limit = c.intget("bandwidth-limit", 0) server_bandwidth_limit = self.server_bandwidth_limit if self.server_bandwidth_limit is None: server_bandwidth_limit = self.get_socket_bandwidth_limit( ) or bandwidth_limit self.bandwidth_limit = min(server_bandwidth_limit, bandwidth_limit) if self.bandwidth_detection: self.bandwidth_detection = c.boolget("bandwidth-detection", True) cd = typedict(c.dictget("connection-data")) self.jitter = cd.intget("jitter", 0) bandwidthlog( "server bandwidth-limit=%s, client bandwidth-limit=%s, value=%s, detection=%s", server_bandwidth_limit, bandwidth_limit, self.bandwidth_limit, self.bandwidth_detection) cinfo = self.get_connect_info() for i, ci in enumerate(cinfo): log.info("%s%s", ["", " "][int(i > 0)], ci) if self.client_proxy: from xpra.version_util import version_compat_check msg = version_compat_check(self.proxy_version) if msg: proxylog.warn( "Warning: proxy version may not be compatible: %s", msg) self.update_connection_data(c.dictget("connection-data")) if getattr(self, "mmap_size", 0) > 0: log("mmap enabled, ignoring bandwidth-limit") self.bandwidth_limit = 0 #adjust max packet size if file transfers are enabled: #TODO: belongs in mixin: file_transfer = getattr(self, "file_transfer", None) if file_transfer: self.protocol.max_packet_size = max( self.protocol.max_packet_size, self.file_size_limit * 1024 * 1024)
def parse_hello(self, c): self.ui_client = c.boolget("ui_client", True) self.wants_encodings = c.boolget("wants_encodings", self.ui_client) self.wants_display = c.boolget("wants_display", self.ui_client) self.wants_events = c.boolget("wants_events", False) self.wants_aliases = c.boolget("wants_aliases", True) self.wants_versions = c.boolget("wants_versions", True) self.wants_features = c.boolget("wants_features", True) self.wants_default_cursor = c.boolget("wants_default_cursor", False) for mixin in ClientConnection.__bases__: mixin.parse_client_caps(self, c) #general features: self.info_namespace = c.boolget("info-namespace") self.send_notifications = c.boolget("notifications") self.send_notifications_actions = c.boolget("notifications.actions") self.share = c.boolget("share") self.lock = c.boolget("lock") self.control_commands = c.strlistget("control_commands") bandwidth_limit = c.intget("bandwidth-limit", 0) if self.server_bandwidth_limit <= 0: self.bandwidth_limit = bandwidth_limit else: self.bandwidth_limit = min(self.server_bandwidth_limit, bandwidth_limit) bandwidthlog( "server bandwidth-limit=%s, client bandwidth-limit=%s, value=%s", self.server_bandwidth_limit, bandwidth_limit, self.bandwidth_limit) log("cursors=%s (encodings=%s), bell=%s, notifications=%s", self.send_cursors, self.cursor_encodings, self.send_bell, self.send_notifications) cinfo = self.get_connect_info() for i, ci in enumerate(cinfo): log.info("%s%s", ["", " "][int(i > 0)], ci) if self.client_proxy: from xpra.version_util import version_compat_check msg = version_compat_check(self.proxy_version) if msg: proxylog.warn( "Warning: proxy version may not be compatible: %s", msg) self.update_connection_data(c.dictget("connection-data")) if self.mmap_size > 0: log("mmap enabled, ignoring bandwidth-limit") self.bandwidth_limit = 0 #adjust max packet size if file transfers are enabled: if self.file_transfer: self.protocol.max_packet_size = max( self.protocol.max_packet_size, self.file_size_limit * 1024 * 1024)
def parse_server_capabilities(self, c: typedict) -> bool: p = self._protocol if p.TYPE == "rfb": #only the xpra protocol provides the server info return True self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("build.version", c.strget("version")) self._remote_revision = c.strget("build.revision", c.strget("revision")) mods = c.get("build.local_modifications") if mods and str(mods).find("dfsg") >= 0: # pragma: no cover get_util_logger().warn( "Warning: the xpra server is running a buggy Debian version") get_util_logger().warn( " those are usually out of date and unstable") else: self._remote_modifications = c.intget("build.local_modifications", 0) self._remote_commit = c.strget("build.commit") self._remote_branch = c.strget("build.branch") self._remote_build_date = c.strget("build.date") self._remote_build_time = c.strget("build.time") self._remote_hostname = c.strget("hostname") self._remote_display = c.strget("display") self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") self._remote_python_version = c.strget("python.version") self._remote_subcommands = c.strtupleget("subcommands") self._remote_server_log = c.strget("server-log") self._remote_lib_versions = get_remote_lib_versions(c) #linux distribution is a tuple of different types, ie: ('Linux Fedora' , 20, 'Heisenbug') pld = c.tupleget("platform.linux_distribution") if pld and len(pld) == 3: def san(v): if isinstance(v, int): return v return bytestostr(v) self._remote_platform_linux_distribution = [san(x) for x in pld] verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit( EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False return True
def parse_server_capabilities(self, c): self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("version") self._remote_version = c.strget("build.version", self._remote_version) self._remote_revision = c.strget("revision") self._remote_revision = c.strget("build.revision", self._remote_revision) self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") #linux distribution is a tuple of different types, ie: ('Linux Fedora' , 20, 'Heisenbug') pld = c.listget("platform.linux_distribution") if pld and len(pld) == 3: def san(v): if type(v) == int: return v return bytestostr(v) self._remote_platform_linux_distribution = [san(x) for x in pld] verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit( EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False if use_rencode and c.boolget("rencode"): self._protocol.enable_rencode() if use_lz4 and c.boolget("lz4") and self.compression_level == 1: self._protocol.enable_lz4() if self.encryption: #server uses a new cipher after second hello: key = self.get_encryption_key() assert key, "encryption key is missing" if not self.set_server_encryption(c, key): return False self._protocol.send_aliases = c.dictget("aliases", {}) if self.pings: self.timeout_add(1000, self.send_ping) else: self.timeout_add(10 * 1000, self.send_ping) return True
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, "incompatible version: %s" % verr) proto.close() return False def auth_failed(msg): log.info("authentication failed: %s", msg) self.timeout_add(1000, self.disconnect_client, proto, msg) #authenticator: username = c.strget("username") if proto.authenticator is None and self.auth_class: try: proto.authenticator = self.auth_class(username) except Exception, e: log.warn("error instantiating %s: %s", self.auth_class, e) auth_failed("authentication failed") return False
def parse_server_capabilities(self, c): self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("version") self._remote_version = c.strget("build.version", self._remote_version) self._remote_revision = c.strget("revision") self._remote_revision = c.strget("build.revision", self._remote_revision) self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") #linux distribution is a tuple of different types, ie: ('Linux Fedora' , 20, 'Heisenbug') pld = c.listget("platform.linux_distribution") if pld and len(pld)==3: def san(v): if type(v)==int: return v return bytestostr(v) self._remote_platform_linux_distribution = [san(x) for x in pld] verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit(EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False if use_rencode and c.boolget("rencode"): self._protocol.enable_rencode() if use_lz4 and c.boolget("lz4") and self.compression_level==1: self._protocol.enable_lz4() if self.encryption: #server uses a new cipher after second hello: key = self.get_encryption_key() assert key, "encryption key is missing" if not self.set_server_encryption(c, key): return False self._protocol.send_aliases = c.dictget("aliases", {}) if self.pings: self.timeout_add(1000, self.send_ping) else: self.timeout_add(10*1000, self.send_ping) return True
def parse_server_capabilities(self, c): self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("version") self._remote_revision = c.strget("revision") self._remote_revision = c.strget("build.revision", self._remote_revision) self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") self._remote_platform_linux_distribution = c.get( "platform.linux_distribution") verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit( EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False self._protocol.chunked_compression = c.boolget("chunked_compression") if use_rencode and c.boolget("rencode"): self._protocol.enable_rencode() if use_lz4 and c.boolget( "lz4" ) and self._protocol.chunked_compression and self.compression_level == 1: self._protocol.enable_lz4() if self.encryption: #server uses a new cipher after second hello: key = self.get_encryption_key() assert key, "encryption key is missing" if not self.set_server_encryption(c, key): return False self._protocol.send_aliases = c.dictget("aliases", {}) if self.pings: self.timeout_add(1000, self.send_ping) else: self.timeout_add(10 * 1000, self.send_ping) return True
def parse_server_capabilities(self, c : typedict) -> bool: self._remote_machine_id = c.strget("machine_id") self._remote_uuid = c.strget("uuid") self._remote_version = c.strget("build.version", c.strget("version")) self._remote_revision = c.strget("build.revision", c.strget("revision")) mods = c.rawget("build.local_modifications") if mods and str(mods).find("dfsg")>=0: get_util_logger().warn("Warning: the xpra server is running a buggy Debian version") get_util_logger().warn(" those are usually out of date and unstable") else: self._remote_modifications = c.intget("build.local_modifications", 0) self._remote_build_date = c.strget("build.date") self._remote_build_time = c.strget("build.time") self._remote_hostname = c.strget("hostname") self._remote_display = c.strget("display") self._remote_platform = c.strget("platform") self._remote_platform_release = c.strget("platform.release") self._remote_platform_platform = c.strget("platform.platform") self._remote_python_version = c.strget("python.version") self._remote_subcommands = c.strtupleget("subcommands") for x in ("glib", "gobject", "gtk", "gdk", "cairo", "pango", "sound.gst", "sound.pygst"): v = c.rawget("%s.version" % x, None) if v is not None: self._remote_lib_versions[x] = v #linux distribution is a tuple of different types, ie: ('Linux Fedora' , 20, 'Heisenbug') pld = c.tupleget("platform.linux_distribution") if pld and len(pld)==3: def san(v): if isinstance(v, int): return v return bytestostr(v) self._remote_platform_linux_distribution = [san(x) for x in pld] verr = version_compat_check(self._remote_version) if verr is not None: self.warn_and_quit(EXIT_INCOMPATIBLE_VERSION, "incompatible remote version '%s': %s" % (self._remote_version, verr)) return False return True
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
def test_version_compat_check_invalid(self): from xpra import __version__ self.assertIsNone(version_compat_check(__version__)) self.assertIsNotNone(version_compat_check("0.1"))
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): authlog.error("Error: authentication failed") authlog.error(" %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: authlog("creating authenticator %s", proto.auth_class) try: auth, aclass, options = proto.auth_class ainstance = aclass(username, **options) proto.authenticator = ainstance authlog("%s=%s", auth, ainstance) except Exception as e: authlog.error("Error instantiating %s:", proto.auth_class) authlog.error(" %s", 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") padding = c.strget("cipher.padding", DEFAULT_PADDING) padding_options = c.strlistget("cipher.padding.options", [DEFAULT_PADDING]) auth_caps = {} if cipher and cipher_iv: if cipher not in ENCRYPTION_CIPHERS: authlog.warn("unsupported cipher: %s", cipher) auth_failed("unsupported cipher") return False encryption_key = self.get_encryption_key(proto.authenticator, proto.keyfile) if encryption_key is None: auth_failed("encryption key is missing") return False if padding not in ALL_PADDING_OPTIONS: auth_failed("unsupported padding: %s" % padding) return False authlog("set output cipher using encryption key '%s'", repr_ellipsized(encryption_key)) proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt, iterations, padding) #use the same cipher as used by the client: auth_caps = new_cipher_caps(proto, cipher, encryption_key, padding_options) authlog("server cipher=%s", auth_caps) else: if proto.encryption: authlog("client does not provide encryption tokens") auth_failed("missing encryption tokens") return False 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") authlog( "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 authlog("challenge: %s", challenge) salt, digest = challenge authlog.info( "Authentication required by %s authenticator module", proto.authenticator) authlog.info(" sending challenge for '%s' using %s digest", username, digest) if digest not in self.digest_modes: auth_failed( "cannot proceed without %s digest support" % digest) return False else: authlog.warn( "Warning: client expects a challenge but this connection is unauthenticated" ) #fake challenge so the client will send the real hello: salt = get_salt() 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 authlog("authentication challenge passed") else: #did the client expect a challenge? if c.boolget("challenge"): authlog.warn( "this server does not require authentication (client supplied a challenge)" ) return auth_caps
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
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): authlog.error("Error: authentication failed") authlog.error(" %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: authlog("creating authenticator %s", proto.auth_class) try: auth, aclass, options = proto.auth_class ainstance = aclass(username, **options) proto.authenticator = ainstance authlog("%s=%s", auth, ainstance) except Exception as e: authlog.error("Error instantiating %s:", proto.auth_class) authlog.error(" %s", e) auth_failed("authentication failed") return False 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") padding = c.strget("cipher.padding", DEFAULT_PADDING) padding_options = c.strlistget("cipher.padding.options", [DEFAULT_PADDING]) auth_caps = {} if cipher and cipher_iv: if cipher not in ENCRYPTION_CIPHERS: authlog.warn("unsupported cipher: %s", cipher) auth_failed("unsupported cipher") return False encryption_key = self.get_encryption_key(proto.authenticator, proto.keyfile) if encryption_key is None: auth_failed("encryption key is missing") return False if padding not in ALL_PADDING_OPTIONS: auth_failed("unsupported padding: %s" % padding) return False authlog("set output cipher using encryption key '%s'", repr_ellipsized(encryption_key)) proto.set_cipher_out(cipher, cipher_iv, encryption_key, key_salt, iterations, padding) #use the same cipher as used by the client: auth_caps = new_cipher_caps(proto, cipher, encryption_key, padding_options) authlog("server cipher=%s", auth_caps) else: if proto.encryption: authlog("client does not provide encryption tokens") auth_failed("missing encryption tokens") return False 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") authlog("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 authlog("challenge: %s", challenge) salt, digest = challenge authlog.info("Authentication required by %s authenticator module", proto.authenticator) authlog.info(" sending challenge for '%s' using %s digest", username, digest) if digest not in digest_modes: auth_failed("cannot proceed without %s digest support" % digest) return False else: authlog.warn("Warning: client expects a challenge but this connection is unauthenticated") #fake challenge so the client will send the real hello: salt = get_salt() 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 authlog("authentication challenge passed") else: #did the client expect a challenge? if c.boolget("challenge"): authlog.warn("this server does not require authentication (client supplied a challenge)") return auth_caps