github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/check_snap.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snapstate
    21  
    22  import (
    23  	"fmt"
    24  	"regexp"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/snapcore/snapd/arch"
    29  	"github.com/snapcore/snapd/asserts"
    30  	"github.com/snapcore/snapd/logger"
    31  	"github.com/snapcore/snapd/osutil"
    32  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    33  	"github.com/snapcore/snapd/overlord/state"
    34  	"github.com/snapcore/snapd/release"
    35  	seccomp_compiler "github.com/snapcore/snapd/sandbox/seccomp"
    36  	"github.com/snapcore/snapd/snap"
    37  	"github.com/snapcore/snapd/snapdtool"
    38  )
    39  
    40  // featureSet contains the flag values that can be listed in assumes entries
    41  // that this ubuntu-core actually provides.
    42  var featureSet = map[string]bool{
    43  	// Support for common data directory across revisions of a snap.
    44  	"common-data-dir": true,
    45  	// Support for the "Environment:" feature in snap.yaml
    46  	"snap-env": true,
    47  	// Support for the "command-chain" feature for apps and hooks in snap.yaml
    48  	"command-chain": true,
    49  }
    50  
    51  // supportedSystemUsernames for now contains the hardcoded list of system
    52  // users (and implied system group of same name) that snaps may specify. This
    53  // will eventually be moved out of here into the store.
    54  //
    55  // Since the snap is mounted read-only and to avoid problems associated with
    56  // different systems using different uids and gids for the same user name and
    57  // group name, snapd will create system-usernames where 'scope' is not
    58  // 'external' (currently snapd only supports 'scope: shared') with the
    59  // following characteristics:
    60  //
    61  // - uid and gid shall match for the specified system-username
    62  // - a snapd-allocated [ug]id for a user/group name shall never change
    63  // - snapd should avoid [ug]ids that are known to overlap with uid ranges of
    64  //   common use cases and user namespace container managers so that DAC and
    65  //   AppArmor owner match work as intended.
    66  // - [ug]id shall be < 2^31 to avoid (at least) broken devpts kernel code
    67  // - [ug]id shall be >= 524288 (0x00080000) to give plenty of room for large
    68  //   sites, default uid/gid ranges for docker (231072-296608), LXD installs
    69  //   that setup a default /etc/sub{uid,gid} (100000-165536) and podman whose
    70  //   tutorials reference setting up a specific default user and range
    71  //   (100000-165536)
    72  // - [ug]id shall be < 1,000,000 and > 1,001,000,000 (ie, 1,000,000 subordinate
    73  //   uid with 1,000,000,000 range) to avoid overlapping with LXD's minimum and
    74  //   maximum id ranges. LXD allows for any id range >= 65536 and doesn't
    75  //   perform any [ug]id overlap detection with current users
    76  // - [ug]ids assigned by snapd initially will fall within a 65536 (2^16) range
    77  //   (see below) where the first [ug]id in the range has the 16 lower bits all
    78  //   set to zero. This allows snapd to conveniently be bitwise aligned, follows
    79  //   sensible conventions (see https://systemd.io/UIDS-GIDS.html) but also
    80  //   potentially discoverable by systemd-nspawn (it assigns a different 65536
    81  //   range to each container. Its allocation algorithm is not sequential and
    82  //   may choose anything within its range that isn't already allocated. It's
    83  //   detection algorithm includes (effectively) performing a getpwent()
    84  //   operation on CANDIDATE_UID & 0XFFFF0000 and selecting another range if it
    85  //   is assigned).
    86  //
    87  // What [ug]id range(s) should snapd use?
    88  //
    89  // While snapd does not employ user namespaces, it will operate on systems with
    90  // container managers that do and will assign from a range of [ug]ids. It is
    91  // desirable that snapd assigns [ug]ids that minimally conflict with the system
    92  // and other software (potential conflicts with admin-assigned ranges in
    93  // /etc/subuid and /etc/subgid cannot be avoided, but can be documented as well
    94  // as detected/logged). Overlapping with container managers is non-fatal for
    95  // snapd and the container, but introduces the possibility that a uid in the
    96  // container matches a uid a snap is using, which is undesirable in terms of
    97  // security (eg, DoS via ulimit, same ownership of files between container and
    98  // snap (even if the other's files are otherwise inaccessible), etc).
    99  //
   100  // snapd shall assign [ug]ids from range(s) of 65536 where the lowest value in
   101  // the range has the 16 lower bits all set to zero (initially just one range,
   102  // but snapd can add more as needed).
   103  //
   104  // To avoid [ug]id overlaps, snapd shall only assign [ug]ids >= 524288
   105  // (0x00080000) and <= 983040 (0x000F0000, ie the first 65536 range under LXD's
   106  // minimum where the lower 16 bits are all zeroes). While [ug]ids >= 1001062400
   107  // (0x3BAB0000, the first 65536 range above LXD's maximum where the lower 16
   108  // bits are all zeroes) would also avoid overlap, considering nested containers
   109  // (eg, LXD snap runs a container that runs a container that runs snapd),
   110  // choosing >= 1001062400 would mean that the admin would need to increase the
   111  // LXD id range for these containers for snapd to be allowed to create its
   112  // [ug]ids in the deeply nested containers. The requirements would both be an
   113  // administrative burden and artificially limit the number of deeply nested
   114  // containers the host could have.
   115  //
   116  // Looking at the LSB and distribution defaults for login.defs, we can observe
   117  // uids and gids in the system's initial 65536 range (ie, 0-65536):
   118  //
   119  // - 0-99        LSB-suggested statically assigned range (eg, root, daemon,
   120  //               etc)
   121  // - 0           mandatory 'root' user
   122  // - 100-499     LSB-suggested dynamically assigned range for system users
   123  //               (distributions often prefer a higher range, see below)
   124  // - 500-999     typical distribution default for dynamically assigned range
   125  //               for system users (some distributions use a smaller
   126  //               SYS_[GU]ID_MIN)
   127  // - 1000-60000  typical distribution default for dynamically assigned range
   128  //               for regular users
   129  // - 65535 (-1)  should not be assigned since '-1' might be evaluated as this
   130  //               with set[ug]id* and chown families of functions
   131  // - 65534 (-2)  nobody/nogroup user for NFS/etc [ug]id anonymous squashing
   132  // - 65519-65533 systemd recommended reserved range for site-local anonymous
   133  //               additions, etc
   134  //
   135  // To facilitate potential future use cases within the 65536 range snapd will
   136  // assign from, snapd will only assign from the following subset of ranges
   137  // relative to the range minimum (ie, its 'base' which has the lower 16 bits
   138  // all set to zero):
   139  //
   140  // - 60500-60999 'scope: shared' system-usernames
   141  // - 61000-65519 'scope: private' system-usernames
   142  //
   143  // Since the first [ug]id range must be >= 524288 and <= 983040 (see above) and
   144  // following the above guide for system-usernames [ug]ids within this 65536
   145  // range, the lowest 'scope: shared' user in this range is 584788 (0x0008EC54).
   146  //
   147  // Since this number is within systemd-nspawn's range of 524288-1879048191
   148  // (0x00080000-0x6FFFFFFF), the number's lower 16 bits are not all zeroes so
   149  // systemd-nspawn won't detect this allocation and could potentially assign the
   150  // 65536 range starting at 0x00080000 to a container. snapd will therefore also
   151  // create the 'snapd-range-524288-root' user and group with [ug]id 524288 to
   152  // work within systemd-nspawn's collision detection. This user/group will not
   153  // be assigned to snaps at this time.
   154  //
   155  // In short (phew!), use the following:
   156  //
   157  // $ snappy-debug.id-range 524288 # 0x00080000
   158  // Host range:              524288-589823 (00080000-0008ffff; 0-65535)
   159  // LSB static range:        524288-524387 (00080000-00080063; 0-99)
   160  // Useradd system range:    524788-525287 (000801f4-000803e7; 500-999)
   161  // Useradd regular range:   525288-584288 (000803e8-0008ea60; 1000-60000)
   162  // Snapd system range:      584788-585287 (0008ec54-0008ee47; 60500-60999)
   163  // Snapd private range:     585288-589807 (0008ee48-0008ffef; 61000-65519)
   164  //
   165  // Snapd is of course free to add more ranges (eg, 589824 (0x00090000)) with
   166  // new snapd-range-<base>-root users, or to allocate differently within its
   167  // 65536 range in the future (sequentially assigned [ug]ids are not required),
   168  // but for now start very regimented to avoid as many problems as possible.
   169  //
   170  // References:
   171  // https://forum.snapcraft.io/t/multiple-users-and-groups-in-snaps/
   172  // https://systemd.io/UIDS-GIDS.html
   173  // https://docs.docker.com/engine/security/userns-remap/
   174  // https://github.com/lxc/lxd/blob/master/doc/userns-idmap.md
   175  var supportedSystemUsernames = map[string]uint32{
   176  	"snap_daemon": 584788,
   177  }
   178  
   179  func checkAssumes(si *snap.Info) error {
   180  	missing := ([]string)(nil)
   181  	for _, flag := range si.Assumes {
   182  		if strings.HasPrefix(flag, "snapd") && checkVersion(flag[5:]) {
   183  			continue
   184  		}
   185  		if !featureSet[flag] {
   186  			missing = append(missing, flag)
   187  		}
   188  	}
   189  	if len(missing) > 0 {
   190  		hint := "try to refresh the core or snapd snaps"
   191  		if release.OnClassic {
   192  			hint = "try to update snapd and refresh the core snap"
   193  		}
   194  		return fmt.Errorf("snap %q assumes unsupported features: %s (%s)", si.InstanceName(), strings.Join(missing, ", "), hint)
   195  	}
   196  	return nil
   197  }
   198  
   199  var versionExp = regexp.MustCompile(`^([1-9][0-9]*)(?:\.([0-9]+)(?:\.([0-9]+))?)?`)
   200  
   201  func checkVersion(version string) bool {
   202  	// double check that the input looks like a snapd version
   203  	req := versionExp.FindStringSubmatch(version)
   204  	if req == nil || req[0] != version {
   205  		return false
   206  	}
   207  
   208  	if snapdtool.Version == "unknown" {
   209  		return true // Development tree.
   210  	}
   211  
   212  	// We could (should?) use strutil.VersionCompare here and simplify
   213  	// this code (see PR#7344). However this would change current
   214  	// behavior, i.e. "2.41~pre1" would *not* match [snapd2.41] anymore
   215  	// (which the code below does).
   216  	cur := versionExp.FindStringSubmatch(snapdtool.Version)
   217  	if cur == nil {
   218  		return false
   219  	}
   220  
   221  	for i := 1; i < len(req); i++ {
   222  		if req[i] == "" {
   223  			return true
   224  		}
   225  		if cur[i] == "" {
   226  			return false
   227  		}
   228  		reqN, err1 := strconv.Atoi(req[i])
   229  		curN, err2 := strconv.Atoi(cur[i])
   230  		if err1 != nil || err2 != nil {
   231  			panic("internal error: version regexp is broken")
   232  		}
   233  		if curN != reqN {
   234  			return curN > reqN
   235  		}
   236  	}
   237  
   238  	return true
   239  }
   240  
   241  type SnapNeedsDevModeError struct {
   242  	Snap string
   243  }
   244  
   245  func (e *SnapNeedsDevModeError) Error() string {
   246  	return fmt.Sprintf("snap %q requires devmode or confinement override", e.Snap)
   247  }
   248  
   249  type SnapNeedsClassicError struct {
   250  	Snap string
   251  }
   252  
   253  func (e *SnapNeedsClassicError) Error() string {
   254  	return fmt.Sprintf("snap %q requires classic confinement", e.Snap)
   255  }
   256  
   257  type SnapNeedsClassicSystemError struct {
   258  	Snap string
   259  }
   260  
   261  func (e *SnapNeedsClassicSystemError) Error() string {
   262  	return fmt.Sprintf("snap %q requires classic confinement which is only available on classic systems", e.Snap)
   263  }
   264  
   265  type SnapNotClassicError struct {
   266  	Snap string
   267  }
   268  
   269  func (e *SnapNotClassicError) Error() string {
   270  	return fmt.Sprintf("snap %q is not a classic confined snap", e.Snap)
   271  }
   272  
   273  // determine whether the flags (and system overrides thereof) are
   274  // compatible with the given *snap.Info
   275  func validateFlagsForInfo(info *snap.Info, snapst *SnapState, flags Flags) error {
   276  	if flags.Classic && !info.NeedsClassic() {
   277  		return &SnapNotClassicError{Snap: info.InstanceName()}
   278  	}
   279  
   280  	switch c := info.Confinement; c {
   281  	case snap.StrictConfinement, "":
   282  		// strict is always fine
   283  		return nil
   284  	case snap.DevModeConfinement:
   285  		// --devmode needs to be specified every time (==> ignore snapst)
   286  		if flags.DevModeAllowed() {
   287  			return nil
   288  		}
   289  		return &SnapNeedsDevModeError{
   290  			Snap: info.InstanceName(),
   291  		}
   292  	case snap.ClassicConfinement:
   293  		if !release.OnClassic {
   294  			return &SnapNeedsClassicSystemError{Snap: info.InstanceName()}
   295  		}
   296  
   297  		if flags.Classic {
   298  			return nil
   299  		}
   300  
   301  		if snapst != nil && snapst.Flags.Classic {
   302  			return nil
   303  		}
   304  
   305  		return &SnapNeedsClassicError{
   306  			Snap: info.InstanceName(),
   307  		}
   308  	default:
   309  		return fmt.Errorf("unknown confinement %q", c)
   310  	}
   311  }
   312  
   313  // do a reasonably lightweight check that a snap described by Info,
   314  // with the given SnapState and the user-specified Flags should be
   315  // installable on the current system.
   316  func validateInfoAndFlags(info *snap.Info, snapst *SnapState, flags Flags) error {
   317  	if err := validateFlagsForInfo(info, snapst, flags); err != nil {
   318  		return err
   319  	}
   320  
   321  	// verify we have a valid architecture
   322  	if !arch.IsSupportedArchitecture(info.Architectures) {
   323  		return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.DpkgArchitecture())
   324  	}
   325  
   326  	// check assumes
   327  	if err := checkAssumes(info); err != nil {
   328  		return err
   329  	}
   330  
   331  	// check and create system-usernames
   332  	if err := checkAndCreateSystemUsernames(info); err != nil {
   333  		return err
   334  	}
   335  
   336  	return nil
   337  }
   338  
   339  var openSnapFile = backend.OpenSnapFile
   340  
   341  func validateContainer(c snap.Container, s *snap.Info, logf func(format string, v ...interface{})) error {
   342  	err := snap.ValidateContainer(c, s, logf)
   343  	if err == nil {
   344  		return nil
   345  	}
   346  	return fmt.Errorf("%v; contact developer", err)
   347  }
   348  
   349  // checkSnap ensures that the snap can be installed.
   350  func checkSnap(st *state.State, snapFilePath, instanceName string, si *snap.SideInfo, curInfo *snap.Info, flags Flags, deviceCtx DeviceContext) error {
   351  	// This assumes that the snap was already verified or --dangerous was used.
   352  
   353  	s, c, err := openSnapFile(snapFilePath, si)
   354  	if err != nil {
   355  		return err
   356  	}
   357  
   358  	if err := validateInfoAndFlags(s, nil, flags); err != nil {
   359  		return err
   360  	}
   361  
   362  	if err := validateContainer(c, s, logger.Noticef); err != nil {
   363  		return err
   364  	}
   365  
   366  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   367  	// update instance key to what was requested
   368  	s.InstanceKey = instanceKey
   369  
   370  	st.Lock()
   371  	defer st.Unlock()
   372  
   373  	// allow registered checks to run first as they may produce more
   374  	// precise errors
   375  	for _, check := range checkSnapCallbacks {
   376  		err := check(st, s, curInfo, c, flags, deviceCtx)
   377  		if err != nil {
   378  			return err
   379  		}
   380  	}
   381  
   382  	if snapName != s.SnapName() {
   383  		return fmt.Errorf("cannot install snap %q using instance name %q", s.SnapName(), instanceName)
   384  	}
   385  
   386  	return nil
   387  }
   388  
   389  // CheckSnapCallback defines callbacks for checking a snap for installation or refresh.
   390  type CheckSnapCallback func(st *state.State, snap, curSnap *snap.Info, snapf snap.Container, flags Flags, deviceCtx DeviceContext) error
   391  
   392  var checkSnapCallbacks []CheckSnapCallback
   393  
   394  // AddCheckSnapCallback installs a callback to check a snap for installation or refresh.
   395  func AddCheckSnapCallback(check CheckSnapCallback) {
   396  	checkSnapCallbacks = append(checkSnapCallbacks, check)
   397  }
   398  
   399  func MockCheckSnapCallbacks(checks []CheckSnapCallback) (restore func()) {
   400  	prev := checkSnapCallbacks
   401  	checkSnapCallbacks = checks
   402  	return func() {
   403  		checkSnapCallbacks = prev
   404  	}
   405  }
   406  
   407  func checkSnapdName(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error {
   408  	if snapInfo.Type() != snap.TypeSnapd {
   409  		// not a relevant check
   410  		return nil
   411  	}
   412  	if snapInfo.InstanceName() != "snapd" {
   413  		return fmt.Errorf(`cannot install snap %q of type "snapd" with a name other than "snapd"`, snapInfo.InstanceName())
   414  	}
   415  
   416  	return nil
   417  }
   418  
   419  func checkCoreName(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error {
   420  	if snapInfo.Type() != snap.TypeOS {
   421  		// not a relevant check
   422  		return nil
   423  	}
   424  	if curInfo != nil {
   425  		// already one of these installed
   426  		return nil
   427  	}
   428  	core, err := coreInfo(st)
   429  	if err == state.ErrNoState {
   430  		return nil
   431  	}
   432  	if err != nil {
   433  		return err
   434  	}
   435  
   436  	// Allow installing "core" even if "ubuntu-core" is already
   437  	// installed. Ideally we should only allow this if we know
   438  	// this install is part of the ubuntu-core->core transition
   439  	// (e.g. via a flag) because if this happens outside of this
   440  	// transition we will end up with not connected interface
   441  	// connections in the "core" snap. But the transition will
   442  	// kick in automatically quickly so an extra flag is overkill.
   443  	if snapInfo.InstanceName() == "core" && core.InstanceName() == "ubuntu-core" {
   444  		return nil
   445  	}
   446  
   447  	// but generally do not allow to have two cores installed
   448  	if core.InstanceName() != snapInfo.InstanceName() {
   449  		return fmt.Errorf("cannot install core snap %q when core snap %q is already present", snapInfo.InstanceName(), core.InstanceName())
   450  	}
   451  
   452  	return nil
   453  }
   454  
   455  func checkGadgetOrKernel(st *state.State, snapInfo, curInfo *snap.Info, snapf snap.Container, flags Flags, deviceCtx DeviceContext) error {
   456  	typ := snapInfo.Type()
   457  	kind := ""
   458  	var whichName func(*asserts.Model) string
   459  	switch typ {
   460  	case snap.TypeGadget:
   461  		kind = "gadget"
   462  		whichName = (*asserts.Model).Gadget
   463  	case snap.TypeKernel:
   464  		kind = "kernel"
   465  		whichName = (*asserts.Model).Kernel
   466  	default:
   467  		// not a relevant check
   468  		return nil
   469  	}
   470  
   471  	ok, err := HasSnapOfType(st, typ)
   472  	if err != nil {
   473  		return fmt.Errorf("cannot detect original %s snap: %v", kind, err)
   474  	}
   475  	// in firstboot we have no gadget/kernel yet - that is ok
   476  	// first install rules are in devicestate!
   477  	if !ok {
   478  		return nil
   479  	}
   480  
   481  	currentSnap, err := infoForDeviceSnap(st, deviceCtx, kind, whichName)
   482  	if err == state.ErrNoState {
   483  		// check if we are in the remodel case
   484  		if deviceCtx != nil && deviceCtx.ForRemodeling() {
   485  			if whichName(deviceCtx.Model()) == snapInfo.InstanceName() {
   486  				return nil
   487  			}
   488  		}
   489  		return fmt.Errorf("internal error: no state for %s snap %q", kind, snapInfo.InstanceName())
   490  	}
   491  	if err != nil {
   492  		return fmt.Errorf("cannot find original %s snap: %v", kind, err)
   493  	}
   494  
   495  	if currentSnap.SnapID != "" && snapInfo.SnapID == "" {
   496  		return fmt.Errorf("cannot replace signed %s snap with an unasserted one", kind)
   497  	}
   498  
   499  	if currentSnap.SnapID != "" && snapInfo.SnapID != "" {
   500  		if currentSnap.SnapID == snapInfo.SnapID {
   501  			// same snap
   502  			return nil
   503  		}
   504  		return fmt.Errorf("cannot replace %s snap with a different one", kind)
   505  	}
   506  
   507  	if currentSnap.InstanceName() != snapInfo.InstanceName() {
   508  		return fmt.Errorf("cannot replace %s snap with a different one", kind)
   509  	}
   510  
   511  	return nil
   512  }
   513  
   514  func checkBases(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error {
   515  	// check if this is relevant
   516  	if snapInfo.Type() != snap.TypeApp && snapInfo.Type() != snap.TypeGadget {
   517  		return nil
   518  	}
   519  	if snapInfo.Base == "" {
   520  		return nil
   521  	}
   522  	if snapInfo.Base == "none" {
   523  		return nil
   524  	}
   525  
   526  	snapStates, err := All(st)
   527  	if err != nil {
   528  		return err
   529  	}
   530  	for otherSnap, snapst := range snapStates {
   531  		typ, err := snapst.Type()
   532  		if err != nil {
   533  			return err
   534  		}
   535  		if typ == snap.TypeBase && otherSnap == snapInfo.Base {
   536  			return nil
   537  		}
   538  		// core can be used instead for core16
   539  		if snapInfo.Base == "core16" && otherSnap == "core" {
   540  			return nil
   541  		}
   542  	}
   543  
   544  	return fmt.Errorf("cannot find required base %q", snapInfo.Base)
   545  }
   546  
   547  func checkEpochs(_ *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, _ Flags, deviceCtx DeviceContext) error {
   548  	if curInfo == nil {
   549  		return nil
   550  	}
   551  	if snapInfo.Epoch.CanRead(curInfo.Epoch) {
   552  		return nil
   553  	}
   554  	desc := "local snap"
   555  	if snapInfo.SideInfo.Revision.Store() {
   556  		desc = fmt.Sprintf("new revision %s", snapInfo.SideInfo.Revision)
   557  	}
   558  
   559  	return fmt.Errorf("cannot refresh %q to %s with epoch %s, because it can't read the current epoch of %s", snapInfo.InstanceName(), desc, snapInfo.Epoch, curInfo.Epoch)
   560  }
   561  
   562  // check that the snap installed in the system (via snapst) can be
   563  // upgraded to info (i.e. that info's epoch can read sanpst's epoch)
   564  func earlyEpochCheck(info *snap.Info, snapst *SnapState) error {
   565  	if snapst == nil {
   566  		// no snapst, no problem
   567  		return nil
   568  	}
   569  	cur, err := snapst.CurrentInfo()
   570  	if err != nil {
   571  		if err == ErrNoCurrent {
   572  			// refreshing a disabled snap (maybe via InstallPath)
   573  			return nil
   574  		}
   575  		return err
   576  	}
   577  
   578  	return checkEpochs(nil, info, cur, nil, Flags{}, nil)
   579  }
   580  
   581  // check that the listed system users are valid
   582  var osutilEnsureUserGroup = osutil.EnsureUserGroup
   583  
   584  func validateSystemUsernames(si *snap.Info) error {
   585  	for _, user := range si.SystemUsernames {
   586  		if _, ok := supportedSystemUsernames[user.Name]; !ok {
   587  			return fmt.Errorf(`snap %q requires unsupported system username "%s"`, si.InstanceName(), user.Name)
   588  		}
   589  
   590  		switch user.Scope {
   591  		case "shared":
   592  			// this is supported
   593  			continue
   594  		case "private", "external":
   595  			// not supported yet
   596  			return fmt.Errorf(`snap %q requires unsupported user scope "%s" for this version of snapd`, si.InstanceName(), user.Scope)
   597  		default:
   598  			return fmt.Errorf(`snap %q requires unsupported user scope "%s"`, si.InstanceName(), user.Scope)
   599  		}
   600  	}
   601  	return nil
   602  }
   603  
   604  func checkAndCreateSystemUsernames(si *snap.Info) error {
   605  	// No need to check support if no system-usernames
   606  	if len(si.SystemUsernames) == 0 {
   607  		return nil
   608  	}
   609  
   610  	// Run /.../snap-seccomp version-info
   611  	vi, err := seccomp_compiler.CompilerVersionInfo(snapdtool.InternalToolPath)
   612  	if err != nil {
   613  		return fmt.Errorf("cannot obtain seccomp compiler information: %v", err)
   614  	}
   615  
   616  	// If the system doesn't support robust argument filtering then we
   617  	// can't support system-usernames
   618  	if err := vi.SupportsRobustArgumentFiltering(); err != nil {
   619  		if re, ok := err.(*seccomp_compiler.BuildTimeRequirementError); ok {
   620  			return fmt.Errorf("snap %q system usernames require a snapd built against %s", si.InstanceName(), re.RequirementsString())
   621  		}
   622  		return err
   623  	}
   624  
   625  	// first validate
   626  	if err := validateSystemUsernames(si); err != nil {
   627  		return err
   628  	}
   629  
   630  	// then create
   631  	// TODO: move user creation to a more appropriate place like "link-snap"
   632  	extrausers := !release.OnClassic
   633  	for _, user := range si.SystemUsernames {
   634  		id := supportedSystemUsernames[user.Name]
   635  		switch user.Scope {
   636  		case "shared":
   637  			// Create the snapd-range-<base>-root user and group so
   638  			// systemd-nspawn can avoid our range. Our ranges will always
   639  			// be in 65536 chunks, so mask off the lower bits to obtain our
   640  			// base (see above)
   641  			rangeStart := id & 0xFFFF0000
   642  			rangeName := fmt.Sprintf("snapd-range-%d-root", rangeStart)
   643  			if err := osutilEnsureUserGroup(rangeName, rangeStart, extrausers); err != nil {
   644  				return fmt.Errorf(`cannot ensure users for snap %q required system username "%s": %v`, si.InstanceName(), user.Name, err)
   645  			}
   646  
   647  			// Create the requested user and group
   648  			if err := osutilEnsureUserGroup(user.Name, id, extrausers); err != nil {
   649  				return fmt.Errorf(`cannot ensure users for snap %q required system username "%s": %v`, si.InstanceName(), user.Name, err)
   650  			}
   651  		}
   652  	}
   653  	return nil
   654  }
   655  
   656  func init() {
   657  	AddCheckSnapCallback(checkCoreName)
   658  	AddCheckSnapCallback(checkSnapdName)
   659  	AddCheckSnapCallback(checkGadgetOrKernel)
   660  	AddCheckSnapCallback(checkBases)
   661  	AddCheckSnapCallback(checkEpochs)
   662  }