github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/interfaces/policy/helpers.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2017 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 policy
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  
    26  	"github.com/snapcore/snapd/asserts"
    27  	"github.com/snapcore/snapd/release"
    28  	"github.com/snapcore/snapd/snap"
    29  	"github.com/snapcore/snapd/strutil"
    30  )
    31  
    32  // check helpers
    33  
    34  func checkSnapType(snapInfo *snap.Info, types []string) error {
    35  	if len(types) == 0 {
    36  		return nil
    37  	}
    38  	snapType := snapInfo.Type()
    39  	s := string(snapType)
    40  	if snapType == snap.TypeOS || snapType == snap.TypeSnapd {
    41  		// we use "core" in the assertions and we need also to
    42  		// allow for the "snapd" snap
    43  		s = "core"
    44  	}
    45  	for _, t := range types {
    46  		if t == s {
    47  			return nil
    48  		}
    49  	}
    50  	return fmt.Errorf("snap type does not match")
    51  }
    52  
    53  func checkID(kind, id string, ids []string, special map[string]string) error {
    54  	if len(ids) == 0 {
    55  		return nil
    56  	}
    57  	if id == "" { // unset values never match
    58  		return fmt.Errorf("%s does not match", kind)
    59  	}
    60  	for _, cand := range ids {
    61  		if strings.HasPrefix(cand, "$") {
    62  			cand = special[cand]
    63  			if cand == "" { // we ignore unknown special "ids"
    64  				continue
    65  			}
    66  		}
    67  		if id == cand {
    68  			return nil
    69  		}
    70  	}
    71  	return fmt.Errorf("%s does not match", kind)
    72  }
    73  
    74  func checkOnClassic(c *asserts.OnClassicConstraint) error {
    75  	if c == nil {
    76  		return nil
    77  	}
    78  	if c.Classic != release.OnClassic {
    79  		return fmt.Errorf("on-classic mismatch")
    80  	}
    81  	if c.Classic && len(c.SystemIDs) != 0 {
    82  		return checkID("operating system ID", release.ReleaseInfo.ID, c.SystemIDs, nil)
    83  	}
    84  	return nil
    85  }
    86  
    87  func checkDeviceScope(c *asserts.DeviceScopeConstraint, model *asserts.Model, store *asserts.Store) error {
    88  	if c == nil {
    89  		return nil
    90  	}
    91  	if model == nil {
    92  		return fmt.Errorf("cannot match on-store/on-brand/on-model without model")
    93  	}
    94  	if store != nil && store.Store() != model.Store() {
    95  		return fmt.Errorf("store assertion and model store must match")
    96  	}
    97  	if len(c.Store) != 0 {
    98  		if !strutil.ListContains(c.Store, model.Store()) {
    99  			mismatch := true
   100  			if store != nil {
   101  				for _, sto := range c.Store {
   102  					if strutil.ListContains(store.FriendlyStores(), sto) {
   103  						mismatch = false
   104  						break
   105  					}
   106  				}
   107  			}
   108  			if mismatch {
   109  				return fmt.Errorf("on-store mismatch")
   110  			}
   111  		}
   112  	}
   113  	if len(c.Brand) != 0 {
   114  		if !strutil.ListContains(c.Brand, model.BrandID()) {
   115  			return fmt.Errorf("on-brand mismatch")
   116  		}
   117  	}
   118  	if len(c.Model) != 0 {
   119  		brandModel := fmt.Sprintf("%s/%s", model.BrandID(), model.Model())
   120  		if !strutil.ListContains(c.Model, brandModel) {
   121  			return fmt.Errorf("on-model mismatch")
   122  		}
   123  	}
   124  	return nil
   125  }
   126  
   127  func checkNameConstraints(c *asserts.NameConstraints, iface, which, name string) error {
   128  	if c == nil {
   129  		return nil
   130  	}
   131  	special := map[string]string{
   132  		"$INTERFACE": iface,
   133  	}
   134  	return c.Check(which, name, special)
   135  }
   136  
   137  func checkPlugConnectionConstraints1(connc *ConnectCandidate, constraints *asserts.PlugConnectionConstraints) error {
   138  	if err := checkNameConstraints(constraints.PlugNames, connc.Plug.Interface(), "plug name", connc.Plug.Name()); err != nil {
   139  		return err
   140  	}
   141  	if err := checkNameConstraints(constraints.SlotNames, connc.Slot.Interface(), "slot name", connc.Slot.Name()); err != nil {
   142  		return err
   143  	}
   144  
   145  	if err := constraints.PlugAttributes.Check(connc.Plug, connc); err != nil {
   146  		return err
   147  	}
   148  	if err := constraints.SlotAttributes.Check(connc.Slot, connc); err != nil {
   149  		return err
   150  	}
   151  	if err := checkSnapType(connc.Slot.Snap(), constraints.SlotSnapTypes); err != nil {
   152  		return err
   153  	}
   154  	if err := checkID("snap id", connc.slotSnapID(), constraints.SlotSnapIDs, nil); err != nil {
   155  		return err
   156  	}
   157  	err := checkID("publisher id", connc.slotPublisherID(), constraints.SlotPublisherIDs, map[string]string{
   158  		"$PLUG_PUBLISHER_ID": connc.plugPublisherID(),
   159  	})
   160  	if err != nil {
   161  		return err
   162  	}
   163  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   164  		return err
   165  	}
   166  	if err := checkDeviceScope(constraints.DeviceScope, connc.Model, connc.Store); err != nil {
   167  		return err
   168  	}
   169  	return nil
   170  }
   171  
   172  func checkPlugConnectionAltConstraints(connc *ConnectCandidate, altConstraints []*asserts.PlugConnectionConstraints) (*asserts.PlugConnectionConstraints, error) {
   173  	var firstErr error
   174  	// OR of constraints
   175  	for _, constraints := range altConstraints {
   176  		err := checkPlugConnectionConstraints1(connc, constraints)
   177  		if err == nil {
   178  			return constraints, nil
   179  		}
   180  		if firstErr == nil {
   181  			firstErr = err
   182  		}
   183  	}
   184  	return nil, firstErr
   185  }
   186  
   187  func checkSlotConnectionConstraints1(connc *ConnectCandidate, constraints *asserts.SlotConnectionConstraints) error {
   188  	if err := checkNameConstraints(constraints.PlugNames, connc.Plug.Interface(), "plug name", connc.Plug.Name()); err != nil {
   189  		return err
   190  	}
   191  	if err := checkNameConstraints(constraints.SlotNames, connc.Slot.Interface(), "slot name", connc.Slot.Name()); err != nil {
   192  		return err
   193  	}
   194  
   195  	if err := constraints.PlugAttributes.Check(connc.Plug, connc); err != nil {
   196  		return err
   197  	}
   198  	if err := constraints.SlotAttributes.Check(connc.Slot, connc); err != nil {
   199  		return err
   200  	}
   201  	if err := checkSnapType(connc.Plug.Snap(), constraints.PlugSnapTypes); err != nil {
   202  		return err
   203  	}
   204  	if err := checkID("snap id", connc.plugSnapID(), constraints.PlugSnapIDs, nil); err != nil {
   205  		return err
   206  	}
   207  	err := checkID("publisher id", connc.plugPublisherID(), constraints.PlugPublisherIDs, map[string]string{
   208  		"$SLOT_PUBLISHER_ID": connc.slotPublisherID(),
   209  	})
   210  	if err != nil {
   211  		return err
   212  	}
   213  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   214  		return err
   215  	}
   216  	if err := checkDeviceScope(constraints.DeviceScope, connc.Model, connc.Store); err != nil {
   217  		return err
   218  	}
   219  	return nil
   220  }
   221  
   222  func checkSlotConnectionAltConstraints(connc *ConnectCandidate, altConstraints []*asserts.SlotConnectionConstraints) (*asserts.SlotConnectionConstraints, error) {
   223  	var firstErr error
   224  	// OR of constraints
   225  	for _, constraints := range altConstraints {
   226  		err := checkSlotConnectionConstraints1(connc, constraints)
   227  		if err == nil {
   228  			return constraints, nil
   229  		}
   230  		if firstErr == nil {
   231  			firstErr = err
   232  		}
   233  	}
   234  	return nil, firstErr
   235  }
   236  
   237  func checkSnapTypeSlotInstallationConstraints1(ic *InstallCandidateMinimalCheck, slot *snap.SlotInfo, constraints *asserts.SlotInstallationConstraints) error {
   238  	if err := checkSnapType(slot.Snap, constraints.SlotSnapTypes); err != nil {
   239  		return err
   240  	}
   241  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   242  		return err
   243  	}
   244  	return nil
   245  }
   246  
   247  func checkMinimalSlotInstallationAltConstraints(ic *InstallCandidateMinimalCheck, slot *snap.SlotInfo, altConstraints []*asserts.SlotInstallationConstraints) (bool, error) {
   248  	var firstErr error
   249  	var hasSnapTypeConstraints bool
   250  	// OR of constraints
   251  	for _, constraints := range altConstraints {
   252  		if constraints.OnClassic == nil && len(constraints.SlotSnapTypes) == 0 {
   253  			continue
   254  		}
   255  		hasSnapTypeConstraints = true
   256  		err := checkSnapTypeSlotInstallationConstraints1(ic, slot, constraints)
   257  		if err == nil {
   258  			return true, nil
   259  		}
   260  		if firstErr == nil {
   261  			firstErr = err
   262  		}
   263  	}
   264  	return hasSnapTypeConstraints, firstErr
   265  }
   266  
   267  func checkSlotInstallationConstraints1(ic *InstallCandidate, slot *snap.SlotInfo, constraints *asserts.SlotInstallationConstraints) error {
   268  	if err := checkNameConstraints(constraints.SlotNames, slot.Interface, "slot name", slot.Name); err != nil {
   269  		return err
   270  	}
   271  
   272  	// TODO: allow evaluated attr constraints here too?
   273  	if err := constraints.SlotAttributes.Check(slot, nil); err != nil {
   274  		return err
   275  	}
   276  	if err := checkSnapType(slot.Snap, constraints.SlotSnapTypes); err != nil {
   277  		return err
   278  	}
   279  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   280  		return err
   281  	}
   282  	if err := checkDeviceScope(constraints.DeviceScope, ic.Model, ic.Store); err != nil {
   283  		return err
   284  	}
   285  	return nil
   286  }
   287  
   288  func checkSlotInstallationAltConstraints(ic *InstallCandidate, slot *snap.SlotInfo, altConstraints []*asserts.SlotInstallationConstraints) error {
   289  	var firstErr error
   290  	// OR of constraints
   291  	for _, constraints := range altConstraints {
   292  		err := checkSlotInstallationConstraints1(ic, slot, constraints)
   293  		if err == nil {
   294  			return nil
   295  		}
   296  		if firstErr == nil {
   297  			firstErr = err
   298  		}
   299  	}
   300  	return firstErr
   301  }
   302  
   303  func checkPlugInstallationConstraints1(ic *InstallCandidate, plug *snap.PlugInfo, constraints *asserts.PlugInstallationConstraints) error {
   304  	if err := checkNameConstraints(constraints.PlugNames, plug.Interface, "plug name", plug.Name); err != nil {
   305  		return err
   306  	}
   307  
   308  	// TODO: allow evaluated attr constraints here too?
   309  	if err := constraints.PlugAttributes.Check(plug, nil); err != nil {
   310  		return err
   311  	}
   312  	if err := checkSnapType(plug.Snap, constraints.PlugSnapTypes); err != nil {
   313  		return err
   314  	}
   315  	if err := checkOnClassic(constraints.OnClassic); err != nil {
   316  		return err
   317  	}
   318  	if err := checkDeviceScope(constraints.DeviceScope, ic.Model, ic.Store); err != nil {
   319  		return err
   320  	}
   321  	return nil
   322  }
   323  
   324  func checkPlugInstallationAltConstraints(ic *InstallCandidate, plug *snap.PlugInfo, altConstraints []*asserts.PlugInstallationConstraints) error {
   325  	var firstErr error
   326  	// OR of constraints
   327  	for _, constraints := range altConstraints {
   328  		err := checkPlugInstallationConstraints1(ic, plug, constraints)
   329  		if err == nil {
   330  			return nil
   331  		}
   332  		if firstErr == nil {
   333  			firstErr = err
   334  		}
   335  	}
   336  	return firstErr
   337  }
   338  
   339  // sideArity carries relevant arity constraints for successful
   340  // allow-auto-connection rules. It implements policy.SideArity.
   341  // ATM only slots-per-plug might have an interesting non-default
   342  // value.
   343  // See: https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438
   344  type sideArity struct {
   345  	slotsPerPlug asserts.SideArityConstraint
   346  }
   347  
   348  func (a sideArity) SlotsPerPlugOne() bool {
   349  	return a.slotsPerPlug.N == 1
   350  }
   351  
   352  func (a sideArity) SlotsPerPlugAny() bool {
   353  	return a.slotsPerPlug.Any()
   354  }