forked from TrueCloudLab/certificates
Add signed failure responses
This commit is contained in:
parent
4fe7179b95
commit
f0050e5ca9
3 changed files with 117 additions and 13 deletions
|
@ -36,7 +36,7 @@ type nextHTTP = func(http.ResponseWriter, *http.Request)
|
||||||
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"
|
||||||
pkiOpHeader = "application/x-pki-message"
|
pkiOperationHeader = "application/x-pki-message"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SCEPRequest is a SCEP server request.
|
// SCEPRequest is a SCEP server request.
|
||||||
|
@ -125,10 +125,6 @@ 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)
|
|
||||||
|
|
||||||
writeSCEPResponse(w, response)
|
writeSCEPResponse(w, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,9 +258,13 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
// 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 {
|
||||||
|
// return the error, because we can't use the msg for creating a CertRep
|
||||||
return SCEPResponse{}, err
|
return SCEPResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is essentially doing the same as microscep.ParsePKIMessage, but
|
||||||
|
// gives us access to the p7 itself in scep.PKIMessage. Essentially a small
|
||||||
|
// wrapper for the microscep implementation.
|
||||||
p7, err := pkcs7.Parse(microMsg.Raw)
|
p7, err := pkcs7.Parse(microMsg.Raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SCEPResponse{}, err
|
return SCEPResponse{}, err
|
||||||
|
@ -283,23 +283,25 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe
|
||||||
return SCEPResponse{}, err
|
return SCEPResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: at this point we have sufficient information for returning nicely signed CertReps
|
||||||
|
csr := msg.CSRReqMessage.CSR
|
||||||
|
|
||||||
if msg.MessageType == microscep.PKCSReq {
|
if msg.MessageType == microscep.PKCSReq {
|
||||||
|
|
||||||
challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword)
|
challengeMatches, err := h.Auth.MatchChallengePassword(ctx, msg.CSRReqMessage.ChallengePassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SCEPResponse{}, err
|
return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when checking password")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !challengeMatches {
|
if !challengeMatches {
|
||||||
return SCEPResponse{}, errors.New("wrong password provided")
|
// TODO: can this be returned safely to the client? In the end, if the password was correct, that gains a bit of info too.
|
||||||
|
return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "wrong password provided")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
csr := msg.CSRReqMessage.CSR
|
|
||||||
|
|
||||||
certRep, err := h.Auth.SignCSR(ctx, csr, msg)
|
certRep, err := h.Auth.SignCSR(ctx, csr, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SCEPResponse{}, err
|
return h.createFailureResponse(ctx, csr, msg, microscep.BadRequest, "error when signing new certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
// //cert := certRep.CertRepMessage.Certificate
|
// //cert := certRep.CertRepMessage.Certificate
|
||||||
|
@ -330,6 +332,11 @@ func formatCapabilities(caps []string) []byte {
|
||||||
|
|
||||||
// writeSCEPResponse writes a SCEP response back to the SCEP client.
|
// writeSCEPResponse writes a SCEP response back to the SCEP client.
|
||||||
func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) {
|
func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) {
|
||||||
|
|
||||||
|
if response.Certificate != nil {
|
||||||
|
api.LogCertificate(w, response.Certificate)
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", contentHeader(response))
|
w.Header().Set("Content-Type", contentHeader(response))
|
||||||
_, err := w.Write(response.Data)
|
_, err := w.Write(response.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -346,6 +353,17 @@ func writeError(w http.ResponseWriter, err error) {
|
||||||
api.WriteError(w, scepError)
|
api.WriteError(w, scepError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) createFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *scep.PKIMessage, info microscep.FailInfo, infoText string) (SCEPResponse, error) {
|
||||||
|
certRepMsg, err := h.Auth.CreateFailureResponse(ctx, csr, msg, scep.FailInfoName(info), infoText)
|
||||||
|
if err != nil {
|
||||||
|
return SCEPResponse{}, err
|
||||||
|
}
|
||||||
|
return SCEPResponse{
|
||||||
|
Operation: opnPKIOperation,
|
||||||
|
Data: certRepMsg.Raw,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func contentHeader(r SCEPResponse) string {
|
func contentHeader(r SCEPResponse) string {
|
||||||
switch r.Operation {
|
switch r.Operation {
|
||||||
case opnGetCACert:
|
case opnGetCACert:
|
||||||
|
@ -354,7 +372,7 @@ func contentHeader(r SCEPResponse) string {
|
||||||
}
|
}
|
||||||
return leafHeader
|
return leafHeader
|
||||||
case opnPKIOperation:
|
case opnPKIOperation:
|
||||||
return pkiOpHeader
|
return pkiOperationHeader
|
||||||
default:
|
default:
|
||||||
return "text/plain"
|
return "text/plain"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error)
|
||||||
MatchChallengePassword(ctx context.Context, password string) (bool, error)
|
MatchChallengePassword(ctx context.Context, password string) (bool, error)
|
||||||
GetCACaps(ctx context.Context) []string
|
GetCACaps(ctx context.Context) []string
|
||||||
|
|
||||||
|
@ -376,6 +377,10 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
|
||||||
Type: oidSCEPrecipientNonce,
|
Type: oidSCEPrecipientNonce,
|
||||||
Value: msg.SenderNonce,
|
Value: msg.SenderNonce,
|
||||||
},
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPsenderNonce,
|
||||||
|
Value: msg.SenderNonce,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,6 +424,74 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m
|
||||||
return crepMsg, nil
|
return crepMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateFailureResponse creates an appropriately signed reply for PKI operations
|
||||||
|
func (a *Authority) CreateFailureResponse(ctx context.Context, csr *x509.CertificateRequest, msg *PKIMessage, info FailInfoName, infoText string) (*PKIMessage, error) {
|
||||||
|
|
||||||
|
config := pkcs7.SignerInfoConfig{
|
||||||
|
ExtraSignedAttributes: []pkcs7.Attribute{
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPtransactionID,
|
||||||
|
Value: msg.TransactionID,
|
||||||
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPpkiStatus,
|
||||||
|
Value: microscep.FAILURE,
|
||||||
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPfailInfo,
|
||||||
|
Value: info,
|
||||||
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPfailInfoText,
|
||||||
|
Value: infoText,
|
||||||
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPmessageType,
|
||||||
|
Value: microscep.CertRep,
|
||||||
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPsenderNonce,
|
||||||
|
Value: msg.SenderNonce,
|
||||||
|
},
|
||||||
|
pkcs7.Attribute{
|
||||||
|
Type: oidSCEPrecipientNonce,
|
||||||
|
Value: msg.SenderNonce,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
signedData, err := pkcs7.NewSignedData(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign the attributes
|
||||||
|
if err := signedData.AddSigner(a.intermediateCertificate, a.service.Signer, config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certRepBytes, err := signedData.Finish()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cr := &CertRepMessage{
|
||||||
|
PKIStatus: microscep.FAILURE,
|
||||||
|
FailInfo: microscep.BadRequest,
|
||||||
|
RecipientNonce: microscep.RecipientNonce(msg.SenderNonce),
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a CertRep message from the original
|
||||||
|
crepMsg := &PKIMessage{
|
||||||
|
Raw: certRepBytes,
|
||||||
|
TransactionID: msg.TransactionID,
|
||||||
|
MessageType: microscep.CertRep,
|
||||||
|
CertRepMessage: cr,
|
||||||
|
}
|
||||||
|
|
||||||
|
return crepMsg, nil
|
||||||
|
}
|
||||||
|
|
||||||
// MatchChallengePassword verifies a SCEP challenge password
|
// MatchChallengePassword verifies a SCEP challenge password
|
||||||
func (a *Authority) MatchChallengePassword(ctx context.Context, password string) (bool, error) {
|
func (a *Authority) MatchChallengePassword(ctx context.Context, password string) (bool, error) {
|
||||||
|
|
||||||
|
|
13
scep/scep.go
13
scep/scep.go
|
@ -11,6 +11,18 @@ import (
|
||||||
"go.mozilla.org/pkcs7"
|
"go.mozilla.org/pkcs7"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FailInfoName models the name/value of failInfo
|
||||||
|
type FailInfoName microscep.FailInfo
|
||||||
|
|
||||||
|
// FailInfo models a failInfo object consisting of a
|
||||||
|
// name/identifier and a failInfoText, the latter of
|
||||||
|
// which can be more descriptive and is intended to be
|
||||||
|
// read by humans.
|
||||||
|
type FailInfo struct {
|
||||||
|
Name FailInfoName
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
// SCEP OIDs
|
// SCEP OIDs
|
||||||
var (
|
var (
|
||||||
oidSCEPmessageType = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 2}
|
oidSCEPmessageType = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 2}
|
||||||
|
@ -20,6 +32,7 @@ var (
|
||||||
oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6}
|
oidSCEPrecipientNonce = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 6}
|
||||||
oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7}
|
oidSCEPtransactionID = asn1.ObjectIdentifier{2, 16, 840, 1, 113733, 1, 9, 7}
|
||||||
oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7}
|
oidChallengePassword = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 7}
|
||||||
|
oidSCEPfailInfoText = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 24}
|
||||||
)
|
)
|
||||||
|
|
||||||
// PKIMessage defines the possible SCEP message types
|
// PKIMessage defines the possible SCEP message types
|
||||||
|
|
Loading…
Reference in a new issue