github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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/snapdtool" 43 "github.com/snapcore/snapd/timings" 44 "github.com/snapcore/snapd/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.Settings.service", 93 } { 94 dst := filepath.Join("/usr/share/dbus-1/services/", srv) 95 src := filepath.Join(coreOrSnapdRoot, dst) 96 97 // we only need the GlobalRootDir for testing 98 dst = filepath.Join(dirs.GlobalRootDir, dst) 99 if !osutil.FilesAreEqual(src, dst) { 100 if err := osutil.CopyFile(src, dst, osutil.CopyFlagPreserveAll); err != nil { 101 return err 102 } 103 } 104 } 105 return nil 106 } 107 108 func setupHostDBusConf(snapInfo *snap.Info) error { 109 sessionContent, systemContent, err := wrappers.DeriveSnapdDBusConfig(snapInfo) 110 if err != nil { 111 return err 112 } 113 114 // We don't use `dirs.SnapDBusSessionPolicyDir because we want 115 // to match the path the package on the host system uses. 116 dest := filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/session.d") 117 if err = os.MkdirAll(dest, 0755); err != nil { 118 return err 119 } 120 _, _, err = osutil.EnsureDirState(dest, "snapd.*.conf", sessionContent) 121 if err != nil { 122 return err 123 } 124 125 dest = filepath.Join(dirs.GlobalRootDir, "/usr/share/dbus-1/system.d") 126 if err = os.MkdirAll(dest, 0755); err != nil { 127 return err 128 } 129 _, _, err = osutil.EnsureDirState(dest, "snapd.*.conf", systemContent) 130 if err != nil { 131 return err 132 } 133 134 return nil 135 } 136 137 // Setup creates dbus configuration files specific to a given snap. 138 // 139 // DBus has no concept of a complain mode so confinment type is ignored. 140 func (b *Backend) Setup(snapInfo *snap.Info, opts interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error { 141 snapName := snapInfo.InstanceName() 142 // Get the snippets that apply to this snap 143 spec, err := repo.SnapSpecification(b.Name(), snapName) 144 if err != nil { 145 return fmt.Errorf("cannot obtain dbus specification for snap %q: %s", snapName, err) 146 } 147 148 // copy some config files when installing core/snapd if we reexec 149 if shouldCopyConfigFiles(snapInfo) { 150 if err := setupDbusServiceForUserd(snapInfo); err != nil { 151 logger.Noticef("cannot create host `snap userd` dbus service file: %s", err) 152 } 153 // TODO: Make this conditional on the dbus-activation 154 // feature flag. 155 if err := setupHostDBusConf(snapInfo); err != nil { 156 logger.Noticef("cannot create host dbus config: %s", err) 157 } 158 } 159 160 // Get the files that this snap should have 161 content, err := b.deriveContent(spec.(*Specification), snapInfo) 162 if err != nil { 163 return fmt.Errorf("cannot obtain expected DBus configuration files for snap %q: %s", snapName, err) 164 } 165 glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName)) 166 dir := dirs.SnapDBusSystemPolicyDir 167 if err := os.MkdirAll(dir, 0755); err != nil { 168 return fmt.Errorf("cannot create directory for DBus configuration files %q: %s", dir, err) 169 } 170 _, _, err = osutil.EnsureDirState(dir, glob, content) 171 if err != nil { 172 return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err) 173 } 174 return nil 175 } 176 177 // Remove removes dbus configuration files of a given snap. 178 // 179 // This method should be called after removing a snap. 180 func (b *Backend) Remove(snapName string) error { 181 glob := fmt.Sprintf("%s.conf", interfaces.SecurityTagGlob(snapName)) 182 _, _, err := osutil.EnsureDirState(dirs.SnapDBusSystemPolicyDir, glob, nil) 183 if err != nil { 184 return fmt.Errorf("cannot synchronize DBus configuration files for snap %q: %s", snapName, err) 185 } 186 return nil 187 } 188 189 // deriveContent combines security snippets collected from all the interfaces 190 // affecting a given snap into a content map applicable to EnsureDirState. 191 func (b *Backend) deriveContent(spec *Specification, snapInfo *snap.Info) (content map[string]osutil.FileState, err error) { 192 for _, appInfo := range snapInfo.Apps { 193 securityTag := appInfo.SecurityTag() 194 appSnippets := spec.SnippetForTag(securityTag) 195 if appSnippets == "" { 196 continue 197 } 198 if content == nil { 199 content = make(map[string]osutil.FileState) 200 } 201 202 addContent(securityTag, appSnippets, content) 203 } 204 205 for _, hookInfo := range snapInfo.Hooks { 206 securityTag := hookInfo.SecurityTag() 207 hookSnippets := spec.SnippetForTag(securityTag) 208 if hookSnippets == "" { 209 continue 210 } 211 if content == nil { 212 content = make(map[string]osutil.FileState) 213 } 214 215 addContent(securityTag, hookSnippets, content) 216 } 217 218 return content, nil 219 } 220 221 func addContent(securityTag string, snippet string, content map[string]osutil.FileState) { 222 var buffer bytes.Buffer 223 buffer.Write(xmlHeader) 224 buffer.WriteString(snippet) 225 buffer.Write(xmlFooter) 226 227 content[fmt.Sprintf("%s.conf", securityTag)] = &osutil.MemoryFileState{ 228 Content: buffer.Bytes(), 229 Mode: 0644, 230 } 231 } 232 233 func (b *Backend) NewSpecification() interfaces.Specification { 234 return &Specification{} 235 } 236 237 // SandboxFeatures returns list of features supported by snapd for dbus communication. 238 func (b *Backend) SandboxFeatures() []string { 239 return []string{"mediated-bus-access"} 240 }