forked from TrueCloudLab/certificates
Add support for challenge password
This commit is contained in:
parent
017e56c9fb
commit
3b86550dbf
4 changed files with 53 additions and 13 deletions
|
@ -14,10 +14,11 @@ type SCEP struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
ForceCN bool `json:"forceCN,omitempty"`
|
ForceCN bool `json:"forceCN,omitempty"`
|
||||||
Options *Options `json:"options,omitempty"`
|
ChallengePassword string `json:"challenge,omitempty"`
|
||||||
Claims *Claims `json:"claims,omitempty"`
|
Options *Options `json:"options,omitempty"`
|
||||||
claimer *Claimer
|
Claims *Claims `json:"claims,omitempty"`
|
||||||
|
claimer *Claimer
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID returns the provisioner unique identifier.
|
// GetID returns the provisioner unique identifier.
|
||||||
|
@ -76,7 +77,7 @@ func (s *SCEP) Init(config Config) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeSign does not do any validation, because all validation is handled
|
// AuthorizeSign does not do any verification, because all verification is handled
|
||||||
// in the SCEP protocol. This method returns a list of modifiers / constraints
|
// in the SCEP protocol. This method returns a list of modifiers / constraints
|
||||||
// on the resulting certificate.
|
// on the resulting certificate.
|
||||||
func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) {
|
func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) {
|
||||||
|
@ -91,6 +92,11 @@ func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChallengePassword returns the challenge password
|
||||||
|
func (s *SCEP) GetChallengePassword() string {
|
||||||
|
return s.ChallengePassword
|
||||||
|
}
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ Interface = (*SCEP)(nil)
|
_ Interface = (*SCEP)(nil)
|
||||||
|
|
|
@ -80,7 +80,6 @@ func New(scepAuth scep.Interface) api.RouterHandler {
|
||||||
// Route traffic and implement the Router interface.
|
// Route traffic and implement the Router interface.
|
||||||
func (h *Handler) Route(r api.Router) {
|
func (h *Handler) Route(r api.Router) {
|
||||||
getLink := h.Auth.GetLinkExplicit
|
getLink := h.Auth.GetLinkExplicit
|
||||||
|
|
||||||
r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get))
|
r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get))
|
||||||
r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post))
|
r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post))
|
||||||
}
|
}
|
||||||
|
@ -140,6 +139,8 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: fix cases in which we get here and there's no certificate (i.e. wrong password, waiting for cert, etc)
|
||||||
|
// We should generate an appropriate response and it should be signed
|
||||||
api.LogCertificate(w, response.Certificate)
|
api.LogCertificate(w, response.Certificate)
|
||||||
|
|
||||||
writeSCEPResponse(w, response)
|
writeSCEPResponse(w, response)
|
||||||
|
@ -238,8 +239,10 @@ func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) {
|
||||||
return SCEPResponse{}, errors.New("missing CA cert")
|
return SCEPResponse{}, errors.New("missing CA cert")
|
||||||
}
|
}
|
||||||
|
|
||||||
response := SCEPResponse{Operation: opnGetCACert}
|
response := SCEPResponse{
|
||||||
response.CACertNum = len(certs)
|
Operation: opnGetCACert,
|
||||||
|
CACertNum: len(certs),
|
||||||
|
}
|
||||||
|
|
||||||
if len(certs) == 1 {
|
if len(certs) == 1 {
|
||||||
response.Data = certs[0].Raw
|
response.Data = certs[0].Raw
|
||||||
|
@ -268,8 +271,6 @@ func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) {
|
||||||
// PKIOperation performs PKI operations and returns a SCEP response
|
// PKIOperation performs PKI operations and returns a SCEP response
|
||||||
func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPResponse, error) {
|
func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPResponse, error) {
|
||||||
|
|
||||||
response := SCEPResponse{Operation: opnPKIOperation}
|
|
||||||
|
|
||||||
// parse the message using microscep implementation
|
// parse the message using microscep implementation
|
||||||
microMsg, err := microscep.ParsePKIMessage(request.Message)
|
microMsg, err := microscep.ParsePKIMessage(request.Message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -295,7 +296,15 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.MessageType == microscep.PKCSReq {
|
if msg.MessageType == microscep.PKCSReq {
|
||||||
// TODO: CSR validation, like challenge password
|
|
||||||
|
challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword)
|
||||||
|
if err != nil {
|
||||||
|
return SCEPResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !challengeMatches {
|
||||||
|
return SCEPResponse{}, errors.New("wrong password provided")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
csr := msg.CSRReqMessage.CSR
|
csr := msg.CSRReqMessage.CSR
|
||||||
|
@ -311,8 +320,11 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
// // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
|
// // TODO: check if CN already exists, if renewal is allowed and if existing should be revoked; fail if not
|
||||||
// // TODO: store the new cert for CN locally; should go into the DB
|
// // TODO: store the new cert for CN locally; should go into the DB
|
||||||
|
|
||||||
response.Data = certRep.Raw
|
response := SCEPResponse{
|
||||||
response.Certificate = certRep.Certificate
|
Operation: opnPKIOperation,
|
||||||
|
Data: certRep.Raw,
|
||||||
|
Certificate: certRep.Certificate,
|
||||||
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
@ -338,6 +350,7 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeError(w http.ResponseWriter, err error) {
|
func writeError(w http.ResponseWriter, err error) {
|
||||||
|
// TODO: this probably needs to use SCEP specific errors (i.e. failInfo)
|
||||||
scepError := &scep.Error{
|
scepError := &scep.Error{
|
||||||
Message: err.Error(),
|
Message: err.Error(),
|
||||||
Status: http.StatusInternalServerError, // TODO: make this a param?
|
Status: http.StatusInternalServerError, // TODO: make this a param?
|
||||||
|
|
|
@ -56,6 +56,7 @@ type Interface interface {
|
||||||
GetCACertificates() ([]*x509.Certificate, error)
|
GetCACertificates() ([]*x509.Certificate, error)
|
||||||
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)
|
||||||
|
|
||||||
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
|
GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string
|
||||||
}
|
}
|
||||||
|
@ -401,6 +402,25 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
|
||||||
return crepMsg, nil
|
return crepMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchChallengePassword verifies a SCEP challenge password
|
||||||
|
func (a *Authority) MatchChallengePassword(ctx context.Context, password string) (bool, error) {
|
||||||
|
|
||||||
|
p, err := ProvisionerFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.GetChallengePassword() == password {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support dynamic challenges, i.e. a list of challenges instead of one?
|
||||||
|
// That's probably a bit harder to configure, though; likely requires some data store
|
||||||
|
// that can be interacted with more easily, via some internal API, for example.
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -14,4 +14,5 @@ type Provisioner interface {
|
||||||
GetName() string
|
GetName() string
|
||||||
DefaultTLSCertDuration() time.Duration
|
DefaultTLSCertDuration() time.Duration
|
||||||
GetOptions() *provisioner.Options
|
GetOptions() *provisioner.Options
|
||||||
|
GetChallengePassword() string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue