1 # Changes a FSMO role owner
3 # Copyright Nadezhda Ivanova 2009
4 # Copyright Jelmer Vernooij 2009
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 import samba
.getopt
as options
23 from ldb
import LdbError
24 from samba
.dcerpc
import drsuapi
, misc
25 from samba
.auth
import system_session
26 from samba
.netcmd
import (
32 from samba
.samdb
import SamDB
34 def get_fsmo_roleowner(samdb
, roledn
):
35 """Gets the owner of an FSMO role
37 :param roledn: The DN of the FSMO role
39 res
= samdb
.search(roledn
,
40 scope
=ldb
.SCOPE_BASE
, attrs
=["fSMORoleOwner"])
42 master_owner
= res
[0]["fSMORoleOwner"][0]
46 def transfer_dns_role(outf
, sambaopts
, credopts
, role
, samdb
):
47 """Transfer dns FSMO role. """
49 if role
== "domaindns":
50 domain_dn
= samdb
.domain_dn()
51 role_object
= "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
52 elif role
== "forestdns":
53 forest_dn
= samba
.dn_from_dns_name(samdb
.forest_dns_name())
54 role_object
= "CN=Infrastructure,DC=ForestDnsZones," + forest_dn
57 res
= samdb
.search(role_object
,
58 attrs
=["fSMORoleOwner"],
60 controls
=["extended_dn:1:1"])
62 if 'fSMORoleOwner' in res
[0]:
64 master_guid
= str(misc
.GUID(ldb
.Dn(samdb
,
65 res
[0]['fSMORoleOwner'][0])
66 .get_extended_component('GUID')))
67 master_owner
= str(ldb
.Dn(samdb
, res
[0]['fSMORoleOwner'][0]))
68 except LdbError
, (num
, msg
):
69 raise CommandError("GUID not found in partition naming master DN %s : %s \n" %
70 (res
[0]['fSMORoleOwner'][0], msg
))
71 except LdbError
, (num
, msg
):
72 raise CommandError("DNS partion %s not found : %s" % (role
, msg
))
74 if role
== "domaindns":
75 master_dns_name
= '%s._msdcs.%s' % (master_guid
,
76 samdb
.domain_dns_name())
77 new_dns_name
= '%s._msdcs.%s' % (samdb
.get_ntds_GUID(),
78 samdb
.domain_dns_name())
79 elif role
== "forestdns":
80 master_dns_name
= '%s._msdcs.%s' % (master_guid
,
81 samdb
.forest_dns_name())
82 new_dns_name
= '%s._msdcs.%s' % (samdb
.get_ntds_GUID(),
83 samdb
.forest_dns_name())
85 new_owner
= samdb
.get_dsServiceName()
87 if master_dns_name
!= new_dns_name
:
88 lp
= sambaopts
.get_loadparm()
89 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
90 samdb
= SamDB(url
="ldap://%s" % (master_dns_name
),
91 session_info
=system_session(),
92 credentials
=creds
, lp
=lp
)
95 m
.dn
= ldb
.Dn(samdb
, role_object
)
96 m
["fSMORoleOwner"] = ldb
.MessageElement(master_owner
,
102 except LdbError
, (num
, msg
):
103 raise CommandError("Failed to delete role '%s': %s" %
107 m
.dn
= ldb
.Dn(samdb
, role_object
)
108 m
["fSMORoleOwner"]= ldb
.MessageElement(new_owner
,
113 except LdbError
, (num
, msg
):
114 raise CommandError("Failed to add role '%s': %s" % (role
, msg
))
117 connection
= samba
.drs_utils
.drsuapi_connect(samdb
.host_dns_name(),
119 except samba
.drs_utils
.drsException
, e
:
120 raise CommandError("Drsuapi Connect failed", e
)
123 drsuapi_connection
= connection
[0]
124 drsuapi_handle
= connection
[1]
125 req_options
= drsuapi
.DRSUAPI_DRS_WRIT_REP
126 NC
= role_object
[18:]
127 samba
.drs_utils
.sendDsReplicaSync(drsuapi_connection
,
131 except samba
.drs_utils
.drsException
, estr
:
132 raise CommandError("Replication failed", estr
)
134 outf
.write("FSMO transfer of '%s' role successful\n" % role
)
137 outf
.write("This DC already has the '%s' FSMO role\n" % role
)
140 def transfer_role(outf
, role
, samdb
):
141 """Transfer standard FSMO role. """
143 domain_dn
= samdb
.domain_dn()
144 rid_dn
= "CN=RID Manager$,CN=System," + domain_dn
145 naming_dn
= "CN=Partitions,%s" % samdb
.get_config_basedn()
146 infrastructure_dn
= "CN=Infrastructure," + domain_dn
147 schema_dn
= str(samdb
.get_schema_basedn())
148 new_owner
= samdb
.get_dsServiceName()
150 m
.dn
= ldb
.Dn(samdb
, "")
152 master_owner
= get_fsmo_roleowner(samdb
, rid_dn
)
153 m
["becomeRidMaster"]= ldb
.MessageElement(
154 "1", ldb
.FLAG_MOD_REPLACE
,
157 master_owner
= get_fsmo_roleowner(samdb
, domain_dn
)
159 res
= samdb
.search(domain_dn
,
160 scope
=ldb
.SCOPE_BASE
, attrs
=["objectSid"])
162 sid
= res
[0]["objectSid"][0]
163 m
["becomePdc"]= ldb
.MessageElement(
164 sid
, ldb
.FLAG_MOD_REPLACE
,
166 elif role
== "naming":
167 master_owner
= get_fsmo_roleowner(samdb
, naming_dn
)
168 m
["becomeDomainMaster"]= ldb
.MessageElement(
169 "1", ldb
.FLAG_MOD_REPLACE
,
170 "becomeDomainMaster")
171 elif role
== "infrastructure":
172 master_owner
= get_fsmo_roleowner(samdb
, infrastructure_dn
)
173 m
["becomeInfrastructureMaster"]= ldb
.MessageElement(
174 "1", ldb
.FLAG_MOD_REPLACE
,
175 "becomeInfrastructureMaster")
176 elif role
== "schema":
177 master_owner
= get_fsmo_roleowner(samdb
, schema_dn
)
178 m
["becomeSchemaMaster"]= ldb
.MessageElement(
179 "1", ldb
.FLAG_MOD_REPLACE
,
180 "becomeSchemaMaster")
182 raise CommandError("Invalid FSMO role.")
184 if master_owner
!= new_owner
:
187 except LdbError
, (num
, msg
):
188 raise CommandError("Transfer of '%s' role failed: %s" %
191 outf
.write("FSMO transfer of '%s' role successful\n" % role
)
194 outf
.write("This DC already has the '%s' FSMO role\n" % role
)
197 class cmd_fsmo_seize(Command
):
198 """Seize the role."""
200 synopsis
= "%prog [options]"
202 takes_optiongroups
= {
203 "sambaopts": options
.SambaOptions
,
204 "credopts": options
.CredentialsOptions
,
205 "versionopts": options
.VersionOptions
,
209 Option("-H", "--URL", help="LDB URL for database or target server",
210 type=str, metavar
="URL", dest
="H"),
212 help="Force seizing of the role without attempting to transfer first.",
213 action
="store_true"),
214 Option("--role", type="choice", choices
=["rid", "pdc", "infrastructure",
215 "schema", "naming", "domaindns", "forestdns", "all"],
216 help="""The FSMO role to seize or transfer.\n
217 rid=RidAllocationMasterRole\n
218 schema=SchemaMasterRole\n
219 pdc=PdcEmulationMasterRole\n
220 naming=DomainNamingMasterRole\n
221 infrastructure=InfrastructureMasterRole\n
222 domaindns=DomainDnsZonesMasterRole\n
223 forestdns=ForestDnsZonesMasterRole\n
224 all=all of the above\n
225 You must provide an Admin user and password."""),
230 def seize_role(self
, role
, samdb
, force
):
231 """Seize standard fsmo role. """
233 serviceName
= samdb
.get_dsServiceName()
234 domain_dn
= samdb
.domain_dn()
235 self
.infrastructure_dn
= "CN=Infrastructure," + domain_dn
236 self
.naming_dn
= "CN=Partitions,%s" % samdb
.get_config_basedn()
237 self
.schema_dn
= str(samdb
.get_schema_basedn())
238 self
.rid_dn
= "CN=RID Manager$,CN=System," + domain_dn
242 m
.dn
= ldb
.Dn(samdb
, self
.rid_dn
)
244 m
.dn
= ldb
.Dn(samdb
, domain_dn
)
245 elif role
== "naming":
246 m
.dn
= ldb
.Dn(samdb
, self
.naming_dn
)
247 elif role
== "infrastructure":
248 m
.dn
= ldb
.Dn(samdb
, self
.infrastructure_dn
)
249 elif role
== "schema":
250 m
.dn
= ldb
.Dn(samdb
, self
.schema_dn
)
252 raise CommandError("Invalid FSMO role.")
253 #first try to transfer to avoid problem if the owner is still active
255 master_owner
= get_fsmo_roleowner(samdb
, m
.dn
)
256 if master_owner
!= serviceName
:
258 self
.message("Attempting transfer...")
259 if not transfer_role(self
.outf
, role
, samdb
):
260 #transfer failed, use the big axe...
262 self
.message("Transfer unsuccessful, seizing...")
264 self
.message("Not seizing role as transfer was successful")
266 if force
is not None or seize
== True:
267 self
.message("Seizing %s FSMO role..." % role
)
268 m
["fSMORoleOwner"]= ldb
.MessageElement(
269 serviceName
, ldb
.FLAG_MOD_REPLACE
,
273 except LdbError
, (num
, msg
):
274 raise CommandError("Failed to seize '%s' role: %s" %
276 self
.outf
.write("FSMO seize of '%s' role successful\n" % role
)
279 self
.outf
.write("This DC already has the '%s' FSMO role\n" % role
)
282 def seize_dns_role(self
, role
, samdb
, credopts
, sambaopts
,
284 """Seize DNS FSMO role. """
286 serviceName
= samdb
.get_dsServiceName()
287 domain_dn
= samdb
.domain_dn()
288 forest_dn
= samba
.dn_from_dns_name(samdb
.forest_dns_name())
289 self
.domaindns_dn
= "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
290 self
.forestdns_dn
= "CN=Infrastructure,DC=ForestDnsZones," + forest_dn
293 if role
== "domaindns":
294 m
.dn
= ldb
.Dn(samdb
, self
.domaindns_dn
)
295 elif role
== "forestdns":
296 m
.dn
= ldb
.Dn(samdb
, self
.forestdns_dn
)
298 raise CommandError("Invalid FSMO role.")
299 #first try to transfer to avoid problem if the owner is still active
301 master_owner
= get_fsmo_roleowner(samdb
, m
.dn
)
302 if master_owner
!= serviceName
:
304 self
.message("Attempting transfer...")
305 if not transfer_dns_role(self
.outf
, sambaopts
, credopts
, role
,
307 #transfer failed, use the big axe...
309 self
.message("Transfer unsuccessful, seizing...")
311 self
.message("Not seizing role as transfer was successful\n")
313 if force
is not None or seize
== True:
314 self
.message("Seizing %s FSMO role..." % role
)
315 m
["fSMORoleOwner"]= ldb
.MessageElement(
316 serviceName
, ldb
.FLAG_MOD_REPLACE
,
320 except LdbError
, (num
, msg
):
321 raise CommandError("Failed to seize '%s' role: %s" %
323 self
.outf
.write("FSMO seize of '%s' role successful\n" % role
)
326 self
.outf
.write("This DC already has the '%s' FSMO role\n" % role
)
329 def run(self
, force
=None, H
=None, role
=None,
330 credopts
=None, sambaopts
=None, versionopts
=None):
332 lp
= sambaopts
.get_loadparm()
333 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
335 samdb
= SamDB(url
=H
, session_info
=system_session(),
336 credentials
=creds
, lp
=lp
)
339 self
.seize_role("rid", samdb
, force
)
340 self
.seize_role("pdc", samdb
, force
)
341 self
.seize_role("naming", samdb
, force
)
342 self
.seize_role("infrastructure", samdb
, force
)
343 self
.seize_role("schema", samdb
, force
)
344 self
.seize_dns_role("domaindns", samdb
, credopts
, sambaopts
,
346 self
.seize_dns_role("forestdns", samdb
, credopts
, sambaopts
,
349 if role
== "domaindns" or role
== "forestdns":
350 self
.seize_dns_role(role
, samdb
, credopts
, sambaopts
,
353 self
.seize_role(role
, samdb
, force
)
356 class cmd_fsmo_show(Command
):
357 """Show the roles."""
359 synopsis
= "%prog [options]"
361 takes_optiongroups
= {
362 "sambaopts": options
.SambaOptions
,
363 "credopts": options
.CredentialsOptions
,
364 "versionopts": options
.VersionOptions
,
368 Option("-H", "--URL", help="LDB URL for database or target server",
369 type=str, metavar
="URL", dest
="H"),
374 def run(self
, H
=None, credopts
=None, sambaopts
=None, versionopts
=None):
375 lp
= sambaopts
.get_loadparm()
376 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
378 samdb
= SamDB(url
=H
, session_info
=system_session(),
379 credentials
=creds
, lp
=lp
)
381 domain_dn
= samdb
.domain_dn()
382 forest_dn
= samba
.dn_from_dns_name(samdb
.forest_dns_name())
383 infrastructure_dn
= "CN=Infrastructure," + domain_dn
384 naming_dn
= "CN=Partitions,%s" % samdb
.get_config_basedn()
385 schema_dn
= samdb
.get_schema_basedn()
386 rid_dn
= "CN=RID Manager$,CN=System," + domain_dn
387 domaindns_dn
= "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
388 forestdns_dn
= "CN=Infrastructure,DC=ForestDnsZones," + forest_dn
390 infrastructureMaster
= get_fsmo_roleowner(samdb
, infrastructure_dn
)
391 pdcEmulator
= get_fsmo_roleowner(samdb
, domain_dn
)
392 namingMaster
= get_fsmo_roleowner(samdb
, naming_dn
)
393 schemaMaster
= get_fsmo_roleowner(samdb
, schema_dn
)
394 ridMaster
= get_fsmo_roleowner(samdb
, rid_dn
)
395 domaindnszonesMaster
= get_fsmo_roleowner(samdb
, domaindns_dn
)
396 forestdnszonesMaster
= get_fsmo_roleowner(samdb
, forestdns_dn
)
398 self
.message("SchemaMasterRole owner: " + schemaMaster
)
399 self
.message("InfrastructureMasterRole owner: " + infrastructureMaster
)
400 self
.message("RidAllocationMasterRole owner: " + ridMaster
)
401 self
.message("PdcEmulationMasterRole owner: " + pdcEmulator
)
402 self
.message("DomainNamingMasterRole owner: " + namingMaster
)
403 self
.message("DomainDnsZonesMasterRole owner: " + domaindnszonesMaster
)
404 self
.message("ForestDnsZonesMasterRole owner: " + forestdnszonesMaster
)
406 class cmd_fsmo_transfer(Command
):
407 """Transfer the role."""
409 synopsis
= "%prog [options]"
411 takes_optiongroups
= {
412 "sambaopts": options
.SambaOptions
,
413 "credopts": options
.CredentialsOptions
,
414 "versionopts": options
.VersionOptions
,
418 Option("-H", "--URL", help="LDB URL for database or target server",
419 type=str, metavar
="URL", dest
="H"),
420 Option("--role", type="choice", choices
=["rid", "pdc", "infrastructure",
421 "schema", "naming", "domaindns", "forestdns", "all"],
422 help="""The FSMO role to seize or transfer.\n
423 rid=RidAllocationMasterRole\n
424 schema=SchemaMasterRole\n
425 pdc=PdcEmulationMasterRole\n
426 naming=DomainNamingMasterRole\n
427 infrastructure=InfrastructureMasterRole\n
428 domaindns=DomainDnsZonesMasterRole\n
429 forestdns=ForestDnsZonesMasterRole\n
430 all=all of the above\n
431 You must provide an Admin user and password."""),
436 def run(self
, force
=None, H
=None, role
=None,
437 credopts
=None, sambaopts
=None, versionopts
=None):
439 lp
= sambaopts
.get_loadparm()
440 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
442 samdb
= SamDB(url
=H
, session_info
=system_session(),
443 credentials
=creds
, lp
=lp
)
446 transfer_role(self
.outf
, "rid", samdb
)
447 transfer_role(self
.outf
, "pdc", samdb
)
448 transfer_role(self
.outf
, "naming", samdb
)
449 transfer_role(self
.outf
, "infrastructure", samdb
)
450 transfer_role(self
.outf
, "schema", samdb
)
451 transfer_dns_role(self
.outf
, sambaopts
, credopts
, "domaindns", samdb
)
452 transfer_dns_role(self
.outf
, sambaopts
, credopts
, "forestdns", samdb
)
454 if role
== "domaindns" or role
== "forestdns":
455 transfer_dns_role(self
.outf
, sambaopts
, credopts
, role
, samdb
)
457 transfer_role(self
.outf
, role
, samdb
)
460 class cmd_fsmo(SuperCommand
):
461 """Flexible Single Master Operations (FSMO) roles management."""
464 subcommands
["seize"] = cmd_fsmo_seize()
465 subcommands
["show"] = cmd_fsmo_show()
466 subcommands
["transfer"] = cmd_fsmo_transfer()