github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/ifacestate/helpers.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2020 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 ifacestate 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 "os" 27 "sort" 28 "strings" 29 30 "github.com/snapcore/snapd/asserts" 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/interfaces" 33 "github.com/snapcore/snapd/interfaces/backends" 34 "github.com/snapcore/snapd/interfaces/builtin" 35 "github.com/snapcore/snapd/interfaces/policy" 36 "github.com/snapcore/snapd/interfaces/utils" 37 "github.com/snapcore/snapd/jsonutil" 38 "github.com/snapcore/snapd/logger" 39 "github.com/snapcore/snapd/overlord/assertstate" 40 "github.com/snapcore/snapd/overlord/snapstate" 41 "github.com/snapcore/snapd/overlord/state" 42 "github.com/snapcore/snapd/snap" 43 "github.com/snapcore/snapd/timings" 44 ) 45 46 func (m *InterfaceManager) selectInterfaceMapper(snaps []*snap.Info) { 47 for _, snapInfo := range snaps { 48 if snapInfo.Type() == snap.TypeSnapd { 49 mapper = &CoreSnapdSystemMapper{} 50 break 51 } 52 } 53 } 54 55 func (m *InterfaceManager) addInterfaces(extra []interfaces.Interface) error { 56 for _, iface := range builtin.Interfaces() { 57 if err := m.repo.AddInterface(iface); err != nil { 58 return err 59 } 60 } 61 for _, iface := range extra { 62 if err := m.repo.AddInterface(iface); err != nil { 63 return err 64 } 65 } 66 return nil 67 } 68 69 func (m *InterfaceManager) addBackends(extra []interfaces.SecurityBackend) error { 70 opts := interfaces.SecurityBackendOptions{Preseed: m.preseed} 71 for _, backend := range backends.All { 72 if err := backend.Initialize(&opts); err != nil { 73 return err 74 } 75 if err := m.repo.AddBackend(backend); err != nil { 76 return err 77 } 78 } 79 for _, backend := range extra { 80 if err := backend.Initialize(&opts); err != nil { 81 return err 82 } 83 if err := m.repo.AddBackend(backend); err != nil { 84 return err 85 } 86 } 87 return nil 88 } 89 90 func (m *InterfaceManager) addSnaps(snaps []*snap.Info) error { 91 for _, snapInfo := range snaps { 92 if err := addImplicitSlots(m.state, snapInfo); err != nil { 93 return err 94 } 95 if err := m.repo.AddSnap(snapInfo); err != nil { 96 logger.Noticef("cannot add snap %q to interface repository: %s", snapInfo.InstanceName(), err) 97 } 98 } 99 return nil 100 } 101 102 func profilesNeedRegenerationImpl() bool { 103 mismatch, err := interfaces.SystemKeyMismatch() 104 if err != nil { 105 logger.Noticef("error trying to compare the snap system key: %v", err) 106 return true 107 } 108 return mismatch 109 } 110 111 var profilesNeedRegeneration = profilesNeedRegenerationImpl 112 var writeSystemKey = interfaces.WriteSystemKey 113 114 // regenerateAllSecurityProfiles will regenerate all security profiles. 115 func (m *InterfaceManager) regenerateAllSecurityProfiles(tm timings.Measurer) error { 116 // Get all the security backends 117 securityBackends := m.repo.Backends() 118 119 // Get all the snap infos 120 snaps, err := snapsWithSecurityProfiles(m.state) 121 if err != nil { 122 return err 123 } 124 125 // Add implicit slots to all snaps 126 for _, snapInfo := range snaps { 127 if err := addImplicitSlots(m.state, snapInfo); err != nil { 128 return err 129 } 130 } 131 132 // The reason the system key is unlinked is to prevent snapd from believing 133 // that an old system key is valid and represents security setup 134 // established in the system. If snapd is reverted following a failed 135 // startup then system key may match the system key that used to be on disk 136 // but some of the system security may have been changed by the new snapd, 137 // the one that was reverted. Unlinking avoids such possibility, forcing 138 // old snapd to re-establish proper security view. 139 shouldWriteSystemKey := true 140 os.Remove(dirs.SnapSystemKeyFile) 141 142 confinementOpts := func(snapName string) interfaces.ConfinementOptions { 143 var snapst snapstate.SnapState 144 if err := snapstate.Get(m.state, snapName, &snapst); err != nil { 145 logger.Noticef("cannot get state of snap %q: %s", snapName, err) 146 } 147 return confinementOptions(snapst.Flags) 148 } 149 150 // For each backend: 151 for _, backend := range securityBackends { 152 if backend.Name() == "" { 153 continue // Test backends have no name, skip them to simplify testing. 154 } 155 if errors := interfaces.SetupMany(m.repo, backend, snaps, confinementOpts, tm); len(errors) > 0 { 156 logger.Noticef("cannot regenerate %s profiles", backend.Name()) 157 for _, err := range errors { 158 logger.Noticef(err.Error()) 159 } 160 shouldWriteSystemKey = false 161 } 162 } 163 164 if shouldWriteSystemKey { 165 if err := writeSystemKey(); err != nil { 166 logger.Noticef("cannot write system key: %v", err) 167 } 168 } 169 return nil 170 } 171 172 // renameCorePlugConnection renames one connection from "core-support" plug to 173 // slot so that the plug name is "core-support-plug" while the slot is 174 // unchanged. This matches a change introduced in 2.24, where the core snap no 175 // longer has the "core-support" plug as that was clashing with the slot with 176 // the same name. 177 func (m *InterfaceManager) renameCorePlugConnection() error { 178 conns, err := getConns(m.state) 179 if err != nil { 180 return err 181 } 182 const oldPlugName = "core-support" 183 const newPlugName = "core-support-plug" 184 // old connection, note that slotRef is the same in both 185 slotRef := interfaces.SlotRef{Snap: "core", Name: oldPlugName} 186 oldPlugRef := interfaces.PlugRef{Snap: "core", Name: oldPlugName} 187 oldConnRef := interfaces.ConnRef{PlugRef: oldPlugRef, SlotRef: slotRef} 188 oldID := oldConnRef.ID() 189 // if the old connection is saved, replace it with the new connection 190 if cState, ok := conns[oldID]; ok { 191 newPlugRef := interfaces.PlugRef{Snap: "core", Name: newPlugName} 192 newConnRef := interfaces.ConnRef{PlugRef: newPlugRef, SlotRef: slotRef} 193 newID := newConnRef.ID() 194 delete(conns, oldID) 195 conns[newID] = cState 196 setConns(m.state, conns) 197 } 198 return nil 199 } 200 201 // removeStaleConnections removes stale connections left by some older versions of snapd. 202 // Connection is considered stale if the snap on either end of the connection doesn't exist anymore. 203 // XXX: this code should eventually go away. 204 var removeStaleConnections = func(st *state.State) error { 205 conns, err := getConns(st) 206 if err != nil { 207 return err 208 } 209 var staleConns []string 210 for id := range conns { 211 connRef, err := interfaces.ParseConnRef(id) 212 if err != nil { 213 return err 214 } 215 var snapst snapstate.SnapState 216 if err := snapstate.Get(st, connRef.PlugRef.Snap, &snapst); err != nil { 217 if err != state.ErrNoState { 218 return err 219 } 220 staleConns = append(staleConns, id) 221 continue 222 } 223 if err := snapstate.Get(st, connRef.SlotRef.Snap, &snapst); err != nil { 224 if err != state.ErrNoState { 225 return err 226 } 227 staleConns = append(staleConns, id) 228 continue 229 } 230 } 231 if len(staleConns) > 0 { 232 for _, id := range staleConns { 233 delete(conns, id) 234 } 235 setConns(st, conns) 236 logger.Noticef("removed stale connections: %s", strings.Join(staleConns, ", ")) 237 } 238 return nil 239 } 240 241 // reloadConnections reloads connections stored in the state in the repository. 242 // Using non-empty snapName the operation can be scoped to connections 243 // affecting a given snap. 244 // 245 // The return value is the list of affected snap names. 246 func (m *InterfaceManager) reloadConnections(snapName string) ([]string, error) { 247 conns, err := getConns(m.state) 248 if err != nil { 249 return nil, err 250 } 251 252 connStateChanged := false 253 affected := make(map[string]bool) 254 for connId, connState := range conns { 255 // Skip entries that just mark a connection as undesired. Those don't 256 // carry attributes that can go stale. In the same spirit, skip 257 // information about hotplug connections that don't have the associated 258 // hotplug hardware. 259 if connState.Undesired || connState.HotplugGone { 260 continue 261 } 262 connRef, err := interfaces.ParseConnRef(connId) 263 if err != nil { 264 return nil, err 265 } 266 // Apply filtering, this allows us to reload only a subset of 267 // connections (and similarly, refresh the static attributes of only a 268 // subset of connections). 269 if snapName != "" && connRef.PlugRef.Snap != snapName && connRef.SlotRef.Snap != snapName { 270 continue 271 } 272 273 plugInfo := m.repo.Plug(connRef.PlugRef.Snap, connRef.PlugRef.Name) 274 slotInfo := m.repo.Slot(connRef.SlotRef.Snap, connRef.SlotRef.Name) 275 276 // The connection refers to a plug or slot that doesn't exist anymore, e.g. because of a refresh 277 // to a new snap revision that doesn't have the given plug/slot. 278 if plugInfo == nil || slotInfo == nil { 279 // automatic connection can simply be removed (it will be re-created automatically if needed) 280 // as long as it wasn't disconnected manually; note that undesired flag is taken care of at 281 // the beginning of the loop. 282 if connState.Auto && !connState.ByGadget && connState.Interface != "core-support" { 283 delete(conns, connId) 284 connStateChanged = true 285 } 286 // otherwise keep it and silently ignore, e.g. in case of a revert. 287 continue 288 } 289 290 var updateStaticAttrs bool 291 staticPlugAttrs := connState.StaticPlugAttrs 292 staticSlotAttrs := connState.StaticSlotAttrs 293 294 // XXX: Refresh the copy of the static connection attributes for "content" interface as long 295 // as its "content" attribute 296 // This is a partial and temporary solution to https://bugs.launchpad.net/snapd/+bug/1825883 297 if plugInfo.Interface == "content" { 298 var plugContent, slotContent string 299 plugInfo.Attr("content", &plugContent) 300 slotInfo.Attr("content", &slotContent) 301 302 if plugContent != "" && plugContent == slotContent { 303 staticPlugAttrs = utils.NormalizeInterfaceAttributes(plugInfo.Attrs).(map[string]interface{}) 304 staticSlotAttrs = utils.NormalizeInterfaceAttributes(slotInfo.Attrs).(map[string]interface{}) 305 updateStaticAttrs = true 306 } else { 307 logger.Noticef("cannot refresh static attributes of the connection %q", connId) 308 } 309 } 310 311 // Note: reloaded connections are not checked against policy again, and also we don't call BeforeConnect* methods on them. 312 if _, err := m.repo.Connect(connRef, staticPlugAttrs, connState.DynamicPlugAttrs, staticSlotAttrs, connState.DynamicSlotAttrs, nil); err != nil { 313 logger.Noticef("%s", err) 314 } else { 315 // If the connection succeeded update the connection state and keep 316 // track of the snaps that were affected. 317 affected[connRef.PlugRef.Snap] = true 318 affected[connRef.SlotRef.Snap] = true 319 320 if updateStaticAttrs { 321 connState.StaticPlugAttrs = staticPlugAttrs 322 connState.StaticSlotAttrs = staticSlotAttrs 323 connStateChanged = true 324 } 325 } 326 } 327 if connStateChanged { 328 setConns(m.state, conns) 329 } 330 331 result := make([]string, 0, len(affected)) 332 for name := range affected { 333 result = append(result, name) 334 } 335 return result, nil 336 } 337 338 // removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap 339 // has no connections in the state. State must be locked by the caller. 340 func (m *InterfaceManager) removeConnections(snapName string) error { 341 conns, err := getConns(m.state) 342 if err != nil { 343 return err 344 } 345 for id := range conns { 346 connRef, err := interfaces.ParseConnRef(id) 347 if err != nil { 348 return err 349 } 350 if connRef.PlugRef.Snap == snapName || connRef.SlotRef.Snap == snapName { 351 return fmt.Errorf("internal error: cannot remove connections of snap %s from the repository while its connections are present in the state", snapName) 352 } 353 } 354 355 repoConns, err := m.repo.Connections(snapName) 356 if err != nil { 357 return fmt.Errorf("internal error: %v", err) 358 } 359 for _, conn := range repoConns { 360 if err := m.repo.Disconnect(conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name); err != nil { 361 return fmt.Errorf("internal error: %v", err) 362 } 363 } 364 return nil 365 } 366 367 func (m *InterfaceManager) setupSecurityByBackend(task *state.Task, snaps []*snap.Info, opts []interfaces.ConfinementOptions, tm timings.Measurer) error { 368 if len(snaps) != len(opts) { 369 return fmt.Errorf("internal error: setupSecurityByBackend received an unexpected number of snaps (expected: %d, got %d)", len(opts), len(snaps)) 370 } 371 confOpts := make(map[string]interfaces.ConfinementOptions, len(snaps)) 372 for i, snapInfo := range snaps { 373 confOpts[snapInfo.InstanceName()] = opts[i] 374 } 375 376 st := task.State() 377 st.Unlock() 378 defer st.Lock() 379 380 // Setup all affected snaps, start with the most important security 381 // backend and run it for all snaps. See LP: 1802581 382 for _, backend := range m.repo.Backends() { 383 errs := interfaces.SetupMany(m.repo, backend, snaps, func(snapName string) interfaces.ConfinementOptions { 384 return confOpts[snapName] 385 }, tm) 386 if len(errs) > 0 { 387 // SetupMany processes all profiles and returns all encountered errors; report just the first one 388 return errs[0] 389 } 390 } 391 392 return nil 393 } 394 395 func (m *InterfaceManager) setupSnapSecurity(task *state.Task, snapInfo *snap.Info, opts interfaces.ConfinementOptions, tm timings.Measurer) error { 396 return m.setupSecurityByBackend(task, []*snap.Info{snapInfo}, []interfaces.ConfinementOptions{opts}, tm) 397 } 398 399 func (m *InterfaceManager) removeSnapSecurity(task *state.Task, instanceName string) error { 400 st := task.State() 401 for _, backend := range m.repo.Backends() { 402 st.Unlock() 403 err := backend.Remove(instanceName) 404 st.Lock() 405 if err != nil { 406 task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), instanceName, err) 407 return err 408 } 409 } 410 return nil 411 } 412 413 func addHotplugSlot(st *state.State, repo *interfaces.Repository, stateSlots map[string]*HotplugSlotInfo, iface interfaces.Interface, slot *snap.SlotInfo) error { 414 if slot.HotplugKey == "" { 415 return fmt.Errorf("internal error: cannot store slot %q, not a hotplug slot", slot.Name) 416 } 417 if iface, ok := iface.(interfaces.SlotSanitizer); ok { 418 if err := iface.BeforePrepareSlot(slot); err != nil { 419 return fmt.Errorf("cannot sanitize hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err) 420 } 421 } 422 423 if err := repo.AddSlot(slot); err != nil { 424 return fmt.Errorf("cannot add hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err) 425 } 426 427 stateSlots[slot.Name] = &HotplugSlotInfo{ 428 Name: slot.Name, 429 Interface: slot.Interface, 430 StaticAttrs: slot.Attrs, 431 HotplugKey: slot.HotplugKey, 432 HotplugGone: false, 433 } 434 setHotplugSlots(st, stateSlots) 435 logger.Debugf("added hotplug slot %s:%s of interface %s, hotplug key %q", slot.Snap.InstanceName(), slot.Name, slot.Interface, slot.HotplugKey) 436 return nil 437 } 438 439 type connState struct { 440 Auto bool `json:"auto,omitempty"` 441 ByGadget bool `json:"by-gadget,omitempty"` 442 Interface string `json:"interface,omitempty"` 443 // Undesired tracks connections that were manually disconnected after being auto-connected, 444 // so that they are not automatically reconnected again in the future. 445 Undesired bool `json:"undesired,omitempty"` 446 StaticPlugAttrs map[string]interface{} `json:"plug-static,omitempty"` 447 DynamicPlugAttrs map[string]interface{} `json:"plug-dynamic,omitempty"` 448 StaticSlotAttrs map[string]interface{} `json:"slot-static,omitempty"` 449 DynamicSlotAttrs map[string]interface{} `json:"slot-dynamic,omitempty"` 450 // Hotplug-related attributes: HotplugGone indicates a connection that 451 // disappeared because the device was removed, but may potentially be 452 // restored in the future if we see the device again. HotplugKey is the 453 // key of the associated device; it's empty for connections of regular 454 // slots. 455 HotplugGone bool `json:"hotplug-gone,omitempty"` 456 HotplugKey snap.HotplugKey `json:"hotplug-key,omitempty"` 457 } 458 459 type gadgetConnect struct { 460 st *state.State 461 task *state.Task 462 repo *interfaces.Repository 463 464 instanceName string 465 466 deviceCtx snapstate.DeviceContext 467 } 468 469 func newGadgetConnect(s *state.State, task *state.Task, repo *interfaces.Repository, instanceName string, deviceCtx snapstate.DeviceContext) *gadgetConnect { 470 return &gadgetConnect{ 471 st: s, 472 task: task, 473 repo: repo, 474 instanceName: instanceName, 475 deviceCtx: deviceCtx, 476 } 477 } 478 479 // addGadgetConnections adds to newconns any applicable connections 480 // from the gadget connections stanza. 481 // conflictError is called to handle checkAutoconnectConflicts errors. 482 func (gc *gadgetConnect) addGadgetConnections(newconns map[string]*interfaces.ConnRef, conns map[string]*connState, conflictError func(*state.Retry, error) error) error { 483 var seeded bool 484 err := gc.st.Get("seeded", &seeded) 485 if err != nil && err != state.ErrNoState { 486 return err 487 } 488 // we apply gadget connections only during seeding or a remodeling 489 if seeded && !gc.deviceCtx.ForRemodeling() { 490 return nil 491 } 492 493 task := gc.task 494 snapName := gc.instanceName 495 496 var snapst snapstate.SnapState 497 if err := snapstate.Get(gc.st, snapName, &snapst); err != nil { 498 return err 499 } 500 501 snapInfo, err := snapst.CurrentInfo() 502 if err != nil { 503 return err 504 } 505 snapID := snapInfo.SnapID 506 if snapID == "" { 507 // not a snap-id identifiable snap, skip 508 return nil 509 } 510 511 gconns, err := snapstate.GadgetConnections(gc.st, gc.deviceCtx) 512 if err != nil { 513 if err == state.ErrNoState { 514 // no gadget yet, nothing to do 515 return nil 516 } 517 return err 518 } 519 520 // consider the gadget connect instructions 521 for _, gconn := range gconns { 522 var plugSnapName, slotSnapName string 523 if gconn.Plug.SnapID == snapID { 524 plugSnapName = snapName 525 } 526 if gconn.Slot.SnapID == snapID { 527 slotSnapName = snapName 528 } 529 530 if plugSnapName == "" && slotSnapName == "" { 531 // no match, nothing to do 532 continue 533 } 534 535 if plugSnapName == "" { 536 var err error 537 plugSnapName, err = resolveSnapIDToName(gc.st, gconn.Plug.SnapID) 538 if err != nil { 539 return err 540 } 541 } 542 plug := gc.repo.Plug(plugSnapName, gconn.Plug.Plug) 543 if plug == nil { 544 task.Logf("gadget connections: ignoring missing plug %s:%s", gconn.Plug.SnapID, gconn.Plug.Plug) 545 continue 546 } 547 548 if slotSnapName == "" { 549 var err error 550 slotSnapName, err = resolveSnapIDToName(gc.st, gconn.Slot.SnapID) 551 if err != nil { 552 return err 553 } 554 } 555 slot := gc.repo.Slot(slotSnapName, gconn.Slot.Slot) 556 if slot == nil { 557 task.Logf("gadget connections: ignoring missing slot %s:%s", gconn.Slot.SnapID, gconn.Slot.Slot) 558 continue 559 } 560 561 if err := addNewConnection(gc.st, task, newconns, conns, plug, slot, conflictError); err != nil { 562 return err 563 } 564 } 565 566 return nil 567 } 568 569 func addNewConnection(st *state.State, task *state.Task, newconns map[string]*interfaces.ConnRef, conns map[string]*connState, plug *snap.PlugInfo, slot *snap.SlotInfo, conflictError func(*state.Retry, error) error) error { 570 connRef := interfaces.NewConnRef(plug, slot) 571 key := connRef.ID() 572 if _, ok := conns[key]; ok { 573 // Suggested connection already exist (or has 574 // Undesired flag set) so don't clobber it. 575 // NOTE: we don't log anything here as this is 576 // a normal and common condition. 577 return nil 578 } 579 if _, ok := newconns[key]; ok { 580 return nil 581 } 582 583 if task.Kind() == "auto-connect" { 584 ignore, err := findSymmetricAutoconnectTask(st, plug.Snap.InstanceName(), slot.Snap.InstanceName(), task) 585 if err != nil { 586 return err 587 } 588 589 if ignore { 590 return nil 591 } 592 } 593 594 if err := checkAutoconnectConflicts(st, task, plug.Snap.InstanceName(), slot.Snap.InstanceName()); err != nil { 595 retry, _ := err.(*state.Retry) 596 return conflictError(retry, err) 597 } 598 599 newconns[key] = connRef 600 return nil 601 } 602 603 type autoConnectChecker struct { 604 st *state.State 605 task *state.Task 606 repo *interfaces.Repository 607 608 deviceCtx snapstate.DeviceContext 609 cache map[string]*asserts.SnapDeclaration 610 baseDecl *asserts.BaseDeclaration 611 } 612 613 func newAutoConnectChecker(s *state.State, task *state.Task, repo *interfaces.Repository, deviceCtx snapstate.DeviceContext) (*autoConnectChecker, error) { 614 baseDecl, err := assertstate.BaseDeclaration(s) 615 if err != nil { 616 return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err) 617 } 618 return &autoConnectChecker{ 619 st: s, 620 task: task, 621 repo: repo, 622 deviceCtx: deviceCtx, 623 cache: make(map[string]*asserts.SnapDeclaration), 624 baseDecl: baseDecl, 625 }, nil 626 } 627 628 func (c *autoConnectChecker) snapDeclaration(snapID string) (*asserts.SnapDeclaration, error) { 629 snapDecl := c.cache[snapID] 630 if snapDecl != nil { 631 return snapDecl, nil 632 } 633 snapDecl, err := assertstate.SnapDeclaration(c.st, snapID) 634 if err != nil { 635 return nil, err 636 } 637 c.cache[snapID] = snapDecl 638 return snapDecl, nil 639 } 640 641 func (c *autoConnectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, interfaces.SideArity, error) { 642 modelAs := c.deviceCtx.Model() 643 644 var storeAs *asserts.Store 645 if modelAs.Store() != "" { 646 var err error 647 storeAs, err = assertstate.Store(c.st, modelAs.Store()) 648 if err != nil && !asserts.IsNotFound(err) { 649 return false, nil, err 650 } 651 } 652 653 var plugDecl *asserts.SnapDeclaration 654 if plug.Snap().SnapID != "" { 655 var err error 656 plugDecl, err = c.snapDeclaration(plug.Snap().SnapID) 657 if err != nil { 658 logger.Noticef("error: cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err) 659 return false, nil, nil 660 } 661 } 662 663 var slotDecl *asserts.SnapDeclaration 664 if slot.Snap().SnapID != "" { 665 var err error 666 slotDecl, err = c.snapDeclaration(slot.Snap().SnapID) 667 if err != nil { 668 logger.Noticef("error: cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err) 669 return false, nil, nil 670 } 671 } 672 673 // check the connection against the declarations' rules 674 ic := policy.ConnectCandidate{ 675 Plug: plug, 676 PlugSnapDeclaration: plugDecl, 677 Slot: slot, 678 SlotSnapDeclaration: slotDecl, 679 BaseDeclaration: c.baseDecl, 680 Model: modelAs, 681 Store: storeAs, 682 } 683 684 arity, err := ic.CheckAutoConnect() 685 if err == nil { 686 return true, arity, nil 687 } 688 689 return false, nil, nil 690 } 691 692 // filterUbuntuCoreSlots filters out any ubuntu-core slots, 693 // if there are both ubuntu-core and core slots. This would occur 694 // during a ubuntu-core -> core transition. 695 func filterUbuntuCoreSlots(candidates []*snap.SlotInfo, arities []interfaces.SideArity) ([]*snap.SlotInfo, []interfaces.SideArity) { 696 hasCore := false 697 hasUbuntuCore := false 698 var withoutUbuntuCore []*snap.SlotInfo 699 var withoutUbuntuCoreArities []interfaces.SideArity 700 for i, candSlot := range candidates { 701 switch candSlot.Snap.InstanceName() { 702 case "ubuntu-core": 703 if !hasUbuntuCore { 704 hasUbuntuCore = true 705 withoutUbuntuCore = append(withoutUbuntuCore, candidates[:i]...) 706 withoutUbuntuCoreArities = append(withoutUbuntuCoreArities, arities[:i]...) 707 } 708 case "core": 709 hasCore = true 710 fallthrough 711 default: 712 if hasUbuntuCore { 713 withoutUbuntuCore = append(withoutUbuntuCore, candSlot) 714 withoutUbuntuCoreArities = append(withoutUbuntuCoreArities, arities[i]) 715 } 716 } 717 } 718 if hasCore && hasUbuntuCore { 719 candidates = withoutUbuntuCore 720 arities = withoutUbuntuCoreArities 721 } 722 return candidates, arities 723 } 724 725 // addAutoConnections adds to newconns any applicable auto-connections 726 // from the given plugs to corresponding candidates slots after 727 // filtering them with optional filter and against preexisting 728 // conns. cannotAutoConnectLog is called to build a log message in 729 // case no applicable pair was found. conflictError is called 730 // to handle checkAutoconnectConflicts errors. 731 func (c *autoConnectChecker) addAutoConnections(newconns map[string]*interfaces.ConnRef, plugs []*snap.PlugInfo, filter func([]*snap.SlotInfo) []*snap.SlotInfo, conns map[string]*connState, cannotAutoConnectLog func(plug *snap.PlugInfo, candRefs []string) string, conflictError func(*state.Retry, error) error) error { 732 for _, plug := range plugs { 733 candSlots, arities := c.repo.AutoConnectCandidateSlots(plug.Snap.InstanceName(), plug.Name, c.check) 734 735 if len(candSlots) == 0 { 736 continue 737 } 738 739 // If we are in a core transition we may have both the 740 // old ubuntu-core snap and the new core snap 741 // providing the same interface. In that situation we 742 // want to ignore any candidates in ubuntu-core and 743 // simply go with those from the new core snap. 744 candSlots, arities = filterUbuntuCoreSlots(candSlots, arities) 745 746 applicable := candSlots 747 // candidate arity check 748 for _, arity := range arities { 749 if !arity.SlotsPerPlugAny() { 750 // ATM not any (*) => none or exactly one 751 if len(candSlots) != 1 { 752 applicable = nil 753 } 754 break 755 } 756 } 757 758 if filter != nil { 759 applicable = filter(applicable) 760 } 761 762 if len(applicable) == 0 { 763 crefs := make([]string, len(candSlots)) 764 for i, candidate := range candSlots { 765 crefs[i] = candidate.String() 766 } 767 c.task.Logf(cannotAutoConnectLog(plug, crefs)) 768 continue 769 } 770 771 for _, slot := range applicable { 772 if err := addNewConnection(c.st, c.task, newconns, conns, plug, slot, conflictError); err != nil { 773 return err 774 } 775 } 776 } 777 778 return nil 779 } 780 781 type connectChecker struct { 782 st *state.State 783 deviceCtx snapstate.DeviceContext 784 baseDecl *asserts.BaseDeclaration 785 } 786 787 func newConnectChecker(s *state.State, deviceCtx snapstate.DeviceContext) (*connectChecker, error) { 788 baseDecl, err := assertstate.BaseDeclaration(s) 789 if err != nil { 790 return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err) 791 } 792 return &connectChecker{ 793 st: s, 794 deviceCtx: deviceCtx, 795 baseDecl: baseDecl, 796 }, nil 797 } 798 799 func (c *connectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, error) { 800 modelAs := c.deviceCtx.Model() 801 802 var storeAs *asserts.Store 803 if modelAs.Store() != "" { 804 var err error 805 storeAs, err = assertstate.Store(c.st, modelAs.Store()) 806 if err != nil && !asserts.IsNotFound(err) { 807 return false, err 808 } 809 } 810 811 var plugDecl *asserts.SnapDeclaration 812 if plug.Snap().SnapID != "" { 813 var err error 814 plugDecl, err = assertstate.SnapDeclaration(c.st, plug.Snap().SnapID) 815 if err != nil { 816 return false, fmt.Errorf("cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err) 817 } 818 } 819 820 var slotDecl *asserts.SnapDeclaration 821 if slot.Snap().SnapID != "" { 822 var err error 823 slotDecl, err = assertstate.SnapDeclaration(c.st, slot.Snap().SnapID) 824 if err != nil { 825 return false, fmt.Errorf("cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err) 826 } 827 } 828 829 // check the connection against the declarations' rules 830 ic := policy.ConnectCandidate{ 831 Plug: plug, 832 PlugSnapDeclaration: plugDecl, 833 Slot: slot, 834 SlotSnapDeclaration: slotDecl, 835 BaseDeclaration: c.baseDecl, 836 Model: modelAs, 837 Store: storeAs, 838 } 839 840 // if either of plug or slot snaps don't have a declaration it 841 // means they were installed with "dangerous", so the security 842 // check should be skipped at this point. 843 if plugDecl != nil && slotDecl != nil { 844 if err := ic.Check(); err != nil { 845 return false, err 846 } 847 } 848 return true, nil 849 } 850 851 func getPlugAndSlotRefs(task *state.Task) (interfaces.PlugRef, interfaces.SlotRef, error) { 852 var plugRef interfaces.PlugRef 853 var slotRef interfaces.SlotRef 854 if err := task.Get("plug", &plugRef); err != nil { 855 return plugRef, slotRef, err 856 } 857 if err := task.Get("slot", &slotRef); err != nil { 858 return plugRef, slotRef, err 859 } 860 return plugRef, slotRef, nil 861 } 862 863 // getConns returns information about connections from the state. 864 // 865 // Connections are transparently re-mapped according to remapIncomingConnRef 866 func getConns(st *state.State) (conns map[string]*connState, err error) { 867 var raw *json.RawMessage 868 err = st.Get("conns", &raw) 869 if err != nil && err != state.ErrNoState { 870 return nil, fmt.Errorf("cannot obtain raw data about existing connections: %s", err) 871 } 872 if raw != nil { 873 err = jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &conns) 874 if err != nil { 875 return nil, fmt.Errorf("cannot decode data about existing connections: %s", err) 876 } 877 } 878 if conns == nil { 879 conns = make(map[string]*connState) 880 } 881 remapped := make(map[string]*connState, len(conns)) 882 for id, cstate := range conns { 883 cref, err := interfaces.ParseConnRef(id) 884 if err != nil { 885 return nil, err 886 } 887 cref.PlugRef.Snap = RemapSnapFromState(cref.PlugRef.Snap) 888 cref.SlotRef.Snap = RemapSnapFromState(cref.SlotRef.Snap) 889 cstate.StaticSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticSlotAttrs).(map[string]interface{}) 890 cstate.DynamicSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicSlotAttrs).(map[string]interface{}) 891 cstate.StaticPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticPlugAttrs).(map[string]interface{}) 892 cstate.DynamicPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicPlugAttrs).(map[string]interface{}) 893 remapped[cref.ID()] = cstate 894 } 895 return remapped, nil 896 } 897 898 // setConns sets information about connections in the state. 899 // 900 // Connections are transparently re-mapped according to remapOutgoingConnRef 901 func setConns(st *state.State, conns map[string]*connState) { 902 remapped := make(map[string]*connState, len(conns)) 903 for id, cstate := range conns { 904 cref, err := interfaces.ParseConnRef(id) 905 if err != nil { 906 // We cannot fail here 907 panic(err) 908 } 909 cref.PlugRef.Snap = RemapSnapToState(cref.PlugRef.Snap) 910 cref.SlotRef.Snap = RemapSnapToState(cref.SlotRef.Snap) 911 remapped[cref.ID()] = cstate 912 } 913 st.Set("conns", remapped) 914 } 915 916 // snapsWithSecurityProfiles returns all snaps that have active 917 // security profiles: these are either snaps that are active, or about 918 // to be active (pending link-snap) with a done setup-profiles 919 func snapsWithSecurityProfiles(st *state.State) ([]*snap.Info, error) { 920 infos, err := snapstate.ActiveInfos(st) 921 if err != nil { 922 return nil, err 923 } 924 seen := make(map[string]bool, len(infos)) 925 for _, info := range infos { 926 seen[info.InstanceName()] = true 927 } 928 for _, t := range st.Tasks() { 929 if t.Kind() != "link-snap" || t.Status().Ready() { 930 continue 931 } 932 snapsup, err := snapstate.TaskSnapSetup(t) 933 if err != nil { 934 return nil, err 935 } 936 instanceName := snapsup.InstanceName() 937 if seen[instanceName] { 938 continue 939 } 940 941 doneProfiles := false 942 for _, t1 := range t.WaitTasks() { 943 if t1.Kind() == "setup-profiles" && t1.Status() == state.DoneStatus { 944 snapsup1, err := snapstate.TaskSnapSetup(t1) 945 if err != nil { 946 return nil, err 947 } 948 if snapsup1.InstanceName() == instanceName { 949 doneProfiles = true 950 break 951 } 952 } 953 } 954 if !doneProfiles { 955 continue 956 } 957 958 seen[instanceName] = true 959 snapInfo, err := snap.ReadInfo(instanceName, snapsup.SideInfo) 960 if err != nil { 961 logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err) 962 continue 963 } 964 infos = append(infos, snapInfo) 965 } 966 967 return infos, nil 968 } 969 970 func resolveSnapIDToName(st *state.State, snapID string) (name string, err error) { 971 if snapID == "system" { 972 // Resolve the system nickname to a concrete snap. 973 return mapper.RemapSnapFromRequest(snapID), nil 974 } 975 decl, err := assertstate.SnapDeclaration(st, snapID) 976 if asserts.IsNotFound(err) { 977 return "", nil 978 } 979 if err != nil { 980 return "", err 981 } 982 return decl.SnapName(), nil 983 } 984 985 // SnapMapper offers APIs for re-mapping snap names in interfaces and the 986 // configuration system. The mapper is designed to apply transformations around 987 // the edges of snapd (state interactions and API interactions) to offer one 988 // view on the inside of snapd and another view on the outside. 989 type SnapMapper interface { 990 // re-map functions for loading and saving objects in the state. 991 RemapSnapFromState(snapName string) string 992 RemapSnapToState(snapName string) string 993 // RamapSnapFromRequest can replace snap names in API requests. 994 // There is no corresponding mapping function for API responses anymore. 995 // The API responses always reflect the real system state. 996 RemapSnapFromRequest(snapName string) string 997 // Returns actual name of the system snap. 998 SystemSnapName() string 999 } 1000 1001 // IdentityMapper implements SnapMapper and performs no transformations at all. 1002 type IdentityMapper struct{} 1003 1004 // RemapSnapFromState doesn't change the snap name in any way. 1005 func (m *IdentityMapper) RemapSnapFromState(snapName string) string { 1006 return snapName 1007 } 1008 1009 // RemapSnapToState doesn't change the snap name in any way. 1010 func (m *IdentityMapper) RemapSnapToState(snapName string) string { 1011 return snapName 1012 } 1013 1014 // RemapSnapFromRequest doesn't change the snap name in any way. 1015 func (m *IdentityMapper) RemapSnapFromRequest(snapName string) string { 1016 return snapName 1017 } 1018 1019 // CoreCoreSystemMapper implements SnapMapper and makes implicit slots 1020 // appear to be on "core" in the state and in memory but as "system" in the API. 1021 // 1022 // NOTE: This mapper can be used to prepare, as an intermediate step, for the 1023 // transition to "snapd" mapper. Using it the state and API layer will look 1024 // exactly the same as with the "snapd" mapper. This can be used to make any 1025 // necessary adjustments the test suite. 1026 type CoreCoreSystemMapper struct { 1027 IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate. 1028 } 1029 1030 // RemapSnapFromRequest renames the "system" snap to the "core" snap. 1031 // 1032 // This allows us to accept connection and disconnection requests that 1033 // explicitly refer to "core" or using the "system" nickname. 1034 func (m *CoreCoreSystemMapper) RemapSnapFromRequest(snapName string) string { 1035 if snapName == "system" { 1036 return m.SystemSnapName() 1037 } 1038 return snapName 1039 } 1040 1041 // SystemSnapName returns actual name of the system snap. 1042 func (m *CoreCoreSystemMapper) SystemSnapName() string { 1043 return "core" 1044 } 1045 1046 // CoreSnapdSystemMapper implements SnapMapper and makes implicit slots 1047 // appear to be on "core" in the state and on "system" in the API while they 1048 // are on "snapd" in memory. 1049 type CoreSnapdSystemMapper struct { 1050 IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate. 1051 } 1052 1053 // RemapSnapFromState renames the "core" snap to the "snapd" snap. 1054 // 1055 // This allows modern snapd to load an old state that remembers connections 1056 // between slots on the "core" snap and other snaps. In memory we are actually 1057 // using "snapd" snap for hosting those slots and this lets us stay compatible. 1058 func (m *CoreSnapdSystemMapper) RemapSnapFromState(snapName string) string { 1059 if snapName == "core" { 1060 return m.SystemSnapName() 1061 } 1062 return snapName 1063 } 1064 1065 // RemapSnapToState renames the "snapd" snap to the "core" snap. 1066 // 1067 // This allows the state to stay backwards compatible as all the connections 1068 // seem to refer to the "core" snap, as in pre core{16,18} days where there was 1069 // only one core snap. 1070 func (m *CoreSnapdSystemMapper) RemapSnapToState(snapName string) string { 1071 if snapName == m.SystemSnapName() { 1072 return "core" 1073 } 1074 return snapName 1075 } 1076 1077 // RemapSnapFromRequest renames the "core" or "system" snaps to the "snapd" snap. 1078 // 1079 // This allows us to accept connection and disconnection requests that 1080 // explicitly refer to "core" or "system" even though we really want them to 1081 // refer to "snapd". Note that this is not fully symmetric with 1082 // RemapSnapToResponse as we explicitly always talk about "system" snap, 1083 // even if the request used "core". 1084 func (m *CoreSnapdSystemMapper) RemapSnapFromRequest(snapName string) string { 1085 if snapName == "system" || snapName == "core" { 1086 return m.SystemSnapName() 1087 } 1088 return snapName 1089 } 1090 1091 // SystemSnapName returns actual name of the system snap. 1092 func (m *CoreSnapdSystemMapper) SystemSnapName() string { 1093 return "snapd" 1094 } 1095 1096 // mapper contains the currently active snap mapper. 1097 var mapper SnapMapper = &CoreCoreSystemMapper{} 1098 1099 // MockSnapMapper mocks the currently used snap mapper. 1100 func MockSnapMapper(new SnapMapper) (restore func()) { 1101 old := mapper 1102 mapper = new 1103 return func() { mapper = old } 1104 } 1105 1106 // RemapSnapFromState renames a snap when loaded from state according to the current mapper. 1107 func RemapSnapFromState(snapName string) string { 1108 return mapper.RemapSnapFromState(snapName) 1109 } 1110 1111 // RemapSnapToState renames a snap when saving to state according to the current mapper. 1112 func RemapSnapToState(snapName string) string { 1113 return mapper.RemapSnapToState(snapName) 1114 } 1115 1116 // RemapSnapFromRequest renames a snap as received from an API request according to the current mapper. 1117 func RemapSnapFromRequest(snapName string) string { 1118 return mapper.RemapSnapFromRequest(snapName) 1119 } 1120 1121 // SystemSnapName returns actual name of the system snap. 1122 func SystemSnapName() string { 1123 return mapper.SystemSnapName() 1124 } 1125 1126 // systemSnapInfo returns current info for system snap. 1127 func systemSnapInfo(st *state.State) (*snap.Info, error) { 1128 return snapstate.CurrentInfo(st, SystemSnapName()) 1129 } 1130 1131 func connectDisconnectAffectedSnaps(t *state.Task) ([]string, error) { 1132 plugRef, slotRef, err := getPlugAndSlotRefs(t) 1133 if err != nil { 1134 return nil, fmt.Errorf("internal error: cannot obtain plug/slot data from task: %s", t.Summary()) 1135 } 1136 return []string{plugRef.Snap, slotRef.Snap}, nil 1137 } 1138 1139 func checkSystemSnapIsPresent(st *state.State) bool { 1140 st.Lock() 1141 defer st.Unlock() 1142 _, err := systemSnapInfo(st) 1143 return err == nil 1144 } 1145 1146 func setHotplugAttrs(task *state.Task, ifaceName string, hotplugKey snap.HotplugKey) { 1147 task.Set("interface", ifaceName) 1148 task.Set("hotplug-key", hotplugKey) 1149 } 1150 1151 func getHotplugAttrs(task *state.Task) (ifaceName string, hotplugKey snap.HotplugKey, err error) { 1152 if err = task.Get("interface", &ifaceName); err != nil { 1153 return "", "", fmt.Errorf("internal error: cannot get interface name from hotplug task: %s", err) 1154 } 1155 if err = task.Get("hotplug-key", &hotplugKey); err != nil { 1156 return "", "", fmt.Errorf("internal error: cannot get hotplug key from hotplug task: %s", err) 1157 } 1158 return ifaceName, hotplugKey, err 1159 } 1160 1161 func allocHotplugSeq(st *state.State) (int, error) { 1162 var seq int 1163 if err := st.Get("hotplug-seq", &seq); err != nil && err != state.ErrNoState { 1164 return 0, fmt.Errorf("internal error: cannot allocate hotplug sequence number: %s", err) 1165 } 1166 seq++ 1167 st.Set("hotplug-seq", seq) 1168 return seq, nil 1169 } 1170 1171 func isHotplugChange(chg *state.Change) bool { 1172 return strings.HasPrefix(chg.Kind(), "hotplug-") 1173 } 1174 1175 func getHotplugChangeAttrs(chg *state.Change) (seq int, hotplugKey snap.HotplugKey, err error) { 1176 if err = chg.Get("hotplug-key", &hotplugKey); err != nil { 1177 return 0, "", fmt.Errorf("internal error: hotplug-key not set on change %q", chg.Kind()) 1178 } 1179 if err = chg.Get("hotplug-seq", &seq); err != nil { 1180 return 0, "", fmt.Errorf("internal error: hotplug-seq not set on change %q", chg.Kind()) 1181 } 1182 return seq, hotplugKey, nil 1183 } 1184 1185 func setHotplugChangeAttrs(chg *state.Change, seq int, hotplugKey snap.HotplugKey) { 1186 chg.Set("hotplug-seq", seq) 1187 chg.Set("hotplug-key", hotplugKey) 1188 } 1189 1190 // addHotplugSeqWaitTask sets mandatory hotplug attributes on the hotplug change, adds "hotplug-seq-wait" task 1191 // and makes all existing tasks of the change wait for it. 1192 func addHotplugSeqWaitTask(hotplugChange *state.Change, hotplugKey snap.HotplugKey, hotplugSeq int) { 1193 st := hotplugChange.State() 1194 setHotplugChangeAttrs(hotplugChange, hotplugSeq, hotplugKey) 1195 seqControl := st.NewTask("hotplug-seq-wait", fmt.Sprintf("Serialize hotplug change for hotplug key %q", hotplugKey)) 1196 tss := state.NewTaskSet(hotplugChange.Tasks()...) 1197 tss.WaitFor(seqControl) 1198 hotplugChange.AddTask(seqControl) 1199 } 1200 1201 type HotplugSlotInfo struct { 1202 Name string `json:"name"` 1203 Interface string `json:"interface"` 1204 StaticAttrs map[string]interface{} `json:"static-attrs,omitempty"` 1205 HotplugKey snap.HotplugKey `json:"hotplug-key"` 1206 1207 // device was unplugged but has connections, so slot is remembered 1208 HotplugGone bool `json:"hotplug-gone"` 1209 } 1210 1211 func getHotplugSlots(st *state.State) (map[string]*HotplugSlotInfo, error) { 1212 var slots map[string]*HotplugSlotInfo 1213 err := st.Get("hotplug-slots", &slots) 1214 if err != nil { 1215 if err != state.ErrNoState { 1216 return nil, err 1217 } 1218 slots = make(map[string]*HotplugSlotInfo) 1219 } 1220 return slots, nil 1221 } 1222 1223 func setHotplugSlots(st *state.State, slots map[string]*HotplugSlotInfo) { 1224 st.Set("hotplug-slots", slots) 1225 } 1226 1227 func findHotplugSlot(stateSlots map[string]*HotplugSlotInfo, ifaceName string, hotplugKey snap.HotplugKey) *HotplugSlotInfo { 1228 for _, slot := range stateSlots { 1229 if slot.HotplugKey == hotplugKey && slot.Interface == ifaceName { 1230 return slot 1231 } 1232 } 1233 return nil 1234 } 1235 1236 func findConnsForHotplugKey(conns map[string]*connState, ifaceName string, hotplugKey snap.HotplugKey) []string { 1237 var connsForDevice []string 1238 for id, connSt := range conns { 1239 if connSt.Interface != ifaceName || connSt.HotplugKey != hotplugKey { 1240 continue 1241 } 1242 connsForDevice = append(connsForDevice, id) 1243 } 1244 sort.Strings(connsForDevice) 1245 return connsForDevice 1246 } 1247 1248 func (m *InterfaceManager) discardSecurityProfilesLate(name string, rev snap.Revision, typ snap.Type) error { 1249 for _, backend := range m.repo.Backends() { 1250 lateDiscardBackend, ok := backend.(interfaces.SecurityBackendDiscardingLate) 1251 if !ok { 1252 continue 1253 } 1254 if err := lateDiscardBackend.RemoveLate(name, rev, typ); err != nil { 1255 return err 1256 } 1257 } 1258 return nil 1259 }