From b2c27d023fe1741e3c194d5fda1e06bbf98ef55e Mon Sep 17 00:00:00 2001 From: XieHan Date: Fri, 3 Feb 2023 23:00:21 +0800 Subject: [PATCH 01/17] Decode more info from MySQL init handshake package, add CLIENT_PLUGIN_AUTH --- src/protocol/MySQLMessage.cc | 42 ++++++++++++++++++++++++----------- src/protocol/MySQLMessage.inl | 4 +++- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 628100d2..cd81ea74 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -197,12 +197,23 @@ int MySQLHandshakeResponse::encode(struct iovec vectors[], int max) 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; const unsigned char *pos; uint16_t cap_flags_lower; uint16_t cap_flags_upper; + size_t len; if (buflen == 0) return -2; @@ -233,6 +244,8 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle server_version_.assign((const char *)buf, pos - buf); buf = pos + 1; + + // TODO: check overflow connection_id_ = uint4korr(buf); buf += 4; memcpy(auth_plugin_data_part_1_, buf, 8); @@ -243,9 +256,19 @@ 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; + len = 12; + if (auth_plugin_data_len_ >= 22) + len = auth_plugin_data_len_ - 9; + memcpy(auth_plugin_data_part_2_, buf, len); + buf += len; + buf++; // one byte zero + if (capability_flags_ & MYSQL_CAPFLAG_CLIENT_PLUGIN_AUTH) + auth_plugin_name_.assign((const char *)buf); return 1; } @@ -259,15 +282,6 @@ static inline std::string __sha1_str(const std::string& str) return std::string((const char *)sha1, 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 - int MySQLSSLRequest::encode(struct iovec vectors[], int max) { unsigned char header[32] = {0}; @@ -281,7 +295,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; @@ -318,7 +333,8 @@ 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; diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index 4e0cf553..683a2be3 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -72,7 +72,9 @@ private: uint32_t capability_flags_; uint8_t character_set_; uint16_t status_flags_; - uint8_t auth_plugin_data_part_2_[12]; + uint8_t auth_plugin_data_len_; + uint8_t auth_plugin_data_part_2_[246]; + std::string auth_plugin_name_; bool disallowed_; public: From b9170b29a50b2853a3c36da46bed77ae2f19c95d Mon Sep 17 00:00:00 2001 From: XieHan Date: Sun, 5 Feb 2023 02:35:53 +0800 Subject: [PATCH 02/17] Add checking overflow --- src/protocol/MySQLMessage.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index cd81ea74..ee709a4d 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -213,7 +213,7 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle const unsigned char *pos; uint16_t cap_flags_lower; uint16_t cap_flags_upper; - size_t len; + int len; if (buflen == 0) return -2; @@ -239,7 +239,7 @@ 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 < 32) return -2; server_version_.assign((const char *)buf, pos - buf); @@ -261,14 +261,24 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle auth_plugin_data_len_ = *buf++; // 10 bytes reserved. All 0s. buf += 10; + len = 12; if (auth_plugin_data_len_ >= 22) len = auth_plugin_data_len_ - 9; + + if (end - buf < len + 1) + return -2; + memcpy(auth_plugin_data_part_2_, buf, len); - buf += len; - buf++; // one byte zero + buf += len + 1; if (capability_flags_ & MYSQL_CAPFLAG_CLIENT_PLUGIN_AUTH) + { + if (buf == end || *(end - 1) != '\0') + return -2; + auth_plugin_name_.assign((const char *)buf); + } + return 1; } From defd4d5922e99143391d2aa4ead7b3b685f40e6c Mon Sep 17 00:00:00 2001 From: XieHan Date: Sun, 5 Feb 2023 03:50:11 +0800 Subject: [PATCH 03/17] Add class MySQLAuthResponse to support auth switch --- src/protocol/MySQLMessage.cc | 30 ++++++++++++++++++++++++++++++ src/protocol/MySQLMessage.inl | 23 ++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index ee709a4d..84dbbd14 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -400,6 +400,36 @@ 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; + + if (end == buf) + return -2; + + if (*buf == 0xfe) + { + pos = ++buf; + while (pos < end && *pos) + pos++; + + if (pos >= end) + return -2; + + plugin_name_.assign((const char *)buf, pos - buf); + buf = pos + 1; + if (buf == end || *(end - 1) != '\0') + return -2; + + plugin_data_.assign((const char *)buf, end - 1 - buf); + auth_switch_ = true; + return 1; + } + + return MySQLResponse::decode_packet(buf, buflen); +} + void MySQLResponse::set_ok_packet() { uint16_t zero16 = 0; diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index 683a2be3..a9fb3da1 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -147,7 +147,28 @@ public: MySQLAuthRequest& operator= (MySQLAuthRequest&& move) = default; }; -using MySQLAuthResponse = MySQLResponse; +class MySQLAuthResponse : public MySQLResponse +{ +public: + std::string auth_plugin_name() const { return plugin_name_; } + std::string auth_plugin_data() const { return plugin_data_; } + bool auth_switched() const { return auth_switch_; } + +private: + virtual int decode_packet(const unsigned char *buf, size_t buflen); + +private: + std::string plugin_name_; + std::string plugin_data_; + bool auth_switch_; + +public: + MySQLAuthResponse() : auth_switch_(false) { } + //move constructor + MySQLAuthResponse(MySQLAuthResponse&& move) = default; + //move operator + MySQLAuthResponse& operator= (MySQLAuthResponse&& move) = default; +}; ////////// From f7285fe708310280192cc3fd939b4b0dae27ae76 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Mon, 6 Feb 2023 02:57:51 +0800 Subject: [PATCH 04/17] Add MySQLBytesRequest --- src/protocol/MySQLMessage.cc | 13 +++++++++---- src/protocol/MySQLMessage.inl | 31 ++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 84dbbd14..b724e1a4 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -408,8 +408,13 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) if (end == buf) return -2; - if (*buf == 0xfe) + switch (*buf) { + case 0x00: + case 0xff: + return MySQLResponse::decode_packet(buf, buflen); + + case 0xfe: pos = ++buf; while (pos < end && *pos) pos++; @@ -423,11 +428,11 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) return -2; plugin_data_.assign((const char *)buf, end - 1 - buf); - auth_switch_ = true; return 1; - } - return MySQLResponse::decode_packet(buf, buflen); + default: + return -2; + } } void MySQLResponse::set_ok_packet() diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index a9fb3da1..d59852e0 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -152,7 +152,6 @@ class MySQLAuthResponse : public MySQLResponse public: std::string auth_plugin_name() const { return plugin_name_; } std::string auth_plugin_data() const { return plugin_data_; } - bool auth_switched() const { return auth_switch_; } private: virtual int decode_packet(const unsigned char *buf, size_t buflen); @@ -160,16 +159,42 @@ private: private: std::string plugin_name_; std::string plugin_data_; - bool auth_switch_; public: - MySQLAuthResponse() : auth_switch_(false) { } + MySQLAuthResponse() { } //move constructor MySQLAuthResponse(MySQLAuthResponse&& move) = default; //move operator MySQLAuthResponse& operator= (MySQLAuthResponse&& move) = default; }; +class MySQLBytesRequest : public MySQLRequest +{ +public: + void set_bytes(const void *bytes, size_t len) + { + buf_.assign((const char *)bytes, len); + } + + std::string get_bytes() const + { + return buf_; + } + +private: + virtual int decode_packet(const unsigned char *buf, size_t buflen) + { + buf_.assign((const char *)buf, buflen); + return 1; + } + +private: + MySQLBytesRequest() { } + MySQLBytesRequest(MySQLBytesRequest&& move) = default; + //move operator + MySQLBytesRequest& operator= (MySQLBytesRequest&& move) = default; +}; + ////////// inline mysql_parser_t *MySQLMessage::get_parser() const From 1a144f3bc036cc3244570a039c899bfbc117d805 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Mon, 6 Feb 2023 18:20:25 +0800 Subject: [PATCH 05/17] Simplify MySQL auth_plugin_data, always 20 bytes --- src/protocol/MySQLMessage.cc | 33 +++++++++++++------------- src/protocol/MySQLMessage.inl | 44 +++++++++++++++++++---------------- src/server/WFMySQLServer.cc | 4 ++-- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index b724e1a4..b043ae0a 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -186,14 +186,14 @@ 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); } @@ -213,7 +213,6 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle const unsigned char *pos; uint16_t cap_flags_lower; uint16_t cap_flags_upper; - int len; if (buflen == 0) return -2; @@ -239,7 +238,7 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle while (pos < end && *pos) pos++; - if (pos >= end || end - pos < 32) + if (pos >= end || end - pos < 45) return -2; server_version_.assign((const char *)buf, pos - buf); @@ -248,7 +247,7 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle // TODO: check overflow 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; @@ -261,22 +260,18 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle auth_plugin_data_len_ = *buf++; // 10 bytes reserved. All 0s. buf += 10; - - len = 12; - if (auth_plugin_data_len_ >= 22) - len = auth_plugin_data_len_ - 9; - - if (end - buf < len + 1) + // auth_plugin_data always 20 bytes + if (auth_plugin_data_len_ > 21) return -2; - memcpy(auth_plugin_data_part_2_, buf, len); - buf += len + 1; + 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); + auth_plugin_name_.assign((const char *)buf, end - 1 - buf); } return 1; @@ -355,8 +350,9 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) else { native.push_back((char)20); + std::string seed = std::string((const char *)challenge_, 20); std::string first = __sha1_str(password_); - std::string second = __sha1_str(challenge_ + __sha1_str(first)); + std::string second = __sha1_str(seed + __sha1_str(first)); for (int i = 0; i < 20; i++) native.push_back(first[i] ^ second[i]); @@ -422,12 +418,15 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) if (pos >= end) return -2; - plugin_name_.assign((const char *)buf, pos - buf); + auth_plugin_name_.assign((const char *)buf, pos - buf); buf = pos + 1; if (buf == end || *(end - 1) != '\0') return -2; - plugin_data_.assign((const char *)buf, end - 1 - buf); + if (end - 1 - buf != 20) + return -2; + + memcpy(auth_plugin_data_, buf, 20); return 1; default: diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index d59852e0..564f7148 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -35,27 +35,28 @@ private: class MySQLHandshakeResponse : public MySQLResponse { public: + std::string get_server_version() const { return server_version_; } + std::string get_auth_plugin_name() const { return auth_plugin_name_; } + void get_challenge(char *arr) const { - memcpy(arr, auth_plugin_data_part_1_, 8); - memcpy(arr + 8, auth_plugin_data_part_2_, 12); + memcpy(arr, 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 uint8_t *auth_plugin_data, 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_, auth_plugin_data, 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,16 +66,15 @@ public: private: virtual int decode_packet(const unsigned char *buf, size_t buflen); - uint8_t protocol_version_; std::string server_version_; - 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_len_; - uint8_t auth_plugin_data_part_2_[246]; std::string auth_plugin_name_; + uint8_t auth_plugin_data_[20]; + uint32_t connection_id_; + uint32_t capability_flags_; + uint16_t status_flags_; + uint8_t character_set_; + uint8_t auth_plugin_data_len_; + uint8_t protocol_version_; bool disallowed_; public: @@ -126,7 +126,7 @@ public: void set_challenge(const char *arr) { - challenge_.assign(arr, 20); + memcpy(challenge_, arr, 20); } private: @@ -136,7 +136,7 @@ private: std::string username_; std::string password_; std::string db_; - std::string challenge_; + uint8_t challenge_[20]; int character_set_; public: @@ -150,15 +150,19 @@ public: class MySQLAuthResponse : public MySQLResponse { public: - std::string auth_plugin_name() const { return plugin_name_; } - std::string auth_plugin_data() const { return plugin_data_; } + std::string get_auth_plugin_name() const { return auth_plugin_name_; } + + void get_challenge(char *arr) const + { + memcpy(arr, auth_plugin_data_, 20); + } private: virtual int decode_packet(const unsigned char *buf, size_t buflen); private: - std::string plugin_name_; - std::string plugin_data_; + std::string auth_plugin_name_; + uint8_t auth_plugin_data_[20]; public: MySQLAuthResponse() { } 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) { From 89ffe576ad2c59d92efc2dfabc93f1e447166cb7 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Mon, 6 Feb 2023 19:32:50 +0800 Subject: [PATCH 06/17] Remove MySQLBytesRequest. Add MySQLAuthSwitchRequest. --- src/protocol/MySQLMessage.cc | 34 ++++++++++++++++++++++++++++++++++ src/protocol/MySQLMessage.inl | 34 ++++++++++++++++++++++++---------- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index b043ae0a..c4e96b54 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -434,6 +434,40 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) } } +int MySQLAuthSwitchRequest::encode(struct iovec vectors[], int max) +{ + if (auth_plugin_name_ == "mysql_clear_password") + { + buf_ = password_; + buf_.push_back('\0'); + } + else if (auth_plugin_name_ == "mysql_native_password") + { + std::string seed = std::string((const char *)challenge_, 20); + std::string first = __sha1_str(password_); + std::string second = __sha1_str(seed + __sha1_str(first)); + + buf_.clear(); + for (int i = 0; i < 20; i++) + buf_.push_back(first[i] ^ second[i]); + } + else if (auth_plugin_name_ == "sha256_password") + { + // TODO + } + else if (auth_plugin_name_ == "caching_sha2_password") + { + // TODO + } + else + { + errno = EINVAL; + return -1; + } + + return this->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 564f7148..4d713108 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -172,33 +172,47 @@ public: MySQLAuthResponse& operator= (MySQLAuthResponse&& move) = default; }; -class MySQLBytesRequest : public MySQLRequest +class MySQLAuthSwitchRequest : public MySQLRequest { public: - void set_bytes(const void *bytes, size_t len) + void set_password(const std::string& password) { - buf_.assign((const char *)bytes, len); + password_ = password; } - std::string get_bytes() const + void set_auth_plugin_name(const std::string& name) { - return buf_; + auth_plugin_name_ = name; + } + + void set_challenge(const char *arr) + { + memcpy(challenge_, arr, 20); } private: + virtual int encode(struct iovec vectors[], int max); + virtual int decode_packet(const unsigned char *buf, size_t buflen) { buf_.assign((const char *)buf, buflen); return 1; } -private: - MySQLBytesRequest() { } - MySQLBytesRequest(MySQLBytesRequest&& move) = default; - //move operator - MySQLBytesRequest& operator= (MySQLBytesRequest&& move) = default; + std::string password_; + std::string auth_plugin_name_; + uint8_t challenge_[20]; + +public: + MySQLAuthSwitchRequest() { } + //move constructor + MySQLAuthSwitchRequest(MySQLAuthSwitchRequest&& move) = default; + //move operator + MySQLAuthSwitchRequest& operator= (MySQLAuthSwitchRequest&& move) = default; }; +using MySQLAuthSwitchResponse = MySQLResponse; + ////////// inline mysql_parser_t *MySQLMessage::get_parser() const From 7ae97a070dfbeeea75d389a2106b210aec30270c Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Mon, 6 Feb 2023 22:41:19 +0800 Subject: [PATCH 07/17] Support MySQL auth switch --- src/factory/MySQLTaskImpl.cc | 55 ++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index f3195095..62135eb4 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -68,6 +68,7 @@ private: { ST_SSL_REQUEST, ST_AUTH_REQUEST, + ST_AUTH_SWITCH_REQUEST, ST_CHARSET_REQUEST, ST_FIRST_USER_REQUEST, ST_USER_REQUEST @@ -75,6 +76,7 @@ private: struct MyConnection : public WFConnection { + std::string auth_plugin_name; char challenge[20]; unsigned char mysql_seqid; enum ConnState state; @@ -86,7 +88,8 @@ private: } }; - int check_handshake(); + int check_handshake(MySQLHandshakeResponse *resp); + int auth_switch(MySQLAuthResponse *resp, MyConnection *conn); SSL *get_ssl() const { @@ -184,6 +187,7 @@ static SSL *__create_ssl(SSL_CTX *ssl_ctx) CommMessageOut *ComplexMySQLTask::message_out() { + MySQLAuthSwitchRequest *auth_switch_req; MySQLAuthRequest *auth_req; MySQLRequest *req; @@ -202,9 +206,18 @@ CommMessageOut *ComplexMySQLTask::message_out() 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_challenge(conn->challenge); + req->set_seqid(conn->mysql_seqid); + break; + + 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(conn->auth_plugin_name); + auth_switch_req->set_challenge(conn->challenge); + req->set_seqid(conn->mysql_seqid); break; case ST_CHARSET_REQUEST: @@ -264,6 +277,10 @@ CommMessageIn *ComplexMySQLTask::message_in() resp = new MySQLAuthResponse; break; + case ST_AUTH_SWITCH_REQUEST: + resp = new MySQLAuthSwitchResponse; + break; + case ST_CHARSET_REQUEST: resp = new MySQLResponse; break; @@ -287,9 +304,8 @@ CommMessageIn *ComplexMySQLTask::message_in() return new MySSLWrapper(resp, get_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()) @@ -337,15 +353,34 @@ 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 (name == "mysql_clear_password" && !is_ssl_) + { + state_ = WFT_STATE_SYS_ERROR; + error_ = EBADMSG; + return 0; + } + + conn->mysql_seqid = resp->get_seqid() + 1; + conn->auth_plugin_name = std::move(name); + resp->get_challenge(conn->challenge); + conn->state = ST_AUTH_SWITCH_REQUEST; + return MYSQL_KEEPALIVE_DEFAULT; +} + int ComplexMySQLTask::keep_alive_timeout() { + auto *msg = (ProtocolMessage *)this->get_message_in(); + 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 (is_ssl_) @@ -360,7 +395,8 @@ int ComplexMySQLTask::keep_alive_timeout() break; case ST_AUTH_REQUEST: - if (!resp->is_ok_packet()) + case ST_AUTH_SWITCH_REQUEST: + if (resp->is_error_packet()) { this->resp = std::move(*resp); state_ = WFT_STATE_TASK_ERROR; @@ -368,6 +404,9 @@ int ComplexMySQLTask::keep_alive_timeout() return 0; } + if (conn->state == ST_AUTH_REQUEST && !resp->is_ok_packet()) + return auth_switch((MySQLAuthResponse *)resp, conn); + if (res_charset_.size() != 0) conn->state = ST_CHARSET_REQUEST; else From 7068e035ba04950d93531ced1b4183feca6f7ad1 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Wed, 8 Feb 2023 18:01:28 +0800 Subject: [PATCH 08/17] Optimize MySQL native password auth --- src/factory/MySQLTaskImpl.cc | 10 +++---- src/protocol/MySQLMessage.cc | 53 +++++++++++++++++------------------ src/protocol/MySQLMessage.inl | 28 +++++++++--------- 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index 62135eb4..989adf2c 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -77,7 +77,7 @@ private: struct MyConnection : public WFConnection { std::string auth_plugin_name; - char challenge[20]; + unsigned char seed[20]; unsigned char mysql_seqid; enum ConnState state; SSL *ssl; @@ -207,7 +207,7 @@ CommMessageOut *ComplexMySQLTask::message_out() req = new MySQLAuthRequest; auth_req = (MySQLAuthRequest *)req; auth_req->set_auth(username_, password_, db_, character_set_); - auth_req->set_challenge(conn->challenge); + auth_req->set_seed(conn->seed); req->set_seqid(conn->mysql_seqid); break; @@ -216,7 +216,7 @@ CommMessageOut *ComplexMySQLTask::message_out() auth_switch_req = (MySQLAuthSwitchRequest *)req; auth_switch_req->set_password(password_); auth_switch_req->set_auth_plugin_name(conn->auth_plugin_name); - auth_switch_req->set_challenge(conn->challenge); + auth_switch_req->set_seed(conn->seed); req->set_seqid(conn->mysql_seqid); break; @@ -341,7 +341,7 @@ int ComplexMySQLTask::check_handshake(MySQLHandshakeResponse *resp) auto *my_conn = new MyConnection(ssl); my_conn->mysql_seqid = resp->get_seqid() + 1; - resp->get_challenge(my_conn->challenge); + resp->get_seed(my_conn->seed); my_conn->state = is_ssl_ ? ST_SSL_REQUEST : ST_AUTH_REQUEST; conn->set_context(my_conn, [](void *ctx) { auto *my_conn = (MyConnection *)ctx; @@ -366,7 +366,7 @@ int ComplexMySQLTask::auth_switch(MySQLAuthResponse *resp, MyConnection *conn) conn->mysql_seqid = resp->get_seqid() + 1; conn->auth_plugin_name = std::move(name); - resp->get_challenge(conn->challenge); + resp->get_seed(conn->seed); conn->state = ST_AUTH_SWITCH_REQUEST; return MYSQL_KEEPALIVE_DEFAULT; } diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index c4e96b54..d7229f65 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -244,7 +244,6 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle server_version_.assign((const char *)buf, pos - buf); buf = pos + 1; - // TODO: check overflow connection_id_ = uint4korr(buf); buf += 4; memcpy(auth_plugin_data_, buf, 8); @@ -277,14 +276,25 @@ int MySQLHandshakeResponse::decode_packet(const unsigned char *buf, size_t bufle 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]; + unsigned char *p = buf2 + 20; + int i; + + // The password is encrypted with: + // SHA1( password ) ^ SHA1( seed + SHA1( SHA1( password ) ) ) + SHA1((unsigned char *)password.c_str(), password.size(), buf1); + memcpy(p, buf1, 20); + SHA1(p, 20, p); + 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); } int MySQLSSLRequest::encode(struct iovec vectors[], int max) @@ -328,9 +338,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 | @@ -346,22 +356,17 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) *pos = (uint8_t)character_set_; if (password_.empty()) - native.push_back((char)0); + str.push_back('\0'); else { - native.push_back((char)20); - std::string seed = std::string((const char *)challenge_, 20); - std::string first = __sha1_str(password_); - std::string second = __sha1_str(seed + __sha1_str(first)); - - for (int i = 0; i < 20; i++) - native.push_back(first[i] ^ second[i]); + str.push_back((char)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); return this->MySQLMessage::encode(vectors, max); } @@ -426,7 +431,7 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) if (end - 1 - buf != 20) return -2; - memcpy(auth_plugin_data_, buf, 20); + memcpy(seed_, buf, 20); return 1; default: @@ -442,15 +447,7 @@ int MySQLAuthSwitchRequest::encode(struct iovec vectors[], int max) buf_.push_back('\0'); } else if (auth_plugin_name_ == "mysql_native_password") - { - std::string seed = std::string((const char *)challenge_, 20); - std::string first = __sha1_str(password_); - std::string second = __sha1_str(seed + __sha1_str(first)); - - buf_.clear(); - for (int i = 0; i < 20; i++) - buf_.push_back(first[i] ^ second[i]); - } + buf_ = __native_password_encrypt(password_, seed_); else if (auth_plugin_name_ == "sha256_password") { // TODO diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index 4d713108..e079576a 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -38,22 +38,22 @@ public: std::string get_server_version() const { return server_version_; } std::string get_auth_plugin_name() const { return auth_plugin_name_; } - void get_challenge(char *arr) const + void get_seed(unsigned char seed[20]) const { - memcpy(arr, auth_plugin_data_, 20); + 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 *auth_plugin_data, + uint32_t connection_id, const unsigned char seed[20], uint32_t capability_flags, uint8_t character_set, uint16_t status_flags) { protocol_version_ = protocol_version; server_version_ = server_version; connection_id_ = connection_id; - memcpy(auth_plugin_data_, auth_plugin_data, 20); + memcpy(auth_plugin_data_, seed, 20); capability_flags_ = capability_flags; character_set_ = character_set; status_flags_ = status_flags; @@ -68,7 +68,7 @@ private: std::string server_version_; std::string auth_plugin_name_; - uint8_t auth_plugin_data_[20]; + unsigned char auth_plugin_data_[20]; uint32_t connection_id_; uint32_t capability_flags_; uint16_t status_flags_; @@ -124,9 +124,9 @@ public: character_set_ = character_set; } - void set_challenge(const char *arr) + void set_seed(const unsigned char seed[20]) { - memcpy(challenge_, arr, 20); + memcpy(seed_, seed, 20); } private: @@ -136,7 +136,7 @@ private: std::string username_; std::string password_; std::string db_; - uint8_t challenge_[20]; + unsigned char seed_[20]; int character_set_; public: @@ -152,9 +152,9 @@ class MySQLAuthResponse : public MySQLResponse public: std::string get_auth_plugin_name() const { return auth_plugin_name_; } - void get_challenge(char *arr) const + void get_seed(unsigned char seed[20]) const { - memcpy(arr, auth_plugin_data_, 20); + memcpy(seed, seed_, 20); } private: @@ -162,7 +162,7 @@ private: private: std::string auth_plugin_name_; - uint8_t auth_plugin_data_[20]; + unsigned char seed_[20]; public: MySQLAuthResponse() { } @@ -185,9 +185,9 @@ public: auth_plugin_name_ = name; } - void set_challenge(const char *arr) + void set_seed(const unsigned char seed[20]) { - memcpy(challenge_, arr, 20); + memcpy(seed_, seed, 20); } private: @@ -201,7 +201,7 @@ private: std::string password_; std::string auth_plugin_name_; - uint8_t challenge_[20]; + unsigned char seed_[20]; public: MySQLAuthSwitchRequest() { } From f95bb1620df6726b92d9f65186fb2793778c79c3 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Wed, 8 Feb 2023 20:38:35 +0800 Subject: [PATCH 09/17] Send auth_plugin_name in auth request --- src/factory/MySQLTaskImpl.cc | 3 +- src/protocol/MySQLMessage.cc | 60 ++++++++++++++++++++++++++--------- src/protocol/MySQLMessage.inl | 8 +++-- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index 989adf2c..081e0ae6 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -278,7 +278,7 @@ CommMessageIn *ComplexMySQLTask::message_in() break; case ST_AUTH_SWITCH_REQUEST: - resp = new MySQLAuthSwitchResponse; + resp = new MySQLAuthResponse; break; case ST_CHARSET_REQUEST: @@ -341,6 +341,7 @@ int ComplexMySQLTask::check_handshake(MySQLHandshakeResponse *resp) auto *my_conn = new MyConnection(ssl); my_conn->mysql_seqid = resp->get_seqid() + 1; + my_conn->auth_plugin_name = resp->get_auth_plugin_name(); resp->get_seed(my_conn->seed); my_conn->state = is_ssl_ ? ST_SSL_REQUEST : ST_AUTH_REQUEST; conn->set_context(my_conn, [](void *ctx) { diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index d7229f65..8fad82e9 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -36,6 +36,11 @@ namespace protocol #define MYSQL_PAYLOAD_MAX ((1 << 24) - 1) +#define MYSQL_NATIVE_PASSWORD "mysql_native_password" +#define CACHING_SHA2_PASSWORD "caching_sha2_password" +#define SHA256_PASSWORD "sha256_password" +#define MYSQL_CLEAR_PASSWORD "mysql_clear_password" + MySQLMessage::~MySQLMessage() { if (parser_) @@ -281,14 +286,11 @@ static std::string __native_password_encrypt(const std::string& password, { unsigned char buf1[20]; unsigned char buf2[40]; - unsigned char *p = buf2 + 20; int i; - // The password is encrypted with: // SHA1( password ) ^ SHA1( seed + SHA1( SHA1( password ) ) ) SHA1((unsigned char *)password.c_str(), password.size(), buf1); - memcpy(p, buf1, 20); - SHA1(p, 20, p); + SHA1(buf1, 20, buf2 + 20); memcpy(buf2, seed, 20); SHA1(buf2, 40, buf2); for (i = 0; i < 20; i++) @@ -297,6 +299,24 @@ static std::string __native_password_encrypt(const std::string& password, return std::string((const char *)buf1, 20); } +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) { unsigned char header[32] = {0}; @@ -357,6 +377,11 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) if (password_.empty()) str.push_back('\0'); + else if (auth_plugin_name_ == CACHING_SHA2_PASSWORD) + { + str.push_back((char)32); + str += __caching_sha2_password_encrypt(password_, seed_); + } else { str.push_back((char)20); @@ -368,6 +393,9 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) buf_.append(username_.c_str(), username_.size() + 1); 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); } @@ -441,21 +469,23 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) int MySQLAuthSwitchRequest::encode(struct iovec vectors[], int max) { - if (auth_plugin_name_ == "mysql_clear_password") + 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_ == SHA256_PASSWORD) + { + // TODO + } + else if (auth_plugin_name_ == MYSQL_CLEAR_PASSWORD) { buf_ = password_; buf_.push_back('\0'); } - else if (auth_plugin_name_ == "mysql_native_password") - buf_ = __native_password_encrypt(password_, seed_); - else if (auth_plugin_name_ == "sha256_password") - { - // TODO - } - else if (auth_plugin_name_ == "caching_sha2_password") - { - // TODO - } else { errno = EINVAL; diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index e079576a..c88d8c1e 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -124,6 +124,11 @@ public: character_set_ = character_set; } + void set_auth_plugin_name(const std::string& name) + { + auth_plugin_name_ = name; + } + void set_seed(const unsigned char seed[20]) { memcpy(seed_, seed, 20); @@ -136,6 +141,7 @@ private: std::string username_; std::string password_; std::string db_; + std::string auth_plugin_name_; unsigned char seed_[20]; int character_set_; @@ -211,8 +217,6 @@ public: MySQLAuthSwitchRequest& operator= (MySQLAuthSwitchRequest&& move) = default; }; -using MySQLAuthSwitchResponse = MySQLResponse; - ////////// inline mysql_parser_t *MySQLMessage::get_parser() const From 7638d18f584916e344d31b78ff34ca24a27b63a1 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Wed, 8 Feb 2023 23:11:09 +0800 Subject: [PATCH 10/17] Support caching sha2 with fast auth or SSL --- src/factory/MySQLTaskImpl.cc | 38 ++++++++++++++++++++++++++++------- src/protocol/MySQLMessage.cc | 24 ++++++++++++++++++++++ src/protocol/MySQLMessage.inl | 18 ++++++++++++++++- 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index 081e0ae6..852df7d8 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -69,6 +69,7 @@ private: ST_SSL_REQUEST, ST_AUTH_REQUEST, ST_AUTH_SWITCH_REQUEST, + ST_PUBLIC_KEY_REQUEST, ST_CHARSET_REQUEST, ST_FIRST_USER_REQUEST, ST_USER_REQUEST @@ -382,6 +383,7 @@ int ComplexMySQLTask::keep_alive_timeout() return check_handshake((MySQLHandshakeResponse *)msg); auto *conn = (MyConnection *)this->get_connection(); + MySQLAuthResponse *auth_resp; MySQLResponse *resp; if (is_ssl_) @@ -405,15 +407,37 @@ int ComplexMySQLTask::keep_alive_timeout() return 0; } - if (conn->state == ST_AUTH_REQUEST && !resp->is_ok_packet()) - return auth_switch((MySQLAuthResponse *)resp, conn); + if (resp->is_ok_packet()) + { + if (res_charset_.size() != 0) + conn->state = ST_CHARSET_REQUEST; + else + conn->state = ST_FIRST_USER_REQUEST; - if (res_charset_.size() != 0) - conn->state = ST_CHARSET_REQUEST; - else - conn->state = ST_FIRST_USER_REQUEST; + break; + } - break; + auth_resp = (MySQLAuthResponse *)resp; + if (auth_resp->is_continue()) + { + if (is_ssl_) + auth_resp->set_auth_plugin_name("mysql_clear_password"); + else + { + conn->state = ST_PUBLIC_KEY_REQUEST; + conn->mysql_seqid = auth_resp->get_seqid() + 1; + break; + } + } + else if (auth_resp->get_auth_plugin_name().size() == 0 || + conn->state == ST_AUTH_SWITCH_REQUEST) + { + state_ = WFT_STATE_SYS_ERROR; + error_ = EBADMSG; + return 0; + } + + return auth_switch(auth_resp, conn); case ST_CHARSET_REQUEST: if (!resp->is_ok_packet()) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 8fad82e9..3a28e4e5 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -433,6 +433,9 @@ 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; @@ -463,6 +466,27 @@ int MySQLAuthResponse::decode_packet(const unsigned char *buf, size_t buflen) 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; } } diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index c88d8c1e..9a48dedc 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -163,15 +163,31 @@ public: memcpy(seed, seed_, 20); } + bool is_continue() const + { + return continue_; + } + + void set_auth_plugin_name(const std::string& name) + { + auth_plugin_name_ = name; + } + + void set_seed(const unsigned char seed[20]) + { + memcpy(seed_, seed, 20); + } + 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() { } + MySQLAuthResponse() : continue_(false) { } //move constructor MySQLAuthResponse(MySQLAuthResponse&& move) = default; //move operator From be464eb782ce82c3fc6c303076a7a92f80fdafa7 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Thu, 9 Feb 2023 03:27:45 +0800 Subject: [PATCH 11/17] Simplify codes. --- src/factory/MySQLTaskImpl.cc | 49 ++++++++++++++++------------------- src/protocol/MySQLMessage.cc | 6 ++--- src/protocol/MySQLMessage.inl | 14 ++-------- 3 files changed, 27 insertions(+), 42 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index 852df7d8..f62549fb 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) */ @@ -69,6 +69,7 @@ private: ST_SSL_REQUEST, ST_AUTH_REQUEST, ST_AUTH_SWITCH_REQUEST, + ST_CLEAR_PASSWORD_REQUEST, ST_PUBLIC_KEY_REQUEST, ST_CHARSET_REQUEST, ST_FIRST_USER_REQUEST, @@ -212,6 +213,8 @@ CommMessageOut *ComplexMySQLTask::message_out() req->set_seqid(conn->mysql_seqid); break; + case ST_CLEAR_PASSWORD_REQUEST: + conn->auth_plugin_name = "mysql_clear_password"; case ST_AUTH_SWITCH_REQUEST: req = new MySQLAuthSwitchRequest; auth_switch_req = (MySQLAuthSwitchRequest *)req; @@ -275,10 +278,8 @@ CommMessageIn *ComplexMySQLTask::message_in() return new SSLHandshaker(get_ssl()); case ST_AUTH_REQUEST: - resp = new MySQLAuthResponse; - break; - case ST_AUTH_SWITCH_REQUEST: + case ST_CLEAR_PASSWORD_REQUEST: resp = new MySQLAuthResponse; break; @@ -359,7 +360,8 @@ int ComplexMySQLTask::auth_switch(MySQLAuthResponse *resp, MyConnection *conn) { std::string name = resp->get_auth_plugin_name(); - if (name == "mysql_clear_password" && !is_ssl_) + if (name.empty() || (name == "mysql_clear_password" && !is_ssl_) || + conn->state != ST_AUTH_REQUEST) { state_ = WFT_STATE_SYS_ERROR; error_ = EBADMSG; @@ -399,17 +401,10 @@ int ComplexMySQLTask::keep_alive_timeout() case ST_AUTH_REQUEST: case ST_AUTH_SWITCH_REQUEST: - if (resp->is_error_packet()) - { - this->resp = std::move(*resp); - state_ = WFT_STATE_TASK_ERROR; - error_ = WFT_ERR_MYSQL_ACCESS_DENIED; - return 0; - } - + case ST_CLEAR_PASSWORD_REQUEST: if (resp->is_ok_packet()) { - if (res_charset_.size() != 0) + if (!res_charset_.empty()) conn->state = ST_CHARSET_REQUEST; else conn->state = ST_FIRST_USER_REQUEST; @@ -417,24 +412,24 @@ int ComplexMySQLTask::keep_alive_timeout() break; } + if (resp->is_error_packet() || conn->state == ST_CLEAR_PASSWORD_REQUEST) + { + this->resp = std::move(*resp); + state_ = WFT_STATE_TASK_ERROR; + error_ = WFT_ERR_MYSQL_ACCESS_DENIED; + return 0; + } + auth_resp = (MySQLAuthResponse *)resp; if (auth_resp->is_continue()) { + conn->mysql_seqid = auth_resp->get_seqid() + 1; if (is_ssl_) - auth_resp->set_auth_plugin_name("mysql_clear_password"); + conn->state = ST_CLEAR_PASSWORD_REQUEST; else - { conn->state = ST_PUBLIC_KEY_REQUEST; - conn->mysql_seqid = auth_resp->get_seqid() + 1; - break; - } - } - else if (auth_resp->get_auth_plugin_name().size() == 0 || - conn->state == ST_AUTH_SWITCH_REQUEST) - { - state_ = WFT_STATE_SYS_ERROR; - error_ = EBADMSG; - return 0; + + break; } return auth_switch(auth_resp, conn); diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 3a28e4e5..91024149 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.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) */ #include @@ -376,7 +376,7 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) *pos = (uint8_t)character_set_; if (password_.empty()) - str.push_back('\0'); + str.push_back((char)0); else if (auth_plugin_name_ == CACHING_SHA2_PASSWORD) { str.push_back((char)32); diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index 9a48dedc..a1983d2c 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -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) */ #include @@ -168,16 +168,6 @@ public: return continue_; } - void set_auth_plugin_name(const std::string& name) - { - auth_plugin_name_ = name; - } - - void set_seed(const unsigned char seed[20]) - { - memcpy(seed_, seed, 20); - } - private: virtual int decode_packet(const unsigned char *buf, size_t buflen); From 30a4c1333be76cacd4bc263554ab3a5cc0674f3d Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Thu, 9 Feb 2023 16:05:17 +0800 Subject: [PATCH 12/17] Unify codes --- src/factory/HttpTaskImpl.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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() From 507003975935ce25676682c15ed1588c6f2a3f17 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Thu, 9 Feb 2023 23:19:49 +0800 Subject: [PATCH 13/17] Support MySQL sha256 password with ssl --- src/factory/MySQLTaskImpl.cc | 128 +++++++++++++++++++++++----------- src/protocol/MySQLMessage.cc | 37 +++++++--- src/protocol/MySQLMessage.inl | 124 ++++++++++++++++++++++++++++---- 3 files changed, 225 insertions(+), 64 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index f62549fb..2e52cf75 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -70,7 +70,9 @@ private: ST_AUTH_REQUEST, ST_AUTH_SWITCH_REQUEST, ST_CLEAR_PASSWORD_REQUEST, - ST_PUBLIC_KEY_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 @@ -78,10 +80,10 @@ private: struct MyConnection : public WFConnection { - std::string auth_plugin_name; + std::string str; // shared by auth, auth_swich and rsa_auth requests unsigned char seed[20]; - unsigned char mysql_seqid; enum ConnState state; + unsigned char mysql_seqid; SSL *ssl; SSLWrapper wrapper; MyConnection(SSL *ssl) : wrapper(&wrapper, ssl) @@ -93,18 +95,6 @@ private: int check_handshake(MySQLHandshakeResponse *resp); int auth_switch(MySQLAuthResponse *resp, MyConnection *conn); - 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; - } - struct MySSLWrapper : public SSLWrapper { MySSLWrapper(ProtocolMessage *msg, SSL *ssl) : @@ -190,6 +180,7 @@ 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; @@ -201,27 +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_auth(username_, password_, db_, character_set_); + auth_req->set_auth_plugin_name(std::move(conn->str)); auth_req->set_seed(conn->seed); - req->set_seqid(conn->mysql_seqid); break; case ST_CLEAR_PASSWORD_REQUEST: - conn->auth_plugin_name = "mysql_clear_password"; + 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(conn->auth_plugin_name); + auth_switch_req->set_auth_plugin_name(std::move(conn->str)); auth_switch_req->set_seed(conn->seed); - req->set_seqid(conn->mysql_seqid); + 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: @@ -255,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() @@ -275,7 +289,7 @@ 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: @@ -283,6 +297,11 @@ CommMessageIn *ComplexMySQLTask::message_in() resp = new MySQLAuthResponse; 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; @@ -301,9 +320,12 @@ 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(MySQLHandshakeResponse *resp) @@ -342,10 +364,10 @@ int ComplexMySQLTask::check_handshake(MySQLHandshakeResponse *resp) auto *conn = this->get_connection(); auto *my_conn = new MyConnection(ssl); - my_conn->mysql_seqid = resp->get_seqid() + 1; - my_conn->auth_plugin_name = resp->get_auth_plugin_name(); + my_conn->str = resp->get_auth_plugin_name(); 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) @@ -360,24 +382,37 @@ int ComplexMySQLTask::auth_switch(MySQLAuthResponse *resp, MyConnection *conn) { std::string name = resp->get_auth_plugin_name(); - if (name.empty() || (name == "mysql_clear_password" && !is_ssl_) || - conn->state != ST_AUTH_REQUEST) + if (conn->state != ST_AUTH_REQUEST || + (name == "mysql_clear_password" && !is_ssl_)) { state_ = WFT_STATE_SYS_ERROR; error_ = EBADMSG; return 0; } - conn->mysql_seqid = resp->get_seqid() + 1; - conn->auth_plugin_name = std::move(name); + 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->state = ST_AUTH_SWITCH_REQUEST; + 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; @@ -385,8 +420,12 @@ int ComplexMySQLTask::keep_alive_timeout() return check_handshake((MySQLHandshakeResponse *)msg); auto *conn = (MyConnection *)this->get_connection(); - MySQLAuthResponse *auth_resp; - 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(); @@ -395,10 +434,6 @@ int ComplexMySQLTask::keep_alive_timeout() switch (conn->state) { - case ST_SSL_REQUEST: - conn->state = ST_AUTH_REQUEST; - break; - case ST_AUTH_REQUEST: case ST_AUTH_SWITCH_REQUEST: case ST_CLEAR_PASSWORD_REQUEST: @@ -423,17 +458,25 @@ int ComplexMySQLTask::keep_alive_timeout() auth_resp = (MySQLAuthResponse *)resp; if (auth_resp->is_continue()) { - conn->mysql_seqid = auth_resp->get_seqid() + 1; if (is_ssl_) conn->state = ST_CLEAR_PASSWORD_REQUEST; else - conn->state = ST_PUBLIC_KEY_REQUEST; + 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_RSA_AUTH_REQUEST: + break; + case ST_CHARSET_REQUEST: if (!resp->is_ok_packet()) { @@ -444,7 +487,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; @@ -456,6 +499,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 91024149..e6edc54b 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -38,7 +38,6 @@ namespace protocol #define MYSQL_NATIVE_PASSWORD "mysql_native_password" #define CACHING_SHA2_PASSWORD "caching_sha2_password" -#define SHA256_PASSWORD "sha256_password" #define MYSQL_CLEAR_PASSWORD "mysql_clear_password" MySQLMessage::~MySQLMessage() @@ -376,15 +375,15 @@ int MySQLAuthRequest::encode(struct iovec vectors[], int max) *pos = (uint8_t)character_set_; if (password_.empty()) - str.push_back((char)0); + str.push_back(0); else if (auth_plugin_name_ == CACHING_SHA2_PASSWORD) { - str.push_back((char)32); + str.push_back(32); str += __caching_sha2_password_encrypt(password_, seed_); } else { - str.push_back((char)20); + str.push_back(20); str += __native_password_encrypt(password_, seed_); } @@ -501,10 +500,6 @@ int MySQLAuthSwitchRequest::encode(struct iovec vectors[], int max) { buf_ = __caching_sha2_password_encrypt(password_, seed_); } - else if (auth_plugin_name_ == SHA256_PASSWORD) - { - // TODO - } else if (auth_plugin_name_ == MYSQL_CLEAR_PASSWORD) { buf_ = password_; @@ -519,6 +514,32 @@ int MySQLAuthSwitchRequest::encode(struct iovec vectors[], int max) 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::encode(struct iovec vectors[], int max) +{ + 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 a1983d2c..be2c148b 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -19,6 +19,7 @@ #include #include +#include #include #include #include "SSLWrapper.h" @@ -113,20 +114,20 @@ 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_auth_plugin_name(const std::string& name) + void set_auth_plugin_name(std::string name) { - auth_plugin_name_ = name; + auth_plugin_name_ = std::move(name); } void set_seed(const unsigned char seed[20]) @@ -187,14 +188,14 @@ public: class MySQLAuthSwitchRequest : public MySQLRequest { public: - void set_password(const std::string& password) + void set_password(std::string password) { - password_ = password; + password_ = std::move(password); } - void set_auth_plugin_name(const std::string& name) + void set_auth_plugin_name(std::string name) { - auth_plugin_name_ = name; + auth_plugin_name_ = std::move(name); } void set_seed(const unsigned char seed[20]) @@ -205,10 +206,10 @@ public: private: virtual int encode(struct iovec vectors[], int max); + /* Not implemented. */ virtual int decode_packet(const unsigned char *buf, size_t buflen) { - buf_.assign((const char *)buf, buflen); - return 1; + return -2; } std::string password_; @@ -223,6 +224,101 @@ public: 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; + } + + 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; +}; + ////////// inline mysql_parser_t *MySQLMessage::get_parser() const From 486762c8b16cc802aaffd0ea68498e6086b745e3 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Fri, 10 Feb 2023 03:35:54 +0800 Subject: [PATCH 14/17] Add MySQL RSA encrypt. --- src/factory/MySQLTaskImpl.cc | 13 ++++++--- src/protocol/MySQLMessage.cc | 55 +++++++++++++++++++++++++++++++++++ src/protocol/MySQLMessage.inl | 2 ++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index 2e52cf75..98449218 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -293,10 +293,14 @@ CommMessageIn *ComplexMySQLTask::message_in() case ST_AUTH_REQUEST: case ST_AUTH_SWITCH_REQUEST: - case ST_CLEAR_PASSWORD_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; @@ -437,6 +441,7 @@ int ComplexMySQLTask::keep_alive_timeout() case ST_AUTH_REQUEST: case ST_AUTH_SWITCH_REQUEST: case ST_CLEAR_PASSWORD_REQUEST: + case ST_RSA_AUTH_REQUEST: if (resp->is_ok_packet()) { if (!res_charset_.empty()) @@ -447,7 +452,9 @@ int ComplexMySQLTask::keep_alive_timeout() break; } - if (resp->is_error_packet() || conn->state == ST_CLEAR_PASSWORD_REQUEST) + 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; @@ -474,8 +481,6 @@ int ComplexMySQLTask::keep_alive_timeout() conn->state = ST_RSA_AUTH_REQUEST; break; - case ST_RSA_AUTH_REQUEST: - break; case ST_CHARSET_REQUEST: if (!resp->is_ok_packet()) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index e6edc54b..4e8a9b72 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -535,8 +535,63 @@ int MySQLPublicKeyResponse::encode(struct iovec vectors[], int max) return MySQLMessage::encode(vectors, max); } +int MySQLRSAAuthRequest::rsa_encrypt(void *ctx) +{ + EVP_PKEY_CTX *pkey_ctx = (EVP_PKEY_CTX *)ctx; + std::string pass = password_; + unsigned char *p = (unsigned char *)pass.c_str(); + unsigned char out[256]; + size_t outlen = 256; + + for (size_t i = 0; i < pass.size(); i++) + p[i] ^= seed_[i % 20]; + + if (EVP_PKEY_encrypt_init(pkey_ctx) > 0 && + EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_OAEP_PADDING) > 0 && + EVP_PKEY_encrypt(pkey_ctx, out, &outlen, p, pass.size()) > 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); + + EVP_PKEY_CTX *ctx; + EVP_PKEY *pkey; + int ret = -1; + + pkey = EVP_PKEY_new(); + if (pkey) + { + EVP_PKEY_assign_RSA(pkey, rsa); + ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (ctx) + { + ret = rsa_encrypt(ctx); + EVP_PKEY_CTX_free(ctx); + } + + EVP_PKEY_free(pkey); + } + else + RSA_free(rsa); + + if (ret < 0) + return -1; + return MySQLMessage::encode(vectors, max); } diff --git a/src/protocol/MySQLMessage.inl b/src/protocol/MySQLMessage.inl index be2c148b..b4caf495 100644 --- a/src/protocol/MySQLMessage.inl +++ b/src/protocol/MySQLMessage.inl @@ -307,6 +307,8 @@ private: return -2; } + int rsa_encrypt(void *ctx); + std::string password_; std::string public_key_; unsigned char seed_[20]; From 195dd5310e96dac193611ede92aed25debe40149 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Fri, 10 Feb 2023 16:09:07 +0800 Subject: [PATCH 15/17] Fix RSA encryption. Fully support sha256 and caching_sha2 now. --- src/protocol/MySQLMessage.cc | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 4e8a9b72..47a2c067 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -538,20 +538,25 @@ int MySQLPublicKeyResponse::encode(struct iovec vectors[], int max) int MySQLRSAAuthRequest::rsa_encrypt(void *ctx) { EVP_PKEY_CTX *pkey_ctx = (EVP_PKEY_CTX *)ctx; - std::string pass = password_; - unsigned char *p = (unsigned char *)pass.c_str(); unsigned char out[256]; size_t outlen = 256; - - for (size_t i = 0; i < pass.size(); i++) - p[i] ^= seed_[i % 20]; + 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 && - EVP_PKEY_encrypt(pkey_ctx, out, &outlen, p, pass.size()) > 0) + EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_OAEP_PADDING) > 0) { - buf_.assign((char *)out, 256); - return 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; @@ -569,7 +574,7 @@ int MySQLRSAAuthRequest::encode(struct iovec vectors[], int max) rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); BIO_free(bio); - EVP_PKEY_CTX *ctx; + EVP_PKEY_CTX *pkey_ctx; EVP_PKEY *pkey; int ret = -1; @@ -577,11 +582,11 @@ int MySQLRSAAuthRequest::encode(struct iovec vectors[], int max) if (pkey) { EVP_PKEY_assign_RSA(pkey, rsa); - ctx = EVP_PKEY_CTX_new(pkey, NULL); - if (ctx) + pkey_ctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pkey_ctx) { - ret = rsa_encrypt(ctx); - EVP_PKEY_CTX_free(ctx); + ret = rsa_encrypt(pkey_ctx); + EVP_PKEY_CTX_free(pkey_ctx); } EVP_PKEY_free(pkey); From d19bf606510728abd479895c8c4e941e1e597705 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Fri, 10 Feb 2023 16:59:43 +0800 Subject: [PATCH 16/17] Set auth plugin name to caching sha2 when server requires sha256 --- src/factory/MySQLTaskImpl.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/factory/MySQLTaskImpl.cc b/src/factory/MySQLTaskImpl.cc index 98449218..63d57174 100644 --- a/src/factory/MySQLTaskImpl.cc +++ b/src/factory/MySQLTaskImpl.cc @@ -369,6 +369,9 @@ int ComplexMySQLTask::check_handshake(MySQLHandshakeResponse *resp) auto *my_conn = new MyConnection(ssl); 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; @@ -481,7 +484,6 @@ int ComplexMySQLTask::keep_alive_timeout() conn->state = ST_RSA_AUTH_REQUEST; break; - case ST_CHARSET_REQUEST: if (!resp->is_ok_packet()) { From 47cf47b76da0e3d1d82f2ca9eadf53e61d6381f2 Mon Sep 17 00:00:00 2001 From: Xie Han <63350856@qq.com> Date: Fri, 10 Feb 2023 17:37:34 +0800 Subject: [PATCH 17/17] Check RSA public key format. --- src/protocol/MySQLMessage.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protocol/MySQLMessage.cc b/src/protocol/MySQLMessage.cc index 47a2c067..83a3a2b3 100644 --- a/src/protocol/MySQLMessage.cc +++ b/src/protocol/MySQLMessage.cc @@ -17,7 +17,6 @@ Wu Jiaxu (wujiaxu@sogou-inc.com) */ -#include #include #include #include @@ -573,6 +572,8 @@ int MySQLRSAAuthRequest::encode(struct iovec vectors[], int max) 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;