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
|
package lookup
import (
"os"
"strconv"
"github.com/cyphar/filepath-securejoin"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/sirupsen/logrus"
)
const (
etcpasswd = "/etc/passwd"
etcgroup = "/etc/group"
)
// Overrides allows you to override defaults in GetUserGroupInfo
type Overrides struct {
DefaultUser *user.ExecUser
ContainerEtcPasswdPath string
ContainerEtcGroupPath string
}
// GetUserGroupInfo takes string forms of the the container's mount path and the container user and
// returns a ExecUser with uid, gid, sgids, and home. And override can be provided for defaults.
func GetUserGroupInfo(containerMount, containerUser string, override *Overrides) (*user.ExecUser, error) {
var (
passwdDest, groupDest string
defaultExecUser *user.ExecUser
err error
)
if override != nil {
// Check for an override /etc/passwd path
if override.ContainerEtcPasswdPath != "" {
passwdDest = override.ContainerEtcPasswdPath
}
// Check for an override for /etc/group path
if override.ContainerEtcGroupPath != "" {
groupDest = override.ContainerEtcGroupPath
}
}
if passwdDest == "" {
// Make sure the /etc/passwd destination is not a symlink to something naughty
if passwdDest, err = securejoin.SecureJoin(containerMount, etcpasswd); err != nil {
logrus.Debug(err)
return nil, err
}
}
if groupDest == "" {
// Make sure the /etc/group destination is not a symlink to something naughty
if groupDest, err = securejoin.SecureJoin(containerMount, etcgroup); err != nil {
logrus.Debug(err)
return nil, err
}
}
// Check for an override default user
if override != nil && override.DefaultUser != nil {
defaultExecUser = override.DefaultUser
} else {
// Define a default container user
//defaultExecUser = &user.ExecUser{
// Uid: 0,
// Gid: 0,
// Home: "/",
defaultExecUser = nil
}
return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest)
}
// GetContainerGroups uses securejoin to get a list of numerical groupids from a container. Per the runc
// function it calls: If a group name cannot be found, an error will be returned. If a group id cannot be found,
// or the given group data is nil, the id will be returned as-is provided it is in the legal range.
func GetContainerGroups(groups []string, containerMount string, override *Overrides) ([]uint32, error) {
var (
groupDest string
err error
uintgids []uint32
)
groupPath := etcgroup
if override != nil && override.ContainerEtcGroupPath != "" {
groupPath = override.ContainerEtcGroupPath
}
if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
logrus.Debug(err)
return nil, err
}
gids, err := user.GetAdditionalGroupsPath(groups, groupDest)
if err != nil {
return nil, err
}
// For libpod, we want []uint32s
for _, gid := range gids {
uintgids = append(uintgids, uint32(gid))
}
return uintgids, nil
}
// GetUser takes a containermount path and user name or ID and returns
// a matching User structure from /etc/passwd. If it cannot locate a user
// with the provided information, an ErrNoPasswdEntries is returned.
// When the provided user name was an ID, a User structure with Uid
// set is returned along with ErrNoPasswdEntries.
func GetUser(containerMount, userIDorName string) (*user.User, error) {
var inputIsName bool
uid, err := strconv.Atoi(userIDorName)
if err != nil {
inputIsName = true
}
passwdDest, err := securejoin.SecureJoin(containerMount, etcpasswd)
if err != nil {
return nil, err
}
users, err := user.ParsePasswdFileFilter(passwdDest, func(u user.User) bool {
if inputIsName {
return u.Name == userIDorName
}
return u.Uid == uid
})
if err != nil && !os.IsNotExist(err) {
return nil, err
}
if len(users) > 0 {
return &users[0], nil
}
if !inputIsName {
return &user.User{Uid: uid}, user.ErrNoPasswdEntries
}
return nil, user.ErrNoPasswdEntries
}
// GetGroup takes a containermount path and a group name or ID and returns
// a match Group struct from /etc/group. If it cannot locate a group,
// an ErrNoGroupEntries error is returned. When the provided group name
// was an ID, a Group structure with Gid set is returned along with
// ErrNoGroupEntries.
func GetGroup(containerMount, groupIDorName string) (*user.Group, error) {
var inputIsName bool
gid, err := strconv.Atoi(groupIDorName)
if err != nil {
inputIsName = true
}
groupDest, err := securejoin.SecureJoin(containerMount, etcgroup)
if err != nil {
return nil, err
}
groups, err := user.ParseGroupFileFilter(groupDest, func(g user.Group) bool {
if inputIsName {
return g.Name == groupIDorName
}
return g.Gid == gid
})
if err != nil && !os.IsNotExist(err) {
return nil, err
}
if len(groups) > 0 {
return &groups[0], nil
}
if !inputIsName {
return &user.Group{Gid: gid}, user.ErrNoGroupEntries
}
return nil, user.ErrNoGroupEntries
}
|