supporting non-ca certs for verification

This commit is contained in:
vishalnayak 2016-02-29 10:40:15 -05:00
parent 6314057b9a
commit f056e8a5a5
2 changed files with 82 additions and 28 deletions

View file

@ -1,6 +1,7 @@
package cert
import (
"crypto/x509"
"fmt"
"strings"
"time"
@ -51,7 +52,7 @@ Defaults to system/backend default TTL time.`,
Callbacks: map[logical.Operation]framework.OperationFunc{
logical.DeleteOperation: b.pathCertDelete,
logical.ReadOperation: b.pathCertRead,
logical.UpdateOperation: b.pathCertWrite,
logical.UpdateOperation: b.pathCertWrite,
},
HelpSynopsis: pathCertHelpSyn,
@ -132,6 +133,22 @@ func (b *backend) pathCertWrite(
return logical.ErrorResponse("failed to parse certificate"), nil
}
// If the certificate is not a CA cert, then ensure that x509.ExtKeyUsageClientAuth is set
if !parsed[0].IsCA {
if parsed[0].ExtKeyUsage == nil {
return logical.ErrorResponse("non-CA certificates should have TLS web client authentication set as an extended key usage"), nil
}
var bool clientAuth
for _, usage := range parsed[0].ExtKeyUsage {
if usage == x509.ExtKeyUsageClientAuth {
clientAuth = true
}
}
if !clientAuth {
return logical.ErrorResponse("non-CA certificates should have TLS web client authentication set as an extended key usage"), nil
}
}
certEntry := &CertEntry{
Name: name,
Certificate: certificate,
@ -140,7 +157,6 @@ func (b *backend) pathCertWrite(
}
// Parse the lease duration or default to backend/system default
var err error
maxTTL := b.System().MaxLeaseTTL()
ttl := time.Duration(d.Get("ttl").(int)) * time.Second
if ttl == time.Duration(0) {

View file

@ -134,28 +134,54 @@ func (b *backend) verifyCredentials(req *logical.Request) (*ParsedCert, *logical
connState := req.Connection.ConnState
// Load the trusted certificates
roots, trusted := b.loadTrustedCerts(req.Storage)
roots, trusted, nonCAPool, trustedNonCAs := b.loadTrustedCerts(req.Storage)
// Validate the connection state is trusted
trustedChains, err := validateConnState(roots, connState)
if err != nil {
return nil, nil, err
}
// If trustedNonCAs is not empty it means that client had registered a non-CA cert
// with the backend.
if len(trustedNonCAs) != 0 {
_, err := validateConnState(nonCAPool, connState)
if err != nil {
return nil, nil, err
}
// Match the trusted non-CA chain with the policy
return b.matchNonCAPolicy(connState.PeerCertificates, trustedNonCAs), nil, nil
} else {
trustedChains, err := validateConnState(roots, connState)
if err != nil {
return nil, nil, err
}
// If no trusted chain was found, client is not authenticated
if len(trustedChains) == 0 {
return nil, logical.ErrorResponse("invalid certificate or no client certificate supplied"), nil
}
// If no trusted chain was found, client is not authenticated
if len(trustedChains) == 0 {
return nil, logical.ErrorResponse("invalid certificate or no client certificate supplied"), nil
}
validChain := b.checkForValidChain(req.Storage, trustedChains)
if !validChain {
return nil, logical.ErrorResponse(
"no chain containing non-revoked certificates could be found for this login certificate",
), nil
}
validChain := b.checkForValidChain(req.Storage, trustedChains)
if !validChain {
return nil, logical.ErrorResponse(
"no chain containing non-revoked certificates could be found for this login certificate",
), nil
}
// Match the trusted chain with the policy
return b.matchPolicy(trustedChains, trusted), nil, nil
// Match the trusted chain with the policy
return b.matchPolicy(trustedChains, trusted), nil, nil
}
}
// matchNonCAPolicy is used to match the client cert with the registered non-CA
// policy to establish client identity.
func (b *backend) matchNonCAPolicy(peerCertificates []*x509.Certificate, trustedNonCAs []*ParsedCert) *ParsedCert {
for _, trustedNonCA := range trustedNonCAs {
for _, tCert := range trustedNonCA.Certificates {
for _, cCert := range peerCertificates {
if tCert.Equal(cCert) {
return trustedNonCA
}
}
}
}
return nil
}
// matchPolicy is used to match the associated policy with the certificate that
@ -177,8 +203,9 @@ func (b *backend) matchPolicy(chains [][]*x509.Certificate, trusted []*ParsedCer
}
// loadTrustedCerts is used to load all the trusted certificates from the backend
func (b *backend) loadTrustedCerts(store logical.Storage) (pool *x509.CertPool, trusted []*ParsedCert) {
func (b *backend) loadTrustedCerts(store logical.Storage) (pool *x509.CertPool, trusted []*ParsedCert, nonCAPool *x509.CertPool, trustedNonCAs []*ParsedCert) {
pool = x509.NewCertPool()
nonCAPool = x509.NewCertPool()
names, err := store.List("cert/")
if err != nil {
b.Logger().Printf("[ERR] cert: failed to list trusted certs: %v", err)
@ -195,15 +222,25 @@ func (b *backend) loadTrustedCerts(store logical.Storage) (pool *x509.CertPool,
b.Logger().Printf("[ERR] cert: failed to parse certificate for '%s'", name)
continue
}
for _, p := range parsed {
pool.AddCert(p)
}
if !parsed[0].IsCA {
for _, p := range parsed {
nonCAPool.AddCert(p)
}
trustedNonCAs = append(trustedNonCAs, &ParsedCert{
Entry: entry,
Certificates: parsed,
})
} else {
for _, p := range parsed {
pool.AddCert(p)
}
// Create a ParsedCert entry
trusted = append(trusted, &ParsedCert{
Entry: entry,
Certificates: parsed,
})
// Create a ParsedCert entry
trusted = append(trusted, &ParsedCert{
Entry: entry,
Certificates: parsed,
})
}
}
return
}
@ -257,6 +294,7 @@ func validateConnState(roots *x509.CertPool, cs *tls.ConnectionState) ([][]*x509
Intermediates: x509.NewCertPool(),
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
certs := cs.PeerCertificates
if len(certs) == 0 {
return nil, nil