diff --git a/src/factory/HttpTaskImpl.cc b/src/factory/HttpTaskImpl.cc index 94054864..474bb12a 100644 --- a/src/factory/HttpTaskImpl.cc +++ b/src/factory/HttpTaskImpl.cc @@ -550,10 +550,7 @@ CommMessageIn *ComplexHttpProxyTask::message_in() return get_ssl_handshaker(); auto *msg = (ProtocolMessage *)this->ComplexHttpTask::message_in(); - if (is_ssl_) - return get_ssl_wrapper(msg); - - return msg; + return is_ssl_ ? get_ssl_wrapper(msg) : msg; } int ComplexHttpProxyTask::keep_alive_timeout() diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index f3195095..63d57174 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -13,8 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. - Authors: Wu Jiaxu (wujiaxu@sogou-inc.com) - Xie Han (xiehan@sogou-inc.com) + Authors: Xie Han (xiehan@sogou-inc.com) + Wu Jiaxu (wujiaxu@sogou-inc.com) Li Yingxin (liyingxin@sogou-inc.com) */ @@ -68,6 +68,11 @@ private: { ST_SSL_REQUEST, ST_AUTH_REQUEST, + ST_AUTH_SWITCH_REQUEST, + ST_CLEAR_PASSWORD_REQUEST, + ST_SHA256_PUBLIC_KEY_REQUEST, + ST_CSHA2_PUBLIC_KEY_REQUEST, + ST_RSA_AUTH_REQUEST, ST_CHARSET_REQUEST, ST_FIRST_USER_REQUEST, ST_USER_REQUEST @@ -75,9 +80,10 @@ private: struct MyConnection : public WFConnection { - char challenge[20]; - unsigned char mysql_seqid; + std::string str; // shared by auth, auth_swich and rsa_auth requests + unsigned char seed[20]; enum ConnState state; + unsigned char mysql_seqid; SSL *ssl; SSLWrapper wrapper; MyConnection(SSL *ssl) : wrapper(&wrapper, ssl) @@ -86,19 +92,8 @@ private: } }; - int check_handshake(); - - SSL *get_ssl() const - { - return ((MyConnection *)this->get_connection())->ssl; - } - - SSLWrapper *get_ssl_wrapper(ProtocolMessage *msg) const - { - auto *conn = (MyConnection *)this->get_connection(); - conn->wrapper = SSLWrapper(msg, conn->ssl); - return &conn->wrapper; - } + int check_handshake(MySQLHandshakeResponse *resp); + int auth_switch(MySQLAuthResponse *resp, MyConnection *conn); struct MySSLWrapper : public SSLWrapper { @@ -184,6 +179,8 @@ static SSL *__create_ssl(SSL_CTX *ssl_ctx) CommMessageOut *ComplexMySQLTask::message_out() { + MySQLAuthSwitchRequest *auth_switch_req; + MySQLRSAAuthRequest *rsa_auth_req; MySQLAuthRequest *auth_req; MySQLRequest *req; @@ -195,16 +192,44 @@ CommMessageOut *ComplexMySQLTask::message_out() switch (conn->state) { case ST_SSL_REQUEST: - req = new MySQLSSLRequest(character_set_, get_ssl()); - req->set_seqid(conn->mysql_seqid++); + req = new MySQLSSLRequest(character_set_, conn->ssl); + req->set_seqid(conn->mysql_seqid); return req; case ST_AUTH_REQUEST: req = new MySQLAuthRequest; auth_req = (MySQLAuthRequest *)req; - auth_req->set_challenge(conn->challenge); auth_req->set_auth(username_, password_, db_, character_set_); - req->set_seqid(conn->mysql_seqid++); + auth_req->set_auth_plugin_name(std::move(conn->str)); + auth_req->set_seed(conn->seed); + break; + + case ST_CLEAR_PASSWORD_REQUEST: + conn->str = "mysql_clear_password"; + case ST_AUTH_SWITCH_REQUEST: + req = new MySQLAuthSwitchRequest; + auth_switch_req = (MySQLAuthSwitchRequest *)req; + auth_switch_req->set_password(password_); + auth_switch_req->set_auth_plugin_name(std::move(conn->str)); + auth_switch_req->set_seed(conn->seed); + break; + + case ST_SHA256_PUBLIC_KEY_REQUEST: + req = new MySQLPublicKeyRequest; + ((MySQLPublicKeyRequest *)req)->set_sha256(); + break; + + case ST_CSHA2_PUBLIC_KEY_REQUEST: + req = new MySQLPublicKeyRequest; + ((MySQLPublicKeyRequest *)req)->set_caching_sha2(); + break; + + case ST_RSA_AUTH_REQUEST: + req = new MySQLRSAAuthRequest; + rsa_auth_req = (MySQLRSAAuthRequest *)req; + rsa_auth_req->set_password(password_); + rsa_auth_req->set_public_key(std::move(conn->str)); + rsa_auth_req->set_seed(conn->seed); break; case ST_CHARSET_REQUEST: @@ -238,13 +263,19 @@ CommMessageOut *ComplexMySQLTask::message_out() return NULL; } + if (!is_user_request_ && conn->state != ST_CHARSET_REQUEST) + req->set_seqid(conn->mysql_seqid); + if (!is_ssl_) return req; if (is_user_request_) - return get_ssl_wrapper(req); + { + conn->wrapper = SSLWrapper(req, conn->ssl); + return &conn->wrapper; + } else - return new MySSLWrapper(req, get_ssl()); + return new MySSLWrapper(req, conn->ssl); } CommMessageIn *ComplexMySQLTask::message_in() @@ -258,12 +289,23 @@ CommMessageIn *ComplexMySQLTask::message_in() switch (conn->state) { case ST_SSL_REQUEST: - return new SSLHandshaker(get_ssl()); + return new SSLHandshaker(conn->ssl); case ST_AUTH_REQUEST: + case ST_AUTH_SWITCH_REQUEST: resp = new MySQLAuthResponse; break; + case ST_CLEAR_PASSWORD_REQUEST: + case ST_RSA_AUTH_REQUEST: + resp = new MySQLResponse; + break; + + case ST_SHA256_PUBLIC_KEY_REQUEST: + case ST_CSHA2_PUBLIC_KEY_REQUEST: + resp = new MySQLPublicKeyResponse; + break; + case ST_CHARSET_REQUEST: resp = new MySQLResponse; break; @@ -282,14 +324,16 @@ CommMessageIn *ComplexMySQLTask::message_in() return resp; if (is_user_request_) - return get_ssl_wrapper(resp); + { + conn->wrapper = SSLWrapper(resp, conn->ssl); + return &conn->wrapper; + } else - return new MySSLWrapper(resp, get_ssl()); + return new MySSLWrapper(resp, conn->ssl); } -int ComplexMySQLTask::check_handshake() +int ComplexMySQLTask::check_handshake(MySQLHandshakeResponse *resp) { - auto *resp = (MySQLHandshakeResponse *)this->get_message_in(); SSL *ssl = NULL; if (resp->host_disallowed()) @@ -324,9 +368,13 @@ int ComplexMySQLTask::check_handshake() auto *conn = this->get_connection(); auto *my_conn = new MyConnection(ssl); - my_conn->mysql_seqid = resp->get_seqid() + 1; - resp->get_challenge(my_conn->challenge); + my_conn->str = resp->get_auth_plugin_name(); + if (my_conn->str == "sha256_password") + my_conn->str = "caching_sha2_password"; + + resp->get_seed(my_conn->seed); my_conn->state = is_ssl_ ? ST_SSL_REQUEST : ST_AUTH_REQUEST; + my_conn->mysql_seqid = resp->get_seqid() + 1; conn->set_context(my_conn, [](void *ctx) { auto *my_conn = (MyConnection *)ctx; if (my_conn->ssl) @@ -337,16 +385,54 @@ int ComplexMySQLTask::check_handshake() return MYSQL_KEEPALIVE_DEFAULT; } +int ComplexMySQLTask::auth_switch(MySQLAuthResponse *resp, MyConnection *conn) +{ + std::string name = resp->get_auth_plugin_name(); + + if (conn->state != ST_AUTH_REQUEST || + (name == "mysql_clear_password" && !is_ssl_)) + { + state_ = WFT_STATE_SYS_ERROR; + error_ = EBADMSG; + return 0; + } + + if (name == "sha256_password") + { + if (is_ssl_) + conn->state = ST_CLEAR_PASSWORD_REQUEST; + else + conn->state = ST_SHA256_PUBLIC_KEY_REQUEST; + } + else + { + conn->str = std::move(name); + conn->state = ST_AUTH_SWITCH_REQUEST; + } + + resp->get_seed(conn->seed); + conn->mysql_seqid = resp->get_seqid() + 1; + return MYSQL_KEEPALIVE_DEFAULT; +} + int ComplexMySQLTask::keep_alive_timeout() { + auto *msg = (ProtocolMessage *)this->get_message_in(); + MySQLAuthResponse *auth_resp; + MySQLResponse *resp; + state_ = WFT_STATE_SUCCESS; error_ = 0; if (this->get_seq() == 0) - return check_handshake(); + return check_handshake((MySQLHandshakeResponse *)msg); auto *conn = (MyConnection *)this->get_connection(); - auto *msg = (ProtocolMessage *)this->get_message_in(); - MySQLResponse *resp; + if (conn->state == ST_SSL_REQUEST) + { + conn->state = ST_AUTH_REQUEST; + conn->mysql_seqid++; + return MYSQL_KEEPALIVE_DEFAULT; + } if (is_ssl_) resp = (MySQLResponse *)((MySSLWrapper *)msg)->get_msg(); @@ -355,12 +441,23 @@ int ComplexMySQLTask::keep_alive_timeout() switch (conn->state) { - case ST_SSL_REQUEST: - conn->state = ST_AUTH_REQUEST; - break; - case ST_AUTH_REQUEST: - if (!resp->is_ok_packet()) + case ST_AUTH_SWITCH_REQUEST: + case ST_CLEAR_PASSWORD_REQUEST: + case ST_RSA_AUTH_REQUEST: + if (resp->is_ok_packet()) + { + if (!res_charset_.empty()) + conn->state = ST_CHARSET_REQUEST; + else + conn->state = ST_FIRST_USER_REQUEST; + + break; + } + + if (resp->is_error_packet() || + conn->state == ST_CLEAR_PASSWORD_REQUEST || + conn->state == ST_RSA_AUTH_REQUEST) { this->resp = std::move(*resp); state_ = WFT_STATE_TASK_ERROR; @@ -368,11 +465,23 @@ int ComplexMySQLTask::keep_alive_timeout() return 0; } - if (res_charset_.size() != 0) - conn->state = ST_CHARSET_REQUEST; - else - conn->state = ST_FIRST_USER_REQUEST; + auth_resp = (MySQLAuthResponse *)resp; + if (auth_resp->is_continue()) + { + if (is_ssl_) + conn->state = ST_CLEAR_PASSWORD_REQUEST; + else + conn->state = ST_CSHA2_PUBLIC_KEY_REQUEST; + break; + } + + return auth_switch(auth_resp, conn); + + case ST_SHA256_PUBLIC_KEY_REQUEST: + case ST_CSHA2_PUBLIC_KEY_REQUEST: + conn->str = ((MySQLPublicKeyResponse *)resp)->get_public_key(); + conn->state = ST_RSA_AUTH_REQUEST; break; case ST_CHARSET_REQUEST: @@ -385,7 +494,7 @@ int ComplexMySQLTask::keep_alive_timeout() } conn->state = ST_FIRST_USER_REQUEST; - break; + return MYSQL_KEEPALIVE_DEFAULT; case ST_FIRST_USER_REQUEST: conn->state = ST_USER_REQUEST; @@ -397,6 +506,7 @@ int ComplexMySQLTask::keep_alive_timeout() return 0; } + conn->mysql_seqid = resp->get_seqid() + 1; return MYSQL_KEEPALIVE_DEFAULT; } diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 628100d2..83a3a2b3 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -13,11 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. - Authors: Wu Jiaxu (wujiaxu@sogou-inc.com) - Xie Han (xiehan@sogou-inc.com) + Authors: Xie Han (xiehan@sogou-inc.com) + Wu Jiaxu (wujiaxu@sogou-inc.com) */ -#include #include #include #include @@ -36,6 +35,10 @@ namespace protocol #define MYSQL_PAYLOAD_MAX ((1 << 24) - 1) +#define MYSQL_NATIVE_PASSWORD "mysql_native_password" +#define CACHING_SHA2_PASSWORD "caching_sha2_password" +#define MYSQL_CLEAR_PASSWORD "mysql_clear_password" + MySQLMessage::~MySQLMessage() { if (parser_) @@ -186,17 +189,27 @@ int MySQLHandshakeResponse::encode(struct iovec vectors[], int max) buf_.append((const char *)&protocol_version_, 1); buf_.append(server_version_.c_str(), server_version_.size() + 1); buf_.append((const char *)&connection_id_, 4); - buf_.append((const char *)auth_plugin_data_part_1_, 8); + buf_.append((const char *)auth_plugin_data_, 8); buf_.append(empty, 1); buf_.append((const char *)&cap_flags_lower, 2); buf_.append((const char *)&character_set_, 1); buf_.append((const char *)&status_flags_, 2); buf_.append((const char *)&cap_flags_upper, 2); buf_.append(empty, 11); - buf_.append((const char *)auth_plugin_data_part_2_, 12); + buf_.append((const char *)auth_plugin_data_ + 8, 12); return this->MySQLMessage::encode(vectors, max); } +#define MYSQL_CAPFLAG_CLIENT_SSL 0x00000800 +#define MYSQL_CAPFLAG_CLIENT_PROTOCOL_41 0x00000200 +#define MYSQL_CAPFLAG_CLIENT_SECURE_CONNECTION 0x00008000 +#define MYSQL_CAPFLAG_CLIENT_CONNECT_WITH_DB 0x00000008 +#define MYSQL_CAPFLAG_CLIENT_MULTI_STATEMENTS 0x00010000 +#define MYSQL_CAPFLAG_CLIENT_MULTI_RESULTS 0x00020000 +#define MYSQL_CAPFLAG_CLIENT_PS_MULTI_RESULTS 0x00040000 +#define MYSQL_CAPFLAG_CLIENT_PLUGIN_AUTH 0x00080000 +#define MYSQL_CAPFLAG_CLIENT_LOCAL_FILES 0x00000080 + int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t buflen) { const unsigned char *end = buf + buflen; @@ -228,14 +241,15 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle while (pos < end && *pos) pos++; - if (pos >= end || end - pos < 43) + if (pos >= end || end - pos < 45) return -2; server_version_.assign((const char *)buf, pos - buf); buf = pos + 1; + connection_id_ = uint4korr(buf); buf += 4; - memcpy(auth_plugin_data_part_1_, buf, 8); + memcpy(auth_plugin_data_, buf, 8); buf += 9; cap_flags_lower = uint2korr(buf); buf += 2; @@ -243,30 +257,63 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle status_flags_ = uint2korr(buf); buf += 2; cap_flags_upper = uint2korr(buf); - buf += 13; - memcpy(auth_plugin_data_part_2_, buf, 12); + buf += 2; capability_flags_ = (cap_flags_upper << 16U) + cap_flags_lower; + auth_plugin_data_len_ = *buf++; + // 10 bytes reserved. All 0s. + buf += 10; + // auth_plugin_data always 20 bytes + if (auth_plugin_data_len_ > 21) + return -2; + + memcpy(auth_plugin_data_ + 8, buf, 12); + buf += 13; + if (capability_flags_ & MYSQL_CAPFLAG_CLIENT_PLUGIN_AUTH) + { + if (buf == end || *(end - 1) != '\0') + return -2; + + auth_plugin_name_.assign((const char *)buf, end - 1 - buf); + } + return 1; } -static inline std::string __sha1_str(const std::string& str) +static std::string __native_password_encrypt(const std::string& password, + unsigned char seed[20]) { - unsigned char sha1[20]; - SHA_CTX ctx; - SHA1_Init(&ctx); - SHA1_Update(&ctx, str.c_str(), str.size()); - SHA1_Final(sha1, &ctx); - return std::string((const char *)sha1, 20); + unsigned char buf1[20]; + unsigned char buf2[40]; + int i; + + // SHA1( password ) ^ SHA1( seed + SHA1( SHA1( password ) ) ) + SHA1((unsigned char *)password.c_str(), password.size(), buf1); + SHA1(buf1, 20, buf2 + 20); + memcpy(buf2, seed, 20); + SHA1(buf2, 40, buf2); + for (i = 0; i < 20; i++) + buf1[i] ^= buf2[i]; + + return std::string((const char *)buf1, 20); } -#define MYSQL_CAPFLAG_CLIENT_SSL 0x00000800 -#define MYSQL_CAPFLAG_CLIENT_PROTOCOL_41 0x00000200 -#define MYSQL_CAPFLAG_CLIENT_SECURE_CONNECTION 0x00008000 -#define MYSQL_CAPFLAG_CLIENT_CONNECT_WITH_DB 0x00000008 -#define MYSQL_CAPFLAG_CLIENT_MULTI_STATEMENTS 0x00010000 -#define MYSQL_CAPFLAG_CLIENT_MULTI_RESULTS 0x00020000 -#define MYSQL_CAPFLAG_CLIENT_PS_MULTI_RESULTS 0x00040000 -#define MYSQL_CAPFLAG_CLIENT_LOCAL_FILES 0x00000080 +static std::string __caching_sha2_password_encrypt(const std::string& password, + unsigned char seed[20]) +{ + unsigned char buf1[32]; + unsigned char buf2[52]; + int i; + + // SHA256( password ) ^ SHA256( SHA256( SHA256( password ) ) + seed) + SHA256((unsigned char *)password.c_str(), password.size(), buf1); + SHA256(buf1, 32, buf2); + memcpy(buf2 + 32, seed, 20); + SHA256(buf2, 52, buf2); + for (i = 0; i < 32; i++) + buf1[i] ^= buf2[i]; + + return std::string((const char *)buf1, 32); +} int MySQLSSLRequest::encode(struct iovec vectors[], int max) { @@ -281,7 +328,8 @@ int MySQLSSLRequest::encode(struct iovec vectors[], int max) MYSQL_CAPFLAG_CLIENT_MULTI_RESULTS| MYSQL_CAPFLAG_CLIENT_LOCAL_FILES | MYSQL_CAPFLAG_CLIENT_MULTI_STATEMENTS | - MYSQL_CAPFLAG_CLIENT_PS_MULTI_RESULTS); + MYSQL_CAPFLAG_CLIENT_PS_MULTI_RESULTS | + MYSQL_CAPFLAG_CLIENT_PLUGIN_AUTH); pos += 4; int4store(pos, 0); pos += 4; @@ -308,9 +356,9 @@ int MySQLSSLRequest::encode(struct iovec vectors[], int max) int MySQLAuthRequest::encode(struct iovec vectors[], int max) { - std::string native; unsigned char header[32] = {0}; unsigned char *pos = header; + std::string str; int4store(pos, MYSQL_CAPFLAG_CLIENT_PROTOCOL_41 | MYSQL_CAPFLAG_CLIENT_SECURE_CONNECTION | @@ -318,29 +366,34 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) MYSQL_CAPFLAG_CLIENT_MULTI_RESULTS| MYSQL_CAPFLAG_CLIENT_LOCAL_FILES | MYSQL_CAPFLAG_CLIENT_MULTI_STATEMENTS | - MYSQL_CAPFLAG_CLIENT_PS_MULTI_RESULTS); + MYSQL_CAPFLAG_CLIENT_PS_MULTI_RESULTS | + MYSQL_CAPFLAG_CLIENT_PLUGIN_AUTH); pos += 4; int4store(pos, 0); pos += 4; *pos = (uint8_t)character_set_; if (password_.empty()) - native.push_back((char)0); + str.push_back(0); + else if (auth_plugin_name_ == CACHING_SHA2_PASSWORD) + { + str.push_back(32); + str += __caching_sha2_password_encrypt(password_, seed_); + } else { - native.push_back((char)20); - std::string first = __sha1_str(password_); - std::string second = __sha1_str(challenge_ + __sha1_str(first)); - - for (int i = 0; i < 20; i++) - native.push_back(first[i] ^ second[i]); + str.push_back(20); + str += __native_password_encrypt(password_, seed_); } buf_.clear(); buf_.append((char *)header, 32); buf_.append(username_.c_str(), username_.size() + 1); - buf_.append(native); + buf_.append(str); buf_.append(db_.c_str(), db_.size() + 1); + if (auth_plugin_name_.size() != 0) + buf_.append(auth_plugin_name_.c_str(), auth_plugin_name_.size() + 1); + return this->MySQLMessage::encode(vectors, max); } @@ -374,6 +427,180 @@ int MySQLAuthRequest::decode_packet(const unsigned char *buf, size_t buflen) return 1; } +int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) +{ + const unsigned char *end = buf + buflen; + const unsigned char *pos; + const unsigned char *str; + unsigned long long len; + int ret; + + if (end == buf) + return -2; + + switch (*buf) + { + case 0x00: + case 0xff: + return MySQLResponse::decode_packet(buf, buflen); + + case 0xfe: + pos = ++buf; + while (pos < end && *pos) + pos++; + + if (pos >= end) + return -2; + + auth_plugin_name_.assign((const char *)buf, pos - buf); + buf = pos + 1; + if (buf == end || *(end - 1) != '\0') + return -2; + + if (end - 1 - buf != 20) + return -2; + + memcpy(seed_, buf, 20); + return 1; + + default: + pos = buf; + ret = decode_string(&str, &len, &pos, end); + if (ret <= 0) + return ret; + + if (len == 1) + { + if (*str == 0x03) + { + if (end > pos) + return MySQLResponse::decode_packet(pos, end - pos); + else + return 0; + } + else if (*str == 0x04) + { + continue_ = true; + return 1; + } + } + + return -2; + } +} + +int MySQLAuthSwitchRequest::encode(struct iovec vectors[], int max) +{ + if (auth_plugin_name_ == MYSQL_NATIVE_PASSWORD) + { + buf_ = __native_password_encrypt(password_, seed_); + } + else if (auth_plugin_name_ == CACHING_SHA2_PASSWORD) + { + buf_ = __caching_sha2_password_encrypt(password_, seed_); + } + else if (auth_plugin_name_ == MYSQL_CLEAR_PASSWORD) + { + buf_ = password_; + buf_.push_back('\0'); + } + else + { + errno = EINVAL; + return -1; + } + + return this->MySQLMessage::encode(vectors, max); +} + +int MySQLPublicKeyResponse::decode_packet(const unsigned char *buf, + size_t buflen) +{ + if (buflen == 0 || *buf != 0x01) + return -2; + + if (buflen == 1) + return 0; + + public_key_.assign((const char *)buf + 1, buflen - 1); + return 1; +} + +int MySQLPublicKeyResponse::encode(struct iovec vectors[], int max) +{ + buf_.clear(); + buf_.push_back(0x01); + buf_ += public_key_; + return MySQLMessage::encode(vectors, max); +} + +int MySQLRSAAuthRequest::rsa_encrypt(void *ctx) +{ + EVP_PKEY_CTX *pkey_ctx = (EVP_PKEY_CTX *)ctx; + unsigned char out[256]; + size_t outlen = 256; + std::string pass; + unsigned char *p; + size_t i; + + if (EVP_PKEY_encrypt_init(pkey_ctx) > 0 && + EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_OAEP_PADDING) > 0) + { + pass.reserve(password_.size() + 1); + p = (unsigned char *)pass.c_str(); + for (i = 0; i <= password_.size(); i++) + p[i] = (unsigned char)password_[i] ^ seed_[i % 20]; + + if (EVP_PKEY_encrypt(pkey_ctx, out, &outlen, p, i) > 0) + { + buf_.assign((char *)out, 256); + return 0; + } + } + + return -1; +} + +int MySQLRSAAuthRequest::encode(struct iovec vectors[], int max) +{ + RSA *rsa; + BIO *bio; + + bio = BIO_new_mem_buf(public_key_.c_str(), public_key_.size()); + if (!bio) + return -1; + + rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!rsa) + return -1; + + EVP_PKEY_CTX *pkey_ctx; + EVP_PKEY *pkey; + int ret = -1; + + pkey = EVP_PKEY_new(); + if (pkey) + { + EVP_PKEY_assign_RSA(pkey, rsa); + pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pkey_ctx) + { + ret = rsa_encrypt(pkey_ctx); + EVP_PKEY_CTX_free(pkey_ctx); + } + + EVP_PKEY_free(pkey); + } + else + RSA_free(rsa); + + if (ret < 0) + return -1; + + return MySQLMessage::encode(vectors, max); +} + void MySQLResponse::set_ok_packet() { uint16_t zero16 = 0; diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index 4e0cf553..b4caf495 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -13,12 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. - Authors: Wu Jiaxu (wujiaxu@sogou-inc.com) - Xie Han (xiehan@sogou-inc.com) + Authors: Xie Han (xiehan@sogou-inc.com) + Wu Jiaxu (wujiaxu@sogou-inc.com) */ #include #include +#include #include #include #include "SSLWrapper.h" @@ -35,27 +36,28 @@ private: class MySQLHandshakeResponse : public MySQLResponse { public: - void get_challenge(char *arr) const + std::string get_server_version() const { return server_version_; } + std::string get_auth_plugin_name() const { return auth_plugin_name_; } + + void get_seed(unsigned char seed[20]) const { - memcpy(arr, auth_plugin_data_part_1_, 8); - memcpy(arr + 8, auth_plugin_data_part_2_, 12); + memcpy(seed, auth_plugin_data_, 20); } virtual int encode(struct iovec vectors[], int max); void server_set(uint8_t protocol_version, const std::string server_version, - uint32_t connection_id, const uint8_t *auth1, + uint32_t connection_id, const unsigned char seed[20], uint32_t capability_flags, uint8_t character_set, - uint16_t status_flags, const uint8_t *auth2) + uint16_t status_flags) { protocol_version_ = protocol_version; server_version_ = server_version; connection_id_ = connection_id; - memcpy(auth_plugin_data_part_1_, auth1, 8); + memcpy(auth_plugin_data_, seed, 20); capability_flags_ = capability_flags; character_set_ = character_set; status_flags_ = status_flags; - memcpy(auth_plugin_data_part_2_, auth2, 12); } bool host_disallowed() const { return disallowed_; } @@ -65,14 +67,15 @@ public: private: virtual int decode_packet(const unsigned char *buf, size_t buflen); - uint8_t protocol_version_; std::string server_version_; + std::string auth_plugin_name_; + unsigned char auth_plugin_data_[20]; uint32_t connection_id_; - uint8_t auth_plugin_data_part_1_[8]; uint32_t capability_flags_; - uint8_t character_set_; uint16_t status_flags_; - uint8_t auth_plugin_data_part_2_[12]; + uint8_t character_set_; + uint8_t auth_plugin_data_len_; + uint8_t protocol_version_; bool disallowed_; public: @@ -111,20 +114,25 @@ public: class MySQLAuthRequest : public MySQLRequest { public: - void set_auth(const std::string& username, - const std::string& password, - const std::string& db, + void set_auth(const std::string username, + const std::string password, + const std::string db, int character_set) { - username_ = username; - password_ = password; - db_ = db; + username_ = std::move(username); + password_ = std::move(password); + db_ = std::move(db); character_set_ = character_set; } - void set_challenge(const char *arr) + void set_auth_plugin_name(std::string name) { - challenge_.assign(arr, 20); + auth_plugin_name_ = std::move(name); + } + + void set_seed(const unsigned char seed[20]) + { + memcpy(seed_, seed, 20); } private: @@ -134,7 +142,8 @@ private: std::string username_; std::string password_; std::string db_; - std::string challenge_; + std::string auth_plugin_name_; + unsigned char seed_[20]; int character_set_; public: @@ -145,7 +154,172 @@ public: MySQLAuthRequest& operator= (MySQLAuthRequest&& move) = default; }; -using MySQLAuthResponse = MySQLResponse; +class MySQLAuthResponse : public MySQLResponse +{ +public: + std::string get_auth_plugin_name() const { return auth_plugin_name_; } + + void get_seed(unsigned char seed[20]) const + { + memcpy(seed, seed_, 20); + } + + bool is_continue() const + { + return continue_; + } + +private: + virtual int decode_packet(const unsigned char *buf, size_t buflen); + +private: + std::string auth_plugin_name_; + unsigned char seed_[20]; + bool continue_; + +public: + MySQLAuthResponse() : continue_(false) { } + //move constructor + MySQLAuthResponse(MySQLAuthResponse&& move) = default; + //move operator + MySQLAuthResponse& operator= (MySQLAuthResponse&& move) = default; +}; + +class MySQLAuthSwitchRequest : public MySQLRequest +{ +public: + void set_password(std::string password) + { + password_ = std::move(password); + } + + void set_auth_plugin_name(std::string name) + { + auth_plugin_name_ = std::move(name); + } + + void set_seed(const unsigned char seed[20]) + { + memcpy(seed_, seed, 20); + } + +private: + virtual int encode(struct iovec vectors[], int max); + + /* Not implemented. */ + virtual int decode_packet(const unsigned char *buf, size_t buflen) + { + return -2; + } + + std::string password_; + std::string auth_plugin_name_; + unsigned char seed_[20]; + +public: + MySQLAuthSwitchRequest() { } + //move constructor + MySQLAuthSwitchRequest(MySQLAuthSwitchRequest&& move) = default; + //move operator + MySQLAuthSwitchRequest& operator= (MySQLAuthSwitchRequest&& move) = default; +}; + +class MySQLPublicKeyRequest : public MySQLRequest +{ +public: + void set_caching_sha2() { byte_ = 0x02; } + void set_sha256() { byte_ = 0x01; } + +private: + virtual int encode(struct iovec vectors[], int max) + { + buf_.assign(&byte_, 1); + return MySQLRequest::encode(vectors, max); + } + + /* Not implemented. */ + virtual int decode_packet(const unsigned char *buf, size_t buflen) + { + return -2; + } + + char byte_; + +public: + MySQLPublicKeyRequest() : byte_(0x01) { } + //move constructor + MySQLPublicKeyRequest(MySQLPublicKeyRequest&& move) = default; + //move operator + MySQLPublicKeyRequest& operator= (MySQLPublicKeyRequest&& move) = default; +}; + +class MySQLPublicKeyResponse : public MySQLResponse +{ +public: + std::string get_public_key() const + { + return public_key_; + } + + void set_public_key(std::string key) + { + public_key_ = std::move(key); + } + +private: + virtual int encode(struct iovec vectors[], int max); + virtual int decode_packet(const unsigned char *buf, size_t buflen); + + std::string public_key_; + +public: + MySQLPublicKeyResponse() { } + //move constructor + MySQLPublicKeyResponse(MySQLPublicKeyResponse&& move) = default; + //move operator + MySQLPublicKeyResponse& operator= (MySQLPublicKeyResponse&& move) = default; +}; + +class MySQLRSAAuthRequest : public MySQLRequest +{ +public: + void set_password(std::string password) + { + password_ = std::move(password); + } + + void set_public_key(std::string key) + { + public_key_ = std::move(key); + } + + void set_seed(const unsigned char seed[20]) + { + memcpy(seed_, seed, 20); + } + +private: + virtual int encode(struct iovec vectors[], int max); + + /* Not implemented. */ + virtual int decode_packet(const unsigned char *buf, size_t buflen) + { + return -2; + } + + int rsa_encrypt(void *ctx); + + std::string password_; + std::string public_key_; + unsigned char seed_[20]; + +public: + MySQLRSAAuthRequest() { } + //move constructor + MySQLRSAAuthRequest(MySQLRSAAuthRequest&& move) = default; + //move operator + MySQLRSAAuthRequest& operator= (MySQLRSAAuthRequest&& move) = default; +}; ////////// diff --git a/src/server/WFMySQLServer.cc b/src/server/WFMySQLServer.cc index 012efa46..d3448f1b 100644 --- a/src/server/WFMySQLServer.cc +++ b/src/server/WFMySQLServer.cc @@ -29,8 +29,8 @@ WFConnection *WFMySQLServer::new_connection(int accept_fd) struct iovec vec[8]; int count; - resp.server_set(0x0a, "5.5", 1, (const uint8_t *)"12345678", - 0, 33, 0, (const uint8_t *)"123456789abc"); + resp.server_set(0x0a, "5.5", 1, (const uint8_t *)"12345678901234567890", + 0, 33, 0); count = resp.encode(vec, 8); if (count >= 0) {