如何搜索具有不可变属性集的文件?

如何搜索具有不可变属性集的文件?

出于配置审核的原因,我希望能够在 ext3 文件系统中搜索具有不可变属性集的文件(通过chattr +i)。我找不到find执行此操作的任何选项或类似选项。此时,恐怕我必须编写自己的脚本来解析lsattr每个目录的输出。是否有一个标准实用程序可以提供更好的方法?

答案1

它可以通过命令管道传输lsattr命令来部分完成grep

lsattr -R | grep +i

但是,我相信当您提到整个ext3文件系统时,搜索可能涉及/proc/dev以及一些其他目录,它们可能会报告一些您只想忽略的错误。您可能可以运行该命令,

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

您可能希望grep通过使用 的 PCRE 工具来更严格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

但它仍然有点太脆弱,需要根据文件系统中的文件进行额外的调整。更不用说作为@史蒂芬查泽勒斯在评论中提到,可以通过在文件名中包含换行符来绕过上述模式来相当容易地进行游戏grep

参考

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

答案2

鉴于该脚本的目的是审计,正确处理任意文件名(例如包含换行符的名称)尤为重要。这使得无法lsattr同时在多个文件上使用,因为在这种情况下的输出lsattr可能不明确。

您可以一次递归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 {} +

lsattr我建议使用 Perl、Python 或 Ruby 等不太古怪的语言,并自己完成工作。lsattr通过发出FS_IOC_GETFLAGSioctl 系统调用并检索文件的索引节点标志。这是 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)

答案3

感谢 Ramesh、slm 和 Stéphane 为我指明了正确的方向(我错过了 的开关-Rlsattr。不幸的是,到目前为止,没有一个答案对我来说正确。

我想出了以下几点:

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

这可以防止使用换行符使文件看起来是不可变的,而实际上它并不是。确实如此不是防止被设置为不可变且文件名中包含换行符的文件。但由于这样的文件必须由 root 以这种方式创建,所以我可以确信在我的用例中我的文件系统上不存在这样的文件。 (在 root 用户可能受到威胁的情况下,此方法不适合入侵检测,但也不适合使用lsattr也属于同一 root 用户的同一系统实用程序。)

答案4

使用find -exec太慢,解析输出lsattr类似地不可靠ls,使用 Python 如吉尔斯的回答ioctl需要根据Python解释器是32位还是64位来选择常数...

手头的问题或多或少是低级的,所以让我们进入更低的级别:C++ 作为脚本语言并没有那么糟糕:) 作为奖励,它可以使用 C 预处理器的全部功能访问系统 C 头文件。

以下程序搜索不可变文件,保留在一个文件系统内,即从不跨越挂载点。要搜索明显的树,根据需要跨越安装点,请删除调用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;
    }
}

相关内容