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