1package vault
2
3import (
4	"context"
5	"crypto/sha256"
6	"crypto/sha512"
7	"encoding/base64"
8	"encoding/hex"
9	"encoding/json"
10	"errors"
11	"fmt"
12	"hash"
13	"net/http"
14	"path/filepath"
15	"sort"
16	"strconv"
17	"strings"
18	"sync"
19	"time"
20
21	"github.com/hashicorp/vault/physical/raft"
22
23	"github.com/hashicorp/errwrap"
24	log "github.com/hashicorp/go-hclog"
25	memdb "github.com/hashicorp/go-memdb"
26	uuid "github.com/hashicorp/go-uuid"
27	"github.com/hashicorp/vault/helper/identity"
28	"github.com/hashicorp/vault/helper/metricsutil"
29	"github.com/hashicorp/vault/helper/namespace"
30	"github.com/hashicorp/vault/sdk/framework"
31	"github.com/hashicorp/vault/sdk/helper/compressutil"
32	"github.com/hashicorp/vault/sdk/helper/consts"
33	"github.com/hashicorp/vault/sdk/helper/jsonutil"
34	"github.com/hashicorp/vault/sdk/helper/parseutil"
35	"github.com/hashicorp/vault/sdk/helper/strutil"
36	"github.com/hashicorp/vault/sdk/helper/wrapping"
37	"github.com/hashicorp/vault/sdk/logical"
38	"github.com/mitchellh/mapstructure"
39)
40
41var (
42	// protectedPaths cannot be accessed via the raw APIs.
43	// This is both for security and to prevent disrupting Vault.
44	protectedPaths = []string{
45		keyringPath,
46		// Changing the cluster info path can change the cluster ID which can be disruptive
47		coreLocalClusterInfoPath,
48	}
49)
50
51const maxBytes = 128 * 1024
52
53func systemBackendMemDBSchema() *memdb.DBSchema {
54	systemSchema := &memdb.DBSchema{
55		Tables: make(map[string]*memdb.TableSchema),
56	}
57
58	schemas := getSystemSchemas()
59
60	for _, schemaFunc := range schemas {
61		schema := schemaFunc()
62		if _, ok := systemSchema.Tables[schema.Name]; ok {
63			panic(fmt.Sprintf("duplicate table name: %s", schema.Name))
64		}
65		systemSchema.Tables[schema.Name] = schema
66	}
67
68	return systemSchema
69}
70
71func NewSystemBackend(core *Core, logger log.Logger) *SystemBackend {
72	db, _ := memdb.NewMemDB(systemBackendMemDBSchema())
73
74	b := &SystemBackend{
75		Core:      core,
76		db:        db,
77		logger:    logger,
78		mfaLogger: core.baseLogger.Named("mfa"),
79		mfaLock:   &sync.RWMutex{},
80	}
81
82	core.AddLogger(b.mfaLogger)
83
84	b.Backend = &framework.Backend{
85		Help: strings.TrimSpace(sysHelpRoot),
86
87		PathsSpecial: &logical.Paths{
88			Root: []string{
89				"auth/*",
90				"remount",
91				"audit",
92				"audit/*",
93				"raw",
94				"raw/*",
95				"replication/primary/secondary-token",
96				"replication/performance/primary/secondary-token",
97				"replication/dr/primary/secondary-token",
98				"replication/reindex",
99				"replication/dr/reindex",
100				"replication/performance/reindex",
101				"rotate",
102				"config/cors",
103				"config/auditing/*",
104				"config/ui/headers/*",
105				"plugins/catalog/*",
106				"revoke-prefix/*",
107				"revoke-force/*",
108				"leases/revoke-prefix/*",
109				"leases/revoke-force/*",
110				"leases/lookup/*",
111			},
112
113			Unauthenticated: []string{
114				"wrapping/lookup",
115				"wrapping/pubkey",
116				"replication/status",
117				"internal/specs/openapi",
118				"internal/ui/mounts",
119				"internal/ui/mounts/*",
120				"internal/ui/namespaces",
121				"replication/performance/status",
122				"replication/dr/status",
123				"replication/dr/secondary/promote",
124				"replication/dr/secondary/update-primary",
125				"replication/dr/secondary/operation-token/delete",
126				"replication/dr/secondary/license",
127				"replication/dr/secondary/reindex",
128				"storage/raft/bootstrap/challenge",
129				"storage/raft/bootstrap/answer",
130				"init",
131				"seal-status",
132				"unseal",
133				"leader",
134				"health",
135				"generate-root/attempt",
136				"generate-root/update",
137				"rekey/init",
138				"rekey/update",
139				"rekey/verify",
140				"rekey-recovery-key/init",
141				"rekey-recovery-key/update",
142				"rekey-recovery-key/verify",
143			},
144
145			LocalStorage: []string{
146				expirationSubPath,
147			},
148		},
149	}
150
151	b.Backend.Paths = append(b.Backend.Paths, entPaths(b)...)
152	b.Backend.Paths = append(b.Backend.Paths, b.configPaths()...)
153	b.Backend.Paths = append(b.Backend.Paths, b.rekeyPaths()...)
154	b.Backend.Paths = append(b.Backend.Paths, b.sealPaths()...)
155	b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogListPaths()...)
156	b.Backend.Paths = append(b.Backend.Paths, b.pluginsCatalogCRUDPath())
157	b.Backend.Paths = append(b.Backend.Paths, b.pluginsReloadPath())
158	b.Backend.Paths = append(b.Backend.Paths, b.auditPaths()...)
159	b.Backend.Paths = append(b.Backend.Paths, b.mountPaths()...)
160	b.Backend.Paths = append(b.Backend.Paths, b.authPaths()...)
161	b.Backend.Paths = append(b.Backend.Paths, b.leasePaths()...)
162	b.Backend.Paths = append(b.Backend.Paths, b.policyPaths()...)
163	b.Backend.Paths = append(b.Backend.Paths, b.wrappingPaths()...)
164	b.Backend.Paths = append(b.Backend.Paths, b.toolsPaths()...)
165	b.Backend.Paths = append(b.Backend.Paths, b.capabilitiesPaths()...)
166	b.Backend.Paths = append(b.Backend.Paths, b.internalPaths()...)
167	b.Backend.Paths = append(b.Backend.Paths, b.remountPath())
168	b.Backend.Paths = append(b.Backend.Paths, b.metricsPath())
169
170	if core.rawEnabled {
171		b.Backend.Paths = append(b.Backend.Paths, &framework.Path{
172			Pattern: "(raw/?$|raw/(?P<path>.+))",
173
174			Fields: map[string]*framework.FieldSchema{
175				"path": &framework.FieldSchema{
176					Type: framework.TypeString,
177				},
178				"value": &framework.FieldSchema{
179					Type: framework.TypeString,
180				},
181			},
182
183			Operations: map[logical.Operation]framework.OperationHandler{
184				logical.ReadOperation: &framework.PathOperation{
185					Callback: b.handleRawRead,
186					Summary:  "Read the value of the key at the given path.",
187				},
188				logical.UpdateOperation: &framework.PathOperation{
189					Callback: b.handleRawWrite,
190					Summary:  "Update the value of the key at the given path.",
191				},
192				logical.DeleteOperation: &framework.PathOperation{
193					Callback: b.handleRawDelete,
194					Summary:  "Delete the key with given path.",
195				},
196				logical.ListOperation: &framework.PathOperation{
197					Callback: b.handleRawList,
198					Summary:  "Return a list keys for a given path prefix.",
199				},
200			},
201
202			HelpSynopsis:    strings.TrimSpace(sysHelp["raw"][0]),
203			HelpDescription: strings.TrimSpace(sysHelp["raw"][1]),
204		})
205	}
206
207	if _, ok := core.underlyingPhysical.(*raft.RaftBackend); ok {
208		b.Backend.Paths = append(b.Backend.Paths, b.raftStoragePaths()...)
209	}
210
211	b.Backend.Invalidate = sysInvalidate(b)
212	return b
213}
214
215// SystemBackend implements logical.Backend and is used to interact with
216// the core of the system. This backend is hardcoded to exist at the "sys"
217// prefix. Conceptually it is similar to procfs on Linux.
218type SystemBackend struct {
219	*framework.Backend
220	Core      *Core
221	db        *memdb.MemDB
222	mfaLock   *sync.RWMutex
223	mfaLogger log.Logger
224	logger    log.Logger
225}
226
227// handleCORSRead returns the current CORS configuration
228func (b *SystemBackend) handleCORSRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
229	corsConf := b.Core.corsConfig
230
231	enabled := corsConf.IsEnabled()
232
233	resp := &logical.Response{
234		Data: map[string]interface{}{
235			"enabled": enabled,
236		},
237	}
238
239	if enabled {
240		corsConf.RLock()
241		resp.Data["allowed_origins"] = corsConf.AllowedOrigins
242		resp.Data["allowed_headers"] = corsConf.AllowedHeaders
243		corsConf.RUnlock()
244	}
245
246	return resp, nil
247}
248
249// handleCORSUpdate sets the list of origins that are allowed to make
250// cross-origin requests and sets the CORS enabled flag to true
251func (b *SystemBackend) handleCORSUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
252	origins := d.Get("allowed_origins").([]string)
253	headers := d.Get("allowed_headers").([]string)
254
255	return nil, b.Core.corsConfig.Enable(ctx, origins, headers)
256}
257
258// handleCORSDelete sets the CORS enabled flag to false and clears the list of
259// allowed origins & headers.
260func (b *SystemBackend) handleCORSDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
261	return nil, b.Core.corsConfig.Disable(ctx)
262}
263
264func (b *SystemBackend) handleTidyLeases(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
265	ns, err := namespace.FromContext(ctx)
266	if err != nil {
267		return nil, err
268	}
269
270	go func() {
271		tidyCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns)
272		err := b.Core.expiration.Tidy(tidyCtx)
273		if err != nil {
274			b.Backend.Logger().Error("failed to tidy leases", "error", err)
275			return
276		}
277	}()
278
279	resp := &logical.Response{}
280	resp.AddWarning("Tidy operation successfully started. Any information from the operation will be printed to Vault's server logs.")
281	return logical.RespondWithStatusCode(resp, req, http.StatusAccepted)
282}
283
284func (b *SystemBackend) handlePluginCatalogTypedList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
285	pluginType, err := consts.ParsePluginType(d.Get("type").(string))
286	if err != nil {
287		return nil, err
288	}
289
290	plugins, err := b.Core.pluginCatalog.List(ctx, pluginType)
291	if err != nil {
292		return nil, err
293	}
294	return logical.ListResponse(plugins), nil
295}
296
297func (b *SystemBackend) handlePluginCatalogUntypedList(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
298	pluginsByType := make(map[string]interface{})
299	for _, pluginType := range consts.PluginTypes {
300		plugins, err := b.Core.pluginCatalog.List(ctx, pluginType)
301		if err != nil {
302			return nil, err
303		}
304		if len(plugins) > 0 {
305			sort.Strings(plugins)
306			pluginsByType[pluginType.String()] = plugins
307		}
308	}
309	return &logical.Response{
310		Data: pluginsByType,
311	}, nil
312}
313
314func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
315	pluginName := d.Get("name").(string)
316	if pluginName == "" {
317		return logical.ErrorResponse("missing plugin name"), nil
318	}
319
320	pluginTypeStr := d.Get("type").(string)
321	if pluginTypeStr == "" {
322		// If the plugin type is not provided, list it as unknown so that we
323		// add it to the catalog and UpdatePlugins later will sort it.
324		pluginTypeStr = "unknown"
325	}
326	pluginType, err := consts.ParsePluginType(pluginTypeStr)
327	if err != nil {
328		return nil, err
329	}
330
331	sha256 := d.Get("sha256").(string)
332	if sha256 == "" {
333		sha256 = d.Get("sha_256").(string)
334		if sha256 == "" {
335			return logical.ErrorResponse("missing SHA-256 value"), nil
336		}
337	}
338
339	command := d.Get("command").(string)
340	if command == "" {
341		return logical.ErrorResponse("missing command value"), nil
342	}
343
344	// For backwards compatibility, also accept args as part of command. Don't
345	// accepts args in both command and args.
346	args := d.Get("args").([]string)
347	parts := strings.Split(command, " ")
348	if len(parts) <= 0 {
349		return logical.ErrorResponse("missing command value"), nil
350	} else if len(parts) > 1 && len(args) > 0 {
351		return logical.ErrorResponse("must not specify args in command and args field"), nil
352	} else if len(parts) > 1 {
353		args = parts[1:]
354	}
355
356	env := d.Get("env").([]string)
357
358	sha256Bytes, err := hex.DecodeString(sha256)
359	if err != nil {
360		return logical.ErrorResponse("Could not decode SHA-256 value from Hex"), err
361	}
362
363	err = b.Core.pluginCatalog.Set(ctx, pluginName, pluginType, parts[0], args, env, sha256Bytes)
364	if err != nil {
365		return nil, err
366	}
367
368	return nil, nil
369}
370
371func (b *SystemBackend) handlePluginCatalogRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
372	pluginName := d.Get("name").(string)
373	if pluginName == "" {
374		return logical.ErrorResponse("missing plugin name"), nil
375	}
376
377	pluginTypeStr := d.Get("type").(string)
378	if pluginTypeStr == "" {
379		// If the plugin type is not provided (i.e. the old
380		// sys/plugins/catalog/:name endpoint is being requested) short-circuit here
381		// and return a warning
382		resp := &logical.Response{}
383		resp.AddWarning(fmt.Sprintf("Deprecated API endpoint, cannot read plugin information from catalog for %q", pluginName))
384		return resp, nil
385	}
386
387	pluginType, err := consts.ParsePluginType(pluginTypeStr)
388	if err != nil {
389		return nil, err
390	}
391
392	plugin, err := b.Core.pluginCatalog.Get(ctx, pluginName, pluginType)
393	if err != nil {
394		return nil, err
395	}
396	if plugin == nil {
397		return nil, nil
398	}
399
400	command := ""
401	if !plugin.Builtin {
402		command, err = filepath.Rel(b.Core.pluginCatalog.directory, plugin.Command)
403		if err != nil {
404			return nil, err
405		}
406	}
407
408	data := map[string]interface{}{
409		"name":    plugin.Name,
410		"args":    plugin.Args,
411		"command": command,
412		"sha256":  hex.EncodeToString(plugin.Sha256),
413		"builtin": plugin.Builtin,
414	}
415
416	return &logical.Response{
417		Data: data,
418	}, nil
419}
420
421func (b *SystemBackend) handlePluginCatalogDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
422	pluginName := d.Get("name").(string)
423	if pluginName == "" {
424		return logical.ErrorResponse("missing plugin name"), nil
425	}
426
427	var resp *logical.Response
428	pluginTypeStr := d.Get("type").(string)
429	if pluginTypeStr == "" {
430		// If the plugin type is not provided (i.e. the old
431		// sys/plugins/catalog/:name endpoint is being requested), set type to
432		// unknown and let pluginCatalog.Delete proceed. It should handle
433		// deregistering out of the old storage path (root of core/plugin-catalog)
434		resp = new(logical.Response)
435		resp.AddWarning(fmt.Sprintf("Deprecated API endpoint, cannot deregister plugin from catalog for %q", pluginName))
436		pluginTypeStr = "unknown"
437	}
438
439	pluginType, err := consts.ParsePluginType(pluginTypeStr)
440	if err != nil {
441		return nil, err
442	}
443	if err := b.Core.pluginCatalog.Delete(ctx, pluginName, pluginType); err != nil {
444		return nil, err
445	}
446
447	return resp, nil
448}
449
450func (b *SystemBackend) handlePluginReloadUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
451	pluginName := d.Get("plugin").(string)
452	pluginMounts := d.Get("mounts").([]string)
453
454	if pluginName != "" && len(pluginMounts) > 0 {
455		return logical.ErrorResponse("plugin and mounts cannot be set at the same time"), nil
456	}
457	if pluginName == "" && len(pluginMounts) == 0 {
458		return logical.ErrorResponse("plugin or mounts must be provided"), nil
459	}
460
461	if pluginName != "" {
462		err := b.Core.reloadMatchingPlugin(ctx, pluginName)
463		if err != nil {
464			return nil, err
465		}
466	} else if len(pluginMounts) > 0 {
467		err := b.Core.reloadMatchingPluginMounts(ctx, pluginMounts)
468		if err != nil {
469			return nil, err
470		}
471	}
472
473	return nil, nil
474}
475
476// handleAuditedHeaderUpdate creates or overwrites a header entry
477func (b *SystemBackend) handleAuditedHeaderUpdate(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
478	header := d.Get("header").(string)
479	hmac := d.Get("hmac").(bool)
480	if header == "" {
481		return logical.ErrorResponse("missing header name"), nil
482	}
483
484	headerConfig := b.Core.AuditedHeadersConfig()
485	err := headerConfig.add(ctx, header, hmac)
486	if err != nil {
487		return nil, err
488	}
489
490	return nil, nil
491}
492
493// handleAuditedHeaderDelete deletes the header with the given name
494func (b *SystemBackend) handleAuditedHeaderDelete(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
495	header := d.Get("header").(string)
496	if header == "" {
497		return logical.ErrorResponse("missing header name"), nil
498	}
499
500	headerConfig := b.Core.AuditedHeadersConfig()
501	err := headerConfig.remove(ctx, header)
502	if err != nil {
503		return nil, err
504	}
505
506	return nil, nil
507}
508
509// handleAuditedHeaderRead returns the header configuration for the given header name
510func (b *SystemBackend) handleAuditedHeaderRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
511	header := d.Get("header").(string)
512	if header == "" {
513		return logical.ErrorResponse("missing header name"), nil
514	}
515
516	headerConfig := b.Core.AuditedHeadersConfig()
517	settings, ok := headerConfig.Headers[strings.ToLower(header)]
518	if !ok {
519		return logical.ErrorResponse("Could not find header in config"), nil
520	}
521
522	return &logical.Response{
523		Data: map[string]interface{}{
524			header: settings,
525		},
526	}, nil
527}
528
529// handleAuditedHeadersRead returns the whole audited headers config
530func (b *SystemBackend) handleAuditedHeadersRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
531	headerConfig := b.Core.AuditedHeadersConfig()
532
533	return &logical.Response{
534		Data: map[string]interface{}{
535			"headers": headerConfig.Headers,
536		},
537	}, nil
538}
539
540// handleCapabilitiesAccessor returns the ACL capabilities of the
541// token associated with the given accessor for a given path.
542func (b *SystemBackend) handleCapabilitiesAccessor(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
543	accessor := d.Get("accessor").(string)
544	if accessor == "" {
545		return logical.ErrorResponse("missing accessor"), nil
546	}
547
548	aEntry, err := b.Core.tokenStore.lookupByAccessor(ctx, accessor, false, false)
549	if err != nil {
550		return nil, err
551	}
552
553	d.Raw["token"] = aEntry.TokenID
554	return b.handleCapabilities(ctx, req, d)
555}
556
557// handleCapabilities returns the ACL capabilities of the token for a given path
558func (b *SystemBackend) handleCapabilities(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
559	var token string
560	if strings.HasSuffix(req.Path, "capabilities-self") {
561		token = req.ClientToken
562	} else {
563		tokenRaw, ok := d.Raw["token"]
564		if ok {
565			token, _ = tokenRaw.(string)
566		}
567	}
568	if token == "" {
569		return nil, fmt.Errorf("no token found")
570	}
571
572	ret := &logical.Response{
573		Data: map[string]interface{}{},
574	}
575
576	paths := d.Get("paths").([]string)
577	if len(paths) == 0 {
578		// Read from the deprecated field
579		paths = d.Get("path").([]string)
580	}
581
582	if len(paths) == 0 {
583		return logical.ErrorResponse("paths must be supplied"), nil
584	}
585
586	for _, path := range paths {
587		pathCap, err := b.Core.Capabilities(ctx, token, path)
588		if err != nil {
589			if !strings.HasSuffix(req.Path, "capabilities-self") && errwrap.Contains(err, logical.ErrPermissionDenied.Error()) {
590				return nil, &logical.StatusBadRequest{Err: "invalid token"}
591			}
592			return nil, err
593		}
594		ret.Data[path] = pathCap
595	}
596
597	// This is only here for backwards compatibility
598	if len(paths) == 1 {
599		ret.Data["capabilities"] = ret.Data[paths[0]]
600	}
601
602	return ret, nil
603}
604
605// handleRekeyRetrieve returns backed-up, PGP-encrypted unseal keys from a
606// rekey operation
607func (b *SystemBackend) handleRekeyRetrieve(
608	ctx context.Context,
609	req *logical.Request,
610	data *framework.FieldData,
611	recovery bool) (*logical.Response, error) {
612	backup, err := b.Core.RekeyRetrieveBackup(ctx, recovery)
613	if err != nil {
614		return nil, errwrap.Wrapf("unable to look up backed-up keys: {{err}}", err)
615	}
616	if backup == nil {
617		return logical.ErrorResponse("no backed-up keys found"), nil
618	}
619
620	keysB64 := map[string][]string{}
621	for k, v := range backup.Keys {
622		for _, j := range v {
623			currB64Keys := keysB64[k]
624			if currB64Keys == nil {
625				currB64Keys = []string{}
626			}
627			key, err := hex.DecodeString(j)
628			if err != nil {
629				return nil, errwrap.Wrapf("error decoding hex-encoded backup key: {{err}}", err)
630			}
631			currB64Keys = append(currB64Keys, base64.StdEncoding.EncodeToString(key))
632			keysB64[k] = currB64Keys
633		}
634	}
635
636	// Format the status
637	resp := &logical.Response{
638		Data: map[string]interface{}{
639			"nonce":       backup.Nonce,
640			"keys":        backup.Keys,
641			"keys_base64": keysB64,
642		},
643	}
644
645	return resp, nil
646}
647
648func (b *SystemBackend) handleRekeyRetrieveBarrier(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
649	return b.handleRekeyRetrieve(ctx, req, data, false)
650}
651
652func (b *SystemBackend) handleRekeyRetrieveRecovery(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
653	return b.handleRekeyRetrieve(ctx, req, data, true)
654}
655
656// handleRekeyDelete deletes backed-up, PGP-encrypted unseal keys from a rekey
657// operation
658func (b *SystemBackend) handleRekeyDelete(
659	ctx context.Context,
660	req *logical.Request,
661	data *framework.FieldData,
662	recovery bool) (*logical.Response, error) {
663	err := b.Core.RekeyDeleteBackup(ctx, recovery)
664	if err != nil {
665		return nil, errwrap.Wrapf("error during deletion of backed-up keys: {{err}}", err)
666	}
667
668	return nil, nil
669}
670
671func (b *SystemBackend) handleRekeyDeleteBarrier(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
672	return b.handleRekeyDelete(ctx, req, data, false)
673}
674
675func (b *SystemBackend) handleRekeyDeleteRecovery(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
676	return b.handleRekeyDelete(ctx, req, data, true)
677}
678
679func mountInfo(entry *MountEntry) map[string]interface{} {
680	info := map[string]interface{}{
681		"type":        entry.Type,
682		"description": entry.Description,
683		"accessor":    entry.Accessor,
684		"local":       entry.Local,
685		"seal_wrap":   entry.SealWrap,
686		"options":     entry.Options,
687		"uuid":        entry.UUID,
688	}
689	entryConfig := map[string]interface{}{
690		"default_lease_ttl": int64(entry.Config.DefaultLeaseTTL.Seconds()),
691		"max_lease_ttl":     int64(entry.Config.MaxLeaseTTL.Seconds()),
692		"force_no_cache":    entry.Config.ForceNoCache,
693	}
694	if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
695		entryConfig["audit_non_hmac_request_keys"] = rawVal.([]string)
696	}
697	if rawVal, ok := entry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok {
698		entryConfig["audit_non_hmac_response_keys"] = rawVal.([]string)
699	}
700	// Even though empty value is valid for ListingVisibility, we can ignore
701	// this case during mount since there's nothing to unset/hide.
702	if len(entry.Config.ListingVisibility) > 0 {
703		entryConfig["listing_visibility"] = entry.Config.ListingVisibility
704	}
705	if rawVal, ok := entry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
706		entryConfig["passthrough_request_headers"] = rawVal.([]string)
707	}
708	if rawVal, ok := entry.synthesizedConfigCache.Load("allowed_response_headers"); ok {
709		entryConfig["allowed_response_headers"] = rawVal.([]string)
710	}
711	if entry.Table == credentialTableType {
712		entryConfig["token_type"] = entry.Config.TokenType.String()
713	}
714
715	info["config"] = entryConfig
716
717	return info
718}
719
720// handleMountTable handles the "mounts" endpoint to provide the mount table
721func (b *SystemBackend) handleMountTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
722	ns, err := namespace.FromContext(ctx)
723	if err != nil {
724		return nil, err
725	}
726
727	b.Core.mountsLock.RLock()
728	defer b.Core.mountsLock.RUnlock()
729
730	resp := &logical.Response{
731		Data: make(map[string]interface{}),
732	}
733
734	for _, entry := range b.Core.mounts.Entries {
735		// Only show entries for current namespace
736		if entry.Namespace().Path != ns.Path {
737			continue
738		}
739
740		cont, err := b.Core.checkReplicatedFiltering(ctx, entry, "")
741		if err != nil {
742			return nil, err
743		}
744		if cont {
745			continue
746		}
747
748		// Populate mount info
749		info := mountInfo(entry)
750		resp.Data[entry.Path] = info
751	}
752
753	return resp, nil
754}
755
756// handleMount is used to mount a new path
757func (b *SystemBackend) handleMount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
758	repState := b.Core.ReplicationState()
759
760	local := data.Get("local").(bool)
761	// If we are a performance secondary cluster we should forward the request
762	// to the primary. We fail early here since the view in use isn't marked as
763	// readonly
764	if !local && repState.HasState(consts.ReplicationPerformanceSecondary) {
765		return nil, logical.ErrReadOnly
766	}
767
768	// Get all the options
769	path := data.Get("path").(string)
770	path = sanitizeMountPath(path)
771
772	logicalType := data.Get("type").(string)
773	description := data.Get("description").(string)
774	pluginName := data.Get("plugin_name").(string)
775	sealWrap := data.Get("seal_wrap").(bool)
776	options := data.Get("options").(map[string]string)
777
778	var config MountConfig
779	var apiConfig APIMountConfig
780
781	configMap := data.Get("config").(map[string]interface{})
782	if configMap != nil && len(configMap) != 0 {
783		err := mapstructure.Decode(configMap, &apiConfig)
784		if err != nil {
785			return logical.ErrorResponse(
786					"unable to convert given mount config information"),
787				logical.ErrInvalidRequest
788		}
789	}
790
791	switch apiConfig.DefaultLeaseTTL {
792	case "":
793	case "system":
794	default:
795		tmpDef, err := parseutil.ParseDurationSecond(apiConfig.DefaultLeaseTTL)
796		if err != nil {
797			return logical.ErrorResponse(fmt.Sprintf(
798					"unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)),
799				logical.ErrInvalidRequest
800		}
801		config.DefaultLeaseTTL = tmpDef
802	}
803
804	switch apiConfig.MaxLeaseTTL {
805	case "":
806	case "system":
807	default:
808		tmpMax, err := parseutil.ParseDurationSecond(apiConfig.MaxLeaseTTL)
809		if err != nil {
810			return logical.ErrorResponse(fmt.Sprintf(
811					"unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)),
812				logical.ErrInvalidRequest
813		}
814		config.MaxLeaseTTL = tmpMax
815	}
816
817	if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL {
818		return logical.ErrorResponse(
819				"given default lease TTL greater than given max lease TTL"),
820			logical.ErrInvalidRequest
821	}
822
823	if config.DefaultLeaseTTL > b.Core.maxLeaseTTL && config.MaxLeaseTTL == 0 {
824		return logical.ErrorResponse(fmt.Sprintf(
825				"given default lease TTL greater than system max lease TTL of %d", int(b.Core.maxLeaseTTL.Seconds()))),
826			logical.ErrInvalidRequest
827	}
828
829	switch logicalType {
830	case "":
831		return logical.ErrorResponse(
832				"backend type must be specified as a string"),
833			logical.ErrInvalidRequest
834	case "plugin":
835		// Only set plugin-name if mount is of type plugin, with apiConfig.PluginName
836		// option taking precedence.
837		switch {
838		case apiConfig.PluginName != "":
839			logicalType = apiConfig.PluginName
840		case pluginName != "":
841			logicalType = pluginName
842		default:
843			return logical.ErrorResponse(
844					"plugin_name must be provided for plugin backend"),
845				logical.ErrInvalidRequest
846		}
847	}
848
849	switch logicalType {
850	case "kv":
851	case "kv-v1":
852		// Alias KV v1
853		logicalType = "kv"
854		if options == nil {
855			options = map[string]string{}
856		}
857		options["version"] = "1"
858
859	case "kv-v2":
860		// Alias KV v2
861		logicalType = "kv"
862		if options == nil {
863			options = map[string]string{}
864		}
865		options["version"] = "2"
866
867	default:
868		if options != nil && options["version"] != "" {
869			return logical.ErrorResponse(fmt.Sprintf(
870					"secrets engine %q does not allow setting a version", logicalType)),
871				logical.ErrInvalidRequest
872		}
873	}
874
875	// Copy over the force no cache if set
876	if apiConfig.ForceNoCache {
877		config.ForceNoCache = true
878	}
879
880	if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil {
881		return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil
882	}
883	config.ListingVisibility = apiConfig.ListingVisibility
884
885	if len(apiConfig.AuditNonHMACRequestKeys) > 0 {
886		config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys
887	}
888	if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
889		config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
890	}
891	if len(apiConfig.PassthroughRequestHeaders) > 0 {
892		config.PassthroughRequestHeaders = apiConfig.PassthroughRequestHeaders
893	}
894	if len(apiConfig.AllowedResponseHeaders) > 0 {
895		config.AllowedResponseHeaders = apiConfig.AllowedResponseHeaders
896	}
897
898	// Create the mount entry
899	me := &MountEntry{
900		Table:       mountTableType,
901		Path:        path,
902		Type:        logicalType,
903		Description: description,
904		Config:      config,
905		Local:       local,
906		SealWrap:    sealWrap,
907		Options:     options,
908	}
909
910	// Attempt mount
911	if err := b.Core.mount(ctx, me); err != nil {
912		b.Backend.Logger().Error("mount failed", "path", me.Path, "error", err)
913		return handleError(err)
914	}
915
916	return nil, nil
917}
918
919// used to intercept an HTTPCodedError so it goes back to callee
920func handleError(
921	err error) (*logical.Response, error) {
922	if strings.Contains(err.Error(), logical.ErrReadOnly.Error()) {
923		return logical.ErrorResponse(err.Error()), err
924	}
925	switch err.(type) {
926	case logical.HTTPCodedError:
927		return logical.ErrorResponse(err.Error()), err
928	default:
929		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
930	}
931}
932
933// Performs a similar function to handleError, but upon seeing a ReadOnlyError
934// will actually strip it out to prevent forwarding
935func handleErrorNoReadOnlyForward(
936	err error) (*logical.Response, error) {
937	if strings.Contains(err.Error(), logical.ErrReadOnly.Error()) {
938		return nil, fmt.Errorf("operation could not be completed as storage is read-only")
939	}
940	switch err.(type) {
941	case logical.HTTPCodedError:
942		return logical.ErrorResponse(err.Error()), err
943	default:
944		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
945	}
946}
947
948// handleUnmount is used to unmount a path
949func (b *SystemBackend) handleUnmount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
950	path := data.Get("path").(string)
951	path = sanitizeMountPath(path)
952
953	ns, err := namespace.FromContext(ctx)
954	if err != nil {
955		return nil, err
956	}
957
958	repState := b.Core.ReplicationState()
959	entry := b.Core.router.MatchingMountEntry(ctx, path)
960
961	// If we are a performance secondary cluster we should forward the request
962	// to the primary. We fail early here since the view in use isn't marked as
963	// readonly
964	if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) {
965		return nil, logical.ErrReadOnly
966	}
967
968	// We return success when the mount does not exists to not expose if the
969	// mount existed or not
970	match := b.Core.router.MatchingMount(ctx, path)
971	if match == "" || ns.Path+path != match {
972		return nil, nil
973	}
974
975	prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, path)
976	if !found {
977		b.Backend.Logger().Error("unable to find storage for path", "path", path)
978		return handleError(fmt.Errorf("unable to find storage for path: %q", path))
979	}
980
981	// Attempt unmount
982	if err := b.Core.unmount(ctx, path); err != nil {
983		b.Backend.Logger().Error("unmount failed", "path", path, "error", err)
984		return handleError(err)
985	}
986
987	// Remove from filtered mounts
988	if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil {
989		b.Backend.Logger().Error("filtered path removal failed", path, "error", err)
990		return handleError(err)
991	}
992
993	return nil, nil
994}
995
996// handleRemount is used to remount a path
997func (b *SystemBackend) handleRemount(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
998	repState := b.Core.ReplicationState()
999
1000	// Get the paths
1001	fromPath := data.Get("from").(string)
1002	toPath := data.Get("to").(string)
1003	if fromPath == "" || toPath == "" {
1004		return logical.ErrorResponse(
1005				"both 'from' and 'to' path must be specified as a string"),
1006			logical.ErrInvalidRequest
1007	}
1008
1009	entry := b.Core.router.MatchingMountEntry(ctx, fromPath)
1010	// If we are a performance secondary cluster we should forward the request
1011	// to the primary. We fail early here since the view in use isn't marked as
1012	// readonly
1013	if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) {
1014		return nil, logical.ErrReadOnly
1015	}
1016
1017	// Attempt remount
1018	if err := b.Core.remount(ctx, fromPath, toPath); err != nil {
1019		b.Backend.Logger().Error("remount failed", "from_path", fromPath, "to_path", toPath, "error", err)
1020		return handleError(err)
1021	}
1022
1023	return nil, nil
1024}
1025
1026// handleAuthTuneRead is used to get config settings on a auth path
1027func (b *SystemBackend) handleAuthTuneRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1028	path := data.Get("path").(string)
1029	if path == "" {
1030		return logical.ErrorResponse(
1031				"path must be specified as a string"),
1032			logical.ErrInvalidRequest
1033	}
1034	return b.handleTuneReadCommon(ctx, "auth/"+path)
1035}
1036
1037// handleMountTuneRead is used to get config settings on a backend
1038func (b *SystemBackend) handleMountTuneRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1039	path := data.Get("path").(string)
1040	if path == "" {
1041		return logical.ErrorResponse(
1042				"path must be specified as a string"),
1043			logical.ErrInvalidRequest
1044	}
1045
1046	// This call will read both logical backend's configuration as well as auth methods'.
1047	// Retaining this behavior for backward compatibility. If this behavior is not desired,
1048	// an error can be returned if path has a prefix of "auth/".
1049	return b.handleTuneReadCommon(ctx, path)
1050}
1051
1052// handleTuneReadCommon returns the config settings of a path
1053func (b *SystemBackend) handleTuneReadCommon(ctx context.Context, path string) (*logical.Response, error) {
1054	path = sanitizeMountPath(path)
1055
1056	sysView := b.Core.router.MatchingSystemView(ctx, path)
1057	if sysView == nil {
1058		b.Backend.Logger().Error("cannot fetch sysview", "path", path)
1059		return handleError(fmt.Errorf("cannot fetch sysview for path %q", path))
1060	}
1061
1062	mountEntry := b.Core.router.MatchingMountEntry(ctx, path)
1063	if mountEntry == nil {
1064		b.Backend.Logger().Error("cannot fetch mount entry", "path", path)
1065		return handleError(fmt.Errorf("cannot fetch mount entry for path %q", path))
1066	}
1067
1068	resp := &logical.Response{
1069		Data: map[string]interface{}{
1070			"default_lease_ttl": int(sysView.DefaultLeaseTTL().Seconds()),
1071			"max_lease_ttl":     int(sysView.MaxLeaseTTL().Seconds()),
1072			"force_no_cache":    mountEntry.Config.ForceNoCache,
1073		},
1074	}
1075
1076	if mountEntry.Table == credentialTableType {
1077		resp.Data["token_type"] = mountEntry.Config.TokenType.String()
1078	}
1079
1080	if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_request_keys"); ok {
1081		resp.Data["audit_non_hmac_request_keys"] = rawVal.([]string)
1082	}
1083
1084	if rawVal, ok := mountEntry.synthesizedConfigCache.Load("audit_non_hmac_response_keys"); ok {
1085		resp.Data["audit_non_hmac_response_keys"] = rawVal.([]string)
1086	}
1087
1088	if len(mountEntry.Config.ListingVisibility) > 0 {
1089		resp.Data["listing_visibility"] = mountEntry.Config.ListingVisibility
1090	}
1091
1092	if rawVal, ok := mountEntry.synthesizedConfigCache.Load("passthrough_request_headers"); ok {
1093		resp.Data["passthrough_request_headers"] = rawVal.([]string)
1094	}
1095
1096	if rawVal, ok := mountEntry.synthesizedConfigCache.Load("allowed_response_headers"); ok {
1097		resp.Data["allowed_response_headers"] = rawVal.([]string)
1098	}
1099
1100	if len(mountEntry.Options) > 0 {
1101		resp.Data["options"] = mountEntry.Options
1102	}
1103
1104	return resp, nil
1105}
1106
1107// handleAuthTuneWrite is used to set config settings on an auth path
1108func (b *SystemBackend) handleAuthTuneWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1109	path := data.Get("path").(string)
1110	if path == "" {
1111		return logical.ErrorResponse("missing path"), nil
1112	}
1113
1114	return b.handleTuneWriteCommon(ctx, "auth/"+path, data)
1115}
1116
1117// handleMountTuneWrite is used to set config settings on a backend
1118func (b *SystemBackend) handleMountTuneWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1119	path := data.Get("path").(string)
1120	if path == "" {
1121		return logical.ErrorResponse("missing path"), nil
1122	}
1123
1124	// This call will write both logical backend's configuration as well as auth methods'.
1125	// Retaining this behavior for backward compatibility. If this behavior is not desired,
1126	// an error can be returned if path has a prefix of "auth/".
1127	return b.handleTuneWriteCommon(ctx, path, data)
1128}
1129
1130// handleTuneWriteCommon is used to set config settings on a path
1131func (b *SystemBackend) handleTuneWriteCommon(ctx context.Context, path string, data *framework.FieldData) (*logical.Response, error) {
1132	repState := b.Core.ReplicationState()
1133
1134	path = sanitizeMountPath(path)
1135
1136	// Prevent protected paths from being changed
1137	for _, p := range untunableMounts {
1138		if strings.HasPrefix(path, p) {
1139			b.Backend.Logger().Error("cannot tune this mount", "path", path)
1140			return handleError(fmt.Errorf("cannot tune %q", path))
1141		}
1142	}
1143
1144	mountEntry := b.Core.router.MatchingMountEntry(ctx, path)
1145	if mountEntry == nil {
1146		b.Backend.Logger().Error("tune failed", "error", "no mount entry found", "path", path)
1147		return handleError(fmt.Errorf("tune of path %q failed: no mount entry found", path))
1148	}
1149	if mountEntry != nil && !mountEntry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) {
1150		return nil, logical.ErrReadOnly
1151	}
1152
1153	var lock *sync.RWMutex
1154	switch {
1155	case strings.HasPrefix(path, credentialRoutePrefix):
1156		lock = &b.Core.authLock
1157	default:
1158		lock = &b.Core.mountsLock
1159	}
1160
1161	lock.Lock()
1162	defer lock.Unlock()
1163
1164	// Check again after grabbing the lock
1165	mountEntry = b.Core.router.MatchingMountEntry(ctx, path)
1166	if mountEntry == nil {
1167		b.Backend.Logger().Error("tune failed", "error", "no mount entry found", "path", path)
1168		return handleError(fmt.Errorf("tune of path %q failed: no mount entry found", path))
1169	}
1170	if mountEntry != nil && !mountEntry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) {
1171		return nil, logical.ErrReadOnly
1172	}
1173
1174	// Timing configuration parameters
1175	{
1176		var newDefault, newMax time.Duration
1177		defTTL := data.Get("default_lease_ttl").(string)
1178		switch defTTL {
1179		case "":
1180			newDefault = mountEntry.Config.DefaultLeaseTTL
1181		case "system":
1182			newDefault = time.Duration(0)
1183		default:
1184			tmpDef, err := parseutil.ParseDurationSecond(defTTL)
1185			if err != nil {
1186				return handleError(err)
1187			}
1188			newDefault = tmpDef
1189		}
1190
1191		maxTTL := data.Get("max_lease_ttl").(string)
1192		switch maxTTL {
1193		case "":
1194			newMax = mountEntry.Config.MaxLeaseTTL
1195		case "system":
1196			newMax = time.Duration(0)
1197		default:
1198			tmpMax, err := parseutil.ParseDurationSecond(maxTTL)
1199			if err != nil {
1200				return handleError(err)
1201			}
1202			newMax = tmpMax
1203		}
1204
1205		if newDefault != mountEntry.Config.DefaultLeaseTTL ||
1206			newMax != mountEntry.Config.MaxLeaseTTL {
1207
1208			if err := b.tuneMountTTLs(ctx, path, mountEntry, newDefault, newMax); err != nil {
1209				b.Backend.Logger().Error("tuning failed", "path", path, "error", err)
1210				return handleError(err)
1211			}
1212		}
1213	}
1214
1215	if rawVal, ok := data.GetOk("description"); ok {
1216		description := rawVal.(string)
1217
1218		oldDesc := mountEntry.Description
1219		mountEntry.Description = description
1220
1221		// Update the mount table
1222		var err error
1223		switch {
1224		case strings.HasPrefix(path, "auth/"):
1225			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1226		default:
1227			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1228		}
1229		if err != nil {
1230			mountEntry.Description = oldDesc
1231			return handleError(err)
1232		}
1233		if b.Core.logger.IsInfo() {
1234			b.Core.logger.Info("mount tuning of description successful", "path", path, "description", description)
1235		}
1236	}
1237
1238	if rawVal, ok := data.GetOk("audit_non_hmac_request_keys"); ok {
1239		auditNonHMACRequestKeys := rawVal.([]string)
1240
1241		oldVal := mountEntry.Config.AuditNonHMACRequestKeys
1242		mountEntry.Config.AuditNonHMACRequestKeys = auditNonHMACRequestKeys
1243
1244		// Update the mount table
1245		var err error
1246		switch {
1247		case strings.HasPrefix(path, "auth/"):
1248			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1249		default:
1250			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1251		}
1252		if err != nil {
1253			mountEntry.Config.AuditNonHMACRequestKeys = oldVal
1254			return handleError(err)
1255		}
1256
1257		mountEntry.SyncCache()
1258
1259		if b.Core.logger.IsInfo() {
1260			b.Core.logger.Info("mount tuning of audit_non_hmac_request_keys successful", "path", path)
1261		}
1262	}
1263
1264	if rawVal, ok := data.GetOk("audit_non_hmac_response_keys"); ok {
1265		auditNonHMACResponseKeys := rawVal.([]string)
1266
1267		oldVal := mountEntry.Config.AuditNonHMACResponseKeys
1268		mountEntry.Config.AuditNonHMACResponseKeys = auditNonHMACResponseKeys
1269
1270		// Update the mount table
1271		var err error
1272		switch {
1273		case strings.HasPrefix(path, "auth/"):
1274			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1275		default:
1276			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1277		}
1278		if err != nil {
1279			mountEntry.Config.AuditNonHMACResponseKeys = oldVal
1280			return handleError(err)
1281		}
1282
1283		mountEntry.SyncCache()
1284
1285		if b.Core.logger.IsInfo() {
1286			b.Core.logger.Info("mount tuning of audit_non_hmac_response_keys successful", "path", path)
1287		}
1288	}
1289
1290	if rawVal, ok := data.GetOk("listing_visibility"); ok {
1291		lvString := rawVal.(string)
1292		listingVisibility := ListingVisibilityType(lvString)
1293
1294		if err := checkListingVisibility(listingVisibility); err != nil {
1295			return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", listingVisibility)), nil
1296		}
1297
1298		oldVal := mountEntry.Config.ListingVisibility
1299		mountEntry.Config.ListingVisibility = listingVisibility
1300
1301		// Update the mount table
1302		var err error
1303		switch {
1304		case strings.HasPrefix(path, "auth/"):
1305			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1306		default:
1307			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1308		}
1309		if err != nil {
1310			mountEntry.Config.ListingVisibility = oldVal
1311			return handleError(err)
1312		}
1313
1314		if b.Core.logger.IsInfo() {
1315			b.Core.logger.Info("mount tuning of listing_visibility successful", "path", path)
1316		}
1317	}
1318
1319	if rawVal, ok := data.GetOk("token_type"); ok {
1320		if !strings.HasPrefix(path, "auth/") {
1321			return logical.ErrorResponse(fmt.Sprintf("'token_type' can only be modified on auth mounts")), logical.ErrInvalidRequest
1322		}
1323		if mountEntry.Type == "token" || mountEntry.Type == "ns_token" {
1324			return logical.ErrorResponse(fmt.Sprintf("'token_type' cannot be set for 'token' or 'ns_token' auth mounts")), logical.ErrInvalidRequest
1325		}
1326
1327		tokenType := logical.TokenTypeDefaultService
1328		ttString := rawVal.(string)
1329
1330		switch ttString {
1331		case "", "default-service":
1332		case "default-batch":
1333			tokenType = logical.TokenTypeDefaultBatch
1334		case "service":
1335			tokenType = logical.TokenTypeService
1336		case "batch":
1337			tokenType = logical.TokenTypeBatch
1338		default:
1339			return logical.ErrorResponse(fmt.Sprintf(
1340				"invalid value for 'token_type'")), logical.ErrInvalidRequest
1341		}
1342
1343		oldVal := mountEntry.Config.TokenType
1344		mountEntry.Config.TokenType = tokenType
1345
1346		// Update the mount table
1347		if err := b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local); err != nil {
1348			mountEntry.Config.TokenType = oldVal
1349			return handleError(err)
1350		}
1351
1352		if b.Core.logger.IsInfo() {
1353			b.Core.logger.Info("mount tuning of token_type successful", "path", path, "token_type", ttString)
1354		}
1355	}
1356
1357	if rawVal, ok := data.GetOk("passthrough_request_headers"); ok {
1358		headers := rawVal.([]string)
1359
1360		oldVal := mountEntry.Config.PassthroughRequestHeaders
1361		mountEntry.Config.PassthroughRequestHeaders = headers
1362
1363		// Update the mount table
1364		var err error
1365		switch {
1366		case strings.HasPrefix(path, "auth/"):
1367			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1368		default:
1369			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1370		}
1371		if err != nil {
1372			mountEntry.Config.PassthroughRequestHeaders = oldVal
1373			return handleError(err)
1374		}
1375
1376		mountEntry.SyncCache()
1377
1378		if b.Core.logger.IsInfo() {
1379			b.Core.logger.Info("mount tuning of passthrough_request_headers successful", "path", path)
1380		}
1381	}
1382
1383	if rawVal, ok := data.GetOk("allowed_response_headers"); ok {
1384		headers := rawVal.([]string)
1385
1386		oldVal := mountEntry.Config.AllowedResponseHeaders
1387		mountEntry.Config.AllowedResponseHeaders = headers
1388
1389		// Update the mount table
1390		var err error
1391		switch {
1392		case strings.HasPrefix(path, "auth/"):
1393			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1394		default:
1395			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1396		}
1397		if err != nil {
1398			mountEntry.Config.AllowedResponseHeaders = oldVal
1399			return handleError(err)
1400		}
1401
1402		mountEntry.SyncCache()
1403
1404		if b.Core.logger.IsInfo() {
1405			b.Core.logger.Info("mount tuning of allowed_response_headers successful", "path", path)
1406		}
1407	}
1408
1409	var err error
1410	var resp *logical.Response
1411	var options map[string]string
1412	if optionsRaw, ok := data.GetOk("options"); ok {
1413		options = optionsRaw.(map[string]string)
1414	}
1415
1416	if len(options) > 0 {
1417		b.Core.logger.Info("mount tuning of options", "path", path, "options", options)
1418		newOptions := make(map[string]string)
1419		var kvUpgraded bool
1420
1421		// The version options should only apply to the KV mount, check that first
1422		if v, ok := options["version"]; ok {
1423			// Special case to make sure we can not disable versioning once it's
1424			// enabled. If the vkv backend suports downgrading this can be removed.
1425			meVersion, err := parseutil.ParseInt(mountEntry.Options["version"])
1426			if err != nil {
1427				return nil, errwrap.Wrapf("unable to parse mount entry: {{err}}", err)
1428			}
1429			optVersion, err := parseutil.ParseInt(v)
1430			if err != nil {
1431				return handleError(errwrap.Wrapf("unable to parse options: {{err}}", err))
1432			}
1433
1434			// Only accept valid versions
1435			switch optVersion {
1436			case 1:
1437			case 2:
1438			default:
1439				return logical.ErrorResponse(fmt.Sprintf("invalid version provided: %d", optVersion)), logical.ErrInvalidRequest
1440			}
1441
1442			if meVersion > optVersion {
1443				// Return early if version option asks for a downgrade
1444				return logical.ErrorResponse(fmt.Sprintf("cannot downgrade mount from version %d", meVersion)), logical.ErrInvalidRequest
1445			}
1446			if meVersion < optVersion {
1447				kvUpgraded = true
1448				resp = &logical.Response{}
1449				resp.AddWarning(fmt.Sprintf("Upgrading mount from version %d to version %d. This mount will be unavailable for a brief period and will resume service shortly.", meVersion, optVersion))
1450			}
1451		}
1452
1453		// Upsert options value to a copy of the existing mountEntry's options
1454		for k, v := range mountEntry.Options {
1455			newOptions[k] = v
1456		}
1457		for k, v := range options {
1458			// If the value of the provided option is empty, delete the key We
1459			// special-case the version value here to guard against KV downgrades, but
1460			// this piece could potentially be refactored in the future to be non-KV
1461			// specific.
1462			if len(v) == 0 && k != "version" {
1463				delete(newOptions, k)
1464			} else {
1465				newOptions[k] = v
1466			}
1467		}
1468
1469		// Update the mount table
1470		oldVal := mountEntry.Options
1471		mountEntry.Options = newOptions
1472		switch {
1473		case strings.HasPrefix(path, "auth/"):
1474			err = b.Core.persistAuth(ctx, b.Core.auth, &mountEntry.Local)
1475		default:
1476			err = b.Core.persistMounts(ctx, b.Core.mounts, &mountEntry.Local)
1477		}
1478		if err != nil {
1479			mountEntry.Options = oldVal
1480			return handleError(err)
1481		}
1482
1483		// Reload the backend to kick off the upgrade process. It should only apply to KV backend so we
1484		// trigger based on the version logic above.
1485		if kvUpgraded {
1486			err = b.Core.reloadBackendCommon(ctx, mountEntry, strings.HasPrefix(path, credentialRoutePrefix))
1487			if err != nil {
1488				b.Core.logger.Error("mount tuning of options: could not reload backend", "error", err, "path", path, "options", options)
1489			}
1490
1491		}
1492	}
1493
1494	return resp, nil
1495}
1496
1497// handleLease is use to view the metadata for a given LeaseID
1498func (b *SystemBackend) handleLeaseLookup(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1499	leaseID := data.Get("lease_id").(string)
1500	if leaseID == "" {
1501		return logical.ErrorResponse("lease_id must be specified"),
1502			logical.ErrInvalidRequest
1503	}
1504
1505	leaseTimes, err := b.Core.expiration.FetchLeaseTimes(ctx, leaseID)
1506	if err != nil {
1507		b.Backend.Logger().Error("error retrieving lease", "lease_id", leaseID, "error", err)
1508		return handleError(err)
1509	}
1510	if leaseTimes == nil {
1511		return logical.ErrorResponse("invalid lease"), logical.ErrInvalidRequest
1512	}
1513
1514	resp := &logical.Response{
1515		Data: map[string]interface{}{
1516			"id":           leaseID,
1517			"issue_time":   leaseTimes.IssueTime,
1518			"expire_time":  nil,
1519			"last_renewal": nil,
1520			"ttl":          int64(0),
1521		},
1522	}
1523	renewable, _ := leaseTimes.renewable()
1524	resp.Data["renewable"] = renewable
1525
1526	if !leaseTimes.LastRenewalTime.IsZero() {
1527		resp.Data["last_renewal"] = leaseTimes.LastRenewalTime
1528	}
1529	if !leaseTimes.ExpireTime.IsZero() {
1530		resp.Data["expire_time"] = leaseTimes.ExpireTime
1531		resp.Data["ttl"] = leaseTimes.ttl()
1532	}
1533	return resp, nil
1534}
1535
1536func (b *SystemBackend) handleLeaseLookupList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1537	prefix := data.Get("prefix").(string)
1538	if prefix != "" && !strings.HasSuffix(prefix, "/") {
1539		prefix = prefix + "/"
1540	}
1541
1542	ns, err := namespace.FromContext(ctx)
1543	if err != nil {
1544		return nil, err
1545	}
1546	view := b.Core.expiration.leaseView(ns)
1547	keys, err := view.List(ctx, prefix)
1548	if err != nil {
1549		b.Backend.Logger().Error("error listing leases", "prefix", prefix, "error", err)
1550		return handleErrorNoReadOnlyForward(err)
1551	}
1552	return logical.ListResponse(keys), nil
1553}
1554
1555// handleRenew is used to renew a lease with a given LeaseID
1556func (b *SystemBackend) handleRenew(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1557	// Get all the options
1558	leaseID := data.Get("lease_id").(string)
1559	if leaseID == "" {
1560		leaseID = data.Get("url_lease_id").(string)
1561	}
1562	if leaseID == "" {
1563		return logical.ErrorResponse("lease_id must be specified"),
1564			logical.ErrInvalidRequest
1565	}
1566	incrementRaw := data.Get("increment").(int)
1567
1568	// Convert the increment
1569	increment := time.Duration(incrementRaw) * time.Second
1570
1571	// Invoke the expiration manager directly
1572	resp, err := b.Core.expiration.Renew(ctx, leaseID, increment)
1573	if err != nil {
1574		b.Backend.Logger().Error("lease renewal failed", "lease_id", leaseID, "error", err)
1575		return handleErrorNoReadOnlyForward(err)
1576	}
1577	return resp, err
1578}
1579
1580// handleRevoke is used to revoke a given LeaseID
1581func (b *SystemBackend) handleRevoke(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1582	// Get all the options
1583	leaseID := data.Get("lease_id").(string)
1584	if leaseID == "" {
1585		leaseID = data.Get("url_lease_id").(string)
1586	}
1587	if leaseID == "" {
1588		return logical.ErrorResponse("lease_id must be specified"),
1589			logical.ErrInvalidRequest
1590	}
1591
1592	ns, err := namespace.FromContext(ctx)
1593	if err != nil {
1594		return nil, err
1595	}
1596	revokeCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns)
1597	if data.Get("sync").(bool) {
1598		// Invoke the expiration manager directly
1599		if err := b.Core.expiration.Revoke(revokeCtx, leaseID); err != nil {
1600			b.Backend.Logger().Error("lease revocation failed", "lease_id", leaseID, "error", err)
1601			return handleErrorNoReadOnlyForward(err)
1602		}
1603
1604		return nil, nil
1605	}
1606
1607	if err := b.Core.expiration.LazyRevoke(revokeCtx, leaseID); err != nil {
1608		b.Backend.Logger().Error("lease revocation failed", "lease_id", leaseID, "error", err)
1609		return handleErrorNoReadOnlyForward(err)
1610	}
1611
1612	return logical.RespondWithStatusCode(nil, nil, http.StatusAccepted)
1613}
1614
1615// handleRevokePrefix is used to revoke a prefix with many LeaseIDs
1616func (b *SystemBackend) handleRevokePrefix(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1617	return b.handleRevokePrefixCommon(ctx, req, data, false, data.Get("sync").(bool))
1618}
1619
1620// handleRevokeForce is used to revoke a prefix with many LeaseIDs, ignoring errors
1621func (b *SystemBackend) handleRevokeForce(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1622	return b.handleRevokePrefixCommon(ctx, req, data, true, true)
1623}
1624
1625// handleRevokePrefixCommon is used to revoke a prefix with many LeaseIDs
1626func (b *SystemBackend) handleRevokePrefixCommon(ctx context.Context,
1627	req *logical.Request, data *framework.FieldData, force, sync bool) (*logical.Response, error) {
1628	// Get all the options
1629	prefix := data.Get("prefix").(string)
1630
1631	ns, err := namespace.FromContext(ctx)
1632	if err != nil {
1633		return nil, err
1634	}
1635
1636	// Invoke the expiration manager directly
1637	revokeCtx := namespace.ContextWithNamespace(b.Core.activeContext, ns)
1638	if force {
1639		err = b.Core.expiration.RevokeForce(revokeCtx, prefix)
1640	} else {
1641		err = b.Core.expiration.RevokePrefix(revokeCtx, prefix, sync)
1642	}
1643	if err != nil {
1644		b.Backend.Logger().Error("revoke prefix failed", "prefix", prefix, "error", err)
1645		return handleErrorNoReadOnlyForward(err)
1646	}
1647
1648	if sync {
1649		return nil, nil
1650	}
1651
1652	return logical.RespondWithStatusCode(nil, nil, http.StatusAccepted)
1653}
1654
1655// handleAuthTable handles the "auth" endpoint to provide the auth table
1656func (b *SystemBackend) handleAuthTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1657	ns, err := namespace.FromContext(ctx)
1658	if err != nil {
1659		return nil, err
1660	}
1661
1662	b.Core.authLock.RLock()
1663	defer b.Core.authLock.RUnlock()
1664
1665	resp := &logical.Response{
1666		Data: make(map[string]interface{}),
1667	}
1668
1669	for _, entry := range b.Core.auth.Entries {
1670		// Only show entries for current namespace
1671		if entry.Namespace().Path != ns.Path {
1672			continue
1673		}
1674
1675		cont, err := b.Core.checkReplicatedFiltering(ctx, entry, credentialRoutePrefix)
1676		if err != nil {
1677			return nil, err
1678		}
1679		if cont {
1680			continue
1681		}
1682
1683		info := mountInfo(entry)
1684		resp.Data[entry.Path] = info
1685	}
1686
1687	return resp, nil
1688}
1689
1690// handleEnableAuth is used to enable a new credential backend
1691func (b *SystemBackend) handleEnableAuth(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1692	repState := b.Core.ReplicationState()
1693	local := data.Get("local").(bool)
1694
1695	// If we are a performance secondary cluster we should forward the request
1696	// to the primary. We fail early here since the view in use isn't marked as
1697	// readonly
1698	if !local && repState.HasState(consts.ReplicationPerformanceSecondary) {
1699		return nil, logical.ErrReadOnly
1700	}
1701
1702	// Get all the options
1703	path := data.Get("path").(string)
1704	path = sanitizeMountPath(path)
1705	logicalType := data.Get("type").(string)
1706	description := data.Get("description").(string)
1707	pluginName := data.Get("plugin_name").(string)
1708	sealWrap := data.Get("seal_wrap").(bool)
1709	options := data.Get("options").(map[string]string)
1710
1711	var config MountConfig
1712	var apiConfig APIMountConfig
1713
1714	configMap := data.Get("config").(map[string]interface{})
1715	if configMap != nil && len(configMap) != 0 {
1716		err := mapstructure.Decode(configMap, &apiConfig)
1717		if err != nil {
1718			return logical.ErrorResponse(
1719					"unable to convert given auth config information"),
1720				logical.ErrInvalidRequest
1721		}
1722	}
1723
1724	switch apiConfig.DefaultLeaseTTL {
1725	case "":
1726	case "system":
1727	default:
1728		tmpDef, err := parseutil.ParseDurationSecond(apiConfig.DefaultLeaseTTL)
1729		if err != nil {
1730			return logical.ErrorResponse(fmt.Sprintf(
1731					"unable to parse default TTL of %s: %s", apiConfig.DefaultLeaseTTL, err)),
1732				logical.ErrInvalidRequest
1733		}
1734		config.DefaultLeaseTTL = tmpDef
1735	}
1736
1737	switch apiConfig.MaxLeaseTTL {
1738	case "":
1739	case "system":
1740	default:
1741		tmpMax, err := parseutil.ParseDurationSecond(apiConfig.MaxLeaseTTL)
1742		if err != nil {
1743			return logical.ErrorResponse(fmt.Sprintf(
1744					"unable to parse max TTL of %s: %s", apiConfig.MaxLeaseTTL, err)),
1745				logical.ErrInvalidRequest
1746		}
1747		config.MaxLeaseTTL = tmpMax
1748	}
1749
1750	if config.MaxLeaseTTL != 0 && config.DefaultLeaseTTL > config.MaxLeaseTTL {
1751		return logical.ErrorResponse(
1752				"given default lease TTL greater than given max lease TTL"),
1753			logical.ErrInvalidRequest
1754	}
1755
1756	if config.DefaultLeaseTTL > b.Core.maxLeaseTTL && config.MaxLeaseTTL == 0 {
1757		return logical.ErrorResponse(fmt.Sprintf(
1758				"given default lease TTL greater than system max lease TTL of %d", int(b.Core.maxLeaseTTL.Seconds()))),
1759			logical.ErrInvalidRequest
1760	}
1761
1762	switch apiConfig.TokenType {
1763	case "", "default-service":
1764		config.TokenType = logical.TokenTypeDefaultService
1765	case "default-batch":
1766		config.TokenType = logical.TokenTypeDefaultBatch
1767	case "service":
1768		config.TokenType = logical.TokenTypeService
1769	case "batch":
1770		config.TokenType = logical.TokenTypeBatch
1771	default:
1772		return logical.ErrorResponse(fmt.Sprintf(
1773			"invalid value for 'token_type'")), logical.ErrInvalidRequest
1774	}
1775
1776	switch logicalType {
1777	case "":
1778		return logical.ErrorResponse(
1779				"backend type must be specified as a string"),
1780			logical.ErrInvalidRequest
1781	case "plugin":
1782		// Only set plugin name if mount is of type plugin, with apiConfig.PluginName
1783		// option taking precedence.
1784		switch {
1785		case apiConfig.PluginName != "":
1786			logicalType = apiConfig.PluginName
1787		case pluginName != "":
1788			logicalType = pluginName
1789		default:
1790			return logical.ErrorResponse(
1791					"plugin_name must be provided for plugin backend"),
1792				logical.ErrInvalidRequest
1793		}
1794	}
1795
1796	if options != nil && options["version"] != "" {
1797		return logical.ErrorResponse(fmt.Sprintf(
1798				"auth method %q does not allow setting a version", logicalType)),
1799			logical.ErrInvalidRequest
1800	}
1801
1802	if err := checkListingVisibility(apiConfig.ListingVisibility); err != nil {
1803		return logical.ErrorResponse(fmt.Sprintf("invalid listing_visibility %s", apiConfig.ListingVisibility)), nil
1804	}
1805	config.ListingVisibility = apiConfig.ListingVisibility
1806
1807	if len(apiConfig.AuditNonHMACRequestKeys) > 0 {
1808		config.AuditNonHMACRequestKeys = apiConfig.AuditNonHMACRequestKeys
1809	}
1810	if len(apiConfig.AuditNonHMACResponseKeys) > 0 {
1811		config.AuditNonHMACResponseKeys = apiConfig.AuditNonHMACResponseKeys
1812	}
1813	if len(apiConfig.PassthroughRequestHeaders) > 0 {
1814		config.PassthroughRequestHeaders = apiConfig.PassthroughRequestHeaders
1815	}
1816	if len(apiConfig.AllowedResponseHeaders) > 0 {
1817		config.AllowedResponseHeaders = apiConfig.AllowedResponseHeaders
1818	}
1819
1820	// Create the mount entry
1821	me := &MountEntry{
1822		Table:       credentialTableType,
1823		Path:        path,
1824		Type:        logicalType,
1825		Description: description,
1826		Config:      config,
1827		Local:       local,
1828		SealWrap:    sealWrap,
1829		Options:     options,
1830	}
1831
1832	// Attempt enabling
1833	if err := b.Core.enableCredential(ctx, me); err != nil {
1834		b.Backend.Logger().Error("enable auth mount failed", "path", me.Path, "error", err)
1835		return handleError(err)
1836	}
1837	return nil, nil
1838}
1839
1840// handleDisableAuth is used to disable a credential backend
1841func (b *SystemBackend) handleDisableAuth(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1842	path := data.Get("path").(string)
1843	path = sanitizeMountPath(path)
1844
1845	ns, err := namespace.FromContext(ctx)
1846	if err != nil {
1847		return nil, err
1848	}
1849	fullPath := credentialRoutePrefix + path
1850
1851	repState := b.Core.ReplicationState()
1852	entry := b.Core.router.MatchingMountEntry(ctx, fullPath)
1853
1854	// If we are a performance secondary cluster we should forward the request
1855	// to the primary. We fail early here since the view in use isn't marked as
1856	// readonly
1857	if entry != nil && !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) {
1858		return nil, logical.ErrReadOnly
1859	}
1860
1861	// We return success when the mount does not exists to not expose if the
1862	// mount existed or not
1863	match := b.Core.router.MatchingMount(ctx, fullPath)
1864	if match == "" || ns.Path+fullPath != match {
1865		return nil, nil
1866	}
1867
1868	prefix, found := b.Core.router.MatchingStoragePrefixByAPIPath(ctx, fullPath)
1869	if !found {
1870		b.Backend.Logger().Error("unable to find storage for path", "path", fullPath)
1871		return handleError(fmt.Errorf("unable to find storage for path: %q", fullPath))
1872	}
1873
1874	// Attempt disable
1875	if err := b.Core.disableCredential(ctx, path); err != nil {
1876		b.Backend.Logger().Error("disable auth mount failed", "path", path, "error", err)
1877		return handleError(err)
1878	}
1879
1880	// Remove from filtered mounts
1881	if err := b.Core.removePrefixFromFilteredPaths(ctx, prefix); err != nil {
1882		b.Backend.Logger().Error("filtered path removal failed", path, "error", err)
1883		return handleError(err)
1884	}
1885
1886	return nil, nil
1887}
1888
1889// handlePoliciesList handles /sys/policy/ and /sys/policies/<type> endpoints to provide the enabled policies
1890func (b *SystemBackend) handlePoliciesList(policyType PolicyType) framework.OperationFunc {
1891	return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1892		ns, err := namespace.FromContext(ctx)
1893		if err != nil {
1894			return nil, err
1895		}
1896		policies, err := b.Core.policyStore.ListPolicies(ctx, policyType)
1897		if err != nil {
1898			return nil, err
1899		}
1900
1901		switch policyType {
1902		case PolicyTypeACL:
1903			// Add the special "root" policy if not egp and we are at the root namespace
1904			if ns.ID == namespace.RootNamespaceID {
1905				policies = append(policies, "root")
1906			}
1907			resp := logical.ListResponse(policies)
1908
1909			// If the request is from sys/policy/ we handle backwards compatibility
1910			if strings.HasPrefix(req.Path, "policy") {
1911				resp.Data["policies"] = resp.Data["keys"]
1912			}
1913			return resp, nil
1914
1915		case PolicyTypeRGP:
1916			return logical.ListResponse(policies), nil
1917
1918		case PolicyTypeEGP:
1919			nsScopedKeyInfo := getEGPListResponseKeyInfo(b, ns)
1920			return &logical.Response{
1921				Data: map[string]interface{}{
1922					"keys":     policies,
1923					"key_info": nsScopedKeyInfo,
1924				},
1925			}, nil
1926		}
1927
1928		return logical.ErrorResponse("unknown policy type"), nil
1929	}
1930}
1931
1932// handlePoliciesRead handles the "/sys/policy/<name>" and "/sys/policies/<type>/<name>" endpoints to read a policy
1933func (b *SystemBackend) handlePoliciesRead(policyType PolicyType) framework.OperationFunc {
1934	return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1935		name := data.Get("name").(string)
1936
1937		policy, err := b.Core.policyStore.GetPolicy(ctx, name, policyType)
1938		if err != nil {
1939			return handleError(err)
1940		}
1941
1942		if policy == nil {
1943			return nil, nil
1944		}
1945
1946		// If the request is from sys/policy/ we handle backwards compatibility
1947		var respDataPolicyName string
1948		if policyType == PolicyTypeACL && strings.HasPrefix(req.Path, "policy") {
1949			respDataPolicyName = "rules"
1950		} else {
1951			respDataPolicyName = "policy"
1952		}
1953
1954		resp := &logical.Response{
1955			Data: map[string]interface{}{
1956				"name":             policy.Name,
1957				respDataPolicyName: policy.Raw,
1958			},
1959		}
1960
1961		switch policy.Type {
1962		case PolicyTypeRGP, PolicyTypeEGP:
1963			addSentinelPolicyData(resp.Data, policy)
1964		}
1965
1966		return resp, nil
1967	}
1968}
1969
1970// handlePoliciesSet handles the "/sys/policy/<name>" and "/sys/policies/<type>/<name>" endpoints to set a policy
1971func (b *SystemBackend) handlePoliciesSet(policyType PolicyType) framework.OperationFunc {
1972	return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
1973		var resp *logical.Response
1974
1975		ns, err := namespace.FromContext(ctx)
1976		if err != nil {
1977			return nil, err
1978		}
1979
1980		policy := &Policy{
1981			Name:      strings.ToLower(data.Get("name").(string)),
1982			Type:      policyType,
1983			namespace: ns,
1984		}
1985		if policy.Name == "" {
1986			return logical.ErrorResponse("policy name must be provided in the URL"), nil
1987		}
1988
1989		policy.Raw = data.Get("policy").(string)
1990		if policy.Raw == "" && policyType == PolicyTypeACL && strings.HasPrefix(req.Path, "policy") {
1991			policy.Raw = data.Get("rules").(string)
1992			if resp == nil {
1993				resp = &logical.Response{}
1994			}
1995			resp.AddWarning("'rules' is deprecated, please use 'policy' instead")
1996		}
1997		if policy.Raw == "" {
1998			return logical.ErrorResponse("'policy' parameter not supplied or empty"), nil
1999		}
2000
2001		if polBytes, err := base64.StdEncoding.DecodeString(policy.Raw); err == nil {
2002			policy.Raw = string(polBytes)
2003		}
2004
2005		switch policyType {
2006		case PolicyTypeACL:
2007			p, err := ParseACLPolicy(ns, policy.Raw)
2008			if err != nil {
2009				return handleError(err)
2010			}
2011			policy.Paths = p.Paths
2012			policy.Templated = p.Templated
2013
2014		case PolicyTypeRGP, PolicyTypeEGP:
2015
2016		default:
2017			return logical.ErrorResponse("unknown policy type"), nil
2018		}
2019
2020		if policy.Type == PolicyTypeRGP || policy.Type == PolicyTypeEGP {
2021			if errResp := inputSentinelPolicyData(data, policy); errResp != nil {
2022				return errResp, nil
2023			}
2024		}
2025
2026		// Update the policy
2027		if err := b.Core.policyStore.SetPolicy(ctx, policy); err != nil {
2028			return handleError(err)
2029		}
2030		return resp, nil
2031	}
2032}
2033
2034func (b *SystemBackend) handlePoliciesDelete(policyType PolicyType) framework.OperationFunc {
2035	return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2036		name := data.Get("name").(string)
2037
2038		if err := b.Core.policyStore.DeletePolicy(ctx, name, policyType); err != nil {
2039			return handleError(err)
2040		}
2041		return nil, nil
2042	}
2043}
2044
2045// handleAuditTable handles the "audit" endpoint to provide the audit table
2046func (b *SystemBackend) handleAuditTable(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2047	b.Core.auditLock.RLock()
2048	defer b.Core.auditLock.RUnlock()
2049
2050	resp := &logical.Response{
2051		Data: make(map[string]interface{}),
2052	}
2053	for _, entry := range b.Core.audit.Entries {
2054		info := map[string]interface{}{
2055			"path":        entry.Path,
2056			"type":        entry.Type,
2057			"description": entry.Description,
2058			"options":     entry.Options,
2059			"local":       entry.Local,
2060		}
2061		resp.Data[entry.Path] = info
2062	}
2063	return resp, nil
2064}
2065
2066// handleAuditHash is used to fetch the hash of the given input data with the
2067// specified audit backend's salt
2068func (b *SystemBackend) handleAuditHash(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2069	path := data.Get("path").(string)
2070	input := data.Get("input").(string)
2071	if input == "" {
2072		return logical.ErrorResponse("the \"input\" parameter is empty"), nil
2073	}
2074
2075	path = sanitizeMountPath(path)
2076
2077	hash, err := b.Core.auditBroker.GetHash(ctx, path, input)
2078	if err != nil {
2079		return logical.ErrorResponse(err.Error()), nil
2080	}
2081
2082	return &logical.Response{
2083		Data: map[string]interface{}{
2084			"hash": hash,
2085		},
2086	}, nil
2087}
2088
2089// handleEnableAudit is used to enable a new audit backend
2090func (b *SystemBackend) handleEnableAudit(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2091	repState := b.Core.ReplicationState()
2092
2093	local := data.Get("local").(bool)
2094	// If we are a performance secondary cluster we should forward the request
2095	// to the primary. We fail early here since the view in use isn't marked as
2096	// readonly
2097	if !local && repState.HasState(consts.ReplicationPerformanceSecondary) {
2098		return nil, logical.ErrReadOnly
2099	}
2100
2101	// Get all the options
2102	path := data.Get("path").(string)
2103	backendType := data.Get("type").(string)
2104	description := data.Get("description").(string)
2105	options := data.Get("options").(map[string]string)
2106
2107	// Create the mount entry
2108	me := &MountEntry{
2109		Table:       auditTableType,
2110		Path:        path,
2111		Type:        backendType,
2112		Description: description,
2113		Options:     options,
2114		Local:       local,
2115	}
2116
2117	// Attempt enabling
2118	if err := b.Core.enableAudit(ctx, me, true); err != nil {
2119		b.Backend.Logger().Error("enable audit mount failed", "path", me.Path, "error", err)
2120		return handleError(err)
2121	}
2122	return nil, nil
2123}
2124
2125// handleDisableAudit is used to disable an audit backend
2126func (b *SystemBackend) handleDisableAudit(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2127	path := data.Get("path").(string)
2128
2129	if !strings.HasSuffix(path, "/") {
2130		path += "/"
2131	}
2132
2133	if path == "/" {
2134		return handleError(errors.New("audit device path must be specified"))
2135	}
2136
2137	b.Core.auditLock.RLock()
2138	table := b.Core.audit.shallowClone()
2139	entry, err := table.find(ctx, path)
2140	b.Core.auditLock.RUnlock()
2141
2142	if err != nil {
2143		return handleError(err)
2144	}
2145	if entry == nil {
2146		return nil, nil
2147	}
2148
2149	repState := b.Core.ReplicationState()
2150
2151	// If we are a performance secondary cluster we should forward the request
2152	// to the primary. We fail early here since the view in use isn't marked as
2153	// readonly
2154	if !entry.Local && repState.HasState(consts.ReplicationPerformanceSecondary) {
2155		return nil, logical.ErrReadOnly
2156	}
2157
2158	// Attempt disable
2159	if existed, err := b.Core.disableAudit(ctx, path, true); existed && err != nil {
2160		b.Backend.Logger().Error("disable audit mount failed", "path", path, "error", err)
2161		return handleError(err)
2162	}
2163	return nil, nil
2164}
2165
2166func (b *SystemBackend) handleConfigUIHeadersRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2167	header := data.Get("header").(string)
2168
2169	value, err := b.Core.uiConfig.GetHeader(ctx, header)
2170	if err != nil {
2171		return nil, err
2172	}
2173	if value == "" {
2174		return nil, nil
2175	}
2176
2177	return &logical.Response{
2178		Data: map[string]interface{}{
2179			"value": value,
2180		},
2181	}, nil
2182}
2183
2184func (b *SystemBackend) handleConfigUIHeadersList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2185	headers, err := b.Core.uiConfig.HeaderKeys(ctx)
2186	if err != nil {
2187		return nil, err
2188	}
2189	if len(headers) == 0 {
2190		return nil, nil
2191	}
2192
2193	return logical.ListResponse(headers), nil
2194}
2195
2196func (b *SystemBackend) handleConfigUIHeadersUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2197	header := data.Get("header").(string)
2198	values := data.Get("values").([]string)
2199	if header == "" || len(values) == 0 {
2200		return logical.ErrorResponse("header and values must be specified"), logical.ErrInvalidRequest
2201	}
2202
2203	lowerHeader := strings.ToLower(header)
2204	if strings.HasPrefix(lowerHeader, "x-vault-") {
2205		return logical.ErrorResponse("X-Vault headers cannot be set"), logical.ErrInvalidRequest
2206	}
2207
2208	// Translate the list of values to the valid header string
2209	value := http.Header{}
2210	for _, v := range values {
2211		value.Add(header, v)
2212	}
2213	err := b.Core.uiConfig.SetHeader(ctx, header, value.Get(header))
2214	if err != nil {
2215		return nil, err
2216	}
2217
2218	// Warn when overriding the CSP
2219	resp := &logical.Response{}
2220	if lowerHeader == "content-security-policy" {
2221		resp.AddWarning("overriding default Content-Security-Policy which is secure by default, proceed with caution")
2222	}
2223
2224	return resp, nil
2225}
2226
2227func (b *SystemBackend) handleConfigUIHeadersDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2228	header := data.Get("header").(string)
2229	err := b.Core.uiConfig.DeleteHeader(ctx, header)
2230	if err != nil {
2231		return nil, err
2232	}
2233	return nil, nil
2234}
2235
2236// handleRawRead is used to read directly from the barrier
2237func (b *SystemBackend) handleRawRead(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2238	path := data.Get("path").(string)
2239
2240	// Prevent access of protected paths
2241	for _, p := range protectedPaths {
2242		if strings.HasPrefix(path, p) {
2243			err := fmt.Sprintf("cannot read '%s'", path)
2244			return logical.ErrorResponse(err), logical.ErrInvalidRequest
2245		}
2246	}
2247
2248	// Run additional checks if needed
2249	if err := checkRaw(b, path); err != nil {
2250		b.Core.logger.Warn(err.Error(), "path", path)
2251		return logical.ErrorResponse("cannot read '%s'", path), logical.ErrInvalidRequest
2252	}
2253
2254	entry, err := b.Core.barrier.Get(ctx, path)
2255	if err != nil {
2256		return handleErrorNoReadOnlyForward(err)
2257	}
2258	if entry == nil {
2259		return nil, nil
2260	}
2261
2262	// Run this through the decompression helper to see if it's been compressed.
2263	// If the input contained the compression canary, `outputBytes` will hold
2264	// the decompressed data. If the input was not compressed, then `outputBytes`
2265	// will be nil.
2266	outputBytes, _, err := compressutil.Decompress(entry.Value)
2267	if err != nil {
2268		return handleErrorNoReadOnlyForward(err)
2269	}
2270
2271	// `outputBytes` is nil if the input is uncompressed. In that case set it to the original input.
2272	if outputBytes == nil {
2273		outputBytes = entry.Value
2274	}
2275
2276	resp := &logical.Response{
2277		Data: map[string]interface{}{
2278			"value": string(outputBytes),
2279		},
2280	}
2281	return resp, nil
2282}
2283
2284// handleRawWrite is used to write directly to the barrier
2285func (b *SystemBackend) handleRawWrite(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2286	path := data.Get("path").(string)
2287
2288	// Prevent access of protected paths
2289	for _, p := range protectedPaths {
2290		if strings.HasPrefix(path, p) {
2291			err := fmt.Sprintf("cannot write '%s'", path)
2292			return logical.ErrorResponse(err), logical.ErrInvalidRequest
2293		}
2294	}
2295
2296	value := data.Get("value").(string)
2297	entry := &logical.StorageEntry{
2298		Key:   path,
2299		Value: []byte(value),
2300	}
2301	if err := b.Core.barrier.Put(ctx, entry); err != nil {
2302		return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest
2303	}
2304	return nil, nil
2305}
2306
2307// handleRawDelete is used to delete directly from the barrier
2308func (b *SystemBackend) handleRawDelete(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2309	path := data.Get("path").(string)
2310
2311	// Prevent access of protected paths
2312	for _, p := range protectedPaths {
2313		if strings.HasPrefix(path, p) {
2314			err := fmt.Sprintf("cannot delete '%s'", path)
2315			return logical.ErrorResponse(err), logical.ErrInvalidRequest
2316		}
2317	}
2318
2319	if err := b.Core.barrier.Delete(ctx, path); err != nil {
2320		return handleErrorNoReadOnlyForward(err)
2321	}
2322	return nil, nil
2323}
2324
2325// handleRawList is used to list directly from the barrier
2326func (b *SystemBackend) handleRawList(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2327	path := data.Get("path").(string)
2328	if path != "" && !strings.HasSuffix(path, "/") {
2329		path = path + "/"
2330	}
2331
2332	// Prevent access of protected paths
2333	for _, p := range protectedPaths {
2334		if strings.HasPrefix(path, p) {
2335			err := fmt.Sprintf("cannot list '%s'", path)
2336			return logical.ErrorResponse(err), logical.ErrInvalidRequest
2337		}
2338	}
2339
2340	// Run additional checks if needed
2341	if err := checkRaw(b, path); err != nil {
2342		b.Core.logger.Warn(err.Error(), "path", path)
2343		return logical.ErrorResponse("cannot list '%s'", path), logical.ErrInvalidRequest
2344	}
2345
2346	keys, err := b.Core.barrier.List(ctx, path)
2347	if err != nil {
2348		return handleErrorNoReadOnlyForward(err)
2349	}
2350	return logical.ListResponse(keys), nil
2351}
2352
2353// handleKeyStatus returns status information about the backend key
2354func (b *SystemBackend) handleKeyStatus(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2355	// Get the key info
2356	info, err := b.Core.barrier.ActiveKeyInfo()
2357	if err != nil {
2358		return nil, err
2359	}
2360
2361	resp := &logical.Response{
2362		Data: map[string]interface{}{
2363			"term":         info.Term,
2364			"install_time": info.InstallTime.Format(time.RFC3339Nano),
2365		},
2366	}
2367	return resp, nil
2368}
2369
2370// handleRotate is used to trigger a key rotation
2371func (b *SystemBackend) handleRotate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2372	repState := b.Core.ReplicationState()
2373	if repState.HasState(consts.ReplicationPerformanceSecondary) {
2374		return logical.ErrorResponse("cannot rotate on a replication secondary"), nil
2375	}
2376
2377	// Rotate to the new term
2378	newTerm, err := b.Core.barrier.Rotate(ctx)
2379	if err != nil {
2380		b.Backend.Logger().Error("failed to create new encryption key", "error", err)
2381		return handleError(err)
2382	}
2383	b.Backend.Logger().Info("installed new encryption key")
2384
2385	// In HA mode, we need to an upgrade path for the standby instances
2386	if b.Core.ha != nil {
2387		// Create the upgrade path to the new term
2388		if err := b.Core.barrier.CreateUpgrade(ctx, newTerm); err != nil {
2389			b.Backend.Logger().Error("failed to create new upgrade", "term", newTerm, "error", err)
2390		}
2391
2392		// Schedule the destroy of the upgrade path
2393		time.AfterFunc(KeyRotateGracePeriod, func() {
2394			b.Backend.Logger().Debug("cleaning up upgrade keys", "waited", KeyRotateGracePeriod)
2395			if err := b.Core.barrier.DestroyUpgrade(b.Core.activeContext, newTerm); err != nil {
2396				b.Backend.Logger().Error("failed to destroy upgrade", "term", newTerm, "error", err)
2397			}
2398		})
2399	}
2400
2401	// Write to the canary path, which will force a synchronous truing during
2402	// replication
2403	if err := b.Core.barrier.Put(ctx, &logical.StorageEntry{
2404		Key:   coreKeyringCanaryPath,
2405		Value: []byte(fmt.Sprintf("new-rotation-term-%d", newTerm)),
2406	}); err != nil {
2407		b.Core.logger.Error("error saving keyring canary", "error", err)
2408		return nil, errwrap.Wrapf("failed to save keyring canary: {{err}}", err)
2409	}
2410
2411	return nil, nil
2412}
2413
2414func (b *SystemBackend) handleWrappingPubkey(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2415	x, _ := b.Core.wrappingJWTKey.X.MarshalText()
2416	y, _ := b.Core.wrappingJWTKey.Y.MarshalText()
2417	return &logical.Response{
2418		Data: map[string]interface{}{
2419			"jwt_x":     string(x),
2420			"jwt_y":     string(y),
2421			"jwt_curve": corePrivateKeyTypeP521,
2422		},
2423	}, nil
2424}
2425
2426func (b *SystemBackend) handleWrappingWrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2427	if req.WrapInfo == nil || req.WrapInfo.TTL == 0 {
2428		return logical.ErrorResponse("endpoint requires response wrapping to be used"), logical.ErrInvalidRequest
2429	}
2430
2431	// N.B.: Do *NOT* allow JWT wrapping tokens to be created through this
2432	// endpoint. JWTs are signed so if we don't allow users to create wrapping
2433	// tokens using them we can ensure that an operator can't spoof a legit JWT
2434	// wrapped token, which makes certain init/rekey/generate-root cases have
2435	// better properties.
2436	req.WrapInfo.Format = "uuid"
2437
2438	return &logical.Response{
2439		Data: data.Raw,
2440	}, nil
2441}
2442
2443// handleWrappingUnwrap will unwrap a response wrapping token or complete a
2444// request that required a control group.
2445func (b *SystemBackend) handleWrappingUnwrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2446	// If a third party is unwrapping (rather than the calling token being the
2447	// wrapping token) we detect this so that we can revoke the original
2448	// wrapping token after reading it
2449	var thirdParty bool
2450
2451	token := data.Get("token").(string)
2452	if token != "" {
2453		thirdParty = true
2454	} else {
2455		token = req.ClientToken
2456	}
2457
2458	// Get the policies so we can determine if this is a normal response
2459	// wrapping request or a control group token.
2460	//
2461	// We use lookupTainted here because the token might have already been used
2462	// by handleRequest(), this happens when it's a normal response wrapping
2463	// request and the token was provided "first party". We want to inspect the
2464	// token policies but will not use this token entry for anything else.
2465	te, err := b.Core.tokenStore.lookupTainted(ctx, token)
2466	if err != nil {
2467		return nil, err
2468	}
2469	if te == nil {
2470		return nil, nil
2471	}
2472	if len(te.Policies) != 1 {
2473		return nil, errors.New("token is not a valid unwrap token")
2474	}
2475
2476	unwrapNS, err := NamespaceByID(ctx, te.NamespaceID, b.Core)
2477	if err != nil {
2478		return nil, err
2479	}
2480	unwrapCtx := namespace.ContextWithNamespace(ctx, unwrapNS)
2481
2482	var response string
2483	switch te.Policies[0] {
2484	case controlGroupPolicyName:
2485		response, err = controlGroupUnwrap(unwrapCtx, b, token, thirdParty)
2486	case responseWrappingPolicyName:
2487		response, err = b.responseWrappingUnwrap(unwrapCtx, te, thirdParty)
2488	}
2489	if err != nil {
2490		var respErr *logical.Response
2491		if len(response) > 0 {
2492			respErr = logical.ErrorResponse(response)
2493		}
2494
2495		return respErr, err
2496	}
2497
2498	resp := &logical.Response{
2499		Data: map[string]interface{}{},
2500	}
2501
2502	// Most of the time we want to just send over the marshalled HTTP bytes.
2503	// However there is a sad separate case: if the original response was using
2504	// bare values we need to use those or else what comes back is garbled.
2505	httpResp := &logical.HTTPResponse{}
2506	err = jsonutil.DecodeJSON([]byte(response), httpResp)
2507	if err != nil {
2508		return nil, errwrap.Wrapf("error decoding wrapped response: {{err}}", err)
2509	}
2510	if httpResp.Data != nil &&
2511		(httpResp.Data[logical.HTTPStatusCode] != nil ||
2512			httpResp.Data[logical.HTTPRawBody] != nil ||
2513			httpResp.Data[logical.HTTPContentType] != nil) {
2514		if httpResp.Data[logical.HTTPStatusCode] != nil {
2515			resp.Data[logical.HTTPStatusCode] = httpResp.Data[logical.HTTPStatusCode]
2516		}
2517		if httpResp.Data[logical.HTTPContentType] != nil {
2518			resp.Data[logical.HTTPContentType] = httpResp.Data[logical.HTTPContentType]
2519		}
2520
2521		rawBody := httpResp.Data[logical.HTTPRawBody]
2522		if rawBody != nil {
2523			// Decode here so that we can audit properly
2524			switch rawBody.(type) {
2525			case string:
2526				// Best effort decoding; if this works, the original value was
2527				// probably a []byte instead of a string, but was marshaled
2528				// when the value was saved, so this restores it as it was
2529				decBytes, err := base64.StdEncoding.DecodeString(rawBody.(string))
2530				if err == nil {
2531					// We end up with []byte, will not be HMAC'd
2532					resp.Data[logical.HTTPRawBody] = decBytes
2533				} else {
2534					// We end up with string, will be HMAC'd
2535					resp.Data[logical.HTTPRawBody] = rawBody
2536				}
2537			default:
2538				b.Core.Logger().Error("unexpected type of raw body when decoding wrapped token", "type", fmt.Sprintf("%T", rawBody))
2539			}
2540
2541			resp.Data[logical.HTTPRawBodyAlreadyJSONDecoded] = true
2542		}
2543
2544		return resp, nil
2545	}
2546
2547	if len(response) == 0 {
2548		resp.Data[logical.HTTPStatusCode] = 204
2549	} else {
2550		resp.Data[logical.HTTPStatusCode] = 200
2551		resp.Data[logical.HTTPRawBody] = []byte(response)
2552		resp.Data[logical.HTTPContentType] = "application/json"
2553	}
2554
2555	return resp, nil
2556}
2557
2558// responseWrappingUnwrap will read the stored response in the cubbyhole and
2559// return the raw HTTP response.
2560func (b *SystemBackend) responseWrappingUnwrap(ctx context.Context, te *logical.TokenEntry, thirdParty bool) (string, error) {
2561	tokenID := te.ID
2562	if thirdParty {
2563		// Use the token to decrement the use count to avoid a second operation on the token.
2564		_, err := b.Core.tokenStore.UseTokenByID(ctx, tokenID)
2565		if err != nil {
2566			return "", errwrap.Wrapf("error decrementing wrapping token's use-count: {{err}}", err)
2567		}
2568
2569		defer b.Core.tokenStore.revokeOrphan(ctx, tokenID)
2570	}
2571
2572	cubbyReq := &logical.Request{
2573		Operation:   logical.ReadOperation,
2574		Path:        "cubbyhole/response",
2575		ClientToken: tokenID,
2576	}
2577	cubbyReq.SetTokenEntry(te)
2578	cubbyResp, err := b.Core.router.Route(ctx, cubbyReq)
2579	if err != nil {
2580		return "", errwrap.Wrapf("error looking up wrapping information: {{err}}", err)
2581	}
2582	if cubbyResp == nil {
2583		return "no information found; wrapping token may be from a previous Vault version", ErrInternalError
2584	}
2585	if cubbyResp != nil && cubbyResp.IsError() {
2586		return cubbyResp.Error().Error(), nil
2587	}
2588	if cubbyResp.Data == nil {
2589		return "wrapping information was nil; wrapping token may be from a previous Vault version", ErrInternalError
2590	}
2591
2592	responseRaw := cubbyResp.Data["response"]
2593	if responseRaw == nil {
2594		return "", fmt.Errorf("no response found inside the cubbyhole")
2595	}
2596	response, ok := responseRaw.(string)
2597	if !ok {
2598		return "", fmt.Errorf("could not decode response inside the cubbyhole")
2599	}
2600
2601	return response, nil
2602}
2603
2604func (b *SystemBackend) handleMetrics(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2605	format := data.Get("format").(string)
2606	if format == "" {
2607		format = metricsutil.FormatFromRequest(req)
2608	}
2609	return b.Core.metricsHelper.ResponseForFormat(format)
2610}
2611
2612func (b *SystemBackend) handleWrappingLookup(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2613	// This ordering of lookups has been validated already in the wrapping
2614	// validation func, we're just doing this for a safety check
2615	token := data.Get("token").(string)
2616	if token == "" {
2617		token = req.ClientToken
2618		if token == "" {
2619			return logical.ErrorResponse("missing \"token\" value in input"), logical.ErrInvalidRequest
2620		}
2621	}
2622
2623	te, err := b.Core.tokenStore.lookupTainted(ctx, token)
2624	if err != nil {
2625		return nil, err
2626	}
2627	if te == nil {
2628		return nil, nil
2629	}
2630	if len(te.Policies) != 1 {
2631		return nil, errors.New("token is not a valid unwrap token")
2632	}
2633
2634	cubbyReq := &logical.Request{
2635		Operation:   logical.ReadOperation,
2636		Path:        "cubbyhole/wrapinfo",
2637		ClientToken: token,
2638	}
2639	cubbyReq.SetTokenEntry(te)
2640	cubbyResp, err := b.Core.router.Route(ctx, cubbyReq)
2641	if err != nil {
2642		return nil, errwrap.Wrapf("error looking up wrapping information: {{err}}", err)
2643	}
2644	if cubbyResp == nil {
2645		return logical.ErrorResponse("no information found; wrapping token may be from a previous Vault version"), nil
2646	}
2647	if cubbyResp != nil && cubbyResp.IsError() {
2648		return cubbyResp, nil
2649	}
2650	if cubbyResp.Data == nil {
2651		return logical.ErrorResponse("wrapping information was nil; wrapping token may be from a previous Vault version"), nil
2652	}
2653
2654	creationTTLRaw := cubbyResp.Data["creation_ttl"]
2655	creationTime := cubbyResp.Data["creation_time"]
2656	creationPath := cubbyResp.Data["creation_path"]
2657
2658	resp := &logical.Response{
2659		Data: map[string]interface{}{},
2660	}
2661	if creationTTLRaw != nil {
2662		creationTTL, err := creationTTLRaw.(json.Number).Int64()
2663		if err != nil {
2664			return nil, errwrap.Wrapf("error reading creation_ttl value from wrapping information: {{err}}", err)
2665		}
2666		resp.Data["creation_ttl"] = time.Duration(creationTTL).Seconds()
2667	}
2668	if creationTime != nil {
2669		// This was JSON marshaled so it's already a string in RFC3339 format
2670		resp.Data["creation_time"] = cubbyResp.Data["creation_time"]
2671	}
2672	if creationPath != nil {
2673		resp.Data["creation_path"] = cubbyResp.Data["creation_path"]
2674	}
2675
2676	return resp, nil
2677}
2678
2679func (b *SystemBackend) handleWrappingRewrap(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2680	// If a third party is rewrapping (rather than the calling token being the
2681	// wrapping token) we detect this so that we can revoke the original
2682	// wrapping token after reading it. Right now wrapped tokens can't unwrap
2683	// themselves, but in case we change it, this will be ready to do the right
2684	// thing.
2685	var thirdParty bool
2686
2687	token := data.Get("token").(string)
2688	if token != "" {
2689		thirdParty = true
2690	} else {
2691		token = req.ClientToken
2692	}
2693
2694	te, err := b.Core.tokenStore.lookupTainted(ctx, token)
2695	if err != nil {
2696		return nil, err
2697	}
2698	if te == nil {
2699		return nil, nil
2700	}
2701	if len(te.Policies) != 1 {
2702		return nil, errors.New("token is not a valid unwrap token")
2703	}
2704
2705	if thirdParty {
2706		// Use the token to decrement the use count to avoid a second operation on the token.
2707		_, err := b.Core.tokenStore.UseTokenByID(ctx, token)
2708		if err != nil {
2709			return nil, errwrap.Wrapf("error decrementing wrapping token's use-count: {{err}}", err)
2710		}
2711		defer b.Core.tokenStore.revokeOrphan(ctx, token)
2712	}
2713
2714	// Fetch the original TTL
2715	cubbyReq := &logical.Request{
2716		Operation:   logical.ReadOperation,
2717		Path:        "cubbyhole/wrapinfo",
2718		ClientToken: token,
2719	}
2720	cubbyReq.SetTokenEntry(te)
2721	cubbyResp, err := b.Core.router.Route(ctx, cubbyReq)
2722	if err != nil {
2723		return nil, errwrap.Wrapf("error looking up wrapping information: {{err}}", err)
2724	}
2725	if cubbyResp == nil {
2726		return logical.ErrorResponse("no information found; wrapping token may be from a previous Vault version"), nil
2727	}
2728	if cubbyResp != nil && cubbyResp.IsError() {
2729		return cubbyResp, nil
2730	}
2731	if cubbyResp.Data == nil {
2732		return logical.ErrorResponse("wrapping information was nil; wrapping token may be from a previous Vault version"), nil
2733	}
2734
2735	// Set the creation TTL on the request
2736	creationTTLRaw := cubbyResp.Data["creation_ttl"]
2737	if creationTTLRaw == nil {
2738		return nil, fmt.Errorf("creation_ttl value in wrapping information was nil")
2739	}
2740	creationTTL, err := cubbyResp.Data["creation_ttl"].(json.Number).Int64()
2741	if err != nil {
2742		return nil, errwrap.Wrapf("error reading creation_ttl value from wrapping information: {{err}}", err)
2743	}
2744
2745	// Get creation_path to return as the response later
2746	creationPathRaw := cubbyResp.Data["creation_path"]
2747	if creationPathRaw == nil {
2748		return nil, fmt.Errorf("creation_path value in wrapping information was nil")
2749	}
2750	creationPath := creationPathRaw.(string)
2751
2752	// Fetch the original response and return it as the data for the new response
2753	cubbyReq = &logical.Request{
2754		Operation:   logical.ReadOperation,
2755		Path:        "cubbyhole/response",
2756		ClientToken: token,
2757	}
2758	cubbyReq.SetTokenEntry(te)
2759	cubbyResp, err = b.Core.router.Route(ctx, cubbyReq)
2760	if err != nil {
2761		return nil, errwrap.Wrapf("error looking up response: {{err}}", err)
2762	}
2763	if cubbyResp == nil {
2764		return logical.ErrorResponse("no information found; wrapping token may be from a previous Vault version"), nil
2765	}
2766	if cubbyResp != nil && cubbyResp.IsError() {
2767		return cubbyResp, nil
2768	}
2769	if cubbyResp.Data == nil {
2770		return logical.ErrorResponse("wrapping information was nil; wrapping token may be from a previous Vault version"), nil
2771	}
2772
2773	response := cubbyResp.Data["response"]
2774	if response == nil {
2775		return nil, fmt.Errorf("no response found inside the cubbyhole")
2776	}
2777
2778	// Return response in "response"; wrapping code will detect the rewrap and
2779	// slot in instead of nesting
2780	return &logical.Response{
2781		Data: map[string]interface{}{
2782			"response": response,
2783		},
2784		WrapInfo: &wrapping.ResponseWrapInfo{
2785			TTL:          time.Duration(creationTTL),
2786			CreationPath: creationPath,
2787		},
2788	}, nil
2789}
2790
2791func (b *SystemBackend) pathHashWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
2792	inputB64 := d.Get("input").(string)
2793	format := d.Get("format").(string)
2794	algorithm := d.Get("urlalgorithm").(string)
2795	if algorithm == "" {
2796		algorithm = d.Get("algorithm").(string)
2797	}
2798
2799	input, err := base64.StdEncoding.DecodeString(inputB64)
2800	if err != nil {
2801		return logical.ErrorResponse(fmt.Sprintf("unable to decode input as base64: %s", err)), logical.ErrInvalidRequest
2802	}
2803
2804	switch format {
2805	case "hex":
2806	case "base64":
2807	default:
2808		return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"hex\" or \"base64\"", format)), nil
2809	}
2810
2811	var hf hash.Hash
2812	switch algorithm {
2813	case "sha2-224":
2814		hf = sha256.New224()
2815	case "sha2-256":
2816		hf = sha256.New()
2817	case "sha2-384":
2818		hf = sha512.New384()
2819	case "sha2-512":
2820		hf = sha512.New()
2821	default:
2822		return logical.ErrorResponse(fmt.Sprintf("unsupported algorithm %s", algorithm)), nil
2823	}
2824	hf.Write(input)
2825	retBytes := hf.Sum(nil)
2826
2827	var retStr string
2828	switch format {
2829	case "hex":
2830		retStr = hex.EncodeToString(retBytes)
2831	case "base64":
2832		retStr = base64.StdEncoding.EncodeToString(retBytes)
2833	}
2834
2835	// Generate the response
2836	resp := &logical.Response{
2837		Data: map[string]interface{}{
2838			"sum": retStr,
2839		},
2840	}
2841	return resp, nil
2842}
2843
2844func (b *SystemBackend) pathRandomWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
2845	bytes := 0
2846	var err error
2847	strBytes := d.Get("urlbytes").(string)
2848	if strBytes != "" {
2849		bytes, err = strconv.Atoi(strBytes)
2850		if err != nil {
2851			return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
2852		}
2853	} else {
2854		bytes = d.Get("bytes").(int)
2855	}
2856	format := d.Get("format").(string)
2857
2858	if bytes < 1 {
2859		return logical.ErrorResponse(`"bytes" cannot be less than 1`), nil
2860	}
2861
2862	if bytes > maxBytes {
2863		return logical.ErrorResponse(`"bytes" should be less than %s`, maxBytes), nil
2864	}
2865
2866	switch format {
2867	case "hex":
2868	case "base64":
2869	default:
2870		return logical.ErrorResponse(fmt.Sprintf("unsupported encoding format %s; must be \"hex\" or \"base64\"", format)), nil
2871	}
2872
2873	randBytes, err := uuid.GenerateRandomBytes(bytes)
2874	if err != nil {
2875		return nil, err
2876	}
2877
2878	var retStr string
2879	switch format {
2880	case "hex":
2881		retStr = hex.EncodeToString(randBytes)
2882	case "base64":
2883		retStr = base64.StdEncoding.EncodeToString(randBytes)
2884	}
2885
2886	// Generate the response
2887	resp := &logical.Response{
2888		Data: map[string]interface{}{
2889			"random_bytes": retStr,
2890		},
2891	}
2892	return resp, nil
2893}
2894
2895func hasMountAccess(ctx context.Context, acl *ACL, path string) bool {
2896	ns, err := namespace.FromContext(ctx)
2897	if err != nil {
2898		return false
2899	}
2900
2901	// If a policy is giving us direct access to the mount path then we can do
2902	// a fast return.
2903	capabilities := acl.Capabilities(ctx, ns.TrimmedPath(path))
2904	if !strutil.StrListContains(capabilities, DenyCapability) {
2905		return true
2906	}
2907
2908	var aclCapabilitiesGiven bool
2909	walkFn := func(s string, v interface{}) bool {
2910		if v == nil {
2911			return false
2912		}
2913
2914		perms := v.(*ACLPermissions)
2915
2916		switch {
2917		case perms.CapabilitiesBitmap&DenyCapabilityInt > 0:
2918			return false
2919
2920		case perms.CapabilitiesBitmap&CreateCapabilityInt > 0,
2921			perms.CapabilitiesBitmap&DeleteCapabilityInt > 0,
2922			perms.CapabilitiesBitmap&ListCapabilityInt > 0,
2923			perms.CapabilitiesBitmap&ReadCapabilityInt > 0,
2924			perms.CapabilitiesBitmap&SudoCapabilityInt > 0,
2925			perms.CapabilitiesBitmap&UpdateCapabilityInt > 0:
2926
2927			aclCapabilitiesGiven = true
2928
2929			return true
2930		}
2931
2932		return false
2933	}
2934
2935	acl.exactRules.WalkPrefix(path, walkFn)
2936	if !aclCapabilitiesGiven {
2937		acl.prefixRules.WalkPrefix(path, walkFn)
2938	}
2939
2940	if !aclCapabilitiesGiven {
2941		if perms := acl.CheckAllowedFromNonExactPaths(path, true); perms != nil {
2942			return true
2943		}
2944	}
2945
2946	return aclCapabilitiesGiven
2947}
2948
2949func (b *SystemBackend) pathInternalUIMountsRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
2950	ns, err := namespace.FromContext(ctx)
2951	if err != nil {
2952		return nil, err
2953	}
2954
2955	resp := &logical.Response{
2956		Data: make(map[string]interface{}),
2957	}
2958
2959	secretMounts := make(map[string]interface{})
2960	authMounts := make(map[string]interface{})
2961	resp.Data["secret"] = secretMounts
2962	resp.Data["auth"] = authMounts
2963
2964	var acl *ACL
2965	var isAuthed bool
2966	if req.ClientToken != "" {
2967		isAuthed = true
2968
2969		var entity *identity.Entity
2970		var te *logical.TokenEntry
2971		// Load the ACL policies so we can walk the prefix for this mount
2972		acl, te, entity, _, err = b.Core.fetchACLTokenEntryAndEntity(ctx, req)
2973		if err != nil {
2974			return nil, err
2975		}
2976		if entity != nil && entity.Disabled {
2977			b.logger.Warn("permission denied as the entity on the token is disabled")
2978			return nil, logical.ErrPermissionDenied
2979		}
2980		if te != nil && te.EntityID != "" && entity == nil {
2981			b.logger.Warn("permission denied as the entity on the token is invalid")
2982			return nil, logical.ErrPermissionDenied
2983		}
2984	}
2985
2986	hasAccess := func(ctx context.Context, me *MountEntry) bool {
2987		if me.Config.ListingVisibility == ListingVisibilityUnauth {
2988			return true
2989		}
2990
2991		if isAuthed {
2992			return hasMountAccess(ctx, acl, me.Namespace().Path+me.Path)
2993		}
2994
2995		return false
2996	}
2997
2998	b.Core.mountsLock.RLock()
2999	for _, entry := range b.Core.mounts.Entries {
3000		filtered, err := b.Core.checkReplicatedFiltering(ctx, entry, "")
3001		if err != nil {
3002			b.Core.mountsLock.RUnlock()
3003			return nil, err
3004		}
3005		if filtered {
3006			continue
3007		}
3008
3009		if ns.ID == entry.NamespaceID && hasAccess(ctx, entry) {
3010			if isAuthed {
3011				// If this is an authed request return all the mount info
3012				secretMounts[entry.Path] = mountInfo(entry)
3013			} else {
3014				secretMounts[entry.Path] = map[string]interface{}{
3015					"type":        entry.Type,
3016					"description": entry.Description,
3017					"options":     entry.Options,
3018				}
3019			}
3020		}
3021	}
3022	b.Core.mountsLock.RUnlock()
3023
3024	b.Core.authLock.RLock()
3025	for _, entry := range b.Core.auth.Entries {
3026		filtered, err := b.Core.checkReplicatedFiltering(ctx, entry, credentialRoutePrefix)
3027		if err != nil {
3028			b.Core.authLock.RUnlock()
3029			return nil, err
3030		}
3031		if filtered {
3032			continue
3033		}
3034
3035		if ns.ID == entry.NamespaceID && hasAccess(ctx, entry) {
3036			if isAuthed {
3037				// If this is an authed request return all the mount info
3038				authMounts[entry.Path] = mountInfo(entry)
3039			} else {
3040				authMounts[entry.Path] = map[string]interface{}{
3041					"type":        entry.Type,
3042					"description": entry.Description,
3043					"options":     entry.Options,
3044				}
3045			}
3046		}
3047	}
3048	b.Core.authLock.RUnlock()
3049
3050	return resp, nil
3051}
3052
3053func (b *SystemBackend) pathInternalUIMountRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
3054	path := d.Get("path").(string)
3055	if path == "" {
3056		return logical.ErrorResponse("path not set"), logical.ErrInvalidRequest
3057	}
3058	path = sanitizeMountPath(path)
3059
3060	errResp := logical.ErrorResponse(fmt.Sprintf("preflight capability check returned 403, please ensure client's policies grant access to path %q", path))
3061
3062	ns, err := namespace.FromContext(ctx)
3063	if err != nil {
3064		return nil, err
3065	}
3066
3067	me := b.Core.router.MatchingMountEntry(ctx, path)
3068	if me == nil {
3069		// Return a permission denied error here so this path cannot be used to
3070		// brute force a list of mounts.
3071		return errResp, logical.ErrPermissionDenied
3072	}
3073
3074	filtered, err := b.Core.checkReplicatedFiltering(ctx, me, "")
3075	if err != nil {
3076		return nil, err
3077	}
3078	if filtered {
3079		return errResp, logical.ErrPermissionDenied
3080	}
3081
3082	resp := &logical.Response{
3083		Data: mountInfo(me),
3084	}
3085	resp.Data["path"] = me.Path
3086	if ns.ID != me.Namespace().ID {
3087		resp.Data["path"] = me.Namespace().Path + me.Path
3088	}
3089
3090	// Load the ACL policies so we can walk the prefix for this mount
3091	acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req)
3092	if err != nil {
3093		return nil, err
3094	}
3095	if entity != nil && entity.Disabled {
3096		b.logger.Warn("permission denied as the entity on the token is disabled")
3097		return errResp, logical.ErrPermissionDenied
3098	}
3099	if te != nil && te.EntityID != "" && entity == nil {
3100		b.logger.Warn("permission denied as the entity on the token is invalid")
3101		return nil, logical.ErrPermissionDenied
3102	}
3103
3104	if !hasMountAccess(ctx, acl, ns.Path+me.Path) {
3105		return errResp, logical.ErrPermissionDenied
3106	}
3107
3108	return resp, nil
3109}
3110
3111func (b *SystemBackend) pathInternalCountersRequests(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
3112	counters, err := b.Core.loadAllRequestCounters(ctx, time.Now())
3113	if err != nil {
3114		return nil, err
3115	}
3116
3117	resp := &logical.Response{
3118		Data: map[string]interface{}{
3119			"counters": counters,
3120		},
3121	}
3122
3123	return resp, nil
3124}
3125
3126func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
3127	if req.ClientToken == "" {
3128		// 204 -- no ACL
3129		return nil, nil
3130	}
3131
3132	acl, te, entity, _, err := b.Core.fetchACLTokenEntryAndEntity(ctx, req)
3133	if err != nil {
3134		return nil, err
3135	}
3136
3137	if entity != nil && entity.Disabled {
3138		b.logger.Warn("permission denied as the entity on the token is disabled")
3139		return logical.ErrorResponse(logical.ErrPermissionDenied.Error()), nil
3140	}
3141	if te != nil && te.EntityID != "" && entity == nil {
3142		b.logger.Warn("permission denied as the entity on the token is invalid")
3143		return logical.ErrorResponse(logical.ErrPermissionDenied.Error()), nil
3144	}
3145
3146	resp := &logical.Response{
3147		Data: map[string]interface{}{
3148			"root": false,
3149		},
3150	}
3151
3152	if acl.root {
3153		resp.Data["root"] = true
3154		return resp, nil
3155	}
3156
3157	exact := map[string]interface{}{}
3158	glob := map[string]interface{}{}
3159
3160	walkFn := func(pt map[string]interface{}, s string, v interface{}) {
3161		if v == nil {
3162			return
3163		}
3164
3165		perms := v.(*ACLPermissions)
3166		capabilities := []string{}
3167
3168		if perms.CapabilitiesBitmap&CreateCapabilityInt > 0 {
3169			capabilities = append(capabilities, CreateCapability)
3170		}
3171		if perms.CapabilitiesBitmap&DeleteCapabilityInt > 0 {
3172			capabilities = append(capabilities, DeleteCapability)
3173		}
3174		if perms.CapabilitiesBitmap&ListCapabilityInt > 0 {
3175			capabilities = append(capabilities, ListCapability)
3176		}
3177		if perms.CapabilitiesBitmap&ReadCapabilityInt > 0 {
3178			capabilities = append(capabilities, ReadCapability)
3179		}
3180		if perms.CapabilitiesBitmap&SudoCapabilityInt > 0 {
3181			capabilities = append(capabilities, SudoCapability)
3182		}
3183		if perms.CapabilitiesBitmap&UpdateCapabilityInt > 0 {
3184			capabilities = append(capabilities, UpdateCapability)
3185		}
3186
3187		// If "deny" is explicitly set or if the path has no capabilities at all,
3188		// set the path capabilities to "deny"
3189		if perms.CapabilitiesBitmap&DenyCapabilityInt > 0 || len(capabilities) == 0 {
3190			capabilities = []string{DenyCapability}
3191		}
3192
3193		res := map[string]interface{}{}
3194		if len(capabilities) > 0 {
3195			res["capabilities"] = capabilities
3196		}
3197		if perms.MinWrappingTTL != 0 {
3198			res["min_wrapping_ttl"] = int64(perms.MinWrappingTTL.Seconds())
3199		}
3200		if perms.MaxWrappingTTL != 0 {
3201			res["max_wrapping_ttl"] = int64(perms.MaxWrappingTTL.Seconds())
3202		}
3203		if len(perms.AllowedParameters) > 0 {
3204			res["allowed_parameters"] = perms.AllowedParameters
3205		}
3206		if len(perms.DeniedParameters) > 0 {
3207			res["denied_parameters"] = perms.DeniedParameters
3208		}
3209		if len(perms.RequiredParameters) > 0 {
3210			res["required_parameters"] = perms.RequiredParameters
3211		}
3212
3213		pt[s] = res
3214	}
3215
3216	exactWalkFn := func(s string, v interface{}) bool {
3217		walkFn(exact, s, v)
3218		return false
3219	}
3220
3221	globWalkFn := func(s string, v interface{}) bool {
3222		walkFn(glob, s, v)
3223		return false
3224	}
3225
3226	acl.exactRules.Walk(exactWalkFn)
3227	acl.prefixRules.Walk(globWalkFn)
3228
3229	resp.Data["exact_paths"] = exact
3230	resp.Data["glob_paths"] = glob
3231
3232	return resp, nil
3233}
3234
3235func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
3236
3237	// Limit output to authorized paths
3238	resp, err := b.pathInternalUIMountsRead(ctx, req, d)
3239	if err != nil {
3240		return nil, err
3241	}
3242
3243	context := d.Get("context").(string)
3244
3245	// Set up target document and convert to map[string]interface{} which is what will
3246	// be received from plugin backends.
3247	doc := framework.NewOASDocument()
3248
3249	procMountGroup := func(group, mountPrefix string) error {
3250		for mount := range resp.Data[group].(map[string]interface{}) {
3251			backend := b.Core.router.MatchingBackend(ctx, mountPrefix+mount)
3252
3253			if backend == nil {
3254				continue
3255			}
3256
3257			req := &logical.Request{
3258				Operation: logical.HelpOperation,
3259				Storage:   req.Storage,
3260			}
3261
3262			resp, err := backend.HandleRequest(ctx, req)
3263			if err != nil {
3264				return err
3265			}
3266
3267			var backendDoc *framework.OASDocument
3268
3269			// Normalize response type, which will be different if received
3270			// from an external plugin.
3271			switch v := resp.Data["openapi"].(type) {
3272			case *framework.OASDocument:
3273				backendDoc = v
3274			case map[string]interface{}:
3275				backendDoc, err = framework.NewOASDocumentFromMap(v)
3276				if err != nil {
3277					return err
3278				}
3279			default:
3280				continue
3281			}
3282
3283			// Prepare to add tags to default builtins that are
3284			// type "unknown" and won't already be tagged.
3285			var tag string
3286			switch mountPrefix + mount {
3287			case "cubbyhole/", "secret/":
3288				tag = "secrets"
3289			case "sys/":
3290				tag = "system"
3291			case "auth/token/":
3292				tag = "auth"
3293			case "identity/":
3294				tag = "identity"
3295			}
3296
3297			// Merge backend paths with existing document
3298			for path, obj := range backendDoc.Paths {
3299				path := strings.TrimPrefix(path, "/")
3300
3301				// Add tags to all of the operations if necessary
3302				if tag != "" {
3303					for _, op := range []*framework.OASOperation{obj.Get, obj.Post, obj.Delete} {
3304						// TODO: a special override for identity is used used here because the backend
3305						// is currently categorized as "secret", which will likely change. Also of interest
3306						// is removing all tag handling here and providing the mount information to OpenAPI.
3307						if op != nil && (len(op.Tags) == 0 || tag == "identity") {
3308							op.Tags = []string{tag}
3309						}
3310					}
3311				}
3312
3313				doc.Paths["/"+mountPrefix+mount+path] = obj
3314			}
3315		}
3316		return nil
3317	}
3318
3319	if err := procMountGroup("secret", ""); err != nil {
3320		return nil, err
3321	}
3322	if err := procMountGroup("auth", "auth/"); err != nil {
3323		return nil, err
3324	}
3325
3326	doc.CreateOperationIDs(context)
3327
3328	buf, err := json.Marshal(doc)
3329	if err != nil {
3330		return nil, err
3331	}
3332
3333	resp = &logical.Response{
3334		Data: map[string]interface{}{
3335			logical.HTTPStatusCode:  200,
3336			logical.HTTPRawBody:     buf,
3337			logical.HTTPContentType: "application/json",
3338		},
3339	}
3340
3341	return resp, nil
3342}
3343
3344func sanitizeMountPath(path string) string {
3345	if !strings.HasSuffix(path, "/") {
3346		path += "/"
3347	}
3348
3349	if strings.HasPrefix(path, "/") {
3350		path = path[1:]
3351	}
3352
3353	return path
3354}
3355
3356func checkListingVisibility(visibility ListingVisibilityType) error {
3357	switch visibility {
3358	case ListingVisibilityDefault:
3359	case ListingVisibilityHidden:
3360	case ListingVisibilityUnauth:
3361	default:
3362		return fmt.Errorf("invalid listing visilibity type")
3363	}
3364
3365	return nil
3366}
3367
3368const sysHelpRoot = `
3369The system backend is built-in to Vault and cannot be remounted or
3370unmounted. It contains the paths that are used to configure Vault itself
3371as well as perform core operations.
3372`
3373
3374// sysHelp is all the help text for the sys backend.
3375var sysHelp = map[string][2]string{
3376	"license": {
3377		"Sets the license of the server.",
3378		`
3379The path responds to the following HTTP methods.
3380
3381    GET /
3382        Returns information on the installed license
3383
3384    POST
3385        Sets the license for the server
3386	`,
3387	},
3388	"config/cors": {
3389		"Configures or returns the current configuration of CORS settings.",
3390		`
3391This path responds to the following HTTP methods.
3392
3393    GET /
3394        Returns the configuration of the CORS setting.
3395
3396    POST /
3397        Sets the comma-separated list of origins that can make cross-origin requests.
3398
3399    DELETE /
3400        Clears the CORS configuration and disables acceptance of CORS requests.
3401		`,
3402	},
3403	"config/ui/headers": {
3404		"Configures response headers that should be returned from the UI.",
3405		`
3406This path responds to the following HTTP methods.
3407    GET /<header>
3408        Returns the header value.
3409    POST /<header>
3410        Sets the header value for the UI.
3411    DELETE /<header>
3412        Clears the header value for UI.
3413
3414    LIST /
3415        List the headers configured for the UI.
3416        `,
3417	},
3418	"init": {
3419		"Initializes or returns the initialization status of the Vault.",
3420		`
3421This path responds to the following HTTP methods.
3422
3423    GET /
3424        Returns the initialization status of the Vault.
3425
3426    POST /
3427        Initializes a new vault.
3428		`,
3429	},
3430	"generate-root": {
3431		"Reads, generates, or deletes a root token regeneration process.",
3432		`
3433This path responds to multiple HTTP methods which change the behavior. Those
3434HTTP methods are listed below.
3435
3436    GET /attempt
3437        Reads the configuration and progress of the current root generation
3438        attempt.
3439
3440    POST /attempt
3441        Initializes a new root generation attempt. Only a single root generation
3442        attempt can take place at a time. One (and only one) of otp or pgp_key
3443        are required.
3444
3445    DELETE /attempt
3446        Cancels any in-progress root generation attempt. This clears any
3447        progress made. This must be called to change the OTP or PGP key being
3448        used.
3449		`,
3450	},
3451	"seal-status": {
3452		"Returns the seal status of the Vault.",
3453		`
3454This path responds to the following HTTP methods.
3455
3456    GET /
3457        Returns the seal status of the Vault. This is an unauthenticated
3458        endpoint.
3459		`,
3460	},
3461	"seal": {
3462		"Seals the Vault.",
3463		`
3464This path responds to the following HTTP methods.
3465
3466    PUT /
3467        Seals the Vault.
3468		`,
3469	},
3470	"unseal": {
3471		"Unseals the Vault.",
3472		`
3473This path responds to the following HTTP methods.
3474
3475    PUT /
3476        Unseals the Vault.
3477		`,
3478	},
3479	"mounts": {
3480		"List the currently mounted backends.",
3481		`
3482This path responds to the following HTTP methods.
3483
3484    GET /
3485        Lists all the mounted secret backends.
3486
3487    GET /<mount point>
3488        Get information about the mount at the specified path.
3489
3490    POST /<mount point>
3491        Mount a new secret backend to the mount point in the URL.
3492
3493    POST /<mount point>/tune
3494        Tune configuration parameters for the given mount point.
3495
3496    DELETE /<mount point>
3497        Unmount the specified mount point.
3498		`,
3499	},
3500
3501	"mount": {
3502		`Mount a new backend at a new path.`,
3503		`
3504Mount a backend at a new path. A backend can be mounted multiple times at
3505multiple paths in order to configure multiple separately configured backends.
3506Example: you might have an AWS backend for the east coast, and one for the
3507west coast.
3508		`,
3509	},
3510
3511	"mount_path": {
3512		`The path to mount to. Example: "aws/east"`,
3513		"",
3514	},
3515
3516	"mount_type": {
3517		`The type of the backend. Example: "passthrough"`,
3518		"",
3519	},
3520
3521	"mount_desc": {
3522		`User-friendly description for this mount.`,
3523		"",
3524	},
3525
3526	"mount_config": {
3527		`Configuration for this mount, such as default_lease_ttl
3528and max_lease_ttl.`,
3529	},
3530
3531	"mount_local": {
3532		`Mark the mount as a local mount, which is not replicated
3533and is unaffected by replication.`,
3534	},
3535
3536	"mount_plugin_name": {
3537		`Name of the plugin to mount based from the name registered
3538in the plugin catalog.`,
3539	},
3540
3541	"mount_options": {
3542		`The options to pass into the backend. Should be a json object with string keys and values.`,
3543	},
3544
3545	"seal_wrap": {
3546		`Whether to turn on seal wrapping for the mount.`,
3547	},
3548
3549	"tune_default_lease_ttl": {
3550		`The default lease TTL for this mount.`,
3551	},
3552
3553	"tune_max_lease_ttl": {
3554		`The max lease TTL for this mount.`,
3555	},
3556
3557	"tune_audit_non_hmac_request_keys": {
3558		`The list of keys in the request data object that will not be HMAC'ed by audit devices.`,
3559	},
3560
3561	"tune_audit_non_hmac_response_keys": {
3562		`The list of keys in the response data object that will not be HMAC'ed by audit devices.`,
3563	},
3564
3565	"tune_mount_options": {
3566		`The options to pass into the backend. Should be a json object with string keys and values.`,
3567	},
3568
3569	"remount": {
3570		"Move the mount point of an already-mounted backend.",
3571		`
3572This path responds to the following HTTP methods.
3573
3574    POST /sys/remount
3575        Changes the mount point of an already-mounted backend.
3576		`,
3577	},
3578
3579	"auth_tune": {
3580		"Tune the configuration parameters for an auth path.",
3581		`Read and write the 'default-lease-ttl' and 'max-lease-ttl' values of
3582the auth path.`,
3583	},
3584
3585	"mount_tune": {
3586		"Tune backend configuration parameters for this mount.",
3587		`Read and write the 'default-lease-ttl' and 'max-lease-ttl' values of
3588the mount.`,
3589	},
3590
3591	"renew": {
3592		"Renew a lease on a secret",
3593		`
3594When a secret is read, it may optionally include a lease interval
3595and a boolean indicating if renew is possible. For secrets that support
3596lease renewal, this endpoint is used to extend the validity of the
3597lease and to prevent an automatic revocation.
3598		`,
3599	},
3600
3601	"lease_id": {
3602		"The lease identifier to renew. This is included with a lease.",
3603		"",
3604	},
3605
3606	"increment": {
3607		"The desired increment in seconds to the lease",
3608		"",
3609	},
3610
3611	"revoke": {
3612		"Revoke a leased secret immediately",
3613		`
3614When a secret is generated with a lease, it is automatically revoked
3615at the end of the lease period if not renewed. However, in some cases
3616you may want to force an immediate revocation. This endpoint can be
3617used to revoke the secret with the given Lease ID.
3618		`,
3619	},
3620
3621	"revoke-sync": {
3622		"Whether or not to perform the revocation synchronously",
3623		`
3624If false, the call will return immediately and revocation will be queued; if it
3625fails, Vault will keep trying. If true, if the revocation fails, Vault will not
3626automatically try again and will return an error. For revoke-prefix, this
3627setting will apply to all leases being revoked. For revoke-force, since errors
3628are ignored, this setting is not supported.
3629`,
3630	},
3631
3632	"revoke-prefix": {
3633		"Revoke all secrets generated in a given prefix",
3634		`
3635Revokes all the secrets generated under a given mount prefix. As
3636an example, "prod/aws/" might be the AWS logical backend, and due to
3637a change in the "ops" policy, we may want to invalidate all the secrets
3638generated. We can do a revoke prefix at "prod/aws/ops" to revoke all
3639the ops secrets. This does a prefix match on the Lease IDs and revokes
3640all matching leases.
3641		`,
3642	},
3643
3644	"revoke-prefix-path": {
3645		`The path to revoke keys under. Example: "prod/aws/ops"`,
3646		"",
3647	},
3648
3649	"revoke-force": {
3650		"Revoke all secrets generated in a given prefix, ignoring errors.",
3651		`
3652See the path help for 'revoke-prefix'; this behaves the same, except that it
3653ignores errors encountered during revocation. This can be used in certain
3654recovery situations; for instance, when you want to unmount a backend, but it
3655is impossible to fix revocation errors and these errors prevent the unmount
3656from proceeding. This is a DANGEROUS operation as it removes Vault's oversight
3657of external secrets. Access to this prefix should be tightly controlled.
3658		`,
3659	},
3660
3661	"revoke-force-path": {
3662		`The path to revoke keys under. Example: "prod/aws/ops"`,
3663		"",
3664	},
3665
3666	"auth-table": {
3667		"List the currently enabled credential backends.",
3668		`
3669This path responds to the following HTTP methods.
3670
3671    GET /
3672        List the currently enabled credential backends: the name, the type of
3673        the backend, and a user friendly description of the purpose for the
3674        credential backend.
3675
3676    POST /<mount point>
3677        Enable a new auth method.
3678
3679    DELETE /<mount point>
3680        Disable the auth method at the given mount point.
3681		`,
3682	},
3683
3684	"auth": {
3685		`Enable a new credential backend with a name.`,
3686		`
3687Enable a credential mechanism at a new path. A backend can be mounted multiple times at
3688multiple paths in order to configure multiple separately configured backends.
3689Example: you might have an OAuth backend for GitHub, and one for Google Apps.
3690		`,
3691	},
3692
3693	"auth_path": {
3694		`The path to mount to. Cannot be delimited. Example: "user"`,
3695		"",
3696	},
3697
3698	"auth_type": {
3699		`The type of the backend. Example: "userpass"`,
3700		"",
3701	},
3702
3703	"auth_desc": {
3704		`User-friendly description for this credential backend.`,
3705		"",
3706	},
3707
3708	"auth_config": {
3709		`Configuration for this mount, such as plugin_name.`,
3710	},
3711
3712	"auth_plugin": {
3713		`Name of the auth plugin to use based from the name in the plugin catalog.`,
3714		"",
3715	},
3716
3717	"auth_options": {
3718		`The options to pass into the backend. Should be a json object with string keys and values.`,
3719	},
3720
3721	"policy-list": {
3722		`List the configured access control policies.`,
3723		`
3724This path responds to the following HTTP methods.
3725
3726    GET /
3727        List the names of the configured access control policies.
3728
3729    GET /<name>
3730        Retrieve the rules for the named policy.
3731
3732    PUT /<name>
3733        Add or update a policy.
3734
3735    DELETE /<name>
3736        Delete the policy with the given name.
3737		`,
3738	},
3739
3740	"policy": {
3741		`Read, Modify, or Delete an access control policy.`,
3742		`
3743Read the rules of an existing policy, create or update the rules of a policy,
3744or delete a policy.
3745		`,
3746	},
3747
3748	"policy-name": {
3749		`The name of the policy. Example: "ops"`,
3750		"",
3751	},
3752
3753	"policy-rules": {
3754		`The rules of the policy.`,
3755		"",
3756	},
3757
3758	"policy-paths": {
3759		`The paths on which the policy should be applied.`,
3760		"",
3761	},
3762
3763	"policy-enforcement-level": {
3764		`The enforcement level to apply to the policy.`,
3765		"",
3766	},
3767
3768	"audit-hash": {
3769		"The hash of the given string via the given audit backend",
3770		"",
3771	},
3772
3773	"audit-table": {
3774		"List the currently enabled audit backends.",
3775		`
3776This path responds to the following HTTP methods.
3777
3778    GET /
3779        List the currently enabled audit backends.
3780
3781    PUT /<path>
3782        Enable an audit backend at the given path.
3783
3784    DELETE /<path>
3785        Disable the given audit backend.
3786		`,
3787	},
3788
3789	"audit_path": {
3790		`The name of the backend. Cannot be delimited. Example: "mysql"`,
3791		"",
3792	},
3793
3794	"audit_type": {
3795		`The type of the backend. Example: "mysql"`,
3796		"",
3797	},
3798
3799	"audit_desc": {
3800		`User-friendly description for this audit backend.`,
3801		"",
3802	},
3803
3804	"audit_opts": {
3805		`Configuration options for the audit backend.`,
3806		"",
3807	},
3808
3809	"audit": {
3810		`Enable or disable audit backends.`,
3811		`
3812Enable a new audit backend or disable an existing backend.
3813		`,
3814	},
3815
3816	"key-status": {
3817		"Provides information about the backend encryption key.",
3818		`
3819		Provides the current backend encryption key term and installation time.
3820		`,
3821	},
3822
3823	"rotate": {
3824		"Rotates the backend encryption key used to persist data.",
3825		`
3826		Rotate generates a new encryption key which is used to encrypt all
3827		data going to the storage backend. The old encryption keys are kept so
3828		that data encrypted using those keys can still be decrypted.
3829		`,
3830	},
3831
3832	"rekey_backup": {
3833		"Allows fetching or deleting the backup of the rotated unseal keys.",
3834		"",
3835	},
3836
3837	"capabilities": {
3838		"Fetches the capabilities of the given token on the given path.",
3839		`Returns the capabilities of the given token on the path.
3840		The path will be searched for a path match in all the policies associated with the token.`,
3841	},
3842
3843	"capabilities_self": {
3844		"Fetches the capabilities of the given token on the given path.",
3845		`Returns the capabilities of the client token on the path.
3846		The path will be searched for a path match in all the policies associated with the client token.`,
3847	},
3848
3849	"capabilities_accessor": {
3850		"Fetches the capabilities of the token associated with the given token, on the given path.",
3851		`When there is no access to the token, token accessor can be used to fetch the token's capabilities
3852		on a given path.`,
3853	},
3854
3855	"tidy_leases": {
3856		`This endpoint performs cleanup tasks that can be run if certain error
3857conditions have occurred.`,
3858		`This endpoint performs cleanup tasks that can be run to clean up the
3859lease entries after certain error conditions. Usually running this is not
3860necessary, and is only required if upgrade notes or support personnel suggest
3861it.`,
3862	},
3863
3864	"wrap": {
3865		"Response-wraps an arbitrary JSON object.",
3866		`Round trips the given input data into a response-wrapped token.`,
3867	},
3868
3869	"wrappubkey": {
3870		"Returns pubkeys used in some wrapping formats.",
3871		"Returns pubkeys used in some wrapping formats.",
3872	},
3873
3874	"unwrap": {
3875		"Unwraps a response-wrapped token.",
3876		`Unwraps a response-wrapped token. Unlike simply reading from cubbyhole/response,
3877		this provides additional validation on the token, and rather than a JSON-escaped
3878		string, the returned response is the exact same as the contained wrapped response.`,
3879	},
3880
3881	"wraplookup": {
3882		"Looks up the properties of a response-wrapped token.",
3883		`Returns the creation TTL and creation time of a response-wrapped token.`,
3884	},
3885
3886	"rewrap": {
3887		"Rotates a response-wrapped token.",
3888		`Rotates a response-wrapped token; the output is a new token with the same
3889		response wrapped inside and the same creation TTL. The original token is revoked.`,
3890	},
3891	"audited-headers-name": {
3892		"Configures the headers sent to the audit logs.",
3893		`
3894This path responds to the following HTTP methods.
3895
3896	GET /<name>
3897		Returns the setting for the header with the given name.
3898
3899	POST /<name>
3900		Enable auditing of the given header.
3901
3902	DELETE /<path>
3903		Disable auditing of the given header.
3904		`,
3905	},
3906	"audited-headers": {
3907		"Lists the headers configured to be audited.",
3908		`Returns a list of headers that have been configured to be audited.`,
3909	},
3910	"plugin-catalog-list-all": {
3911		"Lists all the plugins known to Vault",
3912		`
3913This path responds to the following HTTP methods.
3914		LIST /
3915			Returns a list of names of configured plugins.
3916		`,
3917	},
3918	"plugin-catalog": {
3919		"Configures the plugins known to Vault",
3920		`
3921This path responds to the following HTTP methods.
3922		LIST /
3923			Returns a list of names of configured plugins.
3924
3925		GET /<name>
3926			Retrieve the metadata for the named plugin.
3927
3928		PUT /<name>
3929			Add or update plugin.
3930
3931		DELETE /<name>
3932			Delete the plugin with the given name.
3933		`,
3934	},
3935	"plugin-catalog_name": {
3936		"The name of the plugin",
3937		"",
3938	},
3939	"plugin-catalog_type": {
3940		"The type of the plugin, may be auth, secret, or database",
3941		"",
3942	},
3943	"plugin-catalog_sha-256": {
3944		`The SHA256 sum of the executable used in the
3945command field. This should be HEX encoded.`,
3946		"",
3947	},
3948	"plugin-catalog_command": {
3949		`The command used to start the plugin. The
3950executable defined in this command must exist in vault's
3951plugin directory.`,
3952		"",
3953	},
3954	"plugin-catalog_args": {
3955		`The args passed to plugin command.`,
3956		"",
3957	},
3958	"plugin-catalog_env": {
3959		`The environment variables passed to plugin command.
3960Each entry is of the form "key=value".`,
3961		"",
3962	},
3963	"leases": {
3964		`View or list lease metadata.`,
3965		`
3966This path responds to the following HTTP methods.
3967
3968    PUT /
3969        Retrieve the metadata for the provided lease id.
3970
3971    LIST /<prefix>
3972        Lists the leases for the named prefix.
3973		`,
3974	},
3975
3976	"leases-list-prefix": {
3977		`The path to list leases under. Example: "aws/creds/deploy"`,
3978		"",
3979	},
3980	"plugin-reload": {
3981		"Reload mounts that use a particular backend plugin.",
3982		`Reload mounts that use a particular backend plugin. Either the plugin name
3983		or the desired plugin backend mounts must be provided, but not both. In the
3984		case that the plugin name is provided, all mounted paths that use that plugin
3985		backend will be reloaded.`,
3986	},
3987	"plugin-backend-reload-plugin": {
3988		`The name of the plugin to reload, as registered in the plugin catalog.`,
3989		"",
3990	},
3991	"plugin-backend-reload-mounts": {
3992		`The mount paths of the plugin backends to reload.`,
3993		"",
3994	},
3995	"hash": {
3996		"Generate a hash sum for input data",
3997		"Generates a hash sum of the given algorithm against the given input data.",
3998	},
3999	"random": {
4000		"Generate random bytes",
4001		"This function can be used to generate high-entropy random bytes.",
4002	},
4003	"listing_visibility": {
4004		"Determines the visibility of the mount in the UI-specific listing endpoint. Accepted value are 'unauth' and ''.",
4005		"",
4006	},
4007	"passthrough_request_headers": {
4008		"A list of headers to whitelist and pass from the request to the plugin.",
4009		"",
4010	},
4011	"allowed_response_headers": {
4012		"A list of headers to whitelist and allow a plugin to set on responses.",
4013		"",
4014	},
4015	"token_type": {
4016		"The type of token to issue (service or batch).",
4017		"",
4018	},
4019	"raw": {
4020		"Write, Read, and Delete data directly in the Storage backend.",
4021		"",
4022	},
4023	"internal-ui-mounts": {
4024		"Information about mounts returned according to their tuned visibility. Internal API; its location, inputs, and outputs may change.",
4025		"",
4026	},
4027	"internal-ui-namespaces": {
4028		"Information about visible child namespaces. Internal API; its location, inputs, and outputs may change.",
4029		`Information about visible child namespaces returned starting from the request's
4030		context namespace and filtered based on access from the client token. Internal API;
4031		its location, inputs, and outputs may change.`,
4032	},
4033	"internal-ui-resultant-acl": {
4034		"Information about a token's resultant ACL. Internal API; its location, inputs, and outputs may change.",
4035		"",
4036	},
4037	"metrics": {
4038		"Export the metrics aggregated for telemetry purpose.",
4039		"",
4040	},
4041	"internal-counters-requests": {
4042		"Count of requests seen by this Vault cluster over time.",
4043		"Count of requests seen by this Vault cluster over time. Not included in count: health checks, UI asset requests, requests forwarded from another cluster.",
4044	},
4045}
4046