MySQLユーザーのためのMySQLプロトコル入門#2

前回の記事ではInitial PacketまでParseしました。今回はAuth Response Packetを作って認証までやってみましょう

Handshake Response Packet

認証の一連の流れはhttp://dev.mysql.com/doc/internals/en/connection-phase.htmlに書いてあるので図をさらっと眺めつつ行きます。

ServerからInitial Packetを受け取った後に、ClientがHandshake Response Packetを作ってServerに送信すれば認証結果が返されます。

毎度のごとくdev.mysql.comからデータの定義の説明を参照するんですが、if文が入っていて分かりづらいので認証を通すために必要な部分だけを抜粋してみました:

認証さえとおせればいいのでこれだけ。Capability FlagsはCLIENT_PROTOCOL_41, CLIENT_SECURE_CONNECTION, CLIENT_LONG_PASSWORD, CLIENT_TRANSACTIONS, CLIENT_LONG_FLAGあたりをセットし、その他のFlagはとりあえず横においておきましょう。

Capability Flagsが決まればHandShake Packetのデータサイズが決まるので、bufferを確保してデータを積んで行きます。が、そもそもauth-responseってのがデータサイズよくわかりませんね。何でしょう、コレ?

auth-responseをどうすればよいかは実はServerから最初に送られてくるConnection Phase Packet中のcapability flagsとPlugin名の指定があるのでそれを参照します。
通常の場合mysql_native_passwordとCLIENT_SECURE_CONNECTIONを使え、と指示がされているので
CLIENT_SECURE_CONNECTIONを調べてみたところ、Secure Password Authenticationを使え、ということなのでとりあえずやってみましょう。

Secure Connection

Secure Password Authenticationの場合random dataはInitial Packet中のauth-plugin-data-part-1とauth-plugin-data-part-2の12byteを連結した値となるので前回parseした部分からデータを持ってきます。(auth_plugin_data_part2の最後のデータはゴミデータなので12byte分だけ連結させます)

生成するhash値は下記の通りで生成できます。passwordのsha1 hashデータの各値に対してsha1(random_data + sha1(sha1(password)))して生成したデータをXORしていけば出来上がりです。

これでauth-response-dataにつっこむhash値もできました。あとはbufferの確保をしてたんたんとデータを積んでいくだけです。

MySQL Packetの書き込みから通信まで

MySQL PacketのHeaderは4byte(uint24 length, uint8 sequence_id)というのは前回説明しました。書き込み時は読み込み時と同じようにpayloadの先頭にヘッダを置けばOKです。

断片的なコードで説明しても分かりづらいので、Goで一連の流れを書いてあるのでこれで認証が成功するかやってみましょう。

実際のコマンドの実行結果はこんな感じになるはずです。最後のpayloadが00で始まってれば認証成功です。

OK Packetが来たので認証が成功しましたね!

とはいえ、これだとまだ何もできないので次回はCOM_QUERYを通してクエリの実行から結果の取得までやってみましょう。

それでは、よいコーディングを

参考

Author: chobi_e

PHP, Golang, Cあたりがメインだったんですが気づいたらUnityやってました。なんでもやさん