仙石浩明の日記

2011年6月2日

Android 端末上で透過型プロキシを動かしてみる 〜 VPN のように使えて、しかも省電力 hatena_b

透過型プロキシ (Transparent Proxy) というのは、 ブラウザから 「見えない」 プロキシのこと。 ブラウザ自身は WWW サーバにアクセスしているつもりなのに、 ブラウザが送信したリクエストをプロキシが横取りし、 プロキシから出し直す。 サーバからのレスポンスは当然プロキシに返り、 プロキシがそれをブラウザに送信するのだけど、 パケットがブラウザに届くまでの間に送信元アドレスが書き換えられて、 サーバから直接レスポンスが届いたようにブラウザからは見える。

フツーの 「見える」 プロキシは、 ブラウザ等でプロキシ設定が必要であるのに対し、 透過型プロキシだと設定が不要。 だから一部の ISP (インターネット接続プロバイダ) などで、 フツーのプロキシの代りに使われていたりする (ユーザにプロキシ設定の方法を説明する必要がなくてサポートコストが削減できる)。 あるいは企業等で、 従業員が仕事と関係ない Web ページを閲覧していないか監視するために、 社内から社外へ接続するゲートウェイ等に透過型プロキシを設置して、 社外への http アクセスを記録していたりする (監視だけならパケットをダンプするだけでも用が足りるが、 アクセス先のサイト毎に細かな制御を行なおうとすると、 プロキシを使った方が楽)。

このように、 透過型プロキシはその存在を隠すために使われることが多いが、 ブラウザにプロキシ設定の機能が無い場合は、 透過型プロキシを使わざるを得ない。 例えば、 Android 端末で使われるブラウザの多くが、 なぜかプロキシ設定の機能を持っていない。 プロキシ経由でアクセスされると、 モバイル端末からのアクセスなのか、 フツーの PC からのアクセスなのか、 区別がつかなくなってしまうので、 あえてプロキシ設定できないようにしているのかも知れないが、 プロキシを使わないとアクセスできない場合は困ってしまう。

私の場合、 勤務先の LAN 内のサーバに社外からアクセスするとき、 社内アクセス専用のプロキシ (ここでは仮にホスト名を proxy.klab.org とする) を利用している。 例えば senri.gcd.org (自宅のサーバ、つまり社外) からこのプロキシをアクセスすると、 こんな感じ:

senri:~ $ openssl s_client -connect proxy.klab.org:443 -cert cert.pem -key key.pem -CApath /usr/ssl/certs -quiet
Enter pass phrase for key.pem:xxxxxxxx

depth=1 /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
verify return:1
depth=0 /serialNumber=-cn4oMJtlqoqfQZaTat68U68dNVbM8iQ/C=JP/O=*.klab.org/OU=GT41256819/OU=See www.rapidssl.com/resources/cps (c)09/OU=Domain Control Validated - RapidSSL(R)/CN=*.klab.org
verify return:1
CONNECT irc.klab.org:6667 HTTP/1.1

HTTP/1.0 200 OK

:irc.klab.org NOTICE AUTH :*** Looking up your hostname...
:irc.klab.org NOTICE AUTH :*** Found your hostname

行末に 「」 がある行が入力行。 「」 は Enter キー押下。 秘密鍵 key.pem でクライアント認証を受けて proxy.klab.org に接続し、 CONNECT リクエストを送信することにより、 社内の任意のサーバ:ポートに接続できる。 上記の例では社内 IRC サーバである irc.klab.org:6667 に接続しているが、 もちろん社内の任意の WWW サーバにも接続できる。

説明を簡単にするため proxy.klab.org と書いたが、 実際には VPN Warp を使っている (だから proxy.klab.org というサーバは存在しない)。 VPN Warp も stone で接続することができるので、 以下の説明は VPN Warp の場合もほとんど同様に適用することができる。

プロキシというと、 ファイアウォールの内側から外部のインターネットをアクセスするために用いるものと思っている人が多いせいか、 外からファイアウォールの内側をアクセスするためのプロキシは、 特にリバースプロキシ (reverse proxy) と呼ばれることもある。

proxy.klab.org を透過型プロキシとして利用できれば、 社内の任意のサーバに透過的にアクセスできるようになる。 例えばこんな感じ:

senri:~ $ adb shell
$ getprop ro.build.description
soju-user 2.3.4 GRJ22 121341 release-keys
$ su
# busybox traceroute irc.klab.org
traceroute to irc.klab.org (10.10.0.18), 30 hops max, 38 byte packets
 1  110.158.20.29 (110.158.20.29)  701.203 ms  90.517 ms  85.570 ms
 2  *  *  *
 3  *  *  *
 4  110.158.18.138 (110.158.18.138)  79.540 ms !A  *  *
 5  *  *  *
 6  *  ^C
senri:~ $ adb shell
$ telnet irc.klab.org 6667
:irc.klab.org NOTICE AUTH :*** Looking up your hostname...
:irc.klab.org NOTICE AUTH :*** Found your hostname

soju-user 2.3.4 GRJ22」 つまり Android 2.3.4 な Nexus S で、 パケットが届かないはずの irc.klab.org (10.10.0.18) に対して、 telnet でアクセスできている。 telnet でアクセスできるということは、 もちろん任意の IRC アプリで irc.klab.org にアクセスできるということ。

VPN (Virtual Private Network) でも同じことができる! という声が聞こえてきそうだが、 VPN だと TCP でデータを送受信していないときも、 VPN セッションを張っているだけでいろんなパケット (例えば ARP) が行き交って、 そのたびに電波が飛んで電池を消耗するので、 あまりモバイル向きではないと思う。

あるいは、 ConnectBot などを使って ssh で port forward を行なう方法もあるが、 これまた ssh セッションを張りっぱなしだと電池消耗が心配だし、 かといって IRC を使う前に毎回 ssh 接続を行なうのはメンドクサイ。 また、 通信先/宛先ポートが増えるたび port foward 設定を追加するのもメンドクサイ。

その点、 透過型プロキシだと実際にパケットが飛ぶときのみ通信が行なわれるので、 電池消耗をあまり心配せずに TCP セッションを張りっぱなしにできる。 また、 LAN 内の通信先/宛先ポートが増えても設定変更は不要。 モバイルで LAN 内にアクセスする方法として最適だと思う (便利なのに普及していないのはナゼ?)。

前述したように、 透過型プロキシでは、 クライアントが送信した往路パケットの宛先を書き換えてプロキシへ送り込む必要がある。 これは、 Linux (もちろん Android を含む) だと iptables コマンドを使えば簡単に実現できる:

senri:~ $ adb shell
$ su
# iptables -t nat -p tcp -A OUTPUT -j REDIRECT -d 10.10.0.0/24 --to-port 9980
getsockopt for multiport failed strangely: No such file or directory
getsockopt for multiport failed strangely: No such file or directory
FIX ME! implement getprotobyname() bionic/libc/bionic/stubs.c:378
# 

これで 10.10.0.* (LAN 内の IP アドレス) 宛の TCP パケットが宛先を書き換えられて、 外部に送信されることなく端末自身の 9980番ポートに届く。 ただし端末によっては CONFIG_NF_NAT を有効にしていないカーネルを使っている場合があり (froyo 以前の端末は大半が該当するかも?)、 上記 iptables コマンドを実行すると 「can't initialize iptables table `nat': Table does not exist (do you need to insmod?)」 などのエラーメッセージが出てしまう。 その場合は、 CONFIG_NF_NAT を有効にしてカーネルを再構築すればよい (Linux カーネルは GPL なので再構築に必要なソースは入手できるはず)。

上記の実行例では 「getsockopt for multiport failed strangely: No such file or directory」 というエラーメッセージが出ているが、 これはカーネルに xt_multiport モジュールが組み込まれていないにもかかわらず、 iptables コマンドには multiport 拡張が組み込まれていて、 iptables コマンド実行時に multiport 拡張のバージョンをチェックしようと getsockopt で IPT_SO_GET_REVISION_MATCH オプションの値を得ようとするため。 カーネルの xt_find_revision (net/netfilter/x_tables.c) が xt_multiport モジュールを見つけられず、 ENOENT (No such file or directory) を返すため、 このようなエラーメッセージが表示される。 上記の例では multiport 拡張を使っていないので、 このエラーは無視して構わない。
また、 「FIX ME! implement getprotobyname()」 というエラーメッセージも出ているが、 これは Android の libc である bionic において getprotobyname(3) がまだ実装されておらず、 現時点では常に NULL を返している、 という意味。 iptables コマンドはプロトコル名 (例えば 「tcp」) からプロトコル番号 (tcp の場合なら 6) への変換テーブルを自前で持っているので、 getprotobyname(3) が NULL を返しても問題無い。

あとは 9980番ポートに届いたパケットを、 proxy.klab.org へ転送してやればよい。 ただし、 社内アクセス専用プロキシ proxy.klab.org を利用するには、 前述したように (1) SSL クライアント認証を受けることが必要。 さらに、 クライアント (上記の例だと telnet コマンドや IRC アプリ) がどこへ接続しようとしていたかを調べ、 proxy.klab.org へ (2) CONNECT メソッドを使って接続先を伝える必要がある (上記の例だと、 「CONNECT irc.klab.org:6667 HTTP/1.1」 を送信)。

まず (1) は拙作 stone を使うと簡単に実現できる:

senri:~ $ adb shell
$ cd /data/local/tmp
$ stone -l -q cert=cert.pem -q key=key.pem -q passfile=key.passwd \
        proxy.klab.org:443/ssl localhost:8080 &
$ telnet localhost 8080
CONNECT irc.klab.org:6667 HTTP/1.1

HTTP/1.0 200 OK

:irc.klab.org NOTICE AUTH :*** Looking up your hostname...
:irc.klab.org NOTICE AUTH :*** Found your hostname

クライアントから localhost:8080 に接続すると、 stone が SSL クライアント認証を受けて暗号化を行なった上で proxy.klab.org:443 へ転送してくれる。 つまりクライアントから見ると、 localhost:8080 に認証不要なプロキシがあるように見える。

「-l」 オプションは、 stone のログ出力を Android の logging system へ出力するための指定 (指定しないと stone は標準出力にログを出す)。 logging system へ出力されたログは、 logcat コマンドやアプリで見ることができる。

SSL クライアント認証のための鍵の指定 (「-q cert=」「-q key=」「-q passfile=」オプション) は、 前述した openssl の指定方法とほとんど同じだが、 stone の場合は Android の認証情報ストレージ (Credential storage) を利用することもできる (後述)。

Android 版 stone は、 「パケットリピータ「stone」を Android へポーティング」 から入手できる。 あるいは CVS レポジトリの中の Android.mk を使って最新版の stone.c をビルドしてもよい。

(2) を実現するには、 クライアントが送信し 9980番ポートに届いてしまったパケットの、 元々の宛先を知る必要がある。 パケットの宛先は既に書き換えられてしまっているので役に立たないが、 Linux では getsockopt を使って SO_ORIGINAL_DST オプションの値を調べることにより、 TCP 接続の元々の宛先を得ることができる。 もちろん、 書き換えられた後で外部から届いた TCP 接続の 「元々の宛先」 なんて分かるわけはなく、 あくまで同一マシンで書き換えられて (正確に言えば PREROUTING あるいは OUTPUT chain で REDIRECT されて)、 そのままマシンから出ること無く同一マシンに着信した TCP 接続のみ。

stone では TCP 接続を転送する際、 TCP セッションの冒頭に 「CONNECT irc.klab.org:6667 HTTP/1.1」 等の文字列 (リクエストヘッダ) を付加することができる。 さらに付加する文字列として、 「CONNECT \D HTTP/1.1」 などと 「\D」 を指定すると、 「元々の宛先」 で置き換えてくれる。 つまり、 以下のように stone を実行することで (2) が実現できる:

senri:~ $ adb shell
$ su
# stone -l -q store=sengoku proxy.klab.org:443/ssl,http 9980 \
        'CONNECT \D HTTP/1.1' &
# exit
$ telnet irc.klab.org 6667
:irc.klab.org NOTICE AUTH :*** Looking up your hostname...
:irc.klab.org NOTICE AUTH :*** Found your hostname

転送先の指定 (上記実行例では 「proxy.klab.org:443」) には 「/」 に続けて拡張子をつけることができる。 「ssl」 は SSL で暗号化した上で転送する指定で、 「http」 が 「CONNECT ...」 を proxy.klab.org:443 へ送信するための指定。 「http」 をつけたときは、 転送元 (上記実行例では 「9980」) の次にもう一つ文字列引数が必要になり、 この文字列 ('CONNECT \D HTTP/1.1') をリクエストメソッドとして、 転送先へ送信する。

「-q store=sengoku」 オプションを指定することによって stone は Android の認証情報ストレージ (Credential storage) から秘密鍵等を読み込む。

「認証情報ストレージ」 というのは鍵ペア (証明書と秘密鍵) および CA証明書を保存できるストレージで、 実体は /data/misc/keystore にある。 もちろん暗号化されていて、 stone などのプログラムや、 アプリなどから鍵を利用するには、 Android の設定メニューから 「位置情報とセキュリティの設定」 (Location & security) の中の 「安全な認証情報の使用」 (Use secure credentials) をチェックしてパスワードを入力する必要がある。

stone のオプションで秘密鍵を指定する際、 「-q key=key.pem -q passfile=key.passwd」 などと秘密鍵および秘密鍵のパスワードをファイルで指定するよりは、 「-q store=sengoku」 などと指定 (「sengoku」 は鍵ペアの CN) して認証情報ストレージを使った方が安全といえる。

認証情報ストレージに格納された鍵は、 格納したアプリの権限 (Android ではアプリごとにユーザID が割当てられる) でないと読み出せないが、 設定メニューの「位置情報とセキュリティの設定 ⇒ USBストレージからのインストール」 で格納すると system (uid=1000) 権限で格納された扱いになる。 しかし system 権限だと socket(3) が Permission denied (errno=EACCES) になるので、 stone の実行権限としては不適当。

じゃ、どうすれば? と思ってソース frameworks/base/cmds/keystore/keystore.c を見てみると、 root (uid=0), wifi (1010), vpn (1016) でも uid=1000 な鍵を読むことができるらしい。 上記実行例では (安易だが) root で走らせることにした。

WPA EAP-TLS な無線LAN AP に接続するときに使用する鍵ペアも、 設定メニューの 「USBストレージからのインストール / Install from USB storage」 を使って認証情報ストレージに格納しておく。 wpa_supplicant は wifi (uid=1010) 権限で動くので読み出すことができる。
にしても、 「USBストレージ」 とは何ぞ? (なぜ USB が関係あるのか?) と思ってしまうが、 Android 端末によって 「SD カードからインストール / Install from SD card」 だったり 「Install encrypted certificates」 だったりと、 いろいろなバリエーションがあるようだ。 Nexus S の場合、 「SD カード」 が無いので 「SD カードからインストール」 と書くわけにはいかなかったのだろうが、 普通に 「証明書のインポート」 でいいのではないかと思う。

以上で、 Android 端末上で透過型プロキシを動かすことができた。 あとは Android のブート時に、 前述した iptables コマンドと stone を実行するように設定するだけ。 Nexus S や Nexus One および Google の設定をそのまま踏襲している Android 端末だと、 /init.rc において /system/etc/install-recovery.sh を実行する設定になっているので、 install-recovery.sh に以下のスクリプトを追記すればよい。

iptables -t nat -p tcp -A OUTPUT -j REDIRECT -d 10.10.0.0/24 --to-port 9980
stone -l -n -D -q verbose -q store=sengoku -q verify \
    proxy.klab.org:443/ssl,http 9980 'CONNECT \D HTTP/1.1'

CyanogenMod などのカスタムROM だと、 ブート時に 「run-parts /system/etc/init.d」 を実行してくれるので、 /system/etc/init.d ディレクトリの中に上記内容のスクリプトを 30stone などのファイル名で入れておけば、 ブート時に実行される。

6月6日追記:

これで OK と思っていたら、 テザリングすると iptables で設定した内容が消えてしまった。 正確に言うと、 テザリングを開始すると nat table の POSTROUTING chain に以下のルールが追加され、

Chain POSTROUTING (policy ACCEPT 26086 packets, 1638K bytes)
 pkts bytes target     prot opt in     out     source               destination

    0     0 MASQUERADE  0    --  *      rmnet0  0.0.0.0/0            0.0.0.0/0

テザリングを終了すると filter table の INPUT, OUTPUT, FORWARD の各 chain と、 nat table の全 chain が初期化 (iptables -F) されてしまう。 つまり POSTROUTING chain に追加した上記ルールだけ消せばいいものを、 全 chain の初期化を行なうものだから、 透過型プロキシを実現するために設定した nat table の OUTPUT chain まで巻き添えを食って消されてしまう。

原因は、 /system/bin/netd において NAT を disable する際 (テザリングを終了させるとき) setDefaults() で全 table を初期化する実装 (なんて乱暴な!) になっているため。

iptables -t nat -A POSTROUTING ... で POSTROUTING chain へ追加したルールは、 きちんと iptables -t nat -D POSTROUTING ... で削除すべきだと思うのだが...(*_*) 追加と削除が対にならない事態に陥ることを恐れて、 こういう実装にしているのかも知れないが、 それなら対にならなかったとき (iptables がエラーになったとき) のみ初期化すればいいだけの話。

以下のような修正で、 初期化ではなく個々のルールを削除させることができる。

--- system/netd/NatController.cpp.org        2011-02-28 16:56:08.573705126 +0900
+++ system/netd/NatController.cpp        2011-06-06 18:52:08.730041739 +0900
@@ -92,15 +92,15 @@
     char cmd[255];
 
     // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
-    if (add == false) {
-        if (natCount <= 1) {
-            int ret = setDefaults();
-            if (ret == 0) {
-                natCount=0;
-            }
-            return ret;
-        }
-    }
+//  if (add == false) {
+//      if (natCount <= 1) {
+//          int ret = setDefaults();
+//          if (ret == 0) {
+//              natCount=0;
+//          }
+//          return ret;
+//      }
+//  }
 
     if (!interfaceExists(intIface) || !interfaceExists (extIface)) {
         LOGE("Invalid interface specified");
@@ -128,8 +128,8 @@
     }
 
     // add this if we are the first added nat
-    if (add && natCount == 0) {
-        snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
+    if ((add && natCount == 0) || (!add && natCount == 1)) {
+        snprintf(cmd, sizeof(cmd), "-t nat -%s POSTROUTING -o %s -j MASQUERADE", (add ? "A" : "D"), extIface);
         if (runIptablesCmd(cmd)) {
             // unwind what's been done, but don't care about success - what more could we do?
             setDefaults();;

ちなみに、 com.android.server.NetworkManagementService の enableNat() と disableNat() が netd にコマンドを送って NAT の enable/disable を行なっている。

/system/bin/ndc を使って同等のコマンドを netd へ送ることが可能:

# ndc nat enable usb0 rmnet0
200 Nat operation succeeded
# iptables -t nat -L POSTROUTING -nv
getsockopt for multiport failed strangely: No such file or directory
getsockopt for multiport failed strangely: No such file or directory
Chain POSTROUTING (policy ACCEPT 26282 packets, 1650K bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  0    --  *      rmnet0  0.0.0.0/0            0.0.0.0/0           
# ndc nat disable usb0 rmnet0
200 Nat operation succeeded
# iptables -t nat -L POSTROUTING -nv
getsockopt for multiport failed strangely: No such file or directory
getsockopt for multiport failed strangely: No such file or directory
Chain POSTROUTING (policy ACCEPT 26282 packets, 1650K bytes)
 pkts bytes target     prot opt in     out     source               destination         
# 
Filed under: Android,stone 開発日記 — hiroaki_sengoku @ 09:41

31 Comments

  1. プロキシ設定は回線接続と結びつけられているものがそうではないでしょうか?
    ●WiFiの場合
    設定 ==> 無線とネットワーク
    ==> Wi-Fi設定 (ここでメニューボタンを押す)
    ==> 詳細設定
    –> プロキシー、ポート

    ●3Gの場合
    設定 ==> 無線とネットワーク
    ==> モバイルネットワーク
    ==> アクセスポイント名
    ==> 利用しているAPNを押す
    –> プロキシー、ポート

    Comment by 名無し — 2011年6月8日 @ 12:13

  2. WiFi の詳細設定でプロキシを設定できる機種とできない機種があるようです。設定できる機種は Android のバージョンが 2.1 以前のものが大半であるような印象があるのですが、お使いの機種は何でしょうか?

    APN 設定のプロキシ設定は、プロキシ経由でないとインターネットにアクセスできない APN 用のものだと思いますが、プロキシ無しでインターネットに直接アクセスできる APN であれば、おっしゃる通り流用は可能ですね。例えば、

    stone -l -q store=sengoku proxy.klab.org:443/ssl localhost:8080

    などと stone を動かしておいて、プロキシを「localhost」、ポートを「8080」に設定すればプロキシ経由でイントラをアクセスすることも可能です。イントラ外のサイトもこのプロキシ経由になってしまいますけれど。

    Comment by hiroaki_sengoku — 2011年6月8日 @ 14:42

  3. このページのコマンドがわからない初心者ですが、質問させてください。

    私の使用環境は、Android 2.3.4 のF-12C、EMSIM(プロトルコ制限あり)
    無線ルータ,WHR-HP-GNです。

    以前の環境は、イオンSIM運用でWHR-HP-GNにVPN接続して使っていました。
    EMSIMはプロトルコ制限のためVPN接続はできません。

    現状、WHR-HP-GNはDDNSの設定がしてあり、AndroidのブラウザでDDNSアドレスを入力すると
    ルータの設定画面に入ります。

    目標は、今あるルータでこのページに書かれている透過型プロキシ(VPNのかわり)をしたいです。

    stone for Androidをインストールしてパラメータ入力に
     -dd (ルータのDDNS):80 localhost:8080
    と入力。
    動作確認のパラメータで stone を走らせてブラウザから「http://localhost:8080」へ
    のアクセスするとルータの設定画面に入ります。

    stoneは、ちゃんと動いていると思いますが、ここからの設定がどうすれば良いのかがわかりません。

    コメント欄の2番目のコメントにある プロキシ無しでインターネットに直接アクセスできる APN であれば、流用は可能が
    適用できそうなのですが、
     stone -l -q store= (?) :443/ssl localhost:8080 の
    (?)は何と入力すればよいのでしょうか?

    Comment by 初心者 — 2012年1月20日 @ 21:46

  4. 補足しておきます。

    目標は、Android⇔ルータ⇔インターネットです。
    通信の途中にルータを通したい。(踏み台)です。

    VPNの時は、制限のあったVOIPができるようになったので
    今回もVOIPが最終目的です。

    Comment by 初心者 — 2012年1月20日 @ 23:44

  5. 今のStoneはHeartBleedバグに影響されませんか?
    他所からlibssl32.dllをコピペするだけでは解決できません・・よね。

    どうもSDでコンパイルできないので、もしよろしければ
    stonexp-2.3e.zip を更新していただけるととてもありがたいです。

    Comment by ななっちゃん — 2014年5月5日 @ 09:25

  6. stonexp-2.3e.zip に含まれている OpenSSL は 0.9.8 系列ですので、HeartBleedバグには影響されません。

    が、J-PAKE 検証処理における脆弱性 (CVE-2010-4252) 等があるので、0.9.8 系列の最新版 0.9.8y を利用したほうがいいでしょうね。0.9.8y の *.dll をコピーすれば、問題なく使えると思います。

    Comment by hiroaki_sengoku — 2014年5月7日 @ 23:23

  7. 2015年の今でも使ってます。パケットを簡単に中継するソフトがほかにないので。
    /sslを使っていますが、TLS1.0が最高レベルなのが残念です。

    ・TLS1.2の追加
    ・SSLのDLLの更新(「0.9.8y の *.dll をコピー」をすると、起動自体ができませんでした)

    をしてくれると大変助かります。
    Win10が出てきますが、stone動かなかったらどうしよう・・・

    Comment by 通りすがり — 2015年6月10日 @ 22:38

  8. 取り急ぎ OpenSSL 0.9.8 系列の最新版でコンパイルしてみました。Microsoft Visual Studio 12.0 を使ったので、おそらく Windows 10 でも大丈夫だと思います (未確認ですが)。

    stone version 2.3e 2.3.3.18 for Windows 64bit

    Comment by hiroaki_sengoku — 2015年6月22日 @ 00:49

  9. >stone version 2.3e 2.3.3.18 for Windows 64bit
    わーい最新verだー
    どうもありがとう!!

    SSL部分が最新というだけでも大きな違いです。
    後で今までの石と入れ替えて見ます。

    Comment by 通りすがり — 2015年6月27日 @ 22:42

  10. VS2013のランタイムを入れる手間がありましたが、不自由なく動作しています。

    64だけでなく、32bit版もあればいいですね。
    CodeBlocksしか持ってないので(VSが重いので入れたくない)、それでできるなら試してみるつもりではいますが・・・

    Comment by 通りすがり — 2015年6月28日 @ 18:34

  11. 連投申し訳ないです。
    動作はしますが、連携がうまくいかなかったので戻しました。

    Client2.3e2.3.3.18×64(Win7 64bit) ===X==== stonexp-2.3e(Win7 32bit)Client
    can’t connect error=10035が、最新(左)で頻発。
    右からのアクセス、左から右へのアクセスが不可能に。

    急遽左のexe,dllを全削除し、stonexp-2.3eに差し替えたところ
    問題なく両方向の通信が再開できました(errorなし)

    stone.exe -C ~~~.cnf という風に、設定ファイルを読み込ませて使ってます。

    =======
    設定ファイル左
    -z key=XXXXXX/key.pem
    -z cert=XXXXXX/crt.pem
    -z tls1
    X.X.X.X:XXXX Y.Y.Y.Y:ZZZ/ssl —
    X.X.X.X:XXXX Y.Y.Y.Y:ZXY/ssl
    =======

    =======
    設定ファイル右
    -q tls1
    -q servername=X.X.X.jp
    Y.Y.Y.Y:ZZZ/ssl 127.0.0.1:123 —
    Y.Y.Y.Y:ZXY/ssl 127.0.0.1:124
    =======

    OpenSSLのバージョン違いで何かが起きてる気がします。

    Comment by 通りすがり — 2015年6月28日 @ 19:28

  12. 失礼しました。error=10035 は、
    「ブロック不可のソケット操作をすぐに完了できませんでした。」
    というエラー WSAEWOULDBLOCK です。

    で、stone.c では

    #if !defined(EINPROGRESS) && defined(WSAEWOULDBLOCK)
    #define EINPROGRESS WSAEWOULDBLOCK
    #endif

    などとしていたのですが、VC だと EINPROGRESS が既に define されていて、!defined(EINPROGRESS) の条件が成立しなくて、この define がスキップされてしまっていたのでした。

    というわけで、修正版

    stone version 2.3e 2.3.3.19 for Windows 64bit

    を公開しました。まあ、今後も修正版を出すかもしれないので、この直リンクではなく stone 公式ページからダウンロードするようお願いします。> みなさま

    32bit 版も公開しました。

    Comment by hiroaki_sengoku — 2015年6月28日 @ 22:37

  13. 2.3e 2.3.3.19 bug

    上記のSSL設定ファイルに追加で、
    Client X.X.X.X:12345/ssl 127.0.0.1:5678/udp
    Server Y.Y.Y.Y:1010 X.X.X.X:12345/ssl
    という感じで使っていますが、2.3eからバージョンを差し替えたところ
    UDP/TCPの変換がうまくいかないようです(通信ができません)。

    最近何かおかしい・・・と思い、stoneを見てみると
    クライアント側で以下が滝のように流れてました
    XXXXX 5408 448 UDP: recvfrom err=10047 ???:?/udp
    XXXXX 5408 Unknown node:serv ???:? len=128 error=10047 errno=10047

    Comment by 通りすがり — 2015年7月4日 @ 06:44

  14. errno=10047 は、
    「要求したプロトコルと互換性がないアドレスを使用しました。」
    というエラー WSAEAFNOSUPPORT です。
    VC の recvfrom は MSG_TRUNC をサポートしていないにもかかわらず、MSG_TRUNC を define してしまっています。
    WSARecvMsg では MSG_TRUNC をサポートしているから、という理由のようですが、MSG_TRUNC を define するなら recvfrom でもサポートして欲しいものです。

    修正版 2.3e 2.3.3.20 を公開しました。

    Comment by hiroaki_sengoku — 2015年7月4日 @ 11:21

  15. .20、問題なく動作しています。ありがとうございます。

    さて、数日前にとうとうRC4が亡くなった(rc4nomore.com)ので、cipherを編集しようと
    しているのですが、

    -q cipher=”EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 !RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS”

    -q cipher=”EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS”

    これで理解されるのでしょうか?
    また、「-q verify」というものがあるようですが、「自己署名証明書」の場合
    どのように検証を有効化できますか?
    fingerprint値をセットして、一致しなければ拒否、といったことができればいいなと。

    例 (SHA256)
    -q verify -q fp=EE:44:5F:79:25:4C:B9:C9:A3:F4:98:4B:DB:DF:41:4B:DA:1F:F6:BB:12:BC:62:64:9F:A3:17:6C:4F:DD:7D:0F
    -z verify -z fp=EE:44:5F:79:25:4C:B9:C9:A3:F4:98:4B:DB:DF:41:4B:DA:1F:F6:BB:12:BC:62:64:9F:A3:17:6C:4F:DD:7D:0F

    Comment by 通りすがり — 2015年7月19日 @ 14:09

  16. stoneのクライアント認証についての説明が少なく、試行錯誤と数時間を使って
    成功しました。が、次のような問題がありました。使用バージョンは最新です。

    「Palemoon、Firefoxでクライアント認証後、BASIC認証を使ったサイトへアクセスすると、それ以降の通信が途絶える」

    1. CA自己証明書を作る。(CAp。CAkは「2」後削除)
    2. 2個、CAを使って自己証明を作る。(CLIp,CLIk/SVRp,SVRk)
    3. サーバ側stone

    -dd
    -z tls1
    -z key=……pem;  SVRk
    -z cert=…..pem;  SVRp
    -z CAfile=…pem;  CAp
    -z verify
    -z re0=”^/C=XXXXXXXXXXX$”
    -z re1=”^/C=YYYYYYYYYYY$”
    -z verbose
    127.0.0.1:80 192.168.xxx.server:443/ssl 192.168.xxx.client

    4. クライアント(Firefox)に、P12に変換した証明書(CLIp,CLIk)と、CApを入れる。
    5. つなぐ: ttps://192.168.xxx.server/
    6. 証明書を要求され、送信。通常通りに閲覧できたが、BASIC認証を使ったエリアへ
    アクセスする⇒ログインする⇒「The connection was interrupted」(Firefox)
    ⇒それ以降、どのページもアクセス不能に。
    7. stoneに「SSL_accept lib error:140D9115:SSL routines:
    SSL_GET_PREV_SESSION:session id context uninitialized」というログが大量にある。

    今現在の回避策;
    「クライアント側にもstoneを中継させる」ことで、BASIC認証を使ったサイトへもアクセスできました。
    ・・・最新のブラウザではダメだった、というのが謎です。
    1. stoneクライアント側

    -dd
    -q tls1
    -q key=;; CLIk
    -q cert=;; CLIp
    -q CAfile=;; CAp
    -q verify
    -q re0=”^/C=XXXXXXXXXXX$”
    -q re1=”^/C=YYYYYYYYYYY$”
    -q verbose
    192.168.xxx.server:443/ssl 127.0.0.1:8888

    2. ttp://127.0.0.1:8888/ を使う。

    Comment by fire — 2016年4月19日 @ 11:27

  17. 詳細なレポートありがとうございます。
    「session id context uninitialized」と言ってるので、sid_ctx オプションで session id context を設定してみてもらえますか?
    例えば、

    -dd
    -z tls1
    -z sid_ctx=test
    -z key=……pem;  SVRk
    (以下略)

    みたいな感じで。

    Comment by hiroaki_sengoku — 2016年4月19日 @ 14:01

  18. ご返信ありがとうございます。

    > sid_ctx オプションで session id context を設定

    > session id context uninitialized
    > context id is not generated if ssl server uses optional client verification.
    https://rt.cpan.org/Public/Bug/Display.html?id=76053

    確かに、-z sid_ctx=(長いランダム英数字) をサーバ側に設定したところ
    Firefoxでも正常に接続できました。

    まぁ、クライアントにもstoneを入れたことで、上記以外の通信(*)にもstoneを
    使用しているため、sid_ctxは無くても大丈夫?かもしれないです。

    * SSH,HTTP,その他を、stoneで「クライアント認証TLS」で送受信できるのは便利です。
    Wi-fiが簡単にクラックできる今、通信をクラ認証のみにした(サーバ側Wifi、stone以外全部遮断)ことで、
    証明書なしでの通信が一切できないようになりました。
    (TLS1.2ではないですが、まぁ大丈夫と思いたい)

    話がそれますが、「stone for Android」をF-Droidで公開できないか、Fdroidの方に
    確認中です。(Googleを一切利用しないため、FDroidは便利です)
    https://gitlab.com/fdroid/fdroiddata/issues/438
    Androidでも上記のクラ認証ができれば、いろいろ便利になりそうです。

    Comment by fire — 2016年4月19日 @ 22:24

  19. Androidで試してみたくなり、apkpureからAPKを拾って使ってみました。(1.0.3)

    3点だけ気になりました:
    1. まだRunしてないのに、「右上」→「Exit」→「Yes」をタップすると、裏でターミナルが表示あるいは起動したままになる。
    (ターミナルアプリを入れてない人はどういった挙動になるのかは知りませんが)
    2. Historyと、最初の入力欄にある「-dd http://www.gcd.org:80 …」を削除して、Exit→再度アプリを起動すると復活している。
    3. ポート番号(a1:a2 b1:b2のb2)を1024以下にするとエラー13になる。
    非rootでは当たり前のことですが、非root端末だと
    「This device is not rooted. Change your source port to over 1024」みたいな表示だと気がつきやすいです。
    (DSADブログのほうにAndroidの紹介があったので、そちらに書き込もうとしたら拒否された)

    それら以外にはありません。Windows版stoneのように、安定して使えています。
    https://i.imgur.com/X4DGafs.jpg
    Android[local->Orbot->Stone:]===Wifi(WPA2)===Server[:Stone->Proxy]->VPN->Internet

    ところで、Windows版のビルドには、やはりVisualStudioは必須ですか?
    Code::Blocks(+GNU GCC Compiler)でビルドしようとしたところ、hファイルがなさ過ぎて無理でした。

    Comment by fire — 2016年4月21日 @ 10:28

  20. DSASブログは、海外のIPアドレスからのコメント書き込みを拒否する設定になっていたそうです。先ほど設定を変更してもらったので、いまは書き込めるのではないかと思います。

    MinGW でビルドできるはずですが、現時点での推奨は Visual C++ ですね。無償ですし。

    Comment by hiroaki_sengoku — 2016年4月21日 @ 12:56

  21. TLS1.2をサポートするためには、OpenSSLのバージョンアップとリビルドが必要でしょうか。
    ※ ちなみに、Android 4.0.xでTLS1.2でのデータ送受信を狙っています。
    なにぶん、ネットワーク系の技術が足りていないもので、
    どこから手をつけていのやら途方に暮れています。

    Comment by Takezo — 2016年6月16日 @ 21:30

  22. 最近までLANアクセス等のためにL2TPを使っていたのですが、バッテリーの劣化が激しいのでstoneに切り替えました*。

    最高レベルがTLS1.0なのでTLS1.2に対応させたく VisualC++ 2010 Expressでstoneをコンパイル
    しようとしたのですが、「regex.h」等がなくコンパイルできませんでした。
    もし可能であれば、デバッグできるような.sln一式をWebページに公開いただけないでしょうか?
    (仮想PCにVC2010を入れて試しましたが、そう簡単にはコンパイルできなさそうです)

    > どこから手をつけていのやら
    予想ですが、
    Line 9260: if (isserver) opts->meth = TLSv1_server_method();
    Line 9261: else opts->meth = TLSv1_client_method();
    を「TLSv1_2_method」に変更すると、TLS1.2にできそうな気がします(Cは専門外です)。
    ついでに「TLS1.1以下のコードの完全削除 & スリム化」を考えていました。

    *
    1. Androidに「NoRoot Firewall」をインストール
    2. Firewallは、stoneのみを許可して、ほかは拒否。
    Android[stone(個人証明書認証)]====TLS1.0===>公開WWWサーバ[stone(復号してlocalhostのPrivoxyへ丸投げ)]⇒インターネット/LAN

    Comment by fire — 2017年3月19日 @ 15:31

  23. CVS レポジトリの最新版 stone.c は TLS1.2 に対応しております。

    http://ja.osdn.net/cvs/view/stone/stone/stone.c?view=log

    VisualC++ の場合は、

    nmake win-svc

    などと実行すればビルドできます。この場合、regex.h ではなく pcreposix.h が参照されます。

    PCRE は http://www.pcre.org/ からダウンロードできます。

    Comment by hiroaki_sengoku — 2017年3月20日 @ 01:28

  24. >CVS レポジトリの最新版 stone.c
    6ヶ月前からTLS1.2の準備(v2.4)をされていたのですね、気がつきませんでした。
    帰ってからコンパイルを試してみます。ありがとうございます。

    Comment by fire — 2017年3月20日 @ 09:00

  25. 別件で別ネットワークにstoneを使い始めたのですが、2件問題がありました。

    1: “-z cipher” で、以下を試すと、2つとも「5024 error:1410D0B9:SSL routines:SSL_CTX_set_cipher_list:no cipher match」
    で起動しませんでした。
    “AES256+EECDH:AES256+EDH:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS” <—- nginxで使用中
    "AES256+EECDH:AES256+EDH:AESGCM:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS"
    今は以下のように「HIGH」を追記して起動していますが、通信時いつも [SSL cipher=AES256-SHA] が選択されます。

    2: 今回、なぜか「←」を追加すると、以下のエラーによりクライアントから通信できなくなりました。
    3行削除して再度stoneを起動しなおすと通信できます。
    re0とre1の文字列の確認、入れ替えを試しましたがどうしても失敗します。

    (server side log)
    3068 stone 596: accepted TCP 1052 from XXXXXXXX/ssl mode=0
    4144 596 TCP 1052: [depth1=/C=XX/O=XXXX/OU=XX/CN=rootca.thisisatestserver.office]
    144 596 TCP 1052: SSL_accept lib error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned

    (証明書作成方法)
    以前と同じで、rootCAの公開鍵、秘密鍵を作り、その2つを使って
    サーバ用鍵セットと、クライアント用鍵セットを作成しました。

    [Windows]
    -dd
    -z tls1
    -z servername=wireless.gw.thisisatestserver.office
    -z key=xxxx/device.pri.pem
    -z cert=xxxx/device.pub.pem
    -z CAfile=xxxx/rootCA.pub.pem
    -z cipher="AES256+EECDH:AES256+EDH:HIGH:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS"
    -z verify ←
    -z re0=”^/C=……$” ←
    -z re1=”^/C=……$” ←
    -z verbose
    127.0.0.1:YYYY 192.168.X.X:X/ssl 192.168.X.Z

    [Android]
    -dd
    -q tls1
    -q servername=wireless.gw.thisisatestserver.office
    -q key=/xxAndroid-SDcardxx/device.pri.pem
    -q cert=/xxAndroid-SDcardxx/device.pub.pem
    -q CAfile=/xxAndroid-SDcardxx/rootCA.pub.pem
    -q verbose
    192.168.X.X:X/ssl 127.0.0.1:ZZZZ

    何かヒントをいただけますと幸いです。

    Comment by fire — 2017年3月20日 @ 18:15

  26. 何度もすみません。

    verify の件、原因が判明しました。
    「-q re1=”^/xxxxxxxxxxxx$”」 ← ダブルクオートの記号ミスでした…
    (Notepad++で編集していたため、上の記号がダブルクオートに見えてました)

    ということで、クライアント・サーバ両方でverifyできるようになりましたが、
    1つ気になったことがあるので報告です。

    サーバ側設定
    -z verify
    -z re0=”^/C=…./CN=clientnode0001\.testing….$”
    -z re1=…

    re0/re1の文字列で、1文字でも間違いがあると(clientnode0001がclientnode001だったり)
    クライアントが接続してきたときに
    「632 596 TCP 1052: SSL_accept lib error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned」
    とサーバ側に表示されます。これだと、クライアントが証明書を返していないと勘違いするかもしれないので
    「no certificate returned」の他に「certificate data mismatch(verification failed)」のような
    エラーを追加してほしいです。

    Comment by fire — 2017年3月20日 @ 18:48

  27. Windowsでデバッグしているのに、!Windowsの行を参照してエラーが出たり(#define USE_PCRE、#define WINDOWSを追加)
    「undefined reference to getnameinfo/freeaddrinfo」があったりで(「#define _WIN32_WINNT 0x501」を一番上の辺りに追加)
    コンパイルがいろいろと難しいです。PCRE一式を/includeに入れて、ほとんどのエラーは消えましたが
    「undefined reference to _imp__regexec」がなぜか表示されます(regex.hロード済で)。

    1つ変更点があるとすると
    > stone.c|4523|warning: incompatible implicit declaration of built-in function ‘rindex’
    p = rindex(buf, ‘:’); <—
    index/rindexは非推奨とのことで、「strrchr(buf, ':');」に書き換えるとwarningは消えました。

    Comment by fire — 2017年3月20日 @ 21:27

  28. cipherを指定してもAES-SHAが指定される件について、
    https://ja.osdn.net/ticket/browse.php?group_id=156&tid=34027 にPFS対応(Perfect forward security)のパッチがあるようなので
    セキュリティのためにも、ぜひマージしていただきたいです。

    コメントが複数になりましたので、非表示でも、まとめて頂いてもかまいません。

    Comment by fire — 2017年3月21日 @ 07:47

  29. Unknown node:serv XX.XX.XX.XX:XXXX len=16 err=-3 errno=0
    上記エラーはどのような意味ですか?

    また、上記エラーが頻発するとstoneデーモンが落ちます。

    Comment by テスト — 2017年3月23日 @ 12:12

  30. 「err=-3」は、EAI_AGAIN です。つまり逆引きが (一時的に) できなかったということですね。DNS に「XX.XX.XX.XX:XXXX」の逆引きが登録されていないのであれば、正常な動作です。

    ただし頻発すると落ちるというのは問題なので、動作環境 (OS のバージョンなど) と落ちた時点のスタックトレース等をご連絡頂ければ調査したいと思います。

    とりあえずの対処法としては、stone に -n オプションを指定すれば、逆引きを抑制できます。

    Comment by hiroaki_sengoku — 2017年3月24日 @ 10:43

  31. TLS1.3の仕様が確定されてます。
    OpenVPNもいずれTLS1.3をサポートするようですし、もしstoneを更新されるのであれば1.3もサポートしてほしいです。SSL1.xとTLS1.0は、流石に古い・脆弱性ありなので削除していいと思います。

    スマホで、未だにVPN代わりにstoneを使ってます。OpenVPN、Orbotなどはセッション張ったままでバッテリーが削られるのに対し、stoneはパケット単体で送受信できるのでバッテリーの持ちがいいです。

    Android(Stone for Android) –TLS暗号化–> 自宅鯖(stoneと証明書認証の組み合わせ) —> ネット

    外出先でも日本のIP(自宅鯖)でネットができるという利点付き。

    Comment by fire — 2018年8月28日 @ 22:12

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.