私は自分の問題を分析してデバッグするためのより良い方法を探してインターウェブを検索することに時間を費やしましたが、解決策を見つけることができないようです。だから私は尋ねると思いました。
簡単に言えば。ノンブロッキングのSSL転送プロキシを作成しようとしています。プロキシのサーバー部分は、自己署名サーバー証明書を使用しています。これは、自分のCA証明書を使用して署名しました。重要な場合は、libevを使用しています。最初に暗号化されていないプロキシを正常に作成し(Webトラフィックを盲目的に転送しました)、現在SSLを追加しようとしています。:)
クライアントをプロキシに接続する際に問題が発生します。自動テストが必要だったので、テストクライアントとしてwgetとsslの両方のs_clientを試しました。
sslサーバーのセットアップ(このコードは、EV_READイベントでlibevウォッチャーリスニングソケットaccept_handler()から呼び出されます):
/* setup client side ssl state (we are a SERVER) */
ctx = SSL_CTX_new(SSLv23_server_mode());
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLV2 | SSL_OP_ALL);
SSL_CTX_set_info_callback(ctx, client_info_cb);
SSL_CTX_set_cipher_list(ctx, "ALL:!SSLv2:-aNULL");
//SSL_CTX_load_verify_locations(ctx, CA_CERTIFICATE, NULL);
//SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(CA_CERTIFICATE));
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
SSL_CTX_set_verify_depth(ctx, 0);
SSL *client_ssl = SSN_new(ctx);
SSL_set_mode(client_ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
SSL_set_accept_state(client_ssl);
SSL_set_fd(client_ssl, client_fd);
/* initialize client handshake watchers */
ev_io_init(&ev_r_ch, client_handshake, client_fd, EV_READ);
ev_io_init(&ev_w_ch, client_handshake, client_fd, EV_WRITE);
... other watcher inits and set watcher data portions ...
/* start the read */
ev_io_start(loop, &ev_r_ch);
libevループは次のように設定されました。
loop = ev_default_loop(EVFLAG_AUTO);
シャットダウンフラグやその他のハウスキーピングアクティビティをチェックするためのタイマーなどがあります。
私のclient_handshake()メイン関数は基本的に次のようになります。
int t = SSL_accept(client_ssl);
if (t == 1) { // SSL_ERROR_NONE
end_client_handshake(...);
} else {
int err = SSL_get_error(client_ssl, t);
if (err == SSL_ERROR_WANT_READ) {
ev_io_stop(loop, &ev_w_ch);
ev_io_start(loop, &ev_r_ch);
}
else if (err == SSL_ERROR_WANT_WRITE) {
ev_io_stop(loop, &ev_r_ch);
ev_io_start(loop, &ev_w_ch);
}
else ...
}
client_info_cb()で、進行中に内部SSL状態を出力し、print()関数から次の情報を取得します。
client_info_cb: 8193: SSLv3 read client hello A
client_info_cb: 8193: SSLv3 write server hello A
client_info_cb: 8193: SSLv3 write certificate A
client_info_cb: 8193: SSLv3 write server done A
client_info_cb: 8193: SSLv3 flush data
client_info_cb: 8194: SSLv3 read client certificate A
client_info_cb: 8194: SSLv3 read client certificate A
そして、これはそれがぶら下がっているところです。SSL_ERROR_WANT_READ(上記の2番目の「クライアント証明書Aの読み取り」メッセージの後にSSL_get_error()が返すもの)を検出した場合は、client_handshake()関数をSSL_accept()の周りでloop(1){}に変更してみました。
それは私を無限ループに陥らせ、SSL_accept()を継続的に呼び出しました。
SSLステートマシンには、取得できない追加情報が必要であると想定しています。最初はソケットから読み続ける必要があると思いましたが、うまくいきませんでした。
また、上記のクライアント証明書(SSL_VERIFY_NONE)を検証したくないことを明示的に指定したため、プロキシがクライアント証明書を読み取ろうとしている理由についても混乱しています。その機能の目的を誤解していない限り。
誰かがこれについて何か洞察を持っているなら、私は感謝するでしょう。または、おそらくこの問題をデバッグするためのより良い方法です。strace()はこれには役に立たず、wgetまたはs_clientから適切なリターン/エラーメッセージを受け取りません。
SSLのステートマシン内でalert_callbacksとmsg_callbacksを設定しようとしましたが、情報コールバックよりも多くの情報が得られませんでした。
この時点では、それがソケットの問題なのか、SSLの問題なのか、それとも何なのかわかりません。
edit1:accept_handler()では、accept()を終了する前に、プロキシしているホストの証明書を検証するために、最初にsslを介してサーバーに接続していることを指摘しておきます。操作の順序を逆にして、connect()を接続する前に最初にaccept()を実行すると、機能します。
edit2:s_clientとプロキシの間のtcpdump出力を確認してみました。client_info_cbで参照されているサーバーデータの書き込みとデータのフラッシュの後、クライアントは「クライアントキー交換」、「暗号仕様の変更」、および「暗号化された終了メッセージ」を送信します。ただし、SSLステートマシンはクライアント証明書を探していますか?
--> Client Key Exchange
write to 0x9547a78 [0x9592e90] (523 bytes => 523 (0x20B))
0000 - 16 03 01 02 06 10 00 02-02 02 00 be 51 c7 3d 77 ............Q.=w
0010 - 5a b3 9e 28 81 f4 4e b5-63 ce ce 0b 19 f3 85 64 Z..(..N.c......d
0020 - 29 0e e8 22 83 b8 60 a6-54 e3 7a 62 b3 37 d8 04 ).."..`.T.zb.7..
0030 - 6c f1 8e ff 50 44 ed cc-7b 08 61 0c 16 88 f4 61 l...PD..{.a....a
0040 - 7b 8d f2 1e 04 1d 74 3d-cc ee a4 93 d3 bb 90 ee {.....t=........
<snip>
--> Change Cipher Spec
write to 0x9547a78 [0x9592e90] (6 bytes => 6 (0x6))
0000 - 14 03 01 00 01 01
--> Finished Message ......
write to 0x9547a78 [0x9592e90] (53 bytes => 53 (0x35))
0000 - 16 03 01 00 30 9a 88 8b-14 d6 d1 f1 f7 d8 0d ac ....0...........
0010 - 38 cd 54 78 26 85 7b 11-c8 e9 db 8d a2 0c 6a a8 8.Tx&.{.......j.
0020 - d4 e7 d4 ad 5d 7a 6d 47-eb f9 5f 2c f6 ca 6a 1f ....]zmG.._,..j.
0030 - 17 a6 58 25 41 ..X%A