# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Gather ~user/.ssh/known_hosts contents.

We're only interested in the first field of each line; this field is a
comma-separated list of host names, IP addresses, or patterns (the
list ends at the first whitespace character).  Patterns may start with
'!' to indicate negation.

We also gather the contents of /etc/ssh/ssh_known_hosts, which uses
the same format.

Table columns:

- localuser -- the local user where the known_hosts file is found;
               blank for /etc/ssh/ssh_known_hosts
- host      -- the host name or pattern (one pattern at a time),
               including the leading '!' if present
- ipaddr    -- the IP addrses corresponding to the host name,
               after removal of the leading '!' if present;
               blank if there was no match

Note that for computing trust relationships, this table's information
must be interpreted in reverse compared to the rhosts and hosts_equiv
table; when my known_hosts file on host X mentions host Y, host Y
trusts host X.

XXX We should figure out where popular ssh clients for Windows store
the same information.

"""

import os
import sys
import socket

if __name__ == "__main__":
    try:
        import sigma
    except ImportError:
        sys.path.append(os.path.join(os.pardir, os.pardir))

from sigma.runtime import rtlib

def Gather(collector):
    t = collector.table("known_hosts", ["localuser", "host", "ipaddr"])
    h = rtlib.Host("localhost")
    for acct in list(h.list_local_accounts()) + [None]:
        if acct is None:
            known_hosts = "/etc/ssh/ssh_known_hosts"
            localuser = ""
        else:
            known_hosts = os.path.join(acct.home, ".ssh/known_hosts")
            localuser = acct.username
        try:
            f = open(known_hosts)
        except IOError:
            continue
        try:
            for line in f:
                words = line.split()
                if not words or words[0].startswith("#"):
                    continue
                elif words[0].startswith("|1|"):
                    continue
                hosts = words[0].split(",")
                for host in hosts:
                    realhost = host
                    if realhost.startswith("!"):
                        realhost = realhost[1:]
                    try:
                        ipaddr = socket.gethostbyname(realhost)
                    except socket.error:
                        ipaddr = ""
                    t.add([localuser, host, ipaddr])
        finally:
            f.close()

def _test():
    from sigma.gather import gathering
    gathering.test(Gather)

if __name__ == "__main__":
    _test()
