123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- // +build windows
- package winio
- import (
- "bytes"
- "encoding/binary"
- "fmt"
- "runtime"
- "sync"
- "syscall"
- "unicode/utf16"
- "golang.org/x/sys/windows"
- )
- //sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
- //sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
- //sys revertToSelf() (err error) = advapi32.RevertToSelf
- //sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
- //sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
- //sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
- //sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
- //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
- const (
- SE_PRIVILEGE_ENABLED = 2
- ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
- SeBackupPrivilege = "SeBackupPrivilege"
- SeRestorePrivilege = "SeRestorePrivilege"
- )
- const (
- securityAnonymous = iota
- securityIdentification
- securityImpersonation
- securityDelegation
- )
- var (
- privNames = make(map[string]uint64)
- privNameMutex sync.Mutex
- )
- // PrivilegeError represents an error enabling privileges.
- type PrivilegeError struct {
- privileges []uint64
- }
- func (e *PrivilegeError) Error() string {
- s := ""
- if len(e.privileges) > 1 {
- s = "Could not enable privileges "
- } else {
- s = "Could not enable privilege "
- }
- for i, p := range e.privileges {
- if i != 0 {
- s += ", "
- }
- s += `"`
- s += getPrivilegeName(p)
- s += `"`
- }
- return s
- }
- // RunWithPrivilege enables a single privilege for a function call.
- func RunWithPrivilege(name string, fn func() error) error {
- return RunWithPrivileges([]string{name}, fn)
- }
- // RunWithPrivileges enables privileges for a function call.
- func RunWithPrivileges(names []string, fn func() error) error {
- privileges, err := mapPrivileges(names)
- if err != nil {
- return err
- }
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- token, err := newThreadToken()
- if err != nil {
- return err
- }
- defer releaseThreadToken(token)
- err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
- if err != nil {
- return err
- }
- return fn()
- }
- func mapPrivileges(names []string) ([]uint64, error) {
- var privileges []uint64
- privNameMutex.Lock()
- defer privNameMutex.Unlock()
- for _, name := range names {
- p, ok := privNames[name]
- if !ok {
- err := lookupPrivilegeValue("", name, &p)
- if err != nil {
- return nil, err
- }
- privNames[name] = p
- }
- privileges = append(privileges, p)
- }
- return privileges, nil
- }
- // EnableProcessPrivileges enables privileges globally for the process.
- func EnableProcessPrivileges(names []string) error {
- return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
- }
- // DisableProcessPrivileges disables privileges globally for the process.
- func DisableProcessPrivileges(names []string) error {
- return enableDisableProcessPrivilege(names, 0)
- }
- func enableDisableProcessPrivilege(names []string, action uint32) error {
- privileges, err := mapPrivileges(names)
- if err != nil {
- return err
- }
- p, _ := windows.GetCurrentProcess()
- var token windows.Token
- err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
- if err != nil {
- return err
- }
- defer token.Close()
- return adjustPrivileges(token, privileges, action)
- }
- func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
- var b bytes.Buffer
- binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
- for _, p := range privileges {
- binary.Write(&b, binary.LittleEndian, p)
- binary.Write(&b, binary.LittleEndian, action)
- }
- prevState := make([]byte, b.Len())
- reqSize := uint32(0)
- success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
- if !success {
- return err
- }
- if err == ERROR_NOT_ALL_ASSIGNED {
- return &PrivilegeError{privileges}
- }
- return nil
- }
- func getPrivilegeName(luid uint64) string {
- var nameBuffer [256]uint16
- bufSize := uint32(len(nameBuffer))
- err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
- if err != nil {
- return fmt.Sprintf("<unknown privilege %d>", luid)
- }
- var displayNameBuffer [256]uint16
- displayBufSize := uint32(len(displayNameBuffer))
- var langID uint32
- err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
- if err != nil {
- return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
- }
- return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
- }
- func newThreadToken() (windows.Token, error) {
- err := impersonateSelf(securityImpersonation)
- if err != nil {
- return 0, err
- }
- var token windows.Token
- err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
- if err != nil {
- rerr := revertToSelf()
- if rerr != nil {
- panic(rerr)
- }
- return 0, err
- }
- return token, nil
- }
- func releaseThreadToken(h windows.Token) {
- err := revertToSelf()
- if err != nil {
- panic(err)
- }
- h.Close()
- }
|