1// Package config contains the configuration logic for CFSSL.
2package config
3
4import (
5	"crypto/tls"
6	"crypto/x509"
7	"encoding/asn1"
8	"encoding/json"
9	"errors"
10	"fmt"
11	"io/ioutil"
12	"regexp"
13	"strconv"
14	"strings"
15	"time"
16
17	"github.com/cloudflare/cfssl/auth"
18	cferr "github.com/cloudflare/cfssl/errors"
19	"github.com/cloudflare/cfssl/helpers"
20	"github.com/cloudflare/cfssl/log"
21	ocspConfig "github.com/cloudflare/cfssl/ocsp/config"
22)
23
24// A CSRWhitelist stores booleans for fields in the CSR. If a CSRWhitelist is
25// not present in a SigningProfile, all of these fields may be copied from the
26// CSR into the signed certificate. If a CSRWhitelist *is* present in a
27// SigningProfile, only those fields with a `true` value in the CSRWhitelist may
28// be copied from the CSR to the signed certificate. Note that some of these
29// fields, like Subject, can be provided or partially provided through the API.
30// Since API clients are expected to be trusted, but CSRs are not, fields
31// provided through the API are not subject to whitelisting through this
32// mechanism.
33type CSRWhitelist struct {
34	Subject, PublicKeyAlgorithm, PublicKey, SignatureAlgorithm bool
35	DNSNames, IPAddresses, EmailAddresses                      bool
36}
37
38// OID is our own version of asn1's ObjectIdentifier, so we can define a custom
39// JSON marshal / unmarshal.
40type OID asn1.ObjectIdentifier
41
42// CertificatePolicy represents the ASN.1 PolicyInformation structure from
43// https://tools.ietf.org/html/rfc3280.html#page-106.
44// Valid values of Type are "id-qt-unotice" and "id-qt-cps"
45type CertificatePolicy struct {
46	ID         OID
47	Qualifiers []CertificatePolicyQualifier
48}
49
50// CertificatePolicyQualifier represents a single qualifier from an ASN.1
51// PolicyInformation structure.
52type CertificatePolicyQualifier struct {
53	Type  string
54	Value string
55}
56
57// AuthRemote is an authenticated remote signer.
58type AuthRemote struct {
59	RemoteName  string `json:"remote"`
60	AuthKeyName string `json:"auth_key"`
61}
62
63// CAConstraint specifies various CA constraints on the signed certificate.
64// CAConstraint would verify against (and override) the CA
65// extensions in the given CSR.
66type CAConstraint struct {
67	IsCA           bool `json:"is_ca"`
68	MaxPathLen     int  `json:"max_path_len"`
69	MaxPathLenZero bool `json:"max_path_len_zero"`
70}
71
72// A SigningProfile stores information that the CA needs to store
73// signature policy.
74type SigningProfile struct {
75	Usage               []string     `json:"usages"`
76	IssuerURL           []string     `json:"issuer_urls"`
77	OCSP                string       `json:"ocsp_url"`
78	CRL                 string       `json:"crl_url"`
79	CAConstraint        CAConstraint `json:"ca_constraint"`
80	OCSPNoCheck         bool         `json:"ocsp_no_check"`
81	ExpiryString        string       `json:"expiry"`
82	BackdateString      string       `json:"backdate"`
83	AuthKeyName         string       `json:"auth_key"`
84	RemoteName          string       `json:"remote"`
85	NotBefore           time.Time    `json:"not_before"`
86	NotAfter            time.Time    `json:"not_after"`
87	NameWhitelistString string       `json:"name_whitelist"`
88	AuthRemote          AuthRemote   `json:"auth_remote"`
89	CTLogServers        []string     `json:"ct_log_servers"`
90	AllowedExtensions   []OID        `json:"allowed_extensions"`
91	CertStore           string       `json:"cert_store"`
92
93	Policies                    []CertificatePolicy
94	Expiry                      time.Duration
95	Backdate                    time.Duration
96	Provider                    auth.Provider
97	RemoteProvider              auth.Provider
98	RemoteServer                string
99	RemoteCAs                   *x509.CertPool
100	ClientCert                  *tls.Certificate
101	CSRWhitelist                *CSRWhitelist
102	NameWhitelist               *regexp.Regexp
103	ExtensionWhitelist          map[string]bool
104	ClientProvidesSerialNumbers bool
105}
106
107// UnmarshalJSON unmarshals a JSON string into an OID.
108func (oid *OID) UnmarshalJSON(data []byte) (err error) {
109	if data[0] != '"' || data[len(data)-1] != '"' {
110		return errors.New("OID JSON string not wrapped in quotes." + string(data))
111	}
112	data = data[1 : len(data)-1]
113	parsedOid, err := parseObjectIdentifier(string(data))
114	if err != nil {
115		return err
116	}
117	*oid = OID(parsedOid)
118	return
119}
120
121// MarshalJSON marshals an oid into a JSON string.
122func (oid OID) MarshalJSON() ([]byte, error) {
123	return []byte(fmt.Sprintf(`"%v"`, asn1.ObjectIdentifier(oid))), nil
124}
125
126func parseObjectIdentifier(oidString string) (oid asn1.ObjectIdentifier, err error) {
127	validOID, err := regexp.MatchString("\\d(\\.\\d+)*", oidString)
128	if err != nil {
129		return
130	}
131	if !validOID {
132		err = errors.New("Invalid OID")
133		return
134	}
135
136	segments := strings.Split(oidString, ".")
137	oid = make(asn1.ObjectIdentifier, len(segments))
138	for i, intString := range segments {
139		oid[i], err = strconv.Atoi(intString)
140		if err != nil {
141			return
142		}
143	}
144	return
145}
146
147const timeFormat = "2006-01-02T15:04:05"
148
149// populate is used to fill in the fields that are not in JSON
150//
151// First, the ExpiryString parameter is needed to parse
152// expiration timestamps from JSON. The JSON decoder is not able to
153// decode a string time duration to a time.Duration, so this is called
154// when loading the configuration to properly parse and fill out the
155// Expiry parameter.
156// This function is also used to create references to the auth key
157// and default remote for the profile.
158// It returns true if ExpiryString is a valid representation of a
159// time.Duration, and the AuthKeyString and RemoteName point to
160// valid objects. It returns false otherwise.
161func (p *SigningProfile) populate(cfg *Config) error {
162	if p == nil {
163		return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("can't parse nil profile"))
164	}
165
166	var err error
167	if p.RemoteName == "" && p.AuthRemote.RemoteName == "" {
168		log.Debugf("parse expiry in profile")
169		if p.ExpiryString == "" {
170			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("empty expiry string"))
171		}
172
173		dur, err := time.ParseDuration(p.ExpiryString)
174		if err != nil {
175			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
176		}
177
178		log.Debugf("expiry is valid")
179		p.Expiry = dur
180
181		if p.BackdateString != "" {
182			dur, err = time.ParseDuration(p.BackdateString)
183			if err != nil {
184				return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
185			}
186
187			p.Backdate = dur
188		}
189
190		if !p.NotBefore.IsZero() && !p.NotAfter.IsZero() && p.NotAfter.Before(p.NotBefore) {
191			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
192		}
193
194		if len(p.Policies) > 0 {
195			for _, policy := range p.Policies {
196				for _, qualifier := range policy.Qualifiers {
197					if qualifier.Type != "" && qualifier.Type != "id-qt-unotice" && qualifier.Type != "id-qt-cps" {
198						return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
199							errors.New("invalid policy qualifier type"))
200					}
201				}
202			}
203		}
204	} else if p.RemoteName != "" {
205		log.Debug("match remote in profile to remotes section")
206		if p.AuthRemote.RemoteName != "" {
207			log.Error("profile has both a remote and an auth remote specified")
208			return cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
209		}
210		if remote := cfg.Remotes[p.RemoteName]; remote != "" {
211			if err := p.updateRemote(remote); err != nil {
212				return err
213			}
214		} else {
215			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
216				errors.New("failed to find remote in remotes section"))
217		}
218	} else {
219		log.Debug("match auth remote in profile to remotes section")
220		if remote := cfg.Remotes[p.AuthRemote.RemoteName]; remote != "" {
221			if err := p.updateRemote(remote); err != nil {
222				return err
223			}
224		} else {
225			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
226				errors.New("failed to find remote in remotes section"))
227		}
228	}
229
230	if p.AuthKeyName != "" {
231		log.Debug("match auth key in profile to auth_keys section")
232		if key, ok := cfg.AuthKeys[p.AuthKeyName]; ok == true {
233			if key.Type == "standard" {
234				p.Provider, err = auth.New(key.Key, nil)
235				if err != nil {
236					log.Debugf("failed to create new standard auth provider: %v", err)
237					return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
238						errors.New("failed to create new standard auth provider"))
239				}
240			} else {
241				log.Debugf("unknown authentication type %v", key.Type)
242				return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
243					errors.New("unknown authentication type"))
244			}
245		} else {
246			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
247				errors.New("failed to find auth_key in auth_keys section"))
248		}
249	}
250
251	if p.AuthRemote.AuthKeyName != "" {
252		log.Debug("match auth remote key in profile to auth_keys section")
253		if key, ok := cfg.AuthKeys[p.AuthRemote.AuthKeyName]; ok == true {
254			if key.Type == "standard" {
255				p.RemoteProvider, err = auth.New(key.Key, nil)
256				if err != nil {
257					log.Debugf("failed to create new standard auth provider: %v", err)
258					return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
259						errors.New("failed to create new standard auth provider"))
260				}
261			} else {
262				log.Debugf("unknown authentication type %v", key.Type)
263				return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
264					errors.New("unknown authentication type"))
265			}
266		} else {
267			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
268				errors.New("failed to find auth_remote's auth_key in auth_keys section"))
269		}
270	}
271
272	if p.NameWhitelistString != "" {
273		log.Debug("compiling whitelist regular expression")
274		rule, err := regexp.Compile(p.NameWhitelistString)
275		if err != nil {
276			return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
277				errors.New("failed to compile name whitelist section"))
278		}
279		p.NameWhitelist = rule
280	}
281
282	p.ExtensionWhitelist = map[string]bool{}
283	for _, oid := range p.AllowedExtensions {
284		p.ExtensionWhitelist[asn1.ObjectIdentifier(oid).String()] = true
285	}
286
287	return nil
288}
289
290// updateRemote takes a signing profile and initializes the remote server object
291// to the hostname:port combination sent by remote.
292func (p *SigningProfile) updateRemote(remote string) error {
293	if remote != "" {
294		p.RemoteServer = remote
295	}
296	return nil
297}
298
299// OverrideRemotes takes a signing configuration and updates the remote server object
300// to the hostname:port combination sent by remote
301func (p *Signing) OverrideRemotes(remote string) error {
302	if remote != "" {
303		var err error
304		for _, profile := range p.Profiles {
305			err = profile.updateRemote(remote)
306			if err != nil {
307				return err
308			}
309		}
310		err = p.Default.updateRemote(remote)
311		if err != nil {
312			return err
313		}
314	}
315	return nil
316}
317
318// SetClientCertKeyPairFromFile updates the properties to set client certificates for mutual
319// authenticated TLS remote requests
320func (p *Signing) SetClientCertKeyPairFromFile(certFile string, keyFile string) error {
321	if certFile != "" && keyFile != "" {
322		cert, err := helpers.LoadClientCertificate(certFile, keyFile)
323		if err != nil {
324			return err
325		}
326		for _, profile := range p.Profiles {
327			profile.ClientCert = cert
328		}
329		p.Default.ClientCert = cert
330	}
331	return nil
332}
333
334// SetRemoteCAsFromFile reads root CAs from file and updates the properties to set remote CAs for TLS
335// remote requests
336func (p *Signing) SetRemoteCAsFromFile(caFile string) error {
337	if caFile != "" {
338		remoteCAs, err := helpers.LoadPEMCertPool(caFile)
339		if err != nil {
340			return err
341		}
342		p.SetRemoteCAs(remoteCAs)
343	}
344	return nil
345}
346
347// SetRemoteCAs updates the properties to set remote CAs for TLS
348// remote requests
349func (p *Signing) SetRemoteCAs(remoteCAs *x509.CertPool) {
350	for _, profile := range p.Profiles {
351		profile.RemoteCAs = remoteCAs
352	}
353	p.Default.RemoteCAs = remoteCAs
354}
355
356// NeedsRemoteSigner returns true if one of the profiles has a remote set
357func (p *Signing) NeedsRemoteSigner() bool {
358	for _, profile := range p.Profiles {
359		if profile.RemoteServer != "" {
360			return true
361		}
362	}
363
364	if p.Default.RemoteServer != "" {
365		return true
366	}
367
368	return false
369}
370
371// NeedsLocalSigner returns true if one of the profiles doe not have a remote set
372func (p *Signing) NeedsLocalSigner() bool {
373	for _, profile := range p.Profiles {
374		if profile.RemoteServer == "" {
375			return true
376		}
377	}
378
379	if p.Default.RemoteServer == "" {
380		return true
381	}
382
383	return false
384}
385
386// Usages parses the list of key uses in the profile, translating them
387// to a list of X.509 key usages and extended key usages.  The unknown
388// uses are collected into a slice that is also returned.
389func (p *SigningProfile) Usages() (ku x509.KeyUsage, eku []x509.ExtKeyUsage, unk []string) {
390	for _, keyUse := range p.Usage {
391		if kuse, ok := KeyUsage[keyUse]; ok {
392			ku |= kuse
393		} else if ekuse, ok := ExtKeyUsage[keyUse]; ok {
394			eku = append(eku, ekuse)
395		} else {
396			unk = append(unk, keyUse)
397		}
398	}
399	return
400}
401
402// A valid profile must be a valid local profile or a valid remote profile.
403// A valid local profile has defined at least key usages to be used, and a
404// valid local default profile has defined at least a default expiration.
405// A valid remote profile (default or not) has remote signer initialized.
406// In addition, a remote profile must has a valid auth provider if auth
407// key defined.
408func (p *SigningProfile) validProfile(isDefault bool) bool {
409	if p == nil {
410		return false
411	}
412
413	if p.AuthRemote.RemoteName == "" && p.AuthRemote.AuthKeyName != "" {
414		log.Debugf("invalid auth remote profile: no remote signer specified")
415		return false
416	}
417
418	if p.RemoteName != "" {
419		log.Debugf("validate remote profile")
420
421		if p.RemoteServer == "" {
422			log.Debugf("invalid remote profile: no remote signer specified")
423			return false
424		}
425
426		if p.AuthKeyName != "" && p.Provider == nil {
427			log.Debugf("invalid remote profile: auth key name is defined but no auth provider is set")
428			return false
429		}
430
431		if p.AuthRemote.RemoteName != "" {
432			log.Debugf("invalid remote profile: auth remote is also specified")
433			return false
434		}
435	} else if p.AuthRemote.RemoteName != "" {
436		log.Debugf("validate auth remote profile")
437		if p.RemoteServer == "" {
438			log.Debugf("invalid auth remote profile: no remote signer specified")
439			return false
440		}
441
442		if p.AuthRemote.AuthKeyName == "" || p.RemoteProvider == nil {
443			log.Debugf("invalid auth remote profile: no auth key is defined")
444			return false
445		}
446	} else {
447		log.Debugf("validate local profile")
448		if !isDefault {
449			if len(p.Usage) == 0 {
450				log.Debugf("invalid local profile: no usages specified")
451				return false
452			} else if _, _, unk := p.Usages(); len(unk) == len(p.Usage) {
453				log.Debugf("invalid local profile: no valid usages")
454				return false
455			}
456		} else {
457			if p.Expiry == 0 {
458				log.Debugf("invalid local profile: no expiry set")
459				return false
460			}
461		}
462	}
463
464	log.Debugf("profile is valid")
465	return true
466}
467
468// This checks if the SigningProfile object contains configurations that are only effective with a local signer
469// which has access to CA private key.
470func (p *SigningProfile) hasLocalConfig() bool {
471	if p.Usage != nil ||
472		p.IssuerURL != nil ||
473		p.OCSP != "" ||
474		p.ExpiryString != "" ||
475		p.BackdateString != "" ||
476		p.CAConstraint.IsCA != false ||
477		!p.NotBefore.IsZero() ||
478		!p.NotAfter.IsZero() ||
479		p.NameWhitelistString != "" ||
480		len(p.CTLogServers) != 0 {
481		return true
482	}
483	return false
484}
485
486// warnSkippedSettings prints a log warning message about skipped settings
487// in a SigningProfile, usually due to remote signer.
488func (p *Signing) warnSkippedSettings() {
489	const warningMessage = `The configuration value by "usages", "issuer_urls", "ocsp_url", "crl_url", "ca_constraint", "expiry", "backdate", "not_before", "not_after", "cert_store" and "ct_log_servers" are skipped`
490	if p == nil {
491		return
492	}
493
494	if (p.Default.RemoteName != "" || p.Default.AuthRemote.RemoteName != "") && p.Default.hasLocalConfig() {
495		log.Warning("default profile points to a remote signer: ", warningMessage)
496	}
497
498	for name, profile := range p.Profiles {
499		if (profile.RemoteName != "" || profile.AuthRemote.RemoteName != "") && profile.hasLocalConfig() {
500			log.Warningf("Profiles[%s] points to a remote signer: %s", name, warningMessage)
501		}
502	}
503}
504
505// Signing codifies the signature configuration policy for a CA.
506type Signing struct {
507	Profiles map[string]*SigningProfile `json:"profiles"`
508	Default  *SigningProfile            `json:"default"`
509}
510
511// Config stores configuration information for the CA.
512type Config struct {
513	Signing  *Signing           `json:"signing"`
514	OCSP     *ocspConfig.Config `json:"ocsp"`
515	AuthKeys map[string]AuthKey `json:"auth_keys,omitempty"`
516	Remotes  map[string]string  `json:"remotes,omitempty"`
517}
518
519// Valid ensures that Config is a valid configuration. It should be
520// called immediately after parsing a configuration file.
521func (c *Config) Valid() bool {
522	return c.Signing.Valid()
523}
524
525// Valid checks the signature policies, ensuring they are valid
526// policies. A policy is valid if it has defined at least key usages
527// to be used, and a valid default profile has defined at least a
528// default expiration.
529func (p *Signing) Valid() bool {
530	if p == nil {
531		return false
532	}
533
534	log.Debugf("validating configuration")
535	if !p.Default.validProfile(true) {
536		log.Debugf("default profile is invalid")
537		return false
538	}
539
540	for _, sp := range p.Profiles {
541		if !sp.validProfile(false) {
542			log.Debugf("invalid profile")
543			return false
544		}
545	}
546
547	p.warnSkippedSettings()
548
549	return true
550}
551
552// KeyUsage contains a mapping of string names to key usages.
553var KeyUsage = map[string]x509.KeyUsage{
554	"signing":            x509.KeyUsageDigitalSignature,
555	"digital signature":  x509.KeyUsageDigitalSignature,
556	"content commitment": x509.KeyUsageContentCommitment,
557	"key encipherment":   x509.KeyUsageKeyEncipherment,
558	"key agreement":      x509.KeyUsageKeyAgreement,
559	"data encipherment":  x509.KeyUsageDataEncipherment,
560	"cert sign":          x509.KeyUsageCertSign,
561	"crl sign":           x509.KeyUsageCRLSign,
562	"encipher only":      x509.KeyUsageEncipherOnly,
563	"decipher only":      x509.KeyUsageDecipherOnly,
564}
565
566// ExtKeyUsage contains a mapping of string names to extended key
567// usages.
568var ExtKeyUsage = map[string]x509.ExtKeyUsage{
569	"any":              x509.ExtKeyUsageAny,
570	"server auth":      x509.ExtKeyUsageServerAuth,
571	"client auth":      x509.ExtKeyUsageClientAuth,
572	"code signing":     x509.ExtKeyUsageCodeSigning,
573	"email protection": x509.ExtKeyUsageEmailProtection,
574	"s/mime":           x509.ExtKeyUsageEmailProtection,
575	"ipsec end system": x509.ExtKeyUsageIPSECEndSystem,
576	"ipsec tunnel":     x509.ExtKeyUsageIPSECTunnel,
577	"ipsec user":       x509.ExtKeyUsageIPSECUser,
578	"timestamping":     x509.ExtKeyUsageTimeStamping,
579	"ocsp signing":     x509.ExtKeyUsageOCSPSigning,
580	"microsoft sgc":    x509.ExtKeyUsageMicrosoftServerGatedCrypto,
581	"netscape sgc":     x509.ExtKeyUsageNetscapeServerGatedCrypto,
582}
583
584// An AuthKey contains an entry for a key used for authentication.
585type AuthKey struct {
586	// Type contains information needed to select the appropriate
587	// constructor. For example, "standard" for HMAC-SHA-256,
588	// "standard-ip" for HMAC-SHA-256 incorporating the client's
589	// IP.
590	Type string `json:"type"`
591	// Key contains the key information, such as a hex-encoded
592	// HMAC key.
593	Key string `json:"key"`
594}
595
596// DefaultConfig returns a default configuration specifying basic key
597// usage and a 1 year expiration time. The key usages chosen are
598// signing, key encipherment, client auth and server auth.
599func DefaultConfig() *SigningProfile {
600	d := helpers.OneYear
601	return &SigningProfile{
602		Usage:        []string{"signing", "key encipherment", "server auth", "client auth"},
603		Expiry:       d,
604		ExpiryString: "8760h",
605	}
606}
607
608// LoadFile attempts to load the configuration file stored at the path
609// and returns the configuration. On error, it returns nil.
610func LoadFile(path string) (*Config, error) {
611	log.Debugf("loading configuration file from %s", path)
612	if path == "" {
613		return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path"))
614	}
615
616	body, err := ioutil.ReadFile(path)
617	if err != nil {
618		return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file"))
619	}
620
621	return LoadConfig(body)
622}
623
624// LoadConfig attempts to load the configuration from a byte slice.
625// On error, it returns nil.
626func LoadConfig(config []byte) (*Config, error) {
627	var cfg = &Config{}
628	err := json.Unmarshal(config, &cfg)
629	if err != nil {
630		return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
631			errors.New("failed to unmarshal configuration: "+err.Error()))
632	}
633
634	if cfg.Signing == nil {
635		return nil, errors.New("No \"signing\" field present")
636	}
637
638	if cfg.Signing.Default == nil {
639		log.Debugf("no default given: using default config")
640		cfg.Signing.Default = DefaultConfig()
641	} else {
642		if err := cfg.Signing.Default.populate(cfg); err != nil {
643			return nil, err
644		}
645	}
646
647	for k := range cfg.Signing.Profiles {
648		if err := cfg.Signing.Profiles[k].populate(cfg); err != nil {
649			return nil, err
650		}
651	}
652
653	if !cfg.Valid() {
654		return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid configuration"))
655	}
656
657	log.Debugf("configuration ok")
658	return cfg, nil
659}
660