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 }