github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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 }