summaryrefslogtreecommitdiffhomepage
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/winutil/s4u/lsa_windows.go399
-rw-r--r--util/winutil/s4u/mksyscall.go16
-rw-r--r--util/winutil/s4u/s4u_windows.go947
-rw-r--r--util/winutil/s4u/zsyscall_windows.go104
4 files changed, 0 insertions, 1466 deletions
diff --git a/util/winutil/s4u/lsa_windows.go b/util/winutil/s4u/lsa_windows.go
deleted file mode 100644
index a26a7bcf0..000000000
--- a/util/winutil/s4u/lsa_windows.go
+++ /dev/null
@@ -1,399 +0,0 @@
-// Copyright (c) Tailscale Inc & contributors
-// SPDX-License-Identifier: BSD-3-Clause
-
-package s4u
-
-import (
- "errors"
- "fmt"
- "os"
- "os/user"
- "path/filepath"
- "strings"
- "unicode"
- "unsafe"
-
- "github.com/dblohm7/wingoes"
- "golang.org/x/sys/windows"
- "tailscale.com/types/lazy"
- "tailscale.com/util/winutil"
- "tailscale.com/util/winutil/winenv"
-)
-
-const (
- _MICROSOFT_KERBEROS_NAME = "Kerberos"
- _MSV1_0_PACKAGE_NAME = "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"
-)
-
-type _LSAHANDLE windows.Handle
-type _LSA_OPERATIONAL_MODE uint32
-
-type _KERB_LOGON_SUBMIT_TYPE int32
-
-const (
- _KerbInteractiveLogon _KERB_LOGON_SUBMIT_TYPE = 2
- _KerbSmartCardLogon _KERB_LOGON_SUBMIT_TYPE = 6
- _KerbWorkstationUnlockLogon _KERB_LOGON_SUBMIT_TYPE = 7
- _KerbSmartCardUnlockLogon _KERB_LOGON_SUBMIT_TYPE = 8
- _KerbProxyLogon _KERB_LOGON_SUBMIT_TYPE = 9
- _KerbTicketLogon _KERB_LOGON_SUBMIT_TYPE = 10
- _KerbTicketUnlockLogon _KERB_LOGON_SUBMIT_TYPE = 11
- _KerbS4ULogon _KERB_LOGON_SUBMIT_TYPE = 12
- _KerbCertificateLogon _KERB_LOGON_SUBMIT_TYPE = 13
- _KerbCertificateS4ULogon _KERB_LOGON_SUBMIT_TYPE = 14
- _KerbCertificateUnlockLogon _KERB_LOGON_SUBMIT_TYPE = 15
- _KerbNoElevationLogon _KERB_LOGON_SUBMIT_TYPE = 83
- _KerbLuidLogon _KERB_LOGON_SUBMIT_TYPE = 84
-)
-
-type _KERB_S4U_LOGON_FLAGS uint32
-
-const (
- _KERB_S4U_LOGON_FLAG_CHECK_LOGONHOURS _KERB_S4U_LOGON_FLAGS = 0x2
- //lint:ignore U1000 maps to a win32 API
- _KERB_S4U_LOGON_FLAG_IDENTIFY _KERB_S4U_LOGON_FLAGS = 0x8
-)
-
-type _KERB_S4U_LOGON struct {
- MessageType _KERB_LOGON_SUBMIT_TYPE
- Flags _KERB_S4U_LOGON_FLAGS
- ClientUpn windows.NTUnicodeString
- ClientRealm windows.NTUnicodeString
-}
-
-type _MSV1_0_LOGON_SUBMIT_TYPE int32
-
-const (
- _MsV1_0InteractiveLogon _MSV1_0_LOGON_SUBMIT_TYPE = 2
- _MsV1_0Lm20Logon _MSV1_0_LOGON_SUBMIT_TYPE = 3
- _MsV1_0NetworkLogon _MSV1_0_LOGON_SUBMIT_TYPE = 4
- _MsV1_0SubAuthLogon _MSV1_0_LOGON_SUBMIT_TYPE = 5
- _MsV1_0WorkstationUnlockLogon _MSV1_0_LOGON_SUBMIT_TYPE = 7
- _MsV1_0S4ULogon _MSV1_0_LOGON_SUBMIT_TYPE = 12
- _MsV1_0VirtualLogon _MSV1_0_LOGON_SUBMIT_TYPE = 82
- _MsV1_0NoElevationLogon _MSV1_0_LOGON_SUBMIT_TYPE = 83
- _MsV1_0LuidLogon _MSV1_0_LOGON_SUBMIT_TYPE = 84
-)
-
-type _MSV1_0_S4U_LOGON_FLAGS uint32
-
-const (
- _MSV1_0_S4U_LOGON_FLAG_CHECK_LOGONHOURS _MSV1_0_S4U_LOGON_FLAGS = 0x2
-)
-
-type _MSV1_0_S4U_LOGON struct {
- MessageType _MSV1_0_LOGON_SUBMIT_TYPE
- Flags _MSV1_0_S4U_LOGON_FLAGS
- UserPrincipalName windows.NTUnicodeString
- DomainName windows.NTUnicodeString
-}
-
-type _SECURITY_LOGON_TYPE int32
-
-const (
- _UndefinedLogonType _SECURITY_LOGON_TYPE = 0
- _Interactive _SECURITY_LOGON_TYPE = 2
- _Network _SECURITY_LOGON_TYPE = 3
- _Batch _SECURITY_LOGON_TYPE = 4
- _Service _SECURITY_LOGON_TYPE = 5
- _Proxy _SECURITY_LOGON_TYPE = 6
- _Unlock _SECURITY_LOGON_TYPE = 7
- _NetworkCleartext _SECURITY_LOGON_TYPE = 8
- _NewCredentials _SECURITY_LOGON_TYPE = 9
- _RemoteInteractive _SECURITY_LOGON_TYPE = 10
- _CachedInteractive _SECURITY_LOGON_TYPE = 11
- _CachedRemoteInteractive _SECURITY_LOGON_TYPE = 12
- _CachedUnlock _SECURITY_LOGON_TYPE = 13
-)
-
-const _TOKEN_SOURCE_LENGTH = 8
-
-type _TOKEN_SOURCE struct {
- SourceName [_TOKEN_SOURCE_LENGTH]byte
- SourceIdentifier windows.LUID
-}
-
-type _QUOTA_LIMITS struct {
- PagedPoolLimit uintptr
- NonPagedPoolLimit uintptr
- MinimumWorkingSetSize uintptr
- MaximumWorkingSetSize uintptr
- PagefileLimit uintptr
- TimeLimit int64
-}
-
-var (
- // ErrBadSrcName is returned if srcName contains non-ASCII characters, is
- // empty, or is too long. It may be wrapped with additional information; use
- // errors.Is when checking for it.
- ErrBadSrcName = errors.New("srcName must be ASCII with length > 0 and <= 8")
-)
-
-// LSA packages (and their IDs) are always initialized during system startup,
-// so we can retain their resolved IDs for the lifetime of our process.
-var (
- authPkgIDKerberos lazy.SyncValue[uint32]
- authPkgIDMSV1_0 lazy.SyncValue[uint32]
-)
-
-type lsaSession struct {
- handle _LSAHANDLE
-}
-
-func newLSASessionForQuery() (lsa *lsaSession, err error) {
- var h _LSAHANDLE
- if e := wingoes.ErrorFromNTStatus(lsaConnectUntrusted(&h)); e.Failed() {
- return nil, e
- }
-
- return &lsaSession{handle: h}, nil
-}
-
-func newLSASessionForLogon(processName string) (lsa *lsaSession, err error) {
- // processName is used by LSA for audit logging purposes.
- // If empty, the current process name is used.
- if processName == "" {
- exe, err := os.Executable()
- if err != nil {
- return nil, err
- }
-
- processName = strings.TrimSuffix(filepath.Base(exe), filepath.Ext(exe))
- }
-
- if err := checkASCII(processName); err != nil {
- return nil, err
- }
-
- logonProcessName, err := windows.NewNTString(processName)
- if err != nil {
- return nil, err
- }
-
- var h _LSAHANDLE
- var mode _LSA_OPERATIONAL_MODE
- if e := wingoes.ErrorFromNTStatus(lsaRegisterLogonProcess(logonProcessName, &h, &mode)); e.Failed() {
- return nil, e
- }
-
- return &lsaSession{handle: h}, nil
-}
-
-func (ls *lsaSession) getAuthPkgID(pkgName string) (id uint32, err error) {
- ntPkgName, err := windows.NewNTString(pkgName)
- if err != nil {
- return 0, err
- }
-
- if e := wingoes.ErrorFromNTStatus(lsaLookupAuthenticationPackage(ls.handle, ntPkgName, &id)); e.Failed() {
- return 0, e
- }
-
- return id, nil
-}
-
-func (ls *lsaSession) Close() error {
- if e := wingoes.ErrorFromNTStatus(lsaDeregisterLogonProcess(ls.handle)); e.Failed() {
- return e
- }
- ls.handle = 0
- return nil
-}
-
-func checkASCII(s string) error {
- for _, c := range []byte(s) {
- if c > unicode.MaxASCII {
- return fmt.Errorf("%q must be ASCII but contains value 0x%02X", s, c)
- }
- }
-
- return nil
-}
-
-var (
- thisComputer = []uint16{'.', 0}
- computerName lazy.SyncValue[string]
-)
-
-func getComputerName() (string, error) {
- var buf [windows.MAX_COMPUTERNAME_LENGTH + 1]uint16
- size := uint32(len(buf))
- if err := windows.GetComputerName(&buf[0], &size); err != nil {
- return "", err
- }
-
- return windows.UTF16ToString(buf[:size]), nil
-}
-
-// checkDomainAccount strips out the computer name (if any) from
-// username and returns the result in sanitizedUserName. isDomainAccount is set
-// to true if username contains a domain component that does not refer to the
-// local computer.
-func checkDomainAccount(username string) (sanitizedUserName string, isDomainAccount bool, err error) {
- before, after, hasBackslash := strings.Cut(username, `\`)
- if !hasBackslash {
- return username, false, nil
- }
- if before == "." {
- return after, false, nil
- }
-
- comp, err := computerName.GetErr(getComputerName)
- if err != nil {
- return username, false, err
- }
-
- if strings.EqualFold(before, comp) {
- return after, false, nil
- }
- return username, true, nil
-}
-
-// logonAs performs a S4U logon for u on behalf of srcName, and returns an
-// access token for the user if successful. srcName must be non-empty, ASCII,
-// and no more than 8 characters long. If srcName does not meet this criteria,
-// LogonAs will return ErrBadSrcName wrapped with additional information; use
-// errors.Is to check for it. When capLevel == CapCreateProcess, the logon
-// enforces the user's logon hours policy (when present).
-func (ls *lsaSession) logonAs(srcName string, u *user.User, capLevel CapabilityLevel) (token windows.Token, err error) {
- if ln := len(srcName); ln == 0 || ln > _TOKEN_SOURCE_LENGTH {
- return 0, fmt.Errorf("%w, actual length is %d", ErrBadSrcName, ln)
- }
- if err := checkASCII(srcName); err != nil {
- return 0, fmt.Errorf("%w: %v", ErrBadSrcName, err)
- }
-
- sanitizedUserName, isDomainUser, err := checkDomainAccount(u.Username)
- if err != nil {
- return 0, err
- }
- if isDomainUser && !winenv.IsDomainJoined() {
- return 0, fmt.Errorf("%w: cannot logon as domain user without being joined to a domain", os.ErrInvalid)
- }
-
- var pkgID uint32
- var authInfo unsafe.Pointer
- var authInfoLen uint32
- enforceLogonHours := capLevel == CapCreateProcess
- if isDomainUser {
- pkgID, err = authPkgIDKerberos.GetErr(func() (uint32, error) {
- return ls.getAuthPkgID(_MICROSOFT_KERBEROS_NAME)
- })
- if err != nil {
- return 0, err
- }
-
- upn16, err := samToUPN16(sanitizedUserName)
- if err != nil {
- return 0, fmt.Errorf("samToUPN16: %w", err)
- }
-
- logonInfo, logonInfoLen, slcs := winutil.AllocateContiguousBuffer[_KERB_S4U_LOGON](upn16)
- logonInfo.MessageType = _KerbS4ULogon
- if enforceLogonHours {
- logonInfo.Flags = _KERB_S4U_LOGON_FLAG_CHECK_LOGONHOURS
- }
- winutil.SetNTString(&logonInfo.ClientUpn, slcs[0])
-
- authInfo = unsafe.Pointer(logonInfo)
- authInfoLen = logonInfoLen
- } else {
- pkgID, err = authPkgIDMSV1_0.GetErr(func() (uint32, error) {
- return ls.getAuthPkgID(_MSV1_0_PACKAGE_NAME)
- })
- if err != nil {
- return 0, err
- }
-
- upn16, err := windows.UTF16FromString(sanitizedUserName)
- if err != nil {
- return 0, err
- }
-
- logonInfo, logonInfoLen, slcs := winutil.AllocateContiguousBuffer[_MSV1_0_S4U_LOGON](upn16, thisComputer)
- logonInfo.MessageType = _MsV1_0S4ULogon
- if enforceLogonHours {
- logonInfo.Flags = _MSV1_0_S4U_LOGON_FLAG_CHECK_LOGONHOURS
- }
- for i, nts := range []*windows.NTUnicodeString{&logonInfo.UserPrincipalName, &logonInfo.DomainName} {
- winutil.SetNTString(nts, slcs[i])
- }
-
- authInfo = unsafe.Pointer(logonInfo)
- authInfoLen = logonInfoLen
- }
-
- var srcContext _TOKEN_SOURCE
- copy(srcContext.SourceName[:], []byte(srcName))
- if err := allocateLocallyUniqueId(&srcContext.SourceIdentifier); err != nil {
- return 0, err
- }
-
- originName, err := windows.NewNTString(srcName)
- if err != nil {
- return 0, err
- }
-
- var profileBuf uintptr
- var profileBufLen uint32
- var logonID windows.LUID
- var quotas _QUOTA_LIMITS
- var subNTStatus windows.NTStatus
- ntStatus := lsaLogonUser(ls.handle, originName, _Network, pkgID, authInfo, authInfoLen, nil, &srcContext, &profileBuf, &profileBufLen, &logonID, &token, &quotas, &subNTStatus)
- if e := wingoes.ErrorFromNTStatus(ntStatus); e.Failed() {
- return 0, fmt.Errorf("LsaLogonUser(%q): %w, SubStatus: %v", u.Username, e, subNTStatus)
- }
- if profileBuf != 0 {
- lsaFreeReturnBuffer(profileBuf)
- }
- return token, nil
-}
-
-// samToUPN16 converts SAM-style account name samName to a UPN account name,
-// returned as a UTF-16 slice.
-func samToUPN16(samName string) (upn16 []uint16, err error) {
- _, samAccount, hasSep := strings.Cut(samName, `\`)
- if !hasSep {
- return nil, fmt.Errorf("%w: expected samName to contain a backslash", os.ErrInvalid)
- }
-
- // This is essentially the same algorithm used by Win32-OpenSSH:
- // First, try obtaining a UPN directly...
- upn16, err = translateName(samName, windows.NameSamCompatible, windows.NameUserPrincipal)
- if err == nil {
- return upn16, err
- }
-
- // Fallback: Try manually composing a UPN. First obtain the canonical name...
- canonical16, err := translateName(samName, windows.NameSamCompatible, windows.NameCanonical)
- if err != nil {
- return nil, err
- }
- canonical := windows.UTF16ToString(canonical16)
-
- // Extract the domain name...
- domain, _, _ := strings.Cut(canonical, "/")
-
- // ...and finally create the UPN by joining the samAccount and domain.
- upn := strings.Join([]string{samAccount, domain}, "@")
- return windows.UTF16FromString(upn)
-}
-
-func translateName(from string, fromFmt uint32, toFmt uint32) (result []uint16, err error) {
- from16, err := windows.UTF16PtrFromString(from)
- if err != nil {
- return nil, err
- }
-
- var to16Len uint32
- if err := windows.TranslateName(from16, fromFmt, toFmt, nil, &to16Len); err != nil {
- return nil, err
- }
-
- to16Buf := make([]uint16, to16Len)
- if err := windows.TranslateName(from16, fromFmt, toFmt, unsafe.SliceData(to16Buf), &to16Len); err != nil {
- return nil, err
- }
-
- return to16Buf, nil
-}
diff --git a/util/winutil/s4u/mksyscall.go b/util/winutil/s4u/mksyscall.go
deleted file mode 100644
index b8ab33672..000000000
--- a/util/winutil/s4u/mksyscall.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) Tailscale Inc & contributors
-// SPDX-License-Identifier: BSD-3-Clause
-
-package s4u
-
-//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go mksyscall.go
-//go:generate go run golang.org/x/tools/cmd/goimports -w zsyscall_windows.go
-
-//sys allocateLocallyUniqueId(luid *windows.LUID) (err error) [int32(failretval)==0] = advapi32.AllocateLocallyUniqueId
-//sys impersonateLoggedOnUser(token windows.Token) (err error) [int32(failretval)==0] = advapi32.ImpersonateLoggedOnUser
-//sys lsaConnectUntrusted(lsaHandle *_LSAHANDLE) (ret windows.NTStatus) = secur32.LsaConnectUntrusted
-//sys lsaDeregisterLogonProcess(lsaHandle _LSAHANDLE) (ret windows.NTStatus) = secur32.LsaDeregisterLogonProcess
-//sys lsaFreeReturnBuffer(buffer uintptr) (ret windows.NTStatus) = secur32.LsaFreeReturnBuffer
-//sys lsaLogonUser(lsaHandle _LSAHANDLE, originName *windows.NTString, logonType _SECURITY_LOGON_TYPE, authenticationPackage uint32, authenticationInformation unsafe.Pointer, authenticationInformationLength uint32, localGroups *windows.Tokengroups, sourceContext *_TOKEN_SOURCE, profileBuffer *uintptr, profileBufferLength *uint32, logonID *windows.LUID, token *windows.Token, quotas *_QUOTA_LIMITS, subStatus *windows.NTStatus) (ret windows.NTStatus) = secur32.LsaLogonUser
-//sys lsaLookupAuthenticationPackage(lsaHandle _LSAHANDLE, packageName *windows.NTString, authenticationPackage *uint32) (ret windows.NTStatus) = secur32.LsaLookupAuthenticationPackage
-//sys lsaRegisterLogonProcess(logonProcessName *windows.NTString, lsaHandle *_LSAHANDLE, securityMode *_LSA_OPERATIONAL_MODE) (ret windows.NTStatus) = secur32.LsaRegisterLogonProcess
diff --git a/util/winutil/s4u/s4u_windows.go b/util/winutil/s4u/s4u_windows.go
deleted file mode 100644
index a5b543cab..000000000
--- a/util/winutil/s4u/s4u_windows.go
+++ /dev/null
@@ -1,947 +0,0 @@
-// Copyright (c) Tailscale Inc & contributors
-// SPDX-License-Identifier: BSD-3-Clause
-
-// Package s4u is an API for accessing Service-For-User (S4U) functionality on Windows.
-package s4u
-
-import (
- "encoding/binary"
- "errors"
- "flag"
- "fmt"
- "io"
- "math"
- "os"
- "os/user"
- "runtime"
- "slices"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "unsafe"
-
- "golang.org/x/sys/windows"
- "tailscale.com/cmd/tailscaled/childproc"
- "tailscale.com/types/logger"
- "tailscale.com/util/winutil"
- "tailscale.com/util/winutil/conpty"
-)
-
-func init() {
- childproc.Add("s4u", beRelay)
-}
-
-var errInsufficientCapabilityLevel = errors.New("insufficient capability level")
-
-// ListGroupIDsForSSHPreAuthOnly returns user u's group memberships as a slice
-// containing group SIDs. srcName must contain the name of the service that is
-// retrieving this information. srcName must be non-empty, ASCII-only, and no
-// longer than 8 characters.
-//
-// NOTE: This should only be used by Tailscale SSH! It is not a generic
-// mechanism for access checks!
-func ListGroupIDsForSSHPreAuthOnly(srcName string, u *user.User) ([]string, error) {
- tok, err := createToken(srcName, u, tokenTypeIdentification, CapImpersonateOnly)
- if err != nil {
- return nil, err
- }
- defer tok.Close()
-
- tokenGroups, err := tok.GetTokenGroups()
- if err != nil {
- return nil, err
- }
-
- result := make([]string, 0, tokenGroups.GroupCount)
- for _, group := range tokenGroups.AllGroups() {
- if group.Attributes&windows.SE_GROUP_ENABLED != 0 {
- result = append(result, group.Sid.String())
- }
- }
-
- return result, nil
-}
-
-type tokenType uint
-
-const (
- tokenTypeIdentification tokenType = iota
- tokenTypeImpersonation
-)
-
-// createToken creates a new S4U access token for user u for the purposes
-// specified by s4uType, with capability capLevel. srcName must contain the name
-// of the service that is intended to use the token. srcName must be non-empty,
-// ASCII-only, and no longer than 8 characters.
-//
-// When s4uType is tokenTypeImpersonation, the current OS thread's access token must have SeTcbPrivilege.
-func createToken(srcName string, u *user.User, s4uType tokenType, capLevel CapabilityLevel) (tok windows.Token, err error) {
- if u == nil {
- return 0, os.ErrInvalid
- }
-
- var lsa *lsaSession
- switch s4uType {
- case tokenTypeIdentification:
- lsa, err = newLSASessionForQuery()
- case tokenTypeImpersonation:
- lsa, err = newLSASessionForLogon("")
- default:
- return 0, os.ErrInvalid
- }
- if err != nil {
- return 0, err
- }
- defer lsa.Close()
-
- return lsa.logonAs(srcName, u, capLevel)
-}
-
-// Session encapsulates an S4U login session.
-type Session struct {
- refCnt atomic.Int32
- logf logger.Logf
- token windows.Token
- userProfile *winutil.UserProfile
- capLevel CapabilityLevel
-}
-
-// CapabilityLevel specifies the desired capabilities that will be supported by a Session.
-type CapabilityLevel uint
-
-const (
- // The Session supports Do but none of the StartProcess* methods.
- CapImpersonateOnly CapabilityLevel = iota
- // The Session supports both Do and the StartProcess* methods.
- CapCreateProcess
-)
-
-// Login logs user u into Windows on behalf of service srcName, loads the user's
-// profile, and returns a Session that may be used for impersonating that user,
-// or optionally creating processes as that user. Logs will be written to logf,
-// if provided. srcName must be non-empty, ASCII-only, and no longer than 8
-// characters.
-//
-// The current OS thread's access token must have SeTcbPrivilege.
-func Login(logf logger.Logf, srcName string, u *user.User, capLevel CapabilityLevel) (sess *Session, err error) {
- token, err := createToken(srcName, u, tokenTypeImpersonation, capLevel)
- if err != nil {
- return nil, err
- }
- tokenCloseOnce := sync.OnceFunc(func() { token.Close() })
- defer func() {
- if err != nil {
- tokenCloseOnce()
- }
- }()
-
- sessToken := token
- if capLevel == CapCreateProcess {
- // Obtain token's security descriptor so that it may be applied to
- // a primary token.
- sd, err := windows.GetSecurityInfo(windows.Handle(token),
- windows.SE_KERNEL_OBJECT, windows.DACL_SECURITY_INFORMATION)
- if err != nil {
- return nil, err
- }
-
- sa := windows.SecurityAttributes{
- Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
- SecurityDescriptor: sd,
- }
-
- // token is an impersonation token. Upgrade us to a primary token so that
- // our StartProcess* methods will work correctly.
- var dupToken windows.Token
- if err := windows.DuplicateTokenEx(token, 0, &sa, windows.SecurityImpersonation,
- windows.TokenPrimary, &dupToken); err != nil {
- return nil, err
- }
- sessToken = dupToken
- defer func() {
- if err != nil {
- sessToken.Close()
- }
- }()
- tokenCloseOnce()
- }
-
- userProfile, err := winutil.LoadUserProfile(sessToken, u)
- if err != nil {
- return nil, err
- }
-
- if logf == nil {
- logf = logger.Discard
- } else {
- logf = logger.WithPrefix(logf, "(s4u) ")
- }
-
- return &Session{logf: logf, token: sessToken, userProfile: userProfile, capLevel: capLevel}, nil
-}
-
-// Close unloads the user profile and S4U access token associated with the
-// session. The close operation is not guaranteed to have finished when Close
-// returns; it may remain alive until all processes created by ss have
-// themselves been closed, and no more Do requests are pending.
-func (ss *Session) Close() error {
- refs := ss.refCnt.Load()
- if (refs & 1) != 0 {
- // Close already called
- return nil
- }
-
- // Set the low bit to indicate that a close operation has been requested.
- // We don't have atomic OR so we need to use CAS. Sigh.
- for !ss.refCnt.CompareAndSwap(refs, refs|1) {
- refs = ss.refCnt.Load()
- }
-
- if refs > 1 {
- // Still active processes, just return.
- return nil
- }
-
- return ss.closeInternal()
-}
-
-func (ss *Session) closeInternal() error {
- if ss.userProfile != nil {
- if err := ss.userProfile.Close(); err != nil {
- return err
- }
- ss.userProfile = nil
- }
-
- if ss.token != 0 {
- if err := ss.token.Close(); err != nil {
- return err
- }
- ss.token = 0
- }
- return nil
-}
-
-// CapabilityLevel returns the CapabilityLevel that was specified when the
-// session was created.
-func (ss *Session) CapabilityLevel() CapabilityLevel {
- return ss.capLevel
-}
-
-// Do executes fn while impersonating ss's user. Impersonation only affects
-// the current goroutine; any new goroutines spawned by fn will not be
-// impersonated. Do may be called concurrently by multiple goroutines.
-//
-// Do returns an error if impersonation did not succeed and fn could not be run.
-// If called after ss has already been closed, it will panic.
-func (ss *Session) Do(fn func()) error {
- if fn == nil {
- return os.ErrInvalid
- }
-
- ss.addRef()
- defer ss.release()
-
- // Impersonation touches thread-local state.
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- if err := impersonateLoggedOnUser(ss.token); err != nil {
- return err
- }
- defer func() {
- if err := windows.RevertToSelf(); err != nil {
- // This is not recoverable in any way, shape, or form!
- panic(fmt.Sprintf("RevertToSelf failed: %v", err))
- }
- }()
-
- fn()
- return nil
-}
-
-func (ss *Session) addRef() {
- if (ss.refCnt.Add(2) & 1) != 0 {
- panic("addRef after Close")
- }
-}
-
-func (ss *Session) release() {
- rc := ss.refCnt.Add(-2)
- if rc < 0 {
- panic("negative refcount")
- }
- if rc == 1 {
- ss.closeInternal()
- }
-}
-
-type startProcessOpts struct {
- token windows.Token
- extraEnv map[string]string
- ptySize windows.Coord
- pipes bool
-}
-
-// StartProcess creates a new process running under ss via cmdLineInfo.
-// The process will either be started with its working directory set to the S4U
-// user's profile directory, or for Administrative users, the system32
-// directory. The child process will receive the S4U user's environment.
-// extraEnv, when specified, contains any additional environment
-// variables to be inserted into the environment.
-//
-// If called after ss has already been closed, StartProcess will panic.
-func (ss *Session) StartProcess(cmdLineInfo winutil.CommandLineInfo, extraEnv map[string]string) (psp *Process, err error) {
- if ss.capLevel != CapCreateProcess {
- return nil, errInsufficientCapabilityLevel
- }
-
- opts := startProcessOpts{
- token: ss.token,
- extraEnv: extraEnv,
- }
- return startProcessInternal(ss, ss.logf, cmdLineInfo, opts)
-}
-
-// StartProcessWithPTY creates a new process running under ss via cmdLineInfo
-// with a pseudoconsole initialized to initialPtySize. The resulting Process
-// will return non-nil values from Stdin and Stdout, but Stderr will return nil.
-// The process will either be started with its working directory set to the S4U
-// user's profile directory, or for Administrative users, the system32
-// directory. The child process will receive the S4U user's environment.
-// extraEnv, when specified, contains any additional environment
-// variables to be inserted into the environment.
-//
-// If called after ss has already been closed, StartProcessWithPTY will panic.
-func (ss *Session) StartProcessWithPTY(cmdLineInfo winutil.CommandLineInfo, extraEnv map[string]string, initialPtySize windows.Coord) (psp *Process, err error) {
- if ss.capLevel != CapCreateProcess {
- return nil, errInsufficientCapabilityLevel
- }
-
- opts := startProcessOpts{
- token: ss.token,
- extraEnv: extraEnv,
- ptySize: initialPtySize,
- }
- return startProcessInternal(ss, ss.logf, cmdLineInfo, opts)
-}
-
-// StartProcessWithPipes creates a new process running under ss via cmdLineInfo
-// with all standard handles set to pipes. The resulting Process will return
-// non-nil values from Stdin, Stdout, and Stderr.
-// The process will either be started with its working directory set to the S4U
-// user's profile directory, or for Administrative users, the system32
-// directory. The child process will receive the S4U user's environment.
-// extraEnv, when specified, contains any additional environment
-// variables to be inserted into the environment.
-//
-// If called after ss has already been closed, StartProcessWithPipes will panic.
-func (ss *Session) StartProcessWithPipes(cmdLineInfo winutil.CommandLineInfo, extraEnv map[string]string) (psp *Process, err error) {
- if ss.capLevel != CapCreateProcess {
- return nil, errInsufficientCapabilityLevel
- }
-
- opts := startProcessOpts{
- token: ss.token,
- extraEnv: extraEnv,
- pipes: true,
- }
- return startProcessInternal(ss, ss.logf, cmdLineInfo, opts)
-}
-
-// startProcessInternal is the common implementation behind Session's exported
-// StartProcess* methods. It uses opts to distinguish between the various
-// requested modes of operation.
-//
-// A note on pseudoconsoles:
-// The conpty API currently does not provide a way to create a pseudoconsole for
-// a different user than the current process. The way we deal with this is
-// to first create a "relay" process running with the desired user token,
-// and then create the actual requested process as a child of the relay,
-// at which time we create the pseudoconsole. The relay simply copies the
-// PTY's I/O into/out of its own stdin and stdout, which are piped to the
-// parent still running as LocalSystem. We also relay pseudoconsole resize requests.
-func startProcessInternal(ss *Session, logf logger.Logf, cmdLineInfo winutil.CommandLineInfo, opts startProcessOpts) (psp *Process, err error) {
- var sib winutil.StartupInfoBuilder
- defer sib.Close()
-
- var sp Process
- defer func() {
- if err != nil {
- sp.Close()
- }
- }()
-
- var zeroCoord windows.Coord
- ptySizeValid := opts.ptySize != zeroCoord
- useToken := opts.token != 0
- usePty := ptySizeValid && !useToken
- useRelay := ptySizeValid && useToken
- useSystem32WD := useToken && opts.token.IsElevated()
-
- if usePty {
- sp.pty, err = conpty.NewPseudoConsole(opts.ptySize)
- if err != nil {
- return nil, err
- }
-
- if err := sp.pty.ConfigureStartupInfo(&sib); err != nil {
- return nil, err
- }
-
- sp.wStdin = sp.pty.InputPipe()
- sp.rStdout = sp.pty.OutputPipe()
- } else if useRelay || opts.pipes {
- if sp.wStdin, sp.rStdout, sp.rStderr, err = createStdPipes(&sib); err != nil {
- return nil, err
- }
- }
-
- var relayStderr io.ReadCloser
- if useRelay {
- // Later on we're going to use stderr for logging instead of providing it to the caller.
- relayStderr = sp.rStderr
- sp.rStderr = nil
- defer func() {
- if err != nil {
- relayStderr.Close()
- }
- }()
-
- // Set up a pipe to send PTY resize requests.
- var resizeRead, resizeWrite windows.Handle
- if err := windows.CreatePipe(&resizeRead, &resizeWrite, nil, 0); err != nil {
- return nil, err
- }
- sp.wResize = os.NewFile(uintptr(resizeWrite), "wPTYResizePipe")
- defer windows.CloseHandle(resizeRead)
- if err := sib.InheritHandles(resizeRead); err != nil {
- return nil, err
- }
-
- // Revise the command line. First, get the existing one.
- _, _, strCmdLine, err := cmdLineInfo.Resolve()
- if err != nil {
- return nil, err
- }
-
- // Now rebuild it, passing the strCmdLine as the --cmd argument...
- newArgs := []string{
- "be-child", "s4u",
- "--resize", fmt.Sprintf("0x%x", uintptr(resizeRead)),
- "--x", strconv.Itoa(int(opts.ptySize.X)),
- "--y", strconv.Itoa(int(opts.ptySize.Y)),
- "--cmd", strCmdLine,
- }
-
- // ...to be passed in as arguments to our own executable.
- cmdLineInfo.ExePath, err = os.Executable()
- if err != nil {
- return nil, err
- }
- cmdLineInfo.SetArgs(newArgs)
- }
-
- exePath, cmdLine, cmdLineStr, err := cmdLineInfo.Resolve()
- if err != nil {
- return nil, err
- }
- logf("starting %s", cmdLineStr)
-
- var env []string
- var wd16 *uint16
- if useToken {
- env, err = opts.token.Environ(false)
- if err != nil {
- return nil, err
- }
-
- folderID := windows.FOLDERID_Profile
- if useSystem32WD {
- folderID = windows.FOLDERID_System
- }
- wd, err := opts.token.KnownFolderPath(folderID, windows.KF_FLAG_DEFAULT)
- if err != nil {
- return nil, err
- }
- wd16, err = windows.UTF16PtrFromString(wd)
- if err != nil {
- return nil, err
- }
- } else {
- env = os.Environ()
- }
-
- env = mergeEnv(env, opts.extraEnv)
-
- var env16 *uint16
- if useToken || len(opts.extraEnv) > 0 {
- env16 = winutil.NewEnvBlock(env)
- }
-
- if useToken {
- // We want the child process to be assigned to job such that when it exits,
- // its descendents within the job will be terminated as well.
- job, err := createJob()
- if err != nil {
- return nil, err
- }
- // We don't need to hang onto job beyond this func...
- defer job.Close()
-
- if err := sib.AssignToJob(job.Handle()); err != nil {
- return nil, err
- }
-
- // ...because we're now gonna make a read-only copy...
- qjob, err := job.QueryOnlyClone()
- if err != nil {
- return nil, err
- }
- defer qjob.Close()
-
- // ...which will be inherited by the child process.
- // When the child process terminates, the job will too.
- if err := sib.InheritHandles(qjob.Handle()); err != nil {
- return nil, err
- }
- }
-
- si, inheritHandles, creationFlags, err := sib.Resolve()
- if err != nil {
- return nil, err
- }
-
- var pi windows.ProcessInformation
- if useToken {
- // DETACHED_PROCESS so that the child does not receive a console.
- // CREATE_NEW_PROCESS_GROUP so that the child's console group is isolated from ours.
- creationFlags |= windows.DETACHED_PROCESS | windows.CREATE_NEW_PROCESS_GROUP
- doCreate := func() {
- err = windows.CreateProcessAsUser(opts.token, exePath, cmdLine, nil, nil, inheritHandles, creationFlags, env16, wd16, si, &pi)
- }
- switch {
- case useRelay:
- doCreate()
- case ss != nil:
- // We want to ensure that the executable is accessible via the token's
- // security context, not ours.
- if err := ss.Do(doCreate); err != nil {
- return nil, err
- }
- default:
- panic("should not have reached here")
- }
- } else {
- err = windows.CreateProcess(exePath, cmdLine, nil, nil, inheritHandles, creationFlags, env16, wd16, si, &pi)
- }
- if err != nil {
- return nil, err
- }
- windows.CloseHandle(pi.Thread)
-
- if relayStderr != nil {
- logw := logger.FuncWriter(logger.WithPrefix(logf, fmt.Sprintf("(s4u relay process %d [0x%x]) ", pi.ProcessId, pi.ProcessId)))
- go func() {
- defer relayStderr.Close()
- io.Copy(logw, relayStderr)
- }()
- }
-
- sp.hproc = pi.Process
- sp.pid = pi.ProcessId
- if ss != nil {
- ss.addRef()
- sp.sess = ss
- }
- return &sp, nil
-}
-
-type jobObject windows.Handle
-
-func createJob() (job *jobObject, err error) {
- hjob, err := windows.CreateJobObject(nil, nil)
- if err != nil {
- return nil, err
- }
- defer func() {
- if err != nil {
- windows.CloseHandle(hjob)
- }
- }()
-
- limitInfo := windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION{
- BasicLimitInformation: windows.JOBOBJECT_BASIC_LIMIT_INFORMATION{
- // We want every process within the job to terminate when the job is closed.
- // We also want to allow processes within the job to create child processes
- // that are outside the job (otherwise you couldn't leave background
- // processes running after exiting a session, for example).
- // These flags also match those used by the Win32 port of OpenSSH.
- LimitFlags: windows.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | windows.JOB_OBJECT_LIMIT_BREAKAWAY_OK,
- },
- }
- _, err = windows.SetInformationJobObject(hjob,
- windows.JobObjectExtendedLimitInformation, uintptr(unsafe.Pointer(&limitInfo)),
- uint32(unsafe.Sizeof(limitInfo)))
- if err != nil {
- return nil, err
- }
-
- jo := jobObject(hjob)
- return &jo, nil
-}
-
-func (job *jobObject) Close() error {
- if hjob := job.Handle(); hjob != 0 {
- windows.CloseHandle(hjob)
- *job = 0
- }
- return nil
-}
-
-func (job *jobObject) Handle() windows.Handle {
- if job == nil {
- return 0
- }
- return windows.Handle(*job)
-}
-
-const _JOB_OBJECT_QUERY = 0x0004
-
-func (job *jobObject) QueryOnlyClone() (*jobObject, error) {
- hjob := job.Handle()
- cp := windows.CurrentProcess()
-
- var dupe windows.Handle
- err := windows.DuplicateHandle(cp, hjob, cp, &dupe, _JOB_OBJECT_QUERY, true, 0)
- if err != nil {
- return nil, err
- }
-
- result := jobObject(dupe)
- return &result, nil
-}
-
-func createStdPipes(sib *winutil.StartupInfoBuilder) (stdin io.WriteCloser, stdout, stderr io.ReadCloser, err error) {
- var rStdin, wStdin windows.Handle
- if err := windows.CreatePipe(&rStdin, &wStdin, nil, 0); err != nil {
- return nil, nil, nil, err
- }
- defer func() {
- if err != nil {
- windows.CloseHandle(rStdin)
- windows.CloseHandle(wStdin)
- }
- }()
-
- var rStdout, wStdout windows.Handle
- if err := windows.CreatePipe(&rStdout, &wStdout, nil, 0); err != nil {
- return nil, nil, nil, err
- }
- defer func() {
- if err != nil {
- windows.CloseHandle(rStdout)
- windows.CloseHandle(wStdout)
- }
- }()
-
- var rStderr, wStderr windows.Handle
- if err := windows.CreatePipe(&rStderr, &wStderr, nil, 0); err != nil {
- return nil, nil, nil, err
- }
- defer func() {
- if err != nil {
- windows.CloseHandle(rStderr)
- windows.CloseHandle(wStderr)
- }
- }()
-
- if err := sib.SetStdHandles(rStdin, wStdout, wStderr); err != nil {
- return nil, nil, nil, err
- }
-
- stdin = os.NewFile(uintptr(wStdin), "wStdin")
- stdout = os.NewFile(uintptr(rStdout), "rStdout")
- stderr = os.NewFile(uintptr(rStderr), "rStderr")
- return stdin, stdout, stderr, nil
-}
-
-// Process encapsulates a child process started with a Session.
-type Process struct {
- sess *Session
- wStdin io.WriteCloser
- rStdout io.ReadCloser
- rStderr io.ReadCloser
- wResize io.WriteCloser
- pty *conpty.PseudoConsole
- hproc windows.Handle
- pid uint32
-}
-
-// Stdin returns the write side of a pipe connected to the child process's
-// stdin, or nil if no I/O was requested.
-func (sp *Process) Stdin() io.WriteCloser {
- return sp.wStdin
-}
-
-// Stdout returns the read side of a pipe connected to the child process's
-// stdout, or nil if no I/O was requested.
-func (sp *Process) Stdout() io.ReadCloser {
- return sp.rStdout
-}
-
-// Stderr returns the read side of a pipe connected to the child process's
-// stderr, or nil if no I/O was requested.
-func (sp *Process) Stderr() io.ReadCloser {
- return sp.rStderr
-}
-
-// Terminate kills the process.
-func (sp *Process) Terminate() {
- if sp.hproc != 0 {
- windows.TerminateProcess(sp.hproc, 255)
- }
-}
-
-// Close waits for sp to complete and then cleans up any resources owned by it.
-// Close must wait because the Session associated with sp should not be destroyed
-// until all its processes have terminated. If necessary, call Terminate to
-// forcibly end the process.
-//
-// If the process was created with a pseudoconsole then the caller must continue
-// concurrently draining sp's stdout until either Close finishes executing, or EOF.
-func (sp *Process) Close() error {
- for _, pc := range []*io.WriteCloser{&sp.wStdin, &sp.wResize} {
- if *pc == nil {
- continue
- }
- (*pc).Close()
- (*pc) = nil
- }
-
- if sp.pty != nil {
- if err := sp.pty.Close(); err != nil {
- return err
- }
- sp.pty = nil
- }
-
- if sp.hproc != 0 {
- if _, err := sp.Wait(); err != nil {
- return err
- }
- windows.CloseHandle(sp.hproc)
- sp.hproc = 0
- sp.pid = 0
- if sp.sess != nil {
- sp.sess.release()
- sp.sess = nil
- }
- }
-
- // Order is important here. Do not close sp.rStdout until _after_
- // ss.pty (when present) has been closed! We're going to do one better by
- // doing this after the process is done.
- for _, pc := range []*io.ReadCloser{&sp.rStdout, &sp.rStderr} {
- if *pc == nil {
- continue
- }
- (*pc).Close()
- (*pc) = nil
- }
- return nil
-}
-
-// Wait blocks the caller until sp terminates. It returns the process exit code.
-// exitCode will be set to 254 if the process terminated but the exit code could
-// not be retrieved.
-func (sp *Process) Wait() (exitCode uint32, err error) {
- _, err = windows.WaitForSingleObject(sp.hproc, windows.INFINITE)
- if err == nil {
- if err := windows.GetExitCodeProcess(sp.hproc, &exitCode); err != nil {
- exitCode = 254
- }
- }
- return exitCode, err
-}
-
-// OSProcess returns an *os.Process associated with sp. This is useful for
-// integration with external code that expects an os.Process.
-func (sp *Process) OSProcess() (*os.Process, error) {
- if sp.hproc == 0 {
- return nil, winutil.ErrDefunctProcess
- }
- return os.FindProcess(int(sp.pid))
-}
-
-// PTYResizer returns a function to be called to resize the pseudoconsole.
-// It returns nil if no pseudoconsole was requested when creating sp.
-func (sp *Process) PTYResizer() func(windows.Coord) error {
- if sp.wResize != nil {
- wResize := sp.wResize
- return func(c windows.Coord) error {
- return binary.Write(wResize, binary.LittleEndian, c)
- }
- }
-
- if sp.pty != nil {
- pty := sp.pty
- return func(c windows.Coord) error {
- return pty.Resize(c)
- }
- }
-
- return nil
-}
-
-type relayArgs struct {
- command string
- resize string
- ptyX int
- ptyY int
-}
-
-func parseRelayArgs(args []string) (a relayArgs) {
- flags := flag.NewFlagSet("", flag.ExitOnError)
- flags.StringVar(&a.command, "cmd", "", "the command to run")
- flags.StringVar(&a.resize, "resize", "", "handle to resize pipe")
- flags.IntVar(&a.ptyX, "x", 80, "initial width of pty")
- flags.IntVar(&a.ptyY, "y", 25, "initial height of pty")
- flags.Parse(args)
- return a
-}
-
-func flagSizeErr(flagName byte) error {
- return fmt.Errorf("--%c must be greater than zero and less than %d", flagName, math.MaxInt16)
-}
-
-const debugRelay = false
-
-func beRelay(args []string) error {
- ra := parseRelayArgs(args)
- if ra.command == "" {
- return fmt.Errorf("--cmd must be specified")
- }
-
- bitSize := int(unsafe.Sizeof(windows.Handle(0)) * 8)
- resize64, err := strconv.ParseUint(ra.resize, 0, bitSize)
- if err != nil {
- return err
- }
- hResize := windows.Handle(resize64)
- if ft, _ := windows.GetFileType(hResize); ft != windows.FILE_TYPE_PIPE {
- return fmt.Errorf("--resize is an invalid handle type")
- }
- resize := os.NewFile(uintptr(hResize), "rPTYResizePipe")
- defer resize.Close()
-
- switch {
- case ra.ptyX <= 0 || ra.ptyX > math.MaxInt16:
- return flagSizeErr('x')
- case ra.ptyY <= 0 || ra.ptyY > math.MaxInt16:
- return flagSizeErr('y')
- default:
- }
-
- logf := logger.Discard
- if debugRelay {
- // Our parent process will write our stderr to its log.
- logf = func(format string, args ...any) {
- fmt.Fprintf(os.Stderr, format, args...)
- }
- }
-
- logf("starting")
- argv, err := windows.DecomposeCommandLine(ra.command)
- if err != nil {
- logf("DecomposeCommandLine failed: %v", err)
- return err
- }
-
- cli := winutil.CommandLineInfo{
- ExePath: argv[0],
- }
- cli.SetArgs(argv[1:])
-
- opts := startProcessOpts{
- ptySize: windows.Coord{X: int16(ra.ptyX), Y: int16(ra.ptyY)},
- }
- psp, err := startProcessInternal(nil, logf, cli, opts)
- if err != nil {
- logf("startProcessInternal failed: %v", err)
- return err
- }
- defer psp.Close()
-
- go resizeLoop(logf, resize, psp.PTYResizer())
- if debugRelay {
- go debugLogPTYInput(logf, psp.wStdin, os.Stdin)
- go debugLogPTYOutput(logf, os.Stdout, psp.rStdout)
- } else {
- go io.Copy(psp.wStdin, os.Stdin)
- go io.Copy(os.Stdout, psp.rStdout)
- }
-
- exitCode, err := psp.Wait()
- if err != nil {
- logf("waiting on relayed process: %v", err)
- return err
- }
- if exitCode > 0 {
- logf("relayed process returned %v", exitCode)
- }
-
- if err := psp.Close(); err != nil {
- logf("s4u.Process.Close error: %v", err)
- return err
- }
- return nil
-}
-
-func resizeLoop(logf logger.Logf, resizePipe io.Reader, resizeFn func(windows.Coord) error) {
- var coord windows.Coord
- for binary.Read(resizePipe, binary.LittleEndian, &coord) == nil {
- logf("resizing pty window to %#v", coord)
- resizeFn(coord)
- }
-}
-
-func debugLogPTYInput(logf logger.Logf, w io.Writer, r io.Reader) {
- logw := logger.FuncWriter(logger.WithPrefix(logf, "(pty input) "))
- io.Copy(io.MultiWriter(w, logw), r)
-}
-
-func debugLogPTYOutput(logf logger.Logf, w io.Writer, r io.Reader) {
- logw := logger.FuncWriter(logger.WithPrefix(logf, "(pty output) "))
- io.Copy(w, io.TeeReader(r, logw))
-}
-
-// mergeEnv returns the union of existingEnv and extraEnv, deduplicated and
-// sorted.
-func mergeEnv(existingEnv []string, extraEnv map[string]string) []string {
- if len(extraEnv) == 0 {
- return existingEnv
- }
-
- mergedMap := make(map[string]string, len(existingEnv)+len(extraEnv))
- for _, line := range existingEnv {
- k, v, _ := strings.Cut(line, "=")
- mergedMap[strings.ToUpper(k)] = v
- }
-
- for k, v := range extraEnv {
- mergedMap[strings.ToUpper(k)] = v
- }
-
- result := make([]string, 0, len(mergedMap))
- for k, v := range mergedMap {
- result = append(result, strings.Join([]string{k, v}, "="))
- }
-
- slices.SortFunc(result, func(a, b string) int {
- ka, _, _ := strings.Cut(a, "=")
- kb, _, _ := strings.Cut(b, "=")
- return strings.Compare(ka, kb)
- })
- return result
-}
diff --git a/util/winutil/s4u/zsyscall_windows.go b/util/winutil/s4u/zsyscall_windows.go
deleted file mode 100644
index db647dee4..000000000
--- a/util/winutil/s4u/zsyscall_windows.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Code generated by 'go generate'; DO NOT EDIT.
-
-package s4u
-
-import (
- "syscall"
- "unsafe"
-
- "golang.org/x/sys/windows"
-)
-
-var _ unsafe.Pointer
-
-// Do the interface allocations only once for common
-// Errno values.
-const (
- errnoERROR_IO_PENDING = 997
-)
-
-var (
- errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
- errERROR_EINVAL error = syscall.EINVAL
-)
-
-// errnoErr returns common boxed Errno values, to prevent
-// allocations at runtime.
-func errnoErr(e syscall.Errno) error {
- switch e {
- case 0:
- return errERROR_EINVAL
- case errnoERROR_IO_PENDING:
- return errERROR_IO_PENDING
- }
- // TODO: add more here, after collecting data on the common
- // error values see on Windows. (perhaps when running
- // all.bat?)
- return e
-}
-
-var (
- modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
- modsecur32 = windows.NewLazySystemDLL("secur32.dll")
-
- procAllocateLocallyUniqueId = modadvapi32.NewProc("AllocateLocallyUniqueId")
- procImpersonateLoggedOnUser = modadvapi32.NewProc("ImpersonateLoggedOnUser")
- procLsaConnectUntrusted = modsecur32.NewProc("LsaConnectUntrusted")
- procLsaDeregisterLogonProcess = modsecur32.NewProc("LsaDeregisterLogonProcess")
- procLsaFreeReturnBuffer = modsecur32.NewProc("LsaFreeReturnBuffer")
- procLsaLogonUser = modsecur32.NewProc("LsaLogonUser")
- procLsaLookupAuthenticationPackage = modsecur32.NewProc("LsaLookupAuthenticationPackage")
- procLsaRegisterLogonProcess = modsecur32.NewProc("LsaRegisterLogonProcess")
-)
-
-func allocateLocallyUniqueId(luid *windows.LUID) (err error) {
- r1, _, e1 := syscall.SyscallN(procAllocateLocallyUniqueId.Addr(), uintptr(unsafe.Pointer(luid)))
- if int32(r1) == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func impersonateLoggedOnUser(token windows.Token) (err error) {
- r1, _, e1 := syscall.SyscallN(procImpersonateLoggedOnUser.Addr(), uintptr(token))
- if int32(r1) == 0 {
- err = errnoErr(e1)
- }
- return
-}
-
-func lsaConnectUntrusted(lsaHandle *_LSAHANDLE) (ret windows.NTStatus) {
- r0, _, _ := syscall.SyscallN(procLsaConnectUntrusted.Addr(), uintptr(unsafe.Pointer(lsaHandle)))
- ret = windows.NTStatus(r0)
- return
-}
-
-func lsaDeregisterLogonProcess(lsaHandle _LSAHANDLE) (ret windows.NTStatus) {
- r0, _, _ := syscall.SyscallN(procLsaDeregisterLogonProcess.Addr(), uintptr(lsaHandle))
- ret = windows.NTStatus(r0)
- return
-}
-
-func lsaFreeReturnBuffer(buffer uintptr) (ret windows.NTStatus) {
- r0, _, _ := syscall.SyscallN(procLsaFreeReturnBuffer.Addr(), uintptr(buffer))
- ret = windows.NTStatus(r0)
- return
-}
-
-func lsaLogonUser(lsaHandle _LSAHANDLE, originName *windows.NTString, logonType _SECURITY_LOGON_TYPE, authenticationPackage uint32, authenticationInformation unsafe.Pointer, authenticationInformationLength uint32, localGroups *windows.Tokengroups, sourceContext *_TOKEN_SOURCE, profileBuffer *uintptr, profileBufferLength *uint32, logonID *windows.LUID, token *windows.Token, quotas *_QUOTA_LIMITS, subStatus *windows.NTStatus) (ret windows.NTStatus) {
- r0, _, _ := syscall.SyscallN(procLsaLogonUser.Addr(), uintptr(lsaHandle), uintptr(unsafe.Pointer(originName)), uintptr(logonType), uintptr(authenticationPackage), uintptr(authenticationInformation), uintptr(authenticationInformationLength), uintptr(unsafe.Pointer(localGroups)), uintptr(unsafe.Pointer(sourceContext)), uintptr(unsafe.Pointer(profileBuffer)), uintptr(unsafe.Pointer(profileBufferLength)), uintptr(unsafe.Pointer(logonID)), uintptr(unsafe.Pointer(token)), uintptr(unsafe.Pointer(quotas)), uintptr(unsafe.Pointer(subStatus)))
- ret = windows.NTStatus(r0)
- return
-}
-
-func lsaLookupAuthenticationPackage(lsaHandle _LSAHANDLE, packageName *windows.NTString, authenticationPackage *uint32) (ret windows.NTStatus) {
- r0, _, _ := syscall.SyscallN(procLsaLookupAuthenticationPackage.Addr(), uintptr(lsaHandle), uintptr(unsafe.Pointer(packageName)), uintptr(unsafe.Pointer(authenticationPackage)))
- ret = windows.NTStatus(r0)
- return
-}
-
-func lsaRegisterLogonProcess(logonProcessName *windows.NTString, lsaHandle *_LSAHANDLE, securityMode *_LSA_OPERATIONAL_MODE) (ret windows.NTStatus) {
- r0, _, _ := syscall.SyscallN(procLsaRegisterLogonProcess.Addr(), uintptr(unsafe.Pointer(logonProcessName)), uintptr(unsafe.Pointer(lsaHandle)), uintptr(unsafe.Pointer(securityMode)))
- ret = windows.NTStatus(r0)
- return
-}