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