forked from jsipprell/keyctl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keyring.go
302 lines (253 loc) · 7.59 KB
/
keyring.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// Copyright 2015 Jesse Sipprell. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux
// A Go interface to linux kernel keyrings (keyctl interface)
package keyutils
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"syscall"
)
// All Keys and Keyrings have unique 32-bit serial number identifiers.
type Id interface {
Id() int32
Info() (Info, error)
private()
}
// Basic interface to a linux keyctl keyring.
type Keyring interface {
Id
Add(string, []byte) (*Key, error)
AddType(string, string, []byte) (*Key, error)
Search(string) (*Key, error)
SearchType(string, string) (*Key, error)
SetDefaultTimeout(uint)
AttachPersistent() (Keyring, error)
}
// Named keyrings are user-created keyrings linked to a parent keyring. The
// parent can be either named or one of the in-built keyrings (session, group
// etc). The in-built keyrings have no parents. Keyring searching is performed
// hierarchically.
type NamedKeyring interface {
Keyring
Name() string
}
type keyring struct {
id keyId
defaultTtl uint
}
type namedKeyring struct {
*keyring
parent keyId
name string // for non-anonymous keyrings
ttl uint
}
func (kr *keyring) private() {}
// Returns the 32-bit kernel identifier of a keyring
func (kr *keyring) Id() int32 {
return int32(kr.id)
}
// Returns information about a keyring.
func (kr *keyring) Info() (Info, error) {
return getInfo(kr.id)
}
// Return the name of a NamedKeyring that was set when the keyring was created
// or opened.
func (kr *namedKeyring) Name() string {
return kr.name
}
// Set a default timeout, in seconds, after which newly added keys will be
// destroyed.
func (kr *keyring) SetDefaultTimeout(nsecs uint) {
kr.defaultTtl = nsecs
}
// Add a new key to a keyring. The key can be searched for later by name.
func (kr *keyring) Add(name string, key []byte) (*Key, error) {
return kr.AddType(name, "user", key)
}
// Add a new key to a keyring with a specific key type. The key can be searched
// for later by name.
func (kr *keyring) AddType(name string, keyType string, key []byte) (*Key, error) {
r, err := add_key(keyType, name, key, int32(kr.id))
if err == nil {
key := &Key{Name: name, id: keyId(r), ring: kr.id}
if kr.defaultTtl != 0 {
err = key.ExpireAfter(kr.defaultTtl)
}
return key, err
}
return nil, err
}
// Search for a key by name, this also searches child keyrings linked to this
// one. The key, if found, is linked to the top keyring that Search() was called
// from.
func (kr *keyring) Search(name string) (*Key, error) {
return kr.SearchType(name, "user")
}
// Search for a key by name and type, this also searches child keyrings linked
// to this one. The key, if found, is linked to the top keyring that SearchType()
// was called from.
func (kr *keyring) SearchType(name string, keyType string) (*Key, error) {
id, err := searchKeyring(kr.id, name, keyType)
if err == nil {
return &Key{Name: name, id: id, ring: kr.id}, nil
}
return nil, err
}
// Return the current login session keyring
func SessionKeyring() (Keyring, error) {
return newKeyring(keySpecSessionKeyring)
}
// Return the current user-session keyring (part of session, but private to
// current user)
func UserSessionKeyring() (Keyring, error) {
return newKeyring(keySpecUserSessionKeyring)
}
// Return the current user keyring.
func UserKeyring() (Keyring, error) {
return newKeyring(keySpecUserKeyring)
}
// Return the current group keyring.
func GroupKeyring() (Keyring, error) {
return newKeyring(keySpecGroupKeyring)
}
// Return the keyring specific to the current executing thread.
func ThreadKeyring() (Keyring, error) {
return newKeyring(keySpecThreadKeyring)
}
// Return the keyring specific to the current executing process.
func ProcessKeyring() (Keyring, error) {
return newKeyring(keySpecProcessKeyring)
}
// Return a named global keyring.
func GlobalKeyring(name string) (NamedKeyring, error) {
id, err := requestKey("keyring", name, 0)
if err != nil || id < 0 {
f, err := os.Open("/proc/keys")
if err != nil {
return nil, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < 9 {
continue
}
if fields[3] == "expd" {
continue
}
keyType := fields[7]
if keyType != "keyring" {
continue
}
keyDesc := fields[8]
if !strings.HasPrefix(keyDesc, name) {
continue
}
idInt, err := strconv.ParseInt(fields[0], 16, 32)
if err != nil {
return nil, fmt.Errorf("error parsing key id: %w", err)
}
id = keyId(idInt)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading /proc/keys: %w", err)
}
}
if id < 0 {
return nil, fmt.Errorf("key not found: %w", syscall.ENOKEY)
}
keyring, err := newKeyring(id)
if err != nil {
return nil, err
}
return &namedKeyring{
keyring: keyring,
name: name,
}, nil
}
// Creates a new named-keyring linked to a parent keyring. The parent may be
// one of those returned by SessionKeyring(), UserSessionKeyring() and friends
// or it may be an existing named-keyring. When searching is performed, all
// keyrings form a hierarchy and are searched top-down. If the keyring already
// exists it will be destroyed and a new one with the same name created. Named
// sub-keyrings inherit their initial ttl (if set) from the parent but can
// outlive the parent as the timer is restarted at creation.
func CreateKeyring(parent Keyring, name string) (NamedKeyring, error) {
var ttl uint
parentId := keyId(parent.Id())
kr, err := createKeyring(parentId, name)
if err != nil {
return nil, err
}
if pkr, ok := parent.(*namedKeyring); ok {
ttl = pkr.ttl
}
ring := &namedKeyring{
keyring: kr,
parent: parentId,
name: name,
ttl: ttl,
}
if ttl > 0 {
if err := keyctl_SetTimeout(ring.id, ttl); err != nil {
return nil, err
}
}
return ring, nil
}
// Search for and open an existing keyring with the given name linked to a
// parent keyring (at any depth).
func OpenKeyring(parent Keyring, name string) (NamedKeyring, error) {
parentId := keyId(parent.Id())
id, err := searchKeyring(parentId, name, "keyring")
if err != nil {
return nil, err
}
return &namedKeyring{
keyring: &keyring{id: id},
parent: parentId,
name: name,
}, nil
}
// Set the time to live in seconds for an entire keyring and all of its keys.
// Only named keyrings can have their time-to-live set, the in-built keyrings
// cannot (Session, UserSession, etc).
func SetKeyringTTL(kr NamedKeyring, nsecs uint) error {
err := keyctl_SetTimeout(keyId(kr.Id()), nsecs)
if err == nil {
kr.(*namedKeyring).ttl = nsecs
}
return err
}
// Link an object to a keyring.
func Link(parent Keyring, child Id) error {
return keyctl_Link(keyId(child.Id()), keyId(parent.Id()))
}
// Unlink an object from a keyring.
func Unlink(parent Keyring, child Id) error {
return keyctl_Unlink(keyId(child.Id()), keyId(parent.Id()))
}
// Unlink a named keyring from its parent.
func UnlinkKeyring(kr NamedKeyring) error {
return keyctl_Unlink(keyId(kr.Id()), kr.(*namedKeyring).parent)
}
// Move a key from one keyring to another.
func Move(source Keyring, dest Keyring, child Id, excl bool) error {
var flags uint
if excl {
flags = keyctlMoveExcl
}
return moveKey(keyId(child.Id()), keyId(source.Id()), keyId(dest.Id()), flags)
}
// AttachPersistent attaches the current executing context's persistent
// keyring to this keyring. See persistent-keyring(7) for more info.
// It returns either an error, or the persistent Keyring.
func (kr *keyring) AttachPersistent() (Keyring, error) {
return attachPersistent(kr.id)
}