gitee.com/mysnapcore/mysnapd@v0.1.0/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  	"gitee.com/mysnapcore/mysnapd/dirs"
    46  	"gitee.com/mysnapcore/mysnapd/interfaces"
    47  	"gitee.com/mysnapcore/mysnapd/osutil"
    48  	"gitee.com/mysnapcore/mysnapd/snap"
    49  	"gitee.com/mysnapcore/mysnapd/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  // setupModules creates a conf file with list of kernel modules required by
    71  // given snap, writes it in /etc/modules-load.d/ directory and immediately
    72  // loads the modules using /sbin/modprobe. The devMode is ignored.
    73  func (b *Backend) setupModules(snapInfo *snap.Info, spec *Specification) error {
    74  	content, modules := deriveContent(spec, snapInfo)
    75  	// synchronize the content with the filesystem
    76  	glob := interfaces.SecurityTagGlob(snapInfo.InstanceName())
    77  	dir := dirs.SnapKModModulesDir
    78  	if err := os.MkdirAll(dir, 0755); err != nil {
    79  		return fmt.Errorf("cannot create directory for kmod files %q: %s", dir, err)
    80  	}
    81  
    82  	changed, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, content)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	if len(changed) > 0 {
    88  		b.loadModules(modules)
    89  	}
    90  	return nil
    91  }
    92  
    93  // setupModprobe creates a configuration file under /etc/modprobe.d/ according
    94  // to the specification: this allows to either specify the load parameters for
    95  // a module, or prevent it from being loaded.
    96  // TODO: consider whether
    97  // - a newly blocklisted module should get unloaded
    98  // - a module whose option change should get reloaded
    99  func (b *Backend) setupModprobe(snapInfo *snap.Info, spec *Specification) error {
   100  	dir := dirs.SnapKModModprobeDir
   101  	if err := os.MkdirAll(dir, 0755); err != nil {
   102  		return fmt.Errorf("cannot create directory for kmod files %q: %s", dir, err)
   103  	}
   104  
   105  	glob := interfaces.SecurityTagGlob(snapInfo.InstanceName())
   106  	dirContents := prepareModprobeDirContents(spec, snapInfo)
   107  	_, _, err := osutil.EnsureDirState(dirs.SnapKModModprobeDir, glob, dirContents)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // Setup will make the kmod backend generate the needed system files (such as
   116  // those under /etc/modules-load.d/ and /etc/modprobe.d/) and call the
   117  // appropriate system commands so that the desired kernel module configuration
   118  // will be applied.
   119  // The devMode is ignored.
   120  //
   121  // If the method fails it should be re-tried (with a sensible strategy) by the caller.
   122  func (b *Backend) Setup(snapInfo *snap.Info, confinement interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) error {
   123  	snapName := snapInfo.InstanceName()
   124  	// Get the snippets that apply to this snap
   125  	spec, err := repo.SnapSpecification(b.Name(), snapName)
   126  	if err != nil {
   127  		return fmt.Errorf("cannot obtain kmod specification for snap %q: %s", snapName, err)
   128  	}
   129  
   130  	err = b.setupModprobe(snapInfo, spec.(*Specification))
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	err = b.setupModules(snapInfo, spec.(*Specification))
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	return nil
   141  }
   142  
   143  // Remove removes modules config file specific to a given snap.
   144  //
   145  // This method should be called after removing a snap.
   146  //
   147  // If the method fails it should be re-tried (with a sensible strategy) by the caller.
   148  func (b *Backend) Remove(snapName string) error {
   149  	glob := interfaces.SecurityTagGlob(snapName)
   150  	var errors []error
   151  	if _, _, err := osutil.EnsureDirState(dirs.SnapKModModulesDir, glob, nil); err != nil {
   152  		errors = append(errors, err)
   153  	}
   154  
   155  	if _, _, err := osutil.EnsureDirState(dirs.SnapKModModprobeDir, glob, nil); err != nil {
   156  		errors = append(errors, err)
   157  	}
   158  
   159  	if len(errors) > 0 {
   160  		return fmt.Errorf("cannot remove kernel modules config files: %v", errors)
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func deriveContent(spec *Specification, snapInfo *snap.Info) (map[string]osutil.FileState, []string) {
   167  	if len(spec.modules) == 0 {
   168  		return nil, nil
   169  	}
   170  	content := make(map[string]osutil.FileState)
   171  	var modules []string
   172  	for k := range spec.modules {
   173  		modules = append(modules, k)
   174  	}
   175  	sort.Strings(modules)
   176  
   177  	var buffer bytes.Buffer
   178  	buffer.WriteString("# This file is automatically generated.\n")
   179  	for _, module := range modules {
   180  		buffer.WriteString(module)
   181  		buffer.WriteRune('\n')
   182  	}
   183  	content[fmt.Sprintf("%s.conf", snap.SecurityTag(snapInfo.InstanceName()))] = &osutil.MemoryFileState{
   184  		Content: buffer.Bytes(),
   185  		Mode:    0644,
   186  	}
   187  	return content, modules
   188  }
   189  
   190  func prepareModprobeDirContents(spec *Specification, snapInfo *snap.Info) map[string]osutil.FileState {
   191  	disallowedModules := spec.DisallowedModules()
   192  	if len(disallowedModules) == 0 && len(spec.moduleOptions) == 0 {
   193  		return nil
   194  	}
   195  
   196  	contents := "# Generated by snapd. Do not edit\n\n"
   197  	// First, write down the list of disallowed modules
   198  	for _, module := range disallowedModules {
   199  		contents += fmt.Sprintf("blacklist %s\n", module)
   200  	}
   201  	// Then, write down the module options
   202  	for module, options := range spec.moduleOptions {
   203  		contents += fmt.Sprintf("options %s %s\n", module, options)
   204  	}
   205  
   206  	fileName := fmt.Sprintf("%s.conf", snap.SecurityTag(snapInfo.InstanceName()))
   207  	return map[string]osutil.FileState{
   208  		fileName: &osutil.MemoryFileState{
   209  			Content: []byte(contents),
   210  			Mode:    0644,
   211  		},
   212  	}
   213  }
   214  
   215  func (b *Backend) NewSpecification() interfaces.Specification {
   216  	return &Specification{}
   217  }
   218  
   219  // SandboxFeatures returns the list of features supported by snapd for loading kernel modules.
   220  func (b *Backend) SandboxFeatures() []string {
   221  	return []string{"mediated-modprobe"}
   222  }