2015-11-18 15:16:09 +00:00
package pki
import (
2018-01-08 18:31:38 +00:00
"context"
2017-09-01 03:07:15 +00:00
"crypto/rand"
"crypto/x509"
2015-11-18 15:16:09 +00:00
"encoding/base64"
2017-09-01 03:07:15 +00:00
"encoding/pem"
2015-11-18 15:16:09 +00:00
"fmt"
2017-09-01 03:07:15 +00:00
"reflect"
2018-03-18 20:00:51 +00:00
"strings"
2017-08-31 19:46:13 +00:00
"time"
2015-11-18 15:16:09 +00:00
2019-07-19 01:04:56 +00:00
"github.com/hashicorp/vault/sdk/helper/certutil"
2017-09-01 03:07:15 +00:00
"github.com/hashicorp/errwrap"
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/errutil"
"github.com/hashicorp/vault/sdk/logical"
2015-11-18 15:16:09 +00:00
)
func pathGenerateRoot ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root/generate/" + framework . GenericNameRegex ( "exported" ) ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : b . pathCAGenerateRoot ,
2015-11-18 15:16:09 +00:00
} ,
HelpSynopsis : pathGenerateRootHelpSyn ,
HelpDescription : pathGenerateRootHelpDesc ,
}
ret . Fields = addCACommonFields ( map [ string ] * framework . FieldSchema { } )
ret . Fields = addCAKeyGenerationFields ( ret . Fields )
ret . Fields = addCAIssueFields ( ret . Fields )
return ret
}
2017-08-15 18:00:40 +00:00
func pathDeleteRoot ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . DeleteOperation : b . pathCADeleteRoot ,
} ,
HelpSynopsis : pathDeleteRootHelpSyn ,
HelpDescription : pathDeleteRootHelpDesc ,
}
return ret
}
2015-11-18 15:16:09 +00:00
func pathSignIntermediate ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root/sign-intermediate" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2016-01-07 15:30:47 +00:00
logical . UpdateOperation : b . pathCASignIntermediate ,
2015-11-18 15:16:09 +00:00
} ,
HelpSynopsis : pathSignIntermediateHelpSyn ,
HelpDescription : pathSignIntermediateHelpDesc ,
}
ret . Fields = addCACommonFields ( map [ string ] * framework . FieldSchema { } )
ret . Fields = addCAIssueFields ( ret . Fields )
ret . Fields [ "csr" ] = & framework . FieldSchema {
Type : framework . TypeString ,
Default : "" ,
Description : ` PEM-format CSR to be signed. ` ,
}
ret . Fields [ "use_csr_values" ] = & framework . FieldSchema {
Type : framework . TypeBool ,
Default : false ,
Description : ` If true , then :
1 ) Subject information , including names and alternate
names , will be preserved from the CSR rather than
using values provided in the other parameters to
this path ;
2 ) Any key usages requested in the CSR will be
added to the basic set of key usages used for CA
certs signed by this path ; for instance ,
the non - repudiation flag . ` ,
}
return ret
}
2017-08-31 19:46:13 +00:00
func pathSignSelfIssued ( b * backend ) * framework . Path {
ret := & framework . Path {
Pattern : "root/sign-self-issued" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
2017-09-01 03:07:15 +00:00
logical . UpdateOperation : b . pathCASignSelfIssued ,
} ,
Fields : map [ string ] * framework . FieldSchema {
2021-04-08 16:43:39 +00:00
"certificate" : {
2017-09-01 03:07:15 +00:00
Type : framework . TypeString ,
Description : ` PEM-format self-issued certificate to be signed. ` ,
} ,
2017-08-31 19:46:13 +00:00
} ,
HelpSynopsis : pathSignSelfIssuedHelpSyn ,
HelpDescription : pathSignSelfIssuedHelpDesc ,
}
return ret
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCADeleteRoot ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2018-01-19 06:44:44 +00:00
return nil , req . Storage . Delete ( ctx , "config/ca_bundle" )
2017-08-15 18:00:40 +00:00
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCAGenerateRoot ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2015-11-18 15:16:09 +00:00
var err error
2018-01-19 06:44:44 +00:00
entry , err := req . Storage . Get ( ctx , "config/ca_bundle" )
2017-08-15 18:00:40 +00:00
if err != nil {
return nil , err
}
if entry != nil {
2018-05-09 14:29:54 +00:00
resp := & logical . Response { }
resp . AddWarning ( fmt . Sprintf ( "Refusing to generate a root certificate over an existing root certificate. If you really want to destroy the original root certificate, please issue a delete against %sroot." , req . MountPoint ) )
return resp , nil
2017-08-15 18:00:40 +00:00
}
2015-11-18 15:16:09 +00:00
exported , format , role , errorResp := b . getGenerationParams ( data )
if errorResp != nil {
return errorResp , nil
}
maxPathLengthIface , ok := data . GetOk ( "max_path_length" )
if ok {
maxPathLength := maxPathLengthIface . ( int )
role . MaxPathLength = & maxPathLength
}
2019-05-09 15:43:11 +00:00
input := & inputBundle {
2018-02-16 22:19:34 +00:00
req : req ,
apiData : data ,
role : role ,
}
2019-05-09 15:43:11 +00:00
parsedBundle , err := generateCert ( ctx , b , input , nil , true )
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
2016-07-28 19:19:27 +00:00
case errutil . InternalError :
2015-11-18 15:16:09 +00:00
return nil , err
}
}
cb , err := parsedBundle . ToCertBundle ( )
if err != nil {
2017-10-27 16:02:18 +00:00
return nil , errwrap . Wrapf ( "error converting raw cert bundle to cert bundle: {{err}}" , err )
2015-11-18 15:16:09 +00:00
}
2016-05-09 23:53:28 +00:00
resp := & logical . Response {
Data : map [ string ] interface { } {
2015-11-18 15:16:09 +00:00
"expiration" : int64 ( parsedBundle . Certificate . NotAfter . Unix ( ) ) ,
2015-11-19 21:51:27 +00:00
"serial_number" : cb . SerialNumber ,
2015-11-18 15:16:09 +00:00
} ,
2016-05-09 23:53:28 +00:00
}
2015-11-18 15:16:09 +00:00
switch format {
case "pem" :
2016-02-01 18:19:41 +00:00
resp . Data [ "certificate" ] = cb . Certificate
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = cb . Certificate
2015-11-18 15:16:09 +00:00
if exported {
resp . Data [ "private_key" ] = cb . PrivateKey
resp . Data [ "private_key_type" ] = cb . PrivateKeyType
}
2016-02-01 18:19:41 +00:00
case "pem_bundle" :
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = cb . Certificate
2016-02-01 18:19:41 +00:00
if exported {
resp . Data [ "private_key" ] = cb . PrivateKey
resp . Data [ "private_key_type" ] = cb . PrivateKeyType
2016-09-28 00:50:17 +00:00
resp . Data [ "certificate" ] = fmt . Sprintf ( "%s\n%s" , cb . PrivateKey , cb . Certificate )
2016-02-01 18:19:41 +00:00
} else {
2016-09-28 00:50:17 +00:00
resp . Data [ "certificate" ] = cb . Certificate
2016-02-01 18:19:41 +00:00
}
2015-11-18 15:16:09 +00:00
case "der" :
resp . Data [ "certificate" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . CertificateBytes )
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . CertificateBytes )
2015-11-18 15:16:09 +00:00
if exported {
resp . Data [ "private_key" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . PrivateKeyBytes )
resp . Data [ "private_key_type" ] = cb . 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-19 21:51:27 +00:00
// Store it as the CA bundle
2017-08-15 18:00:40 +00:00
entry , err = logical . StorageEntryJSON ( "config/ca_bundle" , cb )
2015-11-18 15:16:09 +00:00
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
}
2015-11-19 21:51:27 +00:00
// Also store it as just the certificate identified by serial number, so it
// can be revoked
2018-01-19 06:44:44 +00:00
err = req . Storage . Put ( ctx , & logical . StorageEntry {
2017-05-03 14:12:58 +00:00
Key : "certs/" + normalizeSerial ( cb . SerialNumber ) ,
2015-11-19 21:51:27 +00:00
Value : parsedBundle . CertificateBytes ,
} )
if err != nil {
2017-10-27 16:02:18 +00:00
return nil , errwrap . Wrapf ( "unable to store certificate locally: {{err}}" , err )
2015-11-19 21:51:27 +00:00
}
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 = parsedBundle . 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
if err != nil {
return nil , err
}
if parsedBundle . Certificate . MaxPathLen == 0 {
resp . AddWarning ( "Max path length of the generated certificate is zero. This certificate cannot be used to issue intermediate CA certificates." )
}
return resp , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCASignIntermediate ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2015-11-18 15:16:09 +00:00
var err error
format := getFormat ( data )
if format == "" {
return logical . ErrorResponse (
` The "format" path parameter must be "pem" or "der" ` ,
) , nil
}
role := & roleEntry {
2018-02-16 22:19:34 +00:00
OU : data . Get ( "ou" ) . ( [ ] string ) ,
Organization : data . Get ( "organization" ) . ( [ ] string ) ,
Country : data . Get ( "country" ) . ( [ ] string ) ,
Locality : data . Get ( "locality" ) . ( [ ] string ) ,
Province : data . Get ( "province" ) . ( [ ] string ) ,
StreetAddress : data . Get ( "street_address" ) . ( [ ] string ) ,
PostalCode : data . Get ( "postal_code" ) . ( [ ] string ) ,
2018-05-09 14:29:54 +00:00
TTL : time . Duration ( data . Get ( "ttl" ) . ( int ) ) * time . Second ,
2017-09-13 15:42:45 +00:00
AllowLocalhost : true ,
AllowAnyName : true ,
AllowIPSANs : true ,
EnforceHostnames : false ,
KeyType : "any" ,
2018-06-15 19:32:25 +00:00
AllowedURISANs : [ ] string { "*" } ,
2018-06-05 03:18:39 +00:00
AllowedSerialNumbers : [ ] string { "*" } ,
2017-09-13 15:42:45 +00:00
AllowExpirationPastCA : true ,
2015-11-18 15:16:09 +00:00
}
if cn := data . Get ( "common_name" ) . ( string ) ; len ( cn ) == 0 {
role . UseCSRCommonName = true
}
var caErr error
2018-01-19 06:44:44 +00:00
signingBundle , caErr := fetchCAInfo ( ctx , req )
2015-11-18 15:16:09 +00:00
switch caErr . ( type ) {
2016-07-28 19:19:27 +00:00
case errutil . UserError :
return nil , errutil . UserError { Err : fmt . Sprintf (
2015-11-18 15:16:09 +00:00
"could not fetch the CA certificate (was one set?): %s" , caErr ) }
2016-07-28 19:19:27 +00:00
case errutil . InternalError :
return nil , errutil . InternalError { Err : fmt . Sprintf (
2015-11-18 15:16:09 +00:00
"error fetching CA certificate: %s" , caErr ) }
}
useCSRValues := data . Get ( "use_csr_values" ) . ( bool )
maxPathLengthIface , ok := data . GetOk ( "max_path_length" )
if ok {
maxPathLength := maxPathLengthIface . ( int )
role . MaxPathLength = & maxPathLength
}
2019-05-09 15:43:11 +00:00
input := & inputBundle {
req : req ,
apiData : data ,
role : role ,
2018-02-16 22:19:34 +00:00
}
2019-05-09 15:43:11 +00:00
parsedBundle , err := signCert ( b , input , signingBundle , true , useCSRValues )
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
2016-07-28 19:19:27 +00:00
case errutil . InternalError :
2015-11-18 15:16:09 +00:00
return nil , err
}
}
2016-09-28 00:50:17 +00:00
if err := parsedBundle . Verify ( ) ; err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "verification of parsed bundle failed: {{err}}" , err )
2016-09-28 00:50:17 +00:00
}
signingCB , err := signingBundle . ToCertBundle ( )
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "error converting raw signing bundle to cert bundle: {{err}}" , err )
2016-09-28 00:50:17 +00:00
}
2015-11-18 15:16:09 +00:00
cb , err := parsedBundle . ToCertBundle ( )
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "error converting raw cert bundle to cert bundle: {{err}}" , err )
2015-11-18 15:16:09 +00:00
}
2016-05-09 23:53:28 +00:00
resp := & logical . Response {
Data : map [ string ] interface { } {
2015-11-18 15:16:09 +00:00
"expiration" : int64 ( parsedBundle . Certificate . NotAfter . Unix ( ) ) ,
"serial_number" : cb . SerialNumber ,
} ,
2016-05-09 23:53:28 +00:00
}
2015-11-18 15:16:09 +00:00
2017-09-13 15:42:45 +00:00
if signingBundle . Certificate . NotAfter . Before ( parsedBundle . Certificate . NotAfter ) {
resp . AddWarning ( "The expiration time for the signed certificate is after the CA's expiration time. If the new certificate is not treated as a root, validation paths with the certificate past the issuing CA's expiration time will fail." )
}
2016-02-01 18:19:41 +00:00
switch format {
case "pem" :
resp . Data [ "certificate" ] = cb . Certificate
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = signingCB . Certificate
if cb . CAChain != nil && len ( cb . CAChain ) > 0 {
resp . Data [ "ca_chain" ] = cb . CAChain
}
2016-02-01 18:19:41 +00:00
case "pem_bundle" :
2016-09-28 00:50:17 +00:00
resp . Data [ "certificate" ] = cb . ToPEMBundle ( )
resp . Data [ "issuing_ca" ] = signingCB . Certificate
if cb . CAChain != nil && len ( cb . CAChain ) > 0 {
resp . Data [ "ca_chain" ] = cb . CAChain
}
2016-02-01 18:19:41 +00:00
case "der" :
2015-11-18 15:16:09 +00:00
resp . Data [ "certificate" ] = base64 . StdEncoding . EncodeToString ( parsedBundle . CertificateBytes )
2016-09-28 00:50:17 +00:00
resp . Data [ "issuing_ca" ] = base64 . StdEncoding . EncodeToString ( signingBundle . CertificateBytes )
var caChain [ ] string
for _ , caCert := range parsedBundle . CAChain {
caChain = append ( caChain , base64 . StdEncoding . EncodeToString ( caCert . Bytes ) )
}
if caChain != nil && len ( caChain ) > 0 {
resp . Data [ "ca_chain" ] = cb . CAChain
}
2015-11-18 15:16:09 +00:00
}
2018-01-19 06:44:44 +00:00
err = req . Storage . Put ( ctx , & logical . StorageEntry {
2017-05-03 14:12:58 +00:00
Key : "certs/" + normalizeSerial ( cb . SerialNumber ) ,
2015-11-18 15:16:09 +00:00
Value : parsedBundle . CertificateBytes ,
} )
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "unable to store certificate locally: {{err}}" , err )
2015-11-18 15:16:09 +00:00
}
if parsedBundle . Certificate . MaxPathLen == 0 {
resp . AddWarning ( "Max path length of the signed certificate is zero. This certificate cannot be used to issue intermediate CA certificates." )
}
return resp , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathCASignSelfIssued ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( * logical . Response , error ) {
2017-09-01 03:07:15 +00:00
var err error
certPem := data . Get ( "certificate" ) . ( string )
block , _ := pem . Decode ( [ ] byte ( certPem ) )
if block == nil || len ( block . Bytes ) == 0 {
return logical . ErrorResponse ( "certificate could not be PEM-decoded" ) , nil
}
certs , err := x509 . ParseCertificates ( block . Bytes )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "error parsing certificate: %s" , err ) ) , nil
}
if len ( certs ) != 1 {
return logical . ErrorResponse ( fmt . Sprintf ( "%d certificates found in PEM file, expected 1" , len ( certs ) ) ) , nil
}
cert := certs [ 0 ]
if ! cert . IsCA {
return logical . ErrorResponse ( "given certificate is not a CA certificate" ) , nil
}
if ! reflect . DeepEqual ( cert . Issuer , cert . Subject ) {
return logical . ErrorResponse ( "given certificate is not self-issued" ) , nil
}
var caErr error
2018-01-19 06:44:44 +00:00
signingBundle , caErr := fetchCAInfo ( ctx , req )
2017-09-01 03:07:15 +00:00
switch caErr . ( type ) {
case errutil . UserError :
return nil , errutil . UserError { Err : fmt . Sprintf (
"could not fetch the CA certificate (was one set?): %s" , caErr ) }
case errutil . InternalError :
return nil , errutil . InternalError { Err : fmt . Sprintf (
"error fetching CA certificate: %s" , caErr ) }
}
signingCB , err := signingBundle . ToCertBundle ( )
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "error converting raw signing bundle to cert bundle: {{err}}" , err )
2017-09-01 03:07:15 +00:00
}
2019-05-09 15:43:11 +00:00
urls := & certutil . URLEntries { }
2017-09-01 03:07:15 +00:00
if signingBundle . URLs != nil {
urls = signingBundle . URLs
}
cert . IssuingCertificateURL = urls . IssuingCertificates
cert . CRLDistributionPoints = urls . CRLDistributionPoints
cert . OCSPServer = urls . OCSPServers
2017-09-13 15:42:45 +00:00
newCert , err := x509 . CreateCertificate ( rand . Reader , cert , signingBundle . Certificate , cert . PublicKey , signingBundle . PrivateKey )
2017-09-01 03:07:15 +00:00
if err != nil {
return nil , errwrap . Wrapf ( "error signing self-issued certificate: {{err}}" , err )
}
if len ( newCert ) == 0 {
return nil , fmt . Errorf ( "nil cert was created when signing self-issued certificate" )
}
pemCert := pem . EncodeToMemory ( & pem . Block {
Type : "CERTIFICATE" ,
Bytes : newCert ,
} )
return & logical . Response {
Data : map [ string ] interface { } {
2018-03-18 20:00:51 +00:00
"certificate" : strings . TrimSpace ( string ( pemCert ) ) ,
2017-09-01 03:07:15 +00:00
"issuing_ca" : signingCB . Certificate ,
} ,
} , nil
}
2015-11-18 15:16:09 +00:00
const pathGenerateRootHelpSyn = `
Generate a new CA certificate and private key used for signing .
`
const pathGenerateRootHelpDesc = `
See the API documentation for more information .
`
2017-08-15 18:00:40 +00:00
const pathDeleteRootHelpSyn = `
Deletes the root CA key to allow a new one to be generated .
`
const pathDeleteRootHelpDesc = `
See the API documentation for more information .
`
2015-11-18 15:16:09 +00:00
const pathSignIntermediateHelpSyn = `
Issue an intermediate CA certificate based on the provided CSR .
`
const pathSignIntermediateHelpDesc = `
2017-08-31 19:46:13 +00:00
see the API documentation for more information .
`
const pathSignSelfIssuedHelpSyn = `
Signs another CA ' s self - issued certificate .
`
const pathSignSelfIssuedHelpDesc = `
Signs another CA ' s self - issued certificate . This is most often used for rolling roots ; unless you know you need this you probably want to use sign - intermediate instead .
2017-09-13 15:42:45 +00:00
Note that this is a very privileged operation and should be extremely restricted in terms of who is allowed to use it . All values will be taken directly from the incoming certificate and only verification that it is self - issued will be performed .
Configured URLs for CRLs / OCSP / etc . will be copied over and the issuer will be this mount ' s CA cert . Other than that , all other values will be used verbatim .
2015-11-18 15:16:09 +00:00
`