github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/kmod/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 kmod implements a backend which loads kernel modules on behalf of 21 // interfaces. 22 // 23 // Interfaces may request kernel modules to be loaded by providing snippets via 24 // their respective "*Snippet" methods for interfaces.SecurityKMod security 25 // system. The snippet should contain a newline-separated list of requested 26 // kernel modules. The KMod backend stores all the modules needed by given 27 // snap in /etc/modules-load.d/snap.<snapname>.conf file ensuring they are 28 // loaded when the system boots and also loads these modules via modprobe. 29 // If a snap is uninstalled or respective interface gets disconnected, the 30 // corresponding /etc/modules-load.d/ config file gets removed, however no 31 // kernel modules are unloaded. This is by design. 32 // 33 // Note: this mechanism should not be confused with kernel-module-interface; 34 // kmod only loads a well-defined list of modules provided by interface definition 35 // and doesn't grant any special permissions related to kernel modules to snaps, 36 // in contrast to kernel-module-interface. 37 package kmod 38 39 import ( 40 "bytes" 41 "fmt" 42 "os" 43 "sort" 44 45 "github.com/snapcore/snapd/dirs" 46 "github.com/snapcore/snapd/interfaces" 47 "github.com/snapcore/snapd/osutil" 48 "github.com/snapcore/snapd/snap" 49 "github.com/snapcore/snapd/timings" 50 ) 51 52 // Backend is responsible for maintaining kernel modules 53 type Backend struct { 54 preseed bool 55 } 56 57 // Initialize does nothing. 58 func (b *Backend) Initialize(opts *interfaces.SecurityBackendOptions) error { 59 if opts != nil && opts.Preseed { 60 b.preseed = true 61 } 62 return nil 63 } 64 65 // Name returns the name of the backend. 66 func (b *Backend) Name() interfaces.SecuritySystem { 67 return "kmod" 68 } 69 70 // Setup creates a conf file with list of kernel modules required by given snap, 71 // writes it in /etc/modules-load.d/ directory and immediately loads the modules 72 // using /sbin/modprobe. The devMode is ignored. 73 // 74 // If the method fails it should be re-tried (with a sensible strategy) by the caller. 75 func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error { 76 snapName := snapInfo.InstanceName() 77 // Get the snippets that apply to this snap 78 spec, err := repo.SnapSpecification(b.Name(), snapName) 79 if err != nil { 80 return fmt.Errorf("cannot obtain kmod specification for snap %q: %s", snapName, err) 81 } 82 83 content, modules := deriveContent(spec.(*Specification), snapInfo) 84 // synchronize the content with the filesystem 85 glob := interfaces.SecurityTagGlob(snapName) 86 dir := dirs.SnapKModModulesDir 87 if err := os.MkdirAll(dir, 0755); err != nil { 88 return fmt.Errorf("cannot create directory for kmod files %q: %s", dir, err) 89 } 90 91 changed, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, content) 92 if err != nil { 93 return err 94 } 95 96 if len(changed) > 0 { 97 b.loadModules(modules) 98 } 99 return nil 100 } 101 102 // Remove removes modules config file specific to a given snap. 103 // 104 // This method should be called after removing a snap. 105 // 106 // If the method fails it should be re-tried (with a sensible strategy) by the caller. 107 func (b *Backend) Remove(snapName string) error { 108 glob := interfaces.SecurityTagGlob(snapName) 109 _, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, nil) 110 return err 111 } 112 113 func deriveContent(spec *Specification, snapInfo *snap.Info) (map[string]osutil.FileState, []string) { 114 if len(spec.modules) == 0 { 115 return nil, nil 116 } 117 content := make(map[string]osutil.FileState) 118 var modules []string 119 for k := range spec.modules { 120 modules = append(modules, k) 121 } 122 sort.Strings(modules) 123 124 var buffer bytes.Buffer 125 buffer.WriteString("# This file is automatically generated.\n") 126 for _, module := range modules { 127 buffer.WriteString(module) 128 buffer.WriteRune('\n') 129 } 130 content[fmt.Sprintf("%s.conf", snap.SecurityTag(snapInfo.InstanceName()))] = &osutil.MemoryFileState{ 131 Content: buffer.Bytes(), 132 Mode: 0644, 133 } 134 return content, modules 135 } 136 137 func (b *Backend) NewSpecification() interfaces.Specification { 138 return &Specification{} 139 } 140 141 // SandboxFeatures returns the list of features supported by snapd for loading kernel modules. 142 func (b *Backend) SandboxFeatures() []string { 143 return []string{"mediated-modprobe"} 144 }