コード例 #1
0
def generate_new_keypair(self, settings):
    """
    Calls :func:`openssh_generate_new_keypair` or
    :func:`dropbear_generate_new_keypair` depending on what's available on the
    system.
    """
    self.ssh_log.debug('generate_new_keypair()')
    users_ssh_dir = get_ssh_dir(self)
    name = 'id_ecdsa'
    keytype = None
    bits = None
    passphrase = ''
    comment = ''
    if 'name' in settings:
        name = settings['name']
    if 'keytype' in settings:
        keytype = settings['keytype']
    if 'bits' in settings:
        bits = settings['bits']
    if 'passphrase' in settings:
        passphrase = settings['passphrase']
    if 'comment' in settings:
        comment = settings['comment']
    log_metadata = {
        'name': name,
        'keytype': keytype,
        'bits': bits,
        'comment': comment
    }
    self.ssh_log.info("Generating new SSH keypair", metadata=log_metadata)
    if which('ssh-keygen'): # Prefer OpenSSH
        openssh_generate_new_keypair(
            self,
            name, # Name to use when generating the keypair
            users_ssh_dir, # Path to save it
            keytype=keytype,
            passphrase=passphrase,
            bits=bits,
            comment=comment
        )
    elif which('dropbearkey'):
        dropbear_generate_new_keypair(self,
            name, # Name to use when generating the keypair
            users_ssh_dir, # Path to save it
            keytype=keytype,
            passphrase=passphrase,
            bits=bits,
            comment=comment)
コード例 #2
0
ファイル: ssh.py プロジェクト: jumping/GateOne
def generate_new_keypair(self, settings):
    """
    Calls :func:`openssh_generate_new_keypair` or
    :func:`dropbear_generate_new_keypair` depending on what's available on the
    system.
    """
    self.ssh_log.debug('generate_new_keypair()')
    users_ssh_dir = get_ssh_dir(self)
    name = 'id_ecdsa'
    keytype = None
    bits = None
    passphrase = ''
    comment = ''
    if 'name' in settings:
        name = settings['name']
    if 'keytype' in settings:
        keytype = settings['keytype']
    if 'bits' in settings:
        bits = settings['bits']
    if 'passphrase' in settings:
        passphrase = settings['passphrase']
    if 'comment' in settings:
        comment = settings['comment']
    log_metadata = {
        'name': name,
        'keytype': keytype,
        'bits': bits,
        'comment': comment
    }
    self.ssh_log.info("Generating new SSH keypair", metadata=log_metadata)
    if which('ssh-keygen'): # Prefer OpenSSH
        openssh_generate_new_keypair(
            self,
            name, # Name to use when generating the keypair
            users_ssh_dir, # Path to save it
            keytype=keytype,
            passphrase=passphrase,
            bits=bits,
            comment=comment
        )
    elif which('dropbearkey'):
        dropbear_generate_new_keypair(self,
            name, # Name to use when generating the keypair
            users_ssh_dir, # Path to save it
            keytype=keytype,
            passphrase=passphrase,
            bits=bits,
            comment=comment)
コード例 #3
0
ファイル: ssh.py プロジェクト: truth/GateOne
def generate_new_keypair(self, settings):
    """
    Calls :func:`openssh_generate_new_keypair` or
    :func:`dropbear_generate_new_keypair` depending on what's available on the
    system.
    """
    self.ssh_log.debug("generate_new_keypair()")
    users_ssh_dir = get_ssh_dir(self)
    name = "id_ecdsa"
    keytype = None
    bits = None
    passphrase = ""
    comment = ""
    if "name" in settings:
        name = settings["name"]
    if "keytype" in settings:
        keytype = settings["keytype"]
    if "bits" in settings:
        bits = settings["bits"]
    if "passphrase" in settings:
        passphrase = settings["passphrase"]
    if "comment" in settings:
        comment = settings["comment"]
    log_metadata = {"name": name, "keytype": keytype, "bits": bits, "comment": comment}
    self.ssh_log.info("Generating new SSH keypair", metadata=log_metadata)
    if which("ssh-keygen"):  # Prefer OpenSSH
        openssh_generate_new_keypair(
            self,
            name,  # Name to use when generating the keypair
            users_ssh_dir,  # Path to save it
            keytype=keytype,
            passphrase=passphrase,
            bits=bits,
            comment=comment,
        )
    elif which("dropbearkey"):
        dropbear_generate_new_keypair(
            self,
            name,  # Name to use when generating the keypair
            users_ssh_dir,  # Path to save it
            keytype=keytype,
            passphrase=passphrase,
            bits=bits,
            comment=comment,
        )
コード例 #4
0
def openssh_generate_public_key(self, path, passphrase=None, settings=None):
    """
    Generates a public key from the given private key at *path*.  If a
    *passphrase* is provided, it will be used to generate the public key (if
    necessary).
    """
    self.ssh_log.debug('openssh_generate_public_key()')
    ssh_keygen_path = which('ssh-keygen')
    pubkey_path = "%s.pub" % path
    command = (
        "%s "  # Path to ssh-keygen
        "-f '%s' "  # Key path
        "-y "  # Output public key to stdout
        "2>&1 "  # Redirect stderr to stdout so we can catch failures
        "> '%s'"  # Redirect stdout to the public key path
        % (ssh_keygen_path, path, pubkey_path))
    import termio
    m = termio.Multiplex(command)

    def request_passphrase(*args, **kwargs):
        "Called if this key requires a passphrase.  Ask the client to provide"
        message = {'terminal:sshjs_ask_passphrase': settings}
        self.write_message(message)

    def bad_passphrase(m_instance, match):
        "Called if the user entered a bad passphrase"
        settings['bad'] = True
        request_passphrase()

    if passphrase:
        m.expect('^Enter passphrase',
                 "%s\n" % passphrase,
                 optional=True,
                 preprocess=False,
                 timeout=5)
        m.expect('^load failed',
                 bad_passphrase,
                 optional=True,
                 preprocess=False,
                 timeout=5)
    elif settings:
        m.expect('^Enter passphrase',
                 request_passphrase,
                 optional=True,
                 preprocess=False,
                 timeout=5)

    def atexit(child, exitstatus):
        "Raises an SSHKeygenException if the *exitstatus* isn't 0"
        if exitstatus != 0:
            print(m.dump)
            raise SSHKeygenException(
                _("Error generating public key from private key at %s" % path))

    m.spawn(exitfunc=atexit)
コード例 #5
0
ファイル: ssh.py プロジェクト: jumping/GateOne
def open_sub_channel(self, term):
    """
    Opens a sub-channel of communication by executing a new shell on the SSH
    server using OpenSSH's `Master mode <http://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing>`_
    capability (it spawns a new slave) and returns the resulting
    :class:`termio.Multiplex` instance.  If a slave has already been opened for
    this purpose it will re-use the existing channel.
    """
    term = int(term)
    global OPEN_SUBCHANNELS
    if term in OPEN_SUBCHANNELS and OPEN_SUBCHANNELS[term].isalive():
        # Use existing sub-channel (much faster this way)
        return OPEN_SUBCHANNELS[term]
    self.ssh_log.info("Opening SSH sub-channel", metadata={'term': term})
    # NOTE: When connecting a slave via ssh you can't tell it to execute a
    # command like you normally can (e.g. 'ssh user@host <some command>').  This
    # is why we're using the termio.Multiplex.expect() functionality below...
    session = self.ws.session
    session_dir = self.ws.settings['session_dir']
    session_path = os.path.join(session_dir, session)
    if not session_path:
        raise SSHMultiplexingException(_(
            "SSH Plugin: Unable to open slave sub-channel."))
    socket_path = self.loc_terms[term]['ssh_socket']
    # Interesting: When using an existing socket you don't need to give it all
    # the same options as you used to open it but you still need to give it
    # *something* in place of the hostname or it will report a syntax error and
    # print out the help.  So that's why I've put 'go_ssh_remote_cmd' below.
    # ...but I could have just used 'foo' :)
    if not socket_path:
        raise SSHMultiplexingException(_(
            "SSH Plugin: Unable to open slave sub-channel."))
    users_ssh_dir = get_ssh_dir(self)
    ssh_config_path = os.path.join(users_ssh_dir, 'config')
    if not os.path.exists(ssh_config_path):
        # Create it (an empty one so ssh doesn't error out)
        with open(ssh_config_path, 'w') as f:
            f.write('\n')
    # Hopefully 'go_ssh_remote_cmd' will be a clear enough indication of
    # what is going on by anyone that has to review the logs...
    ssh = which('ssh')
    ssh_command = "%s -x -S'%s' -F'%s' go_ssh_remote_cmd" % (
        ssh, socket_path, ssh_config_path)
    OPEN_SUBCHANNELS[term] = m = self.new_multiplex(
        ssh_command, "%s (sub)" % term)
    # Using huge numbers here so we don't miss much (if anything) if the user
    # executes something like "ps -ef".
    m.spawn(rows=100, cols=200) # Hopefully 100/200 lines/cols is enough
    # ...if it isn't, well, that's not really what this is for :)
    # Set the term title so it gets a proper name in the logs
    m.writeline(u'echo -e "\\033]0;Term %s sub-channel\\007"' % term)
    return m
コード例 #6
0
def open_sub_channel(self, term):
    """
    Opens a sub-channel of communication by executing a new shell on the SSH
    server using OpenSSH's `Master mode <http://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing>`_
    capability (it spawns a new slave) and returns the resulting
    :class:`termio.Multiplex` instance.  If a slave has already been opened for
    this purpose it will re-use the existing channel.
    """
    term = int(term)
    global OPEN_SUBCHANNELS
    if term in OPEN_SUBCHANNELS and OPEN_SUBCHANNELS[term].isalive():
        # Use existing sub-channel (much faster this way)
        return OPEN_SUBCHANNELS[term]
    self.ssh_log.info("Opening SSH sub-channel", metadata={'term': term})
    # NOTE: When connecting a slave via ssh you can't tell it to execute a
    # command like you normally can (e.g. 'ssh user@host <some command>').  This
    # is why we're using the termio.Multiplex.expect() functionality below...
    session = self.ws.session
    session_dir = self.ws.settings['session_dir']
    session_path = os.path.join(session_dir, session)
    if not session_path:
        raise SSHMultiplexingException(_(
            "SSH Plugin: Unable to open slave sub-channel."))
    socket_path = self.loc_terms[term]['ssh_socket']
    # Interesting: When using an existing socket you don't need to give it all
    # the same options as you used to open it but you still need to give it
    # *something* in place of the hostname or it will report a syntax error and
    # print out the help.  So that's why I've put 'go_ssh_remote_cmd' below.
    # ...but I could have just used 'foo' :)
    if not socket_path:
        raise SSHMultiplexingException(_(
            "SSH Plugin: Unable to open slave sub-channel."))
    users_ssh_dir = get_ssh_dir(self)
    ssh_config_path = os.path.join(users_ssh_dir, 'config')
    if not os.path.exists(ssh_config_path):
        # Create it (an empty one so ssh doesn't error out)
        with open(ssh_config_path, 'w') as f:
            f.write('\n')
    # Hopefully 'go_ssh_remote_cmd' will be a clear enough indication of
    # what is going on by anyone that has to review the logs...
    ssh = which('ssh')
    ssh_command = "%s -x -S'%s' -F'%s' go_ssh_remote_cmd" % (
        ssh, socket_path, ssh_config_path)
    OPEN_SUBCHANNELS[term] = m = self.new_multiplex(
        ssh_command, "%s (sub)" % term)
    # Using huge numbers here so we don't miss much (if anything) if the user
    # executes something like "ps -ef".
    m.spawn(rows=100, cols=200) # Hopefully 100/200 lines/cols is enough
    # ...if it isn't, well, that's not really what this is for :)
    # Set the term title so it gets a proper name in the logs
    m.writeline(u'echo -e "\\033]0;Term %s sub-channel\\007"' % term)
    return m
コード例 #7
0
def get_host_fingerprint(self, settings):
    """
    Returns a the hash of the given host's public key by making a remote
    connection to the server (not just by looking at known_hosts).
    """
    out_dict = {}
    if 'port' not in settings:
        port = 22
    else:
        port = settings['port']
    if 'host' not in settings:
        out_dict['result'] = _("Error:  You must supply a 'host'.")
        message = {'terminal:sshjs_display_fingerprint': out_dict}
        self.write_message(message)
    else:
        host = settings['host']
    self.ssh_log.debug("get_host_fingerprint(%s:%s)" % (host, port),
                       metadata={
                           'host': host,
                           'port': port
                       })
    out_dict.update({'result': 'Success', 'host': host, 'fingerprint': None})
    ssh = which('ssh')
    command = "%s -p %s -oUserKnownHostsFile=none -F. %s" % (ssh, port, host)
    m = self.new_multiplex(
        command, 'get_host_key',
        logging=False)  # Logging is false so we don't make tons of silly logs

    def grab_fingerprint(m_instance, match):
        out_dict['fingerprint'] = match.splitlines()[-1][:-1]
        m_instance.terminate()
        message = {'terminal:sshjs_display_fingerprint': out_dict}
        self.write_message(message)
        del m_instance

    def errorback(m_instance):
        leftovers = [a.rstrip() for a in m_instance.dump() if a.strip()]
        out_dict['result'] = _(
            "Error: Could not determine the fingerprint of %s:%s... '%s'" %
            (host, port, "\n".join(leftovers)))
        m_instance.terminate()  # Don't leave stuff hanging around!
        message = {'terminal:sshjs_display_fingerprint': out_dict}
        self.write_message(message)
        del m_instance

    # "The authenticity of host 'localhost (127.0.0.1)' can't be established.\r\nECDSA key fingerprint is 83:f5:b1:f1:d3:8c:b8:fe:d3:be:e5:dd:95:a5:ba:73.\r\nAre you sure you want to continue connecting (yes/no)? "
    m.expect('\n.+fingerprint .+\n',
             grab_fingerprint,
             errorback=errorback,
             preprocess=False)
    m.spawn()
コード例 #8
0
ファイル: ssh.py プロジェクト: jumping/GateOne
def get_host_fingerprint(self, settings):
    """
    Returns a the hash of the given host's public key by making a remote
    connection to the server (not just by looking at known_hosts).
    """
    out_dict = {}
    if 'port' not in settings:
        port = 22
    else:
        port = settings['port']
    if 'host' not in settings:
        out_dict['result'] = _("Error:  You must supply a 'host'.")
        message = {'terminal:sshjs_display_fingerprint': out_dict}
        self.write_message(message)
    else:
        host = settings['host']
    self.ssh_log.debug(
        "get_host_fingerprint(%s:%s)" % (host, port),
        metadata={'host': host, 'port': port})
    out_dict.update({
        'result': 'Success',
        'host': host,
        'fingerprint': None
    })
    ssh = which('ssh')
    command = "%s -p %s -oUserKnownHostsFile=none -F. %s" % (ssh, port, host)
    m = self.new_multiplex(
        command,
        'get_host_key',
        logging=False) # Logging is false so we don't make tons of silly logs
    def grab_fingerprint(m_instance, match):
        out_dict['fingerprint'] = match.splitlines()[-1][:-1]
        m_instance.terminate()
        message = {'terminal:sshjs_display_fingerprint': out_dict}
        self.write_message(message)
        del m_instance
    def errorback(m_instance):
        leftovers = [a.rstrip() for a in m_instance.dump() if a.strip()]
        out_dict['result'] = _(
            "Error: Could not determine the fingerprint of %s:%s... '%s'"
            % (host, port, "\n".join(leftovers)))
        m_instance.terminate() # Don't leave stuff hanging around!
        message = {'terminal:sshjs_display_fingerprint': out_dict}
        self.write_message(message)
        del m_instance
    # "The authenticity of host 'localhost (127.0.0.1)' can't be established.\r\nECDSA key fingerprint is 83:f5:b1:f1:d3:8c:b8:fe:d3:be:e5:dd:95:a5:ba:73.\r\nAre you sure you want to continue connecting (yes/no)? "
    m.expect('\n.+fingerprint .+\n',
        grab_fingerprint, errorback=errorback, preprocess=False)
    m.spawn()
コード例 #9
0
ファイル: ssh.py プロジェクト: truth/GateOne
def openssh_generate_public_key(self, path, passphrase=None, settings=None):
    """
    Generates a public key from the given private key at *path*.  If a
    *passphrase* is provided, it will be used to generate the public key (if
    necessary).
    """
    self.ssh_log.debug("openssh_generate_public_key()")
    ssh_keygen_path = which("ssh-keygen")
    pubkey_path = "%s.pub" % path
    command = (
        "%s "  # Path to ssh-keygen
        "-f '%s' "  # Key path
        "-y "  # Output public key to stdout
        "2>&1 "  # Redirect stderr to stdout so we can catch failures
        "> '%s'" % (ssh_keygen_path, path, pubkey_path)  # Redirect stdout to the public key path
    )
    import termio

    m = termio.Multiplex(command)

    def request_passphrase(*args, **kwargs):
        "Called if this key requires a passphrase.  Ask the client to provide"
        message = {"terminal:sshjs_ask_passphrase": settings}
        self.write_message(message)

    def bad_passphrase(m_instance, match):
        "Called if the user entered a bad passphrase"
        settings["bad"] = True
        request_passphrase()

    if passphrase:
        m.expect("^Enter passphrase", "%s\n" % passphrase, optional=True, preprocess=False, timeout=5)
        m.expect("^load failed", bad_passphrase, optional=True, preprocess=False, timeout=5)
    elif settings:
        m.expect("^Enter passphrase", request_passphrase, optional=True, preprocess=False, timeout=5)

    def atexit(child, exitstatus):
        "Raises an SSHKeygenException if the *exitstatus* isn't 0"
        if exitstatus != 0:
            print(m.dump)
            raise SSHKeygenException(_("Error generating public key from private key at %s" % path))

    m.spawn(exitfunc=atexit)
コード例 #10
0
ファイル: ssh.py プロジェクト: truth/GateOne
def get_identities(self, *anything):
    """
    Sends a message to the client with a list of the identities stored on the
    server for the current user.

    *anything* is just there because the client needs to send *something* along
    with the 'action'.
    """
    self.ssh_log.debug("get_identities()")
    out_dict = {"result": "Success"}
    users_ssh_dir = get_ssh_dir(self)
    out_dict["identities"] = []
    ssh_keygen_path = which("ssh-keygen")
    # TODO:  Switch this from using ssh-keygen to determine the keytype to using the string inside the public key.
    try:
        if os.path.exists(users_ssh_dir):
            ssh_files = os.listdir(users_ssh_dir)
            for f in ssh_files:
                if f.endswith(".pub"):
                    # Double-check there's also a private key...
                    identity = f[:-4]  # Will be the same name minus '.pub'
                    if identity in ssh_files:
                        id_path = os.path.join(users_ssh_dir, identity)
                        pub_key_path = os.path.join(users_ssh_dir, f)
                        public_key_contents = open(pub_key_path).read()
                        comment = " ".join(public_key_contents.split(" ")[2:])
                        if public_key_contents.startswith("ecdsa"):
                            keytype = "ECDSA"
                        elif public_key_contents.startswith("ssh-dss"):
                            keytype = "DSA"
                        elif public_key_contents.startswith("ssh-rsa"):
                            keytype = "RSA"
                        else:
                            keytype = "Unknown"
                        keygen_cmd = "'%s' -vlf '%s'" % (ssh_keygen_path, id_path)
                        retcode, key_info = shell_command(keygen_cmd)
                        # This will just wind up as an empty string if the
                        # version of ssh doesn't support randomart:
                        randomart = "\n".join(key_info.splitlines()[1:])
                        bits = key_info.split()[0]
                        fingerprint = key_info.split()[1]
                        retcode, bubblebabble = shell_command("'%s' -Bf '%s'" % (ssh_keygen_path, id_path))
                        bubblebabble = bubblebabble.split()[1]
                        certinfo = ""
                        cert_path = "'%s-cert.pub'" % id_path
                        if os.path.exists(cert_path):
                            retcode, certinfo = shell_command("'%s' -Lf '%s'" % (ssh_keygen_path, cert_path))
                        certinfo = " ".join(certinfo.split(" ")[1:])
                        fixed_certinfo = ""
                        for i, line in enumerate(certinfo.splitlines()):
                            if i == 0:
                                line = line.lstrip()
                            fixed_certinfo += line.replace("    ", " ")
                            fixed_certinfo += "\n"
                        id_obj = {
                            "name": identity,
                            "public": public_key_contents,
                            "keytype": keytype,
                            "bubblebabble": bubblebabble,
                            "fingerprint": fingerprint,
                            "randomart": randomart,
                            "certinfo": fixed_certinfo,
                            "bits": bits,
                            "comment": comment.rstrip(),
                        }
                        out_dict["identities"].append(id_obj)
        # Figure out which identities are defaults
        default_ids = []
        default_ids_exists = False
        users_ssh_dir = get_ssh_dir(self)
        default_ids_path = os.path.join(users_ssh_dir, ".default_ids")
        if os.path.exists(default_ids_path):
            default_ids_exists = True
            with open(default_ids_path) as f:
                default_ids = f.read().splitlines()  # Why not readlines()? \n
        # Convert any absolute paths inside default_ids to just the short names
        default_ids = [os.path.split(a)[1] for a in default_ids]
        if default_ids_exists:
            for i, id_obj in enumerate(out_dict["identities"]):
                if id_obj["name"] in default_ids:
                    out_dict["identities"][i]["default"] = True
                else:
                    out_dict["identities"][i]["default"] = False
    except Exception as e:
        error_msg = _("Error getting identities: %s" % e)
        self.ssh_log.error(error_msg)
        out_dict["result"] = error_msg
    message = {"terminal:sshjs_identities_list": out_dict}
    self.write_message(message)
コード例 #11
0
ファイル: ssh.py プロジェクト: truth/GateOne
def openssh_generate_new_keypair(self, name, path, keytype=None, passphrase="", bits=None, comment=""):
    """
    Generates a new private and public key pair--stored in the user's directory
    using the given *name* and other optional parameters (using OpenSSH).

    If *keytype* is given, it must be one of "ecdsa", "rsa" or "dsa" (case
    insensitive).  If *keytype* is "rsa" or "ecdsa", *bits* may be specified to
    specify the size of the key.

    .. note:: Defaults to generating a 521-byte ecdsa key if OpenSSH is version 5.7+. Otherwise a 2048-bit rsa key will be used.
    """
    self.ssh_log.debug("openssh_generate_new_keypair()")
    openssh_version = shell_command("ssh -V")[1]
    ssh_major_version = int(openssh_version.split()[0].split("_")[1].split(".")[0])
    key_path = os.path.join(path, name)
    ssh_minor_version = int(openssh_version.split()[0].split("_")[1].split(".")[1][0])
    ssh_version = "%s.%s" % (ssh_major_version, ssh_minor_version)
    ssh_version = float(ssh_version)
    if not keytype:
        if ssh_version >= 5.7:
            keytype = "ecdsa"
        else:
            keytype = "rsa"
    else:
        keytype = keytype.lower()
    if not bits and keytype == "ecdsa":
        bits = 521  # Not a typo: five-hundred-twenty-one bits
    elif not bits and keytype == "rsa":
        bits = 2048
    if not passphrase:  # This just makes sure False and None end up as ''
        passphrase = ""
    hostname = os.uname()[1]
    if not comment:
        now = datetime.now().isoformat()
        comment = "Generated by Gate One on %s %s" % (hostname, now)
    ssh_keygen_path = which("ssh-keygen")
    command = (
        "%s "  # Path to ssh-keygen
        "-b %s "  # bits
        "-t %s "  # keytype
        "-C '%s' "  # comment
        "-f '%s'" % (ssh_keygen_path, bits, keytype, comment, key_path)  # Key path
    )
    self.ssh_log.debug("Keygen command: %s" % command)
    m = self.new_multiplex(command, "gen_ssh_keypair")
    call_errorback = partial(errorback, self)
    m.expect("^Overwrite.*", overwrite, optional=True, preprocess=False, timeout=10)
    passphrase_handler = partial(enter_passphrase, passphrase)
    m.expect("^Enter passphrase", passphrase_handler, errorback=call_errorback, preprocess=False, timeout=10)
    m.expect("^Enter same passphrase again", passphrase_handler, errorback=call_errorback, preprocess=False, timeout=10)
    finalize = partial(finished, self)
    # The regex below captures the md5 fingerprint which tells us the
    # operation was successful.
    m.expect(
        "(([0-9a-f][0-9a-f]\:){15}[0-9a-f][0-9a-f])",
        finalize,
        errorback=call_errorback,
        preprocess=False,
        timeout=15,  # Key generation can take a little while
    )
    m.spawn()
コード例 #12
0
ファイル: ssh.py プロジェクト: jumping/GateOne
def get_identities(self, *anything):
    """
    Sends a message to the client with a list of the identities stored on the
    server for the current user.

    *anything* is just there because the client needs to send *something* along
    with the 'action'.
    """
    self.ssh_log.debug('get_identities()')
    out_dict = {'result': 'Success'}
    users_ssh_dir = get_ssh_dir(self)
    out_dict['identities'] = []
    ssh_keygen_path = which('ssh-keygen')
    # TODO:  Switch this from using ssh-keygen to determine the keytype to using the string inside the public key.
    try:
        if os.path.exists(users_ssh_dir):
            ssh_files = os.listdir(users_ssh_dir)
            for f in ssh_files:
                if f.endswith('.pub'):
                    # Double-check there's also a private key...
                    identity = f[:-4] # Will be the same name minus '.pub'
                    if identity in ssh_files:
                        id_path = os.path.join(users_ssh_dir, identity)
                        pub_key_path = os.path.join(users_ssh_dir, f)
                        public_key_contents = open(pub_key_path).read()
                        comment = ' '.join(public_key_contents.split(' ')[2:])
                        if public_key_contents.startswith('ecdsa'):
                            keytype = 'ECDSA'
                        elif public_key_contents.startswith('ssh-dss'):
                            keytype = 'DSA'
                        elif public_key_contents.startswith('ssh-rsa'):
                            keytype = 'RSA'
                        else:
                            keytype = 'Unknown'
                        keygen_cmd = "'%s' -vlf '%s'" % (
                            ssh_keygen_path, id_path)
                        retcode, key_info = shell_command(keygen_cmd)
                        # This will just wind up as an empty string if the
                        # version of ssh doesn't support randomart:
                        randomart = '\n'.join(key_info.splitlines()[1:])
                        bits = key_info.split()[0]
                        fingerprint = key_info.split()[1]
                        retcode, bubblebabble = shell_command(
                            "'%s' -Bf '%s'" % (ssh_keygen_path, id_path))
                        bubblebabble = bubblebabble.split()[1]
                        certinfo = ''
                        cert_path = "'%s-cert.pub'" % id_path
                        if os.path.exists(cert_path):
                            retcode, certinfo = shell_command(
                            "'%s' -Lf '%s'" % (ssh_keygen_path, cert_path))
                        certinfo = ' '.join(certinfo.split(' ')[1:])
                        fixed_certinfo = ''
                        for i, line in enumerate(certinfo.splitlines()):
                            if i == 0:
                                line = line.lstrip()
                            fixed_certinfo += line.replace('    ', ' ')
                            fixed_certinfo += '\n'
                        id_obj = {
                            'name': identity,
                            'public': public_key_contents,
                            'keytype': keytype,
                            'bubblebabble': bubblebabble,
                            'fingerprint': fingerprint,
                            'randomart': randomart,
                            'certinfo': fixed_certinfo,
                            'bits': bits,
                            'comment': comment.rstrip(),
                        }
                        out_dict['identities'].append(id_obj)
        # Figure out which identities are defaults
        default_ids = []
        default_ids_exists = False
        users_ssh_dir = get_ssh_dir(self)
        default_ids_path = os.path.join(users_ssh_dir, '.default_ids')
        if os.path.exists(default_ids_path):
            default_ids_exists = True
            with open(default_ids_path) as f:
                default_ids = f.read().splitlines() # Why not readlines()? \n
        # Convert any absolute paths inside default_ids to just the short names
        default_ids = [os.path.split(a)[1] for a in default_ids]
        if default_ids_exists:
            for i, id_obj in enumerate(out_dict['identities']):
                if id_obj['name'] in default_ids:
                    out_dict['identities'][i]['default'] = True
                else:
                    out_dict['identities'][i]['default'] = False
    except Exception as e:
        error_msg = _("Error getting identities: %s" % e)
        self.ssh_log.error(error_msg)
        out_dict['result'] = error_msg
    message = {
        'terminal:sshjs_identities_list': out_dict
    }
    self.write_message(message)
コード例 #13
0
def get_identities(self, *anything):
    """
    Sends a message to the client with a list of the identities stored on the
    server for the current user.

    *anything* is just there because the client needs to send *something* along
    with the 'action'.
    """
    self.ssh_log.debug('get_identities()')
    out_dict = {'result': 'Success'}
    users_ssh_dir = get_ssh_dir(self)
    out_dict['identities'] = []
    ssh_keygen_path = which('ssh-keygen')
    # TODO:  Switch this from using ssh-keygen to determine the keytype to using the string inside the public key.
    try:
        if os.path.exists(users_ssh_dir):
            ssh_files = os.listdir(users_ssh_dir)
            for f in ssh_files:
                if f.endswith('.pub'):
                    # Double-check there's also a private key...
                    identity = f[:-4] # Will be the same name minus '.pub'
                    if identity in ssh_files:
                        id_path = os.path.join(users_ssh_dir, identity)
                        pub_key_path = os.path.join(users_ssh_dir, f)
                        public_key_contents = open(pub_key_path).read()
                        comment = ' '.join(public_key_contents.split(' ')[2:])
                        if public_key_contents.startswith('ecdsa'):
                            keytype = 'ECDSA'
                        elif public_key_contents.startswith('ssh-dss'):
                            keytype = 'DSA'
                        elif public_key_contents.startswith('ssh-rsa'):
                            keytype = 'RSA'
                        else:
                            keytype = 'Unknown'
                        keygen_cmd = "'%s' -vlf '%s'" % (
                            ssh_keygen_path, id_path)
                        retcode, key_info = shell_command(keygen_cmd)
                        # This will just wind up as an empty string if the
                        # version of ssh doesn't support randomart:
                        randomart = '\n'.join(key_info.splitlines()[1:])
                        bits = key_info.split()[0]
                        fingerprint = key_info.split()[1]
                        retcode, bubblebabble = shell_command(
                            "'%s' -Bf '%s'" % (ssh_keygen_path, id_path))
                        bubblebabble = bubblebabble.split()[1]
                        certinfo = ''
                        cert_path = "'%s-cert.pub'" % id_path
                        if os.path.exists(cert_path):
                            retcode, certinfo = shell_command(
                            "'%s' -Lf '%s'" % (ssh_keygen_path, cert_path))
                        certinfo = ' '.join(certinfo.split(' ')[1:])
                        fixed_certinfo = ''
                        for i, line in enumerate(certinfo.splitlines()):
                            if i == 0:
                                line = line.lstrip()
                            fixed_certinfo += line.replace('    ', ' ')
                            fixed_certinfo += '\n'
                        id_obj = {
                            'name': identity,
                            'public': public_key_contents,
                            'keytype': keytype,
                            'bubblebabble': bubblebabble,
                            'fingerprint': fingerprint,
                            'randomart': randomart,
                            'certinfo': fixed_certinfo,
                            'bits': bits,
                            'comment': comment.rstrip(),
                        }
                        out_dict['identities'].append(id_obj)
        # Figure out which identities are defaults
        default_ids = []
        default_ids_exists = False
        users_ssh_dir = get_ssh_dir(self)
        default_ids_path = os.path.join(users_ssh_dir, '.default_ids')
        if os.path.exists(default_ids_path):
            default_ids_exists = True
            with open(default_ids_path) as f:
                default_ids = f.read().splitlines() # Why not readlines()? \n
        # Convert any absolute paths inside default_ids to just the short names
        default_ids = [os.path.split(a)[1] for a in default_ids]
        if default_ids_exists:
            for i, id_obj in enumerate(out_dict['identities']):
                if id_obj['name'] in default_ids:
                    out_dict['identities'][i]['default'] = True
                else:
                    out_dict['identities'][i]['default'] = False
    except Exception as e:
        error_msg = _("Error getting identities: %s" % e)
        self.ssh_log.error(error_msg)
        out_dict['result'] = error_msg
    message = {
        'terminal:sshjs_identities_list': out_dict
    }
    self.write_message(message)
コード例 #14
0
def openssh_generate_new_keypair(self, name, path,
        keytype=None, passphrase="", bits=None, comment=""):
    """
    Generates a new private and public key pair--stored in the user's directory
    using the given *name* and other optional parameters (using OpenSSH).

    If *keytype* is given, it must be one of "ecdsa", "rsa" or "dsa" (case
    insensitive).  If *keytype* is "rsa" or "ecdsa", *bits* may be specified to
    specify the size of the key.

    .. note:: Defaults to generating a 521-byte ecdsa key if OpenSSH is version 5.7+. Otherwise a 2048-bit rsa key will be used.
    """
    self.ssh_log.debug('openssh_generate_new_keypair()')
    openssh_version = shell_command('ssh -V')[1]
    ssh_major_version = int(
        openssh_version.split()[0].split('_')[1].split('.')[0])
    key_path = os.path.join(path, name)
    ssh_minor_version = int(
        openssh_version.split()[0].split('_')[1].split('.')[1][0])
    ssh_version = "%s.%s" % (ssh_major_version, ssh_minor_version)
    ssh_version = float(ssh_version)
    if not keytype:
        if ssh_version >= 5.7:
            keytype = "ecdsa"
        else:
            keytype = "rsa"
    else:
        keytype = keytype.lower()
    if not bits and keytype == "ecdsa":
        bits = 521 # Not a typo: five-hundred-twenty-one bits
    elif not bits and keytype == "rsa":
        bits = 2048
    if not passphrase: # This just makes sure False and None end up as ''
        passphrase = ''
    hostname = os.uname()[1]
    if not comment:
        now = datetime.now().isoformat()
        comment = "Generated by Gate One on %s %s" % (hostname, now)
    ssh_keygen_path = which('ssh-keygen')
    command = (
        "%s "       # Path to ssh-keygen
        "-b %s "    # bits
        "-t %s "    # keytype
        "-C '%s' "  # comment
        "-f '%s'"   # Key path
        % (ssh_keygen_path, bits, keytype, comment, key_path)
    )
    self.ssh_log.debug("Keygen command: %s" % command)
    m = self.new_multiplex(command, "gen_ssh_keypair")
    call_errorback = partial(errorback, self)
    m.expect('^Overwrite.*',
        overwrite, optional=True, preprocess=False, timeout=10)
    passphrase_handler = partial(enter_passphrase, passphrase)
    m.expect('^Enter passphrase',
        passphrase_handler,
        errorback=call_errorback,
        preprocess=False,
        timeout=10)
    m.expect('^Enter same passphrase again',
        passphrase_handler,
        errorback=call_errorback,
        preprocess=False,
        timeout=10)
    finalize = partial(finished, self)
    # The regex below captures the md5 fingerprint which tells us the
    # operation was successful.
    m.expect(
        '(([0-9a-f][0-9a-f]\:){15}[0-9a-f][0-9a-f])',
        finalize,
        errorback=call_errorback,
        preprocess=False,
        timeout=15 # Key generation can take a little while
    )
    m.spawn()