diff options
| author | Andrew Lytvynov <awly@tailscale.com> | 2026-04-24 16:01:23 -0700 |
|---|---|---|
| committer | Andrew Lytvynov <awly@tailscale.com> | 2026-04-24 16:01:23 -0700 |
| commit | 99d482260085bb93bea0288f9e3417737a750bfd (patch) | |
| tree | 83618f84550cab9ce07897121041a9a502120fd5 | |
| parent | 1b40911611b37947bdc905dec30b2914af540920 (diff) | |
| download | tailscale-awly/deadcode-s4u.tar.xz tailscale-awly/deadcode-s4u.zip | |
util/winutil/s4u: remove unused packageawly/deadcode-s4u
Added in 2024 but appears to be unused.
Updates #cleanup
Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
| -rw-r--r-- | util/winutil/s4u/lsa_windows.go | 399 | ||||
| -rw-r--r-- | util/winutil/s4u/mksyscall.go | 16 | ||||
| -rw-r--r-- | util/winutil/s4u/s4u_windows.go | 947 | ||||
| -rw-r--r-- | util/winutil/s4u/zsyscall_windows.go | 104 |
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, "as, &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 -} |
