sspi.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // +build windows
  2. package SSPI
  3. import (
  4. "fmt"
  5. "strings"
  6. "syscall"
  7. "unsafe"
  8. )
  9. var (
  10. secur32_dll = syscall.NewLazyDLL("secur32.dll")
  11. initSecurityInterface = secur32_dll.NewProc("InitSecurityInterfaceW")
  12. sec_fn *SecurityFunctionTable
  13. )
  14. func init() {
  15. ptr, _, _ := initSecurityInterface.Call()
  16. sec_fn = (*SecurityFunctionTable)(unsafe.Pointer(ptr))
  17. }
  18. const (
  19. SEC_E_OK = 0
  20. SECPKG_CRED_OUTBOUND = 2
  21. SEC_WINNT_AUTH_IDENTITY_UNICODE = 2
  22. ISC_REQ_DELEGATE = 0x00000001
  23. ISC_REQ_REPLAY_DETECT = 0x00000004
  24. ISC_REQ_SEQUENCE_DETECT = 0x00000008
  25. ISC_REQ_CONFIDENTIALITY = 0x00000010
  26. ISC_REQ_CONNECTION = 0x00000800
  27. SECURITY_NETWORK_DREP = 0
  28. SEC_I_CONTINUE_NEEDED = 0x00090312
  29. SEC_I_COMPLETE_NEEDED = 0x00090313
  30. SEC_I_COMPLETE_AND_CONTINUE = 0x00090314
  31. SECBUFFER_VERSION = 0
  32. SECBUFFER_TOKEN = 2
  33. NTLMBUF_LEN = 12000
  34. )
  35. const ISC_REQ = ISC_REQ_CONFIDENTIALITY |
  36. ISC_REQ_REPLAY_DETECT |
  37. ISC_REQ_SEQUENCE_DETECT |
  38. ISC_REQ_CONNECTION |
  39. ISC_REQ_DELEGATE
  40. type SecurityFunctionTable struct {
  41. dwVersion uint32
  42. EnumerateSecurityPackages uintptr
  43. QueryCredentialsAttributes uintptr
  44. AcquireCredentialsHandle uintptr
  45. FreeCredentialsHandle uintptr
  46. Reserved2 uintptr
  47. InitializeSecurityContext uintptr
  48. AcceptSecurityContext uintptr
  49. CompleteAuthToken uintptr
  50. DeleteSecurityContext uintptr
  51. ApplyControlToken uintptr
  52. QueryContextAttributes uintptr
  53. ImpersonateSecurityContext uintptr
  54. RevertSecurityContext uintptr
  55. MakeSignature uintptr
  56. VerifySignature uintptr
  57. FreeContextBuffer uintptr
  58. QuerySecurityPackageInfo uintptr
  59. Reserved3 uintptr
  60. Reserved4 uintptr
  61. Reserved5 uintptr
  62. Reserved6 uintptr
  63. Reserved7 uintptr
  64. Reserved8 uintptr
  65. QuerySecurityContextToken uintptr
  66. EncryptMessage uintptr
  67. DecryptMessage uintptr
  68. }
  69. type SEC_WINNT_AUTH_IDENTITY struct {
  70. User *uint16
  71. UserLength uint32
  72. Domain *uint16
  73. DomainLength uint32
  74. Password *uint16
  75. PasswordLength uint32
  76. Flags uint32
  77. }
  78. type TimeStamp struct {
  79. LowPart uint32
  80. HighPart int32
  81. }
  82. type SecHandle struct {
  83. dwLower uintptr
  84. dwUpper uintptr
  85. }
  86. type SecBuffer struct {
  87. cbBuffer uint32
  88. BufferType uint32
  89. pvBuffer *byte
  90. }
  91. type SecBufferDesc struct {
  92. ulVersion uint32
  93. cBuffers uint32
  94. pBuffers *SecBuffer
  95. }
  96. type SSPIAuth struct {
  97. Domain string
  98. UserName string
  99. Password string
  100. Service string
  101. cred SecHandle
  102. ctxt SecHandle
  103. }
  104. type Auth interface {
  105. InitialBytes() ([]byte, error)
  106. NextBytes([]byte) ([]byte, error)
  107. Free()
  108. }
  109. // GetAuth returns SSPI auth object initialized with given params and true for success
  110. // In case of error, it will return nil SSPI object and false for failure
  111. func GetAuth(user, password, service, workstation string) (Auth, bool) {
  112. if user == "" {
  113. return &SSPIAuth{Service: service}, true
  114. }
  115. if !strings.ContainsRune(user, '\\') {
  116. return nil, false
  117. }
  118. domain_user := strings.SplitN(user, "\\", 2)
  119. return &SSPIAuth{
  120. Domain: domain_user[0],
  121. UserName: domain_user[1],
  122. Password: password,
  123. Service: service,
  124. }, true
  125. }
  126. func (auth *SSPIAuth) InitialBytes() ([]byte, error) {
  127. var identity *SEC_WINNT_AUTH_IDENTITY
  128. if auth.UserName != "" {
  129. identity = &SEC_WINNT_AUTH_IDENTITY{
  130. Flags: SEC_WINNT_AUTH_IDENTITY_UNICODE,
  131. Password: syscall.StringToUTF16Ptr(auth.Password),
  132. PasswordLength: uint32(len(auth.Password)),
  133. Domain: syscall.StringToUTF16Ptr(auth.Domain),
  134. DomainLength: uint32(len(auth.Domain)),
  135. User: syscall.StringToUTF16Ptr(auth.UserName),
  136. UserLength: uint32(len(auth.UserName)),
  137. }
  138. }
  139. var ts TimeStamp
  140. sec_ok, _, _ := syscall.Syscall9(sec_fn.AcquireCredentialsHandle,
  141. 9,
  142. 0,
  143. uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Negotiate"))),
  144. SECPKG_CRED_OUTBOUND,
  145. 0,
  146. uintptr(unsafe.Pointer(identity)),
  147. 0,
  148. 0,
  149. uintptr(unsafe.Pointer(&auth.cred)),
  150. uintptr(unsafe.Pointer(&ts)))
  151. if sec_ok != SEC_E_OK {
  152. return nil, fmt.Errorf("AcquireCredentialsHandle failed %x", sec_ok)
  153. }
  154. var buf SecBuffer
  155. var desc SecBufferDesc
  156. desc.ulVersion = SECBUFFER_VERSION
  157. desc.cBuffers = 1
  158. desc.pBuffers = &buf
  159. outbuf := make([]byte, NTLMBUF_LEN)
  160. buf.cbBuffer = NTLMBUF_LEN
  161. buf.BufferType = SECBUFFER_TOKEN
  162. buf.pvBuffer = &outbuf[0]
  163. var attrs uint32
  164. sec_ok, _, _ = syscall.Syscall12(sec_fn.InitializeSecurityContext,
  165. 12,
  166. uintptr(unsafe.Pointer(&auth.cred)),
  167. 0,
  168. uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))),
  169. ISC_REQ,
  170. 0,
  171. SECURITY_NETWORK_DREP,
  172. 0,
  173. 0,
  174. uintptr(unsafe.Pointer(&auth.ctxt)),
  175. uintptr(unsafe.Pointer(&desc)),
  176. uintptr(unsafe.Pointer(&attrs)),
  177. uintptr(unsafe.Pointer(&ts)))
  178. if sec_ok == SEC_I_COMPLETE_AND_CONTINUE ||
  179. sec_ok == SEC_I_COMPLETE_NEEDED {
  180. syscall.Syscall6(sec_fn.CompleteAuthToken,
  181. 2,
  182. uintptr(unsafe.Pointer(&auth.ctxt)),
  183. uintptr(unsafe.Pointer(&desc)),
  184. 0, 0, 0, 0)
  185. } else if sec_ok != SEC_E_OK &&
  186. sec_ok != SEC_I_CONTINUE_NEEDED {
  187. syscall.Syscall6(sec_fn.FreeCredentialsHandle,
  188. 1,
  189. uintptr(unsafe.Pointer(&auth.cred)),
  190. 0, 0, 0, 0, 0)
  191. return nil, fmt.Errorf("InitialBytes InitializeSecurityContext failed %x", sec_ok)
  192. }
  193. return outbuf[:buf.cbBuffer], nil
  194. }
  195. func (auth *SSPIAuth) NextBytes(bytes []byte) ([]byte, error) {
  196. var in_buf, out_buf SecBuffer
  197. var in_desc, out_desc SecBufferDesc
  198. in_desc.ulVersion = SECBUFFER_VERSION
  199. in_desc.cBuffers = 1
  200. in_desc.pBuffers = &in_buf
  201. out_desc.ulVersion = SECBUFFER_VERSION
  202. out_desc.cBuffers = 1
  203. out_desc.pBuffers = &out_buf
  204. in_buf.BufferType = SECBUFFER_TOKEN
  205. in_buf.pvBuffer = &bytes[0]
  206. in_buf.cbBuffer = uint32(len(bytes))
  207. outbuf := make([]byte, NTLMBUF_LEN)
  208. out_buf.BufferType = SECBUFFER_TOKEN
  209. out_buf.pvBuffer = &outbuf[0]
  210. out_buf.cbBuffer = NTLMBUF_LEN
  211. var attrs uint32
  212. var ts TimeStamp
  213. sec_ok, _, _ := syscall.Syscall12(sec_fn.InitializeSecurityContext,
  214. 12,
  215. uintptr(unsafe.Pointer(&auth.cred)),
  216. uintptr(unsafe.Pointer(&auth.ctxt)),
  217. uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(auth.Service))),
  218. ISC_REQ,
  219. 0,
  220. SECURITY_NETWORK_DREP,
  221. uintptr(unsafe.Pointer(&in_desc)),
  222. 0,
  223. uintptr(unsafe.Pointer(&auth.ctxt)),
  224. uintptr(unsafe.Pointer(&out_desc)),
  225. uintptr(unsafe.Pointer(&attrs)),
  226. uintptr(unsafe.Pointer(&ts)))
  227. if sec_ok == SEC_I_COMPLETE_AND_CONTINUE ||
  228. sec_ok == SEC_I_COMPLETE_NEEDED {
  229. syscall.Syscall6(sec_fn.CompleteAuthToken,
  230. 2,
  231. uintptr(unsafe.Pointer(&auth.ctxt)),
  232. uintptr(unsafe.Pointer(&out_desc)),
  233. 0, 0, 0, 0)
  234. } else if sec_ok != SEC_E_OK &&
  235. sec_ok != SEC_I_CONTINUE_NEEDED {
  236. return nil, fmt.Errorf("NextBytes InitializeSecurityContext failed %x", sec_ok)
  237. }
  238. return outbuf[:out_buf.cbBuffer], nil
  239. }
  240. func (auth *SSPIAuth) Free() {
  241. syscall.Syscall6(sec_fn.DeleteSecurityContext,
  242. 1,
  243. uintptr(unsafe.Pointer(&auth.ctxt)),
  244. 0, 0, 0, 0, 0)
  245. syscall.Syscall6(sec_fn.FreeCredentialsHandle,
  246. 1,
  247. uintptr(unsafe.Pointer(&auth.cred)),
  248. 0, 0, 0, 0, 0)
  249. }