go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/pam.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package resources 5 6 import ( 7 "errors" 8 "io" 9 "strconv" 10 "strings" 11 12 "go.mondoo.com/cnquery/checksums" 13 "go.mondoo.com/cnquery/llx" 14 "go.mondoo.com/cnquery/providers-sdk/v1/plugin" 15 "go.mondoo.com/cnquery/providers/os/connection/shared" 16 "go.mondoo.com/cnquery/providers/os/resources/pam" 17 "go.mondoo.com/cnquery/types" 18 ) 19 20 const ( 21 defaultPamConf = "/etc/pam.conf" 22 defaultPamDir = "/etc/pam.d" 23 ) 24 25 func initPamConf(runtime *plugin.Runtime, args map[string]*llx.RawData) (map[string]*llx.RawData, plugin.Resource, error) { 26 if x, ok := args["path"]; ok { 27 path, ok := x.Value.(string) 28 if !ok { 29 return nil, nil, errors.New("wrong type for 'path' it must be a string") 30 } 31 32 f, err := CreateResource(runtime, "file", map[string]*llx.RawData{ 33 "path": llx.StringData(path), 34 }) 35 if err != nil { 36 return nil, nil, err 37 } 38 args["file"] = llx.ResourceData(f, "file") 39 delete(args, "path") 40 } 41 42 return args, nil, nil 43 } 44 45 func (s *mqlPamConf) id() (string, error) { 46 checksum := checksums.New 47 for i := range s.Files.Data { 48 path := s.Files.Data[i].(*mqlFile).Path.Data 49 checksum = checksum.Add(path) 50 } 51 52 return checksum.String(), nil 53 } 54 55 func (se *mqlPamConfServiceEntry) id() (string, error) { 56 ptype := se.PamType.Data 57 mod := se.Module.Data 58 s := se.Service.Data 59 ln := se.LineNumber.Data 60 lnstr := strconv.FormatInt(ln, 10) 61 62 id := s + "/" + lnstr + "/" + ptype 63 64 // for include mod is empty 65 if mod != "" { 66 id += "/" + mod 67 } 68 69 return id, nil 70 } 71 72 func (s *mqlPamConf) getFiles(confPath string) ([]interface{}, error) { 73 // check if the pam.d directory or pam config file exists 74 raw, err := CreateResource(s.MqlRuntime, "file", map[string]*llx.RawData{ 75 "path": llx.StringData(confPath), 76 }) 77 if err != nil { 78 return nil, err 79 } 80 f := raw.(*mqlFile) 81 exists := f.GetExists() 82 if exists.Error != nil { 83 return nil, exists.Error 84 } 85 86 if !exists.Data { 87 return nil, errors.New(" could not load pam configuration: " + confPath) 88 } 89 90 perm := f.GetPermissions() 91 if perm.Error != nil { 92 return nil, perm.Error 93 } 94 95 if perm.Data.IsDirectory.Data { 96 return s.getConfDFiles(confPath) 97 } else { 98 return []interface{}{f}, nil 99 } 100 } 101 102 func (s *mqlPamConf) getConfDFiles(confD string) ([]interface{}, error) { 103 files, err := CreateResource(s.MqlRuntime, "files.find", map[string]*llx.RawData{ 104 "from": llx.StringData(confD), 105 "type": llx.StringData("file"), 106 }) 107 if err != nil { 108 return nil, err 109 } 110 111 res := files.(*mqlFilesFind).GetList() 112 return res.Data, res.Error 113 } 114 115 // GetFiles is called when the user has not provided a custom path. Otherwise files are set in the init 116 // method and this function is never called then since the data is already cached. 117 func (s *mqlPamConf) files() ([]interface{}, error) { 118 // check if the pam.d directory exists and is a directory 119 // according to the pam spec, pam prefers the directory if it exists over the single file config 120 // see http://www.linux-pam.org/Linux-PAM-html/sag-configuration.html 121 raw, err := CreateResource(s.MqlRuntime, "file", map[string]*llx.RawData{ 122 "path": llx.StringData(defaultPamDir), 123 }) 124 if err != nil { 125 return nil, err 126 } 127 f := raw.(*mqlFile) 128 exist := f.GetExists() 129 if exist.Error != nil { 130 return nil, exist.Error 131 } 132 133 if exist.Data { 134 return s.getFiles(defaultPamDir) 135 } else { 136 return s.getFiles(defaultPamConf) 137 } 138 } 139 140 func (s *mqlPamConf) content(files []interface{}) (string, error) { 141 conn := s.MqlRuntime.Connection.(shared.Connection) 142 143 var res strings.Builder 144 var notReadyError error = nil 145 146 for i := range files { 147 file := files[i].(*mqlFile) 148 149 f, err := conn.FileSystem().Open(file.Path.Data) 150 if err != nil { 151 return "", err 152 } 153 154 raw, err := io.ReadAll(f) 155 f.Close() 156 if err != nil { 157 return "", err 158 } 159 160 res.WriteString(string(raw)) 161 res.WriteString("\n") 162 } 163 164 if notReadyError != nil { 165 return "", notReadyError 166 } 167 168 return res.String(), nil 169 } 170 171 func (s *mqlPamConf) services(files []interface{}) (map[string]interface{}, error) { 172 conn := s.MqlRuntime.Connection.(shared.Connection) 173 174 contents := map[string]string{} 175 var notReadyError error = nil 176 177 for i := range files { 178 file := files[i].(*mqlFile) 179 180 f, err := conn.FileSystem().Open(file.Path.Data) 181 if err != nil { 182 return nil, err 183 } 184 185 raw, err := io.ReadAll(f) 186 f.Close() 187 if err != nil { 188 return nil, err 189 } 190 191 contents[file.Path.Data] = string(raw) 192 } 193 194 if notReadyError != nil { 195 return nil, notReadyError 196 } 197 198 services := map[string]interface{}{} 199 for basename, content := range contents { 200 lines := strings.Split(content, "\n") 201 settings := []interface{}{} 202 var line string 203 for i := range lines { 204 line = lines[i] 205 206 if idx := strings.Index(line, "#"); idx >= 0 { 207 line = line[0:idx] 208 } 209 line = strings.Trim(line, " \t\r") 210 211 if line != "" { 212 settings = append(settings, line) 213 } 214 } 215 services[basename] = settings 216 } 217 218 return services, nil 219 } 220 221 func (s *mqlPamConf) entries(files []interface{}) (map[string]interface{}, error) { 222 conn := s.MqlRuntime.Connection.(shared.Connection) 223 224 contents := map[string]string{} 225 var notReadyError error = nil 226 227 for i := range files { 228 file := files[i].(*mqlFile) 229 230 f, err := conn.FileSystem().Open(file.Path.Data) 231 if err != nil { 232 return nil, err 233 } 234 235 raw, err := io.ReadAll(f) 236 f.Close() 237 if err != nil { 238 return nil, err 239 } 240 241 contents[file.Path.Data] = string(raw) 242 } 243 244 if notReadyError != nil { 245 return nil, notReadyError 246 } 247 248 services := map[string]interface{}{} 249 for basename, content := range contents { 250 lines := strings.Split(content, "\n") 251 settings := []interface{}{} 252 var line string 253 for i := range lines { 254 line = lines[i] 255 256 entry, err := pam.ParseLine(line) 257 if err != nil { 258 return nil, err 259 } 260 261 // empty lines parse as empty object 262 if entry == nil { 263 continue 264 } 265 266 pamEntry, err := CreateResource(s.MqlRuntime, "pam.conf.serviceEntry", map[string]*llx.RawData{ 267 "service": llx.StringData(basename), 268 "lineNumber": llx.IntData(int64(i)), // Used for ID 269 "pamType": llx.StringData(entry.PamType), 270 "control": llx.StringData(entry.Control), 271 "module": llx.StringData(entry.Module), 272 "options": llx.ArrayData(entry.Options, types.String), 273 }) 274 if err != nil { 275 return nil, err 276 } 277 settings = append(settings, pamEntry.(*mqlPamConfServiceEntry)) 278 279 } 280 281 services[basename] = settings 282 } 283 284 return services, nil 285 }