forked from TrueCloudLab/certificates
Add support for configuring capabilities (cacaps)
This commit is contained in:
parent
e4d7ea8fa0
commit
2536a08dc2
4 changed files with 53 additions and 19 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue