Support for SSL certs in login & system keychains on Mac

This commit is contained in:
Steve Streeting 2016-03-02 15:18:04 +00:00
parent d27a09e50f
commit a0adc827d2
5 changed files with 98 additions and 1 deletions

26
lfs/certs.go Normal file

@ -0,0 +1,26 @@
package lfs
import (
"crypto/x509"
)
var (
trustedCerts *x509.CertPool
)
// addGitConfigCerts adds any SSL certs configured in gitconfig
func addGitConfigCerts() {
}
func addCertsFromPEMData(data []byte) bool {
if trustedCerts == nil {
trustedCerts = x509.NewCertPool()
}
return trustedCerts.AppendCertsFromPEM(data)
}
func init() {
addUserPlatformCerts()
addGitConfigCerts()
}

56
lfs/certs_darwin.go Normal file

@ -0,0 +1,56 @@
package lfs
import (
"strings"
"github.com/github/git-lfs/subprocess"
"github.com/github/git-lfs/vendor/_nuts/github.com/rubyist/tracerx"
)
func addUserPlatformCerts() {
// Go loads only the system root certificates by default
// see https://github.com/golang/go/blob/master/src/crypto/x509/root_darwin.go
// We want to load certs configured in the Login keychain and System keychain,
// the 2 places people tend to add custom self-signed certs (former is per-user,
// latter is system-wide)
// To protect against these files moving, use security to list & match
// For now, don't match all keychains to protect against something funky
// both Adobe and Microsoft ship custom keychains on OS X
// Unfortunately since tls.Config only allows the complete replacement of
// all RootCAs, and golang doesn't expose the system certs it's already read,
// we have to get the system root certs too (same technique)
// This keychain is not included in 'security list-keychains'
addCertsFromKeychain("/System/Library/Keychains/SystemRootCertificates.keychain")
// Now find system.keychain and login.keychain for user-added certs
cmd := subprocess.ExecCommand("/usr/bin/security", "list-keychains")
kcout, err := cmd.Output()
if err != nil {
tracerx.Printf("Error listing keychains: %v", err)
return
}
keychains := strings.Split(string(kcout), "\n")
for _, keychain := range keychains {
lc := strings.ToLower(keychain)
if !strings.Contains(lc, "/login.keychain") && !strings.Contains(lc, "/system.keychain") {
continue
}
keychain = strings.Trim(keychain, " \t\"")
addCertsFromKeychain(keychain)
}
}
func addCertsFromKeychain(keychain string) {
// Extract all certs in the keychain in PEM format
cmd := subprocess.ExecCommand("/usr/bin/security", "find-certificate", "-a", "-p", keychain)
data, err := cmd.Output()
if err != nil {
tracerx.Printf("Error reading keychain %q: %v", keychain, err)
return
}
addCertsFromPEMData(data)
}

5
lfs/certs_linux.go Normal file

@ -0,0 +1,5 @@
package lfs
func addUserPlatformCerts() {
// Do nothing, use golang default by leaving trustedCerts nil
}

7
lfs/certs_windows.go Normal file

@ -0,0 +1,7 @@
package lfs
func addUserPlatformCerts() {
// Do nothing, use golang default by leaving trustedCerts nil
// TODO support Windows Certificate Store
}

@ -118,8 +118,11 @@ func (c *Configuration) HttpClient() *HttpClient {
}
sslVerify, _ := c.GitConfig("http.sslverify")
tr.TLSClientConfig = &tls.Config{}
if sslVerify == "false" || Config.GetenvBool("GIT_SSL_NO_VERIFY", false) {
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
tr.TLSClientConfig.InsecureSkipVerify = true
} else {
tr.TLSClientConfig.RootCAs = trustedCerts
}
c.httpClient = &HttpClient{