2010-11-28 04:20:15 +03:00
# implement samba_tool gpo commands
#
# Copyright Andrew Tridgell 2010
2012-07-03 08:20:44 +04:00
# Copyright Amitay Isaacs 2011-2012 <amitay@gmail.com>
2010-11-28 04:20:15 +03:00
#
# based on C implementation by Guenther Deschner and Wilco Baan Hofman
#
# 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/>.
#
2018-11-07 01:43:26 +03:00
from __future__ import print_function
2011-07-28 08:17:19 +04:00
import os
2010-11-28 04:20:15 +03:00
import samba . getopt as options
import ldb
2018-05-07 07:03:13 +03:00
import re
2018-05-21 08:30:40 +03:00
import xml . etree . ElementTree as ET
import shutil
2019-01-10 06:34:27 +03:00
import tempfile
2010-11-28 04:20:15 +03:00
from samba . auth import system_session
from samba . netcmd import (
Command ,
CommandError ,
Option ,
SuperCommand ,
2018-07-30 09:14:37 +03:00
)
2010-11-28 04:20:15 +03:00
from samba . samdb import SamDB
2011-12-07 06:10:10 +04:00
from samba import dsdb
from samba . dcerpc import security
2020-11-06 19:29:57 +03:00
from samba . ndr import ndr_unpack , ndr_pack
from samba . dcerpc import preg
2011-01-11 10:40:54 +03:00
import samba . security
import samba . auth
from samba . auth import AUTH_SESSION_INFO_DEFAULT_GROUPS , AUTH_SESSION_INFO_AUTHENTICATED , AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
2011-07-28 08:07:44 +04:00
from samba . netcmd . common import netcmd_finddc
2011-08-01 09:41:19 +04:00
from samba import policy
2019-01-11 04:53:16 +03:00
from samba . samba3 import param as s3param
2019-01-08 05:10:46 +03:00
from samba . samba3 import libsmb_samba_internal as libsmb
2018-05-21 08:30:40 +03:00
from samba import NTSTATUSError
2011-08-02 10:13:43 +04:00
import uuid
2011-12-07 06:10:10 +04:00
from samba . ntacls import dsacl2fsacl
2012-11-29 12:31:12 +04:00
from samba . dcerpc import nbt
from samba . net import Net
2018-05-29 02:57:26 +03:00
from samba . gp_parse import GPParser , GPNoParserException , GPGeneralizeException
2018-05-23 04:51:08 +03:00
from samba . gp_parse . gp_pol import GPPolParser
2018-06-06 03:57:12 +03:00
from samba . gp_parse . gp_ini import (
GPIniParser ,
GPTIniParser ,
GPFDeploy1IniParser ,
GPScriptsIniParser
)
2018-05-23 04:51:08 +03:00
from samba . gp_parse . gp_csv import GPAuditCsvParser
from samba . gp_parse . gp_inf import GptTmplInfParser
from samba . gp_parse . gp_aas import GPAasParser
2018-10-16 12:36:09 +03:00
from samba import param
2020-06-03 15:02:37 +03:00
from samba . credentials import SMB_SIGNING_REQUIRED
2020-11-19 01:24:47 +03:00
from samba . netcmd . common import attr_default
2020-11-13 18:39:26 +03:00
from samba . common import get_bytes , get_string
2020-11-10 02:28:11 +03:00
from configparser import ConfigParser
2020-11-06 20:44:28 +03:00
from io import StringIO
2010-11-28 04:20:15 +03:00
2011-08-01 09:41:19 +04:00
def gpo_flags_string ( value ) :
''' return gpo flags string '''
flags = policy . get_gpo_flags ( value )
if not flags :
ret = ' NONE '
else :
ret = ' ' . join ( flags )
return ret
def gplink_options_string ( value ) :
''' return gplink options string '''
options = policy . get_gplink_options ( value )
if not options :
ret = ' NONE '
else :
ret = ' ' . join ( options )
return ret
2010-11-28 04:20:15 +03:00
2010-11-28 06:33:12 +03:00
def parse_gplink ( gplink ) :
''' parse a gPLink into an array of dn and options '''
ret = [ ]
2019-02-20 03:43:42 +03:00
if gplink . strip ( ) == ' ' :
return ret
2010-11-28 06:33:12 +03:00
a = gplink . split ( ' ] ' )
for g in a :
if not g :
continue
d = g . split ( ' ; ' )
if len ( d ) != 2 or not d [ 0 ] . startswith ( " [LDAP:// " ) :
raise RuntimeError ( " Badly formed gPLink ' %s ' " % g )
2018-07-30 09:17:14 +03:00
ret . append ( { ' dn ' : d [ 0 ] [ 8 : ] , ' options ' : int ( d [ 1 ] ) } )
2010-11-28 06:33:12 +03:00
return ret
2011-07-14 02:21:19 +04:00
def encode_gplink ( gplist ) :
''' Encode an array of dn and options into gPLink string '''
2019-08-26 00:05:31 +03:00
ret = " " . join ( " [LDAP:// %s ; %d ] " % ( g [ ' dn ' ] , g [ ' options ' ] ) for g in gplist )
2011-07-14 02:21:19 +04:00
return ret
2011-07-28 08:07:44 +04:00
def dc_url ( lp , creds , url = None , dc = None ) :
''' If URL is not specified, return URL for writable DC.
If dc is provided , use that to construct ldap URL '''
if url is None :
if dc is None :
try :
dc = netcmd_finddc ( lp , creds )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-12-14 05:18:57 +04:00
raise RuntimeError ( " Could not find a DC for domain " , e )
2011-07-28 08:07:44 +04:00
url = ' ldap:// ' + dc
return url
def get_gpo_dn ( samdb , gpo ) :
''' Construct the DN for gpo '''
dn = samdb . get_default_basedn ( )
2012-07-03 05:21:25 +04:00
dn . add_child ( ldb . Dn ( samdb , " CN=Policies,CN=System " ) )
2011-07-28 08:07:44 +04:00
dn . add_child ( ldb . Dn ( samdb , " CN= %s " % gpo ) )
return dn
2012-11-20 17:58:13 +04:00
def get_gpo_info ( samdb , gpo = None , displayname = None , dn = None ,
2018-08-22 08:06:23 +03:00
sd_flags = ( security . SECINFO_OWNER |
security . SECINFO_GROUP |
security . SECINFO_DACL |
security . SECINFO_SACL ) ) :
2011-07-28 08:07:44 +04:00
''' Get GPO information using gpo, displayname or dn '''
policies_dn = samdb . get_default_basedn ( )
policies_dn . add_child ( ldb . Dn ( samdb , " CN=Policies,CN=System " ) )
base_dn = policies_dn
search_expr = " (objectClass=groupPolicyContainer) "
search_scope = ldb . SCOPE_ONELEVEL
if gpo is not None :
2011-07-28 11:14:28 +04:00
search_expr = " (&(objectClass=groupPolicyContainer)(name= %s )) " % ldb . binary_encode ( gpo )
2011-07-28 08:07:44 +04:00
if displayname is not None :
2011-07-28 11:14:28 +04:00
search_expr = " (&(objectClass=groupPolicyContainer)(displayname= %s )) " % ldb . binary_encode ( displayname )
2011-07-28 08:07:44 +04:00
if dn is not None :
base_dn = dn
search_scope = ldb . SCOPE_BASE
try :
msg = samdb . search ( base = base_dn , scope = search_scope ,
2018-07-30 09:15:34 +03:00
expression = search_expr ,
attrs = [ ' nTSecurityDescriptor ' ,
' versionNumber ' ,
' flags ' ,
' name ' ,
' displayName ' ,
2019-02-20 06:51:04 +03:00
' gPCFileSysPath ' ,
' gPCMachineExtensionNames ' ,
' gPCUserExtensionNames ' ] ,
2018-07-30 09:15:34 +03:00
controls = [ ' sd_flags:1: %d ' % sd_flags ] )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-07-28 08:07:44 +04:00
if gpo is not None :
mesg = " Cannot get information for GPO %s " % gpo
else :
mesg = " Cannot get information for GPOs "
raise CommandError ( mesg , e )
return msg
2012-07-03 08:13:01 +04:00
def get_gpo_containers ( samdb , gpo ) :
''' lists dn of containers for a GPO '''
search_expr = " (&(objectClass=*)(gPLink=* %s *)) " % gpo
try :
msg = samdb . search ( expression = search_expr , attrs = [ ' gPLink ' ] )
2018-02-14 00:07:23 +03:00
except Exception as e :
2012-07-03 08:13:01 +04:00
raise CommandError ( " Could not find container(s) with GPO %s " % gpo , e )
return msg
def del_gpo_link ( samdb , container_dn , gpo ) :
''' delete GPO link for the container '''
# Check if valid Container DN and get existing GPlinks
try :
msg = samdb . search ( base = container_dn , scope = ldb . SCOPE_BASE ,
2018-07-30 09:15:34 +03:00
expression = " (objectClass=*) " ,
attrs = [ ' gPLink ' ] ) [ 0 ]
2018-02-14 00:07:23 +03:00
except Exception as e :
2012-07-03 08:55:10 +04:00
raise CommandError ( " Container ' %s ' does not exist " % container_dn , e )
2012-07-03 08:13:01 +04:00
found = False
gpo_dn = str ( get_gpo_dn ( samdb , gpo ) )
if ' gPLink ' in msg :
2018-09-05 16:54:24 +03:00
gplist = parse_gplink ( str ( msg [ ' gPLink ' ] [ 0 ] ) )
2012-07-03 08:13:01 +04:00
for g in gplist :
if g [ ' dn ' ] . lower ( ) == gpo_dn . lower ( ) :
gplist . remove ( g )
found = True
break
else :
raise CommandError ( " No GPO(s) linked to this container " )
if not found :
raise CommandError ( " GPO ' %s ' not linked to this container " % gpo )
m = ldb . Message ( )
m . dn = container_dn
if gplist :
gplink_str = encode_gplink ( gplist )
m [ ' r0 ' ] = ldb . MessageElement ( gplink_str , ldb . FLAG_MOD_REPLACE , ' gPLink ' )
else :
m [ ' d0 ' ] = ldb . MessageElement ( msg [ ' gPLink ' ] [ 0 ] , ldb . FLAG_MOD_DELETE , ' gPLink ' )
try :
samdb . modify ( m )
2018-02-14 00:07:23 +03:00
except Exception as e :
2012-07-03 08:13:01 +04:00
raise CommandError ( " Error removing GPO from container " , e )
2011-07-28 08:17:19 +04:00
def parse_unc ( unc ) :
''' Parse UNC string into a hostname, a service, and a filepath '''
2020-01-16 04:12:02 +03:00
tmp = [ ]
if unc . startswith ( ' \\ \\ ' ) :
tmp = unc [ 2 : ] . split ( ' \\ ' , 2 )
elif unc . startswith ( ' // ' ) :
tmp = unc [ 2 : ] . split ( ' / ' , 2 )
if len ( tmp ) != 3 :
raise ValueError ( " Invalid UNC string: %s " % unc )
return tmp
2011-07-28 08:17:19 +04:00
2018-05-07 07:03:13 +03:00
def find_parser ( name , flags = re . IGNORECASE ) :
2020-02-07 01:25:27 +03:00
if re . match ( r ' fdeploy1 \ .ini$ ' , name , flags = flags ) :
2018-05-24 06:17:35 +03:00
return GPFDeploy1IniParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' audit \ .csv$ ' , name , flags = flags ) :
2018-05-23 04:51:08 +03:00
return GPAuditCsvParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' GptTmpl \ .inf$ ' , name , flags = flags ) :
2018-05-23 04:51:08 +03:00
return GptTmplInfParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' GPT \ .INI$ ' , name , flags = flags ) :
2018-05-23 04:51:08 +03:00
return GPTIniParser ( )
2020-02-07 01:27:32 +03:00
if re . match ( r ' scripts \ .ini$ ' , name , flags = flags ) :
2018-06-06 03:57:12 +03:00
return GPScriptsIniParser ( )
2020-02-07 01:27:32 +03:00
if re . match ( r ' psscripts \ .ini$ ' , name , flags = flags ) :
2018-06-06 03:57:12 +03:00
return GPScriptsIniParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' GPE \ .INI$ ' , name , flags = flags ) :
2019-03-08 01:22:55 +03:00
# This file does not appear in the protocol specifications!
#
# It appears to be a legacy file used to maintain gPCUserExtensionNames
# and gPCMachineExtensionNames. We should just copy the file as binary.
return GPParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' .* \ .ini$ ' , name , flags = flags ) :
2018-05-23 04:51:08 +03:00
return GPIniParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' .* \ .pol$ ' , name , flags = flags ) :
2018-05-23 04:51:08 +03:00
return GPPolParser ( )
2020-02-07 01:25:27 +03:00
if re . match ( r ' .* \ .aas$ ' , name , flags = flags ) :
2018-05-23 04:51:08 +03:00
return GPAasParser ( )
2018-05-07 07:03:13 +03:00
return GPParser ( )
def backup_directory_remote_to_local ( conn , remotedir , localdir ) :
SUFFIX = ' .SAMBABACKUP '
if not os . path . isdir ( localdir ) :
os . mkdir ( localdir )
r_dirs = [ remotedir ]
l_dirs = [ localdir ]
while r_dirs :
r_dir = r_dirs . pop ( )
l_dir = l_dirs . pop ( )
dirlist = conn . list ( r_dir , attribs = attr_flags )
2018-09-05 16:54:24 +03:00
dirlist . sort ( key = lambda x : x [ ' name ' ] )
2018-05-07 07:03:13 +03:00
for e in dirlist :
r_name = r_dir + ' \\ ' + e [ ' name ' ]
l_name = os . path . join ( l_dir , e [ ' name ' ] )
2019-01-08 05:10:46 +03:00
if e [ ' attrib ' ] & libsmb . FILE_ATTRIBUTE_DIRECTORY :
2018-05-07 07:03:13 +03:00
r_dirs . append ( r_name )
l_dirs . append ( l_name )
os . mkdir ( l_name )
else :
data = conn . loadfile ( r_name )
2018-09-05 16:54:24 +03:00
with open ( l_name + SUFFIX , ' wb ' ) as f :
2018-05-07 07:03:13 +03:00
f . write ( data )
parser = find_parser ( e [ ' name ' ] )
parser . parse ( data )
parser . write_xml ( l_name + ' .xml ' )
2019-01-08 05:10:46 +03:00
attr_flags = libsmb . FILE_ATTRIBUTE_SYSTEM | \
libsmb . FILE_ATTRIBUTE_DIRECTORY | \
libsmb . FILE_ATTRIBUTE_ARCHIVE | \
libsmb . FILE_ATTRIBUTE_HIDDEN
2011-07-28 08:17:19 +04:00
2018-07-30 09:20:39 +03:00
2011-08-01 09:47:10 +04:00
def copy_directory_remote_to_local ( conn , remotedir , localdir ) :
2011-07-28 08:17:19 +04:00
if not os . path . isdir ( localdir ) :
os . mkdir ( localdir )
2018-07-30 09:17:02 +03:00
r_dirs = [ remotedir ]
l_dirs = [ localdir ]
2011-07-28 08:17:19 +04:00
while r_dirs :
r_dir = r_dirs . pop ( )
l_dir = l_dirs . pop ( )
2018-05-09 06:24:38 +03:00
dirlist = conn . list ( r_dir , attribs = attr_flags )
2018-09-05 16:54:24 +03:00
dirlist . sort ( key = lambda x : x [ ' name ' ] )
2011-07-28 08:17:19 +04:00
for e in dirlist :
r_name = r_dir + ' \\ ' + e [ ' name ' ]
2011-08-01 09:47:10 +04:00
l_name = os . path . join ( l_dir , e [ ' name ' ] )
2011-07-28 08:17:19 +04:00
2019-01-08 05:10:46 +03:00
if e [ ' attrib ' ] & libsmb . FILE_ATTRIBUTE_DIRECTORY :
2011-07-28 08:17:19 +04:00
r_dirs . append ( r_name )
l_dirs . append ( l_name )
os . mkdir ( l_name )
2011-08-01 09:47:10 +04:00
else :
2011-07-28 08:17:19 +04:00
data = conn . loadfile ( r_name )
2018-09-05 16:54:24 +03:00
open ( l_name , ' wb ' ) . write ( data )
2011-07-28 08:17:19 +04:00
2018-05-21 08:30:40 +03:00
def copy_directory_local_to_remote ( conn , localdir , remotedir ,
2019-02-26 07:01:19 +03:00
ignore_existing_dir = False ,
keep_existing_files = False ) :
2011-08-02 10:13:01 +04:00
if not conn . chkpath ( remotedir ) :
conn . mkdir ( remotedir )
2018-07-30 09:17:02 +03:00
l_dirs = [ localdir ]
r_dirs = [ remotedir ]
2011-08-02 10:13:01 +04:00
while l_dirs :
l_dir = l_dirs . pop ( )
r_dir = r_dirs . pop ( )
dirlist = os . listdir ( l_dir )
2018-05-07 07:03:13 +03:00
dirlist . sort ( )
2011-08-02 10:13:01 +04:00
for e in dirlist :
l_name = os . path . join ( l_dir , e )
r_name = r_dir + ' \\ ' + e
if os . path . isdir ( l_name ) :
l_dirs . append ( l_name )
r_dirs . append ( r_name )
2018-05-21 08:30:40 +03:00
try :
conn . mkdir ( r_name )
except NTSTATUSError :
2019-02-26 07:01:19 +03:00
if not ignore_existing_dir :
2018-05-21 08:30:40 +03:00
raise
2011-08-02 10:13:01 +04:00
else :
2019-02-26 07:01:19 +03:00
if keep_existing_files :
try :
conn . loadfile ( r_name )
continue
except NTSTATUSError :
pass
2018-09-05 16:54:24 +03:00
data = open ( l_name , ' rb ' ) . read ( )
2011-08-02 10:13:01 +04:00
conn . savefile ( r_name , data )
def create_directory_hier ( conn , remotedir ) :
elems = remotedir . replace ( ' / ' , ' \\ ' ) . split ( ' \\ ' )
path = " "
for e in elems :
path = path + ' \\ ' + e
if not conn . chkpath ( path ) :
conn . mkdir ( path )
2020-08-13 11:40:23 +03:00
def smb_connection ( dc_hostname , service , lp , creds ) :
2018-12-14 00:47:45 +03:00
# SMB connect to DC
2020-06-03 15:02:37 +03:00
# Force signing for the smb connection
saved_signing_state = creds . get_smb_signing ( )
creds . set_smb_signing ( SMB_SIGNING_REQUIRED )
2018-12-14 00:47:45 +03:00
try :
2019-01-11 04:53:16 +03:00
# the SMB bindings rely on having a s3 loadparm
s3_lp = s3param . get_context ( )
s3_lp . load ( lp . configfile )
2020-05-28 19:11:31 +03:00
conn = libsmb . Conn ( dc_hostname , service , lp = s3_lp , creds = creds )
2018-12-14 00:47:45 +03:00
except Exception :
raise CommandError ( " Error connecting to ' %s ' using SMB " % dc_hostname )
2020-06-03 15:02:37 +03:00
# Reset signing state
creds . set_smb_signing ( saved_signing_state )
2018-12-14 00:47:45 +03:00
return conn
2011-08-02 10:13:01 +04:00
2018-11-07 01:57:13 +03:00
class GPOCommand ( Command ) :
def construct_tmpdir ( self , tmpdir , gpo ) :
""" Ensure that the temporary directory structure used in fetch,
backup , create , and restore is consistent .
If - - tmpdir is used the named directory must be present , which may
contain a ' policy ' subdirectory , but ' policy ' must not itself have
a subdirectory with the gpo name . The policy and gpo directories
will be created .
If - - tmpdir is not used , a temporary directory is securely created .
"""
if tmpdir is None :
tmpdir = tempfile . mkdtemp ( )
print ( " Using temporary directory %s (use --tmpdir to change) " % tmpdir ,
file = self . outf )
if not os . path . isdir ( tmpdir ) :
raise CommandError ( " Temporary directory ' %s ' does not exist " % tmpdir )
localdir = os . path . join ( tmpdir , " policy " )
if not os . path . isdir ( localdir ) :
os . mkdir ( localdir )
gpodir = os . path . join ( localdir , gpo )
if os . path . isdir ( gpodir ) :
raise CommandError (
" GPO directory ' %s ' already exists, refusing to overwrite " % gpodir )
try :
os . mkdir ( gpodir )
except ( IOError , OSError ) as e :
raise CommandError ( " Error creating teporary GPO directory " , e )
return tmpdir , gpodir
2018-11-07 02:15:12 +03:00
def samdb_connect ( self ) :
''' make a ldap connection to the server '''
try :
self . samdb = SamDB ( url = self . url ,
session_info = system_session ( ) ,
credentials = self . creds , lp = self . lp )
except Exception as e :
raise CommandError ( " LDAP connection to %s failed " % self . url , e )
2018-11-07 01:57:13 +03:00
class cmd_listall ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" List all GPOs. """
2010-11-28 04:20:15 +03:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog [options] "
2010-11-28 04:20:15 +03:00
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-11-28 04:20:15 +03:00
takes_options = [
2011-07-25 19:56:10 +04:00
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " )
2018-07-30 09:14:37 +03:00
]
2010-11-28 04:20:15 +03:00
2011-07-14 02:21:19 +04:00
def run ( self , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2010-11-28 04:20:15 +03:00
self . lp = sambaopts . get_loadparm ( )
2010-12-08 00:20:54 +03:00
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2010-11-28 04:20:15 +03:00
2011-07-28 08:07:44 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2010-11-28 04:20:15 +03:00
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2010-11-28 04:20:15 +03:00
2011-07-28 08:07:44 +04:00
msg = get_gpo_info ( self . samdb , None )
2010-11-28 04:20:15 +03:00
for m in msg :
2011-10-13 02:36:44 +04:00
self . outf . write ( " GPO : %s \n " % m [ ' name ' ] [ 0 ] )
self . outf . write ( " display name : %s \n " % m [ ' displayName ' ] [ 0 ] )
self . outf . write ( " path : %s \n " % m [ ' gPCFileSysPath ' ] [ 0 ] )
self . outf . write ( " dn : %s \n " % m . dn )
self . outf . write ( " version : %s \n " % attr_default ( m , ' versionNumber ' , ' 0 ' ) )
self . outf . write ( " flags : %s \n " % gpo_flags_string ( int ( attr_default ( m , ' flags ' , 0 ) ) ) )
self . outf . write ( " \n " )
2010-11-28 04:20:15 +03:00
2018-11-07 01:57:13 +03:00
class cmd_list ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" List GPOs for an account. """
2010-11-28 06:33:12 +03:00
2019-06-28 02:21:57 +03:00
synopsis = " % prog <username|machinename> [options] "
2010-11-28 06:33:12 +03:00
2019-06-28 02:21:57 +03:00
takes_args = [ ' accountname ' ]
2012-02-06 19:33:38 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2010-11-28 06:33:12 +03:00
takes_options = [
2011-10-14 01:27:22 +04:00
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " ,
2018-07-30 09:16:12 +03:00
type = str , metavar = " URL " , dest = " H " )
2018-07-30 09:14:37 +03:00
]
2010-11-28 06:33:12 +03:00
2019-06-28 02:21:57 +03:00
def run ( self , accountname , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2010-11-28 06:33:12 +03:00
self . lp = sambaopts . get_loadparm ( )
2010-12-08 00:20:54 +03:00
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2010-11-28 06:33:12 +03:00
2011-07-28 08:07:44 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2010-11-28 06:33:12 +03:00
try :
2011-07-14 02:21:19 +04:00
msg = self . samdb . search ( expression = ' (&(|(samAccountName= %s )(samAccountName= %s $))(objectClass=User)) ' %
2019-06-28 02:21:57 +03:00
( ldb . binary_encode ( accountname ) , ldb . binary_encode ( accountname ) ) )
2011-07-14 02:21:19 +04:00
user_dn = msg [ 0 ] . dn
2012-07-03 08:55:10 +04:00
except Exception :
2019-06-28 02:21:57 +03:00
raise CommandError ( " Failed to find account %s " % accountname )
2010-11-28 06:33:12 +03:00
# check if its a computer account
try :
msg = self . samdb . search ( base = user_dn , scope = ldb . SCOPE_BASE , attrs = [ ' objectClass ' ] ) [ 0 ]
is_computer = ' computer ' in msg [ ' objectClass ' ]
2012-07-03 08:55:10 +04:00
except Exception :
2019-06-28 02:21:57 +03:00
raise CommandError ( " Failed to find objectClass for %s " % accountname )
2010-11-28 06:33:12 +03:00
2018-07-30 09:16:43 +03:00
session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS |
2018-09-03 16:05:48 +03:00
AUTH_SESSION_INFO_AUTHENTICATED )
2011-01-11 10:40:54 +03:00
# When connecting to a remote server, don't look up the local privilege DB
if self . url is not None and self . url . startswith ( ' ldap ' ) :
session_info_flags | = AUTH_SESSION_INFO_SIMPLE_PRIVILEGES
session = samba . auth . user_session ( self . samdb , lp_ctx = self . lp , dn = user_dn ,
session_info_flags = session_info_flags )
token = session . security_token
2010-11-28 06:33:12 +03:00
gpos = [ ]
inherit = True
dn = ldb . Dn ( self . samdb , str ( user_dn ) ) . parent ( )
while True :
2011-01-13 07:09:03 +03:00
msg = self . samdb . search ( base = dn , scope = ldb . SCOPE_BASE , attrs = [ ' gPLink ' , ' gPOptions ' ] ) [ 0 ]
2010-11-28 06:33:12 +03:00
if ' gPLink ' in msg :
2018-09-05 16:54:24 +03:00
glist = parse_gplink ( str ( msg [ ' gPLink ' ] [ 0 ] ) )
2010-11-28 06:33:12 +03:00
for g in glist :
if not inherit and not ( g [ ' options ' ] & dsdb . GPLINK_OPT_ENFORCE ) :
continue
if g [ ' options ' ] & dsdb . GPLINK_OPT_DISABLE :
continue
2011-01-13 07:09:03 +03:00
try :
2018-08-22 08:06:23 +03:00
sd_flags = ( security . SECINFO_OWNER |
security . SECINFO_GROUP |
security . SECINFO_DACL )
2011-01-13 07:09:03 +03:00
gmsg = self . samdb . search ( base = g [ ' dn ' ] , scope = ldb . SCOPE_BASE ,
2011-07-28 08:07:44 +04:00
attrs = [ ' name ' , ' displayName ' , ' flags ' ,
2012-11-20 17:56:56 +04:00
' nTSecurityDescriptor ' ] ,
controls = [ ' sd_flags:1: %d ' % sd_flags ] )
secdesc_ndr = gmsg [ 0 ] [ ' nTSecurityDescriptor ' ] [ 0 ]
secdesc = ndr_unpack ( security . descriptor , secdesc_ndr )
2011-01-13 07:09:03 +03:00
except Exception :
2012-11-20 17:56:56 +04:00
self . outf . write ( " Failed to fetch gpo object with nTSecurityDescriptor %s \n " %
2018-07-30 09:16:12 +03:00
g [ ' dn ' ] )
2011-01-13 07:09:03 +03:00
continue
2011-01-11 10:40:54 +03:00
try :
2011-01-13 07:09:03 +03:00
samba . security . access_check ( secdesc , token ,
2011-12-07 06:10:10 +04:00
security . SEC_STD_READ_CONTROL |
security . SEC_ADS_LIST |
security . SEC_ADS_READ_PROP )
2011-01-11 10:40:54 +03:00
except RuntimeError :
2011-10-13 02:36:44 +04:00
self . outf . write ( " Failed access check on %s \n " % msg . dn )
2011-01-11 10:40:54 +03:00
continue
2010-11-28 06:33:12 +03:00
# check the flags on the GPO
2011-01-13 07:09:03 +03:00
flags = int ( attr_default ( gmsg [ 0 ] , ' flags ' , 0 ) )
2010-11-28 06:33:12 +03:00
if is_computer and ( flags & dsdb . GPO_FLAG_MACHINE_DISABLE ) :
continue
if not is_computer and ( flags & dsdb . GPO_FLAG_USER_DISABLE ) :
continue
2011-07-28 08:07:44 +04:00
gpos . append ( ( gmsg [ 0 ] [ ' displayName ' ] [ 0 ] , gmsg [ 0 ] [ ' name ' ] [ 0 ] ) )
2010-11-28 06:33:12 +03:00
# check if this blocks inheritance
gpoptions = int ( attr_default ( msg , ' gPOptions ' , 0 ) )
if gpoptions & dsdb . GPO_BLOCK_INHERITANCE :
inherit = False
if dn == self . samdb . get_default_basedn ( ) :
break
dn = dn . parent ( )
2011-07-14 02:21:19 +04:00
if is_computer :
msg_str = ' computer '
else :
msg_str = ' user '
2019-06-28 02:21:57 +03:00
self . outf . write ( " GPOs for %s %s \n " % ( msg_str , accountname ) )
2010-11-28 06:33:12 +03:00
for g in gpos :
2011-10-13 02:36:44 +04:00
self . outf . write ( " %s %s \n " % ( g [ 0 ] , g [ 1 ] ) )
2011-07-14 02:21:19 +04:00
2018-11-07 01:57:13 +03:00
class cmd_show ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Show information for a GPO. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <gpo> [options] "
2011-07-14 02:21:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-14 01:27:22 +04:00
takes_args = [ ' gpo ' ]
2011-07-14 02:21:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str )
2018-07-30 09:14:37 +03:00
]
2011-07-14 02:21:19 +04:00
2011-07-28 08:07:44 +04:00
def run ( self , gpo , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2011-07-14 02:21:19 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2011-07-28 08:07:44 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-14 02:21:19 +04:00
try :
2011-07-28 08:07:44 +04:00
msg = get_gpo_info ( self . samdb , gpo ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " GPO ' %s ' does not exist " % gpo )
2011-07-14 02:21:19 +04:00
2012-11-17 10:13:40 +04:00
try :
secdesc_ndr = msg [ ' nTSecurityDescriptor ' ] [ 0 ]
secdesc = ndr_unpack ( security . descriptor , secdesc_ndr )
secdesc_sddl = secdesc . as_sddl ( )
except Exception :
secdesc_sddl = " <hidden> "
2011-07-14 02:21:19 +04:00
2011-10-13 02:36:44 +04:00
self . outf . write ( " GPO : %s \n " % msg [ ' name ' ] [ 0 ] )
self . outf . write ( " display name : %s \n " % msg [ ' displayName ' ] [ 0 ] )
self . outf . write ( " path : %s \n " % msg [ ' gPCFileSysPath ' ] [ 0 ] )
self . outf . write ( " dn : %s \n " % msg . dn )
self . outf . write ( " version : %s \n " % attr_default ( msg , ' versionNumber ' , ' 0 ' ) )
self . outf . write ( " flags : %s \n " % gpo_flags_string ( int ( attr_default ( msg , ' flags ' , 0 ) ) ) )
2012-11-17 10:13:40 +04:00
self . outf . write ( " ACL : %s \n " % secdesc_sddl )
2011-10-13 02:36:44 +04:00
self . outf . write ( " \n " )
2011-07-14 02:21:19 +04:00
2018-11-07 01:57:13 +03:00
class cmd_getlink ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" List GPO Links for a container. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <container_dn> [options] "
2011-07-14 02:21:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-14 01:27:22 +04:00
takes_args = [ ' container_dn ' ]
2011-07-14 02:21:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str )
2018-07-30 09:14:37 +03:00
]
2011-07-14 02:21:19 +04:00
def run ( self , container_dn , H = None , sambaopts = None , credopts = None ,
2018-07-30 09:15:34 +03:00
versionopts = None ) :
2011-07-14 02:21:19 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2011-07-28 08:07:44 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-14 02:21:19 +04:00
try :
msg = self . samdb . search ( base = container_dn , scope = ldb . SCOPE_BASE ,
expression = " (objectClass=*) " ,
2012-07-03 05:22:55 +04:00
attrs = [ ' gPLink ' ] ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " Container ' %s ' does not exist " % container_dn )
2011-07-14 02:21:19 +04:00
2019-02-20 03:43:42 +03:00
if ' gPLink ' in msg and msg [ ' gPLink ' ] :
2011-10-13 02:36:44 +04:00
self . outf . write ( " GPO(s) linked to DN %s \n " % container_dn )
2018-09-05 16:54:24 +03:00
gplist = parse_gplink ( str ( msg [ ' gPLink ' ] [ 0 ] ) )
2011-07-14 02:21:19 +04:00
for g in gplist :
2011-07-28 08:07:44 +04:00
msg = get_gpo_info ( self . samdb , dn = g [ ' dn ' ] )
2011-10-13 02:36:44 +04:00
self . outf . write ( " GPO : %s \n " % msg [ 0 ] [ ' name ' ] [ 0 ] )
self . outf . write ( " Name : %s \n " % msg [ 0 ] [ ' displayName ' ] [ 0 ] )
self . outf . write ( " Options : %s \n " % gplink_options_string ( g [ ' options ' ] ) )
self . outf . write ( " \n " )
2011-07-14 02:21:19 +04:00
else :
2011-10-13 02:36:44 +04:00
self . outf . write ( " No GPO(s) linked to DN= %s \n " % container_dn )
2011-07-14 02:21:19 +04:00
2018-11-07 01:57:13 +03:00
class cmd_setlink ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Add or update a GPO link to a container. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <container_dn> <gpo> [options] "
2011-07-14 02:21:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-14 01:27:22 +04:00
takes_args = [ ' container_dn ' , ' gpo ' ]
2011-07-14 02:21:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
Option ( " --disable " , dest = " disabled " , default = False , action = ' store_true ' ,
2018-07-30 09:16:12 +03:00
help = " Disable policy " ) ,
2011-07-14 02:21:19 +04:00
Option ( " --enforce " , dest = " enforced " , default = False , action = ' store_true ' ,
2018-07-30 09:16:12 +03:00
help = " Enforce policy " )
2018-07-30 09:14:37 +03:00
]
2011-07-14 02:21:19 +04:00
2011-07-28 08:07:44 +04:00
def run ( self , container_dn , gpo , H = None , disabled = False , enforced = False ,
2018-07-30 09:15:34 +03:00
sambaopts = None , credopts = None , versionopts = None ) :
2011-07-14 02:21:19 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2011-07-28 08:07:44 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-14 02:21:19 +04:00
gplink_options = 0
if disabled :
gplink_options | = dsdb . GPLINK_OPT_DISABLE
if enforced :
gplink_options | = dsdb . GPLINK_OPT_ENFORCE
# Check if valid GPO DN
try :
2011-07-28 08:07:44 +04:00
msg = get_gpo_info ( self . samdb , gpo = gpo ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " GPO ' %s ' does not exist " % gpo )
2012-07-03 05:26:41 +04:00
gpo_dn = str ( get_gpo_dn ( self . samdb , gpo ) )
2011-07-14 02:21:19 +04:00
# Check if valid Container DN
try :
msg = self . samdb . search ( base = container_dn , scope = ldb . SCOPE_BASE ,
expression = " (objectClass=*) " ,
2012-07-03 05:22:55 +04:00
attrs = [ ' gPLink ' ] ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " Container ' %s ' does not exist " % container_dn )
2011-07-14 02:21:19 +04:00
# Update existing GPlinks or Add new one
existing_gplink = False
if ' gPLink ' in msg :
2018-09-05 16:54:24 +03:00
gplist = parse_gplink ( str ( msg [ ' gPLink ' ] [ 0 ] ) )
2011-07-14 02:21:19 +04:00
existing_gplink = True
found = False
for g in gplist :
if g [ ' dn ' ] . lower ( ) == gpo_dn . lower ( ) :
g [ ' options ' ] = gplink_options
found = True
break
2012-07-03 08:55:10 +04:00
if found :
raise CommandError ( " GPO ' %s ' already linked to this container " % gpo )
else :
2018-07-30 09:17:14 +03:00
gplist . insert ( 0 , { ' dn ' : gpo_dn , ' options ' : gplink_options } )
2011-07-14 02:21:19 +04:00
else :
gplist = [ ]
2018-07-30 09:17:14 +03:00
gplist . append ( { ' dn ' : gpo_dn , ' options ' : gplink_options } )
2011-07-14 02:21:19 +04:00
gplink_str = encode_gplink ( gplist )
m = ldb . Message ( )
m . dn = ldb . Dn ( self . samdb , container_dn )
if existing_gplink :
m [ ' new_value ' ] = ldb . MessageElement ( gplink_str , ldb . FLAG_MOD_REPLACE , ' gPLink ' )
else :
m [ ' new_value ' ] = ldb . MessageElement ( gplink_str , ldb . FLAG_MOD_ADD , ' gPLink ' )
try :
self . samdb . modify ( m )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-07-28 08:07:44 +04:00
raise CommandError ( " Error adding GPO Link " , e )
2011-07-14 02:21:19 +04:00
2011-10-13 02:36:44 +04:00
self . outf . write ( " Added/Updated GPO link \n " )
2011-07-14 02:21:19 +04:00
cmd_getlink ( ) . run ( container_dn , H , sambaopts , credopts , versionopts )
2018-11-07 01:57:13 +03:00
class cmd_dellink ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Delete GPO link from a container. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <container_dn> <gpo> [options] "
2011-07-14 02:21:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2012-07-03 08:16:41 +04:00
takes_args = [ ' container ' , ' gpo ' ]
2011-07-14 02:21:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
2018-07-30 09:14:37 +03:00
]
2011-07-14 02:21:19 +04:00
2012-07-03 08:16:41 +04:00
def run ( self , container , gpo , H = None , sambaopts = None , credopts = None ,
2018-07-30 09:15:34 +03:00
versionopts = None ) :
2011-07-14 02:21:19 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2011-07-28 08:07:44 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-14 02:21:19 +04:00
2011-07-28 08:07:44 +04:00
# Check if valid GPO
2011-07-14 02:21:19 +04:00
try :
2012-07-03 08:16:41 +04:00
get_gpo_info ( self . samdb , gpo = gpo ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " GPO ' %s ' does not exist " % gpo )
2011-07-14 02:21:19 +04:00
2012-07-03 08:16:41 +04:00
container_dn = ldb . Dn ( self . samdb , container )
del_gpo_link ( self . samdb , container_dn , gpo )
2011-10-13 02:36:44 +04:00
self . outf . write ( " Deleted GPO link. \n " )
2011-07-14 02:21:19 +04:00
cmd_getlink ( ) . run ( container_dn , H , sambaopts , credopts , versionopts )
2018-11-07 01:57:13 +03:00
class cmd_listcontainers ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" List all linked containers for a GPO. """
2012-07-03 08:22:42 +04:00
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_args = [ ' gpo ' ]
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str )
2018-07-30 09:14:37 +03:00
]
2012-07-03 08:22:42 +04:00
def run ( self , gpo , H = None , sambaopts = None , credopts = None ,
2018-07-30 09:15:34 +03:00
versionopts = None ) :
2012-07-03 08:22:42 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2012-07-03 08:22:42 +04:00
msg = get_gpo_containers ( self . samdb , gpo )
if len ( msg ) :
self . outf . write ( " Container(s) using GPO %s \n " % gpo )
for m in msg :
self . outf . write ( " DN: %s \n " % m [ ' dn ' ] )
else :
self . outf . write ( " No Containers using GPO %s \n " % gpo )
2018-11-07 01:57:13 +03:00
class cmd_getinheritance ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Get inheritance flag for a container. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <container_dn> [options] "
2011-07-14 02:21:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-14 01:27:22 +04:00
takes_args = [ ' container_dn ' ]
2011-07-14 02:21:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str )
2018-07-30 09:14:37 +03:00
]
2011-07-14 02:21:19 +04:00
def run ( self , container_dn , H = None , sambaopts = None , credopts = None ,
2018-07-30 09:15:34 +03:00
versionopts = None ) :
2011-07-14 02:21:19 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2012-07-03 08:17:48 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-14 02:21:19 +04:00
try :
msg = self . samdb . search ( base = container_dn , scope = ldb . SCOPE_BASE ,
expression = " (objectClass=*) " ,
attrs = [ ' gPOptions ' ] ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " Container ' %s ' does not exist " % container_dn )
2011-07-14 02:21:19 +04:00
inheritance = 0
if ' gPOptions ' in msg :
2011-10-08 16:13:04 +04:00
inheritance = int ( msg [ ' gPOptions ' ] [ 0 ] )
2011-07-14 02:21:19 +04:00
if inheritance == dsdb . GPO_BLOCK_INHERITANCE :
2011-10-13 02:36:44 +04:00
self . outf . write ( " Container has GPO_BLOCK_INHERITANCE \n " )
2011-07-14 02:21:19 +04:00
else :
2011-10-13 02:36:44 +04:00
self . outf . write ( " Container has GPO_INHERIT \n " )
2011-07-14 02:21:19 +04:00
2018-11-07 01:57:13 +03:00
class cmd_setinheritance ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Set inheritance flag on a container. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <container_dn> <block|inherit> [options] "
2011-07-14 02:21:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2018-07-30 09:17:02 +03:00
takes_args = [ ' container_dn ' , ' inherit_state ' ]
2011-07-14 02:21:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str )
2018-07-30 09:14:37 +03:00
]
2011-07-14 02:21:19 +04:00
def run ( self , container_dn , inherit_state , H = None , sambaopts = None , credopts = None ,
2018-07-30 09:15:34 +03:00
versionopts = None ) :
2011-07-14 02:21:19 +04:00
if inherit_state . lower ( ) == ' block ' :
inheritance = dsdb . GPO_BLOCK_INHERITANCE
elif inherit_state . lower ( ) == ' inherit ' :
inheritance = dsdb . GPO_INHERIT
else :
raise CommandError ( " Unknown inheritance state ( %s ) " % inherit_state )
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2012-07-03 08:17:48 +04:00
self . url = dc_url ( self . lp , self . creds , H )
2011-07-14 02:21:19 +04:00
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-14 02:21:19 +04:00
try :
msg = self . samdb . search ( base = container_dn , scope = ldb . SCOPE_BASE ,
expression = " (objectClass=*) " ,
attrs = [ ' gPOptions ' ] ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " Container ' %s ' does not exist " % container_dn )
2011-07-14 02:21:19 +04:00
m = ldb . Message ( )
m . dn = ldb . Dn ( self . samdb , container_dn )
if ' gPOptions ' in msg :
m [ ' new_value ' ] = ldb . MessageElement ( str ( inheritance ) , ldb . FLAG_MOD_REPLACE , ' gPOptions ' )
else :
2011-10-08 16:13:04 +04:00
m [ ' new_value ' ] = ldb . MessageElement ( str ( inheritance ) , ldb . FLAG_MOD_ADD , ' gPOptions ' )
2011-07-14 02:21:19 +04:00
try :
self . samdb . modify ( m )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-07-28 08:07:44 +04:00
raise CommandError ( " Error setting inheritance state %s " % inherit_state , e )
2011-07-14 02:21:19 +04:00
2018-11-07 01:57:13 +03:00
class cmd_fetch ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Download a GPO. """
2011-07-14 02:21:19 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <gpo> [options] "
2011-07-28 08:17:19 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-14 01:27:22 +04:00
takes_args = [ ' gpo ' ]
2011-07-28 08:17:19 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
Option ( " --tmpdir " , help = " Temporary directory for copying policy files " , type = str )
2018-07-30 09:14:37 +03:00
]
2011-07-28 08:17:19 +04:00
def run ( self , gpo , H = None , tmpdir = None , sambaopts = None , credopts = None , versionopts = None ) :
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2012-07-03 08:17:48 +04:00
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
2011-07-28 08:17:19 +04:00
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-07-28 08:17:19 +04:00
try :
msg = get_gpo_info ( self . samdb , gpo ) [ 0 ]
2012-07-03 08:55:10 +04:00
except Exception :
raise CommandError ( " GPO ' %s ' does not exist " % gpo )
2011-07-28 08:17:19 +04:00
2011-08-01 09:47:10 +04:00
# verify UNC path
2018-09-05 16:54:24 +03:00
unc = str ( msg [ ' gPCFileSysPath ' ] [ 0 ] )
2011-07-28 08:17:19 +04:00
try :
[ dom_name , service , sharepath ] = parse_unc ( unc )
2011-10-08 16:13:04 +04:00
except ValueError :
2011-07-28 08:17:19 +04:00
raise CommandError ( " Invalid GPO path ( %s ) " % unc )
2011-08-01 09:47:10 +04:00
# SMB connect to DC
2018-12-14 00:47:45 +03:00
conn = smb_connection ( dc_hostname , service , lp = self . lp ,
2020-08-13 11:40:23 +03:00
creds = self . creds )
2011-07-28 08:17:19 +04:00
2011-08-01 09:47:10 +04:00
# Copy GPT
2018-11-07 01:57:13 +03:00
tmpdir , gpodir = self . construct_tmpdir ( tmpdir , gpo )
2019-10-26 03:41:09 +03:00
2011-07-28 08:17:19 +04:00
try :
2011-08-01 09:47:10 +04:00
copy_directory_remote_to_local ( conn , sharepath , gpodir )
2018-02-14 00:07:23 +03:00
except Exception as e :
2011-10-13 02:36:44 +04:00
# FIXME: Catch more specific exception
2011-08-01 09:47:10 +04:00
raise CommandError ( " Error copying GPO from DC " , e )
2011-10-13 02:36:44 +04:00
self . outf . write ( ' GPO copied to %s \n ' % gpodir )
2011-07-28 08:17:19 +04:00
2018-11-07 01:57:13 +03:00
class cmd_backup ( GPOCommand ) :
2018-05-07 07:03:13 +03:00
""" Backup a GPO. """
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_args = [ ' gpo ' ]
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
2018-05-29 02:57:26 +03:00
Option ( " --tmpdir " , help = " Temporary directory for copying policy files " , type = str ) ,
Option ( " --generalize " , help = " Generalize XML entities to restore " ,
default = False , action = ' store_true ' ) ,
Option ( " --entities " , help = " File to export defining XML entities for the restore " ,
dest = ' ent_file ' , type = str )
2018-09-03 16:05:31 +03:00
]
2018-05-07 07:03:13 +03:00
2018-05-29 02:57:26 +03:00
def run ( self , gpo , H = None , tmpdir = None , generalize = False , sambaopts = None ,
credopts = None , versionopts = None , ent_file = None ) :
2018-05-07 07:03:13 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2018-05-07 07:03:13 +03:00
try :
msg = get_gpo_info ( self . samdb , gpo ) [ 0 ]
except Exception :
raise CommandError ( " GPO ' %s ' does not exist " % gpo )
# verify UNC path
2018-09-05 16:54:24 +03:00
unc = str ( msg [ ' gPCFileSysPath ' ] [ 0 ] )
2018-05-07 07:03:13 +03:00
try :
[ dom_name , service , sharepath ] = parse_unc ( unc )
except ValueError :
raise CommandError ( " Invalid GPO path ( %s ) " % unc )
# SMB connect to DC
2018-12-14 00:47:45 +03:00
conn = smb_connection ( dc_hostname , service , lp = self . lp ,
creds = self . creds )
2018-05-07 07:03:13 +03:00
# Copy GPT
2018-11-07 01:57:13 +03:00
tmpdir , gpodir = self . construct_tmpdir ( tmpdir , gpo )
2018-05-07 07:03:13 +03:00
try :
backup_directory_remote_to_local ( conn , sharepath , gpodir )
except Exception as e :
# FIXME: Catch more specific exception
raise CommandError ( " Error copying GPO from DC " , e )
2018-05-29 02:57:26 +03:00
2018-05-07 07:03:13 +03:00
self . outf . write ( ' GPO copied to %s \n ' % gpodir )
2018-05-29 02:57:26 +03:00
if generalize :
self . outf . write ( ' \n Attempting to generalize XML entities: \n ' )
entities = cmd_backup . generalize_xml_entities ( self . outf , gpodir ,
gpodir )
import operator
2019-08-26 00:05:31 +03:00
ents = " " . join ( ' <!ENTITY {} " {} \n " > ' . format ( ent [ 1 ] . strip ( ' &; ' ) , ent [ 0 ] ) \
for ent in sorted ( entities . items ( ) , key = operator . itemgetter ( 1 ) ) )
2018-05-29 02:57:26 +03:00
if ent_file :
with open ( ent_file , ' w ' ) as f :
f . write ( ents )
self . outf . write ( ' Entities successfully written to %s \n ' %
ent_file )
else :
self . outf . write ( ' \n Entities: \n ' )
self . outf . write ( ents )
2019-02-20 06:51:04 +03:00
# Backup the enabled GPO extension names
for ext in ( ' gPCMachineExtensionNames ' , ' gPCUserExtensionNames ' ) :
if ext in msg :
with open ( os . path . join ( gpodir , ext + ' .SAMBAEXT ' ) , ' wb ' ) as f :
f . write ( msg [ ext ] [ 0 ] )
2018-05-29 02:57:26 +03:00
@staticmethod
def generalize_xml_entities ( outf , sourcedir , targetdir ) :
entities = { }
if not os . path . exists ( targetdir ) :
os . mkdir ( targetdir )
l_dirs = [ sourcedir ]
r_dirs = [ targetdir ]
while l_dirs :
l_dir = l_dirs . pop ( )
r_dir = r_dirs . pop ( )
dirlist = os . listdir ( l_dir )
dirlist . sort ( )
for e in dirlist :
l_name = os . path . join ( l_dir , e )
r_name = os . path . join ( r_dir , e )
if os . path . isdir ( l_name ) :
l_dirs . append ( l_name )
r_dirs . append ( r_name )
if not os . path . exists ( r_name ) :
os . mkdir ( r_name )
else :
if l_name . endswith ( ' .xml ' ) :
# Restore the xml file if possible
# Get the filename to find the parser
to_parse = os . path . basename ( l_name ) [ : - 4 ]
parser = find_parser ( to_parse )
try :
with open ( l_name , ' r ' ) as ltemp :
data = ltemp . read ( )
concrete_xml = ET . fromstring ( data )
found_entities = parser . generalize_xml ( concrete_xml , r_name , entities )
except GPGeneralizeException :
outf . write ( ' SKIPPING: Generalizing failed for %s \n ' % to_parse )
else :
# No need to generalize non-xml files.
#
# TODO This could be improved with xml files stored in
# the renamed backup file (with custom extension) by
# inlining them into the exported backups.
if not os . path . samefile ( l_name , r_name ) :
shutil . copy2 ( l_name , r_name )
return entities
2018-05-07 07:03:13 +03:00
2018-11-07 01:57:13 +03:00
class cmd_create ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Create an empty GPO. """
2011-08-02 10:13:43 +04:00
2011-10-14 01:27:22 +04:00
synopsis = " % prog <displayname> [options] "
2011-08-02 10:13:43 +04:00
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
2011-10-14 01:27:22 +04:00
takes_args = [ ' displayname ' ]
2011-08-02 10:13:43 +04:00
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
Option ( " --tmpdir " , help = " Temporary directory for copying policy files " , type = str )
2018-07-30 09:14:37 +03:00
]
2011-08-02 10:13:43 +04:00
2012-09-27 20:30:47 +04:00
def run ( self , displayname , H = None , tmpdir = None , sambaopts = None , credopts = None ,
2011-08-02 10:13:43 +04:00
versionopts = None ) :
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
2012-11-29 12:31:12 +04:00
net = Net ( creds = self . creds , lp = self . lp )
2012-07-03 08:17:48 +04:00
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
2012-11-29 12:31:12 +04:00
flags = ( nbt . NBT_SERVER_LDAP |
nbt . NBT_SERVER_DS |
nbt . NBT_SERVER_WRITABLE )
cldap_ret = net . finddc ( address = dc_hostname , flags = flags )
2012-07-03 08:17:48 +04:00
else :
2012-11-29 12:31:12 +04:00
flags = ( nbt . NBT_SERVER_LDAP |
nbt . NBT_SERVER_DS |
nbt . NBT_SERVER_WRITABLE )
cldap_ret = net . finddc ( domain = self . lp . get ( ' realm ' ) , flags = flags )
dc_hostname = cldap_ret . pdc_dns_name
2012-07-03 08:17:48 +04:00
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
2011-08-02 10:13:43 +04:00
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2011-08-02 10:13:43 +04:00
msg = get_gpo_info ( self . samdb , displayname = displayname )
if msg . count > 0 :
raise CommandError ( " A GPO already existing with name ' %s ' " % displayname )
# Create new GUID
guid = str ( uuid . uuid4 ( ) )
gpo = " { %s } " % guid . upper ( )
2018-05-21 08:30:40 +03:00
self . gpo_name = gpo
2012-11-29 12:31:12 +04:00
realm = cldap_ret . dns_domain
2011-08-02 10:13:43 +04:00
unc_path = " \\ \\ %s \\ sysvol \\ %s \\ Policies \\ %s " % ( realm , realm , gpo )
# Create GPT
2018-11-07 01:57:13 +03:00
self . tmpdir , gpodir = self . construct_tmpdir ( tmpdir , gpo )
2018-05-21 08:30:40 +03:00
self . gpodir = gpodir
2011-08-02 10:13:43 +04:00
try :
os . mkdir ( os . path . join ( gpodir , " Machine " ) )
os . mkdir ( os . path . join ( gpodir , " User " ) )
gpt_contents = " [General] \r \n Version=0 \r \n "
2018-04-11 07:03:34 +03:00
open ( os . path . join ( gpodir , " GPT.INI " ) , " w " ) . write ( gpt_contents )
2018-02-14 00:07:23 +03:00
except Exception as e :
2012-09-27 20:30:47 +04:00
raise CommandError ( " Error Creating GPO files " , e )
2011-08-02 10:13:43 +04:00
2011-12-14 05:18:57 +04:00
# Connect to DC over SMB
[ dom_name , service , sharepath ] = parse_unc ( unc_path )
2018-05-21 08:30:40 +03:00
self . sharepath = sharepath
2018-12-14 00:47:45 +03:00
conn = smb_connection ( dc_hostname , service , lp = self . lp ,
creds = self . creds )
2011-08-02 10:13:43 +04:00
2018-05-21 08:30:40 +03:00
self . conn = conn
2011-12-14 05:18:57 +04:00
self . samdb . transaction_start ( )
2011-08-02 10:13:43 +04:00
try :
2011-12-14 05:18:57 +04:00
# Add cn=<guid>
2012-07-03 08:16:41 +04:00
gpo_dn = get_gpo_dn ( self . samdb , gpo )
2011-12-14 05:18:57 +04:00
m = ldb . Message ( )
2012-07-03 08:16:41 +04:00
m . dn = gpo_dn
2011-12-14 05:18:57 +04:00
m [ ' a01 ' ] = ldb . MessageElement ( " groupPolicyContainer " , ldb . FLAG_MOD_ADD , " objectClass " )
2011-08-02 10:13:43 +04:00
self . samdb . add ( m )
2011-12-14 05:18:57 +04:00
# Add cn=User,cn=<guid>
m = ldb . Message ( )
m . dn = ldb . Dn ( self . samdb , " CN=User, %s " % str ( gpo_dn ) )
m [ ' a01 ' ] = ldb . MessageElement ( " container " , ldb . FLAG_MOD_ADD , " objectClass " )
2011-08-02 10:13:43 +04:00
self . samdb . add ( m )
2011-12-14 05:18:57 +04:00
# Add cn=Machine,cn=<guid>
m = ldb . Message ( )
m . dn = ldb . Dn ( self . samdb , " CN=Machine, %s " % str ( gpo_dn ) )
m [ ' a01 ' ] = ldb . MessageElement ( " container " , ldb . FLAG_MOD_ADD , " objectClass " )
self . samdb . add ( m )
2011-08-02 10:13:43 +04:00
2011-12-14 05:18:57 +04:00
# Get new security descriptor
2018-07-30 09:16:43 +03:00
ds_sd_flags = ( security . SECINFO_OWNER |
2018-09-03 16:05:48 +03:00
security . SECINFO_GROUP |
security . SECINFO_DACL )
2012-11-29 12:31:12 +04:00
msg = get_gpo_info ( self . samdb , gpo = gpo , sd_flags = ds_sd_flags ) [ 0 ]
2012-11-20 17:51:46 +04:00
ds_sd_ndr = msg [ ' nTSecurityDescriptor ' ] [ 0 ]
2011-12-14 05:18:57 +04:00
ds_sd = ndr_unpack ( security . descriptor , ds_sd_ndr ) . as_sddl ( )
2011-08-02 10:13:43 +04:00
2011-12-14 05:18:57 +04:00
# Create a file system security descriptor
2012-11-05 13:44:14 +04:00
domain_sid = security . dom_sid ( self . samdb . get_domain_sid ( ) )
2012-06-19 10:49:33 +04:00
sddl = dsacl2fsacl ( ds_sd , domain_sid )
2012-11-05 13:44:14 +04:00
fs_sd = security . descriptor . from_sddl ( sddl , domain_sid )
2011-08-02 10:13:43 +04:00
2012-11-29 12:31:12 +04:00
# Copy GPO directory
create_directory_hier ( conn , sharepath )
2011-12-14 05:18:57 +04:00
# Set ACL
2018-07-30 09:16:43 +03:00
sio = ( security . SECINFO_OWNER |
2018-09-03 16:05:48 +03:00
security . SECINFO_GROUP |
security . SECINFO_DACL |
security . SECINFO_PROTECTED_DACL )
2012-06-19 10:49:33 +04:00
conn . set_acl ( sharepath , fs_sd , sio )
2012-11-29 12:31:12 +04:00
# Copy GPO files over SMB
copy_directory_local_to_remote ( conn , gpodir , sharepath )
m = ldb . Message ( )
m . dn = gpo_dn
m [ ' a02 ' ] = ldb . MessageElement ( displayname , ldb . FLAG_MOD_REPLACE , " displayName " )
m [ ' a03 ' ] = ldb . MessageElement ( unc_path , ldb . FLAG_MOD_REPLACE , " gPCFileSysPath " )
m [ ' a05 ' ] = ldb . MessageElement ( " 0 " , ldb . FLAG_MOD_REPLACE , " versionNumber " )
m [ ' a07 ' ] = ldb . MessageElement ( " 2 " , ldb . FLAG_MOD_REPLACE , " gpcFunctionalityVersion " )
m [ ' a04 ' ] = ldb . MessageElement ( " 0 " , ldb . FLAG_MOD_REPLACE , " flags " )
2018-07-30 09:18:03 +03:00
controls = [ " permissive_modify:0 " ]
2012-11-29 12:31:12 +04:00
self . samdb . modify ( m , controls = controls )
2012-07-03 08:55:10 +04:00
except Exception :
2011-12-14 05:18:57 +04:00
self . samdb . transaction_cancel ( )
2012-02-25 20:50:14 +04:00
raise
else :
self . samdb . transaction_commit ( )
2011-08-02 10:13:43 +04:00
2011-10-13 02:36:44 +04:00
self . outf . write ( " GPO ' %s ' created as %s \n " % ( displayname , gpo ) )
2011-08-02 10:13:43 +04:00
2011-07-14 02:21:19 +04:00
2018-05-21 08:30:40 +03:00
class cmd_restore ( cmd_create ) :
""" Restore a GPO to a new container. """
synopsis = " % prog <displayname> <backup location> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_args = [ ' displayname ' , ' backup ' ]
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
Option ( " --tmpdir " , help = " Temporary directory for copying policy files " , type = str ) ,
2019-02-26 07:01:19 +03:00
Option ( " --entities " , help = " File defining XML entities to insert into DOCTYPE header " , type = str ) ,
Option ( " --restore-metadata " , help = " Keep the old GPT.INI file and associated version number " ,
default = False , action = " store_true " )
2018-09-03 16:05:31 +03:00
]
2018-05-21 08:30:40 +03:00
def restore_from_backup_to_local_dir ( self , sourcedir , targetdir , dtd_header = ' ' ) :
SUFFIX = ' .SAMBABACKUP '
if not os . path . exists ( targetdir ) :
os . mkdir ( targetdir )
l_dirs = [ sourcedir ]
r_dirs = [ targetdir ]
while l_dirs :
l_dir = l_dirs . pop ( )
r_dir = r_dirs . pop ( )
dirlist = os . listdir ( l_dir )
2018-05-29 02:57:26 +03:00
dirlist . sort ( )
2018-05-21 08:30:40 +03:00
for e in dirlist :
l_name = os . path . join ( l_dir , e )
r_name = os . path . join ( r_dir , e )
if os . path . isdir ( l_name ) :
l_dirs . append ( l_name )
r_dirs . append ( r_name )
if not os . path . exists ( r_name ) :
os . mkdir ( r_name )
else :
if l_name . endswith ( ' .xml ' ) :
# Restore the xml file if possible
# Get the filename to find the parser
to_parse = os . path . basename ( l_name ) [ : - 4 ]
parser = find_parser ( to_parse )
try :
with open ( l_name , ' r ' ) as ltemp :
data = ltemp . read ( )
2018-06-12 07:19:41 +03:00
xml_head = ' <?xml version= " 1.0 " encoding= " utf-8 " ?> '
if data . startswith ( xml_head ) :
# It appears that sometimes the DTD rejects
# the xml header being after it.
data = data [ len ( xml_head ) : ]
# Load the XML file with the DTD (entity) header
parser . load_xml ( ET . fromstring ( xml_head + dtd_header + data ) )
else :
parser . load_xml ( ET . fromstring ( dtd_header + data ) )
2018-05-21 08:30:40 +03:00
# Write out the substituted files in the output
# location, ready to copy over.
parser . write_binary ( r_name [ : - 4 ] )
except GPNoParserException :
# In the failure case, we fallback
original_file = l_name [ : - 4 ] + SUFFIX
shutil . copy2 ( original_file , r_name [ : - 4 ] )
self . outf . write ( ' WARNING: No such parser for %s \n ' % to_parse )
self . outf . write ( ' WARNING: Falling back to simple copy-restore. \n ' )
except :
import traceback
traceback . print_exc ( )
# In the failure case, we fallback
original_file = l_name [ : - 4 ] + SUFFIX
shutil . copy2 ( original_file , r_name [ : - 4 ] )
self . outf . write ( ' WARNING: Error during parsing for %s \n ' % l_name )
self . outf . write ( ' WARNING: Falling back to simple copy-restore. \n ' )
def run ( self , displayname , backup , H = None , tmpdir = None , entities = None , sambaopts = None , credopts = None ,
2019-02-26 07:01:19 +03:00
versionopts = None , restore_metadata = None ) :
2018-05-21 08:30:40 +03:00
dtd_header = ' '
if not os . path . exists ( backup ) :
raise CommandError ( " Backup directory does not exist %s " % backup )
if entities is not None :
# DOCTYPE name is meant to match root element, but ElementTree does
# not seem to care, so this seems to be enough.
dtd_header = ' <!DOCTYPE foobar [ \n '
if not os . path . exists ( entities ) :
raise CommandError ( " Entities file does not exist %s " %
entities )
with open ( entities , ' r ' ) as entities_file :
entities_content = entities_file . read ( )
# Do a basic regex test of the entities file format
if re . match ( ' ( \ s*<!ENTITY \ s*[a-zA-Z0-9_]+ \ s*.*?>)+ \ s* \ Z ' ,
entities_content , flags = re . MULTILINE ) is None :
raise CommandError ( " Entities file does not appear to "
" conform to format \n "
' e.g. <!ENTITY entity " value " > ' )
dtd_header + = entities_content . strip ( )
dtd_header + = ' \n ]> \n '
super ( cmd_restore , self ) . run ( displayname , H , tmpdir , sambaopts ,
2018-09-03 16:05:52 +03:00
credopts , versionopts )
2018-05-21 08:30:40 +03:00
try :
# Iterate over backup files and restore with DTD
self . restore_from_backup_to_local_dir ( backup , self . gpodir ,
dtd_header )
2019-02-26 07:01:19 +03:00
keep_new_files = not restore_metadata
2018-05-21 08:30:40 +03:00
# Copy GPO files over SMB
copy_directory_local_to_remote ( self . conn , self . gpodir ,
self . sharepath ,
2019-02-26 07:01:19 +03:00
ignore_existing_dir = True ,
keep_existing_files = keep_new_files )
2018-05-21 08:30:40 +03:00
2019-02-20 06:51:04 +03:00
gpo_dn = get_gpo_dn ( self . samdb , self . gpo_name )
# Restore the enabled extensions
for ext in ( ' gPCMachineExtensionNames ' , ' gPCUserExtensionNames ' ) :
ext_file = os . path . join ( backup , ext + ' .SAMBAEXT ' )
if os . path . exists ( ext_file ) :
with open ( ext_file , ' rb ' ) as f :
data = f . read ( )
m = ldb . Message ( )
m . dn = gpo_dn
m [ ext ] = ldb . MessageElement ( data , ldb . FLAG_MOD_REPLACE ,
ext )
self . samdb . modify ( m )
2018-05-21 08:30:40 +03:00
except Exception as e :
import traceback
traceback . print_exc ( )
self . outf . write ( str ( e ) + ' \n ' )
self . outf . write ( " Failed to restore GPO -- deleting... \n " )
cmd = cmd_del ( )
cmd . run ( self . gpo_name , H , sambaopts , credopts , versionopts )
raise CommandError ( " Failed to restore: %s " % e )
2018-11-07 01:57:13 +03:00
class cmd_del ( GPOCommand ) :
2012-10-08 14:32:58 +04:00
""" Delete a GPO. """
2012-07-03 08:23:48 +04:00
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_args = [ ' gpo ' ]
takes_options = [
Option ( " -H " , help = " LDB URL for database or target server " , type = str ) ,
2018-07-30 09:14:37 +03:00
]
2012-07-03 08:23:48 +04:00
def run ( self , gpo , H = None , sambaopts = None , credopts = None ,
2018-07-30 09:15:34 +03:00
versionopts = None ) :
2012-07-03 08:23:48 +04:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2012-07-03 08:23:48 +04:00
# Check if valid GPO
try :
2012-11-29 12:31:12 +04:00
msg = get_gpo_info ( self . samdb , gpo = gpo ) [ 0 ]
2018-09-05 16:54:24 +03:00
unc_path = str ( msg [ ' gPCFileSysPath ' ] [ 0 ] )
2012-07-03 08:23:48 +04:00
except Exception :
2012-07-03 08:55:10 +04:00
raise CommandError ( " GPO ' %s ' does not exist " % gpo )
2012-07-03 08:23:48 +04:00
# Connect to DC over SMB
[ dom_name , service , sharepath ] = parse_unc ( unc_path )
2018-12-14 00:47:45 +03:00
conn = smb_connection ( dc_hostname , service , lp = self . lp ,
creds = self . creds )
2012-07-03 08:23:48 +04:00
self . samdb . transaction_start ( )
try :
# Check for existing links
msg = get_gpo_containers ( self . samdb , gpo )
if len ( msg ) :
self . outf . write ( " GPO %s is linked to containers \n " % gpo )
for m in msg :
del_gpo_link ( self . samdb , m [ ' dn ' ] , gpo )
self . outf . write ( " Removed link from %s . \n " % m [ ' dn ' ] )
# Remove LDAP entries
gpo_dn = get_gpo_dn ( self . samdb , gpo )
self . samdb . delete ( ldb . Dn ( self . samdb , " CN=User, %s " % str ( gpo_dn ) ) )
self . samdb . delete ( ldb . Dn ( self . samdb , " CN=Machine, %s " % str ( gpo_dn ) ) )
self . samdb . delete ( gpo_dn )
# Remove GPO files
conn . deltree ( sharepath )
except Exception :
self . samdb . transaction_cancel ( )
raise
else :
self . samdb . transaction_commit ( )
self . outf . write ( " GPO %s deleted. \n " % gpo )
2018-11-07 01:57:13 +03:00
class cmd_aclcheck ( GPOCommand ) :
2012-11-05 12:36:28 +04:00
""" Check all GPOs have matching LDAP and DS ACLs. """
synopsis = " % prog [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " )
2018-07-30 09:14:37 +03:00
]
2012-11-05 12:36:28 +04:00
def run ( self , H = None , sambaopts = None , credopts = None , versionopts = None ) :
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
self . url = dc_url ( self . lp , self . creds , H )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
2018-11-07 02:15:12 +03:00
self . samdb_connect ( )
2012-11-05 12:36:28 +04:00
msg = get_gpo_info ( self . samdb , None )
for m in msg :
# verify UNC path
2018-09-05 16:54:24 +03:00
unc = str ( m [ ' gPCFileSysPath ' ] [ 0 ] )
2012-11-05 12:36:28 +04:00
try :
[ dom_name , service , sharepath ] = parse_unc ( unc )
except ValueError :
raise CommandError ( " Invalid GPO path ( %s ) " % unc )
# SMB connect to DC
2018-12-14 00:47:45 +03:00
conn = smb_connection ( dc_hostname , service , lp = self . lp ,
creds = self . creds )
2012-11-05 12:36:28 +04:00
fs_sd = conn . get_acl ( sharepath , security . SECINFO_OWNER | security . SECINFO_GROUP | security . SECINFO_DACL , security . SEC_FLAG_MAXIMUM_ALLOWED )
2019-01-29 03:25:55 +03:00
if ' nTSecurityDescriptor ' not in m :
raise CommandError ( " Could not read nTSecurityDescriptor. "
" This requires an Administrator account " )
2012-11-20 17:51:46 +04:00
ds_sd_ndr = m [ ' nTSecurityDescriptor ' ] [ 0 ]
2012-11-05 12:36:28 +04:00
ds_sd = ndr_unpack ( security . descriptor , ds_sd_ndr ) . as_sddl ( )
# Create a file system security descriptor
domain_sid = security . dom_sid ( self . samdb . get_domain_sid ( ) )
expected_fs_sddl = dsacl2fsacl ( ds_sd , domain_sid )
if ( fs_sd . as_sddl ( domain_sid ) != expected_fs_sddl ) :
raise CommandError ( " Invalid GPO ACL %s on path ( %s ), should be %s " % ( fs_sd . as_sddl ( domain_sid ) , sharepath , expected_fs_sddl ) )
2018-10-16 12:36:09 +03:00
class cmd_admxload ( Command ) :
""" Loads samba admx files to sysvol """
synopsis = " % prog [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
Option ( " --admx-dir " , help = " Directory where admx templates are stored " ,
type = str , default = os . path . join ( param . data_dir ( ) , ' samba/admx ' ) )
]
def run ( self , H = None , sambaopts = None , credopts = None , versionopts = None ,
admx_dir = None ) :
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
2020-08-13 11:40:23 +03:00
creds = self . creds )
2018-10-16 12:36:09 +03:00
smb_dir = ' \\ ' . join ( [ self . lp . get ( ' realm ' ) . lower ( ) ,
' Policies ' , ' PolicyDefinitions ' ] )
try :
conn . mkdir ( smb_dir )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
elif e . args [ 0 ] != 0xC0000035 : # STATUS_OBJECT_NAME_COLLISION
raise
for dirname , dirs , files in os . walk ( admx_dir ) :
for fname in files :
path_in_admx = dirname . replace ( admx_dir , ' ' )
full_path = os . path . join ( dirname , fname )
sub_dir = ' \\ ' . join ( [ smb_dir , path_in_admx ] ) . replace ( ' / ' , ' \\ ' )
smb_path = ' \\ ' . join ( [ sub_dir , fname ] )
try :
conn . mkdir ( sub_dir )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
elif e . args [ 0 ] != 0xC0000035 : # STATUS_OBJECT_NAME_COLLISION
raise
with open ( full_path , ' rb ' ) as f :
try :
conn . savefile ( smb_path , f . read ( ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
2012-11-05 12:36:28 +04:00
2020-11-10 01:48:28 +03:00
class cmd_add_sudoers ( Command ) :
""" Adds a Samba Sudoers Group Policy to the sysvol
2020-11-06 19:30:35 +03:00
This command adds a sudo rule to the sysvol for applying to winbind clients .
Example :
samba - tool gpo manage sudoers add { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 } ' fakeu ALL=(ALL) NOPASSWD: ALL '
2020-11-10 01:48:28 +03:00
"""
synopsis = " % prog <gpo> <entry> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " , " entry " ]
def run ( self , gpo , entry , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2020-11-06 19:30:35 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
pol_dir = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo , ' MACHINE ' ] )
pol_file = ' \\ ' . join ( [ pol_dir , ' Registry.pol ' ] )
try :
pol_data = ndr_unpack ( preg . file , conn . loadfile ( pol_file ) )
except NTSTATUSError as e :
# STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_NOT_FOUND
if e . args [ 0 ] in [ 0xC0000033 , 0xC0000034 ] :
pol_data = preg . file ( ) # The file doesn't exist
elif e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
else :
raise
e = preg . entry ( )
e . keyname = b ' Software \\ Policies \\ Samba \\ Unix Settings \\ Sudo Rights '
e . valuename = b ' Software \\ Policies \\ Samba \\ Unix Settings '
e . type = 1
e . data = get_bytes ( entry )
entries = list ( pol_data . entries )
entries . append ( e )
pol_data . entries = entries
pol_data . num_entries = len ( entries )
try :
create_directory_hier ( conn , pol_dir )
conn . savefile ( pol_file , ndr_pack ( pol_data ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
2020-11-10 01:48:28 +03:00
2020-11-09 18:34:28 +03:00
class cmd_list_sudoers ( Command ) :
""" List Samba Sudoers Group Policy from the sysvol
2020-11-06 19:29:57 +03:00
This command lists sudo rules from the sysvol that will be applied to winbind clients .
Example :
samba - tool gpo manage sudoers list { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 }
2020-11-09 18:34:28 +03:00
"""
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " ]
def run ( self , gpo , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2020-11-06 19:29:57 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
pol_file = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo ,
' MACHINE \\ Registry.pol ' ] )
try :
pol_data = ndr_unpack ( preg . file , conn . loadfile ( pol_file ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000033 : # STATUS_OBJECT_NAME_INVALID
return # The file doesn't exist, so there is nothing to list
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
keyname = b ' Software \\ Policies \\ Samba \\ Unix Settings \\ Sudo Rights '
for entry in pol_data . entries :
if get_bytes ( entry . keyname ) == keyname :
self . outf . write ( ' %s \n ' % entry . data )
2020-11-09 18:34:28 +03:00
2020-11-10 02:08:59 +03:00
class cmd_remove_sudoers ( Command ) :
""" Removes a Samba Sudoers Group Policy from the sysvol
2020-11-06 19:54:59 +03:00
This command removes a sudo rule from the sysvol from applying to winbind clients .
Example :
samba - tool gpo manage sudoers remove { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 } ' fakeu ALL=(ALL) NOPASSWD: ALL '
2020-11-10 02:08:59 +03:00
"""
synopsis = " % prog <gpo> <entry> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " , " entry " ]
def run ( self , gpo , entry , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2020-11-06 19:54:59 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
pol_file = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo ,
' MACHINE \\ Registry.pol ' ] )
try :
pol_data = ndr_unpack ( preg . file , conn . loadfile ( pol_file ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000033 : # STATUS_OBJECT_NAME_INVALID
raise CommandError ( " The specified entry does not exist " )
elif e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
if entry not in [ e . data for e in pol_data . entries ] :
raise CommandError ( " Cannot remove ' %s ' because it does not exist " %
entry )
entries = [ e for e in pol_data . entries if e . data != entry ]
pol_data . num_entries = len ( entries )
pol_data . entries = entries
try :
conn . savefile ( pol_file , ndr_pack ( pol_data ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
2020-11-10 02:08:59 +03:00
2020-11-09 18:34:28 +03:00
class cmd_sudoers ( SuperCommand ) :
""" Manage Sudoers Group Policy Objects """
subcommands = { }
2020-11-10 01:48:28 +03:00
subcommands [ " add " ] = cmd_add_sudoers ( )
2020-11-09 18:34:28 +03:00
subcommands [ " list " ] = cmd_list_sudoers ( )
2020-11-10 02:08:59 +03:00
subcommands [ " remove " ] = cmd_remove_sudoers ( )
2020-11-09 18:34:28 +03:00
2020-11-10 18:05:37 +03:00
class cmd_set_security ( Command ) :
""" Set Samba Security Group Policy to the sysvol
2020-11-06 22:19:12 +03:00
This command sets a security setting to the sysvol for applying to winbind
clients . Not providing a value will unset the policy .
These settings only apply to the ADDC .
Example :
samba - tool gpo manage security set { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 } MaxTicketAge 10
Possible policies :
MaxTicketAge Maximum lifetime for user ticket
Defined in hours
MaxServiceAge Maximum lifetime for service ticket
Defined in minutes
MaxRenewAge Maximum lifetime for user ticket renewal
Defined in minutes
MinimumPasswordAge Minimum password age
Defined in days
MaximumPasswordAge Maximum password age
Defined in days
MinimumPasswordLength Minimum password length
Defined in characters
PasswordComplexity Password must meet complexity requirements
1 is Enabled , 0 is Disabled
2020-11-10 18:05:37 +03:00
"""
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " , " policy " , " value? " ]
def run ( self , gpo , policy , value = None , H = None , sambaopts = None ,
credopts = None , versionopts = None ) :
2020-11-06 22:19:12 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
inf_dir = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo ,
' MACHINE \\ Microsoft \\ Windows NT \\ SecEdit ' ] )
inf_file = ' \\ ' . join ( [ inf_dir , ' GptTmpl.inf ' ] )
try :
inf_data = ConfigParser ( interpolation = None )
inf_data . optionxform = str
raw = conn . loadfile ( inf_file )
try :
inf_data . readfp ( StringIO ( raw . decode ( ) ) )
except UnicodeDecodeError :
inf_data . readfp ( StringIO ( raw . decode ( ' utf-16 ' ) ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
# STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_PATH_NOT_FOUND
if e . args [ 0 ] not in [ 0xC0000033 , 0xC000003A ] :
raise
section_map = { ' MaxTicketAge ' : ' Kerberos Policy ' ,
' MaxServiceAge ' : ' Kerberos Policy ' ,
' MaxRenewAge ' : ' Kerberos Policy ' ,
' MinimumPasswordAge ' : ' System Access ' ,
' MaximumPasswordAge ' : ' System Access ' ,
' MinimumPasswordLength ' : ' System Access ' ,
' PasswordComplexity ' : ' System Access '
}
section = section_map [ policy ]
if not inf_data . has_section ( section ) :
inf_data . add_section ( section )
if value is not None :
inf_data . set ( section , policy , value )
else :
inf_data . remove_option ( section , policy )
out = StringIO ( )
inf_data . write ( out )
try :
create_directory_hier ( conn , inf_dir )
conn . savefile ( inf_file , get_bytes ( out . getvalue ( ) ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
else :
raise
2020-11-10 18:05:37 +03:00
2020-11-10 02:28:11 +03:00
class cmd_list_security ( Command ) :
""" List Samba Security Group Policy from the sysvol
2020-11-06 20:44:28 +03:00
This command lists security settings from the sysvol that will be applied to winbind clients .
These settings only apply to the ADDC .
Example :
samba - tool gpo manage security list { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 }
2020-11-10 02:28:11 +03:00
"""
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " ]
def run ( self , gpo , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2020-11-06 20:44:28 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
inf_file = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo ,
' MACHINE \\ Microsoft \\ Windows NT \\ SecEdit \\ GptTmpl.inf ' ] )
try :
inf_data = ConfigParser ( interpolation = None )
inf_data . optionxform = str
raw = conn . loadfile ( inf_file )
try :
inf_data . readfp ( StringIO ( raw . decode ( ) ) )
except UnicodeDecodeError :
inf_data . readfp ( StringIO ( raw . decode ( ' utf-16 ' ) ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000033 : # STATUS_OBJECT_NAME_INVALID
return # The file doesn't exist, so there is nothing to list
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
for section in inf_data . sections ( ) :
if section not in [ ' Kerberos Policy ' , ' System Access ' ] :
continue
for key , value in inf_data . items ( section ) :
self . outf . write ( ' %s = %s \n ' % ( key , value ) )
2020-11-10 02:28:11 +03:00
2020-11-10 18:05:37 +03:00
class cmd_security ( SuperCommand ) :
""" Manage Security Group Policy Objects """
subcommands = { }
subcommands [ " set " ] = cmd_set_security ( )
2020-11-10 02:28:11 +03:00
subcommands [ " list " ] = cmd_list_security ( )
2020-11-10 18:05:37 +03:00
2020-11-12 21:13:50 +03:00
class cmd_list_smb_conf ( Command ) :
""" List Samba smb.conf Group Policy from the sysvol
2020-11-12 21:19:37 +03:00
This command lists smb . conf settings from the sysvol that will be applied to winbind clients .
Example :
samba - tool gpo manage smb_conf list { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 }
2020-11-12 21:13:50 +03:00
"""
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " ]
def run ( self , gpo , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2020-11-12 21:19:37 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
pol_file = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo ,
' MACHINE \\ Registry.pol ' ] )
try :
pol_data = ndr_unpack ( preg . file , conn . loadfile ( pol_file ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000033 : # STATUS_OBJECT_NAME_INVALID
return # The file doesn't exist, so there is nothing to list
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
keyname = b ' Software \\ Policies \\ Samba \\ smb_conf '
lp = param . LoadParm ( )
for entry in pol_data . entries :
if get_bytes ( entry . keyname ) == keyname :
lp . set ( entry . valuename , str ( entry . data ) )
val = lp . get ( entry . valuename )
self . outf . write ( ' %s = %s \n ' % ( entry . valuename , val ) )
2020-11-12 21:13:50 +03:00
2020-11-13 17:28:00 +03:00
class cmd_set_smb_conf ( Command ) :
""" Sets a Samba smb.conf Group Policy to the sysvol
2020-11-13 18:39:26 +03:00
This command sets an smb . conf setting to the sysvol for applying to winbind
clients . Not providing a value will unset the policy .
Example :
samba - tool gpo manage smb_conf set { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 } ' apply gpo policies ' yes
2020-11-13 17:28:00 +03:00
"""
synopsis = " % prog <gpo> <entry> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " , " setting " , " value? " ]
def run ( self , gpo , setting , value = None , H = None , sambaopts = None , credopts = None ,
versionopts = None ) :
2020-11-13 18:39:26 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
pol_dir = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo , ' MACHINE ' ] )
pol_file = ' \\ ' . join ( [ pol_dir , ' Registry.pol ' ] )
try :
pol_data = ndr_unpack ( preg . file , conn . loadfile ( pol_file ) )
except NTSTATUSError as e :
# STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_NOT_FOUND
if e . args [ 0 ] in [ 0xC0000033 , 0xC0000034 ] :
pol_data = preg . file ( ) # The file doesn't exist
elif e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
else :
raise
if value is None :
if setting not in [ e . valuename for e in pol_data . entries ] :
raise CommandError ( " Cannot remove ' %s ' because it does "
" not exist " % setting )
entries = [ e for e in pol_data . entries \
if e . valuename != setting ]
pol_data . entries = entries
pol_data . num_entries = len ( entries )
else :
if get_string ( value ) . lower ( ) in [ ' yes ' , ' true ' , ' 1 ' ] :
etype = 4
val = 1
elif get_string ( value ) . lower ( ) in [ ' no ' , ' false ' , ' 0 ' ] :
etype = 4
val = 0
elif get_string ( value ) . isnumeric ( ) :
etype = 4
val = int ( get_string ( value ) )
else :
etype = 1
val = get_bytes ( value )
e = preg . entry ( )
e . keyname = b ' Software \\ Policies \\ Samba \\ smb_conf '
e . valuename = get_bytes ( setting )
e . type = etype
e . data = val
entries = list ( pol_data . entries )
entries . append ( e )
pol_data . entries = entries
pol_data . num_entries = len ( entries )
try :
create_directory_hier ( conn , pol_dir )
conn . savefile ( pol_file , ndr_pack ( pol_data ) )
except NTSTATUSError as e :
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
2020-11-13 17:28:00 +03:00
2020-11-12 21:13:50 +03:00
class cmd_smb_conf ( SuperCommand ) :
""" Manage smb.conf Group Policy Objects """
subcommands = { }
subcommands [ " list " ] = cmd_list_smb_conf ( )
2020-11-13 17:28:00 +03:00
subcommands [ " set " ] = cmd_set_smb_conf ( )
2020-11-12 21:13:50 +03:00
2021-01-21 19:49:48 +03:00
class cmd_list_symlink ( Command ) :
""" List VGP Symbolic Link Group Policy from the sysvol
This command lists symlink settings from the sysvol that will be applied to winbind clients .
Example :
samba - tool gpo manage symlink list { 31 B2F340 - 016 D - 11 D2 - 945 F - 00 C04FB984F9 }
"""
synopsis = " % prog <gpo> [options] "
takes_optiongroups = {
" sambaopts " : options . SambaOptions ,
" versionopts " : options . VersionOptions ,
" credopts " : options . CredentialsOptions ,
}
takes_options = [
Option ( " -H " , " --URL " , help = " LDB URL for database or target server " , type = str ,
metavar = " URL " , dest = " H " ) ,
]
takes_args = [ " gpo " ]
def run ( self , gpo , H = None , sambaopts = None , credopts = None , versionopts = None ) :
2021-01-21 20:08:15 +03:00
self . lp = sambaopts . get_loadparm ( )
self . creds = credopts . get_credentials ( self . lp , fallback_machine = True )
# We need to know writable DC to setup SMB connection
if H and H . startswith ( ' ldap:// ' ) :
dc_hostname = H [ 7 : ]
self . url = H
else :
dc_hostname = netcmd_finddc ( self . lp , self . creds )
self . url = dc_url ( self . lp , self . creds , dc = dc_hostname )
# SMB connect to DC
conn = smb_connection ( dc_hostname ,
' sysvol ' ,
lp = self . lp ,
creds = self . creds )
realm = self . lp . get ( ' realm ' )
vgp_xml = ' \\ ' . join ( [ realm . lower ( ) , ' Policies ' , gpo ,
' MACHINE \\ VGP \\ VTLA \\ Unix ' ,
' Symlink \\ manifest.xml ' ] )
try :
xml_data = ET . fromstring ( conn . loadfile ( vgp_xml ) )
except NTSTATUSError as e :
# STATUS_OBJECT_NAME_INVALID, STATUS_OBJECT_NAME_NOT_FOUND,
# STATUS_OBJECT_PATH_NOT_FOUND
if e . args [ 0 ] in [ 0xC0000033 , 0xC0000034 , 0xC000003A ] :
return # The file doesn't exist, so there is nothing to list
if e . args [ 0 ] == 0xC0000022 : # STATUS_ACCESS_DENIED
raise CommandError ( " The authenticated user does "
" not have sufficient privileges " )
raise
policy = xml_data . find ( ' policysetting ' )
data = policy . find ( ' data ' )
for file_properties in data . findall ( ' file_properties ' ) :
source = file_properties . find ( ' source ' )
target = file_properties . find ( ' target ' )
self . outf . write ( ' ln -s %s %s \n ' % ( source . text , target . text ) )
2021-01-21 19:49:48 +03:00
class cmd_symlink ( SuperCommand ) :
""" Manage symlink Group Policy Objects """
subcommands = { }
subcommands [ " list " ] = cmd_list_symlink ( )
2020-11-09 18:34:28 +03:00
class cmd_manage ( SuperCommand ) :
""" Manage Group Policy Objects """
subcommands = { }
subcommands [ " sudoers " ] = cmd_sudoers ( )
2020-11-10 02:28:11 +03:00
subcommands [ " security " ] = cmd_security ( )
2020-11-12 21:13:50 +03:00
subcommands [ " smb_conf " ] = cmd_smb_conf ( )
2021-01-21 19:49:48 +03:00
subcommands [ " symlink " ] = cmd_symlink ( )
2020-11-09 18:34:28 +03:00
2010-11-28 04:20:15 +03:00
class cmd_gpo ( SuperCommand ) :
2012-10-09 13:53:21 +04:00
""" Group Policy Object (GPO) management. """
2010-11-28 04:20:15 +03:00
subcommands = { }
subcommands [ " listall " ] = cmd_listall ( )
2010-11-28 06:33:12 +03:00
subcommands [ " list " ] = cmd_list ( )
2011-07-14 02:21:19 +04:00
subcommands [ " show " ] = cmd_show ( )
subcommands [ " getlink " ] = cmd_getlink ( )
subcommands [ " setlink " ] = cmd_setlink ( )
subcommands [ " dellink " ] = cmd_dellink ( )
2012-07-03 08:22:42 +04:00
subcommands [ " listcontainers " ] = cmd_listcontainers ( )
2011-07-14 02:21:19 +04:00
subcommands [ " getinheritance " ] = cmd_getinheritance ( )
subcommands [ " setinheritance " ] = cmd_setinheritance ( )
subcommands [ " fetch " ] = cmd_fetch ( )
subcommands [ " create " ] = cmd_create ( )
2012-07-03 08:23:48 +04:00
subcommands [ " del " ] = cmd_del ( )
2012-11-05 12:36:28 +04:00
subcommands [ " aclcheck " ] = cmd_aclcheck ( )
2018-05-07 07:03:13 +03:00
subcommands [ " backup " ] = cmd_backup ( )
2018-05-21 08:30:40 +03:00
subcommands [ " restore " ] = cmd_restore ( )
2018-10-16 12:36:09 +03:00
subcommands [ " admxload " ] = cmd_admxload ( )
2020-11-09 18:34:28 +03:00
subcommands [ " manage " ] = cmd_manage ( )