func main() { flagAddr := flag.String("a", ":8888", "listening address") flagRootFile := flag.String("roots", "", "configuration file specifying root keys") flagDefaultLabel := flag.String("l", "", "specify a default label") flag.IntVar(&log.Level, "loglevel", log.LevelInfo, "log level (0 = DEBUG, 4 = ERROR)") flag.Parse() if *flagRootFile == "" { log.Fatal("no root file specified") } roots, err := config.Parse(*flagRootFile) if err != nil { log.Fatalf("%v", err) } for label, root := range roots { s, err := parseSigner(root) if err != nil { log.Criticalf("%v", err) } signers[label] = s if root.ACL != nil { whitelists[label] = root.ACL } log.Info("loaded signer ", label) } defaultLabel = *flagDefaultLabel initStats() infoHandler, err := info.NewMultiHandler(signers, defaultLabel) if err != nil { log.Criticalf("%v", err) } var localhost = whitelist.NewBasic() localhost.Add(net.ParseIP("127.0.0.1")) localhost.Add(net.ParseIP("::1")) metrics, err := whitelist.NewHandlerFunc(dumpMetrics, metricsDisallowed, localhost) if err != nil { log.Criticalf("failed to set up the metrics whitelist: %v", err) } http.HandleFunc("/api/v1/cfssl/authsign", dispatchRequest) http.Handle("/api/v1/cfssl/info", infoHandler) http.Handle("/api/v1/cfssl/metrics", metrics) log.Info("listening on ", *flagAddr) log.Error(http.ListenAndServe(*flagAddr, nil)) }
// 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 *SignHandler) 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 signer.SignRequest err = json.Unmarshal(body, &req) if err != nil { return err } if len(req.Hosts) == 0 { return errors.NewBadRequestString("missing paratmeter 'hosts'") } if req.Request == "" { return errors.NewBadRequestString("missing parameter 'certificate_request'") } var cert []byte var profile *config.SigningProfile policy := h.signer.Policy() if policy != nil && policy.Profiles != nil && req.Profile != "" { profile = policy.Profiles[req.Profile] } if profile == nil && policy != nil { profile = policy.Default } if profile.Provider != nil { log.Error("profile requires authentication") return errors.NewBadRequestString("authentication required") } cert, err = h.signer.Sign(req) if err != nil { log.Warningf("failed to sign request: %v", err) return err } result := map[string]string{"certificate": string(cert)} log.Info("wrote response") return api.SendResponse(w, result) }
// Handle responds to requests for a ocsp signature. It creates and signs // a ocsp response for the provided certificate and status. If the status // is revoked then it also adds reason and revoked_at. The response is // base64 encoded. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { body, err := ioutil.ReadAll(r.Body) if err != nil { return err } r.Body.Close() // Default the status to good so it matches the cli req := &jsonSignRequest{ Status: "good", } err = json.Unmarshal(body, req) if err != nil { return errors.NewBadRequestString("Unable to parse sign request") } cert, err := helpers.ParseCertificatePEM([]byte(req.Certificate)) if err != nil { log.Error("Error from ParseCertificatePEM", err) return errors.NewBadRequestString("Malformed certificate") } signReq := ocsp.SignRequest{ Certificate: cert, Status: req.Status, } // We need to convert the time from being a string to a time.Time if req.Status == "revoked" { signReq.Reason = req.Reason // "now" is accepted and the default on the cli so default that here if req.RevokedAt == "" || req.RevokedAt == "now" { signReq.RevokedAt = time.Now() } else { signReq.RevokedAt, err = time.Parse("2006-01-02", req.RevokedAt) if err != nil { return errors.NewBadRequestString("Malformed revocation time") } } } resp, err := h.signer.Sign(signReq) if err != nil { return err } b64Resp := base64.StdEncoding.EncodeToString(resp) result := map[string]string{"ocspResponse": b64Resp} return api.SendResponse(w, result) }
// 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]string{"certificate": string(cert)} log.Info("wrote response") return api.SendResponse(w, result) }
// ParseAndLoad converts HashAlgo and KeyAlgo to corresponding ubiquity value and load // certificates into internal KeyStore from KeyStoreFiles func (p *Platform) ParseAndLoad() (ok bool) { p.HashUbiquity = p.hashUbiquity() p.KeyAlgoUbiquity = p.keyAlgoUbiquity() p.KeyStore = map[string]bool{} if p.KeyStoreFile != "" { pemBytes, err := ioutil.ReadFile(p.KeyStoreFile) if err != nil { log.Error(err) return false } // Best effort parsing the PEMs such that ignore all borken pem, // since some of CA certs have negative serial number which trigger errors. for len(pemBytes) > 0 { var certs []*x509.Certificate certs, rest, err := helpers.ParseOneCertificateFromPEM(pemBytes) // If one certificate object is parsed, possibly a PKCS#7 // structure containing multiple certs, record the raw SHA1 hash(es). if err == nil && certs != nil { for _, cert := range certs { p.KeyStore.Add(cert) } } if len(rest) < len(pemBytes) { pemBytes = rest } else { // No progress in bytes parsing, bail out. break } } } if p.HashUbiquity <= UnknownHashUbiquity || p.KeyAlgoUbiquity <= UnknownAlgoUbiquity { return false } return true }
// populate is used to fill in the fields that are not in JSON // // First, the ExpiryString parameter is needed to parse // expiration timestamps from JSON. The JSON decoder is not able to // decode a string time duration to a time.Duration, so this is called // when loading the configuration to properly parse and fill out the // Expiry parameter. // This function is also used to create references to the auth key // and default remote for the profile. // It returns true if ExpiryString is a valid representation of a // time.Duration, and the AuthKeyString and RemoteName point to // valid objects. It returns false otherwise. func (p *SigningProfile) populate(cfg *Config) error { if p == nil { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("can't parse nil profile")) } var err error if p.RemoteName == "" && p.AuthRemote.RemoteName == "" { log.Debugf("parse expiry in profile") if p.ExpiryString == "" { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("empty expiry string")) } dur, err := time.ParseDuration(p.ExpiryString) if err != nil { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err) } log.Debugf("expiry is valid") p.Expiry = dur if p.BackdateString != "" { dur, err = time.ParseDuration(p.BackdateString) if err != nil { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err) } p.Backdate = dur } if !p.NotBefore.IsZero() && !p.NotAfter.IsZero() && p.NotAfter.Before(p.NotBefore) { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err) } if len(p.Policies) > 0 { for _, policy := range p.Policies { for _, qualifier := range policy.Qualifiers { if qualifier.Type != "" && qualifier.Type != "id-qt-unotice" && qualifier.Type != "id-qt-cps" { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid policy qualifier type")) } } } } } else if p.RemoteName != "" { log.Debug("match remote in profile to remotes section") if p.AuthRemote.RemoteName != "" { log.Error("profile has both a remote and an auth remote specified") return cferr.New(cferr.PolicyError, cferr.InvalidPolicy) } if remote := cfg.Remotes[p.RemoteName]; remote != "" { if err := p.updateRemote(remote); err != nil { return err } } else { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to find remote in remotes section")) } } else { log.Debug("match auth remote in profile to remotes section") if remote := cfg.Remotes[p.AuthRemote.RemoteName]; remote != "" { if err := p.updateRemote(remote); err != nil { return err } } else { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to find remote in remotes section")) } } if p.AuthKeyName != "" { log.Debug("match auth key in profile to auth_keys section") if key, ok := cfg.AuthKeys[p.AuthKeyName]; ok == true { if key.Type == "standard" { p.Provider, err = auth.New(key.Key, nil) if err != nil { log.Debugf("failed to create new standard auth provider: %v", err) return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to create new standard auth provider")) } } else { log.Debugf("unknown authentication type %v", key.Type) return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("unknown authentication type")) } } else { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to find auth_key in auth_keys section")) } } if p.AuthRemote.AuthKeyName != "" { log.Debug("match auth remote key in profile to auth_keys section") if key, ok := cfg.AuthKeys[p.AuthRemote.AuthKeyName]; ok == true { if key.Type == "standard" { p.RemoteProvider, err = auth.New(key.Key, nil) if err != nil { log.Debugf("failed to create new standard auth provider: %v", err) return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to create new standard auth provider")) } } else { log.Debugf("unknown authentication type %v", key.Type) return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("unknown authentication type")) } } else { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to find auth_remote's auth_key in auth_keys section")) } } if p.NameWhitelistString != "" { log.Debug("compiling whitelist regular expression") rule, err := regexp.Compile(p.NameWhitelistString) if err != nil { return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to compile name whitelist section")) } p.NameWhitelist = rule } return nil }
// 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") } if !profile.Provider.Verify(&aReq) { 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]string{"certificate": string(cert)} log.Info("wrote response") return api.SendResponse(w, result) }
func gencertMain(args []string, c cli.Config) (err error) { csrJSONFile, args, err := cli.PopFirstArgument(args) if err != nil { return } csrJSONFileBytes, err := cli.ReadStdin(csrJSONFile) if err != nil { return } req := csr.CertificateRequest{ KeyRequest: csr.NewBasicKeyRequest(), } err = json.Unmarshal(csrJSONFileBytes, &req) if err != nil { return } if c.IsCA { var key, csrPEM, cert []byte log.Infof("re-generate a CA certificate from CSR and CA key") cert, csrPEM, err = initca.NewFromPEM(&req, c.CAKeyFile) if err != nil { log.Errorf("%v\n", err) log.Infof("fallback to generating a new CA key and certificate from CSR") cert, csrPEM, key, err = initca.New(&req) if err != nil { return } } cli.PrintCert(key, csrPEM, cert) } else { if req.CA != nil { err = errors.New("ca section only permitted in initca") return } // Remote can be forced on the command line or in the config if c.Remote == "" && c.CFG == nil { if c.CAFile == "" { log.Error("need a CA certificate (provide one with -ca)") return } if c.CAKeyFile == "" { log.Error("need a CA key (provide one with -ca-key)") return } } var key, csrBytes []byte g := &csr.Generator{Validator: genkey.Validator} csrBytes, key, err = g.ProcessRequest(&req) if err != nil { key = nil return } s, err := sign.SignerFromConfig(c) if err != nil { return err } var cert []byte req := signer.SignRequest{ Request: string(csrBytes), Hosts: signer.SplitHosts(c.Hostname), Profile: c.Profile, Label: c.Label, } cert, err = s.Sign(req) if err != nil { return err } cli.PrintCert(key, csrBytes, cert) } return nil }