libgit2: fix ssh host key verification regression

Earlier, host key verification could potentially fail if there were
multiple entries in the known_hosts file and if the intended encryption
algorithm wasn't the first entry. This happened because we used the same
hasher object to compute the sum of all the public keys present in the
known_hosts file, which led to invalid hashes, resulting in a mismatch
when compared with the hash of the advertised public key. This is fixed,
by not creating the hasher ourselves and instead delegating that to the
function actually doing the matching, ensuring that a new hasher is used
for each comparison.

Regression introduced in v0.25.0 and reported in
https://github.com/fluxcd/image-automation-controller/issues/378

Signed-off-by: Sanskar Jaiswal <jaiswalsanskar078@gmail.com>
This commit is contained in:
Sanskar Jaiswal 2022-06-08 13:31:19 +05:30
parent 2031c84961
commit 47692530d7
4 changed files with 19 additions and 20 deletions

2
go.mod
View File

@ -28,7 +28,7 @@ require (
github.com/fluxcd/pkg/helmtestserver v0.7.3 github.com/fluxcd/pkg/helmtestserver v0.7.3
github.com/fluxcd/pkg/lockedfile v0.1.0 github.com/fluxcd/pkg/lockedfile v0.1.0
github.com/fluxcd/pkg/runtime v0.16.1 github.com/fluxcd/pkg/runtime v0.16.1
github.com/fluxcd/pkg/ssh v0.4.1 github.com/fluxcd/pkg/ssh v0.5.0
github.com/fluxcd/pkg/testserver v0.2.0 github.com/fluxcd/pkg/testserver v0.2.0
github.com/fluxcd/pkg/untar v0.1.0 github.com/fluxcd/pkg/untar v0.1.0
github.com/fluxcd/pkg/version v0.1.0 github.com/fluxcd/pkg/version v0.1.0

4
go.sum
View File

@ -282,8 +282,8 @@ github.com/fluxcd/pkg/lockedfile v0.1.0 h1:YsYFAkd6wawMCcD74ikadAKXA4s2sukdxrn7w
github.com/fluxcd/pkg/lockedfile v0.1.0/go.mod h1:EJLan8t9MiOcgTs8+puDjbE6I/KAfHbdvIy9VUgIjm8= github.com/fluxcd/pkg/lockedfile v0.1.0/go.mod h1:EJLan8t9MiOcgTs8+puDjbE6I/KAfHbdvIy9VUgIjm8=
github.com/fluxcd/pkg/runtime v0.16.1 h1:WU1vNZz4TAzmATQ/tl2zB/FX6GIUTgYeBn/G5RuTA2c= github.com/fluxcd/pkg/runtime v0.16.1 h1:WU1vNZz4TAzmATQ/tl2zB/FX6GIUTgYeBn/G5RuTA2c=
github.com/fluxcd/pkg/runtime v0.16.1/go.mod h1:cgVJkOXCg9OmrIUGklf/0UtV28MNzkuoBJhaEQICT6E= github.com/fluxcd/pkg/runtime v0.16.1/go.mod h1:cgVJkOXCg9OmrIUGklf/0UtV28MNzkuoBJhaEQICT6E=
github.com/fluxcd/pkg/ssh v0.4.1 h1:O5FCjb5NIZ9PeRjdF2iL9jaPNM+RL+IjrMBZPkqF9W4= github.com/fluxcd/pkg/ssh v0.5.0 h1:jE9F2XvUXC2mgseeXMATvO014fLqdB30/VzlPLKsk20=
github.com/fluxcd/pkg/ssh v0.4.1/go.mod h1:KGgOUOy1uI6RC6+qxIBLvP1AeOOs/nLB25Ca6TZMIXE= github.com/fluxcd/pkg/ssh v0.5.0/go.mod h1:KGgOUOy1uI6RC6+qxIBLvP1AeOOs/nLB25Ca6TZMIXE=
github.com/fluxcd/pkg/testserver v0.2.0 h1:Mj0TapmKaywI6Fi5wvt1LAZpakUHmtzWQpJNKQ0Krt4= github.com/fluxcd/pkg/testserver v0.2.0 h1:Mj0TapmKaywI6Fi5wvt1LAZpakUHmtzWQpJNKQ0Krt4=
github.com/fluxcd/pkg/testserver v0.2.0/go.mod h1:bgjjydkXsZTeFzjz9Cr4heGANr41uTB1Aj1Q5qzuYVk= github.com/fluxcd/pkg/testserver v0.2.0/go.mod h1:bgjjydkXsZTeFzjz9Cr4heGANr41uTB1Aj1Q5qzuYVk=
github.com/fluxcd/pkg/untar v0.1.0 h1:k97V/xV5hFrAkIkVPuv5AVhyxh1ZzzAKba/lbDfGo6o= github.com/fluxcd/pkg/untar v0.1.0 h1:k97V/xV5hFrAkIkVPuv5AVhyxh1ZzzAKba/lbDfGo6o=

View File

@ -1,9 +1,7 @@
package managed package managed
import ( import (
"crypto/sha256"
"fmt" "fmt"
"hash"
"net" "net"
pkgkh "github.com/fluxcd/pkg/ssh/knownhosts" pkgkh "github.com/fluxcd/pkg/ssh/knownhosts"
@ -42,11 +40,9 @@ func KnownHostsCallback(host string, knownHosts []byte) git2go.CertificateCheckC
} }
var fingerprint []byte var fingerprint []byte
var hasher hash.Hash
switch { switch {
case cert.Hostkey.Kind&git2go.HostkeySHA256 > 0: case cert.Hostkey.Kind&git2go.HostkeySHA256 > 0:
fingerprint = cert.Hostkey.HashSHA256[:] fingerprint = cert.Hostkey.HashSHA256[:]
hasher = sha256.New()
default: default:
return fmt.Errorf("invalid host key kind, expected to be of kind SHA256") return fmt.Errorf("invalid host key kind, expected to be of kind SHA256")
} }
@ -57,7 +53,7 @@ func KnownHostsCallback(host string, knownHosts []byte) git2go.CertificateCheckC
// is an entry for the hostname _and_ port. // is an entry for the hostname _and_ port.
h := knownhosts.Normalize(host) h := knownhosts.Normalize(host)
for _, k := range kh { for _, k := range kh {
if k.Matches(h, fingerprint, hasher) { if k.Matches(h, fingerprint) {
return nil return nil
} }
} }

View File

@ -1,10 +1,7 @@
package managed package managed
import ( import (
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem"
"errors"
"fmt" "fmt"
"testing" "testing"
@ -14,7 +11,10 @@ import (
// knownHostsFixture is known_hosts fixture in the expected // knownHostsFixture is known_hosts fixture in the expected
// format. // format.
var knownHostsFixture = `github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==` var knownHostsFixture = `github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==
github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=
github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
`
func TestKnownHostsCallback(t *testing.T) { func TestKnownHostsCallback(t *testing.T) {
tests := []struct { tests := []struct {
@ -41,6 +41,17 @@ func TestKnownHostsCallback(t *testing.T) {
expectedHost: "github.com:22", expectedHost: "github.com:22",
want: nil, want: nil,
}, },
{
// Test case to specifically detect a regression introduced in v0.25.0
// Ref: https://github.com/fluxcd/image-automation-controller/issues/378
name: "Match regardless of order of known_hosts",
host: "github.com",
knownHosts: []byte(knownHostsFixture),
// Use ecdsa-sha2-nistp256 instead of ssh-rsa
hostkey: git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256, HashSHA256: sha256Fingerprint("p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM")},
expectedHost: "github.com:22",
want: nil,
},
{ {
name: "Hostname mismatch", name: "Hostname mismatch",
host: "github.com", host: "github.com",
@ -83,11 +94,3 @@ func sha256Fingerprint(in string) [32]byte {
copy(out[:], d) copy(out[:], d)
return out return out
} }
func certificateFromPEM(pemBytes string) (*x509.Certificate, error) {
block, _ := pem.Decode([]byte(pemBytes))
if block == nil {
return nil, errors.New("failed to decode PEM")
}
return x509.ParseCertificate(block.Bytes)
}