github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/interfaces/policy/policy.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 implements the declaration based policy checks for
    21  // connecting or permitting installation of snaps based on their slots
    22  // and plugs.
    23  package policy
    24  
    25  import (
    26  	"fmt"
    27  
    28  	"github.com/snapcore/snapd/asserts"
    29  	"github.com/snapcore/snapd/interfaces"
    30  	"github.com/snapcore/snapd/snap"
    31  )
    32  
    33  // InstallCandidate represents a candidate snap for installation.
    34  type InstallCandidate struct {
    35  	Snap            *snap.Info
    36  	SnapDeclaration *asserts.SnapDeclaration
    37  
    38  	BaseDeclaration *asserts.BaseDeclaration
    39  
    40  	Model *asserts.Model
    41  	Store *asserts.Store
    42  }
    43  
    44  func (ic *InstallCandidate) checkSlotRule(slot *snap.SlotInfo, rule *asserts.SlotRule, snapRule bool) error {
    45  	context := ""
    46  	if snapRule {
    47  		context = fmt.Sprintf(" for %q snap", ic.SnapDeclaration.SnapName())
    48  	}
    49  	if checkSlotInstallationAltConstraints(ic, slot, rule.DenyInstallation) == nil {
    50  		return fmt.Errorf("installation denied by %q slot rule of interface %q%s", slot.Name, slot.Interface, context)
    51  	}
    52  	if checkSlotInstallationAltConstraints(ic, slot, rule.AllowInstallation) != nil {
    53  		return fmt.Errorf("installation not allowed by %q slot rule of interface %q%s", slot.Name, slot.Interface, context)
    54  	}
    55  	return nil
    56  }
    57  
    58  func (ic *InstallCandidate) checkPlugRule(plug *snap.PlugInfo, rule *asserts.PlugRule, snapRule bool) error {
    59  	context := ""
    60  	if snapRule {
    61  		context = fmt.Sprintf(" for %q snap", ic.SnapDeclaration.SnapName())
    62  	}
    63  	if checkPlugInstallationAltConstraints(ic, plug, rule.DenyInstallation) == nil {
    64  		return fmt.Errorf("installation denied by %q plug rule of interface %q%s", plug.Name, plug.Interface, context)
    65  	}
    66  	if checkPlugInstallationAltConstraints(ic, plug, rule.AllowInstallation) != nil {
    67  		return fmt.Errorf("installation not allowed by %q plug rule of interface %q%s", plug.Name, plug.Interface, context)
    68  	}
    69  	return nil
    70  }
    71  
    72  func (ic *InstallCandidate) checkSlot(slot *snap.SlotInfo) error {
    73  	iface := slot.Interface
    74  	if snapDecl := ic.SnapDeclaration; snapDecl != nil {
    75  		if rule := snapDecl.SlotRule(iface); rule != nil {
    76  			return ic.checkSlotRule(slot, rule, true)
    77  		}
    78  	}
    79  	if rule := ic.BaseDeclaration.SlotRule(iface); rule != nil {
    80  		return ic.checkSlotRule(slot, rule, false)
    81  	}
    82  	return nil
    83  }
    84  
    85  func (ic *InstallCandidate) checkPlug(plug *snap.PlugInfo) error {
    86  	iface := plug.Interface
    87  	if snapDecl := ic.SnapDeclaration; snapDecl != nil {
    88  		if rule := snapDecl.PlugRule(iface); rule != nil {
    89  			return ic.checkPlugRule(plug, rule, true)
    90  		}
    91  	}
    92  	if rule := ic.BaseDeclaration.PlugRule(iface); rule != nil {
    93  		return ic.checkPlugRule(plug, rule, false)
    94  	}
    95  	return nil
    96  }
    97  
    98  // Check checks whether the installation is allowed.
    99  func (ic *InstallCandidate) Check() error {
   100  	if ic.BaseDeclaration == nil {
   101  		return fmt.Errorf("internal error: improperly initialized InstallCandidate")
   102  	}
   103  
   104  	for _, slot := range ic.Snap.Slots {
   105  		err := ic.checkSlot(slot)
   106  		if err != nil {
   107  			return err
   108  		}
   109  	}
   110  
   111  	for _, plug := range ic.Snap.Plugs {
   112  		err := ic.checkPlug(plug)
   113  		if err != nil {
   114  			return err
   115  		}
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  // ConnectCandidate represents a candidate connection.
   122  type ConnectCandidate struct {
   123  	Plug                *interfaces.ConnectedPlug
   124  	PlugSnapDeclaration *asserts.SnapDeclaration
   125  
   126  	Slot                *interfaces.ConnectedSlot
   127  	SlotSnapDeclaration *asserts.SnapDeclaration
   128  
   129  	BaseDeclaration *asserts.BaseDeclaration
   130  
   131  	Model *asserts.Model
   132  	Store *asserts.Store
   133  }
   134  
   135  func nestedGet(which string, attrs interfaces.Attrer, path string) (interface{}, error) {
   136  	val, ok := attrs.Lookup(path)
   137  	if !ok {
   138  		return nil, fmt.Errorf("%s attribute %q not found", which, path)
   139  	}
   140  	return val, nil
   141  }
   142  
   143  func (connc *ConnectCandidate) PlugAttr(arg string) (interface{}, error) {
   144  	return nestedGet("plug", connc.Plug, arg)
   145  }
   146  
   147  func (connc *ConnectCandidate) SlotAttr(arg string) (interface{}, error) {
   148  	return nestedGet("slot", connc.Slot, arg)
   149  }
   150  
   151  func (connc *ConnectCandidate) plugSnapID() string {
   152  	if connc.PlugSnapDeclaration != nil {
   153  		return connc.PlugSnapDeclaration.SnapID()
   154  	}
   155  	return "" // never a valid snap-id
   156  }
   157  
   158  func (connc *ConnectCandidate) slotSnapID() string {
   159  	if connc.SlotSnapDeclaration != nil {
   160  		return connc.SlotSnapDeclaration.SnapID()
   161  	}
   162  	return "" // never a valid snap-id
   163  }
   164  
   165  func (connc *ConnectCandidate) plugPublisherID() string {
   166  	if connc.PlugSnapDeclaration != nil {
   167  		return connc.PlugSnapDeclaration.PublisherID()
   168  	}
   169  	return "" // never a valid publisher-id
   170  }
   171  
   172  func (connc *ConnectCandidate) slotPublisherID() string {
   173  	if connc.SlotSnapDeclaration != nil {
   174  		return connc.SlotSnapDeclaration.PublisherID()
   175  	}
   176  	return "" // never a valid publisher-id
   177  }
   178  
   179  func (connc *ConnectCandidate) checkPlugRule(kind string, rule *asserts.PlugRule, snapRule bool) (interfaces.SideArity, error) {
   180  	context := ""
   181  	if snapRule {
   182  		context = fmt.Sprintf(" for %q snap", connc.PlugSnapDeclaration.SnapName())
   183  	}
   184  	denyConst := rule.DenyConnection
   185  	allowConst := rule.AllowConnection
   186  	if kind == "auto-connection" {
   187  		denyConst = rule.DenyAutoConnection
   188  		allowConst = rule.AllowAutoConnection
   189  	}
   190  	if _, err := checkPlugConnectionAltConstraints(connc, denyConst); err == nil {
   191  		return nil, fmt.Errorf("%s denied by plug rule of interface %q%s", kind, connc.Plug.Interface(), context)
   192  	}
   193  
   194  	allowedConstraints, err := checkPlugConnectionAltConstraints(connc, allowConst)
   195  	if err != nil {
   196  		return nil, fmt.Errorf("%s not allowed by plug rule of interface %q%s", kind, connc.Plug.Interface(), context)
   197  	}
   198  	return sideArity{allowedConstraints.SlotsPerPlug}, nil
   199  }
   200  
   201  func (connc *ConnectCandidate) checkSlotRule(kind string, rule *asserts.SlotRule, snapRule bool) (interfaces.SideArity, error) {
   202  	context := ""
   203  	if snapRule {
   204  		context = fmt.Sprintf(" for %q snap", connc.SlotSnapDeclaration.SnapName())
   205  	}
   206  	denyConst := rule.DenyConnection
   207  	allowConst := rule.AllowConnection
   208  	if kind == "auto-connection" {
   209  		denyConst = rule.DenyAutoConnection
   210  		allowConst = rule.AllowAutoConnection
   211  	}
   212  	if _, err := checkSlotConnectionAltConstraints(connc, denyConst); err == nil {
   213  		return nil, fmt.Errorf("%s denied by slot rule of interface %q%s", kind, connc.Plug.Interface(), context)
   214  	}
   215  
   216  	allowedConstraints, err := checkSlotConnectionAltConstraints(connc, allowConst)
   217  	if err != nil {
   218  		return nil, fmt.Errorf("%s not allowed by slot rule of interface %q%s", kind, connc.Plug.Interface(), context)
   219  	}
   220  	return sideArity{allowedConstraints.SlotsPerPlug}, nil
   221  }
   222  
   223  func (connc *ConnectCandidate) check(kind string) (interfaces.SideArity, error) {
   224  	baseDecl := connc.BaseDeclaration
   225  	if baseDecl == nil {
   226  		return nil, fmt.Errorf("internal error: improperly initialized ConnectCandidate")
   227  	}
   228  
   229  	iface := connc.Plug.Interface()
   230  
   231  	if connc.Slot.Interface() != iface {
   232  		return nil, fmt.Errorf("cannot connect mismatched plug interface %q to slot interface %q", iface, connc.Slot.Interface())
   233  	}
   234  
   235  	if plugDecl := connc.PlugSnapDeclaration; plugDecl != nil {
   236  		if rule := plugDecl.PlugRule(iface); rule != nil {
   237  			return connc.checkPlugRule(kind, rule, true)
   238  		}
   239  	}
   240  	if slotDecl := connc.SlotSnapDeclaration; slotDecl != nil {
   241  		if rule := slotDecl.SlotRule(iface); rule != nil {
   242  			return connc.checkSlotRule(kind, rule, true)
   243  		}
   244  	}
   245  	if rule := baseDecl.PlugRule(iface); rule != nil {
   246  		return connc.checkPlugRule(kind, rule, false)
   247  	}
   248  	if rule := baseDecl.SlotRule(iface); rule != nil {
   249  		return connc.checkSlotRule(kind, rule, false)
   250  	}
   251  	return nil, nil
   252  }
   253  
   254  // Check checks whether the connection is allowed.
   255  func (connc *ConnectCandidate) Check() error {
   256  	_, err := connc.check("connection")
   257  	return err
   258  }
   259  
   260  // CheckAutoConnect checks whether the connection is allowed to auto-connect.
   261  func (connc *ConnectCandidate) CheckAutoConnect() (interfaces.SideArity, error) {
   262  	arity, err := connc.check("auto-connection")
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	if arity == nil {
   267  		// shouldn't happen but be safe, the callers should be able
   268  		// to assume arity to be non nil
   269  		arity = sideArity{asserts.SideArityConstraint{N: 1}}
   270  	}
   271  	return arity, nil
   272  }
   273  
   274  // InstallCandidateMinimalCheck represents a candidate snap installed with --dangerous flag that should pass minimum checks
   275  // against snap type (if present). It doesn't check interface attributes.
   276  type InstallCandidateMinimalCheck struct {
   277  	Snap            *snap.Info
   278  	BaseDeclaration *asserts.BaseDeclaration
   279  	Model           *asserts.Model
   280  	Store           *asserts.Store
   281  }
   282  
   283  func (ic *InstallCandidateMinimalCheck) checkSlotRule(slot *snap.SlotInfo, rule *asserts.SlotRule) error {
   284  	if hasConstraints, err := checkMinimalSlotInstallationAltConstraints(ic, slot, rule.DenyInstallation); hasConstraints && err == nil {
   285  		return fmt.Errorf("installation denied by %q slot rule of interface %q", slot.Name, slot.Interface)
   286  	}
   287  
   288  	// TODO check that the snap is an app or gadget if allow-installation had no slot-snap-type constraints
   289  	if _, err := checkMinimalSlotInstallationAltConstraints(ic, slot, rule.AllowInstallation); err != nil {
   290  		return fmt.Errorf("installation not allowed by %q slot rule of interface %q", slot.Name, slot.Interface)
   291  	}
   292  	return nil
   293  }
   294  
   295  func (ic *InstallCandidateMinimalCheck) checkSlot(slot *snap.SlotInfo) error {
   296  	iface := slot.Interface
   297  	if rule := ic.BaseDeclaration.SlotRule(iface); rule != nil {
   298  		return ic.checkSlotRule(slot, rule)
   299  	}
   300  	return nil
   301  }
   302  
   303  // Check checks whether the installation is allowed.
   304  func (ic *InstallCandidateMinimalCheck) Check() error {
   305  	if ic.BaseDeclaration == nil {
   306  		return fmt.Errorf("internal error: improperly initialized InstallCandidateMinimalCheck")
   307  	}
   308  
   309  	for _, slot := range ic.Snap.Slots {
   310  		err := ic.checkSlot(slot)
   311  		if err != nil {
   312  			return err
   313  		}
   314  	}
   315  
   316  	return nil
   317  }