github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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/overlord/snapstate"
    33  	"github.com/snapcore/snapd/overlord/state"
    34  	"github.com/snapcore/snapd/release"
    35  	"github.com/snapcore/snapd/snap"
    36  )
    37  
    38  // Add the given assertion to the system assertion database.
    39  func Add(s *state.State, a asserts.Assertion) error {
    40  	// TODO: deal together with asserts itself with (cascading) side effects of possible assertion updates
    41  	return cachedDB(s).Add(a)
    42  }
    43  
    44  // AddBatch adds the given assertion batch to the system assertion database.
    45  func AddBatch(s *state.State, batch *asserts.Batch, opts *asserts.CommitOptions) error {
    46  	return batch.CommitTo(cachedDB(s), opts)
    47  }
    48  
    49  func findError(format string, ref *asserts.Ref, err error) error {
    50  	if asserts.IsNotFound(err) {
    51  		return fmt.Errorf(format, ref)
    52  	} else {
    53  		return fmt.Errorf(format+": %v", ref, err)
    54  	}
    55  }
    56  
    57  // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites.
    58  func RefreshSnapDeclarations(s *state.State, userID int) error {
    59  	deviceCtx, err := snapstate.DevicePastSeeding(s, nil)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	modelAs := deviceCtx.Model()
    64  
    65  	snapStates, err := snapstate.All(s)
    66  	if err != nil {
    67  		return nil
    68  	}
    69  	fetching := func(f asserts.Fetcher) error {
    70  		for _, snapst := range snapStates {
    71  			info, err := snapst.CurrentInfo()
    72  			if err != nil {
    73  				return err
    74  			}
    75  			if info.SnapID == "" {
    76  				continue
    77  			}
    78  			if err := snapasserts.FetchSnapDeclaration(f, info.SnapID); err != nil {
    79  				if notRetried, ok := err.(*httputil.PerstistentNetworkError); ok {
    80  					return notRetried
    81  				}
    82  				return fmt.Errorf("cannot refresh snap-declaration for %q: %v", info.InstanceName(), err)
    83  			}
    84  		}
    85  
    86  		// fetch store assertion if available
    87  		if modelAs.Store() != "" {
    88  			err := snapasserts.FetchStore(f, modelAs.Store())
    89  			if err != nil && !asserts.IsNotFound(err) {
    90  				return err
    91  			}
    92  		}
    93  
    94  		return nil
    95  	}
    96  	return doFetch(s, userID, deviceCtx, fetching)
    97  }
    98  
    99  type refreshControlError struct {
   100  	errs []error
   101  }
   102  
   103  func (e *refreshControlError) Error() string {
   104  	if len(e.errs) == 1 {
   105  		return e.errs[0].Error()
   106  	}
   107  	l := []string{""}
   108  	for _, e := range e.errs {
   109  		l = append(l, e.Error())
   110  	}
   111  	return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - "))
   112  }
   113  
   114  // ValidateRefreshes validates the refresh candidate revisions represented by
   115  // the snapInfos, looking for the needed refresh control validation assertions,
   116  // it returns a validated subset in validated and a summary error if not all
   117  // candidates validated. ignoreValidation is a set of snap-instance-names that
   118  // should not be gated.
   119  func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) (validated []*snap.Info, err error) {
   120  	// maps gated snap-ids to gating snap-ids
   121  	controlled := make(map[string][]string)
   122  	// maps gating snap-ids to their snap names
   123  	gatingNames := make(map[string]string)
   124  
   125  	db := DB(s)
   126  	snapStates, err := snapstate.All(s)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	for instanceName, snapst := range snapStates {
   131  		info, err := snapst.CurrentInfo()
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  		if info.SnapID == "" {
   136  			continue
   137  		}
   138  		gatingID := info.SnapID
   139  		if gatingNames[gatingID] != "" {
   140  			continue
   141  		}
   142  		a, err := db.Find(asserts.SnapDeclarationType, map[string]string{
   143  			"series":  release.Series,
   144  			"snap-id": gatingID,
   145  		})
   146  		if err != nil {
   147  			return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", instanceName, err)
   148  		}
   149  		decl := a.(*asserts.SnapDeclaration)
   150  		control := decl.RefreshControl()
   151  		if len(control) == 0 {
   152  			continue
   153  		}
   154  		gatingNames[gatingID] = decl.SnapName()
   155  		for _, gatedID := range control {
   156  			controlled[gatedID] = append(controlled[gatedID], gatingID)
   157  		}
   158  	}
   159  
   160  	var errs []error
   161  	for _, candInfo := range snapInfos {
   162  		if ignoreValidation[candInfo.InstanceName()] {
   163  			validated = append(validated, candInfo)
   164  			continue
   165  		}
   166  		gatedID := candInfo.SnapID
   167  		gating := controlled[gatedID]
   168  		if len(gating) == 0 { // easy case, no refresh control
   169  			validated = append(validated, candInfo)
   170  			continue
   171  		}
   172  
   173  		var validationRefs []*asserts.Ref
   174  
   175  		fetching := func(f asserts.Fetcher) error {
   176  			for _, gatingID := range gating {
   177  				valref := &asserts.Ref{
   178  					Type:       asserts.ValidationType,
   179  					PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()},
   180  				}
   181  				err := f.Fetch(valref)
   182  				if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType {
   183  					return fmt.Errorf("no validation by %q", gatingNames[gatingID])
   184  				}
   185  				if err != nil {
   186  					return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err)
   187  				}
   188  				validationRefs = append(validationRefs, valref)
   189  			}
   190  			return nil
   191  		}
   192  		err := doFetch(s, userID, deviceCtx, fetching)
   193  		if err != nil {
   194  			errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.InstanceName(), candInfo.Revision, err))
   195  			continue
   196  		}
   197  
   198  		var revoked *asserts.Validation
   199  		for _, valref := range validationRefs {
   200  			a, err := valref.Resolve(db.Find)
   201  			if err != nil {
   202  				return nil, findError("internal error: cannot find just fetched %v", valref, err)
   203  			}
   204  			if val := a.(*asserts.Validation); val.Revoked() {
   205  				revoked = val
   206  				break
   207  			}
   208  		}
   209  		if revoked != nil {
   210  			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()))
   211  			continue
   212  		}
   213  
   214  		validated = append(validated, candInfo)
   215  	}
   216  
   217  	if errs != nil {
   218  		return validated, &refreshControlError{errs}
   219  	}
   220  
   221  	return validated, nil
   222  }
   223  
   224  // BaseDeclaration returns the base-declaration assertion with policies governing all snaps.
   225  func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) {
   226  	// TODO: switch keeping this in the DB and have it revisioned/updated
   227  	// via the store
   228  	baseDecl := asserts.BuiltinBaseDeclaration()
   229  	if baseDecl == nil {
   230  		return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType}
   231  	}
   232  	return baseDecl, nil
   233  }
   234  
   235  // SnapDeclaration returns the snap-declaration for the given snap-id if it is present in the system assertion database.
   236  func SnapDeclaration(s *state.State, snapID string) (*asserts.SnapDeclaration, error) {
   237  	db := DB(s)
   238  	a, err := db.Find(asserts.SnapDeclarationType, map[string]string{
   239  		"series":  release.Series,
   240  		"snap-id": snapID,
   241  	})
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	return a.(*asserts.SnapDeclaration), nil
   246  }
   247  
   248  // Publisher returns the account assertion for publisher of the given snap-id if it is present in the system assertion database.
   249  func Publisher(s *state.State, snapID string) (*asserts.Account, error) {
   250  	db := DB(s)
   251  	a, err := db.Find(asserts.SnapDeclarationType, map[string]string{
   252  		"series":  release.Series,
   253  		"snap-id": snapID,
   254  	})
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  	snapDecl := a.(*asserts.SnapDeclaration)
   259  	a, err = db.Find(asserts.AccountType, map[string]string{
   260  		"account-id": snapDecl.PublisherID(),
   261  	})
   262  	if err != nil {
   263  		return nil, fmt.Errorf("internal error: cannot find account assertion for the publisher of snap %q: %v", snapDecl.SnapName(), err)
   264  	}
   265  	return a.(*asserts.Account), nil
   266  }
   267  
   268  // Store returns the store assertion with the given name/id if it is
   269  // present in the system assertion database.
   270  func Store(s *state.State, store string) (*asserts.Store, error) {
   271  	db := DB(s)
   272  	a, err := db.Find(asserts.StoreType, map[string]string{
   273  		"store": store,
   274  	})
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  	return a.(*asserts.Store), nil
   279  }
   280  
   281  // AutoAliases returns the explicit automatic aliases alias=>app mapping for the given installed snap.
   282  func AutoAliases(s *state.State, info *snap.Info) (map[string]string, error) {
   283  	if info.SnapID == "" {
   284  		// without declaration
   285  		return nil, nil
   286  	}
   287  	decl, err := SnapDeclaration(s, info.SnapID)
   288  	if err != nil {
   289  		return nil, fmt.Errorf("internal error: cannot find snap-declaration for installed snap %q: %v", info.InstanceName(), err)
   290  	}
   291  	explicitAliases := decl.Aliases()
   292  	if len(explicitAliases) != 0 {
   293  		return explicitAliases, nil
   294  	}
   295  	// XXX: old header fallback, just to keep edge working while we fix the
   296  	// store, to remove before next release!
   297  	oldAutoAliases := decl.AutoAliases()
   298  	if len(oldAutoAliases) == 0 {
   299  		return nil, nil
   300  	}
   301  	res := make(map[string]string, len(oldAutoAliases))
   302  	for _, alias := range oldAutoAliases {
   303  		app := info.LegacyAliases[alias]
   304  		if app == nil {
   305  			// not a known alias anymore or yet, skip
   306  			continue
   307  
   308  		}
   309  		res[alias] = app.Name
   310  	}
   311  	return res, nil
   312  }
   313  
   314  func delayedCrossMgrInit() {
   315  	// hook validation of refreshes into snapstate logic
   316  	snapstate.ValidateRefreshes = ValidateRefreshes
   317  	// hook auto refresh of assertions into snapstate
   318  	snapstate.AutoRefreshAssertions = AutoRefreshAssertions
   319  	// hook retrieving auto-aliases into snapstate logic
   320  	snapstate.AutoAliases = AutoAliases
   321  }
   322  
   323  // AutoRefreshAssertions tries to refresh all assertions
   324  func AutoRefreshAssertions(s *state.State, userID int) error {
   325  	return RefreshSnapDeclarations(s, userID)
   326  }