本站源代码
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.

421 lines
10.0KB

  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 := mc.buf.takeSmallBuffer(4 + 1)
  308. data[4] = cachingSha2PasswordRequestPublicKey
  309. mc.writePacket(data)
  310. // parse public key
  311. data, err := mc.readPacket()
  312. if err != nil {
  313. return err
  314. }
  315. block, _ := pem.Decode(data[1:])
  316. pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
  317. if err != nil {
  318. return err
  319. }
  320. pubKey = pkix.(*rsa.PublicKey)
  321. }
  322. // send encrypted password
  323. err = mc.sendEncryptedPassword(oldAuthData, pubKey)
  324. if err != nil {
  325. return err
  326. }
  327. }
  328. return mc.readResultOK()
  329. default:
  330. return ErrMalformPkt
  331. }
  332. default:
  333. return ErrMalformPkt
  334. }
  335. case "sha256_password":
  336. switch len(authData) {
  337. case 0:
  338. return nil // auth successful
  339. default:
  340. block, _ := pem.Decode(authData)
  341. pub, err := x509.ParsePKIXPublicKey(block.Bytes)
  342. if err != nil {
  343. return err
  344. }
  345. // send encrypted password
  346. err = mc.sendEncryptedPassword(oldAuthData, pub.(*rsa.PublicKey))
  347. if err != nil {
  348. return err
  349. }
  350. return mc.readResultOK()
  351. }
  352. default:
  353. return nil // auth successful
  354. }
  355. return err
  356. }
上海开阖软件有限公司 沪ICP备12045867号-1