Ejemplo n.º 1
0
 def test_implementation_refers_to_public_algo_lists(self):
     t = Transport(
         sock=Mock(),
         disabled_algorithms={
             "ciphers": ["aes128-cbc"],
             "macs": ["hmac-md5"],
             "keys": ["ssh-dss"],
             "kex": ["diffie-hellman-group14-sha256"],
             "compression": ["zlib"],
         },
     )
     # Enable compression cuz otherwise disabling one option for it makes no
     # sense...
     t.use_compression(True)
     # Effectively a random spot check, but kex init touches most/all of the
     # algorithm lists so it's a good spot.
     t._send_message = Mock()
     t._send_kex_init()
     # Cribbed from Transport._parse_kex_init, which didn't feel worth
     # refactoring given all the vars involved :(
     m = t._send_message.call_args[0][0]
     m.rewind()
     m.get_byte()  # the msg type
     m.get_bytes(16)  # cookie, discarded
     kexen = m.get_list()
     server_keys = m.get_list()
     ciphers = m.get_list()
     m.get_list()
     macs = m.get_list()
     m.get_list()
     compressions = m.get_list()
     # OK, now we can actually check that our disabled algos were not
     # included (as this message includes the full lists)
     assert "aes128-cbc" not in ciphers
     assert "hmac-md5" not in macs
     assert "ssh-dss" not in server_keys
     assert "diffie-hellman-group14-sha256" not in kexen
     assert "zlib" not in compressions
Ejemplo n.º 2
0
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")