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 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 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): 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 _test_hmac_auth(self, mod_name, password, **kwargs): for test_password in (password, "somethingelse"): a = self._init_auth(mod_name, **kwargs) assert a.requires_challenge() assert a.get_passwords() salt, mac = a.get_challenge( [x for x in get_digests() if x.startswith("hmac")]) assert salt assert mac.startswith("hmac"), "invalid mac: %s" % mac client_salt = strtobytes(uuid.uuid4().hex + uuid.uuid4().hex) salt_digest = a.choose_salt_digest(get_digests()) auth_salt = strtobytes(gendigest(salt_digest, client_salt, salt)) digestmod = get_digest_module(mac) verify = hmac.HMAC(strtobytes(test_password), auth_salt, digestmod=digestmod).hexdigest() passed = a.authenticate(verify, client_salt) assert passed == ( test_password == password ), "expected authentication to %s with %s vs %s" % ([ "fail", "succeed" ][test_password == password], test_password, password) assert not a.authenticate( verify, client_salt ), "should not be able to athenticate again with the same values"
def authenticate_check(self, challenge_response: str, client_salt: str = None) -> bool: if self.salt is None: log.error( "Error: illegal challenge response received - salt cleared or unset" ) return False salt = self.get_response_salt(client_salt) password = gendigest("xor", challenge_response, salt) log("authenticate_check(%s, %s) response salt=%s", obsc(repr(challenge_response)), repr(client_salt), repr(salt)) #warning: enabling logging here would log the actual system password! #log.info("authenticate(%s, %s) password=%s (%s)", # hexstr(challenge_response), hexstr(client_salt), password, hexstr(password)) #verify login: try: ret = self.check(password) log("authenticate_check(..)=%s", ret) except Exception as e: log("check(..)", exc_info=True) log.error("Error: %s authentication check failed:", self) log.error(" %s", e) return False return ret
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 _test_file_auth(self, mod_name, genauthdata, display_count=0): #no file, no go: a = self._init_auth(mod_name) assert a.requires_challenge() p = a.get_passwords() assert not p, "got passwords from %s: %s" % (a, p) #challenge twice is a fail assert a.get_challenge(get_digests()) assert not a.get_challenge(get_digests()) assert not a.get_challenge(get_digests()) for muck in (0, 1): with TempFileContext(prefix=mod_name) as context: f = context.file filename = context.filename with f: a = self._init_auth(mod_name, filename=filename) password, filedata = genauthdata(a) #print("saving password file data='%s' to '%s'" % (filedata, filename)) f.write(strtobytes(filedata)) f.flush() assert a.requires_challenge() salt, mac = a.get_challenge(get_digests()) assert salt assert mac in get_digests() assert mac != "xor" password = strtobytes(password) client_salt = strtobytes(uuid.uuid4().hex + uuid.uuid4().hex)[:len(salt)] salt_digest = a.choose_salt_digest(get_digests()) assert salt_digest auth_salt = strtobytes( gendigest(salt_digest, client_salt, salt)) if muck == 0: digestmod = get_digest_module(mac) verify = hmac.HMAC(password, auth_salt, digestmod=digestmod).hexdigest() assert a.authenticate( verify, client_salt), "%s failed" % a.authenticate if display_count > 0: sessions = a.get_sessions() assert len(sessions) >= 3 displays = sessions[2] assert len( displays ) == display_count, "expected %i displays but got %i : %s" % ( display_count, len(sessions), sessions) assert not a.authenticate( verify, client_salt), "authenticated twice!" passwords = a.get_passwords() assert len( passwords ) == 1, "expected just one password in file, got %i" % len( passwords) assert password in passwords elif muck == 1: for verify in ("whatever", None, "bad"): assert not a.authenticate(verify, client_salt) return a
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 get_response_salt(self, client_salt=None): server_salt = self.salt #make sure it does not get re-used: self.salt = None if client_salt is None: return server_salt salt = gendigest(self.salt_digest, client_salt, server_salt) if salt in SysAuthenticator.USED_SALT: raise Exception("danger: an attempt was made to re-use the same computed salt") log("combined salt(%s, %s)=%s", hexstr(server_salt), hexstr(client_salt), hexstr(salt)) SysAuthenticator.USED_SALT.append(salt) return salt
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 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 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 test_invalid_digest(self): for invalid_digest in (None, "foo", "hmac", "hmac+INVALID_HASH_ALGO"): assert get_digest_module(invalid_digest) is None assert gendigest(invalid_digest, "bar", "0" * 16) is None