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)