You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ntlm.go 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // +build !windows
  2. package mssql
  3. import (
  4. "crypto/des"
  5. "crypto/md5"
  6. "crypto/rand"
  7. "encoding/binary"
  8. "errors"
  9. "strings"
  10. "unicode/utf16"
  11. "golang.org/x/crypto/md4"
  12. )
  13. const (
  14. _NEGOTIATE_MESSAGE = 1
  15. _CHALLENGE_MESSAGE = 2
  16. _AUTHENTICATE_MESSAGE = 3
  17. )
  18. const (
  19. _NEGOTIATE_UNICODE = 0x00000001
  20. _NEGOTIATE_OEM = 0x00000002
  21. _NEGOTIATE_TARGET = 0x00000004
  22. _NEGOTIATE_SIGN = 0x00000010
  23. _NEGOTIATE_SEAL = 0x00000020
  24. _NEGOTIATE_DATAGRAM = 0x00000040
  25. _NEGOTIATE_LMKEY = 0x00000080
  26. _NEGOTIATE_NTLM = 0x00000200
  27. _NEGOTIATE_ANONYMOUS = 0x00000800
  28. _NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000
  29. _NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000
  30. _NEGOTIATE_ALWAYS_SIGN = 0x00008000
  31. _NEGOTIATE_TARGET_TYPE_DOMAIN = 0x00010000
  32. _NEGOTIATE_TARGET_TYPE_SERVER = 0x00020000
  33. _NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000
  34. _NEGOTIATE_IDENTIFY = 0x00100000
  35. _REQUEST_NON_NT_SESSION_KEY = 0x00400000
  36. _NEGOTIATE_TARGET_INFO = 0x00800000
  37. _NEGOTIATE_VERSION = 0x02000000
  38. _NEGOTIATE_128 = 0x20000000
  39. _NEGOTIATE_KEY_EXCH = 0x40000000
  40. _NEGOTIATE_56 = 0x80000000
  41. )
  42. const _NEGOTIATE_FLAGS = _NEGOTIATE_UNICODE |
  43. _NEGOTIATE_NTLM |
  44. _NEGOTIATE_OEM_DOMAIN_SUPPLIED |
  45. _NEGOTIATE_OEM_WORKSTATION_SUPPLIED |
  46. _NEGOTIATE_ALWAYS_SIGN |
  47. _NEGOTIATE_EXTENDED_SESSIONSECURITY
  48. type ntlmAuth struct {
  49. Domain string
  50. UserName string
  51. Password string
  52. Workstation string
  53. }
  54. func getAuth(user, password, service, workstation string) (auth, bool) {
  55. if !strings.ContainsRune(user, '\\') {
  56. return nil, false
  57. }
  58. domain_user := strings.SplitN(user, "\\", 2)
  59. return &ntlmAuth{
  60. Domain: domain_user[0],
  61. UserName: domain_user[1],
  62. Password: password,
  63. Workstation: workstation,
  64. }, true
  65. }
  66. func utf16le(val string) []byte {
  67. var v []byte
  68. for _, r := range val {
  69. if utf16.IsSurrogate(r) {
  70. r1, r2 := utf16.EncodeRune(r)
  71. v = append(v, byte(r1), byte(r1>>8))
  72. v = append(v, byte(r2), byte(r2>>8))
  73. } else {
  74. v = append(v, byte(r), byte(r>>8))
  75. }
  76. }
  77. return v
  78. }
  79. func (auth *ntlmAuth) InitialBytes() ([]byte, error) {
  80. domain_len := len(auth.Domain)
  81. workstation_len := len(auth.Workstation)
  82. msg := make([]byte, 40+domain_len+workstation_len)
  83. copy(msg, []byte("NTLMSSP\x00"))
  84. binary.LittleEndian.PutUint32(msg[8:], _NEGOTIATE_MESSAGE)
  85. binary.LittleEndian.PutUint32(msg[12:], _NEGOTIATE_FLAGS)
  86. // Domain Name Fields
  87. binary.LittleEndian.PutUint16(msg[16:], uint16(domain_len))
  88. binary.LittleEndian.PutUint16(msg[18:], uint16(domain_len))
  89. binary.LittleEndian.PutUint32(msg[20:], 40)
  90. // Workstation Fields
  91. binary.LittleEndian.PutUint16(msg[24:], uint16(workstation_len))
  92. binary.LittleEndian.PutUint16(msg[26:], uint16(workstation_len))
  93. binary.LittleEndian.PutUint32(msg[28:], uint32(40+domain_len))
  94. // Version
  95. binary.LittleEndian.PutUint32(msg[32:], 0)
  96. binary.LittleEndian.PutUint32(msg[36:], 0)
  97. // Payload
  98. copy(msg[40:], auth.Domain)
  99. copy(msg[40+domain_len:], auth.Workstation)
  100. return msg, nil
  101. }
  102. var errorNTLM = errors.New("NTLM protocol error")
  103. func createDesKey(bytes, material []byte) {
  104. material[0] = bytes[0]
  105. material[1] = (byte)(bytes[0]<<7 | (bytes[1]&0xff)>>1)
  106. material[2] = (byte)(bytes[1]<<6 | (bytes[2]&0xff)>>2)
  107. material[3] = (byte)(bytes[2]<<5 | (bytes[3]&0xff)>>3)
  108. material[4] = (byte)(bytes[3]<<4 | (bytes[4]&0xff)>>4)
  109. material[5] = (byte)(bytes[4]<<3 | (bytes[5]&0xff)>>5)
  110. material[6] = (byte)(bytes[5]<<2 | (bytes[6]&0xff)>>6)
  111. material[7] = (byte)(bytes[6] << 1)
  112. }
  113. func oddParity(bytes []byte) {
  114. for i := 0; i < len(bytes); i++ {
  115. b := bytes[i]
  116. needsParity := (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ (b >> 1)) & 0x01) == 0
  117. if needsParity {
  118. bytes[i] = bytes[i] | byte(0x01)
  119. } else {
  120. bytes[i] = bytes[i] & byte(0xfe)
  121. }
  122. }
  123. }
  124. func encryptDes(key []byte, cleartext []byte, ciphertext []byte) {
  125. var desKey [8]byte
  126. createDesKey(key, desKey[:])
  127. cipher, err := des.NewCipher(desKey[:])
  128. if err != nil {
  129. panic(err)
  130. }
  131. cipher.Encrypt(ciphertext, cleartext)
  132. }
  133. func response(challenge [8]byte, hash [21]byte) (ret [24]byte) {
  134. encryptDes(hash[:7], challenge[:], ret[:8])
  135. encryptDes(hash[7:14], challenge[:], ret[8:16])
  136. encryptDes(hash[14:], challenge[:], ret[16:])
  137. return
  138. }
  139. func lmHash(password string) (hash [21]byte) {
  140. var lmpass [14]byte
  141. copy(lmpass[:14], []byte(strings.ToUpper(password)))
  142. magic := []byte("KGS!@#$%")
  143. encryptDes(lmpass[:7], magic, hash[:8])
  144. encryptDes(lmpass[7:], magic, hash[8:])
  145. return
  146. }
  147. func lmResponse(challenge [8]byte, password string) [24]byte {
  148. hash := lmHash(password)
  149. return response(challenge, hash)
  150. }
  151. func ntlmHash(password string) (hash [21]byte) {
  152. h := md4.New()
  153. h.Write(utf16le(password))
  154. h.Sum(hash[:0])
  155. return
  156. }
  157. func ntResponse(challenge [8]byte, password string) [24]byte {
  158. hash := ntlmHash(password)
  159. return response(challenge, hash)
  160. }
  161. func clientChallenge() (nonce [8]byte) {
  162. _, err := rand.Read(nonce[:])
  163. if err != nil {
  164. panic(err)
  165. }
  166. return
  167. }
  168. func ntlmSessionResponse(clientNonce [8]byte, serverChallenge [8]byte, password string) [24]byte {
  169. var sessionHash [16]byte
  170. h := md5.New()
  171. h.Write(serverChallenge[:])
  172. h.Write(clientNonce[:])
  173. h.Sum(sessionHash[:0])
  174. var hash [8]byte
  175. copy(hash[:], sessionHash[:8])
  176. passwordHash := ntlmHash(password)
  177. return response(hash, passwordHash)
  178. }
  179. func (auth *ntlmAuth) NextBytes(bytes []byte) ([]byte, error) {
  180. if string(bytes[0:8]) != "NTLMSSP\x00" {
  181. return nil, errorNTLM
  182. }
  183. if binary.LittleEndian.Uint32(bytes[8:12]) != _CHALLENGE_MESSAGE {
  184. return nil, errorNTLM
  185. }
  186. flags := binary.LittleEndian.Uint32(bytes[20:24])
  187. var challenge [8]byte
  188. copy(challenge[:], bytes[24:32])
  189. var lm, nt []byte
  190. if (flags & _NEGOTIATE_EXTENDED_SESSIONSECURITY) != 0 {
  191. nonce := clientChallenge()
  192. var lm_bytes [24]byte
  193. copy(lm_bytes[:8], nonce[:])
  194. lm = lm_bytes[:]
  195. nt_bytes := ntlmSessionResponse(nonce, challenge, auth.Password)
  196. nt = nt_bytes[:]
  197. } else {
  198. lm_bytes := lmResponse(challenge, auth.Password)
  199. lm = lm_bytes[:]
  200. nt_bytes := ntResponse(challenge, auth.Password)
  201. nt = nt_bytes[:]
  202. }
  203. lm_len := len(lm)
  204. nt_len := len(nt)
  205. domain16 := utf16le(auth.Domain)
  206. domain_len := len(domain16)
  207. user16 := utf16le(auth.UserName)
  208. user_len := len(user16)
  209. workstation16 := utf16le(auth.Workstation)
  210. workstation_len := len(workstation16)
  211. msg := make([]byte, 88+lm_len+nt_len+domain_len+user_len+workstation_len)
  212. copy(msg, []byte("NTLMSSP\x00"))
  213. binary.LittleEndian.PutUint32(msg[8:], _AUTHENTICATE_MESSAGE)
  214. // Lm Challenge Response Fields
  215. binary.LittleEndian.PutUint16(msg[12:], uint16(lm_len))
  216. binary.LittleEndian.PutUint16(msg[14:], uint16(lm_len))
  217. binary.LittleEndian.PutUint32(msg[16:], 88)
  218. // Nt Challenge Response Fields
  219. binary.LittleEndian.PutUint16(msg[20:], uint16(nt_len))
  220. binary.LittleEndian.PutUint16(msg[22:], uint16(nt_len))
  221. binary.LittleEndian.PutUint32(msg[24:], uint32(88+lm_len))
  222. // Domain Name Fields
  223. binary.LittleEndian.PutUint16(msg[28:], uint16(domain_len))
  224. binary.LittleEndian.PutUint16(msg[30:], uint16(domain_len))
  225. binary.LittleEndian.PutUint32(msg[32:], uint32(88+lm_len+nt_len))
  226. // User Name Fields
  227. binary.LittleEndian.PutUint16(msg[36:], uint16(user_len))
  228. binary.LittleEndian.PutUint16(msg[38:], uint16(user_len))
  229. binary.LittleEndian.PutUint32(msg[40:], uint32(88+lm_len+nt_len+domain_len))
  230. // Workstation Fields
  231. binary.LittleEndian.PutUint16(msg[44:], uint16(workstation_len))
  232. binary.LittleEndian.PutUint16(msg[46:], uint16(workstation_len))
  233. binary.LittleEndian.PutUint32(msg[48:], uint32(88+lm_len+nt_len+domain_len+user_len))
  234. // Encrypted Random Session Key Fields
  235. binary.LittleEndian.PutUint16(msg[52:], 0)
  236. binary.LittleEndian.PutUint16(msg[54:], 0)
  237. binary.LittleEndian.PutUint32(msg[56:], uint32(88+lm_len+nt_len+domain_len+user_len+workstation_len))
  238. // Negotiate Flags
  239. binary.LittleEndian.PutUint32(msg[60:], flags)
  240. // Version
  241. binary.LittleEndian.PutUint32(msg[64:], 0)
  242. binary.LittleEndian.PutUint32(msg[68:], 0)
  243. // MIC
  244. binary.LittleEndian.PutUint32(msg[72:], 0)
  245. binary.LittleEndian.PutUint32(msg[76:], 0)
  246. binary.LittleEndian.PutUint32(msg[88:], 0)
  247. binary.LittleEndian.PutUint32(msg[84:], 0)
  248. // Payload
  249. copy(msg[88:], lm)
  250. copy(msg[88+lm_len:], nt)
  251. copy(msg[88+lm_len+nt_len:], domain16)
  252. copy(msg[88+lm_len+nt_len+domain_len:], user16)
  253. copy(msg[88+lm_len+nt_len+domain_len+user_len:], workstation16)
  254. return msg, nil
  255. }
  256. func (auth *ntlmAuth) Free() {
  257. }