1 # Unix SMB/CIFS implementation. Tests for dsdb
2 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2023
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Tests for samba.dsdb."""
20 from samba
.credentials
import Credentials
21 from samba
.samdb
import SamDB
22 from samba
.auth
import system_session
23 from samba
.param
import LoadParm
24 from samba
import dsdb
, functional_level
28 from samba
.tests
.samba_tool
.base
import SambaToolCmdTest
33 class SambaFLStartUpTests(SambaToolCmdTest
):
34 """Test the samba binary sets the DC FL on startup for RW DCs"""
39 cls
.classtempdir
= tempfile
.mkdtemp()
40 cls
.tempsambadir
= os
.path
.join(cls
.classtempdir
, "samba")
45 "--realm=foo.example.com " +
47 ("--targetdir=%s " % cls
.tempsambadir
) +
51 (result
, out
, err
) = cls
.run_command(command
)
56 def tearDownClass(cls
):
57 super().tearDownClass()
58 shutil
.rmtree(cls
.tempsambadir
)
62 path
= os
.path
.join(self
.tempsambadir
, "etc/smb.conf")
63 self
.lp
= LoadParm(filename_for_non_global_lp
=path
)
64 self
.creds
= Credentials()
65 self
.creds
.guess(self
.lp
)
66 self
.session
= system_session()
67 self
.samdb
= SamDB(session_info
=self
.session
,
68 credentials
=self
.creds
,
72 def test_initial_db_fl_state(self
):
73 server_dn
= self
.samdb
.get_dsServiceName()
74 res
= self
.samdb
.search(base
=server_dn
,
76 attrs
=["msDS-Behavior-Version"])
77 # This confirms the domain is in FL 2008 R2 by default, this is
78 # important to verify the original state
79 self
.assertEqual(int(res
[0]["msDS-Behavior-Version"][0]),
80 dsdb
.DS_DOMAIN_FUNCTION_2008_R2
)
82 def test_initial_rootdse_domain_fl_state(self
):
83 res
= self
.samdb
.search(base
="",
85 attrs
=["domainControllerFunctionality"])
86 self
.assertEqual(int(res
[0]["domainControllerFunctionality"][0]),
87 dsdb
.DS_DOMAIN_FUNCTION_2008_R2
)
89 def test_initial_rootdse_dc_fl_state(self
):
90 res
= self
.samdb
.search(base
="",
92 attrs
=["domainFunctionality"])
93 self
.assertEqual(int(res
[0]["domainFunctionality"][0]),
94 dsdb
.DS_DOMAIN_FUNCTION_2008_R2
)
96 def test_initial_lp_fl_state(self
):
97 lp_fl
= self
.lp
.get("ad dc functional level")
98 # This confirms the domain is in FL 2008 R2 by default, this is
99 # important to verify the original state
100 self
.assertEqual(lp_fl
, "2008_R2")
102 def test_initial_lp_fl_state_mapped(self
):
103 # Confirm the same via the dc_level_from_lp wrapper
104 self
.assertEqual(functional_level
.dc_level_from_lp(self
.lp
),
105 dsdb
.DS_DOMAIN_FUNCTION_2008_R2
)
107 def fixup_fl(self
, dn
, fl
):
110 msg
["msDS-Behavior-Version"] = (
111 ldb
.MessageElement(str(fl
),
112 ldb
.FLAG_MOD_REPLACE
,
113 "msDS-Behavior-Version"))
114 self
.samdb
.modify(msg
)
116 def test_change_db_dc_fl(self
):
117 server_dn
= ldb
.Dn(self
.samdb
, self
.samdb
.get_dsServiceName())
120 msg
["msDS-Behavior-Version"] = (
121 ldb
.MessageElement(str(dsdb
.DS_DOMAIN_FUNCTION_2012_R2
),
122 ldb
.FLAG_MOD_REPLACE
,
123 "msDS-Behavior-Version"))
124 self
.samdb
.modify(msg
)
125 self
.addCleanup(self
.fixup_fl
, msg
.dn
, dsdb
.DS_DOMAIN_FUNCTION_2008_R2
)
127 samdb2
= SamDB(session_info
=self
.session
,
128 credentials
=self
.creds
,
131 # Check that the DB set to 2012_R2 has got as far as the rootDSE handler on a new connection
132 res
= samdb2
.search(base
="",
133 scope
=ldb
.SCOPE_BASE
,
134 attrs
=["domainControllerFunctionality"])
135 self
.assertEqual(int(res
[0]["domainControllerFunctionality"][0]),
136 dsdb
.DS_DOMAIN_FUNCTION_2012_R2
)
138 def test_incorrect_db_dc_fl(self
):
139 server_dn
= ldb
.Dn(self
.samdb
, self
.samdb
.get_dsServiceName())
140 self
.addCleanup(self
.fixup_fl
, server_dn
, dsdb
.DS_DOMAIN_FUNCTION_2008_R2
)
142 old_lp_fl
= self
.lp
.get("ad dc functional level")
143 self
.lp
.set("ad dc functional level",
145 self
.addCleanup(self
.lp
.set, "ad dc functional level", old_lp_fl
)
147 dsdb
.check_and_update_fl(self
.samdb
, self
.lp
)
149 # Check this has been set to 2016 per the smb.conf setting
150 res
= self
.samdb
.search(base
="",
151 scope
=ldb
.SCOPE_BASE
,
152 attrs
=["domainControllerFunctionality"])
153 self
.assertEqual(int(res
[0]["domainControllerFunctionality"][0]),
154 dsdb
.DS_DOMAIN_FUNCTION_2016
)
156 samdb3
= SamDB(session_info
=self
.session
,
157 credentials
=self
.creds
,
160 # Check this is still set on re-read (not just the opaque)
161 res
= samdb3
.search(base
="",
162 scope
=ldb
.SCOPE_BASE
,
163 attrs
=["domainControllerFunctionality"])
164 self
.assertEqual(int(res
[0]["domainControllerFunctionality"][0]),
165 dsdb
.DS_DOMAIN_FUNCTION_2016
)
167 res
= self
.samdb
.search(base
=server_dn
,
168 scope
=ldb
.SCOPE_BASE
,
169 attrs
=["msDS-Behavior-Version"])
170 self
.assertEqual(int(res
[0]["msDS-Behavior-Version"][0]),
171 dsdb
.DS_DOMAIN_FUNCTION_2016
)
173 self
.assertEqual(functional_level
.dc_level_from_lp(self
.lp
),
174 dsdb
.DS_DOMAIN_FUNCTION_2016
)
175 self
.assertEqual(self
.lp
.get("ad dc functional level"),
178 if __name__
== "__main__":