gitee.com/mysnapcore/mysnapd@v0.1.0/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  	"gitee.com/mysnapcore/mysnapd/dirs"
    37  	"gitee.com/mysnapcore/mysnapd/interfaces"
    38  	"gitee.com/mysnapcore/mysnapd/logger"
    39  	"gitee.com/mysnapcore/mysnapd/osutil"
    40  	"gitee.com/mysnapcore/mysnapd/release"
    41  	"gitee.com/mysnapcore/mysnapd/snap"
    42  	"gitee.com/mysnapcore/mysnapd/snapdtool"
    43  	"gitee.com/mysnapcore/mysnapd/timings"
    44  	"gitee.com/mysnapcore/mysnapd/wrappers"
    45  )
    46  
    47  // Backend is responsible for maintaining DBus policy files.
    48  type Backend struct{}
    49  
    50  // Initialize does nothing.
    51  func (b *Backend) Initialize(*interfaces.SecurityBackendOptions) error {
    52  	return nil
    53  }
    54  
    55  // Name returns the name of the backend.
    56  func (b *Backend) Name() interfaces.SecuritySystem {
    57  	return "dbus"
    58  }
    59  
    60  func shouldCopyConfigFiles(snapInfo *snap.Info) bool {
    61  	// Only copy config files on classic distros
    62  	if !release.OnClassic {
    63  		return false
    64  	}
    65  	// Only copy config files if we have been reexecuted
    66  	if reexecd, _ := snapdtool.IsReexecd(); !reexecd {
    67  		return false
    68  	}
    69  	switch snapInfo.Type() {
    70  	case snap.TypeOS:
    71  		// XXX: ugly but we need to make sure that the content
    72  		// of the "snapd" snap wins
    73  		//
    74  		// TODO: this is also racy but the content of the
    75  		// files in core and snapd is identical.  Cleanup
    76  		// after link-snap and setup-profiles are unified
    77  		return !osutil.FileExists(filepath.Join(snapInfo.MountDir(), "../..", "snapd/current"))
    78  	case snap.TypeSnapd:
    79  		return true
    80  	default:
    81  		return false
    82  	}
    83  }
    84  
    85  // setupDbusServiceForUserd will setup the service file for the new
    86  // `snap userd` instance on re-exec
    87  func setupDbusServiceForUserd(snapInfo *snap.Info) error {
    88  	coreOrSnapdRoot := snapInfo.MountDir()
    89  
    90  	for _, srv := range []string{
    91  		"io.snapcraft.Launcher.service",
    92  		"io.snapcraft.Prompt.service",
    93  		"io.snapcraft.Settings.service",
    94  	} {
    95  		dst := filepath.Join("/usr/share/dbus-1/services/", srv)
    96  		src := filepath.Join(coreOrSnapdRoot, dst)
    97  
    98  		// we only need the GlobalRootDir for testing
    99  		dst = filepath.Join(dirs.GlobalRootDir, dst)
   100  		if !osutil.FilesAreEqual(src, dst) {
   101  			if err := osutil.CopyFile(src, dst, osutil.CopyFlagPreserveAll); err != nil {
   102  				return err
   103  			}
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  func setupHostDBusConf(snapInfo *snap.Info) error {
   110  	sessionContent, systemContent, err := wrappers.DeriveSnapdDBusConfig(snapInfo)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	// We don't use `dirs.SnapDBusSessionPolicyDir because we want
   116  	// to match the path the package on the host system uses.
   117  	dest := filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/session.d")
   118  	if err = os.MkdirAll(dest, 0755); err != nil {
   119  		return err
   120  	}
   121  	_, _, err = osutil.EnsureDirState(dest, "snapd.*.conf", sessionContent)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	dest = filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/system.d")
   127  	if err = os.MkdirAll(dest, 0755); err != nil {
   128  		return err
   129  	}
   130  	_, _, err = osutil.EnsureDirState(dest, "snapd.*.conf", systemContent)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  // Setup creates dbus configuration files specific to a given snap.
   139  //
   140  // DBus has no concept of a complain mode so confinment type is ignored.
   141  func (b *Backend) Setup(snapInfo *snap.Info, opts interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
   142  	snapName := snapInfo.InstanceName()
   143  	// Get the snippets that apply to this snap
   144  	spec, err := repo.SnapSpecification(b.Name(), snapName)
   145  	if err != nil {
   146  		return fmt.Errorf("cannot obtain dbus specification for snap %q: %s", snapName, err)
   147  	}
   148  
   149  	// copy some config files when installing core/snapd if we reexec
   150  	if shouldCopyConfigFiles(snapInfo) {
   151  		if err := setupDbusServiceForUserd(snapInfo); err != nil {
   152  			logger.Noticef("cannot create host `snap userd` dbus service file: %s", err)
   153  		}
   154  		// TODO: Make this conditional on the dbus-activation
   155  		// feature flag.
   156  		if err := setupHostDBusConf(snapInfo); err != nil {
   157  			logger.Noticef("cannot create host dbus config: %s", err)
   158  		}
   159  	}
   160  
   161  	// Get the files that this snap should have
   162  	content := b.deriveContent(spec.(*Specification), snapInfo)
   163  
   164  	glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName))
   165  	dir := dirs.SnapDBusSystemPolicyDir
   166  	if err := os.MkdirAll(dir, 0755); err != nil {
   167  		return fmt.Errorf("cannot create directory for DBus configuration files %q: %s", dir, err)
   168  	}
   169  	_, _, err = osutil.EnsureDirState(dir, glob, content)
   170  	if err != nil {
   171  		return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err)
   172  	}
   173  	return nil
   174  }
   175  
   176  // Remove removes dbus configuration files of a given snap.
   177  //
   178  // This method should be called after removing a snap.
   179  func (b *Backend) Remove(snapName string) error {
   180  	glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName))
   181  	_, _, err := osutil.EnsureDirState(dirs.SnapDBusSystemPolicyDir, glob, nil)
   182  	if err != nil {
   183  		return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err)
   184  	}
   185  	return nil
   186  }
   187  
   188  // deriveContent combines security snippets collected from all the interfaces
   189  // affecting a given snap into a content map applicable to EnsureDirState.
   190  func (b *Backend) deriveContent(spec *Specification, snapInfo *snap.Info) (content map[string]osutil.FileState) {
   191  	for _, appInfo := range snapInfo.Apps {
   192  		securityTag := appInfo.SecurityTag()
   193  		appSnippets := spec.SnippetForTag(securityTag)
   194  		if appSnippets == "" {
   195  			continue
   196  		}
   197  		if content == nil {
   198  			content = make(map[string]osutil.FileState)
   199  		}
   200  
   201  		addContent(securityTag, appSnippets, content)
   202  	}
   203  
   204  	for _, hookInfo := range snapInfo.Hooks {
   205  		securityTag := hookInfo.SecurityTag()
   206  		hookSnippets := spec.SnippetForTag(securityTag)
   207  		if hookSnippets == "" {
   208  			continue
   209  		}
   210  		if content == nil {
   211  			content = make(map[string]osutil.FileState)
   212  		}
   213  
   214  		addContent(securityTag, hookSnippets, content)
   215  	}
   216  
   217  	return content
   218  }
   219  
   220  func addContent(securityTag string, snippet string, content map[string]osutil.FileState) {
   221  	var buffer bytes.Buffer
   222  	buffer.Write(xmlHeader)
   223  	buffer.WriteString(snippet)
   224  	buffer.Write(xmlFooter)
   225  
   226  	content[fmt.Sprintf("%s.conf", securityTag)] = &osutil.MemoryFileState{
   227  		Content: buffer.Bytes(),
   228  		Mode:    0644,
   229  	}
   230  }
   231  
   232  func (b *Backend) NewSpecification() interfaces.Specification {
   233  	return &Specification{}
   234  }
   235  
   236  // SandboxFeatures returns list of features supported by snapd for dbus communication.
   237  func (b *Backend) SandboxFeatures() []string {
   238  	return []string{"mediated-bus-access"}
   239  }