-
Notifications
You must be signed in to change notification settings - Fork 291
/
auth_sys.go
250 lines (208 loc) · 6.1 KB
/
auth_sys.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
//
// (C) Copyright 2018-2024 Intel Corporation.
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
package auth
import (
"bytes"
"crypto"
"os"
"os/user"
"strconv"
"strings"
"github.com/pkg/errors"
"google.golang.org/protobuf/proto"
"github.com/daos-stack/daos/src/control/security"
)
// User is an interface wrapping a representation of a specific system user.
type User interface {
Username() string
GroupIDs() ([]uint32, error)
Gid() (uint32, error)
}
// UserExt is an interface that wraps system user-related external functions.
type UserExt interface {
Current() (User, error)
LookupUserID(uid uint32) (User, error)
LookupGroupID(gid uint32) (*user.Group, error)
}
// UserInfo is an exported implementation of the security.User interface.
type UserInfo struct {
Info *user.User
}
// Username is a wrapper for user.Username.
func (u *UserInfo) Username() string {
return u.Info.Username
}
// GroupIDs is a wrapper for user.GroupIds.
func (u *UserInfo) GroupIDs() ([]uint32, error) {
gidStrs, err := u.Info.GroupIds()
if err != nil {
return nil, err
}
gids := []uint32{}
for _, gstr := range gidStrs {
gid, err := strconv.Atoi(gstr)
if err != nil {
continue
}
gids = append(gids, uint32(gid))
}
return gids, nil
}
// Gid is a wrapper for user.Gid.
func (u *UserInfo) Gid() (uint32, error) {
gid, err := strconv.Atoi(u.Info.Gid)
return uint32(gid), errors.Wrap(err, "user gid")
}
// External is an exported implementation of the UserExt interface.
type External struct{}
// LookupUserId is a wrapper for user.LookupId.
func (e *External) LookupUserID(uid uint32) (User, error) {
uidStr := strconv.FormatUint(uint64(uid), 10)
info, err := user.LookupId(uidStr)
if err != nil {
return nil, err
}
return &UserInfo{Info: info}, nil
}
// LookupGroupId is a wrapper for user.LookupGroupId.
func (e *External) LookupGroupID(gid uint32) (*user.Group, error) {
gidStr := strconv.FormatUint(uint64(gid), 10)
return user.LookupGroupId(gidStr)
}
// Current is a wrapper for user.Current.
func (e *External) Current() (User, error) {
info, err := user.Current()
if err != nil {
return nil, err
}
return &UserInfo{Info: info}, nil
}
// VerifierFromToken will return a SHA512 hash of the token data. If a signing key
// is passed in it will additionally sign the hash of the token.
func VerifierFromToken(key crypto.PublicKey, token *Token) ([]byte, error) {
var sig []byte
tokenBytes, err := proto.Marshal(token)
if err != nil {
return nil, errors.Wrap(err, "unable to marshal Token")
}
signer := security.DefaultTokenSigner()
if key == nil {
return signer.Hash(tokenBytes)
}
sig, err = signer.Sign(key, tokenBytes)
return sig, errors.Wrap(err, "signing verifier failed")
}
// VerifyToken takes the auth token and the signature bytes in the verifier and
// verifies it against the public key provided for the agent who claims to have
// provided the token.
func VerifyToken(key crypto.PublicKey, token *Token, sig []byte) error {
tokenBytes, err := proto.Marshal(token)
if err != nil {
return errors.Wrap(err, "unable to marshal Token")
}
signer := security.DefaultTokenSigner()
if key == nil {
digest, err := signer.Hash(tokenBytes)
if err != nil {
return err
}
if bytes.Equal(digest, sig) {
return nil
}
return errors.Errorf("unsigned hash failed to verify.")
}
err = signer.Verify(key, tokenBytes, sig)
return errors.Wrap(err, "token verification Failed")
}
func sysNameToPrincipalName(name string) string {
return name + "@"
}
// GetMachineName returns the "short" hostname by stripping the domain from the FQDN.
func GetMachineName() (string, error) {
name, err := os.Hostname()
if err != nil {
return "", err
}
return strings.Split(name, ".")[0], nil
}
// AuthSysRequestFromCreds takes the domain info credentials gathered
// during the dRPC request and creates an AuthSys security request to obtain
// a handle from the management service.
func AuthSysRequestFromCreds(ext UserExt, creds *security.DomainInfo, signing crypto.PrivateKey) (*Credential, error) {
if creds == nil {
return nil, errors.New("No credentials supplied")
}
userInfo, err := ext.LookupUserID(creds.Uid())
if err != nil {
return nil, errors.Wrapf(err, "Failed to lookup uid %v",
creds.Uid())
}
groupInfo, err := ext.LookupGroupID(creds.Gid())
if err != nil {
return nil, errors.Wrapf(err, "Failed to lookup gid %v",
creds.Gid())
}
groups, err := userInfo.GroupIDs()
if err != nil {
return nil, errors.Wrapf(err, "Failed to get group IDs for user %v",
userInfo.Username())
}
host, err := GetMachineName()
if err != nil {
host = "unavailable"
}
var groupList = []string{}
// Convert groups to gids
for _, gid := range groups {
gInfo, err := ext.LookupGroupID(gid)
if err != nil {
// Skip this group
continue
}
groupList = append(groupList, sysNameToPrincipalName(gInfo.Name))
}
// Craft AuthToken
sys := Sys{
Stamp: 0,
Machinename: host,
User: sysNameToPrincipalName(userInfo.Username()),
Group: sysNameToPrincipalName(groupInfo.Name),
Groups: groupList,
Secctx: creds.Ctx()}
// Marshal our AuthSys token into a byte array
tokenBytes, err := proto.Marshal(&sys)
if err != nil {
return nil, errors.Wrap(err, "Unable to marshal AuthSys token")
}
token := Token{
Flavor: Flavor_AUTH_SYS,
Data: tokenBytes}
verifier, err := VerifierFromToken(signing, &token)
if err != nil {
return nil, errors.WithMessage(err, "Unable to generate verifier")
}
verifierToken := Token{
Flavor: Flavor_AUTH_SYS,
Data: verifier}
credential := Credential{
Token: &token,
Verifier: &verifierToken,
Origin: "agent"}
return &credential, nil
}
// AuthSysFromAuthToken takes an opaque AuthToken and turns it into a
// concrete AuthSys data structure.
func AuthSysFromAuthToken(authToken *Token) (*Sys, error) {
if authToken.GetFlavor() != Flavor_AUTH_SYS {
return nil, errors.New("Attempting to convert an invalid AuthSys Token")
}
sysToken := &Sys{}
err := proto.Unmarshal(authToken.GetData(), sysToken)
if err != nil {
return nil, errors.Wrapf(err, "unmarshaling %s", authToken.GetFlavor())
}
return sysToken, nil
}