1 /* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 // First include (the generated) my_config.h, to get correct platform defines.
29 #include "my_config.h"
30
31 #if defined(HAVE_OPENSSL)
32 #include "crypt_genhash_impl.h"
33 #include "mysql/client_authentication.h"
34 #include "m_ctype.h"
35 #include "sql_common.h"
36 #include "errmsg.h"
37 #include "sql_string.h"
38
39 #include <string.h>
40 #include <stdarg.h>
41 #include <openssl/rsa.h>
42 #include <openssl/pem.h>
43 #include <openssl/err.h>
44 #if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C)
45 #include <openssl/applink.c>
46 #endif
47 #include "mysql/service_my_plugin_log.h"
48
49 #define MAX_CIPHER_LENGTH 1024
50
51 mysql_mutex_t g_public_key_mutex;
52
sha256_password_init(char * a,size_t b,int c,va_list d)53 int sha256_password_init(char *a, size_t b, int c, va_list d)
54 {
55 mysql_mutex_init(0,&g_public_key_mutex, MY_MUTEX_INIT_SLOW);
56 return 0;
57 }
58
sha256_password_deinit(void)59 int sha256_password_deinit(void)
60 {
61 mysql_mutex_destroy(&g_public_key_mutex);
62 return 0;
63 }
64
65
66 /**
67 Reads and parse RSA public key data from a file.
68
69 @param mysql connection handle with file path data
70
71 @return Pointer to the RSA public key storage buffer
72 */
73
rsa_init(MYSQL * mysql)74 RSA *rsa_init(MYSQL *mysql)
75 {
76 static RSA *g_public_key= NULL;
77 RSA *key= NULL;
78
79 mysql_mutex_lock(&g_public_key_mutex);
80 key= g_public_key;
81 mysql_mutex_unlock(&g_public_key_mutex);
82
83 if (key != NULL)
84 return key;
85
86 FILE *pub_key_file= NULL;
87
88 if (mysql->options.extension != NULL &&
89 mysql->options.extension->server_public_key_path != NULL &&
90 mysql->options.extension->server_public_key_path[0] != '\0')
91 {
92 pub_key_file= fopen(mysql->options.extension->server_public_key_path,
93 "r");
94 }
95 /* No public key is used; return 0 without errors to indicate this. */
96 else
97 return 0;
98
99 if (pub_key_file == NULL)
100 {
101 /*
102 If a key path was submitted but no key located then we print an error
103 message. Else we just report that there is no public key.
104 */
105 fprintf(stderr,"Can't locate server public key '%s'\n",
106 mysql->options.extension->server_public_key_path);
107
108 return 0;
109 }
110
111 mysql_mutex_lock(&g_public_key_mutex);
112 key= g_public_key= PEM_read_RSA_PUBKEY(pub_key_file, 0, 0, 0);
113 mysql_mutex_unlock(&g_public_key_mutex);
114 fclose(pub_key_file);
115 if (g_public_key == NULL)
116 {
117 ERR_clear_error();
118 fprintf(stderr, "Public key is not in PEM format: '%s'\n",
119 mysql->options.extension->server_public_key_path);
120 return 0;
121 }
122
123 return key;
124 }
125
126 /**
127 Authenticate the client using the RSA or TLS and a SHA256 salted password.
128
129 @param vio Provides plugin access to communication channel
130 @param mysql Client connection handler
131
132 @return Error status
133 @retval CR_ERROR An error occurred.
134 @retval CR_OK Authentication succeeded.
135 */
136
137 extern "C"
sha256_password_auth_client(MYSQL_PLUGIN_VIO * vio,MYSQL * mysql)138 int sha256_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
139 {
140 bool uses_password= mysql->passwd[0] != 0;
141 unsigned char encrypted_password[MAX_CIPHER_LENGTH];
142 static char request_public_key= '\1';
143 RSA *public_key= NULL;
144 bool got_public_key_from_server= false;
145 bool connection_is_secure= false;
146 unsigned char scramble_pkt[20];
147 unsigned char *pkt;
148
149
150 DBUG_ENTER("sha256_password_auth_client");
151
152 /*
153 Get the scramble from the server because we need it when sending encrypted
154 password.
155 */
156 if (vio->read_packet(vio, &pkt) != SCRAMBLE_LENGTH + 1)
157 {
158 DBUG_PRINT("info",("Scramble is not of correct length."));
159 DBUG_RETURN(CR_ERROR);
160 }
161 if (pkt[SCRAMBLE_LENGTH] != '\0')
162 {
163 DBUG_PRINT("info",("Missing protocol token in scramble data."));
164 DBUG_RETURN(CR_ERROR);
165 }
166 /*
167 Copy the scramble to the stack or it will be lost on the next use of the
168 net buffer.
169 */
170 memcpy(scramble_pkt, pkt, SCRAMBLE_LENGTH);
171
172 if (mysql_get_ssl_cipher(mysql) != NULL)
173 connection_is_secure= true;
174
175 /* If connection isn't secure attempt to get the RSA public key file */
176 if (!connection_is_secure)
177 public_key= rsa_init(mysql);
178
179 if (!uses_password)
180 {
181 /* We're not using a password */
182 static const unsigned char zero_byte= '\0';
183 if (vio->write_packet(vio, (const unsigned char *) &zero_byte, 1))
184 DBUG_RETURN(CR_ERROR);
185 }
186 else
187 {
188 /* Password is a 0-terminated byte array ('\0' character included) */
189 unsigned int passwd_len= strlen(mysql->passwd) + 1;
190 if (!connection_is_secure)
191 {
192 /*
193 If no public key; request one from the server.
194 */
195 if (public_key == NULL)
196 {
197 if (vio->write_packet(vio, (const unsigned char *) &request_public_key,
198 1))
199 DBUG_RETURN(CR_ERROR);
200
201 int pkt_len= 0;
202 unsigned char *pkt;
203 if ((pkt_len= vio->read_packet(vio, &pkt)) == -1)
204 DBUG_RETURN(CR_ERROR);
205 BIO* bio= BIO_new_mem_buf(pkt, pkt_len);
206 public_key= PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
207 BIO_free(bio);
208 if (public_key == 0)
209 {
210 ERR_clear_error();
211 DBUG_RETURN(CR_ERROR);
212 }
213 got_public_key_from_server= true;
214 }
215
216 /* Obfuscate the plain text password with the session scramble */
217 xor_string(mysql->passwd, strlen(mysql->passwd), (char *) scramble_pkt,
218 SCRAMBLE_LENGTH);
219 /* Encrypt the password and send it to the server */
220 int cipher_length= RSA_size(public_key);
221 /*
222 When using RSA_PKCS1_OAEP_PADDING the password length must be less
223 than RSA_size(rsa) - 41.
224 */
225 if (passwd_len + 41 >= (unsigned) cipher_length)
226 {
227 /* password message is to long */
228 DBUG_RETURN(CR_ERROR);
229 }
230 RSA_public_encrypt(passwd_len, (unsigned char *) mysql->passwd,
231 encrypted_password,
232 public_key, RSA_PKCS1_OAEP_PADDING);
233 if (got_public_key_from_server)
234 RSA_free(public_key);
235
236 if (vio->write_packet(vio, (uchar*) encrypted_password, cipher_length))
237 DBUG_RETURN(CR_ERROR);
238 }
239 else
240 {
241 /* The vio is encrypted already; just send the plain text passwd */
242 if (vio->write_packet(vio, (uchar*) mysql->passwd, passwd_len))
243 DBUG_RETURN(CR_ERROR);
244 }
245
246 memset(mysql->passwd, 0, passwd_len);
247 }
248
249 DBUG_RETURN(CR_OK);
250 }
251
252 #endif
253