JPCERT コーディネーションセンター

POS35-C. シンボリックリンクの存在をチェックするときには競合状態を避ける

Windows や UNIX などの一般的オペレーティングシステムの多くは、シンボリック(ソフト)リンクをサポートしている。シンボリックリンクは、UNIX では ln -s コマンドを使って作成できる。Windows では NTFS のディレクトリジャンクションまたは Linkd.exe (Win 2K リソースキット)、あるいはフリーウェアの "junction" を使って作成できる。

シンボリックリンクのチェックは、正しく行わないと競合状態につながる可能性がある。

違反コード

POSIX lstat() 関数は、シンボリックリンクの参照先ではなく、シンボリックリンク自体に関する情報を収集する。この違反コード例では、lstat() 関数を使ってファイルに関する情報を収集し、その st_mode フィールドをチェックしてシンボリックリンクであるかどうかを調べ、シンボリックリンクでなければファイルをオープンする。

char *filename = /*ファイル名 */;
char *userbuf = /* ユーザデータ */;
unsigned int userlen = /* userbuf にはいっている文字列の長さ */;

struct stat lstat_info;
int fd;
/* ... */
if (lstat(filename, &lstat_info) == -1) {
  /* エラー処理 */
}

if (!S_ISLNK(lstat_info.st_mode)) {
   fd = open(filename, O_RDWR);
   if (fd == -1) {
       /* エラー処理 */
   }
}
if (write(fd, userbuf, userlen) < userlen) {
  /* エラー処理 */
}

このコードには、lstat() の呼び出しとそれに続く open() の呼び出しの間に TOCTOU 競合状態が存在する。いずれの関数もファイル名を受け取って動作するが、ファイル名はプログラムの実行に対して非同期的に操作される可能性があるからである(「FIO01-C. ファイルを使用してファイルを識別する関数の使用に注意する」を参照)。

適合コード

以下の適合コードでは、次のように競合状態を排除している。

  1. ファイル名を使って lstat() を呼び出す
  2. open() を呼び出してファイルを開く
  3. open() が返すファイル記述子を使って fstat() を呼び出す
  4. lstat()fstat() が返したファイルの情報を比較し、同じであることを確認する
char *filename = /*ファイル名 */;
char *userbuf = /* ユーザデータ */;
unsigned int userlen = /* userbuf にはいっている文字列の長さ */;

struct stat lstat_info;
struct stat fstat_info;
int fd;
/* ... */
if (lstat(filename, &lstat_info) == -1) {
  /* エラー処理 */
}

fd = open(filename, O_RDWR);
if (fd == -1) {
  /* エラー処理 */
}

if (fstat(fd, &fstat_info) == -1) {
  /* エラー処理 */
}

if (lstat_info.st_mode == fstat_info.st_mode &&
    lstat_info.st_ino == fstat_info.st_ino  &&
    lstat_info.st_dev == fstat_info.st_dev) {
  if (write(fd, userbuf, userlen) < userlen) {
    /* エラー処理 */
  }
}

fstat() はファイル名ではなくファイル記述子に適用されるため、fstat() に渡されるファイルは、オープンされたファイルと一致する。このため TOCTOU 競合状態は排除される。lstat() 関数はシンボリックリンクのリンク先をたどらないが、open() 関数はリンク先をたどる。シンボリックリンクの有無をチェックするには、st_mode フィールドの値を比較すれば十分である。

st_ino フィールドを使って i ノードを比較し、st_dev フィールドを使ってデバイスを比較することで、lstat() に渡されるファイルが fstat() に渡されるファイルと一致することを保証している(「FIO05-C. 複数のファイル属性を使用してファイルを識別する」を参照)。

リスク評価

TOCTOU 競合状態の脆弱性に対する攻撃で、特権を取得される可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

POS35-C

P18

L1

自動検出

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

このルールに対する違反をチェックできる場合もある。特に、 open() 呼出しが、lstat()open()fstat() という順で呼び出されているかどうかをチェックする。

Related Guidelines
MITRE CWE CWE-363, Race condition enabling link following
CWE-365, Race condition in switch
参考資料
[Dowd 2006] Chapter 9, "UNIX 1: Privileges and Files"
[ISO/IEC 9899:2011] Section 7.21, "Input/output <stdio.h>"
[Open Group 2004] lstat()
fstat()
open()
[Seacord 2013] Chapter 8, "File I/O"
翻訳元

これは以下のページを翻訳したものです。

POS35-C. Avoid race conditions while checking for the existence of a symbolic link (revision 65)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter