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  }