ttt

getttyent

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

2013-03-04 20:58:38 | デジタル・インターネット

コマンド検索パスを指定する環境変数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

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