github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/interfaces/policy/helpers.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 21 22 import ( 23 "fmt" 24 "strings" 25 26 "github.com/snapcore/snapd/asserts" 27 "github.com/snapcore/snapd/release" 28 "github.com/snapcore/snapd/snap" 29 "github.com/snapcore/snapd/strutil" 30 ) 31 32 // check helpers 33 34 func checkSnapType(snapInfo *snap.Info, types []string) error { 35 if len(types) == 0 { 36 return nil 37 } 38 snapType := snapInfo.Type() 39 s := string(snapType) 40 if snapType == snap.TypeOS || snapType == snap.TypeSnapd { 41 // we use "core" in the assertions and we need also to 42 // allow for the "snapd" snap 43 s = "core" 44 } 45 for _, t := range types { 46 if t == s { 47 return nil 48 } 49 } 50 return fmt.Errorf("snap type does not match") 51 } 52 53 func checkID(kind, id string, ids []string, special map[string]string) error { 54 if len(ids) == 0 { 55 return nil 56 } 57 if id == "" { // unset values never match 58 return fmt.Errorf("%s does not match", kind) 59 } 60 for _, cand := range ids { 61 if strings.HasPrefix(cand, "$") { 62 cand = special[cand] 63 if cand == "" { // we ignore unknown special "ids" 64 continue 65 } 66 } 67 if id == cand { 68 return nil 69 } 70 } 71 return fmt.Errorf("%s does not match", kind) 72 } 73 74 func checkOnClassic(c *asserts.OnClassicConstraint) error { 75 if c == nil { 76 return nil 77 } 78 if c.Classic != release.OnClassic { 79 return fmt.Errorf("on-classic mismatch") 80 } 81 if c.Classic && len(c.SystemIDs) != 0 { 82 return checkID("operating system ID", release.ReleaseInfo.ID, c.SystemIDs, nil) 83 } 84 return nil 85 } 86 87 func checkDeviceScope(c *asserts.DeviceScopeConstraint, model *asserts.Model, store *asserts.Store) error { 88 if c == nil { 89 return nil 90 } 91 if model == nil { 92 return fmt.Errorf("cannot match on-store/on-brand/on-model without model") 93 } 94 if store != nil && store.Store() != model.Store() { 95 return fmt.Errorf("store assertion and model store must match") 96 } 97 if len(c.Store) != 0 { 98 if !strutil.ListContains(c.Store, model.Store()) { 99 mismatch := true 100 if store != nil { 101 for _, sto := range c.Store { 102 if strutil.ListContains(store.FriendlyStores(), sto) { 103 mismatch = false 104 break 105 } 106 } 107 } 108 if mismatch { 109 return fmt.Errorf("on-store mismatch") 110 } 111 } 112 } 113 if len(c.Brand) != 0 { 114 if !strutil.ListContains(c.Brand, model.BrandID()) { 115 return fmt.Errorf("on-brand mismatch") 116 } 117 } 118 if len(c.Model) != 0 { 119 brandModel := fmt.Sprintf("%s/%s", model.BrandID(), model.Model()) 120 if !strutil.ListContains(c.Model, brandModel) { 121 return fmt.Errorf("on-model mismatch") 122 } 123 } 124 return nil 125 } 126 127 func checkNameConstraints(c *asserts.NameConstraints, iface, which, name string) error { 128 if c == nil { 129 return nil 130 } 131 special := map[string]string{ 132 "$INTERFACE": iface, 133 } 134 return c.Check(which, name, special) 135 } 136 137 func checkPlugConnectionConstraints1(connc *ConnectCandidate, constraints *asserts.PlugConnectionConstraints) error { 138 if err := checkNameConstraints(constraints.PlugNames, connc.Plug.Interface(), "plug name", connc.Plug.Name()); err != nil { 139 return err 140 } 141 if err := checkNameConstraints(constraints.SlotNames, connc.Slot.Interface(), "slot name", connc.Slot.Name()); err != nil { 142 return err 143 } 144 145 if err := constraints.PlugAttributes.Check(connc.Plug, connc); err != nil { 146 return err 147 } 148 if err := constraints.SlotAttributes.Check(connc.Slot, connc); err != nil { 149 return err 150 } 151 if err := checkSnapType(connc.Slot.Snap(), constraints.SlotSnapTypes); err != nil { 152 return err 153 } 154 if err := checkID("snap id", connc.slotSnapID(), constraints.SlotSnapIDs, nil); err != nil { 155 return err 156 } 157 err := checkID("publisher id", connc.slotPublisherID(), constraints.SlotPublisherIDs, map[string]string{ 158 "$PLUG_PUBLISHER_ID": connc.plugPublisherID(), 159 }) 160 if err != nil { 161 return err 162 } 163 if err := checkOnClassic(constraints.OnClassic); err != nil { 164 return err 165 } 166 if err := checkDeviceScope(constraints.DeviceScope, connc.Model, connc.Store); err != nil { 167 return err 168 } 169 return nil 170 } 171 172 func checkPlugConnectionAltConstraints(connc *ConnectCandidate, altConstraints []*asserts.PlugConnectionConstraints) (*asserts.PlugConnectionConstraints, error) { 173 var firstErr error 174 // OR of constraints 175 for _, constraints := range altConstraints { 176 err := checkPlugConnectionConstraints1(connc, constraints) 177 if err == nil { 178 return constraints, nil 179 } 180 if firstErr == nil { 181 firstErr = err 182 } 183 } 184 return nil, firstErr 185 } 186 187 func checkSlotConnectionConstraints1(connc *ConnectCandidate, constraints *asserts.SlotConnectionConstraints) error { 188 if err := checkNameConstraints(constraints.PlugNames, connc.Plug.Interface(), "plug name", connc.Plug.Name()); err != nil { 189 return err 190 } 191 if err := checkNameConstraints(constraints.SlotNames, connc.Slot.Interface(), "slot name", connc.Slot.Name()); err != nil { 192 return err 193 } 194 195 if err := constraints.PlugAttributes.Check(connc.Plug, connc); err != nil { 196 return err 197 } 198 if err := constraints.SlotAttributes.Check(connc.Slot, connc); err != nil { 199 return err 200 } 201 if err := checkSnapType(connc.Plug.Snap(), constraints.PlugSnapTypes); err != nil { 202 return err 203 } 204 if err := checkID("snap id", connc.plugSnapID(), constraints.PlugSnapIDs, nil); err != nil { 205 return err 206 } 207 err := checkID("publisher id", connc.plugPublisherID(), constraints.PlugPublisherIDs, map[string]string{ 208 "$SLOT_PUBLISHER_ID": connc.slotPublisherID(), 209 }) 210 if err != nil { 211 return err 212 } 213 if err := checkOnClassic(constraints.OnClassic); err != nil { 214 return err 215 } 216 if err := checkDeviceScope(constraints.DeviceScope, connc.Model, connc.Store); err != nil { 217 return err 218 } 219 return nil 220 } 221 222 func checkSlotConnectionAltConstraints(connc *ConnectCandidate, altConstraints []*asserts.SlotConnectionConstraints) (*asserts.SlotConnectionConstraints, error) { 223 var firstErr error 224 // OR of constraints 225 for _, constraints := range altConstraints { 226 err := checkSlotConnectionConstraints1(connc, constraints) 227 if err == nil { 228 return constraints, nil 229 } 230 if firstErr == nil { 231 firstErr = err 232 } 233 } 234 return nil, firstErr 235 } 236 237 func checkSnapTypeSlotInstallationConstraints1(ic *InstallCandidateMinimalCheck, slot *snap.SlotInfo, constraints *asserts.SlotInstallationConstraints) error { 238 if err := checkSnapType(slot.Snap, constraints.SlotSnapTypes); err != nil { 239 return err 240 } 241 if err := checkOnClassic(constraints.OnClassic); err != nil { 242 return err 243 } 244 return nil 245 } 246 247 func checkMinimalSlotInstallationAltConstraints(ic *InstallCandidateMinimalCheck, slot *snap.SlotInfo, altConstraints []*asserts.SlotInstallationConstraints) (bool, error) { 248 var firstErr error 249 var hasSnapTypeConstraints bool 250 // OR of constraints 251 for _, constraints := range altConstraints { 252 if constraints.OnClassic == nil && len(constraints.SlotSnapTypes) == 0 { 253 continue 254 } 255 hasSnapTypeConstraints = true 256 err := checkSnapTypeSlotInstallationConstraints1(ic, slot, constraints) 257 if err == nil { 258 return true, nil 259 } 260 if firstErr == nil { 261 firstErr = err 262 } 263 } 264 return hasSnapTypeConstraints, firstErr 265 } 266 267 func checkSlotInstallationConstraints1(ic *InstallCandidate, slot *snap.SlotInfo, constraints *asserts.SlotInstallationConstraints) error { 268 if err := checkNameConstraints(constraints.SlotNames, slot.Interface, "slot name", slot.Name); err != nil { 269 return err 270 } 271 272 // TODO: allow evaluated attr constraints here too? 273 if err := constraints.SlotAttributes.Check(slot, nil); err != nil { 274 return err 275 } 276 if err := checkSnapType(slot.Snap, constraints.SlotSnapTypes); err != nil { 277 return err 278 } 279 if err := checkOnClassic(constraints.OnClassic); err != nil { 280 return err 281 } 282 if err := checkDeviceScope(constraints.DeviceScope, ic.Model, ic.Store); err != nil { 283 return err 284 } 285 return nil 286 } 287 288 func checkSlotInstallationAltConstraints(ic *InstallCandidate, slot *snap.SlotInfo, altConstraints []*asserts.SlotInstallationConstraints) error { 289 var firstErr error 290 // OR of constraints 291 for _, constraints := range altConstraints { 292 err := checkSlotInstallationConstraints1(ic, slot, constraints) 293 if err == nil { 294 return nil 295 } 296 if firstErr == nil { 297 firstErr = err 298 } 299 } 300 return firstErr 301 } 302 303 func checkPlugInstallationConstraints1(ic *InstallCandidate, plug *snap.PlugInfo, constraints *asserts.PlugInstallationConstraints) error { 304 if err := checkNameConstraints(constraints.PlugNames, plug.Interface, "plug name", plug.Name); err != nil { 305 return err 306 } 307 308 // TODO: allow evaluated attr constraints here too? 309 if err := constraints.PlugAttributes.Check(plug, nil); err != nil { 310 return err 311 } 312 if err := checkSnapType(plug.Snap, constraints.PlugSnapTypes); err != nil { 313 return err 314 } 315 if err := checkOnClassic(constraints.OnClassic); err != nil { 316 return err 317 } 318 if err := checkDeviceScope(constraints.DeviceScope, ic.Model, ic.Store); err != nil { 319 return err 320 } 321 return nil 322 } 323 324 func checkPlugInstallationAltConstraints(ic *InstallCandidate, plug *snap.PlugInfo, altConstraints []*asserts.PlugInstallationConstraints) error { 325 var firstErr error 326 // OR of constraints 327 for _, constraints := range altConstraints { 328 err := checkPlugInstallationConstraints1(ic, plug, constraints) 329 if err == nil { 330 return nil 331 } 332 if firstErr == nil { 333 firstErr = err 334 } 335 } 336 return firstErr 337 } 338 339 // sideArity carries relevant arity constraints for successful 340 // allow-auto-connection rules. It implements policy.SideArity. 341 // ATM only slots-per-plug might have an interesting non-default 342 // value. 343 // See: https://forum.snapcraft.io/t/plug-slot-declaration-rules-greedy-plugs/12438 344 type sideArity struct { 345 slotsPerPlug asserts.SideArityConstraint 346 } 347 348 func (a sideArity) SlotsPerPlugOne() bool { 349 return a.slotsPerPlug.N == 1 350 } 351 352 func (a sideArity) SlotsPerPlugAny() bool { 353 return a.slotsPerPlug.Any() 354 }