github.com/google/osv-scalibr@v0.4.1/detector/cis/generic_linux/etcpasswdpermissions/etcpasswdpermissions.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build linux || darwin 16 17 // Package etcpasswdpermissions implements a detector for the "Ensure permissions on /etc/passwd- are configured" CIS check. 18 package etcpasswdpermissions 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "io/fs" 25 "os" 26 "syscall" 27 28 "github.com/google/osv-scalibr/detector" 29 scalibrfs "github.com/google/osv-scalibr/fs" 30 "github.com/google/osv-scalibr/inventory" 31 "github.com/google/osv-scalibr/packageindex" 32 "github.com/google/osv-scalibr/plugin" 33 ) 34 35 const ( 36 // Name of the detector. 37 Name = "cis/generic-linux/etcpasswdpermissions" 38 ) 39 40 // Detector is a SCALIBR Detector for the CIS check "Ensure permissions on /etc/passwd- are configured" 41 // from the CIS Distribution Independent Linux benchmarks. 42 type Detector struct{} 43 44 // New returns a detector. 45 func New() detector.Detector { 46 return &Detector{} 47 } 48 49 // Name of the detector. 50 func (Detector) Name() string { return Name } 51 52 // Version of the detector. 53 func (Detector) Version() int { return 0 } 54 55 // RequiredExtractors returns an empty list as there are no dependencies. 56 func (Detector) RequiredExtractors() []string { return []string{} } 57 58 // Requirements of the Detector. 59 func (Detector) Requirements() *plugin.Capabilities { return &plugin.Capabilities{OS: plugin.OSUnix} } 60 61 // Scan starts the scan. 62 func (d Detector) Scan(ctx context.Context, scanRoot *scalibrfs.ScanRoot, px *packageindex.PackageIndex) (inventory.Finding, error) { 63 return d.ScanFS(ctx, scanRoot.FS, px) 64 } 65 66 // DetectedFinding returns generic vulnerability information about what is detected. 67 func (d Detector) DetectedFinding() inventory.Finding { 68 return d.findingForTarget(nil) 69 } 70 71 func (Detector) findingForTarget(target *inventory.GenericFindingTargetDetails) inventory.Finding { 72 return inventory.Finding{GenericFindings: []*inventory.GenericFinding{{ 73 Adv: &inventory.GenericFindingAdvisory{ 74 ID: &inventory.AdvisoryID{ 75 Publisher: "CIS", 76 Reference: "etc-passwd-permissions", 77 }, 78 Title: "Ensure permissions on /etc/passwd are configured", 79 Description: "The /etc/passwd file contains user account information that " + 80 "is used by many system utilities and therefore must be readable for these " + 81 "utilities to operate.", 82 Recommendation: "Run the following command to set permissions on /etc/passwd :\n" + 83 "# chown root:root /etc/passwd\n" + 84 "# chmod 644 /etc/passwd", 85 Sev: inventory.SeverityMinimal, 86 }, 87 Target: target, 88 }}} 89 } 90 91 // ScanFS starts the scan from a pseudo-filesystem. 92 func (d Detector) ScanFS(ctx context.Context, fs fs.FS, px *packageindex.PackageIndex) (inventory.Finding, error) { 93 f, err := fs.Open("etc/passwd") 94 if err != nil { 95 if errors.Is(err, os.ErrNotExist) { 96 // File doesn't exist, check not applicable. 97 return inventory.Finding{}, nil 98 } 99 return inventory.Finding{}, err 100 } 101 defer f.Close() 102 info, err := f.Stat() 103 if err != nil { 104 return inventory.Finding{}, err 105 } 106 107 problems := "" 108 if info.Mode().Perm() != 0644 { 109 problems = fmt.Sprintf("file permissions %03o, expected 644\n", info.Mode().Perm()) 110 } 111 112 stat, ok := info.Sys().(*syscall.Stat_t) 113 if !ok { 114 return inventory.Finding{}, errors.New("failed to get file ownership info") 115 } 116 117 if stat.Uid != 0 { 118 problems += fmt.Sprintf("file owner %d, expected 0/root\n", stat.Uid) 119 } 120 if stat.Gid != 0 { 121 problems += fmt.Sprintf("file group %d, expected 0/root\n", stat.Gid) 122 } 123 124 if len(problems) == 0 { 125 return inventory.Finding{}, nil 126 } 127 128 target := &inventory.GenericFindingTargetDetails{Extra: "/etc/passwd: " + problems} 129 return d.findingForTarget(target), nil 130 }