github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/kernel/auth/credentials.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/errors/linuxerr" 20 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/seccheck" 21 pb "github.com/nicocha30/gvisor-ligolo/pkg/sentry/seccheck/points/points_go_proto" 22 ) 23 24 // Credentials contains information required to authorize privileged operations 25 // in a user namespace. 26 // 27 // +stateify savable 28 type Credentials struct { 29 // Real/effective/saved user/group IDs in the root user namespace. None of 30 // these should ever be NoID. 31 RealKUID KUID 32 EffectiveKUID KUID 33 SavedKUID KUID 34 RealKGID KGID 35 EffectiveKGID KGID 36 SavedKGID KGID 37 38 // Filesystem user/group IDs are not implemented. "... setfsuid() is 39 // nowadays unneeded and should be avoided in new applications (likewise 40 // for setfsgid(2))." - setfsuid(2) 41 42 // Supplementary groups used by set/getgroups. 43 // 44 // ExtraKGIDs slices are immutable, allowing multiple Credentials with the 45 // same ExtraKGIDs to share the same slice. 46 ExtraKGIDs []KGID 47 48 // The capability sets applicable to this set of credentials. 49 PermittedCaps CapabilitySet 50 InheritableCaps CapabilitySet 51 EffectiveCaps CapabilitySet 52 BoundingCaps CapabilitySet 53 // Ambient capabilities are not introduced until Linux 4.3. 54 55 // KeepCaps is the flag for PR_SET_KEEPCAPS which allow capabilities to be 56 // maintained after a switch from root user to non-root user via setuid(). 57 KeepCaps bool 58 59 // The user namespace associated with the owner of the credentials. 60 UserNamespace *UserNamespace 61 } 62 63 // NewAnonymousCredentials returns a set of credentials with no capabilities in 64 // any user namespace. 65 func NewAnonymousCredentials() *Credentials { 66 // Create a new root user namespace. Since the new namespace's owner is 67 // KUID 0 and the returned credentials have non-zero KUID/KGID, the 68 // returned credentials do not have any capabilities in the new namespace. 69 // Since the new namespace is not part of any existing user namespace 70 // hierarchy, the returned credentials do not have any capabilities in any 71 // other namespace. 72 return &Credentials{ 73 RealKUID: NobodyKUID, 74 EffectiveKUID: NobodyKUID, 75 SavedKUID: NobodyKUID, 76 RealKGID: NobodyKGID, 77 EffectiveKGID: NobodyKGID, 78 SavedKGID: NobodyKGID, 79 UserNamespace: NewRootUserNamespace(), 80 } 81 } 82 83 // NewRootCredentials returns a set of credentials with KUID and KGID 0 (i.e. 84 // global root) in user namespace ns. 85 func NewRootCredentials(ns *UserNamespace) *Credentials { 86 // I can't find documentation for this anywhere, but it's correct for the 87 // inheritable capability set to be initially empty (the capabilities test 88 // checks for this property). 89 return &Credentials{ 90 RealKUID: RootKUID, 91 EffectiveKUID: RootKUID, 92 SavedKUID: RootKUID, 93 RealKGID: RootKGID, 94 EffectiveKGID: RootKGID, 95 SavedKGID: RootKGID, 96 PermittedCaps: AllCapabilities, 97 EffectiveCaps: AllCapabilities, 98 BoundingCaps: AllCapabilities, 99 UserNamespace: ns, 100 } 101 } 102 103 // NewUserCredentials returns a set of credentials based on the given UID, GIDs, 104 // and capabilities in a given namespace. If all arguments are their zero 105 // values, this returns the same credentials as NewRootCredentials. 106 func NewUserCredentials(kuid KUID, kgid KGID, extraKGIDs []KGID, capabilities *TaskCapabilities, ns *UserNamespace) *Credentials { 107 creds := NewRootCredentials(ns) 108 109 // Set the UID. 110 uid := kuid 111 creds.RealKUID = uid 112 creds.EffectiveKUID = uid 113 creds.SavedKUID = uid 114 115 // Set GID. 116 gid := kgid 117 creds.RealKGID = gid 118 creds.EffectiveKGID = gid 119 creds.SavedKGID = gid 120 121 // Set additional GIDs. 122 creds.ExtraKGIDs = append(creds.ExtraKGIDs, extraKGIDs...) 123 124 // Set capabilities. 125 if capabilities != nil { 126 creds.PermittedCaps = capabilities.PermittedCaps 127 creds.EffectiveCaps = capabilities.EffectiveCaps 128 creds.BoundingCaps = capabilities.BoundingCaps 129 creds.InheritableCaps = capabilities.InheritableCaps 130 // TODO(gvisor.dev/issue/3166): Support ambient capabilities. 131 } else { 132 // If no capabilities are specified, grant capabilities consistent with 133 // setresuid + setresgid from NewRootCredentials to the given uid and 134 // gid. 135 if kuid == RootKUID { 136 creds.PermittedCaps = AllCapabilities 137 creds.EffectiveCaps = AllCapabilities 138 } else { 139 creds.PermittedCaps = 0 140 creds.EffectiveCaps = 0 141 } 142 creds.BoundingCaps = AllCapabilities 143 } 144 145 return creds 146 } 147 148 // Fork generates an identical copy of a set of credentials. 149 func (c *Credentials) Fork() *Credentials { 150 nc := new(Credentials) 151 *nc = *c // Copy-by-value; this is legal for all fields. 152 return nc 153 } 154 155 // InGroup returns true if c is in group kgid. Compare Linux's 156 // kernel/groups.c:in_group_p(). 157 func (c *Credentials) InGroup(kgid KGID) bool { 158 if c.EffectiveKGID == kgid { 159 return true 160 } 161 for _, extraKGID := range c.ExtraKGIDs { 162 if extraKGID == kgid { 163 return true 164 } 165 } 166 return false 167 } 168 169 // HasCapabilityIn returns true if c has capability cp in ns. 170 func (c *Credentials) HasCapabilityIn(cp linux.Capability, ns *UserNamespace) bool { 171 for { 172 // "1. A process has a capability inside a user namespace if it is a member 173 // of that namespace and it has the capability in its effective capability 174 // set." - user_namespaces(7) 175 if c.UserNamespace == ns { 176 return CapabilitySetOf(cp)&c.EffectiveCaps != 0 177 } 178 // "3. ... A process that resides in the parent of the user namespace and 179 // whose effective user ID matches the owner of the namespace has all 180 // capabilities in the namespace." 181 if c.UserNamespace == ns.parent && c.EffectiveKUID == ns.owner { 182 return true 183 } 184 // "2. If a process has a capability in a user namespace, then it has that 185 // capability in all child (and further removed descendant) namespaces as 186 // well." 187 if ns.parent == nil { 188 return false 189 } 190 ns = ns.parent 191 } 192 } 193 194 // HasCapability returns true if c has capability cp in its user namespace. 195 func (c *Credentials) HasCapability(cp linux.Capability) bool { 196 return c.HasCapabilityIn(cp, c.UserNamespace) 197 } 198 199 // UseUID checks that c can use uid in its user namespace, then translates it 200 // to the root user namespace. 201 // 202 // The checks UseUID does are common, but you should verify that it's doing 203 // exactly what you want. 204 func (c *Credentials) UseUID(uid UID) (KUID, error) { 205 // uid must be mapped. 206 kuid := c.UserNamespace.MapToKUID(uid) 207 if !kuid.Ok() { 208 return NoID, linuxerr.EINVAL 209 } 210 // If c has CAP_SETUID, then it can use any UID in its user namespace. 211 if c.HasCapability(linux.CAP_SETUID) { 212 return kuid, nil 213 } 214 // Otherwise, c must already have the UID as its real, effective, or saved 215 // set-user-ID. 216 if kuid == c.RealKUID || kuid == c.EffectiveKUID || kuid == c.SavedKUID { 217 return kuid, nil 218 } 219 return NoID, linuxerr.EPERM 220 } 221 222 // UseGID checks that c can use gid in its user namespace, then translates it 223 // to the root user namespace. 224 func (c *Credentials) UseGID(gid GID) (KGID, error) { 225 kgid := c.UserNamespace.MapToKGID(gid) 226 if !kgid.Ok() { 227 return NoID, linuxerr.EINVAL 228 } 229 if c.HasCapability(linux.CAP_SETGID) { 230 return kgid, nil 231 } 232 if kgid == c.RealKGID || kgid == c.EffectiveKGID || kgid == c.SavedKGID { 233 return kgid, nil 234 } 235 return NoID, linuxerr.EPERM 236 } 237 238 // SetUID translates the provided uid to the root user namespace and updates c's 239 // uids to it. This performs no permissions or capabilities checks, the caller 240 // is responsible for ensuring the calling context is permitted to modify c. 241 func (c *Credentials) SetUID(uid UID) error { 242 kuid := c.UserNamespace.MapToKUID(uid) 243 if !kuid.Ok() { 244 return linuxerr.EINVAL 245 } 246 c.RealKUID = kuid 247 c.EffectiveKUID = kuid 248 c.SavedKUID = kuid 249 return nil 250 } 251 252 // SetGID translates the provided gid to the root user namespace and updates c's 253 // gids to it. This performs no permissions or capabilities checks, the caller 254 // is responsible for ensuring the calling context is permitted to modify c. 255 func (c *Credentials) SetGID(gid GID) error { 256 kgid := c.UserNamespace.MapToKGID(gid) 257 if !kgid.Ok() { 258 return linuxerr.EINVAL 259 } 260 c.RealKGID = kgid 261 c.EffectiveKGID = kgid 262 c.SavedKGID = kgid 263 return nil 264 } 265 266 // LoadSeccheckData sets credential data based on mask. 267 func (c *Credentials) LoadSeccheckData(mask seccheck.FieldMask, info *pb.ContextData) { 268 if mask.Contains(seccheck.FieldCtxtCredentials) { 269 info.Credentials = &pb.Credentials{ 270 RealUid: uint32(c.RealKUID), 271 EffectiveUid: uint32(c.EffectiveKUID), 272 SavedUid: uint32(c.SavedKUID), 273 RealGid: uint32(c.RealKGID), 274 EffectiveGid: uint32(c.EffectiveKGID), 275 SavedGid: uint32(c.SavedKGID), 276 } 277 } 278 }