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