-
Notifications
You must be signed in to change notification settings - Fork 51
/
user.go
144 lines (131 loc) · 3.86 KB
/
user.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
// Copyright (c) 2014-2020 Canonical Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package osutil
import (
"fmt"
"os"
"os/user"
"strconv"
"github.com/canonical/pebble/internals/osutil/sys"
)
var (
userCurrent = user.Current
userLookup = user.Lookup
userLookupGroup = user.LookupGroup
)
// RealUser finds the user behind a sudo invocation when root, if applicable
// and possible.
//
// Don't check SUDO_USER when not root and simply return the current uid
// to properly support sudo'ing from root to a non-root user
func RealUser() (*user.User, error) {
cur, err := userCurrent()
if err != nil {
return nil, err
}
// not root, so no sudo invocation we care about
if cur.Uid != "0" {
return cur, nil
}
realName := os.Getenv("SUDO_USER")
if realName == "" {
// not sudo; current is correct
return cur, nil
}
real, err := user.Lookup(realName)
// can happen when sudo is used to enter a chroot (e.g. pbuilder)
if _, ok := err.(user.UnknownUserError); ok {
return cur, nil
}
if err != nil {
return nil, err
}
return real, nil
}
// UidGid returns the uid and gid of the given user, as uint32s
//
// XXX this should go away soon
func UidGid(u *user.User) (sys.UserID, sys.GroupID, error) {
// XXX this will be wrong for high uids on 32-bit arches (for now)
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return sys.FlagID, sys.FlagID, fmt.Errorf("cannot parse user id %s: %s", u.Uid, err)
}
gid, err := strconv.Atoi(u.Gid)
if err != nil {
return sys.FlagID, sys.FlagID, fmt.Errorf("cannot parse group id %s: %s", u.Gid, err)
}
return sys.UserID(uid), sys.GroupID(gid), nil
}
// NormalizeUidGid returns the "normalized" UID and GID for the given IDs and
// names. If both uid and username are specified, the username's UID must match
// the given uid (similar for gid and group), otherwise an error is returned.
func NormalizeUidGid(uid, gid *int, username, group string) (*int, *int, error) {
if uid == nil && username == "" && gid == nil && group == "" {
return nil, nil, nil
}
if username != "" {
u, err := userLookup(username)
if err != nil {
return nil, nil, err
}
n, _ := strconv.Atoi(u.Uid)
if uid != nil && *uid != n {
return nil, nil, fmt.Errorf("user %q UID (%d) does not match user-id (%d)",
username, n, *uid)
}
uid = &n
if gid == nil && group == "" {
// Group not specified; use user's primary group ID
gidVal, _ := strconv.Atoi(u.Gid)
gid = &gidVal
}
}
if group != "" {
g, err := userLookupGroup(group)
if err != nil {
return nil, nil, err
}
n, _ := strconv.Atoi(g.Gid)
if gid != nil && *gid != n {
return nil, nil, fmt.Errorf("group %q GID (%d) does not match group-id (%d)",
group, n, *gid)
}
gid = &n
}
if uid == nil && gid != nil {
return nil, nil, fmt.Errorf("must specify user, not just group")
}
if uid != nil && gid == nil {
return nil, nil, fmt.Errorf("must specify group, not just UID")
}
return uid, gid, nil
}
// IsCurrent reports whether the given user ID and group ID are those of the
// current user.
func IsCurrent(uid, gid int) (bool, error) {
current, err := userCurrent()
if err != nil {
return false, err
}
currentUid, err := strconv.Atoi(current.Uid)
if err != nil {
return false, err
}
currentGid, err := strconv.Atoi(current.Gid)
if err != nil {
return false, err
}
return uid == currentUid && gid == currentGid, nil
}