ssh/knownhosts: verify declared key type matches decoded key The parseLine function previously ignored the key type field (e.g., "ssh-rsa") in known_hosts entries, relying solely on the type information embedded within the base64-encoded key blob. OpenSSH's sshkey_read implementation explicitly verifies that the key type declared in the text matches the type of the parsed key, returning SSH_ERR_KEY_TYPE_MISMATCH if they differ. This change adds a check to ensure the declared key type matches key.Type(), returning an error for malformed lines where they diverge. This issue was found during a security audit by NCC Group Cryptography Services, sponsored by Teleport, and was assessed and is being fixed as a non-security bug. Change-Id: Id4f35c74055f5691088273630b50cdd02c81bfe9 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/782427 Reviewed-by: David Chase <drchase@google.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Junyang Shao <shaojunyang@google.com>
diff --git a/ssh/knownhosts/knownhosts.go b/ssh/knownhosts/knownhosts.go index 1ef1c35..c7eda05 100644 --- a/ssh/knownhosts/knownhosts.go +++ b/ssh/knownhosts/knownhosts.go
@@ -192,8 +192,7 @@ return "", "", nil, errors.New("knownhosts: missing host pattern") } - // ignore the keytype as it's in the key blob anyway. - _, line = nextWord(line) + wantType, line := nextWord(line) if len(line) == 0 { return "", "", nil, errors.New("knownhosts: missing key type pattern") } @@ -209,6 +208,10 @@ return "", "", nil, err } + if key.Type() != wantType { + return "", "", nil, fmt.Errorf("knownhosts: key type mismatch: found %q, want %q", key.Type(), wantType) + } + return marker, host, key, nil }
diff --git a/ssh/knownhosts/knownhosts_test.go b/ssh/knownhosts/knownhosts_test.go index 8e1fba2..f3af2da 100644 --- a/ssh/knownhosts/knownhosts_test.go +++ b/ssh/knownhosts/knownhosts_test.go
@@ -12,6 +12,7 @@ "fmt" "net" "reflect" + "strings" "testing" "golang.org/x/crypto/ssh" @@ -446,3 +447,18 @@ t.Errorf("Did not match dirty host, implying parsing issue") } } + +func TestKeyTypeMismatch(t *testing.T) { + line := fmt.Sprintf("server.org ssh-rsa %s", base64.StdEncoding.EncodeToString(edKey.Marshal())) + + db := newHostKeyDB() + err := db.Read(bytes.NewBufferString(line), "testdb") + if err == nil { + t.Fatal("Read succeeded on line with key type mismatch, expected error") + } + + expectedErr := `knownhosts: key type mismatch: found "ssh-ed25519", want "ssh-rsa"` + if !strings.Contains(err.Error(), expectedErr) { + t.Fatalf("got error %q, want to contain %q", err.Error(), expectedErr) + } +}