go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/windows/security_products.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package windows 5 6 import ( 7 "encoding/json" 8 "io" 9 "time" 10 11 "go.mondoo.com/cnquery/providers/os/connection/shared" 12 "go.mondoo.com/cnquery/providers/os/resources/powershell" 13 ) 14 15 // This implementation reads the security products from Windows Desktop Systems 16 // Initially we tried to use C# with DLL loading to iwscapi but that turned out to be quite complex, therefore 17 // we read that via Get-CimInstance -Namespace root/SecurityCenter2 -Classname AntiVirusProduct 18 // This retrieves the information from WMI (which does not exist on Windows Server). The Windows-internal 19 // firewall does not show up in that list. This is to be expected and we do not try to mimic the iwscapi 20 // which adds the firewall to the list. Instead, the firewall is being exposed via separate resources. 21 // 22 // References: 23 // https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/ 24 // https://social.msdn.microsoft.com/Forums/en-US/8da083a9-59bf-4e93-9f1b-209a2c5b9c72/how-to-use-wscapidll-in-c-for-getting-details-about-antivirus-softwares?forum=csharpgeneral 25 // https://social.msdn.microsoft.com/Forums/vstudio/en-US/8da083a9-59bf-4e93-9f1b-209a2c5b9c72/how-to-use-wscapidll-in-c-for-getting-details-about-antivirus-softwares?forum=csharpgeneral 26 27 const windowsSecurityProducts = ` 28 $securityProducts = New-Object PSObject 29 Add-Member -InputObject $securityProducts -MemberType NoteProperty -Name firewall -Value @(Get-CimInstance -Namespace root/SecurityCenter2 -Classname FirewallProduct) 30 Add-Member -InputObject $securityProducts -MemberType NoteProperty -Name antiVirus -Value @(Get-CimInstance -Namespace root/SecurityCenter2 -Classname AntiVirusProduct) 31 Add-Member -InputObject $securityProducts -MemberType NoteProperty -Name antiSpyware -Value @(Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiSpywareProduct) 32 ConvertTo-Json -Depth 3 -Compress $securityProducts 33 ` 34 35 // powershellBitlockerVolumeStatus is the struct to parse the powershell result 36 type powershelSecurityProducts struct { 37 Firewall []powershellSecurityProduct 38 AntiVirus []powershellSecurityProduct 39 AntiSpyware []powershellSecurityProduct 40 } 41 42 type powershellSecurityProduct struct { 43 DisplayName string 44 InstanceGUID string 45 PathToSignedProductExe string 46 PathToSignedReportingExe string 47 ProductState uint32 48 Timestamp string 49 } 50 51 type securityProduct struct { 52 Type string 53 Guid string 54 Name string 55 SignedProductExe string 56 SignedReportingExe string 57 State int64 58 ProductStatus string 59 SignatureStatus string 60 Timestamp time.Time 61 } 62 63 func parseTimestamp(timestamp string) time.Time { 64 var ts time.Time 65 if timestamp != "" { 66 // parse "Mon, 18 Apr 2022 08:05:47 GMT" 67 parsedTime, err := time.Parse(time.RFC1123, timestamp) 68 if err == nil { 69 ts = parsedTime 70 } 71 } 72 return ts 73 } 74 75 // https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/ne-iwscapi-wsc_security_product_state 76 var securityProductStatusValues = map[uint32]string{ 77 0: "ON", 78 1: "OFF", 79 2: "SNOOZED", 80 3: "EXPIRED", 81 } 82 83 // https://docs.microsoft.com/en-us/windows/win32/api/iwscapi/ne-iwscapi-wsc_security_signature_status 84 var securitySignatureStatusValues = map[uint32]string{ 85 0: "OUT-OF-DATE", 86 1: "UP-TO-DATE", 87 } 88 89 var securityProductOwner = map[uint32]string{ 90 0: "NonMS", 91 1: "Microsoft", 92 } 93 94 const ( 95 SignatureStatus = 0x00F0 96 ProductOwner = 0x0F00 97 ProductState = 0xF000 98 99 // ProductState 100 Off = 0x0000 101 On = 0x1000 102 Snoozed = 0x2000 103 Expired = 0x3000 104 105 // SignatureStatus 106 UpToDate = 0x00 107 OutOfDate = 0x10 108 109 // ProductOwner 110 NonMs = 0x000 111 Windows = 0x100 112 ) 113 114 type productState struct { 115 Product uint32 116 Owner uint32 117 Signature uint32 118 } 119 120 // product state is encoded in a 4-byte, the last byte is not used 121 // https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/identifying-antivirus-engine-state 122 123 func parseProductState(state uint32) productState { 124 res := productState{} 125 126 switch state & ProductState { 127 case On: 128 res.Product = 0 129 case Off: 130 res.Product = 1 131 case Snoozed: 132 res.Product = 2 133 case Expired: 134 res.Product = 3 135 } 136 137 switch state & SignatureStatus { 138 case UpToDate: 139 res.Signature = 1 140 case OutOfDate: 141 res.Signature = 0 142 } 143 144 switch state & ProductOwner { 145 case NonMs: 146 res.Owner = 0 147 case Windows: 148 res.Owner = 1 149 } 150 151 return res 152 } 153 154 func GetSecurityProducts(p shared.Connection) ([]securityProduct, error) { 155 c, err := p.RunCommand(powershell.Encode(windowsSecurityProducts)) 156 if err != nil { 157 return nil, err 158 } 159 160 return ParseWindowsSecurityProducts(c.Stdout) 161 } 162 163 func ParseWindowsSecurityProducts(r io.Reader) ([]securityProduct, error) { 164 var psSecProducts powershelSecurityProducts 165 data, err := io.ReadAll(r) 166 if err != nil { 167 return nil, err 168 } 169 170 err = json.Unmarshal(data, &psSecProducts) 171 if err != nil { 172 return nil, err 173 } 174 175 res := []securityProduct{} 176 for i := range psSecProducts.AntiVirus { 177 p := psSecProducts.AntiVirus[i] 178 179 res = append(res, securityProduct{ 180 Type: "antivirus", 181 Guid: p.InstanceGUID, 182 Name: p.DisplayName, 183 SignedProductExe: p.PathToSignedProductExe, 184 SignedReportingExe: p.PathToSignedReportingExe, 185 State: int64(p.ProductState), 186 ProductStatus: securityProductStatusValues[parseProductState(p.ProductState).Product], 187 SignatureStatus: securitySignatureStatusValues[parseProductState(p.ProductState).Signature], 188 Timestamp: parseTimestamp(p.Timestamp), 189 }) 190 } 191 192 for i := range psSecProducts.Firewall { 193 p := psSecProducts.Firewall[i] 194 195 res = append(res, securityProduct{ 196 Type: "firewall", 197 Guid: p.InstanceGUID, 198 Name: p.DisplayName, 199 SignedProductExe: p.PathToSignedProductExe, 200 SignedReportingExe: p.PathToSignedReportingExe, 201 State: int64(p.ProductState), 202 ProductStatus: securityProductStatusValues[parseProductState(p.ProductState).Product], 203 SignatureStatus: securitySignatureStatusValues[parseProductState(p.ProductState).Signature], 204 Timestamp: parseTimestamp(p.Timestamp), 205 }) 206 } 207 208 for i := range psSecProducts.AntiSpyware { 209 p := psSecProducts.AntiSpyware[i] 210 211 res = append(res, securityProduct{ 212 Type: "antispyware", 213 Guid: p.InstanceGUID, 214 Name: p.DisplayName, 215 SignedProductExe: p.PathToSignedProductExe, 216 SignedReportingExe: p.PathToSignedReportingExe, 217 State: int64(p.ProductState), 218 ProductStatus: securityProductStatusValues[parseProductState(p.ProductState).Product], 219 SignatureStatus: securitySignatureStatusValues[parseProductState(p.ProductState).Signature], 220 Timestamp: parseTimestamp(p.Timestamp), 221 }) 222 } 223 224 return res, nil 225 }