auth.go 12 KB

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