Skip to content

Commit

Permalink
add support for parsing known hosts key metadata. Provide an avenue f…
Browse files Browse the repository at this point in the history
…or consumers to access it
  • Loading branch information
based64god committed Oct 21, 2024
1 parent 7cfb916 commit 0ec04d5
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
44 changes: 34 additions & 10 deletions ssh/knownhosts/knownhosts.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,39 +176,41 @@ func nextWord(line []byte) (string, []byte) {
return string(line[:i]), bytes.TrimSpace(line[i:])
}

func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) {
func parseLine(line []byte) (marker, host, comments string, key ssh.PublicKey, err error) {
if w, next := nextWord(line); w == markerCert || w == markerRevoked {
marker = w
line = next
}

host, line = nextWord(line)
if len(line) == 0 {
return "", "", nil, errors.New("knownhosts: missing host pattern")
return "", "", "", nil, errors.New("knownhosts: missing host pattern")
}

// ignore the keytype as it's in the key blob anyway.
_, line = nextWord(line)
if len(line) == 0 {
return "", "", nil, errors.New("knownhosts: missing key type pattern")
return "", "", "", nil, errors.New("knownhosts: missing key type pattern")
}

keyBlob, _ := nextWord(line)
keyBlob, line := nextWord(line)

keyBytes, err := base64.StdEncoding.DecodeString(keyBlob)
if err != nil {
return "", "", nil, err
return "", "", "", nil, err
}
key, err = ssh.ParsePublicKey(keyBytes)
if err != nil {
return "", "", nil, err
return "", "", "", nil, err
}
// the rest of the line is the comment, and may include whitespace.
restOfLine := string(bytes.TrimSpace(line))

return marker, host, key, nil
return marker, host, restOfLine, key, nil
}

func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error {
marker, pattern, key, err := parseLine(line)
marker, pattern, comments, key, err := parseLine(line)
if err != nil {
return err
}
Expand All @@ -218,6 +220,7 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
Key: key,
Filename: filename,
Line: linenum,
Comments: comments,
}

return nil
Expand All @@ -229,6 +232,7 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
Filename: filename,
Line: linenum,
Key: key,
Comments: comments,
},
}

Expand All @@ -241,7 +245,6 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
if err != nil {
return err
}

db.lines = append(db.lines, entry)
return nil
}
Expand Down Expand Up @@ -290,10 +293,11 @@ type KnownKey struct {
Key ssh.PublicKey
Filename string
Line int
Comments string
}

func (k *KnownKey) String() string {
return fmt.Sprintf("%s:%d: %s", k.Filename, k.Line, serialize(k.Key))
return fmt.Sprintf("%s:%d: %s %s", k.Filename, k.Line, serialize(k.Key), k.Comments)
}

// KeyError is returned if we did not find the key in the host key
Expand Down Expand Up @@ -435,6 +439,26 @@ func New(files ...string) (ssh.HostKeyCallback, error) {
return certChecker.CheckHostKey, nil
}

func NewKnownKeys(files ...string) ([]KnownKey, error) {
db := newHostKeyDB()
for _, fn := range files {
f, err := os.Open(fn)
if err != nil {
return nil, err
}
defer f.Close()
if err := db.Read(f, fn); err != nil {
return nil, err
}
}

keys := make([]KnownKey, 0, len(db.lines))
for _, l := range db.lines {
keys = append(keys, l.knownKey)
}
return keys, nil
}

// Normalize normalizes an address into the form used in known_hosts
func Normalize(address string) string {
host, port, err := net.SplitHostPort(address)
Expand Down
5 changes: 4 additions & 1 deletion ssh/knownhosts/knownhosts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import (
)

const edKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBAarftlLeoyf+v+nVchEZII/vna2PCV8FaX4vsF5BX"
const edKeyComments = "comments are ignored"
const alternateEdKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIXffBYeYL+WVzVru8npl5JHt2cjlr4ornFTWzoij9sx"
const ecKeyStr = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLCu01+wpXe3xB5olXCN4SqU2rQu0qjSRKJO4Bg+JRCPU+ENcgdA5srTU8xYDz/GEa4dzK5ldPw4J/gZgSXCMs="
const ecKeyComments = "and may include whitespace"

var ecKey, alternateEdKey, edKey ssh.PublicKey
var testAddr = &net.TCPAddr{
Expand Down Expand Up @@ -163,7 +165,7 @@ func TestIPv6Address(t *testing.T) {
}

func TestBasic(t *testing.T) {
str := fmt.Sprintf("#comment\n\nserver.org,%s %s\notherhost %s", testAddr, edKeyStr, ecKeyStr)
str := fmt.Sprintf("#comment\n\nserver.org,%s %s %s\notherhost %s %s", testAddr, edKeyStr, edKeyComments, ecKeyStr, ecKeyComments)
db := testDB(t, str)
if err := db.check("server.org:22", testAddr, edKey); err != nil {
t.Errorf("got error %v, want none", err)
Expand All @@ -173,6 +175,7 @@ func TestBasic(t *testing.T) {
Key: edKey,
Filename: "testdb",
Line: 3,
Comments: edKeyComments,
}
if err := db.check("server.org:22", testAddr, ecKey); err == nil {
t.Errorf("succeeded, want KeyError")
Expand Down

0 comments on commit 0ec04d5

Please sign in to comment.