@@ -15,8 +15,72 @@ import (
15
15
"crypto/sha256"
16
16
"crypto/x509"
17
17
"encoding/pem"
18
+ "sync"
18
19
)
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
+
20
84
// Hash password using pre 4.1 (old password) method
21
85
// https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
22
86
type myRnd struct {
@@ -154,19 +218,22 @@ func scrambleSHA256Password(scramble []byte, password string) []byte {
154
218
return message1
155
219
}
156
220
157
- func ( mc * mysqlConn ) sendEncryptedPassword ( seed []byte , pub * rsa.PublicKey ) error {
158
- plain := make ([]byte , len (mc . cfg . Passwd )+ 1 )
159
- copy (plain , mc . cfg . Passwd )
221
+ func encryptPassword ( password string , seed []byte , pub * rsa.PublicKey ) ([] byte , error ) {
222
+ plain := make ([]byte , len (password )+ 1 )
223
+ copy (plain , password )
160
224
for i := range plain {
161
225
j := i % len (seed )
162
226
plain [i ] ^= seed [j ]
163
227
}
164
228
sha1 := sha1 .New ()
165
- enc , err := rsa .EncryptOAEP (sha1 , rand .Reader , pub , plain , nil )
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 )
166
234
if err != nil {
167
235
return err
168
236
}
169
-
170
237
return mc .writeAuthSwitchPacket (enc , false )
171
238
}
172
239
@@ -211,9 +278,16 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error)
211
278
// write cleartext auth packet
212
279
return []byte (mc .cfg .Passwd ), true , nil
213
280
}
214
- // request public key
215
- // TODO: allow to specify a local file with the pub key via the DSN
216
- return []byte {1 }, false , nil
281
+
282
+ pubKey := mc .cfg .pubKey
283
+ if pubKey == nil {
284
+ // request public key from server
285
+ return []byte {1 }, false , nil
286
+ }
287
+
288
+ // encrypted password
289
+ enc , err := encryptPassword (mc .cfg .Passwd , authData , pubKey )
290
+ return enc , false , err
217
291
218
292
default :
219
293
errLog .Print ("unknown auth plugin:" , plugin )
@@ -283,28 +357,29 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
283
357
return err
284
358
}
285
359
} else {
286
- // TODO: allow to specify a local file with the pub key via
287
- // the DSN
288
-
289
- // request public key
290
- data := mc .buf .takeSmallBuffer (4 + 1 )
291
- data [4 ] = cachingSha2PasswordRequestPublicKey
292
- mc .writePacket (data )
293
-
294
- // parse public key
295
- data , err := mc .readPacket ()
296
- if err != nil {
297
- return err
298
- }
299
-
300
- block , _ := pem .Decode (data [1 :])
301
- pub , err := x509 .ParsePKIXPublicKey (block .Bytes )
302
- if err != nil {
303
- return err
360
+ pubKey := mc .cfg .pubKey
361
+ if pubKey == nil {
362
+ // request public key from server
363
+ data := mc .buf .takeSmallBuffer (4 + 1 )
364
+ data [4 ] = cachingSha2PasswordRequestPublicKey
365
+ mc .writePacket (data )
366
+
367
+ // parse public key
368
+ data , err := mc .readPacket ()
369
+ if err != nil {
370
+ return err
371
+ }
372
+
373
+ block , _ := pem .Decode (data [1 :])
374
+ pkix , err := x509 .ParsePKIXPublicKey (block .Bytes )
375
+ if err != nil {
376
+ return err
377
+ }
378
+ pubKey = pkix .(* rsa.PublicKey )
304
379
}
305
380
306
381
// send encrypted password
307
- err = mc .sendEncryptedPassword (oldAuthData , pub .( * rsa. PublicKey ) )
382
+ err = mc .sendEncryptedPassword (oldAuthData , pubKey )
308
383
if err != nil {
309
384
return err
310
385
}
0 commit comments