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 }