summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/lookup/lookup.go156
-rw-r--r--pkg/spec/config_linux.go2
-rw-r--r--pkg/spec/parse.go14
3 files changed, 164 insertions, 8 deletions
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go
new file mode 100644
index 000000000..b27e2a724
--- /dev/null
+++ b/pkg/lookup/lookup.go
@@ -0,0 +1,156 @@
+package lookup
+
+import (
+ "github.com/cyphar/filepath-securejoin"
+ "github.com/opencontainers/runc/libcontainer/user"
+ "github.com/sirupsen/logrus"
+ "strconv"
+)
+
+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
+ )
+ passwdPath := etcpasswd
+ groupPath := etcgroup
+
+ if override != nil {
+ // Check for an override /etc/passwd path
+ if override.ContainerEtcPasswdPath != "" {
+ passwdPath = override.ContainerEtcPasswdPath
+ }
+ // Check for an override for /etc/group path
+ if override.ContainerEtcGroupPath != "" {
+ groupPath = override.ContainerEtcGroupPath
+ }
+ }
+
+ // 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
+
+ }
+
+ // Make sure the /etc/group and /etc/passwd destinations are not a symlink to something naughty
+ if passwdDest, err = securejoin.SecureJoin(containerMount, passwdPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ 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.
+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 {
+ return nil, err
+ }
+ if len(users) > 0 {
+ return &users[0], nil
+ }
+ return nil, user.ErrNoPasswdEntries
+}
+
+// GetGroup takes ac ontainermount 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.
+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 {
+ return nil, err
+ }
+ if len(groups) > 0 {
+ return &groups[0], nil
+ }
+ return nil, user.ErrNoGroupEntries
+}
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index 20cdcc458..5bf8eff43 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -28,7 +28,7 @@ func Device(d *configs.Device) spec.LinuxDevice {
}
func addDevice(g *generate.Generator, device string) error {
- src, dst, permissions, err := parseDevice(device)
+ src, dst, permissions, err := ParseDevice(device)
if err != nil {
return err
}
diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go
index 9b2dd1347..4d20e35d4 100644
--- a/pkg/spec/parse.go
+++ b/pkg/spec/parse.go
@@ -148,21 +148,21 @@ func getLoggingPath(opts []string) string {
return ""
}
-// parseDevice parses device mapping string to a src, dest & permissions string
-func parseDevice(device string) (string, string, string, error) { //nolint
+// ParseDevice parses device mapping string to a src, dest & permissions string
+func ParseDevice(device string) (string, string, string, error) { //nolint
src := ""
dst := ""
permissions := "rwm"
arr := strings.Split(device, ":")
switch len(arr) {
case 3:
- if !validDeviceMode(arr[2]) {
+ if !IsValidDeviceMode(arr[2]) {
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2])
}
permissions = arr[2]
fallthrough
case 2:
- if validDeviceMode(arr[1]) {
+ if IsValidDeviceMode(arr[1]) {
permissions = arr[1]
} else {
if arr[1][0] != '/' {
@@ -183,9 +183,9 @@ func parseDevice(device string) (string, string, string, error) { //nolint
return src, dst, permissions, nil
}
-// validDeviceMode checks if the mode for device is valid or not.
-// Valid mode is a composition of r (read), w (write), and m (mknod).
-func validDeviceMode(mode string) bool {
+// IsValidDeviceMode checks if the mode for device is valid or not.
+// IsValid mode is a composition of r (read), w (write), and m (mknod).
+func IsValidDeviceMode(mode string) bool {
var legalDeviceMode = map[rune]bool{
'r': true,
'w': true,