月別アーカイブ: 2014年10月

動作中のプロセスのptyをつなぎかえられる、reptyrを使ってみる

UNIX板のGNU screenスレッドで紹介されていたreptyrを使ってみました。

screen外のプロセスにattachさせる

既にscreenを実行している状況で、screenのセッション外のプロセスを起動してみます。適当なterminalを起動するなり、外からsshでつなげるなりしてみます。あとでプロセスを探しやすいようにttyコマンドで現在使っているptyデバイスが何かを確認しておきます。

$ tty
/dev/pts/7

次にscreen内の任意のウインドウのshellから、当該プロセスにつなげてみます。

$ reptyr -s `pgrep -t pts/7 bash`
[-] Timed out waiting for child stop.
[+] Allocated scratch page: 7faebd18b000
[+] Opened the new tty in the child: 3
[+] Set the controlling tty
$

この時点で、元のターミナルは無反応になり、bashの制御がscreen下に移りました。shellを終了すればreptyrに制御が戻ります。

$ exit
ログアウト
$

gdbと組み合わせる

reptyrのもう一つの機能、ptyを外部から使わせるために確保する-lオプションを使ってみます。

$ reptyr -l
Opened a new pty: /dev/pts/7

別のshellからgdbでこのptyを使ってみます。set inferior-ttyで/dev/pts/7を指定することで、gdbが動かすプロセスの出力をreptyr側の端末に変更できます。

$ gdb ls
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /bin/ls...(no debugging symbols found)...done.
(gdb) set inferior-tty /dev/pts/7
(gdb) run
Starting program: /bin/ls
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Inferior 1 (process 15662) exited normally]
(gdb)

reptyrを実行していたshell上に、lsの出力が表示されます。標準入出力をデバッガの画面と切り離すことができて便利です。

実装方法

以前「起動済みプロセスのリダイレクト先を設定したい」という記事を見かけた記憶があったので、基本的には同じようなやりかただろうと思いつつソースを見てみたら、その通りでした。

起動しているプロセスに対してptrace(2)でattachし、ptyをdup2でfile descripter 0, 1, 2に複製しています。ただし、単なるリダイレクトと違い仮想端末が相手なのでresize等に対応するためのコードも含まれているようです。

前述の記事や本プログラムのドキュメントにもありますが、Ubuntuのカーネルはデフォルトで一般ユーザーのptraceを禁止しています。procfsを見てそのあたりをチェックするコードも入っています。Ubuntuユーザーが利用する場合、rootで実行するか、sysctlの設定を変更する必要があります。

SSLv3の脆弱性(POODLE)対応

SSL 3.0に、プロトコル自体の脆弱性がGoogleによって発見されました。”Paddning Oracle On Downgrade Legacy Encryption”略してPOODLEと呼ばれています。

検証サイト https://www.poodletest.com/ が立ち上がっており、脆弱性の詳細に関する元ソースへのリンクもあります。

今回の脆弱性はサーバー、クライアントともに対策が必要になります。とり急ぎ自分の周りにあるSSL/TLSを使うサービスに対策を施しました。

Apache+mod_ssl

SSL_Engine onとしているセクション内で、以下の設定をしました。

SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS"

SSL 2.0, 3.0を無効にし、利用可能な暗号からMD5,  3DES等を外しています(参考: httpsだからというだけで安全?調べたら怖くなってきたSSLの話!?)。

Postfix

smtpd_use_tls=yes
smtpd_tls_mandatory_protocols = !SSLv3, !SSLv2
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5, RC4

参考: Postfix TLS Support

Courier-imapd

##NAME: TLS_PROTOCOL:0
#
# TLS_PROTOCOL sets the protocol version.  The possible versions are:
#
# OpenSSL:
#
# SSL3 - SSLv3
# SSL23 - either SSLv2 or SSLv3 (also TLS1, it seems)
# TLS1 - TLS1
#
# Note that this setting, with OpenSSL, is modified by the TLS_CIPHER_LIST
# setting, below.
#
# GnuTLS:
#
# SSL3   - SSLv3
# TLS1   - TLS 1.0
# TLS1_1 - TLS 1.1
#
# When compiled against GnuTLS, multiple protocols can be selected as follows:
#
# TLS_PROTOCOL="TLS1_1:TLS1:SSL3"
#
# DEFAULT VALUES:
#
# SSL23 (OpenSSL), or "TLS_1:TLS1:SSL3" (GnuTLS)
TLS_PROTOCOL="TLS1"

Debianのcourier-imapdはOpenSSLをリンクしているのでTLS1のみ設定しています。

確認方法

OpenSSLのs_clientを利用することで、任意のバージョンのSSL/TLSを使った接続を試すことができます。

$ openssl s_client -connect target.server.example.jp:443 -ssl3
(略)
SSL handshake has read 2844 bytes and written 289 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-AES256-SHA
(略)
$ openssl s_client -connect example.jp:443 -ssl3 -cipher `openssl ciphers RC4`
(略)
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-RC4-SHA

上記サンプルでは、1回目はSSLv3, AESでの接続、2回目はSSLv3, RC4での接続ができています(参考: 私が愛した openssl (SSL/TLS 編))。設定が正しくできれば、以下のような結果となりネゴシエーションに失敗します。

SSL handshake has read 7 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : SSLv3
    Cipher    : 0000

STARTTLSを使う場合は-starttlsオプションが利用できます。

$ openssl s_client -connect example.jp:587 -ssl3 -cipher `openssl ciphers RC4` -starttls smtp
(略)
SSL handshake has read 2128 bytes and written 264 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-RC4-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: zlib compression
Expansion: zlib compression
SSL-Session:
    Protocol  : SSLv3
    Cipher    : ECDHE-RSA-RC4-SHA

Debianでの対応状況(2014/10/16朝頃)

mikutter-perlの公開

マルチプラットフォームで動作する、Rubyで書かれたTwitterクライアントmikutterのプラグイン、mikutter-perlを書きました。

動機: RubyからPerlを呼びたい

前々からNamazuのRuby実装を作りたい、ということを考えていました。現状のNamazuは独自のデータ形式を持っていますが、新しい実装ではバックエンドをGroongaにしたいと思っています。現状、Groongaのperl bindingはこれといった定番がないのに対し、RubyにはRroongaがあるので、作るならRubyだろうと考えています。

ただ、これまでPerlで書かれたフィルター群をどうにか継承できないか、とも考えていました。Namazuのフィルターはさまざまな形式のファイルからテキストを抽出するもので、今となっては結構な種類の数があります。

フィルター部分のみを呼び出すために、RubyからPerlのコードを呼び出したいという考えを実現する最初の一歩として、mikutter-perlを書いてみたというわけです。なお、Perl側には逆のことをするInline::Rubyというものがあります。

libperl呼び出し部分

最初に書いておきますが、やっていることは大したことありません。perlembedのサンプルとそう大きくは変わりません。

当初は直接libperl.soをDL::Importで読み込んで実行することを考えていたのですが、その手法には問題がありました。構造体へのアクセスができず、初期化に必要な作業が実行できません。以下にperlembedでも出てくるコード例を示します。

#include <EXTERN.h>
#include <perl.h>

int main(int argc, char **argv, char **env)
{
    PERL_SYS_INIT3(&argc,&argv,&env);
    my_perl = perl_alloc();
    perl_construct(my_perl);
    PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
    perl_parse(my_perl, NULL, argc, argv, (char **)NULL);
    perl_run(my_perl);
    perl_destruct(my_perl);
    perl_free(my_perl);
    PERL_SYS_TERM();
}

肝となるのはPL_exit_flagsです。perl_alloc, perl_constructで作成したperlインタプリタのインスタンス的なものに対し、終了時の挙動をフラグとして設定する箇所です。

gcc -Eオプションを使ってこの部分がどのような内容に展開されるかを確認すると、以下のようになります(on Debian wheezy)。

$ gcc -I /usr/lib/perl/5.14/CORE perltest.c  -E
(中略)
    static PerlInterpreter *my_perl;

    int main(int argc, char **argv, char **env)
    {
        Perl_sys_init3(&argc, &argv, &env);
        my_perl = perl_alloc();
        perl_construct(my_perl);
        (my_perl->Iexit_flags) |= 0x02;
        perl_parse(my_perl, ((void *)0), argc, argv, (char **)((void *)0));
        perl_run(my_perl);
        perl_destruct(my_perl);
        perl_free(my_perl);
        Perl_sys_term();
    }

Perlinterpreterはstruct interpreterをtypedefしたもので、my_perlのIexit_flagsに値を代入するようなコードに展開されます。

デバッグ情報、シンボル情報などをstripした状態で、dlopen(2)だけでこれらにアクセスすることはできません。そもそもPerl周りはたくさんのマクロを使っているので、そこをクリアしたとしても汎用性が担保できません。

結局、そのあたりを全部担当するためのコードをCで書き、シンプルなインターフェースだけにまとめてDL::Import(dlopen)で呼び出せるようなコードを書きました(perlstub.c)。

RubyとPerlの間のやりとりも単純な文字列だけなので、割とシンプルに書きあがりました(mikutter-perl.c)。DL::Importはchar *を自動的にRubyの文字列に変換してくれるので楽です。

PL_exit_flagsさえ気にしなければ、DL::Importだけでコードを完結させることはできます。実際に書いたコードをgistにおいてあります。

mikutterプラグインの開発にあたって

今回初めてmikutterのプラグインを作成しました。参考にした資料は以下の通りです。

mikutter pluigin特有だと思われる、自分がはまった点についても列挙しておきます。

  • プラグイン内部で発生した例外は自力で受けないとスルーされる。ちょっとしたエラーがあっても止まらないのでわからない
  • mypostイベントは、mikutter起動時に取得される過去の自身のツイートを受け取った時にも発生する。今まさにポストしたツイートに対応するイベントはpostedになる

Windowsの証明書自動更新と認証proxyの罠(CryptoAPI)

過去にCentOS/RHEL5にてGlobalSignのルート証明書が期限切れとなる事態が起きました(参考: RHEL5/CentOS5でGlobalSignのルート証明書が有効期限切れで大騒ぎ)。一般的なGNU/Linuxディストリビューションでは、SSLのルート証明書等をパッケージとして配布しています。たとえばDebianではca-certificatesパッケージがそれに該当します。

かたやWindowsは、XPまではWindows Updateで最新の証明書セットを配布していました。しかしVista以降はマイクロソフトが各種証明局と連携して、自動的に更新する仕組みを導入しています。しかし、システムが利用するhttp proxyがユーザー認証つきの場合、これが動作しないということに最近気付きましたので、記録として残しておきます。

CryptoAPI

SSL証明書の自動更新は、CryptoAPIのサービス、Cryptographic Servicesが行います。管理ツールのサービス一覧を見ると、このサービスが動作しているのがわかります。

まったく新規のWindows環境を用意して、台湾のVISA取得サービス(https://visawebapp.boca.gov.tw/BOCA_MRVWeb/subroot/MRVWeb0_form.jsp)にアクセスする場合を例示してみます。

新規のWindows環境では、台湾のルート証明書は古いものが搭載されています。前述のURLにアクセスすると、CryptoAPIサービスが新しい証明書(http://grca.nat.gov.tw/repository/CRL/CA.crl)を取得し、検証用のエンドポイント(http://gca.nat.gov.tw/cgi-bin/OCSP/ocsp_server.exe)で証明書が正しいことを確認した上で、OSの証明書ストアを新しいものに更新します。

困ったことに、CryptoAPIは認証つきhttp proxyを経由したアクセスをサポートしていません。これは仕様としてそうなっているようです。

したがって、認証つきhttp proxyしかない環境下では、SSL証明書の更新がうまく動作しません。

一つの対応策として、User-Agentが”Crypto-API”である場合に認証を不要とする方法があります。それ以外には、stone等を使って認証不要なhttp proxyをローカルに立ち上げるという手段も考えられます。