不変の属性が設定されたファイルを検索する方法は?


17

構成監査の理由により、不変の属性が設定されているファイルをext3ファイルシステムで検索できるようにしたい(経由chattr +i)。findこれを行うためのオプションまたは類似のオプションが見つかりません。この時点で、lsattr各ディレクトリの出力を解析するための独自のスクリプトを作成する必要があると思います。より良い方法を提供する標準ユーティリティはありますか?


私の場合、侵入検知ではなく、構成管理のためだけに監査していることを明確にしておく必要があります。それら。それにもかかわらず、改行の問題は心に留めておく価値があるので、質問はそのままにします。
14年

回答:


9

grepコマンドをlsattrコマンドにパイプすることで部分的に実行できます。

lsattr -R | grep +i

ただし、ext3ファイルシステム全体に言及すると、検索に関係する可能性があり/proc/devエラーを報告する場合は無視したい他のディレクトリがあります。おそらく次のようにコマンドを実行できます。

lsattr -R 2>/dev/null | grep -- "-i-"

のPCRE機能をgrep使用しgrepて「-i-」をより明示的に一致させることで、少し厳密にしたい場合があります。

lsattr -R 2>/dev/null | grep -P "(?<=-)i(?=-)"

これは、次のような状況で機能します。

$ lsattr -R 2>/dev/null afile | grep -P "(?<=-)i(?=-)"
----i--------e-- afile

しかし、不完全です。不変フラグの周囲に追加の属性が有効になっている場合、それらは一致しません。これは、次のような名前が上記のパターンに一致するファイルによってだまされます。

$ lsattr -R 2>/dev/null afile* | grep -P "(?<=-)i(?=-)"
----i--------e-- afile
-------------e-- afile-i-am

次のように、パターンをもう少し強化できます。

$ lsattr -a -R 2>/dev/null afile* | grep -P "(?<=-)i(?=-).* "
----i--------e-- afile

しかし、それでもまだ壊れやすく、ファイルシステム内のファイルに応じて追加の調整が必要になります。@StephaneChazelesがコメントで言及しているのは言うまでもありませんが、上記のパターンをバイパスするファイル名を持つ改行を含めることで、これをかなり簡単に処理できると述べていますgrep

参照資料

https://groups.google.com/forum/#!topic/alt.os.linux/LkatROg2SlM


1
私は同じスレッドを読んで、投稿しようとしていました。代わりにあなたのエキストラを追加します。
slm

@slm、あなたはどんな変更でも大歓迎です:)
ラメシュ

2
そのアプローチでは、ファイル名に改行文字を含めることで不変ファイルを偽造または非表示にすることができるため、おそらく監査には適していません。また、ファイル名に-i-名前が含まれることは珍しくありません(現在ログオンしているシステムには34個あります)。おそらくたいと思う-aだけでなくオプションを
ステファンChazelas

1
好奇心から+i、最初の例の目的は何ですか?私にはうまくいきません。また、grepping for -i-は、隣接する属性i(などa)が設定されていないことを前提としています。
depquid

1
なぜ単にgrep forしないの^....iですか?または、少なくとも5番目以外の位置にある^[^ ]*i場合のようなものiです。
ルスラン

6

スクリプトの目的が監査である場合、改行を含む名前など、任意のファイル名を正しく処理することが特に重要です。これによりlsattr、複数のファイルで同時に使用することができなくなりますlsattr。その場合、の出力があいまいになる可能性があるためです。

一度に1つのファイルを再帰的にfind呼び出して呼び出すことができlsattrます。ただし、かなり遅くなります。

find / -xdev -exec sh -c '
  for i do
     attrs=$(lsattr -d "$i"); attrs=${attrs%% *}
     case $attrs in
       *i*) printf "%s\0" "$i";;
     esac
  done' sh {} +

Perl、Python、Rubyなどのあまり気味の悪い言語を使用して、lsattr自分で作業を行うことをお勧めします。ioctl syscallをlsattr発行しFS_IOC_GETFLAGS、ファイルのiノードフラグを取得することにより動作します。Pythonの概念実証です。

#!/usr/bin/env python2
import array, fcntl, os, sys
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
count = 0
def check(filename):
    fd = os.open(filename, os.O_RDONLY)
    a = array.array('L', [0])
    fcntl.ioctl(fd, FS_IOC_GETFLAGS, a, True)
    if a[0] & EXT3_IMMUTABLE_FL: 
        sys.stdout.write(filename + '\0')
        global count
        count += 1
    os.close(fd)
for x in sys.argv[1:]:
    for (dirpath, dirnames, filenames) in os.walk(x):
        for name in dirnames + filenames:
            check(os.path.join(dirpath, name))
if count != 0: exit(1)

1
FYI私のシステムです。FS_IOC_GETFLAGS0x80046601
アントノン

1
の値はにFS_IOC_GETFLAGS依存しsizeof(long)ます。たとえば、次のbashコマンドを参照して、C:でマクロが展開される内容を確認してくださいgcc -E - <<< $'#include <linux/fs.h>\nFS_IOC_GETFLAGS' | tail -n1。:私は、それから、次の式だ(((2U) << (((0 +8)+8)+14)) | ((('f')) << (0 +8)) | (((1)) << 0) | ((((sizeof(long)))) << ((0 +8)+8)))、のように簡単に(2U << 30) | ('f' << 8) | 1 | (sizeof(long) << 16)
ルスラン

3

任意のファイル名(改行文字を含むものを含む)を処理するための通常のトリックは、の.//.代わりに内部のファイルを見つけることです.//通常、ディレクトリツリーを走査している間は発生しないため//find(またはここでlsattr -R)出力で新しいファイル名の開始を通知することは確実です。

lsattr -R .//. | awk '
  function process() {
    i = index(record, " ")
    if (i && index(substr(record,1,i), "i"))
      print substr(record, i+4)
  }
  {
    if (/\/\//) {
      process()
      record=$0
    } else {
      record = record "\n" $0
    }
  }
  END{process()}'

出力は引き続き改行で区切られることに注意してください。後処理が必要な場合は、適応させる必要があります。たとえば、を追加-v ORS='\0'してGNUにフィードできるようにすることができxargs -r0ます。

またことに注意してくださいlsattr -R(少なくとも、1.42.13)がパスよりも大きくなっているファイルのフラグを報告することはできませんPATH_MAXを誰かができるよう、(通常は4096)隠すリードにすることをその親ディレクトリ(またはパスコンポーネントのいずれかを移動することによって、このような不変のファイルをそれは、不変なのでそれ自体を除いて)非常に深いディレクトリに。

回避策は、以下で使用findすること-execdirです。

find . -execdir sh -c '
  a=$(lsattr -d "$1") &&
    case ${a%% *} in
      (*i*) ;;
      (*) false
    esac' sh {} \; -print0

さて、で-print0、それは後処理可能ですが、それらのパスで何かをするつもりなら、PATH_MAXより大きいファイルパスでのシステムコールはまだ失敗し、ディレクトリコンポーネントはその間に名前が変更されるかもしれないことに注意してください。

他のユーザーが書き込み可能なディレクトリツリーに関する信頼できるレポートを取得する場合は、lsattrコマンド自体に固有の問題がいくつかあります。

  • lsattr -R .ディレクトリツリーを横断する方法は、競合状態の影響を受けます。.適切なタイミングでいくつかのディレクトリをシンボリックリンクに置き換えることにより、ルーティングされたディレクトリツリーの外部のディレクトリに降りることができます。
  • lsattr -d file競合状態さえあります。これらの属性は、通常のファイルまたはディレクトリにのみ適用されます。だから、lsattrないlstat()ファイルは右のタイプがあり、その後、ないことを確認するために最初にopen()続くioctl()属性を取得します。しかし、それは(O_NOCTTYもopen()なしで)呼び出しますO_NOFOLLOW。誰かが置き換えることができますfileへのシンボリックリンクと/dev/watchdogの間で例えばlstat()open()し、再起動するシステムを引き起こします。それはないはずですopen(O_PATH|O_NOFOLLOW)が続きfstat()openat()そしてioctl()ここでは競合状態を避けるために。

2

Ramesh、slm、Stéphaneに正しい方向を教えてくれてありがとう(私はの-Rスイッチがありませんでしたlsattr)。残念ながら、これまでのところ、答えはどれも正しく機能しませんでした。

私は次のことを思いつきました:

lsattr -aR .//. | sed -rn '/i.+\.\/\/\./s/\.\/\///p'

これにより、ファイルが不変ではないときに不変として見えるようにするために使用される改行から保護されます。不変として設定され、ファイル名に改行があるファイルに対して保護しませ。しかし、そのようなファイルはルートでそのように作成する必要があるので、ユースケースではそのようなファイルがファイルシステムに存在しないと確信できます。(この方法は、rootユーザーが危険にさらされる可能性があるが、どちらlsattrも同じrootユーザーが所有する同じシステムのユーティリティを使用していない場合の侵入検知には適していません。)


ルートのみがファイルに不変ビットを追加できますが、潜在的に他のユーザーはそれらのファイルにつながるパスコンポーネントの名前を後で変更できるため、ファイルパスに改行が含まれる場合があります。また、ユーザーはファイルパス(不変ではない)を作成して、スクリプトを欺いて別のファイルが不変であると考えるようにすることもできます。
ステファンシャゼラス

2

使用find -execの出力を解析すると、遅すぎるされてlsattrいるのと同様に信頼性のないls中としてPythonを使用して、ジルによって答えは一定を選択する必要がioctlPythonインタプリタは、32ビットまたは64ビットであるかに応じて...

目の前の問題は多かれ少なかれ低レベルですので、低レベルに行きましょう:C ++はスクリプト言語ほど悪くはありません:)ボーナスとして、CプリプロセッサのフルパワーでシステムCヘッダーにアクセスできます。

次のプログラムは、1つのファイルシステム内にある不変ファイルを検索します。つまり、マウントポイントを決して超えません。見かけのツリーを検索し、必要に応じてマウントポイントを通過するにFTW_MOUNTは、nftw呼び出しのフラグを削除します。また、シンボリックリンクをたどりません。それらに従うには、FTW_PHYSフラグを削除します。

#define _FILE_OFFSET_BITS 64
#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include <ftw.h>

bool isImmutable(const char* path)
{
    static const int EXT3_IMMUTABLE_FLAG=0x10;

    const int fd=open(path,O_RDONLY|O_NONBLOCK|O_LARGEFILE);
    if(fd<=0)
    {
        perror(("Failed to open file \""+std::string(path)+"\"").c_str());
        return false;
    }
    unsigned long attrs;
    if(ioctl(fd,FS_IOC_GETFLAGS,&attrs)==-1)
    {
        perror(("Failed to get flags for file \""+std::string(path)+"\"").c_str());
        close(fd);
        return false;
    }
    close(fd);
    return attrs & EXT3_IMMUTABLE_FLAG;
}

int processPath(const char* path, const struct stat* info, int type, FTW* ftwbuf)
{
    switch(type)
    {
    case FTW_DNR:
        std::cerr << "Failed to read directory: " << path << "\n";
        return 0;
    case FTW_F:
        if(isImmutable(path))
            std::cout << path << '\n';
        return 0;
    }
    return 0;
}

int main(int argc, char** argv)
{
    if(argc!=2)
    {
        std::cerr << "Usage: " << argv[0] << " dir\n";
        return 1;
    }
    static const int maxOpenFDs=15;
    if(nftw(argv[1],processPath,maxOpenFDs,FTW_PHYS|FTW_MOUNT))
    {
        perror("nftw failed");
        return 1;
    }
}

-1

出力をgrepにパイプする代わりに、awkを使用して出力の最初のフィールドの「i」のみを一致させるのはなぜですか?

lsattr -Ra 2>/dev/null /|awk '$1 ~ /i/ && $1 !~ /^\// {print}'

実際、これを毎日cronで実行して、数百のサーバー上の/ etcディレクトリをスキャンし、出力をsyslogに送信します。その後、Splunk経由で日次レポートを生成できます。

lsattr -Ra 2>/dev/null /etc|awk '$1 ~ /i/ && $1 !~ /^\// {print "Immutable_file="$2}'|logger -p local0.notice -t find_immutable

最初のコードスニペットにはタイプミスがあり、2番目のコードスニペットには私のシステム上で不変のファイルが見つかりません。
14

最初のコマンドの誤字を修正しました。おそらく、2番目のものは不変ファイルがないため、不変ファイルを見つけられないのでしょうか?
Rootdev 14

私は2番目のコマンドが覗いていることに気づかなかった/etc。しかし、両方のコマンドが誤って作成された非不変のファイルを見つけるtouch `"echo -e "bogus\n---------i---e-- changeable"`"
depquid

私の元の投稿では、/ etcディレクトリをスキャンするためにcron経由で実行していると書かれています。あなたがそれを実行する前に投稿またはコマンドのいずれかを読まなかったなら、私はそれを助けることができません。そして、はい、おそらくあなたはそれを感じたらどんな検索でもfすためにエッジケースを構築することができますが、私の元のコマンドでタイプミスをすぐに指摘したので(最後の 'がありません)、私はそれを指摘しますコマンドは書かれたとおりに機能しないため、何も作成されません!:-)
Rootdev 14

私の悪い。これを試してください:touch "`echo -e 'bogus\n---------i---e-- changeable'`"
depquid

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.