go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/kernel/manager.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package kernel
     5  
     6  import (
     7  	"io"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/cockroachdb/errors"
    12  	"github.com/rs/zerolog/log"
    13  	"github.com/spf13/afero"
    14  	"go.mondoo.com/cnquery/providers/os/connection/shared"
    15  )
    16  
    17  const sysctlPath = "/proc/sys/"
    18  
    19  type KernelInfo struct {
    20  	Version   string            `json:"version"`
    21  	Path      string            `json:"path"`
    22  	Device    string            `json:"device"`
    23  	Arguments map[string]string `json:"args"`
    24  }
    25  
    26  type KernelModule struct {
    27  	Name   string
    28  	Size   string // int64
    29  	UsedBy string // int
    30  }
    31  
    32  type OSKernelManager interface {
    33  	Name() string
    34  	Parameters() (map[string]string, error)
    35  	Modules() ([]*KernelModule, error)
    36  	Info() (KernelInfo, error)
    37  }
    38  
    39  func ResolveManager(conn shared.Connection) (OSKernelManager, error) {
    40  	var kmm OSKernelManager
    41  
    42  	platform := conn.Asset().Platform
    43  
    44  	// check darwin before unix since darwin is also a unix
    45  	if platform.IsFamily("darwin") {
    46  		kmm = &OSXKernelManager{conn: conn}
    47  	} else if platform.IsFamily("linux") {
    48  		kmm = &LinuxKernelManager{conn: conn}
    49  	} else if platform.Name == "freebsd" {
    50  		// NOTE: kldstat may work on other bsd linux
    51  		kmm = &FreebsdKernelManager{conn: conn}
    52  	}
    53  
    54  	if kmm == nil {
    55  		return nil, errors.New("could not detect suitable kernel module manager for platform: " + platform.Name)
    56  	}
    57  
    58  	return kmm, nil
    59  }
    60  
    61  type LinuxKernelManager struct {
    62  	conn shared.Connection
    63  }
    64  
    65  func (s *LinuxKernelManager) Name() string {
    66  	return "Linux Kernel Module Manager"
    67  }
    68  
    69  func (s *LinuxKernelManager) Info() (KernelInfo, error) {
    70  	res := KernelInfo{}
    71  
    72  	cmdlineRaw, err := s.conn.FileSystem().Open("/proc/cmdline")
    73  	if err != nil {
    74  		return res, err
    75  	}
    76  	defer cmdlineRaw.Close()
    77  
    78  	args, err := ParseLinuxKernelArguments(cmdlineRaw)
    79  	if err != nil {
    80  		return res, err
    81  	}
    82  	res.Path = args.Path
    83  	res.Device = args.Device
    84  	res.Arguments = args.Arguments
    85  
    86  	versionRaw, err := s.conn.FileSystem().Open("/proc/version")
    87  	if err != nil {
    88  		return res, err
    89  	}
    90  	defer versionRaw.Close()
    91  
    92  	version, err := ParseLinuxKernelVersion(versionRaw)
    93  	if err != nil {
    94  		return res, err
    95  	}
    96  
    97  	res.Version = version
    98  
    99  	return res, nil
   100  }
   101  
   102  func (s *LinuxKernelManager) Parameters() (map[string]string, error) {
   103  	if s.conn.Capabilities().Has(shared.Capability_RunCommand) {
   104  		cmd, err := s.conn.RunCommand("/sbin/sysctl -a")
   105  		// in case of err, the command is not there and we fallback to /proc/sys walking
   106  		if err == nil && cmd.ExitStatus == 0 {
   107  			log.Debug().Msg("using sysctl to read kernel parameters")
   108  			return ParseSysctl(cmd.Stdout, "=")
   109  		}
   110  	}
   111  
   112  	log.Debug().Msg("using /proc/sys walking to read kernel parameters")
   113  	fs := s.conn.FileSystem()
   114  	fsUtil := afero.Afero{Fs: fs}
   115  	kernelParameters := make(map[string]string)
   116  	err := fsUtil.Walk(sysctlPath, func(path string, f os.FileInfo, err error) error {
   117  		if f != nil && !f.IsDir() {
   118  			stat, err := s.conn.FileSystem().Stat(path)
   119  			if err != nil {
   120  				log.Error().Err(err)
   121  				return nil
   122  			}
   123  			details := shared.FileModeDetails{
   124  				FileMode: stat.Mode(),
   125  			}
   126  			if !details.UserReadable() {
   127  				return nil
   128  			}
   129  
   130  			f, err := s.conn.FileSystem().Open(path)
   131  			if err != nil {
   132  				log.Error().Err(err)
   133  				return err
   134  			}
   135  
   136  			content, err := io.ReadAll(f)
   137  			if err != nil {
   138  				log.Error().Err(err).Msg("cannot read content")
   139  				return nil
   140  			}
   141  			// remove leading sysctl path
   142  			k := strings.Replace(path, sysctlPath, "", -1)
   143  			k = strings.Replace(k, "/", ".", -1)
   144  			kernelParameters[k] = strings.TrimSpace(string(content))
   145  		}
   146  		return nil
   147  	})
   148  
   149  	return kernelParameters, err
   150  }
   151  
   152  func (s *LinuxKernelManager) Modules() ([]*KernelModule, error) {
   153  	// TODO: use proc in future
   154  	cmd, err := s.conn.RunCommand("/sbin/lsmod")
   155  	if err != nil {
   156  		return nil, errors.Wrap(err, "could not read kernel modules")
   157  	}
   158  
   159  	return ParseLsmod(cmd.Stdout), nil
   160  }
   161  
   162  type OSXKernelManager struct {
   163  	conn shared.Connection
   164  }
   165  
   166  func (s *OSXKernelManager) Name() string {
   167  	return "macOS Kernel Manager"
   168  }
   169  
   170  func (s *OSXKernelManager) Info() (KernelInfo, error) {
   171  	cmd, err := s.conn.RunCommand("uname -r")
   172  	if err != nil {
   173  		return KernelInfo{}, errors.Wrap(err, "could not read kernel parameters")
   174  	}
   175  
   176  	data, err := io.ReadAll(cmd.Stdout)
   177  	if err != nil {
   178  		return KernelInfo{}, errors.Wrap(err, "could not read kernel parameters")
   179  	}
   180  
   181  	return KernelInfo{
   182  		Version: strings.TrimSpace(string(data)),
   183  	}, nil
   184  }
   185  
   186  func (s *OSXKernelManager) Parameters() (map[string]string, error) {
   187  	cmd, err := s.conn.RunCommand("sysctl -a")
   188  	if err != nil {
   189  		return nil, errors.Wrap(err, "could not read kernel parameters")
   190  	}
   191  
   192  	return ParseSysctl(cmd.Stdout, ":")
   193  }
   194  
   195  func (s *OSXKernelManager) Modules() ([]*KernelModule, error) {
   196  	cmd, err := s.conn.RunCommand("kextstat")
   197  	if err != nil {
   198  		return nil, errors.Wrap(err, "could not read kernel modules")
   199  	}
   200  
   201  	return ParseKextstat(cmd.Stdout), nil
   202  }
   203  
   204  type FreebsdKernelManager struct {
   205  	conn shared.Connection
   206  }
   207  
   208  func (s *FreebsdKernelManager) Name() string {
   209  	return "Freebsd Kernel Manager"
   210  }
   211  
   212  func (s *FreebsdKernelManager) Info() (KernelInfo, error) {
   213  	return KernelInfo{}, nil
   214  }
   215  
   216  func (s *FreebsdKernelManager) Parameters() (map[string]string, error) {
   217  	cmd, err := s.conn.RunCommand("sysctl -a")
   218  	if err != nil {
   219  		return nil, errors.Wrap(err, "could not read kernel parameters")
   220  	}
   221  
   222  	return ParseSysctl(cmd.Stdout, ":")
   223  }
   224  
   225  func (s *FreebsdKernelManager) Modules() ([]*KernelModule, error) {
   226  	cmd, err := s.conn.RunCommand("kldstat")
   227  	if err != nil {
   228  		return nil, errors.Wrap(err, "could not read kernel modules")
   229  	}
   230  
   231  	return ParseKldstat(cmd.Stdout), nil
   232  }