func (b *backend) nonLockedSetSecretIDStorageEntry(s logical.Storage, roleNameHMAC, secretIDHMAC string, secretEntry *secretIDStorageEntry) error {
	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")
	}
	entryIndex := fmt.Sprintf("secret_id/%s/%s", roleNameHMAC, secretIDHMAC)
	if entry, err := logical.StorageEntryJSON(entryIndex, secretEntry); err != nil {
		return err
	} else if err = s.Put(entry); err != nil {
		return err
	}
	return 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.
package approle
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strings"
	"sync"
	"time"
	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/vault/helper/cidrutil"
	"github.com/hashicorp/vault/helper/strutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)
type secretIDStorageEntry struct {
	SecretIDAccessor string `json:"secret_id_accessor" structs:"secret_id_accessor" mapstructure:"secret_id_accessor"`
	SecretIDNumUses int `json:"secret_id_num_uses" structs:"secret_id_num_uses" mapstructure:"secret_id_num_uses"`
	SecretIDTTL time.Duration `json:"secret_id_ttl" structs:"secret_id_ttl" mapstructure:"secret_id_ttl"`
	CreationTime time.Time `json:"creation_time" structs:"creation_time" mapstructure:"creation_time"`
	ExpirationTime time.Time `json:"expiration_time" structs:"expiration_time" mapstructure:"expiration_time"`
	LastUpdatedTime time.Time `json:"last_updated_time" structs:"last_updated_time" mapstructure:"last_updated_time"`
	Metadata map[string]string `json:"metadata" structs:"metadata" mapstructure:"metadata"`
	CIDRList []string `json:"cidr_list" structs:"cidr_list" mapstructure:"cidr_list"`
	SecretIDNumUsesDeprecated int `json:"SecretIDNumUses" structs:"SecretIDNumUses" mapstructure:"SecretIDNumUses"`
}
type secretIDAccessorStorageEntry struct {
	SecretIDHMAC string `json:"secret_id_hmac" structs:"secret_id_hmac" mapstructure:"secret_id_hmac"`
}
func (b *backend) validateRoleID(s logical.Storage, roleID string) (*roleStorageEntry, string, error) {
	roleIDIndex, err := b.roleIDEntry(s, roleID)
	if err != nil {
		return nil, "", err
	}
	if roleIDIndex == nil {
		return nil, "", fmt.Errorf("failed to find secondary index for role_id %q\n", roleID)
	}
	role, err := b.roleEntry(s, roleIDIndex.Name)
	if err != nil {
		return nil, "", err
	}
	if role == nil {
		return nil, "", fmt.Errorf("role %q referred by the SecretID does not exist", roleIDIndex.Name)
	}
	return role, roleIDIndex.Name, nil
}
func (b *backend) validateCredentials(req *logical.Request, data *framework.FieldData) (*roleStorageEntry, string, map[string]string, error) {
	var metadata map[string]string
	roleID := strings.TrimSpace(data.Get("role_id").(string))
	if roleID == "" {
		return nil, "", metadata, fmt.Errorf("missing role_id")
	}
	role, roleName, err := b.validateRoleID(req.Storage, roleID)
	if err != nil {
		return nil, "", metadata, err
	}
	if role == nil || roleName == "" {
		return nil, "", metadata, fmt.Errorf("failed to validate role_id")
	}
	if role.TokenTTL, role.TokenMaxTTL, err = b.SanitizeTTL(role.TokenTTL, role.TokenMaxTTL); err != nil {
		return nil, "", metadata, err
	}
	if role.BindSecretID {
		secretID := strings.TrimSpace(data.Get("secret_id").(string))
		if secretID == "" {
			return nil, "", metadata, fmt.Errorf("missing secret_id")
		}
		var valid bool
		valid, metadata, err = b.validateBindSecretID(req, roleName, secretID, role.HMACKey, role.BoundCIDRList)
		if err != nil {
			return nil, "", metadata, err
		}
		if !valid {
			return nil, "", metadata, fmt.Errorf("invalid secret_id %q", secretID)
		}
	}
	if role.BoundCIDRList != "" {
		if req.Connection == nil || req.Connection.RemoteAddr == "" {
			return nil, "", metadata, fmt.Errorf("failed to get connection information")
		}
		belongs, err := cidrutil.IPBelongsToCIDRBlocksString(req.Connection.RemoteAddr, role.BoundCIDRList, ",")
		if err != nil {
			return nil, "", metadata, fmt.Errorf("failed to verify the CIDR restrictions set on the role: %v", err)
		}
		if !belongs {
			return nil, "", metadata, fmt.Errorf("source address unauthorized through CIDR restrictions on the role")
		}
	}
	return role, roleName, metadata, nil
}
func (b *backend) validateBindSecretID(req *logical.Request, roleName, secretID,
	hmacKey, roleBoundCIDRList string) (bool, map[string]string, error) {
	secretIDHMAC, err := createHMAC(hmacKey, secretID)
	if err != nil {
		return false, nil, fmt.Errorf("failed to create HMAC of secret_id: %v", err)
	}
	roleNameHMAC, err := createHMAC(hmacKey, roleName)
	if err != nil {
		return false, nil, fmt.Errorf("failed to create HMAC of role_name: %v", err)
	}
	entryIndex := fmt.Sprintf("secret_id/%s/%s", roleNameHMAC, secretIDHMAC)
	lock := b.secretIDLock(secretIDHMAC)
	lock.RLock()
	result, err := b.nonLockedSecretIDStorageEntry(req.Storage, roleNameHMAC, secretIDHMAC)
	if err != nil {
		lock.RUnlock()
		return false, nil, err
	} else if result == nil {
		lock.RUnlock()
		return false, nil, nil
	}
	if result.SecretIDNumUses == 0 {
		if err := verifyCIDRRoleSecretIDSubset(result.CIDRList,
			roleBoundCIDRList); err != nil {
			return false, nil, err
		}
		if len(result.CIDRList) != 0 {
			if req.Connection == nil || req.Connection.RemoteAddr == "" {
				return false, nil, fmt.Errorf("failed to get connection information")
			}
			if belongs, err := cidrutil.IPBelongsToCIDRBlocksSlice(req.Connection.RemoteAddr, result.CIDRList); !belongs || err != nil {
				return false, nil, fmt.Errorf("source address unauthorized through CIDR restrictions on the secret ID: %v", err)
			}
		}
		lock.RUnlock()
		return true, result.Metadata, nil
	}
	lock.RUnlock()
	lock.Lock()
	defer lock.Unlock()
	result, err = b.nonLockedSecretIDStorageEntry(req.Storage, roleNameHMAC, secretIDHMAC)
	if err != nil {
		return false, nil, err
	}
	if result == nil {
		return false, nil, nil
	}
	if result.SecretIDNumUses == 1 {
		if err := b.deleteSecretIDAccessorEntry(req.Storage, result.SecretIDAccessor); err != nil {
			return false, nil, err
		}
		if err := req.Storage.Delete(entryIndex); err != nil {
			return false, nil, fmt.Errorf("failed to delete secret ID: %v", err)
		}
	} else {
		result.SecretIDNumUses -= 1
		result.LastUpdatedTime = time.Now()
		if entry, err := logical.StorageEntryJSON(entryIndex, &result); err != nil {
			return false, nil, fmt.Errorf("failed to decrement the use count for secret ID %q", secretID)
		} else if err = req.Storage.Put(entry); err != nil {
			return false, nil, fmt.Errorf("failed to decrement the use count for secret ID %q", secretID)
		}
	}
	if err := verifyCIDRRoleSecretIDSubset(result.CIDRList,
		roleBoundCIDRList); err != nil {
		return false, nil, err
	}
	if len(result.CIDRList) != 0 {
		if req.Connection == nil || req.Connection.RemoteAddr == "" {
			return false, nil, fmt.Errorf("failed to get connection information")
		}
		if belongs, err := cidrutil.IPBelongsToCIDRBlocksSlice(req.Connection.RemoteAddr, result.CIDRList); !belongs || err != nil {
			return false, nil, fmt.Errorf("source address unauthorized through CIDR restrictions on the secret ID: %v", err)
		}
	}
	return true, result.Metadata, nil
}
func verifyCIDRRoleSecretIDSubset(secretIDCIDRs []string, roleBoundCIDRList string) error {
	if len(secretIDCIDRs) != 0 {
		roleCIDRs := strutil.ParseDedupAndSortStrings(roleBoundCIDRList, ",")
		if len(roleCIDRs) != 0 {
			subset, err := cidrutil.SubsetBlocks(roleCIDRs, secretIDCIDRs)
			if !subset || err != nil {
				return fmt.Errorf("failed to verify subset relationship between CIDR blocks on the role %q and CIDR blocks on the secret ID %q: %v", roleCIDRs, secretIDCIDRs, err)
			}
		}
	}
	return nil
}
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
}
func (b *backend) secretIDLock(secretIDHMAC string) *sync.RWMutex {
	var lock *sync.RWMutex
	var ok bool
	if len(secretIDHMAC) >= 2 {
		lock, ok = b.secretIDLocksMap[secretIDHMAC[0:2]]
	}
	if !ok || lock == nil {
		lock = b.secretIDLocksMap["custom"]
	}
	return lock
}
func (b *backend) secretIDAccessorLock(secretIDAccessor string) *sync.RWMutex {
	var lock *sync.RWMutex
	var ok bool
	if len(secretIDAccessor) >= 2 {
		lock, ok = b.secretIDAccessorLocksMap[secretIDAccessor[0:2]]
	}
	if !ok || lock == nil {
		lock = b.secretIDAccessorLocksMap["custom"]
	}
	return lock
}
func (b *backend) nonLockedSecretIDStorageEntry(s logical.Storage, roleNameHMAC, secretIDHMAC string) (*secretIDStorageEntry, error) {
	if secretIDHMAC == "" {
		return nil, fmt.Errorf("missing secret ID HMAC")
	}
	if roleNameHMAC == "" {
		return nil, fmt.Errorf("missing role name HMAC")
	}
	entryIndex := fmt.Sprintf("secret_id/%s/%s", roleNameHMAC, secretIDHMAC)
	entry, err := s.Get(entryIndex)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}
	result := secretIDStorageEntry{}
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}
	persistNeeded := false
	if result.SecretIDNumUsesDeprecated != 0 {
		if result.SecretIDNumUses == 0 ||
			result.SecretIDNumUsesDeprecated < result.SecretIDNumUses {
			result.SecretIDNumUses = result.SecretIDNumUsesDeprecated
			persistNeeded = true
		}
		if result.SecretIDNumUses < result.SecretIDNumUsesDeprecated {
			result.SecretIDNumUsesDeprecated = result.SecretIDNumUses
			persistNeeded = true
		}
	}
	if persistNeeded {
		if err := b.nonLockedSetSecretIDStorageEntry(s, roleNameHMAC, secretIDHMAC, &result); err != nil {
			return nil, fmt.Errorf("failed to upgrade role storage entry", err)
		}
	}
	return &result, nil
}