Skip to content

Commit

Permalink
Add support for challenge password
Browse files Browse the repository at this point in the history
  • Loading branch information
hslatman committed Mar 6, 2021
1 parent 2d21b09 commit e4d7ea8
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 13 deletions.
16 changes: 11 additions & 5 deletions authority/provisioner/scep.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ type SCEP struct {
Type string `json:"type"`
Name string `json:"name"`

ForceCN bool `json:"forceCN,omitempty"`
Options *Options `json:"options,omitempty"`
Claims *Claims `json:"claims,omitempty"`
claimer *Claimer
ForceCN bool `json:"forceCN,omitempty"`
ChallengePassword string `json:"challenge,omitempty"`
Options *Options `json:"options,omitempty"`
Claims *Claims `json:"claims,omitempty"`
claimer *Claimer
}

// GetID returns the provisioner unique identifier.
Expand Down Expand Up @@ -76,7 +77,7 @@ func (s *SCEP) Init(config Config) (err error) {
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
// on the resulting certificate.
func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) {
Expand All @@ -91,6 +92,11 @@ func (s *SCEP) AuthorizeSign(ctx context.Context, token string) ([]SignOption, e
}, nil
}

// GetChallengePassword returns the challenge password
func (s *SCEP) GetChallengePassword() string {
return s.ChallengePassword
}

// Interface guards
var (
_ Interface = (*SCEP)(nil)
Expand Down
29 changes: 21 additions & 8 deletions scep/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ func New(scepAuth scep.Interface) api.RouterHandler {
// Route traffic and implement the Router interface.
func (h *Handler) Route(r api.Router) {
getLink := h.Auth.GetLinkExplicit

r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get))
r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post))
}
Expand Down Expand Up @@ -140,6 +139,8 @@ func (h *Handler) Post(w http.ResponseWriter, r *http.Request) {
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)

writeSCEPResponse(w, response)
Expand Down Expand Up @@ -238,8 +239,10 @@ func (h *Handler) GetCACert(ctx context.Context) (SCEPResponse, error) {
return SCEPResponse{}, errors.New("missing CA cert")
}

response := SCEPResponse{Operation: opnGetCACert}
response.CACertNum = len(certs)
response := SCEPResponse{
Operation: opnGetCACert,
CACertNum: len(certs),
}

if len(certs) == 1 {
response.Data = certs[0].Raw
Expand Down Expand Up @@ -268,8 +271,6 @@ func (h *Handler) GetCACaps(ctx context.Context) (SCEPResponse, error) {
// PKIOperation performs PKI operations and returns a SCEP response
func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPResponse, error) {

response := SCEPResponse{Operation: opnPKIOperation}

// parse the message using microscep implementation
microMsg, err := microscep.ParsePKIMessage(request.Message)
if err != nil {
Expand All @@ -295,7 +296,15 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
}

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
Expand All @@ -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: store the new cert for CN locally; should go into the DB

response.Data = certRep.Raw
response.Certificate = certRep.Certificate
response := SCEPResponse{
Operation: opnPKIOperation,
Data: certRep.Raw,
Certificate: certRep.Certificate,
}

return response, nil
}
Expand All @@ -338,6 +350,7 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) {
}

func writeError(w http.ResponseWriter, err error) {
// TODO: this probably needs to use SCEP specific errors (i.e. failInfo)
scepError := &scep.Error{
Message: err.Error(),
Status: http.StatusInternalServerError, // TODO: make this a param?
Expand Down
20 changes: 20 additions & 0 deletions scep/authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type Interface interface {
GetCACertificates() ([]*x509.Certificate, error)
DecryptPKIEnvelope(ctx context.Context, msg *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
}
Expand Down Expand Up @@ -401,6 +402,25 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
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
func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) {
var buf bytes.Buffer
Expand Down
1 change: 1 addition & 0 deletions scep/provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ type Provisioner interface {
GetName() string
DefaultTLSCertDuration() time.Duration
GetOptions() *provisioner.Options
GetChallengePassword() string
}

0 comments on commit e4d7ea8

Please sign in to comment.