fleet/orbit/pkg/windows/windowsupdate/iupdate.go

387 lines
13 KiB
Go
Raw Normal View History

2023-11-02 02:11:35 +00:00
// based on github.com/kolide/launcher/pkg/osquery/tables
package windowsupdate
import (
"time"
"github.com/fleetdm/fleet/v4/orbit/pkg/windows/oleconv"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)
// IUpdate contains the properties and methods that are available to an update.
// https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nn-wuapi-iupdate
type IUpdate struct {
disp *ole.IDispatch
AutoDownload int32 // enum https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdate5-get_autodownload
AutoSelection int32 // enum https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdate5-get_autoselection
AutoSelectOnWebSites bool
BundledUpdates []*IUpdateIdentity // These are full IUpdate objects, but we truncate them
BrowseOnly bool // From IUpdate3
CanRequireSource bool
Categories []*ICategory
CveIDs []string // From IUpdate2
Deadline *time.Time
DeltaCompressedContentAvailable bool
DeltaCompressedContentPreferred bool
DeploymentAction int32 // enum https://docs.microsoft.com/zh-cn/windows/win32/api/wuapi/ne-wuapi-deploymentaction
Description string
DownloadContents []*IUpdateDownloadContent
DownloadPriority int32 // enum https://docs.microsoft.com/zh-cn/windows/win32/api/wuapi/ne-wuapi-downloadpriority
EulaAccepted bool
EulaText string
HandlerID string
Identity *IUpdateIdentity
Image *IImageInformation
InstallationBehavior *IInstallationBehavior
IsBeta bool
IsDownloaded bool
IsHidden bool
IsInstalled bool
IsMandatory bool
IsPresent bool // From IUpdate2
IsUninstallable bool
KBArticleIDs []string
Languages []string
LastDeploymentChangeTime *time.Time
MaxDownloadSize int64
MinDownloadSize int64
MoreInfoUrls []string
MsrcSeverity string
PerUser bool // From IUpdate4
RebootRequired bool // From IUpdate2
RecommendedCpuSpeed int32
RecommendedHardDiskSpace int32
RecommendedMemory int32
ReleaseNotes string
SecurityBulletinIDs []string
SupersededUpdateIDs []string
SupportUrl string
Title string
UninstallationBehavior *IInstallationBehavior
UninstallationNotes string
UninstallationSteps []string
}
// toIUpdates takes a IUpdateCollection and returns a []*IUpdate
func toIUpdates(updatesDisp *ole.IDispatch) ([]*IUpdate, error) {
count, err := oleconv.ToInt32Err(oleutil.GetProperty(updatesDisp, "Count"))
if err != nil {
return nil, err
}
updates := make([]*IUpdate, count)
for i := 0; i < int(count); i++ {
updateDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updatesDisp, "Item", i))
if err != nil {
return nil, err
}
update, err := toIUpdate(updateDisp)
if err != nil {
return nil, err
}
updates[i] = update
}
return updates, nil
}
// toIUpdates takes a IUpdateCollection and returns the a
// []*IUpdateIdentity of the contained IUpdates. This is *not* recursive, though possible is should be
func toIUpdatesIdentities(updatesDisp *ole.IDispatch) ([]*IUpdateIdentity, error) {
if updatesDisp == nil {
return nil, nil
}
count, err := oleconv.ToInt32Err(oleutil.GetProperty(updatesDisp, "Count"))
if err != nil {
return nil, err
}
identities := make([]*IUpdateIdentity, count)
for i := 0; i < int(count); i++ {
updateDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updatesDisp, "Item", i))
if err != nil {
return nil, err
}
identityDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "Identity"))
if err != nil {
return nil, err
}
if identityDisp != nil {
if identities[i], err = toIUpdateIdentity(identityDisp); err != nil {
return nil, err
}
}
}
return identities, nil
}
func toIUpdate(updateDisp *ole.IDispatch) (*IUpdate, error) {
var err error
iUpdate := &IUpdate{
disp: updateDisp,
}
if iUpdate.AutoDownload, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "AutoDownload")); err != nil {
return nil, err
}
if iUpdate.AutoSelection, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "AutoSelection")); err != nil {
return nil, err
}
if iUpdate.AutoSelectOnWebSites, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "AutoSelectOnWebSites")); err != nil {
return nil, err
}
arrDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "BundledUpdates"))
if err != nil {
return nil, err
}
if iUpdate.BundledUpdates, err = toIUpdatesIdentities(arrDisp); err != nil {
return nil, err
}
if iUpdate.BrowseOnly, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "BrowseOnly")); err != nil {
return nil, err
}
if iUpdate.CanRequireSource, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "CanRequireSource")); err != nil {
return nil, err
}
if categoriesDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "Categories")); err != nil {
return nil, err
} else if categoriesDisp != nil {
if iUpdate.Categories, err = toICategories(categoriesDisp); err != nil {
return nil, err
}
}
if iUpdate.CveIDs, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "CveIDs"))); err != nil {
return nil, err
}
if iUpdate.Deadline, err = oleconv.ToTimeErr(oleutil.GetProperty(updateDisp, "Deadline")); err != nil {
return nil, err
}
if iUpdate.DeltaCompressedContentAvailable, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "DeltaCompressedContentAvailable")); err != nil {
return nil, err
}
if iUpdate.DeltaCompressedContentPreferred, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "DeltaCompressedContentPreferred")); err != nil {
return nil, err
}
if iUpdate.DeploymentAction, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "DeploymentAction")); err != nil {
return nil, err
}
if iUpdate.Description, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "Description")); err != nil {
return nil, err
}
downloadContentsDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "DownloadContents"))
if err != nil {
return nil, err
}
if downloadContentsDisp != nil {
if iUpdate.DownloadContents, err = toIUpdateDownloadContents(downloadContentsDisp); err != nil {
return nil, err
}
}
if iUpdate.DownloadPriority, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "DownloadPriority")); err != nil {
return nil, err
}
if iUpdate.EulaAccepted, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "EulaAccepted")); err != nil {
return nil, err
}
if iUpdate.EulaText, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "EulaText")); err != nil {
return nil, err
}
if iUpdate.HandlerID, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "HandlerID")); err != nil {
return nil, err
}
identityDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "Identity"))
if err != nil {
return nil, err
}
if identityDisp != nil {
if iUpdate.Identity, err = toIUpdateIdentity(identityDisp); err != nil {
return nil, err
}
}
imageDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "Image"))
if err != nil {
return nil, err
}
if imageDisp != nil {
if iUpdate.Image, err = toIImageInformation(imageDisp); err != nil {
return nil, err
}
}
installationBehaviorDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "InstallationBehavior"))
if err != nil {
return nil, err
}
if installationBehaviorDisp != nil {
if iUpdate.InstallationBehavior, err = toIInstallationBehavior(installationBehaviorDisp); err != nil {
return nil, err
}
}
if iUpdate.IsBeta, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsBeta")); err != nil {
return nil, err
}
if iUpdate.IsDownloaded, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsDownloaded")); err != nil {
return nil, err
}
if iUpdate.IsHidden, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsHidden")); err != nil {
return nil, err
}
if iUpdate.IsInstalled, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsInstalled")); err != nil {
return nil, err
}
if iUpdate.IsMandatory, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsMandatory")); err != nil {
return nil, err
}
if iUpdate.IsPresent, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsPresent")); err != nil {
return nil, err
}
if iUpdate.IsUninstallable, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "IsUninstallable")); err != nil {
return nil, err
}
if iUpdate.KBArticleIDs, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "KBArticleIDs"))); err != nil {
return nil, err
}
if iUpdate.Languages, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "Languages"))); err != nil {
return nil, err
}
if iUpdate.LastDeploymentChangeTime, err = oleconv.ToTimeErr(oleutil.GetProperty(updateDisp, "LastDeploymentChangeTime")); err != nil {
return nil, err
}
if iUpdate.MaxDownloadSize, err = oleconv.ToInt64Err(oleutil.GetProperty(updateDisp, "MaxDownloadSize")); err != nil {
return nil, err
}
if iUpdate.MinDownloadSize, err = oleconv.ToInt64Err(oleutil.GetProperty(updateDisp, "MinDownloadSize")); err != nil {
return nil, err
}
if iUpdate.MoreInfoUrls, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "MoreInfoUrls"))); err != nil {
return nil, err
}
if iUpdate.MsrcSeverity, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "MsrcSeverity")); err != nil {
return nil, err
}
if iUpdate.PerUser, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "PerUser")); err != nil {
return nil, err
}
if iUpdate.RebootRequired, err = oleconv.ToBoolErr(oleutil.GetProperty(updateDisp, "RebootRequired")); err != nil {
return nil, err
}
if iUpdate.RecommendedCpuSpeed, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "RecommendedCpuSpeed")); err != nil {
return nil, err
}
if iUpdate.RecommendedHardDiskSpace, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "RecommendedHardDiskSpace")); err != nil {
return nil, err
}
if iUpdate.RecommendedMemory, err = oleconv.ToInt32Err(oleutil.GetProperty(updateDisp, "RecommendedMemory")); err != nil {
return nil, err
}
if iUpdate.ReleaseNotes, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "ReleaseNotes")); err != nil {
return nil, err
}
if iUpdate.SecurityBulletinIDs, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "SecurityBulletinIDs"))); err != nil {
return nil, err
}
if iUpdate.SupersededUpdateIDs, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "SupersededUpdateIDs"))); err != nil {
return nil, err
}
if iUpdate.SupportUrl, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "SupportUrl")); err != nil {
return nil, err
}
if iUpdate.Title, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "Title")); err != nil {
return nil, err
}
uninstallationBehaviorDisp, err := oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "UninstallationBehavior"))
if err != nil {
return nil, err
}
if uninstallationBehaviorDisp != nil {
if iUpdate.UninstallationBehavior, err = toIInstallationBehavior(uninstallationBehaviorDisp); err != nil {
return nil, err
}
}
if iUpdate.UninstallationNotes, err = oleconv.ToStringErr(oleutil.GetProperty(updateDisp, "UninstallationNotes")); err != nil {
return nil, err
}
if iUpdate.UninstallationSteps, err = iStringCollectionToStringArrayErr(oleconv.ToIDispatchErr(oleutil.GetProperty(updateDisp, "UninstallationSteps"))); err != nil {
return nil, err
}
return iUpdate, nil
}
//nolint:unused
func toIUpdateCollection(updates []*IUpdate) (*ole.IDispatch, error) {
unknown, err := oleutil.CreateObject("Microsoft.Update.UpdateColl")
if err != nil {
return nil, err
}
coll, err := unknown.QueryInterface(ole.IID_IDispatch)
if err != nil {
return nil, err
}
for _, update := range updates {
_, err := oleutil.CallMethod(coll, "Add", update.disp)
if err != nil {
return nil, err
}
}
return coll, nil
}
// AcceptEula accepts the Microsoft Software License Terms that are associated with Windows Update. Administrators and power users can call this method.
// https://docs.microsoft.com/en-us/windows/win32/api/wuapi/nf-wuapi-iupdate-accepteula
func (iUpdate *IUpdate) AcceptEula() error {
_, err := oleutil.CallMethod(iUpdate.disp, "AcceptEula")
return err
}