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.

auth.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
  2. //
  3. // Copyright 2018 The Go-MySQL-Driver Authors. All rights reserved.
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public
  6. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  7. // You can obtain one at http://mozilla.org/MPL/2.0/.
  8. package mysql
  9. import (
  10. "crypto/rand"
  11. "crypto/rsa"
  12. "crypto/sha1"
  13. "crypto/sha256"
  14. "crypto/x509"
  15. "encoding/pem"
  16. "sync"
  17. )
  18. // server pub keys registry
  19. var (
  20. serverPubKeyLock sync.RWMutex
  21. serverPubKeyRegistry map[string]*rsa.PublicKey
  22. )
  23. // RegisterServerPubKey registers a server RSA public key which can be used to
  24. // send data in a secure manner to the server without receiving the public key
  25. // in a potentially insecure way from the server first.
  26. // Registered keys can afterwards be used adding serverPubKey=<name> to the DSN.
  27. //
  28. // Note: The provided rsa.PublicKey instance is exclusively owned by the driver
  29. // after registering it and may not be modified.
  30. //
  31. // data, err := ioutil.ReadFile("mykey.pem")
  32. // if err != nil {
  33. // log.Fatal(err)
  34. // }
  35. //
  36. // block, _ := pem.Decode(data)
  37. // if block == nil || block.Type != "PUBLIC KEY" {
  38. // log.Fatal("failed to decode PEM block containing public key")
  39. // }
  40. //
  41. // pub, err := x509.ParsePKIXPublicKey(block.Bytes)
  42. // if err != nil {
  43. // log.Fatal(err)
  44. // }
  45. //
  46. // if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
  47. // mysql.RegisterServerPubKey("mykey", rsaPubKey)
  48. // } else {
  49. // log.Fatal("not a RSA public key")
  50. // }
  51. //
  52. func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) {
  53. serverPubKeyLock.Lock()
  54. if serverPubKeyRegistry == nil {
  55. serverPubKeyRegistry = make(map[string]*rsa.PublicKey)
  56. }
  57. serverPubKeyRegistry[name] = pubKey
  58. serverPubKeyLock.Unlock()
  59. }
  60. // DeregisterServerPubKey removes the public key registered with the given name.
  61. func DeregisterServerPubKey(name string) {
  62. serverPubKeyLock.Lock()
  63. if serverPubKeyRegistry != nil {
  64. delete(serverPubKeyRegistry, name)
  65. }
  66. serverPubKeyLock.Unlock()
  67. }
  68. func getServerPubKey(name string) (pubKey *rsa.PublicKey) {
  69. serverPubKeyLock.RLock()
  70. if v, ok := serverPubKeyRegistry[name]; ok {
  71. pubKey = v
  72. }
  73. serverPubKeyLock.RUnlock()
  74. return
  75. }
  76. // Hash password using pre 4.1 (old password) method
  77. // https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
  78. type myRnd struct {
  79. seed1, seed2 uint32
  80. }
  81. const myRndMaxVal = 0x3FFFFFFF
  82. // Pseudo random number generator
  83. func newMyRnd(seed1, seed2 uint32) *myRnd {
  84. return &myRnd{
  85. seed1: seed1 % myRndMaxVal,
  86. seed2: seed2 % myRndMaxVal,
  87. }
  88. }
  89. // Tested to be equivalent to MariaDB's floating point variant
  90. // http://play.golang.org/p/QHvhd4qved
  91. // http://play.golang.org/p/RG0q4ElWDx
  92. func (r *myRnd) NextByte() byte {
  93. r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal
  94. r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal
  95. return byte(uint64(r.seed1) * 31 / myRndMaxVal)
  96. }
  97. // Generate binary hash from byte string using insecure pre 4.1 method
  98. func pwHash(password []byte) (result [2]uint32) {
  99. var add uint32 = 7
  100. var tmp uint32
  101. result[0] = 1345345333
  102. result[1] = 0x12345671
  103. for _, c := range password {
  104. // skip spaces and tabs in password
  105. if c == ' ' || c == '\t' {
  106. continue
  107. }
  108. tmp = uint32(c)
  109. result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8)
  110. result[1] += (result[1] << 8) ^ result[0]
  111. add += tmp
  112. }
  113. // Remove sign bit (1<<31)-1)
  114. result[0] &= 0x7FFFFFFF
  115. result[1] &= 0x7FFFFFFF
  116. return
  117. }
  118. // Hash password using insecure pre 4.1 method
  119. func scrambleOldPassword(scramble []byte, password string) []byte {
  120. if len(password) == 0 {
  121. return nil
  122. }
  123. scramble = scramble[:8]
  124. hashPw := pwHash([]byte(password))
  125. hashSc := pwHash(scramble)
  126. r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
  127. var out [8]byte
  128. for i := range out {
  129. out[i] = r.NextByte() + 64
  130. }
  131. mask := r.NextByte()
  132. for i := range out {
  133. out[i] ^= mask
  134. }
  135. return out[:]
  136. }
  137. // Hash password using 4.1+ method (SHA1)
  138. func scramblePassword(scramble []byte, password string) []byte {
  139. if len(password) == 0 {
  140. return nil
  141. }
  142. // stage1Hash = SHA1(password)
  143. crypt := sha1.New()
  144. crypt.Write([]byte(password))
  145. stage1 := crypt.Sum(nil)
  146. // scrambleHash = SHA1(scramble + SHA1(stage1Hash))
  147. // inner Hash
  148. crypt.Reset()
  149. crypt.Write(stage1)
  150. hash := crypt.Sum(nil)
  151. // outer Hash
  152. crypt.Reset()
  153. crypt.Write(scramble)
  154. crypt.Write(hash)
  155. scramble = crypt.Sum(nil)
  156. // token = scrambleHash XOR stage1Hash
  157. for i := range scramble {
  158. scramble[i] ^= stage1[i]
  159. }
  160. return scramble
  161. }
  162. // Hash password using MySQL 8+ method (SHA256)
  163. func scrambleSHA256Password(scramble []byte, password string) []byte {
  164. if len(password) == 0 {
  165. return nil
  166. }
  167. // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))
  168. crypt := sha256.New()
  169. crypt.Write([]byte(password))
  170. message1 := crypt.Sum(nil)
  171. crypt.Reset()
  172. crypt.Write(message1)
  173. message1Hash := crypt.Sum(nil)
  174. crypt.Reset()
  175. crypt.Write(message1Hash)
  176. crypt.Write(scramble)
  177. message2 := crypt.Sum(nil)
  178. for i := range message1 {
  179. message1[i] ^= message2[i]
  180. }
  181. return message1
  182. }
  183. func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte, error) {
  184. plain := make([]byte, len(password)+1)
  185. copy(plain, password)
  186. for i := range plain {
  187. j := i % len(seed)
  188. plain[i] ^= seed[j]
  189. }
  190. sha1 := sha1.New()
  191. return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil)
  192. }
  193. func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error {
  194. enc, err := encryptPassword(mc.cfg.Passwd, seed, pub)
  195. if err != nil {
  196. return err
  197. }
  198. return mc.writeAuthSwitchPacket(enc)
  199. }
  200. func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
  201. switch plugin {
  202. case "caching_sha2_password":
  203. authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
  204. return authResp, nil
  205. case "mysql_old_password":
  206. if !mc.cfg.AllowOldPasswords {
  207. return nil, ErrOldPassword
  208. }
  209. // Note: there are edge cases where this should work but doesn't;
  210. // this is currently "wontfix":
  211. // https://github.com/go-sql-driver/mysql/issues/184
  212. authResp := append(scrambleOldPassword(authData[:8], mc.cfg.Passwd), 0)
  213. return authResp, nil
  214. case "mysql_clear_password":
  215. if !mc.cfg.AllowCleartextPasswords {
  216. return nil, ErrCleartextPassword
  217. }
  218. // http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
  219. // http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
  220. return append([]byte(mc.cfg.Passwd), 0), nil
  221. case "mysql_native_password":
  222. if !mc.cfg.AllowNativePasswords {
  223. return nil, ErrNativePassword
  224. }
  225. // https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
  226. // Native password authentication only need and will need 20-byte challenge.
  227. authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
  228. return authResp, nil
  229. case "sha256_password":
  230. if len(mc.cfg.Passwd) == 0 {
  231. return []byte{0}, nil
  232. }
  233. if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
  234. // write cleartext auth packet
  235. return append([]byte(mc.cfg.Passwd), 0), nil
  236. }
  237. pubKey := mc.cfg.pubKey
  238. if pubKey == nil {
  239. // request public key from server
  240. return []byte{1}, nil
  241. }
  242. // encrypted password
  243. enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
  244. return enc, err
  245. default:
  246. errLog.Print("unknown auth plugin:", plugin)
  247. return nil, ErrUnknownPlugin
  248. }
  249. }
  250. func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
  251. // Read Result Packet
  252. authData, newPlugin, err := mc.readAuthResult()
  253. if err != nil {
  254. return err
  255. }
  256. // handle auth plugin switch, if requested
  257. if newPlugin != "" {
  258. // If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
  259. // sent and we have to keep using the cipher sent in the init packet.
  260. if authData == nil {
  261. authData = oldAuthData
  262. } else {
  263. // copy data from read buffer to owned slice
  264. copy(oldAuthData, authData)
  265. }
  266. plugin = newPlugin
  267. authResp, err := mc.auth(authData, plugin)
  268. if err != nil {
  269. return err
  270. }
  271. if err = mc.writeAuthSwitchPacket(authResp); err != nil {
  272. return err
  273. }
  274. // Read Result Packet
  275. authData, newPlugin, err = mc.readAuthResult()
  276. if err != nil {
  277. return err
  278. }
  279. // Do not allow to change the auth plugin more than once
  280. if newPlugin != "" {
  281. return ErrMalformPkt
  282. }
  283. }
  284. switch plugin {
  285. // https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
  286. case "caching_sha2_password":
  287. switch len(authData) {
  288. case 0:
  289. return nil // auth successful
  290. case 1:
  291. switch authData[0] {
  292. case cachingSha2PasswordFastAuthSuccess:
  293. if err = mc.readResultOK(); err == nil {
  294. return nil // auth successful
  295. }
  296. case cachingSha2PasswordPerformFullAuthentication:
  297. if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
  298. // write cleartext auth packet
  299. err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
  300. if err != nil {
  301. return err
  302. }
  303. } else {
  304. pubKey := mc.cfg.pubKey
  305. if pubKey == nil {
  306. // request public key from server
  307. data, err := mc.buf.takeSmallBuffer(4 + 1)
  308. if err != nil {
  309. return err
  310. }
  311. data[4] = cachingSha2PasswordRequestPublicKey
  312. mc.writePacket(data)
  313. // parse public key
  314. if data, err = mc.readPacket(); err != nil {
  315. return err
  316. }
  317. block, _ := pem.Decode(data[1:])
  318. pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
  319. if err != nil {
  320. return err
  321. }
  322. pubKey = pkix.(*rsa.PublicKey)
  323. }
  324. // send encrypted password
  325. err = mc.sendEncryptedPassword(oldAuthData, pubKey)
  326. if err != nil {
  327. return err
  328. }
  329. }
  330. return mc.readResultOK()
  331. default:
  332. return ErrMalformPkt
  333. }
  334. default:
  335. return ErrMalformPkt
  336. }
  337. case "sha256_password":
  338. switch len(authData) {
  339. case 0:
  340. return nil // auth successful
  341. default:
  342. block, _ := pem.Decode(authData)
  343. pub, err := x509.ParsePKIXPublicKey(block.Bytes)
  344. if err != nil {
  345. return err
  346. }
  347. // send encrypted password
  348. err = mc.sendEncryptedPassword(oldAuthData, pub.(*rsa.PublicKey))
  349. if err != nil {
  350. return err
  351. }
  352. return mc.readResultOK()
  353. }
  354. default:
  355. return nil // auth successful
  356. }
  357. return err
  358. }