1 # rodc related commands
3 # Copyright Andrew Tridgell 2010
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 from samba
.netcmd
import Command
, CommandError
, Option
, SuperCommand
20 import samba
.getopt
as options
21 from samba
.samdb
import SamDB
22 from samba
.auth
import system_session
24 from samba
.dcerpc
import misc
, drsuapi
25 from samba
.drs_utils
import drs_Replicate
28 class RODCException(Exception):
29 def __init__(self
, value
):
33 return "%s: %s" % (self
.__class
__.__name
__, self
.value
)
35 class NamingError(RODCException
):
38 class ReplicationError(RODCException
):
41 class cmd_rodc_preload(Command
):
42 """Preload accounts for an RODC. Multiple accounts may be requested."""
44 synopsis
= "%prog (<SID>|<DN>|<accountname>)+ ... [options]"
46 takes_optiongroups
= {
47 "sambaopts": options
.SambaOptions
,
48 "versionopts": options
.VersionOptions
,
49 "credopts": options
.CredentialsOptions
,
53 Option("--server", help="DC to use", type=str),
54 Option("--file", help="Read account list from a file, or - for stdin (one per line)", type=str),
55 Option("--ignore-errors", help="When preloading multiple accounts, skip any failing accounts", action
="store_true"),
58 takes_args
= ["account*"]
60 def get_dn(self
, samdb
, account
):
61 '''work out what DN they meant'''
63 # we accept the account in SID, accountname or DN form
64 if account
[0:2] == 'S-':
65 res
= samdb
.search(base
="<SID=%s>" % account
,
66 expression
="objectclass=user",
67 scope
=ldb
.SCOPE_BASE
, attrs
=[])
68 elif account
.find('=') >= 0:
69 res
= samdb
.search(base
=account
,
70 expression
="objectclass=user",
71 scope
=ldb
.SCOPE_BASE
, attrs
=[])
73 res
= samdb
.search(expression
="(&(samAccountName=%s)(objectclass=user))" % ldb
.binary_encode(account
),
74 scope
=ldb
.SCOPE_SUBTREE
, attrs
=[])
76 raise NamingError("Failed to find account '%s'" % account
)
77 return str(res
[0]["dn"])
80 def run(self
, *accounts
, **kwargs
):
81 sambaopts
= kwargs
.get("sambaopts")
82 credopts
= kwargs
.get("credopts")
83 versionpts
= kwargs
.get("versionopts")
84 server
= kwargs
.get("server")
85 accounts_file
= kwargs
.get("file")
86 ignore_errors
= kwargs
.get("ignore_errors")
89 raise Exception("You must supply a server")
91 if accounts_file
is not None:
93 if accounts_file
== "-":
94 for line
in sys
.stdin
:
95 accounts
.append(line
.strip())
97 for line
in open(accounts_file
, 'r'):
98 accounts
.append(line
.strip())
100 lp
= sambaopts
.get_loadparm()
102 creds
= credopts
.get_credentials(lp
, fallback_machine
=True)
104 # connect to the remote and local SAMs
105 samdb
= SamDB(url
="ldap://%s" % server
,
106 session_info
=system_session(),
107 credentials
=creds
, lp
=lp
)
109 local_samdb
= SamDB(url
=None, session_info
=system_session(),
110 credentials
=creds
, lp
=lp
)
112 destination_dsa_guid
= misc
.GUID(local_samdb
.get_ntds_GUID())
114 binding_options
= "seal"
115 if lp
.log_level() >= 9:
116 binding_options
+= ",print"
117 repl
= drs_Replicate("ncacn_ip_tcp:%s[%s]" % (server
, binding_options
),
119 local_samdb
, destination_dsa_guid
)
122 for account
in accounts
:
123 # work out the source and destination GUIDs
124 dc_ntds_dn
= samdb
.get_dsServiceName()
125 res
= samdb
.search(base
=dc_ntds_dn
, scope
=ldb
.SCOPE_BASE
, attrs
=["invocationId"])
126 source_dsa_invocation_id
= misc
.GUID(local_samdb
.schema_format_value("objectGUID", res
[0]["invocationId"][0]))
129 dn
= self
.get_dn(samdb
, account
)
130 except RODCException
, e
:
131 if not ignore_errors
:
132 raise CommandError(str(e
))
136 self
.outf
.write("Replicating DN %s\n" % dn
)
138 local_samdb
.transaction_start()
140 repl
.replicate(dn
, source_dsa_invocation_id
, destination_dsa_guid
,
141 exop
=drsuapi
.DRSUAPI_EXOP_REPL_SECRET
, rodc
=True)
143 local_samdb
.transaction_cancel()
144 if not ignore_errors
:
145 raise CommandError("Error replicating DN %s" % dn
)
146 errors
.append(ReplicationError("Error replicating DN %s" % dn
))
149 local_samdb
.transaction_commit()
152 print "\nPreload encountered problematic users:"
157 class cmd_rodc(SuperCommand
):
158 """Read-Only Domain Controller (RODC) management."""
161 subcommands
["preload"] = cmd_rodc_preload()