libdrizzle Developer Documentation

libdrizzle/handshake.c
Go to the documentation of this file.
00001 /*
00002  * Drizzle Client & Protocol Library
00003  *
00004  * Copyright (C) 2008 Eric Day (eday@oddments.org)
00005  * All rights reserved.
00006  *
00007  * Use and distribution licensed under the BSD license.  See
00008  * the COPYING file in this directory for full text.
00009  */
00010 
00016 #include "common.h"
00017 
00018 /*
00019  * Client Definitions
00020  */
00021 
00022 drizzle_return_t drizzle_handshake_server_read(drizzle_con_st *con)
00023 {
00024   if (drizzle_state_none(con))
00025   {
00026     drizzle_state_push(con, drizzle_state_handshake_server_read);
00027     drizzle_state_push(con, drizzle_state_packet_read);
00028   }
00029 
00030   return drizzle_state_loop(con);
00031 }
00032 
00033 drizzle_return_t drizzle_handshake_client_write(drizzle_con_st *con)
00034 {
00035   if (drizzle_state_none(con))
00036   {
00037     drizzle_state_push(con, drizzle_state_write);
00038     drizzle_state_push(con, drizzle_state_handshake_client_write);
00039   }
00040 
00041   return drizzle_state_loop(con);
00042 }
00043 
00044 /*
00045  * Server Definitions
00046  */
00047 
00048 drizzle_return_t drizzle_handshake_server_write(drizzle_con_st *con)
00049 {
00050   if (drizzle_state_none(con))
00051   {
00052     drizzle_state_push(con, drizzle_state_write);
00053     drizzle_state_push(con, drizzle_state_handshake_server_write);
00054   }
00055 
00056   return drizzle_state_loop(con);
00057 }
00058 
00059 drizzle_return_t drizzle_handshake_client_read(drizzle_con_st *con)
00060 {
00061   if (drizzle_state_none(con))
00062   {
00063     drizzle_state_push(con, drizzle_state_handshake_client_read);
00064     drizzle_state_push(con, drizzle_state_packet_read);
00065   }
00066 
00067   return drizzle_state_loop(con);
00068 }
00069 
00070 /*
00071  * State Definitions
00072  */
00073 
00074 drizzle_return_t drizzle_state_handshake_server_read(drizzle_con_st *con)
00075 {
00076   uint8_t *ptr;
00077 
00078   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_server_read");
00079 
00080   /* Assume the entire handshake packet will fit in the buffer. */
00081   if (con->buffer_size < con->packet_size)
00082   {
00083     drizzle_state_push(con, drizzle_state_read);
00084     return DRIZZLE_RETURN_OK;
00085   }
00086 
00087   if (con->packet_size < 46)
00088   {
00089     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00090                       "bad packet size:>=46:%zu", con->packet_size);
00091     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00092   }
00093 
00094   con->protocol_version= con->buffer_ptr[0];
00095   con->buffer_ptr++;
00096 
00097   if (con->protocol_version != 10)
00098   {
00099     /* This is a special case where the server determines that authentication
00100        will be impossible and denies any attempt right away. */
00101     if (con->protocol_version == 255)
00102     {
00103       drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00104                         "%.*s", (int32_t)con->packet_size - 3,
00105                         con->buffer_ptr + 2);
00106       return DRIZZLE_RETURN_AUTH_FAILED;
00107     }
00108 
00109     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00110                       "protocol version not supported:%d",
00111                       con->protocol_version);
00112     return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
00113   }
00114 
00115   /* Look for null-terminated server version string. */
00116   ptr= memchr(con->buffer_ptr, 0, con->buffer_size - 1);
00117   if (ptr == NULL)
00118   {
00119     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00120                       "server version string not found");
00121     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00122   }
00123 
00124   if (con->packet_size != (46 + (size_t)(ptr - con->buffer_ptr)))
00125   {
00126     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00127                       "bad packet size:%zu:%zu",
00128                       (46 + (size_t)(ptr - con->buffer_ptr)), con->packet_size);
00129     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00130   }
00131 
00132   strncpy(con->server_version, (char *)con->buffer_ptr,
00133           DRIZZLE_MAX_SERVER_VERSION_SIZE);
00134   con->server_version[DRIZZLE_MAX_SERVER_VERSION_SIZE - 1]= 0;
00135   con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
00136 
00137   con->thread_id= (uint32_t)drizzle_get_byte4(con->buffer_ptr);
00138   con->buffer_ptr+= 4;
00139 
00140   con->scramble= con->scramble_buffer;
00141   memcpy(con->scramble, con->buffer_ptr, 8);
00142   /* Skip scramble and filler. */
00143   con->buffer_ptr+= 9;
00144 
00145   /* Even though drizzle_capabilities is more than 2 bytes, the protocol only
00146      allows for 2. This means some capabilities are not possible during this
00147      handshake step. The options beyond 2 bytes are for client response only. */
00148   con->capabilities= (drizzle_capabilities_t)drizzle_get_byte2(con->buffer_ptr);
00149   con->buffer_ptr+= 2;
00150 
00151   if (con->options & DRIZZLE_CON_MYSQL &&
00152       !(con->capabilities & DRIZZLE_CAPABILITIES_PROTOCOL_41))
00153   {
00154     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00155                       "protocol version not supported, must be MySQL 4.1+");
00156     return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
00157   }
00158 
00159   con->charset= con->buffer_ptr[0];
00160   con->buffer_ptr+= 1;
00161 
00162   con->status= drizzle_get_byte2(con->buffer_ptr);
00163   /* Skip status and filler. */
00164   con->buffer_ptr+= 15;
00165 
00166   memcpy(con->scramble + 8, con->buffer_ptr, 12);
00167   con->buffer_ptr+= 13;
00168 
00169   con->buffer_size-= con->packet_size;
00170   if (con->buffer_size != 0)
00171   {
00172     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_read",
00173                       "unexpected data after packet:%zu", con->buffer_size);
00174     return DRIZZLE_RETURN_UNEXPECTED_DATA;
00175   }
00176 
00177   con->buffer_ptr= con->buffer;
00178 
00179   drizzle_state_pop(con);
00180 
00181   if (!(con->options & DRIZZLE_CON_RAW_PACKET))
00182   {
00183     drizzle_state_push(con, drizzle_state_handshake_result_read);
00184     drizzle_state_push(con, drizzle_state_packet_read);
00185     drizzle_state_push(con, drizzle_state_write);
00186     drizzle_state_push(con, drizzle_state_handshake_client_write);
00187   }
00188 
00189   return DRIZZLE_RETURN_OK;
00190 }
00191 
00192 drizzle_return_t drizzle_state_handshake_server_write(drizzle_con_st *con)
00193 {
00194   uint8_t *ptr;
00195 
00196   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_server_write");
00197 
00198   /* Calculate max packet size. */
00199   con->packet_size= 1   /* Protocol version */
00200                   + strlen(con->server_version) + 1
00201                   + 4   /* Thread ID */
00202                   + 8   /* Scramble */
00203                   + 1   /* NULL */
00204                   + 2   /* Capabilities */
00205                   + 1   /* Language */
00206                   + 2   /* Status */
00207                   + 13  /* Unused */
00208                   + 12  /* Scramble */
00209                   + 1;  /* NULL */
00210 
00211   /* Assume the entire handshake packet will fit in the buffer. */
00212   if ((con->packet_size + 4) > DRIZZLE_MAX_BUFFER_SIZE)
00213   {
00214     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_write",
00215                       "buffer too small:%zu", con->packet_size + 4);
00216     return DRIZZLE_RETURN_INTERNAL_ERROR;
00217   }
00218 
00219   ptr= con->buffer_ptr;
00220 
00221   /* Store packet size and packet number first. */
00222   drizzle_set_byte3(ptr, con->packet_size);
00223   ptr[3]= 0;
00224   con->packet_number= 1;
00225   ptr+= 4;
00226 
00227   ptr[0]= con->protocol_version;
00228   ptr++;
00229 
00230   memcpy(ptr, con->server_version, strlen(con->server_version));
00231   ptr+= strlen(con->server_version);
00232 
00233   ptr[0]= 0;
00234   ptr++;
00235 
00236   drizzle_set_byte4(ptr, con->thread_id);
00237   ptr+= 4;
00238 
00239   if (con->scramble == NULL)
00240     memset(ptr, 0, 8);
00241   else
00242     memcpy(ptr, con->scramble, 8);
00243   ptr+= 8;
00244 
00245   ptr[0]= 0;
00246   ptr++;
00247 
00248   if (con->options & DRIZZLE_CON_MYSQL)
00249     con->capabilities|= DRIZZLE_CAPABILITIES_PROTOCOL_41;
00250 
00251   /* We can only send two bytes worth, this is a protocol limitation. */
00252   drizzle_set_byte2(ptr, con->capabilities);
00253   ptr+= 2;
00254 
00255   ptr[0]= con->charset;
00256   ptr++;
00257 
00258   drizzle_set_byte2(ptr, con->status);
00259   ptr+= 2;
00260 
00261   memset(ptr, 0, 13);
00262   ptr+= 13;
00263 
00264   if (con->scramble == NULL)
00265     memset(ptr, 0, 12);
00266   else
00267     memcpy(ptr, con->scramble + 8, 12);
00268   ptr+= 12;
00269 
00270   ptr[0]= 0;
00271   ptr++;
00272 
00273   con->buffer_size+= (4 + con->packet_size);
00274 
00275   /* Make sure we packed it correctly. */
00276   if ((size_t)(ptr - con->buffer_ptr) != (4 + con->packet_size))
00277   {
00278     drizzle_set_error(con->drizzle, "drizzle_state_handshake_server_write",
00279                       "error packing server handshake:%zu:%zu",
00280                       (size_t)(ptr - con->buffer_ptr), 4 + con->packet_size);
00281     return DRIZZLE_RETURN_INTERNAL_ERROR;
00282   }
00283 
00284   drizzle_state_pop(con);
00285   return DRIZZLE_RETURN_OK;
00286 }
00287 
00288 drizzle_return_t drizzle_state_handshake_client_read(drizzle_con_st *con)
00289 {
00290   size_t real_size;
00291   uint8_t *ptr;
00292   uint8_t scramble_size;
00293 
00294   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_client_read");
00295 
00296   /* Assume the entire handshake packet will fit in the buffer. */
00297   if (con->buffer_size < con->packet_size)
00298   {
00299     drizzle_state_push(con, drizzle_state_read);
00300     return DRIZZLE_RETURN_OK;
00301   }
00302 
00303   /* This is the minimum packet size. */
00304   if (con->packet_size < 34)
00305   {
00306     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00307                       "bad packet size:>=34:%zu", con->packet_size);
00308     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00309   }
00310 
00311   real_size= 34;
00312 
00313   con->capabilities= drizzle_get_byte4(con->buffer_ptr);
00314   con->buffer_ptr+= 4;
00315 
00316   if (con->options & DRIZZLE_CON_MYSQL &&
00317       !(con->capabilities & DRIZZLE_CAPABILITIES_PROTOCOL_41))
00318   {
00319     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00320                       "protocol version not supported, must be MySQL 4.1+");
00321     return DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED;
00322   }
00323 
00324   con->max_packet_size= (uint32_t)drizzle_get_byte4(con->buffer_ptr);
00325   con->buffer_ptr+= 4;
00326 
00327   con->charset= con->buffer_ptr[0];
00328   con->buffer_ptr+= 1;
00329 
00330   /* Skip unused. */
00331   con->buffer_ptr+= 23;
00332 
00333   /* Look for null-terminated user string. */
00334   ptr= memchr(con->buffer_ptr, 0, con->buffer_size - 32);
00335   if (ptr == NULL)
00336   {
00337     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00338                       "user string not found");
00339     return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00340   }
00341 
00342   if (con->buffer_ptr == ptr)
00343   {
00344     con->user[0]= 0;
00345     con->buffer_ptr++;
00346   }
00347   else
00348   {
00349     real_size+= (size_t)(ptr - con->buffer_ptr);
00350     if (con->packet_size < real_size)
00351     {
00352       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00353                         "bad packet size:>=%zu:%zu", real_size,
00354                         con->packet_size);
00355       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00356     }
00357 
00358     strncpy(con->user, (char *)con->buffer_ptr, DRIZZLE_MAX_USER_SIZE);
00359     con->user[DRIZZLE_MAX_USER_SIZE - 1]= 0;
00360     con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
00361   }
00362 
00363   scramble_size= con->buffer_ptr[0];
00364   con->buffer_ptr+= 1;
00365 
00366   if (scramble_size == 0)
00367     con->scramble= NULL;
00368   else
00369   {
00370     if (scramble_size != DRIZZLE_MAX_SCRAMBLE_SIZE)
00371     {
00372       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00373                         "wrong scramble size");
00374       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00375     }
00376 
00377     real_size+= scramble_size;
00378     con->scramble= con->scramble_buffer;
00379     memcpy(con->scramble, con->buffer_ptr, DRIZZLE_MAX_SCRAMBLE_SIZE);
00380 
00381     con->buffer_ptr+= DRIZZLE_MAX_SCRAMBLE_SIZE;
00382   }
00383 
00384   /* Look for null-terminated db string. */
00385   if ((34 + strlen(con->user) + scramble_size) == con->packet_size)
00386     con->db[0]= 0;
00387   else
00388   {
00389     ptr= memchr(con->buffer_ptr, 0, con->buffer_size -
00390                                     (34 + strlen(con->user) + scramble_size));
00391     if (ptr == NULL)
00392     {
00393       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00394                         "db string not found");
00395       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00396     }
00397 
00398     real_size+= ((size_t)(ptr - con->buffer_ptr) + 1);
00399     if (con->packet_size != real_size)
00400     {
00401       drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00402                         "bad packet size:%zu:%zu", real_size, con->packet_size);
00403       return DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET;
00404     }
00405 
00406     if (con->buffer_ptr == ptr)
00407     {
00408       con->db[0]= 0;
00409       con->buffer_ptr++;
00410     }
00411     else
00412     {
00413       strncpy(con->db, (char *)con->buffer_ptr, DRIZZLE_MAX_DB_SIZE);
00414       con->db[DRIZZLE_MAX_DB_SIZE - 1]= 0;
00415       con->buffer_ptr+= ((ptr - con->buffer_ptr) + 1);
00416     }
00417   }
00418 
00419   con->buffer_size-= con->packet_size;
00420   if (con->buffer_size != 0)
00421   {
00422     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_read",
00423                       "unexpected data after packet:%zu", con->buffer_size);
00424     return DRIZZLE_RETURN_UNEXPECTED_DATA;
00425   }
00426 
00427   con->buffer_ptr= con->buffer;
00428 
00429   drizzle_state_pop(con);
00430   return DRIZZLE_RETURN_OK;
00431 }
00432 
00433 drizzle_return_t drizzle_state_handshake_client_write(drizzle_con_st *con)
00434 {
00435   uint8_t *ptr;
00436   drizzle_capabilities_t capabilities;
00437   drizzle_return_t ret;
00438 
00439   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_client_write");
00440 
00441   /* Calculate max packet size. */
00442   con->packet_size= 4   /* Capabilities */
00443                   + 4   /* Max packet size */
00444                   + 1   /* Charset */
00445                   + 23  /* Unused */
00446                   + strlen(con->user) + 1
00447                   + 1   /* Scramble size */
00448                   + DRIZZLE_MAX_SCRAMBLE_SIZE
00449                   + strlen(con->db) + 1;
00450 
00451   /* Assume the entire handshake packet will fit in the buffer. */
00452   if ((con->packet_size + 4) > DRIZZLE_MAX_BUFFER_SIZE)
00453   {
00454     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_write",
00455                       "buffer too small:%zu", con->packet_size + 4);
00456     return DRIZZLE_RETURN_INTERNAL_ERROR;
00457   }
00458 
00459   ptr= con->buffer_ptr;
00460 
00461   /* Store packet size at the end since it may change. */
00462   ptr[3]= con->packet_number;
00463   con->packet_number++;
00464   ptr+= 4;
00465 
00466   if (con->options & DRIZZLE_CON_MYSQL)
00467     con->capabilities|= DRIZZLE_CAPABILITIES_PROTOCOL_41;
00468 
00469   capabilities= con->capabilities & DRIZZLE_CAPABILITIES_CLIENT;
00470   capabilities&= ~(DRIZZLE_CAPABILITIES_COMPRESS | DRIZZLE_CAPABILITIES_SSL);
00471   if (con->db[0] == 0)
00472     capabilities&= ~DRIZZLE_CAPABILITIES_CONNECT_WITH_DB;
00473 
00474   drizzle_set_byte4(ptr, capabilities);
00475   ptr+= 4;
00476 
00477   drizzle_set_byte4(ptr, con->max_packet_size);
00478   ptr+= 4;
00479 
00480   ptr[0]= con->charset;
00481   ptr++;
00482 
00483   memset(ptr, 0, 23);
00484   ptr+= 23;
00485 
00486   ptr= drizzle_pack_auth(con, ptr, &ret);
00487   if (ret != DRIZZLE_RETURN_OK)
00488     return ret;
00489 
00490   con->buffer_size+= (4 + con->packet_size);
00491 
00492   /* Make sure we packed it correctly. */
00493   if ((size_t)(ptr - con->buffer_ptr) != (4 + con->packet_size))
00494   {
00495     drizzle_set_error(con->drizzle, "drizzle_state_handshake_client_write",
00496                       "error packing client handshake:%zu:%zu",
00497                       (size_t)(ptr - con->buffer_ptr), 4 + con->packet_size);
00498     return DRIZZLE_RETURN_INTERNAL_ERROR;
00499   }
00500 
00501   /* Store packet size now. */
00502   drizzle_set_byte3(con->buffer_ptr, con->packet_size);
00503 
00504   drizzle_state_pop(con);
00505   return DRIZZLE_RETURN_OK;
00506 }
00507 
00508 drizzle_return_t drizzle_state_handshake_result_read(drizzle_con_st *con)
00509 {
00510   drizzle_return_t ret;
00511   drizzle_result_st result;
00512 
00513   drizzle_log_debug(con->drizzle, "drizzle_state_handshake_result_read");
00514 
00515   if (drizzle_result_create(con, &result) == NULL)
00516     return DRIZZLE_RETURN_MEMORY;
00517 
00518   con->result= &result;
00519 
00520   ret= drizzle_state_result_read(con);
00521   if (drizzle_state_none(con))
00522   {
00523     if (ret == DRIZZLE_RETURN_OK)
00524     {
00525       if (drizzle_result_eof(&result))
00526       {
00527         drizzle_set_error(con->drizzle, "drizzle_state_handshake_result_read",
00528                          "old insecure authentication mechanism not supported");
00529         ret= DRIZZLE_RETURN_AUTH_FAILED;
00530       }
00531       else
00532         con->options|= DRIZZLE_CON_READY;
00533     }
00534   }
00535 
00536   drizzle_result_free(&result);
00537 
00538   if (ret == DRIZZLE_RETURN_ERROR_CODE)
00539     return DRIZZLE_RETURN_HANDSHAKE_FAILED;
00540 
00541   return ret;
00542 }