Advertisement
Guest User

Untitled

a guest
Mar 19th, 2017
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.62 KB | None | 0 0
  1. Connection::Connection(
  2. std::string const& username,
  3. std::string const& password,
  4. std::string const& database,
  5. std::map<std::string, std::string> const& options,
  6. ConectReader& pr,
  7. ConectWriter& pw)
  8. : packageReader(pr)
  9. , packageWriter(pw)
  10. {
  11. std::unique_ptr<RespPackage> initPack = recvMessage(
  12. {{0x0A, [](int firstByte, ConectReader& reader
  13. )
  14. {return new RespPackageHandShake(firstByte, reader);}
  15. }
  16. });
  17. std::unique_ptr<RespPackageHandShake> handshake = downcastUniquePtr<RespPackageHandShake>(std::move(initPack));
  18. packageReader.initFromHandshake(handshake->getCapabilities(), handshake->getCharset());
  19. packageWriter.initFromHandshake(handshake->getCapabilities(), handshake->getCharset());
  20.  
  21. RequPackageHandShakeResponse handshakeresp(username, password, options, database, *handshake);
  22. std::unique_ptr<RespPackage> ok = sendHandshakeMessage<RespPackage>(handshakeresp,
  23. {{0xFE, [](int firstByte, ConectReader& reader)
  24. {return new RespPackageAuthSwitchRequest(firstByte, reader);}
  25. }
  26. });
  27.  
  28. if (!ok)
  29. {
  30. throw std::domain_error("Connection::Connection: Handshake failed: Unexpected Package");
  31. }
  32. if (!(ok->isOK()))
  33. {
  34. throw std::domain_error(errorMsg("Connection::Connection: Handshake failed: Got: ", (*ok)));
  35. }
  36. }
  37.  
  38. #ifndef THORS_ANVIL_MYSQL_DETAILS_PACKAGE_RESP_HAND_SHAKE_H
  39. #define THORS_ANVIL_MYSQL_DETAILS_PACKAGE_RESP_HAND_SHAKE_H
  40.  
  41. #include "RespPackage.h"
  42. #include <string>
  43. #include <sstream>
  44. #include <ostream>
  45. #include <iomanip>
  46.  
  47. namespace ThorsAnvil
  48. {
  49. namespace MySQL
  50. {
  51.  
  52. class ConectReader;
  53.  
  54. class RespPackageHandShake: public RespPackage
  55. {
  56. std::string serverVersion;
  57. long connectionID;
  58. std::string authPluginData;
  59. long check;
  60. //--
  61. long statusFlag;
  62. long authPluginLength;
  63. std::string reserved;
  64. std::string authPluginName;
  65. bool isV9;
  66. long capabilities;
  67. char charset;
  68.  
  69. public:
  70. RespPackageHandShake(int firstByte, ConectReader& reader);
  71. virtual std::ostream& print(std::ostream& s) const;
  72. long getCapabilities() const {return capabilities;}
  73. long getCharset() const {return charset;}
  74. std::string const& getAuthPluginName() const {return authPluginName;}
  75. std::string const& getAuthPluginData() const {return authPluginData;}
  76. };
  77.  
  78. inline std::ostream& RespPackageHandShake::print(std::ostream& s) const
  79. {
  80. std::stringstream reservedDecoded;
  81. for (char x: reserved)
  82. {
  83. reservedDecoded << "0x" << std::hex << static_cast<int>(x) << " ";
  84. }
  85. std::stringstream authPluginDataDecoded;
  86. for (char x: authPluginData)
  87. {
  88. authPluginDataDecoded << "0x" << std::hex << static_cast<int>(x) << " ";
  89. }
  90.  
  91. return s << "RespPackageHandShake: "
  92. << "serverVersion(" << serverVersion << ") "
  93. << "connectionID(" << connectionID << ") "
  94. << "authPluginData(" << authPluginDataDecoded.str() << ") "
  95. << "check(" << check << ") "
  96. << "statusFlag( 0x" << std::hex << std::setw(8) << std::setfill('0') << statusFlag << ") "
  97. << "authPluginLength(" << authPluginLength << ") "
  98. << "reserved(" << reservedDecoded.str() << ") "
  99. << "authPluginName(" << authPluginName << ") "
  100. << "capabilities(" << capabilities << ") "
  101. << "isV9(" << isV9 << ") "
  102. << std::dec;
  103. }
  104.  
  105. }
  106. }
  107.  
  108. #endif
  109.  
  110. #include "ThorMySQL.h"
  111. #include "ConectReader.h"
  112. #include "RespPackageHandShake.h"
  113. #include <cassert>
  114.  
  115. using namespace ThorsAnvil::MySQL;
  116.  
  117. RespPackageHandShake::RespPackageHandShake(int firstbyte, ConectReader& reader)
  118. : RespPackage(reader, "HandShake")
  119. , serverVersion(reader.nulTerminatedString())
  120. , connectionID(reader.fixedLengthInteger<4>())
  121. , authPluginData(reader.fixedLengthString(8))
  122. , check(0)
  123. , statusFlag(0)
  124. , authPluginLength(0)
  125. , isV9(false)
  126. , capabilities(0)
  127. {
  128. // https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeV10
  129. assert(firstbyte = 0x0A);
  130.  
  131. if (reader.isEmpty())
  132. {
  133. isV9 = true;
  134. return;
  135. }
  136. check = reader.fixedLengthInteger<1>();
  137. capabilities = reader.fixedLengthInteger<2>();
  138.  
  139. if (reader.isEmpty())
  140. {
  141. return;
  142. }
  143.  
  144. charset = reader.fixedLengthInteger<1>();
  145. statusFlag = reader.fixedLengthInteger<2>();
  146. long cap2 = reader.fixedLengthInteger<2>();
  147. authPluginLength= 0;
  148.  
  149. capabilities = capabilities | (cap2 << 16);
  150.  
  151. if (capabilities & CLIENT_PLUGIN_AUTH)
  152. {
  153. authPluginLength= reader.fixedLengthInteger<1>();
  154. }
  155. else
  156. {
  157. int fill = reader.fixedLengthInteger<1>();
  158. assert(fill == 0);
  159. }
  160.  
  161. reserved = reader.fixedLengthString(10);
  162. std::string authPluginData2;
  163. if (capabilities & CLIENT_SECURE_CONNECTION)
  164. {
  165. authPluginData2 = reader.variableLengthString(std::max(13L, authPluginLength - 8L));
  166. }
  167. if (capabilities & CLIENT_PLUGIN_AUTH)
  168. {
  169. authPluginName = reader.nulTerminatedString();
  170. }
  171.  
  172. // Because of the way 13L is the min size for authPluginData2 data
  173. // This may exceed the actual size of the auth data. So after concatenating the
  174. // data make sure we only use the required length `authPluginLength`. This means
  175. // removing the extra terminating '' character.
  176. // see: mysql-5.7/sql/auth/sql_authentication.cc 538
  177. // the 13th byte is " byte, terminating the second part of a scramble"
  178. authPluginData = (authPluginData + authPluginData2).substr(0, authPluginLength-1);
  179. }
  180.  
  181. #ifndef THORS_ANVIL_MYSQL_PACKAGE_REQU_HANDSHAKE_RESPONSE_H
  182. #define THORS_ANVIL_MYSQL_PACKAGE_REQU_HANDSHAKE_RESPONSE_H
  183.  
  184. #include "RequPackage.h"
  185. #include "ThorSQL/SQLUtil.h"
  186. #include <string>
  187. #include <ostream>
  188.  
  189. namespace ThorsAnvil
  190. {
  191. namespace MySQL
  192. {
  193.  
  194. using Options=SQL::Options;
  195. class RespPackageHandShake;
  196. class ConectWriter;
  197.  
  198. class RequPackageHandShakeResponse: public RequPackage
  199. {
  200. std::string const& username;
  201. std::string authResponse;
  202. Options const& options;
  203. std::string const& database;
  204. std::string const& authPluginName;
  205. long capabilities;
  206.  
  207. public:
  208. RequPackageHandShakeResponse(std::string const& username,
  209. std::string const& password,
  210. Options const& options,
  211. std::string const& database,
  212. RespPackageHandShake const& handshake);
  213.  
  214. virtual std::ostream& print(std::ostream& s) const override;
  215. virtual void build(ConectWriter& writer) const override;
  216. };
  217.  
  218. }
  219. }
  220.  
  221. #endif
  222.  
  223. #include "ThorMySQL.h"
  224. #include "RequPackageHandShakeResp.h"
  225. #include "RespPackageHandShake.h"
  226. #include "ThorCryptWrapper.h"
  227.  
  228. using namespace ThorsAnvil::MySQL;
  229.  
  230. RequPackageHandShakeResponse::RequPackageHandShakeResponse(std::string const& username,
  231. std::string const& password,
  232. Options const& options,
  233. std::string const& database,
  234. RespPackageHandShake const& handshake)
  235. : RequPackage("RequPackageHandShakeResponse", "HandShake-Response")
  236. , username(username)
  237. , options(options)
  238. , database(database)
  239. , authPluginName(handshake.getAuthPluginName())
  240. , capabilities(handshake.getCapabilities())
  241. {
  242. if (authPluginName == "mysql_old_password")
  243. {
  244. throw std::runtime_error(
  245. errorMsg("ThorsAnvil::MySQL::HandshakeResponsePackage::HandshakeResponsePackage: ",
  246. "mysql_old_password: not supported"
  247. ));
  248. }
  249. else if (authPluginName == "mysql_clear_password")
  250. {
  251. throw std::runtime_error(
  252. errorMsg("ThorsAnvil::MySQL::HandshakeResponsePackage::HandshakeResponsePackage: ",
  253. "mysql_clear_password: not supported"
  254. ));
  255. }
  256. else if (authPluginName == "authentication_windows_client")
  257. {
  258. throw std::runtime_error(
  259. errorMsg("ThorsAnvil::MySQL::HandshakeResponsePackage::HandshakeResponsePackage: ",
  260. "authentication_windows_client: not supported"
  261. ));
  262. }
  263. else if (authPluginName == "sha256_password")
  264. {
  265. throw std::runtime_error(
  266. errorMsg("ThorsAnvil::MySQL::HandshakeResponsePackage::HandshakeResponsePackage: ",
  267. "sha256_password: not supported"
  268. ));
  269. }
  270. else if (authPluginName == "mysql_native_password")
  271. {
  272. // Requires CLIENT_SECURE_CONNECTION
  273. // SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
  274. ThorSHADigestStore stage1;
  275. thorSHA1(stage1, password);
  276.  
  277.  
  278. ThorSHADigestStore stage2;
  279. thorSHA1(stage2, stage1);
  280.  
  281. std::string extendedAuth = handshake.getAuthPluginData() + std::string(stage2, stage2 + SHA_DIGEST_LENGTH);
  282. ThorSHADigestStore extendedHash;
  283. thorSHA1(extendedHash, extendedAuth);
  284.  
  285. for (int loop=0;loop < SHA_DIGEST_LENGTH;++loop)
  286. {
  287. extendedHash[loop] = extendedHash[loop] ^ stage1[loop];
  288. }
  289. authResponse = std::string(extendedHash, extendedHash + SHA_DIGEST_LENGTH);
  290. }
  291. else
  292. {
  293. throw std::runtime_error(
  294. errorMsg("ThorsAnvil::MySQL::HandshakeResponsePackage::HandshakeResponsePackage: ",
  295. "UNKNOWN authentication method(", authPluginName, "): not supported"
  296. ));
  297. }
  298. }
  299.  
  300. void RequPackageHandShakeResponse::build(ConectWriter& writer) const
  301. {
  302. // These capabilities mirror the `mysql` tool.
  303. // We will leave this for now but it may change
  304. // as the understanding of the system updates.
  305. unsigned long cap = CLIENT_SET_CLIENT | CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | CLIENT_CONNECT_ATTRS
  306. | CLIENT_PLUGIN_AUTH | CLIENT_PS_MULTI_RESULTS | CLIENT_MULTI_RESULTS | CLIENT_MULTI_STATEMENTS
  307. | CLIENT_SECURE_CONNECTION | CLIENT_TRANSACTIONS
  308. | CLIENT_INTERACTIVE | CLIENT_PROTOCOL_41
  309. | CLIENT_LOCAL_FILES
  310. | CLIENT_CONNECT_WITH_DB | CLIENT_LONG_FLAG | CLIENT_LONG_PASSWORD;
  311.  
  312. // https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse41
  313. // Turn off flags not supported by the server
  314. unsigned long localCap = capabilities & cap;
  315. writer.writeFixedLengthInteger<4>(localCap);
  316.  
  317. long maxPacketSize = 0x01000000;
  318. writer.writeFixedLengthInteger<4>(maxPacketSize);
  319.  
  320. unsigned char charset = 0x21;
  321. writer.writeFixedLengthInteger<1>(charset);
  322.  
  323. std::string reserved(23, '');
  324. writer.writeFixedLengthString(reserved, 23);
  325.  
  326. writer.writeNullTerminatedString(username);
  327.  
  328. if (localCap & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)
  329. {
  330. writer.writeLengthEncodedInteger(authResponse.size());
  331. writer.writeFixedLengthString(authResponse, authResponse.size());
  332. }
  333. else if (localCap & CLIENT_SECURE_CONNECTION)
  334. {
  335. char val = authResponse.size();
  336. writer.writeFixedLengthInteger<1>(val);
  337. writer.writeFixedLengthString(authResponse, authResponse.size());
  338. }
  339. else
  340. {
  341. writer.writeNullTerminatedString(authResponse);
  342. }
  343.  
  344. if (localCap & CLIENT_CONNECT_WITH_DB)
  345. {
  346. writer.writeNullTerminatedString(database);
  347. }
  348.  
  349. if (localCap & CLIENT_PLUGIN_AUTH)
  350. {
  351. writer.writeNullTerminatedString(authPluginName);
  352. }
  353.  
  354. // TODO Add Key Values
  355. // For now empty the options
  356. if (localCap & CLIENT_CONNECT_ATTRS)
  357. {
  358. std::size_t size = 0;
  359. for (auto const& loop: options)
  360. {
  361. size += loop.first.size() + loop.second.size();
  362. }
  363. writer.writeLengthEncodedInteger(size);
  364. for (auto const& loop: options)
  365. {
  366. writer.writeLengthEncodedString(loop.first);
  367. writer.writeLengthEncodedString(loop.second);
  368. }
  369. }
  370. }
  371.  
  372. std::ostream& RequPackageHandShakeResponse::print(std::ostream& s) const
  373. {
  374. std::stringstream authRespDecoded;
  375. for (char x: authResponse)
  376. { authRespDecoded << "0x" << std::hex << static_cast<unsigned int>(static_cast<unsigned char>(x)) << " ";
  377. }
  378. std::stringstream keyValDecoded;
  379. for (auto const& val: options)
  380. { keyValDecoded << "KV(" << val.first << " => " << val.second << ") ";
  381. }
  382.  
  383. return s << "HandshakeResponsePackage: "
  384. << "username(" << username << ") "
  385. << "authResponse(" << authRespDecoded.str() << ") "
  386. << "options(" << keyValDecoded.str() << ") "
  387. << "database(" << database << ") "
  388. << "authPluginName(" << authPluginName << ") "
  389. << "capabilities(" << capabilities << ") ";
  390. }
  391.  
  392. #ifndef THORS_ANVIL_MYSQL_DETAILS_PACKAGE_RESP_EOF_H
  393. #define THORS_ANVIL_MYSQL_DETAILS_PACKAGE_RESP_EOF_H
  394.  
  395. #include "RespPackage.h"
  396. #include <string>
  397. #include <ostream>
  398. #include <iomanip>
  399. #include <cassert>
  400.  
  401. namespace ThorsAnvil
  402. {
  403. namespace MySQL
  404. {
  405.  
  406. class ConectReader;
  407.  
  408. class RespPackageAuthSwitchRequest: public RespPackage
  409. {
  410. public:
  411. std::string pluginName;
  412. std::string pluginData;
  413.  
  414. RespPackageAuthSwitchRequest(int firstByte, ConectReader& reader)
  415. : RespPackage(reader, "AuthSwitchRequest")
  416. , pluginName(reader.nulTerminatedString())
  417. , pluginData(reader.restOfPacketString())
  418. {
  419. // https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest
  420. assert(firstByte == 0xFE);
  421. }
  422. virtual std::ostream& print(std::ostream& s) const
  423. {
  424. s << "AuthSwitchRequest: "
  425. << "pluginName(" << pluginName << ") "
  426. << "pluginData(";
  427. for (auto c: pluginData)
  428. {
  429. s << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c) << " ";
  430. }
  431. s << ") " << std::dec;
  432. return s;
  433. }
  434. };
  435.  
  436. }
  437. }
  438.  
  439. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement