1package approle
2
3import (
4	"context"
5	"sync"
6
7	"github.com/hashicorp/vault/sdk/framework"
8	"github.com/hashicorp/vault/sdk/helper/consts"
9	"github.com/hashicorp/vault/sdk/helper/locksutil"
10	"github.com/hashicorp/vault/sdk/helper/salt"
11	"github.com/hashicorp/vault/sdk/logical"
12)
13
14const (
15	secretIDPrefix              = "secret_id/"
16	secretIDLocalPrefix         = "secret_id_local/"
17	secretIDAccessorPrefix      = "accessor/"
18	secretIDAccessorLocalPrefix = "accessor_local/"
19)
20
21type backend struct {
22	*framework.Backend
23
24	// The salt value to be used by the information to be accessed only
25	// by this backend.
26	salt      *salt.Salt
27	saltMutex sync.RWMutex
28
29	// The view to use when creating the salt
30	view logical.Storage
31
32	// Guard to clean-up the expired SecretID entries
33	tidySecretIDCASGuard *uint32
34
35	// Locks to make changes to role entries. These will be initialized to a
36	// predefined number of locks when the backend is created, and will be
37	// indexed based on salted role names.
38	roleLocks []*locksutil.LockEntry
39
40	// Locks to make changes to the storage entries of RoleIDs generated. These
41	// will be initialized to a predefined number of locks when the backend is
42	// created, and will be indexed based on the salted RoleIDs.
43	roleIDLocks []*locksutil.LockEntry
44
45	// Locks to make changes to the storage entries of SecretIDs generated.
46	// These will be initialized to a predefined number of locks when the
47	// backend is created, and will be indexed based on the HMAC-ed SecretIDs.
48	secretIDLocks []*locksutil.LockEntry
49
50	// Locks to make changes to the storage entries of SecretIDAccessors
51	// generated. These will be initialized to a predefined number of locks
52	// when the backend is created, and will be indexed based on the
53	// SecretIDAccessors itself.
54	secretIDAccessorLocks []*locksutil.LockEntry
55
56	// secretIDListingLock is a dedicated lock for listing SecretIDAccessors
57	// for all the SecretIDs issued against an approle
58	secretIDListingLock sync.RWMutex
59}
60
61func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
62	b, err := Backend(conf)
63	if err != nil {
64		return nil, err
65	}
66	if err := b.Setup(ctx, conf); err != nil {
67		return nil, err
68	}
69	return b, nil
70}
71
72func Backend(conf *logical.BackendConfig) (*backend, error) {
73	// Create a backend object
74	b := &backend{
75		view: conf.StorageView,
76
77		// Create locks to modify the registered roles
78		roleLocks: locksutil.CreateLocks(),
79
80		// Create locks to modify the generated RoleIDs
81		roleIDLocks: locksutil.CreateLocks(),
82
83		// Create locks to modify the generated SecretIDs
84		secretIDLocks: locksutil.CreateLocks(),
85
86		// Create locks to modify the generated SecretIDAccessors
87		secretIDAccessorLocks: locksutil.CreateLocks(),
88
89		tidySecretIDCASGuard: new(uint32),
90	}
91
92	// Attach the paths and secrets that are to be handled by the backend
93	b.Backend = &framework.Backend{
94		// Register a periodic function that deletes the expired SecretID entries
95		PeriodicFunc: b.periodicFunc,
96		Help:         backendHelp,
97		AuthRenew:    b.pathLoginRenew,
98		PathsSpecial: &logical.Paths{
99			Unauthenticated: []string{
100				"login",
101			},
102			LocalStorage: []string{
103				secretIDLocalPrefix,
104				secretIDAccessorLocalPrefix,
105			},
106		},
107		Paths: framework.PathAppend(
108			rolePaths(b),
109			[]*framework.Path{
110				pathLogin(b),
111				pathTidySecretID(b),
112			},
113		),
114		Invalidate:  b.invalidate,
115		BackendType: logical.TypeCredential,
116	}
117	return b, nil
118}
119
120func (b *backend) Salt(ctx context.Context) (*salt.Salt, error) {
121	b.saltMutex.RLock()
122	if b.salt != nil {
123		defer b.saltMutex.RUnlock()
124		return b.salt, nil
125	}
126	b.saltMutex.RUnlock()
127	b.saltMutex.Lock()
128	defer b.saltMutex.Unlock()
129	if b.salt != nil {
130		return b.salt, nil
131	}
132	salt, err := salt.NewSalt(ctx, b.view, &salt.Config{
133		HashFunc: salt.SHA256Hash,
134		Location: salt.DefaultLocation,
135	})
136	if err != nil {
137		return nil, err
138	}
139	b.salt = salt
140	return salt, nil
141}
142
143func (b *backend) invalidate(_ context.Context, key string) {
144	switch key {
145	case salt.DefaultLocation:
146		b.saltMutex.Lock()
147		defer b.saltMutex.Unlock()
148		b.salt = nil
149	}
150}
151
152// periodicFunc of the backend will be invoked once a minute by the RollbackManager.
153// RoleRole backend utilizes this function to delete expired SecretID entries.
154// This could mean that the SecretID may live in the backend upto 1 min after its
155// expiration. The deletion of SecretIDs are not security sensitive and it is okay
156// to delay the removal of SecretIDs by a minute.
157func (b *backend) periodicFunc(ctx context.Context, req *logical.Request) error {
158	// Initiate clean-up of expired SecretID entries
159	if b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary|consts.ReplicationPerformanceStandby) {
160		b.tidySecretID(ctx, req)
161	}
162	return nil
163}
164
165const backendHelp = `
166Any registered Role can authenticate itself with Vault. The credentials
167depends on the constraints that are set on the Role. One common required
168credential is the 'role_id' which is a unique identifier of the Role.
169It can be retrieved from the 'role/<appname>/role-id' endpoint.
170
171The default constraint configuration is 'bind_secret_id', which requires
172the credential 'secret_id' to be presented during login. Refer to the
173documentation for other types of constraints.`
174