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

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