def main(argv): from xpra.util import xor from xpra.net.digest import get_salt, get_digests, gendigest from xpra.platform import program_context with program_context("LDAP-Password-Auth", "LDAP-Password-Authentication"): for x in list(argv): if x in ("-v", "--verbose"): enable_debug_for("auth") argv.remove(x) if len(argv) not in (3,4,5,6,7): sys.stderr.write("%s invalid arguments\n" % argv[0]) sys.stderr.write("usage: %s username password [host] [port] [tls] [username_format]\n" % argv[0]) return 1 username = argv[1] password = argv[2] kwargs = {} if len(argv)>=4: kwargs["host"] = argv[3] if len(argv)>=5: kwargs["port"] = argv[4] if len(argv)>=6: kwargs["tls"] = argv[5] if len(argv)>=7: kwargs["username_format"] = argv[6] a = Authenticator(username, **kwargs) server_salt, digest = a.get_challenge(["xor"]) salt_digest = a.choose_salt_digest(get_digests()) assert digest=="xor" client_salt = get_salt(len(server_salt)) combined_salt = gendigest(salt_digest, client_salt, server_salt) response = xor(password, combined_salt) r = a.authenticate(response, client_salt) print("success: %s" % r) return int(not r)
def main(argv): from xpra.platform import program_context with program_context("Kerberos-Password-Auth", "Kerberos-Password-Authentication"): if len(argv) not in (3, 4, 5): sys.stderr.write("%s invalid arguments\n" % argv[0]) sys.stderr.write( "usage: %s username password [service [realm]]\n" % argv[0]) return 1 username = argv[1] password = argv[2] kwargs = {"username": username} if len(argv) >= 4: kwargs["service"] = argv[3] if len(argv) == 5: kwargs["realm"] = argv[4] a = Authenticator(**kwargs) server_salt, digest = a.get_challenge(["xor"]) salt_digest = a.choose_salt_digest(get_digests()) assert digest == "xor" client_salt = get_salt(len(server_salt)) combined_salt = gendigest(salt_digest, client_salt, server_salt) response = xor(password, combined_salt) caps = typedict({ "challenge_response": response, "challenge_client_salt": client_salt, }) a.authenticate(caps) return 0
def new_cipher_caps(proto, cipher, cipher_mode, encryption_key, padding_options) -> dict: assert backend iv = get_iv() key_salt = get_salt() key_size = DEFAULT_KEYSIZE key_hash = DEFAULT_KEY_HASH key_stretch = DEFAULT_KEY_STRETCH iterations = get_iterations() padding = choose_padding(padding_options) proto.set_cipher_in(cipher + "-" + cipher_mode, iv, encryption_key, key_salt, key_hash, key_size, iterations, padding) return { "cipher": cipher, "cipher.mode": cipher_mode, "cipher.mode.options": MODES, "cipher.iv": iv, "cipher.key_salt": key_salt, "cipher.key_hash": key_hash, "cipher.key_size": key_size, "cipher.key_stretch": key_stretch, "cipher.key_stretch.options": KEY_STRETCHING, "cipher.key_stretch_iterations": iterations, "cipher.padding": padding, "cipher.padding.options": PADDING_OPTIONS, }
def send_hello(self, challenge_response=None, client_salt=None): if not self._protocol: log("send_hello(..) skipped, no protocol (listen mode?)") return try: hello = self.make_hello_base() if self.has_password() and not challenge_response: #avoid sending the full hello: tell the server we want #a packet challenge first hello["challenge"] = True else: hello.update(self.make_hello()) except InitExit as e: log.error("error preparing connection:") log.error(" %s", e) self.quit(EXIT_INTERNAL_ERROR) return except Exception as e: log.error("error preparing connection: %s", e, exc_info=True) self.quit(EXIT_INTERNAL_ERROR) return if challenge_response: hello["challenge_response"] = challenge_response #make it harder for a passive attacker to guess the password length #by observing packet sizes (only relevant for wss and ssl) hello["challenge_padding"] = get_salt( max(32, 512 - len(challenge_response))) if client_salt: hello["challenge_client_salt"] = client_salt log("send_hello(%s) packet=%s", hexstr(challenge_response or ""), hello) self.send("hello", hello)
def main(argv): #pylint: disable=import-outside-toplevel from xpra.platform import program_context with program_context("Kerberos-Token-Auth", "Kerberos Token Authentication"): if len(argv) != 3: sys.stderr.write("%s invalid arguments\n" % argv[0]) sys.stderr.write("usage: %s username token\n" % argv[0]) return 1 username = argv[1] token = argv[2] kwargs = {"username": username} a = Authenticator(**kwargs) server_salt, digest = a.get_challenge(["xor"]) salt_digest = a.choose_salt_digest(get_digests()) assert digest == "xor" client_salt = get_salt(len(server_salt)) combined_salt = gendigest(salt_digest, client_salt, server_salt) response = xor(token, combined_salt) caps = typedict({ "challenge_response": response, "challenge_client_salt": client_salt, }) a.authenticate(caps) return 0
def main(argv): #pylint: disable=import-outside-toplevel from xpra.platform import program_context with program_context("GSS-Auth", "GSS-Authentication"): if len(argv) != 3: sys.stderr.write("%s invalid arguments\n" % argv[0]) sys.stderr.write("usage: %s username token\n" % argv[0]) return 1 username = argv[1] token = argv[2] kwargs = {"username": username} a = Authenticator(**kwargs) server_salt, digest = a.get_challenge(["gss"]) salt_digest = a.choose_salt_digest(get_digests()) assert digest.startswith("gss:"), "unexpected digest %r" % digest client_salt = get_salt(len(server_salt)) combined_salt = gendigest(salt_digest, client_salt, server_salt) response = gendigest(digest, token, combined_salt) caps = typedict({ "challenge_response": response, "challenge_client_salt": client_salt, }) r = a.authenticate(caps) print("success: %s" % bool(r)) return r
def make_hello_base(self): capabilities = flatten_dict(get_network_caps()) #add "kerberos", "gss" and "u2f" digests if enabled: for handler in self.challenge_handlers: digest = handler.get_digest() if digest: capabilities["digest"].append(digest) capabilities.update(FilePrintMixin.get_caps(self)) capabilities.update({ "version": XPRA_VERSION, "websocket.multi-packet": True, "hostname": socket.gethostname(), "uuid": self.uuid, "session-id": self.session_id, "username": self.username, "name": get_name(), "client_type": self.client_type(), "python.version": sys.version_info[:3], "python.bits": BITS, "compression_level": self.compression_level, "argv": sys.argv, }) capabilities.update(self.get_file_transfer_features()) if self.display: capabilities["display"] = self.display def up(prefix, d): updict(capabilities, prefix, d) up("build", self.get_version_info()) mid = get_machine_id() if mid: capabilities["machine_id"] = mid encryption = self.get_encryption() if encryption: crypto_backend_init() assert encryption in ENCRYPTION_CIPHERS, "invalid encryption '%s', options: %s" % ( encryption, csv(ENCRYPTION_CIPHERS)) iv = get_iv() key_salt = get_salt() iterations = get_iterations() padding = choose_padding(self.server_padding_options) up( "cipher", { "": encryption, "iv": iv, "key_salt": key_salt, "key_stretch_iterations": iterations, "padding": padding, "padding.options": PADDING_OPTIONS, }) key = self.get_encryption_key() self._protocol.set_cipher_in(encryption, iv, key, key_salt, iterations, padding) netlog( "encryption capabilities: %s", dict((k, v) for k, v in capabilities.items() if k.startswith("cipher"))) capabilities.update(self.hello_extra) return capabilities
def send_challenge_reply(self, packet, password): if not password: if self.password_file: self.auth_error( EXIT_PASSWORD_FILE_ERROR, "failed to load password from file%s %s" % (engs(self.password_file), csv(self.password_file)), "no password available") else: self.auth_error( EXIT_PASSWORD_REQUIRED, "this server requires authentication and no password is available" ) return 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: self.auth_error(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: server_salt = bytestostr(packet[1]) digest = bytestostr(packet[3]) actual_digest = digest.split(":", 1)[0] l = len(server_salt) salt_digest = "xor" if len(packet) >= 5: salt_digest = bytestostr(packet[4]) if salt_digest == "xor": #with xor, we have to match the size assert l >= 16, "server salt is too short: only %i bytes, minimum is 16" % l assert l <= 256, "server salt is too long: %i bytes, maximum is 256" % l else: #other digest, 32 random bytes is enough: l = 32 client_salt = get_salt(l) salt = gendigest(salt_digest, client_salt, server_salt) authlog("combined %s salt(%s, %s)=%s", salt_digest, hexstr(server_salt), hexstr(client_salt), hexstr(salt)) challenge_response = gendigest(actual_digest, password, salt) if not challenge_response: log("invalid digest module '%s': %s", actual_digest) self.auth_error( EXIT_UNSUPPORTED, "server requested '%s' digest but it is not supported" % actual_digest, "invalid digest") return authlog("%s(%s, %s)=%s", actual_digest, repr(password), repr(salt), repr(challenge_response)) self.do_send_challenge_reply(challenge_response, client_salt)
def get_challenge(self, digests): if self.salt is not None: log.error("Error: authentication challenge already sent!") return None self.salt = get_salt() self.digest = choose_digest(digests) self.challenge_sent = True return self.salt, self.digest
def get_challenge(self, digests): if "u2f" not in digests: log.error("Error: client does not support u2f authentication") return None self.salt = get_salt() self.digest = "u2f:xor" self.challenge_sent = True return self.salt, self.digest
def get_challenge(self, digests): assert not self.challenge_sent if "kerberos" not in digests: log.error("Error: client does not support kerberos authentication") return None self.salt = get_salt() self.challenge_sent = True return self.salt, "kerberos:%s" % self.service
def get_challenge(self, digests): if self.salt is not None: log.error("challenge already sent!") if self.salt is not False: self.salt = False return None self.salt = get_salt() self.digest = choose_digest(digests) self.challenge_sent = True return self.salt, self.digest
def make_hello_base(self): capabilities = flatten_dict(get_network_caps()) #add "kerberos" and "gss" if enabled: default_on = "all" in self.challenge_handlers or "auto" in self.challenge_handlers for auth in ("kerberos", "gss", "u2f"): if default_on or auth in self.challenge_handlers: capabilities["digest"].append(auth) capabilities.update(FilePrintMixin.get_caps(self)) capabilities.update({ "version" : XPRA_VERSION, "encoding.generic" : True, "namespace" : True, "websocket.multi-packet": True, "hostname" : socket.gethostname(), "uuid" : self.uuid, "username" : self.username, "name" : get_name(), "client_type" : self.client_type(), "python.version" : sys.version_info[:3], "python.bits" : BITS, "compression_level" : self.compression_level, "argv" : sys.argv, }) capabilities.update(self.get_file_transfer_features()) if self.display: capabilities["display"] = self.display def up(prefix, d): updict(capabilities, prefix, d) up("build", self.get_version_info()) mid = get_machine_id() if mid: capabilities["machine_id"] = mid if self.encryption: assert self.encryption in ENCRYPTION_CIPHERS iv = get_iv() key_salt = get_salt() iterations = get_iterations() padding = choose_padding(self.server_padding_options) up("cipher", { "" : self.encryption, "iv" : iv, "key_salt" : key_salt, "key_stretch_iterations": iterations, "padding" : padding, "padding.options" : PADDING_OPTIONS, }) key = self.get_encryption_key() if key is None: self.warn_and_quit(EXIT_ENCRYPTION, "encryption key is missing") return None self._protocol.set_cipher_in(self.encryption, iv, key, key_salt, iterations, padding) netlog("encryption capabilities: %s", dict((k,v) for k,v in capabilities.items() if k.startswith("cipher"))) capabilities.update(self.hello_extra) return capabilities
def test_all_digests(self): for digest in get_digests(): if digest.startswith("hmac"): m = get_digest_module(digest) assert m is not None, "digest module not found for '%s'" % digest salt = get_salt(32) password = "******" d = gendigest(digest, password, salt) assert d is not None assert not verify_digest(digest, None, salt, d) assert not verify_digest(digest, password, None, d) assert not verify_digest(digest, password, salt, None) assert not verify_digest(digest, password, salt, d[:-1]) assert verify_digest(digest, password, salt, d)
def new_cipher_caps(proto, cipher, encryption_key, padding_options): assert backend iv = get_iv() key_salt = get_salt() iterations = get_iterations() padding = choose_padding(padding_options) proto.set_cipher_in(cipher, iv, encryption_key, key_salt, iterations, padding) return { "cipher" : cipher, "cipher.iv" : iv, "cipher.key_salt" : key_salt, "cipher.key_stretch_iterations": iterations, "cipher.padding" : padding, "cipher.padding.options" : PADDING_OPTIONS, }
def main(argv): from xpra.platform import program_context with program_context("GSS-Auth", "GSS-Authentication"): if len(argv) != 3: sys.stderr.write("%s invalid arguments\n" % argv[0]) sys.stderr.write("usage: %s username token\n" % argv[0]) return 1 username = argv[1] token = argv[2] kwargs = {} a = Authenticator(username, **kwargs) server_salt, digest = a.get_challenge(["xor"]) salt_digest = a.choose_salt_digest(get_digests()) assert digest == "xor" client_salt = get_salt(len(server_salt)) combined_salt = gendigest(salt_digest, client_salt, server_salt) response = xor(token, combined_salt) a.authenticate(response, client_salt) return 0
def main(argv): #pylint: disable=import-outside-toplevel from xpra.net.digest import get_salt, get_digests, gendigest from xpra.platform import program_context with program_context("LDAP-Password-Auth", "LDAP-Password-Authentication"): for x in list(argv): if x in ("-v", "--verbose"): enable_debug_for("auth") argv.remove(x) if len(argv) not in (3, 4, 5, 6, 7): sys.stderr.write("%s invalid arguments\n" % argv[0]) sys.stderr.write( "usage: %s username password [host] [port] [tls] [username_format]\n" % argv[0]) return 1 username = argv[1] password = argv[2] kwargs = {"username": username} if len(argv) >= 4: kwargs["host"] = argv[3] if len(argv) >= 5: kwargs["port"] = argv[4] if len(argv) >= 6: kwargs["tls"] = argv[5] if len(argv) >= 7: kwargs["username_format"] = argv[6] a = Authenticator(**kwargs) server_salt, digest = a.get_challenge(["xor"]) salt_digest = a.choose_salt_digest(get_digests()) assert digest == "xor" client_salt = get_salt(len(server_salt)) combined_salt = gendigest(salt_digest, client_salt, server_salt) assert digest == "xor" response = gendigest(digest, password, combined_salt) caps = typedict({ "challenge_response": response, "challenge_client_salt": client_salt, }) r = a.authenticate(caps) print("success: %s" % bool(r)) return int(not r)
def process_server_packet(self, proto, packet): packet_type = bytestostr(packet[0]) log("process_server_packet: %s", packet_type) if packet_type == Protocol.CONNECTION_LOST: self.stop(proto, "server connection lost") return if packet_type == "disconnect": reason = bytestostr(packet[1]) log("got disconnect from server: %s", reason) if self.exit: self.server_protocol.close() else: self.stop(None, "disconnect from server", reason) elif packet_type == "hello": c = typedict(packet[1]) if c.boolget("ping-echo-sourceid"): self.schedule_server_ping() maxw, maxh = c.intpair("max_desktop_size", (4096, 4096)) caps = self.filter_server_caps(c) #add new encryption caps: if self.cipher: from xpra.net.crypto import crypto_backend_init, new_cipher_caps, DEFAULT_PADDING crypto_backend_init() padding_options = self.caps.strtupleget( "cipher.padding.options", [DEFAULT_PADDING]) auth_caps = new_cipher_caps(self.client_protocol, self.cipher, self.encryption_key, padding_options) caps.update(auth_caps) #may need to bump packet size: proto.max_packet_size = max(16 * 1024 * 1024, maxw * maxh * 4 * 4) file_transfer = self.caps.boolget("file-transfer") and c.boolget( "file-transfer") file_size_limit = max(self.caps.intget("file-size-limit"), c.intget("file-size-limit")) file_max_packet_size = int(file_transfer) * ( 1024 + file_size_limit * 1024 * 1024) self.client_protocol.max_packet_size = max( self.client_protocol.max_packet_size, file_max_packet_size) self.server_protocol.max_packet_size = max( self.server_protocol.max_packet_size, file_max_packet_size) packet = ("hello", caps) elif packet_type == "ping_echo" and self.server_ping_timer and len( packet) >= 7 and packet[6] == strtobytes(self.uuid): #this is one of our ping packets: self.server_last_ping_echo = packet[1] self.server_last_ping_latency = 1000 * monotonic_time( ) - self.server_last_ping_echo log("ping-echo: server latency=%.1fms", self.server_last_ping_latency) return elif packet_type == "info-response": #adds proxy info: #note: this is only seen by the client application #"xpra info" is a new connection, which talks to the proxy server... info = packet[1] info.update(self.get_proxy_info(proto)) elif packet_type == "lost-window": wid = packet[1] #mark it as lost so we can drop any current/pending frames self.lost_windows.add(wid) #queue it so it gets cleaned safely (for video encoders mostly): self.encode_queue.put(packet) #and fall through so tell the client immediately elif packet_type == "draw": #use encoder thread: self.encode_queue.put(packet) #which will queue the packet itself when done: return #we do want to reformat cursor packets... #as they will have been uncompressed by the network layer already: elif packet_type == "cursor": #packet = ["cursor", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", "png", x, y, width, height, xhot, yhot, serial, pixels, name] #or: #packet = ["cursor", ""] if len(packet) >= 8: #hard to distinguish png cursors from normal cursors... try: int(packet[1]) self._packet_recompress(packet, 8, "cursor") except (TypeError, ValueError): self._packet_recompress(packet, 9, "cursor") elif packet_type == "window-icon": self._packet_recompress(packet, 5, "icon") elif packet_type == "send-file": if packet[6]: packet[6] = Compressed("file-data", packet[6]) elif packet_type == "send-file-chunk": if packet[3]: packet[3] = Compressed("file-chunk-data", packet[3]) elif packet_type == "challenge": password = self.disp_desc.get("password", self.session_options.get("password")) log("password from %s / %s = %s", self.disp_desc, self.session_options, password) if not password: if not PASSTHROUGH_AUTH: self.stop(None, "authentication requested by the server,", "but no password is available for this session") #otherwise, just forward it to the client self.client_challenge_packet = packet else: from xpra.net.digest import get_salt, gendigest #client may have already responded to the challenge, #so we have to handle authentication from this end server_salt = bytestostr(packet[1]) l = len(server_salt) digest = bytestostr(packet[3]) salt_digest = "xor" if len(packet) >= 5: salt_digest = bytestostr(packet[4]) if salt_digest in ("xor", "des"): if not LEGACY_SALT_DIGEST: self.stop( None, "server uses legacy salt digest '%s'" % salt_digest) return log.warn( "Warning: server using legacy support for '%s' salt digest", salt_digest) if salt_digest == "xor": #with xor, we have to match the size assert l >= 16, "server salt is too short: only %i bytes, minimum is 16" % l assert l <= 256, "server salt is too long: %i bytes, maximum is 256" % l else: #other digest, 32 random bytes is enough: l = 32 client_salt = get_salt(l) salt = gendigest(salt_digest, client_salt, server_salt) challenge_response = gendigest(digest, password, salt) if not challenge_response: log("invalid digest module '%s': %s", digest) self.stop( None, "server requested '%s' digest but it is not supported" % digest) return log.info("sending %s challenge response", digest) self.send_hello(challenge_response, client_salt) return self.queue_client_packet(packet)
def make_hello_base(self): capabilities = flatten_dict(get_network_caps()) #add "kerberos", "gss" and "u2f" digests if enabled: for handler in self.challenge_handlers: digest = handler.get_digest() if digest: capabilities["digest"].append(digest) capabilities.update(FilePrintMixin.get_caps(self)) capabilities.update({ "version" : XPRA_VERSION, "websocket.multi-packet": True, "hostname" : socket.gethostname(), "uuid" : self.uuid, "session-id" : self.session_id, "username" : self.username, "name" : get_name(), "client_type" : self.client_type(), "python.version" : sys.version_info[:3], "python.bits" : BITS, "compression_level" : self.compression_level, "argv" : sys.argv, }) capabilities.update(self.get_file_transfer_features()) if self.display: capabilities["display"] = self.display def up(prefix, d): updict(capabilities, prefix, d) up("build", self.get_version_info()) mid = get_machine_id() if mid: capabilities["machine_id"] = mid encryption = self.get_encryption() cryptolog("encryption=%s", encryption) if encryption: crypto_backend_init() enc, mode = (encryption+"-").split("-")[:2] if not mode: mode = DEFAULT_MODE assert enc in ENCRYPTION_CIPHERS, "invalid encryption '%s', options: %s" % (enc, csv(ENCRYPTION_CIPHERS)) assert mode in MODES, "invalid encryption mode '%s', options: %s" % (mode, csv(MODES)) iv = get_iv() key_salt = get_salt() iterations = get_iterations() padding = choose_padding(self.server_padding_options) cipher_caps = { "" : enc, "mode" : mode, "iv" : iv, "key_salt" : key_salt, "key_size" : DEFAULT_KEYSIZE, "key_hash" : DEFAULT_KEY_HASH, "key_stretch" : "PBKDF2", "key_stretch_iterations": iterations, "padding" : padding, "padding.options" : PADDING_OPTIONS, } cryptolog("cipher_caps=%s", cipher_caps) up("cipher", cipher_caps) key = self.get_encryption_key() self._protocol.set_cipher_in(encryption, iv, key, key_salt, DEFAULT_KEY_HASH, DEFAULT_KEYSIZE, iterations, padding) capabilities.update(self.hello_extra) return capabilities
def get_challenge(self, digests): self.challenge_sent = True return get_salt(), choose_digest(digests)