2023-03-15 16:00:52 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2015-03-06 01:23:56 +00:00
package vault
import (
2018-01-08 18:31:38 +00:00
"context"
2015-03-06 01:23:56 +00:00
"fmt"
2021-10-13 16:51:20 +00:00
"regexp"
2015-03-06 01:23:56 +00:00
"strings"
"sync"
2018-03-16 17:35:19 +00:00
"sync/atomic"
2015-04-08 23:43:17 +00:00
"time"
2015-03-06 01:23:56 +00:00
2022-04-26 16:13:45 +00:00
"github.com/armon/go-metrics"
"github.com/armon/go-radix"
"github.com/hashicorp/go-hclog"
2021-07-16 00:17:31 +00:00
"github.com/hashicorp/go-secure-stdlib/strutil"
2018-09-18 03:03:00 +00:00
"github.com/hashicorp/vault/helper/namespace"
2019-04-13 07:44:06 +00:00
"github.com/hashicorp/vault/sdk/helper/consts"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/helper/salt"
"github.com/hashicorp/vault/sdk/logical"
2015-03-06 01:23:56 +00:00
)
2021-04-08 16:43:39 +00:00
var deniedPassthroughRequestHeaders = [ ] string {
consts . AuthHeaderName ,
}
2018-10-01 19:20:31 +00:00
2021-10-13 16:51:20 +00:00
// matches when '+' is next to a non-slash char
var wcAdjacentNonSlashRegEx = regexp . MustCompile ( ` \+[^/]|[^/]\+ ` ) . MatchString
2015-03-06 01:23:56 +00:00
// Router is used to do prefix based routing of a request to a logical backend
type Router struct {
2017-06-26 17:14:36 +00:00
l sync . RWMutex
root * radix . Tree
mountUUIDCache * radix . Tree
mountAccessorCache * radix . Tree
2018-03-08 19:21:11 +00:00
tokenStoreSaltFunc func ( context . Context ) ( * salt . Salt , error )
2017-01-07 23:18:22 +00:00
// storagePrefix maps the prefix used for storage (ala the BarrierView)
// to the backend. This is used to map a key back into the backend that owns it.
2017-09-15 13:02:29 +00:00
// For example, logical/uuid1/foobar -> secrets/ (kv backend) + foobar
2017-01-07 23:18:22 +00:00
storagePrefix * radix . Tree
2019-07-03 02:16:43 +00:00
logger hclog . Logger
2015-03-06 01:23:56 +00:00
}
// NewRouter returns a new router
func NewRouter ( ) * Router {
r := & Router {
2017-06-26 17:14:36 +00:00
root : radix . New ( ) ,
storagePrefix : radix . New ( ) ,
mountUUIDCache : radix . New ( ) ,
mountAccessorCache : radix . New ( ) ,
2022-04-26 16:13:45 +00:00
// this will get replaced in production with a real logger but it's useful to have a default in place for tests
logger : hclog . NewNullLogger ( ) ,
2015-03-06 01:23:56 +00:00
}
return r
}
2015-09-04 20:58:12 +00:00
// routeEntry is used to represent a mount point in the router
type routeEntry struct {
2017-10-23 18:59:37 +00:00
tainted bool
backend logical . Backend
mountEntry * MountEntry
storageView logical . Storage
storagePrefix string
2018-03-16 17:35:19 +00:00
rootPaths atomic . Value
loginPaths atomic . Value
2018-03-21 19:04:27 +00:00
l sync . RWMutex
2015-03-06 01:23:56 +00:00
}
2021-10-13 16:51:20 +00:00
type wildcardPath struct {
// this sits in the hot path of requests so we are micro-optimizing by
// storing pre-split slices of path segments
segments [ ] string
isPrefix bool
}
// loginPathsEntry is used to hold the routeEntry loginPaths
type loginPathsEntry struct {
paths * radix . Tree
wildcardPaths [ ] wildcardPath
}
2021-08-30 19:31:11 +00:00
type ValidateMountResponse struct {
2017-10-11 17:21:20 +00:00
MountType string ` json:"mount_type" structs:"mount_type" mapstructure:"mount_type" `
MountAccessor string ` json:"mount_accessor" structs:"mount_accessor" mapstructure:"mount_accessor" `
MountPath string ` json:"mount_path" structs:"mount_path" mapstructure:"mount_path" `
2018-04-23 17:46:14 +00:00
MountLocal bool ` json:"mount_local" structs:"mount_local" mapstructure:"mount_local" `
2017-10-11 17:21:20 +00:00
}
2019-07-03 02:16:43 +00:00
func ( r * Router ) reset ( ) {
r . l . Lock ( )
defer r . l . Unlock ( )
r . root = radix . New ( )
r . storagePrefix = radix . New ( )
r . mountUUIDCache = radix . New ( )
r . mountAccessorCache = radix . New ( )
}
2022-11-04 16:39:09 +00:00
func ( r * Router ) GetRecords ( tag string ) ( [ ] map [ string ] interface { } , error ) {
r . l . RLock ( )
defer r . l . RUnlock ( )
var data [ ] map [ string ] interface { }
var tree * radix . Tree
switch tag {
case "root" :
tree = r . root
case "uuid" :
tree = r . mountUUIDCache
case "accessor" :
tree = r . mountAccessorCache
case "storage" :
tree = r . storagePrefix
default :
return nil , logical . ErrUnsupportedPath
}
for _ , v := range tree . ToMap ( ) {
info := v . ( Deserializable ) . Deserialize ( )
data = append ( data , info )
}
return data , nil
}
func ( entry * routeEntry ) Deserialize ( ) map [ string ] interface { } {
entry . l . RLock ( )
defer entry . l . RUnlock ( )
ret := map [ string ] interface { } {
"tainted" : entry . tainted ,
"storage_prefix" : entry . storagePrefix ,
}
for k , v := range entry . mountEntry . Deserialize ( ) {
ret [ k ] = v
}
return ret
}
2021-08-30 19:31:11 +00:00
// ValidateMountByAccessor returns the mount type and ID for a given mount
2017-10-11 17:21:20 +00:00
// accessor
2021-08-30 19:31:11 +00:00
func ( r * Router ) ValidateMountByAccessor ( accessor string ) * ValidateMountResponse {
2017-10-11 17:21:20 +00:00
if accessor == "" {
return nil
}
mountEntry := r . MatchingMountByAccessor ( accessor )
if mountEntry == nil {
return nil
}
2017-11-14 06:31:10 +00:00
mountPath := mountEntry . Path
if mountEntry . Table == credentialTableType {
mountPath = credentialRoutePrefix + mountPath
}
2021-08-30 19:31:11 +00:00
return & ValidateMountResponse {
2017-10-11 17:21:20 +00:00
MountAccessor : mountEntry . Accessor ,
MountType : mountEntry . Type ,
2017-11-14 06:31:10 +00:00
MountPath : mountPath ,
2018-04-23 17:46:14 +00:00
MountLocal : mountEntry . Local ,
2017-10-11 17:21:20 +00:00
}
}
2016-05-15 16:58:36 +00:00
// SaltID is used to apply a salt and hash to an ID to make sure its not reversible
2015-09-04 20:58:12 +00:00
func ( re * routeEntry ) SaltID ( id string ) string {
return salt . SaltID ( re . mountEntry . UUID , id , salt . SHA1Hash )
2015-04-03 21:42:39 +00:00
}
// Mount is used to expose a logical backend at a given prefix, using a unique salt,
// and the barrier view for that path.
2015-09-15 16:27:22 +00:00
func ( r * Router ) Mount ( backend logical . Backend , prefix string , mountEntry * MountEntry , storageView * BarrierView ) error {
2015-03-06 01:23:56 +00:00
r . l . Lock ( )
defer r . l . Unlock ( )
2018-09-18 03:03:00 +00:00
// prepend namespace
prefix = mountEntry . Namespace ( ) . Path + prefix
2015-03-06 01:23:56 +00:00
// Check if this is a nested mount
if existing , _ , ok := r . root . LongestPrefix ( prefix ) ; ok && existing != "" {
2018-04-05 15:49:21 +00:00
return fmt . Errorf ( "cannot mount under existing mount %q" , existing )
2015-03-06 01:23:56 +00:00
}
2015-03-31 00:46:18 +00:00
// Build the paths
2017-09-01 05:02:03 +00:00
paths := new ( logical . Paths )
if backend != nil {
specialPaths := backend . SpecialPaths ( )
if specialPaths != nil {
paths = specialPaths
}
2015-03-06 01:23:56 +00:00
}
// Create a mount entry
2015-09-04 20:58:12 +00:00
re := & routeEntry {
2019-06-04 17:33:36 +00:00
tainted : mountEntry . Tainted ,
2017-10-23 18:59:37 +00:00
backend : backend ,
mountEntry : mountEntry ,
2019-01-31 14:25:18 +00:00
storagePrefix : storageView . Prefix ( ) ,
2018-03-20 14:42:57 +00:00
storageView : storageView ,
2015-03-06 01:23:56 +00:00
}
2018-03-16 17:35:19 +00:00
re . rootPaths . Store ( pathsToRadix ( paths . Root ) )
2021-10-13 16:51:20 +00:00
loginPathsEntry , err := parseUnauthenticatedPaths ( paths . Unauthenticated )
if err != nil {
return err
}
re . loginPaths . Store ( loginPathsEntry )
2017-05-05 16:54:37 +00:00
2017-06-29 21:00:13 +00:00
switch {
case prefix == "" :
return fmt . Errorf ( "missing prefix to be used for router entry; mount_path: %q, mount_type: %q" , re . mountEntry . Path , re . mountEntry . Type )
2017-10-23 18:59:37 +00:00
case re . storagePrefix == "" :
2017-06-29 21:00:13 +00:00
return fmt . Errorf ( "missing storage view prefix; mount_path: %q, mount_type: %q" , re . mountEntry . Path , re . mountEntry . Type )
case re . mountEntry . UUID == "" :
return fmt . Errorf ( "missing mount identifier; mount_path: %q, mount_type: %q" , re . mountEntry . Path , re . mountEntry . Type )
case re . mountEntry . Accessor == "" :
return fmt . Errorf ( "missing mount accessor; mount_path: %q, mount_type: %q" , re . mountEntry . Path , re . mountEntry . Type )
}
2015-09-04 20:58:12 +00:00
r . root . Insert ( prefix , re )
2017-10-23 18:59:37 +00:00
r . storagePrefix . Insert ( re . storagePrefix , re )
2017-06-17 03:54:19 +00:00
r . mountUUIDCache . Insert ( re . mountEntry . UUID , re . mountEntry )
2017-06-26 17:14:36 +00:00
r . mountAccessorCache . Insert ( re . mountEntry . Accessor , re . mountEntry )
2015-09-10 01:58:09 +00:00
2015-03-06 01:23:56 +00:00
return nil
}
// Unmount is used to remove a logical backend from a given prefix
2018-01-19 06:44:44 +00:00
func ( r * Router ) Unmount ( ctx context . Context , prefix string ) error {
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
prefix = ns . Path + prefix
2015-03-06 01:23:56 +00:00
r . l . Lock ( )
defer r . l . Unlock ( )
2015-09-10 14:11:37 +00:00
2017-01-07 23:18:22 +00:00
// Fast-path out if the backend doesn't exist
raw , ok := r . root . Get ( prefix )
if ! ok {
return nil
2015-09-10 14:11:37 +00:00
}
2017-01-07 23:18:22 +00:00
// Call backend's Cleanup routine
re := raw . ( * routeEntry )
2017-10-23 18:59:37 +00:00
if re . backend != nil {
2018-01-19 06:44:44 +00:00
re . backend . Cleanup ( ctx )
2017-10-23 18:59:37 +00:00
}
2017-01-07 23:18:22 +00:00
// Purge from the radix trees
2015-03-06 01:23:56 +00:00
r . root . Delete ( prefix )
2017-10-23 18:59:37 +00:00
r . storagePrefix . Delete ( re . storagePrefix )
2017-06-17 03:54:19 +00:00
r . mountUUIDCache . Delete ( re . mountEntry . UUID )
2017-06-26 17:14:36 +00:00
r . mountAccessorCache . Delete ( re . mountEntry . Accessor )
2017-05-05 16:54:37 +00:00
2015-03-06 01:23:56 +00:00
return nil
}
// Remount is used to change the mount location of a logical backend
2018-09-18 03:03:00 +00:00
func ( r * Router ) Remount ( ctx context . Context , src , dst string ) error {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
src = ns . Path + src
dst = ns . Path + dst
2015-03-06 01:23:56 +00:00
r . l . Lock ( )
defer r . l . Unlock ( )
// Check for existing mount
raw , ok := r . root . Get ( src )
if ! ok {
2018-04-05 15:49:21 +00:00
return fmt . Errorf ( "no mount at %q" , src )
2015-03-06 01:23:56 +00:00
}
// Update the mount point
r . root . Delete ( src )
r . root . Insert ( dst , raw )
return nil
}
2015-04-02 18:12:13 +00:00
// Taint is used to mark a path as tainted. This means only RollbackOperation
2016-03-01 01:29:04 +00:00
// RevokeOperation requests are allowed to proceed
2018-09-18 03:03:00 +00:00
func ( r * Router ) Taint ( ctx context . Context , path string ) error {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
path = ns . Path + path
2015-04-02 18:12:13 +00:00
r . l . Lock ( )
defer r . l . Unlock ( )
_ , raw , ok := r . root . LongestPrefix ( path )
if ok {
2015-09-04 20:58:12 +00:00
raw . ( * routeEntry ) . tainted = true
2015-04-02 18:12:13 +00:00
}
return nil
}
2015-04-02 19:01:53 +00:00
// Untaint is used to unmark a path as tainted.
2018-09-18 03:03:00 +00:00
func ( r * Router ) Untaint ( ctx context . Context , path string ) error {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return err
}
path = ns . Path + path
2015-04-02 19:01:53 +00:00
r . l . Lock ( )
defer r . l . Unlock ( )
_ , raw , ok := r . root . LongestPrefix ( path )
if ok {
2015-09-04 20:58:12 +00:00
raw . ( * routeEntry ) . tainted = false
2015-04-02 19:01:53 +00:00
}
return nil
}
2017-06-17 03:54:19 +00:00
func ( r * Router ) MatchingMountByUUID ( mountID string ) * MountEntry {
2017-05-05 16:54:37 +00:00
if mountID == "" {
return nil
}
r . l . RLock ( )
2018-06-14 13:49:10 +00:00
2018-06-14 17:44:13 +00:00
_ , raw , ok := r . mountUUIDCache . LongestPrefix ( mountID )
2017-05-05 16:54:37 +00:00
if ! ok {
2018-06-14 17:44:13 +00:00
r . l . RUnlock ( )
2017-05-05 16:54:37 +00:00
return nil
}
2018-06-14 17:44:13 +00:00
r . l . RUnlock ( )
2017-05-05 16:54:37 +00:00
return raw . ( * MountEntry )
}
2017-08-08 04:18:59 +00:00
// MatchingMountByAccessor returns the MountEntry by accessor lookup
2017-06-26 17:14:36 +00:00
func ( r * Router ) MatchingMountByAccessor ( mountAccessor string ) * MountEntry {
if mountAccessor == "" {
return nil
}
r . l . RLock ( )
2018-06-14 13:49:10 +00:00
2018-06-14 17:44:13 +00:00
_ , raw , ok := r . mountAccessorCache . LongestPrefix ( mountAccessor )
2017-06-26 17:14:36 +00:00
if ! ok {
2018-06-14 17:44:13 +00:00
r . l . RUnlock ( )
2017-06-26 17:14:36 +00:00
return nil
}
2018-06-14 17:44:13 +00:00
r . l . RUnlock ( )
2017-06-26 17:14:36 +00:00
return raw . ( * MountEntry )
}
2015-04-02 18:03:59 +00:00
// MatchingMount returns the mount prefix that would be used for a path
2018-09-18 03:03:00 +00:00
func ( r * Router ) MatchingMount ( ctx context . Context , path string ) string {
2015-03-12 00:56:01 +00:00
r . l . RLock ( )
2018-09-18 03:03:00 +00:00
mount := r . matchingMountInternal ( ctx , path )
2018-06-14 13:49:10 +00:00
r . l . RUnlock ( )
2017-11-06 20:29:09 +00:00
return mount
}
2018-09-18 03:03:00 +00:00
func ( r * Router ) matchingMountInternal ( ctx context . Context , path string ) string {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return ""
}
path = ns . Path + path
2015-03-12 00:56:01 +00:00
mount , _ , ok := r . root . LongestPrefix ( path )
if ! ok {
return ""
}
return mount
}
2017-11-06 20:29:09 +00:00
// matchingPrefixInternal returns a mount prefix that a path may be a part of
2018-09-18 03:03:00 +00:00
func ( r * Router ) matchingPrefixInternal ( ctx context . Context , path string ) string {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return ""
}
path = ns . Path + path
var existing string
fn := func ( existingPath string , v interface { } ) bool {
if strings . HasPrefix ( existingPath , path ) {
existing = existingPath
2017-11-06 20:29:09 +00:00
return true
}
return false
}
r . root . WalkPrefix ( path , fn )
return existing
}
// MountConflict determines if there are potential path conflicts
2018-09-18 03:03:00 +00:00
func ( r * Router ) MountConflict ( ctx context . Context , path string ) string {
2017-11-06 20:29:09 +00:00
r . l . RLock ( )
defer r . l . RUnlock ( )
2018-09-18 03:03:00 +00:00
if exactMatch := r . matchingMountInternal ( ctx , path ) ; exactMatch != "" {
return exactMatch
2017-11-06 20:29:09 +00:00
}
2018-09-18 03:03:00 +00:00
if prefixMatch := r . matchingPrefixInternal ( ctx , path ) ; prefixMatch != "" {
return prefixMatch
2017-11-06 20:29:09 +00:00
}
return ""
}
2017-10-23 18:59:37 +00:00
// MatchingStorageByAPIPath/StoragePath returns the storage used for
// API/Storage paths respectively
2018-09-18 03:03:00 +00:00
func ( r * Router ) MatchingStorageByAPIPath ( ctx context . Context , path string ) logical . Storage {
return r . matchingStorage ( ctx , path , true )
2017-10-23 18:59:37 +00:00
}
2021-04-08 16:43:39 +00:00
2018-09-18 03:03:00 +00:00
func ( r * Router ) MatchingStorageByStoragePath ( ctx context . Context , path string ) logical . Storage {
return r . matchingStorage ( ctx , path , false )
2017-10-23 18:59:37 +00:00
}
2021-04-08 16:43:39 +00:00
2018-09-18 03:03:00 +00:00
func ( r * Router ) matchingStorage ( ctx context . Context , path string , apiPath bool ) logical . Storage {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil
}
path = ns . Path + path
2017-10-23 18:59:37 +00:00
var raw interface { }
var ok bool
2015-04-02 18:03:59 +00:00
r . l . RLock ( )
2017-10-23 18:59:37 +00:00
if apiPath {
_ , raw , ok = r . root . LongestPrefix ( path )
} else {
_ , raw , ok = r . storagePrefix . LongestPrefix ( path )
}
2015-04-02 18:03:59 +00:00
r . l . RUnlock ( )
if ! ok {
return nil
}
2015-09-15 16:27:22 +00:00
return raw . ( * routeEntry ) . storageView
2015-09-04 20:58:12 +00:00
}
// MatchingMountEntry returns the MountEntry used for a path
2018-09-18 03:03:00 +00:00
func ( r * Router ) MatchingMountEntry ( ctx context . Context , path string ) * MountEntry {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil
}
path = ns . Path + path
2015-09-04 20:58:12 +00:00
r . l . RLock ( )
_ , raw , ok := r . root . LongestPrefix ( path )
r . l . RUnlock ( )
if ! ok {
return nil
}
return raw . ( * routeEntry ) . mountEntry
}
2017-08-08 04:18:59 +00:00
// MatchingBackend returns the backend used for a path
2018-09-18 03:03:00 +00:00
func ( r * Router ) MatchingBackend ( ctx context . Context , path string ) logical . Backend {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil
}
path = ns . Path + path
2015-09-15 15:28:07 +00:00
r . l . RLock ( )
_ , raw , ok := r . root . LongestPrefix ( path )
r . l . RUnlock ( )
if ! ok {
return nil
}
2023-03-14 14:36:37 +00:00
re := raw . ( * routeEntry )
re . l . RLock ( )
defer re . l . RUnlock ( )
return re . backend
2015-09-15 15:28:07 +00:00
}
2015-09-04 20:58:12 +00:00
// MatchingSystemView returns the SystemView used for a path
2018-09-18 03:03:00 +00:00
func ( r * Router ) MatchingSystemView ( ctx context . Context , path string ) logical . SystemView {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil
}
path = ns . Path + path
2015-09-04 20:58:12 +00:00
r . l . RLock ( )
_ , raw , ok := r . root . LongestPrefix ( path )
r . l . RUnlock ( )
2019-12-10 18:48:30 +00:00
if ! ok || raw . ( * routeEntry ) . backend == nil {
2015-09-04 20:58:12 +00:00
return nil
}
return raw . ( * routeEntry ) . backend . System ( )
2015-04-02 18:03:59 +00:00
}
2020-06-26 21:13:16 +00:00
func ( r * Router ) MatchingMountByAPIPath ( ctx context . Context , path string ) string {
me , _ , _ := r . matchingMountEntryByPath ( ctx , path , true )
if me == nil {
return ""
}
return me . Path
}
2018-09-18 03:03:00 +00:00
// MatchingStoragePrefixByAPIPath the storage prefix for the given api path
func ( r * Router ) MatchingStoragePrefixByAPIPath ( ctx context . Context , path string ) ( string , bool ) {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return "" , false
}
path = ns . Path + path
_ , prefix , found := r . matchingMountEntryByPath ( ctx , path , true )
return prefix , found
2017-10-23 18:59:37 +00:00
}
2018-09-18 03:03:00 +00:00
// MatchingAPIPrefixByStoragePath the api path information for the given storage path
func ( r * Router ) MatchingAPIPrefixByStoragePath ( ctx context . Context , path string ) ( * namespace . Namespace , string , string , bool ) {
me , prefix , found := r . matchingMountEntryByPath ( ctx , path , false )
if ! found {
return nil , "" , "" , found
}
mountPath := me . Path
// Add back the prefix for credential backends
if strings . HasPrefix ( path , credentialBarrierPrefix ) {
mountPath = credentialRoutePrefix + mountPath
}
return me . Namespace ( ) , mountPath , prefix , found
2017-10-23 18:59:37 +00:00
}
2018-09-18 03:03:00 +00:00
func ( r * Router ) matchingMountEntryByPath ( ctx context . Context , path string , apiPath bool ) ( * MountEntry , string , bool ) {
2017-10-23 18:59:37 +00:00
var raw interface { }
var ok bool
2017-01-07 23:18:22 +00:00
r . l . RLock ( )
2017-10-23 18:59:37 +00:00
if apiPath {
_ , raw , ok = r . root . LongestPrefix ( path )
} else {
_ , raw , ok = r . storagePrefix . LongestPrefix ( path )
}
2017-01-07 23:18:22 +00:00
r . l . RUnlock ( )
if ! ok {
2018-09-18 03:03:00 +00:00
return nil , "" , false
2017-01-07 23:18:22 +00:00
}
// Extract the mount path and storage prefix
re := raw . ( * routeEntry )
2017-10-23 18:59:37 +00:00
prefix := re . storagePrefix
2017-07-04 18:00:36 +00:00
2018-09-18 03:03:00 +00:00
return re . mountEntry , prefix , true
2017-01-07 23:18:22 +00:00
}
2015-03-06 01:23:56 +00:00
// Route is used to route a given request
2018-01-08 18:31:38 +00:00
func ( r * Router ) Route ( ctx context . Context , req * logical . Request ) ( * logical . Response , error ) {
resp , _ , _ , err := r . routeCommon ( ctx , req , false )
2016-01-07 20:10:05 +00:00
return resp , err
}
2018-09-18 03:03:00 +00:00
// RouteExistenceCheck is used to route a given existence check request
2018-10-15 16:56:24 +00:00
func ( r * Router ) RouteExistenceCheck ( ctx context . Context , req * logical . Request ) ( * logical . Response , bool , bool , error ) {
resp , ok , exists , err := r . routeCommon ( ctx , req , true )
return resp , ok , exists , err
2016-01-07 20:10:05 +00:00
}
2018-01-08 18:31:38 +00:00
func ( r * Router ) routeCommon ( ctx context . Context , req * logical . Request , existenceCheck bool ) ( * logical . Response , bool , bool , error ) {
2018-09-18 03:03:00 +00:00
ns , err := namespace . FromContext ( ctx )
if err != nil {
return nil , false , false , err
}
2015-03-06 01:23:56 +00:00
// Find the mount point
r . l . RLock ( )
2017-08-31 20:50:03 +00:00
adjustedPath := req . Path
2018-09-18 03:03:00 +00:00
mount , raw , ok := r . root . LongestPrefix ( ns . Path + adjustedPath )
2017-08-31 20:50:03 +00:00
if ! ok && ! strings . HasSuffix ( adjustedPath , "/" ) {
2015-05-07 19:18:50 +00:00
// Re-check for a backend by appending a slash. This lets "foo" mean
// "foo/" at the root level which is almost always what we want.
2017-08-31 20:50:03 +00:00
adjustedPath += "/"
2018-09-18 03:03:00 +00:00
mount , raw , ok = r . root . LongestPrefix ( ns . Path + adjustedPath )
2015-05-07 19:18:50 +00:00
}
2015-03-06 01:23:56 +00:00
r . l . RUnlock ( )
if ! ok {
2022-04-26 16:13:45 +00:00
return logical . ErrorResponse ( fmt . Sprintf ( "no handler for route %q. route entry not found." , req . Path ) ) , false , false , logical . ErrUnsupportedPath
2015-03-06 01:23:56 +00:00
}
2017-08-31 20:50:03 +00:00
req . Path = adjustedPath
2022-05-05 17:22:19 +00:00
if ! existenceCheck {
defer metrics . MeasureSince ( [ ] string {
"route" , string ( req . Operation ) ,
2022-08-03 19:22:48 +00:00
strings . ReplaceAll ( mount , "/" , "-" ) ,
2022-05-05 17:22:19 +00:00
} , time . Now ( ) )
}
2015-09-04 20:58:12 +00:00
re := raw . ( * routeEntry )
2015-03-06 01:23:56 +00:00
2018-03-21 19:04:27 +00:00
// Grab a read lock on the route entry, this protects against the backend
2019-04-08 17:40:54 +00:00
// being reloaded during a request. The exception is a renew request on the
// token store; such a request will have already been routed through the
// token store -> exp manager -> here so we need to not grab the lock again
// or we'll be recursively grabbing it.
2019-04-08 18:25:29 +00:00
if ! ( req . Operation == logical . RenewOperation && strings . HasPrefix ( req . Path , "auth/token/" ) ) {
2019-04-08 17:40:54 +00:00
re . l . RLock ( )
defer re . l . RUnlock ( )
}
2018-03-21 19:04:27 +00:00
2017-10-23 18:59:37 +00:00
// Filtered mounts will have a nil backend
if re . backend == nil {
2022-04-26 16:13:45 +00:00
return logical . ErrorResponse ( fmt . Sprintf ( "no handler for route %q. route entry found, but backend is nil." , req . Path ) ) , false , false , logical . ErrUnsupportedPath
2017-10-23 18:59:37 +00:00
}
2015-04-02 18:12:13 +00:00
// If the path is tainted, we reject any operation except for
// Rollback and Revoke
2015-09-04 20:58:12 +00:00
if re . tainted {
2015-04-02 18:12:13 +00:00
switch req . Operation {
case logical . RevokeOperation , logical . RollbackOperation :
default :
2022-04-26 16:13:45 +00:00
return logical . ErrorResponse ( fmt . Sprintf ( "no handler for route %q. route entry is tainted." , req . Path ) ) , false , false , logical . ErrUnsupportedPath
2015-04-02 18:12:13 +00:00
}
}
2015-03-24 22:12:52 +00:00
// Adjust the path to exclude the routing prefix
2016-09-29 04:01:28 +00:00
originalPath := req . Path
2018-09-18 03:03:00 +00:00
req . Path = strings . TrimPrefix ( ns . Path + req . Path , mount )
2015-05-27 18:46:42 +00:00
req . MountPoint = mount
2017-04-24 19:15:50 +00:00
req . MountType = re . mountEntry . Type
2015-04-04 03:48:04 +00:00
if req . Path == "/" {
req . Path = ""
}
2015-03-24 22:12:52 +00:00
// Attach the storage view for the request
2015-09-15 16:27:22 +00:00
req . Storage = re . storageView
2015-03-24 22:12:52 +00:00
2017-10-11 17:21:20 +00:00
originalEntityID := req . EntityID
2017-10-23 18:59:37 +00:00
// Hash the request token unless the request is being routed to the token
// or system backend.
2015-03-24 22:12:52 +00:00
clientToken := req . ClientToken
2015-09-10 01:58:09 +00:00
switch {
2016-09-29 04:01:28 +00:00
case strings . HasPrefix ( originalPath , "auth/token/" ) :
case strings . HasPrefix ( originalPath , "sys/" ) :
2021-09-27 17:55:29 +00:00
case strings . HasPrefix ( originalPath , "identity/" ) :
2018-09-18 03:03:00 +00:00
case strings . HasPrefix ( originalPath , cubbyholeMountPath ) :
if req . Operation == logical . RollbackOperation {
// Backend doesn't support this and it can't properly look up a
// cubbyhole ID so just return here
return nil , false , false , nil
}
2018-10-15 16:56:24 +00:00
te := req . TokenEntry ( )
if te == nil {
2018-09-18 03:03:00 +00:00
return nil , false , false , fmt . Errorf ( "nil token entry" )
2017-07-18 16:02:03 +00:00
}
2018-09-18 03:03:00 +00:00
2018-10-15 16:56:24 +00:00
if te . Type != logical . TokenTypeService {
return logical . ErrorResponse ( ` cubbyhole operations are only supported by "service" type tokens ` ) , false , false , nil
}
2018-10-17 20:23:04 +00:00
switch {
2022-03-01 20:24:45 +00:00
case te . NamespaceID == namespace . RootNamespaceID && ! strings . HasPrefix ( req . ClientToken , consts . LegacyServiceTokenPrefix ) &&
2022-02-17 19:43:07 +00:00
! strings . HasPrefix ( req . ClientToken , consts . ServiceTokenPrefix ) :
2018-09-18 03:03:00 +00:00
// In order for the token store to revoke later, we need to have the same
// salted ID, so we double-salt what's going to the cubbyhole backend
salt , err := r . tokenStoreSaltFunc ( ctx )
if err != nil {
return nil , false , false , err
}
req . ClientToken = re . SaltID ( salt . SaltID ( req . ClientToken ) )
default :
2018-10-15 16:56:24 +00:00
if te . CubbyholeID == "" {
2018-09-18 03:03:00 +00:00
return nil , false , false , fmt . Errorf ( "empty cubbyhole id" )
}
2018-10-15 16:56:24 +00:00
req . ClientToken = te . CubbyholeID
2018-09-18 03:03:00 +00:00
}
2015-09-10 01:58:09 +00:00
default :
2015-09-04 20:58:12 +00:00
req . ClientToken = re . SaltID ( req . ClientToken )
2015-03-24 22:12:52 +00:00
}
2015-03-06 01:23:56 +00:00
2016-02-18 16:22:04 +00:00
// Cache the pointer to the original connection object
2015-03-31 03:55:01 +00:00
originalConn := req . Connection
2016-07-24 01:46:28 +00:00
// Cache the identifier of the request
originalReqID := req . ID
2017-03-08 22:36:50 +00:00
// Cache the client token's number of uses in the request
originalClientTokenRemainingUses := req . ClientTokenRemainingUses
req . ClientTokenRemainingUses = 0
2019-03-25 18:18:43 +00:00
originalMFACreds := req . MFACreds
2018-09-18 03:03:00 +00:00
req . MFACreds = nil
2019-03-25 18:18:43 +00:00
originalControlGroup := req . ControlGroup
req . ControlGroup = nil
2018-03-21 23:56:47 +00:00
// Cache the headers
2017-02-02 19:49:20 +00:00
headers := req . Headers
2019-02-05 21:02:15 +00:00
req . Headers = nil
2017-02-02 19:49:20 +00:00
2022-02-17 19:43:07 +00:00
// Cache the saved request SSC token
inboundToken := req . InboundSSCToken
// Ensure that the inbound token we cache in the
// request during token creation isn't sent to backends
req . InboundSSCToken = ""
2018-03-21 23:56:47 +00:00
// Filter and add passthrough headers to the backend
var passthroughRequestHeaders [ ] string
if rawVal , ok := re . mountEntry . synthesizedConfigCache . Load ( "passthrough_request_headers" ) ; ok {
passthroughRequestHeaders = rawVal . ( [ ] string )
2018-03-21 19:04:27 +00:00
}
2019-02-05 21:02:15 +00:00
var allowedResponseHeaders [ ] string
if rawVal , ok := re . mountEntry . synthesizedConfigCache . Load ( "allowed_response_headers" ) ; ok {
allowedResponseHeaders = rawVal . ( [ ] string )
}
if len ( passthroughRequestHeaders ) > 0 {
req . Headers = filteredHeaders ( headers , passthroughRequestHeaders , deniedPassthroughRequestHeaders )
}
2018-03-21 19:04:27 +00:00
2017-01-04 21:44:03 +00:00
// Cache the wrap info of the request
var wrapInfo * logical . RequestWrapInfo
if req . WrapInfo != nil {
wrapInfo = & logical . RequestWrapInfo {
2017-11-09 17:47:42 +00:00
TTL : req . WrapInfo . TTL ,
Format : req . WrapInfo . Format ,
SealWrap : req . WrapInfo . SealWrap ,
2017-01-04 21:44:03 +00:00
}
}
2016-09-29 04:01:28 +00:00
2018-09-18 03:03:00 +00:00
originalPolicyOverride := req . PolicyOverride
2018-06-08 21:53:28 +00:00
reqTokenEntry := req . TokenEntry ( )
req . SetTokenEntry ( nil )
2015-03-06 01:23:56 +00:00
// Reset the request before returning
2018-06-14 17:44:13 +00:00
defer func ( ) {
2016-09-29 04:01:28 +00:00
req . Path = originalPath
2017-04-24 19:15:50 +00:00
req . MountPoint = mount
req . MountType = re . mountEntry . Type
2015-03-31 03:55:01 +00:00
req . Connection = originalConn
2016-07-24 01:46:28 +00:00
req . ID = originalReqID
2015-03-15 20:54:20 +00:00
req . Storage = nil
2015-03-24 18:09:25 +00:00
req . ClientToken = clientToken
2017-03-08 22:36:50 +00:00
req . ClientTokenRemainingUses = originalClientTokenRemainingUses
2017-01-04 21:44:03 +00:00
req . WrapInfo = wrapInfo
2017-02-02 19:49:20 +00:00
req . Headers = headers
2018-09-18 03:03:00 +00:00
req . PolicyOverride = originalPolicyOverride
2017-03-01 17:39:42 +00:00
// This is only set in one place, after routing, so should never be set
// by a backend
req . SetLastRemoteWAL ( 0 )
2017-10-11 17:21:20 +00:00
// This will be used for attaching the mount accessor for the identities
// returned by the authentication backends
req . MountAccessor = re . mountEntry . Accessor
req . EntityID = originalEntityID
2018-06-08 21:53:28 +00:00
2019-03-25 18:18:43 +00:00
req . MFACreds = originalMFACreds
2018-09-18 03:03:00 +00:00
2022-02-17 19:43:07 +00:00
req . InboundSSCToken = inboundToken
// Before resetting the tokenEntry, see if an ExternalID was added
if req . TokenEntry ( ) != nil && req . TokenEntry ( ) . ExternalID != "" {
reqTokenEntry . ExternalID = req . TokenEntry ( ) . ExternalID
}
2018-06-08 21:53:28 +00:00
req . SetTokenEntry ( reqTokenEntry )
2019-03-25 18:18:43 +00:00
req . ControlGroup = originalControlGroup
2018-06-14 17:44:13 +00:00
} ( )
2015-03-06 01:23:56 +00:00
// Invoke the backend
2016-01-07 20:10:05 +00:00
if existenceCheck {
2018-01-08 18:31:38 +00:00
ok , exists , err := re . backend . HandleExistenceCheck ( ctx , req )
2016-01-12 20:09:16 +00:00
return nil , ok , exists , err
2016-01-07 20:10:05 +00:00
} else {
2018-01-08 18:31:38 +00:00
resp , err := re . backend . HandleRequest ( ctx , req )
2019-02-05 21:02:15 +00:00
if resp != nil {
if len ( allowedResponseHeaders ) > 0 {
resp . Headers = filteredHeaders ( resp . Headers , allowedResponseHeaders , nil )
} else {
resp . Headers = nil
2017-11-02 20:05:48 +00:00
}
2018-10-15 16:56:24 +00:00
2019-02-05 21:02:15 +00:00
if resp . Auth != nil {
// When a token gets renewed, the request hits this path and
// reaches token store. Token store delegates the renewal to the
// expiration manager. Expiration manager in-turn creates a
// different logical request and forwards the request to the auth
// backend that had initially authenticated the login request. The
// forwarding to auth backend will make this code path hit for the
// second time for the same renewal request. The accessors in the
// Alias structs should be of the auth backend and not of the token
// store. Therefore, avoiding the overwriting of accessors by
// having a check for path prefix having "renew". This gets applied
// for "renew" and "renew-self" requests.
if ! strings . HasPrefix ( req . Path , "renew" ) {
if resp . Auth . Alias != nil {
resp . Auth . Alias . MountAccessor = re . mountEntry . Accessor
2018-10-15 16:56:24 +00:00
}
2019-02-05 21:02:15 +00:00
for _ , alias := range resp . Auth . GroupAliases {
alias . MountAccessor = re . mountEntry . Accessor
}
}
switch re . mountEntry . Type {
case "token" , "ns_token" :
// Nothing; we respect what the token store is telling us and
// we don't allow tuning
default :
switch re . mountEntry . Config . TokenType {
case logical . TokenTypeService , logical . TokenTypeBatch :
resp . Auth . TokenType = re . mountEntry . Config . TokenType
case logical . TokenTypeDefault , logical . TokenTypeDefaultService :
2019-07-03 02:16:43 +00:00
switch resp . Auth . TokenType {
case logical . TokenTypeDefault , logical . TokenTypeDefaultService , logical . TokenTypeService :
2019-02-05 21:02:15 +00:00
resp . Auth . TokenType = logical . TokenTypeService
2019-07-03 02:16:43 +00:00
default :
resp . Auth . TokenType = logical . TokenTypeBatch
2019-02-05 21:02:15 +00:00
}
case logical . TokenTypeDefaultBatch :
2019-07-03 02:16:43 +00:00
switch resp . Auth . TokenType {
case logical . TokenTypeDefault , logical . TokenTypeDefaultBatch , logical . TokenTypeBatch :
2019-02-05 21:02:15 +00:00
resp . Auth . TokenType = logical . TokenTypeBatch
2019-07-03 02:16:43 +00:00
default :
resp . Auth . TokenType = logical . TokenTypeService
2019-02-05 21:02:15 +00:00
}
2018-10-15 16:56:24 +00:00
}
}
2017-11-02 20:05:48 +00:00
}
2017-10-11 17:21:20 +00:00
}
2018-09-18 03:03:00 +00:00
2016-01-12 20:09:16 +00:00
return resp , false , false , err
2016-01-07 20:10:05 +00:00
}
2015-03-06 01:23:56 +00:00
}
// RootPath checks if the given path requires root privileges
2018-09-18 03:03:00 +00:00
func ( r * Router ) RootPath ( ctx context . Context , path string ) bool {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return false
}
adjustedPath := ns . Path + path
2015-03-06 01:23:56 +00:00
r . l . RLock ( )
2018-09-18 03:03:00 +00:00
mount , raw , ok := r . root . LongestPrefix ( adjustedPath )
2015-03-06 01:23:56 +00:00
r . l . RUnlock ( )
if ! ok {
return false
}
2015-09-04 20:58:12 +00:00
re := raw . ( * routeEntry )
2015-03-06 01:23:56 +00:00
// Trim to get remaining path
2018-09-18 03:03:00 +00:00
remain := strings . TrimPrefix ( adjustedPath , mount )
2015-03-06 01:23:56 +00:00
// Check the rootPaths of this backend
2018-03-16 17:35:19 +00:00
rootPaths := re . rootPaths . Load ( ) . ( * radix . Tree )
match , raw , ok := rootPaths . LongestPrefix ( remain )
2015-03-06 01:23:56 +00:00
if ! ok {
return false
}
prefixMatch := raw . ( bool )
// Handle the prefix match case
if prefixMatch {
return strings . HasPrefix ( remain , match )
}
// Handle the exact match case
return match == remain
}
2015-03-23 18:47:55 +00:00
// LoginPath checks if the given path is used for logins
2021-10-13 16:51:20 +00:00
// Matching Priority
// 1. prefix
// 2. exact
// 3. wildcard
2018-09-18 03:03:00 +00:00
func ( r * Router ) LoginPath ( ctx context . Context , path string ) bool {
ns , err := namespace . FromContext ( ctx )
if err != nil {
return false
}
adjustedPath := ns . Path + path
2015-03-23 18:47:55 +00:00
r . l . RLock ( )
2018-09-18 03:03:00 +00:00
mount , raw , ok := r . root . LongestPrefix ( adjustedPath )
2015-03-23 18:47:55 +00:00
r . l . RUnlock ( )
if ! ok {
return false
}
2015-09-04 20:58:12 +00:00
re := raw . ( * routeEntry )
2015-03-23 18:47:55 +00:00
// Trim to get remaining path
2018-09-18 03:03:00 +00:00
remain := strings . TrimPrefix ( adjustedPath , mount )
2015-03-23 18:47:55 +00:00
// Check the loginPaths of this backend
2021-10-13 16:51:20 +00:00
pe := re . loginPaths . Load ( ) . ( * loginPathsEntry )
match , raw , ok := pe . paths . LongestPrefix ( remain )
if ! ok && len ( pe . wildcardPaths ) == 0 {
// no match found
2015-03-23 18:47:55 +00:00
return false
}
2021-10-13 16:51:20 +00:00
if ok {
prefixMatch := raw . ( bool )
if prefixMatch {
// Handle the prefix match case
return strings . HasPrefix ( remain , match )
}
if match == remain {
// Handle the exact match case
return true
}
2015-03-23 18:47:55 +00:00
}
2021-10-13 16:51:20 +00:00
// check Login Paths containing wildcards
reqPathParts := strings . Split ( remain , "/" )
for _ , w := range pe . wildcardPaths {
if pathMatchesWildcardPath ( reqPathParts , w . segments , w . isPrefix ) {
return true
}
}
return false
}
// pathMatchesWildcardPath returns true if the path made up of the path slice
// matches the given wildcard path slice
func pathMatchesWildcardPath ( path , wcPath [ ] string , isPrefix bool ) bool {
if len ( wcPath ) == 0 {
return false
}
if len ( path ) < len ( wcPath ) {
// check if the path coming in is shorter; if so it can't match
return false
}
if ! isPrefix && len ( wcPath ) != len ( path ) {
// If it's not a prefix we expect the same number of segments
return false
}
for i , wcPathPart := range wcPath {
switch {
case wcPathPart == "+" :
case wcPathPart == path [ i ] :
case isPrefix && i == len ( wcPath ) - 1 && strings . HasPrefix ( path [ i ] , wcPathPart ) :
default :
// we encountered segments that did not match
return false
}
}
return true
}
func wildcardError ( path , msg string ) error {
return fmt . Errorf ( "path %q: invalid use of wildcards %s" , path , msg )
}
func isValidUnauthenticatedPath ( path string ) ( bool , error ) {
switch {
case strings . Count ( path , "*" ) > 1 :
return false , wildcardError ( path , "(multiple '*' is forbidden)" )
case strings . Contains ( path , "+*" ) :
return false , wildcardError ( path , "('+*' is forbidden)" )
case strings . Contains ( path , "*" ) && path [ len ( path ) - 1 ] != '*' :
return false , wildcardError ( path , "('*' is only allowed at the end of a path)" )
case wcAdjacentNonSlashRegEx ( path ) :
return false , wildcardError ( path , "('+' is not allowed next to a non-slash)" )
}
return true , nil
}
// parseUnauthenticatedPaths converts a list of special paths to a
// loginPathsEntry
func parseUnauthenticatedPaths ( paths [ ] string ) ( * loginPathsEntry , error ) {
var tempPaths [ ] string
tempWildcardPaths := make ( [ ] wildcardPath , 0 )
for _ , path := range paths {
if ok , err := isValidUnauthenticatedPath ( path ) ; ! ok {
return nil , err
}
if strings . Contains ( path , "+" ) {
// Paths with wildcards are not stored in the radix tree because
// the radix tree does not handle wildcards in the middle of strings.
isPrefix := false
if path [ len ( path ) - 1 ] == '*' {
isPrefix = true
path = path [ 0 : len ( path ) - 1 ]
}
// We are micro-optimizing by storing pre-split slices of path segments
wcPath := wildcardPath { segments : strings . Split ( path , "/" ) , isPrefix : isPrefix }
tempWildcardPaths = append ( tempWildcardPaths , wcPath )
} else {
// accumulate paths that do not contain wildcards
// to be stored in the radix tree
tempPaths = append ( tempPaths , path )
}
}
return & loginPathsEntry {
paths : pathsToRadix ( tempPaths ) ,
wildcardPaths : tempWildcardPaths ,
} , nil
2015-03-23 18:47:55 +00:00
}
2019-02-18 18:05:04 +00:00
// pathsToRadix converts a list of special paths to a radix tree.
2015-03-23 18:47:55 +00:00
func pathsToRadix ( paths [ ] string ) * radix . Tree {
tree := radix . New ( )
for _ , path := range paths {
// Check if this is a prefix or exact match
prefixMatch := len ( path ) >= 1 && path [ len ( path ) - 1 ] == '*'
if prefixMatch {
path = path [ : len ( path ) - 1 ]
}
2015-03-31 00:46:18 +00:00
2015-03-23 18:47:55 +00:00
tree . Insert ( path , prefixMatch )
}
2015-03-31 00:46:18 +00:00
2015-03-23 18:47:55 +00:00
return tree
}
2018-03-21 23:56:47 +00:00
2019-02-05 21:02:15 +00:00
// filteredHeaders returns a headers map[string][]string that
// contains the filtered values contained in candidateHeaders. Filtering of
// candidateHeaders from the origHeaders is done is a case-insensitive manner.
// Headers that match values from deniedHeaders will be ignored.
func filteredHeaders ( origHeaders map [ string ] [ ] string , candidateHeaders , deniedHeaders [ ] string ) map [ string ] [ ] string {
2018-03-21 23:56:47 +00:00
// Short-circuit if there's nothing to filter
2019-02-05 21:02:15 +00:00
if len ( candidateHeaders ) == 0 {
return nil
2018-03-21 23:56:47 +00:00
}
2019-02-05 21:02:15 +00:00
retHeaders := make ( map [ string ] [ ] string , len ( origHeaders ) )
// Filter candidateHeaders values through deniedHeaders first. Returns the
// lowercased complement set. We call even if no denied headers to get the
// values lowercased.
allowedCandidateHeaders := strutil . Difference ( candidateHeaders , deniedHeaders , true )
2018-10-01 19:20:31 +00:00
2018-03-21 23:56:47 +00:00
// Create a map that uses lowercased header values as the key and the original
// header naming as the value for comparison down below.
2019-02-05 21:02:15 +00:00
lowerOrigHeaderKeys := make ( map [ string ] string , len ( origHeaders ) )
2018-03-21 23:56:47 +00:00
for key := range origHeaders {
2019-02-05 21:02:15 +00:00
lowerOrigHeaderKeys [ strings . ToLower ( key ) ] = key
2018-03-21 23:56:47 +00:00
}
// Case-insensitive compare of passthrough headers against originating
// headers. The returned headers will be the same casing as the originating
// header name.
2019-02-05 21:02:15 +00:00
for _ , ch := range allowedCandidateHeaders {
if header , ok := lowerOrigHeaderKeys [ ch ] ; ok {
2018-03-21 23:56:47 +00:00
retHeaders [ header ] = origHeaders [ header ]
}
}
return retHeaders
}