github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/asserts/snap_asserts.go (about)

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