1// Package bundle implements the HTTP handler for the bundle command.
2package bundle
3
4import (
5	"net/http"
6
7	"github.com/cloudflare/cfssl/api"
8	"github.com/cloudflare/cfssl/bundler"
9	"github.com/cloudflare/cfssl/errors"
10	"github.com/cloudflare/cfssl/log"
11)
12
13// Handler accepts requests for either remote or uploaded
14// certificates to be bundled, and returns a certificate bundle (or
15// error).
16type Handler struct {
17	bundler *bundler.Bundler
18}
19
20// NewHandler creates a new bundler that uses the root bundle and
21// intermediate bundle in the trust chain.
22func NewHandler(caBundleFile, intBundleFile string) (http.Handler, error) {
23	var err error
24
25	b := new(Handler)
26	if b.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile); err != nil {
27		return nil, err
28	}
29
30	log.Info("bundler API ready")
31	return api.HTTPHandler{Handler: b, Methods: []string{"POST"}}, nil
32}
33
34// Handle implements an http.Handler interface for the bundle handler.
35func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
36	blob, matched, err := api.ProcessRequestFirstMatchOf(r,
37		[][]string{
38			{"certificate"},
39			{"domain"},
40		})
41	if err != nil {
42		log.Warningf("invalid request: %v", err)
43		return err
44	}
45
46	flavor := blob["flavor"]
47	bf := bundler.Ubiquitous
48	if flavor != "" {
49		bf = bundler.BundleFlavor(flavor)
50	}
51	log.Infof("request for flavor %v", bf)
52
53	var result *bundler.Bundle
54	switch matched[0] {
55	case "domain":
56		bundle, err := h.bundler.BundleFromRemote(blob["domain"], blob["ip"], bf)
57		if err != nil {
58			log.Warningf("couldn't bundle from remote: %v", err)
59			return err
60		}
61		result = bundle
62	case "certificate":
63		bundle, err := h.bundler.BundleFromPEMorDER([]byte(blob["certificate"]), []byte(blob["private_key"]), bf, "")
64		if err != nil {
65			log.Warning("bad PEM certifcate or private key")
66			return err
67		}
68
69		serverName := blob["domain"]
70		ip := blob["ip"]
71
72		if serverName != "" {
73			err := bundle.Cert.VerifyHostname(serverName)
74			if err != nil {
75				return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
76			}
77
78		}
79
80		if ip != "" {
81			err := bundle.Cert.VerifyHostname(ip)
82			if err != nil {
83				return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
84			}
85		}
86
87		result = bundle
88	}
89	log.Info("wrote response")
90	return api.SendResponse(w, result)
91}
92