選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

auth.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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. 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. scramble = scramble[:8]
  121. hashPw := pwHash([]byte(password))
  122. hashSc := pwHash(scramble)
  123. r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
  124. var out [8]byte
  125. for i := range out {
  126. out[i] = r.NextByte() + 64
  127. }
  128. mask := r.NextByte()
  129. for i := range out {
  130. out[i] ^= mask
  131. }
  132. return out[:]
  133. }
  134. // Hash password using 4.1+ method (SHA1)
  135. func scramblePassword(scramble []byte, password string) []byte {
  136. if len(password) == 0 {
  137. return nil
  138. }
  139. // stage1Hash = SHA1(password)
  140. crypt := sha1.New()
  141. crypt.Write([]byte(password))
  142. stage1 := crypt.Sum(nil)
  143. // scrambleHash = SHA1(scramble + SHA1(stage1Hash))
  144. // inner Hash
  145. crypt.Reset()
  146. crypt.Write(stage1)
  147. hash := crypt.Sum(nil)
  148. // outer Hash
  149. crypt.Reset()
  150. crypt.Write(scramble)
  151. crypt.Write(hash)
  152. scramble = crypt.Sum(nil)
  153. // token = scrambleHash XOR stage1Hash
  154. for i := range scramble {
  155. scramble[i] ^= stage1[i]
  156. }
  157. return scramble
  158. }
  159. // Hash password using MySQL 8+ method (SHA256)
  160. func scrambleSHA256Password(scramble []byte, password string) []byte {
  161. if len(password) == 0 {
  162. return nil
  163. }
  164. // XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))
  165. crypt := sha256.New()
  166. crypt.Write([]byte(password))
  167. message1 := crypt.Sum(nil)
  168. crypt.Reset()
  169. crypt.Write(message1)
  170. message1Hash := crypt.Sum(nil)
  171. crypt.Reset()
  172. crypt.Write(message1Hash)
  173. crypt.Write(scramble)
  174. message2 := crypt.Sum(nil)
  175. for i := range message1 {
  176. message1[i] ^= message2[i]
  177. }
  178. return message1
  179. }
  180. func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte, error) {
  181. plain := make([]byte, len(password)+1)
  182. copy(plain, password)
  183. for i := range plain {
  184. j := i % len(seed)
  185. plain[i] ^= seed[j]
  186. }
  187. sha1 := sha1.New()
  188. return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil)
  189. }
  190. func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error {
  191. enc, err := encryptPassword(mc.cfg.Passwd, seed, pub)
  192. if err != nil {
  193. return err
  194. }
  195. return mc.writeAuthSwitchPacket(enc)
  196. }
  197. func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
  198. switch plugin {
  199. case "caching_sha2_password":
  200. authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
  201. return authResp, nil
  202. case "mysql_old_password":
  203. if !mc.cfg.AllowOldPasswords {
  204. return nil, ErrOldPassword
  205. }
  206. if len(mc.cfg.Passwd) == 0 {
  207. return nil, nil
  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. // unlike caching_sha2_password, sha256_password does not accept
  234. // cleartext password on unix transport.
  235. if mc.cfg.TLS != nil {
  236. // write cleartext auth packet
  237. return append([]byte(mc.cfg.Passwd), 0), nil
  238. }
  239. pubKey := mc.cfg.pubKey
  240. if pubKey == nil {
  241. // request public key from server
  242. return []byte{1}, nil
  243. }
  244. // encrypted password
  245. enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
  246. return enc, err
  247. default:
  248. errLog.Print("unknown auth plugin:", plugin)
  249. return nil, ErrUnknownPlugin
  250. }
  251. }
  252. func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
  253. // Read Result Packet
  254. authData, newPlugin, err := mc.readAuthResult()
  255. if err != nil {
  256. return err
  257. }
  258. // handle auth plugin switch, if requested
  259. if newPlugin != "" {
  260. // If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
  261. // sent and we have to keep using the cipher sent in the init packet.
  262. if authData == nil {
  263. authData = oldAuthData
  264. } else {
  265. // copy data from read buffer to owned slice
  266. copy(oldAuthData, authData)
  267. }
  268. plugin = newPlugin
  269. authResp, err := mc.auth(authData, plugin)
  270. if err != nil {
  271. return err
  272. }
  273. if err = mc.writeAuthSwitchPacket(authResp); err != nil {
  274. return err
  275. }
  276. // Read Result Packet
  277. authData, newPlugin, err = mc.readAuthResult()
  278. if err != nil {
  279. return err
  280. }
  281. // Do not allow to change the auth plugin more than once
  282. if newPlugin != "" {
  283. return ErrMalformPkt
  284. }
  285. }
  286. switch plugin {
  287. // https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
  288. case "caching_sha2_password":
  289. switch len(authData) {
  290. case 0:
  291. return nil // auth successful
  292. case 1:
  293. switch authData[0] {
  294. case cachingSha2PasswordFastAuthSuccess:
  295. if err = mc.readResultOK(); err == nil {
  296. return nil // auth successful
  297. }
  298. case cachingSha2PasswordPerformFullAuthentication:
  299. if mc.cfg.TLS != nil || mc.cfg.Net == "unix" {
  300. // write cleartext auth packet
  301. err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
  302. if err != nil {
  303. return err
  304. }
  305. } else {
  306. pubKey := mc.cfg.pubKey
  307. if pubKey == nil {
  308. // request public key from server
  309. data, err := mc.buf.takeSmallBuffer(4 + 1)
  310. if err != nil {
  311. return err
  312. }
  313. data[4] = cachingSha2PasswordRequestPublicKey
  314. err = mc.writePacket(data)
  315. if err != nil {
  316. return err
  317. }
  318. if data, err = mc.readPacket(); err != nil {
  319. return err
  320. }
  321. if data[0] != iAuthMoreData {
  322. return fmt.Errorf("unexpect resp from server for caching_sha2_password perform full authentication")
  323. }
  324. // parse public key
  325. block, rest := pem.Decode(data[1:])
  326. if block == nil {
  327. return fmt.Errorf("No Pem data found, data: %s", rest)
  328. }
  329. pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
  330. if err != nil {
  331. return err
  332. }
  333. pubKey = pkix.(*rsa.PublicKey)
  334. }
  335. // send encrypted password
  336. err = mc.sendEncryptedPassword(oldAuthData, pubKey)
  337. if err != nil {
  338. return err
  339. }
  340. }
  341. return mc.readResultOK()
  342. default:
  343. return ErrMalformPkt
  344. }
  345. default:
  346. return ErrMalformPkt
  347. }
  348. case "sha256_password":
  349. switch len(authData) {
  350. case 0:
  351. return nil // auth successful
  352. default:
  353. block, _ := pem.Decode(authData)
  354. if block == nil {
  355. return fmt.Errorf("no Pem data found, data: %s", authData)
  356. }
  357. pub, err := x509.ParsePKIXPublicKey(block.Bytes)
  358. if err != nil {
  359. return err
  360. }
  361. // send encrypted password
  362. err = mc.sendEncryptedPassword(oldAuthData, pub.(*rsa.PublicKey))
  363. if err != nil {
  364. return err
  365. }
  366. return mc.readResultOK()
  367. }
  368. default:
  369. return nil // auth successful
  370. }
  371. return err
  372. }