Bashで「mv」を使用したシンボリックリンク競合状態の防止

Bashで「mv」を使用したシンボリックリンク競合状態の防止

次のように、「echo」(または他のBash組み込み機能)を使用してターゲットファイル(通常のLinuxではrootとして)に安全に書きたいと思います。

echo "foo" > /destination/dir/filename

ただし、問題は、通常のシステムユーザーが/ detination / dirにアクセスできるため、シンボリックリンク条件が発生する危険性があることです。

Cを使用するときにTOC-TOUを防ぐ方法に関するすべての「方法」を読んだので、シンボリックリンクを確認または削除してから開かないでください(一般的なアドバイスはopen()にO_NOFOLLOWを使用しているようです)。

しかし、これらすべて(カーネルopen()とそのフラグへのアクセス)はBashを介して可能ではありません(または私が間違っていますか?)。

それから私は思った

  • mktempを使用して一時ファイルを作成する
  • chown+chmod 一時ファイルを適切に
  • 一時ファイルに書き込む内容を書き込みます。
  • Bashパラメーター「-T」を使用して、一時ファイルを宛先ディレクトリに移動します。

したがって、いくつかのBash疑似コード(どこにもエラーチェックはありません)

TEMPFILE=$(mktemp)

chown root:root $TEMPFILE
chmod 0600 $TEMPFILE
echo "contents" > $TEMPFILE
mv -T $TEMPFILE /destination/dir/filename

私はシステムファイルへのシンボリックリンクとして "/destination/dir/filename"を使ってテストしましたが、うまくいきます。 "mv"は一時ファイルを "filename"に正しく移動し、シンボリックリンクは削除されます(私の意図でした)。 、ファイルを上書きしません。

安全/競争条件などの問題が欠落していると思いますか?

ありがとうございます:-)

ベストアンサー1

安全に存在すると仮定しましょう/destination/dir。 (そうでない場合は、root以外のユーザーがサブディレクトリにアクセスまたは作成できないように十分に制限された権限を使用して最上位ディレクトリを作成します。階層が完了したら権限を軽減します。)

mktemp1つの方法は、ターゲットディレクトリ内に一時的ですが、一意の名前を持つファイルを作成することです。その後、その内容が記録され、最後にターゲットmvに記録されます。

重要なのは、mv同じファイルシステムでソースとターゲットを使用する場合、名前変更プロセスの一部としてターゲットが削除されることです。これはrename(2)、システムコールを介してカーネル自体で実行されるアトミックアクションです。

すでに存在する場合は、newpathそのアイテムにアクセスしようとしている他のプロセスで見つからないアイテムが見つからないように自動的に置き換えられますnewpath。ただし、名前が変更されたファイルを参照するウィンドウが表示されることoldpathがあります。newpath

あなたの実装に非常に似ている簡単な実装は次のとおりです。

base='/destination/dir'                # Use '.' for current directory
file='filename'

tf=$(mktemp "$base/XXXXXXXXXX.tmp")    # Created with mode 600
echo "contents" >"$tf"                 # Always double-quote variables when used
if ! mv -Tf "$tf" "$base/$file"
then
    echo "Error writing to $base/$file" >&2
    rm -f "$tf"                        # Clean up temporary file
fi

POSIXの世界では、これに対応することを達成することはmv -Tより困難です。ここでは、一時ディレクトリを作成する能力に依存します。実際の状況では、成功するまで別のディレクトリ名を繰り返すループで処理するのが最善ですmkdir。しかし、ここでは一度だけ生成しようとしています。

base='/destination/dir'                # Use '.' for current directory
file='filename'

td="$base/dir.$$.tmp"                  # Must be unique
if mkdir -m700 "$td"                   # Will fail if not unique
then
    echo "contents" >"$td/$file"
    if ! mv -f "$td/$file" "$base/"    # Overwrite/replace $base/filename, or fail
    then
        echo "Error writing to $base/$file" >&2
    fi
    rm -rf "$td"                       # Clean up temporary directory
else
    echo "Error creating temporary directory $td" >&2
fi

このmktempコマンドもPOSIXではありませんが、次のものがあります。推奨事項の実装これには利用可能です。

おすすめ記事