// initialCAHandler is an HTTP handler that accepts a JSON blob in the // same format as the CSR endpoint; this blob should contain the // identity information for the CA's root key. This endpoint is not // suitable for creating intermediate certificates. func initialCAHandler(w http.ResponseWriter, r *http.Request) error { log.Info("setting up initial CA handler") body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } req := new(csr.CertificateRequest) req.KeyRequest = csr.NewBasicKeyRequest() err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } cert, _, key, err := initca.New(req) if err != nil { log.Warningf("failed to initialise new CA: %v", err) return err } response := api.NewSuccessResponse(&NewCA{string(key), string(cert)}) enc := json.NewEncoder(w) err = enc.Encode(response) return err }
// Handle implements an http.Handler interface for the bundle handler. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) (err error) { blob, matched, err := api.ProcessRequestFirstMatchOf(r, [][]string{ []string{"certificate"}, []string{"domain"}, }) if err != nil { log.Warningf("invalid request: %v", err) return err } var cert *certinfo.Certificate switch matched[0] { case "domain": if cert, err = certinfo.ParseCertificateDomain(blob["domain"]); err != nil { log.Warningf("couldn't parse remote certificate: %v", err) return err } case "certificate": if cert, err = certinfo.ParseCertificatePEM([]byte(blob["certificate"])); err != nil { log.Warningf("bad PEM certifcate: %v", err) return err } } return api.SendResponse(w, cert) }
// Handle accepts client information requests, and uses the label to // look up the signer whose public certificate should be retrieved. If // the label is empty, the default label is used. func (h *MultiHandler) Handle(w http.ResponseWriter, r *http.Request) error { req := new(info.Req) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } log.Debug("checking label") if req.Label == "" { req.Label = h.defaultLabel } if _, ok := h.signers[req.Label]; !ok { log.Warningf("request for invalid endpoint") return errors.NewBadRequestString("bad label") } log.Debug("getting info") resp, err := h.signers[req.Label].Info(*req) if err != nil { log.Infof("error getting certificate: %v", err) return err } response := api.NewSuccessResponse(resp) w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) return enc.Encode(response) }
// Handle implements an http.Handler interface for the bundle handler. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { blob, matched, err := api.ProcessRequestFirstMatchOf(r, [][]string{ []string{"certificate"}, []string{"domain"}, }) if err != nil { log.Warningf("invalid request: %v", err) return err } flavor := blob["flavor"] bf := bundler.Ubiquitous if flavor != "" { bf = bundler.BundleFlavor(flavor) } log.Infof("request for flavor %v", bf) var result *bundler.Bundle switch matched[0] { case "domain": bundle, err := h.bundler.BundleFromRemote(blob["domain"], blob["ip"], bf) if err != nil { log.Warningf("couldn't bundle from remote: %v", err) return err } result = bundle case "certificate": bundle, err := h.bundler.BundleFromPEMorDER([]byte(blob["certificate"]), []byte(blob["private_key"]), bf, "") if err != nil { log.Warning("bad PEM certifcate or private key") return err } serverName := blob["domain"] ip := blob["ip"] if serverName != "" { err := bundle.Cert.VerifyHostname(serverName) if err != nil { return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } } if ip != "" { err := bundle.Cert.VerifyHostname(ip) if err != nil { return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err) } } result = bundle } log.Info("wrote response") return api.SendResponse(w, result) }
// worker does all the parsing and validation of the certificate(s) // contained in a single file. It first reads all the data in the // file, then begins parsing certificates in the file. Those // certificates are then checked for revocation. func worker(paths chan string, bundler chan *x509.Certificate, pool *sync.WaitGroup) { defer (*pool).Done() for { path, ok := <-paths if !ok { return } log.Infof("Loading %s", path) fileData, err := ioutil.ReadFile(path) if err != nil { log.Warningf("%v", err) continue } for { var block *pem.Block if len(fileData) == 0 { break } block, fileData = pem.Decode(fileData) if block == nil { log.Warningf("%s: no PEM data found", path) break } else if block.Type != "CERTIFICATE" { log.Info("Skipping non-certificate") continue } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { log.Warningf("Invalid certificate: %v", err) continue } log.Infof("Validating %+v", cert.Subject) revoked, ok := revoke.VerifyCertificate(cert) if !ok { log.Warning("Failed to verify certificate.") } else if !revoked { bundler <- cert } else { log.Info("Skipping revoked certificate") } } } }
// check a cert against a specific CRL. Returns the same bool pair // as revCheck. func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { crl, ok := CRLSet[url] if ok && crl == nil { ok = false delete(CRLSet, url) } var shouldFetchCRL = true if ok { if !crl.HasExpired(time.Now()) { shouldFetchCRL = false } } if shouldFetchCRL { var err error crl, err = fetchCRL(url) if err != nil { log.Warningf("failed to fetch CRL: %v", err) return false, false } CRLSet[url] = crl } for _, revoked := range crl.TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { log.Info("Serial number match: intermediate is revoked.") return true, true } } return false, true }
func scanMain(args []string, c cli.Config) (err error) { if c.List { printJSON(scan.Default) } else { if err = scan.LoadRootCAs(c.CABundleFile); err != nil { return } if len(args) >= c.MaxHosts { log.Warningf("Only scanning max-hosts=%d out of %d args given", c.MaxHosts, len(args)) args = args[:c.MaxHosts] } else if c.CSVFile != "" { args, err = parseCSV(args, c.CSVFile, c.MaxHosts) if err != nil { return } } ctx := newContext(c, c.NumWorkers) // Execute for each HOST argument given for len(args) > 0 { var host string host, args, err = cli.PopFirstArgument(args) if err != nil { return } ctx.hosts <- host } close(ctx.hosts) ctx.Wait() } return }
func (ctx *context) copyResults(timeout time.Duration) map[string]FamilyResult { var timedOut bool done := make(chan bool, 1) results := make(map[string]FamilyResult) go func() { for result := range ctx.resultChan { if timedOut { log.Debugf("Received result after timeout: %v", result) continue } if results[result.Family] == nil { results[result.Family] = make(FamilyResult) } results[result.Family][result.Scanner] = result.ScannerResult } done <- true }() select { case <-done: case <-time.After(timeout): timedOut = true log.Warningf("Scan timed out after %v", timeout) } return results }
// serverMain is the command line entry point to the API server. It sets up a // new HTTP server to handle sign, bundle, and validate requests. func serverMain(args []string, c cli.Config) error { conf = c // serve doesn't support arguments. if len(args) > 0 { return errors.New("argument is provided but not defined; please refer to the usage by flag -h") } bundler.IntermediateStash = conf.IntDir var err error if err = ubiquity.LoadPlatforms(conf.Metadata); err != nil { return err } if c.DBConfigFile != "" { db, err = certdb.DBFromConfig(c.DBConfigFile) if err != nil { return err } } log.Info("Initializing signer") if s, err = sign.SignerFromConfigAndDB(c, db); err != nil { log.Warningf("couldn't initialize signer: %v", err) } if ocspSigner, err = ocspsign.SignerFromConfig(c); err != nil { log.Warningf("couldn't initialize ocsp signer: %v", err) } registerHandlers() addr := net.JoinHostPort(conf.Address, strconv.Itoa(conf.Port)) if conf.TLSCertFile == "" || conf.TLSKeyFile == "" { log.Info("Now listening on ", addr) return http.ListenAndServe(addr, nil) } log.Info("Now listening on https://", addr) return http.ListenAndServeTLS(addr, conf.TLSCertFile, conf.TLSKeyFile, nil) }
// Handle responds to requests for the CA to generate a new private // key and certificate request on behalf of the client. The format for // these requests is documented in the API documentation. func (g *Handler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("request for CSR") body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } req := new(csr.CertificateRequest) req.KeyRequest = csr.NewBasicKeyRequest() err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } if req.CA != nil { log.Warningf("request received with CA section") return errors.NewBadRequestString("ca section only permitted in initca") } csr, key, err := g.generator.ProcessRequest(req) if err != nil { log.Warningf("failed to process CSR: %v", err) // The validator returns a *cfssl/errors.HttpError return err } sum, err := computeSum(csr) if err != nil { return errors.NewBadRequest(err) } // Both key and csr are returned PEM-encoded. response := api.NewSuccessResponse(&CertRequest{ Key: string(key), CSR: string(csr), Sums: map[string]Sum{"certificate_request": sum}, }) w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) err = enc.Encode(response) return err }
// We can't handle LDAP certificates, so this checks to see if the // URL string points to an LDAP resource so that we can ignore it. func ldapURL(url string) bool { u, err := neturl.Parse(url) if err != nil { log.Warningf("error parsing url %s: %v", url, err) return false } if u.Scheme == "ldap" { return true } return false }
// registerHandlers instantiates various handlers and associate them to corresponding endpoints. func registerHandlers() { for path, getHandler := range endpoints { path = v1APIPath(path) log.Infof("Setting up '%s' endpoint", path) if handler, err := getHandler(); err != nil { log.Warningf("endpoint '%s' is disabled: %v", path, err) } else { http.Handle(path, handler) } } log.Info("Handler set up complete.") }
// Handle listens for incoming requests for CA information, and returns // a list containing information on each root certificate. func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error { req := new(info.Req) body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } resp, err := h.sign.Info(*req) if err != nil { return err } response := api.NewSuccessResponse(resp) w.Header().Set("Content-Type", "application/json") enc := json.NewEncoder(w) return enc.Encode(response) }
// ProcessRequest validates and processes the incoming request. It is // a wrapper around a validator and the ParseRequest function. func (g *Generator) ProcessRequest(req *CertificateRequest) (csr, key []byte, err error) { log.Info("generate received request") err = g.Validator(req) if err != nil { log.Warningf("invalid request: %v", err) return } csr, key, err = ParseRequest(req) if err != nil { return nil, nil, err } return }
// scanHandler is an HTTP handler that accepts GET parameters for host (required) // family and scanner, and uses these to perform scans, returning a JSON blob result. func scanHandler(w http.ResponseWriter, r *http.Request) error { if err := r.ParseForm(); err != nil { log.Warningf("failed to parse body: %v", err) return errors.NewBadRequest(err) } family := r.Form.Get("family") scanner := r.Form.Get("scanner") ip := r.Form.Get("ip") timeoutStr := r.Form.Get("timeout") var timeout time.Duration var err error if timeoutStr != "" { if timeout, err = time.ParseDuration(timeoutStr); err != nil { return errors.NewBadRequest(err) } if timeout < time.Second || timeout > 5*time.Minute { return errors.NewBadRequestString("invalid timeout given") } } else { timeout = time.Minute } host := r.Form.Get("host") if host == "" { log.Warningf("no host given") return errors.NewBadRequestString("no host given") } results, err := scan.Default.RunScans(host, ip, family, scanner, timeout) if err != nil { return errors.NewBadRequest(err) } return json.NewEncoder(w).Encode(api.NewSuccessResponse(results)) }
// 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) }
// Handle responds to requests for the CA to generate a new private // key and certificate on behalf of the client. The format for these // requests is documented in the API documentation. func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error { log.Info("request for CSR") req := new(genSignRequest) req.Request = csr.New() body, err := ioutil.ReadAll(r.Body) if err != nil { log.Warningf("failed to read request body: %v", err) return errors.NewBadRequest(err) } err = json.Unmarshal(body, req) if err != nil { log.Warningf("failed to unmarshal request: %v", err) return errors.NewBadRequest(err) } if req.Request == nil { log.Warning("empty request received") return errors.NewBadRequestString("missing request section") } if req.Request.CA != nil { log.Warningf("request received with CA section") return errors.NewBadRequestString("ca section only permitted in initca") } csr, key, err := cg.generator.ProcessRequest(req.Request) if err != nil { log.Warningf("failed to process CSR: %v", err) // The validator returns a *cfssl/errors.HttpError return err } signReq := signer.SignRequest{ Request: string(csr), Profile: req.Profile, Label: req.Label, } certBytes, err := cg.signer.Sign(signReq) if err != nil { log.Warningf("failed to sign request: %v", err) return err } reqSum, err := computeSum(csr) if err != nil { return errors.NewBadRequest(err) } certSum, err := computeSum(certBytes) if err != nil { return errors.NewBadRequest(err) } result := map[string]interface{}{ "private_key": string(key), "certificate_request": string(csr), "certificate": string(certBytes), "sums": map[string]Sum{ "certificate_request": reqSum, "certificate": certSum, }, } if len(req.Request.Hosts) == 0 { return api.SendResponseWithMessage(w, result, CSRNoHostMessage, errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode) } return api.SendResponse(w, result) }