Skip to content

Commit 361bd2b

Browse files
committed
ssh/knownhosts: check more than one key
The problem was that it was keeping only the first known key of each type found. If you have a server advertising multiple keys of the same type, you might get a missmatch key error. Per sshd(8) man page, it should allow reapeatable hosts with different host keys, although it don't specify anything about hosts being from different types: "It is permissible (but not recommended) to have several lines or different host keys for the same names. This will inevitably happen when short forms of host names from different domains are put in the file. It is possible that the files contain conflicting information; authentication is accepted if valid information can be found from either file." So, this changes knownhosts behavior to accept any of the keys for a given host, regardless of type. Fixes #36126 Signed-off-by: Carlos Alexandro Becker <[email protected]>
1 parent 911360c commit 361bd2b

File tree

2 files changed

+24
-36
lines changed

2 files changed

+24
-36
lines changed

ssh/knownhosts/knownhosts.go

+11-25
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ func (k *KnownKey) String() string {
302302
// applications can offer an interactive prompt to the user.
303303
type KeyError struct {
304304
// Want holds the accepted host keys. For each key algorithm,
305-
// there can be one hostkey. If Want is empty, the host is
306-
// unknown. If Want is non-empty, there was a mismatch, which
305+
// there can be multiple hostkeys. If Want is empty, the host
306+
// is unknown. If Want is non-empty, there was a mismatch, which
307307
// can signify a MITM attack.
308308
Want []KnownKey
309309
}
@@ -358,34 +358,20 @@ func (db *hostKeyDB) checkAddr(a addr, remoteKey ssh.PublicKey) error {
358358
// is just a key for the IP address, but not for the
359359
// hostname?
360360

361-
// Algorithm => key.
362-
knownKeys := map[string]KnownKey{}
363-
for _, l := range db.lines {
364-
if l.match(a) {
365-
typ := l.knownKey.Key.Type()
366-
if _, ok := knownKeys[typ]; !ok {
367-
knownKeys[typ] = l.knownKey
368-
}
369-
}
370-
}
371-
372361
keyErr := &KeyError{}
373-
for _, v := range knownKeys {
374-
keyErr.Want = append(keyErr.Want, v)
375-
}
376362

377-
// Unknown remote host.
378-
if len(knownKeys) == 0 {
379-
return keyErr
380-
}
363+
for _, l := range db.lines {
364+
if !l.match(a) {
365+
continue
366+
}
381367

382-
// If the remote host starts using a different, unknown key type, we
383-
// also interpret that as a mismatch.
384-
if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known.Key, remoteKey) {
385-
return keyErr
368+
keyErr.Want = append(keyErr.Want, l.knownKey)
369+
if keyEq(l.knownKey.Key, remoteKey) {
370+
return nil
371+
}
386372
}
387373

388-
return nil
374+
return keyErr
389375
}
390376

391377
// The Read function parses file contents.

ssh/knownhosts/knownhosts_test.go

+13-11
Original file line numberDiff line numberDiff line change
@@ -201,17 +201,6 @@ func TestHostNamePrecedence(t *testing.T) {
201201
}
202202
}
203203

204-
func TestDBOrderingPrecedenceKeyType(t *testing.T) {
205-
str := fmt.Sprintf("server.org,%s %s\nserver.org,%s %s", testAddr, edKeyStr, testAddr, alternateEdKeyStr)
206-
db := testDB(t, str)
207-
208-
if err := db.check("server.org:22", testAddr, alternateEdKey); err == nil {
209-
t.Errorf("check succeeded")
210-
} else if _, ok := err.(*KeyError); !ok {
211-
t.Errorf("got %T, want *KeyError", err)
212-
}
213-
}
214-
215204
func TestNegate(t *testing.T) {
216205
str := fmt.Sprintf("%s,!server.org %s", testAddr, edKeyStr)
217206
db := testDB(t, str)
@@ -354,3 +343,16 @@ func TestHashedHostkeyCheck(t *testing.T) {
354343
t.Errorf("got error %v, want %v", got, want)
355344
}
356345
}
346+
347+
func TestIssue36126(t *testing.T) {
348+
str := fmt.Sprintf("server.org,%s %s\nserver.org,%s %s", testAddr, edKeyStr, testAddr, alternateEdKeyStr)
349+
db := testDB(t, str)
350+
351+
if err := db.check("server.org:22", testAddr, edKey); err != nil {
352+
t.Errorf("should have passed the check, got %v", err)
353+
}
354+
355+
if err := db.check("server.org:22", testAddr, alternateEdKey); err != nil {
356+
t.Errorf("should have passed the check, got %v", err)
357+
}
358+
}

0 commit comments

Comments
 (0)