Skip to content

Commit 2ccd649

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 <caarlos0@users.noreply.github.com>
1 parent 911360c commit 2ccd649

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)