2013年3月5日火曜日

(FreeBSD) 環境変数PATHに「::」というのが入ってると、カレントディレクトリの意味

コマンド検索パスを指定する環境変数PATHに「::」というのが入ってたんだけど、それって実はヤバいことだった、という話。



環境変数PATHに、カレントディレクトリを意味する「.」を入れてはいけない、というのはセキュリティ対策として基本中の基本です。
たまたまカレントディレクトリに「ls」という名前の実行ファイルがあって、そこでlsコマンドを実行してしまうと、カレントディレクトリにある得体の知れないlsコマンドが実行されてしまい、危険である!っていうことなんですが。



自分もそんなことは十分承知していたのですが、たまたま.cshrcの編集ミスで、PATHの設定が少しおかしなことになっていて、それが実はとんでもないことになっていた、という恥ずかしい話。



具体的には、環境変数PATHの途中に「::」っていうのが入ってたんです。たぶんエディタで.cshrcを書き換えたときの編集ミスが原因です。



たとえばこんな感じです。こんなことが起きます。



% cd /tmp
% setenv PATH /sbin::/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/home/nhh/bin
% cat > ls
echo "Aho------!!!"
% chmod +x ls
% ls
Aho------!!!
% which ls
./ls



まじっすか!



カレントディレクトリのファイルが実行されちゃいました。



「::」を「:」に戻すと・・・



% setenv PATH /sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/home/nhh/bin
% rehash
% ls
dbus-B9NLymYlNW         dbus-yMFq5nXnHn         ls
~~~ファイル一覧あれこれ~~~



% which ls
/bin/ls



ということで正常になりました。



へー!!!はじめてUNIXに触れて20うん年、ぜんぜん知りませんでした。環境変数PATHに「::」があるとカレントディレクトリを指定したと同じことになるとは。



ちなみに、このことに気がついたきっかけは、たしかperlで書かれた何かのコマンドを実行したときに、PATHにカレントディレクトリを含めちゃいけないよ、と警告されたことでした。





ん~、これってFreeBSDだけの挙動ですかね。



気になったらさっそくソースコードを調べよう!



たぶん、execlp()の処理を読めばわかるんじゃないかな?と検討がつくので、適当にgrepしてみる。なんというローテク(笑)



% grep -Rl execlp /usr/src/lib
/usr/src/lib/libc/net/rcmdsh.c
/usr/src/lib/libc/gen/Makefile.inc
/usr/src/lib/libc/gen/Symbol.map
/usr/src/lib/libc/gen/exec.3
/usr/src/lib/libc/gen/exec.c
/usr/src/lib/libedit/vi.c
/usr/src/lib/libutil/pw_util.c



たぶん、/usr/src/lib/libc/gen/exec.c がそれっぽいです。



Webでもソースコードが見られます。



http://svnweb.freebsd.org/base/release/9.1.0/lib/libc/gen/exec.c?revision=243808&view=markup



execlpを追っかけていくと、execvPe()にきて、そこでPATHを検索していました。



・・・ありました!ご丁寧なことに、::だったらカレントディレクトリだよん、という意味のコメントがついてるじゃないですか!



        strcpy(cur, path);
        while ((p = strsep(&cur, ":")) != NULL) {
                /*
                 * It's a SHELL path -- double, leading and trailing colons
                 * mean the current directory.
                 */
                if (*p == '\0') {
                        p = ".";
                        lp = 1;
                } else
                        lp = strlen(p);
                ln = strlen(name);



う~ん、これが仕様なんですね。





20130304



写真は本文と関係ありません。







0 件のコメント:

コメントを投稿