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  }