"Обычный" путь состоит в том, чтобы использовать DKMS (первоначально разработанный Dell для поддержки определенных драйверов для их серверов в соответствии с Linux). Даже Nvidia имеет теперь интеграцию их драйвера видеокарты с DKMS - что драйвер должен быть перекомпилирован с каждым обновлением ядра, также.
Спасибо Ramesh, slm и Stéphane за то, что указали мне правильное направление (я пропустил переключатель -R
для lsattr
). К сожалению, до сих пор ни один из ответов не сработал для меня правильно.
Я придумал следующее:
lsattr -aR .//. | sed -rn '/i.+\.\/\/\./s/\.\/\///p'
Это защищает от использования новых строк для того, чтобы файл выглядел неизменным, когда это не так. Это делает а не защиту от файлов, которые установлены как неизменяемые и имеют новые строки в именах файлов. Но так как такой файл должен быть сделан таким образом корневым, я могу быть уверен, что таких файлов не существует в моей файловой системе для моего случая использования. (Этот метод не подходит для обнаружения вторжений в случаях, когда пользователь root может быть скомпрометирован, но тогда и он не использует ту же самую утилиту системы lsattr
, которая также принадлежит тому же пользователю root).
Вместо того, чтобы строить вывод 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
Учитывая, что целью сценария является аудит, особенно важно правильно работать с произвольными именами файлов, напримерс именами, содержащими символы новой строки. Это делает невозможным использование 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 {} +
Я рекомендую использовать менее капризный язык, такой как Perl, Python или Ruby, и выполнять работу lsattr
самостоятельно. lsattr
запускает системный вызов FS_IOC_GETFLAGS
ioctl и получает флаги inode файла . Вот доказательство концепции 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()}'
Обратите внимание, что вывод по-прежнему будет разделен новой строкой. Если вам нужно его обработать, вам придется его адаптировать. Например, вы можете добавить -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 файл
имеет состояние гонки. Эти атрибуты применимы только к обычным файлам или каталогам.Итак, lsattr
сначала выполняет lstat ()
, чтобы проверить правильность типа файла, а затем выполняет open ()
, а затем ioctl ()
для получения атрибутов. Но он вызывает open ()
без O_NOFOLLOW
(или O_NOCTTY). Кто-то может заменить файл
символической ссылкой на / dev / watchdog
, например, между lstat ()
и open ()
и вызвать систему перезагрузить. Он должен выполнить open (O_PATH | O_NOFOLLOW)
, за которым следует fstat ()
, openat ()
и ioctl ()
, чтобы избежать условия гонки. Использование 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;
}
}
Используйте это для поиска всех неизменяемых файлов:
lsattr -laR | grep "Immutable" | awk {'print $1'}
Вероятно, немного поздно добавлять, но я создал три разных файла с неизменяемыми битами в разных подпапках -моего каталога /etc.
Вот как я их нашел:
# lsattr -aR /etc 2>/dev/null | awk '{ print $1 " " $2 }' | grep -w i