Skip to content

Commit

Permalink
add support for parsing known hosts key metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
based64god committed Oct 21, 2024
1 parent 7cfb916 commit 8963009
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 8963009

Please sign in to comment.