def check(self, password): log("check(%s)", obsc(password)) def emsg(e): try: log.warn(" LDAP Error: %s", e.message["desc"]) if "info" in e.message: log.warn(" %s", e.message["info"]) except: #python3: no way to get to the message dict? log.warn(" %s", e) try: import ldap except ImportError as e: log("check(..)", exc_info=True) log.warn("Warning: cannot use ldap authentication:") log.warn(" %s", e) return False try: assert self.username and password if self.tls: protocol = "ldaps" else: protocol = "ldap" server = "%s://%s:%i" % (protocol, self.host, self.port) conn = ldap.initialize(server, trace_level=LDAP_TRACE_LEVEL or is_debug_enabled("auth")) conn.protocol_version = LDAP_PROTOCOL_VERSION conn.set_option(ldap.OPT_REFERRALS, LDAP_REFERRALS) if self.cacert: conn.set_option(ldap.OPT_X_TLS_CACERTFILE, self.cacert) log("ldap.open(%s)=%s", server, conn) try: domain = socket.getfqdn().split(".", 1)[1] except: domain = "localdomain" user = self.username_format.replace("%username", self.username).replace("%domain", domain) log("user=%s", user) try: #password should be the result of a digest function, #ie: xor will return bytes.. p = bytestostr(password) password = p.encode(self.encoding) log("ldap encoded password as %s", self.encoding) except: pass v = conn.simple_bind_s(user, password) log("simple_bind_s(%s, %s)=%s", user, obsc(password), v) return True except ldap.INVALID_CREDENTIALS: log("check(..)", exc_info=True) return False except ldap.SERVER_DOWN as e: log("check(..)", exc_info=True) log.warn("Warning: LDAP %sserver at %s:%i is unreachable", ["", "TLS "][self.tls], self.host, self.port) emsg(e) except ldap.LDAPError as e: log("check(..)", exc_info=True) log.warn("Error: ldap authentication failed:") emsg(e) return False
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 encryption = self.get_encryption() if 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 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] if actual_digest=="des": salt = client_salt = server_salt else: 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, obsc(password), repr(salt), obsc(challenge_response)) self.do_send_challenge_reply(challenge_response, client_salt)
def do_process_challenge_prompt(self, packet, prompt="password"): authlog("do_process_challenge_prompt() use_tty=%s", use_tty()) if use_tty(): import getpass authlog("stdin isatty, using password prompt") password = getpass.getpass("%s :" % self.get_challenge_prompt(prompt)) authlog("password read from tty via getpass: %s", obsc(password)) self.send_challenge_reply(packet, password) return True else: from xpra.platform.paths import get_nodock_command cmd = get_nodock_command() + ["_pass", prompt] try: from subprocess import Popen, PIPE proc = Popen(cmd, stdout=PIPE) getChildReaper().add_process(proc, "password-prompt", cmd, True, True) out, err = proc.communicate(None, 60) authlog("err(%s)=%s", cmd, err) password = out.decode() self.send_challenge_reply(packet, password) return True except Exception: log("Error: failed to show GUi for password prompt", exc_info=True) return False
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 get_host_info(obfuscate=False) -> dict: #this function is for non UI thread info info = { "pid": os.getpid(), "byteorder": sys.byteorder, "python": { "bits": BITS, "full_version": sys.version, "version": ".".join(str(x) for x in sys.version_info[:3]), }, } try: hostname = socket.gethostname() if obfuscate and hostname.find(".") > 0: parts = hostname.split(".") for i, part in enumerate(parts): if i > 0: parts[i] = obsc(part) hostname = ".".join(parts) if hostname: info["hostname"] = hostname except OSError: pass if POSIX: info.update({ "uid": os.getuid(), "gid": os.getgid(), }) return info
def process_challenge_env(self, packet): k = "XPRA_PASSWORD" password = os.environ.get(k) authlog("process_challenge_env() %s=%s", k, obsc(password)) if password: self.send_challenge_reply(packet, password) return True return False
def do_process_challenge_prompt(self, packet, prompt="password"): authlog("do_process_challenge_prompt() use_tty=%s", use_tty()) if use_tty(): import getpass authlog("stdin isatty, using password prompt") password = getpass.getpass("%s :" % self.get_challenge_prompt(prompt)) authlog("password read from tty via getpass: %s", obsc(password)) self.send_challenge_reply(packet, password) return True return False
def process_challenge_file(self, packet): if self.password_index < len(self.password_file): password_file = self.password_file[self.password_index] self.password_index += 1 filename = os.path.expanduser(password_file) password = load_binary_file(filename) authlog("password read from file %i '%s': %s", self.password_index, password_file, obsc(password)) self.send_challenge_reply(packet, password) return True return False
def do_process_challenge_prompt(self, packet, prompt="password"): authlog("do_process_challenge_prompt() isatty=%s", sys.stdin.isatty()) if sys.stdin.isatty() and not os.environ.get("MSYSCON"): import getpass authlog("stdin isatty, using password prompt") password = getpass.getpass("%s :" % self.get_challenge_prompt(prompt)) authlog("password read from tty via getpass: %s", obsc(password)) self.send_challenge_reply(packet, password) return True return False
def check(self, password): log("check(%s)", obsc(password)) try: from ldap3 import Server, Connection, Tls, ALL, SIMPLE, SASL, NTLM #@UnresolvedImport except ImportError as e: log("check(..)", exc_info=True) log.warn("Warning: cannot use ldap3 authentication:") log.warn(" %s", e) return False try: MECHANISM = { "SIMPLE": SIMPLE, "SASL": SASL, "NTLM": NTLM, } authentication = MECHANISM[self.authentication] tls = None if self.tls: tls = Tls(validate=self.tls_validate, version=self.tls_version, ca_certs_file=self.cacert) log("TLS=%s", tls) server = Server(self.host, port=self.port, tls=tls, use_ssl=self.tls, get_info=ALL) log("ldap3 Server(%s)=%s", (self.host, self.port, self.tls), server) conn = Connection(server, user=self.username, password=password, authentication=authentication, receive_timeout=10) log("ldap3 Connection(%s, %s, %s)=%s", server, self.username, self.authentication, conn) if self.tls: conn.start_tls() r = conn.bind() log("ldap3 %s.bind()=%s", conn, r) if not r: return False if is_debug_enabled("auth"): log("ldap3 server info:") for l in server.info.splitlines(): log(" %s", l) log("ldap3 who_am_i()=%s", conn.extend.standard.who_am_i()) return True except Exception as e: log("ldap3 check(..)", exc_info=True) log.error("Error: ldap3 authentication failed:") log.error(" %s", e) return False
def authenticate_hmac(self, challenge_response, client_salt=None): if not self.salt: log.error("Error: illegal challenge response received - salt cleared or unset") return None salt = self.get_response_salt(client_salt) password = self.get_password() log("authenticate_hmac() get_password()=%s", obsc(password)) if not password: log.warn("Warning: authentication failed") log.warn(" no password for '%s' in '%s'", self.username, self.password_filename) return False if not verify_digest(self.digest, password, salt, challenge_response): log.warn("Warning: %s challenge for '%s' does not match", self.digest, self.username) return False return True