LoginSignup
0

More than 5 years have passed since last update.

OpenSSL ソースリーディング その2

Last updated at Posted at 2018-06-24

前回からの続き。
read_state_machine/write_state_machineを見ます。

read_state_machine

ネゴシエーションパケットの読み込み側の処理です。
この処理は以下のフローから成ります。

  • ヘッダの読み込み
  • transition
  • ボディの読み込み
  • process_message
  • post_process_message

post_process_message後に書き込みフェーズに移行する場合はSUB_STATE_FINISHEDを返却します。

transition/process_message/post_process_messageの処理の実体は、クライアント側であればssl/statem/statem_clnt.cに、サーバ側であればssl/statem/statem_srvr.cにあります。

ヘッダの読み込み

まだレコードヘッダを読み込んでいない場合、read_stateはREAD_STATE_HEADERであり、tls_get_message_header関数でレコードヘッダとハンドシェイクタイプ、長さ、バージョンまでのデータをソケットから読み出します。

transition

現在の状態と、読み込んだレコードのタイプから、SSL/TLSネゴシエーション状態の遷移を行います。
ネゴシエーション状態は、「ClientHello読み込み」や「ServerHello送信」のことであり、include/openssl/ssl.hのOSSL_HANDSHAKE_STATEのenum型です。

サーバ側のServerHelloDone送信状態(TLS_ST_SW_SRVR_DONE)で、read側のtransitionを行う場合、読み込んだパケットのタイプがClientKeyExchangeであれば、状態をサーバ側でKeyExchangeを受信した状態(TLS_ST_SR_KEY_EXCH)に遷移することになります。

ボディの読み込み

ヘッダの読み込みが完了した場合、取得したデータの長さ分をtls_get_message_body関数で読み込みます。
ソケットがブロッキングで動作している場合、レコードの読み込みが完了するまで、ヘッダの読み込み処理やボディの読み込み処理から戻りません。

process_message

ネゴシエーションデータの読み込みを行います。

post_process_message

ネゴシエーションデータの読み込み後の処理を行います。この処理は不要な場合は行いません。
OpenSSL-1.1.0hのサーバ側では、ClientHello受信時と、ClientKeyExchange受信時しか処理を行いません。

write_state_machine

ネゴシエーションデータの書き込み(送信)側の処理です。
この処理は以下のフローから成ります。

  • transition
  • pre_work
  • 送信処理
  • post_work

transition

現在の状態から、SSL/TLSネゴシエーション状態の遷移を行います。
ネゴシエーション状態は、読み込み側と共通のOSSL_HANDSHAKE_STATEのenum型です。

サーバ側のServerHello送信状態(TLS_ST_SW_SRVR_HELLO)の場合、ネゴシエーションの状況によって、ChangeCipherSpecを呼び出すか、サーバ証明書の送信に入るかなど、分岐します。

pre_work

送信前処理です。pre_workに登録した関数自体では、やることはあまりありません。
pre_work後にconstruct_messageで取得できる関数を利用して、ネゴシエーションパケットを構築します。

送信処理

statem_do_write関数により、レコードレイヤを構築して、ソケットに送信依頼を行います。

post_work

送信後処理です。
連続するネゴシエーションパケットがない場合は、ソケットをflushしてデータを送り出します。

state_machine後の動作

ブロッキングソケットの場合、ネゴシエーションが完了するまでループから抜けません。そのため、SSL_accept関数から戻った場合、ネゴシエーションが完了したか、失敗したかのいずれかになります。

ノンブロッキングソケットで、ソケットの読み込みデータがまだない場合、tls_get_message_header関数やtls_get_message_body関数が0を返却します。その場合はread_state_machine関数でSUB_STATE_ERRORを返却し、state_machine関数のwhileループから抜け、SSL_accept関数から戻ります。
その場合、SSL_get_error関数でSSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITEを取得できますので、再度SSL_accept関数やSSL_do_handshake関数を呼び出して、ネゴシエーションを再開させます。

次回

SSL_read関数を見ていきます。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0