空の segment を使いたい、つまり / を連続させたいという要求は、典型的には、URL の中に URL を埋め込みたいというときにおこる。
・ apache-bug:10775 ・ http://tdiary.ishinao.net/20060221.html#p01
ただ、この要求が簡単な話か茨の道かは微妙なところである。
まず、URL はつねに escaped form なので、それをもう一度 escape するかしないかという選択がある。
escape するんなら話は簡単である。そのままかける文字以外は escape してしまえばよい。ただ、もともと percent encoding されていたのは2重になるし、美しくない。
escape しないで埋め込むとなると、まず構文的に埋め込めるかどうかが問題になる。 path の部分に URL を埋め込むとすると、ここで RFC:3986 の構文に従えば、完全には埋め込めない。具体的には、IP-literal で使う [, ] が path の部分には書けないので、そういうのは埋め込めない。まぁ、現状では IPv6 だけなので、IPv6 を生で書くのは気にしないことにしてこの問題はクリアできたことにしよう。
つぎの問題は、おそらく、そういう URL の情報をサーバ側でちゃんととれるかどうかというところである。
具体的な問題としては、たとえば、Apache は / の連続をつぶしてしまう。また、CGI という規格では、PATH_INFO は percent encoding を decode したものをプログラムに渡すので、もともとの形はわからない。
まぁ、Apache であれば REQUEST_URI があるのでなんとかならないわけでもないのだが、そこまでするかというと面倒臭い気がする。
escape せずに渡すにしても query にしとけば QUERY_STRING にそのままくるし、面倒がないような。
ふと、bash に /dev/tcp とかがあることを思いだし調べてみようかという気になる。
しかし、Debian の bash はあえてサポートしていないようである。 bash(1) に次のようにある。
NOTE: Bash, as packaged for Debian, does not support using the /dev/tcp and /dev/udp files.
めげずに自前でコンパイルして試してみると、例えば次のようにしてネットワークにアクセスできる。
$ ( exec 3> /dev/tcp/localhost/80; printf 'GET / HTTP/1.0\r\n\r\n' >&3; cat <&3; exec 3<&- ) HTTP/1.1 200 OK Date: Wed, 01 Mar 2006 14:40:24 GMT Server: Apache/2.0.55 (Debian) Content-Length: 569 Connection: close Content-Type: text/html; charset=UTF-8 ...
/dev/tcp などの怪しげなものは redir.c に定義されている。
bash-3.1/redir.c:448-462
448: static STRING_INT_ALIST _redir_special_filenames[] = { 449: #if !defined (HAVE_DEV_FD) 450: { "/dev/fd/[0-9]*", RF_DEVFD }, 451: #endif 452: #if !defined (HAVE_DEV_STDIN) 453: { "/dev/stderr", RF_DEVSTDERR }, 454: { "/dev/stdin", RF_DEVSTDIN }, 455: { "/dev/stdout", RF_DEVSTDOUT }, 456: #endif 457: #if defined (NETWORK_REDIRECTIONS) 458: { "/dev/tcp/*/*", RF_DEVTCP }, 459: { "/dev/udp/*/*", RF_DEVUDP }, 460: #endif 461: { (char *)NULL, -1 } 462: };
なんか、* が使われてるが、/dev/tcp/*/* をみるとこれは glob である。 (正規表現ではない) しかし、とすると、/dev/fd/[0-9]* というのは必ずしも /dev/fd/非負整数以外にもマッチすることになる。
怪しいかも、と思ってたどると、マッチした後でちゃんと検査しているようだ。
bash-3.1/redir.c:480-488
480: case RF_DEVFD: 481: if (all_digits (filename+8) && legal_number (filename+8, &lfd) && lfd == (int)lfd) 482: { 483: fd = lfd; 484: fd = fcntl (fd, F_DUPFD, 10); 485: } 486: else 487: fd = AMBIGUOUS_REDIRECT; 488: break;
それはそれとして、tcp をたどると、netopen にいきつく。
bash-3.1/redir.c:504-507
504: case RF_DEVTCP: 505: case RF_DEVUDP: 506: #if defined (HAVE_NETWORK) 507: fd = netopen (filename);
引数を見るとファイル名しかない。 /dev/fd/ もそうだったが、このへんは入力とか出力とかは気にしないようだ。
まぁ、tcp は両方向だからどちらともいえないわけだが。
ふと [ruby-talk:182074] をみて、あげられた https な URL を open-uri でアクセスしてみると、certificate verify failed になる。
% ruby -ropen-uri -e 'open("https://www.knightonlineworld.com/index.php?pg=rankings&sub=2&radServer=1&clanid=12199")' /home/akr/ruby/lib/ruby/1.9/net/http.rb:586:in `OpenSSL::SSL::SSLSocket#connect': certificate verify failed (OpenSSL::SSL::SSLError) from /home/akr/ruby/lib/ruby/1.9/net/http.rb:586:in `connect' from /home/akr/ruby/lib/ruby/1.9/net/http.rb:553:in `do_start' from /home/akr/ruby/lib/ruby/1.9/net/http.rb:542:in `Net::HTTP#start' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:293:in `OpenURI#open_http' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:712:in `URI::HTTP#buffer_open' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:195:in `open_loop' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:193:in `open_loop' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:139:in `OpenURI#open_uri' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:614:in `OpenURI::OpenRead#open' from /home/akr/ruby/lib/ruby/1.9/open-uri.rb:86:in `open' from -e:1
w3m では certificate has expired: accept? (y/n) となる。
が、firefox ではなんの問題もなく表示される。
おそらく、firefox が自前で持っている証明書と Debian の ca-certificate パッケージの違いなのではないかと思うのだが、どう違うかよくわからない。
% google-count --words シラバス C 830000 シラバス C % google-count --words シラバス php 243000 シラバス php % google-count --words シラバス D 189000 シラバス D % google-count --words シラバス Q 103000 シラバス Q % google-count --words シラバス java 37100 シラバス java % google-count --words シラバス Z 37000 シラバス Z % google-count --words シラバス ruby 10800 シラバス ruby % google-count --words シラバス perl 818 シラバス perl % google-count --words シラバス awk 423 シラバス awk % google-count --words シラバス scheme 388 シラバス scheme % google-count --words シラバス prolog 379 シラバス prolog % google-count --words シラバス pascal 359 シラバス pascal % google-count --words シラバス lisp 259 シラバス lisp % google-count --words シラバス python 167 シラバス python
ぬぅ、今日再度 https://www.knightonlineworld.com/index.php?pg=rankings&sub=2&radServer=1&clanid=12199 にアクセスしたら firefox でも検証に失敗する。
いや、失敗したりしなかったり?
firefox が怪しい?
なんとか再現したので失敗する状況とそうでない状況で証明書を比べてみる。
よくわかっていないが、3つあるうちのまんなかの奴が違う感じ。
少なくとも Serial Number, Certificate Signature Algorithm, Not After が違う。
直接的には Not After でひっかかっているのが失敗の原因みたいである。w3m でのメッセージが理解できるし、もう一方の検証に成功する方だと Not After が問題ない値になっている。
しかし、そもそも証明書が?変わるぽいのがなんである。
<URL:http://sheepman.parfait.ne.jp/20060303.html#p01>
ふむ。なるほど。たしかにそう考えると、firefox の挙動を理解できますね。
ぜんぜんまとまってないが。
・ shell をとられたらすでに負けているので shell にそういう機能があっても問題ないはず ・ セキュリティの基本は多層防御であり、すでにひとつ負けているからといって降伏する必要はなく、cracker を邪魔する意味はある ・ shell が buffer overflow でとられたのだとすれば、原理的には (バイナリでシステムコールを発行するコードを注入されれば) /dev/tcp と同様のことはできる。単なる手間の問題を防御と考えるのはいかがなものか ・ buffer overflow 以外で shell がとられるケースでは意味がある可能性を否定できないのではないか ・ shell に /dev/tcp という機能をつくと管理者が disable するのが難しい (kernel に /dev/tcp という機能がつくのに比べて) ・ shell にそういう機能があるのは管理者に見過ごされがちで、その見逃しが問題を生む土壌になる可能性がある ・ /dev/tcp は標準化されているわけでもなく、使用するスクリプトは現状非常に少ないため、機能がなくても困らない
結局、shell から直接ネットワークにアクセスできないという期待とできてしまうという現実とのギャップが本質的かなぁ。
1. サーバに OS command injection 問題がある 2. chroot でシェルから使えるコマンドを皆無にしてある 3. サーバから shell にコネクションの fd が継承される (FD_CLOEXEC をつけてない) 4. クラッカーの狙いは踏台をつくること
というくらいに条件が揃うと、bash の /dev/tcp の有無がクラッカーの狙いの達成の有無に効いてくる、かな。
ここで、管理者は (2) によってシェルがとられたときでもネットワークという資源を使えないことを期待しているわけだが、/dev/tcp はその期待を裏切ることができる。
もちろん、最初の問題は (1) の OS command injection なわけで、そっちはそっちで直すべきだけれど、だからといってネットワークアクセスという不要な権限をあたえる必要はどこにもない。
問題が波及するのを防ぐためには、与える権限を最小にするのが重要である。その点 shell から行使できる権限はおおざっぱには呼び出せるコマンドによって決まるので、管理者は chroot によって制約をかけられる。だが、/dev/tcp はその制約にひっかからないので、抜け道になりやすい。
この考えでいけば、もし /dev/tcp が kernel で実装されるのであれば、chroot 内に /dev/tcp というデバイスファイルを置かないとか、permission を適当に設定することにより、管理者は他のデバイスと同様に制御できるので問題は減るかもしれない。サーバ自身が /dev/tcp を必要としたときに permissison で十分に制御できるのかという問題はあるけれど。
まぁ、chroot による制約というやりかた自体がいかがなものかという気はする。ネットワークにアクセスさせたくないならば、もっと直接的にプロセスから権限を取り上げる方法を用意するほうが適切であろう。とはいえ、現時点では決定版がないのでなんだが。
てゆーか、そもそも chroot のもともとの用途って、たしかセキュリティじゃなくて、BSD でのインストールがどうこうという話だと読んだ覚えがある。そうだとすると、これもまたセキュリティ的な効果を期待したところで現実とのギャップに悩むという奴なのかもしれない。
検索してみると、Jails: Confining the omnipotent root., Poul-Henning Kamp, Robert N. M. Watson に以下の記載がある。
http://docs.freebsd.org/44doc/papers/jail/jail-9.html
[CHROOT] Dr. Marshall Kirk Mckusick, private communication: ``According to the SCCS logs, the chroot call was added by Bill Joy on March 18, 1982 approximately 1.5 years before 4.2BSD was released. That was well before we had ftp servers of any sort (ftp did not show up in the source tree until January 1983). My best guess as to its purpose was to allow Bill to chroot into the /4.2BSD build directory and build a system using only the files, include files, etc contained in that tree. That was the only use of chroot that I remember from the early days.''
つまり、chroot を目的外に使用しているわけだから、そんな制約はうまく働かなくてもしょうがない、という考え方もできる。
もうひとつ考えられる話は、shell に与えるべき権限というのはなんによって決まるのか、というところである。 Unix における現状という話ではなく、セキュリティ的に都合のいいやりかたとしてはどんなものが考えられるだろうか。
ひとにはいえないメールを出してみる。
(すくなくともしばらくは)
あるコード (プログラム、関数、メソッド、etc) が動作する権限は、2つ考えられる。
・ そのコードを作ったひとの権限で動く ・ そのコードを呼んだひとの権限で動く
いつ、どういう状況で、どちらを選ぶべきか。
bash では、shared object を動的にロードできる。
enable [-adnps] [-f filename] [name ...] ... The -f option means to load the new builtin command name from shared object filename, on systems that support dynamic loading.
ふむ。/dev/tcp と違って Debian で disable しているとは書いていない。使える?
% bash $ enable -f a b bash: enable: cannot open shared object a: a: cannot open shared object file: No such file or directory
機能は有効なようだ。
こっちを有効にしたまま /dev/tcp を禁止することに意味はあるか?
というわけで暗号化メールをはじめて使ったわけだが、自分宛に Bcc する意味があるのだろうかという点に疑問を持った。
<URL:http://www.daionet.gr.jp/~knok/diary/?200603a&to=200603082#200603082>
おぉ、そんなことができるとは。
というか、自分の出したのもそうなってました。なるほど。
コードを調べてみようと思ってとりあえず funcall:dlopen を検索するとなにもみつからない。
疑問に思って調べてみると、bash の builtin は *.def というファイルに入っていて、純粋な C のファイルじゃないらしい。
bash-3.1/builtins/enable.def:76-78
76: #if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM) 77: static int dyn_load_builtin __P((WORD_LIST *, int, char *)); 78: #endif
bash-3.1/builtins/Makefile.in:96,105-111
96: MKBUILTINS = mkbuiltins$(EXEEXT)
...
105: .SUFFIXES: .def .c .o 106: # How to make a .o file from a .def file. 107: .def.o: 108: $(RM) $@ 109: ./$(MKBUILTINS) $(DIRECTDEFINE) $< 110: $(CC) -c $(CCFLAGS) $*.c || ( $(RM) $*.c ; exit 1 ) 111: $(RM) $*.c
ふと思ったのだが、shell に適当な機能を追加するのは POSIX に反しないような気がする。
では、strcpy とかに適当な機能を追加するのも POSIX に反しなかったりするのだろうか。
大域的に設定したので、文字数などが出てくるはず。
... 出てこないひとがいるとおもったら、リンク元を表示させておく必要がある模様。
uintptr_t という型がある。(C99 には)
ポインタを表現するのに十分な整数型であるが、AS/400 ではどうなってしまうのであろう。
ZFS には (Solaris 10 には?) SEEK_HOLE, SEEK_DATA があるそうな。
<URL:http://blogs.sun.com/roller/page/bonwick?entry=seek_hole_and_seek_data>
gcc 4.0.3 では char[0x80000000] はだめなようだ。
% cat t.c int v = sizeof(char[0x80000000]); % gcc -c t.c t.c:1: error: size of array 'type name' is too large zsh: exit 1 gcc -c t.c
char[0x7fffffff] は問題ない。
まぁ、ssize_t とか ptrdiff_t とか、これ以上のを許すといろいろと問題が出てくるからこれもひとつの見識という気はしないでもない。
ふと思ったのだが、もし、chroot 後のディレクトリツリーの中に、setuid/setgid なファイルがなければ、user 権限での chroot を許してもいいのではなかろうか。
まぁ、効率の点で現実的ではない気がするが、セキュリティ的な観点からは問題ない - root をとられる原因にはならない - ように思う。
<URL:http://gcc.gnu.org/gcc-4.0/changes.html#4.0.3>
うぉ、getcontext のがいちばんでかい変更なのか?
どうも (SEMI で) メールを暗号化する操作が覚えられない。info を読むことになる。
もっと頻繁に暗号化メールを送れば、指が操作を覚えるだろうか。
% google-count 暗号化メール 暗号メール 27400 暗号化メール 56400 暗号メール
ぬぅ。
test
Unix において sandbox っぽい機構はいくつかある。
・ chroot: file system の限定 ・ uid: root 以外であれば、他の uid の資源には手を出せない
process はそれっぽいが、ptrace を考慮すると悪意には対抗できない。 (悪意のない失敗には有効であるが)
さて、sandbox は、内部を閉じ込めると同時に、制限された形で外部と通信できなければならない。まったく通信できないとしたら、わざわざ sandbox をつくってその中でなにかを行う意味がない。
chroot は file system 以外は制限されないので、外部との通信手段としては network などが使用できる。 uid でも network は問題なく使える。
shell は、一見、外部コマンドを使わずにできることはずいぶんと制限されている。
これは、sandbox を意図してそうなっているというよりは、外部コマンドでできることは外部コマンド (とその組合せ) に任せるという toolbox アプローチに起因していると思う。
ただ、意図はどうあれ、制限されているとすれば、sandbox として使用することは考えられる。 shell から直接外部に影響を与えるところはかなり制限されているので、あとはなんらかの方法で使用可能な外部コマンドを制限すれば、全体として外部への影響を制限できるわけである。この制限には、chroot も使えるし、昔話としては restricted shell という話もあった。
そういえば、restricted shell はなんで廃れちゃったんだろう? 外部コマンドを制限するという方針自体は間違ってはいないように思える。
そういえば、忘れていたが、user level というのも sandbox っぽい。外との通信は system call である。
sandbox の中から外へのアクセスは制限される。この制限は回避できてはならない。
それに対し、外から中へのアクセスはどうか。こちらは、制限があってもいいし、なくてもいい。ただし、制限はあるにしても、sandbox とのインターフェースを実現できる程度でなければならない。また、sandbox 内のコードを不用意に (外の権限で) 動かさないように、注意しないといけない。その意味で、簡単には呼び出せないようにある程度の障壁はあるほうが望ましい。
ところで、g新部さんに尋ねたのだが Linux/x86 は kernel から user のコードを単純に call や jump できるようである。 (仮想メモリとかを考えるとどんな状況でも動くというわけではないようだが、限定された状況では動く模様)
にもかかわらず、そういうことがおこなわれないと信じられるのは、user のコードは C の関数呼び出しでは (リンクできないから) 呼び出せないというあたりらしい。ただ、関数へのポインタに関してはそれだけでは説明できないので、kernel の世界に user level の関数ポインタが入り込まない、もしくは入り込んでもそれが関数ポインタとして扱われないということは微妙で、自明でない。
最近は、(1word の) 整数の sort は O(n log log n) でできるそうな。
<URL:http://portal.acm.org/citation.cfm?id=509993>
どうやってんのかと思って少し読んでみたら、expornential search tree なるものを使うらしい。
あるノードの下にある要素の数の指数に比例した分岐にするというものだが、木を根から葉までたどるコストをちょっと計算してみると、log(n) でいつもどおりである。おかしいとおもって調べてみると、あるノードで直下のノードが d 個あるときにその中から選ぶコストを log(d) と仮定したのがいけなかったらしい。
十分に前処理を行うと、log(d) よりも小さいコストでいけるらしい。
ただ、前処理に O(d**4) とかかかるらしいので、そっちが支配的になっちゃうんじゃないかと思ったが、考えてみると、d は全体の要素数に対して指数的に小さいことが exponential search tree であることから保証されるため、前処理全体のコストは全体の要素数にたいして線形以下になるというわけか。
これは Four Russians Paradigm に似ている。あれは、与えられた問題に対して、まずある程度以下の小さな問題を全て解いておくのだが、そのスレッショルドを log(n) くらいにしておくと、問題のサイズの指数くらいの数の問題があっても、全体としては線形で済む、という話であった。
コストのかかる前処理を、全体のオーダを引き上げない程度に小さなところに適用して高速化するというところが似ているように思う。
そういえば、なぜ Four Russians というのか調べてみると、最初に発表したのが 4人のロシア人だったからの模様。たぶん。おそらく。
Arlazarov V.L., Dinic E.A., Kronrod M.A., Faradzev I.A., On economic construction of the transitive closure of a directed graph, Dokl. Akad. Nauk SSSR, 194, 487-488, 1970 (in Russian). English translation in Soviet Math. Dokl., 11,1209-1210, 1975.
ふと perl の hash 関数を調べてみた。
perl-5.8.7/hv.h:51-109
51: /* FYI: This is the "One-at-a-Time" algorithm by Bob Jenkins 52: * from requirements by Colin Plumb. 53: * (http://burtleburtle.net/bob/hash/doobs.html) */ 54: /* The use of a temporary pointer and the casting games 55: * is needed to serve the dual purposes of 56: * (a) the hashed data being interpreted as "unsigned char" (new since 5.8, 57: * a "char" can be either signed or signed, depending on the compiler) 58: * (b) catering for old code that uses a "char" 59: * 60: * The "hash seed" feature was added in Perl 5.8.1 to perturb the results 61: * to avoid "algorithmic complexity attacks". 62: * 63: * If USE_HASH_SEED is defined, hash randomisation is done by default 64: * If USE_HASH_SEED_EXPLICIT is defined, hash randomisation is done 65: * only if the environment variable PERL_HASH_SEED is set. 66: * For maximal control, one can define PERL_HASH_SEED. 67: * (see also perl.c:perl_parse()). 68: */ 69: #ifndef PERL_HASH_SEED 70: # if defined(USE_HASH_SEED) || defined(USE_HASH_SEED_EXPLICIT) 71: # define PERL_HASH_SEED PL_hash_seed 72: # else 73: # define PERL_HASH_SEED 0 74: # endif 75: #endif 76: #define PERL_HASH(hash,str,len) \ 77: STMT_START { \ 78: register const char *s_PeRlHaSh_tmp = str; \ 79: register const unsigned char *s_PeRlHaSh = (const unsigned char *)s_PeRlHaSh_tmp; \ 80: register I32 i_PeRlHaSh = len; \ 81: register U32 hash_PeRlHaSh = PERL_HASH_SEED; \ 82: while (i_PeRlHaSh--) { \ 83: hash_PeRlHaSh += *s_PeRlHaSh++; \ 84: hash_PeRlHaSh += (hash_PeRlHaSh << 10); \ 85: hash_PeRlHaSh ^= (hash_PeRlHaSh >> 6); \ 86: } \ 87: hash_PeRlHaSh += (hash_PeRlHaSh << 3); \ 88: hash_PeRlHaSh ^= (hash_PeRlHaSh >> 11); \ 89: (hash) = (hash_PeRlHaSh + (hash_PeRlHaSh << 15)); \ 90: } STMT_END 91: 92: /* Only hv.c and mod_perl should be doing this. */ 93: #ifdef PERL_HASH_INTERNAL_ACCESS 94: #define PERL_HASH_INTERNAL(hash,str,len) \ 95: STMT_START { \ 96: register const char *s_PeRlHaSh_tmp = str; \ 97: register const unsigned char *s_PeRlHaSh = (const unsigned char *)s_PeRlHaSh_tmp; \ 98: register I32 i_PeRlHaSh = len; \ 99: register U32 hash_PeRlHaSh = PL_rehash_seed; \
100: while (i_PeRlHaSh--) { \ 101: hash_PeRlHaSh += *s_PeRlHaSh++; \ 102: hash_PeRlHaSh += (hash_PeRlHaSh << 10); \ 103: hash_PeRlHaSh ^= (hash_PeRlHaSh >> 6); \ 104: } \ 105: hash_PeRlHaSh += (hash_PeRlHaSh << 3); \ 106: hash_PeRlHaSh ^= (hash_PeRlHaSh >> 11); \ 107: (hash) = (hash_PeRlHaSh + (hash_PeRlHaSh << 15)); \ 108: } STMT_END 109: #endif
"One-at-a-Time" algorithm by Bob Jenkins というものらしい。
http://burtleburtle.net/bob/hash/doobs.html
gonzui で attack とか attacks を探すと、いろいろと過去の話が引っかかるようだ。
お、Tokyo PyPy Sprint がアナウンスされたか。(23rd - 29th April 2006)
<URL:http://codespeak.net/pipermail/pypy-dev/2006q1/002917.html>
蒲田
蒲田
[latest]