def mksockdir(self, d, mode=0o700, uid=None, gid=None): if not d: return if not os.path.exists(d): if uid is None: uid = self.uid if gid is None: gid = self.gid parent = os.path.dirname(d) if parent and parent != "/" and not os.path.exists(parent): self.mksockdir(parent, mode, uid, gid) with umask_context(0): os.mkdir(d, mode) if uid != os.getuid() or gid != os.getgid(): os.lchown(d, uid, gid) elif d != "/tmp": try: st_mode = os.stat(d).st_mode if st_mode & 0o777 != mode: log = get_util_logger() log.warn("Warning: socket directory '%s'", d) log.warn(" expected permissions %s but found %s", oct(mode), oct(st_mode & 0o777)) except OSError: get_util_logger().log("mksockdir%s", (d, mode, uid, gid), exc_info=True)
def safe_open_download_file(basefilename, mimetype): from xpra.platform.paths import get_download_dir dd = os.path.expanduser(get_download_dir()) filename = os.path.abspath(os.path.join(dd, basename(basefilename))) ext = MIMETYPE_EXTS.get(mimetype) if ext and not filename.endswith("."+ext): #on some platforms (win32), #we want to force an extension #so that the file manager can display them properly when you double-click on them filename += "."+ext #make sure we use a filename that does not exist already: root, ext = os.path.splitext(filename) base = 0 while os.path.exists(filename): filelog("cannot save file as %s: file already exists", filename) base += 1 filename = root+("-%s" % base)+ext filelog("safe_open_download_file(%s, %s) will use '%s'", basefilename, mimetype, filename) flags = os.O_CREAT | os.O_RDWR | os.O_EXCL try: flags |= os.O_BINARY #@UndefinedVariable (win32 only) except AttributeError: pass with umask_context(0o133): fd = os.open(filename, flags) filelog("using filename '%s', file descriptor=%s", filename, fd) return filename, fd
def mksockdir(self, d, mode=0o700, uid=None, gid=None): if d and not os.path.exists(d): if uid is None: uid = self.uid if gid is None: gid = self.gid with umask_context(0): os.mkdir(d, mode) if uid != os.getuid() or gid != os.getgid(): os.lchown(d, uid, gid)
def create_unix_domain_socket(sockpath, socket_permissions=0o600): assert POSIX #convert this to a umask! umask = (0o777 - socket_permissions) & 0o777 listener = socket.socket(socket.AF_UNIX) listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #bind the socket, using umask to set the correct permissions with umask_context(umask): listener.bind(sockpath) try: inode = os.stat(sockpath).st_ino except OSError: inode = -1 #set to the "xpra" group if we are a member of it, or if running as root: uid = getuid() username = get_username_for_uid(uid) groups = get_groups(username) if uid == 0 or GROUP in groups: group_id = get_group_id(GROUP) if group_id >= 0: try: os.lchown(sockpath, -1, group_id) except Exception as e: log = get_network_logger() log.warn("Warning: failed to set '%s' group ownership", GROUP) log.warn(" on socket '%s':", sockpath) log.warn(" %s", e) #don't know why this doesn't work: #os.fchown(listener.fileno(), -1, group_id) def cleanup_socket(): log = get_network_logger() try: cur_inode = os.stat(sockpath).st_ino except OSError: log.info("socket '%s' already deleted", sockpath) return delpath = sockpath log("cleanup_socket '%s', original inode=%s, new inode=%s", sockpath, inode, cur_inode) if cur_inode == inode: log.info("removing unix domain socket '%s'", delpath) try: os.unlink(delpath) except OSError: pass return listener, cleanup_socket
def write_runner_shell_scripts(contents, overwrite=True): assert POSIX # This used to be given a display-specific name, but now we give it a # single fixed name and if multiple servers are started then the last one # will clobber the rest. This isn't great, but the tradeoff is that it # makes it possible to use bare 'ssh:hostname' display names and # autodiscover the proper numeric display name when only one xpra server # is running on the remote host. Might need to revisit this later if # people run into problems or autodiscovery turns out to be less useful # than expected. log = get_util_logger() MODE = 0o700 from xpra.platform.paths import get_script_bin_dirs for d in get_script_bin_dirs(): scriptdir = osexpand(d) if not os.path.exists(scriptdir): try: os.mkdir(scriptdir, MODE) except Exception as e: log("os.mkdir(%s, %s)", scriptdir, oct(MODE), exc_info=True) log.warn("Warning: failed to create script directory '%s':", scriptdir) log.warn(" %s", e) if scriptdir.startswith( "/var/run/user") or scriptdir.startswith("/run/user"): log.warn(" ($XDG_RUNTIME_DIR has not been created?)") continue scriptpath = os.path.join(scriptdir, "run-xpra") if os.path.exists(scriptpath) and not overwrite: continue # Write out a shell-script so that we can start our proxy in a clean # environment: try: with umask_context(0o022): h = os.open(scriptpath, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, MODE) try: os.write(h, contents) finally: os.close(h) except Exception as e: log("writing to %s", scriptpath, exc_info=True) log.error("Error: failed to write script file '%s':", scriptpath) log.error(" %s\n", e)
def create_system_dir(self, sps): if not POSIX or OSX or not sps: return xpra_group_id = get_group_id(SOCKET_DIR_GROUP) if sps.startswith("/run/xpra") or sps.startswith("/var/run/xpra"): #create the directory and verify its permissions #which should have been set correctly by tmpfiles.d, #but may have been set wrong if created by systemd's socket activation instead d = sps.split("/xpra")[0] + "/xpra" try: if os.path.exists(d): stat = os.stat(d) mode = stat.st_mode if (mode & SOCKET_DIR_MODE) != SOCKET_DIR_MODE: log.warn("Warning: invalid permissions on '%s' : %s", d, oct(mode)) mode = mode | SOCKET_DIR_MODE log.warn(" changing to %s", oct(mode)) os.chmod(d, mode) if xpra_group_id >= 0 and stat.st_gid != xpra_group_id: import grp group = grp.getgrgid(stat.st_gid)[0] log.warn("Warning: invalid group on '%s': %s", d, group) log.warn(" changing to '%s'", SOCKET_DIR_GROUP) os.lchown(d, stat.st_uid, xpra_group_id) else: log.info( "creating '%s' with permissions %s and group '%s'", d, oct(SOCKET_DIR_MODE), SOCKET_DIR_GROUP) with umask_context(0): os.mkdir(d, SOCKET_DIR_MODE) stat = os.stat(d) if xpra_group_id >= 0 and stat.st_gid != xpra_group_id: os.lchown(d, stat.st_uid, xpra_group_id) mode = os.stat(d).st_mode log("%s permissions: %s", d, oct(mode)) except OSError as e: log("create_system_dir()", exc_info=True) log.error( "Error: failed to create or change the permissions on '%s':", d) log.error(" %s", e)
def do_ssh_paramiko_connect_to(transport, host, username, password, host_config=None, keyfiles=None, paramiko_config=None): from paramiko import SSHException, PasswordRequiredException from paramiko.agent import Agent from paramiko.hostkeys import HostKeys log("do_ssh_paramiko_connect_to%s", (transport, host, username, password, host_config, keyfiles, paramiko_config)) log("SSH transport %s", transport) def configvalue(key): #if the paramiko config has a setting, honour it: if paramiko_config and key in paramiko_config: return paramiko_config.get(key) #fallback to the value from the host config: return (host_config or {}).get(key) def configbool(key, default_value=True): return parse_bool(key, configvalue(key), default_value) def configint(key, default_value=0): v = configvalue(key) if v is None: return default_value return int(v) host_key = transport.get_remote_server_key() assert host_key, "no remote server key" log("remote_server_key=%s", keymd5(host_key)) if configbool("verify-hostkey", VERIFY_HOSTKEY): host_keys = HostKeys() host_keys_filename = None KNOWN_HOSTS = get_ssh_known_hosts_files() for known_hosts in KNOWN_HOSTS: host_keys.clear() try: path = os.path.expanduser(known_hosts) if os.path.exists(path): host_keys.load(path) log("HostKeys.load(%s) successful", path) host_keys_filename = path break except IOError: log("HostKeys.load(%s)", known_hosts, exc_info=True) log("host keys=%s", host_keys) keys = host_keys.lookup(host) known_host_key = (keys or {}).get(host_key.get_name()) def keyname(): return host_key.get_name().replace("ssh-", "") if host_key==known_host_key: assert host_key log("%s host key '%s' OK for host '%s'", keyname(), keymd5(host_key), host) else: dnscheck = "" if configbool("verifyhostkeydns"): try: from xpra.net.sshfp import check_host_key dnscheck = check_host_key(host, host_key) except ImportError as e: log("verifyhostkeydns failed", exc_info=True) log.warn("Warning: cannot check SSHFP DNS records") log.warn(" %s", e) log("dnscheck=%s", dnscheck) def adddnscheckinfo(q): if dnscheck is not True: if dnscheck: q += [ "SSHFP validation failed:", dnscheck ] else: q += [ "SSHFP validation failed" ] if dnscheck is True: #DNSSEC provided a matching record log.info("found a valid SSHFP record for host %s", host) elif known_host_key: log.warn("Warning: SSH server key mismatch") qinfo = [ "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!", "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!", "Someone could be eavesdropping on you right now (man-in-the-middle attack)!", "It is also possible that a host key has just been changed.", "The fingerprint for the %s key sent by the remote host is" % keyname(), keymd5(host_key), ] adddnscheckinfo(qinfo) if configbool("stricthostkeychecking", VERIFY_STRICT): log.warn("Host key verification failed.") #TODO: show alert with no option to accept key qinfo += [ "Please contact your system administrator.", "Add correct host key in %s to get rid of this message.", "Offending %s key in %s" % (keyname(), host_keys_filename), "ECDSA host key for %s has changed and you have requested strict checking." % keyname(), ] sys.stderr.write(os.linesep.join(qinfo)) transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") else: assert (not keys) or (host_key.get_name() not in keys) if not keys: log.warn("Warning: unknown SSH host") else: log.warn("Warning: unknown %s SSH host key", keyname()) qinfo = [ "The authenticity of host '%s' can't be established." % (host,), "%s key fingerprint is" % keyname(), keymd5(host_key), ] adddnscheckinfo(qinfo) if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "Unknown SSH host '%s'" % host) if configbool("addkey", ADD_KEY): try: if not host_keys_filename: #the first one is the default, #ie: ~/.ssh/known_hosts on posix host_keys_filename = os.path.expanduser(KNOWN_HOSTS[0]) log("adding %s key for host '%s' to '%s'", keyname(), host, host_keys_filename) if not os.path.exists(host_keys_filename): keys_dir = os.path.dirname(host_keys_filename) if not os.path.exists(keys_dir): log("creating keys directory '%s'", keys_dir) os.mkdir(keys_dir, 0o700) elif not os.path.isdir(keys_dir): log.warn("Warning: '%s' is not a directory") log.warn(" key not saved") if os.path.exists(keys_dir) and os.path.isdir(keys_dir): log("creating known host file '%s'", host_keys_filename) with umask_context(0o133): with open(host_keys_filename, 'a+'): pass host_keys.add(host, host_key.get_name(), host_key) host_keys.save(host_keys_filename) except OSError as e: log("failed to add key to '%s'", host_keys_filename) log.error("Error adding key to '%s'", host_keys_filename) log.error(" %s", e) except Exception as e: log.error("cannot add key", exc_info=True) else: log("ssh host key verification skipped") def auth_agent(): agent = Agent() agent_keys = agent.get_keys() log("agent keys: %s", agent_keys) if agent_keys: for agent_key in agent_keys: log("trying ssh-agent key '%s'", keymd5(agent_key)) try: transport.auth_publickey(username, agent_key) if transport.is_authenticated(): log("authenticated using agent and key '%s'", keymd5(agent_key)) break except SSHException: log("agent key '%s' rejected", keymd5(agent_key), exc_info=True) if not transport.is_authenticated(): log.info("agent authentication failed, tried %i key%s", len(agent_keys), engs(agent_keys)) def auth_publickey(): log("trying public key authentication using %s", keyfiles) for keyfile_path in keyfiles: if not os.path.exists(keyfile_path): log("no keyfile at '%s'", keyfile_path) continue log("trying '%s'", keyfile_path) key = None import paramiko for pkey_classname in ("RSA", "DSS", "ECDSA", "Ed25519"): pkey_class = getattr(paramiko, "%sKey" % pkey_classname, None) if pkey_class is None: log("no %s key type", pkey_classname) continue log("trying to load as %s", pkey_classname) try: key = pkey_class.from_private_key_file(keyfile_path) log.info("loaded %s private key from '%s'", pkey_classname, keyfile_path) break except PasswordRequiredException as e: log("%s keyfile requires a passphrase; %s", keyfile_path, e) passphrase = input_pass("please enter the passphrase for %s:" % (keyfile_path,)) if passphrase: try: key = pkey_class.from_private_key_file(keyfile_path, passphrase) log.info("loaded %s private key from '%s'", pkey_classname, keyfile_path) except SSHException as e: log("from_private_key_file", exc_info=True) log.info("cannot load key from file '%s':", keyfile_path) log.info(" %s", e) break except Exception as e: log("auth_publickey() loading as %s", pkey_classname, exc_info=True) key_data = load_binary_file(keyfile_path) if key_data and key_data.find(b"BEGIN OPENSSH PRIVATE KEY")>=0 and paramiko.__version__<"2.7": log.warn("Warning: private key '%s'", keyfile_path) log.warn(" this file seems to be using OpenSSH's own format") log.warn(" please convert it to something more standard (ie: PEM)") log.warn(" so it can be used with the paramiko backend") log.warn(" or switch to the OpenSSH backend with '--ssh=ssh'") if key: log("auth_publickey using %s as %s: %s", keyfile_path, pkey_classname, keymd5(key)) try: transport.auth_publickey(username, key) except SSHException as e: log("key '%s' rejected", keyfile_path, exc_info=True) log.info("SSH authentication using key '%s' failed:", keyfile_path) log.info(" %s", e) else: if transport.is_authenticated(): break else: log.error("Error: cannot load private key '%s'", keyfile_path) def auth_none(): log("trying none authentication") try: transport.auth_none(username) except SSHException: log("auth_none()", exc_info=True) def auth_password(): log("trying password authentication") try: transport.auth_password(username, password) except SSHException as e: log("auth_password(..)", exc_info=True) log.info("SSH password authentication failed:") log.info(" %s", getattr(e, "message", e)) def auth_interactive(): log("trying interactive authentication") class iauthhandler: def __init__(self): self.authcount = 0 def handlestuff(self, _title, _instructions, prompt_list): p = [] for pent in prompt_list: if self.authcount==0 and password: p.append(password) else: p.append(input_pass(pent[0])) self.authcount += 1 return p try: myiauthhandler = iauthhandler() transport.auth_interactive(username, myiauthhandler.handlestuff, "") except SSHException as e: log("auth_interactive(..)", exc_info=True) log.info("SSH password authentication failed:") log.info(" %s", getattr(e, "message", e)) banner = transport.get_banner() if banner: log.info("SSH server banner:") for x in banner.splitlines(): log.info(" %s", x) if paramiko_config and "auth" in paramiko_config: auth = paramiko_config.get("auth", "").split("+") AUTH_OPTIONS = ("none", "agent", "key", "password") if any(a for a in auth if a not in AUTH_OPTIONS): raise InitExit(EXIT_SSH_FAILURE, "invalid ssh authentication module specified: %s" % csv(a for a in auth if a not in AUTH_OPTIONS)) else: auth = [] if configbool("noneauthentication", NONE_AUTH): auth.append("none") if password and configbool("passwordauthentication", PASSWORD_AUTH): auth.append("password") if configbool("agentauthentication", AGENT_AUTH): auth.append("agent") # Some people do two-factor using KEY_AUTH to kick things off, so this happens first if configbool("keyauthentication", KEY_AUTH): auth.append("key") if not password and configbool("passwordauthentication", PASSWORD_AUTH): auth.append("password") #def doauth(authtype): # return authtype in auth and not transport.is_authenticated() log("starting authentication, authentication methods: %s", auth) # per the RFC we probably should do none first always and read off the supported # methods, however, the current code seems to work fine with OpenSSH while not transport.is_authenticated() and auth: a = auth.pop(0) log("auth=%s", a) if a=="none": auth_none() elif a=="agent": auth_agent() elif a=="key": auth_publickey() elif a=="password": auth_interactive() if not transport.is_authenticated(): if password: auth_password() else: tries = configint("numberofpasswordprompts", PASSWORD_RETRY) for _ in range(tries): password = input_pass("please enter the SSH password for %s@%s:" % (username, host)) if not password: break auth_password() if transport.is_authenticated(): break if not transport.is_authenticated(): transport.close() raise InitExit(EXIT_CONNECTION_FAILED, "SSH Authentication on %s failed" % host)
def do_ssh_paramiko_connect_to(transport, host, username, password, host_config=None): from paramiko import SSHException, RSAKey, PasswordRequiredException from paramiko.agent import Agent from paramiko.hostkeys import HostKeys log("do_ssh_paramiko_connect_to%s", (transport, host, username, password, host_config)) log("SSH transport %s", transport) host_key = transport.get_remote_server_key() assert host_key, "no remote server key" log("remote_server_key=%s", keymd5(host_key)) if VERIFY_HOSTKEY: host_keys = HostKeys() host_keys_filename = None KNOWN_HOSTS = get_ssh_known_hosts_files() for known_hosts in KNOWN_HOSTS: host_keys.clear() try: path = os.path.expanduser(known_hosts) if os.path.exists(path): host_keys.load(path) log("HostKeys.load(%s) successful", path) host_keys_filename = path break except IOError: log("HostKeys.load(%s)", known_hosts, exc_info=True) log("host keys=%s", host_keys) keys = host_keys.lookup(host) known_host_key = (keys or {}).get(host_key.get_name()) def keyname(): return host_key.get_name().replace("ssh-", "") if host_key == known_host_key: assert host_key log("%s host key '%s' OK for host '%s'", keyname(), keymd5(host_key), host) else: dnscheck = "" if host_config: verifyhostkeydns = host_config.get("verifyhostkeydns") if verifyhostkeydns and verifyhostkeydns.lower( ) in TRUE_OPTIONS: try: from xpra.net.sshfp import check_host_key dnscheck = check_host_key(host, host_key) except ImportError as e: log("verifyhostkeydns failed", exc_info=True) log.warn("Warning: cannot check SSHFP DNS records") log.warn(" %s", e) log.info("dnscheck=%s", dnscheck) def adddnscheckinfo(q): if dnscheck is not True: q += ["SSHFP validation failed:", dnscheck] if dnscheck is True: #DNSSEC provided a matching record log.info("found a valid SSHFP record for host %s", host) elif known_host_key: log.warn("Warning: SSH server key mismatch") qinfo = [ "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!", "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!", "Someone could be eavesdropping on you right now (man-in-the-middle attack)!", "It is also possible that a host key has just been changed.", "The fingerprint for the %s key sent by the remote host is" % keyname(), keymd5(host_key), ] adddnscheckinfo(qinfo) if VERIFY_STRICT: log.warn("Host key verification failed.") #TODO: show alert with no option to accept key qinfo += [ "Please contact your system administrator.", "Add correct host key in %s to get rid of this message.", "Offending %s key in %s" % (keyname(), host_keys_filename), "ECDSA host key for %s has changed and you have requested strict checking." % keyname(), ] sys.stderr.write(os.linesep.join(qinfo)) transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") else: assert (not keys) or (host_key.get_name() not in keys) if not keys: log.warn("Warning: unknown SSH host") else: log.warn("Warning: unknown %s SSH host key", keyname()) qinfo = [ "The authenticity of host '%s' can't be established." % (host, ), "%s key fingerprint is" % keyname(), keymd5(host_key), ] adddnscheckinfo(qinfo) if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "Unknown SSH host '%s'" % host) if ADD_KEY: try: if not host_keys_filename: #the first one is the default, #ie: ~/.ssh/known_hosts on posix host_keys_filename = os.path.expanduser(KNOWN_HOSTS[0]) log("adding %s key for host '%s' to '%s'", keyname(), host, host_keys_filename) if not os.path.exists(host_keys_filename): keys_dir = os.path.dirname(host_keys_filename) if not os.path.exists(keys_dir): log("creating keys directory '%s'", keys_dir) os.mkdir(keys_dir, 0o700) elif not os.path.isdir(keys_dir): log.warn("Warning: '%s' is not a directory") log.warn(" key not saved") if os.path.exists(keys_dir) and os.path.isdir( keys_dir): log("creating known host file '%s'", host_keys_filename) with umask_context(0o133): with open(host_keys_filename, 'a+'): pass host_keys.add(host, host_key.get_name(), host_key) host_keys.save(host_keys_filename) except OSError as e: log("failed to add key to '%s'", host_keys_filename) log.error("Error adding key to '%s'", host_keys_filename) log.error(" %s", e) except Exception as e: log.error("cannot add key", exc_info=True) def auth_agent(): agent = Agent() agent_keys = agent.get_keys() log("agent keys: %s", agent_keys) if agent_keys: for agent_key in agent_keys: log("trying ssh-agent key '%s'", keymd5(agent_key)) try: transport.auth_publickey(username, agent_key) if transport.is_authenticated(): log("authenticated using agent and key '%s'", keymd5(agent_key)) break except SSHException: log("agent key '%s' rejected", keymd5(agent_key), exc_info=True) if not transport.is_authenticated(): log.info("agent authentication failed, tried %i key%s", len(agent_keys), engs(agent_keys)) def auth_publickey(): log("trying public key authentication") for keyfile in ("id_rsa", "id_dsa"): keyfile_path = osexpand(os.path.join("~/", ".ssh", keyfile)) if not os.path.exists(keyfile_path): log("no keyfile at '%s'", keyfile_path) continue key = None try: key = RSAKey.from_private_key_file(keyfile_path) except PasswordRequiredException: log("%s keyfile requires a passphrase", keyfile_path) passphrase = input_pass("please enter the passphrase for %s:" % (keyfile_path, )) if passphrase: try: key = RSAKey.from_private_key_file( keyfile_path, passphrase) except SSHException as e: log("from_private_key_file", exc_info=True) log.info("cannot load key from file '%s':", keyfile_path) log.info(" %s", e) if key: log("auth_publickey using %s: %s", keyfile_path, keymd5(key)) try: transport.auth_publickey(username, key) except SSHException as e: log("key '%s' rejected", keyfile_path, exc_info=True) log.info("SSH authentication using key '%s' failed:", keyfile_path) log.info(" %s", e) else: if transport.is_authenticated(): break def auth_none(): log("trying none authentication") try: transport.auth_none(username) except SSHException as e: log("auth_none()", exc_info=True) def auth_password(): log("trying password authentication") try: transport.auth_password(username, password) except SSHException as e: log("auth_password(..)", exc_info=True) log.info("SSH password authentication failed: %s", e) def auth_interactive(): log("trying interactive authentication") class iauthhandler: def __init__(self): self.authcount = 0 def handlestuff(self, title, instructions, prompt_list): p = [] for pent in prompt_list: if self.authcount == 0 and password: p.append(password) else: p.append(input_pass(pent[0])) self.authcount += 1 return p try: myiauthhandler = iauthhandler() transport.auth_interactive(username, myiauthhandler.handlestuff, "") except SSHException as e: log("auth_interactive(..)", exc_info=True) log.info("SSH password authentication failed: %s", e) banner = transport.get_banner() if banner: log.info("SSH server banner:") for x in banner.splitlines(): log.info(" %s", x) log("starting authentication") # per the RFC we probably should do none first always and read off the supported # methods, however, the current code seems to work fine with OpenSSH if not transport.is_authenticated() and NONE_AUTH: auth_none() # Some people do two-factor using KEY_AUTH to kick things off, so this happens first if not transport.is_authenticated() and KEY_AUTH: auth_publickey() if not transport.is_authenticated() and PASSWORD_AUTH: auth_interactive() if not transport.is_authenticated() and PASSWORD_AUTH and password: auth_password() if not transport.is_authenticated() and AGENT_AUTH: auth_agent() if not transport.is_authenticated() and PASSWORD_AUTH and not password: for _ in range(1 + PASSWORD_RETRY): password = input_pass("please enter the SSH password for %s@%s" % (username, host)) if not password: break auth_password() if transport.is_authenticated(): break if not transport.is_authenticated(): transport.close() raise InitException("SSH Authentication on %s failed" % host)
def do_ssh_paramiko_connect_to(sock, host, port, username, password, proxy_command, remote_xpra, socket_dir, display_as_args, target): from paramiko import SSHException, Transport, Agent, RSAKey, PasswordRequiredException from paramiko.hostkeys import HostKeys transport = Transport(sock) transport.use_compression(False) log("SSH transport %s", transport) try: transport.start_client() except SSHException as e: log("start_client()", exc_info=True) raise InitException("SSH negotiation failed: %s" % e) host_key = transport.get_remote_server_key() assert host_key, "no remote server key" log("remote_server_key=%s", keymd5(host_key)) if VERIFY_HOSTKEY: host_keys = HostKeys() host_keys_filename = None KNOWN_HOSTS = get_ssh_known_hosts_files() for known_hosts in KNOWN_HOSTS: host_keys.clear() try: path = os.path.expanduser(known_hosts) if os.path.exists(path): host_keys.load(path) log("HostKeys.load(%s) successful", path) host_keys_filename = path break except IOError: log("HostKeys.load(%s)", known_hosts, exc_info=True) log("host keys=%s", host_keys) keys = host_keys.lookup(host) known_host_key = (keys or {}).get(host_key.get_name()) def keyname(): return host_key.get_name().replace("ssh-", "") if host_key==known_host_key: assert host_key log("%s host key '%s' OK for host '%s'", keyname(), keymd5(host_key), host) else: if known_host_key: log.warn("Warning: SSH server key mismatch") qinfo = [ "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!", "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!", "Someone could be eavesdropping on you right now (man-in-the-middle attack)!", "It is also possible that a host key has just been changed.", "The fingerprint for the %s key sent by the remote host is" % keyname(), keymd5(host_key), ] if VERIFY_STRICT: log.warn("Host key verification failed.") #TODO: show alert with no option to accept key qinfo += [ "Please contact your system administrator.", "Add correct host key in %s to get rid of this message.", "Offending %s key in %s" % (keyname(), host_keys_filename), "ECDSA host key for %s has changed and you have requested strict checking." % keyname(), ] sys.stderr.write(os.linesep.join(qinfo)) transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "SSH Host key has changed") else: assert (not keys) or (host_key.get_name() not in keys) if not keys: log.warn("Warning: unknown SSH host") else: log.warn("Warning: unknown %s SSH host key", keyname()) qinfo = [ "The authenticity of host '%s' can't be established." % (host,), "%s key fingerprint is" % keyname(), keymd5(host_key), ] if not confirm_key(qinfo): transport.close() raise InitExit(EXIT_SSH_KEY_FAILURE, "Unknown SSH host '%s'" % host) if ADD_KEY: try: if not host_keys_filename: #the first one is the default, #ie: ~/.ssh/known_hosts on posix host_keys_filename = os.path.expanduser(KNOWN_HOSTS[0]) log("adding %s key for host '%s' to '%s'", keyname(), host, host_keys_filename) if not os.path.exists(host_keys_filename): keys_dir = os.path.dirname(host_keys_filename) if not os.path.exists(keys_dir): log("creating keys directory '%s'", keys_dir) os.mkdir(keys_dir, 0o700) elif not os.path.isdir(keys_dir): log.warn("Warning: '%s' is not a directory") log.warn(" key not saved") if os.path.exists(keys_dir) and os.path.isdir(keys_dir): log("creating known host file '%s'", host_keys_filename) with umask_context(0o133): with open(host_keys_filename, 'a+'): pass host_keys.add(host, host_key.get_name(), host_key) host_keys.save(host_keys_filename) except OSError as e: log("failed to add key to '%s'", host_keys_filename) log.error("Error adding key to '%s'", host_keys_filename) log.error(" %s", e) except Exception as e: log.error("cannot add key", exc_info=True) def auth_agent(): agent = Agent() agent_keys = agent.get_keys() log("agent keys: %s", agent_keys) if agent_keys: for agent_key in agent_keys: log("trying ssh-agent key '%s'", keymd5(agent_key)) try: transport.auth_publickey(username, agent_key) if transport.is_authenticated(): log("authenticated using agent and key '%s'", keymd5(agent_key)) break except SSHException: log("agent key '%s' rejected", keymd5(agent_key), exc_info=True) if not transport.is_authenticated(): log.info("agent authentication failed, tried %i key%s", len(agent_keys), engs(agent_keys)) def auth_publickey(): log("trying public key authentication") for keyfile in ("id_rsa", "id_dsa"): keyfile_path = osexpand(os.path.join("~/", ".ssh", keyfile)) if not os.path.exists(keyfile_path): log("no keyfile at '%s'", keyfile_path) continue key = None try: key = RSAKey.from_private_key_file(keyfile_path) except PasswordRequiredException: log("%s keyfile requires a passphrase", keyfile_path) passphrase = input_pass("please enter the passphrase for %s:" % (keyfile_path,)) if passphrase: try: key = RSAKey.from_private_key_file(keyfile_path, passphrase) except SSHException as e: log("from_private_key_file", exc_info=True) log.info("cannot load key from file '%s':", keyfile_path) log.info(" %s", e) if key: log("auth_publickey using %s: %s", keyfile_path, keymd5(key)) try: transport.auth_publickey(username, key) except SSHException as e: log("key '%s' rejected", keyfile_path, exc_info=True) log.info("SSH authentication using key '%s' failed:", keyfile_path) log.info(" %s", e) else: if transport.is_authenticated(): break def auth_none(): log("trying none authentication") try: transport.auth_none(username) except SSHException as e: log("auth_none()", exc_info=True) def auth_password(): log("trying password authentication") try: transport.auth_password(username, password) except SSHException as e: log("auth_password(..)", exc_info=True) log.info("SSH password authentication failed: %s", e) banner = transport.get_banner() if banner: log.info("SSH server banner:") for x in banner.splitlines(): log.info(" %s", x) log("starting authentication") if not transport.is_authenticated() and NONE_AUTH: auth_none() if not transport.is_authenticated() and PASSWORD_AUTH and password: auth_password() if not transport.is_authenticated() and AGENT_AUTH: auth_agent() if not transport.is_authenticated() and KEY_AUTH: auth_publickey() if not transport.is_authenticated() and PASSWORD_AUTH and not password: for _ in range(1+PASSWORD_RETRY): password = input_pass("please enter the SSH password for %s@%s" % (username, host)) if not password: break auth_password() if transport.is_authenticated(): break if not transport.is_authenticated(): transport.close() raise InitException("SSH Authentication failed") assert len(remote_xpra)>0 log("will try to run xpra from: %s", remote_xpra) for xpra_cmd in remote_xpra: try: chan = transport.open_session(window_size=None, max_packet_size=0, timeout=60) chan.set_name("find %s" % xpra_cmd) except SSHException as e: log("open_session", exc_info=True) raise InitException("failed to open SSH session: %s" % e) cmd = "which %s" % xpra_cmd log("exec_command('%s')", cmd) chan.exec_command(cmd) #poll until the command terminates: start = monotonic_time() while not chan.exit_status_ready(): if monotonic_time()-start>10: chan.close() raise InitException("SSH test command '%s' timed out" % cmd) log("exit status is not ready yet, sleeping") time.sleep(0.01) r = chan.recv_exit_status() log("exec_command('%s')=%s", cmd, r) chan.close() if r!=0: continue cmd = xpra_cmd + " " + " ".join(shellquote(x) for x in proxy_command) if socket_dir: cmd += " \"--socket-dir=%s\"" % socket_dir if display_as_args: cmd += " " cmd += " ".join(shellquote(x) for x in display_as_args) log("cmd(%s, %s)=%s", proxy_command, display_as_args, cmd) #see https://github.com/paramiko/paramiko/issues/175 #WINDOW_SIZE = 2097152 log("trying to open SSH session, window-size=%i, timeout=%i", WINDOW_SIZE, TIMEOUT) try: chan = transport.open_session(window_size=WINDOW_SIZE, max_packet_size=0, timeout=TIMEOUT) chan.set_name("run-xpra") except SSHException as e: log("open_session", exc_info=True) raise InitException("failed to open SSH session: %s" % e) else: log("channel exec_command(%s)" % cmd) chan.exec_command(cmd) info = { "host" : host, "port" : port, } conn = SSHSocketConnection(chan, sock, target, info) conn.timeout = SOCKET_TIMEOUT conn.start_stderr_reader() child = None conn.process = (child, "ssh", cmd) return conn raise Exception("all SSH remote proxy commands have failed")