| // 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" |
| SecondaryKeyring = "%keyring:.secondary_trusted_keys" |
| IMAKeyring = "%keyring:.ima" |
| ) |
| |
| var ( |
| execCommand = exec.Command |
| ) |
| |
| type Module struct { |
| Name string |
| Path string |
| Deps []*Module |
| SkipNotFound bool |
| } |
| |
| // LoadModule loads a given kernel module to kernel. |
| func LoadModule(module *Module, moduleParams ModuleParameters) error { |
| if module.SkipNotFound { |
| if exists, _ := utils.CheckFileExists(module.Path); !exists { |
| log.V(2).Infof("skipping module %s as it does not exist", module.Name) |
| return nil |
| } |
| } |
| loaded, err := isModuleLoaded(module.Name) |
| if err != nil { |
| return errors.Wrapf(err, "failed to load module %s (%s)", module.Name, module.Path) |
| } |
| if loaded { |
| return nil |
| } |
| // load Module dependencies before module |
| for _, dep := range module.Deps { |
| if err := LoadModule(dep, moduleParams); err != nil { |
| return err |
| } |
| } |
| if err := loadModule(module.Path, moduleParams[module.Name]); err != nil { |
| return errors.Wrapf(err, "failed to load module %s (%s)", module.Name, module.Path) |
| } |
| 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 + "\n"); 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 system keyring. |
| func LoadPublicKey(keyName, keyPath, keyring string) error { |
| log.Infof("Loading %s to keyring %s", keyName, keyring) |
| |
| 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) |
| cmd.Stdin = bytes.NewBuffer(keyBytes) |
| if err := cmd.Run(); err != nil { |
| return errors.Wrapf(err, "failed to load %s to keyring %s", keyName, keyring) |
| } |
| log.Infof("Successfully load key %s into keyring %s.", keyName, keyring) |
| 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, moduleParams []string) error { |
| insmodArgs := append([]string{modulePath}, moduleParams...) |
| cmd := execCommand("insmod", insmodArgs...) |
| log.Infof("loading module: %v", cmd) |
| if err := cmd.Run(); err != nil { |
| return errors.Wrapf(err, "failed to run command `insmod %v`", insmodArgs) |
| } |
| return nil |
| } |