diff --git a/ca/ca.go b/ca/ca.go index fa71abd6..a60346ee 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -212,7 +212,7 @@ func (ca *CA) Init(config *config.Config) (*CA, error) { }) } - // helpful routine for logging all routes // + // helpful routine for logging all routes //dumpRoutes(mux) // Add monitoring if configured diff --git a/scep/api/api.go b/scep/api/api.go index f2d11fb7..4df5d6a1 100644 --- a/scep/api/api.go +++ b/scep/api/api.go @@ -9,8 +9,10 @@ import ( "io" "io/ioutil" "net/http" + "net/url" "strings" + "github.com/go-chi/chi" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/scep" @@ -76,14 +78,10 @@ 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 - //fmt.Println(getLink) + getLink := h.Auth.GetLinkExplicit - //r.MethodFunc("GET", "/bla", h.baseURLFromRequest(h.lookupProvisioner(nil))) - //r.MethodFunc("GET", getLink(acme.NewNonceLink, "{provisionerID}", false, nil), h.baseURLFromRequest(h.lookupProvisioner(h.addNonce(h.GetNonce)))) - - r.MethodFunc(http.MethodGet, "/", h.lookupProvisioner(h.Get)) - r.MethodFunc(http.MethodPost, "/", h.lookupProvisioner(h.Post)) + r.MethodFunc(http.MethodGet, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Get)) + r.MethodFunc(http.MethodPost, getLink("{provisionerID}", false, nil), h.lookupProvisioner(h.Post)) } @@ -202,16 +200,12 @@ func decodeSCEPRequest(r *http.Request) (SCEPRequest, error) { func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP { return func(w http.ResponseWriter, r *http.Request) { - // name := chi.URLParam(r, "provisionerID") - // provisionerID, err := url.PathUnescape(name) - // if err != nil { - // api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name)) - // return - // } - - // TODO: make this configurable; and we might want to look at being able to provide multiple, - // like the ACME one? The below assumes a SCEP provider (scep/) called "scep1" exists. - provisionerID := "scep1" + name := chi.URLParam(r, "provisionerID") + provisionerID, err := url.PathUnescape(name) + if err != nil { + api.WriteError(w, fmt.Errorf("error url unescaping provisioner id '%s'", name)) + return + } p, err := h.Auth.LoadProvisionerByID("scep/" + provisionerID) if err != nil { @@ -275,6 +269,8 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response := SCEPResponse{Operation: opnPKIOperation} + fmt.Println("BEFORE PARSING") + microMsg, err := microscep.ParsePKIMessage(request.Message) if err != nil { return SCEPResponse{}, err @@ -287,7 +283,12 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe Raw: microMsg.Raw, } + fmt.Println("len raw:", len(microMsg.Raw)) + + fmt.Println("AFTER PARSING") + if err := h.Auth.DecryptPKIEnvelope(ctx, msg); err != nil { + fmt.Println("ERROR IN DECRYPTPKIENVELOPE") return SCEPResponse{}, err } @@ -311,6 +312,8 @@ func (h *Handler) PKIOperation(ctx context.Context, request SCEPRequest) (SCEPRe response.Data = certRep.Raw response.Certificate = certRep.Certificate + fmt.Println("HERE!!!") + return response, nil } @@ -336,8 +339,8 @@ func writeSCEPResponse(w http.ResponseWriter, response SCEPResponse) { func writeError(w http.ResponseWriter, err error) { scepError := &scep.Error{ - Err: fmt.Errorf("post request failed: %w", err), - Status: http.StatusInternalServerError, // TODO: make this a param? + Message: err.Error(), + Status: http.StatusInternalServerError, // TODO: make this a param? } api.WriteError(w, scepError) } diff --git a/scep/authority.go b/scep/authority.go index 013550a6..e5d1ea48 100644 --- a/scep/authority.go +++ b/scep/authority.go @@ -8,6 +8,7 @@ import ( "crypto/x509" "errors" "fmt" + "net/url" "github.com/smallstep/certificates/authority/provisioner" database "github.com/smallstep/certificates/db" @@ -55,6 +56,8 @@ 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) + + GetLinkExplicit(provName string, absoluteLink bool, baseURL *url.URL, inputs ...string) string } // Authority is the layer that handles all SCEP interactions. @@ -130,6 +133,44 @@ func (a *Authority) LoadProvisionerByID(id string) (provisioner.Interface, error return a.signAuth.LoadProvisionerByID(id) } +// GetLinkExplicit returns the requested link from the directory. +func (a *Authority) GetLinkExplicit(provName string, abs bool, baseURL *url.URL, inputs ...string) string { + // TODO: taken from ACME; move it to directory (if we need a directory in SCEP)? + return a.getLinkExplicit(provName, abs, baseURL, inputs...) +} + +// getLinkExplicit returns an absolute or partial path to the given resource and a base +// URL dynamically obtained from the request for which the link is being calculated. +func (a *Authority) getLinkExplicit(provisionerName string, abs bool, baseURL *url.URL, inputs ...string) string { + + // TODO: do we need to provide a way to provide a different suffix/base? + // Like "/cgi-bin/pkiclient.exe"? Or would it be enough to have that as the name? + link := fmt.Sprintf("/%s", provisionerName) + + if abs { + // Copy the baseURL value from the pointer. https://github.com/golang/go/issues/38351 + u := url.URL{} + if baseURL != nil { + u = *baseURL + } + + // If no Scheme is set, then default to https. + if u.Scheme == "" { + u.Scheme = "https" + } + + // If no Host is set, then use the default (first DNS attr in the ca.json). + if u.Host == "" { + u.Host = a.dns + } + + u.Path = a.prefix + link + return u.String() + } + + return link +} + // GetCACertificates returns the certificate (chain) for the CA func (a *Authority) GetCACertificates() ([]*x509.Certificate, error) { @@ -164,6 +205,8 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err return err } + fmt.Println("len content:", len(p7.Content)) + var tID microscep.TransactionID if err := p7.UnmarshalSignedAttribute(oidSCEPtransactionID, &tID); err != nil { return err @@ -176,11 +219,17 @@ func (a *Authority) DecryptPKIEnvelope(ctx context.Context, msg *PKIMessage) err msg.p7 = p7 + //p7c, err := pkcs7.Parse(p7.Content) p7c, err := pkcs7.Parse(p7.Content) if err != nil { return err } + fmt.Println(tID) + fmt.Println(msgType) + + fmt.Println("len p7c content:", len(p7c.Content)) + envelope, err := p7c.Decrypt(a.intermediateCertificate, a.service.Decrypter) if err != nil { return err @@ -308,7 +357,7 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m // fmt.Println(string(cert.SubjectKeyId)) // create a degenerate cert structure - deg, err := DegenerateCertificates([]*x509.Certificate{cert}) + deg, err := degenerateCertificates([]*x509.Certificate{cert}) if err != nil { return nil, err } @@ -380,8 +429,8 @@ func (a *Authority) SignCSR(ctx context.Context, csr *x509.CertificateRequest, m return crepMsg, nil } -// DegenerateCertificates creates degenerate certificates pkcs#7 type -func DegenerateCertificates(certs []*x509.Certificate) ([]byte, error) { +// degenerateCertificates creates degenerate certificates pkcs#7 type +func degenerateCertificates(certs []*x509.Certificate) ([]byte, error) { var buf bytes.Buffer for _, cert := range certs { buf.Write(cert.Raw) diff --git a/scep/errors.go b/scep/errors.go index 52fff8ae..8454e16d 100644 --- a/scep/errors.go +++ b/scep/errors.go @@ -4,8 +4,8 @@ package scep type Error struct { // Type ProbType // Detail string - Err error - Status int + Message string `json:"message"` + Status int `json:"-"` // Sub []*Error // Identifier *Identifier } @@ -15,5 +15,5 @@ func (e *Error) Error() string { // if e.Err == nil { // return e.Detail // } - return e.Err.Error() + return e.Message }