2015-05-15 16:13:05 +00:00
package pki
import (
2018-01-08 18:31:38 +00:00
"context"
2015-05-15 16:13:05 +00:00
"encoding/pem"
"fmt"
2018-03-18 20:00:51 +00:00
"strings"
2015-05-15 16:13:05 +00:00
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-05-15 16:13:05 +00:00
)
2015-06-19 16:48:18 +00:00
// Returns the CA in raw format
2015-05-15 16:13:05 +00:00
func pathFetchCA ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : ` ca(/pem)? ` ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : b . pathFetchRead ,
} ,
HelpSynopsis : pathFetchHelpSyn ,
HelpDescription : pathFetchHelpDesc ,
}
}
2016-09-28 00:50:17 +00:00
// Returns the CA chain
func pathFetchCAChain ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : ` (cert/)?ca_chain ` ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : b . pathFetchRead ,
} ,
HelpSynopsis : pathFetchHelpSyn ,
HelpDescription : pathFetchHelpDesc ,
}
}
2015-06-19 16:48:18 +00:00
// Returns the CRL in raw format
2015-05-15 16:13:05 +00:00
func pathFetchCRL ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : ` crl(/pem)? ` ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : b . pathFetchRead ,
} ,
HelpSynopsis : pathFetchHelpSyn ,
HelpDescription : pathFetchHelpDesc ,
}
}
2015-06-19 16:48:18 +00:00
// Returns any valid (non-revoked) cert. Since "ca" fits the pattern, this path
// also handles returning the CA cert in a non-raw format.
2015-05-15 16:13:05 +00:00
func pathFetchValid ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : ` cert/(?P<serial>[0-9A-Fa-f-:]+) ` ,
Fields : map [ string ] * framework . FieldSchema {
"serial" : & framework . FieldSchema {
2015-06-18 14:44:02 +00:00
Type : framework . TypeString ,
Description : ` Certificate serial number , in colon - or
hyphen - separated octal ` ,
2015-05-15 16:13:05 +00:00
} ,
} ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : b . pathFetchRead ,
} ,
HelpSynopsis : pathFetchHelpSyn ,
HelpDescription : pathFetchHelpDesc ,
}
}
2015-06-19 16:48:18 +00:00
// This returns the CRL in a non-raw format
2015-05-15 16:13:05 +00:00
func pathFetchCRLViaCertPath ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : ` cert/crl ` ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ReadOperation : b . pathFetchRead ,
} ,
HelpSynopsis : pathFetchHelpSyn ,
HelpDescription : pathFetchHelpDesc ,
}
}
2016-06-08 15:46:58 +00:00
// This returns the list of serial numbers for certs
func pathFetchListCerts ( b * backend ) * framework . Path {
return & framework . Path {
Pattern : "certs/?$" ,
Callbacks : map [ logical . Operation ] framework . OperationFunc {
logical . ListOperation : b . pathFetchCertList ,
} ,
HelpSynopsis : pathFetchHelpSyn ,
HelpDescription : pathFetchHelpDesc ,
}
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathFetchCertList ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( response * logical . Response , retErr error ) {
2018-01-19 06:44:44 +00:00
entries , err := req . Storage . List ( ctx , "certs/" )
2016-06-08 15:46:58 +00:00
if err != nil {
return nil , err
}
return logical . ListResponse ( entries ) , nil
}
2018-01-08 18:31:38 +00:00
func ( b * backend ) pathFetchRead ( ctx context . Context , req * logical . Request , data * framework . FieldData ) ( response * logical . Response , retErr error ) {
2016-03-07 15:57:38 +00:00
var serial , pemType , contentType string
var certEntry , revokedEntry * logical . StorageEntry
2015-06-19 16:48:18 +00:00
var funcErr error
2015-05-15 16:13:05 +00:00
var certificate [ ] byte
2016-03-07 15:57:38 +00:00
var revocationTime int64
2015-05-15 16:13:05 +00:00
response = & logical . Response {
Data : map [ string ] interface { } { } ,
}
2015-06-19 16:48:18 +00:00
// Some of these need to return raw and some non-raw;
// this is basically handled by setting contentType or not.
// Errors don't cause an immediate exit, because the raw
// paths still need to return raw output.
2015-05-15 16:13:05 +00:00
switch {
case req . Path == "ca" || req . Path == "ca/pem" :
serial = "ca"
contentType = "application/pkix-cert"
if req . Path == "ca/pem" {
pemType = "CERTIFICATE"
}
2016-09-28 00:50:17 +00:00
case req . Path == "ca_chain" || req . Path == "cert/ca_chain" :
serial = "ca_chain"
if req . Path == "ca_chain" {
contentType = "application/pkix-cert"
}
2015-05-15 16:13:05 +00:00
case req . Path == "crl" || req . Path == "crl/pem" :
serial = "crl"
contentType = "application/pkix-crl"
if req . Path == "crl/pem" {
pemType = "X509 CRL"
}
case req . Path == "cert/crl" :
serial = "crl"
pemType = "X509 CRL"
default :
serial = data . Get ( "serial" ) . ( string )
pemType = "CERTIFICATE"
}
if len ( serial ) == 0 {
response = logical . ErrorResponse ( "The serial number must be provided" )
goto reply
}
2016-09-28 00:50:17 +00:00
if serial == "ca_chain" {
2018-01-19 06:44:44 +00:00
caInfo , err := fetchCAInfo ( ctx , req )
2016-09-28 00:50:17 +00:00
switch err . ( type ) {
case errutil . UserError :
2017-08-15 18:00:40 +00:00
response = logical . ErrorResponse ( err . Error ( ) )
2016-09-28 00:50:17 +00:00
goto reply
case errutil . InternalError :
retErr = err
goto reply
}
caChain := caInfo . GetCAChain ( )
2018-02-20 05:03:45 +00:00
var certStr string
2016-09-28 00:50:17 +00:00
for _ , ca := range caChain {
block := pem . Block {
Type : "CERTIFICATE" ,
Bytes : ca . Bytes ,
}
2018-03-18 20:00:51 +00:00
certStr = strings . Join ( [ ] string { certStr , strings . TrimSpace ( string ( pem . EncodeToMemory ( & block ) ) ) } , "\n" )
2016-09-28 00:50:17 +00:00
}
2018-12-12 20:38:35 +00:00
certificate = [ ] byte ( strings . TrimSpace ( certStr ) )
2016-09-28 00:50:17 +00:00
goto reply
}
2018-01-19 06:44:44 +00:00
certEntry , funcErr = fetchCertBySerial ( ctx , req , req . Path , serial )
2016-02-22 15:36:26 +00:00
if funcErr != nil {
switch funcErr . ( type ) {
2016-07-28 19:19:27 +00:00
case errutil . UserError :
2016-02-22 15:36:26 +00:00
response = logical . ErrorResponse ( funcErr . Error ( ) )
goto reply
2016-07-28 19:19:27 +00:00
case errutil . InternalError :
2016-02-22 15:36:26 +00:00
retErr = funcErr
goto reply
}
}
if certEntry == nil {
2017-08-15 18:00:40 +00:00
response = nil
2015-05-15 16:13:05 +00:00
goto reply
}
2015-06-19 16:48:18 +00:00
certificate = certEntry . Value
2015-05-15 16:13:05 +00:00
if len ( pemType ) != 0 {
block := pem . Block {
Type : pemType ,
Bytes : certEntry . Value ,
}
2018-03-18 20:00:51 +00:00
// This is convoluted on purpose to ensure that we don't have trailing
// newlines via various paths
certificate = [ ] byte ( strings . TrimSpace ( string ( pem . EncodeToMemory ( & block ) ) ) )
2015-05-15 16:13:05 +00:00
}
2018-01-19 06:44:44 +00:00
revokedEntry , funcErr = fetchCertBySerial ( ctx , req , "revoked/" , serial )
2016-03-07 15:57:38 +00:00
if funcErr != nil {
switch funcErr . ( type ) {
2016-07-28 19:19:27 +00:00
case errutil . UserError :
2016-03-07 15:57:38 +00:00
response = logical . ErrorResponse ( funcErr . Error ( ) )
goto reply
2016-07-28 19:19:27 +00:00
case errutil . InternalError :
2016-03-07 15:57:38 +00:00
retErr = funcErr
goto reply
}
}
if revokedEntry != nil {
var revInfo revocationInfo
err := revokedEntry . DecodeJSON ( & revInfo )
if err != nil {
return logical . ErrorResponse ( fmt . Sprintf ( "Error decoding revocation entry for serial %s: %s" , serial , err ) ) , nil
}
revocationTime = revInfo . RevocationTime
}
2015-05-15 16:13:05 +00:00
reply :
switch {
case len ( contentType ) != 0 :
response = & logical . Response {
Data : map [ string ] interface { } {
logical . HTTPContentType : contentType ,
logical . HTTPRawBody : certificate ,
} }
if retErr != nil {
2016-08-19 20:45:17 +00:00
if b . Logger ( ) . IsWarn ( ) {
2018-10-09 16:43:17 +00:00
b . Logger ( ) . Warn ( "possible error, but cannot return in raw response. Note that an empty CA probably means none was configured, and an empty CRL is possibly correct" , "error" , retErr )
2016-08-19 20:45:17 +00:00
}
2015-05-15 16:13:05 +00:00
}
retErr = nil
2016-09-28 00:50:17 +00:00
if len ( certificate ) > 0 {
response . Data [ logical . HTTPStatusCode ] = 200
} else {
response . Data [ logical . HTTPStatusCode ] = 204
}
2015-05-15 16:13:05 +00:00
case retErr != nil :
response = nil
2017-08-15 18:00:40 +00:00
return
case response == nil :
return
case response . IsError ( ) :
return response , nil
2015-05-15 16:13:05 +00:00
default :
response . Data [ "certificate" ] = string ( certificate )
2016-03-07 15:57:38 +00:00
response . Data [ "revocation_time" ] = revocationTime
2015-05-15 16:13:05 +00:00
}
return
}
const pathFetchHelpSyn = `
2016-09-28 00:50:17 +00:00
Fetch a CA , CRL , CA Chain , or non - revoked certificate .
2015-05-15 16:13:05 +00:00
`
const pathFetchHelpDesc = `
2015-06-19 16:48:18 +00:00
This allows certificates to be fetched . If using the fetch / prefix any non - revoked certificate can be fetched .
2015-05-15 16:13:05 +00:00
Using "ca" or "crl" as the value fetches the appropriate information in DER encoding . Add "/pem" to either to get PEM encoding .
2016-09-28 00:50:17 +00:00
Using "ca_chain" as the value fetches the certificate authority trust chain in PEM encoding .
2015-05-15 16:13:05 +00:00
`