gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/interfaces/policy/basedeclaration_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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_test 21 22 import ( 23 "fmt" 24 "strings" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/asserts" 29 "github.com/snapcore/snapd/interfaces" 30 "github.com/snapcore/snapd/interfaces/builtin" 31 "github.com/snapcore/snapd/interfaces/policy" 32 "github.com/snapcore/snapd/release" 33 "github.com/snapcore/snapd/snap" 34 "github.com/snapcore/snapd/snap/snaptest" 35 "github.com/snapcore/snapd/strutil" 36 "github.com/snapcore/snapd/testutil" 37 ) 38 39 type baseDeclSuite struct { 40 baseDecl *asserts.BaseDeclaration 41 restoreSanitize func() 42 } 43 44 var _ = Suite(&baseDeclSuite{}) 45 46 func (s *baseDeclSuite) SetUpSuite(c *C) { 47 s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 48 s.baseDecl = asserts.BuiltinBaseDeclaration() 49 } 50 51 func (s *baseDeclSuite) TearDownSuite(c *C) { 52 s.restoreSanitize() 53 } 54 55 func (s *baseDeclSuite) connectCand(c *C, iface, slotYaml, plugYaml string) *policy.ConnectCandidate { 56 if slotYaml == "" { 57 slotYaml = fmt.Sprintf(`name: slot-snap 58 version: 0 59 slots: 60 %s: 61 `, iface) 62 } 63 if plugYaml == "" { 64 plugYaml = fmt.Sprintf(`name: plug-snap 65 version: 0 66 plugs: 67 %s: 68 `, iface) 69 } 70 slotSnap := snaptest.MockInfo(c, slotYaml, nil) 71 plugSnap := snaptest.MockInfo(c, plugYaml, nil) 72 return &policy.ConnectCandidate{ 73 Plug: interfaces.NewConnectedPlug(plugSnap.Plugs[iface], nil, nil), 74 Slot: interfaces.NewConnectedSlot(slotSnap.Slots[iface], nil, nil), 75 BaseDeclaration: s.baseDecl, 76 } 77 } 78 79 func (s *baseDeclSuite) installSlotCand(c *C, iface string, snapType snap.Type, yaml string) *policy.InstallCandidate { 80 if yaml == "" { 81 yaml = fmt.Sprintf(`name: install-slot-snap 82 version: 0 83 type: %s 84 slots: 85 %s: 86 `, snapType, iface) 87 } 88 snap := snaptest.MockInfo(c, yaml, nil) 89 return &policy.InstallCandidate{ 90 Snap: snap, 91 BaseDeclaration: s.baseDecl, 92 } 93 } 94 95 func (s *baseDeclSuite) installPlugCand(c *C, iface string, snapType snap.Type, yaml string) *policy.InstallCandidate { 96 if yaml == "" { 97 yaml = fmt.Sprintf(`name: install-plug-snap 98 version: 0 99 type: %s 100 plugs: 101 %s: 102 `, snapType, iface) 103 } 104 snap := snaptest.MockInfo(c, yaml, nil) 105 return &policy.InstallCandidate{ 106 Snap: snap, 107 BaseDeclaration: s.baseDecl, 108 } 109 } 110 111 const declTempl = `type: snap-declaration 112 authority-id: canonical 113 series: 16 114 snap-name: @name@ 115 snap-id: @snapid@ 116 publisher-id: @publisher@ 117 @plugsSlots@ 118 timestamp: 2016-09-30T12:00:00Z 119 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 120 121 AXNpZw==` 122 123 func (s *baseDeclSuite) mockSnapDecl(c *C, name, snapID, publisher string, plugsSlots string) *asserts.SnapDeclaration { 124 encoded := strings.Replace(declTempl, "@name@", name, 1) 125 encoded = strings.Replace(encoded, "@snapid@", snapID, 1) 126 encoded = strings.Replace(encoded, "@publisher@", publisher, 1) 127 if plugsSlots != "" { 128 encoded = strings.Replace(encoded, "@plugsSlots@", strings.TrimSpace(plugsSlots), 1) 129 } else { 130 encoded = strings.Replace(encoded, "@plugsSlots@\n", "", 1) 131 } 132 a, err := asserts.Decode([]byte(encoded)) 133 c.Assert(err, IsNil) 134 return a.(*asserts.SnapDeclaration) 135 } 136 137 func (s *baseDeclSuite) TestAutoConnection(c *C) { 138 all := builtin.Interfaces() 139 140 // these have more complex or in flux policies and have their 141 // own separate tests 142 snowflakes := map[string]bool{ 143 "content": true, 144 "core-support": true, 145 "home": true, 146 "lxd-support": true, 147 "microstack-support": true, 148 "multipass-support": true, 149 "packagekit-control": true, 150 "snapd-control": true, 151 "dummy": true, 152 } 153 154 // these simply auto-connect, anything else doesn't 155 autoconnect := map[string]bool{ 156 "audio-playback": true, 157 "browser-support": true, 158 "desktop": true, 159 "desktop-legacy": true, 160 "gsettings": true, 161 "media-hub": true, 162 "mir": true, 163 "network": true, 164 "network-bind": true, 165 "network-status": true, 166 "online-accounts-service": true, 167 "opengl": true, 168 "optical-drive": true, 169 "screen-inhibit-control": true, 170 "ubuntu-download-manager": true, 171 "unity7": true, 172 "unity8": true, 173 "upower-observe": true, 174 "wayland": true, 175 "x11": true, 176 } 177 178 for _, iface := range all { 179 if snowflakes[iface.Name()] { 180 continue 181 } 182 expected := autoconnect[iface.Name()] 183 comm := Commentf(iface.Name()) 184 185 // check base declaration 186 cand := s.connectCand(c, iface.Name(), "", "") 187 arity, err := cand.CheckAutoConnect() 188 if expected { 189 c.Check(err, IsNil, comm) 190 c.Check(arity.SlotsPerPlugAny(), Equals, false) 191 } else { 192 c.Check(err, NotNil, comm) 193 } 194 } 195 } 196 197 func (s *baseDeclSuite) TestAutoConnectPlugSlot(c *C) { 198 all := builtin.Interfaces() 199 200 // these have more complex or in flux policies and have their 201 // own separate tests 202 snowflakes := map[string]bool{ 203 "classic-support": true, 204 "content": true, 205 "home": true, 206 "lxd-support": true, 207 // netlink-driver needs the family-name attributes to match 208 "netlink-driver": true, 209 } 210 211 for _, iface := range all { 212 if snowflakes[iface.Name()] { 213 continue 214 } 215 c.Check(iface.AutoConnect(nil, nil), Equals, true) 216 } 217 } 218 219 func (s *baseDeclSuite) TestInterimAutoConnectionHome(c *C) { 220 restore := release.MockOnClassic(true) 221 defer restore() 222 cand := s.connectCand(c, "home", "", "") 223 arity, err := cand.CheckAutoConnect() 224 c.Check(err, IsNil) 225 c.Check(arity.SlotsPerPlugAny(), Equals, false) 226 227 release.OnClassic = false 228 _, err = cand.CheckAutoConnect() 229 c.Check(err, ErrorMatches, `auto-connection denied by slot rule of interface \"home\"`) 230 } 231 232 func (s *baseDeclSuite) TestHomeReadAll(c *C) { 233 const plugYaml = `name: plug-snap 234 version: 0 235 plugs: 236 home: 237 read: all 238 ` 239 restore := release.MockOnClassic(true) 240 defer restore() 241 cand := s.connectCand(c, "home", "", plugYaml) 242 err := cand.Check() 243 c.Check(err, NotNil) 244 245 _, err = cand.CheckAutoConnect() 246 c.Check(err, NotNil) 247 248 release.OnClassic = false 249 err = cand.Check() 250 c.Check(err, NotNil) 251 252 _, err = cand.CheckAutoConnect() 253 c.Check(err, NotNil) 254 } 255 256 func (s *baseDeclSuite) TestHomeReadDefault(c *C) { 257 const plugYaml = `name: plug-snap 258 version: 0 259 plugs: 260 home: null 261 ` 262 restore := release.MockOnClassic(true) 263 defer restore() 264 cand := s.connectCand(c, "home", "", plugYaml) 265 err := cand.Check() 266 c.Check(err, IsNil) 267 268 // Same as TestInterimAutoConnectionHome() 269 arity, err := cand.CheckAutoConnect() 270 c.Check(err, IsNil) 271 c.Check(arity.SlotsPerPlugAny(), Equals, false) 272 273 release.OnClassic = false 274 err = cand.Check() 275 c.Check(err, IsNil) 276 277 // Same as TestInterimAutoConnectionHome() 278 _, err = cand.CheckAutoConnect() 279 c.Check(err, NotNil) 280 } 281 282 func (s *baseDeclSuite) TestAutoConnectionSnapdControl(c *C) { 283 cand := s.connectCand(c, "snapd-control", "", "") 284 _, err := cand.CheckAutoConnect() 285 c.Check(err, NotNil) 286 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"snapd-control\"") 287 288 plugsSlots := ` 289 plugs: 290 snapd-control: 291 allow-auto-connection: true 292 ` 293 294 lxdDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 295 cand.PlugSnapDeclaration = lxdDecl 296 arity, err := cand.CheckAutoConnect() 297 c.Check(err, IsNil) 298 c.Check(arity.SlotsPerPlugAny(), Equals, false) 299 } 300 301 func (s *baseDeclSuite) TestAutoConnectionContent(c *C) { 302 // random snaps cannot connect with content 303 // (Sanitize* will now also block this) 304 cand := s.connectCand(c, "content", "", "") 305 _, err := cand.CheckAutoConnect() 306 c.Check(err, NotNil) 307 308 slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "") 309 plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "") 310 plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "") 311 312 // same publisher, same content 313 cand = s.connectCand(c, "stuff", ` 314 name: slot-snap 315 version: 0 316 slots: 317 stuff: 318 interface: content 319 content: mk1 320 `, ` 321 name: plug-snap 322 version: 0 323 plugs: 324 stuff: 325 interface: content 326 content: mk1 327 `) 328 cand.SlotSnapDeclaration = slotDecl1 329 cand.PlugSnapDeclaration = plugDecl1 330 arity, err := cand.CheckAutoConnect() 331 c.Check(err, IsNil) 332 c.Check(arity.SlotsPerPlugAny(), Equals, false) 333 334 // different publisher, same content 335 cand.SlotSnapDeclaration = slotDecl1 336 cand.PlugSnapDeclaration = plugDecl2 337 _, err = cand.CheckAutoConnect() 338 c.Check(err, NotNil) 339 340 // same publisher, different content 341 cand = s.connectCand(c, "stuff", `name: slot-snap 342 version: 0 343 slots: 344 stuff: 345 interface: content 346 content: mk1 347 `, ` 348 name: plug-snap 349 version: 0 350 plugs: 351 stuff: 352 interface: content 353 content: mk2 354 `) 355 cand.SlotSnapDeclaration = slotDecl1 356 cand.PlugSnapDeclaration = plugDecl1 357 _, err = cand.CheckAutoConnect() 358 c.Check(err, NotNil) 359 } 360 361 func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverride(c *C) { 362 // by default, don't auto-connect 363 cand := s.connectCand(c, "lxd-support", "", "") 364 _, err := cand.CheckAutoConnect() 365 c.Check(err, NotNil) 366 367 plugsSlots := ` 368 plugs: 369 lxd-support: 370 allow-auto-connection: true 371 ` 372 373 lxdDecl := s.mockSnapDecl(c, "lxd", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 374 cand.PlugSnapDeclaration = lxdDecl 375 _, err = cand.CheckAutoConnect() 376 c.Check(err, IsNil) 377 } 378 379 func (s *baseDeclSuite) TestAutoConnectionLxdSupportOverrideRevoke(c *C) { 380 cand := s.connectCand(c, "lxd-support", "", "") 381 plugsSlots := ` 382 plugs: 383 lxd-support: 384 allow-auto-connection: false 385 ` 386 387 lxdDecl := s.mockSnapDecl(c, "notlxd", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 388 cand.PlugSnapDeclaration = lxdDecl 389 _, err := cand.CheckAutoConnect() 390 c.Check(err, NotNil) 391 c.Assert(err, ErrorMatches, "auto-connection not allowed by plug rule of interface \"lxd-support\" for \"notlxd\" snap") 392 } 393 394 func (s *baseDeclSuite) TestAutoConnectionKernelModuleControlOverride(c *C) { 395 cand := s.connectCand(c, "kernel-module-control", "", "") 396 _, err := cand.CheckAutoConnect() 397 c.Check(err, NotNil) 398 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"kernel-module-control\"") 399 400 plugsSlots := ` 401 plugs: 402 kernel-module-control: 403 allow-auto-connection: true 404 ` 405 406 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 407 cand.PlugSnapDeclaration = snapDecl 408 _, err = cand.CheckAutoConnect() 409 c.Check(err, IsNil) 410 } 411 412 func (s *baseDeclSuite) TestAutoConnectionDockerSupportOverride(c *C) { 413 cand := s.connectCand(c, "docker-support", "", "") 414 _, err := cand.CheckAutoConnect() 415 c.Check(err, NotNil) 416 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"docker-support\"") 417 418 plugsSlots := ` 419 plugs: 420 docker-support: 421 allow-auto-connection: true 422 ` 423 424 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 425 cand.PlugSnapDeclaration = snapDecl 426 _, err = cand.CheckAutoConnect() 427 c.Check(err, IsNil) 428 } 429 430 func (s *baseDeclSuite) TestAutoConnectionClassicSupportOverride(c *C) { 431 cand := s.connectCand(c, "classic-support", "", "") 432 _, err := cand.CheckAutoConnect() 433 c.Check(err, NotNil) 434 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"classic-support\"") 435 436 plugsSlots := ` 437 plugs: 438 classic-support: 439 allow-auto-connection: true 440 ` 441 442 snapDecl := s.mockSnapDecl(c, "classic", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 443 cand.PlugSnapDeclaration = snapDecl 444 _, err = cand.CheckAutoConnect() 445 c.Check(err, IsNil) 446 } 447 448 func (s *baseDeclSuite) TestAutoConnectionKubernetesSupportOverride(c *C) { 449 cand := s.connectCand(c, "kubernetes-support", "", "") 450 _, err := cand.CheckAutoConnect() 451 c.Check(err, NotNil) 452 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"kubernetes-support\"") 453 454 plugsSlots := ` 455 plugs: 456 kubernetes-support: 457 allow-auto-connection: true 458 ` 459 460 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 461 cand.PlugSnapDeclaration = snapDecl 462 _, err = cand.CheckAutoConnect() 463 c.Check(err, IsNil) 464 } 465 466 func (s *baseDeclSuite) TestAutoConnectionMicroStackSupportOverride(c *C) { 467 cand := s.connectCand(c, "microstack-support", "", "") 468 _, err := cand.CheckAutoConnect() 469 c.Check(err, NotNil) 470 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"microstack-support\"") 471 472 plugsSlots := ` 473 plugs: 474 microstack-support: 475 allow-auto-connection: true 476 ` 477 478 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 479 cand.PlugSnapDeclaration = snapDecl 480 _, err = cand.CheckAutoConnect() 481 c.Check(err, IsNil) 482 } 483 func (s *baseDeclSuite) TestAutoConnectionGreengrassSupportOverride(c *C) { 484 cand := s.connectCand(c, "greengrass-support", "", "") 485 _, err := cand.CheckAutoConnect() 486 c.Check(err, NotNil) 487 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"greengrass-support\"") 488 489 plugsSlots := ` 490 plugs: 491 greengrass-support: 492 allow-auto-connection: true 493 ` 494 495 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 496 cand.PlugSnapDeclaration = snapDecl 497 _, err = cand.CheckAutoConnect() 498 c.Check(err, IsNil) 499 } 500 501 func (s *baseDeclSuite) TestAutoConnectionMultipassSupportOverride(c *C) { 502 cand := s.connectCand(c, "multipass-support", "", "") 503 _, err := cand.CheckAutoConnect() 504 c.Check(err, NotNil) 505 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"multipass-support\"") 506 507 plugsSlots := ` 508 plugs: 509 multipass-support: 510 allow-auto-connection: true 511 ` 512 513 snapDecl := s.mockSnapDecl(c, "multipass-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 514 cand.PlugSnapDeclaration = snapDecl 515 _, err = cand.CheckAutoConnect() 516 c.Check(err, IsNil) 517 } 518 519 func (s *baseDeclSuite) TestAutoConnectionBlockDevicesOverride(c *C) { 520 cand := s.connectCand(c, "block-devices", "", "") 521 _, err := cand.CheckAutoConnect() 522 c.Check(err, NotNil) 523 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"block-devices\"") 524 525 plugsSlots := ` 526 plugs: 527 block-devices: 528 allow-auto-connection: true 529 ` 530 531 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 532 cand.PlugSnapDeclaration = snapDecl 533 _, err = cand.CheckAutoConnect() 534 c.Check(err, IsNil) 535 } 536 537 func (s *baseDeclSuite) TestAutoConnectionPackagekitControlOverride(c *C) { 538 cand := s.connectCand(c, "packagekit-control", "", "") 539 _, err := cand.CheckAutoConnect() 540 c.Check(err, NotNil) 541 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"packagekit-control\"") 542 543 plugsSlots := ` 544 plugs: 545 packagekit-control: 546 allow-auto-connection: true 547 ` 548 549 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 550 cand.PlugSnapDeclaration = snapDecl 551 _, err = cand.CheckAutoConnect() 552 c.Check(err, IsNil) 553 } 554 555 func (s *baseDeclSuite) TestAutoConnectionOverrideMultiple(c *C) { 556 plugsSlots := ` 557 plugs: 558 network-bind: 559 allow-auto-connection: true 560 network-control: 561 allow-auto-connection: true 562 kernel-module-control: 563 allow-auto-connection: true 564 system-observe: 565 allow-auto-connection: true 566 hardware-observe: 567 allow-auto-connection: true 568 ` 569 570 snapDecl := s.mockSnapDecl(c, "some-snap", "J60k4JY0HppjwOjW8dZdYc8obXKxujRu", "canonical", plugsSlots) 571 572 all := builtin.Interfaces() 573 // these are a mixture interfaces that the snap plugs 574 plugged := map[string]bool{ 575 "network-bind": true, 576 "network-control": true, 577 "kernel-module-control": true, 578 "system-observe": true, 579 "hardware-observe": true, 580 } 581 for _, iface := range all { 582 if !plugged[iface.Name()] { 583 continue 584 } 585 586 cand := s.connectCand(c, iface.Name(), "", "") 587 cand.PlugSnapDeclaration = snapDecl 588 arity, err := cand.CheckAutoConnect() 589 c.Check(err, IsNil) 590 c.Check(arity.SlotsPerPlugAny(), Equals, false) 591 } 592 } 593 594 // describe installation rules for slots succinctly for cross-checking, 595 // if an interface is not mentioned here a slot of its type can only 596 // be installed by a core snap (and this was taken care by 597 // BeforePrepareSlot), 598 // otherwise the entry for the interface is the list of snap types it 599 // can be installed by (using the declaration naming); 600 // ATM a nil entry means even stricter rules that would need be tested 601 // separately and whose implementation is in flux for now 602 var ( 603 slotInstallation = map[string][]string{ 604 // other 605 "adb-support": {"core"}, 606 "audio-playback": {"app", "core"}, 607 "audio-record": {"app", "core"}, 608 "autopilot-introspection": {"core"}, 609 "avahi-control": {"app", "core"}, 610 "avahi-observe": {"app", "core"}, 611 "bluez": {"app", "core"}, 612 "bool-file": {"core", "gadget"}, 613 "browser-support": {"core"}, 614 "content": {"app", "gadget"}, 615 "core-support": {"core"}, 616 "cups": {"app"}, 617 "cups-control": {"app", "core"}, 618 "dbus": {"app"}, 619 "docker-support": {"core"}, 620 "desktop-launch": {"core"}, 621 "dsp": {"core", "gadget"}, 622 "dummy": {"app"}, 623 "fwupd": {"app", "core"}, 624 "gpio": {"core", "gadget"}, 625 "gpio-control": {"core"}, 626 "greengrass-support": {"core"}, 627 "hidraw": {"core", "gadget"}, 628 "i2c": {"core", "gadget"}, 629 "iio": {"core", "gadget"}, 630 "kubernetes-support": {"core"}, 631 "location-control": {"app"}, 632 "location-observe": {"app"}, 633 "lxd-support": {"core"}, 634 "maliit": {"app"}, 635 "media-hub": {"app", "core"}, 636 "mir": {"app"}, 637 "microstack-support": {"core"}, 638 "modem-manager": {"app", "core"}, 639 "mpris": {"app"}, 640 "netlink-driver": {"core", "gadget"}, 641 "network-manager": {"app", "core"}, 642 "network-manager-observe": {"app", "core"}, 643 "network-status": {"core"}, 644 "ofono": {"app", "core"}, 645 "online-accounts-service": {"app"}, 646 "power-control": {"core"}, 647 "ppp": {"core"}, 648 "pulseaudio": {"app", "core"}, 649 "pwm": {"core", "gadget"}, 650 "raw-volume": {"core", "gadget"}, 651 "sd-control": {"core"}, 652 "serial-port": {"core", "gadget"}, 653 "spi": {"core", "gadget"}, 654 "storage-framework-service": {"app"}, 655 "thumbnailer-service": {"app"}, 656 "ubuntu-download-manager": {"app"}, 657 "udisks2": {"app", "core"}, 658 "uhid": {"core"}, 659 "uio": {"core", "gadget"}, 660 "unity8": {"app"}, 661 "unity8-calendar": {"app"}, 662 "unity8-contacts": {"app"}, 663 "upower-observe": {"app", "core"}, 664 "wayland": {"app", "core"}, 665 "x11": {"app", "core"}, 666 // snowflakes 667 "classic-support": nil, 668 "docker": nil, 669 "lxd": nil, 670 } 671 672 restrictedPlugInstallation = map[string][]string{ 673 "core-support": {"core"}, 674 } 675 676 snapTypeMap = map[string]snap.Type{ 677 "core": snap.TypeOS, 678 "app": snap.TypeApp, 679 "kernel": snap.TypeKernel, 680 "gadget": snap.TypeGadget, 681 } 682 ) 683 684 func (s *baseDeclSuite) TestSlotInstallation(c *C) { 685 all := builtin.Interfaces() 686 687 for _, iface := range all { 688 types, ok := slotInstallation[iface.Name()] 689 if !ok { // common ones, only core can install them, 690 types = []string{"core"} 691 } 692 693 if types == nil { 694 // snowflake needs to be tested specially 695 continue 696 } 697 for name, snapType := range snapTypeMap { 698 ok := strutil.ListContains(types, name) 699 ic := s.installSlotCand(c, iface.Name(), snapType, ``) 700 err := ic.Check() 701 comm := Commentf("%s by %s snap", iface.Name(), name) 702 if ok { 703 c.Check(err, IsNil, comm) 704 } else { 705 c.Check(err, NotNil, comm) 706 } 707 } 708 } 709 710 // test docker specially 711 ic := s.installSlotCand(c, "docker", snap.TypeApp, ``) 712 err := ic.Check() 713 c.Assert(err, Not(IsNil)) 714 c.Assert(err, ErrorMatches, "installation not allowed by \"docker\" slot rule of interface \"docker\"") 715 716 // test lxd specially 717 ic = s.installSlotCand(c, "lxd", snap.TypeApp, ``) 718 err = ic.Check() 719 c.Assert(err, Not(IsNil)) 720 c.Assert(err, ErrorMatches, "installation not allowed by \"lxd\" slot rule of interface \"lxd\"") 721 } 722 723 func (s *baseDeclSuite) TestPlugInstallation(c *C) { 724 all := builtin.Interfaces() 725 726 restricted := map[string]bool{ 727 "block-devices": true, 728 "classic-support": true, 729 "desktop-launch": true, 730 "dm-crypt": true, 731 "docker-support": true, 732 "greengrass-support": true, 733 "gpio-control": true, 734 "ion-memory-control": true, 735 "kernel-module-control": true, 736 "kubernetes-support": true, 737 "lxd-support": true, 738 "microstack-support": true, 739 "multipass-support": true, 740 "packagekit-control": true, 741 "personal-files": true, 742 "sd-control": true, 743 "snap-refresh-control": true, 744 "snapd-control": true, 745 "system-files": true, 746 "tee": true, 747 "uinput": true, 748 "unity8": true, 749 } 750 751 for _, iface := range all { 752 types, ok := restrictedPlugInstallation[iface.Name()] 753 // If plug installation is restricted to specific snap types we 754 // need to make sure this is really the case here. If that is not 755 // the case we continue as normal. 756 if ok { 757 for name, snapType := range snapTypeMap { 758 ok := strutil.ListContains(types, name) 759 ic := s.installPlugCand(c, iface.Name(), snapType, ``) 760 err := ic.Check() 761 comm := Commentf("%s by %s snap", iface.Name(), name) 762 if ok { 763 c.Check(err, IsNil, comm) 764 } else { 765 c.Check(err, NotNil, comm) 766 } 767 } 768 } else { 769 ic := s.installPlugCand(c, iface.Name(), snap.TypeApp, ``) 770 err := ic.Check() 771 comm := Commentf("%s", iface.Name()) 772 if restricted[iface.Name()] { 773 c.Check(err, NotNil, comm) 774 } else { 775 c.Check(err, IsNil, comm) 776 } 777 } 778 } 779 } 780 781 func (s *baseDeclSuite) TestConnection(c *C) { 782 all := builtin.Interfaces() 783 784 // connecting with these interfaces needs to be allowed on 785 // case-by-case basis 786 noconnect := map[string]bool{ 787 "content": true, 788 "cups": true, 789 "docker": true, 790 "fwupd": true, 791 "location-control": true, 792 "location-observe": true, 793 "lxd": true, 794 "maliit": true, 795 "mir": true, 796 "online-accounts-service": true, 797 "raw-volume": true, 798 "storage-framework-service": true, 799 "thumbnailer-service": true, 800 "ubuntu-download-manager": true, 801 "unity8-calendar": true, 802 "unity8-contacts": true, 803 } 804 805 for _, iface := range all { 806 expected := !noconnect[iface.Name()] 807 comm := Commentf(iface.Name()) 808 809 // check base declaration 810 cand := s.connectCand(c, iface.Name(), "", "") 811 err := cand.Check() 812 813 if expected { 814 c.Check(err, IsNil, comm) 815 } else { 816 c.Check(err, NotNil, comm) 817 } 818 } 819 } 820 821 func (s *baseDeclSuite) TestConnectionOnClassic(c *C) { 822 restore := release.MockOnClassic(false) 823 defer restore() 824 825 all := builtin.Interfaces() 826 827 // connecting with these interfaces needs to be allowed on 828 // case-by-case basis when not on classic 829 noconnect := map[string]bool{ 830 "audio-record": true, 831 "modem-manager": true, 832 "network-manager": true, 833 "ofono": true, 834 "pulseaudio": true, 835 "upower-observe": true, 836 } 837 838 for _, onClassic := range []bool{true, false} { 839 release.OnClassic = onClassic 840 for _, iface := range all { 841 if !noconnect[iface.Name()] { 842 continue 843 } 844 expected := onClassic 845 comm := Commentf(iface.Name()) 846 847 // check base declaration 848 cand := s.connectCand(c, iface.Name(), "", "") 849 err := cand.Check() 850 851 if expected { 852 c.Check(err, IsNil, comm) 853 } else { 854 c.Check(err, NotNil, comm) 855 } 856 } 857 } 858 } 859 860 func (s *baseDeclSuite) TestConnectionImplicitOnClassicOrAppSnap(c *C) { 861 restore := release.MockOnClassic(false) 862 defer restore() 863 864 all := builtin.Interfaces() 865 866 // These interfaces represent when the interface might be implicit on 867 // classic or when the interface is provided via an app snap. As such, 868 // they all share the following: 869 // 870 // - implicitOnCore: false 871 // - implicitOnClassis: true 872 // - base declaration uses: 873 // allow-installation: 874 // slot-snap-type: 875 // - app 876 // - core 877 // deny-connection: 878 // on-classic: false 879 // deny-auto-connection: true|false|unspecified 880 // 881 // connecting with these interfaces needs to be allowed on 882 // case-by-case basis when not on classic 883 ifaces := map[string]bool{ 884 "audio-playback": true, 885 "audio-record": true, 886 "cups-control": true, 887 "modem-manager": true, 888 "network-manager": true, 889 "ofono": true, 890 "pulseaudio": true, 891 "upower-observe": true, 892 } 893 894 for _, iface := range all { 895 if !ifaces[iface.Name()] { 896 continue 897 } 898 comm := Commentf(iface.Name()) 899 900 // verify the interface is setup as expected wrt 901 // implicitOnCore, implicitOnClassic, no plugs and has 902 // expected slots (ignoring AutoConnection) 903 si := interfaces.StaticInfoOf(iface) 904 c.Assert(si.ImplicitOnCore, Equals, false) 905 c.Assert(si.ImplicitOnClassic, Equals, true) 906 907 c.Assert(s.baseDecl.PlugRule(iface.Name()), IsNil) 908 909 sr := s.baseDecl.SlotRule(iface.Name()) 910 c.Assert(sr, Not(IsNil)) 911 c.Assert(sr.AllowInstallation, HasLen, 1) 912 c.Check(sr.AllowInstallation[0].SlotSnapTypes, DeepEquals, []string{"app", "core"}, comm) 913 c.Assert(sr.DenyConnection, HasLen, 1) 914 c.Check(sr.DenyConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: false}, comm) 915 916 for _, onClassic := range []bool{true, false} { 917 release.OnClassic = onClassic 918 919 for _, implicit := range []bool{true, false} { 920 // When implicitOnCore is false, there is 921 // nothing to test on Core 922 if implicit && !onClassic { 923 continue 924 } 925 926 snapType := "app" 927 if implicit { 928 snapType = "os" 929 } 930 slotYaml := fmt.Sprintf(`name: slot-snap 931 version: 0 932 type: %s 933 slots: 934 %s: 935 `, snapType, iface.Name()) 936 937 // XXX: eventually 'onClassic && !implicit' but 938 // the current declaration allows connection on 939 // Core when 'type: app'. See: 940 // https://github.com/snapcore/snapd/pull/8920/files#r471678529 941 expected := onClassic 942 943 // check base declaration 944 cand := s.connectCand(c, iface.Name(), slotYaml, "") 945 err := cand.Check() 946 947 if expected { 948 c.Check(err, IsNil, comm) 949 } else { 950 c.Check(err, NotNil, comm) 951 } 952 } 953 } 954 } 955 } 956 957 func (s *baseDeclSuite) TestSanity(c *C) { 958 all := builtin.Interfaces() 959 960 // these interfaces have rules both for the slots and plugs side 961 // given how the rules work this can be delicate, 962 // listed here to make sure that was a conscious decision 963 bothSides := map[string]bool{ 964 "block-devices": true, 965 "audio-playback": true, 966 "classic-support": true, 967 "core-support": true, 968 "desktop-launch": true, 969 "dm-crypt": true, 970 "docker-support": true, 971 "greengrass-support": true, 972 "gpio-control": true, 973 "ion-memory-control": true, 974 "kernel-module-control": true, 975 "kubernetes-support": true, 976 "lxd-support": true, 977 "microstack-support": true, 978 "multipass-support": true, 979 "packagekit-control": true, 980 "personal-files": true, 981 "sd-control": true, 982 "snap-refresh-control": true, 983 "snapd-control": true, 984 "system-files": true, 985 "tee": true, 986 "udisks2": true, 987 "uinput": true, 988 "unity8": true, 989 "wayland": true, 990 } 991 992 for _, iface := range all { 993 plugRule := s.baseDecl.PlugRule(iface.Name()) 994 slotRule := s.baseDecl.SlotRule(iface.Name()) 995 if plugRule == nil && slotRule == nil { 996 c.Logf("%s is not considered in the base declaration", iface.Name()) 997 c.Fail() 998 } 999 if plugRule != nil && slotRule != nil { 1000 if !bothSides[iface.Name()] { 1001 c.Logf("%s have both a base declaration slot rule and plug rule, make sure that's intended and correct", iface.Name()) 1002 c.Fail() 1003 } 1004 } 1005 } 1006 } 1007 1008 func (s *baseDeclSuite) TestConnectionContent(c *C) { 1009 // we let connect explicitly as long as content matches (or is absent on both sides) 1010 1011 // random (Sanitize* will now also block this) 1012 cand := s.connectCand(c, "content", "", "") 1013 err := cand.Check() 1014 c.Check(err, NotNil) 1015 1016 slotDecl1 := s.mockSnapDecl(c, "slot-snap", "slot-snap-id", "pub1", "") 1017 plugDecl1 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub1", "") 1018 plugDecl2 := s.mockSnapDecl(c, "plug-snap", "plug-snap-id", "pub2", "") 1019 1020 // same publisher, same content 1021 cand = s.connectCand(c, "stuff", `name: slot-snap 1022 version: 0 1023 slots: 1024 stuff: 1025 interface: content 1026 content: mk1 1027 `, ` 1028 name: plug-snap 1029 version: 0 1030 plugs: 1031 stuff: 1032 interface: content 1033 content: mk1 1034 `) 1035 cand.SlotSnapDeclaration = slotDecl1 1036 cand.PlugSnapDeclaration = plugDecl1 1037 err = cand.Check() 1038 c.Check(err, IsNil) 1039 1040 // different publisher, same content 1041 cand.SlotSnapDeclaration = slotDecl1 1042 cand.PlugSnapDeclaration = plugDecl2 1043 err = cand.Check() 1044 c.Check(err, IsNil) 1045 1046 // same publisher, different content 1047 cand = s.connectCand(c, "stuff", ` 1048 name: slot-snap 1049 version: 0 1050 slots: 1051 stuff: 1052 interface: content 1053 content: mk1 1054 `, ` 1055 name: plug-snap 1056 version: 0 1057 plugs: 1058 stuff: 1059 interface: content 1060 content: mk2 1061 `) 1062 cand.SlotSnapDeclaration = slotDecl1 1063 cand.PlugSnapDeclaration = plugDecl1 1064 err = cand.Check() 1065 c.Check(err, NotNil) 1066 } 1067 1068 func (s *baseDeclSuite) TestComposeBaseDeclaration(c *C) { 1069 decl, err := policy.ComposeBaseDeclaration(nil) 1070 c.Assert(err, IsNil) 1071 c.Assert(string(decl), testutil.Contains, ` 1072 type: base-declaration 1073 authority-id: canonical 1074 series: 16 1075 revision: 0 1076 `) 1077 } 1078 1079 func (s *baseDeclSuite) TestDoesNotPanic(c *C) { 1080 // In case there are any issues in the actual interfaces we'd get a panic 1081 // on snapd startup. This test prevents this from happing unnoticed. 1082 _, err := policy.ComposeBaseDeclaration(builtin.Interfaces()) 1083 c.Assert(err, IsNil) 1084 } 1085 1086 func (s *baseDeclSuite) TestBrowserSupportAllowSandbox(c *C) { 1087 const plugYaml = `name: plug-snap 1088 version: 0 1089 plugs: 1090 browser-support: 1091 allow-sandbox: true 1092 ` 1093 cand := s.connectCand(c, "browser-support", "", plugYaml) 1094 err := cand.Check() 1095 c.Check(err, NotNil) 1096 1097 _, err = cand.CheckAutoConnect() 1098 c.Check(err, NotNil) 1099 } 1100 1101 func (s *baseDeclSuite) TestOpticalDriveWrite(c *C) { 1102 type options struct { 1103 readonlyYamls []string 1104 writableYamls []string 1105 } 1106 1107 opts := &options{ 1108 readonlyYamls: []string{ 1109 // Non-specified "write" attribute 1110 `name: plug-snap 1111 version: 0 1112 plugs: 1113 optical-drive: null 1114 `, 1115 // Undefined "write" attribute 1116 `name: plug-snap 1117 version: 0 1118 plugs: 1119 optical-drive: {} 1120 `, 1121 // False "write" attribute 1122 `name: plug-snap 1123 version: 0 1124 plugs: 1125 optical-drive: 1126 write: false 1127 `, 1128 }, 1129 writableYamls: []string{ 1130 // True "write" attribute 1131 `name: plug-snap 1132 version: 0 1133 plugs: 1134 optical-drive: 1135 write: true 1136 `, 1137 }, 1138 } 1139 1140 checkOpticalDriveAutoConnect := func(plugYaml string, checker Checker) { 1141 cand := s.connectCand(c, "optical-drive", "", plugYaml) 1142 err := cand.Check() 1143 c.Check(err, checker) 1144 _, err = cand.CheckAutoConnect() 1145 c.Check(err, checker) 1146 } 1147 1148 for _, plugYaml := range opts.readonlyYamls { 1149 checkOpticalDriveAutoConnect(plugYaml, IsNil) 1150 } 1151 for _, plugYaml := range opts.writableYamls { 1152 checkOpticalDriveAutoConnect(plugYaml, NotNil) 1153 } 1154 } 1155 1156 func (s *baseDeclSuite) TestRawVolumeOverride(c *C) { 1157 slotYaml := `name: slot-snap 1158 type: gadget 1159 version: 0 1160 slots: 1161 raw-volume: 1162 path: /dev/mmcblk0p1 1163 ` 1164 slotSnap := snaptest.MockInfo(c, slotYaml, nil) 1165 // mock a well-formed slot snap decl with SnapID 1166 slotSnapDecl := s.mockSnapDecl(c, "slot-snap", "slotsnapidididididididididididid", "canonical", "") 1167 1168 plugYaml := `name: plug-snap 1169 version: 0 1170 plugs: 1171 raw-volume: 1172 ` 1173 plugSnap := snaptest.MockInfo(c, plugYaml, nil) 1174 1175 // no plug-side declaration 1176 cand := &policy.ConnectCandidate{ 1177 Plug: interfaces.NewConnectedPlug(plugSnap.Plugs["raw-volume"], nil, nil), 1178 Slot: interfaces.NewConnectedSlot(slotSnap.Slots["raw-volume"], nil, nil), 1179 SlotSnapDeclaration: slotSnapDecl, 1180 BaseDeclaration: s.baseDecl, 1181 } 1182 1183 err := cand.Check() 1184 c.Check(err, NotNil) 1185 c.Assert(err, ErrorMatches, "connection denied by slot rule of interface \"raw-volume\"") 1186 _, err = cand.CheckAutoConnect() 1187 c.Check(err, NotNil) 1188 c.Assert(err, ErrorMatches, "auto-connection denied by slot rule of interface \"raw-volume\"") 1189 1190 // specific plug-side declaration for connection only 1191 plugsOverride := ` 1192 plugs: 1193 raw-volume: 1194 allow-connection: 1195 slot-snap-id: 1196 - slotsnapidididididididididididid 1197 allow-auto-connection: false 1198 ` 1199 plugSnapDecl := s.mockSnapDecl(c, "plug-snap", "plugsnapidididididididididididid", "canonical", plugsOverride) 1200 cand.PlugSnapDeclaration = plugSnapDecl 1201 err = cand.Check() 1202 c.Check(err, IsNil) 1203 _, err = cand.CheckAutoConnect() 1204 c.Check(err, NotNil) 1205 c.Assert(err, ErrorMatches, "auto-connection not allowed by plug rule of interface \"raw-volume\" for \"plug-snap\" snap") 1206 1207 // specific plug-side declaration for connection and auto-connection 1208 plugsOverride = ` 1209 plugs: 1210 raw-volume: 1211 allow-connection: 1212 slot-snap-id: 1213 - slotsnapidididididididididididid 1214 allow-auto-connection: 1215 slot-snap-id: 1216 - slotsnapidididididididididididid 1217 ` 1218 plugSnapDecl = s.mockSnapDecl(c, "plug-snap", "plugsnapidididididididididididid", "canonical", plugsOverride) 1219 cand.PlugSnapDeclaration = plugSnapDecl 1220 err = cand.Check() 1221 c.Check(err, IsNil) 1222 arity, err := cand.CheckAutoConnect() 1223 c.Check(err, IsNil) 1224 c.Check(arity.SlotsPerPlugAny(), Equals, false) 1225 1226 // blanket allow for connection and auto-connection to any slotting snap 1227 plugsOverride = ` 1228 plugs: 1229 raw-volume: 1230 allow-connection: true 1231 allow-auto-connection: true 1232 ` 1233 plugSnapDecl = s.mockSnapDecl(c, "some-snap", "plugsnapidididididididididididid", "canonical", plugsOverride) 1234 cand.PlugSnapDeclaration = plugSnapDecl 1235 err = cand.Check() 1236 c.Check(err, IsNil) 1237 arity, err = cand.CheckAutoConnect() 1238 c.Check(err, IsNil) 1239 c.Check(arity.SlotsPerPlugAny(), Equals, false) 1240 } 1241 1242 func (s *baseDeclSuite) TestAutoConnectionDesktopLaunchOverride(c *C) { 1243 cand := s.connectCand(c, "desktop-launch", "", "") 1244 _, err := cand.CheckAutoConnect() 1245 c.Check(err, NotNil) 1246 c.Assert(err, ErrorMatches, "auto-connection denied by plug rule of interface \"desktop-launch\"") 1247 1248 plugsSlots := ` 1249 plugs: 1250 desktop-launch: 1251 allow-auto-connection: true 1252 ` 1253 1254 snapDecl := s.mockSnapDecl(c, "some-snap", "some-snap-with-desktop-launch-id", "canonical", plugsSlots) 1255 cand.PlugSnapDeclaration = snapDecl 1256 _, err = cand.CheckAutoConnect() 1257 c.Check(err, IsNil) 1258 }