github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/kernel/auth/id_map.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package auth 16 17 import ( 18 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 19 "github.com/nicocha30/gvisor-ligolo/pkg/context" 20 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 21 ) 22 23 // MapFromKUID translates kuid, a UID in the root namespace, to a UID in ns. 24 func (ns *UserNamespace) MapFromKUID(kuid KUID) UID { 25 if ns.parent == nil { 26 return UID(kuid) 27 } 28 return UID(ns.mapID(&ns.uidMapFromParent, uint32(ns.parent.MapFromKUID(kuid)))) 29 } 30 31 // MapFromKGID translates kgid, a GID in the root namespace, to a GID in ns. 32 func (ns *UserNamespace) MapFromKGID(kgid KGID) GID { 33 if ns.parent == nil { 34 return GID(kgid) 35 } 36 return GID(ns.mapID(&ns.gidMapFromParent, uint32(ns.parent.MapFromKGID(kgid)))) 37 } 38 39 // MapToKUID translates uid, a UID in ns, to a UID in the root namespace. 40 func (ns *UserNamespace) MapToKUID(uid UID) KUID { 41 if ns.parent == nil { 42 return KUID(uid) 43 } 44 return ns.parent.MapToKUID(UID(ns.mapID(&ns.uidMapToParent, uint32(uid)))) 45 } 46 47 // MapToKGID translates gid, a GID in ns, to a GID in the root namespace. 48 func (ns *UserNamespace) MapToKGID(gid GID) KGID { 49 if ns.parent == nil { 50 return KGID(gid) 51 } 52 return ns.parent.MapToKGID(GID(ns.mapID(&ns.gidMapToParent, uint32(gid)))) 53 } 54 55 func (ns *UserNamespace) mapID(m *idMapSet, id uint32) uint32 { 56 if id == NoID { 57 return NoID 58 } 59 ns.mu.Lock() 60 defer ns.mu.Unlock() 61 if it := m.FindSegment(id); it.Ok() { 62 return it.Value() + (id - it.Start()) 63 } 64 return NoID 65 } 66 67 // allIDsMapped returns true if all IDs in the range [start, end) are mapped in 68 // m. 69 // 70 // Preconditions: end >= start. 71 func (ns *UserNamespace) allIDsMapped(m *idMapSet, start, end uint32) bool { 72 ns.mu.NestedLock(userNamespaceLockNs) 73 defer ns.mu.NestedUnlock(userNamespaceLockNs) 74 return m.SpanRange(idMapRange{start, end}) == end-start 75 } 76 77 // An IDMapEntry represents a mapping from a range of contiguous IDs in a user 78 // namespace to an equally-sized range of contiguous IDs in the namespace's 79 // parent. 80 // 81 // +stateify savable 82 type IDMapEntry struct { 83 // FirstID is the first ID in the range in the namespace. 84 FirstID uint32 85 86 // FirstParentID is the first ID in the range in the parent namespace. 87 FirstParentID uint32 88 89 // Length is the number of IDs in the range. 90 Length uint32 91 } 92 93 // SetUIDMap instructs ns to translate UIDs as specified by entries. 94 // 95 // Note: SetUIDMap does not place an upper bound on the number of entries, but 96 // Linux does. This restriction is implemented in SetUIDMap's caller, the 97 // implementation of /proc/[pid]/uid_map. 98 func (ns *UserNamespace) SetUIDMap(ctx context.Context, entries []IDMapEntry) error { 99 c := CredentialsFromContext(ctx) 100 101 ns.mu.Lock() 102 defer ns.mu.Unlock() 103 // "After the creation of a new user namespace, the uid_map file of *one* 104 // of the processes in the namespace may be written to *once* to define the 105 // mapping of user IDs in the new user namespace. An attempt to write more 106 // than once to a uid_map file in a user namespace fails with the error 107 // EPERM. Similar rules apply for gid_map files." - user_namespaces(7) 108 if !ns.uidMapFromParent.IsEmpty() { 109 return linuxerr.EPERM 110 } 111 // "At least one line must be written to the file." 112 if len(entries) == 0 { 113 return linuxerr.EINVAL 114 } 115 // """ 116 // In order for a process to write to the /proc/[pid]/uid_map 117 // (/proc/[pid]/gid_map) file, all of the following requirements must be 118 // met: 119 // 120 // 1. The writing process must have the CAP_SETUID (CAP_SETGID) capability 121 // in the user namespace of the process pid. 122 // """ 123 if !c.HasCapabilityIn(linux.CAP_SETUID, ns) { 124 return linuxerr.EPERM 125 } 126 // "2. The writing process must either be in the user namespace of the process 127 // pid or be in the parent user namespace of the process pid." 128 if c.UserNamespace != ns && c.UserNamespace != ns.parent { 129 return linuxerr.EPERM 130 } 131 // """ 132 // 3. (see trySetUIDMap) 133 // 134 // 4. One of the following two cases applies: 135 // 136 // * Either the writing process has the CAP_SETUID (CAP_SETGID) capability 137 // in the parent user namespace. 138 // """ 139 if !c.HasCapabilityIn(linux.CAP_SETUID, ns.parent) { 140 // """ 141 // * Or otherwise all of the following restrictions apply: 142 // 143 // + The data written to uid_map (gid_map) must consist of a single line 144 // that maps the writing process' effective user ID (group ID) in the 145 // parent user namespace to a user ID (group ID) in the user namespace. 146 // """ 147 if len(entries) != 1 || ns.parent.MapToKUID(UID(entries[0].FirstParentID)) != c.EffectiveKUID || entries[0].Length != 1 { 148 return linuxerr.EPERM 149 } 150 // """ 151 // + The writing process must have the same effective user ID as the 152 // process that created the user namespace. 153 // """ 154 if c.EffectiveKUID != ns.owner { 155 return linuxerr.EPERM 156 } 157 } 158 // trySetUIDMap leaves data in maps if it fails. 159 if err := ns.trySetUIDMap(entries); err != nil { 160 ns.uidMapFromParent.RemoveAll() 161 ns.uidMapToParent.RemoveAll() 162 return err 163 } 164 return nil 165 } 166 167 func (ns *UserNamespace) trySetUIDMap(entries []IDMapEntry) error { 168 for _, e := range entries { 169 // Determine upper bounds and check for overflow. This implicitly 170 // checks for NoID. 171 lastID := e.FirstID + e.Length 172 if lastID <= e.FirstID { 173 return linuxerr.EINVAL 174 } 175 lastParentID := e.FirstParentID + e.Length 176 if lastParentID <= e.FirstParentID { 177 return linuxerr.EINVAL 178 } 179 // "3. The mapped user IDs (group IDs) must in turn have a mapping in 180 // the parent user namespace." 181 // Only the root namespace has a nil parent, and root is assigned 182 // mappings when it's created, so SetUIDMap would have returned EPERM 183 // without reaching this point if ns is root. 184 if !ns.parent.allIDsMapped(&ns.parent.uidMapToParent, e.FirstParentID, lastParentID) { 185 return linuxerr.EPERM 186 } 187 // If either of these Adds fail, we have an overlapping range. 188 if !ns.uidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) { 189 return linuxerr.EINVAL 190 } 191 if !ns.uidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) { 192 return linuxerr.EINVAL 193 } 194 } 195 return nil 196 } 197 198 // SetGIDMap instructs ns to translate GIDs as specified by entries. 199 func (ns *UserNamespace) SetGIDMap(ctx context.Context, entries []IDMapEntry) error { 200 c := CredentialsFromContext(ctx) 201 202 ns.mu.Lock() 203 defer ns.mu.Unlock() 204 if !ns.gidMapFromParent.IsEmpty() { 205 return linuxerr.EPERM 206 } 207 if len(entries) == 0 { 208 return linuxerr.EINVAL 209 } 210 if !c.HasCapabilityIn(linux.CAP_SETGID, ns) { 211 return linuxerr.EPERM 212 } 213 if c.UserNamespace != ns && c.UserNamespace != ns.parent { 214 return linuxerr.EPERM 215 } 216 if !c.HasCapabilityIn(linux.CAP_SETGID, ns.parent) { 217 if len(entries) != 1 || ns.parent.MapToKGID(GID(entries[0].FirstParentID)) != c.EffectiveKGID || entries[0].Length != 1 { 218 return linuxerr.EPERM 219 } 220 // It's correct for this to still be UID. 221 if c.EffectiveKUID != ns.owner { 222 return linuxerr.EPERM 223 } 224 // "In the case of gid_map, use of the setgroups(2) system call must 225 // first be denied by writing "deny" to the /proc/[pid]/setgroups file 226 // (see below) before writing to gid_map." (This file isn't implemented 227 // in the version of Linux we're emulating; see comment in 228 // UserNamespace.) 229 } 230 if err := ns.trySetGIDMap(entries); err != nil { 231 ns.gidMapFromParent.RemoveAll() 232 ns.gidMapToParent.RemoveAll() 233 return err 234 } 235 return nil 236 } 237 238 func (ns *UserNamespace) trySetGIDMap(entries []IDMapEntry) error { 239 for _, e := range entries { 240 lastID := e.FirstID + e.Length 241 if lastID <= e.FirstID { 242 return linuxerr.EINVAL 243 } 244 lastParentID := e.FirstParentID + e.Length 245 if lastParentID <= e.FirstParentID { 246 return linuxerr.EINVAL 247 } 248 if !ns.parent.allIDsMapped(&ns.parent.gidMapToParent, e.FirstParentID, lastParentID) { 249 return linuxerr.EPERM 250 } 251 if !ns.gidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) { 252 return linuxerr.EINVAL 253 } 254 if !ns.gidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) { 255 return linuxerr.EINVAL 256 } 257 } 258 return nil 259 } 260 261 // UIDMap returns the user ID mappings configured for ns. If no mappings 262 // have been configured, UIDMap returns nil. 263 func (ns *UserNamespace) UIDMap() []IDMapEntry { 264 return ns.getIDMap(&ns.uidMapToParent) 265 } 266 267 // GIDMap returns the group ID mappings configured for ns. If no mappings 268 // have been configured, GIDMap returns nil. 269 func (ns *UserNamespace) GIDMap() []IDMapEntry { 270 return ns.getIDMap(&ns.gidMapToParent) 271 } 272 273 func (ns *UserNamespace) getIDMap(m *idMapSet) []IDMapEntry { 274 ns.mu.Lock() 275 defer ns.mu.Unlock() 276 var entries []IDMapEntry 277 for it := m.FirstSegment(); it.Ok(); it = it.NextSegment() { 278 entries = append(entries, IDMapEntry{ 279 FirstID: it.Start(), 280 FirstParentID: it.Value(), 281 Length: it.Range().Length(), 282 }) 283 } 284 return entries 285 }