gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/snap_asserts.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2022 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 asserts
    21  
    22  import (
    23  	"bytes"
    24  	"crypto"
    25  	"fmt"
    26  	"time"
    27  
    28  	// expected for digests
    29  	_ "golang.org/x/crypto/sha3"
    30  
    31  	"gitee.com/mysnapcore/mysnapd/osutil"
    32  	"gitee.com/mysnapcore/mysnapd/release"
    33  	"gitee.com/mysnapcore/mysnapd/snap/naming"
    34  	"gitee.com/mysnapcore/mysnapd/strutil"
    35  )
    36  
    37  // SnapDeclaration holds a snap-declaration assertion, declaring a
    38  // snap binding its identifying snap-id to a name, asserting its
    39  // publisher and its other properties.
    40  type SnapDeclaration struct {
    41  	assertionBase
    42  	refreshControl      []string
    43  	plugRules           map[string]*PlugRule
    44  	slotRules           map[string]*SlotRule
    45  	autoAliases         []string
    46  	aliases             map[string]string
    47  	revisionAuthorities []*RevisionAuthority
    48  	timestamp           time.Time
    49  }
    50  
    51  // Series returns the series for which the snap is being declared.
    52  func (snapdcl *SnapDeclaration) Series() string {
    53  	return snapdcl.HeaderString("series")
    54  }
    55  
    56  // SnapID returns the snap id of the declared snap.
    57  func (snapdcl *SnapDeclaration) SnapID() string {
    58  	return snapdcl.HeaderString("snap-id")
    59  }
    60  
    61  // SnapName returns the declared snap name.
    62  func (snapdcl *SnapDeclaration) SnapName() string {
    63  	return snapdcl.HeaderString("snap-name")
    64  }
    65  
    66  // PublisherID returns the identifier of the publisher of the declared snap.
    67  func (snapdcl *SnapDeclaration) PublisherID() string {
    68  	return snapdcl.HeaderString("publisher-id")
    69  }
    70  
    71  // Timestamp returns the time when the snap-declaration was issued.
    72  func (snapdcl *SnapDeclaration) Timestamp() time.Time {
    73  	return snapdcl.timestamp
    74  }
    75  
    76  // RefreshControl returns the ids of snaps whose updates are controlled by this declaration.
    77  func (snapdcl *SnapDeclaration) RefreshControl() []string {
    78  	return snapdcl.refreshControl
    79  }
    80  
    81  // PlugRule returns the plug-side rule about the given interface if one was included in the plugs stanza of the declaration, otherwise it returns nil.
    82  func (snapdcl *SnapDeclaration) PlugRule(interfaceName string) *PlugRule {
    83  	return snapdcl.plugRules[interfaceName]
    84  }
    85  
    86  // SlotRule returns the slot-side rule about the given interface if one was included in the slots stanza of the declaration, otherwise it returns nil.
    87  func (snapdcl *SnapDeclaration) SlotRule(interfaceName string) *SlotRule {
    88  	return snapdcl.slotRules[interfaceName]
    89  }
    90  
    91  // AutoAliases returns the optional auto-aliases granted to this snap.
    92  // XXX: deprecated, will go away
    93  func (snapdcl *SnapDeclaration) AutoAliases() []string {
    94  	return snapdcl.autoAliases
    95  }
    96  
    97  // Aliases returns the optional explicit aliases granted to this snap.
    98  func (snapdcl *SnapDeclaration) Aliases() map[string]string {
    99  	return snapdcl.aliases
   100  }
   101  
   102  // RevisionAuthority return any revision authority entries matching the given
   103  // provenance.
   104  func (snapdcl *SnapDeclaration) RevisionAuthority(provenance string) []*RevisionAuthority {
   105  	res := make([]*RevisionAuthority, 0, 1)
   106  	for _, ra := range snapdcl.revisionAuthorities {
   107  		if strutil.ListContains(ra.Provenance, provenance) {
   108  			res = append(res, ra)
   109  		}
   110  	}
   111  	if len(res) == 0 {
   112  		return nil
   113  	}
   114  	return res
   115  }
   116  
   117  // Implement further consistency checks.
   118  func (snapdcl *SnapDeclaration) checkConsistency(db RODatabase, acck *AccountKey) error {
   119  	if !db.IsTrustedAccount(snapdcl.AuthorityID()) {
   120  		return fmt.Errorf("snap-declaration assertion for %q (id %q) is not signed by a directly trusted authority: %s", snapdcl.SnapName(), snapdcl.SnapID(), snapdcl.AuthorityID())
   121  	}
   122  	_, err := db.Find(AccountType, map[string]string{
   123  		"account-id": snapdcl.PublisherID(),
   124  	})
   125  	if IsNotFound(err) {
   126  		return fmt.Errorf("snap-declaration assertion for %q (id %q) does not have a matching account assertion for the publisher %q", snapdcl.SnapName(), snapdcl.SnapID(), snapdcl.PublisherID())
   127  	}
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  // expected interface is implemented
   136  var _ consistencyChecker = (*SnapDeclaration)(nil)
   137  
   138  // Prerequisites returns references to this snap-declaration's prerequisite assertions.
   139  func (snapdcl *SnapDeclaration) Prerequisites() []*Ref {
   140  	return []*Ref{
   141  		{Type: AccountType, PrimaryKey: []string{snapdcl.PublisherID()}},
   142  	}
   143  }
   144  
   145  func compilePlugRules(plugs map[string]interface{}, compiled func(iface string, plugRule *PlugRule)) error {
   146  	for iface, rule := range plugs {
   147  		plugRule, err := compilePlugRule(iface, rule)
   148  		if err != nil {
   149  			return err
   150  		}
   151  		compiled(iface, plugRule)
   152  	}
   153  	return nil
   154  }
   155  
   156  func compileSlotRules(slots map[string]interface{}, compiled func(iface string, slotRule *SlotRule)) error {
   157  	for iface, rule := range slots {
   158  		slotRule, err := compileSlotRule(iface, rule)
   159  		if err != nil {
   160  			return err
   161  		}
   162  		compiled(iface, slotRule)
   163  	}
   164  	return nil
   165  }
   166  
   167  func snapDeclarationFormatAnalyze(headers map[string]interface{}, body []byte) (formatnum int, err error) {
   168  	_, plugsOk := headers["plugs"]
   169  	_, slotsOk := headers["slots"]
   170  	if !(plugsOk || slotsOk) {
   171  		return 0, nil
   172  	}
   173  
   174  	formatnum = 1
   175  	setFormatNum := func(num int) {
   176  		if num > formatnum {
   177  			formatnum = num
   178  		}
   179  	}
   180  
   181  	plugs, err := checkMap(headers, "plugs")
   182  	if err != nil {
   183  		return 0, err
   184  	}
   185  	err = compilePlugRules(plugs, func(_ string, rule *PlugRule) {
   186  		if rule.feature(dollarAttrConstraintsFeature) {
   187  			setFormatNum(2)
   188  		}
   189  		if rule.feature(deviceScopeConstraintsFeature) {
   190  			setFormatNum(3)
   191  		}
   192  		if rule.feature(nameConstraintsFeature) {
   193  			setFormatNum(4)
   194  		}
   195  		if rule.feature(altAttrMatcherFeature) {
   196  			setFormatNum(5)
   197  		}
   198  	})
   199  	if err != nil {
   200  		return 0, err
   201  	}
   202  
   203  	slots, err := checkMap(headers, "slots")
   204  	if err != nil {
   205  		return 0, err
   206  	}
   207  	err = compileSlotRules(slots, func(_ string, rule *SlotRule) {
   208  		if rule.feature(dollarAttrConstraintsFeature) {
   209  			setFormatNum(2)
   210  		}
   211  		if rule.feature(deviceScopeConstraintsFeature) {
   212  			setFormatNum(3)
   213  		}
   214  		if rule.feature(nameConstraintsFeature) {
   215  			setFormatNum(4)
   216  		}
   217  		if rule.feature(altAttrMatcherFeature) {
   218  			setFormatNum(5)
   219  		}
   220  	})
   221  	if err != nil {
   222  		return 0, err
   223  	}
   224  
   225  	return formatnum, nil
   226  }
   227  
   228  func checkAliases(headers map[string]interface{}) (map[string]string, error) {
   229  	value, ok := headers["aliases"]
   230  	if !ok {
   231  		return nil, nil
   232  	}
   233  	aliasList, ok := value.([]interface{})
   234  	if !ok {
   235  		return nil, fmt.Errorf(`"aliases" header must be a list of alias maps`)
   236  	}
   237  	if len(aliasList) == 0 {
   238  		return nil, nil
   239  	}
   240  
   241  	aliasMap := make(map[string]string, len(aliasList))
   242  	for i, item := range aliasList {
   243  		aliasItem, ok := item.(map[string]interface{})
   244  		if !ok {
   245  			return nil, fmt.Errorf(`"aliases" header must be a list of alias maps`)
   246  		}
   247  
   248  		what := fmt.Sprintf(`in "aliases" item %d`, i+1)
   249  		name, err := checkStringMatchesWhat(aliasItem, "name", what, naming.ValidAlias)
   250  		if err != nil {
   251  			return nil, err
   252  		}
   253  
   254  		what = fmt.Sprintf(`for alias %q`, name)
   255  		target, err := checkStringMatchesWhat(aliasItem, "target", what, naming.ValidApp)
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  
   260  		if _, ok := aliasMap[name]; ok {
   261  			return nil, fmt.Errorf(`duplicated definition in "aliases" for alias %q`, name)
   262  		}
   263  
   264  		aliasMap[name] = target
   265  	}
   266  
   267  	return aliasMap, nil
   268  }
   269  
   270  func assembleSnapDeclaration(assert assertionBase) (Assertion, error) {
   271  	_, err := checkExistsString(assert.headers, "snap-name")
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  
   276  	_, err = checkNotEmptyString(assert.headers, "publisher-id")
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  
   286  	var refControl []string
   287  	var plugRules map[string]*PlugRule
   288  	var slotRules map[string]*SlotRule
   289  
   290  	refControl, err = checkStringList(assert.headers, "refresh-control")
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  
   295  	plugs, err := checkMap(assert.headers, "plugs")
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	if plugs != nil {
   300  		plugRules = make(map[string]*PlugRule, len(plugs))
   301  		err := compilePlugRules(plugs, func(iface string, rule *PlugRule) {
   302  			plugRules[iface] = rule
   303  		})
   304  		if err != nil {
   305  			return nil, err
   306  		}
   307  	}
   308  
   309  	slots, err := checkMap(assert.headers, "slots")
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  	if slots != nil {
   314  		slotRules = make(map[string]*SlotRule, len(slots))
   315  		err := compileSlotRules(slots, func(iface string, rule *SlotRule) {
   316  			slotRules[iface] = rule
   317  		})
   318  		if err != nil {
   319  			return nil, err
   320  		}
   321  	}
   322  
   323  	// XXX: depracated, will go away later
   324  	autoAliases, err := checkStringListMatches(assert.headers, "auto-aliases", naming.ValidAlias)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	aliases, err := checkAliases(assert.headers)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	var ras []*RevisionAuthority
   335  
   336  	ra, ok := assert.headers["revision-authority"]
   337  	if ok {
   338  		ramaps, ok := ra.([]interface{})
   339  		if !ok {
   340  			return nil, fmt.Errorf("revision-authority stanza must be a list of maps")
   341  		}
   342  		if len(ramaps) == 0 {
   343  			// there is no syntax producing this scenario but be robust
   344  			return nil, fmt.Errorf("revision-authority stanza cannot be empty")
   345  		}
   346  		ras = make([]*RevisionAuthority, 0, len(ramaps))
   347  		for _, ramap := range ramaps {
   348  			m, ok := ramap.(map[string]interface{})
   349  			if !ok {
   350  				return nil, fmt.Errorf("revision-authority stanza must be a list of maps")
   351  			}
   352  			accountID, err := checkStringMatchesWhat(m, "account-id", "in revision authority", validAccountID)
   353  			if err != nil {
   354  				return nil, err
   355  			}
   356  			prov, err := checkStringListInMap(m, "provenance", "provenance in revision authority", naming.ValidProvenance)
   357  			if err != nil {
   358  				return nil, err
   359  			}
   360  			if len(prov) == 0 {
   361  				return nil, fmt.Errorf("provenance in revision authority cannot be empty")
   362  			}
   363  			minRevision := 1
   364  			maxRevision := 0
   365  			if _, ok := m["min-revision"]; ok {
   366  				var err error
   367  				minRevision, err = checkSnapRevisionWhat(m, "min-revision", "in revision authority")
   368  				if err != nil {
   369  					return nil, err
   370  				}
   371  			}
   372  			if _, ok := m["max-revision"]; ok {
   373  				var err error
   374  				maxRevision, err = checkSnapRevisionWhat(m, "max-revision", "in revision authority")
   375  				if err != nil {
   376  					return nil, err
   377  				}
   378  			}
   379  			if maxRevision != 0 && maxRevision < minRevision {
   380  				return nil, fmt.Errorf("optional max-revision cannot be less than min-revision in revision-authority")
   381  			}
   382  			devscope, err := compileDeviceScopeConstraint(m, "revision-authority")
   383  			if err != nil {
   384  				return nil, err
   385  			}
   386  			ras = append(ras, &RevisionAuthority{
   387  				AccountID:   accountID,
   388  				Provenance:  prov,
   389  				MinRevision: minRevision,
   390  				MaxRevision: maxRevision,
   391  				DeviceScope: devscope,
   392  			})
   393  		}
   394  
   395  	}
   396  
   397  	return &SnapDeclaration{
   398  		assertionBase:       assert,
   399  		refreshControl:      refControl,
   400  		plugRules:           plugRules,
   401  		slotRules:           slotRules,
   402  		autoAliases:         autoAliases,
   403  		aliases:             aliases,
   404  		revisionAuthorities: ras,
   405  		timestamp:           timestamp,
   406  	}, nil
   407  }
   408  
   409  // RevisionAuthority holds information about an account that can sign revisions
   410  // for a given snap.
   411  type RevisionAuthority struct {
   412  	AccountID  string
   413  	Provenance []string
   414  
   415  	MinRevision int
   416  	MaxRevision int
   417  
   418  	DeviceScope *DeviceScopeConstraint
   419  }
   420  
   421  // Check tests whether rev matches the revision authority constraints.
   422  // Optional model and store must be provided to cross-check device-specific
   423  // constraints.
   424  func (ra *RevisionAuthority) Check(rev *SnapRevision, model *Model, store *Store) error {
   425  	if !strutil.ListContains(ra.Provenance, rev.Provenance()) {
   426  		return fmt.Errorf("provenance mismatch")
   427  	}
   428  	if rev.AuthorityID() != ra.AccountID {
   429  		return fmt.Errorf("authority-id mismatch")
   430  	}
   431  	revno := rev.SnapRevision()
   432  	if revno < ra.MinRevision {
   433  		return fmt.Errorf("snap revision %d is less than min-revision %d", revno, ra.MinRevision)
   434  	}
   435  	if ra.MaxRevision != 0 && revno > ra.MaxRevision {
   436  		return fmt.Errorf("snap revision %d is greater than max-revision %d", revno, ra.MaxRevision)
   437  	}
   438  	if ra.DeviceScope != nil && model != nil {
   439  		opts := DeviceScopeConstraintCheckOptions{UseFriendlyStores: true}
   440  		if err := ra.DeviceScope.Check(model, store, &opts); err != nil {
   441  			return err
   442  		}
   443  	}
   444  	return nil
   445  }
   446  
   447  // SnapFileSHA3_384 computes the SHA3-384 digest of the given snap file.
   448  // It also returns its size.
   449  func SnapFileSHA3_384(snapPath string) (digest string, size uint64, err error) {
   450  	sha3_384Dgst, size, err := osutil.FileDigest(snapPath, crypto.SHA3_384)
   451  	if err != nil {
   452  		return "", 0, fmt.Errorf("cannot compute snap %q digest: %v", snapPath, err)
   453  	}
   454  
   455  	sha3_384, err := EncodeDigest(crypto.SHA3_384, sha3_384Dgst)
   456  	if err != nil {
   457  		return "", 0, fmt.Errorf("cannot encode snap %q digest: %v", snapPath, err)
   458  	}
   459  	return sha3_384, size, nil
   460  }
   461  
   462  // SnapBuild holds a snap-build assertion, asserting the properties of a snap
   463  // at the time it was built by the developer.
   464  type SnapBuild struct {
   465  	assertionBase
   466  	size      uint64
   467  	timestamp time.Time
   468  }
   469  
   470  // SnapSHA3_384 returns the SHA3-384 digest of the snap.
   471  func (snapbld *SnapBuild) SnapSHA3_384() string {
   472  	return snapbld.HeaderString("snap-sha3-384")
   473  }
   474  
   475  // SnapID returns the snap id of the snap.
   476  func (snapbld *SnapBuild) SnapID() string {
   477  	return snapbld.HeaderString("snap-id")
   478  }
   479  
   480  // SnapSize returns the size of the snap.
   481  func (snapbld *SnapBuild) SnapSize() uint64 {
   482  	return snapbld.size
   483  }
   484  
   485  // Grade returns the grade of the snap: devel|stable
   486  func (snapbld *SnapBuild) Grade() string {
   487  	return snapbld.HeaderString("grade")
   488  }
   489  
   490  // Timestamp returns the time when the snap-build assertion was created.
   491  func (snapbld *SnapBuild) Timestamp() time.Time {
   492  	return snapbld.timestamp
   493  }
   494  
   495  func assembleSnapBuild(assert assertionBase) (Assertion, error) {
   496  	_, err := checkDigest(assert.headers, "snap-sha3-384", crypto.SHA3_384)
   497  	if err != nil {
   498  		return nil, err
   499  	}
   500  
   501  	_, err = checkNotEmptyString(assert.headers, "snap-id")
   502  	if err != nil {
   503  		return nil, err
   504  	}
   505  
   506  	_, err = checkNotEmptyString(assert.headers, "grade")
   507  	if err != nil {
   508  		return nil, err
   509  	}
   510  
   511  	size, err := checkUint(assert.headers, "snap-size", 64)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  
   516  	timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
   517  	if err != nil {
   518  		return nil, err
   519  	}
   520  	// ignore extra headers and non-empty body for future compatibility
   521  	return &SnapBuild{
   522  		assertionBase: assert,
   523  		size:          size,
   524  		timestamp:     timestamp,
   525  	}, nil
   526  }
   527  
   528  // SnapRevision holds a snap-revision assertion, which is a statement by the
   529  // store acknowledging the receipt of a build of a snap and labeling it with a
   530  // snap revision.
   531  type SnapRevision struct {
   532  	assertionBase
   533  	snapSize     uint64
   534  	snapRevision int
   535  	timestamp    time.Time
   536  }
   537  
   538  // SnapSHA3_384 returns the SHA3-384 digest of the snap.
   539  func (snaprev *SnapRevision) SnapSHA3_384() string {
   540  	return snaprev.HeaderString("snap-sha3-384")
   541  }
   542  
   543  // Provenance returns the optional provenance of the snap (defaults to
   544  // global-upload (naming.DefaultProvenance)).
   545  func (snaprev *SnapRevision) Provenance() string {
   546  	return snaprev.HeaderString("provenance")
   547  }
   548  
   549  // SnapID returns the snap id of the snap.
   550  func (snaprev *SnapRevision) SnapID() string {
   551  	return snaprev.HeaderString("snap-id")
   552  }
   553  
   554  // SnapSize returns the size in bytes of the snap submitted to the store.
   555  func (snaprev *SnapRevision) SnapSize() uint64 {
   556  	return snaprev.snapSize
   557  }
   558  
   559  // SnapRevision returns the revision assigned to this build of the snap.
   560  func (snaprev *SnapRevision) SnapRevision() int {
   561  	return snaprev.snapRevision
   562  }
   563  
   564  // DeveloperID returns the id of the developer that submitted this build of the
   565  // snap.
   566  func (snaprev *SnapRevision) DeveloperID() string {
   567  	return snaprev.HeaderString("developer-id")
   568  }
   569  
   570  // Timestamp returns the time when the snap-revision was issued.
   571  func (snaprev *SnapRevision) Timestamp() time.Time {
   572  	return snaprev.timestamp
   573  }
   574  
   575  // Implement further consistency checks.
   576  func (snaprev *SnapRevision) checkConsistency(db RODatabase, acck *AccountKey) error {
   577  	otherProvenance := snaprev.Provenance() != naming.DefaultProvenance
   578  	if !otherProvenance && !db.IsTrustedAccount(snaprev.AuthorityID()) {
   579  		// delegating global-upload revisions is not allowed
   580  		return fmt.Errorf("snap-revision assertion for snap id %q is not signed by a store: %s", snaprev.SnapID(), snaprev.AuthorityID())
   581  	}
   582  	_, err := db.Find(AccountType, map[string]string{
   583  		"account-id": snaprev.DeveloperID(),
   584  	})
   585  	if IsNotFound(err) {
   586  		return fmt.Errorf("snap-revision assertion for snap id %q does not have a matching account assertion for the developer %q", snaprev.SnapID(), snaprev.DeveloperID())
   587  	}
   588  	if err != nil {
   589  		return err
   590  	}
   591  	a, err := db.Find(SnapDeclarationType, map[string]string{
   592  		// XXX: mediate getting current series through some context object? this gets the job done for now
   593  		"series":  release.Series,
   594  		"snap-id": snaprev.SnapID(),
   595  	})
   596  	if IsNotFound(err) {
   597  		return fmt.Errorf("snap-revision assertion for snap id %q does not have a matching snap-declaration assertion", snaprev.SnapID())
   598  	}
   599  	if err != nil {
   600  		return err
   601  	}
   602  	if otherProvenance {
   603  		decl := a.(*SnapDeclaration)
   604  		ras := decl.RevisionAuthority(snaprev.Provenance())
   605  		matchingRevAuthority := false
   606  		for _, ra := range ras {
   607  			// model==store==nil, we do not perform device-specific
   608  			// checks at this level, those are performed at
   609  			// higher-level guarding installing actual snaps
   610  			if err := ra.Check(snaprev, nil, nil); err == nil {
   611  				matchingRevAuthority = true
   612  				break
   613  			}
   614  		}
   615  		if !matchingRevAuthority {
   616  			return fmt.Errorf("snap-revision assertion with provenance %q for snap id %q is not signed by an authorized authority: %s", snaprev.Provenance(), snaprev.SnapID(), snaprev.AuthorityID())
   617  		}
   618  	}
   619  	return nil
   620  }
   621  
   622  // expected interface is implemented
   623  var _ consistencyChecker = (*SnapRevision)(nil)
   624  
   625  // Prerequisites returns references to this snap-revision's prerequisite assertions.
   626  func (snaprev *SnapRevision) Prerequisites() []*Ref {
   627  	return []*Ref{
   628  		// XXX: mediate getting current series through some context object? this gets the job done for now
   629  		{Type: SnapDeclarationType, PrimaryKey: []string{release.Series, snaprev.SnapID()}},
   630  		{Type: AccountType, PrimaryKey: []string{snaprev.DeveloperID()}},
   631  	}
   632  }
   633  
   634  func checkSnapRevisionWhat(headers map[string]interface{}, name, what string) (snapRevision int, err error) {
   635  	snapRevision, err = checkIntWhat(headers, name, what)
   636  	if err != nil {
   637  		return 0, err
   638  	}
   639  	if snapRevision < 1 {
   640  		return 0, fmt.Errorf(`%q %s must be >=1: %d`, name, what, snapRevision)
   641  	}
   642  	return snapRevision, nil
   643  }
   644  
   645  func assembleSnapRevision(assert assertionBase) (Assertion, error) {
   646  	_, err := checkDigest(assert.headers, "snap-sha3-384", crypto.SHA3_384)
   647  	if err != nil {
   648  		return nil, err
   649  	}
   650  
   651  	_, err = checkStringMatches(assert.headers, "provenance", naming.ValidProvenance)
   652  	if err != nil {
   653  		return nil, err
   654  	}
   655  
   656  	_, err = checkNotEmptyString(assert.headers, "snap-id")
   657  	if err != nil {
   658  		return nil, err
   659  	}
   660  
   661  	snapSize, err := checkUint(assert.headers, "snap-size", 64)
   662  	if err != nil {
   663  		return nil, err
   664  	}
   665  
   666  	snapRevision, err := checkSnapRevisionWhat(assert.headers, "snap-revision", "header")
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  
   671  	_, err = checkNotEmptyString(assert.headers, "developer-id")
   672  	if err != nil {
   673  		return nil, err
   674  	}
   675  
   676  	timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
   677  	if err != nil {
   678  		return nil, err
   679  	}
   680  
   681  	return &SnapRevision{
   682  		assertionBase: assert,
   683  		snapSize:      snapSize,
   684  		snapRevision:  snapRevision,
   685  		timestamp:     timestamp,
   686  	}, nil
   687  }
   688  
   689  // Validation holds a validation assertion, describing that a combination of
   690  // (snap-id, approved-snap-id, approved-revision) has been validated for
   691  // the series, meaning updating to that revision of approved-snap-id
   692  // has been approved by the owner of the gating snap with snap-id.
   693  type Validation struct {
   694  	assertionBase
   695  	revoked              bool
   696  	timestamp            time.Time
   697  	approvedSnapRevision int
   698  }
   699  
   700  // Series returns the series for which the validation holds.
   701  func (validation *Validation) Series() string {
   702  	return validation.HeaderString("series")
   703  }
   704  
   705  // SnapID returns the ID of the gating snap.
   706  func (validation *Validation) SnapID() string {
   707  	return validation.HeaderString("snap-id")
   708  }
   709  
   710  // ApprovedSnapID returns the ID of the gated snap.
   711  func (validation *Validation) ApprovedSnapID() string {
   712  	return validation.HeaderString("approved-snap-id")
   713  }
   714  
   715  // ApprovedSnapRevision returns the approved revision of the gated snap.
   716  func (validation *Validation) ApprovedSnapRevision() int {
   717  	return validation.approvedSnapRevision
   718  }
   719  
   720  // Revoked returns true if the validation has been revoked.
   721  func (validation *Validation) Revoked() bool {
   722  	return validation.revoked
   723  }
   724  
   725  // Timestamp returns the time when the validation was issued.
   726  func (validation *Validation) Timestamp() time.Time {
   727  	return validation.timestamp
   728  }
   729  
   730  // Implement further consistency checks.
   731  func (validation *Validation) checkConsistency(db RODatabase, acck *AccountKey) error {
   732  	_, err := db.Find(SnapDeclarationType, map[string]string{
   733  		"series":  validation.Series(),
   734  		"snap-id": validation.ApprovedSnapID(),
   735  	})
   736  	if IsNotFound(err) {
   737  		return fmt.Errorf("validation assertion by snap-id %q does not have a matching snap-declaration assertion for approved-snap-id %q", validation.SnapID(), validation.ApprovedSnapID())
   738  	}
   739  	if err != nil {
   740  		return err
   741  	}
   742  	a, err := db.Find(SnapDeclarationType, map[string]string{
   743  		"series":  validation.Series(),
   744  		"snap-id": validation.SnapID(),
   745  	})
   746  	if IsNotFound(err) {
   747  		return fmt.Errorf("validation assertion by snap-id %q does not have a matching snap-declaration assertion", validation.SnapID())
   748  	}
   749  	if err != nil {
   750  		return err
   751  	}
   752  
   753  	gatingDecl := a.(*SnapDeclaration)
   754  	if gatingDecl.PublisherID() != validation.AuthorityID() {
   755  		return fmt.Errorf("validation assertion by snap %q (id %q) not signed by its publisher", gatingDecl.SnapName(), validation.SnapID())
   756  	}
   757  
   758  	return nil
   759  }
   760  
   761  // expected interface is implemented
   762  var _ consistencyChecker = (*Validation)(nil)
   763  
   764  // Prerequisites returns references to this validation's prerequisite assertions.
   765  func (validation *Validation) Prerequisites() []*Ref {
   766  	return []*Ref{
   767  		{Type: SnapDeclarationType, PrimaryKey: []string{validation.Series(), validation.SnapID()}},
   768  		{Type: SnapDeclarationType, PrimaryKey: []string{validation.Series(), validation.ApprovedSnapID()}},
   769  	}
   770  }
   771  
   772  func assembleValidation(assert assertionBase) (Assertion, error) {
   773  	approvedSnapRevision, err := checkSnapRevisionWhat(assert.headers, "approved-snap-revision", "header")
   774  	if err != nil {
   775  		return nil, err
   776  	}
   777  
   778  	revoked, err := checkOptionalBool(assert.headers, "revoked")
   779  	if err != nil {
   780  		return nil, err
   781  	}
   782  
   783  	timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
   784  	if err != nil {
   785  		return nil, err
   786  	}
   787  
   788  	return &Validation{
   789  		assertionBase:        assert,
   790  		revoked:              revoked,
   791  		timestamp:            timestamp,
   792  		approvedSnapRevision: approvedSnapRevision,
   793  	}, nil
   794  }
   795  
   796  // BaseDeclaration holds a base-declaration assertion, declaring the
   797  // policies (to start with interface ones) applying to all snaps of
   798  // a series.
   799  type BaseDeclaration struct {
   800  	assertionBase
   801  	plugRules map[string]*PlugRule
   802  	slotRules map[string]*SlotRule
   803  	timestamp time.Time
   804  }
   805  
   806  // Series returns the series whose snaps are governed by the declaration.
   807  func (basedcl *BaseDeclaration) Series() string {
   808  	return basedcl.HeaderString("series")
   809  }
   810  
   811  // Timestamp returns the time when the base-declaration was issued.
   812  func (basedcl *BaseDeclaration) Timestamp() time.Time {
   813  	return basedcl.timestamp
   814  }
   815  
   816  // PlugRule returns the plug-side rule about the given interface if one was included in the plugs stanza of the declaration, otherwise it returns nil.
   817  func (basedcl *BaseDeclaration) PlugRule(interfaceName string) *PlugRule {
   818  	return basedcl.plugRules[interfaceName]
   819  }
   820  
   821  // SlotRule returns the slot-side rule about the given interface if one was included in the slots stanza of the declaration, otherwise it returns nil.
   822  func (basedcl *BaseDeclaration) SlotRule(interfaceName string) *SlotRule {
   823  	return basedcl.slotRules[interfaceName]
   824  }
   825  
   826  // Implement further consistency checks.
   827  func (basedcl *BaseDeclaration) checkConsistency(db RODatabase, acck *AccountKey) error {
   828  	// XXX: not signed or stored yet in a db, but being ready for that
   829  	if !db.IsTrustedAccount(basedcl.AuthorityID()) {
   830  		return fmt.Errorf("base-declaration assertion for series %s is not signed by a directly trusted authority: %s", basedcl.Series(), basedcl.AuthorityID())
   831  	}
   832  	return nil
   833  }
   834  
   835  // expected interface is implemented
   836  var _ consistencyChecker = (*BaseDeclaration)(nil)
   837  
   838  func assembleBaseDeclaration(assert assertionBase) (Assertion, error) {
   839  	var plugRules map[string]*PlugRule
   840  	plugs, err := checkMap(assert.headers, "plugs")
   841  	if err != nil {
   842  		return nil, err
   843  	}
   844  	if plugs != nil {
   845  		plugRules = make(map[string]*PlugRule, len(plugs))
   846  		err := compilePlugRules(plugs, func(iface string, rule *PlugRule) {
   847  			plugRules[iface] = rule
   848  		})
   849  		if err != nil {
   850  			return nil, err
   851  		}
   852  	}
   853  
   854  	var slotRules map[string]*SlotRule
   855  	slots, err := checkMap(assert.headers, "slots")
   856  	if err != nil {
   857  		return nil, err
   858  	}
   859  	if slots != nil {
   860  		slotRules = make(map[string]*SlotRule, len(slots))
   861  		err := compileSlotRules(slots, func(iface string, rule *SlotRule) {
   862  			slotRules[iface] = rule
   863  		})
   864  		if err != nil {
   865  			return nil, err
   866  		}
   867  	}
   868  
   869  	timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
   870  	if err != nil {
   871  		return nil, err
   872  	}
   873  
   874  	return &BaseDeclaration{
   875  		assertionBase: assert,
   876  		plugRules:     plugRules,
   877  		slotRules:     slotRules,
   878  		timestamp:     timestamp,
   879  	}, nil
   880  }
   881  
   882  var builtinBaseDeclaration *BaseDeclaration
   883  
   884  // BuiltinBaseDeclaration exposes the initialized builtin base-declaration assertion. This is used by overlord/assertstate, other code should use assertstate.BaseDeclaration.
   885  func BuiltinBaseDeclaration() *BaseDeclaration {
   886  	return builtinBaseDeclaration
   887  }
   888  
   889  var (
   890  	builtinBaseDeclarationCheckOrder      = []string{"type", "authority-id", "series"}
   891  	builtinBaseDeclarationExpectedHeaders = map[string]interface{}{
   892  		"type":         "base-declaration",
   893  		"authority-id": "canonical",
   894  		"series":       release.Series,
   895  	}
   896  )
   897  
   898  // InitBuiltinBaseDeclaration initializes the builtin base-declaration based on headers (or resets it if headers is nil).
   899  func InitBuiltinBaseDeclaration(headers []byte) error {
   900  	if headers == nil {
   901  		builtinBaseDeclaration = nil
   902  		return nil
   903  	}
   904  	trimmed := bytes.TrimSpace(headers)
   905  	h, err := parseHeaders(trimmed)
   906  	if err != nil {
   907  		return err
   908  	}
   909  	for _, name := range builtinBaseDeclarationCheckOrder {
   910  		expected := builtinBaseDeclarationExpectedHeaders[name]
   911  		if h[name] != expected {
   912  			return fmt.Errorf("the builtin base-declaration %q header is not set to expected value %q", name, expected)
   913  		}
   914  	}
   915  	revision, err := checkRevision(h)
   916  	if err != nil {
   917  		return fmt.Errorf("cannot assemble the builtin-base declaration: %v", err)
   918  	}
   919  	h["timestamp"] = time.Now().UTC().Format(time.RFC3339)
   920  	a, err := assembleBaseDeclaration(assertionBase{
   921  		headers:   h,
   922  		body:      nil,
   923  		revision:  revision,
   924  		content:   trimmed,
   925  		signature: []byte("$builtin"),
   926  	})
   927  	if err != nil {
   928  		return fmt.Errorf("cannot assemble the builtin base-declaration: %v", err)
   929  	}
   930  	builtinBaseDeclaration = a.(*BaseDeclaration)
   931  	return nil
   932  }
   933  
   934  type dateRange struct {
   935  	Since time.Time
   936  	Until time.Time
   937  }
   938  
   939  // SnapDeveloper holds a snap-developer assertion, defining the developers who
   940  // can collaborate on a snap while it's owned by a specific publisher.
   941  //
   942  // The primary key (snap-id, publisher-id) allows a snap to have many
   943  // snap-developer assertions, e.g. to allow a future publisher's collaborations
   944  // to be defined before the snap is transferred. However only the
   945  // snap-developer for the current publisher (the snap-declaration publisher-id)
   946  // is relevant to a device.
   947  type SnapDeveloper struct {
   948  	assertionBase
   949  	developerRanges map[string][]*dateRange
   950  }
   951  
   952  // SnapID returns the snap id of the snap.
   953  func (snapdev *SnapDeveloper) SnapID() string {
   954  	return snapdev.HeaderString("snap-id")
   955  }
   956  
   957  // PublisherID returns the publisher's account id.
   958  func (snapdev *SnapDeveloper) PublisherID() string {
   959  	return snapdev.HeaderString("publisher-id")
   960  }
   961  
   962  func (snapdev *SnapDeveloper) checkConsistency(db RODatabase, acck *AccountKey) error {
   963  	// Check authority is the publisher or trusted.
   964  	authorityID := snapdev.AuthorityID()
   965  	publisherID := snapdev.PublisherID()
   966  	if !db.IsTrustedAccount(authorityID) && (publisherID != authorityID) {
   967  		return fmt.Errorf("snap-developer must be signed by the publisher or a trusted authority but got authority %q and publisher %q", authorityID, publisherID)
   968  	}
   969  
   970  	// Check snap-declaration for the snap-id exists for the series.
   971  	// Note: the current publisher is irrelevant here because this assertion
   972  	// may be for a future publisher.
   973  	_, err := db.Find(SnapDeclarationType, map[string]string{
   974  		// XXX: mediate getting current series through some context object? this gets the job done for now
   975  		"series":  release.Series,
   976  		"snap-id": snapdev.SnapID(),
   977  	})
   978  	if err != nil {
   979  		if IsNotFound(err) {
   980  			return fmt.Errorf("snap-developer assertion for snap id %q does not have a matching snap-declaration assertion", snapdev.SnapID())
   981  		}
   982  		return err
   983  	}
   984  
   985  	// check there's an account for the publisher-id
   986  	_, err = db.Find(AccountType, map[string]string{"account-id": publisherID})
   987  	if err != nil {
   988  		if IsNotFound(err) {
   989  			return fmt.Errorf("snap-developer assertion for snap-id %q does not have a matching account assertion for the publisher %q", snapdev.SnapID(), publisherID)
   990  		}
   991  		return err
   992  	}
   993  
   994  	// check there's an account for each developer
   995  	for developerID := range snapdev.developerRanges {
   996  		if developerID == publisherID {
   997  			continue
   998  		}
   999  		_, err = db.Find(AccountType, map[string]string{"account-id": developerID})
  1000  		if err != nil {
  1001  			if IsNotFound(err) {
  1002  				return fmt.Errorf("snap-developer assertion for snap-id %q does not have a matching account assertion for the developer %q", snapdev.SnapID(), developerID)
  1003  			}
  1004  			return err
  1005  		}
  1006  	}
  1007  
  1008  	return nil
  1009  }
  1010  
  1011  // expected interface is implemented
  1012  var _ consistencyChecker = (*SnapDeveloper)(nil)
  1013  
  1014  // Prerequisites returns references to this snap-developer's prerequisite assertions.
  1015  func (snapdev *SnapDeveloper) Prerequisites() []*Ref {
  1016  	// Capacity for the snap-declaration, the publisher and all developers.
  1017  	refs := make([]*Ref, 0, 2+len(snapdev.developerRanges))
  1018  
  1019  	// snap-declaration
  1020  	// XXX: mediate getting current series through some context object? this gets the job done for now
  1021  	refs = append(refs, &Ref{SnapDeclarationType, []string{release.Series, snapdev.SnapID()}})
  1022  
  1023  	// the publisher and developers
  1024  	publisherID := snapdev.PublisherID()
  1025  	refs = append(refs, &Ref{AccountType, []string{publisherID}})
  1026  	for developerID := range snapdev.developerRanges {
  1027  		if developerID != publisherID {
  1028  			refs = append(refs, &Ref{AccountType, []string{developerID}})
  1029  		}
  1030  	}
  1031  
  1032  	return refs
  1033  }
  1034  
  1035  func assembleSnapDeveloper(assert assertionBase) (Assertion, error) {
  1036  	developerRanges, err := checkDevelopers(assert.headers)
  1037  	if err != nil {
  1038  		return nil, err
  1039  	}
  1040  
  1041  	return &SnapDeveloper{
  1042  		assertionBase:   assert,
  1043  		developerRanges: developerRanges,
  1044  	}, nil
  1045  }
  1046  
  1047  func checkDevelopers(headers map[string]interface{}) (map[string][]*dateRange, error) {
  1048  	value, ok := headers["developers"]
  1049  	if !ok {
  1050  		return nil, nil
  1051  	}
  1052  	developers, ok := value.([]interface{})
  1053  	if !ok {
  1054  		return nil, fmt.Errorf(`"developers" must be a list of developer maps`)
  1055  	}
  1056  	if len(developers) == 0 {
  1057  		return nil, nil
  1058  	}
  1059  
  1060  	// Used to check for a developer with revoking and non-revoking items.
  1061  	// No entry means developer not yet seen, false means seen but not revoked,
  1062  	// true means seen and revoked.
  1063  	revocationStatus := map[string]bool{}
  1064  
  1065  	developerRanges := make(map[string][]*dateRange)
  1066  	for i, item := range developers {
  1067  		developer, ok := item.(map[string]interface{})
  1068  		if !ok {
  1069  			return nil, fmt.Errorf(`"developers" must be a list of developer maps`)
  1070  		}
  1071  
  1072  		what := fmt.Sprintf(`in "developers" item %d`, i+1)
  1073  		accountID, err := checkStringMatchesWhat(developer, "developer-id", what, validAccountID)
  1074  		if err != nil {
  1075  			return nil, err
  1076  		}
  1077  
  1078  		what = fmt.Sprintf(`in "developers" item %d for developer %q`, i+1, accountID)
  1079  		since, err := checkRFC3339DateWhat(developer, "since", what)
  1080  		if err != nil {
  1081  			return nil, err
  1082  		}
  1083  		until, err := checkRFC3339DateWithDefaultWhat(developer, "until", what, time.Time{})
  1084  		if err != nil {
  1085  			return nil, err
  1086  		}
  1087  		if !until.IsZero() && since.After(until) {
  1088  			return nil, fmt.Errorf(`"since" %s must be less than or equal to "until"`, what)
  1089  		}
  1090  
  1091  		// Track/check for revocation conflicts.
  1092  		revoked := since.Equal(until)
  1093  		previouslyRevoked, ok := revocationStatus[accountID]
  1094  		if !ok {
  1095  			revocationStatus[accountID] = revoked
  1096  		} else if previouslyRevoked || revoked {
  1097  			return nil, fmt.Errorf(`revocation for developer %q must be standalone but found other "developers" items`, accountID)
  1098  		}
  1099  
  1100  		developerRanges[accountID] = append(developerRanges[accountID], &dateRange{since, until})
  1101  	}
  1102  
  1103  	return developerRanges, nil
  1104  }