2010-08-26 16:37:24 +10:00
# rodc related commands
#
# Copyright Andrew Tridgell 2010
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from samba . netcmd import Command , CommandError , Option , SuperCommand
import samba . getopt as options
from samba . samdb import SamDB
from samba . auth import system_session
import ldb
from samba . dcerpc import misc , drsuapi
from samba . drs_utils import drs_Replicate
2016-02-24 16:39:38 +13:00
import sys
2011-09-06 13:25:30 -04:00
2016-04-15 09:59:11 +12:00
class RODCException ( Exception ) :
def __init__ ( self , value ) :
self . value = value
def __str__ ( self ) :
return " %s : %s " % ( self . __class__ . __name__ , self . value )
class NamingError ( RODCException ) :
pass
class ReplicationError ( RODCException ) :
pass
2010-08-26 16:37:24 +10:00
class cmd_rodc_preload ( Command ) :
2016-02-24 16:39:38 +13:00
""" Preload accounts for an RODC. Multiple accounts may be requested. """
2010-08-26 16:37:24 +10:00
2016-02-24 16:39:38 +13:00
synopsis = " % prog (<SID>|<DN>|<accountname>)+ ... [options] "
2010-08-26 16:37:24 +10:00
2012-02-06 16:33:38 +01:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-08-26 16:37:24 +10:00
takes_options = [
Option ( " --server " , help = " DC to use " , type = str ) ,
2016-02-24 16:39:38 +13:00
Option ( " --file " , help = " Read account list from a file, or - for stdin (one per line) " , type = str ) ,
2016-04-15 09:59:11 +12:00
Option ( " --ignore-errors " , help = " When preloading multiple accounts, skip any failing accounts " , action = " store_true " ) ,
2018-07-30 18:14:37 +12:00
]
2010-08-26 16:37:24 +10:00
2016-02-24 16:39:38 +13:00
takes_args = [ " account* " ]
2010-08-26 16:37:24 +10:00
def get_dn ( self , samdb , account ) :
''' work out what DN they meant '''
# we accept the account in SID, accountname or DN form
if account [ 0 : 2 ] == ' S- ' :
res = samdb . search ( base = " <SID= %s > " % account ,
expression = " objectclass=user " ,
scope = ldb . SCOPE_BASE , attrs = [ ] )
elif account . find ( ' = ' ) > = 0 :
res = samdb . search ( base = account ,
expression = " objectclass=user " ,
scope = ldb . SCOPE_BASE , attrs = [ ] )
else :
2011-07-28 17:14:28 +10:00
res = samdb . search ( expression = " (&(samAccountName= %s )(objectclass=user)) " % ldb . binary_encode ( account ) ,
2010-08-26 16:37:24 +10:00
scope = ldb . SCOPE_SUBTREE , attrs = [ ] )
if len ( res ) != 1 :
2016-04-15 09:59:11 +12:00
raise NamingError ( " Failed to find account ' %s ' " % account )
2010-08-26 16:37:24 +10:00
return str ( res [ 0 ] [ " dn " ] )
2016-02-24 16:39:38 +13:00
def run ( self , * accounts , * * kwargs ) :
sambaopts = kwargs . get ( " sambaopts " )
credopts = kwargs . get ( " credopts " )
versionpts = kwargs . get ( " versionopts " )
server = kwargs . get ( " server " )
accounts_file = kwargs . get ( " file " )
2016-04-15 09:59:11 +12:00
ignore_errors = kwargs . get ( " ignore_errors " )
2010-08-26 16:37:24 +10:00
if server is None :
raise Exception ( " You must supply a server " )
2016-02-24 16:39:38 +13:00
if accounts_file is not None :
accounts = [ ]
if accounts_file == " - " :
for line in sys . stdin :
accounts . append ( line . strip ( ) )
else :
for line in open ( accounts_file , ' r ' ) :
accounts . append ( line . strip ( ) )
2010-08-26 16:37:24 +10:00
lp = sambaopts . get_loadparm ( )
2010-12-08 08:20:54 +11:00
creds = credopts . get_credentials ( lp , fallback_machine = True )
2010-08-26 16:37:24 +10:00
# connect to the remote and local SAMs
samdb = SamDB ( url = " ldap:// %s " % server ,
session_info = system_session ( ) ,
credentials = creds , lp = lp )
local_samdb = SamDB ( url = None , session_info = system_session ( ) ,
credentials = creds , lp = lp )
destination_dsa_guid = misc . GUID ( local_samdb . get_ntds_GUID ( ) )
2017-09-06 16:40:05 +12:00
binding_options = " seal "
if lp . log_level ( ) > = 9 :
binding_options + = " ,print "
repl = drs_Replicate ( " ncacn_ip_tcp: %s [ %s ] " % ( server , binding_options ) ,
lp , creds ,
2013-11-21 10:48:33 -05:00
local_samdb , destination_dsa_guid )
2016-04-15 09:59:11 +12:00
errors = [ ]
2016-02-24 16:39:38 +13:00
for account in accounts :
# work out the source and destination GUIDs
dc_ntds_dn = samdb . get_dsServiceName ( )
res = samdb . search ( base = dc_ntds_dn , scope = ldb . SCOPE_BASE , attrs = [ " invocationId " ] )
source_dsa_invocation_id = misc . GUID ( local_samdb . schema_format_value ( " objectGUID " , res [ 0 ] [ " invocationId " ] [ 0 ] ) )
2016-04-15 09:59:11 +12:00
try :
dn = self . get_dn ( samdb , account )
2018-02-14 10:07:23 +13:00
except RODCException as e :
2016-04-15 09:59:11 +12:00
if not ignore_errors :
raise CommandError ( str ( e ) )
errors . append ( e )
continue
2016-02-24 16:39:38 +13:00
self . outf . write ( " Replicating DN %s \n " % dn )
local_samdb . transaction_start ( )
try :
repl . replicate ( dn , source_dsa_invocation_id , destination_dsa_guid ,
exop = drsuapi . DRSUAPI_EXOP_REPL_SECRET , rodc = True )
2018-02-14 10:07:23 +13:00
except Exception as e :
2016-02-24 16:39:38 +13:00
local_samdb . transaction_cancel ( )
2016-04-15 09:59:11 +12:00
if not ignore_errors :
raise CommandError ( " Error replicating DN %s " % dn )
errors . append ( ReplicationError ( " Error replicating DN %s " % dn ) )
continue
2016-02-24 16:39:38 +13:00
local_samdb . transaction_commit ( )
2010-08-26 16:37:24 +10:00
2016-04-15 09:59:11 +12:00
if len ( errors ) > 0 :
2018-02-01 15:28:28 +13:00
self . message ( " \n Preload encountered problematic users: " )
2016-04-15 09:59:11 +12:00
for error in errors :
2018-02-01 15:28:28 +13:00
self . message ( " %s " % error )
2010-08-26 16:37:24 +10:00
2010-08-27 12:08:49 +10:00
2010-08-26 16:37:24 +10:00
class cmd_rodc ( SuperCommand ) :
2012-10-09 11:53:21 +02:00
""" Read-Only Domain Controller (RODC) management. """
2010-08-26 16:37:24 +10:00
subcommands = { }
subcommands [ " preload " ] = cmd_rodc_preload ( )