From 1e024f6568ec03f7361a941ba7f3d7fb5801a30e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 23 Feb 2023 15:15:55 +0100 Subject: [PATCH] domain_update: implement updates 82-89 in order to reach the latest w2016 level I implemented them by looking at source4/setup/adprep/WindowsServerDocs/Domain-Wide-Updates.md.unused and looking at a network capture where a Windows 2022 joins an Windows 2008R2 domain. The strange thing is that Windows (tested with server 2022) uses c81fc9cc-0130-f4d1-b272-634d74818133 for update 83, while Domain-Wide-Updates.md and a fresh installation use c81fc9cc-0130-4fd1-b272-634d74818133. In order to match a fresh installation we use c81fc9cc-0130-4fd1-b272-634d74818133. Signed-off-by: Stefan Metzmacher Reviewed-by: Andrew Bartlett --- python/samba/domain_update.py | 251 +++++++++++++++++++++++++++++++++- 1 file changed, 248 insertions(+), 3 deletions(-) diff --git a/python/samba/domain_update.py b/python/samba/domain_update.py index 93dc3ff7d62..fd55027b780 100644 --- a/python/samba/domain_update.py +++ b/python/samba/domain_update.py @@ -35,7 +35,7 @@ from samba.dsdb import ( ) MIN_UPDATE = 75 -MAX_UPDATE = 81 +MAX_UPDATE = 89 update_map = { # Missing updates from 2008 R2 - version 5 @@ -49,14 +49,30 @@ update_map = { 81: "ff4f9d27-7157-4cb0-80a9-5d6f2b14c8ff", # Windows Server 2012 R2 - version 10 # No updates + # Windows Server 2016 - version 15 + 82: "83c53da7-427e-47a4-a07a-a324598b88f7", + # from the documentation and a fresh installtion + # 83 is this: + # c81fc9cc-0130-4fd1-b272-634d74818133 + # adprep will use this on the wire: + # c81fc9cc-0130-f4d1-b272-634d74818133 + 83: "c81fc9cc-0130-4fd1-b272-634d74818133", + 84: "e5f9e791-d96d-4fc9-93c9-d53e1dc439ba", + 85: "e6d5fd00-385d-4e65-b02d-9da3493ed850", + 86: "3a6b3fbf-3168-4312-a10d-dd5b3393952d", + 87: "7f950403-0ab3-47f9-9730-5d7b0269f9bd", + 88: "434bb40d-dbc9-4fe7-81d4-d57229f7b080", + # Windows Server 2016 - version 16 + 89: "a0c238ba-9e30-4ee6-80a6-43f731e9a5cd", } + functional_level_to_max_update = { DS_DOMAIN_FUNCTION_2008: 74, DS_DOMAIN_FUNCTION_2008_R2: 77, DS_DOMAIN_FUNCTION_2012: 81, DS_DOMAIN_FUNCTION_2012_R2: 81, - DS_DOMAIN_FUNCTION_2016: 88, + DS_DOMAIN_FUNCTION_2016: 89, } functional_level_to_version = { @@ -64,7 +80,7 @@ functional_level_to_version = { DS_DOMAIN_FUNCTION_2008_R2: 5, DS_DOMAIN_FUNCTION_2012: 9, DS_DOMAIN_FUNCTION_2012_R2: 10, - DS_DOMAIN_FUNCTION_2016: 15, + DS_DOMAIN_FUNCTION_2016: 16, } # No update numbers have been skipped over @@ -329,3 +345,232 @@ objectClass: msImaging-PSPs if self.add_update_container: self.update_add(op) + + ## ## Windows Server 2016: Domain-wide updates + ## + ## After the operations that are performed by domainprep in Windows + ## Server 2016 (operations 82-88) complete, the revision attribute for the + ## CN=ActiveDirectoryUpdate,CN=DomainUpdates,CN=System,DC=ForestRootDomain + ## object is set to 15. + + ## Operation 82: {83c53da7-427e-47a4-a07a-a324598b88f7} + ## + ## Create CN=Keys container at root of domain + ## + ## - objectClass: container + ## - description: Default container for key credential objects + ## - ShowInAdvancedViewOnly: TRUE + ## + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA) + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA) + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY) + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DD) + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;ED) + ## + def operation_82(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + keys_dn = "CN=Keys,%s" % str(self.domain_dn) + + sddl = "O:DA" + sddl += "D:" + sddl += "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EA)" + sddl += "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DA)" + sddl += "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;SY)" + sddl += "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;DD)" + sddl += "(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;ED)" + + ldif = """ +dn: %s +objectClass: container +description: Default container for key credential objects +ShowInAdvancedViewOnly: TRUE +nTSecurityDescriptor: %s +""" % (keys_dn, sddl) + + self.samdb.add_ldif(ldif) + + if self.add_update_container: + self.update_add(op) + + ## Operation 83: {c81fc9cc-0130-4fd1-b272-634d74818133} + ## + ## Add Full Control allow aces to CN=Keys container for "domain\Key Admins" + ## and "rootdomain\Enterprise Key Admins". + ## + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;Key Admins) + ## (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;Enterprise Key Admins) + ## + def operation_83(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + keys_dn = "CN=Keys,%s" % str(self.domain_dn) + + aces = ["(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;KA)"] + aces += ["(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EK)"] + + self.sd_utils.update_aces_in_dacl(keys_dn, add_aces=aces) + + if self.add_update_container: + self.update_add(op) + + + ## Operation 84: {e5f9e791-d96d-4fc9-93c9-d53e1dc439ba} + ## + ## Modify otherWellKnownObjects attribute to point to the CN=Keys container. + ## + ## - otherWellKnownObjects: B:32:683A24E2E8164BD3AF86AC3C2CF3F981:CN=Keys,%ws + def operation_84(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + keys_dn = "CN=Keys,%s" % str(self.domain_dn) + + ldif = """ +dn: %s +changetype: modify +add: otherWellKnownObjects +otherWellKnownObjects: B:32:683A24E2E8164BD3AF86AC3C2CF3F981:%s +""" % (str(self.domain_dn), keys_dn) + + self.samdb.modify_ldif(ldif) + + if self.add_update_container: + self.update_add(op) + + + ## Operation 85: {e6d5fd00-385d-4e65-b02d-9da3493ed850} + ## + ## Modify the domain NC to permit "domain\Key Admins" and + ## "rootdomain\Enterprise Key Admins" + ## to modify the msds-KeyCredentialLink attribute. + ## + ## (OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;Key Admins) + ## (OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;Enterprise Key Admins) + ## in root domain, but in non-root domains resulted in a bogus domain-relative + ## ACE with a non-resolvable -527 SID + ## + def operation_85(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + aces = ["(OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;KA)"] + # we use an explicit sid in order to replay the windows mistake + aces += ["(OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;%s-527)" % + str(self.domain_sid)] + + self.sd_utils.update_aces_in_dacl(self.domain_dn, add_aces=aces) + + if self.add_update_container: + self.update_add(op) + + + ## Operation 86: {3a6b3fbf-3168-4312-a10d-dd5b3393952d} + ## + ## Grant the DS-Validated-Write-Computer CAR to creator owner and self + ## + ## (OA;CIIO;SW;9b026da6-0d3c-465c-8bee-5199d7165cba;bf967a86-0de6-11d0-a285-00aa003049e2;PS) + ## (OA;CIIO;SW;9b026da6-0d3c-465c-8bee-5199d7165cba;bf967a86-0de6-11d0-a285-00aa003049e2;CO) + ## + def operation_86(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + aces = ["(OA;CIIO;SW;9b026da6-0d3c-465c-8bee-5199d7165cba;bf967a86-0de6-11d0-a285-00aa003049e2;PS)"] + aces += ["(OA;CIIO;SW;9b026da6-0d3c-465c-8bee-5199d7165cba;bf967a86-0de6-11d0-a285-00aa003049e2;CO)"] + + self.sd_utils.update_aces_in_dacl(self.domain_dn, add_aces=aces) + + if self.add_update_container: + self.update_add(op) + + ## Operation 87: {7f950403-0ab3-47f9-9730-5d7b0269f9bd} + ## + ## Delete the ACE granting Full Control to the incorrect + ## domain-relative Enterprise Key Admins group, and add + ## an ACE granting Full Control to Enterprise Key Admins group. + ## + ## Delete (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;Enterprise Key Admins) + ## Add (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;Enterprise Key Admins) + ## + def operation_87(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + # we use an explicit sid in order to replay the windows mistake + # note this is also strange for a 2nd reason because it doesn't + # delete: ["(OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;%s-527)" + # which was added in operation_85, so the del is basically a noop + # and the result is one additional ace + del_aces = ["(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;%s-527)" % + str(self.domain_sid)] + add_aces = ["(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EK)"] + + self.sd_utils.update_aces_in_dacl(self.domain_dn, + del_aces=del_aces, + add_aces=add_aces) + + if self.add_update_container: + self.update_add(op) + + ## Operation 88: {434bb40d-dbc9-4fe7-81d4-d57229f7b080} + ## + ## Add "msDS-ExpirePasswordsOnSmartCardOnlyAccounts" on the domain NC object + ## and set default value to FALSE + ## + def operation_88(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + ldif = """ +dn: %s +changetype: modify +add: msDS-ExpirePasswordsOnSmartCardOnlyAccounts +msDS-ExpirePasswordsOnSmartCardOnlyAccounts: FALSE +""" % str(self.domain_dn) + + self.samdb.modify_ldif(ldif) + + if self.add_update_container: + self.update_add(op) + + ## Windows Server 2016 (operation 89) complete, the **revision** attribute for the + ## CN=ActiveDirectoryUpdate,CN=DomainUpdates,CN=System,DC=ForestRootDomain object + ## is set to **16**. + ## + + ## Operation 89: {a0c238ba-9e30-4ee6-80a6-43f731e9a5cd} + ## + ## Delete the ACE granting Full Control to Enterprise Key Admins and + ## add an ACE granting Enterprise Key Admins Full Control over just + ## the msdsKeyCredentialLink attribute. + ## + ## Delete (A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;Enterprise Key Admins) + ## Add (OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;Enterprise Key Admins)| + ## + def operation_89(self, op): + if self.update_exists(op): + return + self.raise_if_not_fix(op) + + # Note this only fixes the mistake from operation_87 + # but leaves the mistake of operation_85 if we're + # not in the root domain... + del_aces = ["(A;CI;RPWPCRLCLOCCDCRCWDWOSDDTSW;;;EK)"] + add_aces = ["(OA;CI;RPWP;5b47d60f-6090-40b2-9f37-2a4de88f3063;;EK)"] + + self.sd_utils.update_aces_in_dacl(self.domain_dn, + del_aces=del_aces, + add_aces=add_aces) + + if self.add_update_container: + self.update_add(op)