sspi.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build windows
  5. package sspi
  6. import (
  7. "fmt"
  8. "syscall"
  9. "time"
  10. "unsafe"
  11. )
  12. // TODO: add documentation
  13. type PackageInfo struct {
  14. Capabilities uint32
  15. Version uint16
  16. RPCID uint16
  17. MaxToken uint32
  18. Name string
  19. Comment string
  20. }
  21. func QueryPackageInfo(pkgname string) (*PackageInfo, error) {
  22. name, err := syscall.UTF16PtrFromString(pkgname)
  23. if err != nil {
  24. return nil, err
  25. }
  26. var pi *SecPkgInfo
  27. ret := QuerySecurityPackageInfo(name, &pi)
  28. if ret != SEC_E_OK {
  29. return nil, ret
  30. }
  31. defer FreeContextBuffer((*byte)(unsafe.Pointer(pi)))
  32. return &PackageInfo{
  33. Capabilities: pi.Capabilities,
  34. Version: pi.Version,
  35. RPCID: pi.RPCID,
  36. MaxToken: pi.MaxToken,
  37. Name: syscall.UTF16ToString((*[2 << 12]uint16)(unsafe.Pointer(pi.Name))[:]),
  38. Comment: syscall.UTF16ToString((*[2 << 12]uint16)(unsafe.Pointer(pi.Comment))[:]),
  39. }, nil
  40. }
  41. type Credentials struct {
  42. Handle CredHandle
  43. expiry syscall.Filetime
  44. }
  45. // AcquireCredentials calls the windows AcquireCredentialsHandle function and
  46. // returns Credentials containing a security handle that can be used for
  47. // InitializeSecurityContext or AcceptSecurityContext operations.
  48. // As a special case, passing an empty string as the principal parameter will
  49. // pass a null string to the underlying function.
  50. func AcquireCredentials(principal string, pkgname string, creduse uint32, authdata *byte) (*Credentials, error) {
  51. var principalName *uint16
  52. if principal != "" {
  53. var err error
  54. principalName, err = syscall.UTF16PtrFromString(principal)
  55. if err != nil {
  56. return nil, err
  57. }
  58. }
  59. name, err := syscall.UTF16PtrFromString(pkgname)
  60. if err != nil {
  61. return nil, err
  62. }
  63. var c Credentials
  64. ret := AcquireCredentialsHandle(principalName, name, creduse, nil, authdata, 0, 0, &c.Handle, &c.expiry)
  65. if ret != SEC_E_OK {
  66. return nil, ret
  67. }
  68. return &c, nil
  69. }
  70. func (c *Credentials) Release() error {
  71. if c == nil {
  72. return nil
  73. }
  74. ret := FreeCredentialsHandle(&c.Handle)
  75. if ret != SEC_E_OK {
  76. return ret
  77. }
  78. return nil
  79. }
  80. func (c *Credentials) Expiry() time.Time {
  81. return time.Unix(0, c.expiry.Nanoseconds())
  82. }
  83. // TODO: add functions to display and manage RequestedFlags and EstablishedFlags fields.
  84. // TODO: maybe get rid of RequestedFlags and EstablishedFlags fields, and replace them with input parameter for New...Context and return value of Update (instead of current bool parameter).
  85. type updateFunc func(c *Context, targname *uint16, h, newh *CtxtHandle, out, in *SecBufferDesc) syscall.Errno
  86. type Context struct {
  87. Cred *Credentials
  88. Handle *CtxtHandle
  89. handle CtxtHandle
  90. updFn updateFunc
  91. expiry syscall.Filetime
  92. RequestedFlags uint32
  93. EstablishedFlags uint32
  94. }
  95. func NewClientContext(cred *Credentials, flags uint32) *Context {
  96. return &Context{
  97. Cred: cred,
  98. updFn: initialize,
  99. RequestedFlags: flags,
  100. }
  101. }
  102. func NewServerContext(cred *Credentials, flags uint32) *Context {
  103. return &Context{
  104. Cred: cred,
  105. updFn: accept,
  106. RequestedFlags: flags,
  107. }
  108. }
  109. func initialize(c *Context, targname *uint16, h, newh *CtxtHandle, out, in *SecBufferDesc) syscall.Errno {
  110. return InitializeSecurityContext(&c.Cred.Handle, h, targname, c.RequestedFlags,
  111. 0, SECURITY_NATIVE_DREP, in, 0, newh, out, &c.EstablishedFlags, &c.expiry)
  112. }
  113. func accept(c *Context, targname *uint16, h, newh *CtxtHandle, out, in *SecBufferDesc) syscall.Errno {
  114. return AcceptSecurityContext(&c.Cred.Handle, h, in, c.RequestedFlags,
  115. SECURITY_NATIVE_DREP, newh, out, &c.EstablishedFlags, &c.expiry)
  116. }
  117. func (c *Context) Update(targname *uint16, out, in *SecBufferDesc) syscall.Errno {
  118. h := c.Handle
  119. if c.Handle == nil {
  120. c.Handle = &c.handle
  121. }
  122. return c.updFn(c, targname, h, c.Handle, out, in)
  123. }
  124. func (c *Context) Release() error {
  125. if c == nil {
  126. return nil
  127. }
  128. ret := DeleteSecurityContext(c.Handle)
  129. if ret != SEC_E_OK {
  130. return ret
  131. }
  132. return nil
  133. }
  134. func (c *Context) Expiry() time.Time {
  135. return time.Unix(0, c.expiry.Nanoseconds())
  136. }
  137. // TODO: add comment to function doco that this "impersonation" is applied to current OS thread.
  138. func (c *Context) ImpersonateUser() error {
  139. ret := ImpersonateSecurityContext(c.Handle)
  140. if ret != SEC_E_OK {
  141. return ret
  142. }
  143. return nil
  144. }
  145. func (c *Context) RevertToSelf() error {
  146. ret := RevertSecurityContext(c.Handle)
  147. if ret != SEC_E_OK {
  148. return ret
  149. }
  150. return nil
  151. }
  152. // Sizes queries the context for the sizes used in per-message functions.
  153. // It returns the maximum token size used in authentication exchanges, the
  154. // maximum signature size, the preferred integral size of messages, the
  155. // size of any security trailer, and any error.
  156. func (c *Context) Sizes() (uint32, uint32, uint32, uint32, error) {
  157. var s _SecPkgContext_Sizes
  158. ret := QueryContextAttributes(c.Handle, _SECPKG_ATTR_SIZES, (*byte)(unsafe.Pointer(&s)))
  159. if ret != SEC_E_OK {
  160. return 0, 0, 0, 0, ret
  161. }
  162. return s.MaxToken, s.MaxSignature, s.BlockSize, s.SecurityTrailer, nil
  163. }
  164. // VerifyFlags determines if all flags used to construct the context
  165. // were honored (see NewClientContext). It should be called after c.Update.
  166. func (c *Context) VerifyFlags() error {
  167. return c.VerifySelectiveFlags(c.RequestedFlags)
  168. }
  169. // VerifySelectiveFlags determines if the given flags were honored (see NewClientContext).
  170. // It should be called after c.Update.
  171. func (c *Context) VerifySelectiveFlags(flags uint32) error {
  172. if valid, missing, extra := verifySelectiveFlags(flags, c.RequestedFlags); !valid {
  173. return fmt.Errorf("sspi: invalid flags check: desired=%b requested=%b missing=%b extra=%b", flags, c.RequestedFlags, missing, extra)
  174. }
  175. if valid, missing, extra := verifySelectiveFlags(flags, c.EstablishedFlags); !valid {
  176. return fmt.Errorf("sspi: invalid flags: desired=%b established=%b missing=%b extra=%b", flags, c.EstablishedFlags, missing, extra)
  177. }
  178. return nil
  179. }
  180. // verifySelectiveFlags determines if all bits requested in flags are set in establishedFlags.
  181. // missing represents the bits set in flags that are not set in establishedFlags.
  182. // extra represents the bits set in establishedFlags that are not set in flags.
  183. // valid is true and missing is zero when establishedFlags has all of the requested flags.
  184. func verifySelectiveFlags(flags, establishedFlags uint32) (valid bool, missing, extra uint32) {
  185. missing = flags&establishedFlags ^ flags
  186. extra = flags | establishedFlags ^ flags
  187. valid = missing == 0
  188. return valid, missing, extra
  189. }
  190. // NewSecBufferDesc returns an initialized SecBufferDesc describing the
  191. // provided SecBuffer.
  192. func NewSecBufferDesc(b []SecBuffer) *SecBufferDesc {
  193. return &SecBufferDesc{
  194. Version: SECBUFFER_VERSION,
  195. BuffersCount: uint32(len(b)),
  196. Buffers: &b[0],
  197. }
  198. }