static PyObject * python_password_scramble(PyObject *self, PyObject *args){ size_t challenge_len; const char *challenge; size_t hashed_password_len; const char *hashed_password; if(!PyArg_ParseTuple(args, "s#s#", &challenge, &challenge_len, &hashed_password, &hashed_password_len)) return NULL; GString *response; response = g_string_new(NULL); network_mysqld_proto_password_scramble(response, challenge, challenge_len, hashed_password, hashed_password_len); PyObject *result = PyString_FromStringAndSize(S(response)); g_string_free(response, TRUE); return result; }
void test_mysqld_auth_with_pw(void) { const char raw_packet[] = ":\0\0\1" "\205\246\3\0" "\0\0\0\1" "\10" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "root\0" "\24\241\304\260>\255\1:F,\256\337K\323\340\4\273\354I\256\204" ; const char raw_challenge[] = "%@R[SoWC" /* part 1 */ "+L|LG_+R={tV"; /* part 2 */ GString *packet, *challenge, *hashed_password; network_mysqld_auth_response *auth; auth = network_mysqld_auth_response_new(); g_string_assign(auth->username, "root"); auth->capabilities = CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES | CLIENT_PROTOCOL_41 | CLIENT_INTERACTIVE | CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION | CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS; auth->max_packet_size = 1 << 24; auth->charset = 8; challenge = g_string_new(NULL); hashed_password = g_string_new(NULL); g_string_append_len(challenge, raw_challenge, sizeof(raw_challenge) - 1); network_mysqld_proto_password_hash(hashed_password, C("123")); network_mysqld_proto_password_scramble(auth->response, S(challenge), S(hashed_password)); g_string_free(hashed_password, TRUE); packet = g_string_new(NULL); network_mysqld_proto_append_int8(packet, 58); network_mysqld_proto_append_int8(packet, 0); network_mysqld_proto_append_int8(packet, 0); network_mysqld_proto_append_int8(packet, 1); g_assert(0 == network_mysqld_proto_append_auth_response(packet, auth)); g_assert(packet->len == sizeof(raw_packet) - 1); #if 0 for (i = 0; i < packet->len; i++) { g_message("%s: [%d] %02x %c= %02x", G_STRLOC, i, packet->str[i], packet->str[i] == raw_packet[i] ? '=' : '!', raw_packet[i]); } #endif g_assert(0 == memcmp(packet->str, raw_packet, sizeof(raw_packet) - 1)); network_mysqld_auth_response_free(auth); g_string_free(packet, TRUE); g_string_free(challenge, TRUE); }
network_socket *self_connect(network_mysqld_con *con, network_backend_t *backend, GHashTable *pwd_table) { /*make sure that the max conn for the backend is no more than the config number *when max_conn_for_a_backend is no more than 0, there is no limitation for max connection for a backend; * */ if (con->srv->max_conn_for_a_backend > 0 && backend->connected_clients >= con->srv->max_conn_for_a_backend) { g_critical("%s.%d: self_connect:%08x's connected_clients is %d, which are too many!",__FILE__, __LINE__, backend, backend->connected_clients); return NULL; } //1. connect DB network_socket *sock = network_socket_new(); network_address_copy(sock->dst, backend->addr); if (-1 == (sock->fd = socket(sock->dst->addr.common.sa_family, sock->socket_type, 0))) { g_critical("%s.%d: socket(%s) failed: %s (%d)", __FILE__, __LINE__, sock->dst->name->str, g_strerror(errno), errno); network_socket_free(sock); return NULL; } if (-1 == (connect(sock->fd, &sock->dst->addr.common, sock->dst->len))) { g_message("%s.%d: connecting to backend (%s) failed, marking it as down for ...", __FILE__, __LINE__, sock->dst->name->str); network_socket_free(sock); if (backend->state != BACKEND_STATE_OFFLINE) backend->state = BACKEND_STATE_DOWN; return NULL; } //2. read handshake,重点是获取20个字节的随机串 off_t to_read = NET_HEADER_SIZE; guint offset = 0; guchar header[NET_HEADER_SIZE]; while (to_read > 0) { gssize len = recv(sock->fd, header + offset, to_read, 0); if (len == -1 || len == 0) { network_socket_free(sock); return NULL; } offset += len; to_read -= len; } to_read = header[0] + (header[1] << 8) + (header[2] << 16); offset = 0; GString *data = g_string_sized_new(to_read); while (to_read > 0) { gssize len = recv(sock->fd, data->str + offset, to_read, 0); if (len == -1 || len == 0) { network_socket_free(sock); g_string_free(data, TRUE); return NULL; } offset += len; to_read -= len; } data->len = offset; network_packet packet; packet.data = data; packet.offset = 0; network_mysqld_auth_challenge *challenge = network_mysqld_auth_challenge_new(); network_mysqld_proto_get_auth_challenge(&packet, challenge); //3. 生成response GString *response = g_string_sized_new(20); GString *hashed_password = g_hash_table_lookup(pwd_table, con->client->response->username->str); if (hashed_password) { network_mysqld_proto_password_scramble(response, S(challenge->challenge), S(hashed_password)); } else { network_socket_free(sock); g_string_free(data, TRUE); network_mysqld_auth_challenge_free(challenge); g_string_free(response, TRUE); return NULL; } //4. send auth off_t to_write = 58 + con->client->response->username->len; offset = 0; g_string_truncate(data, 0); char tmp[] = {to_write - 4, 0, 0, 1, 0x85, 0xa6, 3, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; g_string_append_len(data, tmp, 36); g_string_append_len(data, con->client->response->username->str, con->client->response->username->len); g_string_append_len(data, "\0\x14", 2); g_string_append_len(data, response->str, 20); g_string_free(response, TRUE); while (to_write > 0) { gssize len = send(sock->fd, data->str + offset, to_write, 0); if (len == -1) { network_socket_free(sock); g_string_free(data, TRUE); network_mysqld_auth_challenge_free(challenge); return NULL; } offset += len; to_write -= len; } //5. read auth result to_read = NET_HEADER_SIZE; offset = 0; while (to_read > 0) { gssize len = recv(sock->fd, header + offset, to_read, 0); if (len == -1 || len == 0) { network_socket_free(sock); g_string_free(data, TRUE); network_mysqld_auth_challenge_free(challenge); return NULL; } offset += len; to_read -= len; } to_read = header[0] + (header[1] << 8) + (header[2] << 16); offset = 0; g_string_truncate(data, 0); g_string_set_size(data, to_read); while (to_read > 0) { gssize len = recv(sock->fd, data->str + offset, to_read, 0); if (len == -1 || len == 0) { network_socket_free(sock); g_string_free(data, TRUE); network_mysqld_auth_challenge_free(challenge); return NULL; } offset += len; to_read -= len; } data->len = offset; if (data->str[0] != MYSQLD_PACKET_OK) { network_socket_free(sock); g_string_free(data, TRUE); network_mysqld_auth_challenge_free(challenge); return NULL; } g_string_free(data, TRUE); //6. set non-block network_socket_set_non_blocking(sock); network_socket_connect_setopts(sock); //此句是否需要?是否应该放在第1步末尾? sock->challenge = challenge; sock->response = network_mysqld_auth_response_copy(con->client->response); g_atomic_int_inc(&backend->connected_clients); return sock; }