1 // Package config contains the configuration logic for CFSSL.
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"
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
33 type CSRWhitelist struct {
34 Subject, PublicKeyAlgorithm, PublicKey, SignatureAlgorithm bool
35 DNSNames, IPAddresses, EmailAddresses bool
38 // OID is our own version of asn1's ObjectIdentifier, so we can define a custom
39 // JSON marshal / unmarshal.
40 type OID asn1.ObjectIdentifier
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"
45 type CertificatePolicy struct {
47 Qualifiers []CertificatePolicyQualifier
50 // CertificatePolicyQualifier represents a single qualifier from an ASN.1
51 // PolicyInformation structure.
52 type CertificatePolicyQualifier struct {
57 // AuthRemote is an authenticated remote signer.
58 type AuthRemote struct {
59 RemoteName string `json:"remote"`
60 AuthKeyName string `json:"auth_key"`
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.
66 type CAConstraint struct {
67 IsCA bool `json:"is_ca"`
68 MaxPathLen int `json:"max_path_len"`
69 MaxPathLenZero bool `json:"max_path_len_zero"`
72 // A SigningProfile stores information that the CA needs to store
74 type 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"`
93 Policies []CertificatePolicy
95 Backdate time.Duration
96 Provider auth.Provider
97 RemoteProvider auth.Provider
99 RemoteCAs *x509.CertPool
100 ClientCert *tls.Certificate
101 CSRWhitelist *CSRWhitelist
102 NameWhitelist *regexp.Regexp
103 ExtensionWhitelist map[string]bool
104 ClientProvidesSerialNumbers bool
107 // UnmarshalJSON unmarshals a JSON string into an OID.
108 func (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))
112 data = data[1 : len(data)-1]
113 parsedOid, err := parseObjectIdentifier(string(data))
117 *oid = OID(parsedOid)
121 // MarshalJSON marshals an oid into a JSON string.
122 func (oid OID) MarshalJSON() ([]byte, error) {
123 return []byte(fmt.Sprintf(`"%v"`, asn1.ObjectIdentifier(oid))), nil
126 func parseObjectIdentifier(oidString string) (oid asn1.ObjectIdentifier, err error) {
127 validOID, err := regexp.MatchString("\\d(\\.\\d+)*", oidString)
132 err = errors.New("Invalid OID")
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)
147 const timeFormat = "2006-01-02T15:04:05"
149 // populate is used to fill in the fields that are not in JSON
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
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.
161 func (p *SigningProfile) populate(cfg *Config) error {
163 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("can't parse nil profile"))
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"))
173 dur, err := time.ParseDuration(p.ExpiryString)
175 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
178 log.Debugf("expiry is valid")
181 if p.BackdateString != "" {
182 dur, err = time.ParseDuration(p.BackdateString)
184 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
190 if !p.NotBefore.IsZero() && !p.NotAfter.IsZero() && p.NotAfter.Before(p.NotBefore) {
191 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
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"))
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)
210 if remote := cfg.Remotes[p.RemoteName]; remote != "" {
211 if err := p.updateRemote(remote); err != nil {
215 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
216 errors.New("failed to find remote in remotes section"))
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 {
225 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
226 errors.New("failed to find remote in remotes section"))
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)
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"))
241 log.Debugf("unknown authentication type %v", key.Type)
242 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
243 errors.New("unknown authentication type"))
246 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
247 errors.New("failed to find auth_key in auth_keys section"))
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)
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"))
262 log.Debugf("unknown authentication type %v", key.Type)
263 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
264 errors.New("unknown authentication type"))
267 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
268 errors.New("failed to find auth_remote's auth_key in auth_keys section"))
272 if p.NameWhitelistString != "" {
273 log.Debug("compiling whitelist regular expression")
274 rule, err := regexp.Compile(p.NameWhitelistString)
276 return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
277 errors.New("failed to compile name whitelist section"))
279 p.NameWhitelist = rule
282 p.ExtensionWhitelist = map[string]bool{}
283 for _, oid := range p.AllowedExtensions {
284 p.ExtensionWhitelist[asn1.ObjectIdentifier(oid).String()] = true
290 // updateRemote takes a signing profile and initializes the remote server object
291 // to the hostname:port combination sent by remote.
292 func (p *SigningProfile) updateRemote(remote string) error {
294 p.RemoteServer = remote
299 // OverrideRemotes takes a signing configuration and updates the remote server object
300 // to the hostname:port combination sent by remote
301 func (p *Signing) OverrideRemotes(remote string) error {
304 for _, profile := range p.Profiles {
305 err = profile.updateRemote(remote)
310 err = p.Default.updateRemote(remote)
318 // SetClientCertKeyPairFromFile updates the properties to set client certificates for mutual
319 // authenticated TLS remote requests
320 func (p *Signing) SetClientCertKeyPairFromFile(certFile string, keyFile string) error {
321 if certFile != "" && keyFile != "" {
322 cert, err := helpers.LoadClientCertificate(certFile, keyFile)
326 for _, profile := range p.Profiles {
327 profile.ClientCert = cert
329 p.Default.ClientCert = cert
334 // SetRemoteCAsFromFile reads root CAs from file and updates the properties to set remote CAs for TLS
336 func (p *Signing) SetRemoteCAsFromFile(caFile string) error {
338 remoteCAs, err := helpers.LoadPEMCertPool(caFile)
342 p.SetRemoteCAs(remoteCAs)
347 // SetRemoteCAs updates the properties to set remote CAs for TLS
349 func (p *Signing) SetRemoteCAs(remoteCAs *x509.CertPool) {
350 for _, profile := range p.Profiles {
351 profile.RemoteCAs = remoteCAs
353 p.Default.RemoteCAs = remoteCAs
356 // NeedsRemoteSigner returns true if one of the profiles has a remote set
357 func (p *Signing) NeedsRemoteSigner() bool {
358 for _, profile := range p.Profiles {
359 if profile.RemoteServer != "" {
364 if p.Default.RemoteServer != "" {
371 // NeedsLocalSigner returns true if one of the profiles doe not have a remote set
372 func (p *Signing) NeedsLocalSigner() bool {
373 for _, profile := range p.Profiles {
374 if profile.RemoteServer == "" {
379 if p.Default.RemoteServer == "" {
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.
389 func (p *SigningProfile) Usages() (ku x509.KeyUsage, eku []x509.ExtKeyUsage, unk []string) {
390 for _, keyUse := range p.Usage {
391 if kuse, ok := KeyUsage[keyUse]; ok {
393 } else if ekuse, ok := ExtKeyUsage[keyUse]; ok {
394 eku = append(eku, ekuse)
396 unk = append(unk, keyUse)
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
408 func (p *SigningProfile) validProfile(isDefault bool) bool {
413 if p.AuthRemote.RemoteName == "" && p.AuthRemote.AuthKeyName != "" {
414 log.Debugf("invalid auth remote profile: no remote signer specified")
418 if p.RemoteName != "" {
419 log.Debugf("validate remote profile")
421 if p.RemoteServer == "" {
422 log.Debugf("invalid remote profile: no remote signer specified")
426 if p.AuthKeyName != "" && p.Provider == nil {
427 log.Debugf("invalid remote profile: auth key name is defined but no auth provider is set")
431 if p.AuthRemote.RemoteName != "" {
432 log.Debugf("invalid remote profile: auth remote is also specified")
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")
442 if p.AuthRemote.AuthKeyName == "" || p.RemoteProvider == nil {
443 log.Debugf("invalid auth remote profile: no auth key is defined")
447 log.Debugf("validate local profile")
449 if len(p.Usage) == 0 {
450 log.Debugf("invalid local profile: no usages specified")
452 } else if _, _, unk := p.Usages(); len(unk) == len(p.Usage) {
453 log.Debugf("invalid local profile: no valid usages")
458 log.Debugf("invalid local profile: no expiry set")
464 log.Debugf("profile is valid")
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.
470 func (p *SigningProfile) hasLocalConfig() bool {
472 p.IssuerURL != nil ||
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 {
486 // warnSkippedSettings prints a log warning message about skipped settings
487 // in a SigningProfile, usually due to remote signer.
488 func (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`
494 if (p.Default.RemoteName != "" || p.Default.AuthRemote.RemoteName != "") && p.Default.hasLocalConfig() {
495 log.Warning("default profile points to a remote signer: ", warningMessage)
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)
505 // Signing codifies the signature configuration policy for a CA.
506 type Signing struct {
507 Profiles map[string]*SigningProfile `json:"profiles"`
508 Default *SigningProfile `json:"default"`
511 // Config stores configuration information for the CA.
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"`
519 // Valid ensures that Config is a valid configuration. It should be
520 // called immediately after parsing a configuration file.
521 func (c *Config) Valid() bool {
522 return c.Signing.Valid()
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.
529 func (p *Signing) Valid() bool {
534 log.Debugf("validating configuration")
535 if !p.Default.validProfile(true) {
536 log.Debugf("default profile is invalid")
540 for _, sp := range p.Profiles {
541 if !sp.validProfile(false) {
542 log.Debugf("invalid profile")
547 p.warnSkippedSettings()
552 // KeyUsage contains a mapping of string names to key usages.
553 var KeyUsage = map[string]x509.KeyUsage{
554 "signing": x509.KeyUsageDigitalSignature,
555 "digital signature": x509.KeyUsageDigitalSignature,
556 "content committment": 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,
566 // ExtKeyUsage contains a mapping of string names to extended key
568 var 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,
584 // An AuthKey contains an entry for a key used for authentication.
585 type 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
590 Type string `json:"type"`
591 // Key contains the key information, such as a hex-encoded
593 Key string `json:"key"`
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.
599 func DefaultConfig() *SigningProfile {
601 return &SigningProfile{
602 Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
604 ExpiryString: "8760h",
608 // LoadFile attempts to load the configuration file stored at the path
609 // and returns the configuration. On error, it returns nil.
610 func LoadFile(path string) (*Config, error) {
611 log.Debugf("loading configuration file from %s", path)
613 return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path"))
616 body, err := ioutil.ReadFile(path)
618 return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file"))
621 return LoadConfig(body)
624 // LoadConfig attempts to load the configuration from a byte slice.
625 // On error, it returns nil.
626 func LoadConfig(config []byte) (*Config, error) {
628 err := json.Unmarshal(config, &cfg)
630 return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
631 errors.New("failed to unmarshal configuration: "+err.Error()))
634 if cfg.Signing == nil {
635 return nil, errors.New("No \"signing\" field present")
638 if cfg.Signing.Default == nil {
639 log.Debugf("no default given: using default config")
640 cfg.Signing.Default = DefaultConfig()
642 if err := cfg.Signing.Default.populate(cfg); err != nil {
647 for k := range cfg.Signing.Profiles {
648 if err := cfg.Signing.Profiles[k].populate(cfg); err != nil {
654 return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid configuration"))
657 log.Debugf("configuration ok")