github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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 checkSlotInstallationConstraints(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 checkSlotInstallationConstraints(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 checkPlugInstallationConstraints(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 checkPlugInstallationConstraints(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) 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 checkPlugConnectionConstraints(connc, denyConst) == nil {
   191  		return fmt.Errorf("%s denied by plug rule of interface %q%s", kind, connc.Plug.Interface(), context)
   192  	}
   193  	if checkPlugConnectionConstraints(connc, allowConst) != nil {
   194  		return fmt.Errorf("%s not allowed by plug rule of interface %q%s", kind, connc.Plug.Interface(), context)
   195  	}
   196  	return nil
   197  }
   198  
   199  func (connc *ConnectCandidate) checkSlotRule(kind string, rule *asserts.SlotRule, snapRule bool) error {
   200  	context := ""
   201  	if snapRule {
   202  		context = fmt.Sprintf(" for %q snap", connc.SlotSnapDeclaration.SnapName())
   203  	}
   204  	denyConst := rule.DenyConnection
   205  	allowConst := rule.AllowConnection
   206  	if kind == "auto-connection" {
   207  		denyConst = rule.DenyAutoConnection
   208  		allowConst = rule.AllowAutoConnection
   209  	}
   210  	if checkSlotConnectionConstraints(connc, denyConst) == nil {
   211  		return fmt.Errorf("%s denied by slot rule of interface %q%s", kind, connc.Plug.Interface(), context)
   212  	}
   213  	if checkSlotConnectionConstraints(connc, allowConst) != nil {
   214  		return fmt.Errorf("%s not allowed by slot rule of interface %q%s", kind, connc.Plug.Interface(), context)
   215  	}
   216  	return nil
   217  }
   218  
   219  func (connc *ConnectCandidate) check(kind string) error {
   220  	baseDecl := connc.BaseDeclaration
   221  	if baseDecl == nil {
   222  		return fmt.Errorf("internal error: improperly initialized ConnectCandidate")
   223  	}
   224  
   225  	iface := connc.Plug.Interface()
   226  
   227  	if connc.Slot.Interface() != iface {
   228  		return fmt.Errorf("cannot connect mismatched plug interface %q to slot interface %q", iface, connc.Slot.Interface())
   229  	}
   230  
   231  	if plugDecl := connc.PlugSnapDeclaration; plugDecl != nil {
   232  		if rule := plugDecl.PlugRule(iface); rule != nil {
   233  			return connc.checkPlugRule(kind, rule, true)
   234  		}
   235  	}
   236  	if slotDecl := connc.SlotSnapDeclaration; slotDecl != nil {
   237  		if rule := slotDecl.SlotRule(iface); rule != nil {
   238  			return connc.checkSlotRule(kind, rule, true)
   239  		}
   240  	}
   241  	if rule := baseDecl.PlugRule(iface); rule != nil {
   242  		return connc.checkPlugRule(kind, rule, false)
   243  	}
   244  	if rule := baseDecl.SlotRule(iface); rule != nil {
   245  		return connc.checkSlotRule(kind, rule, false)
   246  	}
   247  	return nil
   248  }
   249  
   250  // Check checks whether the connection is allowed.
   251  func (connc *ConnectCandidate) Check() error {
   252  	return connc.check("connection")
   253  }
   254  
   255  // CheckAutoConnect checks whether the connection is allowed to auto-connect.
   256  func (connc *ConnectCandidate) CheckAutoConnect() error {
   257  	return connc.check("auto-connection")
   258  }
   259  
   260  // InstallCandidateMinimalCheck represents a candidate snap installed with --dangerous flag that should pass minimum checks
   261  // against snap type (if present). It doesn't check interface attributes.
   262  type InstallCandidateMinimalCheck struct {
   263  	Snap            *snap.Info
   264  	BaseDeclaration *asserts.BaseDeclaration
   265  	Model           *asserts.Model
   266  	Store           *asserts.Store
   267  }
   268  
   269  func (ic *InstallCandidateMinimalCheck) checkSlotRule(slot *snap.SlotInfo, rule *asserts.SlotRule) error {
   270  	if hasConstraints, err := checkMinimalSlotInstallationConstraints(ic, slot, rule.DenyInstallation); hasConstraints && err == nil {
   271  		return fmt.Errorf("installation denied by %q slot rule of interface %q", slot.Name, slot.Interface)
   272  	}
   273  
   274  	// TODO check that the snap is an app or gadget if allow-installation had no slot-snap-type constraints
   275  	if _, err := checkMinimalSlotInstallationConstraints(ic, slot, rule.AllowInstallation); err != nil {
   276  		return fmt.Errorf("installation not allowed by %q slot rule of interface %q", slot.Name, slot.Interface)
   277  	}
   278  	return nil
   279  }
   280  
   281  func (ic *InstallCandidateMinimalCheck) checkSlot(slot *snap.SlotInfo) error {
   282  	iface := slot.Interface
   283  	if rule := ic.BaseDeclaration.SlotRule(iface); rule != nil {
   284  		return ic.checkSlotRule(slot, rule)
   285  	}
   286  	return nil
   287  }
   288  
   289  // Check checks whether the installation is allowed.
   290  func (ic *InstallCandidateMinimalCheck) Check() error {
   291  	if ic.BaseDeclaration == nil {
   292  		return fmt.Errorf("internal error: improperly initialized InstallCandidateMinimalCheck")
   293  	}
   294  
   295  	for _, slot := range ic.Snap.Slots {
   296  		err := ic.checkSlot(slot)
   297  		if err != nil {
   298  			return err
   299  		}
   300  	}
   301  
   302  	return nil
   303  }