Как устроен ntds.dit?

от автора


Все данные каталога Active Directory хранятся в БД в файле ntds.dit. Подавляющее большинство приложений взаимодействуют с каталогом через прослойку DSA реализованную в ntdsa.dll. В свою очередь функции из ntdsa.dll не работают напрямую с ntds.dit, их функционал ограничен потребностями службы каталогов и они не могут дать нам представление о внутреннем устройстве БД Active Directory. Тем не менее ntds.dit представляет собой не что иное как БД JET Blue. В каждой версии windows (начиная с Windows 2000) есть всё необходимое для работы с этой БД.

В статье ниже я попробую осветить следующие вопросы:

  • Какова структура БД?
  • Как данных в ntds.dit получается «дерево»?
  • Как реализовано членство в группах?
  • Каков формат атрибута replPropertyMetaData и с какой точностью в метаданных репликации хранятся временные метки?

Что нужно, чтобы заглянуть в ntds.dit ?

Как минимум: esent.dll
Для «комфортного» использования из различных языков программирования над ESENT API имеются различные «обёртки». Я использовал Meneged Esent в связке с C#. На сайте проекта имеется много примеров — поэтому далее я постараюсь сконцентрироваться именно на содержимом ntds.dit.

Также следует обратить внимание, что у БД JET Blue есть такой параметр как PageSize. По-умолчанию, он равен 4096 (для тех версий esent.dll, с которыми я сталкивался). Так вот в ntds.dit размер страницы равен 8192 и этот параметр следует корректно установить до открытия БД.
У API ESENT есть несколько версий. Эти версии несовместимы! Так ntds.dit из Windows Server 2008 R2 на windows xp не откроется, только на Windoes 7. (Обратную совместимость я не проверял)

Вопрос первый: Какова структура БД?

Функция GetTableNames возвратит список таблиц в базе:
datatable — основная таблица со всеми данными каталога
hiddentable
link_table — согласно статье на technet таблица с информацией о связанных атрибутах (например MemberOf)
quota_rebuild_progress_table
quota_table
sdpropcounttable
sd_table — согласно статье на technet таблица содержит информацию о наследованных правилах доступа для каждого объекта каталога.

Остановимся подробнее на таблице datatable. Список её столбцов имеет вид (на скриншоте первые несколько строк):

Тупик? Нет!
Имена большинства колонок имеют формат ATT<одна латинская буква><цифровой код>. Так вот этот цифровой код — уникальный идентификатор атрибута Active Directory. В таблице есть одна строка, в которой значение цифрового кода столбца совпадает с его значением. Если вывести все значения типа Text из этой строки, то мы увидим, что это определение атрибута «attributeID».

Теперь ничего не мешает получить соответствие всех столбцов таблицы атрибутам Active Directory (таблица приведена для примера — она сокращена, иначе не помещалась в пост)

JET_COLUMNID Column Name Jet Column Type AD atribute Name
JET_COLUMNID(0x6) ab_cnt_col Long Single-value
JET_COLUMNID(0x100) Ancestors_col LongBinary Single-value
JET_COLUMNID(0x536) ATTb131079 Long subRefs Multi-value
JET_COLUMNID(0x121) ATTb131088 Long nCName Multi-value
JET_COLUMNID(0x2dd) ATTb131108 Long dMDLocation Multi-value
JET_COLUMNID(0x42c) ATTb1376270 Long documentAuthor Multi-value
JET_COLUMNID(0x169) ATTb1376277 Long secretary Multi-value
JET_COLUMNID(0x2b8) ATTb1376294 Long associatedName Multi-value
JET_COLUMNID(0x470) ATTb33 Long roleOccupant Multi-value
JET_COLUMNID(0x203) ATTb34 Long seeAlso Multi-value
JET_COLUMNID(0x2d2) ATTb49 Long distinguishedName Multi-value
JET_COLUMNID(0x4cc) ATTb50 Long uniqueMember Multi-value
JET_COLUMNID(0x206) ATTb589856 Long domainPolicyObject Multi-value
JET_COLUMNID(0x250) ATTb589864 Long fromServer Multi-value
JET_COLUMNID(0x205) ATTb589881 Long defaultLocalPolicyObject Multi-value
JET_COLUMNID(0x1cc) ATTb589921 Long preferredOU Multi-value
JET_COLUMNID(0x5a6) ATTb590037 Long defaultClassStore Multi-value
JET_COLUMNID(0x270) ATTb590038 Long nextLevelStore Multi-value
JET_COLUMNID(0x183) ATTb590127 Long notificationList Multi-value
JET_COLUMNID(0x238) ATTb590192 Long rIDManagerReference Multi-value
JET_COLUMNID(0x57c) ATTb590193 Long fSMORoleOwner Multi-value
JET_COLUMNID(0x3c8) ATTb590246 Long domainPolicyReference Multi-value
JET_COLUMNID(0x566) ATTb590281 Long localPolicyReference Multi-value
JET_COLUMNID(0x3c1) ATTb590295 Long trustParent Multi-value
JET_COLUMNID(0x4c1) ATTb590296 Long domainCrossRef Multi-value
JET_COLUMNID(0x5bc) ATTb590304 Long defaultGroup Multi-value
JET_COLUMNID(0x57f) ATTb590318 Long siteServer Multi-value
JET_COLUMNID(0x532) ATTb590338 Long physicalLocationObject Multi-value
JET_COLUMNID(0x563) ATTb590341 Long ipsecPolicyReference Multi-value
JET_COLUMNID(0x24f) ATTb590361 Long dynamicLDAPServer Multi-value
JET_COLUMNID(0x1a4) ATTb590381 Long parentCA Multi-value
JET_COLUMNID(0x233) ATTb590448 Long ipsecOwnersReference Multi-value
JET_COLUMNID(0x345) ATTq590722 Currency aCSNonReservedTxSize Multi-value
JET_COLUMNID(0x398) ATTq591137 Currency aCSMaxTokenBucketPerFlow Multi-value
JET_COLUMNID(0x19b) ATTq591138 Currency aCSMaximumSDUSize Multi-value
JET_COLUMNID(0x4d3) ATTq591139 Currency aCSMinimumPolicedSize Multi-value
JET_COLUMNID(0x244) ATTq591140 Currency aCSMinimumLatency Multi-value
JET_COLUMNID(0x12f) ATTq591141 Currency aCSMinimumDelayVariation Multi-value
JET_COLUMNID(0x16e) ATTq591142 Currency aCSNonReservedPeakRate Multi-value
JET_COLUMNID(0x344) ATTq591143 Currency aCSNonReservedTokenSize Multi-value
JET_COLUMNID(0x4d4) ATTq591144 Currency aCSNonReservedMaxSDUSize Multi-value
JET_COLUMNID(0x343) ATTq591145 Currency aCSNonReservedMinPolicedSize Multi-value
JET_COLUMNID(0x5ac) ATTq591191 Currency mS-SQL-Memory Multi-value
JET_COLUMNID(0x213) ATTq591204 Currency mS-SQL-Status Multi-value
JET_COLUMNID(0x4d5) ATTq591220 Currency mS-SQL-Size Multi-value
JET_COLUMNID(0x2c5) ATTq591266 Currency msDS-Cached-Membership-Time-Stamp Multi-value
JET_COLUMNID(0x548) ATTq591456 Currency msWMI-Int8Default Multi-value
JET_COLUMNID(0x52a) ATTq591457 Currency msWMI-Int8Max Multi-value
JET_COLUMNID(0x53f) ATTq591458 Currency msWMI-Int8Min Multi-value
JET_COLUMNID(0x214) ATTq591459 Currency msWMI-Int8ValidValues Multi-value
JET_COLUMNID(0x2c1) ATTq591520 Currency lastLogonTimestamp Multi-value
JET_COLUMNID(0x2cc) ATTq591794 Currency msDS-LastSuccessfulInteractiveLogonTime Multi-value
JET_COLUMNID(0x462) ATTq591795 Currency msDS-LastFailedInteractiveLogonTime Multi-value
JET_COLUMNID(0x440) ATTq591835 Currency msDS-MaximumPasswordAge Multi-value
JET_COLUMNID(0x2a5) ATTq591836 Currency msDS-MinimumPasswordAge Multi-value
JET_COLUMNID(0x200) ATTq591841 Currency msDS-LockoutObservationWindow Multi-value
JET_COLUMNID(0x2e7) ATTq591842 Currency msDS-LockoutDuration Multi-value
JET_COLUMNID(0x3d0) ATTq591879 Currency msDS-USNLastSyncSuccess Multi-value
JET_COLUMNID(0x49a) ATTq591922 Currency msDS-ClaimValueType Multi-value
JET_COLUMNID(0x476) ATTq592002 Currency msKds-UseStartTime Multi-value
JET_COLUMNID(0x477) ATTq592003 Currency msKds-CreateTime Multi-value
JET_COLUMNID(0x3a0) ATTq592007 Currency msDS-GeoCoordinatesAltitude Multi-value
JET_COLUMNID(0x3a1) ATTq592008 Currency msDS-GeoCoordinatesLatitude Multi-value
JET_COLUMNID(0x3a2) ATTq592009 Currency msDS-GeoCoordinatesLongitude Multi-value
JET_COLUMNID(0x43b) ATTr589945 LongBinary securityIdentifier Multi-value
JET_COLUMNID(0x1c9) ATTr589970 LongBinary objectSid Multi-value
JET_COLUMNID(0x276) ATTr590433 LongBinary sIDHistory Multi-value
JET_COLUMNID(0x443) ATTr590491 LongBinary syncWithSID Multi-value
JET_COLUMNID(0x5e4) ATTr591234 LongBinary mS-DS-CreatorSID Multi-value
JET_COLUMNID(0x50a) ATTr591668 LongBinary msDS-QuotaTrustee Multi-value
JET_COLUMNID(0x1c2) ATTr591978 LongBinary msAuthz-CentralAccessPolicyID Multi-value
JET_COLUMNID(0x5) cnt_col Long Single-value
JET_COLUMNID(0x1) DNT_col Long Single-value
JET_COLUMNID(0x5f1) extendedprocesslinks_col LongBinary Single-value
JET_COLUMNID(0x9) IsVisibleInAB UnsignedByte Single-value
JET_COLUMNID(0x8) NCDNT_col Long Single-value
JET_COLUMNID(0x3) OBJ_col UnsignedByte Single-value
JET_COLUMNID(0x2) PDNT_col Long Single-value
JET_COLUMNID(0x4) RDNtyp_col Long Single-value
JET_COLUMNID(0xa) recycle_time_col Currency Single-value
JET_COLUMNID(0x7) time_col Currency Single-value

Остался один «нерасшифрованый» атрибут ATTc0 — это ссылка на «objectClass».
Обратите внимание — все колонки, хранящие атрибуты каталога Active Directory заведены как Multi-value в не зависимости от схемы.

Вопрос второй: Как данных в ntds.dit получается «дерево»?

Название столбца «DNT_col» провоцирует предположить, что он как-то связан с distinguishedName объекта (как позже выяснилось — они равны)
Значения колонки DNT_col начинаются с 1, причём строка с DNT_col=1 соответствует интересному объекту со значенем атрибута name="$NOT_AN_OBJECT1$"
Строка с DNT_col=2 содержит в себе атрибуты объекта с именем "$ROOT_OBJECT$" (Не путать с RootDSE)
С DNT_col=6 начинаются определения objectClass’ов
Опять тупик? Опять нет!
Пойдём другим путём.
Поиск по колонке ATTm589825 (name) значения типа Text «Administrator» вернул запись с DNT_col=3841 и PDNT_col=1951
Поиск по колонке ATTm589825 (name) значения типа Text «Users» (контейнер, в котором расположена учётная запись стандартного администратора) вернул запись с DNT_col=1951 и PDNT_col=1944
Вот она связь! столбец PDNT_col содержит идентификатор DNT_col родительского объекта.
В строке с DNT_col=1944 ( PDNT_col=1943) — оказался объект домена второго уровня (ntds.dit был взять с тестового контроллера домена второго уровня)
В строке с DNT_col=1943 ( PDNT_col=2) — объект домена первого уровня.

Если вы когда-нибудь задавались вопросом: «Почему в при подключении к dc=contoso,dc=com не отображаются объекты из cn=Configuration,dc=contoso,dc=com?» то вам следует обратить внимание на колонку NCDNT_col — она содержит ссылки на объект контекста именования. (причём, объект домена первого уровня не имеет контекста именования — моэет по этому он и не виден?). Для Объектов в контекстах именования «dc=contoso,dc=com» и «cn=Configuration,dc=contoso,dc=com» значение этой колонки отличается.

Не менее интересно как из этих чисел получается полное distinguishedName объекта? Какой аттрибут обекта является относительным уникальным именем (RDN) и используется для построения полного distinguishedName.
Колонка RDNtyp_col содержит идентификатор атрибута (attributeID), содержащего RDN.
В колонке CNT_col содержится количество объектов, связанных с текущим. Эта колонка используется механизмом Link Cleaner
Если бит в колонке OBJ_col установлен в 1, значит данная строка описывает объект каталога. Иначе — это объект-фантом.

Как реализован SubTree поиск?
Понятно, что при такой структуре для поиска OneLavel достаточно поискать среди записей с PDNT_col равной DN базы поиска, а вот как поискать по SubTree? Обходить все ветви? Нет это слишком сложно.
Присмотримся к значениям в колонке с говорящим именем Ancestors_col. В глаза бросается, что чем глубже в иерархии расположен объект, тем длиннее значение в этой колонке. Каждый уровень вложенности добавляет к длине 4 байта. Это не что иное как список родительских объектов в порядке очерёдности.
Причём список начинается с dn=2, т.е. с "$ROOT_OBJECT$"
Кто занимается поддержанием Ancestors_col в актуальном состоянии? Согласно документу при перемещении объекта в новое место изменяется только его PDNT_col, а значение Ancestors_col обновляется механизмом SDProp заодно с пересчётом новых правил доступа к объекту.

Вопрос третий: Как реализовано членство в группах?

Почему c 2003-го сервера появилась возможность влючать в группу брактически неограниченное число прользователей, и при этом в строковый или числовой multi-value атрибут не удастся записать боле ~1200 значений?
Да потому, что «member» и «memberOf» определены как ссылки. В таблице datatable нет столбцов, соответствующих этим атрибутам. Значения этих атрибутов реализованы как соответствия в отдельной таблице link_table.
Быть может именно поэтому при удалении группы (без использования RecucleBin) мы теряем сведения о её членах — объект удалённой группы получает новый идентификатор в DNT_col при перемещении в контейнер удалённых объектов (а связь группы и её члена построена именно по этому идентификатору)

Посмотрим на столбцы таблицы link_table

JET_COLUMNID(0x2) backlink_DNT Long Single-value
JET_COLUMNID(0x3) link_base Long Single-value
JET_COLUMNID(0x100) link_data LongBinary Single-value
JET_COLUMNID(0x4) link_deactivetime Currency Single-value
JET_COLUMNID(0x5) link_deltime Currency Single-value
JET_COLUMNID(0x1) link_DNT Long Single-value
JET_COLUMNID(0x80) link_metadata Binary Single-value
JET_COLUMNID(0x7) link_ncdnt Long Single-value
JET_COLUMNID(0x101) link_ndesc Long Single-value
JET_COLUMNID(0x6) link_usnchanged Currency Single-value

Названия и тип столбцов намекают, а анализ содержащихся в них данных подтверждает, что:
link_DNT — Содержит идентификатор dn (соответствует DNT_col из datatable) объекта, связь которого описывается (например, объекта группы)
backlink_DNT — Содержит идентификатор dn связанного с ним объекта (например, объекта пользователя)
link_ncdnt — Содержит идентификатор dn контекста именования, в котором расположены участники связи.
link_usnchanged — Содержит USN последнего изменения всязи
link_data — Я видел это поле заполненным только для связей объектов NTDS Settings. Да! Объекты NTDS Settings имеют связи с объектами разделов каталога повидимому посредством этих связей задаются параметры репликации различных разделов каталога.
link_deltime — Содержит время удаления связи.
link_metadata — содержит метаданные, необходимые для репликации внесённых изменений. Заполеннными значения этой колонки я видел только на Windows 2012. Christoffer Andersson пишет, что колонка содержит структуру DS_REPL_VALUE_META_DATA и здесь я с ним категорически не согласен. Структура DS_REPL_VALUE_META_DATA используется на более высоком уровне — её возвращают вызовы ntdsa.dll. Данные, содержащаеся в этой колонке имеют размер меньше, чем необходимо под структуру DS_REPL_VALUE_META_DATA. Пока данная колонка остаётся для меня загадкой. Впринципе метаданные репликации для аттрибутов-связей можно получить из построенного атрибута msDS-ReplValueMetaData, но это не раскрывает внутреннего механизма обработки и хранения этих данных.

Вопрос четвёртый: Каков формат атрибута replPropertyMetaData и с какой точностью в метаданных репликации хранятся временные метки?

В этом атрибуте хранятся метаданные репликации в двоичном виде:

Что хранится в первых 8-ми байтах данной структуры я пока не разобрался.
Следует обратить внимание на тот факт, что в структуре replPropertyMetaData перечисляются только реплицируемые атрибуты со значениями. Так, например, вы не увидите в replPropertyMetaData идентификатора атрибута logonTimestamp.
Тут есть интересный момент: в структуре replPropertyMetaData для объектов безопасности Active Directory (пользователей, групп и компьютеров) присутствует атрибут objectSid.
Кстати, чтобы расшифровать значение этого атрибута — не обязательно так напрягаться — существует построенный атрибут msDS-ReplAttributeMetaData — он собой предстваляет не что иное как разобранное значение replPropertyMetaData.

Временные метки в Active Directory это 64-битные беззнаковые целые числа, указывающие количество секунд, прошедших с 00:00 1 января 1601 года. Отсюда вытекает интересный нюанс: при правке одного атрибута одного и того же объекта на двух контроллерах домена в течение 1 секунды (например при пакетной обработке большого числа пользователей) можно получить неожиданный результат.
По данным статьи на technet выигрывает версия атрибута от контроллера с меньшим GUID.

Использованная информация:

technet.microsoft.com/en-us/library/cc772829%28v=ws.10%29.aspx
blogs.chrisse.se/2012/02/20/how-the-active-directory-data-store-really-works-inside-ntds-dit-part-1
blogs.chrisse.se/2012/02/20/how-the-active-directory-data-store-really-works-inside-ntds-dit-part-2
blogs.chrisse.se/2012/02/20/how-the-active-directory-data-store-really-works-inside-ntds-dit-part-3
blogs.chrisse.se/2012/02/20/how-the-active-directory-data-store-really-works-inside-ntds-dit-part-4
gexeg.blogspot.ru/2009/12/active-directory.html
www.ntdsxtract.com/

Все эксперименты ставились над ntds.dit из Windows Server 2012 Eng

ссылка на оригинал статьи http://habrahabr.ru/post/172865/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *