123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- // Package signhandler provides the handlers for signers.
- package signhandler
- import (
- "encoding/json"
- "io/ioutil"
- "math/big"
- "net/http"
- "github.com/cloudflare/cfssl/api"
- "github.com/cloudflare/cfssl/auth"
- "github.com/cloudflare/cfssl/bundler"
- "github.com/cloudflare/cfssl/errors"
- "github.com/cloudflare/cfssl/log"
- "github.com/cloudflare/cfssl/signer"
- )
- // NoBundlerMessage is used to alert the user that the server does not have a bundler initialized.
- const NoBundlerMessage = `This request requires a bundler, but one is not initialized for the API server.`
- // A Handler accepts requests with a hostname and certficate
- // parameter (which should be PEM-encoded) and returns a new signed
- // certificate. It includes upstream servers indexed by their
- // profile name.
- type Handler struct {
- signer signer.Signer
- bundler *bundler.Bundler
- }
- // NewHandlerFromSigner generates a new Handler directly from
- // an existing signer.
- func NewHandlerFromSigner(signer signer.Signer) (h *api.HTTPHandler, err error) {
- policy := signer.Policy()
- if policy == nil {
- err = errors.New(errors.PolicyError, errors.InvalidPolicy)
- return
- }
- // Sign will only respond for profiles that have no auth provider.
- // So if all of the profiles require authentication, we return an error.
- haveUnauth := (policy.Default.Provider == nil)
- for _, profile := range policy.Profiles {
- haveUnauth = haveUnauth || (profile.Provider == nil)
- }
- if !haveUnauth {
- err = errors.New(errors.PolicyError, errors.InvalidPolicy)
- return
- }
- return &api.HTTPHandler{
- Handler: &Handler{
- signer: signer,
- },
- Methods: []string{"POST"},
- }, nil
- }
- // SetBundler allows injecting an optional Bundler into the Handler.
- func (h *Handler) SetBundler(caBundleFile, intBundleFile string) (err error) {
- h.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
- return err
- }
- // This type is meant to be unmarshalled from JSON so that there can be a
- // hostname field in the API
- // TODO: Change the API such that the normal struct can be used.
- type jsonSignRequest struct {
- Hostname string `json:"hostname"`
- Hosts []string `json:"hosts"`
- Request string `json:"certificate_request"`
- Subject *signer.Subject `json:"subject,omitempty"`
- Profile string `json:"profile"`
- Label string `json:"label"`
- Serial *big.Int `json:"serial,omitempty"`
- Bundle bool `json:"bundle"`
- }
- func jsonReqToTrue(js jsonSignRequest) signer.SignRequest {
- sub := new(signer.Subject)
- if js.Subject == nil {
- sub = nil
- } else {
- // make a copy
- *sub = *js.Subject
- }
- if js.Hostname != "" {
- return signer.SignRequest{
- Hosts: signer.SplitHosts(js.Hostname),
- Subject: sub,
- Request: js.Request,
- Profile: js.Profile,
- Label: js.Label,
- Serial: js.Serial,
- }
- }
- return signer.SignRequest{
- Hosts: js.Hosts,
- Subject: sub,
- Request: js.Request,
- Profile: js.Profile,
- Label: js.Label,
- Serial: js.Serial,
- }
- }
- // Handle responds to requests for the CA to sign the certificate request
- // present in the "certificate_request" parameter for the host named
- // in the "hostname" parameter. The certificate should be PEM-encoded. If
- // provided, subject information from the "subject" parameter will be used
- // in place of the subject information from the CSR.
- func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
- log.Info("signature request received")
- body, err := ioutil.ReadAll(r.Body)
- if err != nil {
- return err
- }
- r.Body.Close()
- var req jsonSignRequest
- err = json.Unmarshal(body, &req)
- if err != nil {
- return errors.NewBadRequestString("Unable to parse sign request")
- }
- signReq := jsonReqToTrue(req)
- if req.Request == "" {
- return errors.NewBadRequestString("missing parameter 'certificate_request'")
- }
- var cert []byte
- profile, err := signer.Profile(h.signer, req.Profile)
- if err != nil {
- return err
- }
- if profile.Provider != nil {
- log.Error("profile requires authentication")
- return errors.NewBadRequestString("authentication required")
- }
- cert, err = h.signer.Sign(signReq)
- if err != nil {
- log.Warningf("failed to sign request: %v", err)
- return err
- }
- result := map[string]interface{}{"certificate": string(cert)}
- if req.Bundle {
- if h.bundler == nil {
- return api.SendResponseWithMessage(w, result, NoBundlerMessage,
- errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
- }
- bundle, err := h.bundler.BundleFromPEMorDER(cert, nil, bundler.Optimal, "")
- if err != nil {
- return err
- }
- result["bundle"] = bundle
- }
- log.Info("wrote response")
- return api.SendResponse(w, result)
- }
- // An AuthHandler verifies and signs incoming signature requests.
- type AuthHandler struct {
- signer signer.Signer
- bundler *bundler.Bundler
- }
- // NewAuthHandlerFromSigner creates a new AuthHandler from the signer
- // that is passed in.
- func NewAuthHandlerFromSigner(signer signer.Signer) (http.Handler, error) {
- policy := signer.Policy()
- if policy == nil {
- return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
- }
- if policy.Default == nil && policy.Profiles == nil {
- return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
- }
- // AuthSign will not respond for profiles that have no auth provider.
- // So if there are no profiles with auth providers in this policy,
- // we return an error.
- haveAuth := (policy.Default.Provider != nil)
- for _, profile := range policy.Profiles {
- if haveAuth {
- break
- }
- haveAuth = (profile.Provider != nil)
- }
- if !haveAuth {
- return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
- }
- return &api.HTTPHandler{
- Handler: &AuthHandler{
- signer: signer,
- },
- Methods: []string{"POST"},
- }, nil
- }
- // SetBundler allows injecting an optional Bundler into the Handler.
- func (h *AuthHandler) SetBundler(caBundleFile, intBundleFile string) (err error) {
- h.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
- return err
- }
- // Handle receives the incoming request, validates it, and processes it.
- func (h *AuthHandler) Handle(w http.ResponseWriter, r *http.Request) error {
- log.Info("signature request received")
- body, err := ioutil.ReadAll(r.Body)
- if err != nil {
- log.Errorf("failed to read response body: %v", err)
- return err
- }
- r.Body.Close()
- var aReq auth.AuthenticatedRequest
- err = json.Unmarshal(body, &aReq)
- if err != nil {
- log.Errorf("failed to unmarshal authenticated request: %v", err)
- return errors.NewBadRequest(err)
- }
- var req jsonSignRequest
- err = json.Unmarshal(aReq.Request, &req)
- if err != nil {
- log.Errorf("failed to unmarshal request from authenticated request: %v", err)
- return errors.NewBadRequestString("Unable to parse authenticated sign request")
- }
- // Sanity checks to ensure that we have a valid policy. This
- // should have been checked in NewAuthHandler.
- policy := h.signer.Policy()
- if policy == nil {
- log.Critical("signer was initialised without a signing policy")
- return errors.NewBadRequestString("invalid policy")
- }
- profile, err := signer.Profile(h.signer, req.Profile)
- if err != nil {
- return err
- }
- if profile.Provider == nil {
- log.Error("profile has no authentication provider")
- return errors.NewBadRequestString("no authentication provider")
- }
- validAuth := false
- if profile.Provider.Verify(&aReq) {
- validAuth = true
- } else if profile.PrevProvider != nil && profile.PrevProvider.Verify(&aReq) {
- validAuth = true
- }
- if !validAuth {
- log.Warning("received authenticated request with invalid token")
- return errors.NewBadRequestString("invalid token")
- }
- signReq := jsonReqToTrue(req)
- if signReq.Request == "" {
- return errors.NewBadRequestString("missing parameter 'certificate_request'")
- }
- cert, err := h.signer.Sign(signReq)
- if err != nil {
- log.Errorf("signature failed: %v", err)
- return err
- }
- result := map[string]interface{}{"certificate": string(cert)}
- if req.Bundle {
- if h.bundler == nil {
- return api.SendResponseWithMessage(w, result, NoBundlerMessage,
- errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
- }
- bundle, err := h.bundler.BundleFromPEMorDER(cert, nil, bundler.Optimal, "")
- if err != nil {
- return err
- }
- result["bundle"] = bundle
- }
- log.Info("wrote response")
- return api.SendResponse(w, result)
- }
|