github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/ifacestate/helpers.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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.GetType() == 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  	for _, backend := range backends.All {
    71  		if err := backend.Initialize(); err != nil {
    72  			return err
    73  		}
    74  		if err := m.repo.AddBackend(backend); err != nil {
    75  			return err
    76  		}
    77  	}
    78  	for _, backend := range extra {
    79  		if err := backend.Initialize(); err != nil {
    80  			return err
    81  		}
    82  		if err := m.repo.AddBackend(backend); err != nil {
    83  			return err
    84  		}
    85  	}
    86  	return nil
    87  }
    88  
    89  func (m *InterfaceManager) addSnaps(snaps []*snap.Info) error {
    90  	for _, snapInfo := range snaps {
    91  		if err := addImplicitSlots(m.state, snapInfo); err != nil {
    92  			return err
    93  		}
    94  		if err := m.repo.AddSnap(snapInfo); err != nil {
    95  			logger.Noticef("cannot add snap %q to interface repository: %s", snapInfo.InstanceName(), err)
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  func profilesNeedRegenerationImpl() bool {
   102  	mismatch, err := interfaces.SystemKeyMismatch()
   103  	if err != nil {
   104  		logger.Noticef("error trying to compare the snap system key: %v", err)
   105  		return true
   106  	}
   107  	return mismatch
   108  }
   109  
   110  var profilesNeedRegeneration = profilesNeedRegenerationImpl
   111  var writeSystemKey = interfaces.WriteSystemKey
   112  
   113  // regenerateAllSecurityProfiles will regenerate all security profiles.
   114  func (m *InterfaceManager) regenerateAllSecurityProfiles(tm timings.Measurer) error {
   115  	// Get all the security backends
   116  	securityBackends := m.repo.Backends()
   117  
   118  	// Get all the snap infos
   119  	snaps, err := snapsWithSecurityProfiles(m.state)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	// Add implicit slots to all snaps
   125  	for _, snapInfo := range snaps {
   126  		if err := addImplicitSlots(m.state, snapInfo); err != nil {
   127  			return err
   128  		}
   129  	}
   130  
   131  	// The reason the system key is unlinked is to prevent snapd from believing
   132  	// that an old system key is valid and represents security setup
   133  	// established in the system. If snapd is reverted following a failed
   134  	// startup then system key may match the system key that used to be on disk
   135  	// but some of the system security may have been changed by the new snapd,
   136  	// the one that was reverted. Unlinking avoids such possibility, forcing
   137  	// old snapd to re-establish proper security view.
   138  	shouldWriteSystemKey := true
   139  	os.Remove(dirs.SnapSystemKeyFile)
   140  
   141  	// For each snap:
   142  	for _, snapInfo := range snaps {
   143  		snapName := snapInfo.InstanceName()
   144  		// Get the state of the snap so we can compute the confinement option
   145  		var snapst snapstate.SnapState
   146  		if err := snapstate.Get(m.state, snapName, &snapst); err != nil {
   147  			logger.Noticef("cannot get state of snap %q: %s", snapName, err)
   148  		}
   149  
   150  		// Compute confinement options
   151  		opts := confinementOptions(snapst.Flags)
   152  
   153  		// For each backend:
   154  		for _, backend := range securityBackends {
   155  			if backend.Name() == "" {
   156  				continue // Test backends have no name, skip them to simplify testing.
   157  			}
   158  			// Refresh security of this snap and backend
   159  			timings.Run(tm, "setup-security-backend", fmt.Sprintf("setup security backend %q for snap %q", backend.Name(), snapInfo.InstanceName()), func(nesttm timings.Measurer) {
   160  				if err := backend.Setup(snapInfo, opts, m.repo, nesttm); err != nil {
   161  					// Let's log this but carry on without writing the system key.
   162  					logger.Noticef("cannot regenerate %s profile for snap %q: %s",
   163  						backend.Name(), snapName, err)
   164  					shouldWriteSystemKey = false
   165  				}
   166  			})
   167  		}
   168  	}
   169  
   170  	if shouldWriteSystemKey {
   171  		if err := writeSystemKey(); err != nil {
   172  			logger.Noticef("cannot write system key: %v", err)
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  // renameCorePlugConnection renames one connection from "core-support" plug to
   179  // slot so that the plug name is "core-support-plug" while the slot is
   180  // unchanged. This matches a change introduced in 2.24, where the core snap no
   181  // longer has the "core-support" plug as that was clashing with the slot with
   182  // the same name.
   183  func (m *InterfaceManager) renameCorePlugConnection() error {
   184  	conns, err := getConns(m.state)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	const oldPlugName = "core-support"
   189  	const newPlugName = "core-support-plug"
   190  	// old connection, note that slotRef is the same in both
   191  	slotRef := interfaces.SlotRef{Snap: "core", Name: oldPlugName}
   192  	oldPlugRef := interfaces.PlugRef{Snap: "core", Name: oldPlugName}
   193  	oldConnRef := interfaces.ConnRef{PlugRef: oldPlugRef, SlotRef: slotRef}
   194  	oldID := oldConnRef.ID()
   195  	// if the old connection is saved, replace it with the new connection
   196  	if cState, ok := conns[oldID]; ok {
   197  		newPlugRef := interfaces.PlugRef{Snap: "core", Name: newPlugName}
   198  		newConnRef := interfaces.ConnRef{PlugRef: newPlugRef, SlotRef: slotRef}
   199  		newID := newConnRef.ID()
   200  		delete(conns, oldID)
   201  		conns[newID] = cState
   202  		setConns(m.state, conns)
   203  	}
   204  	return nil
   205  }
   206  
   207  // removeStaleConnections removes stale connections left by some older versions of snapd.
   208  // Connection is considered stale if the snap on either end of the connection doesn't exist anymore.
   209  // XXX: this code should eventually go away.
   210  var removeStaleConnections = func(st *state.State) error {
   211  	conns, err := getConns(st)
   212  	if err != nil {
   213  		return err
   214  	}
   215  	var staleConns []string
   216  	for id := range conns {
   217  		connRef, err := interfaces.ParseConnRef(id)
   218  		if err != nil {
   219  			return err
   220  		}
   221  		var snapst snapstate.SnapState
   222  		if err := snapstate.Get(st, connRef.PlugRef.Snap, &snapst); err != nil {
   223  			if err != state.ErrNoState {
   224  				return err
   225  			}
   226  			staleConns = append(staleConns, id)
   227  			continue
   228  		}
   229  		if err := snapstate.Get(st, connRef.SlotRef.Snap, &snapst); err != nil {
   230  			if err != state.ErrNoState {
   231  				return err
   232  			}
   233  			staleConns = append(staleConns, id)
   234  			continue
   235  		}
   236  	}
   237  	if len(staleConns) > 0 {
   238  		for _, id := range staleConns {
   239  			delete(conns, id)
   240  		}
   241  		setConns(st, conns)
   242  		logger.Noticef("removed stale connections: %s", strings.Join(staleConns, ", "))
   243  	}
   244  	return nil
   245  }
   246  
   247  // reloadConnections reloads connections stored in the state in the repository.
   248  // Using non-empty snapName the operation can be scoped to connections
   249  // affecting a given snap.
   250  //
   251  // The return value is the list of affected snap names.
   252  func (m *InterfaceManager) reloadConnections(snapName string) ([]string, error) {
   253  	conns, err := getConns(m.state)
   254  	if err != nil {
   255  		return nil, err
   256  	}
   257  
   258  	connStateChanged := false
   259  	affected := make(map[string]bool)
   260  	for connId, connState := range conns {
   261  		// Skip entries that just mark a connection as undesired. Those don't
   262  		// carry attributes that can go stale. In the same spirit, skip
   263  		// information about hotplug connections that don't have the associated
   264  		// hotplug hardware.
   265  		if connState.Undesired || connState.HotplugGone {
   266  			continue
   267  		}
   268  		connRef, err := interfaces.ParseConnRef(connId)
   269  		if err != nil {
   270  			return nil, err
   271  		}
   272  		// Apply filtering, this allows us to reload only a subset of
   273  		// connections (and similarly, refresh the static attributes of only a
   274  		// subset of connections).
   275  		if snapName != "" && connRef.PlugRef.Snap != snapName && connRef.SlotRef.Snap != snapName {
   276  			continue
   277  		}
   278  
   279  		// Some versions of snapd may have left stray connections that don't
   280  		// have the corresponding plug or slot anymore. Before we choose how to
   281  		// deal with this data we want to silently ignore that error not to
   282  		// worry the users.
   283  		plugInfo := m.repo.Plug(connRef.PlugRef.Snap, connRef.PlugRef.Name)
   284  		slotInfo := m.repo.Slot(connRef.SlotRef.Snap, connRef.SlotRef.Name)
   285  		if plugInfo == nil || slotInfo == nil {
   286  			continue
   287  		}
   288  
   289  		var updateStaticAttrs bool
   290  		staticPlugAttrs := connState.StaticPlugAttrs
   291  		staticSlotAttrs := connState.StaticSlotAttrs
   292  
   293  		// XXX: Refresh the copy of the static connection attributes for "content" interface as long
   294  		// as its "content" attribute
   295  		// This is a partial and temporary solution to https://bugs.launchpad.net/snapd/+bug/1825883
   296  		if plugInfo.Interface == "content" {
   297  			var plugContent, slotContent string
   298  			plugInfo.Attr("content", &plugContent)
   299  			slotInfo.Attr("content", &slotContent)
   300  
   301  			if plugContent != "" && plugContent == slotContent {
   302  				staticPlugAttrs = utils.NormalizeInterfaceAttributes(plugInfo.Attrs).(map[string]interface{})
   303  				staticSlotAttrs = utils.NormalizeInterfaceAttributes(slotInfo.Attrs).(map[string]interface{})
   304  				updateStaticAttrs = true
   305  			} else {
   306  				logger.Noticef("cannot refresh static attributes of the connection %q", connId)
   307  			}
   308  		}
   309  
   310  		// Note: reloaded connections are not checked against policy again, and also we don't call BeforeConnect* methods on them.
   311  		if _, err := m.repo.Connect(connRef, staticPlugAttrs, connState.DynamicPlugAttrs, staticSlotAttrs, connState.DynamicSlotAttrs, nil); err != nil {
   312  			logger.Noticef("%s", err)
   313  		} else {
   314  			// If the connection succeeded update the connection state and keep
   315  			// track of the snaps that were affected.
   316  			affected[connRef.PlugRef.Snap] = true
   317  			affected[connRef.SlotRef.Snap] = true
   318  
   319  			if updateStaticAttrs {
   320  				connState.StaticPlugAttrs = staticPlugAttrs
   321  				connState.StaticSlotAttrs = staticSlotAttrs
   322  				connStateChanged = true
   323  			}
   324  		}
   325  	}
   326  	if connStateChanged {
   327  		setConns(m.state, conns)
   328  	}
   329  
   330  	result := make([]string, 0, len(affected))
   331  	for name := range affected {
   332  		result = append(result, name)
   333  	}
   334  	return result, nil
   335  }
   336  
   337  // removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap
   338  // has no connections in the state. State must be locked by the caller.
   339  func (m *InterfaceManager) removeConnections(snapName string) error {
   340  	conns, err := getConns(m.state)
   341  	if err != nil {
   342  		return err
   343  	}
   344  	for id := range conns {
   345  		connRef, err := interfaces.ParseConnRef(id)
   346  		if err != nil {
   347  			return err
   348  		}
   349  		if connRef.PlugRef.Snap == snapName || connRef.SlotRef.Snap == snapName {
   350  			return fmt.Errorf("internal error: cannot remove connections of snap %s from the repository while its connections are present in the state", snapName)
   351  		}
   352  	}
   353  
   354  	repoConns, err := m.repo.Connections(snapName)
   355  	if err != nil {
   356  		return fmt.Errorf("internal error: %v", err)
   357  	}
   358  	for _, conn := range repoConns {
   359  		if err := m.repo.Disconnect(conn.PlugRef.Snap, conn.PlugRef.Name, conn.SlotRef.Snap, conn.SlotRef.Name); err != nil {
   360  			return fmt.Errorf("internal error: %v", err)
   361  		}
   362  	}
   363  	return nil
   364  }
   365  
   366  func (m *InterfaceManager) setupSecurityByBackend(task *state.Task, snaps []*snap.Info, opts []interfaces.ConfinementOptions, tm timings.Measurer) error {
   367  	st := task.State()
   368  
   369  	// Setup all affected snaps, start with the most important security
   370  	// backend and run it for all snaps. See LP: 1802581
   371  	for _, backend := range m.repo.Backends() {
   372  		for i, snapInfo := range snaps {
   373  			st.Unlock()
   374  			var err error
   375  			timings.Run(tm, "setup-security-backend", fmt.Sprintf("setup security backend %q for snap %q", backend.Name(), snapInfo.InstanceName()), func(nesttm timings.Measurer) {
   376  				err = backend.Setup(snapInfo, opts[i], m.repo, nesttm)
   377  			})
   378  			st.Lock()
   379  			if err != nil {
   380  				task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), snapInfo.InstanceName(), err)
   381  				return err
   382  			}
   383  		}
   384  	}
   385  
   386  	return nil
   387  }
   388  
   389  func (m *InterfaceManager) setupSnapSecurity(task *state.Task, snapInfo *snap.Info, opts interfaces.ConfinementOptions, tm timings.Measurer) error {
   390  	return m.setupSecurityByBackend(task, []*snap.Info{snapInfo}, []interfaces.ConfinementOptions{opts}, tm)
   391  }
   392  
   393  func (m *InterfaceManager) removeSnapSecurity(task *state.Task, instanceName string) error {
   394  	st := task.State()
   395  	for _, backend := range m.repo.Backends() {
   396  		st.Unlock()
   397  		err := backend.Remove(instanceName)
   398  		st.Lock()
   399  		if err != nil {
   400  			task.Errorf("cannot setup %s for snap %q: %s", backend.Name(), instanceName, err)
   401  			return err
   402  		}
   403  	}
   404  	return nil
   405  }
   406  
   407  func addHotplugSlot(st *state.State, repo *interfaces.Repository, stateSlots map[string]*HotplugSlotInfo, iface interfaces.Interface, slot *snap.SlotInfo) error {
   408  	if slot.HotplugKey == "" {
   409  		return fmt.Errorf("internal error: cannot store slot %q, not a hotplug slot", slot.Name)
   410  	}
   411  	if iface, ok := iface.(interfaces.SlotSanitizer); ok {
   412  		if err := iface.BeforePrepareSlot(slot); err != nil {
   413  			return fmt.Errorf("cannot sanitize hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err)
   414  		}
   415  	}
   416  
   417  	if err := repo.AddSlot(slot); err != nil {
   418  		return fmt.Errorf("cannot add hotplug slot %q for interface %s: %s", slot.Name, slot.Interface, err)
   419  	}
   420  
   421  	stateSlots[slot.Name] = &HotplugSlotInfo{
   422  		Name:        slot.Name,
   423  		Interface:   slot.Interface,
   424  		StaticAttrs: slot.Attrs,
   425  		HotplugKey:  slot.HotplugKey,
   426  		HotplugGone: false,
   427  	}
   428  	setHotplugSlots(st, stateSlots)
   429  	logger.Debugf("added hotplug slot %s:%s of interface %s, hotplug key %q", slot.Snap.InstanceName(), slot.Name, slot.Interface, slot.HotplugKey)
   430  	return nil
   431  }
   432  
   433  type connState struct {
   434  	Auto      bool   `json:"auto,omitempty"`
   435  	ByGadget  bool   `json:"by-gadget,omitempty"`
   436  	Interface string `json:"interface,omitempty"`
   437  	// Undesired tracks connections that were manually disconnected after being auto-connected,
   438  	// so that they are not automatically reconnected again in the future.
   439  	Undesired        bool                   `json:"undesired,omitempty"`
   440  	StaticPlugAttrs  map[string]interface{} `json:"plug-static,omitempty"`
   441  	DynamicPlugAttrs map[string]interface{} `json:"plug-dynamic,omitempty"`
   442  	StaticSlotAttrs  map[string]interface{} `json:"slot-static,omitempty"`
   443  	DynamicSlotAttrs map[string]interface{} `json:"slot-dynamic,omitempty"`
   444  	// Hotplug-related attributes: HotplugGone indicates a connection that
   445  	// disappeared because the device was removed, but may potentially be
   446  	// restored in the future if we see the device again. HotplugKey is the
   447  	// key of the associated device; it's empty for connections of regular
   448  	// slots.
   449  	HotplugGone bool            `json:"hotplug-gone,omitempty"`
   450  	HotplugKey  snap.HotplugKey `json:"hotplug-key,omitempty"`
   451  }
   452  
   453  type autoConnectChecker struct {
   454  	st        *state.State
   455  	deviceCtx snapstate.DeviceContext
   456  	cache     map[string]*asserts.SnapDeclaration
   457  	baseDecl  *asserts.BaseDeclaration
   458  }
   459  
   460  func newAutoConnectChecker(s *state.State, deviceCtx snapstate.DeviceContext) (*autoConnectChecker, error) {
   461  	baseDecl, err := assertstate.BaseDeclaration(s)
   462  	if err != nil {
   463  		return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err)
   464  	}
   465  	return &autoConnectChecker{
   466  		st:        s,
   467  		deviceCtx: deviceCtx,
   468  		cache:     make(map[string]*asserts.SnapDeclaration),
   469  		baseDecl:  baseDecl,
   470  	}, nil
   471  }
   472  
   473  func (c *autoConnectChecker) snapDeclaration(snapID string) (*asserts.SnapDeclaration, error) {
   474  	snapDecl := c.cache[snapID]
   475  	if snapDecl != nil {
   476  		return snapDecl, nil
   477  	}
   478  	snapDecl, err := assertstate.SnapDeclaration(c.st, snapID)
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  	c.cache[snapID] = snapDecl
   483  	return snapDecl, nil
   484  }
   485  
   486  func (c *autoConnectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, error) {
   487  	modelAs := c.deviceCtx.Model()
   488  
   489  	var storeAs *asserts.Store
   490  	if modelAs.Store() != "" {
   491  		var err error
   492  		storeAs, err = assertstate.Store(c.st, modelAs.Store())
   493  		if err != nil && !asserts.IsNotFound(err) {
   494  			return false, err
   495  		}
   496  	}
   497  
   498  	var plugDecl *asserts.SnapDeclaration
   499  	if plug.Snap().SnapID != "" {
   500  		var err error
   501  		plugDecl, err = c.snapDeclaration(plug.Snap().SnapID)
   502  		if err != nil {
   503  			logger.Noticef("error: cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err)
   504  			return false, nil
   505  		}
   506  	}
   507  
   508  	var slotDecl *asserts.SnapDeclaration
   509  	if slot.Snap().SnapID != "" {
   510  		var err error
   511  		slotDecl, err = c.snapDeclaration(slot.Snap().SnapID)
   512  		if err != nil {
   513  			logger.Noticef("error: cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err)
   514  			return false, nil
   515  		}
   516  	}
   517  
   518  	// check the connection against the declarations' rules
   519  	ic := policy.ConnectCandidate{
   520  		Plug:                plug,
   521  		PlugSnapDeclaration: plugDecl,
   522  		Slot:                slot,
   523  		SlotSnapDeclaration: slotDecl,
   524  		BaseDeclaration:     c.baseDecl,
   525  		Model:               modelAs,
   526  		Store:               storeAs,
   527  	}
   528  
   529  	return ic.CheckAutoConnect() == nil, nil
   530  }
   531  
   532  type connectChecker struct {
   533  	st        *state.State
   534  	deviceCtx snapstate.DeviceContext
   535  	baseDecl  *asserts.BaseDeclaration
   536  }
   537  
   538  func newConnectChecker(s *state.State, deviceCtx snapstate.DeviceContext) (*connectChecker, error) {
   539  	baseDecl, err := assertstate.BaseDeclaration(s)
   540  	if err != nil {
   541  		return nil, fmt.Errorf("internal error: cannot find base declaration: %v", err)
   542  	}
   543  	return &connectChecker{
   544  		st:        s,
   545  		deviceCtx: deviceCtx,
   546  		baseDecl:  baseDecl,
   547  	}, nil
   548  }
   549  
   550  func (c *connectChecker) check(plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) (bool, error) {
   551  	modelAs := c.deviceCtx.Model()
   552  
   553  	var storeAs *asserts.Store
   554  	if modelAs.Store() != "" {
   555  		var err error
   556  		storeAs, err = assertstate.Store(c.st, modelAs.Store())
   557  		if err != nil && !asserts.IsNotFound(err) {
   558  			return false, err
   559  		}
   560  	}
   561  
   562  	var plugDecl *asserts.SnapDeclaration
   563  	if plug.Snap().SnapID != "" {
   564  		var err error
   565  		plugDecl, err = assertstate.SnapDeclaration(c.st, plug.Snap().SnapID)
   566  		if err != nil {
   567  			return false, fmt.Errorf("cannot find snap declaration for %q: %v", plug.Snap().InstanceName(), err)
   568  		}
   569  	}
   570  
   571  	var slotDecl *asserts.SnapDeclaration
   572  	if slot.Snap().SnapID != "" {
   573  		var err error
   574  		slotDecl, err = assertstate.SnapDeclaration(c.st, slot.Snap().SnapID)
   575  		if err != nil {
   576  			return false, fmt.Errorf("cannot find snap declaration for %q: %v", slot.Snap().InstanceName(), err)
   577  		}
   578  	}
   579  
   580  	// check the connection against the declarations' rules
   581  	ic := policy.ConnectCandidate{
   582  		Plug:                plug,
   583  		PlugSnapDeclaration: plugDecl,
   584  		Slot:                slot,
   585  		SlotSnapDeclaration: slotDecl,
   586  		BaseDeclaration:     c.baseDecl,
   587  		Model:               modelAs,
   588  		Store:               storeAs,
   589  	}
   590  
   591  	// if either of plug or slot snaps don't have a declaration it
   592  	// means they were installed with "dangerous", so the security
   593  	// check should be skipped at this point.
   594  	if plugDecl != nil && slotDecl != nil {
   595  		if err := ic.Check(); err != nil {
   596  			return false, err
   597  		}
   598  	}
   599  	return true, nil
   600  }
   601  
   602  func getPlugAndSlotRefs(task *state.Task) (interfaces.PlugRef, interfaces.SlotRef, error) {
   603  	var plugRef interfaces.PlugRef
   604  	var slotRef interfaces.SlotRef
   605  	if err := task.Get("plug", &plugRef); err != nil {
   606  		return plugRef, slotRef, err
   607  	}
   608  	if err := task.Get("slot", &slotRef); err != nil {
   609  		return plugRef, slotRef, err
   610  	}
   611  	return plugRef, slotRef, nil
   612  }
   613  
   614  // getConns returns information about connections from the state.
   615  //
   616  // Connections are transparently re-mapped according to remapIncomingConnRef
   617  func getConns(st *state.State) (conns map[string]*connState, err error) {
   618  	var raw *json.RawMessage
   619  	err = st.Get("conns", &raw)
   620  	if err != nil && err != state.ErrNoState {
   621  		return nil, fmt.Errorf("cannot obtain raw data about existing connections: %s", err)
   622  	}
   623  	if raw != nil {
   624  		err = jsonutil.DecodeWithNumber(bytes.NewReader(*raw), &conns)
   625  		if err != nil {
   626  			return nil, fmt.Errorf("cannot decode data about existing connections: %s", err)
   627  		}
   628  	}
   629  	if conns == nil {
   630  		conns = make(map[string]*connState)
   631  	}
   632  	remapped := make(map[string]*connState, len(conns))
   633  	for id, cstate := range conns {
   634  		cref, err := interfaces.ParseConnRef(id)
   635  		if err != nil {
   636  			return nil, err
   637  		}
   638  		cref.PlugRef.Snap = RemapSnapFromState(cref.PlugRef.Snap)
   639  		cref.SlotRef.Snap = RemapSnapFromState(cref.SlotRef.Snap)
   640  		cstate.StaticSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticSlotAttrs).(map[string]interface{})
   641  		cstate.DynamicSlotAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicSlotAttrs).(map[string]interface{})
   642  		cstate.StaticPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.StaticPlugAttrs).(map[string]interface{})
   643  		cstate.DynamicPlugAttrs = utils.NormalizeInterfaceAttributes(cstate.DynamicPlugAttrs).(map[string]interface{})
   644  		remapped[cref.ID()] = cstate
   645  	}
   646  	return remapped, nil
   647  }
   648  
   649  // setConns sets information about connections in the state.
   650  //
   651  // Connections are transparently re-mapped according to remapOutgoingConnRef
   652  func setConns(st *state.State, conns map[string]*connState) {
   653  	remapped := make(map[string]*connState, len(conns))
   654  	for id, cstate := range conns {
   655  		cref, err := interfaces.ParseConnRef(id)
   656  		if err != nil {
   657  			// We cannot fail here
   658  			panic(err)
   659  		}
   660  		cref.PlugRef.Snap = RemapSnapToState(cref.PlugRef.Snap)
   661  		cref.SlotRef.Snap = RemapSnapToState(cref.SlotRef.Snap)
   662  		remapped[cref.ID()] = cstate
   663  	}
   664  	st.Set("conns", remapped)
   665  }
   666  
   667  // snapsWithSecurityProfiles returns all snaps that have active
   668  // security profiles: these are either snaps that are active, or about
   669  // to be active (pending link-snap) with a done setup-profiles
   670  func snapsWithSecurityProfiles(st *state.State) ([]*snap.Info, error) {
   671  	infos, err := snapstate.ActiveInfos(st)
   672  	if err != nil {
   673  		return nil, err
   674  	}
   675  	seen := make(map[string]bool, len(infos))
   676  	for _, info := range infos {
   677  		seen[info.InstanceName()] = true
   678  	}
   679  	for _, t := range st.Tasks() {
   680  		if t.Kind() != "link-snap" || t.Status().Ready() {
   681  			continue
   682  		}
   683  		snapsup, err := snapstate.TaskSnapSetup(t)
   684  		if err != nil {
   685  			return nil, err
   686  		}
   687  		instanceName := snapsup.InstanceName()
   688  		if seen[instanceName] {
   689  			continue
   690  		}
   691  
   692  		doneProfiles := false
   693  		for _, t1 := range t.WaitTasks() {
   694  			if t1.Kind() == "setup-profiles" && t1.Status() == state.DoneStatus {
   695  				snapsup1, err := snapstate.TaskSnapSetup(t)
   696  				if err != nil {
   697  					return nil, err
   698  				}
   699  				if snapsup1.InstanceName() == instanceName {
   700  					doneProfiles = true
   701  					break
   702  				}
   703  			}
   704  		}
   705  		if !doneProfiles {
   706  			continue
   707  		}
   708  
   709  		seen[instanceName] = true
   710  		snapInfo, err := snap.ReadInfo(instanceName, snapsup.SideInfo)
   711  		if err != nil {
   712  			logger.Noticef("cannot retrieve info for snap %q: %s", instanceName, err)
   713  			continue
   714  		}
   715  		infos = append(infos, snapInfo)
   716  	}
   717  
   718  	return infos, nil
   719  }
   720  
   721  func resolveSnapIDToName(st *state.State, snapID string) (name string, err error) {
   722  	if snapID == "system" {
   723  		// Resolve the system nickname to a concrete snap.
   724  		return mapper.RemapSnapFromRequest(snapID), nil
   725  	}
   726  	decl, err := assertstate.SnapDeclaration(st, snapID)
   727  	if asserts.IsNotFound(err) {
   728  		return "", nil
   729  	}
   730  	if err != nil {
   731  		return "", err
   732  	}
   733  	return decl.SnapName(), nil
   734  }
   735  
   736  // SnapMapper offers APIs for re-mapping snap names in interfaces and the
   737  // configuration system. The mapper is designed to apply transformations around
   738  // the edges of snapd (state interactions and API interactions) to offer one
   739  // view on the inside of snapd and another view on the outside.
   740  type SnapMapper interface {
   741  	// re-map functions for loading and saving objects in the state.
   742  	RemapSnapFromState(snapName string) string
   743  	RemapSnapToState(snapName string) string
   744  	// RamapSnapFromRequest can replace snap names in API requests.
   745  	// There is no corresponding mapping function for API responses anymore.
   746  	// The API responses always reflect the real system state.
   747  	RemapSnapFromRequest(snapName string) string
   748  	// Returns actual name of the system snap.
   749  	SystemSnapName() string
   750  }
   751  
   752  // IdentityMapper implements SnapMapper and performs no transformations at all.
   753  type IdentityMapper struct{}
   754  
   755  // RemapSnapFromState doesn't change the snap name in any way.
   756  func (m *IdentityMapper) RemapSnapFromState(snapName string) string {
   757  	return snapName
   758  }
   759  
   760  // RemapSnapToState doesn't change the snap name in any way.
   761  func (m *IdentityMapper) RemapSnapToState(snapName string) string {
   762  	return snapName
   763  }
   764  
   765  // RemapSnapFromRequest  doesn't change the snap name in any way.
   766  func (m *IdentityMapper) RemapSnapFromRequest(snapName string) string {
   767  	return snapName
   768  }
   769  
   770  // CoreCoreSystemMapper implements SnapMapper and makes implicit slots
   771  // appear to be on "core" in the state and in memory but as "system" in the API.
   772  //
   773  // NOTE: This mapper can be used to prepare, as an intermediate step, for the
   774  // transition to "snapd" mapper. Using it the state and API layer will look
   775  // exactly the same as with the "snapd" mapper. This can be used to make any
   776  // necessary adjustments the test suite.
   777  type CoreCoreSystemMapper struct {
   778  	IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate.
   779  }
   780  
   781  // RemapSnapFromRequest renames the "system" snap to the "core" snap.
   782  //
   783  // This allows us to accept connection and disconnection requests that
   784  // explicitly refer to "core" or using the "system" nickname.
   785  func (m *CoreCoreSystemMapper) RemapSnapFromRequest(snapName string) string {
   786  	if snapName == "system" {
   787  		return m.SystemSnapName()
   788  	}
   789  	return snapName
   790  }
   791  
   792  // SystemSnapName returns actual name of the system snap.
   793  func (m *CoreCoreSystemMapper) SystemSnapName() string {
   794  	return "core"
   795  }
   796  
   797  // CoreSnapdSystemMapper implements SnapMapper and makes implicit slots
   798  // appear to be on "core" in the state and on "system" in the API while they
   799  // are on "snapd" in memory.
   800  type CoreSnapdSystemMapper struct {
   801  	IdentityMapper // Embedding the identity mapper allows us to cut on boilerplate.
   802  }
   803  
   804  // RemapSnapFromState renames the "core" snap to the "snapd" snap.
   805  //
   806  // This allows modern snapd to load an old state that remembers connections
   807  // between slots on the "core" snap and other snaps. In memory we are actually
   808  // using "snapd" snap for hosting those slots and this lets us stay compatible.
   809  func (m *CoreSnapdSystemMapper) RemapSnapFromState(snapName string) string {
   810  	if snapName == "core" {
   811  		return m.SystemSnapName()
   812  	}
   813  	return snapName
   814  }
   815  
   816  // RemapSnapToState renames the "snapd" snap to the "core" snap.
   817  //
   818  // This allows the state to stay backwards compatible as all the connections
   819  // seem to refer to the "core" snap, as in pre core{16,18} days where there was
   820  // only one core snap.
   821  func (m *CoreSnapdSystemMapper) RemapSnapToState(snapName string) string {
   822  	if snapName == m.SystemSnapName() {
   823  		return "core"
   824  	}
   825  	return snapName
   826  }
   827  
   828  // RemapSnapFromRequest renames the "core" or "system" snaps to the "snapd" snap.
   829  //
   830  // This allows us to accept connection and disconnection requests that
   831  // explicitly refer to "core" or "system" even though we really want them to
   832  // refer to "snapd". Note that this is not fully symmetric with
   833  // RemapSnapToResponse as we explicitly always talk about "system" snap,
   834  // even if the request used "core".
   835  func (m *CoreSnapdSystemMapper) RemapSnapFromRequest(snapName string) string {
   836  	if snapName == "system" || snapName == "core" {
   837  		return m.SystemSnapName()
   838  	}
   839  	return snapName
   840  }
   841  
   842  // SystemSnapName returns actual name of the system snap.
   843  func (m *CoreSnapdSystemMapper) SystemSnapName() string {
   844  	return "snapd"
   845  }
   846  
   847  // mapper contains the currently active snap mapper.
   848  var mapper SnapMapper = &CoreCoreSystemMapper{}
   849  
   850  // MockSnapMapper mocks the currently used snap mapper.
   851  func MockSnapMapper(new SnapMapper) (restore func()) {
   852  	old := mapper
   853  	mapper = new
   854  	return func() { mapper = old }
   855  }
   856  
   857  // RemapSnapFromState renames a snap when loaded from state according to the current mapper.
   858  func RemapSnapFromState(snapName string) string {
   859  	return mapper.RemapSnapFromState(snapName)
   860  }
   861  
   862  // RemapSnapToState renames a snap when saving to state according to the current mapper.
   863  func RemapSnapToState(snapName string) string {
   864  	return mapper.RemapSnapToState(snapName)
   865  }
   866  
   867  // RemapSnapFromRequest renames a snap as received from an API request according to the current mapper.
   868  func RemapSnapFromRequest(snapName string) string {
   869  	return mapper.RemapSnapFromRequest(snapName)
   870  }
   871  
   872  // SystemSnapName returns actual name of the system snap.
   873  func SystemSnapName() string {
   874  	return mapper.SystemSnapName()
   875  }
   876  
   877  // systemSnapInfo returns current info for system snap.
   878  func systemSnapInfo(st *state.State) (*snap.Info, error) {
   879  	return snapstate.CurrentInfo(st, SystemSnapName())
   880  }
   881  
   882  func connectDisconnectAffectedSnaps(t *state.Task) ([]string, error) {
   883  	plugRef, slotRef, err := getPlugAndSlotRefs(t)
   884  	if err != nil {
   885  		return nil, fmt.Errorf("internal error: cannot obtain plug/slot data from task: %s", t.Summary())
   886  	}
   887  	return []string{plugRef.Snap, slotRef.Snap}, nil
   888  }
   889  
   890  func checkSystemSnapIsPresent(st *state.State) bool {
   891  	st.Lock()
   892  	defer st.Unlock()
   893  	_, err := systemSnapInfo(st)
   894  	return err == nil
   895  }
   896  
   897  func setHotplugAttrs(task *state.Task, ifaceName string, hotplugKey snap.HotplugKey) {
   898  	task.Set("interface", ifaceName)
   899  	task.Set("hotplug-key", hotplugKey)
   900  }
   901  
   902  func getHotplugAttrs(task *state.Task) (ifaceName string, hotplugKey snap.HotplugKey, err error) {
   903  	if err = task.Get("interface", &ifaceName); err != nil {
   904  		return "", "", fmt.Errorf("internal error: cannot get interface name from hotplug task: %s", err)
   905  	}
   906  	if err = task.Get("hotplug-key", &hotplugKey); err != nil {
   907  		return "", "", fmt.Errorf("internal error: cannot get hotplug key from hotplug task: %s", err)
   908  	}
   909  	return ifaceName, hotplugKey, err
   910  }
   911  
   912  func allocHotplugSeq(st *state.State) (int, error) {
   913  	var seq int
   914  	if err := st.Get("hotplug-seq", &seq); err != nil && err != state.ErrNoState {
   915  		return 0, fmt.Errorf("internal error: cannot allocate hotplug sequence number: %s", err)
   916  	}
   917  	seq++
   918  	st.Set("hotplug-seq", seq)
   919  	return seq, nil
   920  }
   921  
   922  func isHotplugChange(chg *state.Change) bool {
   923  	return strings.HasPrefix(chg.Kind(), "hotplug-")
   924  }
   925  
   926  func getHotplugChangeAttrs(chg *state.Change) (seq int, hotplugKey snap.HotplugKey, err error) {
   927  	if err = chg.Get("hotplug-key", &hotplugKey); err != nil {
   928  		return 0, "", fmt.Errorf("internal error: hotplug-key not set on change %q", chg.Kind())
   929  	}
   930  	if err = chg.Get("hotplug-seq", &seq); err != nil {
   931  		return 0, "", fmt.Errorf("internal error: hotplug-seq not set on change %q", chg.Kind())
   932  	}
   933  	return seq, hotplugKey, nil
   934  }
   935  
   936  func setHotplugChangeAttrs(chg *state.Change, seq int, hotplugKey snap.HotplugKey) {
   937  	chg.Set("hotplug-seq", seq)
   938  	chg.Set("hotplug-key", hotplugKey)
   939  }
   940  
   941  // addHotplugSeqWaitTask sets mandatory hotplug attributes on the hotplug change, adds "hotplug-seq-wait" task
   942  // and makes all existing tasks of the change wait for it.
   943  func addHotplugSeqWaitTask(hotplugChange *state.Change, hotplugKey snap.HotplugKey, hotplugSeq int) {
   944  	st := hotplugChange.State()
   945  	setHotplugChangeAttrs(hotplugChange, hotplugSeq, hotplugKey)
   946  	seqControl := st.NewTask("hotplug-seq-wait", fmt.Sprintf("Serialize hotplug change for hotplug key %q", hotplugKey))
   947  	tss := state.NewTaskSet(hotplugChange.Tasks()...)
   948  	tss.WaitFor(seqControl)
   949  	hotplugChange.AddTask(seqControl)
   950  }
   951  
   952  type HotplugSlotInfo struct {
   953  	Name        string                 `json:"name"`
   954  	Interface   string                 `json:"interface"`
   955  	StaticAttrs map[string]interface{} `json:"static-attrs,omitempty"`
   956  	HotplugKey  snap.HotplugKey        `json:"hotplug-key"`
   957  
   958  	// device was unplugged but has connections, so slot is remembered
   959  	HotplugGone bool `json:"hotplug-gone"`
   960  }
   961  
   962  func getHotplugSlots(st *state.State) (map[string]*HotplugSlotInfo, error) {
   963  	var slots map[string]*HotplugSlotInfo
   964  	err := st.Get("hotplug-slots", &slots)
   965  	if err != nil {
   966  		if err != state.ErrNoState {
   967  			return nil, err
   968  		}
   969  		slots = make(map[string]*HotplugSlotInfo)
   970  	}
   971  	return slots, nil
   972  }
   973  
   974  func setHotplugSlots(st *state.State, slots map[string]*HotplugSlotInfo) {
   975  	st.Set("hotplug-slots", slots)
   976  }
   977  
   978  func findHotplugSlot(stateSlots map[string]*HotplugSlotInfo, ifaceName string, hotplugKey snap.HotplugKey) *HotplugSlotInfo {
   979  	for _, slot := range stateSlots {
   980  		if slot.HotplugKey == hotplugKey && slot.Interface == ifaceName {
   981  			return slot
   982  		}
   983  	}
   984  	return nil
   985  }
   986  
   987  func findConnsForHotplugKey(conns map[string]*connState, ifaceName string, hotplugKey snap.HotplugKey) []string {
   988  	var connsForDevice []string
   989  	for id, connSt := range conns {
   990  		if connSt.Interface != ifaceName || connSt.HotplugKey != hotplugKey {
   991  			continue
   992  		}
   993  		connsForDevice = append(connsForDevice, id)
   994  	}
   995  	sort.Strings(connsForDevice)
   996  	return connsForDevice
   997  }