Replace instead of prepend provisioner extension

With non standard SANs this will generate the SAN and provisioner
extension in the same order.
This commit is contained in:
Mariano Cano 2022-08-09 16:48:00 -07:00
parent 2ab1e6658e
commit 21427d5d65
2 changed files with 29 additions and 26 deletions

View file

@ -5,7 +5,6 @@ import (
"crypto/ed25519" "crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/json" "encoding/json"
"net" "net"
"net/http" "net/http"
@ -425,18 +424,6 @@ func (v *x509NamePolicyValidator) Valid(cert *x509.Certificate, _ SignOptions) e
return v.policyEngine.IsX509CertificateAllowed(cert) return v.policyEngine.IsX509CertificateAllowed(cert)
} }
// var (
// stepOIDRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64}
// stepOIDProvisioner = append(asn1.ObjectIdentifier(nil), append(stepOIDRoot, 1)...)
// )
// type stepProvisionerASN1 struct {
// Type int
// Name []byte
// CredentialID []byte
// KeyValuePairs []string `asn1:"optional,omitempty"`
// }
type forceCNOption struct { type forceCNOption struct {
ForceCN bool ForceCN bool
} }
@ -481,13 +468,14 @@ func (o *provisionerExtensionOption) Modify(cert *x509.Certificate, _ SignOption
if err != nil { if err != nil {
return errs.NewError(http.StatusInternalServerError, err, "error creating certificate") return errs.NewError(http.StatusInternalServerError, err, "error creating certificate")
} }
// Prepend the provisioner extension. In the auth.Sign code we will // Replace or append the provisioner extension to avoid the inclusions of
// force the resulting certificate to only have one extension, the // malicious stepOIDProvisioner using templates.
// first stepOIDProvisioner that is found in the ExtraExtensions. for i, e := range cert.ExtraExtensions {
// A client could pass a csr containing a malicious stepOIDProvisioner if e.Id.Equal(StepOIDProvisioner) {
// ExtraExtension. If we were to append (rather than prepend) the correct cert.ExtraExtensions[i] = ext
// stepOIDProvisioner extension, then the resulting certificate would return nil
// contain the malicious extension, rather than the one applied by step-ca. }
cert.ExtraExtensions = append([]pkix.Extension{ext}, cert.ExtraExtensions...) }
cert.ExtraExtensions = append(cert.ExtraExtensions, ext)
return nil return nil
} }

View file

@ -3,6 +3,7 @@ package provisioner
import ( import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
@ -625,6 +626,16 @@ func Test_profileDefaultDuration_Option(t *testing.T) {
} }
func Test_newProvisionerExtension_Option(t *testing.T) { func Test_newProvisionerExtension_Option(t *testing.T) {
expectedValue, err := asn1.Marshal(extensionASN1{
Type: int(TypeJWK),
Name: []byte("name"),
CredentialID: []byte("credentialId"),
KeyValuePairs: []string{"key", "value"},
})
if err != nil {
t.Fatal(err)
}
type test struct { type test struct {
cert *x509.Certificate cert *x509.Certificate
valid func(*x509.Certificate) valid func(*x509.Certificate)
@ -636,18 +647,22 @@ func Test_newProvisionerExtension_Option(t *testing.T) {
valid: func(cert *x509.Certificate) { valid: func(cert *x509.Certificate) {
if assert.Len(t, 1, cert.ExtraExtensions) { if assert.Len(t, 1, cert.ExtraExtensions) {
ext := cert.ExtraExtensions[0] ext := cert.ExtraExtensions[0]
assert.Equals(t, ext.Id, StepOIDProvisioner) assert.Equals(t, StepOIDProvisioner, ext.Id)
assert.Equals(t, expectedValue, ext.Value)
assert.False(t, ext.Critical)
} }
}, },
} }
}, },
"ok/prepend": func() test { "ok/replace": func() test {
return test{ return test{
cert: &x509.Certificate{ExtraExtensions: []pkix.Extension{{Id: StepOIDProvisioner, Critical: true}, {Id: []int{1, 2, 3}}}}, cert: &x509.Certificate{ExtraExtensions: []pkix.Extension{{Id: StepOIDProvisioner, Critical: true}, {Id: []int{1, 2, 3}}}},
valid: func(cert *x509.Certificate) { valid: func(cert *x509.Certificate) {
if assert.Len(t, 3, cert.ExtraExtensions) { if assert.Len(t, 2, cert.ExtraExtensions) {
ext := cert.ExtraExtensions[0] ext := cert.ExtraExtensions[0]
assert.Equals(t, ext.Id, StepOIDProvisioner) assert.Equals(t, StepOIDProvisioner, ext.Id)
assert.Equals(t, expectedValue, ext.Value)
assert.False(t, ext.Critical) assert.False(t, ext.Critical)
} }
}, },
@ -657,7 +672,7 @@ func Test_newProvisionerExtension_Option(t *testing.T) {
for name, run := range tests { for name, run := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
tt := run() tt := run()
assert.FatalError(t, newProvisionerExtensionOption(TypeJWK, "foo", "bar", "baz", "zap").Modify(tt.cert, SignOptions{})) assert.FatalError(t, newProvisionerExtensionOption(TypeJWK, "name", "credentialId", "key", "value").Modify(tt.cert, SignOptions{}))
tt.valid(tt.cert) tt.valid(tt.cert)
}) })
} }