github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/proc/uid_gid_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 proc 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 22 "github.com/SagerNet/gvisor/pkg/abi/linux" 23 "github.com/SagerNet/gvisor/pkg/context" 24 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 25 "github.com/SagerNet/gvisor/pkg/hostarch" 26 "github.com/SagerNet/gvisor/pkg/sentry/fs" 27 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 28 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 29 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 30 "github.com/SagerNet/gvisor/pkg/usermem" 31 "github.com/SagerNet/gvisor/pkg/waiter" 32 ) 33 34 // LINT.IfChange 35 36 // idMapInodeOperations implements fs.InodeOperations for 37 // /proc/[pid]/{uid,gid}_map. 38 // 39 // +stateify savable 40 type idMapInodeOperations struct { 41 fsutil.InodeGenericChecker `state:"nosave"` 42 fsutil.InodeNoopRelease `state:"nosave"` 43 fsutil.InodeNoopWriteOut `state:"nosave"` 44 fsutil.InodeNotAllocatable `state:"nosave"` 45 fsutil.InodeNotDirectory `state:"nosave"` 46 fsutil.InodeNotMappable `state:"nosave"` 47 fsutil.InodeNotSocket `state:"nosave"` 48 fsutil.InodeNotSymlink `state:"nosave"` 49 fsutil.InodeNotTruncatable `state:"nosave"` 50 fsutil.InodeVirtual `state:"nosave"` 51 52 fsutil.InodeSimpleAttributes 53 fsutil.InodeSimpleExtendedAttributes 54 55 t *kernel.Task 56 gids bool 57 } 58 59 var _ fs.InodeOperations = (*idMapInodeOperations)(nil) 60 61 // newUIDMap returns a new uid_map file. 62 func newUIDMap(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Inode { 63 return newIDMap(ctx, t, msrc, false /* gids */) 64 } 65 66 // newGIDMap returns a new gid_map file. 67 func newGIDMap(ctx context.Context, t *kernel.Task, msrc *fs.MountSource) *fs.Inode { 68 return newIDMap(ctx, t, msrc, true /* gids */) 69 } 70 71 func newIDMap(ctx context.Context, t *kernel.Task, msrc *fs.MountSource, gids bool) *fs.Inode { 72 return newProcInode(ctx, &idMapInodeOperations{ 73 InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, fs.RootOwner, fs.FilePermsFromMode(0644), linux.PROC_SUPER_MAGIC), 74 t: t, 75 gids: gids, 76 }, msrc, fs.SpecialFile, t) 77 } 78 79 // GetFile implements fs.InodeOperations.GetFile. 80 func (imio *idMapInodeOperations) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 81 return fs.NewFile(ctx, dirent, flags, &idMapFileOperations{ 82 iops: imio, 83 }), nil 84 } 85 86 // +stateify savable 87 type idMapFileOperations struct { 88 fsutil.FileGenericSeek `state:"nosave"` 89 fsutil.FileNoIoctl `state:"nosave"` 90 fsutil.FileNoMMap `state:"nosave"` 91 fsutil.FileNoSplice `state:"nosave"` 92 fsutil.FileNoopFlush `state:"nosave"` 93 fsutil.FileNoopFsync `state:"nosave"` 94 fsutil.FileNoopRelease `state:"nosave"` 95 fsutil.FileNotDirReaddir `state:"nosave"` 96 fsutil.FileUseInodeUnstableAttr `state:"nosave"` 97 waiter.AlwaysReady `state:"nosave"` 98 99 iops *idMapInodeOperations 100 } 101 102 var _ fs.FileOperations = (*idMapFileOperations)(nil) 103 104 // "There is an (arbitrary) limit on the number of lines in the file. As at 105 // Linux 3.18, the limit is five lines." - user_namespaces(7) 106 const maxIDMapLines = 5 107 108 // Read implements fs.FileOperations.Read. 109 func (imfo *idMapFileOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) { 110 if offset < 0 { 111 return 0, linuxerr.EINVAL 112 } 113 var entries []auth.IDMapEntry 114 if imfo.iops.gids { 115 entries = imfo.iops.t.UserNamespace().GIDMap() 116 } else { 117 entries = imfo.iops.t.UserNamespace().UIDMap() 118 } 119 var buf bytes.Buffer 120 for _, e := range entries { 121 fmt.Fprintf(&buf, "%10d %10d %10d\n", e.FirstID, e.FirstParentID, e.Length) 122 } 123 if offset >= int64(buf.Len()) { 124 return 0, io.EOF 125 } 126 n, err := dst.CopyOut(ctx, buf.Bytes()[offset:]) 127 return int64(n), err 128 } 129 130 // Write implements fs.FileOperations.Write. 131 func (imfo *idMapFileOperations) Write(ctx context.Context, file *fs.File, src usermem.IOSequence, offset int64) (int64, error) { 132 // "In addition, the number of bytes written to the file must be less than 133 // the system page size, and the write must be performed at the start of 134 // the file ..." - user_namespaces(7) 135 srclen := src.NumBytes() 136 if srclen >= hostarch.PageSize || offset != 0 { 137 return 0, linuxerr.EINVAL 138 } 139 b := make([]byte, srclen) 140 if _, err := src.CopyIn(ctx, b); err != nil { 141 return 0, err 142 } 143 144 // Truncate from the first NULL byte. 145 var nul int64 146 nul = int64(bytes.IndexByte(b, 0)) 147 if nul == -1 { 148 nul = srclen 149 } 150 b = b[:nul] 151 // Remove the last \n. 152 if nul >= 1 && b[nul-1] == '\n' { 153 b = b[:nul-1] 154 } 155 lines := bytes.SplitN(b, []byte("\n"), maxIDMapLines+1) 156 if len(lines) > maxIDMapLines { 157 return 0, linuxerr.EINVAL 158 } 159 160 entries := make([]auth.IDMapEntry, len(lines)) 161 for i, l := range lines { 162 var e auth.IDMapEntry 163 _, err := fmt.Sscan(string(l), &e.FirstID, &e.FirstParentID, &e.Length) 164 if err != nil { 165 return 0, linuxerr.EINVAL 166 } 167 entries[i] = e 168 } 169 var err error 170 if imfo.iops.gids { 171 err = imfo.iops.t.UserNamespace().SetGIDMap(ctx, entries) 172 } else { 173 err = imfo.iops.t.UserNamespace().SetUIDMap(ctx, entries) 174 } 175 if err != nil { 176 return 0, err 177 } 178 179 // On success, Linux's kernel/user_namespace.c:map_write() always returns 180 // count, even if fewer bytes were used. 181 return int64(srclen), nil 182 } 183 184 // LINT.ThenChange(../../fsimpl/proc/task_files.go)