1 # LDIF helper functions for the samba_kcc tool
3 # Copyright (C) Dave Craft 2011
4 # Copyright (C) Andrew Bartlett 2015
6 # Andrew Bartlett's alleged work performed by his underlings Douglas
7 # Bagnall and Garming Sam.
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 from samba
import Ldb
, ldb
, read_and_sub_file
25 from samba
.auth
import system_session
26 from samba
.samdb
import SamDB
27 from samba
.common
import dsdb_Dn
30 class LdifError(Exception):
34 def write_search_result(samdb
, f
, res
):
36 lstr
= samdb
.write_ldif(msg
, ldb
.CHANGETYPE_NONE
)
40 def ldif_to_samdb(dburl
, lp
, ldif_file
, forced_local_dsa
=None):
41 """Routine to import all objects and attributes that are relevent
42 to the KCC algorithms from a previously exported LDIF file.
44 The point of this function is to allow a programmer/debugger to
45 import an LDIF file with non-security relevent information that
46 was previously extracted from a DC database. The LDIF file is used
47 to create a temporary abbreviated database. The KCC algorithm can
48 then run against this abbreviated database for debug or test
49 verification that the topology generated is computationally the
50 same between different OSes and algorithms.
52 :param dburl: path to the temporary abbreviated db to create
53 :param ldif_file: path to the ldif file to import
55 if os
.path
.exists(dburl
):
56 raise LdifError("Specify a database (%s) that doesn't already exist." %
59 # Use ["modules:"] as we are attempting to build a sam
60 # database as opposed to start it here.
61 tmpdb
= Ldb(url
=dburl
, session_info
=system_session(),
62 lp
=lp
, options
=["modules:"])
64 tmpdb
.transaction_start()
66 data
= read_and_sub_file(ldif_file
, None)
67 tmpdb
.add_ldif(data
, None)
69 tmpdb
.modify_ldif("""dn: @ROOTDSE
71 replace: dsServiceName
72 dsServiceName: CN=NTDS Settings,%s
74 """ % forced_local_dsa
)
76 except Exception, estr
:
77 tmpdb
.transaction_cancel()
78 raise LdifError("Failed to import %s: %s" % (ldif_file
, estr
))
80 tmpdb
.transaction_commit()
82 # We have an abbreviated list of options here because we have built
83 # an abbreviated database. We use the rootdse and extended-dn
84 # modules only during this re-open
85 samdb
= SamDB(url
=dburl
, session_info
=system_session(), lp
=lp
,
86 options
=["modules:rootdse,extended_dn_in,"
87 "extended_dn_out_ldb"])
91 def samdb_to_ldif_file(samdb
, dburl
, lp
, creds
, ldif_file
):
92 """Routine to extract all objects and attributes that are relevent
93 to the KCC algorithms from a DC database.
95 The point of this function is to allow a programmer/debugger to
96 extract an LDIF file with non-security relevent information from
97 a DC database. The LDIF file can then be used to "import" via
98 the import_ldif() function this file into a temporary abbreviated
99 database. The KCC algorithm can then run against this abbreviated
100 database for debug or test verification that the topology generated
101 is computationally the same between different OSes and algorithms.
103 :param dburl: LDAP database URL to extract info from
104 :param ldif_file: output LDIF file name to create
107 samdb
= SamDB(url
=dburl
,
108 session_info
=system_session(),
109 credentials
=creds
, lp
=lp
)
110 except ldb
.LdbError
, (enum
, estr
):
111 raise LdifError("Unable to open sam database (%s) : %s" %
114 if os
.path
.exists(ldif_file
):
115 raise LdifError("Specify a file (%s) that doesn't already exist." %
119 f
= open(ldif_file
, "w")
120 except IOError as ioerr
:
121 raise LdifError("Unable to open (%s) : %s" % (ldif_file
, str(ioerr
)))
125 attrs
= ["objectClass",
134 "msDS-NC-Replica-Locations",
135 "msDS-NC-RO-Replica-Locations"]
137 sstr
= "CN=Partitions,%s" % samdb
.get_config_basedn()
138 res
= samdb
.search(base
=sstr
, scope
=ldb
.SCOPE_SUBTREE
,
140 expression
="(objectClass=crossRef)")
142 # Write partitions output
143 write_search_result(samdb
, f
, res
)
145 # Query cross reference container
146 attrs
= ["objectClass",
152 "msDS-Behavior-Version",
153 "msDS-EnabledFeature"]
155 sstr
= "CN=Partitions,%s" % samdb
.get_config_basedn()
156 res
= samdb
.search(base
=sstr
, scope
=ldb
.SCOPE_SUBTREE
,
158 expression
="(objectClass=crossRefContainer)")
160 # Write cross reference container output
161 write_search_result(samdb
, f
, res
)
164 attrs
= ["objectClass",
170 sstr
= "CN=Sites,%s" % samdb
.get_config_basedn()
171 sites
= samdb
.search(base
=sstr
, scope
=ldb
.SCOPE_SUBTREE
,
173 expression
="(objectClass=site)")
176 write_search_result(samdb
, f
, sites
)
178 # Query NTDS Site Settings
180 sitestr
= str(msg
.dn
)
182 attrs
= ["objectClass",
186 "interSiteTopologyGenerator",
187 "interSiteTopologyFailover",
191 sstr
= "CN=NTDS Site Settings,%s" % sitestr
192 res
= samdb
.search(base
=sstr
, scope
=ldb
.SCOPE_BASE
,
195 # Write Site Settings output
196 write_search_result(samdb
, f
, res
)
198 # Naming context list
201 # Query Directory Service Agents
205 ncattrs
= ["hasMasterNCs",
207 "hasPartialReplicaNCs",
209 "msDS-hasFullReplicaNCs",
210 "msDS-HasInstantiatedNCs"]
211 attrs
= ["objectClass",
218 "msDS-Behavior-Version"]
220 res
= samdb
.search(base
=sstr
, scope
=ldb
.SCOPE_SUBTREE
,
221 attrs
=attrs
+ ncattrs
,
222 expression
="(objectClass=nTDSDSA)")
224 # Spin thru all the DSAs looking for NC replicas
225 # and build a list of all possible Naming Contexts
226 # for subsequent retrieval below
231 # Some of these have binary DNs so
232 # use dsdb_Dn to split out relevent parts
233 dsdn
= dsdb_Dn(samdb
, value
)
235 if dnstr
not in nclist
:
239 write_search_result(samdb
, f
, res
)
241 # Query NTDS Connections
245 attrs
= ["objectClass",
257 res
= samdb
.search(base
=sstr
, scope
=ldb
.SCOPE_SUBTREE
,
259 expression
="(objectClass=nTDSConnection)")
260 # Write NTDS Connection output
261 write_search_result(samdb
, f
, res
)
263 # Query Intersite transports
264 attrs
= ["objectClass",
270 "bridgeheadServerListBL",
271 "transportAddressAttribute"]
273 sstr
= "CN=Inter-Site Transports,CN=Sites,%s" % \
274 samdb
.get_config_basedn()
275 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_SUBTREE
,
277 expression
="(objectClass=interSiteTransport)")
279 # Write inter-site transport output
280 write_search_result(samdb
, f
, res
)
283 attrs
= ["objectClass",
294 sstr
= "CN=Sites,%s" % \
295 samdb
.get_config_basedn()
296 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_SUBTREE
,
298 expression
="(objectClass=siteLink)",
299 controls
=['extended_dn:0'])
301 # Write siteLink output
302 write_search_result(samdb
, f
, res
)
304 # Query siteLinkBridge
305 attrs
= ["objectClass",
311 sstr
= "CN=Sites,%s" % samdb
.get_config_basedn()
312 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_SUBTREE
,
314 expression
="(objectClass=siteLinkBridge)")
316 # Write siteLinkBridge output
317 write_search_result(samdb
, f
, res
)
319 # Query servers containers
320 # Needed for samdb.server_site_name()
321 attrs
= ["objectClass",
327 sstr
= "CN=Sites,%s" % samdb
.get_config_basedn()
328 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_SUBTREE
,
330 expression
="(objectClass=serversContainer)")
332 # Write servers container output
333 write_search_result(samdb
, f
, res
)
336 # Needed because some transport interfaces refer back to
337 # attributes found in the server object. Also needed
338 # so extended-dn will be happy with dsServiceName in rootDSE
339 attrs
= ["objectClass",
347 sstr
= "CN=Sites,%s" % samdb
.get_config_basedn()
348 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_SUBTREE
,
350 expression
="(objectClass=server)")
352 # Write server output
353 write_search_result(samdb
, f
, res
)
355 # Query Naming Context replicas
356 attrs
= ["objectClass",
362 "msDS-Behavior-Version",
367 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_BASE
,
370 # Write naming context output
371 write_search_result(samdb
, f
, res
)
373 # Query rootDSE replicas
374 attrs
= ["objectClass",
378 "rootDomainNamingContext",
379 "configurationNamingContext",
380 "schemaNamingContext",
381 "defaultNamingContext",
385 res
= samdb
.search(sstr
, scope
=ldb
.SCOPE_BASE
,
388 # Record the rootDSE object as a dn as it
389 # would appear in the base ldb file. We have
390 # to save it this way because we are going to
391 # be importing as an abbreviated database.
392 res
[0].dn
= ldb
.Dn(samdb
, "@ROOTDSE")
394 # Write rootdse output
395 write_search_result(samdb
, f
, res
)
397 except ldb
.LdbError
, (enum
, estr
):
398 raise LdifError("Error processing (%s) : %s" % (sstr
, estr
))