From 1fc968a06d6f37493fc8d78a101b9ad149f7b51a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Thu, 8 Oct 2026 14:48:43 +0200 Subject: [PATCH] headers: remove unused netapi32, secur32 and schedule_service (#2550) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Claude Opus 5.5 Signed-off-by: Jan-Otto Kröpke --- internal/headers/netapi32/netapi32.go | 132 ----------------- .../schedule_service/schedule_service.go | 93 ------------ internal/headers/secur32/secur32.go | 137 ------------------ internal/headers/secur32/secur32_test.go | 33 ----- internal/headers/secur32/types.go | 129 ----------------- 5 files changed, 524 deletions(-) delete mode 100644 internal/headers/netapi32/netapi32.go delete mode 100644 internal/headers/schedule_service/schedule_service.go delete mode 100644 internal/headers/secur32/secur32.go delete mode 100644 internal/headers/secur32/secur32_test.go delete mode 100644 internal/headers/secur32/types.go diff --git a/internal/headers/netapi32/netapi32.go b/internal/headers/netapi32/netapi32.go deleted file mode 100644 index d820ee45..00000000 --- a/internal/headers/netapi32/netapi32.go +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows - -package netapi32 - -import ( - "errors" - "unsafe" - - "golang.org/x/sys/windows" -) - -// WKSTAInfo102 is a wrapper of WKSTA_Info_102 -// https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/ns-lmwksta-wksta_info_102 -type wKSTAInfo102 struct { - wki102_platform_id uint32 - wki102_computername *uint16 - wki102_langroup *uint16 - wki102_ver_major uint32 - wki102_ver_minor uint32 - wki102_lanroot *uint16 - wki102_logged_on_users uint32 -} - -// WorkstationInfo is an idiomatic wrapper of WKSTAInfo102. -type WorkstationInfo struct { - PlatformId uint32 - ComputerName string - LanGroup string - VersionMajor uint32 - VersionMinor uint32 - LanRoot string - LoggedOnUsers uint32 -} - -//nolint:gochecknoglobals -var ( - netapi32 = windows.NewLazySystemDLL("netapi32") - procNetWkstaGetInfo = netapi32.NewProc("NetWkstaGetInfo") - procNetApiBufferFree = netapi32.NewProc("NetApiBufferFree") -) - -// NetApiStatus is a map of Network Management Error Codes. -// https://docs.microsoft.com/en-gb/windows/win32/netmgmt/network-management-error-codes?redirectedfrom=MSDN -// -//nolint:gochecknoglobals -var NetApiStatus = map[uint32]string{ - // Success - 0: "NERR_Success", - // This computer name is invalid. - 2351: "NERR_InvalidComputer", - // This operation is only allowed on the primary domain controller of the domain. - 2226: "NERR_NotPrimary", - // This operation is not allowed on this special group. - 2234: "NERR_SpeGroupOp", - // This operation is not allowed on the last administrative account. - 2452: "NERR_LastAdmin", - // The password parameter is invalid. - 2203: "NERR_BadPassword", - // The password does not meet the password policy requirements. - // Check the minimum password length, password complexity and password history requirements. - 2245: "NERR_PasswordTooShort", - // The user name could not be found. - 2221: "NERR_UserNotFound", - // Errors - 5: "ERROR_ACCESS_DENIED", - 8: "ERROR_NOT_ENOUGH_MEMORY", - 87: "ERROR_INVALID_PARAMETER", - 123: "ERROR_INVALID_NAME", - 124: "ERROR_INVALID_LEVEL", - 234: "ERROR_MORE_DATA", - 1219: "ERROR_SESSION_CREDENTIAL_CONFLICT", -} - -// NetApiBufferFree frees the memory other network management functions use internally to return information. -// https://docs.microsoft.com/en-us/windows/win32/api/lmapibuf/nf-lmapibuf-netapibufferfree -func netApiBufferFree(buffer *wKSTAInfo102) { - procNetApiBufferFree.Call(uintptr(unsafe.Pointer(buffer))) //nolint:errcheck -} - -// NetWkstaGetInfo returns information about the configuration of a workstation. -// https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstagetinfo -func netWkstaGetInfo() (wKSTAInfo102, uint32, error) { - var lpwi *wKSTAInfo102 - - pLevel := uintptr(102) - - r1, _, _ := procNetWkstaGetInfo.Call(0, pLevel, uintptr(unsafe.Pointer(&lpwi))) - defer netApiBufferFree(lpwi) - - if ret := *(*uint32)(unsafe.Pointer(&r1)); ret != 0 { - return wKSTAInfo102{}, ret, errors.New(NetApiStatus[ret]) - } - - deref := *lpwi - - return deref, 0, nil -} - -// GetWorkstationInfo is an idiomatic wrapper for netWkstaGetInfo. -func GetWorkstationInfo() (WorkstationInfo, error) { - info, _, err := netWkstaGetInfo() - if err != nil { - return WorkstationInfo{}, err - } - - workstationInfo := WorkstationInfo{ - PlatformId: info.wki102_platform_id, - ComputerName: windows.UTF16PtrToString(info.wki102_computername), - LanGroup: windows.UTF16PtrToString(info.wki102_langroup), - VersionMajor: info.wki102_ver_major, - VersionMinor: info.wki102_ver_minor, - LanRoot: windows.UTF16PtrToString(info.wki102_lanroot), - LoggedOnUsers: info.wki102_logged_on_users, - } - - return workstationInfo, nil -} diff --git a/internal/headers/schedule_service/schedule_service.go b/internal/headers/schedule_service/schedule_service.go deleted file mode 100644 index 60123ce0..00000000 --- a/internal/headers/schedule_service/schedule_service.go +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows - -package schedule_service - -import ( - "errors" - "fmt" - "runtime" - - "github.com/go-ole/go-ole" - "github.com/go-ole/go-ole/oleutil" -) - -type ScheduleService struct { - taskSchedulerObj *ole.IUnknown - taskServiceObj *ole.IDispatch - taskService *ole.VARIANT -} - -func New() *ScheduleService { - return &ScheduleService{} -} - -func (s *ScheduleService) Connect() error { - // The only way to run WMI queries in parallel while being thread-safe is to - // ensure the CoInitialize[Ex]() call is bound to its current OS thread. - // Otherwise, attempting to initialize and run parallel queries across - // goroutines will result in protected memory errors. - runtime.LockOSThread() - - defer runtime.UnlockOSThread() - - if err := ole.CoInitializeEx(0, ole.COINIT_APARTMENTTHREADED|ole.COINIT_DISABLE_OLE1DDE); err != nil { - var oleCode *ole.OleError - if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != 0x00000001 { - return err - } - } - - scheduleClassID, err := ole.ClassIDFrom("Schedule.Service") - if err != nil { - return err - } - - s.taskSchedulerObj, err = ole.CreateInstance(scheduleClassID, nil) - if err != nil || s.taskSchedulerObj == nil { - return err - } - - s.taskService, err = oleutil.CallMethod(s.taskServiceObj, "Connect") - if err != nil { - return fmt.Errorf("failed to connect to task service: %w", err) - } - - s.taskServiceObj = s.taskSchedulerObj.MustQueryInterface(ole.IID_IDispatch) - - return nil -} - -func (s *ScheduleService) GetOLETaskServiceObj() *ole.IDispatch { - return s.taskServiceObj -} - -func (s *ScheduleService) Close() { - if s.taskService != nil { - _ = s.taskService.Clear() - } - - if s.taskServiceObj != nil { - s.taskServiceObj.Release() - } - - if s.taskSchedulerObj != nil { - s.taskSchedulerObj.Release() - } - - ole.CoUninitialize() -} diff --git a/internal/headers/secur32/secur32.go b/internal/headers/secur32/secur32.go deleted file mode 100644 index e8ea42b2..00000000 --- a/internal/headers/secur32/secur32.go +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows - -package secur32 - -import ( - "errors" - "fmt" - "time" - "unsafe" - - "golang.org/x/sys/windows" -) - -// based on https://github.com/carlpett/winlsa/blob/master/winlsa.go -// -//nolint:gochecknoglobals -var ( - secur32 = windows.NewLazySystemDLL("Secur32.dll") - advapi32 = windows.NewLazySystemDLL("advapi32.dll") - - procLsaEnumerateLogonSessions = secur32.NewProc("LsaEnumerateLogonSessions") - procLsaGetLogonSessionData = secur32.NewProc("LsaGetLogonSessionData") - procLsaFreeReturnBuffer = secur32.NewProc("LsaFreeReturnBuffer") - procLsaNtStatusToWinError = advapi32.NewProc("LsaNtStatusToWinError") -) - -func GetLogonSessions() ([]*LogonSessionData, error) { - var ( - buffer uintptr - sessionCount uint32 - ) - - err := LsaEnumerateLogonSessions(&sessionCount, &buffer) - if err != nil { - return nil, err - } - - if buffer != 0 { - defer func(buffer uintptr) { - _ = LsaFreeReturnBuffer(buffer) - }(buffer) - } - - sizeLUID := unsafe.Sizeof(windows.LUID{}) - - sessionDataSlice := make([]*LogonSessionData, 0, sessionCount) - - for i := range sessionCount { - curPtr := unsafe.Pointer(buffer + (uintptr(i) * sizeLUID)) - luid := (*windows.LUID)(curPtr) - - sessionData, err := GetLogonSessionData(luid) - if err != nil { - if errors.Is(err, windows.ERROR_ACCESS_DENIED) { - // Skip logon sessions that we don't have access to - continue - } - - return nil, err - } - - sessionDataSlice = append(sessionDataSlice, sessionData) - } - - return sessionDataSlice, nil -} - -func GetLogonSessionData(luid *windows.LUID) (*LogonSessionData, error) { - var dataBuffer *SECURITY_LOGON_SESSION_DATA - if err := LsaGetLogonSessionData(luid, &dataBuffer); err != nil { - return nil, fmt.Errorf("failed to get logon session data: %w", err) - } - - defer func(buffer uintptr) { - _ = LsaFreeReturnBuffer(buffer) - }(uintptr(unsafe.Pointer(dataBuffer))) - - return newLogonSessionData(dataBuffer), nil -} - -func LsaEnumerateLogonSessions(sessionCount *uint32, sessions *uintptr) error { - r0, _, _ := procLsaEnumerateLogonSessions.Call(uintptr(unsafe.Pointer(sessionCount)), uintptr(unsafe.Pointer(sessions))) - - return LsaNtStatusToWinError(r0) -} - -func LsaGetLogonSessionData(luid *windows.LUID, ppLogonSessionData **SECURITY_LOGON_SESSION_DATA) error { - r0, _, _ := procLsaGetLogonSessionData.Call(uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(ppLogonSessionData))) - - return LsaNtStatusToWinError(r0) -} - -func LsaFreeReturnBuffer(buffer uintptr) error { - r0, _, _ := procLsaFreeReturnBuffer.Call(buffer) - - return LsaNtStatusToWinError(r0) -} - -func LsaNtStatusToWinError(ntstatus uintptr) error { - r0, _, err := procLsaNtStatusToWinError.Call(ntstatus) - - switch { - case errors.Is(err, windows.ERROR_SUCCESS): - if r0 == 0 { - return nil - } - case errors.Is(err, windows.ERROR_MR_MID_NOT_FOUND): - return fmt.Errorf("unknown LSA NTSTATUS code %x", ntstatus) - } - - return windows.Errno(r0) -} - -func newLogonSessionData(data *SECURITY_LOGON_SESSION_DATA) *LogonSessionData { - return &LogonSessionData{ - LogonId: data.LogonId, - UserName: data.UserName.String(), - LogonDomain: data.LogonDomain.String(), - LogonType: data.LogonType, - LogonTime: time.Unix(0, data.LogonTime.Nanoseconds()), - } -} diff --git a/internal/headers/secur32/secur32_test.go b/internal/headers/secur32/secur32_test.go deleted file mode 100644 index bfd975e9..00000000 --- a/internal/headers/secur32/secur32_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows - -package secur32_test - -import ( - "testing" - - "github.com/prometheus-community/windows_exporter/internal/headers/secur32" - "github.com/stretchr/testify/require" -) - -func TestGetLogonSessions(t *testing.T) { - t.Parallel() - - sessionData, err := secur32.GetLogonSessions() - require.NoError(t, err) - require.NotEmpty(t, sessionData) -} diff --git a/internal/headers/secur32/types.go b/internal/headers/secur32/types.go deleted file mode 100644 index dc8e50c3..00000000 --- a/internal/headers/secur32/types.go +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// -// Copyright The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build windows - -package secur32 - -import ( - "fmt" - "time" - - "golang.org/x/sys/windows" -) - -type LogonType uint32 - -type LSA_LAST_INTER_LOGON_INFO struct { - LastSuccessfulLogon windows.Filetime - LastFailedLogon windows.Filetime - FailedAttemptCountSinceLastSuccessfulLogon uint32 -} - -type SECURITY_LOGON_SESSION_DATA struct { - Size uint32 - LogonId LUID - UserName windows.NTUnicodeString - LogonDomain windows.NTUnicodeString - AuthenticationPackage windows.NTUnicodeString - LogonType LogonType - Session uint32 - Sid *windows.SID - LogonTime windows.Filetime - LogonServer windows.NTUnicodeString - DnsDomainName windows.NTUnicodeString - Upn windows.NTUnicodeString - UserFlags uint32 - LastLogonInfo LSA_LAST_INTER_LOGON_INFO - LogonScript windows.NTUnicodeString - ProfilePath windows.NTUnicodeString - HomeDirectory windows.NTUnicodeString - HomeDirectoryDrive windows.NTUnicodeString - LogoffTime windows.Filetime - KickOffTime windows.Filetime - PasswordLastSet windows.Filetime - PasswordCanChange windows.Filetime - PasswordMustChange windows.Filetime -} - -const ( - // LogonTypeSystem Not explicitly defined in LSA, but according to - // https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-logonsession, - // LogonType=0 is "Used only by the System account." - LogonTypeSystem LogonType = iota - _ // LogonType=1 is not used - LogonTypeInteractive - LogonTypeNetwork - LogonTypeBatch - LogonTypeService - LogonTypeProxy - LogonTypeUnlock - LogonTypeNetworkCleartext - LogonTypeNewCredentials - LogonTypeRemoteInteractive - LogonTypeCachedInteractive - LogonTypeCachedRemoteInteractive - LogonTypeCachedUnlock -) - -func (lt LogonType) String() string { - switch lt { - case LogonTypeSystem: - return "System" - case LogonTypeInteractive: - return "Interactive" - case LogonTypeNetwork: - return "Network" - case LogonTypeBatch: - return "Batch" - case LogonTypeService: - return "Service" - case LogonTypeProxy: - return "Proxy" - case LogonTypeUnlock: - return "Unlock" - case LogonTypeNetworkCleartext: - return "NetworkCleartext" - case LogonTypeNewCredentials: - return "NewCredentials" - case LogonTypeRemoteInteractive: - return "RemoteInteractive" - case LogonTypeCachedInteractive: - return "CachedInteractive" - case LogonTypeCachedRemoteInteractive: - return "CachedRemoteInteractive" - case LogonTypeCachedUnlock: - return "CachedUnlock" - default: - return fmt.Sprintf("Undefined LogonType(%d)", lt) - } -} - -type LogonSessionData struct { - LogonId LUID - UserName string - LogonDomain string - AuthenticationPackage string - LogonType LogonType - Session uint32 - Sid *windows.SID - LogonTime time.Time -} - -type LUID windows.LUID - -func (l LUID) String() string { - return fmt.Sprintf("0x%x:0x%x", l.HighPart, l.LowPart) -}