1 | #include <ma_global.h> |
2 | #include <ma_sys.h> |
3 | #include <errmsg.h> |
4 | #include <string.h> |
5 | #include <ma_common.h> |
6 | #include <mysql/client_plugin.h> |
7 | |
8 | typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; |
9 | static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, size_t); |
10 | static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); |
11 | static int dummy_fallback_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql __attribute__((unused))); |
12 | extern void read_user_name(char *name); |
13 | extern char *ma_send_connect_attr(MYSQL *mysql, unsigned char *buffer); |
14 | extern int ma_read_ok_packet(MYSQL *mysql, uchar *pos, ulong length); |
15 | |
16 | typedef struct { |
17 | int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); |
18 | int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, size_t pkt_len); |
19 | void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); |
20 | /* -= end of MYSQL_PLUGIN_VIO =- */ |
21 | MYSQL *mysql; |
22 | auth_plugin_t *plugin; /**< what plugin we're under */ |
23 | const char *db; |
24 | struct { |
25 | uchar *pkt; /**< pointer into NET::buff */ |
26 | uint pkt_len; |
27 | } cached_server_reply; |
28 | uint packets_read, packets_written; /**< counters for send/received packets */ |
29 | my_bool mysql_change_user; /**< if it's mysql_change_user() */ |
30 | int last_read_packet_len; /**< the length of the last *read* packet */ |
31 | } MCPVIO_EXT; |
32 | /* |
33 | #define compile_time_assert(A) \ |
34 | do {\ |
35 | typedef char constraint[(A) ? 1 : -1];\ |
36 | } while (0); |
37 | */ |
38 | |
39 | auth_plugin_t mysql_native_password_client_plugin= |
40 | { |
41 | MYSQL_CLIENT_AUTHENTICATION_PLUGIN, |
42 | MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, |
43 | native_password_plugin_name, |
44 | "R.J.Silk, Sergei Golubchik" , |
45 | "Native MySQL authentication" , |
46 | {1, 0, 0}, |
47 | "LGPL" , |
48 | NULL, |
49 | NULL, |
50 | NULL, |
51 | NULL, |
52 | native_password_auth_client |
53 | }; |
54 | |
55 | |
56 | static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) |
57 | { |
58 | int pkt_len; |
59 | uchar *pkt; |
60 | |
61 | if (((MCPVIO_EXT *)vio)->mysql_change_user) |
62 | { |
63 | /* |
64 | in mysql_change_user() the client sends the first packet. |
65 | we use the old scramble. |
66 | */ |
67 | pkt= (uchar*)mysql->scramble_buff; |
68 | pkt_len= SCRAMBLE_LENGTH + 1; |
69 | } |
70 | else |
71 | { |
72 | /* read the scramble */ |
73 | if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) |
74 | return CR_ERROR; |
75 | |
76 | if (pkt_len != SCRAMBLE_LENGTH + 1) |
77 | return CR_SERVER_HANDSHAKE_ERR; |
78 | |
79 | /* save it in MYSQL */ |
80 | memmove(mysql->scramble_buff, pkt, SCRAMBLE_LENGTH); |
81 | mysql->scramble_buff[SCRAMBLE_LENGTH] = 0; |
82 | } |
83 | |
84 | if (mysql && mysql->passwd[0]) |
85 | { |
86 | char scrambled[SCRAMBLE_LENGTH + 1]; |
87 | ma_scramble_41((uchar *)scrambled, (char*)pkt, mysql->passwd); |
88 | if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH)) |
89 | return CR_ERROR; |
90 | } |
91 | else |
92 | if (vio->write_packet(vio, 0, 0)) /* no password */ |
93 | return CR_ERROR; |
94 | |
95 | return CR_OK; |
96 | } |
97 | |
98 | auth_plugin_t dummy_fallback_client_plugin= |
99 | { |
100 | MYSQL_CLIENT_AUTHENTICATION_PLUGIN, |
101 | MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, |
102 | "dummy_fallback_auth" , |
103 | "Sergei Golubchik" , |
104 | "Dummy fallback plugin" , |
105 | {1, 0, 0}, |
106 | "LGPL" , |
107 | NULL, |
108 | NULL, |
109 | NULL, |
110 | NULL, |
111 | dummy_fallback_auth_client |
112 | }; |
113 | |
114 | |
115 | static int dummy_fallback_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql __attribute__((unused))) |
116 | { |
117 | char last_error[MYSQL_ERRMSG_SIZE]; |
118 | unsigned int i, last_errno= ((MCPVIO_EXT *)vio)->mysql->net.last_errno; |
119 | if (last_errno) |
120 | strncpy(last_error, ((MCPVIO_EXT *)vio)->mysql->net.last_error, |
121 | sizeof(last_error)); |
122 | |
123 | /* safety-wise we only do 10 round-trips */ |
124 | for (i=0; i < 10; i++) |
125 | { |
126 | uchar *pkt; |
127 | if (vio->read_packet(vio, &pkt) < 0) |
128 | break; |
129 | if (vio->write_packet(vio, 0, 0)) |
130 | break; |
131 | } |
132 | if (last_errno) |
133 | strncpy(((MCPVIO_EXT *)vio)->mysql->net.last_error, last_error, |
134 | sizeof(((MCPVIO_EXT *)vio)->mysql->net.last_error)); |
135 | return CR_ERROR; |
136 | } |
137 | |
138 | static int send_change_user_packet(MCPVIO_EXT *mpvio, |
139 | const uchar *data, int data_len) |
140 | { |
141 | MYSQL *mysql= mpvio->mysql; |
142 | char *buff, *end; |
143 | int res= 1; |
144 | size_t conn_attr_len= (mysql->options.extension) ? |
145 | mysql->options.extension->connect_attrs_len : 0; |
146 | |
147 | buff= malloc(USERNAME_LENGTH+1 + data_len+1 + NAME_LEN+1 + 2 + NAME_LEN+1 + 9 + conn_attr_len); |
148 | |
149 | end= ma_strmake(buff, mysql->user, USERNAME_LENGTH) + 1; |
150 | |
151 | if (!data_len) |
152 | *end++= 0; |
153 | else |
154 | { |
155 | if (mysql->client_flag & CLIENT_SECURE_CONNECTION) |
156 | { |
157 | DBUG_ASSERT(data_len <= 255); |
158 | if (data_len > 255) |
159 | { |
160 | my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0); |
161 | goto error; |
162 | } |
163 | *end++= data_len; |
164 | } |
165 | else |
166 | { |
167 | DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); |
168 | DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0); |
169 | } |
170 | memcpy(end, data, data_len); |
171 | end+= data_len; |
172 | } |
173 | end= ma_strmake(end, mpvio->db ? mpvio->db : "" , NAME_LEN) + 1; |
174 | |
175 | if (mysql->server_capabilities & CLIENT_PROTOCOL_41) |
176 | { |
177 | int2store(end, (ushort) mysql->charset->nr); |
178 | end+= 2; |
179 | } |
180 | |
181 | if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) |
182 | end= ma_strmake(end, mpvio->plugin->name, NAME_LEN) + 1; |
183 | |
184 | end= ma_send_connect_attr(mysql, (unsigned char *)end); |
185 | |
186 | res= ma_simple_command(mysql, COM_CHANGE_USER, |
187 | buff, (ulong)(end-buff), 1, NULL); |
188 | |
189 | error: |
190 | free(buff); |
191 | return res; |
192 | } |
193 | |
194 | |
195 | |
196 | static int send_client_reply_packet(MCPVIO_EXT *mpvio, |
197 | const uchar *data, int data_len) |
198 | { |
199 | MYSQL *mysql= mpvio->mysql; |
200 | NET *net= &mysql->net; |
201 | char *buff, *end; |
202 | size_t conn_attr_len= (mysql->options.extension) ? |
203 | mysql->options.extension->connect_attrs_len : 0; |
204 | |
205 | /* see end= buff+32 below, fixed size of the packet is 32 bytes */ |
206 | buff= malloc(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN + conn_attr_len + 9); |
207 | end= buff; |
208 | |
209 | mysql->client_flag|= mysql->options.client_flag; |
210 | mysql->client_flag|= CLIENT_CAPABILITIES; |
211 | |
212 | if (mysql->client_flag & CLIENT_MULTI_STATEMENTS) |
213 | mysql->client_flag|= CLIENT_MULTI_RESULTS; |
214 | |
215 | #if defined(HAVE_TLS) && !defined(EMBEDDED_LIBRARY) |
216 | if (mysql->options.ssl_key || mysql->options.ssl_cert || |
217 | mysql->options.ssl_ca || mysql->options.ssl_capath || |
218 | mysql->options.ssl_cipher || mysql->options.use_ssl || |
219 | (mysql->options.client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) |
220 | mysql->options.use_ssl= 1; |
221 | if (mysql->options.use_ssl) |
222 | mysql->client_flag|= CLIENT_SSL; |
223 | #endif /* HAVE_TLS && !EMBEDDED_LIBRARY*/ |
224 | if (mpvio->db) |
225 | mysql->client_flag|= CLIENT_CONNECT_WITH_DB; |
226 | |
227 | /* if server doesn't support SSL and verification of server certificate |
228 | was set to mandatory, we need to return an error */ |
229 | if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL)) |
230 | { |
231 | if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) || |
232 | (mysql->options.extension && (mysql->options.extension->tls_fp || |
233 | mysql->options.extension->tls_fp_list))) |
234 | { |
235 | my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
236 | ER(CR_SSL_CONNECTION_ERROR), |
237 | "SSL is required, but the server does not support it" ); |
238 | goto error; |
239 | } |
240 | } |
241 | |
242 | |
243 | /* Remove options that server doesn't support */ |
244 | mysql->client_flag= mysql->client_flag & |
245 | (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41) |
246 | | mysql->server_capabilities); |
247 | |
248 | #ifndef HAVE_COMPRESS |
249 | mysql->client_flag&= ~CLIENT_COMPRESS; |
250 | #endif |
251 | |
252 | if (mysql->client_flag & CLIENT_PROTOCOL_41) |
253 | { |
254 | /* 4.1 server and 4.1 client has a 32 byte option flag */ |
255 | if (!(mysql->server_capabilities & CLIENT_MYSQL)) |
256 | mysql->client_flag&= ~CLIENT_MYSQL; |
257 | int4store(buff,mysql->client_flag); |
258 | int4store(buff+4, net->max_packet_size); |
259 | buff[8]= (char) mysql->charset->nr; |
260 | memset(buff + 9, 0, 32-9); |
261 | if (!(mysql->server_capabilities & CLIENT_MYSQL)) |
262 | { |
263 | mysql->extension->mariadb_client_flag = MARIADB_CLIENT_SUPPORTED_FLAGS >> 32; |
264 | int4store(buff + 28, mysql->extension->mariadb_client_flag); |
265 | } |
266 | end= buff+32; |
267 | } |
268 | else |
269 | { |
270 | int2store(buff, mysql->client_flag); |
271 | int3store(buff+2, net->max_packet_size); |
272 | end= buff+5; |
273 | } |
274 | #ifdef HAVE_TLS |
275 | if (mysql->options.ssl_key || |
276 | mysql->options.ssl_cert || |
277 | mysql->options.ssl_ca || |
278 | mysql->options.ssl_capath || |
279 | mysql->options.ssl_cipher |
280 | #ifdef CRL_IMPLEMENTED |
281 | || (mysql->options.extension && |
282 | (mysql->options.extension->ssl_crl || |
283 | mysql->options.extension->ssl_crlpath)) |
284 | #endif |
285 | ) |
286 | mysql->options.use_ssl= 1; |
287 | if (mysql->options.use_ssl && |
288 | (mysql->client_flag & CLIENT_SSL)) |
289 | { |
290 | /* |
291 | Send mysql->client_flag, max_packet_size - unencrypted otherwise |
292 | the server does not know we want to do SSL |
293 | */ |
294 | if (ma_net_write(net, (unsigned char *)buff, (size_t) (end-buff)) || ma_net_flush(net)) |
295 | { |
296 | my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, |
297 | ER(CR_SERVER_LOST_EXTENDED), |
298 | "sending connection information to server" , |
299 | errno); |
300 | goto error; |
301 | } |
302 | if (ma_pvio_start_ssl(mysql->net.pvio)) |
303 | goto error; |
304 | } |
305 | #endif /* HAVE_TLS */ |
306 | |
307 | /* This needs to be changed as it's not useful with big packets */ |
308 | if (mysql->user && mysql->user[0]) |
309 | ma_strmake(end, mysql->user, USERNAME_LENGTH); |
310 | else |
311 | read_user_name(end); |
312 | |
313 | /* We have to handle different version of handshake here */ |
314 | end+= strlen(end) + 1; |
315 | if (data_len) |
316 | { |
317 | if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) |
318 | { |
319 | *end++= data_len; |
320 | memcpy(end, data, data_len); |
321 | end+= data_len; |
322 | } |
323 | else |
324 | { |
325 | DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */ |
326 | memcpy(end, data, data_len); |
327 | end+= data_len; |
328 | } |
329 | } |
330 | else |
331 | *end++= 0; |
332 | |
333 | /* Add database if needed */ |
334 | if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) |
335 | { |
336 | end= ma_strmake(end, mpvio->db, NAME_LEN) + 1; |
337 | mysql->db= strdup(mpvio->db); |
338 | } |
339 | |
340 | if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) |
341 | end= ma_strmake(end, mpvio->plugin->name, NAME_LEN) + 1; |
342 | |
343 | end= ma_send_connect_attr(mysql, (unsigned char *)end); |
344 | |
345 | /* Write authentication package */ |
346 | if (ma_net_write(net, (unsigned char *)buff, (size_t) (end-buff)) || ma_net_flush(net)) |
347 | { |
348 | my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, |
349 | ER(CR_SERVER_LOST_EXTENDED), |
350 | "sending authentication information" , |
351 | errno); |
352 | goto error; |
353 | } |
354 | free(buff); |
355 | return 0; |
356 | |
357 | error: |
358 | free(buff); |
359 | return 1; |
360 | } |
361 | |
362 | /** |
363 | vio->read_packet() callback method for client authentication plugins |
364 | |
365 | This function is called by a client authentication plugin, when it wants |
366 | to read data from the server. |
367 | */ |
368 | |
369 | static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf) |
370 | { |
371 | MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv; |
372 | MYSQL *mysql= mpvio->mysql; |
373 | ulong pkt_len; |
374 | |
375 | /* there are cached data left, feed it to a plugin */ |
376 | if (mpvio->cached_server_reply.pkt) |
377 | { |
378 | *buf= mpvio->cached_server_reply.pkt; |
379 | mpvio->cached_server_reply.pkt= 0; |
380 | mpvio->packets_read++; |
381 | return mpvio->cached_server_reply.pkt_len; |
382 | } |
383 | |
384 | if (mpvio->packets_read == 0) |
385 | { |
386 | /* |
387 | the server handshake packet came from the wrong plugin, |
388 | or it's mysql_change_user(). Either way, there is no data |
389 | for a plugin to read. send a dummy packet to the server |
390 | to initiate a dialog. |
391 | */ |
392 | if (client_mpvio_write_packet(mpv, 0, 0)) |
393 | return (int)packet_error; |
394 | } |
395 | |
396 | /* otherwise read the data */ |
397 | if ((pkt_len= ma_net_safe_read(mysql)) == packet_error) |
398 | return (int)packet_error; |
399 | |
400 | mpvio->last_read_packet_len= pkt_len; |
401 | *buf= mysql->net.read_pos; |
402 | |
403 | /* was it a request to change plugins ? */ |
404 | if (pkt_len && **buf == 254) |
405 | return (int)packet_error; /* if yes, this plugin shan't continue */ |
406 | |
407 | /* |
408 | the server sends \1\255 or \1\254 instead of just \255 or \254 - |
409 | for us to not confuse it with an error or "change plugin" packets. |
410 | We remove this escaping \1 here. |
411 | |
412 | See also server_mpvio_write_packet() where the escaping is done. |
413 | */ |
414 | if (pkt_len && **buf == 1) |
415 | { |
416 | (*buf)++; |
417 | pkt_len--; |
418 | } |
419 | mpvio->packets_read++; |
420 | return pkt_len; |
421 | } |
422 | |
423 | /** |
424 | vio->write_packet() callback method for client authentication plugins |
425 | |
426 | This function is called by a client authentication plugin, when it wants |
427 | to send data to the server. |
428 | |
429 | It transparently wraps the data into a change user or authentication |
430 | handshake packet, if necessary. |
431 | */ |
432 | |
433 | static int client_mpvio_write_packet(struct st_plugin_vio *mpv, |
434 | const uchar *pkt, size_t pkt_len) |
435 | { |
436 | int res; |
437 | MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv; |
438 | |
439 | if (mpvio->packets_written == 0) |
440 | { |
441 | if (mpvio->mysql_change_user) |
442 | res= send_change_user_packet(mpvio, pkt, (int)pkt_len); |
443 | else |
444 | res= send_client_reply_packet(mpvio, pkt, (int)pkt_len); |
445 | } |
446 | else |
447 | { |
448 | NET *net= &mpvio->mysql->net; |
449 | if (mpvio->mysql->thd) |
450 | res= 1; /* no chit-chat in embedded */ |
451 | else |
452 | res= ma_net_write(net, (unsigned char *)pkt, pkt_len) || ma_net_flush(net); |
453 | } |
454 | |
455 | if (res) |
456 | { |
457 | /* don't overwrite errors */ |
458 | if (!mysql_errno(mpvio->mysql)) |
459 | my_set_error(mpvio->mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, |
460 | ER(CR_SERVER_LOST_EXTENDED), |
461 | "sending authentication information" , |
462 | errno); |
463 | } |
464 | mpvio->packets_written++; |
465 | return res; |
466 | } |
467 | |
468 | /** |
469 | fills MYSQL_PLUGIN_VIO_INFO structure with the information about the |
470 | connection |
471 | */ |
472 | |
473 | void mpvio_info(MARIADB_PVIO *pvio, MYSQL_PLUGIN_VIO_INFO *info) |
474 | { |
475 | memset(info, 0, sizeof(*info)); |
476 | switch (pvio->type) { |
477 | case PVIO_TYPE_SOCKET: |
478 | info->protocol= MYSQL_VIO_TCP; |
479 | ma_pvio_get_handle(pvio, &info->socket); |
480 | return; |
481 | case PVIO_TYPE_UNIXSOCKET: |
482 | info->protocol= MYSQL_VIO_SOCKET; |
483 | ma_pvio_get_handle(pvio, &info->socket); |
484 | return; |
485 | /* |
486 | case VIO_TYPE_SSL: |
487 | { |
488 | struct sockaddr addr; |
489 | SOCKET_SIZE_TYPE addrlen= sizeof(addr); |
490 | if (getsockname(vio->sd, &addr, &addrlen)) |
491 | return; |
492 | info->protocol= addr.sa_family == AF_UNIX ? |
493 | MYSQL_VIO_SOCKET : MYSQL_VIO_TCP; |
494 | info->socket= vio->sd; |
495 | return; |
496 | } |
497 | */ |
498 | #ifdef _WIN32 |
499 | /* |
500 | case VIO_TYPE_NAMEDPIPE: |
501 | info->protocol= MYSQL_VIO_PIPE; |
502 | info->handle= vio->hPipe; |
503 | return; |
504 | */ |
505 | /* not supported yet |
506 | case VIO_TYPE_SHARED_MEMORY: |
507 | info->protocol= MYSQL_VIO_MEMORY; |
508 | info->handle= vio->handle_file_map; |
509 | return; |
510 | */ |
511 | #endif |
512 | default: DBUG_ASSERT(0); |
513 | } |
514 | } |
515 | |
516 | static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio, |
517 | MYSQL_PLUGIN_VIO_INFO *info) |
518 | { |
519 | MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio; |
520 | mpvio_info(mpvio->mysql->net.pvio, info); |
521 | } |
522 | |
523 | /** |
524 | Client side of the plugin driver authentication. |
525 | |
526 | @note this is used by both the mysql_real_connect and mysql_change_user |
527 | |
528 | @param mysql mysql |
529 | @param data pointer to the plugin auth data (scramble) in the |
530 | handshake packet |
531 | @param data_len the length of the data |
532 | @param data_plugin a plugin that data were prepared for |
533 | or 0 if it's mysql_change_user() |
534 | @param db initial db to use, can be 0 |
535 | |
536 | @retval 0 ok |
537 | @retval 1 error |
538 | */ |
539 | |
540 | int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, |
541 | const char *data_plugin, const char *db) |
542 | { |
543 | const char *auth_plugin_name; |
544 | auth_plugin_t *auth_plugin; |
545 | MCPVIO_EXT mpvio; |
546 | ulong pkt_length; |
547 | int res; |
548 | |
549 | /* determine the default/initial plugin to use */ |
550 | if (mysql->options.extension && mysql->options.extension->default_auth && |
551 | mysql->server_capabilities & CLIENT_PLUGIN_AUTH) |
552 | { |
553 | auth_plugin_name= mysql->options.extension->default_auth; |
554 | if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql, |
555 | auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) |
556 | auth_plugin= &dummy_fallback_client_plugin; |
557 | } |
558 | else |
559 | { |
560 | if (mysql->server_capabilities & CLIENT_PROTOCOL_41) |
561 | auth_plugin= &mysql_native_password_client_plugin; |
562 | else |
563 | { |
564 | if (!(auth_plugin= (auth_plugin_t*)mysql_client_find_plugin(mysql, |
565 | "mysql_old_password" , MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) |
566 | auth_plugin= &dummy_fallback_client_plugin; |
567 | } |
568 | auth_plugin_name= auth_plugin->name; |
569 | } |
570 | |
571 | mysql->net.last_errno= 0; /* just in case */ |
572 | |
573 | if (data_plugin && strcmp(data_plugin, auth_plugin_name)) |
574 | { |
575 | /* data was prepared for a different plugin, don't show it to this one */ |
576 | data= 0; |
577 | data_len= 0; |
578 | } |
579 | |
580 | mpvio.mysql_change_user= data_plugin == 0; |
581 | mpvio.cached_server_reply.pkt= (uchar*)data; |
582 | mpvio.cached_server_reply.pkt_len= data_len; |
583 | mpvio.read_packet= client_mpvio_read_packet; |
584 | mpvio.write_packet= client_mpvio_write_packet; |
585 | mpvio.info= client_mpvio_info; |
586 | mpvio.mysql= mysql; |
587 | mpvio.packets_read= mpvio.packets_written= 0; |
588 | mpvio.db= db; |
589 | |
590 | retry: |
591 | mpvio.plugin= auth_plugin; |
592 | |
593 | mysql->net.read_pos[0]= 0; |
594 | res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); |
595 | |
596 | if ((res == CR_ERROR && !mysql->net.buff) || |
597 | (res > CR_OK && mysql->net.read_pos[0] != 254)) |
598 | { |
599 | /* |
600 | the plugin returned an error. write it down in mysql, |
601 | unless the error code is CR_ERROR and mysql->net.last_errno |
602 | is already set (the plugin has done it) |
603 | */ |
604 | if (res > CR_ERROR) |
605 | my_set_error(mysql, res, SQLSTATE_UNKNOWN, 0); |
606 | else |
607 | if (!mysql->net.last_errno) { |
608 | my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0); |
609 | } |
610 | return 1; |
611 | } |
612 | |
613 | /* read the OK packet (or use the cached value in mysql->net.read_pos */ |
614 | if (res == CR_OK) |
615 | pkt_length= ma_net_safe_read(mysql); |
616 | else /* res == CR_OK_HANDSHAKE_COMPLETE or an error */ |
617 | pkt_length= mpvio.last_read_packet_len; |
618 | |
619 | if (pkt_length == packet_error) |
620 | { |
621 | if (mysql->net.last_errno == CR_SERVER_LOST) |
622 | my_set_error(mysql, CR_SERVER_LOST, SQLSTATE_UNKNOWN, |
623 | ER(CR_SERVER_LOST_EXTENDED), |
624 | "reading authorization packet" , |
625 | errno); |
626 | return 1; |
627 | } |
628 | if (mysql->net.read_pos[0] == 254) |
629 | { |
630 | /* The server asked to use a different authentication plugin */ |
631 | if (pkt_length == 1) |
632 | { |
633 | /* old "use short scramble" packet */ |
634 | auth_plugin_name= old_password_plugin_name; |
635 | mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble_buff; |
636 | mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; |
637 | } |
638 | else |
639 | { |
640 | /* new "use different plugin" packet */ |
641 | uint len; |
642 | auth_plugin_name= (char*)mysql->net.read_pos + 1; |
643 | len= (uint)strlen(auth_plugin_name); /* safe as ma_net_read always appends \0 */ |
644 | mpvio.cached_server_reply.pkt_len= pkt_length - len - 2; |
645 | mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2; |
646 | } |
647 | if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql, |
648 | auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) |
649 | auth_plugin= &dummy_fallback_client_plugin; |
650 | |
651 | goto retry; |
652 | |
653 | } |
654 | /* |
655 | net->read_pos[0] should always be 0 here if the server implements |
656 | the protocol correctly |
657 | */ |
658 | if (mysql->net.read_pos[0] == 0) |
659 | return ma_read_ok_packet(mysql, mysql->net.read_pos + 1, pkt_length); |
660 | return 1; |
661 | } |
662 | |
663 | |