Skip to content

Commit

Permalink
Support wildcard CNs in root leaf certificates
Browse files Browse the repository at this point in the history
Signed-off-by: Evan Cordell <[email protected]>
  • Loading branch information
ecordell committed Jan 27, 2017
1 parent 905fffb commit dfa8a5b
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 2 deletions.
17 changes: 15 additions & 2 deletions trustpinning/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/docker/notary/tuf/utils"
)

const wildcard = "*"

// ErrValidationFail is returned when there is no valid trusted certificates
// being served inside of the roots.json
type ErrValidationFail struct {
Expand Down Expand Up @@ -175,6 +177,17 @@ func ValidateRoot(prevRoot *data.SignedRoot, root *data.Signed, gun string, trus
return data.RootFromSigned(root)
}

// MatchCNToGun checks that the common name in a cert is valid for the given gun.
// This allows wildcards as suffixes, e.g. `namespace/*`
func MatchCNToGun(commonName, gun string) bool {
if strings.HasSuffix(commonName, wildcard) {
prefix := strings.TrimRight(commonName, wildcard)
logrus.Debugf("checking gun %s against wildcard prefix %s", gun, prefix)
return strings.HasPrefix(gun, prefix)
}
return commonName == gun
}

// validRootLeafCerts returns a list of possibly (if checkExpiry is true) non-expired, non-sha1 certificates
// found in root whose Common-Names match the provided GUN. Note that this
// "validity" alone does not imply any measure of trust.
Expand All @@ -183,8 +196,8 @@ func validRootLeafCerts(allLeafCerts map[string]*x509.Certificate, gun string, c

// Go through every leaf certificate and check that the CN matches the gun
for id, cert := range allLeafCerts {
// Validate that this leaf certificate has a CN that matches the exact gun
if cert.Subject.CommonName != gun {
// Validate that this leaf certificate has a CN that matches the gun
if !MatchCNToGun(cert.Subject.CommonName, gun) {
logrus.Debugf("error leaf certificate CN: %s doesn't match the given GUN: %s",
cert.Subject.CommonName, gun)
continue
Expand Down
76 changes: 76 additions & 0 deletions trustpinning/certs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1299,3 +1299,79 @@ func TestValidateRootWithExpiredIntermediate(t *testing.T) {
)
require.Error(t, err, "failed to invalidate expired intermediate certificate")
}

func TestCheckingWildcardCert(t *testing.T) {
memStore := trustmanager.NewKeyMemoryStore(passphraseRetriever)
cs := cryptoservice.NewCryptoService(memStore)
testPubKey, err := cs.Create(data.CanonicalRootRole, "docker.io/notary/*", data.ECDSAKey)
require.NoError(t, err)
testPrivKey, _, err := memStore.GetKey(testPubKey.ID())
require.NoError(t, err)

testCert, err := generateTestingCertificate(testPrivKey, "docker.io/notary/*", notary.Year)
require.NoError(t, err)
testCertPubKey, err := utils.ParsePEMPublicKey(utils.CertToPEM(testCert))
require.NoError(t, err)

rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{testCertPubKey.ID()}, nil)
require.NoError(t, err)
testRoot, err := data.NewRoot(
map[string]data.PublicKey{testCertPubKey.ID(): testCertPubKey},
map[string]*data.RootRole{
data.CanonicalRootRole: &rootRole.RootRole,
data.CanonicalTimestampRole: &rootRole.RootRole,
data.CanonicalTargetsRole: &rootRole.RootRole,
data.CanonicalSnapshotRole: &rootRole.RootRole},
false,
)
testRoot.Signed.Version = 1
require.NoError(t, err, "Failed to create new root")

signedTestRoot, err := testRoot.ToSigned()
require.NoError(t, err)

err = signed.Sign(cs, signedTestRoot, []data.PublicKey{testCertPubKey}, 1, nil)
require.NoError(t, err)

_, err = trustpinning.ValidateRoot(
nil,
signedTestRoot,
"docker.io/notary/test",
trustpinning.TrustPinConfig{},
)
require.NoError(t, err, "expected wildcard cert to validate")

_, err = trustpinning.ValidateRoot(
nil,
signedTestRoot,
"docker.io/not-a-match",
trustpinning.TrustPinConfig{},
)
require.Error(t, err, "expected wildcard cert not to validate")
}

func TestWildcardMatching(t *testing.T) {
var wildcardTests = []struct {
CN string
gun string
out bool
}{
{"docker.com/*", "docker.com/notary", true},
{"docker.com/**", "docker.com/notary", true},
{"*", "docker.com/any", true},
{"*", "", true},
{"**", "docker.com/any", true},
{"test/*******", "test/many/wildcard", true},
{"test/**/*/", "test/test", false},
{"test/*/wild", "test/test/wild", false},
{"*/all", "test/all", false},
{"docker.com/*/*", "docker.com/notary/test", false},
{"docker.com/*/**", "docker.com/notary/test", false},
{"", "*", false},
{"*abc*", "abc", false},
{"test/*/wild*", "test/test/wild", false},
}
for _, tt := range wildcardTests {
require.Equal(t, trustpinning.MatchCNToGun(tt.CN, tt.gun), tt.out)
}
}

0 comments on commit dfa8a5b

Please sign in to comment.