1 /*
2  * Drizzle Client & Protocol Library
3  *
4  * Copyright (C) 2008 Eric Day (eday@oddments.org)
5  * All rights reserved.
6  *
7  * Use and distribution licensed under the BSD license.  See
8  * the COPYING file in this directory for full text.
9  */
10 
11 /**
12  * @file
13  * @brief Handshake Definitions
14  */
15 
16 #include "common.h"
17 
18 /*
19  * Client Definitions
20  */
21 
drizzle_handshake_server_read(drizzle_con_st * con)22 drizzle_return_t drizzle_handshake_server_read(drizzle_con_st *con)
23 {
24   if (drizzle_state_none(con))
25   {
26     drizzle_state_push(con, drizzle_state_handshake_server_read);
27     drizzle_state_push(con, drizzle_state_packet_read);
28   }
29 
30   return drizzle_state_loop(con);
31 }
32 
drizzle_handshake_client_write(drizzle_con_st * con)33 drizzle_return_t drizzle_handshake_client_write(drizzle_con_st *con)
34 {
35   if (drizzle_state_none(con))
36   {
37     drizzle_state_push(con, drizzle_state_write);
38     drizzle_state_push(con, drizzle_state_handshake_client_write);
39   }
40 
41   return drizzle_state_loop(con);
42 }
43 
44 /*
45  * Server Definitions
46  */
47 
drizzle_handshake_server_write(drizzle_con_st * con)48 drizzle_return_t drizzle_handshake_server_write(drizzle_con_st *con)
49 {
50   if (drizzle_state_none(con))
51   {
52     drizzle_state_push(con, drizzle_state_write);
53     drizzle_state_push(con, drizzle_state_handshake_server_write);
54   }
55 
56   return drizzle_state_loop(con);
57 }
58 
drizzle_handshake_client_read(drizzle_con_st * con)59 drizzle_return_t drizzle_handshake_client_read(drizzle_con_st *con)
60 {
61   if (drizzle_state_none(con))
62   {
63     drizzle_state_push(con, drizzle_state_handshake_client_read);
64     drizzle_state_push(con, drizzle_state_packet_read);
65   }
66 
67   return drizzle_state_loop(con);
68 }
69 
70 /*
71  * State Definitions
72  */
73 
drizzle_state_handshake_server_read(drizzle_con_st * con)74 drizzle_return_t drizzle_state_handshake_server_read(drizzle_con_st *con)
75 {
76   uint8_t *ptr;
77 
78   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_server_read");
79 
80   /* Assume the entire handshake packet will fit in the buffer. */
81   if (con->buffer_size < con->packet_size)
82   {
83     drizzle_state_push(con, drizzle_state_read);
84     return DRIZZLE_RETURN_OK;
85   }
86 
87   if (con->packet_size < 46)
88   {
89     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
90                       "bad packet size:>=46:%zu", con->packet_size);
91     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
92   }
93 
94   con->protocol_version= con->buffer_ptr[0];
95   con->buffer_ptr++;
96 
97   if (con->protocol_version != 10)
98   {
99     /* This is a special case where the server determines that authentication
100        will be impossible and denies any attempt right away. */
101     if (con->protocol_version == 255)
102     {
103       drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
104                         "%.*s", (int32_t)con->packet_size - 3,
105                         con->buffer_ptr + 2);
106       return DRIZZLE_RETURN_AUTH_FAILED;
107     }
108 
109     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
110                       "protocol version not supported:%d",
111                       con->protocol_version);
112     return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
113   }
114 
115   /* Look for null-terminated server version string. */
116   ptr= memchr(con->buffer_ptr, 0, con->buffer_size - 1);
117   if (ptr == NULL)
118   {
119     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
120                       "server version string not found");
121     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
122   }
123 
124   if (con->packet_size != (46 + (size_t)(ptr - con->buffer_ptr)))
125   {
126     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
127                       "bad packet size:%zu:%zu",
128                       (46 + (size_t)(ptr - con->buffer_ptr)), con->packet_size);
129     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
130   }
131 
132   strncpy(con->server_version, (char *)con->buffer_ptr,
133           DRIZZLE_MAX_SERVER_VERSION_SIZE);
134   con->server_version[DRIZZLE_MAX_SERVER_VERSION_SIZE - 1]= 0;
135   con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
136 
137   con->thread_id= (uint32_t)drizzle_get_byte4(con->buffer_ptr);
138   con->buffer_ptr+= 4;
139 
140   con->scramble= con->scramble_buffer;
141   memcpy(con->scramble, con->buffer_ptr, 8);
142   /* Skip scramble and filler. */
143   con->buffer_ptr+= 9;
144 
145   /* Even though drizzle_capabilities is more than 2 bytes, the protocol only
146      allows for 2. This means some capabilities are not possible during this
147      handshake step. The options beyond 2 bytes are for client response only. */
148   con->capabilities= (drizzle_capabilities_t)drizzle_get_byte2(con->buffer_ptr);
149   con->buffer_ptr+= 2;
150 
151   if (con->options & DRIZZLE_CON_MYSQL &&
152       !(con->capabilities & DRIZZLE_CAPABILITIES_PROTOCOL_41))
153   {
154     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
155                       "protocol version not supported, must be MySQL 4.1+");
156     return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
157   }
158 
159   con->charset= con->buffer_ptr[0];
160   con->buffer_ptr+= 1;
161 
162   con->status= drizzle_get_byte2(con->buffer_ptr);
163   /* Skip status and filler. */
164   con->buffer_ptr+= 15;
165 
166   memcpy(con->scramble + 8, con->buffer_ptr, 12);
167   con->buffer_ptr+= 13;
168 
169   con->buffer_size-= con->packet_size;
170   if (con->buffer_size != 0)
171   {
172     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
173                       "unexpected data after packet:%zu", con->buffer_size);
174     return DRIZZLE_RETURN_UNEXPECTED_DATA;
175   }
176 
177   con->buffer_ptr= con->buffer;
178 
179   drizzle_state_pop(con);
180 
181   if (!(con->options & DRIZZLE_CON_RAW_PACKET))
182   {
183     drizzle_state_push(con, drizzle_state_handshake_result_read);
184     drizzle_state_push(con, drizzle_state_packet_read);
185     drizzle_state_push(con, drizzle_state_write);
186     drizzle_state_push(con, drizzle_state_handshake_client_write);
187   }
188 
189   return DRIZZLE_RETURN_OK;
190 }
191 
drizzle_state_handshake_server_write(drizzle_con_st * con)192 drizzle_return_t drizzle_state_handshake_server_write(drizzle_con_st *con)
193 {
194   uint8_t *ptr;
195 
196   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_server_write");
197 
198   /* Calculate max packet size. */
199   con->packet_size= 1   /* Protocol version */
200                   + strlen(con->server_version) + 1
201                   + 4   /* Thread ID */
202                   + 8   /* Scramble */
203                   + 1   /* NULL */
204                   + 2   /* Capabilities */
205                   + 1   /* Language */
206                   + 2   /* Status */
207                   + 13  /* Unused */
208                   + 12  /* Scramble */
209                   + 1;  /* NULL */
210 
211   /* Assume the entire handshake packet will fit in the buffer. */
212   if ((con->packet_size + 4) > DRIZZLE_MAX_BUFFER_SIZE)
213   {
214     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_write",
215                       "buffer too small:%zu", con->packet_size + 4);
216     return DRIZZLE_RETURN_INTERNAL_ERROR;
217   }
218 
219   ptr= con->buffer_ptr;
220 
221   /* Store packet size and packet number first. */
222   drizzle_set_byte3(ptr, con->packet_size);
223   ptr[3]= 0;
224   con->packet_number= 1;
225   ptr+= 4;
226 
227   ptr[0]= con->protocol_version;
228   ptr++;
229 
230   memcpy(ptr, con->server_version, strlen(con->server_version));
231   ptr+= strlen(con->server_version);
232 
233   ptr[0]= 0;
234   ptr++;
235 
236   drizzle_set_byte4(ptr, con->thread_id);
237   ptr+= 4;
238 
239   if (con->scramble == NULL)
240     memset(ptr, 0, 8);
241   else
242     memcpy(ptr, con->scramble, 8);
243   ptr+= 8;
244 
245   ptr[0]= 0;
246   ptr++;
247 
248   if (con->options & DRIZZLE_CON_MYSQL)
249     con->capabilities|= DRIZZLE_CAPABILITIES_PROTOCOL_41;
250 
251   /* We can only send two bytes worth, this is a protocol limitation. */
252   drizzle_set_byte2(ptr, con->capabilities);
253   ptr+= 2;
254 
255   ptr[0]= con->charset;
256   ptr++;
257 
258   drizzle_set_byte2(ptr, con->status);
259   ptr+= 2;
260 
261   memset(ptr, 0, 13);
262   ptr+= 13;
263 
264   if (con->scramble == NULL)
265     memset(ptr, 0, 12);
266   else
267     memcpy(ptr, con->scramble + 8, 12);
268   ptr+= 12;
269 
270   ptr[0]= 0;
271   ptr++;
272 
273   con->buffer_size+= (4 + con->packet_size);
274 
275   /* Make sure we packed it correctly. */
276   if ((size_t)(ptr - con->buffer_ptr) != (4 + con->packet_size))
277   {
278     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_write",
279                       "error packing server handshake:%zu:%zu",
280                       (size_t)(ptr - con->buffer_ptr), 4 + con->packet_size);
281     return DRIZZLE_RETURN_INTERNAL_ERROR;
282   }
283 
284   drizzle_state_pop(con);
285   return DRIZZLE_RETURN_OK;
286 }
287 
drizzle_state_handshake_client_read(drizzle_con_st * con)288 drizzle_return_t drizzle_state_handshake_client_read(drizzle_con_st *con)
289 {
290   size_t real_size;
291   uint8_t *ptr;
292   uint8_t scramble_size;
293 
294   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_client_read");
295 
296   /* Assume the entire handshake packet will fit in the buffer. */
297   if (con->buffer_size < con->packet_size)
298   {
299     drizzle_state_push(con, drizzle_state_read);
300     return DRIZZLE_RETURN_OK;
301   }
302 
303   /* This is the minimum packet size. */
304   if (con->packet_size < 34)
305   {
306     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
307                       "bad packet size:>=34:%zu", con->packet_size);
308     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
309   }
310 
311   real_size= 34;
312 
313   con->capabilities= drizzle_get_byte4(con->buffer_ptr);
314   con->buffer_ptr+= 4;
315 
316   if (con->options & DRIZZLE_CON_MYSQL &&
317       !(con->capabilities & DRIZZLE_CAPABILITIES_PROTOCOL_41))
318   {
319     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
320                       "protocol version not supported, must be MySQL 4.1+");
321     return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
322   }
323 
324   con->max_packet_size= (uint32_t)drizzle_get_byte4(con->buffer_ptr);
325   con->buffer_ptr+= 4;
326 
327   con->charset= con->buffer_ptr[0];
328   con->buffer_ptr+= 1;
329 
330   /* Skip unused. */
331   con->buffer_ptr+= 23;
332 
333   /* Look for null-terminated user string. */
334   ptr= memchr(con->buffer_ptr, 0, con->buffer_size - 32);
335   if (ptr == NULL)
336   {
337     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
338                       "user string not found");
339     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
340   }
341 
342   if (con->buffer_ptr == ptr)
343   {
344     con->user[0]= 0;
345     con->buffer_ptr++;
346   }
347   else
348   {
349     real_size+= (size_t)(ptr - con->buffer_ptr);
350     if (con->packet_size < real_size)
351     {
352       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
353                         "bad packet size:>=%zu:%zu", real_size,
354                         con->packet_size);
355       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
356     }
357 
358     strncpy(con->user, (char *)con->buffer_ptr, DRIZZLE_MAX_USER_SIZE);
359     con->user[DRIZZLE_MAX_USER_SIZE - 1]= 0;
360     con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
361   }
362 
363   scramble_size= con->buffer_ptr[0];
364   con->buffer_ptr+= 1;
365 
366   if (scramble_size == 0)
367     con->scramble= NULL;
368   else
369   {
370     if (scramble_size != DRIZZLE_MAX_SCRAMBLE_SIZE)
371     {
372       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
373                         "wrong scramble size");
374       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
375     }
376 
377     real_size+= scramble_size;
378     con->scramble= con->scramble_buffer;
379     memcpy(con->scramble, con->buffer_ptr, DRIZZLE_MAX_SCRAMBLE_SIZE);
380 
381     con->buffer_ptr+= DRIZZLE_MAX_SCRAMBLE_SIZE;
382   }
383 
384   /* Look for null-terminated db string. */
385   if ((34 + strlen(con->user) + scramble_size) == con->packet_size)
386     con->db[0]= 0;
387   else
388   {
389     ptr= memchr(con->buffer_ptr, 0, con->buffer_size -
390                                     (34 + strlen(con->user) + scramble_size));
391     if (ptr == NULL)
392     {
393       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
394                         "db string not found");
395       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
396     }
397 
398     real_size+= ((size_t)(ptr - con->buffer_ptr) + 1);
399     if (con->packet_size != real_size)
400     {
401       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
402                         "bad packet size:%zu:%zu", real_size, con->packet_size);
403       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
404     }
405 
406     if (con->buffer_ptr == ptr)
407     {
408       con->db[0]= 0;
409       con->buffer_ptr++;
410     }
411     else
412     {
413       strncpy(con->db, (char *)con->buffer_ptr, DRIZZLE_MAX_DB_SIZE);
414       con->db[DRIZZLE_MAX_DB_SIZE - 1]= 0;
415       con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
416     }
417   }
418 
419   con->buffer_size-= con->packet_size;
420   if (con->buffer_size != 0)
421   {
422     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
423                       "unexpected data after packet:%zu", con->buffer_size);
424     return DRIZZLE_RETURN_UNEXPECTED_DATA;
425   }
426 
427   con->buffer_ptr= con->buffer;
428 
429   drizzle_state_pop(con);
430   return DRIZZLE_RETURN_OK;
431 }
432 
drizzle_state_handshake_client_write(drizzle_con_st * con)433 drizzle_return_t drizzle_state_handshake_client_write(drizzle_con_st *con)
434 {
435   uint8_t *ptr;
436   drizzle_capabilities_t capabilities;
437   drizzle_return_t ret;
438 
439   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_client_write");
440 
441   /* Calculate max packet size. */
442   con->packet_size= 4   /* Capabilities */
443                   + 4   /* Max packet size */
444                   + 1   /* Charset */
445                   + 23  /* Unused */
446                   + strlen(con->user) + 1
447                   + 1   /* Scramble size */
448                   + DRIZZLE_MAX_SCRAMBLE_SIZE
449                   + strlen(con->db) + 1;
450 
451   /* Assume the entire handshake packet will fit in the buffer. */
452   if ((con->packet_size + 4) > DRIZZLE_MAX_BUFFER_SIZE)
453   {
454     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_write",
455                       "buffer too small:%zu", con->packet_size + 4);
456     return DRIZZLE_RETURN_INTERNAL_ERROR;
457   }
458 
459   ptr= con->buffer_ptr;
460 
461   /* Store packet size at the end since it may change. */
462   ptr[3]= con->packet_number;
463   con->packet_number++;
464   ptr+= 4;
465 
466   if (con->options & DRIZZLE_CON_MYSQL)
467     con->capabilities|= DRIZZLE_CAPABILITIES_PROTOCOL_41;
468 
469   capabilities= con->capabilities & DRIZZLE_CAPABILITIES_CLIENT;
470   capabilities&= ~(DRIZZLE_CAPABILITIES_COMPRESS | DRIZZLE_CAPABILITIES_SSL);
471   if (con->db[0] == 0)
472     capabilities&= ~DRIZZLE_CAPABILITIES_CONNECT_WITH_DB;
473 
474   drizzle_set_byte4(ptr, capabilities);
475   ptr+= 4;
476 
477   drizzle_set_byte4(ptr, con->max_packet_size);
478   ptr+= 4;
479 
480   ptr[0]= con->charset;
481   ptr++;
482 
483   memset(ptr, 0, 23);
484   ptr+= 23;
485 
486   ptr= drizzle_pack_auth(con, ptr, &ret);
487   if (ret != DRIZZLE_RETURN_OK)
488     return ret;
489 
490   con->buffer_size+= (4 + con->packet_size);
491 
492   /* Make sure we packed it correctly. */
493   if ((size_t)(ptr - con->buffer_ptr) != (4 + con->packet_size))
494   {
495     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_write",
496                       "error packing client handshake:%zu:%zu",
497                       (size_t)(ptr - con->buffer_ptr), 4 + con->packet_size);
498     return DRIZZLE_RETURN_INTERNAL_ERROR;
499   }
500 
501   /* Store packet size now. */
502   drizzle_set_byte3(con->buffer_ptr, con->packet_size);
503 
504   drizzle_state_pop(con);
505   return DRIZZLE_RETURN_OK;
506 }
507 
drizzle_state_handshake_result_read(drizzle_con_st * con)508 drizzle_return_t drizzle_state_handshake_result_read(drizzle_con_st *con)
509 {
510   drizzle_return_t ret;
511   drizzle_result_st result;
512 
513   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_result_read");
514 
515   if (drizzle_result_create(con, &result) == NULL)
516     return DRIZZLE_RETURN_MEMORY;
517 
518   con->result= &result;
519 
520   ret= drizzle_state_result_read(con);
521   if (drizzle_state_none(con))
522   {
523     if (ret == DRIZZLE_RETURN_OK)
524     {
525       if (drizzle_result_eof(&result))
526       {
527         drizzle_set_error(con->drizzle, "drizzle_state_handshake_result_read",
528                          "old insecure authentication mechanism not supported");
529         ret= DRIZZLE_RETURN_AUTH_FAILED;
530       }
531       else
532         con->options|= DRIZZLE_CON_READY;
533     }
534   }
535 
536   drizzle_result_free(&result);
537 
538   if (ret == DRIZZLE_RETURN_ERROR_CODE)
539     return DRIZZLE_RETURN_HANDSHAKE_FAILED;
540 
541   return ret;
542 }
543