privilege.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // +build windows
  2. package winio
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "fmt"
  7. "runtime"
  8. "sync"
  9. "syscall"
  10. "unicode/utf16"
  11. "golang.org/x/sys/windows"
  12. )
  13. //sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
  14. //sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
  15. //sys revertToSelf() (err error) = advapi32.RevertToSelf
  16. //sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
  17. //sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
  18. //sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
  19. //sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
  20. //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
  21. const (
  22. SE_PRIVILEGE_ENABLED = 2
  23. ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
  24. SeBackupPrivilege = "SeBackupPrivilege"
  25. SeRestorePrivilege = "SeRestorePrivilege"
  26. )
  27. const (
  28. securityAnonymous = iota
  29. securityIdentification
  30. securityImpersonation
  31. securityDelegation
  32. )
  33. var (
  34. privNames = make(map[string]uint64)
  35. privNameMutex sync.Mutex
  36. )
  37. // PrivilegeError represents an error enabling privileges.
  38. type PrivilegeError struct {
  39. privileges []uint64
  40. }
  41. func (e *PrivilegeError) Error() string {
  42. s := ""
  43. if len(e.privileges) > 1 {
  44. s = "Could not enable privileges "
  45. } else {
  46. s = "Could not enable privilege "
  47. }
  48. for i, p := range e.privileges {
  49. if i != 0 {
  50. s += ", "
  51. }
  52. s += `"`
  53. s += getPrivilegeName(p)
  54. s += `"`
  55. }
  56. return s
  57. }
  58. // RunWithPrivilege enables a single privilege for a function call.
  59. func RunWithPrivilege(name string, fn func() error) error {
  60. return RunWithPrivileges([]string{name}, fn)
  61. }
  62. // RunWithPrivileges enables privileges for a function call.
  63. func RunWithPrivileges(names []string, fn func() error) error {
  64. privileges, err := mapPrivileges(names)
  65. if err != nil {
  66. return err
  67. }
  68. runtime.LockOSThread()
  69. defer runtime.UnlockOSThread()
  70. token, err := newThreadToken()
  71. if err != nil {
  72. return err
  73. }
  74. defer releaseThreadToken(token)
  75. err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
  76. if err != nil {
  77. return err
  78. }
  79. return fn()
  80. }
  81. func mapPrivileges(names []string) ([]uint64, error) {
  82. var privileges []uint64
  83. privNameMutex.Lock()
  84. defer privNameMutex.Unlock()
  85. for _, name := range names {
  86. p, ok := privNames[name]
  87. if !ok {
  88. err := lookupPrivilegeValue("", name, &p)
  89. if err != nil {
  90. return nil, err
  91. }
  92. privNames[name] = p
  93. }
  94. privileges = append(privileges, p)
  95. }
  96. return privileges, nil
  97. }
  98. // EnableProcessPrivileges enables privileges globally for the process.
  99. func EnableProcessPrivileges(names []string) error {
  100. return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
  101. }
  102. // DisableProcessPrivileges disables privileges globally for the process.
  103. func DisableProcessPrivileges(names []string) error {
  104. return enableDisableProcessPrivilege(names, 0)
  105. }
  106. func enableDisableProcessPrivilege(names []string, action uint32) error {
  107. privileges, err := mapPrivileges(names)
  108. if err != nil {
  109. return err
  110. }
  111. p, _ := windows.GetCurrentProcess()
  112. var token windows.Token
  113. err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
  114. if err != nil {
  115. return err
  116. }
  117. defer token.Close()
  118. return adjustPrivileges(token, privileges, action)
  119. }
  120. func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
  121. var b bytes.Buffer
  122. binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
  123. for _, p := range privileges {
  124. binary.Write(&b, binary.LittleEndian, p)
  125. binary.Write(&b, binary.LittleEndian, action)
  126. }
  127. prevState := make([]byte, b.Len())
  128. reqSize := uint32(0)
  129. success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
  130. if !success {
  131. return err
  132. }
  133. if err == ERROR_NOT_ALL_ASSIGNED {
  134. return &PrivilegeError{privileges}
  135. }
  136. return nil
  137. }
  138. func getPrivilegeName(luid uint64) string {
  139. var nameBuffer [256]uint16
  140. bufSize := uint32(len(nameBuffer))
  141. err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
  142. if err != nil {
  143. return fmt.Sprintf("<unknown privilege %d>", luid)
  144. }
  145. var displayNameBuffer [256]uint16
  146. displayBufSize := uint32(len(displayNameBuffer))
  147. var langID uint32
  148. err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
  149. if err != nil {
  150. return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
  151. }
  152. return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
  153. }
  154. func newThreadToken() (windows.Token, error) {
  155. err := impersonateSelf(securityImpersonation)
  156. if err != nil {
  157. return 0, err
  158. }
  159. var token windows.Token
  160. err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
  161. if err != nil {
  162. rerr := revertToSelf()
  163. if rerr != nil {
  164. panic(rerr)
  165. }
  166. return 0, err
  167. }
  168. return token, nil
  169. }
  170. func releaseThreadToken(h windows.Token) {
  171. err := revertToSelf()
  172. if err != nil {
  173. panic(err)
  174. }
  175. h.Close()
  176. }