allow multiple identical SANs in cert

This commit is contained in:
max furman 2019-01-31 11:20:21 -06:00
parent f0683c2e0a
commit e6e8443f3c
8 changed files with 95 additions and 75 deletions

2
Gopkg.lock generated
View file

@ -231,7 +231,7 @@
"utils", "utils",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "a1df67d396f730ff0e7bf1916cd27ae8d5255236" revision = "49d4a4c26c802e83c5ed160abdd5babab1c9b5c6"
[[projects]] [[projects]]
branch = "master" branch = "master"

View file

@ -45,7 +45,7 @@ required = [
name = "github.com/go-chi/chi" name = "github.com/go-chi/chi"
[[override]] [[override]]
branch = "master" branch = "sans"
name = "github.com/smallstep/cli" name = "github.com/smallstep/cli"
[prune] [prune]

View file

@ -1,10 +1,8 @@
package authority package authority
import ( import (
"bytes"
"fmt"
"net" "net"
"sort" "reflect"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -50,17 +48,16 @@ type dnsNamesClaim struct {
// Valid checks that certificate request common name matches the one configured. // Valid checks that certificate request common name matches the one configured.
func (c *dnsNamesClaim) Valid(crt *x509.Certificate) error { func (c *dnsNamesClaim) Valid(crt *x509.Certificate) error {
sort.Strings(c.names) tokMap := make(map[string]int)
sort.Strings(crt.DNSNames) for _, e := range c.names {
if len(c.names) != len(crt.DNSNames) { tokMap[e] = 1
fmt.Printf("len(c.names) = %+v, len(crt.DNSNames) = %+v\n", len(c.names), len(crt.DNSNames))
return errors.Errorf("DNS names claim failed - got %s, want %s", crt.DNSNames, c.names)
} }
for i := range c.names { crtMap := make(map[string]int)
if c.names[i] != crt.DNSNames[i] { for _, e := range crt.DNSNames {
fmt.Printf("c.names[i] = %+v, crt.DNSNames[i] = %+v\n", c.names[i], crt.DNSNames[i]) crtMap[e] = 1
return errors.Errorf("DNS names claim failed - got %s, want %s", crt.DNSNames, c.names) }
} if !reflect.DeepEqual(tokMap, crtMap) {
return errors.Errorf("DNS names claim failed - got %s, want %s", crt.DNSNames, c.names)
} }
return nil return nil
} }
@ -71,19 +68,16 @@ type ipAddressesClaim struct {
// Valid checks that certificate request common name matches the one configured. // Valid checks that certificate request common name matches the one configured.
func (c *ipAddressesClaim) Valid(crt *x509.Certificate) error { func (c *ipAddressesClaim) Valid(crt *x509.Certificate) error {
if len(c.ips) != len(crt.IPAddresses) { tokMap := make(map[string]int)
return errors.Errorf("IP Addresses claim failed - got %v, want %v", crt.IPAddresses, c.ips) for _, e := range c.ips {
tokMap[e.String()] = 1
} }
sort.Slice(c.ips, func(i, j int) bool { crtMap := make(map[string]int)
return bytes.Compare(c.ips[i], c.ips[j]) < 0 for _, e := range crt.IPAddresses {
}) crtMap[e.String()] = 1
sort.Slice(crt.IPAddresses, func(i, j int) bool { }
return bytes.Compare(crt.IPAddresses[i], crt.IPAddresses[j]) < 0 if !reflect.DeepEqual(tokMap, crtMap) {
}) return errors.Errorf("IP Addresses claim failed - got %v, want %v", crt.IPAddresses, c.ips)
for i := range c.ips {
if !c.ips[i].Equal(crt.IPAddresses[i]) {
return errors.Errorf("IP Addresses claim failed - got %v, want %v", crt.IPAddresses[i], c.ips[i])
}
} }
return nil return nil
} }

View file

@ -52,23 +52,28 @@ func TestIPAddressesClaim_Valid(t *testing.T) {
crt *x509.Certificate crt *x509.Certificate
err error err error
}{ }{
"unexpected-ip": { "unexpected-ip-in-crt": {
iac: &ipAddressesClaim{name: "127.0.0.1"}, iac: &ipAddressesClaim{ips: []net.IP{net.ParseIP("127.0.0.1")}},
crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("1.1.1.1")}}, crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("1.1.1.1")}},
err: errors.New("IP addresses claim failed - got 1.1.1.1, want 127.0.0.1"), err: errors.New("IP Addresses claim failed - got [127.0.0.1 1.1.1.1], want [127.0.0.1]"),
},
"missing-ip-in-crt": {
iac: &ipAddressesClaim{ips: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("1.1.1.1")}},
crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}},
err: errors.New("IP Addresses claim failed - got [127.0.0.1], want [127.0.0.1 1.1.1.1]"),
}, },
"invalid-matcher-nonempty-ips": { "invalid-matcher-nonempty-ips": {
iac: &ipAddressesClaim{name: "invalid"}, iac: &ipAddressesClaim{ips: []net.IP{}},
crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}, crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}},
err: errors.New("IP addresses claim failed - got [127.0.0.1], want none"), err: errors.New("IP Addresses claim failed - got [127.0.0.1], want []"),
}, },
"ok": { "ok": {
iac: &ipAddressesClaim{name: "127.0.0.1"}, iac: &ipAddressesClaim{ips: []net.IP{net.ParseIP("127.0.0.1")}},
crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}, crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}},
}, },
"ok-empty-ips": { "ok-multiple-identical-ip-entries": {
iac: &ipAddressesClaim{name: "127.0.0.1"}, iac: &ipAddressesClaim{ips: []net.IP{net.ParseIP("127.0.0.1")}},
crt: &x509.Certificate{IPAddresses: []net.IP{}}, crt: &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("127.0.0.1"), net.ParseIP("127.0.0.1")}},
}, },
} }
@ -92,21 +97,22 @@ func TestDNSNamesClaim_Valid(t *testing.T) {
crt *x509.Certificate crt *x509.Certificate
err error err error
}{ }{
"wrong-dns-name": { "unexpected-dns-name-in-crt": {
dnc: &dnsNamesClaim{name: "foo"}, dnc: &dnsNamesClaim{names: []string{"foo"}},
crt: &x509.Certificate{DNSNames: []string{"foo", "bar"}}, crt: &x509.Certificate{DNSNames: []string{"foo", "bar"}},
err: errors.New("DNS names claim failed - got bar, want foo"), err: errors.New("DNS names claim failed - got [foo bar], want [foo]"),
}, },
"ok": { "ok": {
dnc: &dnsNamesClaim{name: "foo"}, dnc: &dnsNamesClaim{names: []string{"foo", "bar"}},
crt: &x509.Certificate{DNSNames: []string{"foo"}}, crt: &x509.Certificate{DNSNames: []string{"bar", "foo"}},
}, },
"ok-empty-dnsNames": { "missing-dns-name-in-crt": {
dnc: &dnsNamesClaim{"foo"}, dnc: &dnsNamesClaim{names: []string{"foo", "bar"}},
crt: &x509.Certificate{}, crt: &x509.Certificate{DNSNames: []string{"foo"}},
err: errors.New("DNS names claim failed - got [foo], want [foo bar]"),
}, },
"ok-multiple-identical-dns-entries": { "ok-multiple-identical-dns-entries": {
dnc: &dnsNamesClaim{name: "foo"}, dnc: &dnsNamesClaim{names: []string{"foo"}},
crt: &x509.Certificate{DNSNames: []string{"foo", "foo", "foo"}}, crt: &x509.Certificate{DNSNames: []string{"foo", "foo", "foo"}},
}, },
} }

View file

@ -197,15 +197,15 @@ func (a *Authority) Renew(ocx *x509.Certificate) (*x509.Certificate, *x509.Certi
ExtKeyUsage: oldCert.ExtKeyUsage, ExtKeyUsage: oldCert.ExtKeyUsage,
UnknownExtKeyUsage: oldCert.UnknownExtKeyUsage, UnknownExtKeyUsage: oldCert.UnknownExtKeyUsage,
BasicConstraintsValid: oldCert.BasicConstraintsValid, BasicConstraintsValid: oldCert.BasicConstraintsValid,
IsCA: oldCert.IsCA, IsCA: oldCert.IsCA,
MaxPathLen: oldCert.MaxPathLen, MaxPathLen: oldCert.MaxPathLen,
MaxPathLenZero: oldCert.MaxPathLenZero, MaxPathLenZero: oldCert.MaxPathLenZero,
OCSPServer: oldCert.OCSPServer, OCSPServer: oldCert.OCSPServer,
IssuingCertificateURL: oldCert.IssuingCertificateURL, IssuingCertificateURL: oldCert.IssuingCertificateURL,
DNSNames: oldCert.DNSNames, DNSNames: oldCert.DNSNames,
EmailAddresses: oldCert.EmailAddresses, EmailAddresses: oldCert.EmailAddresses,
IPAddresses: oldCert.IPAddresses, IPAddresses: oldCert.IPAddresses,
URIs: oldCert.URIs, URIs: oldCert.URIs,
PermittedDNSDomainsCritical: oldCert.PermittedDNSDomainsCritical, PermittedDNSDomainsCritical: oldCert.PermittedDNSDomainsCritical,
PermittedDNSDomains: oldCert.PermittedDNSDomains, PermittedDNSDomains: oldCert.PermittedDNSDomains,
ExcludedDNSDomains: oldCert.ExcludedDNSDomains, ExcludedDNSDomains: oldCert.ExcludedDNSDomains,

View file

@ -94,6 +94,7 @@ func generateBootstrapToken(ca, subject, sha string) string {
cl := struct { cl := struct {
SHA string `json:"sha"` SHA string `json:"sha"`
jwt.Claims jwt.Claims
SANS []string `json:"sans"`
}{ }{
SHA: sha, SHA: sha,
Claims: jwt.Claims{ Claims: jwt.Claims{
@ -104,6 +105,7 @@ func generateBootstrapToken(ca, subject, sha string) string {
Expiry: jwt.NewNumericDate(now.Add(time.Minute)), Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
Audience: []string{ca + "/sign"}, Audience: []string{ca + "/sign"},
}, },
SANS: []string{subject},
} }
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize() raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
if err != nil { if err != nil {

View file

@ -154,13 +154,19 @@ ZEp7knvU2psWRw==
"fail commonname-claim": func(t *testing.T) *signTest { "fail commonname-claim": func(t *testing.T) *signTest {
jti, err := randutil.ASCII(32) jti, err := randutil.ASCII(32)
assert.FatalError(t, err) assert.FatalError(t, err)
cl := jwt.Claims{ cl := struct {
Subject: "invalid", jwt.Claims
Issuer: "step-cli", SANS []string `json:"sans"`
NotBefore: jwt.NewNumericDate(now), }{
Expiry: jwt.NewNumericDate(now.Add(time.Minute)), Claims: jwt.Claims{
Audience: validAud, Subject: "invalid",
ID: jti, Issuer: "step-cli",
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
Audience: validAud,
ID: jti,
},
SANS: []string{"invalid"},
} }
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize() raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err) assert.FatalError(t, err)
@ -181,13 +187,19 @@ ZEp7knvU2psWRw==
"ok": func(t *testing.T) *signTest { "ok": func(t *testing.T) *signTest {
jti, err := randutil.ASCII(32) jti, err := randutil.ASCII(32)
assert.FatalError(t, err) assert.FatalError(t, err)
cl := jwt.Claims{ cl := struct {
Subject: "test.smallstep.com", jwt.Claims
Issuer: "step-cli", SANS []string `json:"sans"`
NotBefore: jwt.NewNumericDate(now), }{
Expiry: jwt.NewNumericDate(now.Add(time.Minute)), Claims: jwt.Claims{
Audience: validAud, Subject: "test.smallstep.com",
ID: jti, Issuer: "step-cli",
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
Audience: validAud,
ID: jti,
},
SANS: []string{"test.smallstep.com"},
} }
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize() raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err) assert.FatalError(t, err)

View file

@ -39,13 +39,19 @@ func generateOTT(subject string) string {
if err != nil { if err != nil {
panic(err) panic(err)
} }
cl := jwt.Claims{ cl := struct {
ID: id, jwt.Claims
Subject: subject, SANS []string `json:"sans"`
Issuer: "mariano", }{
NotBefore: jwt.NewNumericDate(now), Claims: jwt.Claims{
Expiry: jwt.NewNumericDate(now.Add(time.Minute)), ID: id,
Audience: []string{"https://127.0.0.1:0/sign"}, Subject: subject,
Issuer: "mariano",
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
Audience: []string{"https://127.0.0.1:0/sign"},
},
SANS: []string{subject},
} }
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize() raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
if err != nil { if err != nil {