2015-05-15 16:13:05 +00:00
package pki
import (
"encoding/pem"
"fmt"
2016-07-28 19:19:27 +00:00
"github.com/hashicorp/vault/helper/errutil"
2015-05-15 16:13:05 +00:00
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework"
)
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 ,
}
}
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 ,
}
}
func ( b * backend ) pathFetchCertList ( req * logical . Request , data * framework . FieldData ) ( response * logical . Response , retErr error ) {
entries , err := req . Storage . List ( "certs/" )
if err != nil {
return nil , err
}
return logical . ListResponse ( entries ) , nil
}
2015-05-15 16:13:05 +00:00
func ( b * backend ) pathFetchRead ( 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"
}
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
}
2015-06-19 16:48:18 +00:00
certEntry , funcErr = fetchCertBySerial ( 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 {
response = logical . ErrorResponse ( fmt . Sprintf ( "certificate with serial %s not found" , serial ) )
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 ,
}
certificate = pem . EncodeToMemory ( & block )
}
2016-03-07 15:57:38 +00:00
revokedEntry , funcErr = fetchCertBySerial ( req , "revoked/" , serial )
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 {
b . Logger ( ) . Printf ( "Possible error, but cannot return in raw response: %s. Note that an empty CA probably means none was configured, and an empty CRL is quite possibly correct" , retErr )
}
retErr = nil
response . Data [ logical . HTTPStatusCode ] = 200
case retErr != nil :
response = nil
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 = `
2015-06-19 16:48:18 +00:00
Fetch a CA , CRL , 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 .
`