Skip to content

Commit a8e546a

Browse files
committed
add support for parsing known hosts key metadata
1 parent 7cfb916 commit a8e546a

File tree

2 files changed

+38
-11
lines changed

2 files changed

+38
-11
lines changed

ssh/knownhosts/knownhosts.go

+34-10
Original file line numberDiff line numberDiff line change
@@ -176,39 +176,41 @@ func nextWord(line []byte) (string, []byte) {
176176
return string(line[:i]), bytes.TrimSpace(line[i:])
177177
}
178178

179-
func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) {
179+
func parseLine(line []byte) (marker, host, comments string, key ssh.PublicKey, err error) {
180180
if w, next := nextWord(line); w == markerCert || w == markerRevoked {
181181
marker = w
182182
line = next
183183
}
184184

185185
host, line = nextWord(line)
186186
if len(line) == 0 {
187-
return "", "", nil, errors.New("knownhosts: missing host pattern")
187+
return "", "", "", nil, errors.New("knownhosts: missing host pattern")
188188
}
189189

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

196-
keyBlob, _ := nextWord(line)
196+
keyBlob, line := nextWord(line)
197197

198198
keyBytes, err := base64.StdEncoding.DecodeString(keyBlob)
199199
if err != nil {
200-
return "", "", nil, err
200+
return "", "", "", nil, err
201201
}
202202
key, err = ssh.ParsePublicKey(keyBytes)
203203
if err != nil {
204-
return "", "", nil, err
204+
return "", "", "", nil, err
205205
}
206+
// the rest of the line is the comment, and may include whitespace.
207+
restOfLine := string(bytes.TrimSpace(line))
206208

207-
return marker, host, key, nil
209+
return marker, host, restOfLine, key, nil
208210
}
209211

210212
func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error {
211-
marker, pattern, key, err := parseLine(line)
213+
marker, pattern, comments, key, err := parseLine(line)
212214
if err != nil {
213215
return err
214216
}
@@ -218,6 +220,7 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
218220
Key: key,
219221
Filename: filename,
220222
Line: linenum,
223+
Comments: comments,
221224
}
222225

223226
return nil
@@ -229,6 +232,7 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
229232
Filename: filename,
230233
Line: linenum,
231234
Key: key,
235+
Comments: comments,
232236
},
233237
}
234238

@@ -241,7 +245,6 @@ func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error
241245
if err != nil {
242246
return err
243247
}
244-
245248
db.lines = append(db.lines, entry)
246249
return nil
247250
}
@@ -290,10 +293,11 @@ type KnownKey struct {
290293
Key ssh.PublicKey
291294
Filename string
292295
Line int
296+
Comments string
293297
}
294298

295299
func (k *KnownKey) String() string {
296-
return fmt.Sprintf("%s:%d: %s", k.Filename, k.Line, serialize(k.Key))
300+
return fmt.Sprintf("%s:%d: %s %s", k.Filename, k.Line, serialize(k.Key), k.Comments)
297301
}
298302

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

442+
func NewKnownKeys(files ...string) ([]KnownKey, error) {
443+
db := newHostKeyDB()
444+
for _, fn := range files {
445+
f, err := os.Open(fn)
446+
if err != nil {
447+
return nil, err
448+
}
449+
defer f.Close()
450+
if err := db.Read(f, fn); err != nil {
451+
return nil, err
452+
}
453+
}
454+
455+
keys := make([]KnownKey, 0, len(db.lines))
456+
for _, l := range db.lines {
457+
keys = append(keys, l.knownKey)
458+
}
459+
return keys, nil
460+
}
461+
438462
// Normalize normalizes an address into the form used in known_hosts
439463
func Normalize(address string) string {
440464
host, port, err := net.SplitHostPort(address)

ssh/knownhosts/knownhosts_test.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ import (
1515
)
1616

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

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

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

0 commit comments

Comments
 (0)