blob: eb6e4e2854716d480474bb71ee074826e6869f7b [file] [log] [blame]
// Package modules provides fucntionality to install and sign Linux kernel modules.
package modules
import (
"bytes"
"encoding/binary"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
log "github.com/golang/glog"
"github.com/pkg/errors"
"cos.googlesource.com/cos/tools.git/src/pkg/utils"
)
const (
// PKEYIDPKCS7 is a constant defined in https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
PKEYIDPKCS7 = byte(2)
// magicNumber is a constant defined in https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
magicNumber = "~Module signature appended~\n"
)
var (
execCommand = exec.Command
)
// LoadModule loads a given kernel module to kernel.
func LoadModule(moduleName, modulePath string) error {
loaded, err := isModuleLoaded(moduleName)
if err != nil {
return errors.Wrapf(err, "failed to load module %s (%s)", moduleName, modulePath)
}
if loaded {
return nil
}
if err := loadModule(modulePath); err != nil {
return errors.Wrapf(err, "failed to load module %s (%s)", moduleName, modulePath)
}
return nil
}
// UpdateHostLdCache updates the ld cache on host.
func UpdateHostLdCache(hostRootDir, moduleLibDir string) error {
log.Info("Updating host's ld cache")
ldPath := filepath.Join(hostRootDir, "/etc/ld.so.conf")
f, err := os.OpenFile(ldPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return errors.Wrapf(err, "failed to open %s", ldPath)
}
defer f.Close()
if _, err := f.WriteString(moduleLibDir); err != nil {
return errors.Wrapf(err, "failed to write \"%s\" to %s", moduleLibDir, ldPath)
}
if err := execCommand("ldconfig", "-r", hostRootDir).Run(); err != nil {
return errors.Wrapf(err, "failed to run `ldconfig -r %s`", hostRootDir)
}
return nil
}
// LoadPublicKey loads the given public key to the secondary keyring.
func LoadPublicKey(keyName, keyPath string) error {
log.Infof("Loading %s to secondary system keyring", keyName)
keyBytes, err := ioutil.ReadFile(keyPath)
if err != nil {
return errors.Wrapf(err, "failed to read key %s", keyPath)
}
cmd := execCommand("/bin/keyctl", "padd", "asymmetric", keyName, "%keyring:.secondary_trusted_keys")
cmd.Stdin = bytes.NewBuffer(keyBytes)
if err := cmd.Run(); err != nil {
return errors.Wrapf(err, "failed to load %s to system keyring", keyName)
}
log.Infof("Successfully load key %s into secondary system keyring.", keyName)
return nil
}
// AppendSignature appends a raw PKCS#7 signature to the end of a given kernel module.
// This is basically the Go implementation of `scripts/sign-file -s` in Linux upstream.
func AppendSignature(outfilePath, modulefilePath, sigfilePath string) error {
tempFile, err := ioutil.TempFile("", "tempFile")
if err != nil {
return errors.Wrap(err, "failed to create temp file")
}
defer os.Remove(tempFile.Name())
defer tempFile.Close()
// Copy bytes of kernel module into the temp file.
modulefile, err := os.Open(modulefilePath)
if err != nil {
return errors.Wrapf(err, "failed to open file %s", modulefilePath)
}
defer modulefile.Close()
_, err = io.Copy(tempFile, modulefile)
if err != nil {
return errors.Wrap(err, "failed to copy file")
}
// Append bytes of module signature into the temp file.
sigfile, err := os.Open(sigfilePath)
if err != nil {
return errors.Wrapf(err, "failed to open file %s", sigfilePath)
}
defer sigfile.Close()
sigSize, err := io.Copy(tempFile, sigfile)
if err != nil {
return errors.Wrap(err, "failed to copy file")
}
// Append the marker and the PKCS#7 message.
// moduleSignature is the struct module_signature defined in
// https://github.com/torvalds/linux/blob/master/scripts/sign-file.c
moduleSignature := [12]byte{}
// moduleSignature[2] is the id_type of struct module_signature
moduleSignature[2] = PKEYIDPKCS7
// moduleSignature[8:12] is the sig_len of struct module_signature.
// Using BigEndian as the sig_len should be in network byte order.
binary.BigEndian.PutUint32(moduleSignature[8:12], uint32(sigSize))
_, err = tempFile.Write(moduleSignature[:])
if err != nil {
return errors.Wrapf(err, "failed to write to file %s", tempFile.Name())
}
_, err = tempFile.Write([]byte(magicNumber))
if err != nil {
return errors.Wrapf(err, "failed to write to file %s", tempFile.Name())
}
if err := tempFile.Close(); err != nil {
return errors.Wrapf(err, "failed to close file %s", tempFile.Name())
}
// Finally, move the outfile to specified location.
// It overwrites the original module file if we are appending in place.
if err := utils.MoveFile(tempFile.Name(), outfilePath); err != nil {
return errors.Wrapf(err, "failed to rename file from %s to %s", tempFile.Name(), outfilePath)
}
return nil
}
func isModuleLoaded(moduleName string) (bool, error) {
out, err := execCommand("lsmod").Output()
if err != nil {
return false, errors.Wrap(err, "failed to run command `lsmod`")
}
for _, line := range strings.Split(string(out), "\n") {
fields := strings.Fields(line)
if len(fields) > 0 && fields[0] == moduleName {
return true, nil
}
}
return false, nil
}
func loadModule(modulePath string) error {
if err := execCommand("insmod", modulePath).Run(); err != nil {
return errors.Wrapf(err, "failed to run command `insmod %s`", modulePath)
}
return nil
}