github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/kernel/auth/id_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 auth
    16  
    17  import (
    18  	"github.com/ttpreport/gvisor-ligolo/pkg/abi/linux"
    19  	"github.com/ttpreport/gvisor-ligolo/pkg/context"
    20  	"github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr"
    21  )
    22  
    23  // MapFromKUID translates kuid, a UID in the root namespace, to a UID in ns.
    24  func (ns *UserNamespace) MapFromKUID(kuid KUID) UID {
    25  	if ns.parent == nil {
    26  		return UID(kuid)
    27  	}
    28  	return UID(ns.mapID(&ns.uidMapFromParent, uint32(ns.parent.MapFromKUID(kuid))))
    29  }
    30  
    31  // MapFromKGID translates kgid, a GID in the root namespace, to a GID in ns.
    32  func (ns *UserNamespace) MapFromKGID(kgid KGID) GID {
    33  	if ns.parent == nil {
    34  		return GID(kgid)
    35  	}
    36  	return GID(ns.mapID(&ns.gidMapFromParent, uint32(ns.parent.MapFromKGID(kgid))))
    37  }
    38  
    39  // MapToKUID translates uid, a UID in ns, to a UID in the root namespace.
    40  func (ns *UserNamespace) MapToKUID(uid UID) KUID {
    41  	if ns.parent == nil {
    42  		return KUID(uid)
    43  	}
    44  	return ns.parent.MapToKUID(UID(ns.mapID(&ns.uidMapToParent, uint32(uid))))
    45  }
    46  
    47  // MapToKGID translates gid, a GID in ns, to a GID in the root namespace.
    48  func (ns *UserNamespace) MapToKGID(gid GID) KGID {
    49  	if ns.parent == nil {
    50  		return KGID(gid)
    51  	}
    52  	return ns.parent.MapToKGID(GID(ns.mapID(&ns.gidMapToParent, uint32(gid))))
    53  }
    54  
    55  func (ns *UserNamespace) mapID(m *idMapSet, id uint32) uint32 {
    56  	if id == NoID {
    57  		return NoID
    58  	}
    59  	ns.mu.Lock()
    60  	defer ns.mu.Unlock()
    61  	if it := m.FindSegment(id); it.Ok() {
    62  		return it.Value() + (id - it.Start())
    63  	}
    64  	return NoID
    65  }
    66  
    67  // allIDsMapped returns true if all IDs in the range [start, end) are mapped in
    68  // m.
    69  //
    70  // Preconditions: end >= start.
    71  func (ns *UserNamespace) allIDsMapped(m *idMapSet, start, end uint32) bool {
    72  	ns.mu.NestedLock(userNamespaceLockNs)
    73  	defer ns.mu.NestedUnlock(userNamespaceLockNs)
    74  	return m.SpanRange(idMapRange{start, end}) == end-start
    75  }
    76  
    77  // An IDMapEntry represents a mapping from a range of contiguous IDs in a user
    78  // namespace to an equally-sized range of contiguous IDs in the namespace's
    79  // parent.
    80  //
    81  // +stateify savable
    82  type IDMapEntry struct {
    83  	// FirstID is the first ID in the range in the namespace.
    84  	FirstID uint32
    85  
    86  	// FirstParentID is the first ID in the range in the parent namespace.
    87  	FirstParentID uint32
    88  
    89  	// Length is the number of IDs in the range.
    90  	Length uint32
    91  }
    92  
    93  // SetUIDMap instructs ns to translate UIDs as specified by entries.
    94  //
    95  // Note: SetUIDMap does not place an upper bound on the number of entries, but
    96  // Linux does. This restriction is implemented in SetUIDMap's caller, the
    97  // implementation of /proc/[pid]/uid_map.
    98  func (ns *UserNamespace) SetUIDMap(ctx context.Context, entries []IDMapEntry) error {
    99  	c := CredentialsFromContext(ctx)
   100  
   101  	ns.mu.Lock()
   102  	defer ns.mu.Unlock()
   103  	// "After the creation of a new user namespace, the uid_map file of *one*
   104  	// of the processes in the namespace may be written to *once* to define the
   105  	// mapping of user IDs in the new user namespace. An attempt to write more
   106  	// than once to a uid_map file in a user namespace fails with the error
   107  	// EPERM. Similar rules apply for gid_map files." - user_namespaces(7)
   108  	if !ns.uidMapFromParent.IsEmpty() {
   109  		return linuxerr.EPERM
   110  	}
   111  	// "At least one line must be written to the file."
   112  	if len(entries) == 0 {
   113  		return linuxerr.EINVAL
   114  	}
   115  	// """
   116  	// In order for a process to write to the /proc/[pid]/uid_map
   117  	// (/proc/[pid]/gid_map) file, all of the following requirements must be
   118  	// met:
   119  	//
   120  	// 1. The writing process must have the CAP_SETUID (CAP_SETGID) capability
   121  	// in the user namespace of the process pid.
   122  	// """
   123  	if !c.HasCapabilityIn(linux.CAP_SETUID, ns) {
   124  		return linuxerr.EPERM
   125  	}
   126  	// "2. The writing process must either be in the user namespace of the process
   127  	// pid or be in the parent user namespace of the process pid."
   128  	if c.UserNamespace != ns && c.UserNamespace != ns.parent {
   129  		return linuxerr.EPERM
   130  	}
   131  	// """
   132  	// 3. (see trySetUIDMap)
   133  	//
   134  	// 4. One of the following two cases applies:
   135  	//
   136  	//	* Either the writing process has the CAP_SETUID (CAP_SETGID) capability
   137  	//		in the parent user namespace.
   138  	// """
   139  	if !c.HasCapabilityIn(linux.CAP_SETUID, ns.parent) {
   140  		// """
   141  		//	* Or otherwise all of the following restrictions apply:
   142  		//
   143  		//		+ The data written to uid_map (gid_map) must consist of a single line
   144  		//			that maps the writing process' effective user ID (group ID) in the
   145  		//			parent user namespace to a user ID (group ID) in the user namespace.
   146  		// """
   147  		if len(entries) != 1 || ns.parent.MapToKUID(UID(entries[0].FirstParentID)) != c.EffectiveKUID || entries[0].Length != 1 {
   148  			return linuxerr.EPERM
   149  		}
   150  		// """
   151  		//   + The writing process must have the same effective user ID as the
   152  		//   process that created the user namespace.
   153  		// """
   154  		if c.EffectiveKUID != ns.owner {
   155  			return linuxerr.EPERM
   156  		}
   157  	}
   158  	// trySetUIDMap leaves data in maps if it fails.
   159  	if err := ns.trySetUIDMap(entries); err != nil {
   160  		ns.uidMapFromParent.RemoveAll()
   161  		ns.uidMapToParent.RemoveAll()
   162  		return err
   163  	}
   164  	return nil
   165  }
   166  
   167  func (ns *UserNamespace) trySetUIDMap(entries []IDMapEntry) error {
   168  	for _, e := range entries {
   169  		// Determine upper bounds and check for overflow. This implicitly
   170  		// checks for NoID.
   171  		lastID := e.FirstID + e.Length
   172  		if lastID <= e.FirstID {
   173  			return linuxerr.EINVAL
   174  		}
   175  		lastParentID := e.FirstParentID + e.Length
   176  		if lastParentID <= e.FirstParentID {
   177  			return linuxerr.EINVAL
   178  		}
   179  		// "3. The mapped user IDs (group IDs) must in turn have a mapping in
   180  		// the parent user namespace."
   181  		// Only the root namespace has a nil parent, and root is assigned
   182  		// mappings when it's created, so SetUIDMap would have returned EPERM
   183  		// without reaching this point if ns is root.
   184  		if !ns.parent.allIDsMapped(&ns.parent.uidMapToParent, e.FirstParentID, lastParentID) {
   185  			return linuxerr.EPERM
   186  		}
   187  		// If either of these Adds fail, we have an overlapping range.
   188  		if !ns.uidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) {
   189  			return linuxerr.EINVAL
   190  		}
   191  		if !ns.uidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) {
   192  			return linuxerr.EINVAL
   193  		}
   194  	}
   195  	return nil
   196  }
   197  
   198  // SetGIDMap instructs ns to translate GIDs as specified by entries.
   199  func (ns *UserNamespace) SetGIDMap(ctx context.Context, entries []IDMapEntry) error {
   200  	c := CredentialsFromContext(ctx)
   201  
   202  	ns.mu.Lock()
   203  	defer ns.mu.Unlock()
   204  	if !ns.gidMapFromParent.IsEmpty() {
   205  		return linuxerr.EPERM
   206  	}
   207  	if len(entries) == 0 {
   208  		return linuxerr.EINVAL
   209  	}
   210  	if !c.HasCapabilityIn(linux.CAP_SETGID, ns) {
   211  		return linuxerr.EPERM
   212  	}
   213  	if c.UserNamespace != ns && c.UserNamespace != ns.parent {
   214  		return linuxerr.EPERM
   215  	}
   216  	if !c.HasCapabilityIn(linux.CAP_SETGID, ns.parent) {
   217  		if len(entries) != 1 || ns.parent.MapToKGID(GID(entries[0].FirstParentID)) != c.EffectiveKGID || entries[0].Length != 1 {
   218  			return linuxerr.EPERM
   219  		}
   220  		// It's correct for this to still be UID.
   221  		if c.EffectiveKUID != ns.owner {
   222  			return linuxerr.EPERM
   223  		}
   224  		// "In the case of gid_map, use of the setgroups(2) system call must
   225  		// first be denied by writing "deny" to the /proc/[pid]/setgroups file
   226  		// (see below) before writing to gid_map." (This file isn't implemented
   227  		// in the version of Linux we're emulating; see comment in
   228  		// UserNamespace.)
   229  	}
   230  	if err := ns.trySetGIDMap(entries); err != nil {
   231  		ns.gidMapFromParent.RemoveAll()
   232  		ns.gidMapToParent.RemoveAll()
   233  		return err
   234  	}
   235  	return nil
   236  }
   237  
   238  func (ns *UserNamespace) trySetGIDMap(entries []IDMapEntry) error {
   239  	for _, e := range entries {
   240  		lastID := e.FirstID + e.Length
   241  		if lastID <= e.FirstID {
   242  			return linuxerr.EINVAL
   243  		}
   244  		lastParentID := e.FirstParentID + e.Length
   245  		if lastParentID <= e.FirstParentID {
   246  			return linuxerr.EINVAL
   247  		}
   248  		if !ns.parent.allIDsMapped(&ns.parent.gidMapToParent, e.FirstParentID, lastParentID) {
   249  			return linuxerr.EPERM
   250  		}
   251  		if !ns.gidMapFromParent.Add(idMapRange{e.FirstParentID, lastParentID}, e.FirstID) {
   252  			return linuxerr.EINVAL
   253  		}
   254  		if !ns.gidMapToParent.Add(idMapRange{e.FirstID, lastID}, e.FirstParentID) {
   255  			return linuxerr.EINVAL
   256  		}
   257  	}
   258  	return nil
   259  }
   260  
   261  // UIDMap returns the user ID mappings configured for ns. If no mappings
   262  // have been configured, UIDMap returns nil.
   263  func (ns *UserNamespace) UIDMap() []IDMapEntry {
   264  	return ns.getIDMap(&ns.uidMapToParent)
   265  }
   266  
   267  // GIDMap returns the group ID mappings configured for ns. If no mappings
   268  // have been configured, GIDMap returns nil.
   269  func (ns *UserNamespace) GIDMap() []IDMapEntry {
   270  	return ns.getIDMap(&ns.gidMapToParent)
   271  }
   272  
   273  func (ns *UserNamespace) getIDMap(m *idMapSet) []IDMapEntry {
   274  	ns.mu.Lock()
   275  	defer ns.mu.Unlock()
   276  	var entries []IDMapEntry
   277  	for it := m.FirstSegment(); it.Ok(); it = it.NextSegment() {
   278  		entries = append(entries, IDMapEntry{
   279  			FirstID:       it.Start(),
   280  			FirstParentID: it.Value(),
   281  			Length:        it.Range().Length(),
   282  		})
   283  	}
   284  	return entries
   285  }