github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/dbus/backend.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 dbus implements interaction between snappy and dbus.
    21  //
    22  // Snappy creates dbus configuration files that describe how various
    23  // services on the system bus can communicate with other peers.
    24  //
    25  // Each configuration is an XML file containing <busconfig>...</busconfig>.
    26  // Particular security snippets define whole <policy>...</policy> entires.
    27  // This is explained in detail in https://dbus.freedesktop.org/doc/dbus-daemon.1.html
    28  package dbus
    29  
    30  import (
    31  	"bytes"
    32  	"fmt"
    33  	"os"
    34  	"path/filepath"
    35  
    36  	"github.com/snapcore/snapd/dirs"
    37  	"github.com/snapcore/snapd/interfaces"
    38  	"github.com/snapcore/snapd/logger"
    39  	"github.com/snapcore/snapd/osutil"
    40  	"github.com/snapcore/snapd/release"
    41  	"github.com/snapcore/snapd/snap"
    42  	"github.com/snapcore/snapd/timings"
    43  )
    44  
    45  // Backend is responsible for maintaining DBus policy files.
    46  type Backend struct{}
    47  
    48  // Initialize does nothing.
    49  func (b *Backend) Initialize() error {
    50  	return nil
    51  }
    52  
    53  // Name returns the name of the backend.
    54  func (b *Backend) Name() interfaces.SecuritySystem {
    55  	return "dbus"
    56  }
    57  
    58  // setupDbusServiceForUserd will setup the service file for the new
    59  // `snap userd` instance on re-exec
    60  func setupDbusServiceForUserd(snapInfo *snap.Info) error {
    61  	coreOrSnapdRoot := snapInfo.MountDir()
    62  
    63  	// fugly - but we need to make sure that the content of the
    64  	// "snapd" snap wins
    65  	//
    66  	// TODO: this is also racy but the content of the files in core and
    67  	// snapd is identical cleanup after link-snap and
    68  	// setup-profiles are unified
    69  	if snapInfo.InstanceName() == "core" && osutil.FileExists(filepath.Join(coreOrSnapdRoot, "../..", "snapd/current")) {
    70  		return nil
    71  	}
    72  
    73  	for _, srv := range []string{
    74  		"io.snapcraft.Launcher.service",
    75  		"io.snapcraft.Settings.service",
    76  	} {
    77  		dst := filepath.Join("/usr/share/dbus-1/services/", srv)
    78  		src := filepath.Join(coreOrSnapdRoot, dst)
    79  
    80  		// we only need the GlobalRootDir for testing
    81  		dst = filepath.Join(dirs.GlobalRootDir, dst)
    82  		if !osutil.FilesAreEqual(src, dst) {
    83  			if err := osutil.CopyFile(src, dst, osutil.CopyFlagPreserveAll); err != nil {
    84  				return err
    85  			}
    86  		}
    87  	}
    88  	return nil
    89  }
    90  
    91  // Setup creates dbus configuration files specific to a given snap.
    92  //
    93  // DBus has no concept of a complain mode so confinment type is ignored.
    94  func (b *Backend) Setup(snapInfo *snap.Info, opts interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
    95  	snapName := snapInfo.InstanceName()
    96  	// Get the snippets that apply to this snap
    97  	spec, err := repo.SnapSpecification(b.Name(), snapName)
    98  	if err != nil {
    99  		return fmt.Errorf("cannot obtain dbus specification for snap %q: %s", snapName, err)
   100  	}
   101  
   102  	// core/snapd on classic are special
   103  	if (snapInfo.GetType() == snap.TypeOS || snapInfo.GetType() == snap.TypeSnapd) && release.OnClassic {
   104  		if err := setupDbusServiceForUserd(snapInfo); err != nil {
   105  			logger.Noticef("cannot create host `snap userd` dbus service file: %s", err)
   106  		}
   107  	}
   108  
   109  	// Get the files that this snap should have
   110  	content, err := b.deriveContent(spec.(*Specification), snapInfo)
   111  	if err != nil {
   112  		return fmt.Errorf("cannot obtain expected DBus configuration files for snap %q: %s", snapName, err)
   113  	}
   114  	glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName))
   115  	dir := dirs.SnapBusPolicyDir
   116  	if err := os.MkdirAll(dir, 0755); err != nil {
   117  		return fmt.Errorf("cannot create directory for DBus configuration files %q: %s", dir, err)
   118  	}
   119  	_, _, err = osutil.EnsureDirState(dir, glob, content)
   120  	if err != nil {
   121  		return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err)
   122  	}
   123  	return nil
   124  }
   125  
   126  // Remove removes dbus configuration files of a given snap.
   127  //
   128  // This method should be called after removing a snap.
   129  func (b *Backend) Remove(snapName string) error {
   130  	glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName))
   131  	_, _, err := osutil.EnsureDirState(dirs.SnapBusPolicyDir, glob, nil)
   132  	if err != nil {
   133  		return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err)
   134  	}
   135  	return nil
   136  }
   137  
   138  // deriveContent combines security snippets collected from all the interfaces
   139  // affecting a given snap into a content map applicable to EnsureDirState.
   140  func (b *Backend) deriveContent(spec *Specification, snapInfo *snap.Info) (content map[string]*osutil.FileState, err error) {
   141  	for _, appInfo := range snapInfo.Apps {
   142  		securityTag := appInfo.SecurityTag()
   143  		appSnippets := spec.SnippetForTag(securityTag)
   144  		if appSnippets == "" {
   145  			continue
   146  		}
   147  		if content == nil {
   148  			content = make(map[string]*osutil.FileState)
   149  		}
   150  
   151  		addContent(securityTag, appSnippets, content)
   152  	}
   153  
   154  	for _, hookInfo := range snapInfo.Hooks {
   155  		securityTag := hookInfo.SecurityTag()
   156  		hookSnippets := spec.SnippetForTag(securityTag)
   157  		if hookSnippets == "" {
   158  			continue
   159  		}
   160  		if content == nil {
   161  			content = make(map[string]*osutil.FileState)
   162  		}
   163  
   164  		addContent(securityTag, hookSnippets, content)
   165  	}
   166  
   167  	return content, nil
   168  }
   169  
   170  func addContent(securityTag string, snippet string, content map[string]*osutil.FileState) {
   171  	var buffer bytes.Buffer
   172  	buffer.Write(xmlHeader)
   173  	buffer.WriteString(snippet)
   174  	buffer.Write(xmlFooter)
   175  
   176  	content[fmt.Sprintf("%s.conf", securityTag)] = &osutil.FileState{
   177  		Content: buffer.Bytes(),
   178  		Mode:    0644,
   179  	}
   180  }
   181  
   182  func (b *Backend) NewSpecification() interfaces.Specification {
   183  	return &Specification{}
   184  }
   185  
   186  // SandboxFeatures returns list of features supported by snapd for dbus communication.
   187  func (b *Backend) SandboxFeatures() []string {
   188  	return []string{"mediated-bus-access"}
   189  }