mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
286 lines
9.6 KiB
Go
286 lines
9.6 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// 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.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
|
"gvisor.dev/gvisor/pkg/sentry/seccheck"
|
|
pb "gvisor.dev/gvisor/pkg/sentry/seccheck/points/points_go_proto"
|
|
)
|
|
|
|
// Credentials contains information required to authorize privileged operations
|
|
// in a user namespace.
|
|
//
|
|
// +stateify savable
|
|
type Credentials struct {
|
|
// Real/effective/saved user/group IDs in the root user namespace. None of
|
|
// these should ever be NoID.
|
|
RealKUID KUID
|
|
EffectiveKUID KUID
|
|
SavedKUID KUID
|
|
RealKGID KGID
|
|
EffectiveKGID KGID
|
|
SavedKGID KGID
|
|
|
|
// Filesystem user/group IDs are not implemented. "... setfsuid() is
|
|
// nowadays unneeded and should be avoided in new applications (likewise
|
|
// for setfsgid(2))." - setfsuid(2)
|
|
|
|
// Supplementary groups used by set/getgroups.
|
|
//
|
|
// ExtraKGIDs slices are immutable, allowing multiple Credentials with the
|
|
// same ExtraKGIDs to share the same slice.
|
|
ExtraKGIDs []KGID
|
|
|
|
// The capability sets applicable to this set of credentials.
|
|
PermittedCaps CapabilitySet
|
|
InheritableCaps CapabilitySet
|
|
EffectiveCaps CapabilitySet
|
|
BoundingCaps CapabilitySet
|
|
AmbientCaps CapabilitySet
|
|
|
|
// KeepCaps is the flag for PR_SET_KEEPCAPS which allow capabilities to be
|
|
// maintained after a switch from root user to non-root user via setuid().
|
|
KeepCaps bool
|
|
|
|
// The user namespace associated with the owner of the credentials.
|
|
UserNamespace *UserNamespace
|
|
}
|
|
|
|
// NewAnonymousCredentials returns a set of credentials with no capabilities in
|
|
// any user namespace.
|
|
func NewAnonymousCredentials() *Credentials {
|
|
// Create a new root user namespace. Since the new namespace's owner is
|
|
// KUID 0 and the returned credentials have non-zero KUID/KGID, the
|
|
// returned credentials do not have any capabilities in the new namespace.
|
|
// Since the new namespace is not part of any existing user namespace
|
|
// hierarchy, the returned credentials do not have any capabilities in any
|
|
// other namespace.
|
|
return &Credentials{
|
|
RealKUID: NobodyKUID,
|
|
EffectiveKUID: NobodyKUID,
|
|
SavedKUID: NobodyKUID,
|
|
RealKGID: NobodyKGID,
|
|
EffectiveKGID: NobodyKGID,
|
|
SavedKGID: NobodyKGID,
|
|
UserNamespace: NewRootUserNamespace(),
|
|
}
|
|
}
|
|
|
|
// NewRootCredentials returns a set of credentials with KUID and KGID 0 (i.e.
|
|
// global root) in user namespace ns.
|
|
func NewRootCredentials(ns *UserNamespace) *Credentials {
|
|
// I can't find documentation for this anywhere, but it's correct for the
|
|
// inheritable capability set to be initially empty (the capabilities test
|
|
// checks for this property).
|
|
return &Credentials{
|
|
RealKUID: RootKUID,
|
|
EffectiveKUID: RootKUID,
|
|
SavedKUID: RootKUID,
|
|
RealKGID: RootKGID,
|
|
EffectiveKGID: RootKGID,
|
|
SavedKGID: RootKGID,
|
|
PermittedCaps: AllCapabilities,
|
|
EffectiveCaps: AllCapabilities,
|
|
BoundingCaps: AllCapabilities,
|
|
UserNamespace: ns,
|
|
}
|
|
}
|
|
|
|
// NewUserCredentials returns a set of credentials based on the given UID, GIDs,
|
|
// and capabilities in a given namespace. If all arguments are their zero
|
|
// values, this returns the same credentials as NewRootCredentials.
|
|
func NewUserCredentials(kuid KUID, kgid KGID, extraKGIDs []KGID, capabilities *TaskCapabilities, ns *UserNamespace) *Credentials {
|
|
creds := NewRootCredentials(ns)
|
|
|
|
// Set the UID.
|
|
uid := kuid
|
|
creds.RealKUID = uid
|
|
creds.EffectiveKUID = uid
|
|
creds.SavedKUID = uid
|
|
|
|
// Set GID.
|
|
gid := kgid
|
|
creds.RealKGID = gid
|
|
creds.EffectiveKGID = gid
|
|
creds.SavedKGID = gid
|
|
|
|
// Set additional GIDs.
|
|
creds.ExtraKGIDs = append(creds.ExtraKGIDs, extraKGIDs...)
|
|
|
|
// Set capabilities.
|
|
if capabilities != nil {
|
|
creds.PermittedCaps = capabilities.PermittedCaps
|
|
creds.EffectiveCaps = capabilities.EffectiveCaps
|
|
creds.BoundingCaps = capabilities.BoundingCaps
|
|
creds.InheritableCaps = capabilities.InheritableCaps
|
|
creds.AmbientCaps = capabilities.AmbientCaps
|
|
} else {
|
|
// If no capabilities are specified, grant capabilities consistent with
|
|
// setresuid + setresgid from NewRootCredentials to the given uid and
|
|
// gid.
|
|
if kuid == RootKUID {
|
|
creds.PermittedCaps = AllCapabilities
|
|
creds.EffectiveCaps = AllCapabilities
|
|
} else {
|
|
creds.PermittedCaps = 0
|
|
creds.EffectiveCaps = 0
|
|
}
|
|
creds.BoundingCaps = AllCapabilities
|
|
}
|
|
|
|
return creds
|
|
}
|
|
|
|
// Fork generates an identical copy of a set of credentials.
|
|
func (c *Credentials) Fork() *Credentials {
|
|
nc := new(Credentials)
|
|
*nc = *c // Copy-by-value; this is legal for all fields.
|
|
return nc
|
|
}
|
|
|
|
// ForkIntoUserNamespace returns a copy of c after its caller has entered user
|
|
// namespace ns.
|
|
func (c *Credentials) ForkIntoUserNamespace(ns *UserNamespace) *Credentials {
|
|
nc := c.Fork()
|
|
nc.UserNamespace = ns
|
|
// "The child process created by clone(2) with the CLONE_NEWUSER flag
|
|
// starts out with a complete set of capabilities in the new user
|
|
// namespace. Likewise, a process that creates a new user namespace using
|
|
// unshare(2) or joins an existing user namespace using setns(2) gains a
|
|
// full set of capabilities in that namespace."
|
|
nc.PermittedCaps = AllCapabilities
|
|
nc.InheritableCaps = 0
|
|
nc.EffectiveCaps = AllCapabilities
|
|
nc.BoundingCaps = AllCapabilities
|
|
nc.AmbientCaps = 0
|
|
// "A call to clone(2), unshare(2), or setns(2) using the CLONE_NEWUSER
|
|
// flag sets the "securebits" flags (see capabilities(7)) to their default
|
|
// values (all flags disabled) in the child (for clone(2)) or caller (for
|
|
// unshare(2), or setns(2)." - user_namespaces(7)
|
|
nc.KeepCaps = false
|
|
return nc
|
|
}
|
|
|
|
// InGroup returns true if c is in group kgid. Compare Linux's
|
|
// kernel/groups.c:in_group_p().
|
|
func (c *Credentials) InGroup(kgid KGID) bool {
|
|
if c.EffectiveKGID == kgid {
|
|
return true
|
|
}
|
|
for _, extraKGID := range c.ExtraKGIDs {
|
|
if extraKGID == kgid {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// HasCapabilityIn returns true if c has capability cp in ns.
|
|
func (c *Credentials) HasCapabilityIn(cp linux.Capability, ns *UserNamespace) bool {
|
|
for {
|
|
// "1. A process has a capability inside a user namespace if it is a member
|
|
// of that namespace and it has the capability in its effective capability
|
|
// set." - user_namespaces(7)
|
|
if c.UserNamespace == ns {
|
|
return CapabilitySetOf(cp)&c.EffectiveCaps != 0
|
|
}
|
|
// "3. ... A process that resides in the parent of the user namespace and
|
|
// whose effective user ID matches the owner of the namespace has all
|
|
// capabilities in the namespace."
|
|
if c.UserNamespace == ns.parent && c.EffectiveKUID == ns.owner {
|
|
return true
|
|
}
|
|
// "2. If a process has a capability in a user namespace, then it has that
|
|
// capability in all child (and further removed descendant) namespaces as
|
|
// well."
|
|
if ns.parent == nil {
|
|
return false
|
|
}
|
|
ns = ns.parent
|
|
}
|
|
}
|
|
|
|
// HasSelfCapability returns true if c has capability cp in its user namespace.
|
|
func (c *Credentials) HasSelfCapability(cp linux.Capability) bool {
|
|
return c.HasCapabilityIn(cp, c.UserNamespace)
|
|
}
|
|
|
|
// HasRootCapability returns true if c has capability cp in c's user namespace's root.
|
|
func (c *Credentials) HasRootCapability(cp linux.Capability) bool {
|
|
return c.HasCapabilityIn(cp, c.UserNamespace.Root())
|
|
}
|
|
|
|
// HasCapabilityOnFile returns true if creds has the given capability with
|
|
// respect to a file with the given owning UID and GID, consistent with Linux's
|
|
// kernel/capability.c:capable_wrt_inode_uidgid().
|
|
func (c *Credentials) HasCapabilityOnFile(cp linux.Capability, kuid KUID, kgid KGID) bool {
|
|
return c.HasSelfCapability(cp) && c.UserNamespace.MapFromKUID(kuid).Ok() && c.UserNamespace.MapFromKGID(kgid).Ok()
|
|
}
|
|
|
|
// UseUID checks that c can use uid in its user namespace, then translates it
|
|
// to the root user namespace.
|
|
//
|
|
// The checks UseUID does are common, but you should verify that it's doing
|
|
// exactly what you want.
|
|
func (c *Credentials) UseUID(uid UID) (KUID, error) {
|
|
// uid must be mapped.
|
|
kuid := c.UserNamespace.MapToKUID(uid)
|
|
if !kuid.Ok() {
|
|
return NoID, linuxerr.EINVAL
|
|
}
|
|
// If c has CAP_SETUID, then it can use any UID in its user namespace.
|
|
if c.HasSelfCapability(linux.CAP_SETUID) {
|
|
return kuid, nil
|
|
}
|
|
// Otherwise, c must already have the UID as its real, effective, or saved
|
|
// set-user-ID.
|
|
if kuid == c.RealKUID || kuid == c.EffectiveKUID || kuid == c.SavedKUID {
|
|
return kuid, nil
|
|
}
|
|
return NoID, linuxerr.EPERM
|
|
}
|
|
|
|
// UseGID checks that c can use gid in its user namespace, then translates it
|
|
// to the root user namespace.
|
|
func (c *Credentials) UseGID(gid GID) (KGID, error) {
|
|
kgid := c.UserNamespace.MapToKGID(gid)
|
|
if !kgid.Ok() {
|
|
return NoID, linuxerr.EINVAL
|
|
}
|
|
if c.HasSelfCapability(linux.CAP_SETGID) {
|
|
return kgid, nil
|
|
}
|
|
if kgid == c.RealKGID || kgid == c.EffectiveKGID || kgid == c.SavedKGID {
|
|
return kgid, nil
|
|
}
|
|
return NoID, linuxerr.EPERM
|
|
}
|
|
|
|
// LoadSeccheckData sets credential data based on mask.
|
|
func (c *Credentials) LoadSeccheckData(mask seccheck.FieldMask, info *pb.ContextData) {
|
|
if mask.Contains(seccheck.FieldCtxtCredentials) {
|
|
info.Credentials = &pb.Credentials{
|
|
RealUid: uint32(c.RealKUID),
|
|
EffectiveUid: uint32(c.EffectiveKUID),
|
|
SavedUid: uint32(c.SavedKUID),
|
|
RealGid: uint32(c.RealKGID),
|
|
EffectiveGid: uint32(c.EffectiveKGID),
|
|
SavedGid: uint32(c.SavedKGID),
|
|
}
|
|
}
|
|
}
|