web-dev-qa-db-ja.com

不変の属性が設定されたファイルを検索するにはどうすればよいですか?

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

18
depquid

Ramesh、slm、Stéphaneのおかげで私を正しい方向に向けてくれました(-Rlsattrのスイッチ)。残念ながら、これまでのところ、どの回答も正しく機能しませんでした。

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

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

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

2
depquid

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

lsattr -R | grep +i

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

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

grepのPCRE機能を使用して "-i-"をより明確に一致させることにより、grepをもう少し厳密にすることができます。

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

9
Ramesh

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

findで再帰し、一度に1つのファイルに対して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の作業を行うこと。lsattrFS_IOC_GETFLAGS ioctl syscallおよびファイルの取得 inode flags 。 Python=概念実証です。

#!/usr/bin/env python2
import array, fcntl, os, sys
S_IFMT =  0o170000
S_IFDIR = 0o040000
S_IFREG = 0o100000
FS_IOC_GETFLAGS = 0x80086601
EXT3_IMMUTABLE_FL = 0x00000010
count = 0
def check(filename):
    mode = os.lstat(filename).st_mode
    if mode & S_IFMT not in [S_IFREG, S_IFDIR]:
        return
    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)

任意のファイル名(改行文字を含むものも含む)を処理するには、 通常のトリック は、_.//._ではなく_._内のファイルを検索します。 _//_は通常、ディレクトリツリーをトラバースしている間は発生しないため、_//_は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()}'
_

出力はまだ改行で区切られていることに注意してください。それを後処理する必要がある場合は、それを適応させる必要があります。たとえば、GNUの_-v ORS='\0'_にフィードできるように_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_NOFOLLOW_(またはO_NOCTTY)なしでopen()を呼び出します。誰かがfileを__/dev/watchdog_へのシンボリックリンクに置き換えると、たとえばlstat()open()の間でシステムが再起動する可能性があります。競合状態を回避するために、ここでopen(O_PATH|O_NOFOLLOW)の後にfstat()openat()ioctl()を実行する必要があります。
3

find -execの使用は遅すぎ、lsattrの解析出力は ls と同様に信頼できず、Pythonを使用して Gilles による回答では、Pythonインタプリタが32ビットか64ビットかによって、ioctlの定数を選択する必要があります...

手元の問題は多かれ少なかれ低レベルなので、低レベルに行きましょう:C++はスクリプト言語ほど悪くありません:)おまけに、Cプリプロセッサーの全機能を備えたシステムCヘッダーにアクセスできます。

次のプログラムは、不変のファイルを検索し、1つのファイルシステム内にとどまります。つまり、マウントポイントを超えることはありません。必要に応じてマウントポイントを横切って見かけ上のツリーを検索するには、nftw呼び出しのFTW_MOUNTフラグを削除します。また、シンボリックリンクをたどりません。それらを実行するには、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;
    }
}
2
Ruslan