Системные администраторы часто спрашивали меня, возможно ли получить список всех файлов, созданных определенным пользователем, работая со структурой каталогов, содержащей более 20 тысяч файлов. На первый взгляд этот вопрос казался абсурдным – единственным способом получить такую информацию был бы опрос каждого отдельного файла, с целью получения имени его владельца, и составление списка. Выполнение этой операции потребовало бы вечности. Однако, я вспомнил, что несколько лет назад работал с утилитой, представляющей собой набор ресурсов, под названием subinacl.exe. С ее помощью я менял владельцев файлов. В то же время я обнаружил, что помимо остальных возможностей этой утилиты, она позволяет просматривать свойства файла, в том числе и идентификатор безопасности (SID) пользователя, которому принадлежит файл. Зная, что утилита Subinacl поможет мне получить необходимую информацию, я создал сценарий, названный ownedby.cmd (он приведен в Листинге 1), чтобы реализовать аудит владельцев файлов.

Утилита Subinacl.exe

Существует несколько версий утилиты Subinacl: компания Microsoft выпустила первую версию вместе с пакетом .Microsoft Windows NT Server 4.0 Resource Kit, а вторую, обновленную версию, - с пакетом .Microsoft Windows 2000 Resource Kit. Эти версии немного различаются, так как Microsoft добавила дополнительные возможности в версию для системы Windows 2000, чтобы использовать особенности новой операционной системы. Однако при работе сценария ownedby.cmd, обе версии утилиты Subinacl будут вести себя одинаково. Чтобы просмотреть полный список параметров данной утилиты, наберите в командной строке команду:
subinacl.exe /help /full
Чтобы использовать команду Subinacl для просмотра списка управления доступом для определенного файла, просто наберите
subinacl.exe /file filename

где filename I – это имя файла. Так как вам не требуется вся информация, предоставляемая этой командой, вы можете добавить параметр /noverbose, чтобы отобразить лишь сводку по информации из списка управления доступом файла. На Рисунке 1 показан пример вывода командой Subinacl с использованием всех параметров. Как вы видите, одна из строк содержит фразу «/owner = S-1-5-32-544», которая означает, что идентификатор безопасности пользователя – «S-1-5-32-544».

Вы можете спросить, а что хорошего даст нам знание идентификатора пользователя. В конце концов, не многие администраторы знают наизусть идентификаторы безопасности своих пользователей. Алгоритм, используемый в сценарии ownedby.cmd, на основе известного имени пользователя определяет идентификатор безопасности данного пользователя, после чего ищет все файлы, связанные с данным идентификатором. Поэтому, чтобы реализовать следующий пункт алгоритма, вам понадобится средство для конвертации имени пользователя в его идентификатор безопасности. К счастью компания Microsoft добавила такое средство под названием Getsid в наборы ресурсов для систем Windows 2000 и NT 4.0.

Утилита Getsid.exe

Утилита Getsid.exe сравнивает идентификаторы безопасности двух учетных записей. Синтаксис команды представлен ниже:

getsid server1 account1 server2 account2
где выражения server1 и server2 определяют серверы, которые вы хотите опросить, чтобы получить информацию об идентификаторе безопасности, а account1 и account2 – учетные записи, которые вы хотите сравнить. Сценарий Ownedby.cmd использует утилиту Getsid, чтобы получить идентификатор безопасности для определенной учетной записи пользователя, поэтому, в данном случае, значения server1 и server2 будут одинаковы, равно как и значения account1 и account2. Например, чтобы получить идентификатор безопасности для пользователя JDoe с контролера домена mydc, я ввел команду
getsid mydc JDoe mydc JDoe

Принцип работы сценария

Теперь у вас есть все необходимые средства для работы сценария. Утилита Subinacl позволяет получить идентификатор безопасности пользователя, а утилита Getsid возвращает идентификатор безопасности для указанного имени пользователя. С помощью этих средств вы сможете произвести сравнение «имя пользователя-владелец файла». Задачей сценария Ownedby.cmd является вывод имени и пути для каждого файла в соответствующей структуре каталогов, принадлежащего определенному пользователю. Сценарий использует один жестко кодируемый параметр (имя контроллера домена) и три рабочих параметра: имя пользователя (userid), корневой каталог структуры (root_directory), и им выходного файла (outputfile). Окончательный синтаксис команды Ownedby выглядит следующим образом:

ownedby.cmd userid root_directory outputfile
Например, если я наберу
ownedby.cmd JDoe G:users results.txt

сценарий выведет в файл results.txt, информацию по всем файлам, принадлежащим пользователю JDoe, в папке G:users и ее подкаталогах.

При запуске сценарий должен определить идентификатор безопасности указанного пользователя, и далее сохранить этот идентификатор в переменной sid для дальнейшего использования, как показано в блоке A Листинга 1. Этот код сначала очищает переменную sid, если она была предварительно определена. Далее сценарий определяет идентификатор безопасности пользователя посредством использования утилиты Getsid в цикле и фильтрации вывода, которая применяется для отображения только тех записей, которые содержат выражение "S-" (префикс идентификатора безопасности). Сценарий извлекает значение идентификатора из седьмой строки ("tokens=7") результирующего вывода утилиты Getsid и сохраняет это значение в переменной sid. Значение константы %DC% - это жестко кодируемое имя котроллера домена, которое предварительно уславливается в сценарии. Значение константы %USERID% содержит имя пользователя, которое вы указываете среди параметров командной строки при запуске сценария.

Далее, как видно из кода в блоке B, сценарий запускает команду Dir из тела цикла For для получения списка файлов, которые сценарий будет сравнивать со значением имени пользователя (%USERID%). Некоторые параметры указывают, что команда не должна включать в окончательный вывод заголовок или сводную информацию для всех подкаталогов (/s), что команда должна выводить результаты в формате «bare» (/b), и (так как вам интересны только файлы, а не папки), что команда должна выводить только объекты, не имеющие атрибут «directory» (/a-d). Переменная ROOTDIR содержит имя папки, которую вы хотите просмотреть. В приведенном выше примере, переменная ROOTDIR будет содержать строку «G:users».

После этого начинается исполнение блока с меткой :checkowner для каждого файла, выданного командой Dir. В этом блоке сначала запускается утилита Subinacl для данного имени файла и сохраняет информацию о владельце в переменную owner. Далее сценарий определяет, принадлежит ли файл указанному пользователю, передавая значение, полученное от команды Subinacl в команду Find, чтобы проверить, совпадают ли идентификатор безопасности пользователя и идентификатор безопасности владельца файла. Если значение переменной ERRORLEVEL равно 0, считается, что совпадение было найдено, и сценарий устанавливает переменную found в значение 1, как видно из кода в блоке D. Так как сценарий очищает переменную found перед ее установкой в 1, вы сможете различить случаи, когда флаг found устанавливает в ходе проверки совпадения, и когда значение переменной found устанавливается сценарием. Если переменная установлена в 1, считается, что совпадение найдено, и сценарий добавляет имя файла в выходной файл, как это видно из кода в блоке E.

Если вы посмотрите на сценарий, вы заметите, что он также сравнивает переменную owner с параметром USERID, как это видно из кода в блоке C. Этот шаг необходимо осуществить, так как если вы используете сценарий для сервера с системой NT 4.0, команда Subinacl будет возвращать реальное имя пользователя вместо скрытого идентификатора безопасности. Так как сценарий ownedby.cmd содержит этот дополнительный блок кода, вы сможете исполнять сценарий как в новейших системах Windows Server 2003 и Windows 2000, так и в старых системах NT 4.0.

Подготовка к работе

Хотя сценарий ownedby.cmd относительно короткий и понятный, он имеет некоторые незначительные особенности. Я привожу несколько аспектов, на которые необходимо обратить внимание для обеспечения корректной работы сценария:

  1. Убедитесь, что утилиты subinacl.exe и getsid.exe находятся в той же папке, что и сценарий, или, по крайней мере, в папке, которая является частью вашей переменной окружения PATH.
  2. В выражении Name of DC замените значение mydc именем одного из ваших контроллеров домена, чтобы гарантировать, что команда Getsid «знает» куда направлять запрос на получение идентификаторов безопасности.
  3. Если вы исполняете сценарий в системах Windows XP или Windows 2000, в командной строке для каждого файла, опрашиваемого командой Subinacl, вы будете видеть текст заголовка, подобный приведенному ниже:
  4. Default Sam Server will be 0
    Default Sam Server will be 0
    Эти строки появляются, так как команда Subinacl не позволяет перенаправить текст заголовка, поэтому сценарий ownedby.cmd не может не отображать его и сделать выходной файл «чище».
  5. Сценарий Ownedby.cmd не может определить, какие файлы принадлежат пользователю с учетной записью администратора. Когда администратор создает файл, система Windows автоматически назначает владельцем файла группу Administrators. В результате вы не можете определить, какой именно администратор создал файл, если только у вас на сервере не разрешен аудит файлов.
Я писал и тестировал сценарий ownedby.cmd в системах Windows 2000 Service Pack 4 (SP4) , SP3 и NT 4.0 SP6a, но сценарий должен также хорошо работать и в системах с другими системами. Хотя может показаться, что сценарий имеет ограниченную область применения, я использовал результаты его работы, по меньшей мере, 12 раз по различным причинам – чаще всего для проведения аудита файлов, созданных пользователем, когда пользователь не может ответить на вопросы, касающиеся этих файлов. Сценарий Ownedby.cmd выполняет задачу, требуя минимума усилий с вашей стороны, и каждый раз вы будете выглядеть просто спасителем.
Листинг 1. Сценарий Ownedby.cmd
 
@ECHO OFF
:: -----------------------------------------------------------------
:: Имя файла: ownedby.cmd
::
:: Автор: Steve Seguis
::
:: Задача: Вывод списка всех файлов данной папки и ее подкаталогов,
к которым пользователь имеет доступ
 
:: Результат : Выводится в указанный выходной файл
::
:: Синтаксис : ownedby.cmd   
::
:: Пример: ownedby.cmd JDoe "G:users" results.txt
::   Эта команда выводит в файл results.txt список всех файлов,
::   принадлежащих пользователю Jdoe и находящихся в папке G:users и ее подкаталогах. Параметр
::   root_directory может содержать пробелы и заключается в двойные кавычки.
::
:: Требования: утилиты subinacl.exe и getsid.exe должны находиться в той же папке, что и сценарий, или, по
::       крайней мере, в папке, которая является частью вашей переменной окружения PATH.
:: ------------------------------------------------------------------
setlocal
:: --- Убедитесь, что пользователь указал все три параметра. ---
If "%3"=="" Goto syntax
:: --- Имя контроллера домена ---
Set DC=mydc
:: --- Сохранение параметров. ---
Set USERID=%1
Set ROOTDIR=%2
Set ROOTDIR=%ROOTDIR:"=%
Set OUTPUTFILE=%3
If not exist "%ROOTDIR%" ECHO "%ROOTDIR%" Does not exist & Goto syntax
If "%DC%"=="mydc" ECHO Please change the DC parameter in the script and rerun & Goto 
:EOF
' BEGIN CALLOUT A
:: --- Get SID for user. ---
Set sid=
For /f "tokens=7" %%i in ('getsid \%DC% %USERID% \%DC% %USERID% ^| find "S-"') Do set 
sid=%%i
' END CALLOUT A
:: --- Заголовок для выходного файла. ---
ECHO Listing all files in %rootdir% owned by %userid%... > %OUTPUTFILE%
ECHO -------------------------------------------------------------- >> %OUTPUTFILE%
ECHO. >> %OUTPUTFILE%
:: --- Для каждого файла в папке %ROOTDIR% и ее подкаталогах определяется,
::   принадлежит ли файл пользователю %USERID%. ---
' BEGIN CALLOUT B
For /f "tokens=*" %%i in ('dir /s /b /a-d "%ROOTDIR%"') Do call :checkowner "%%i"
endlocal
Goto :EOF
' END CALLOUT B
:checkowner
Set FILENAME=%1
:: --- Используйте команду Subinacl для вывода информации по владельцу файла и поиска
 совпадений с именем ::   USERID и идентификатором безопасности пользователя ---
Set owner=
For /F "tokens=*" %%j in ('SUBINACL /noverbose /file %FILENAME% ^| find "/owner"') Do set 
owner=%%j
If not defined owner Goto :EOF
' BEGIN CALLOUT C
:: --- Вывод в файл, если пользователь является владельцем. ---
Set found=
ECHO %owner% | find /I "%USERID%" > NUL
If %ERRORLEVEL% EQU 0 Set found=1
' END CALLOUT C
' BEGIN CALLOUT D
If not defined sid Goto :nosid
ECHO %owner% | find /I "%sid%" > NUL
If %ERRORLEVEL% EQU 0 Set found=1
:nosid
' END CALLOUT D
' BEGIN CALLOUT E
If defined found ECHO %FILENAME% >> %OUTPUTFILE%
' END CALLOUT E
Goto :EOF
:syntax
ECHO.
ECHO Syntax: ownedby.cmd ^ ^ ^
ECHO.
ECHO Purpose: Outputs all files in root directory and all its subdirectories
ECHO     that are owned by userid to file specified. If the root_directory
ECHO     contains spaces in the path, the entire path should be
ECHO     enclosed in double quotes.
ECHO.