Drizzled Public API Documentation

handshake.cc
Go to the documentation of this file.
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2  *
3  * Drizzle Client & Protocol Library
4  *
5  * Copyright (C) 2008 Eric Day (eday@oddments.org)
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following disclaimer
17  * in the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * * The names of its contributors may not be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  */
37 
43 #include <libdrizzle/common.h>
44 
45 /*
46  * Client Definitions
47  */
48 
49 drizzle_return_t drizzle_handshake_server_read(drizzle_con_st *con)
50 {
51  if (drizzle_state_none(con))
52  {
53  drizzle_state_push(con, drizzle_state_handshake_server_read);
54  drizzle_state_push(con, drizzle_state_packet_read);
55  }
56 
57  return drizzle_state_loop(con);
58 }
59 
60 drizzle_return_t drizzle_handshake_client_write(drizzle_con_st *con)
61 {
62  if (drizzle_state_none(con))
63  {
64  drizzle_state_push(con, drizzle_state_write);
65  drizzle_state_push(con, drizzle_state_handshake_client_write);
66  }
67 
68  return drizzle_state_loop(con);
69 }
70 
71 /*
72  * Server Definitions
73  */
74 
75 drizzle_return_t drizzle_handshake_server_write(drizzle_con_st *con)
76 {
77  if (drizzle_state_none(con))
78  {
79  drizzle_state_push(con, drizzle_state_write);
80  drizzle_state_push(con, drizzle_state_handshake_server_write);
81  }
82 
83  return drizzle_state_loop(con);
84 }
85 
86 drizzle_return_t drizzle_handshake_client_read(drizzle_con_st *con)
87 {
88  if (drizzle_state_none(con))
89  {
90  drizzle_state_push(con, drizzle_state_handshake_client_read);
91  drizzle_state_push(con, drizzle_state_packet_read);
92  }
93 
94  return drizzle_state_loop(con);
95 }
96 
97 /*
98  * State Definitions
99  */
100 
101 drizzle_return_t drizzle_state_handshake_server_read(drizzle_con_st *con)
102 {
103  uint8_t *ptr;
104  int extra_length;
105  unsigned char* packet_end;
106 
107  if (con == NULL)
108  {
109  return DRIZZLE_RETURN_INVALID_ARGUMENT;
110  }
111  drizzle_log_debug(con->drizzle, "drizzle_state_handshake_server_read");
112 
113  /* Assume the entire handshake packet will fit in the buffer. */
114  if (con->buffer_size < con->packet_size)
115  {
116  drizzle_state_push(con, drizzle_state_read);
117  return DRIZZLE_RETURN_OK;
118  }
119 
120  if (con->packet_size < 46)
121  {
122  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
123  "bad packet size:>=46:%zu", con->packet_size);
124  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
125  }
126 
127  packet_end= con->buffer_ptr + con->packet_size;
128  con->protocol_version= con->buffer_ptr[0];
129  con->buffer_ptr++;
130 
131  if (con->protocol_version != 10)
132  {
133  /* This is a special case where the server determines that authentication
134  will be impossible and denies any attempt right away. */
135  if (con->protocol_version == 255)
136  {
137  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
138  "%.*s", (int32_t)con->packet_size - 3,
139  con->buffer_ptr + 2);
140  return DRIZZLE_RETURN_AUTH_FAILED;
141  }
142 
143  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
144  "protocol version not supported:%d",
145  con->protocol_version);
146  return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
147  }
148 
149  /* Look for null-terminated server version string. */
150  ptr= (uint8_t*)memchr(con->buffer_ptr, 0, con->buffer_size - 1);
151  if (ptr == NULL)
152  {
153  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
154  "server version string not found");
155  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
156  }
157 
158  if (con->packet_size < (46 + (size_t)(ptr - con->buffer_ptr)))
159  {
160  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
161  "bad packet size:%zu:%zu",
162  (46 + (size_t)(ptr - con->buffer_ptr)), con->packet_size);
163  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
164  }
165 
166  strncpy(con->server_version, (char *)con->buffer_ptr,
167  DRIZZLE_MAX_SERVER_VERSION_SIZE);
168  con->server_version[DRIZZLE_MAX_SERVER_VERSION_SIZE - 1]= 0;
169  con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
170 
171  con->thread_id= (uint32_t)drizzle_get_byte4(con->buffer_ptr);
172  con->buffer_ptr+= 4;
173 
174  con->scramble= con->scramble_buffer;
175  memcpy(con->scramble, con->buffer_ptr, 8);
176  /* Skip scramble and filler. */
177  con->buffer_ptr+= 9;
178 
179  /* Even though drizzle_capabilities is more than 2 bytes, the protocol only
180  allows for 2. This means some capabilities are not possible during this
181  handshake step. The options beyond 2 bytes are for client response only. */
182  con->capabilities= (drizzle_capabilities_t)drizzle_get_byte2(con->buffer_ptr);
183  con->buffer_ptr+= 2;
184 
185  if (con->options & DRIZZLE_CON_MYSQL &&
186  !(con->capabilities & DRIZZLE_CAPABILITIES_PROTOCOL_41))
187  {
188  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
189  "protocol version not supported, must be MySQL 4.1+");
190  return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
191  }
192 
193  con->charset= con->buffer_ptr[0];
194  con->buffer_ptr+= 1;
195 
196  con->status= drizzle_con_status_t(drizzle_get_byte2(con->buffer_ptr));
197  /* Skip status and filler. */
198  con->buffer_ptr+= 15;
199 
200  memcpy(con->scramble + 8, con->buffer_ptr, 12);
201  con->buffer_ptr+= 13;
202 
203  /* MySQL 5.5 adds "mysql_native_password" after the server greeting. */
204  extra_length= packet_end - con->buffer_ptr;
205  assert(extra_length >= 0);
206  if (extra_length > DRIZZLE_MAX_SERVER_EXTRA_SIZE - 1)
207  extra_length= DRIZZLE_MAX_SERVER_EXTRA_SIZE - 1;
208  memcpy(con->server_extra, (char *)con->buffer_ptr, extra_length);
209  con->server_extra[extra_length]= 0;
210 
211  con->buffer_size-= con->packet_size;
212  if (con->buffer_size != 0)
213  {
214  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
215  "unexpected data after packet:%zu", con->buffer_size);
216  return DRIZZLE_RETURN_UNEXPECTED_DATA;
217  }
218 
219  con->buffer_ptr= con->buffer;
220 
221  drizzle_state_pop(con);
222 
223  if (!(con->options & DRIZZLE_CON_RAW_PACKET))
224  {
225  drizzle_state_push(con, drizzle_state_handshake_result_read);
226  drizzle_state_push(con, drizzle_state_packet_read);
227  drizzle_state_push(con, drizzle_state_write);
228  drizzle_state_push(con, drizzle_state_handshake_client_write);
229  }
230 
231  return DRIZZLE_RETURN_OK;
232 }
233 
234 drizzle_return_t drizzle_state_handshake_server_write(drizzle_con_st *con)
235 {
236  uint8_t *ptr;
237 
238  if (con == NULL)
239  {
240  return DRIZZLE_RETURN_INVALID_ARGUMENT;
241  }
242  drizzle_log_debug(con->drizzle, "drizzle_state_handshake_server_write");
243 
244  /* Calculate max packet size. */
245  con->packet_size= 1 /* Protocol version */
246  + strlen(con->server_version) + 1
247  + 4 /* Thread ID */
248  + 8 /* Scramble */
249  + 1 /* NULL */
250  + 2 /* Capabilities */
251  + 1 /* Language */
252  + 2 /* Status */
253  + 13 /* Unused */
254  + 12 /* Scramble */
255  + 1; /* NULL */
256 
257  /* Assume the entire handshake packet will fit in the buffer. */
258  if ((con->packet_size + 4) > DRIZZLE_MAX_BUFFER_SIZE)
259  {
260  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_write",
261  "buffer too small:%zu", con->packet_size + 4);
262  return DRIZZLE_RETURN_INTERNAL_ERROR;
263  }
264 
265  ptr= con->buffer_ptr;
266 
267  /* Store packet size and packet number first. */
268  drizzle_set_byte3(ptr, con->packet_size);
269  ptr[3]= 0;
270  con->packet_number= 1;
271  ptr+= 4;
272 
273  ptr[0]= con->protocol_version;
274  ptr++;
275 
276  memcpy(ptr, con->server_version, strlen(con->server_version));
277  ptr+= strlen(con->server_version);
278 
279  ptr[0]= 0;
280  ptr++;
281 
282  drizzle_set_byte4(ptr, con->thread_id);
283  ptr+= 4;
284 
285  if (con->scramble == NULL)
286  {
287  memset(ptr, 0, 8);
288  }
289  else
290  {
291  memcpy(ptr, con->scramble, 8);
292  }
293  ptr+= 8;
294 
295  ptr[0]= 0;
296  ptr++;
297 
298  if (con->options & DRIZZLE_CON_MYSQL)
299  con->capabilities|= DRIZZLE_CAPABILITIES_PROTOCOL_41;
300 
301  /* We can only send two bytes worth, this is a protocol limitation. */
302  drizzle_set_byte2(ptr, con->capabilities);
303  ptr+= 2;
304 
305  ptr[0]= con->charset;
306  ptr++;
307 
308  drizzle_set_byte2(ptr, con->status);
309  ptr+= 2;
310 
311  memset(ptr, 0, 13);
312  ptr+= 13;
313 
314  if (con->scramble == NULL)
315  {
316  memset(ptr, 0, 12);
317  }
318  else
319  {
320  memcpy(ptr, con->scramble + 8, 12);
321  }
322  ptr+= 12;
323 
324  ptr[0]= 0;
325  ptr++;
326 
327  con->buffer_size+= (4 + con->packet_size);
328 
329  /* Make sure we packed it correctly. */
330  if ((size_t)(ptr - con->buffer_ptr) != (4 + con->packet_size))
331  {
332  drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_write",
333  "error packing server handshake:%zu:%zu",
334  (size_t)(ptr - con->buffer_ptr), 4 + con->packet_size);
335  return DRIZZLE_RETURN_INTERNAL_ERROR;
336  }
337 
338  drizzle_state_pop(con);
339  return DRIZZLE_RETURN_OK;
340 }
341 
342 drizzle_return_t drizzle_state_handshake_client_read(drizzle_con_st *con)
343 {
344  size_t real_size;
345  uint8_t scramble_size;
346 
347  if (con == NULL)
348  {
349  return DRIZZLE_RETURN_INVALID_ARGUMENT;
350  }
351  drizzle_log_debug(con->drizzle, "drizzle_state_handshake_client_read");
352 
353  /* Assume the entire handshake packet will fit in the buffer. */
354  if (con->buffer_size < con->packet_size)
355  {
356  drizzle_state_push(con, drizzle_state_read);
357  return DRIZZLE_RETURN_OK;
358  }
359 
360  /* This is the minimum packet size. */
361  if (con->packet_size < 34)
362  {
363  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
364  "bad packet size:>=34:%zu", con->packet_size);
365  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
366  }
367 
368  real_size= 34;
369 
370  con->capabilities= drizzle_get_byte4(con->buffer_ptr);
371  con->buffer_ptr+= 4;
372 
373  if (con->options & DRIZZLE_CON_MYSQL &&
374  !(con->capabilities & DRIZZLE_CAPABILITIES_PROTOCOL_41))
375  {
376  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
377  "protocol version not supported, must be MySQL 4.1+");
378  return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
379  }
380 
381  con->max_packet_size= (uint32_t)drizzle_get_byte4(con->buffer_ptr);
382  con->buffer_ptr+= 4;
383 
384  con->charset= con->buffer_ptr[0];
385  con->buffer_ptr+= 1;
386 
387  /* Skip unused. */
388  con->buffer_ptr+= 23;
389 
390  /* Look for null-terminated user string. */
391  uint8_t *ptr= (uint8_t*)memchr(con->buffer_ptr, 0, con->buffer_size - 32);
392  if (ptr == NULL)
393  {
394  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
395  "user string not found");
396  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
397  }
398 
399  if (con->buffer_ptr == ptr)
400  {
401  con->user[0]= 0;
402  con->buffer_ptr++;
403  }
404  else
405  {
406  real_size+= (size_t)(ptr - con->buffer_ptr);
407  if (con->packet_size < real_size)
408  {
409  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
410  "bad packet size:>=%zu:%zu", real_size,
411  con->packet_size);
412  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
413  }
414 
415  strncpy(con->user, (char *)con->buffer_ptr, DRIZZLE_MAX_USER_SIZE);
416  con->user[DRIZZLE_MAX_USER_SIZE - 1]= 0;
417  con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
418  }
419 
420  scramble_size= con->buffer_ptr[0];
421  con->buffer_ptr+= 1;
422 
423  if (scramble_size == 0)
424  {
425  con->scramble= NULL;
426  }
427  else
428  {
429  if (scramble_size != DRIZZLE_MAX_SCRAMBLE_SIZE)
430  {
431  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
432  "wrong scramble size");
433  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
434  }
435 
436  real_size+= scramble_size;
437  con->scramble= con->scramble_buffer;
438  memcpy(con->scramble, con->buffer_ptr, DRIZZLE_MAX_SCRAMBLE_SIZE);
439 
440  con->buffer_ptr+= DRIZZLE_MAX_SCRAMBLE_SIZE;
441  }
442 
443  /* Look for null-terminated db string. */
444  if ((34 + strlen(con->user) + scramble_size) == con->packet_size)
445  {
446  con->db[0]= 0;
447  }
448  else
449  {
450  ptr= (uint8_t*)memchr(con->buffer_ptr, 0, con->buffer_size -
451  (34 + strlen(con->user) + scramble_size));
452  if (ptr == NULL)
453  {
454  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
455  "db string not found");
456  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
457  }
458 
459  real_size+= ((size_t)(ptr - con->buffer_ptr) + 1);
460  if (con->packet_size != real_size)
461  {
462  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
463  "bad packet size:%zu:%zu", real_size, con->packet_size);
464  return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
465  }
466 
467  if (con->buffer_ptr == ptr)
468  {
469  con->db[0]= 0;
470  con->buffer_ptr++;
471  }
472  else
473  {
474  strncpy(con->db, (char *)con->buffer_ptr, DRIZZLE_MAX_DB_SIZE);
475  con->db[DRIZZLE_MAX_DB_SIZE - 1]= 0;
476  con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
477  }
478  }
479 
480  con->buffer_size-= con->packet_size;
481  if (con->buffer_size != 0)
482  {
483  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
484  "unexpected data after packet:%zu", con->buffer_size);
485  return DRIZZLE_RETURN_UNEXPECTED_DATA;
486  }
487 
488  con->buffer_ptr= con->buffer;
489 
490  drizzle_state_pop(con);
491  return DRIZZLE_RETURN_OK;
492 }
493 
494 drizzle_return_t drizzle_state_handshake_client_write(drizzle_con_st *con)
495 {
496  uint8_t *ptr;
497  int capabilities;
498  drizzle_return_t ret;
499 
500  if (con == NULL)
501  {
502  return DRIZZLE_RETURN_INVALID_ARGUMENT;
503  }
504  drizzle_log_debug(con->drizzle, "drizzle_state_handshake_client_write");
505 
506  /* Calculate max packet size. */
507  con->packet_size= 4 /* Capabilities */
508  + 4 /* Max packet size */
509  + 1 /* Charset */
510  + 23 /* Unused */
511  + strlen(con->user) + 1
512  + 1 /* Scramble size */
513  + DRIZZLE_MAX_SCRAMBLE_SIZE
514  + strlen(con->db) + 1;
515 
516  /* Assume the entire handshake packet will fit in the buffer. */
517  if ((con->packet_size + 4) > DRIZZLE_MAX_BUFFER_SIZE)
518  {
519  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_write",
520  "buffer too small:%zu", con->packet_size + 4);
521  return DRIZZLE_RETURN_INTERNAL_ERROR;
522  }
523 
524  ptr= con->buffer_ptr;
525 
526  /* Store packet size at the end since it may change. */
527  ptr[3]= con->packet_number;
528  con->packet_number++;
529  ptr+= 4;
530 
531  if (con->options & DRIZZLE_CON_MYSQL)
532  con->capabilities|= DRIZZLE_CAPABILITIES_PROTOCOL_41;
533 
534  capabilities= con->capabilities & int(DRIZZLE_CAPABILITIES_CLIENT);
535  if (!(con->options & DRIZZLE_CON_FOUND_ROWS))
536  capabilities&= ~int(DRIZZLE_CAPABILITIES_FOUND_ROWS);
537 
538  if (con->options & DRIZZLE_CON_INTERACTIVE)
539  {
540  capabilities|= int(DRIZZLE_CAPABILITIES_INTERACTIVE);
541  }
542 
543  if (con->options & DRIZZLE_CON_MULTI_STATEMENTS)
544  {
545  capabilities|= int(DRIZZLE_CAPABILITIES_MULTI_STATEMENTS);
546  }
547 
548  if (con->options & DRIZZLE_CON_AUTH_PLUGIN)
549  {
550  capabilities|= int(DRIZZLE_CAPABILITIES_PLUGIN_AUTH);
551  }
552 
553  capabilities&= ~(int(DRIZZLE_CAPABILITIES_COMPRESS) | int(DRIZZLE_CAPABILITIES_SSL));
554  if (con->db[0] == 0)
555  capabilities&= ~int(DRIZZLE_CAPABILITIES_CONNECT_WITH_DB);
556 
557  drizzle_set_byte4(ptr, capabilities);
558  ptr+= 4;
559 
560  drizzle_set_byte4(ptr, con->max_packet_size);
561  ptr+= 4;
562 
563  ptr[0]= con->charset;
564  ptr++;
565 
566  memset(ptr, 0, 23);
567  ptr+= 23;
568 
569  ptr= drizzle_pack_auth(con, ptr, &ret);
570  if (ret != DRIZZLE_RETURN_OK)
571  return ret;
572 
573  con->buffer_size+= (4 + con->packet_size);
574 
575  /* Make sure we packed it correctly. */
576  if ((size_t)(ptr - con->buffer_ptr) != (4 + con->packet_size))
577  {
578  drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_write",
579  "error packing client handshake:%zu:%zu",
580  (size_t)(ptr - con->buffer_ptr), 4 + con->packet_size);
581  return DRIZZLE_RETURN_INTERNAL_ERROR;
582  }
583 
584  /* Store packet size now. */
585  drizzle_set_byte3(con->buffer_ptr, con->packet_size);
586 
587  drizzle_state_pop(con);
588  return DRIZZLE_RETURN_OK;
589 }
590 
591 drizzle_return_t drizzle_state_handshake_result_read(drizzle_con_st *con)
592 {
593  if (con == NULL)
594  {
595  return DRIZZLE_RETURN_INVALID_ARGUMENT;
596  }
597  drizzle_log_debug(con->drizzle, "drizzle_state_handshake_result_read");
598 
599  drizzle_result_st result;
600  if (drizzle_result_create(con, &result) == NULL)
601  {
602  return DRIZZLE_RETURN_MEMORY;
603  }
604 
605  con->result= &result;
606 
607  drizzle_return_t ret= drizzle_state_result_read(con);
608  if (drizzle_state_none(con))
609  {
610  if (ret == DRIZZLE_RETURN_OK)
611  {
612  if (drizzle_result_eof(&result))
613  {
614  drizzle_set_error(con->drizzle, "drizzle_state_handshake_result_read",
615  "old insecure authentication mechanism not supported");
616  ret= DRIZZLE_RETURN_AUTH_FAILED;
617  }
618  else
619  {
620  con->options|= DRIZZLE_CON_READY;
621  }
622  }
623  }
624 
625  drizzle_result_free(&result);
626 
627  if (ret == DRIZZLE_RETURN_ERROR_CODE)
628  {
629  return DRIZZLE_RETURN_HANDSHAKE_FAILED;
630  }
631 
632  return ret;
633 }