github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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  	"github.com/snapcore/snapd/strutil"
    39  )
    40  
    41  // featureSet contains the flag values that can be listed in assumes entries
    42  // that this ubuntu-core actually provides.
    43  var featureSet = map[string]bool{
    44  	// Support for common data directory across revisions of a snap.
    45  	"common-data-dir": true,
    46  	// Support for the "Environment:" feature in snap.yaml
    47  	"snap-env": true,
    48  	// Support for the "command-chain" feature for apps and hooks in snap.yaml
    49  	"command-chain": true,
    50  	// Support for "kernel-assets" in gadget.yaml. I.e. having volume
    51  	// content of the style $kernel:ref`
    52  	"kernel-assets": true,
    53  }
    54  
    55  func checkAssumes(si *snap.Info) error {
    56  	missing := ([]string)(nil)
    57  	for _, flag := range si.Assumes {
    58  		if strings.HasPrefix(flag, "snapd") && checkVersion(flag[5:]) {
    59  			continue
    60  		}
    61  		if !featureSet[flag] {
    62  			missing = append(missing, flag)
    63  		}
    64  	}
    65  	if len(missing) > 0 {
    66  		hint := "try to refresh the core or snapd snaps"
    67  		if release.OnClassic {
    68  			hint = "try to update snapd and refresh the core snap"
    69  		}
    70  		return fmt.Errorf("snap %q assumes unsupported features: %s (%s)", si.InstanceName(), strings.Join(missing, ", "), hint)
    71  	}
    72  	return nil
    73  }
    74  
    75  // regular expression which matches a version expressed as groups of digits
    76  // separated with dots, with optional non-numbers afterwards
    77  var versionExp = regexp.MustCompile(`^(?:[1-9][0-9]*)(?:\.(?:[0-9]+))*`)
    78  
    79  func checkVersion(version string) bool {
    80  	// double check that the input looks like a snapd version
    81  	reqVersionNumMatch := versionExp.FindStringSubmatch(version)
    82  	if reqVersionNumMatch == nil {
    83  		return false
    84  	}
    85  	// this check ensures that no one can use an assumes like snapd2.48.3~pre2
    86  	// or snapd2.48.5+20.10, as modifiers past the version number are not meant
    87  	// to be relied on for snaps via assumes, however the check against the real
    88  	// snapd version number below allows such non-numeric modifiers since real
    89  	// snapds do have versions like that (for example debian pkg of snapd)
    90  	if reqVersionNumMatch[0] != version {
    91  		return false
    92  	}
    93  
    94  	req := strings.Split(reqVersionNumMatch[0], ".")
    95  
    96  	if snapdtool.Version == "unknown" {
    97  		return true // Development tree.
    98  	}
    99  
   100  	// We could (should?) use strutil.VersionCompare here and simplify
   101  	// this code (see PR#7344). However this would change current
   102  	// behavior, i.e. "2.41~pre1" would *not* match [snapd2.41] anymore
   103  	// (which the code below does).
   104  	curVersionNumMatch := versionExp.FindStringSubmatch(snapdtool.Version)
   105  	if curVersionNumMatch == nil {
   106  		return false
   107  	}
   108  	cur := strings.Split(curVersionNumMatch[0], ".")
   109  
   110  	for i := range req {
   111  		if i == len(cur) {
   112  			// we hit the end of the elements of the current version number and have
   113  			// more required version numbers left, so this doesn't match, if the
   114  			// previous element was higher we would have broken out already, so the
   115  			// only case left here is where we have version requirements that are
   116  			// not met
   117  			return false
   118  		}
   119  		reqN, err1 := strconv.Atoi(req[i])
   120  		curN, err2 := strconv.Atoi(cur[i])
   121  		if err1 != nil || err2 != nil {
   122  			panic("internal error: version regexp is broken")
   123  		}
   124  		if curN != reqN {
   125  			return curN > reqN
   126  		}
   127  	}
   128  
   129  	return true
   130  }
   131  
   132  type SnapNeedsDevModeError struct {
   133  	Snap string
   134  }
   135  
   136  func (e *SnapNeedsDevModeError) Error() string {
   137  	return fmt.Sprintf("snap %q requires devmode or confinement override", e.Snap)
   138  }
   139  
   140  type SnapNeedsClassicError struct {
   141  	Snap string
   142  }
   143  
   144  func (e *SnapNeedsClassicError) Error() string {
   145  	return fmt.Sprintf("snap %q requires classic confinement", e.Snap)
   146  }
   147  
   148  type SnapNeedsClassicSystemError struct {
   149  	Snap string
   150  }
   151  
   152  func (e *SnapNeedsClassicSystemError) Error() string {
   153  	return fmt.Sprintf("snap %q requires classic confinement which is only available on classic systems", e.Snap)
   154  }
   155  
   156  type SnapNotClassicError struct {
   157  	Snap string
   158  }
   159  
   160  func (e *SnapNotClassicError) Error() string {
   161  	return fmt.Sprintf("snap %q is not a classic confined snap", e.Snap)
   162  }
   163  
   164  // determine whether the flags (and system overrides thereof) are
   165  // compatible with the given *snap.Info
   166  func validateFlagsForInfo(info *snap.Info, snapst *SnapState, flags Flags) error {
   167  	if flags.Classic && !info.NeedsClassic() {
   168  		return &SnapNotClassicError{Snap: info.InstanceName()}
   169  	}
   170  
   171  	switch c := info.Confinement; c {
   172  	case snap.StrictConfinement, "":
   173  		// strict is always fine
   174  		return nil
   175  	case snap.DevModeConfinement:
   176  		// --devmode needs to be specified every time (==> ignore snapst)
   177  		if flags.DevModeAllowed() {
   178  			return nil
   179  		}
   180  		return &SnapNeedsDevModeError{
   181  			Snap: info.InstanceName(),
   182  		}
   183  	case snap.ClassicConfinement:
   184  		if !release.OnClassic {
   185  			return &SnapNeedsClassicSystemError{Snap: info.InstanceName()}
   186  		}
   187  
   188  		if flags.Classic {
   189  			return nil
   190  		}
   191  
   192  		if snapst != nil && snapst.Flags.Classic {
   193  			return nil
   194  		}
   195  
   196  		return &SnapNeedsClassicError{
   197  			Snap: info.InstanceName(),
   198  		}
   199  	default:
   200  		return fmt.Errorf("unknown confinement %q", c)
   201  	}
   202  }
   203  
   204  // do a reasonably lightweight check that a snap described by Info,
   205  // with the given SnapState and the user-specified Flags should be
   206  // installable on the current system.
   207  func validateInfoAndFlags(info *snap.Info, snapst *SnapState, flags Flags) error {
   208  	if err := validateFlagsForInfo(info, snapst, flags); err != nil {
   209  		return err
   210  	}
   211  
   212  	// verify we have a valid architecture
   213  	if !arch.IsSupportedArchitecture(info.Architectures) {
   214  		return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.DpkgArchitecture())
   215  	}
   216  
   217  	// check assumes
   218  	if err := checkAssumes(info); err != nil {
   219  		return err
   220  	}
   221  
   222  	// check and create system-usernames
   223  	if err := checkAndCreateSystemUsernames(info); err != nil {
   224  		return err
   225  	}
   226  
   227  	return nil
   228  }
   229  
   230  var openSnapFile = backend.OpenSnapFile
   231  
   232  func validateContainer(c snap.Container, s *snap.Info, logf func(format string, v ...interface{})) error {
   233  	err := snap.ValidateContainer(c, s, logf)
   234  	if err == nil {
   235  		return nil
   236  	}
   237  	return fmt.Errorf("%v; contact developer", err)
   238  }
   239  
   240  // checkSnap ensures that the snap can be installed.
   241  func checkSnap(st *state.State, snapFilePath, instanceName string, si *snap.SideInfo, curInfo *snap.Info, flags Flags, deviceCtx DeviceContext) error {
   242  	// This assumes that the snap was already verified or --dangerous was used.
   243  
   244  	s, c, err := openSnapFile(snapFilePath, si)
   245  	if err != nil {
   246  		return err
   247  	}
   248  
   249  	if err := validateInfoAndFlags(s, nil, flags); err != nil {
   250  		return err
   251  	}
   252  
   253  	if err := validateContainer(c, s, logger.Noticef); err != nil {
   254  		return err
   255  	}
   256  
   257  	snapName, instanceKey := snap.SplitInstanceName(instanceName)
   258  	// update instance key to what was requested
   259  	s.InstanceKey = instanceKey
   260  
   261  	st.Lock()
   262  	defer st.Unlock()
   263  
   264  	// allow registered checks to run first as they may produce more
   265  	// precise errors
   266  	for _, check := range checkSnapCallbacks {
   267  		err := check(st, s, curInfo, c, flags, deviceCtx)
   268  		if err != nil {
   269  			return err
   270  		}
   271  	}
   272  
   273  	if snapName != s.SnapName() {
   274  		return fmt.Errorf("cannot install snap %q using instance name %q", s.SnapName(), instanceName)
   275  	}
   276  
   277  	return nil
   278  }
   279  
   280  // CheckSnapCallback defines callbacks for checking a snap for installation or refresh.
   281  type CheckSnapCallback func(st *state.State, snap, curSnap *snap.Info, snapf snap.Container, flags Flags, deviceCtx DeviceContext) error
   282  
   283  var checkSnapCallbacks []CheckSnapCallback
   284  
   285  // AddCheckSnapCallback installs a callback to check a snap for installation or refresh.
   286  func AddCheckSnapCallback(check CheckSnapCallback) {
   287  	checkSnapCallbacks = append(checkSnapCallbacks, check)
   288  }
   289  
   290  func MockCheckSnapCallbacks(checks []CheckSnapCallback) (restore func()) {
   291  	prev := checkSnapCallbacks
   292  	checkSnapCallbacks = checks
   293  	return func() {
   294  		checkSnapCallbacks = prev
   295  	}
   296  }
   297  
   298  func checkSnapdName(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error {
   299  	if snapInfo.Type() != snap.TypeSnapd {
   300  		// not a relevant check
   301  		return nil
   302  	}
   303  	if snapInfo.InstanceName() != "snapd" {
   304  		return fmt.Errorf(`cannot install snap %q of type "snapd" with a name other than "snapd"`, snapInfo.InstanceName())
   305  	}
   306  
   307  	return nil
   308  }
   309  
   310  func checkCoreName(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error {
   311  	if snapInfo.Type() != snap.TypeOS {
   312  		// not a relevant check
   313  		return nil
   314  	}
   315  	if curInfo != nil {
   316  		// already one of these installed
   317  		return nil
   318  	}
   319  	core, err := coreInfo(st)
   320  	if err == state.ErrNoState {
   321  		return nil
   322  	}
   323  	if err != nil {
   324  		return err
   325  	}
   326  
   327  	// Allow installing "core" even if "ubuntu-core" is already
   328  	// installed. Ideally we should only allow this if we know
   329  	// this install is part of the ubuntu-core->core transition
   330  	// (e.g. via a flag) because if this happens outside of this
   331  	// transition we will end up with not connected interface
   332  	// connections in the "core" snap. But the transition will
   333  	// kick in automatically quickly so an extra flag is overkill.
   334  	if snapInfo.InstanceName() == "core" && core.InstanceName() == "ubuntu-core" {
   335  		return nil
   336  	}
   337  
   338  	// but generally do not allow to have two cores installed
   339  	if core.InstanceName() != snapInfo.InstanceName() {
   340  		return fmt.Errorf("cannot install core snap %q when core snap %q is already present", snapInfo.InstanceName(), core.InstanceName())
   341  	}
   342  
   343  	return nil
   344  }
   345  
   346  func checkGadgetOrKernel(st *state.State, snapInfo, curInfo *snap.Info, snapf snap.Container, flags Flags, deviceCtx DeviceContext) error {
   347  	typ := snapInfo.Type()
   348  	kind := ""
   349  	var whichName func(*asserts.Model) string
   350  	switch typ {
   351  	case snap.TypeGadget:
   352  		kind = "gadget"
   353  		whichName = (*asserts.Model).Gadget
   354  	case snap.TypeKernel:
   355  		kind = "kernel"
   356  		whichName = (*asserts.Model).Kernel
   357  	default:
   358  		// not a relevant check
   359  		return nil
   360  	}
   361  
   362  	ok, err := HasSnapOfType(st, typ)
   363  	if err != nil {
   364  		return fmt.Errorf("cannot detect original %s snap: %v", kind, err)
   365  	}
   366  	// in firstboot we have no gadget/kernel yet - that is ok
   367  	// first install rules are in devicestate!
   368  	if !ok {
   369  		return nil
   370  	}
   371  
   372  	currentSnap, err := infoForDeviceSnap(st, deviceCtx, kind, whichName)
   373  	if err == state.ErrNoState {
   374  		// check if we are in the remodel case
   375  		if deviceCtx != nil && deviceCtx.ForRemodeling() {
   376  			if whichName(deviceCtx.Model()) == snapInfo.InstanceName() {
   377  				return nil
   378  			}
   379  		}
   380  		return fmt.Errorf("internal error: no state for %s snap %q", kind, snapInfo.InstanceName())
   381  	}
   382  	if err != nil {
   383  		return fmt.Errorf("cannot find original %s snap: %v", kind, err)
   384  	}
   385  
   386  	if currentSnap.SnapID != "" && snapInfo.SnapID == "" {
   387  		return fmt.Errorf("cannot replace signed %s snap with an unasserted one", kind)
   388  	}
   389  
   390  	if currentSnap.SnapID != "" && snapInfo.SnapID != "" {
   391  		if currentSnap.SnapID == snapInfo.SnapID {
   392  			// same snap
   393  			return nil
   394  		}
   395  		return fmt.Errorf("cannot replace %s snap with a different one", kind)
   396  	}
   397  
   398  	if currentSnap.InstanceName() != snapInfo.InstanceName() {
   399  		return fmt.Errorf("cannot replace %s snap with a different one", kind)
   400  	}
   401  
   402  	return nil
   403  }
   404  
   405  func checkBases(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error {
   406  	// check if this is relevant
   407  	if snapInfo.Type() != snap.TypeApp && snapInfo.Type() != snap.TypeGadget {
   408  		return nil
   409  	}
   410  	if snapInfo.Base == "" {
   411  		return nil
   412  	}
   413  	if snapInfo.Base == "none" {
   414  		return nil
   415  	}
   416  
   417  	snapStates, err := All(st)
   418  	if err != nil {
   419  		return err
   420  	}
   421  	for otherSnap, snapst := range snapStates {
   422  		typ, err := snapst.Type()
   423  		if err != nil {
   424  			return err
   425  		}
   426  		if typ == snap.TypeBase && otherSnap == snapInfo.Base {
   427  			return nil
   428  		}
   429  		// core can be used instead for core16
   430  		if snapInfo.Base == "core16" && otherSnap == "core" {
   431  			return nil
   432  		}
   433  	}
   434  
   435  	return fmt.Errorf("cannot find required base %q", snapInfo.Base)
   436  }
   437  
   438  func checkEpochs(_ *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, _ Flags, deviceCtx DeviceContext) error {
   439  	if curInfo == nil {
   440  		return nil
   441  	}
   442  	if snapInfo.Epoch.CanRead(curInfo.Epoch) {
   443  		return nil
   444  	}
   445  	desc := "local snap"
   446  	if snapInfo.SideInfo.Revision.Store() {
   447  		desc = fmt.Sprintf("new revision %s", snapInfo.SideInfo.Revision)
   448  	}
   449  
   450  	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)
   451  }
   452  
   453  // check that the snap installed in the system (via snapst) can be
   454  // upgraded to info (i.e. that info's epoch can read sanpst's epoch)
   455  func earlyEpochCheck(info *snap.Info, snapst *SnapState) error {
   456  	if snapst == nil {
   457  		// no snapst, no problem
   458  		return nil
   459  	}
   460  	cur, err := snapst.CurrentInfo()
   461  	if err != nil {
   462  		if err == ErrNoCurrent {
   463  			// refreshing a disabled snap (maybe via InstallPath)
   464  			return nil
   465  		}
   466  		return err
   467  	}
   468  
   469  	return checkEpochs(nil, info, cur, nil, Flags{}, nil)
   470  }
   471  
   472  func earlyChecks(st *state.State, snapst *SnapState, update *snap.Info, flags Flags) (Flags, error) {
   473  	flags, err := ensureInstallPreconditions(st, update, flags, snapst)
   474  	if err != nil {
   475  		return flags, err
   476  	}
   477  
   478  	if err := earlyEpochCheck(update, snapst); err != nil {
   479  		return flags, err
   480  	}
   481  	return flags, nil
   482  }
   483  
   484  // check that the listed system users are valid
   485  var osutilEnsureUserGroup = osutil.EnsureUserGroup
   486  
   487  func validateSystemUsernames(si *snap.Info) error {
   488  	for _, user := range si.SystemUsernames {
   489  		systemUserName, ok := snap.SupportedSystemUsernames[user.Name]
   490  		if !ok {
   491  			return fmt.Errorf(`snap %q requires unsupported system username "%s"`, si.InstanceName(), user.Name)
   492  		}
   493  
   494  		if systemUserName.AllowedSnapIds != nil && si.SnapID != "" {
   495  			// Only certain snaps can use this user; let's check whether ours
   496  			// is one of these
   497  			if !strutil.ListContains(systemUserName.AllowedSnapIds, si.SnapID) {
   498  				return fmt.Errorf(`snap %q is not allowed to use the system user %q`,
   499  					si.InstanceName(), user.Name)
   500  			}
   501  		}
   502  
   503  		switch user.Scope {
   504  		case "shared":
   505  			// this is supported
   506  			continue
   507  		case "private", "external":
   508  			// not supported yet
   509  			return fmt.Errorf(`snap %q requires unsupported user scope "%s" for this version of snapd`, si.InstanceName(), user.Scope)
   510  		default:
   511  			return fmt.Errorf(`snap %q requires unsupported user scope "%s"`, si.InstanceName(), user.Scope)
   512  		}
   513  	}
   514  	return nil
   515  }
   516  
   517  func checkAndCreateSystemUsernames(si *snap.Info) error {
   518  	// No need to check support if no system-usernames
   519  	if len(si.SystemUsernames) == 0 {
   520  		return nil
   521  	}
   522  
   523  	// Run /.../snap-seccomp version-info
   524  	vi, err := seccomp_compiler.CompilerVersionInfo(snapdtool.InternalToolPath)
   525  	if err != nil {
   526  		return fmt.Errorf("cannot obtain seccomp compiler information: %v", err)
   527  	}
   528  
   529  	// If the system doesn't support robust argument filtering then we
   530  	// can't support system-usernames
   531  	if err := vi.SupportsRobustArgumentFiltering(); err != nil {
   532  		if re, ok := err.(*seccomp_compiler.BuildTimeRequirementError); ok {
   533  			return fmt.Errorf("snap %q system usernames require a snapd built against %s", si.InstanceName(), re.RequirementsString())
   534  		}
   535  		return err
   536  	}
   537  
   538  	// first validate
   539  	if err := validateSystemUsernames(si); err != nil {
   540  		return err
   541  	}
   542  
   543  	// then create
   544  	// TODO: move user creation to a more appropriate place like "link-snap"
   545  	extrausers := !release.OnClassic
   546  	for _, user := range si.SystemUsernames {
   547  		id := snap.SupportedSystemUsernames[user.Name].Id
   548  		switch user.Scope {
   549  		case "shared":
   550  			// Create the snapd-range-<base>-root user and group so
   551  			// systemd-nspawn can avoid our range. Our ranges will always
   552  			// be in 65536 chunks, so mask off the lower bits to obtain our
   553  			// base (see above)
   554  			rangeStart := id & 0xFFFF0000
   555  			rangeName := fmt.Sprintf("snapd-range-%d-root", rangeStart)
   556  			if err := osutilEnsureUserGroup(rangeName, rangeStart, extrausers); err != nil {
   557  				return fmt.Errorf(`cannot ensure users for snap %q required system username "%s": %v`, si.InstanceName(), user.Name, err)
   558  			}
   559  
   560  			// Create the requested user and group
   561  			if err := osutilEnsureUserGroup(user.Name, id, extrausers); err != nil {
   562  				return fmt.Errorf(`cannot ensure users for snap %q required system username "%s": %v`, si.InstanceName(), user.Name, err)
   563  			}
   564  		}
   565  	}
   566  	return nil
   567  }
   568  
   569  func init() {
   570  	AddCheckSnapCallback(checkCoreName)
   571  	AddCheckSnapCallback(checkSnapdName)
   572  	AddCheckSnapCallback(checkGadgetOrKernel)
   573  	AddCheckSnapCallback(checkBases)
   574  	AddCheckSnapCallback(checkEpochs)
   575  }