Convert x509util.WithOptions to new modifiers.

This commit is contained in:
Mariano Cano 2020-07-07 19:00:06 -07:00
parent dcb962bdde
commit 9032018cf2

View file

@ -6,63 +6,54 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/json"
"net"
"net/url"
"reflect"
"time"
"github.com/pkg/errors"
"github.com/smallstep/cli/crypto/x509util"
"github.com/smallstep/certificates/x509util"
"golang.org/x/crypto/ed25519"
)
// Options contains the options that can be passed to the Sign method. Backdate
// is automatically filled and can only be configured in the CA.
type Options struct {
NotAfter TimeDuration `json:"notAfter"`
NotBefore TimeDuration `json:"notBefore"`
Backdate time.Duration `json:"-"`
NotAfter TimeDuration `json:"notAfter"`
NotBefore TimeDuration `json:"notBefore"`
UserData json.RawMessage `json:"data"`
Backdate time.Duration `json:"-"`
}
// SignOption is the interface used to collect all extra options used in the
// Sign method.
type SignOption interface{}
// CertificateValidator is the interface used to validate a X.509 certificate.
// CertificateValidator is an interface used to validate a given X.509 certificate.
type CertificateValidator interface {
SignOption
Valid(cert *x509.Certificate, o Options) error
Valid(cert *x509.Certificate, opts Options) error
}
// CertificateRequestValidator is the interface used to validate a X.509
// certificate request.
// CertificateRequestValidator is an interface used to validate a given X.509 certificate request.
type CertificateRequestValidator interface {
SignOption
Valid(req *x509.CertificateRequest) error
Valid(cr *x509.CertificateRequest) error
}
// ProfileModifier is the interface used to add custom options to the profile
// constructor. The options are used to modify the final certificate.
type ProfileModifier interface {
SignOption
Option(o Options) x509util.WithOption
// CertificateModifier is an interface used to modify a given X.509 certificate.
// Types implementing this interface will be validated with a
// CertificateValidator.
type CertificateModifier interface {
Modify(cert *x509.Certificate, opts Options) error
}
// CertificateEnforcer is the interface used to modify a certificate after
// validation.
// CertificateEnforcer is an interface used to modify a given X.509 certificate.
// Types implemented this interface will NOT be validated with a
// CertificateValidator.
type CertificateEnforcer interface {
SignOption
Enforce(cert *x509.Certificate) error
}
// profileWithOption is a wrapper against x509util.WithOption to conform the
// interface.
type profileWithOption x509util.WithOption
func (v profileWithOption) Option(Options) x509util.WithOption {
return x509util.WithOption(v)
}
// emailOnlyIdentity is a CertificateRequestValidator that checks that the only
// SAN provided is the given email address.
type emailOnlyIdentity string
@ -254,27 +245,26 @@ func (eee ExtraExtsEnforcer) Enforce(cert *x509.Certificate) error {
return nil
}
// profileDefaultDuration is a wrapper against x509util.WithOption to conform
// the SignOption interface.
// profileDefaultDuration is a modifier that sets the certificate
// duration.
type profileDefaultDuration time.Duration
func (v profileDefaultDuration) Option(so Options) x509util.WithOption {
func (v profileDefaultDuration) Modify(cert *x509.Certificate, so Options) error {
var backdate time.Duration
notBefore := so.NotBefore.Time()
if notBefore.IsZero() {
notBefore = now()
backdate = -1 * so.Backdate
}
notAfter := so.NotAfter.RelativeTime(notBefore)
return func(p x509util.Profile) error {
fn := x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(v))
if err := fn(p); err != nil {
return err
}
crt := p.Subject()
crt.NotBefore = crt.NotBefore.Add(backdate)
return nil
if notAfter.IsZero() {
notAfter = notBefore.Add(time.Duration(v))
}
cert.NotBefore = notBefore.Add(backdate)
cert.NotAfter = notAfter
return nil
}
// profileLimitDuration is an x509 profile option that modifies an x509 validity
@ -286,40 +276,37 @@ type profileLimitDuration struct {
// Option returns an x509util option that limits the validity period of a
// certificate to one that is superficially imposed.
func (v profileLimitDuration) Option(so Options) x509util.WithOption {
return func(p x509util.Profile) error {
var backdate time.Duration
n := now()
notBefore := so.NotBefore.Time()
if notBefore.IsZero() {
notBefore = n
backdate = -1 * so.Backdate
}
if notBefore.Before(v.notBefore) {
return errors.Errorf("requested certificate notBefore (%s) is before "+
"the active validity window of the provisioning credential (%s)",
notBefore, v.notBefore)
}
notAfter := so.NotAfter.RelativeTime(notBefore)
if notAfter.After(v.notAfter) {
return errors.Errorf("requested certificate notAfter (%s) is after "+
"the expiration of the provisioning credential (%s)",
notAfter, v.notAfter)
}
if notAfter.IsZero() {
t := notBefore.Add(v.def)
if t.After(v.notAfter) {
notAfter = v.notAfter
} else {
notAfter = t
}
}
crt := p.Subject()
crt.NotBefore = notBefore.Add(backdate)
crt.NotAfter = notAfter
return nil
func (v profileLimitDuration) Modify(cert *x509.Certificate, so Options) error {
var backdate time.Duration
notBefore := so.NotBefore.Time()
if notBefore.IsZero() {
notBefore = now()
backdate = -1 * so.Backdate
}
if notBefore.Before(v.notBefore) {
return errors.Errorf("requested certificate notBefore (%s) is before "+
"the active validity window of the provisioning credential (%s)",
notBefore, v.notBefore)
}
notAfter := so.NotAfter.RelativeTime(notBefore)
if notAfter.After(v.notAfter) {
return errors.Errorf("requested certificate notAfter (%s) is after "+
"the expiration of the provisioning credential (%s)",
notAfter, v.notAfter)
}
if notAfter.IsZero() {
t := notBefore.Add(v.def)
if t.After(v.notAfter) {
notAfter = v.notAfter
} else {
notAfter = t
}
}
cert.NotBefore = notBefore.Add(backdate)
cert.NotAfter = notAfter
return nil
}
// validityValidator validates the certificate validity settings.
@ -385,22 +372,21 @@ func newForceCNOption(forceCN bool) *forceCNOption {
return &forceCNOption{forceCN}
}
func (o *forceCNOption) Option(Options) x509util.WithOption {
return func(p x509util.Profile) error {
if !o.ForceCN {
// Forcing CN is disabled, do nothing to certificate
return nil
}
crt := p.Subject()
if crt.Subject.CommonName == "" {
if len(crt.DNSNames) > 0 {
crt.Subject.CommonName = crt.DNSNames[0]
} else {
return errors.New("Cannot force CN, DNSNames is empty")
}
}
func (o *forceCNOption) Modify(cert *x509.Certificate, _ Options) error {
if !o.ForceCN {
// Forcing CN is disabled, do nothing to certificate
return nil
}
if cert.Subject.CommonName == "" {
if len(cert.DNSNames) > 0 {
cert.Subject.CommonName = cert.DNSNames[0]
} else {
return errors.New("Cannot force CN, DNSNames is empty")
}
}
return nil
}
type provisionerExtensionOption struct {
@ -419,23 +405,20 @@ func newProvisionerExtensionOption(typ Type, name, credentialID string, keyValue
}
}
func (o *provisionerExtensionOption) Option(Options) x509util.WithOption {
return func(p x509util.Profile) error {
crt := p.Subject()
ext, err := createProvisionerExtension(o.Type, o.Name, o.CredentialID, o.KeyValuePairs...)
if err != nil {
return err
}
// Prepend the provisioner extension. In the auth.Sign code we will
// force the resulting certificate to only have one extension, the
// first stepOIDProvisioner that is found in the ExtraExtensions.
// A client could pass a csr containing a malicious stepOIDProvisioner
// ExtraExtension. If we were to append (rather than prepend) the correct
// stepOIDProvisioner extension, then the resulting certificate would
// contain the malicious extension, rather than the one applied by step-ca.
crt.ExtraExtensions = append([]pkix.Extension{ext}, crt.ExtraExtensions...)
return nil
func (o *provisionerExtensionOption) Modify(cert *x509.Certificate, _ Options) error {
ext, err := createProvisionerExtension(o.Type, o.Name, o.CredentialID, o.KeyValuePairs...)
if err != nil {
return err
}
// Prepend the provisioner extension. In the auth.Sign code we will
// force the resulting certificate to only have one extension, the
// first stepOIDProvisioner that is found in the ExtraExtensions.
// A client could pass a csr containing a malicious stepOIDProvisioner
// ExtraExtension. If we were to append (rather than prepend) the correct
// stepOIDProvisioner extension, then the resulting certificate would
// contain the malicious extension, rather than the one applied by step-ca.
cert.ExtraExtensions = append([]pkix.Extension{ext}, cert.ExtraExtensions...)
return nil
}
func createProvisionerExtension(typ int, name, credentialID string, keyValuePairs ...string) (pkix.Extension, error) {
@ -454,8 +437,3 @@ func createProvisionerExtension(typ int, name, credentialID string, keyValuePair
Value: b,
}, nil
}
func init() {
// Avoid dead-code warning in profileWithOption
_ = profileWithOption(nil)
}