1package rootcerts
2
3import (
4	"crypto/tls"
5	"crypto/x509"
6	"fmt"
7	"io/ioutil"
8	"os"
9	"path/filepath"
10)
11
12// Config determines where LoadCACerts will load certificates from. When both
13// CAFile and CAPath are blank, this library's functions will either load
14// system roots explicitly and return them, or set the CertPool to nil to allow
15// Go's standard library to load system certs.
16type Config struct {
17	// CAFile is a path to a PEM-encoded certificate file or bundle. Takes
18	// precedence over CAPath.
19	CAFile string
20
21	// CAPath is a path to a directory populated with PEM-encoded certificates.
22	CAPath string
23}
24
25// ConfigureTLS sets up the RootCAs on the provided tls.Config based on the
26// Config specified.
27func ConfigureTLS(t *tls.Config, c *Config) error {
28	if t == nil {
29		return nil
30	}
31	pool, err := LoadCACerts(c)
32	if err != nil {
33		return err
34	}
35	t.RootCAs = pool
36	return nil
37}
38
39// LoadCACerts loads a CertPool based on the Config specified.
40func LoadCACerts(c *Config) (*x509.CertPool, error) {
41	if c == nil {
42		c = &Config{}
43	}
44	if c.CAFile != "" {
45		return LoadCAFile(c.CAFile)
46	}
47	if c.CAPath != "" {
48		return LoadCAPath(c.CAPath)
49	}
50
51	return LoadSystemCAs()
52}
53
54// LoadCAFile loads a single PEM-encoded file from the path specified.
55func LoadCAFile(caFile string) (*x509.CertPool, error) {
56	pool := x509.NewCertPool()
57
58	pem, err := ioutil.ReadFile(caFile)
59	if err != nil {
60		return nil, fmt.Errorf("Error loading CA File: %s", err)
61	}
62
63	ok := pool.AppendCertsFromPEM(pem)
64	if !ok {
65		return nil, fmt.Errorf("Error loading CA File: Couldn't parse PEM in: %s", caFile)
66	}
67
68	return pool, nil
69}
70
71// LoadCAPath walks the provided path and loads all certificates encounted into
72// a pool.
73func LoadCAPath(caPath string) (*x509.CertPool, error) {
74	pool := x509.NewCertPool()
75	walkFn := func(path string, info os.FileInfo, err error) error {
76		if err != nil {
77			return err
78		}
79
80		if info.IsDir() {
81			return nil
82		}
83
84		pem, err := ioutil.ReadFile(path)
85		if err != nil {
86			return fmt.Errorf("Error loading file from CAPath: %s", err)
87		}
88
89		ok := pool.AppendCertsFromPEM(pem)
90		if !ok {
91			return fmt.Errorf("Error loading CA Path: Couldn't parse PEM in: %s", path)
92		}
93
94		return nil
95	}
96
97	err := filepath.Walk(caPath, walkFn)
98	if err != nil {
99		return nil, err
100	}
101
102	return pool, nil
103}
104