github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/kernel/ipc/registry.go (about) 1 // Copyright 2021 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 ipc 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 "github.com/nicocha30/gvisor-ligolo/pkg/log" 22 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 23 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 24 ) 25 26 // Registry is similar to Object, but for registries. It represent an abstract 27 // SysV IPC registry with fields common to all SysV registries. Registry is not 28 // thread-safe, and should be protected using a mutex. 29 // 30 // +stateify savable 31 type Registry struct { 32 // UserNS owning the IPC namespace this registry belongs to. Immutable. 33 UserNS *auth.UserNamespace 34 35 // objects is a map of IDs to IPC mechanisms. 36 objects map[ID]Mechanism 37 38 // KeysToIDs maps a lookup key to an ID. 39 keysToIDs map[Key]ID 40 41 // lastIDUsed is used to find the next available ID for object creation. 42 lastIDUsed ID 43 } 44 45 // NewRegistry return a new, initialized ipc.Registry. 46 func NewRegistry(userNS *auth.UserNamespace) *Registry { 47 return &Registry{ 48 UserNS: userNS, 49 objects: make(map[ID]Mechanism), 50 keysToIDs: make(map[Key]ID), 51 } 52 } 53 54 // Find uses key to search for and return a SysV mechanism. Find returns an 55 // error if an object is found by shouldn't be, or if the user doesn't have 56 // permission to use the object. If no object is found, Find checks create 57 // flag, and returns an error only if it's false. 58 func (r *Registry) Find(ctx context.Context, key Key, mode linux.FileMode, create, exclusive bool) (Mechanism, error) { 59 if id, ok := r.keysToIDs[key]; ok { 60 mech := r.objects[id] 61 mech.Lock() 62 defer mech.Unlock() 63 64 obj := mech.Object() 65 creds := auth.CredentialsFromContext(ctx) 66 if !obj.CheckPermissions(creds, vfs.AccessTypes(mode&linux.ModeOtherAll)) { 67 // The [calling process / user] does not have permission to access 68 // the set, and does not have the CAP_IPC_OWNER capability in the 69 // user namespace that governs its IPC namespace. 70 return nil, linuxerr.EACCES 71 } 72 73 if create && exclusive { 74 // IPC_CREAT and IPC_EXCL were specified, but an object already 75 // exists for key. 76 return nil, linuxerr.EEXIST 77 } 78 return mech, nil 79 } 80 81 if !create { 82 // No object exists for key and msgflg did not specify IPC_CREAT. 83 return nil, linuxerr.ENOENT 84 } 85 86 return nil, nil 87 } 88 89 // Register adds the given object into Registry.Objects, and assigns it a new 90 // ID. It returns an error if all IDs are exhausted. 91 func (r *Registry) Register(m Mechanism) error { 92 id, err := r.newID() 93 if err != nil { 94 return err 95 } 96 97 obj := m.Object() 98 obj.ID = id 99 100 r.objects[id] = m 101 r.keysToIDs[obj.Key] = id 102 103 return nil 104 } 105 106 // newID finds the first unused ID in the registry, and returns an error if 107 // non is found. 108 func (r *Registry) newID() (ID, error) { 109 // Find the next available ID. 110 for id := r.lastIDUsed + 1; id != r.lastIDUsed; id++ { 111 // Handle wrap around. 112 if id < 0 { 113 id = 0 114 continue 115 } 116 if r.objects[id] == nil { 117 r.lastIDUsed = id 118 return id, nil 119 } 120 } 121 122 log.Warningf("ids exhausted, they may be leaking") 123 124 // The man pages for shmget(2) mention that ENOSPC should be used if "All 125 // possible shared memory IDs have been taken (SHMMNI)". Other SysV 126 // mechanisms don't have a specific errno for running out of IDs, but they 127 // return ENOSPC if the max number of objects is exceeded, so we assume that 128 // it's the same case. 129 return 0, linuxerr.ENOSPC 130 } 131 132 // Remove removes the mechanism with the given id from the registry, and calls 133 // mechanism.Destroy to perform mechanism-specific removal. 134 func (r *Registry) Remove(id ID, creds *auth.Credentials) error { 135 mech := r.objects[id] 136 if mech == nil { 137 return linuxerr.EINVAL 138 } 139 140 mech.Lock() 141 defer mech.Unlock() 142 143 obj := mech.Object() 144 145 // The effective user ID of the calling process must match the creator or 146 // owner of the [mechanism], or the caller must be privileged. 147 if !obj.CheckOwnership(creds) { 148 return linuxerr.EPERM 149 } 150 151 delete(r.objects, obj.ID) 152 delete(r.keysToIDs, obj.Key) 153 mech.Destroy() 154 155 return nil 156 } 157 158 // ForAllObjects executes a given function for all given objects. 159 func (r *Registry) ForAllObjects(f func(o Mechanism)) { 160 for _, o := range r.objects { 161 f(o) 162 } 163 } 164 165 // FindByID returns the mechanism with the given ID, nil if non exists. 166 func (r *Registry) FindByID(id ID) Mechanism { 167 return r.objects[id] 168 } 169 170 // DissociateKey removes the association between a mechanism and its key 171 // (deletes it from r.keysToIDs), preventing it from being discovered by any new 172 // process, but not necessarily destroying it. If the given key doesn't exist, 173 // nothing is changed. 174 func (r *Registry) DissociateKey(key Key) { 175 delete(r.keysToIDs, key) 176 } 177 178 // DissociateID removes the association between a mechanism and its ID (deletes 179 // it from r.objects). An ID can't be removed unless the associated key is 180 // removed already, this is done to prevent the users from acquiring nil a 181 // Mechanism. 182 // 183 // Precondition: must be preceded by a call to r.DissociateKey. 184 func (r *Registry) DissociateID(id ID) { 185 delete(r.objects, id) 186 } 187 188 // ObjectCount returns the number of registered objects. 189 func (r *Registry) ObjectCount() int { 190 return len(r.objects) 191 } 192 193 // LastIDUsed returns the last used ID. 194 func (r *Registry) LastIDUsed() ID { 195 return r.lastIDUsed 196 }