1package validate
2
3import (
4	"errors"
5	"fmt"
6	"strings"
7
8	"github.com/opencontainers/runc/libcontainer/configs"
9)
10
11// rootlessEUID makes sure that the config can be applied when runc
12// is being executed as a non-root user (euid != 0) in the current user namespace.
13func (v *ConfigValidator) rootlessEUID(config *configs.Config) error {
14	if err := rootlessEUIDMappings(config); err != nil {
15		return err
16	}
17	if err := rootlessEUIDMount(config); err != nil {
18		return err
19	}
20
21	// XXX: We currently can't verify the user config at all, because
22	//      configs.Config doesn't store the user-related configs. So this
23	//      has to be verified by setupUser() in init_linux.go.
24
25	return nil
26}
27
28func hasIDMapping(id int, mappings []configs.IDMap) bool {
29	for _, m := range mappings {
30		if id >= m.ContainerID && id < m.ContainerID+m.Size {
31			return true
32		}
33	}
34	return false
35}
36
37func rootlessEUIDMappings(config *configs.Config) error {
38	if !config.Namespaces.Contains(configs.NEWUSER) {
39		return errors.New("rootless container requires user namespaces")
40	}
41
42	if len(config.UidMappings) == 0 {
43		return errors.New("rootless containers requires at least one UID mapping")
44	}
45	if len(config.GidMappings) == 0 {
46		return errors.New("rootless containers requires at least one GID mapping")
47	}
48	return nil
49}
50
51// mount verifies that the user isn't trying to set up any mounts they don't have
52// the rights to do. In addition, it makes sure that no mount has a `uid=` or
53// `gid=` option that doesn't resolve to root.
54func rootlessEUIDMount(config *configs.Config) error {
55	// XXX: We could whitelist allowed devices at this point, but I'm not
56	//      convinced that's a good idea. The kernel is the best arbiter of
57	//      access control.
58
59	for _, mount := range config.Mounts {
60		// Check that the options list doesn't contain any uid= or gid= entries
61		// that don't resolve to root.
62		for _, opt := range strings.Split(mount.Data, ",") {
63			if strings.HasPrefix(opt, "uid=") {
64				var uid int
65				n, err := fmt.Sscanf(opt, "uid=%d", &uid)
66				if n != 1 || err != nil {
67					// Ignore unknown mount options.
68					continue
69				}
70				if !hasIDMapping(uid, config.UidMappings) {
71					return errors.New("cannot specify uid= mount options for unmapped uid in rootless containers")
72				}
73			}
74
75			if strings.HasPrefix(opt, "gid=") {
76				var gid int
77				n, err := fmt.Sscanf(opt, "gid=%d", &gid)
78				if n != 1 || err != nil {
79					// Ignore unknown mount options.
80					continue
81				}
82				if !hasIDMapping(gid, config.GidMappings) {
83					return errors.New("cannot specify gid= mount options for unmapped gid in rootless containers")
84				}
85			}
86		}
87	}
88
89	return nil
90}
91