{"id":1100,"date":"2024-08-19T13:58:15","date_gmt":"2024-08-19T11:58:15","guid":{"rendered":"https:\/\/blog.chrisse.se\/?p=1100"},"modified":"2024-08-19T13:58:15","modified_gmt":"2024-08-19T11:58:15","slug":"the-active-directory-database-epoch-copy-protection","status":"publish","type":"post","link":"https:\/\/blog.chrisse.se\/?p=1100","title":{"rendered":"The Active Directory Database Epoch \/ Copy Protection"},"content":{"rendered":"\n<p>This blog post will describe and go into details about the may not so known Active Directory Database Epoch \/ Copy Protection.<\/p>\n\n\n\n<p>This concept that was introduced with Active Directory Application Mode) ADAM initial release (Now follows some ADAM history), but never made it to Active Directory Domain Services (AD DS) until Longhorn\/WS2008 for some good reasons. One of the changes with Windows Server 2008 was that AD got exposed as a windows service allowing admins to stop, restart and start the service on DCs, this behavior already existed since day one in Active Directory Application Mode (ADAM ) first introduced as a standalone package to the web in November 2003 and was also targeting Windows XP, in Windows Server 2003 R2, Active Directory Application Mode (ADAM) Service Pack 1 (SP1) is included as a windows component (On CD2) but still ship as a download for other operating systems, Active Directory Application Mode (ADAM) Service Pack 2 (SP2) is the latest version to ship except some QFEs and Security Updates before the source code of Active Directory Application Mode (ADAM) merges into the Directory Service (DS) source depot, integrates into windows builds and get\u2019s available again with Windows Server 2008\/Windows 7 rebranded as Active Directory Lightweight Directory Service (AD LDS) \u00a0as an installable role with the operating system \u2013 no more downloads are available.<\/p>\n\n\n\n<p><strong>The potential issues and damages that this feature is trying to protect from, given the above.<\/strong><\/p>\n\n\n\n<p>Pre-Windows Server 2008 and ADAM it wasn\u2019t that easy to manually restore or replace the database (DIT) using none supported restore methods for let\u2019s say average sysadmins \u2013 you needed to boot into DSRM \u2013 Directory Services Restore Mode cause the DB was locked by LSASS\/ESE and the database by default was located under C:\\Windows folder.<\/p>\n\n\n\n<p>Now with these requirements gone as peer Windows Server 2008 and ADAM\/AD LDS \u2013 Microsoft wanted to prevent some scenarios:<\/p>\n\n\n\n<p><strong>Potentially foreseen scenarios:<\/strong><\/p>\n\n\n\n<p>\u2022 Stop service, copy off database, restart service, make changes that replicate, stop service, copy old database back in.<\/p>\n\n\n\n<p>\u2022 Stop service, copy off database from instance1, stop second service, copy database over data files for instance2.<\/p>\n\n\n\n<p>Both these scenarios breaks the Active Directory replication model, because two different\/distinct changes could get the same &lt;OriginatingInvocationID&gt;:&lt;OriginatingUSN&gt; pair (lets call this the ChangeID).&nbsp; If two changes have the same ChangeID, one of those two changes would fail to replicate, because the DSA will claim to have \u201cseen\u201d the change with the same ChangeID, from the previous instance of the database, and fail to replicate the new change with this ChangeID.&nbsp; Also, other partners of this instance, will assume they\u2019ve seen any new changes made that match previous changes this DSA has replicated out.<\/p>\n\n\n\n<p><strong>The implementation<\/strong> \u2013 a database epoch stored in both in the database (DIT) and the registry, during initialization of the DSA and the DB a random value is written in case of a none-existent epoch or the current epoch +1 is written both to the database (DIT) \u2013 more specifically to the &nbsp;\u201c<strong>epoch_col<\/strong>\u201d column in the <strong>hiddentable<\/strong> and the \u201c<strong>HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\NTDS\\Parameters\\ DSA Database Epoch<\/strong>\u201d Registry DWORD.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"515\" src=\"https:\/\/blog.chrisse.se\/wp-content\/uploads\/2024\/08\/epoch-1024x515.png\" alt=\"\" class=\"wp-image-1102\" srcset=\"https:\/\/blog.chrisse.se\/wp-content\/uploads\/2024\/08\/epoch-1024x515.png 1024w, https:\/\/blog.chrisse.se\/wp-content\/uploads\/2024\/08\/epoch-300x151.png 300w, https:\/\/blog.chrisse.se\/wp-content\/uploads\/2024\/08\/epoch-768x386.png 768w, https:\/\/blog.chrisse.se\/wp-content\/uploads\/2024\/08\/epoch.png 1348w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/figure>\n\n\n\n<p><strong>Rules are as following<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>If both the registry and the database have NULL \u2013 it\u2019s considered a match. The epoch is initially set with rand() in both the database and the registry<\/li>\n\n\n\n<li>if the value stored in the database is > than the value stored in the registry \u2013 it\u2019s considered a match.<\/li>\n\n\n\n<li>If the value stored in the database and the registry match \u2013 it\u2019s considered a match<\/li>\n\n\n\n<li>If either 2 or 3 the epoch is advanced by 1 in both the database and the registry, if any of the updates fail the ESE update to the DIT is rolledback.<\/li>\n\n\n\n<li>If the \u201c<strong>HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\NTDS\\Parameters\\ Disable DSA Database Epoch Check<\/strong>\u201d Registry DWORD is set to <strong>1<\/strong> it\u2019s considered a match.<\/li>\n\n\n\n<li>If the values do not match as per above 1-3, a \u201crestore\u201d is forced to get a new invocationID for the DSA\/DRA and the following event is logged:<\/li>\n<\/ol>\n\n\n\n<p>Table&nbsp;1: Epoch mismatch and restore initiated.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Event ID<\/td><td>Source<\/td><td>Category<\/td><td>Description<\/td><\/tr><tr><td>2524<\/td><td>ActiveDirectory_DomainService<\/td><td>Backup<\/td><td>The Directory Server detected that the database has been replaced.&nbsp; This is an unsafe and unsupported operation. &nbsp; User Action: None.&nbsp; Active Directory Domain Services was able to recover the database in this instance, but this is not guaranteed in all circumstances. Replacing the database is strongly discouraged.&nbsp; The user is strongly encouraged to use the backup and restore facility to rollback the database.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>The \u201crestore\u201d is forced by writing\/setting the \u201c<strong>state_col<\/strong>\u201d in the \u201c<strong>hiddentable<\/strong>\u201d to \u201c<strong>4<\/strong>\u201d aka \u201cBackedupDIT\u201d as well \u201cuns_col\u201d to next USN-1 and \u201c<strong>backupexpiration<\/strong>_col\u201d to the next day.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>If there is any other failure than retrieving the epoch from the database than that the column is null\/nonexistent or that the registry value is nonexistent \u2013 the DSA is going to fail init and stop hard with the error message:<\/li>\n<\/ul>\n\n\n\n<p>Table&nbsp;2: Epoch mismatch fatal<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>Event ID<\/td><td>Source<\/td><td>Category<\/td><td>Description<\/td><\/tr><tr><td>2542<\/td><td>ActiveDirectory_DomainService<\/td><td>Backup<\/td><td>The Directory Server detected that the database has been replaced.&nbsp; This is an unsafe and unsupported operation. The service will stop until the problem is corrected. &nbsp; User Action: Restore the previous copy of the database that was in use on this machine. In the future, the user is strongly encouraged to use the backup and restore facility to rollback the database. &nbsp; This error can be suppressed and the database repaired by removing the following registry key. &nbsp; Additional Data Registry key: System\\CurrentControlSet\\Services\\NTDS\\Parameters Registry value: DSA Database Epoch<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Note that this feature could have been implemented differently, technically there is no need to change\/advance the epoch each time during init, not even during an originate write to the database (DIT) \u2013 however it must only really change if a new originate write is replicated off the local DSA.<\/p>\n\n\n\n<p>I wrote this blog post because I got a question \u2013 \u201cSo what happens when distribution DIT is mounted\u201d \u2013 If you don\u2019t know what the distribution DIT is you can read about it here: <a href=\"https:\/\/blog.chrisse.se\/?p=1005\">https:\/\/blog.chrisse.se\/?p=1005<\/a><\/p>\n\n\n\n<p>The answer to the question can be figured out by reading this post (Rule 1 above), The answer explained is that the \u201c<strong>epoch_col<\/strong>\u201d is NULL in the <a>Distribution DIT <\/a>and once the DSA Initialize on the Distribution DIT for the first time the registry value don\u2019t exist and peer above that is considered a match, a random value is written as the initial epoch to both the database (DIT) and the registry on the DSA.<\/p>\n\n\n\n<p>Bonus: the \u201c<strong>state_col<\/strong>\u201d of a distribution DIT should be \u201c<strong>1<\/strong>\u201d.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This blog post will describe and go into details about the may not so known Active Directory Database Epoch \/ Copy Protection. This concept that was introduced with Active Directory Application Mode) ADAM initial release (Now follows some ADAM history), but never made it to Active Directory Domain Services (AD DS) until Longhorn\/WS2008 for some &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/blog.chrisse.se\/?p=1100\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;The Active Directory Database Epoch \/ Copy Protection&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[1],"tags":[16],"class_list":["post-1100","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-ntds-dit"],"_links":{"self":[{"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=\/wp\/v2\/posts\/1100","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1100"}],"version-history":[{"count":3,"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=\/wp\/v2\/posts\/1100\/revisions"}],"predecessor-version":[{"id":1104,"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=\/wp\/v2\/posts\/1100\/revisions\/1104"}],"wp:attachment":[{"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1100"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1100"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.chrisse.se\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1100"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}