2015-11-18 15:16:09 +00:00
|
|
|
package pki
|
|
|
|
|
|
|
|
import (
|
2018-01-08 18:31:38 +00:00
|
|
|
"context"
|
2015-11-18 15:16:09 +00:00
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
|
2019-04-13 07:44:06 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/framework"
|
2019-04-12 21:54:35 +00:00
|
|
|
"github.com/hashicorp/vault/sdk/helper/certutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/helper/errutil"
|
|
|
|
"github.com/hashicorp/vault/sdk/logical"
|
2015-11-18 15:16:09 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func pathGenerateIntermediate(b *backend) *framework.Path {
|
|
|
|
ret := &framework.Path{
|
|
|
|
Pattern: "intermediate/generate/" + framework.GenericNameRegex("exported"),
|
2022-02-25 18:26:34 +00:00
|
|
|
Operations: map[logical.Operation]framework.OperationHandler{
|
|
|
|
logical.UpdateOperation: &framework.PathOperation{
|
|
|
|
Callback: b.pathGenerateIntermediate,
|
|
|
|
// Read more about why these flags are set in backend.go
|
|
|
|
ForwardPerformanceStandby: true,
|
|
|
|
ForwardPerformanceSecondary: true,
|
|
|
|
},
|
2015-11-18 15:16:09 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathGenerateIntermediateHelpSyn,
|
|
|
|
HelpDescription: pathGenerateIntermediateHelpDesc,
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.Fields = addCACommonFields(map[string]*framework.FieldSchema{})
|
|
|
|
ret.Fields = addCAKeyGenerationFields(ret.Fields)
|
2018-03-20 14:09:59 +00:00
|
|
|
ret.Fields["add_basic_constraints"] = &framework.FieldSchema{
|
|
|
|
Type: framework.TypeBool,
|
|
|
|
Description: `Whether to add a Basic Constraints
|
|
|
|
extension with CA: true. Only needed as a
|
|
|
|
workaround in some compatibility scenarios
|
|
|
|
with Active Directory Certificate Services.`,
|
|
|
|
}
|
2015-11-18 15:16:09 +00:00
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func pathSetSignedIntermediate(b *backend) *framework.Path {
|
|
|
|
ret := &framework.Path{
|
|
|
|
Pattern: "intermediate/set-signed",
|
|
|
|
|
|
|
|
Fields: map[string]*framework.FieldSchema{
|
2021-04-08 16:43:39 +00:00
|
|
|
"certificate": {
|
2015-11-18 15:16:09 +00:00
|
|
|
Type: framework.TypeString,
|
|
|
|
Description: `PEM-format certificate. This must be a CA
|
|
|
|
certificate with a public key matching the
|
|
|
|
previously-generated key from the generation
|
|
|
|
endpoint.`,
|
|
|
|
},
|
|
|
|
},
|
2022-02-25 18:26:34 +00:00
|
|
|
Operations: map[logical.Operation]framework.OperationHandler{
|
|
|
|
logical.UpdateOperation: &framework.PathOperation{
|
|
|
|
Callback: b.pathSetSignedIntermediate,
|
|
|
|
// Read more about why these flags are set in backend.go
|
|
|
|
ForwardPerformanceStandby: true,
|
|
|
|
ForwardPerformanceSecondary: true,
|
|
|
|
},
|
2015-11-18 15:16:09 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
HelpSynopsis: pathSetSignedIntermediateHelpSyn,
|
|
|
|
HelpDescription: pathSetSignedIntermediateHelpDesc,
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *backend) pathGenerateIntermediate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2015-11-18 15:16:09 +00:00
|
|
|
var err error
|
|
|
|
|
2022-03-03 21:30:18 +00:00
|
|
|
exported, format, role, errorResp := b.getGenerationParams(ctx, data, req.MountPoint)
|
2015-11-18 15:16:09 +00:00
|
|
|
if errorResp != nil {
|
|
|
|
return errorResp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var resp *logical.Response
|
2019-05-09 15:43:11 +00:00
|
|
|
input := &inputBundle{
|
2018-02-16 22:19:34 +00:00
|
|
|
role: role,
|
|
|
|
req: req,
|
|
|
|
apiData: data,
|
|
|
|
}
|
2022-01-27 04:06:25 +00:00
|
|
|
parsedBundle, err := generateIntermediateCSR(ctx, b, input, b.Backend.GetRandomReader())
|
2015-11-18 15:16:09 +00:00
|
|
|
if err != nil {
|
|
|
|
switch err.(type) {
|
2016-07-28 19:19:27 +00:00
|
|
|
case errutil.UserError:
|
2015-11-18 15:16:09 +00:00
|
|
|
return logical.ErrorResponse(err.Error()), nil
|
2020-03-06 21:32:36 +00:00
|
|
|
default:
|
2015-11-18 15:16:09 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
csrb, err := parsedBundle.ToCSRBundle()
|
|
|
|
if err != nil {
|
2021-04-22 15:20:59 +00:00
|
|
|
return nil, fmt.Errorf("error converting raw CSR bundle to CSR bundle: %w", err)
|
2015-11-18 15:16:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
resp = &logical.Response{
|
|
|
|
Data: map[string]interface{}{},
|
|
|
|
}
|
|
|
|
|
|
|
|
switch format {
|
|
|
|
case "pem":
|
|
|
|
resp.Data["csr"] = csrb.CSR
|
|
|
|
if exported {
|
|
|
|
resp.Data["private_key"] = csrb.PrivateKey
|
|
|
|
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
|
|
|
}
|
2016-02-01 18:19:41 +00:00
|
|
|
|
|
|
|
case "pem_bundle":
|
|
|
|
resp.Data["csr"] = csrb.CSR
|
|
|
|
if exported {
|
|
|
|
resp.Data["csr"] = fmt.Sprintf("%s\n%s", csrb.PrivateKey, csrb.CSR)
|
|
|
|
resp.Data["private_key"] = csrb.PrivateKey
|
|
|
|
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
|
|
|
}
|
|
|
|
|
2015-11-18 15:16:09 +00:00
|
|
|
case "der":
|
|
|
|
resp.Data["csr"] = base64.StdEncoding.EncodeToString(parsedBundle.CSRBytes)
|
|
|
|
if exported {
|
|
|
|
resp.Data["private_key"] = base64.StdEncoding.EncodeToString(parsedBundle.PrivateKeyBytes)
|
|
|
|
resp.Data["private_key_type"] = csrb.PrivateKeyType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 17:05:07 +00:00
|
|
|
if data.Get("private_key_format").(string) == "pkcs8" {
|
|
|
|
err = convertRespToPKCS8(resp)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-18 15:16:09 +00:00
|
|
|
cb := &certutil.CertBundle{}
|
|
|
|
cb.PrivateKey = csrb.PrivateKey
|
|
|
|
cb.PrivateKeyType = csrb.PrivateKeyType
|
|
|
|
|
|
|
|
entry, err := logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
err = req.Storage.Put(ctx, entry)
|
2015-11-18 15:16:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2018-01-08 18:31:38 +00:00
|
|
|
func (b *backend) pathSetSignedIntermediate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
2015-11-18 15:16:09 +00:00
|
|
|
cert := data.Get("certificate").(string)
|
|
|
|
|
|
|
|
if cert == "" {
|
2017-05-10 14:28:35 +00:00
|
|
|
return logical.ErrorResponse("no certificate provided in the \"certificate\" parameter"), nil
|
2015-11-18 15:16:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inputBundle, err := certutil.ParsePEMBundle(cert)
|
|
|
|
if err != nil {
|
|
|
|
switch err.(type) {
|
2016-07-28 19:19:27 +00:00
|
|
|
case errutil.InternalError:
|
2015-11-18 15:16:09 +00:00
|
|
|
return nil, err
|
|
|
|
default:
|
|
|
|
return logical.ErrorResponse(err.Error()), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if inputBundle.Certificate == nil {
|
|
|
|
return logical.ErrorResponse("supplied certificate could not be successfully parsed"), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
cb := &certutil.CertBundle{}
|
2018-01-19 06:44:44 +00:00
|
|
|
entry, err := req.Storage.Get(ctx, "config/ca_bundle")
|
2015-11-18 15:16:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if entry == nil {
|
|
|
|
return logical.ErrorResponse("could not find any existing entry with a private key"), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
err = entry.DecodeJSON(cb)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(cb.PrivateKey) == 0 || cb.PrivateKeyType == "" {
|
2016-02-09 21:08:30 +00:00
|
|
|
return logical.ErrorResponse("could not find an existing private key"), nil
|
2015-11-18 15:16:09 +00:00
|
|
|
}
|
|
|
|
|
2022-01-27 04:06:25 +00:00
|
|
|
parsedCB, err := parseCABundle(ctx, b, req, cb)
|
2015-11-18 15:16:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if parsedCB.PrivateKey == nil {
|
|
|
|
return nil, fmt.Errorf("saved key could not be parsed successfully")
|
|
|
|
}
|
|
|
|
|
|
|
|
inputBundle.PrivateKey = parsedCB.PrivateKey
|
|
|
|
inputBundle.PrivateKeyType = parsedCB.PrivateKeyType
|
|
|
|
inputBundle.PrivateKeyBytes = parsedCB.PrivateKeyBytes
|
|
|
|
|
|
|
|
if !inputBundle.Certificate.IsCA {
|
|
|
|
return logical.ErrorResponse("the given certificate is not marked for CA use and cannot be used with this backend"), nil
|
|
|
|
}
|
|
|
|
|
2016-09-28 00:50:17 +00:00
|
|
|
if err := inputBundle.Verify(); err != nil {
|
2021-04-22 15:20:59 +00:00
|
|
|
return nil, fmt.Errorf("verification of parsed bundle failed: %w", err)
|
2016-09-28 00:50:17 +00:00
|
|
|
}
|
|
|
|
|
2015-11-18 15:16:09 +00:00
|
|
|
cb, err = inputBundle.ToCertBundle()
|
|
|
|
if err != nil {
|
2021-04-22 15:20:59 +00:00
|
|
|
return nil, fmt.Errorf("error converting raw values into cert bundle: %w", err)
|
2015-11-18 15:16:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
entry, err = logical.StorageEntryJSON("config/ca_bundle", cb)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-19 06:44:44 +00:00
|
|
|
err = req.Storage.Put(ctx, entry)
|
2015-11-18 15:16:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-03 14:12:58 +00:00
|
|
|
entry.Key = "certs/" + normalizeSerial(cb.SerialNumber)
|
2016-02-24 21:19:01 +00:00
|
|
|
entry.Value = inputBundle.CertificateBytes
|
2018-01-19 06:44:44 +00:00
|
|
|
err = req.Storage.Put(ctx, entry)
|
2016-02-24 21:19:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-11-18 15:16:09 +00:00
|
|
|
// For ease of later use, also store just the certificate at a known
|
2015-11-19 21:51:27 +00:00
|
|
|
// location
|
2015-11-18 15:16:09 +00:00
|
|
|
entry.Key = "ca"
|
|
|
|
entry.Value = inputBundle.CertificateBytes
|
2018-01-19 06:44:44 +00:00
|
|
|
err = req.Storage.Put(ctx, entry)
|
2015-11-18 15:16:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2015-11-19 21:51:27 +00:00
|
|
|
// Build a fresh CRL
|
2018-08-21 15:20:57 +00:00
|
|
|
err = buildCRL(ctx, b, req, true)
|
2015-11-18 15:16:09 +00:00
|
|
|
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
const pathGenerateIntermediateHelpSyn = `
|
|
|
|
Generate a new CSR and private key used for signing.
|
|
|
|
`
|
|
|
|
|
|
|
|
const pathGenerateIntermediateHelpDesc = `
|
|
|
|
See the API documentation for more information.
|
|
|
|
`
|
|
|
|
|
|
|
|
const pathSetSignedIntermediateHelpSyn = `
|
|
|
|
Provide the signed intermediate CA cert.
|
|
|
|
`
|
|
|
|
|
|
|
|
const pathSetSignedIntermediateHelpDesc = `
|
|
|
|
See the API documentation for more information.
|
|
|
|
`
|