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  }