// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package auth import ( "gvisor.googlesource.com/gvisor/pkg/abi/linux" "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/syserror" ) // MapFromKUID translates kuid, a UID in the root namespace, to a UID in ns. func (ns *UserNamespace) MapFromKUID(kuid KUID) UID { if ns.parent == nil { return UID(kuid) } return UID(ns.mapID(&ns.uidMapFromParent, uint32(ns.parent.MapFromKUID(kuid)))) } // MapFromKGID translates kgid, a GID in the root namespace, to a GID in ns. func (ns *UserNamespace) MapFromKGID(kgid KGID) GID { if ns.parent == nil { return GID(kgid) } return GID(ns.mapID(&ns.gidMapFromParent, uint32(ns.parent.MapFromKGID(kgid)))) } // MapToKUID translates uid, a UID in ns, to a UID in the root namespace. func (ns *UserNamespace) MapToKUID(uid UID) KUID { if ns.parent == nil { return KUID(uid) } return ns.parent.MapToKUID(UID(ns.mapID(&ns.uidMapToParent, uint32(uid)))) } // MapToKGID translates gid, a GID in ns, to a GID in the root namespace. func (ns *UserNamespace) MapToKGID(gid GID) KGID { if ns.parent == nil { return KGID(gid) } return ns.parent.MapToKGID(GID(ns.mapID(&ns.gidMapToParent, uint32(gid)))) } func (ns *UserNamespace) mapID(m *idMapSet, id uint32) uint32 { if id == NoID { return NoID } ns.mu.Lock() defer ns.mu.Unlock() if it := m.FindSegment(id); it.Ok() { return it.Value() + (id - it.Start()) } return NoID } // allIDsMapped returns true if all IDs in the range [start, end) are mapped in // m. // // Preconditions: end >= start. func (ns *UserNamespace) allIDsMapped(m *idMapSet, start, end uint32) bool { ns.mu.Lock() defer ns.mu.Unlock() return m.SpanRange(idMapRange{start, end}) == end-start } // An IDMapEntry represents a mapping from a range of contiguous IDs in a user // namespace to an equally-sized range of contiguous IDs in the namespace's // parent. type IDMapEntry struct { // FirstID is the first ID in the range in the namespace. FirstID uint32 // FirstParentID is the first ID in the range in the parent namespace. FirstParentID uint32 // Length is the number of IDs in the range. Length uint32 } // SetUIDMap instructs ns to translate UIDs as specified by entries. // // Note: SetUIDMap does not place an upper bound on the number of entries, but // Linux does. This restriction is implemented in SetUIDMap's caller, the // implementation of /proc/[pid]/uid_map. func (ns *UserNamespace) SetUIDMap(ctx context.Context, entries []IDMapEntry) error { c := CredentialsFromContext(ctx) ns.mu.Lock() defer ns.mu.Unlock() // "After the creation of a new user namespace, the uid_map file of *one* // of the processes in the namespace may be written to *once* to define the // mapping of user IDs in the new user namespace. An attempt to write more // than once to a uid_map file in a user namespace fails with the error // EPERM. Similar rules apply for gid_map files." - user_namespaces(7) if !ns.uidMapFromParent.IsEmpty() { return syserror.EPERM } // "At least one line must be written to the file." if len(entries) == 0 { return syserror.EINVAL } // """ // In order for a process to write to the /proc/[pid]/uid_map // (/proc/[pid]/gid_map) file, all of the following requirements must be // met: // // 1. The writing process must have the CAP_SETUID (CAP_SETGID) capability // in the user namespace of the process pid. // """ if !c.HasCapabilityIn(linux.CAP_SETUID, ns) { return syserror.EPERM } // "2. The writing process must either be in the user namespace of the process // pid or be in the parent user namespace of the process pid." if c.UserNamespace != ns && c.UserNamespace != ns.parent { return syserror.EPERM } // """ // 3. (see trySetUIDMap) // // 4. One of the following two cases applies: // // * Either the writing process has the CAP_SETUID (CAP_SETGID) capability // in the parent user namespace. // """ if !c.HasCapabilityIn(linux.CAP_SETUID, ns.parent) { // """ // * Or otherwise all of the following restrictions apply: // // + The data written to uid_map (gid_map) must consist of a single line // that maps the writing process' effective user ID (group ID) in the // parent user namespace to a user ID (group ID) in the user namespace. // """ if len(entries) != 1 || ns.parent.MapToKUID(UID(entries[0].FirstParentID)) != c.EffectiveKUID || entries[0].Length != 1 { return syserror.EPERM } // """ // + The writing process must have the same effective user ID as the // process that created the user namespace. // """ if c.EffectiveKUID != ns.owner { return syserror.EPERM } } // trySetUIDMap leaves data in maps if it fails. if err := ns.trySetUIDMap(entries); err != nil { ns.uidMapFromParent.RemoveAll() ns.uidMapToParent.RemoveAll() return err } return nil } func (ns *UserNamespace) trySetUIDMap(entries []IDMapEntry) error { for _, e := range entries { // Determine upper bounds and check for overflow. This implicitly // checks for NoID. lastID := e.FirstID + e.Length if lastID <= e.FirstID { return syserror.EINVAL } lastParentID := e.FirstParentID + e.Length if lastParentID <= e.FirstParentID { return syserror.EINVAL } // "3. The mapped user IDs (group IDs) must in turn have a mapping in // the parent user namespace." // Only the root namespace has a nil parent, and root is assigned // mappings when it's created, so SetUIDMap would have returned EPERM // without reaching this point if ns is root. if !ns.parent.allIDsMapped(&ns.parent.uidMapToParent, e.FirstParentID, lastParentID) { return syserror.EPERM } // If either of these Adds fail, we have an overlapping range. if !ns.uidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) { return syserror.EINVAL } if !ns.uidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) { return syserror.EINVAL } } return nil } // SetGIDMap instructs ns to translate GIDs as specified by entries. func (ns *UserNamespace) SetGIDMap(ctx context.Context, entries []IDMapEntry) error { c := CredentialsFromContext(ctx) ns.mu.Lock() defer ns.mu.Unlock() if !ns.gidMapFromParent.IsEmpty() { return syserror.EPERM } if len(entries) == 0 { return syserror.EINVAL } if !c.HasCapabilityIn(linux.CAP_SETGID, ns) { return syserror.EPERM } if c.UserNamespace != ns && c.UserNamespace != ns.parent { return syserror.EPERM } if !c.HasCapabilityIn(linux.CAP_SETGID, ns.parent) { if len(entries) != 1 || ns.parent.MapToKGID(GID(entries[0].FirstParentID)) != c.EffectiveKGID || entries[0].Length != 1 { return syserror.EPERM } // It's correct for this to still be UID. if c.EffectiveKUID != ns.owner { return syserror.EPERM } // "In the case of gid_map, use of the setgroups(2) system call must // first be denied by writing "deny" to the /proc/[pid]/setgroups file // (see below) before writing to gid_map." (This file isn't implemented // in the version of Linux we're emulating; see comment in // UserNamespace.) } if err := ns.trySetGIDMap(entries); err != nil { ns.gidMapFromParent.RemoveAll() ns.gidMapToParent.RemoveAll() return err } return nil } func (ns *UserNamespace) trySetGIDMap(entries []IDMapEntry) error { for _, e := range entries { lastID := e.FirstID + e.Length if lastID <= e.FirstID { return syserror.EINVAL } lastParentID := e.FirstParentID + e.Length if lastParentID <= e.FirstParentID { return syserror.EINVAL } if !ns.parent.allIDsMapped(&ns.parent.gidMapToParent, e.FirstParentID, lastParentID) { return syserror.EPERM } if !ns.gidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) { return syserror.EINVAL } if !ns.gidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) { return syserror.EINVAL } } return nil } // UIDMap returns the user ID mappings configured for ns. If no mappings // have been configured, UIDMap returns nil. func (ns *UserNamespace) UIDMap() []IDMapEntry { return ns.getIDMap(&ns.uidMapToParent) } // GIDMap returns the group ID mappings configured for ns. If no mappings // have been configured, GIDMap returns nil. func (ns *UserNamespace) GIDMap() []IDMapEntry { return ns.getIDMap(&ns.gidMapToParent) } func (ns *UserNamespace) getIDMap(m *idMapSet) []IDMapEntry { ns.mu.Lock() defer ns.mu.Unlock() var entries []IDMapEntry for it := m.FirstSegment(); it.Ok(); it = it.NextSegment() { entries = append(entries, IDMapEntry{ FirstID: it.Start(), FirstParentID: it.Value(), Length: it.Range().Length(), }) } return entries }