github.com/git-lfs/git-lfs@v2.5.2+incompatible/lfsapi/certs.go (about)

     1  package lfsapi
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"path/filepath"
     9  
    10  	"github.com/git-lfs/git-lfs/config"
    11  	"github.com/rubyist/tracerx"
    12  )
    13  
    14  // isCertVerificationDisabledForHost returns whether SSL certificate verification
    15  // has been disabled for the given host, or globally
    16  func isCertVerificationDisabledForHost(c *Client, host string) bool {
    17  	hostSslVerify, _ := c.uc.Get("http", fmt.Sprintf("https://%v", host), "sslverify")
    18  	if hostSslVerify == "false" {
    19  		return true
    20  	}
    21  
    22  	return c.SkipSSLVerify
    23  }
    24  
    25  // isClientCertEnabledForHost returns whether client certificate
    26  // are configured for the given host
    27  func isClientCertEnabledForHost(c *Client, host string) bool {
    28  	_, hostSslKeyOk := c.uc.Get("http", fmt.Sprintf("https://%v/", host), "sslKey")
    29  	_, hostSslCertOk := c.uc.Get("http", fmt.Sprintf("https://%v/", host), "sslCert")
    30  
    31  	return hostSslKeyOk && hostSslCertOk
    32  }
    33  
    34  // getClientCertForHost returns a client certificate for a specific host (which may
    35  // be "host:port" loaded from the gitconfig
    36  func getClientCertForHost(c *Client, host string) tls.Certificate {
    37  	hostSslKey, _ := c.uc.Get("http", fmt.Sprintf("https://%v/", host), "sslKey")
    38  	hostSslCert, _ := c.uc.Get("http", fmt.Sprintf("https://%v/", host), "sslCert")
    39  	cert, err := tls.LoadX509KeyPair(hostSslCert, hostSslKey)
    40  	if err != nil {
    41  		tracerx.Printf("Error reading client cert/key %v", err)
    42  	}
    43  	return cert
    44  }
    45  
    46  // getRootCAsForHost returns a certificate pool for that specific host (which may
    47  // be "host:port" loaded from either the gitconfig or from a platform-specific
    48  // source which is not included by default in the golang certificate search)
    49  // May return nil if it doesn't have anything to add, in which case the default
    50  // RootCAs will be used if passed to TLSClientConfig.RootCAs
    51  func getRootCAsForHost(c *Client, host string) *x509.CertPool {
    52  	// don't init pool, want to return nil not empty if none found; init only on successful add cert
    53  	var pool *x509.CertPool
    54  
    55  	// gitconfig first
    56  	pool = appendRootCAsForHostFromGitconfig(c.osEnv, c.gitEnv, pool, host)
    57  	// Platform specific
    58  	return appendRootCAsForHostFromPlatform(pool, host)
    59  }
    60  
    61  func appendRootCAsForHostFromGitconfig(osEnv, gitEnv config.Environment, pool *x509.CertPool, host string) *x509.CertPool {
    62  	// Accumulate certs from all these locations:
    63  
    64  	// GIT_SSL_CAINFO first
    65  	if cafile, _ := osEnv.Get("GIT_SSL_CAINFO"); len(cafile) > 0 {
    66  		return appendCertsFromFile(pool, cafile)
    67  	}
    68  	// http.<url>/.sslcainfo or http.<url>.sslcainfo
    69  	uc := config.NewURLConfig(gitEnv)
    70  	if cafile, ok := uc.Get("http", fmt.Sprintf("https://%v/", host), "sslcainfo"); ok {
    71  		return appendCertsFromFile(pool, cafile)
    72  	}
    73  	// GIT_SSL_CAPATH
    74  	if cadir, _ := osEnv.Get("GIT_SSL_CAPATH"); len(cadir) > 0 {
    75  		return appendCertsFromFilesInDir(pool, cadir)
    76  	}
    77  	// http.sslcapath
    78  	if cadir, ok := gitEnv.Get("http.sslcapath"); ok {
    79  		return appendCertsFromFilesInDir(pool, cadir)
    80  	}
    81  
    82  	return pool
    83  }
    84  
    85  func appendCertsFromFilesInDir(pool *x509.CertPool, dir string) *x509.CertPool {
    86  	files, err := ioutil.ReadDir(dir)
    87  	if err != nil {
    88  		tracerx.Printf("Error reading cert dir %q: %v", dir, err)
    89  		return pool
    90  	}
    91  	for _, f := range files {
    92  		pool = appendCertsFromFile(pool, filepath.Join(dir, f.Name()))
    93  	}
    94  	return pool
    95  }
    96  
    97  func appendCertsFromFile(pool *x509.CertPool, filename string) *x509.CertPool {
    98  	data, err := ioutil.ReadFile(filename)
    99  	if err != nil {
   100  		tracerx.Printf("Error reading cert file %q: %v", filename, err)
   101  		return pool
   102  	}
   103  	// Firstly, try parsing as binary certificate
   104  	if certs, err := x509.ParseCertificates(data); err == nil {
   105  		return appendCerts(pool, certs)
   106  	}
   107  	// If not binary certs, try PEM data
   108  	return appendCertsFromPEMData(pool, data)
   109  }
   110  
   111  func appendCerts(pool *x509.CertPool, certs []*x509.Certificate) *x509.CertPool {
   112  	if len(certs) == 0 {
   113  		// important to return unmodified (may be nil)
   114  		return pool
   115  	}
   116  
   117  	if pool == nil {
   118  		pool = x509.NewCertPool()
   119  	}
   120  
   121  	for _, cert := range certs {
   122  		pool.AddCert(cert)
   123  	}
   124  
   125  	return pool
   126  }
   127  
   128  func appendCertsFromPEMData(pool *x509.CertPool, data []byte) *x509.CertPool {
   129  	if len(data) == 0 {
   130  		return pool
   131  	}
   132  
   133  	// Bit of a dance, need to ensure if AppendCertsFromPEM fails we still return
   134  	// nil and not an empty pool, so system roots still get used
   135  	var ret *x509.CertPool
   136  	if pool == nil {
   137  		ret = x509.NewCertPool()
   138  	} else {
   139  		ret = pool
   140  	}
   141  	if !ret.AppendCertsFromPEM(data) {
   142  		// Return unmodified input pool (may be nil, do not replace with empty)
   143  		return pool
   144  	}
   145  	return ret
   146  }