2016-05-30 18:30:01 +00:00
package approle
import (
2018-01-19 06:44:44 +00:00
"context"
2016-05-30 18:30:01 +00:00
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"time"
2018-02-03 01:34:32 +00:00
"github.com/hashicorp/errwrap"
2019-01-09 00:48:57 +00:00
uuid "github.com/hashicorp/go-uuid"
2019-04-12 22:03:59 +00:00
"github.com/hashicorp/vault/sdk/helper/cidrutil"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/helper/locksutil"
"github.com/hashicorp/vault/sdk/logical"
2016-05-30 18:30:01 +00:00
)
// secretIDStorageEntry represents the information stored in storage
// when a SecretID is created. The structure of the SecretID storage
// entry is the same for all the types of SecretIDs generated.
type secretIDStorageEntry struct {
// Accessor for the SecretID. It is a random UUID serving as
// a secondary index for the SecretID. This uniquely identifies
// the SecretID it belongs to, and hence can be used for listing
// and deleting SecretIDs. Accessors cannot be used as valid
// SecretIDs during login.
2018-06-05 20:12:11 +00:00
SecretIDAccessor string ` json:"secret_id_accessor" mapstructure:"secret_id_accessor" `
2016-05-30 18:30:01 +00:00
2016-09-21 23:41:08 +00:00
// Number of times this SecretID can be used to perform the login
// operation
2018-06-05 20:12:11 +00:00
SecretIDNumUses int ` json:"secret_id_num_uses" mapstructure:"secret_id_num_uses" `
2016-05-30 18:30:01 +00:00
2017-04-04 16:29:18 +00:00
// Duration after which this SecretID should expire. This is capped by
2016-09-21 23:41:08 +00:00
// the backend mount's max TTL value.
2018-06-05 20:12:11 +00:00
SecretIDTTL time . Duration ` json:"secret_id_ttl" mapstructure:"secret_id_ttl" `
2016-05-30 18:30:01 +00:00
// The time when the SecretID was created
2018-06-05 20:12:11 +00:00
CreationTime time . Time ` json:"creation_time" mapstructure:"creation_time" `
2016-05-30 18:30:01 +00:00
2016-09-21 23:41:08 +00:00
// The time when the SecretID becomes eligible for tidy operation.
// Tidying is performed by the PeriodicFunc of the backend which is 1
// minute apart.
2018-06-05 20:12:11 +00:00
ExpirationTime time . Time ` json:"expiration_time" mapstructure:"expiration_time" `
2016-05-30 18:30:01 +00:00
// The time representing the last time this storage entry was modified
2018-06-05 20:12:11 +00:00
LastUpdatedTime time . Time ` json:"last_updated_time" mapstructure:"last_updated_time" `
2016-05-30 18:30:01 +00:00
2016-09-21 23:41:08 +00:00
// Metadata that belongs to the SecretID
2018-06-05 20:12:11 +00:00
Metadata map [ string ] string ` json:"metadata" mapstructure:"metadata" `
2016-09-21 23:41:08 +00:00
// CIDRList is a set of CIDR blocks that impose source address
// restrictions on the usage of SecretID
2018-06-05 20:12:11 +00:00
CIDRList [ ] string ` json:"cidr_list" mapstructure:"cidr_list" `
2016-09-28 16:01:16 +00:00
2018-08-21 15:54:04 +00:00
// TokenBoundCIDRs is a set of CIDR blocks that impose source address
// restrictions on the usage of the token generated by this SecretID
TokenBoundCIDRs [ ] string ` json:"token_cidr_list" mapstructure:"token_bound_cidrs" `
2016-09-28 16:01:16 +00:00
// This is a deprecated field
2018-06-05 20:12:11 +00:00
SecretIDNumUsesDeprecated int ` json:"SecretIDNumUses" mapstructure:"SecretIDNumUses" `
2016-05-30 18:30:01 +00:00
}
2016-09-21 23:41:08 +00:00
// Represents the payload of the storage entry of the accessor that maps to a
// unique SecretID. Note that SecretIDs should never be stored in plaintext
// anywhere in the backend. SecretIDHMAC will be used as an index to fetch the
// properties of the SecretID and to delete the SecretID.
2016-05-30 18:30:01 +00:00
type secretIDAccessorStorageEntry struct {
// Hash of the SecretID which can be used to find the storage index at which
// properties of SecretID is stored.
2018-06-05 20:12:11 +00:00
SecretIDHMAC string ` json:"secret_id_hmac" mapstructure:"secret_id_hmac" `
2016-05-30 18:30:01 +00:00
}
2016-09-21 23:41:08 +00:00
// verifyCIDRRoleSecretIDSubset checks if the CIDR blocks set on the secret ID
// are a subset of CIDR blocks set on the role
2018-03-08 22:49:08 +00:00
func verifyCIDRRoleSecretIDSubset ( secretIDCIDRs [ ] string , roleBoundCIDRList [ ] string ) error {
2016-09-21 23:41:08 +00:00
if len ( secretIDCIDRs ) != 0 {
// If there are no CIDR blocks on the role, then the subset
// requirement would be satisfied
2018-03-08 22:49:08 +00:00
if len ( roleBoundCIDRList ) != 0 {
subset , err := cidrutil . SubsetBlocks ( roleBoundCIDRList , secretIDCIDRs )
2016-09-21 23:41:08 +00:00
if ! subset || err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( fmt . Sprintf ( "failed to verify subset relationship between CIDR blocks on the role %q and CIDR blocks on the secret ID %q: {{err}}" , roleBoundCIDRList , secretIDCIDRs ) , err )
2016-09-21 23:41:08 +00:00
}
}
}
return nil
}
2016-09-15 21:49:14 +00:00
// Creates a SHA256 HMAC of the given 'value' using the given 'key' and returns
// a hex encoded string.
2016-05-30 18:30:01 +00:00
func createHMAC ( key , value string ) ( string , error ) {
if key == "" {
return "" , fmt . Errorf ( "invalid HMAC key" )
}
hm := hmac . New ( sha256 . New , [ ] byte ( key ) )
hm . Write ( [ ] byte ( value ) )
return hex . EncodeToString ( hm . Sum ( nil ) ) , nil
}
2017-03-07 16:21:32 +00:00
func ( b * backend ) secretIDLock ( secretIDHMAC string ) * locksutil . LockEntry {
return locksutil . LockForKey ( b . secretIDLocks , secretIDHMAC )
2016-05-30 18:30:01 +00:00
}
2017-03-07 16:21:32 +00:00
func ( b * backend ) secretIDAccessorLock ( secretIDAccessor string ) * locksutil . LockEntry {
return locksutil . LockForKey ( b . secretIDAccessorLocks , secretIDAccessor )
2016-09-15 21:49:14 +00:00
}
2016-09-28 16:01:16 +00:00
// nonLockedSecretIDStorageEntry fetches the secret ID properties from physical
// storage. The entry will be indexed based on the given HMACs of both role
// name and the secret ID. This method will not acquire secret ID lock to fetch
// the storage entry. Locks need to be acquired before calling this method.
2018-04-23 14:51:55 +00:00
func ( b * backend ) nonLockedSecretIDStorageEntry ( ctx context . Context , s logical . Storage , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC string ) ( * secretIDStorageEntry , error ) {
2016-09-28 16:01:16 +00:00
if secretIDHMAC == "" {
return nil , fmt . Errorf ( "missing secret ID HMAC" )
}
if roleNameHMAC == "" {
return nil , fmt . Errorf ( "missing role name HMAC" )
}
// Prepare the storage index at which the secret ID will be stored
2018-04-23 14:51:55 +00:00
entryIndex := fmt . Sprintf ( "%s%s/%s" , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC )
2016-09-28 16:01:16 +00:00
2018-01-19 06:44:44 +00:00
entry , err := s . Get ( ctx , entryIndex )
2016-09-28 16:01:16 +00:00
if err != nil {
return nil , err
}
if entry == nil {
return nil , nil
}
result := secretIDStorageEntry { }
if err := entry . DecodeJSON ( & result ) ; err != nil {
return nil , err
}
2016-09-28 22:17:13 +00:00
// TODO: Remove this upgrade bit in future releases
2016-09-28 16:01:16 +00:00
persistNeeded := false
if result . SecretIDNumUsesDeprecated != 0 {
if result . SecretIDNumUses == 0 ||
result . SecretIDNumUsesDeprecated < result . SecretIDNumUses {
result . SecretIDNumUses = result . SecretIDNumUsesDeprecated
2016-09-28 19:24:19 +00:00
persistNeeded = true
}
if result . SecretIDNumUses < result . SecretIDNumUsesDeprecated {
result . SecretIDNumUsesDeprecated = result . SecretIDNumUses
persistNeeded = true
2016-09-28 16:01:16 +00:00
}
}
if persistNeeded {
2018-04-23 14:51:55 +00:00
if err := b . nonLockedSetSecretIDStorageEntry ( ctx , s , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC , & result ) ; err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to upgrade role storage entry {{err}}" , err )
2016-09-28 16:01:16 +00:00
}
}
return & result , nil
}
// nonLockedSetSecretIDStorageEntry creates or updates a secret ID entry at the
// physical storage. The entry will be indexed based on the given HMACs of both
// role name and the secret ID. This method will not acquire secret ID lock to
// create/update the storage entry. Locks need to be acquired before calling
// this method.
2018-04-23 14:51:55 +00:00
func ( b * backend ) nonLockedSetSecretIDStorageEntry ( ctx context . Context , s logical . Storage , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC string , secretEntry * secretIDStorageEntry ) error {
if roleSecretIDPrefix == "" {
return fmt . Errorf ( "missing secret ID prefix" )
}
2016-09-28 16:01:16 +00:00
if secretIDHMAC == "" {
return fmt . Errorf ( "missing secret ID HMAC" )
}
if roleNameHMAC == "" {
return fmt . Errorf ( "missing role name HMAC" )
}
if secretEntry == nil {
return fmt . Errorf ( "nil secret entry" )
}
2018-04-23 14:51:55 +00:00
entryIndex := fmt . Sprintf ( "%s%s/%s" , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC )
2016-09-28 16:01:16 +00:00
if entry , err := logical . StorageEntryJSON ( entryIndex , secretEntry ) ; err != nil {
return err
2018-01-19 06:44:44 +00:00
} else if err = s . Put ( ctx , entry ) ; err != nil {
2016-09-28 16:01:16 +00:00
return err
}
return nil
}
2016-05-30 18:30:01 +00:00
// registerSecretIDEntry creates a new storage entry for the given SecretID.
2018-04-23 14:51:55 +00:00
func ( b * backend ) registerSecretIDEntry ( ctx context . Context , s logical . Storage , roleName , secretID , hmacKey , roleSecretIDPrefix string , secretEntry * secretIDStorageEntry ) ( * secretIDStorageEntry , error ) {
2016-05-30 18:30:01 +00:00
secretIDHMAC , err := createHMAC ( hmacKey , secretID )
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to create HMAC of secret ID: {{err}}" , err )
2016-05-30 18:30:01 +00:00
}
roleNameHMAC , err := createHMAC ( hmacKey , roleName )
if err != nil {
2018-04-05 15:49:21 +00:00
return nil , errwrap . Wrapf ( "failed to create HMAC of role_name: {{err}}" , err )
2016-05-30 18:30:01 +00:00
}
lock := b . secretIDLock ( secretIDHMAC )
lock . RLock ( )
2018-04-23 14:51:55 +00:00
entry , err := b . nonLockedSecretIDStorageEntry ( ctx , s , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC )
2016-05-30 18:30:01 +00:00
if err != nil {
lock . RUnlock ( )
return nil , err
}
if entry != nil {
lock . RUnlock ( )
return nil , fmt . Errorf ( "SecretID is already registered" )
}
// If there isn't an entry for the secretID already, switch the read lock
// with a write lock and create an entry.
lock . RUnlock ( )
lock . Lock ( )
defer lock . Unlock ( )
// But before saving a new entry, check if the secretID entry was created during the lock switch.
2018-04-23 14:51:55 +00:00
entry , err = b . nonLockedSecretIDStorageEntry ( ctx , s , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC )
2016-05-30 18:30:01 +00:00
if err != nil {
return nil , err
}
if entry != nil {
return nil , fmt . Errorf ( "SecretID is already registered" )
}
2016-09-28 16:01:16 +00:00
//
2016-05-30 18:30:01 +00:00
// Create a new entry for the SecretID
2016-09-28 16:01:16 +00:00
//
2016-05-30 18:30:01 +00:00
// Set the creation time for the SecretID
currentTime := time . Now ( )
secretEntry . CreationTime = currentTime
secretEntry . LastUpdatedTime = currentTime
// If SecretIDTTL is not specified or if it crosses the backend mount's limit,
// cap the expiration to backend's max. Otherwise, use it to determine the
// expiration time.
if secretEntry . SecretIDTTL < time . Duration ( 0 ) || secretEntry . SecretIDTTL > b . System ( ) . MaxLeaseTTL ( ) {
secretEntry . ExpirationTime = currentTime . Add ( b . System ( ) . MaxLeaseTTL ( ) )
} else if secretEntry . SecretIDTTL != time . Duration ( 0 ) {
// Set the ExpirationTime only if SecretIDTTL was set. SecretIDs should not
// expire by default.
secretEntry . ExpirationTime = currentTime . Add ( secretEntry . SecretIDTTL )
}
// Before storing the SecretID, store its accessor.
2018-04-23 20:19:05 +00:00
if err := b . createSecretIDAccessorEntry ( ctx , s , secretEntry , secretIDHMAC , roleSecretIDPrefix ) ; err != nil {
2016-05-30 18:30:01 +00:00
return nil , err
}
2018-04-23 14:51:55 +00:00
if err := b . nonLockedSetSecretIDStorageEntry ( ctx , s , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC , secretEntry ) ; err != nil {
2016-05-30 18:30:01 +00:00
return nil , err
}
return secretEntry , nil
}
// secretIDAccessorEntry is used to read the storage entry that maps an
2016-09-15 21:49:14 +00:00
// accessor to a secret_id.
2018-04-23 20:19:05 +00:00
func ( b * backend ) secretIDAccessorEntry ( ctx context . Context , s logical . Storage , secretIDAccessor , roleSecretIDPrefix string ) ( * secretIDAccessorStorageEntry , error ) {
2016-05-30 18:30:01 +00:00
if secretIDAccessor == "" {
return nil , fmt . Errorf ( "missing secretIDAccessor" )
}
var result secretIDAccessorStorageEntry
// Create index entry, mapping the accessor to the token ID
2018-03-08 19:21:11 +00:00
salt , err := b . Salt ( ctx )
2017-05-09 21:51:09 +00:00
if err != nil {
return nil , err
}
2018-04-23 20:19:05 +00:00
accessorPrefix := secretIDAccessorPrefix
if roleSecretIDPrefix == secretIDLocalPrefix {
accessorPrefix = secretIDAccessorLocalPrefix
}
entryIndex := accessorPrefix + salt . SaltID ( secretIDAccessor )
2016-05-30 18:30:01 +00:00
2016-09-15 21:49:14 +00:00
accessorLock := b . secretIDAccessorLock ( secretIDAccessor )
accessorLock . RLock ( )
defer accessorLock . RUnlock ( )
2018-01-19 06:44:44 +00:00
if entry , err := s . Get ( ctx , entryIndex ) ; err != nil {
2016-05-30 18:30:01 +00:00
return nil , err
} else if entry == nil {
return nil , nil
} else if err := entry . DecodeJSON ( & result ) ; err != nil {
return nil , err
}
return & result , nil
}
2016-09-15 21:49:14 +00:00
// createSecretIDAccessorEntry creates an identifier for the SecretID. A storage index,
2016-05-30 18:30:01 +00:00
// mapping the accessor to the SecretID is also created. This method should
// be called when the lock for the corresponding SecretID is held.
2018-04-23 20:19:05 +00:00
func ( b * backend ) createSecretIDAccessorEntry ( ctx context . Context , s logical . Storage , entry * secretIDStorageEntry , secretIDHMAC , roleSecretIDPrefix string ) error {
2016-05-30 18:30:01 +00:00
// Create a random accessor
accessorUUID , err := uuid . GenerateUUID ( )
if err != nil {
return err
}
entry . SecretIDAccessor = accessorUUID
// Create index entry, mapping the accessor to the token ID
2018-03-08 19:21:11 +00:00
salt , err := b . Salt ( ctx )
2017-05-09 21:51:09 +00:00
if err != nil {
return err
}
2018-04-23 20:19:05 +00:00
accessorPrefix := secretIDAccessorPrefix
if roleSecretIDPrefix == secretIDLocalPrefix {
accessorPrefix = secretIDAccessorLocalPrefix
}
entryIndex := accessorPrefix + salt . SaltID ( entry . SecretIDAccessor )
2016-09-15 21:49:14 +00:00
accessorLock := b . secretIDAccessorLock ( accessorUUID )
accessorLock . Lock ( )
defer accessorLock . Unlock ( )
2016-05-30 18:30:01 +00:00
if entry , err := logical . StorageEntryJSON ( entryIndex , & secretIDAccessorStorageEntry {
SecretIDHMAC : secretIDHMAC ,
} ) ; err != nil {
return err
2018-01-19 06:44:44 +00:00
} else if err = s . Put ( ctx , entry ) ; err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to persist accessor index entry: {{err}}" , err )
2016-05-30 18:30:01 +00:00
}
return nil
}
2016-09-15 21:49:14 +00:00
// deleteSecretIDAccessorEntry deletes the storage index mapping the accessor to a SecretID.
2018-04-23 20:19:05 +00:00
func ( b * backend ) deleteSecretIDAccessorEntry ( ctx context . Context , s logical . Storage , secretIDAccessor , roleSecretIDPrefix string ) error {
2018-03-08 19:21:11 +00:00
salt , err := b . Salt ( ctx )
2017-05-09 21:51:09 +00:00
if err != nil {
return err
}
2018-04-23 20:19:05 +00:00
accessorPrefix := secretIDAccessorPrefix
if roleSecretIDPrefix == secretIDLocalPrefix {
accessorPrefix = secretIDAccessorLocalPrefix
}
entryIndex := accessorPrefix + salt . SaltID ( secretIDAccessor )
2016-09-15 21:49:14 +00:00
accessorLock := b . secretIDAccessorLock ( secretIDAccessor )
accessorLock . Lock ( )
defer accessorLock . Unlock ( )
// Delete the accessor of the SecretID first
2018-04-23 20:19:05 +00:00
if err := s . Delete ( ctx , entryIndex ) ; err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to delete accessor storage entry: {{err}}" , err )
2016-09-15 21:49:14 +00:00
}
return nil
}
2016-05-30 18:30:01 +00:00
// flushRoleSecrets deletes all the SecretIDs that belong to the given
// RoleID.
2018-04-23 14:51:55 +00:00
func ( b * backend ) flushRoleSecrets ( ctx context . Context , s logical . Storage , roleName , hmacKey , roleSecretIDPrefix string ) error {
2016-05-30 18:30:01 +00:00
roleNameHMAC , err := createHMAC ( hmacKey , roleName )
if err != nil {
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( "failed to create HMAC of role_name: {{err}}" , err )
2016-05-30 18:30:01 +00:00
}
// Acquire the custom lock to perform listing of SecretIDs
2017-02-01 23:13:49 +00:00
b . secretIDListingLock . RLock ( )
defer b . secretIDListingLock . RUnlock ( )
2016-05-30 18:30:01 +00:00
2018-04-23 14:51:55 +00:00
secretIDHMACs , err := s . List ( ctx , fmt . Sprintf ( "%s%s/" , roleSecretIDPrefix , roleNameHMAC ) )
2016-05-30 18:30:01 +00:00
if err != nil {
return err
}
for _ , secretIDHMAC := range secretIDHMACs {
// Acquire the lock belonging to the SecretID
lock := b . secretIDLock ( secretIDHMAC )
lock . Lock ( )
2018-04-23 14:51:55 +00:00
entryIndex := fmt . Sprintf ( "%s%s/%s" , roleSecretIDPrefix , roleNameHMAC , secretIDHMAC )
2018-01-19 06:44:44 +00:00
if err := s . Delete ( ctx , entryIndex ) ; err != nil {
2016-05-30 18:30:01 +00:00
lock . Unlock ( )
2018-04-05 15:49:21 +00:00
return errwrap . Wrapf ( fmt . Sprintf ( "error deleting SecretID %q from storage: {{err}}" , secretIDHMAC ) , err )
2016-05-30 18:30:01 +00:00
}
lock . Unlock ( )
}
return nil
}