@ -225,21 +225,40 @@ func isSIDValidPrincipal(uid string) bool {
}
}
// EnableCurrentThreadPrivilege enables the named privilege in the current
// thread access token. The current goroutine is also locked to the OS thread
// (runtime.LockOSThread). Callers must call the returned disable function when
// done with the privileged task.
func EnableCurrentThreadPrivilege ( name string ) ( disable func ( ) error , err error ) {
// EnableCurrentThreadPrivilege enables the named privilege
// in the current thread's access token. The current goroutine is also locked to
// the OS thread (runtime.LockOSThread). Callers must call the returned disable
// function when done with the privileged task.
func EnableCurrentThreadPrivilege ( name string ) ( disable func ( ) , err error ) {
return EnableCurrentThreadPrivileges ( [ ] string { name } )
}
// EnableCurrentThreadPrivileges enables the named privileges
// in the current thread's access token. The current goroutine is also locked to
// the OS thread (runtime.LockOSThread). Callers must call the returned disable
// function when done with the privileged task.
func EnableCurrentThreadPrivileges ( names [ ] string ) ( disable func ( ) , err error ) {
runtime . LockOSThread ( )
if len ( names ) == 0 {
// Nothing to enable; no-op isn't really an error...
return runtime . UnlockOSThread , nil
}
if err := windows . ImpersonateSelf ( windows . SecurityImpersonation ) ; err != nil {
runtime . UnlockOSThread ( )
return nil , err
}
disable = func ( ) error {
disable = func ( ) {
defer runtime . UnlockOSThread ( )
return windows . RevertToSelf ( )
// If RevertToSelf fails, it's not really recoverable and we should panic.
// Failure to do so would leak the privileges we're enabling, which is a
// security issue.
if err := windows . RevertToSelf ( ) ; err != nil {
panic ( fmt . Sprintf ( "RevertToSelf failed: %v" , err ) )
}
}
defer func ( ) {
if err != nil {
disable ( )
@ -254,19 +273,38 @@ func EnableCurrentThreadPrivilege(name string) (disable func() error, err error)
}
defer t . Close ( )
var tp windows . Tokenprivileges
privStr , err := syscall . UTF16PtrFromString ( name )
tp := newTokenPrivileges ( len ( names ) )
privs := tp . AllPrivileges ( )
for i := range privs {
var privStr * uint16
privStr , err = windows . UTF16PtrFromString ( names [ i ] )
if err != nil {
return nil , err
}
err = windows . LookupPrivilegeValue ( nil , privStr , & tp . Privileges [ 0 ] . Luid )
err = windows . LookupPrivilegeValue ( nil , privStr , & privs [ i ] . Luid )
if err != nil {
return nil , err
}
privs [ i ] . Attributes = windows . SE_PRIVILEGE_ENABLED
}
err = windows . AdjustTokenPrivileges ( t , false , tp , 0 , nil , nil )
if err != nil {
return nil , err
}
tp . PrivilegeCount = 1
tp . Privileges [ 0 ] . Attributes = windows . SE_PRIVILEGE_ENABLED
return disable , windows . AdjustTokenPrivileges ( t , false , & tp , 0 , nil , nil )
return disable , nil
}
func newTokenPrivileges ( numPrivs int ) * windows . Tokenprivileges {
if numPrivs <= 0 {
panic ( "numPrivs must be > 0" )
}
numBytes := unsafe . Sizeof ( windows . Tokenprivileges { } ) + ( uintptr ( numPrivs - 1 ) * unsafe . Sizeof ( windows . LUIDAndAttributes { } ) )
buf := make ( [ ] byte , numBytes )
result := ( * windows . Tokenprivileges ) ( unsafe . Pointer ( unsafe . SliceData ( buf ) ) )
result . PrivilegeCount = uint32 ( numPrivs )
return result
}
// StartProcessAsChild starts exePath process as a child of parentPID.
@ -346,35 +384,22 @@ func CreateAppMutex(name string) (windows.Handle, error) {
return windows . CreateMutex ( nil , false , windows . StringToUTF16Ptr ( name ) )
}
func getTokenInfo ( token windows . Token , infoClass uint32 ) ( [ ] byte , error ) {
func getTokenInfo [ T any ] ( token windows . Token , infoClass uint32 ) ( * T , error ) {
var buf [ ] byte
var desiredLen uint32
err := windows . GetTokenInformation ( token , infoClass , nil , 0 , & desiredLen )
if err != nil && err != windows . ERROR_INSUFFICIENT_BUFFER {
return nil , err
}
buf := make ( [ ] byte , desiredLen )
actualLen := desiredLen
err = windows . GetTokenInformation ( token , infoClass , & buf [ 0 ] , desiredLen , & actualLen )
return buf , err
}
err := windows . GetTokenInformation ( token , infoClass , nil , 0 , & desiredLen )
func getTokenUserInfo ( token windows . Token ) ( * windows . Tokenuser , error ) {
buf , err := getTokenInfo ( token , windows . TokenUser )
if err != nil {
return nil , err
for err == windows . ERROR_INSUFFICIENT_BUFFER {
buf = make ( [ ] byte , desiredLen )
err = windows . GetTokenInformation ( token , infoClass , unsafe . SliceData ( buf ) , desiredLen , & desiredLen )
}
return ( * windows . Tokenuser ) ( unsafe . Pointer ( & buf [ 0 ] ) ) , nil
}
func getTokenPrimaryGroupInfo ( token windows . Token ) ( * windows . Tokenprimarygroup , error ) {
buf , err := getTokenInfo ( token , windows . TokenPrimaryGroup )
if err != nil {
return nil , err
}
return ( * windows. Tokenprimarygroup ) ( unsafe . Pointer ( & buf [ 0 ] ) ) , nil
return ( * T ) ( unsafe . Pointer ( unsafe . SliceData ( buf ) ) ) , nil
}
type tokenElevationType int32
@ -417,12 +442,12 @@ func GetCurrentUserSIDs() (*UserSIDs, error) {
}
defer token . Close ( )
userInfo , err := getTokenUserInfo ( token )
userInfo , err := token . GetTokenUser ( )
if err != nil {
return nil , err
}
primaryGroup , err := getTokenPrimaryGroupInfo ( token )
primaryGroup , err := token . GetTokenPrimaryGroup ( )
if err != nil {
return nil , err
}
@ -645,3 +670,40 @@ func registerForRestart(opts RegisterForRestartOpts) error {
return nil
}
// ProcessImageName returns the fully-qualified path to the executable image
// associated with process.
func ProcessImageName ( process windows . Handle ) ( string , error ) {
var pathBuf [ windows . MAX_PATH ] uint16
pathBufLen := uint32 ( len ( pathBuf ) )
if err := windows . QueryFullProcessImageName ( process , 0 , & pathBuf [ 0 ] , & pathBufLen ) ; err != nil {
return "" , err
}
return windows . UTF16ToString ( pathBuf [ : pathBufLen ] ) , nil
}
// TSSessionIDToLogonSessionID retrieves the logon session ID associated with
// tsSessionId, which is a Terminal Services / RDP session ID. The calling
// process must be running as LocalSystem.
func TSSessionIDToLogonSessionID ( tsSessionID uint32 ) ( logonSessionID windows . LUID , err error ) {
var token windows . Token
if err := windows . WTSQueryUserToken ( tsSessionID , & token ) ; err != nil {
return logonSessionID , fmt . Errorf ( "WTSQueryUserToken: %w" , err )
}
defer token . Close ( )
return LogonSessionID ( token )
}
type tokenOrigin struct {
originatingLogonSession windows . LUID
}
// LogonSessionID obtains the logon session ID associated with token.
func LogonSessionID ( token windows . Token ) ( logonSessionID windows . LUID , err error ) {
origin , err := getTokenInfo [ tokenOrigin ] ( token , windows . TokenOrigin )
if err != nil {
return logonSessionID , err
}
return origin . originatingLogonSession , nil
}