出于配置审核的原因,我希望能够在 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_GETFLAGS
ioctl 系统调用并检索文件的索引节点标志。这是 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 为我指明了正确的方向(我错过了 的开关-R
)lsattr
。不幸的是,到目前为止,没有一个答案对我来说正确。
我想出了以下几点:
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;
}
}