Add support for configuring capabilities (cacaps)

This commit is contained in:
Herman Slatman 2021-03-07 00:50:00 +01:00
parent e4d7ea8fa0
commit 2536a08dc2
No known key found for this signature in database
GPG key ID: F4D8A44EA0A75A4F
4 changed files with 53 additions and 19 deletions

View file

@ -16,6 +16,7 @@ type SCEP struct {
ForceCN bool `json:"forceCN,omitempty"` ForceCN bool `json:"forceCN,omitempty"`
ChallengePassword string `json:"challenge,omitempty"` ChallengePassword string `json:"challenge,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Options *Options `json:"options,omitempty"` Options *Options `json:"options,omitempty"`
Claims *Claims `json:"claims,omitempty"` Claims *Claims `json:"claims,omitempty"`
claimer *Claimer claimer *Claimer
@ -97,6 +98,11 @@ func (s *SCEP) GetChallengePassword() string {
return s.ChallengePassword return s.ChallengePassword
} }
// GetCapabilities returns the CA capabilities
func (s *SCEP) GetCapabilities() []string {
return s.Capabilities
}
// Interface guards // Interface guards
var ( var (
_ Interface = (*SCEP)(nil) _ Interface = (*SCEP)(nil)

View file

@ -33,20 +33,6 @@ const maxPayloadSize = 2 << 20
type nextHTTP = func(http.ResponseWriter, *http.Request) type nextHTTP = func(http.ResponseWriter, *http.Request)
var (
// TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2
// TODO: move capabilities to Authority or Provisioner, so that they can be configured?
defaultCapabilities = []string{
"Renewal",
"SHA-1",
"SHA-256",
"AES",
"DES3",
"SCEPStandard",
"POSTPKIOperation",
}
)
const ( const (
certChainHeader = "application/x-x509-ca-ra-cert" certChainHeader = "application/x-x509-ca-ra-cert"
leafHeader = "application/x-x509-ca-cert" leafHeader = "application/x-x509-ca-cert"
@ -260,10 +246,12 @@ func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) {
// GetCACaps returns the CA capabilities in a SCEP response // GetCACaps returns the CA capabilities in a SCEP response
func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) { func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) {
response := SCEPResponse{Operation: opnGetCACaps} caps := h.Auth.GetCACaps(ctx)
// TODO: get the actual capabilities from provisioner config response := SCEPResponse{
response.Data = formatCapabilities(defaultCapabilities) Operation: opnGetCACaps,
Data: formatCapabilities(caps),
}
return response, nil return response, nil
} }

View file

@ -57,6 +57,7 @@ type Interface interface {
DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) error
SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error) SignCSR(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage) (*PKIMessage, error)
MatchChallengePassword(ctx context.Context, password string) (bool, error) MatchChallengePassword(ctx context.Context, password string) (bool, error)
GetCACaps(ctx context.Context) []string
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
} }
@ -128,6 +129,19 @@ func New(signAuth SignAuthority, ops AuthorityOptions) (*Authority, error) {
}, nil }, nil
} }
var (
// TODO: check the default capabilities; https://tools.ietf.org/html/rfc8894#section-3.5.2
defaultCapabilities = []string{
"Renewal",
"SHA-1",
"SHA-256",
"AES",
"DES3",
"SCEPStandard",
"POSTPKIOperation",
}
)
// LoadProvisionerByID calls out to the SignAuthority interface to load a // LoadProvisionerByID calls out to the SignAuthority interface to load a
// provisioner by ID. // provisioner by ID.
func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) { func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error) {
@ -155,9 +169,9 @@ func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *u
u = *baseURL u = *baseURL
} }
// If no Scheme is set, then default to https. // If no Scheme is set, then default to http (in case of SCEP)
if u.Scheme == "" { if u.Scheme == "" {
u.Scheme = "https" u.Scheme = "http"
} }
// If no Host is set, then use the default (first DNS attr in the ca.json). // If no Host is set, then use the default (first DNS attr in the ca.json).
@ -188,6 +202,9 @@ func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) {
// //
// Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in // Using an RA does not seem to exist in https://tools.ietf.org/html/rfc8894, but is mentioned in
// https://tools.ietf.org/id/draft-nourse-scep-21.html. Will continue using the CA directly for now. // https://tools.ietf.org/id/draft-nourse-scep-21.html. Will continue using the CA directly for now.
//
// The certificate to use should probably depend on the (configured) Provisioner and may
// use a distinct certificate, apart from the intermediate.
if a.intermediateCertificate == nil { if a.intermediateCertificate == nil {
return nil, errors.New("no intermediate certificate available in SCEP authority") return nil, errors.New("no intermediate certificate available in SCEP authority")
@ -421,6 +438,28 @@ func (a *Authority) MatchChallengePassword(ctx context.Context, password string)
return false, nil return false, nil
} }
// GetCACaps returns the CA capabilities
func (a *Authority) GetCACaps(ctx context.Context) []string {
p, err := ProvisionerFromContext(ctx)
if err != nil {
return defaultCapabilities
}
caps := p.GetCapabilities()
if len(caps) == 0 {
return defaultCapabilities
}
// TODO: validate the caps? Ensure they are the right format according to RFC?
// TODO: ensure that the capabilities are actually "enforced"/"verified" in code too:
// check that only parts of the spec are used in the implementation belonging to the capabilities.
// For example for renewals, which we could disable in the provisioner, should then also
// not be reported in cacaps operation.
return caps
}
// degenerateCertificates creates degenerate certificates pkcs#7 type // degenerateCertificates creates degenerate certificates pkcs#7 type
func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer

View file

@ -15,4 +15,5 @@ type Provisioner interface {
DefaultTLSCertDuration() time.Duration DefaultTLSCertDuration() time.Duration
GetOptions() *provisioner.Options GetOptions() *provisioner.Options
GetChallengePassword() string GetChallengePassword() string
GetCapabilities() []string
} }