Skip to content

Commit d1ee618

Browse files
author
Philip Laine
authored
Merge pull request #290 from fluxcd/fix/hash-type
libgit2: check hostkey type when validating hostkey
2 parents ca2bb80 + 0a1631d commit d1ee618

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

pkg/git/libgit2/transport.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ package libgit2
1919
import (
2020
"bufio"
2121
"bytes"
22+
"crypto/md5"
2223
"crypto/sha1"
24+
"crypto/sha256"
2325
"crypto/x509"
2426
"fmt"
27+
"hash"
2528
"net"
2629
"net/url"
2730
"strings"
@@ -157,7 +160,7 @@ func (s *PublicKeyAuth) Method(secret corev1.Secret) (*git.Auth, error) {
157160
// is an entry for the hostname _and_ port.
158161
host = knownhosts.Normalize(s.host)
159162
for _, k := range kk {
160-
if k.matches(host, cert.Hostkey.HashSHA1[:]) {
163+
if k.matches(host, cert.Hostkey) {
161164
return git2go.ErrOk
162165
}
163166
}
@@ -195,17 +198,28 @@ func parseKnownHosts(s string) ([]knownKey, error) {
195198
return knownHosts, nil
196199
}
197200

198-
func (k knownKey) matches(host string, key []byte) bool {
201+
func (k knownKey) matches(host string, hostkey git2go.HostkeyCertificate) bool {
199202
if !containsHost(k.hosts, host) {
200203
return false
201204
}
202205

203-
hash := sha1.Sum(k.key.Marshal())
204-
if bytes.Compare(hash[:], key) != 0 {
206+
var fingerprint []byte
207+
var hasher hash.Hash
208+
switch {
209+
case hostkey.Kind&git2go.HostkeySHA256 > 0:
210+
fingerprint = hostkey.HashSHA256[:]
211+
hasher = sha256.New()
212+
case hostkey.Kind&git2go.HostkeySHA1 > 0:
213+
fingerprint = hostkey.HashSHA1[:]
214+
hasher = sha1.New()
215+
case hostkey.Kind&git2go.HostkeyMD5 > 0:
216+
fingerprint = hostkey.HashMD5[:]
217+
hasher = md5.New()
218+
default:
205219
return false
206220
}
207-
208-
return true
221+
hasher.Write(k.key.Marshal())
222+
return bytes.Compare(hasher.Sum(nil), fingerprint) == 0
209223
}
210224

211225
func containsHost(hosts []string, host string) bool {

pkg/git/libgit2/transport_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ limitations under the License.
1717
package libgit2
1818

1919
import (
20+
"encoding/base64"
2021
"reflect"
2122
"testing"
2223

24+
git2go "github.com/libgit2/git2go/v31"
2325
corev1 "k8s.io/api/core/v1"
2426

2527
"github.com/fluxcd/source-controller/pkg/git"
@@ -145,3 +147,60 @@ func TestPublicKeyStrategy_Method(t *testing.T) {
145147
})
146148
}
147149
}
150+
151+
func TestKnownKeyHash(t *testing.T) {
152+
tests := []struct {
153+
name string
154+
hostkey git2go.HostkeyCertificate
155+
wantMatches bool
156+
}{
157+
{"good sha256 hostkey", git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256 | git2go.HostkeySHA1 | git2go.HostkeyMD5, HashSHA256: sha256Fingerprint("nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8")}, true},
158+
{"bad sha256 hostkey", git2go.HostkeyCertificate{Kind: git2go.HostkeySHA256 | git2go.HostkeySHA1 | git2go.HostkeyMD5, HashSHA256: sha256Fingerprint("ROQFvPThGrW4RuWLoL9tq9I9zJ42fK4XywyRtbOz/EQ")}, false},
159+
{"good sha1 hostkey", git2go.HostkeyCertificate{Kind: git2go.HostkeySHA1 | git2go.HostkeyMD5, HashSHA1: sha1Fingerprint("v2toJdKXfFEaR1u++4iq1UqSrHM")}, true},
160+
{"bad sha1 hostkey", git2go.HostkeyCertificate{Kind: git2go.HostkeySHA1 | git2go.HostkeyMD5, HashSHA1: sha1Fingerprint("tfpLlQhDDFP3yGdewTvHNxWmAdk")}, false},
161+
{"good md5 hostkey", git2go.HostkeyCertificate{Kind: git2go.HostkeyMD5, HashMD5: md5Fingerprint("\x16\x27\xac\xa5\x76\x28\x2d\x36\x63\x1b\x56\x4d\xeb\xdf\xa6\x48")}, true},
162+
{"bad md5 hostkey", git2go.HostkeyCertificate{Kind: git2go.HostkeyMD5, HashMD5: md5Fingerprint("\xb6\x03\x0e\x39\x97\x9e\xd0\xe7\x24\xce\xa3\x77\x3e\x01\x42\x09")}, false},
163+
{"invalid hostkey", git2go.HostkeyCertificate{}, false},
164+
}
165+
for _, tt := range tests {
166+
t.Run(tt.name, func(t *testing.T) {
167+
knownKeys, err := parseKnownHosts(knownHostsFixture)
168+
if err != nil {
169+
t.Error(err)
170+
return
171+
}
172+
173+
matches := knownKeys[0].matches("github.com", tt.hostkey)
174+
if matches != tt.wantMatches {
175+
t.Errorf("Method() matches = %v, wantMatches %v", matches, tt.wantMatches)
176+
return
177+
}
178+
})
179+
}
180+
}
181+
182+
func md5Fingerprint(in string) [16]byte {
183+
var out [16]byte
184+
copy(out[:], []byte(in))
185+
return out
186+
}
187+
188+
func sha1Fingerprint(in string) [20]byte {
189+
d, err := base64.RawStdEncoding.DecodeString(in)
190+
if err != nil {
191+
panic(err)
192+
}
193+
var out [20]byte
194+
copy(out[:], d)
195+
return out
196+
}
197+
198+
func sha256Fingerprint(in string) [32]byte {
199+
d, err := base64.RawStdEncoding.DecodeString(in)
200+
if err != nil {
201+
panic(err)
202+
}
203+
var out [32]byte
204+
copy(out[:], d)
205+
return out
206+
}

0 commit comments

Comments
 (0)