github.com/tompreston/snapd@v0.0.0-20210817193607-954edfcb9611/overlord/assertstate/assertstate.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2019 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 assertstate implements the manager and state aspects responsible
    21  // for the enforcement of assertions in the system and manages the system-wide
    22  // assertion database.
    23  package assertstate
    24  
    25  import (
    26  	"fmt"
    27  	"strings"
    28  
    29  	"github.com/snapcore/snapd/asserts"
    30  	"github.com/snapcore/snapd/asserts/snapasserts"
    31  	"github.com/snapcore/snapd/httputil"
    32  	"github.com/snapcore/snapd/logger"
    33  	"github.com/snapcore/snapd/overlord/snapstate"
    34  	"github.com/snapcore/snapd/overlord/state"
    35  	"github.com/snapcore/snapd/release"
    36  	"github.com/snapcore/snapd/snap"
    37  )
    38  
    39  // Add the given assertion to the system assertion database.
    40  func Add(s *state.State, a asserts.Assertion) error {
    41  	// TODO: deal together with asserts itself with (cascading) side effects of possible assertion updates
    42  	return cachedDB(s).Add(a)
    43  }
    44  
    45  // AddBatch adds the given assertion batch to the system assertion database.
    46  func AddBatch(s *state.State, batch *asserts.Batch, opts *asserts.CommitOptions) error {
    47  	return batch.CommitTo(cachedDB(s), opts)
    48  }
    49  
    50  func findError(format string, ref *asserts.Ref, err error) error {
    51  	if asserts.IsNotFound(err) {
    52  		return fmt.Errorf(format, ref)
    53  	} else {
    54  		return fmt.Errorf(format+": %v", ref, err)
    55  	}
    56  }
    57  
    58  // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites.
    59  func RefreshSnapDeclarations(s *state.State, userID int) error {
    60  	deviceCtx, err := snapstate.DevicePastSeeding(s, nil)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	snapStates, err := snapstate.All(s)
    66  	if err != nil {
    67  		return nil
    68  	}
    69  
    70  	err = bulkRefreshSnapDeclarations(s, snapStates, userID, deviceCtx)
    71  	if err == nil {
    72  		// done
    73  		return nil
    74  	}
    75  	if _, ok := err.(*bulkAssertionFallbackError); !ok {
    76  		// not an error that indicates the server rejecting/failing
    77  		// the bulk request itself
    78  		return err
    79  	}
    80  	logger.Noticef("bulk refresh of snap-declarations failed, falling back to one-by-one assertion fetching: %v", err)
    81  
    82  	modelAs := deviceCtx.Model()
    83  
    84  	fetching := func(f asserts.Fetcher) error {
    85  		for instanceName, snapst := range snapStates {
    86  			sideInfo := snapst.CurrentSideInfo()
    87  			if sideInfo.SnapID == "" {
    88  				continue
    89  			}
    90  			if err := snapasserts.FetchSnapDeclaration(f, sideInfo.SnapID); err != nil {
    91  				if notRetried, ok := err.(*httputil.PersistentNetworkError); ok {
    92  					return notRetried
    93  				}
    94  				return fmt.Errorf("cannot refresh snap-declaration for %q: %v", instanceName, err)
    95  			}
    96  		}
    97  
    98  		// fetch store assertion if available
    99  		if modelAs.Store() != "" {
   100  			err := snapasserts.FetchStore(f, modelAs.Store())
   101  			if err != nil && !asserts.IsNotFound(err) {
   102  				return err
   103  			}
   104  		}
   105  
   106  		return nil
   107  	}
   108  	return doFetch(s, userID, deviceCtx, fetching)
   109  }
   110  
   111  type refreshControlError struct {
   112  	errs []error
   113  }
   114  
   115  func (e *refreshControlError) Error() string {
   116  	if len(e.errs) == 1 {
   117  		return e.errs[0].Error()
   118  	}
   119  	l := []string{""}
   120  	for _, e := range e.errs {
   121  		l = append(l, e.Error())
   122  	}
   123  	return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - "))
   124  }
   125  
   126  // ValidateRefreshes validates the refresh candidate revisions represented by
   127  // the snapInfos, looking for the needed refresh control validation assertions,
   128  // it returns a validated subset in validated and a summary error if not all
   129  // candidates validated. ignoreValidation is a set of snap-instance-names that
   130  // should not be gated.
   131  func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) (validated []*snap.Info, err error) {
   132  	// maps gated snap-ids to gating snap-ids
   133  	controlled := make(map[string][]string)
   134  	// maps gating snap-ids to their snap names
   135  	gatingNames := make(map[string]string)
   136  
   137  	db := DB(s)
   138  	snapStates, err := snapstate.All(s)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	for instanceName, snapst := range snapStates {
   143  		info, err := snapst.CurrentInfo()
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  		if info.SnapID == "" {
   148  			continue
   149  		}
   150  		gatingID := info.SnapID
   151  		if gatingNames[gatingID] != "" {
   152  			continue
   153  		}
   154  		a, err := db.Find(asserts.SnapDeclarationType, map[string]string{
   155  			"series":  release.Series,
   156  			"snap-id": gatingID,
   157  		})
   158  		if err != nil {
   159  			return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", instanceName, err)
   160  		}
   161  		decl := a.(*asserts.SnapDeclaration)
   162  		control := decl.RefreshControl()
   163  		if len(control) == 0 {
   164  			continue
   165  		}
   166  		gatingNames[gatingID] = decl.SnapName()
   167  		for _, gatedID := range control {
   168  			controlled[gatedID] = append(controlled[gatedID], gatingID)
   169  		}
   170  	}
   171  
   172  	var errs []error
   173  	for _, candInfo := range snapInfos {
   174  		if ignoreValidation[candInfo.InstanceName()] {
   175  			validated = append(validated, candInfo)
   176  			continue
   177  		}
   178  		gatedID := candInfo.SnapID
   179  		gating := controlled[gatedID]
   180  		if len(gating) == 0 { // easy case, no refresh control
   181  			validated = append(validated, candInfo)
   182  			continue
   183  		}
   184  
   185  		var validationRefs []*asserts.Ref
   186  
   187  		fetching := func(f asserts.Fetcher) error {
   188  			for _, gatingID := range gating {
   189  				valref := &asserts.Ref{
   190  					Type:       asserts.ValidationType,
   191  					PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()},
   192  				}
   193  				err := f.Fetch(valref)
   194  				if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType {
   195  					return fmt.Errorf("no validation by %q", gatingNames[gatingID])
   196  				}
   197  				if err != nil {
   198  					return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err)
   199  				}
   200  				validationRefs = append(validationRefs, valref)
   201  			}
   202  			return nil
   203  		}
   204  		err := doFetch(s, userID, deviceCtx, fetching)
   205  		if err != nil {
   206  			errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.InstanceName(), candInfo.Revision, err))
   207  			continue
   208  		}
   209  
   210  		var revoked *asserts.Validation
   211  		for _, valref := range validationRefs {
   212  			a, err := valref.Resolve(db.Find)
   213  			if err != nil {
   214  				return nil, findError("internal error: cannot find just fetched %v", valref, err)
   215  			}
   216  			if val := a.(*asserts.Validation); val.Revoked() {
   217  				revoked = val
   218  				break
   219  			}
   220  		}
   221  		if revoked != nil {
   222  			errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: validation by %q (id %q) revoked", candInfo.InstanceName(), candInfo.Revision, gatingNames[revoked.SnapID()], revoked.SnapID()))
   223  			continue
   224  		}
   225  
   226  		validated = append(validated, candInfo)
   227  	}
   228  
   229  	if errs != nil {
   230  		return validated, &refreshControlError{errs}
   231  	}
   232  
   233  	return validated, nil
   234  }
   235  
   236  // BaseDeclaration returns the base-declaration assertion with policies governing all snaps.
   237  func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) {
   238  	// TODO: switch keeping this in the DB and have it revisioned/updated
   239  	// via the store
   240  	baseDecl := asserts.BuiltinBaseDeclaration()
   241  	if baseDecl == nil {
   242  		return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType}
   243  	}
   244  	return baseDecl, nil
   245  }
   246  
   247  // SnapDeclaration returns the snap-declaration for the given snap-id if it is present in the system assertion database.
   248  func SnapDeclaration(s *state.State, snapID string) (*asserts.SnapDeclaration, error) {
   249  	db := DB(s)
   250  	a, err := db.Find(asserts.SnapDeclarationType, map[string]string{
   251  		"series":  release.Series,
   252  		"snap-id": snapID,
   253  	})
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  	return a.(*asserts.SnapDeclaration), nil
   258  }
   259  
   260  // Publisher returns the account assertion for publisher of the given snap-id if it is present in the system assertion database.
   261  func Publisher(s *state.State, snapID string) (*asserts.Account, error) {
   262  	db := DB(s)
   263  	a, err := db.Find(asserts.SnapDeclarationType, map[string]string{
   264  		"series":  release.Series,
   265  		"snap-id": snapID,
   266  	})
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	snapDecl := a.(*asserts.SnapDeclaration)
   271  	a, err = db.Find(asserts.AccountType, map[string]string{
   272  		"account-id": snapDecl.PublisherID(),
   273  	})
   274  	if err != nil {
   275  		return nil, fmt.Errorf("internal error: cannot find account assertion for the publisher of snap %q: %v", snapDecl.SnapName(), err)
   276  	}
   277  	return a.(*asserts.Account), nil
   278  }
   279  
   280  // Store returns the store assertion with the given name/id if it is
   281  // present in the system assertion database.
   282  func Store(s *state.State, store string) (*asserts.Store, error) {
   283  	db := DB(s)
   284  	a, err := db.Find(asserts.StoreType, map[string]string{
   285  		"store": store,
   286  	})
   287  	if err != nil {
   288  		return nil, err
   289  	}
   290  	return a.(*asserts.Store), nil
   291  }
   292  
   293  // AutoAliases returns the explicit automatic aliases alias=>app mapping for the given installed snap.
   294  func AutoAliases(s *state.State, info *snap.Info) (map[string]string, error) {
   295  	if info.SnapID == "" {
   296  		// without declaration
   297  		return nil, nil
   298  	}
   299  	decl, err := SnapDeclaration(s, info.SnapID)
   300  	if err != nil {
   301  		return nil, fmt.Errorf("internal error: cannot find snap-declaration for installed snap %q: %v", info.InstanceName(), err)
   302  	}
   303  	explicitAliases := decl.Aliases()
   304  	if len(explicitAliases) != 0 {
   305  		return explicitAliases, nil
   306  	}
   307  	// XXX: old header fallback, just to keep edge working while we fix the
   308  	// store, to remove before next release!
   309  	oldAutoAliases := decl.AutoAliases()
   310  	if len(oldAutoAliases) == 0 {
   311  		return nil, nil
   312  	}
   313  	res := make(map[string]string, len(oldAutoAliases))
   314  	for _, alias := range oldAutoAliases {
   315  		app := info.LegacyAliases[alias]
   316  		if app == nil {
   317  			// not a known alias anymore or yet, skip
   318  			continue
   319  
   320  		}
   321  		res[alias] = app.Name
   322  	}
   323  	return res, nil
   324  }
   325  
   326  func delayedCrossMgrInit() {
   327  	// hook validation of refreshes into snapstate logic
   328  	snapstate.ValidateRefreshes = ValidateRefreshes
   329  	// hook auto refresh of assertions (snap declarations) into snapstate
   330  	snapstate.AutoRefreshAssertions = AutoRefreshAssertions
   331  	// hook retrieving auto-aliases into snapstate logic
   332  	snapstate.AutoAliases = AutoAliases
   333  	// hook the helper for getting enforced validation sets
   334  	snapstate.EnforcedValidationSets = EnforcedValidationSets
   335  }
   336  
   337  // AutoRefreshAssertions tries to refresh all assertions
   338  func AutoRefreshAssertions(s *state.State, userID int) error {
   339  	if err := RefreshSnapDeclarations(s, userID); err != nil {
   340  		return err
   341  	}
   342  	return RefreshValidationSetAssertions(s, userID)
   343  }
   344  
   345  // RefreshValidationSetAssertions tries to refresh all validation set
   346  // assertions.
   347  func RefreshValidationSetAssertions(s *state.State, userID int) error {
   348  	deviceCtx, err := snapstate.DevicePastSeeding(s, nil)
   349  	if err != nil {
   350  		return err
   351  	}
   352  
   353  	vsets, err := ValidationSets(s)
   354  	if err != nil {
   355  		return err
   356  	}
   357  	if len(vsets) == 0 {
   358  		return nil
   359  	}
   360  
   361  	monitorModeSets := make(map[string]*ValidationSetTracking)
   362  	enforceModeSets := make(map[string]*ValidationSetTracking)
   363  	for vk, vset := range vsets {
   364  		if vset.Mode == Monitor {
   365  			monitorModeSets[vk] = vset
   366  		} else {
   367  			enforceModeSets[vk] = vset
   368  		}
   369  	}
   370  
   371  	updateTracking := func(sets map[string]*ValidationSetTracking) error {
   372  		// update validation set tracking state
   373  		for _, vs := range sets {
   374  			if vs.PinnedAt == 0 {
   375  				headers := map[string]string{
   376  					"series":     release.Series,
   377  					"account-id": vs.AccountID,
   378  					"name":       vs.Name,
   379  				}
   380  				db := DB(s)
   381  				as, err := db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat())
   382  				if err != nil {
   383  					return fmt.Errorf("internal error: cannot find assertion %v when refreshing validation-set assertions", headers)
   384  				}
   385  				if vs.Current != as.Sequence() {
   386  					vs.Current = as.Sequence()
   387  					UpdateValidationSet(s, vs)
   388  				}
   389  			}
   390  		}
   391  		return nil
   392  	}
   393  
   394  	if err := bulkRefreshValidationSetAsserts(s, monitorModeSets, nil, userID, deviceCtx); err != nil {
   395  		return err
   396  	}
   397  	if err := updateTracking(monitorModeSets); err != nil {
   398  		return err
   399  	}
   400  
   401  	checkForConflicts := func(db *asserts.Database, bs asserts.Backstore) error {
   402  		vsets := snapasserts.NewValidationSets()
   403  		tmpDb := db.WithStackedBackstore(bs)
   404  		for _, vs := range enforceModeSets {
   405  			headers := map[string]string{
   406  				"series":     release.Series,
   407  				"account-id": vs.AccountID,
   408  				"name":       vs.Name,
   409  			}
   410  			var err error
   411  			var as asserts.Assertion
   412  			if vs.PinnedAt > 0 {
   413  				headers["sequence"] = fmt.Sprintf("%d", vs.PinnedAt)
   414  				as, err = tmpDb.Find(asserts.ValidationSetType, headers)
   415  			} else {
   416  				as, err = tmpDb.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat())
   417  			}
   418  			if err != nil {
   419  				return fmt.Errorf("internal error: cannot find validation set assertion: %v", err)
   420  			}
   421  
   422  			vsass, ok := as.(*asserts.ValidationSet)
   423  			if !ok {
   424  				return fmt.Errorf("internal error: unexpected assertion type %s for %s", vsass.Type().Name, ValidationSetKey(vs.AccountID, vs.Name))
   425  			}
   426  			if err := vsets.Add(vsass); err != nil {
   427  				return fmt.Errorf("internal error: cannot check validation sets conflicts: %v", err)
   428  			}
   429  		}
   430  		return vsets.Conflict()
   431  	}
   432  
   433  	if err := bulkRefreshValidationSetAsserts(s, enforceModeSets, checkForConflicts, userID, deviceCtx); err != nil {
   434  		if _, ok := err.(*snapasserts.ValidationSetsConflictError); ok {
   435  			logger.Noticef("cannot refresh to conflicting validation set assertions: %v", err)
   436  			return nil
   437  		}
   438  		return err
   439  	}
   440  	if err := updateTracking(enforceModeSets); err != nil {
   441  		return err
   442  	}
   443  
   444  	return nil
   445  }
   446  
   447  // ResolveOptions carries extra options for ValidationSetAssertionForMonitor.
   448  type ResolveOptions struct {
   449  	AllowLocalFallback bool
   450  }
   451  
   452  // ValidationSetAssertionForMonitor tries to fetch or refresh the validation
   453  // set assertion with accountID/name/sequence (sequence is optional) using pool.
   454  // If assertion cannot be fetched but exists locally and opts.AllowLocalFallback
   455  // is set then the local one is returned
   456  func ValidationSetAssertionForMonitor(st *state.State, accountID, name string, sequence int, pinned bool, userID int, opts *ResolveOptions) (as *asserts.ValidationSet, local bool, err error) {
   457  	if opts == nil {
   458  		opts = &ResolveOptions{}
   459  	}
   460  	deviceCtx, err := snapstate.DevicePastSeeding(st, nil)
   461  	if err != nil {
   462  		return nil, false, err
   463  	}
   464  
   465  	var vs asserts.Assertion
   466  	headers := map[string]string{
   467  		"series":     release.Series,
   468  		"account-id": accountID,
   469  		"name":       name,
   470  	}
   471  
   472  	db := cachedDB(st)
   473  
   474  	// try to get existing one from db
   475  	if sequence > 0 {
   476  		headers["sequence"] = fmt.Sprintf("%d", sequence)
   477  		vs, err = db.Find(asserts.ValidationSetType, headers)
   478  	} else {
   479  		// find latest
   480  		vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1)
   481  	}
   482  	if err != nil && !asserts.IsNotFound(err) {
   483  		return nil, false, err
   484  	}
   485  	if err == nil {
   486  		as = vs.(*asserts.ValidationSet)
   487  	}
   488  
   489  	// try to resolve or update with pool
   490  	pool := asserts.NewPool(db, maxGroups)
   491  	atSeq := &asserts.AtSequence{
   492  		Type:        asserts.ValidationSetType,
   493  		SequenceKey: []string{release.Series, accountID, name},
   494  		Sequence:    sequence,
   495  		Pinned:      pinned,
   496  	}
   497  	if as != nil {
   498  		atSeq.Revision = as.Revision()
   499  	} else {
   500  		atSeq.Revision = asserts.RevisionNotKnown
   501  	}
   502  
   503  	// resolve if not found locally, otherwise add for update
   504  	if as == nil {
   505  		if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil {
   506  			return nil, false, err
   507  		}
   508  	} else {
   509  		atSeq.Sequence = as.Sequence()
   510  		// found locally, try to update
   511  		atSeq.Revision = as.Revision()
   512  		if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil {
   513  			return nil, false, err
   514  		}
   515  	}
   516  
   517  	if err := resolvePoolNoFallback(st, pool, nil, userID, deviceCtx); err != nil {
   518  		rerr, ok := err.(*resolvePoolError)
   519  		if ok && as != nil && opts.AllowLocalFallback {
   520  			if e := rerr.errors[atSeq.Unique()]; asserts.IsNotFound(e) {
   521  				// fallback: support the scenario of local assertion (snap ack)
   522  				// not available in the store.
   523  				return as, true, nil
   524  			}
   525  		}
   526  		return nil, false, err
   527  	}
   528  
   529  	// fetch the requested assertion again
   530  	if pinned {
   531  		vs, err = db.Find(asserts.ValidationSetType, headers)
   532  	} else {
   533  		vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat())
   534  	}
   535  	if err == nil {
   536  		as = vs.(*asserts.ValidationSet)
   537  	}
   538  	return as, false, err
   539  }
   540  
   541  // ValidationSetAssertionForEnforce tries to fetch the validation set assertion
   542  // with the given accountID/name/sequence (sequence is optional) using pool and
   543  // checks if it's not in conflict with existing validation sets in enforcing mode
   544  // (all currently tracked validation set assertions get refreshed), and if they
   545  // are valid for installed snaps.
   546  func ValidationSetAssertionForEnforce(st *state.State, accountID, name string, sequence int, userID int, snaps []*snapasserts.InstalledSnap) (vs *asserts.ValidationSet, err error) {
   547  	deviceCtx, err := snapstate.DevicePastSeeding(st, nil)
   548  	if err != nil {
   549  		return nil, err
   550  	}
   551  
   552  	// refresh all currently tracked validation set assertions (this may or may not
   553  	// include the one requested by the caller).
   554  	if err = RefreshValidationSetAssertions(st, userID); err != nil {
   555  		return nil, err
   556  	}
   557  
   558  	valsets, err := EnforcedValidationSets(st)
   559  	if err != nil {
   560  		return nil, err
   561  	}
   562  
   563  	getSpecificSequenceOrLatest := func(db *asserts.Database, headers map[string]string) (vs *asserts.ValidationSet, err error) {
   564  		var a asserts.Assertion
   565  		if _, ok := headers["sequence"]; ok {
   566  			a, err = db.Find(asserts.ValidationSetType, headers)
   567  		} else {
   568  			a, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1)
   569  		}
   570  		if err != nil {
   571  			return nil, err
   572  		}
   573  		vs = a.(*asserts.ValidationSet)
   574  		return vs, nil
   575  	}
   576  
   577  	// try to get existing from the db. It will be the latest one if it was
   578  	// tracked already and thus refreshed via RefreshValidationSetAssertions.
   579  	// Otherwise, it may be a local assertion that was tracked in the past and
   580  	// then forgotten, in which case we need to refresh it explicitly.
   581  	db := cachedDB(st)
   582  	headers := map[string]string{
   583  		"series":     release.Series,
   584  		"account-id": accountID,
   585  		"name":       name,
   586  	}
   587  	if sequence > 0 {
   588  		headers["sequence"] = fmt.Sprintf("%d", sequence)
   589  	}
   590  
   591  	pool := asserts.NewPool(db, maxGroups)
   592  	atSeq := &asserts.AtSequence{
   593  		Type:        asserts.ValidationSetType,
   594  		SequenceKey: []string{release.Series, accountID, name},
   595  		Sequence:    sequence,
   596  		Revision:    asserts.RevisionNotKnown,
   597  		Pinned:      sequence > 0,
   598  	}
   599  
   600  	vs, err = getSpecificSequenceOrLatest(db, headers)
   601  
   602  	checkForConflicts := func() error {
   603  		if err := valsets.Add(vs); err != nil {
   604  			return fmt.Errorf("internal error: cannot check validation sets conflicts: %v", err)
   605  		}
   606  		if err := valsets.Conflict(); err != nil {
   607  			return err
   608  		}
   609  		if err := valsets.CheckInstalledSnaps(snaps); err != nil {
   610  			return err
   611  		}
   612  		return nil
   613  	}
   614  
   615  	// found locally
   616  	if err == nil {
   617  		// check if we were tracking it already; if not, that
   618  		// means we found an old assertion (it was very likely tracked in the
   619  		// past) and we need to update it as it wasn't covered
   620  		// by RefreshValidationSetAssertions.
   621  		var tr ValidationSetTracking
   622  		trerr := GetValidationSet(st, accountID, name, &tr)
   623  		if trerr != nil && trerr != state.ErrNoState {
   624  			return nil, trerr
   625  		}
   626  		// not tracked, update the assertion
   627  		if trerr == state.ErrNoState {
   628  			// update with pool
   629  			atSeq.Sequence = vs.Sequence()
   630  			atSeq.Revision = vs.Revision()
   631  			if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil {
   632  				return nil, err
   633  			}
   634  		} else {
   635  			// was already tracked, add to validation sets and check
   636  			if err := checkForConflicts(); err != nil {
   637  				return nil, err
   638  			}
   639  			return vs, nil
   640  		}
   641  	} else {
   642  		if !asserts.IsNotFound(err) {
   643  			return nil, err
   644  		}
   645  
   646  		// try to resolve with pool
   647  		if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil {
   648  			return nil, err
   649  		}
   650  	}
   651  
   652  	checkBeforeCommit := func(db *asserts.Database, bs asserts.Backstore) error {
   653  		tmpDb := db.WithStackedBackstore(bs)
   654  		// get the resolved validation set assert, add to validation sets and check
   655  		vs, err = getSpecificSequenceOrLatest(tmpDb, headers)
   656  		if err != nil {
   657  			return fmt.Errorf("internal error: cannot find validation set assertion: %v", err)
   658  		}
   659  		if err := checkForConflicts(); err != nil {
   660  			return err
   661  		}
   662  		// all fine, will be committed (along with its prerequisites if any) on
   663  		// return by resolvePoolNoFallback
   664  		return nil
   665  	}
   666  
   667  	if err := resolvePoolNoFallback(st, pool, checkBeforeCommit, userID, deviceCtx); err != nil {
   668  		return nil, err
   669  	}
   670  
   671  	return vs, err
   672  }
   673  
   674  // TemporaryDB returns a temporary database stacked on top of the assertions
   675  // database. Writing to it will not affect the assertions database.
   676  func TemporaryDB(st *state.State) *asserts.Database {
   677  	db := cachedDB(st)
   678  	return db.WithStackedBackstore(asserts.NewMemoryBackstore())
   679  }