// 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 } log.Info("Initializing signer") if s, err = sign.SignerFromConfig(c); 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)) log.Info("Now listening on ", addr) return http.ListenAndServe(addr, nil) }
// 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 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) }
// 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") 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, 0) if err != nil { log.Warningf("%v", err) return errors.NewBadRequest(err) } response := api.NewSuccessResponse(results) 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) }
// 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 }
// 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 v1Endpoints { path = "/api/v1/cfssl/" + 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) } } for path, getHandler := range staticEndpoints { 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 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 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 }
// 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) }
// RunScans iterates over AllScans, running scans matching the family and scanner // regular expressions. func (fs FamilySet) RunScans(host, ip, family, scanner string, dur time.Duration) (map[string]FamilyResult, error) { hostname, port, err := net.SplitHostPort(host) if err != nil { hostname = host port = "443" } var addr string if net.ParseIP(ip) != nil { addr = net.JoinHostPort(ip, port) } else { addr = net.JoinHostPort(hostname, port) } familyRegexp, err := regexp.Compile(family) if err != nil { return nil, err } scannerRegexp, err := regexp.Compile(scanner) if err != nil { return nil, err } results := make(chan map[string]FamilyResult) timeout := make(chan bool) if dur > 0 { go startTimer(dur, timeout) } familyResults := make(map[string]FamilyResult) go func() { for familyName, family := range fs { if familyRegexp.MatchString(familyName) { scannerResults := make(map[string]ScannerResult) for scannerName, scanner := range family.Scanners { if scannerRegexp.MatchString(scannerName) { grade, output, err := scanner.Scan(addr, hostname) if grade != Skipped { result := ScannerResult{ Grade: grade.String(), Output: output, } if err != nil { result.Error = err.Error() } scannerResults[scannerName] = result } } } if len(scannerResults) > 0 { familyResults[familyName] = scannerResults } } } results <- familyResults }() select { case <-timeout: log.Warningf("scan: %s timed out after %v", host, dur) return familyResults, nil case res := <-results: return res, nil } }
// 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) 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, }, } return api.SendResponse(w, result) }