privilege.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package winio
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "runtime"
  7. "syscall"
  8. "unicode/utf16"
  9. )
  10. //sys adjustTokenPrivileges(token syscall.Handle, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
  11. //sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
  12. //sys revertToSelf() (err error) = advapi32.RevertToSelf
  13. //sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *syscall.Handle) (err error) = advapi32.OpenThreadToken
  14. //sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
  15. //sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
  16. //sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
  17. //sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
  18. const (
  19. SE_PRIVILEGE_ENABLED = 2
  20. ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
  21. SeBackupPrivilege = "SeBackupPrivilege"
  22. SeRestorePrivilege = "SeRestorePrivilege"
  23. )
  24. const (
  25. securityAnonymous = iota
  26. securityIdentification
  27. securityImpersonation
  28. securityDelegation
  29. )
  30. type PrivilegeError struct {
  31. privileges []uint64
  32. }
  33. func (e *PrivilegeError) Error() string {
  34. s := ""
  35. if len(e.privileges) > 1 {
  36. s = "Could not enable privileges "
  37. } else {
  38. s = "Could not enable privilege "
  39. }
  40. for i, p := range e.privileges {
  41. if i != 0 {
  42. s += ", "
  43. }
  44. s += `"`
  45. s += getPrivilegeName(p)
  46. s += `"`
  47. }
  48. return s
  49. }
  50. func RunWithPrivilege(name string, fn func() error) error {
  51. return RunWithPrivileges([]string{name}, fn)
  52. }
  53. func RunWithPrivileges(names []string, fn func() error) error {
  54. var privileges []uint64
  55. for _, name := range names {
  56. p := uint64(0)
  57. err := lookupPrivilegeValue("", name, &p)
  58. if err != nil {
  59. return err
  60. }
  61. privileges = append(privileges, p)
  62. }
  63. runtime.LockOSThread()
  64. defer runtime.UnlockOSThread()
  65. token, err := newThreadToken()
  66. if err != nil {
  67. return err
  68. }
  69. defer releaseThreadToken(token)
  70. err = adjustPrivileges(token, privileges)
  71. if err != nil {
  72. return err
  73. }
  74. return fn()
  75. }
  76. func adjustPrivileges(token syscall.Handle, privileges []uint64) error {
  77. var b bytes.Buffer
  78. binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
  79. for _, p := range privileges {
  80. binary.Write(&b, binary.LittleEndian, p)
  81. binary.Write(&b, binary.LittleEndian, uint32(SE_PRIVILEGE_ENABLED))
  82. }
  83. prevState := make([]byte, b.Len())
  84. reqSize := uint32(0)
  85. success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
  86. if !success {
  87. return err
  88. }
  89. if err == ERROR_NOT_ALL_ASSIGNED {
  90. return &PrivilegeError{privileges}
  91. }
  92. return nil
  93. }
  94. func getPrivilegeName(luid uint64) string {
  95. var nameBuffer [256]uint16
  96. bufSize := uint32(len(nameBuffer))
  97. err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
  98. if err != nil {
  99. return fmt.Sprintf("<unknown privilege %d>", luid)
  100. }
  101. var displayNameBuffer [256]uint16
  102. displayBufSize := uint32(len(displayNameBuffer))
  103. var langId uint32
  104. err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langId)
  105. if err != nil {
  106. return fmt.Sprintf("<unknown privilege %s>", utf16.Decode(nameBuffer[:bufSize]))
  107. }
  108. return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
  109. }
  110. func newThreadToken() (syscall.Handle, error) {
  111. err := impersonateSelf(securityImpersonation)
  112. if err != nil {
  113. panic(err)
  114. return 0, err
  115. }
  116. var token syscall.Handle
  117. err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
  118. if err != nil {
  119. rerr := revertToSelf()
  120. if rerr != nil {
  121. panic(rerr)
  122. }
  123. return 0, err
  124. }
  125. return token, nil
  126. }
  127. func releaseThreadToken(h syscall.Handle) {
  128. err := revertToSelf()
  129. if err != nil {
  130. panic(err)
  131. }
  132. syscall.Close(h)
  133. }