bool cuev_emit(struct curlev *cuev, char *url, struct curl_slist *headers, curlev_cb_t cb, void *cb_data) { struct curlex *ex = NULL; CURL *easy = curl_easy_init(); CURLMcode code; if (!easy) { xsyslog(LOG_WARNING, "curl_easy_init() failed for url '%s' (errno: %d)", url, errno); return false; } curl_easy_setopt(easy, CURLOPT_URL, url); curl_easy_setopt(easy, CURLOPT_VERBOSE, 0L); curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, _curl_write_cb); if (cb) { ex = calloc(1, sizeof(*ex)); if (!ex) { xsyslog(LOG_WARNING, "cuev_emit() failed: calloc(%d) with errno: %d", (int)sizeof(*ex), errno); curl_easy_cleanup(easy); return false; } ex->cb = cb; ex->cb_data = cb_data; curl_easy_setopt(easy, CURLOPT_WRITEDATA, ex); curl_easy_setopt(easy, CURLOPT_PRIVATE, ex); } else { curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL); curl_easy_setopt(easy, CURLOPT_PRIVATE, NULL); } if (headers) { /* TODO: добавить хидеры */ } if ((code = curl_multi_add_handle(cuev->multi, easy)) != CURLM_OK) { xsyslog(LOG_WARNING, "curl_multi_add_handle() failed for url '%s', code: %d", url, code); curl_easy_cleanup(easy); return false; } return true; }
static void _ev_timer_curl_cb(struct ev_loop *loop, struct ev_timer *w, int revents) { int running = 0; struct curlev *cuev = w->data; curl_multi_socket_action(cuev->multi, CURL_SOCKET_TIMEOUT, 0, &running); xsyslog(LOG_INFO, "ev timer: %p, running: %d", (void*)cuev, running); }
static int _curl_socket_cb(CURL *e, curl_socket_t s, int what, struct curlev *cuev, struct ev_io *evio) { xsyslog(LOG_DEBUG, "socket #%d action: %d, evio %p", s, what, (void*)evio); /* события curl с сокетами (what): * CURL_POLL_NONE: ничего * CURL_POLL_REMOVE: удаление * CURL_POLL_IN: регистрирация для чтения * CURL_POLL_OUT: регистрация для записи * CURL_POLL_INOUT: регистрация для чтения и записи * * до вызова curl_multi_assign() аргумент evio == NULL */ if (what == CURL_POLL_REMOVE) { xsyslog(LOG_DEBUG, "curl remove socket #%d", s); if (!evio) { xsyslog(LOG_WARNING, "curl: no data to remove"); return 0; } ev_io_stop(cuev->loop, evio); free(evio); } else { int eva = (what & CURL_POLL_IN ? EV_READ : 0) | (what & CURL_POLL_OUT ? EV_WRITE : 0); if (!evio) { xsyslog(LOG_DEBUG, "curl alloc socket #%d (action: %d)", s, eva); /* не проциниализировано */ evio = calloc(1, sizeof(struct ev_io)); evio->data = cuev; ev_io_init(evio, _ev_event_curl_cb, s, eva); ev_io_start(cuev->loop, evio); curl_multi_assign(cuev->multi, s, evio); } else { xsyslog(LOG_DEBUG, "curl update socket #%d (action: %d)", s, eva); /* изменение состояния */ ev_io_stop(cuev->loop, evio); ev_io_set(evio, s, eva); ev_io_start(cuev->loop, evio); } } return 0; }
/* find the address of (MPVIO_EXT *)->scramble (the random string sent to the client for "mysql_native_password" authentication) * * see sql/sql_acl.cc native_password_authenticate() for the layout of `struct MPVIO_EXT' * for mysql57, see sql/auth/sql_authentication.h * * [0] : MYSQL_PLUGIN_VIO sizeof() == 24 bytes * * we could hardcode the offset somewhere. ideally, we should have an API to query if from `sql_acl.cc' */ void _find_addr_scramble(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info, struct auth_flex_data *d_flex_data) { void **addr_scramble = NULL; int i; for (i = 30; i < 50; ++i) { void *addr = ((void *)&vio[1]) + sizeof(*info) + 4 * i; DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : compare at %p (* -> %p) (diff %ld)", __func__, addr, *(void **)addr, (long int)(addr - (void *)vio)); /* addr_scramble should be +/- sizeof(void *) depending on if `info->host_or_ip' is an host or an ip ... * we will figure if it is an host or an ip later below. */ if (*(void **)addr == info->host_or_ip) { DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : found info->host_or_ip (%s) at %p!", __func__, info->host_or_ip, addr); /* assume info->host_or_ip == (MPVIO_EXT *)->host */ { /* be careful..., arithmetic on a (void **) would move 4x faster than on a (void *)... */ addr_scramble = (void **)(((void *)addr) - 4 * 16); } if (strspn(info->host_or_ip, "0123456789.") == strlen(info->host_or_ip)) { DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : info->host_or_ip is an ip, fix !", __func__); /* info->host_or_ip was info->ip instead of info->host */ { /* (void **) arithmetic... move 4 bytes indeed... */ addr_scramble += 1; } } DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : scramble should be at %p (diff %ld)", __func__, *(char **)addr_scramble, (long int)((void *)addr_scramble - (void *)vio)); DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : scramble [%ld] `%s'", __func__, (long int)strlen(*(char **)addr_scramble), *(char **)addr_scramble); break; } } d_flex_data->addr_scramble_ptr = addr_scramble; d_flex_data->addr_scramble = *d_flex_data->addr_scramble_ptr; }
static int MS_CALLBACK slg_write(BIO *b, const char *in, int inl) { int ret= inl; char* buf; char* pp; int priority, i; static struct { int strl; char str[10]; int log_level; } mapping[] = { { 6, "PANIC ", LOG_EMERG }, { 6, "EMERG ", LOG_EMERG }, { 4, "EMR ", LOG_EMERG }, { 6, "ALERT ", LOG_ALERT }, { 4, "ALR ", LOG_ALERT }, { 5, "CRIT ", LOG_CRIT }, { 4, "CRI ", LOG_CRIT }, { 6, "ERROR ", LOG_ERR }, { 4, "ERR ", LOG_ERR }, { 8, "WARNING ", LOG_WARNING }, { 5, "WARN ", LOG_WARNING }, { 4, "WAR ", LOG_WARNING }, { 7, "NOTICE ", LOG_NOTICE }, { 5, "NOTE ", LOG_NOTICE }, { 4, "NOT ", LOG_NOTICE }, { 5, "INFO ", LOG_INFO }, { 4, "INF ", LOG_INFO }, { 6, "DEBUG ", LOG_DEBUG }, { 4, "DBG ", LOG_DEBUG }, { 0, "", LOG_ERR } /* The default */ }; if((buf= (char *)OPENSSL_malloc(inl+ 1)) == NULL){ return(0); } strncpy(buf, in, inl); buf[inl]= '\0'; i = 0; while(strncmp(buf, mapping[i].str, mapping[i].strl) != 0) i++; priority = mapping[i].log_level; pp = buf + mapping[i].strl; xsyslog(b, priority, pp); OPENSSL_free(buf); return(ret); }
static int _curl_timer_cb(CURLM *multi, long timeout_ms, struct curlev *cuev) { if (cuev->timered) { ev_timer_stop(cuev->loop, &cuev->timer); cuev->timered = false; } xsyslog(LOG_INFO, "curl update timer to %ldms", timeout_ms); if (timeout_ms > 0) { double _t = timeout_ms / 1000.0; ev_timer_set(&cuev->timer, _t, 0.); ev_timer_start(cuev->loop, &cuev->timer); cuev->timered = true; } else { _ev_timer_curl_cb(cuev->loop, &cuev->timer, 0); } return 0; }
/* find the address of (MPVIO_EXT *)->client_capabilities * * we use it to known if the client support CLIENT_PLUGIN_AUTH (change user plugin to "mysql_clear_password" request) */ void _find_addr_client_capabilities(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info, struct auth_flex_data *d_flex_data) { ulong client_capabilities; void **addr_client_capabilities_ptr; /* be careful..., arithmetic on a (void **) would move 4x faster than on a (void *)... */ #if DBMS_mysql < 57 addr_client_capabilities_ptr = ((void *)d_flex_data->addr_scramble_ptr) - sizeof(void *); #else /* DBMS_mysql < 57 */ /* mysql 5.7 use a c++ class to hold client_capabilities, so we will have to use advanced black magic. * * http://stackoverflow.com/questions/12378271/what-does-an-object-look-like-in-memory/12378515#12378515 * * As you suspect, the data members (fields) are laid out sequentially. * This also includes the fields of base classes. * * If the class (or one of its base classes) contain any virtual methods, the layout typically starts * with a vptr, i.e a pointer to a virtual table (or vtable) which is a table of pointers to function * implementations related to that class. * * Please note that this is not defined by standard, but AFAIK all current compilers use this approach. * Also, with multiple inheritance it gets more hairy, so let's ignore it for the moment. * * +-----------+ * | vptr | pointer to vtable which is located elsewhere * +-----------+ * | fieldA | first member * | fieldB | ... * | fieldC | * | ... | * +-----------+ * */ void **addr_protocol_classic_ptr = ((void *)d_flex_data->addr_scramble_ptr) + 5 * sizeof(void *); addr_client_capabilities_ptr = *addr_protocol_classic_ptr + sizeof(void *); #endif /* DBMS_mysql < 57 */ client_capabilities = *(ulong *)addr_client_capabilities_ptr; DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : client_capabilities %ld (CLIENT_PLUGIN_AUTH : %d)", __func__, client_capabilities, client_capabilities & CLIENT_PLUGIN_AUTH ? 1 : 0); d_flex_data->client_capabilities = client_capabilities; }
static void _ev_event_curl_cb(struct ev_loop *loop, struct ev_io *w, int revents) { struct curlev *cuev = w->data; CURLMcode rc = 0; int curla = (revents & EV_READ ? CURL_POLL_IN : 0) | ( revents & EV_WRITE ? CURL_POLL_OUT : 0); int running = 0; if ((rc = curl_multi_socket_action(cuev->multi, w->fd, curla, &running)) != CURLM_OK) { xsyslog(LOG_WARNING, "curl_multi_socket_action() error: rc=%d", rc); return; } /* TODO: ? */ /* проверка состояния */ { int _msgs = 0; CURLMsg *_msg = NULL; CURL *_easy = NULL; struct curlex *_ex = NULL; while ((_msg = curl_multi_info_read(cuev->multi, &_msgs)) != NULL) { if (_msg->msg == CURLMSG_DONE) { _easy = _msg->easy_handle; /* освбождение всякого мусора */ if (!curl_easy_getinfo(_easy, CURLINFO_PRIVATE, &_ex) && _ex) { if (_ex->cb) { /* финализация записи */ _ex->cb(NULL, 0ul, _ex->cb_data); } free(_ex); } curl_multi_remove_handle(cuev->multi, _easy); curl_easy_cleanup(_easy); } } /* while */ } }
/* ask the client to "change plugin", so that it will send us the password in cleartext... * * HELP: * - http://lists.mysql.com/commits/136992 * - http://bugs.mysql.com/bug.php?id=57442 * - wireshark... */ void flex_change_plugin_to_cleartext(MYSQL_PLUGIN_VIO *vio, struct auth_flex_data *d_auth_flex_data) { char * __attribute__ ((unused)) pkt_change_plugin = "mysql_clear_password"; #if DBMS_mysql < 57 void **addr_net_ptr = NULL; /* we cannot call vio->write_packet() because send_plugin_request_packet() would concatenate * and send the current auth pluging + the requested one (\254 + "mysql_native_password\0" + "mysql_clear_password\0") * so, below we send the "plugin change" request ourselves on the wire. (\254 + "mysql_clear_password\0") */ { /* be careful..., arithmetic on a (void **) would move 4x faster than on a (void *)... */ addr_net_ptr = (void *)(((void *)d_auth_flex_data->addr_scramble_ptr) + sizeof(void *) * 4 + sizeof(ulong)); } DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : addr_scramble/%p addr_net/%p (diff %ld)", __func__, d_auth_flex_data->addr_scramble_ptr, addr_net_ptr, (long int)(addr_net_ptr - d_auth_flex_data->addr_scramble_ptr)); net_write_command(*addr_net_ptr, 254, pkt_change_plugin, strlen(pkt_change_plugin) + 1, "", 0); #else /* DBMS_mysql < 57 */ /* for mysql 5.7, things get a bit hairy. * * we have to simulate a MYSQL_PLUGIN_VIO struct, fill in some interesting fields * and send it back to our mysql client so it can retry auth with desired plugin. * * don't do this at home, kids. * */ struct MYSQL_PLUGIN_VIO_FAKE { MYSQL_PLUGIN_VIO plugin_vio; MYSQL_SERVER_AUTH_INFO auth_info; void *acl_user; struct st_plugin_int { LEX_STRING name; struct st_mysql_plugin *plugin; } *plugin; LEX_STRING db; struct { char *plugin, *pkt; uint pkt_len; } cached_client_reply; struct { char *pkt; uint pkt_len; } cached_server_packet; int packets_read, packets_written; enum { SUCCESS, FAILURE, RESTART } status; }; struct MYSQL_PLUGIN_VIO_FAKE *vio_fake = (struct MYSQL_PLUGIN_VIO_FAKE *)vio; int old_status = vio_fake->status; vio_fake->status = RESTART; struct st_mysql_auth tmp_auth = {0}; tmp_auth.client_auth_plugin = pkt_change_plugin; struct st_mysql_plugin tmp_plugin = {0}; tmp_plugin.info = &tmp_auth; struct st_plugin_int tmp_int = {0}; tmp_int.plugin = &tmp_plugin; void *old_plugin = vio_fake->plugin; vio_fake->plugin = &tmp_int; DEBUG xsyslog(LOG_LOCAL7 | LOG_NOTICE, "%s : packets read/written %d/%d", __func__, vio_fake->packets_read, vio_fake->packets_written); vio->write_packet(vio, "", 0); /* This will somehow trigger the CHANGE_PLUGIN procedure... */ vio_fake->plugin = old_plugin; vio_fake->status = old_status; #endif /* DBMS_mysql < 57 */ }