2 # Copyright Luke Morrison <luc785@.hotmail.com> July 2013
3 # Co-Edited by Matthieu Pattou July 2013 from original August 2013
4 # Edited by Garming Sam Feb. 2014
5 # Edited by Luke Morrison April 2014
6 # Edited by David Mulder May 2017
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 '''This script reads a log file of previous GPO, gets all GPO from sysvol
22 and sorts them by container. Then, it applies the ones that haven't been
23 applied, have changed, or is in the right container'''
28 sys.path.insert(0, "bin/python")
31 from samba import getopt as options
32 from samba.auth import system_session
34 from samba.samdb import SamDB
37 from samba.gpclass import *
38 from samba.net import Net
39 from samba.dcerpc import nbt
41 import samba.gpo as gpo
45 ''' Fetch the hostname of a writable DC '''
46 def get_dc_hostname(creds, lp):
47 net = Net(creds=creds, lp=lp)
48 cldap_ret = net.finddc(domain=lp.get('realm'), flags=(nbt.NBT_SERVER_LDAP |
50 return cldap_ret.pdc_dns_name
52 ''' Fetch a list of GUIDs for applicable GPOs '''
53 def get_gpo_list(dc_hostname, creds, lp):
55 ads = gpo.ADS_STRUCT(dc_hostname, lp, creds)
57 gpos = ads.get_gpo_list(creds.get_username())
60 def apply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
61 gp_db = store.get_gplog(creds.get_username())
62 dc_hostname = get_dc_hostname(creds, lp)
64 conn = smb.SMB(dc_hostname, 'sysvol', lp=lp, creds=creds)
66 logger.error('Error connecting to \'%s\' using SMB' % dc_hostname)
68 gpos = get_gpo_list(dc_hostname, creds, lp)
72 if guid == 'Local Policy':
74 path = os.path.join(lp.get('realm').lower(), 'Policies', guid)
75 local_path = os.path.join(lp.get("path", "sysvol"), path)
76 version = int(gpo.gpo_get_sysvol_gpt_version(local_path)[1])
77 if version != store.get_int(guid):
78 logger.info('GPO %s has changed' % guid)
79 gp_db.state(GPOSTATE.APPLY)
81 gp_db.state(GPOSTATE.ENFORCE)
84 for ext in gp_extensions:
86 ext.parse(ext.list(path), test_ldb, conn, gp_db, lp)
87 except Exception as e:
88 logger.error('Failed to parse gpo %s for extension %s' % \
90 logger.error('Message was: ' + str(e))
93 store.store(guid, '%i' % version)
96 def unapply_log(gp_db):
98 item = gp_db.apply_log_pop()
104 def unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
105 gp_db = store.get_gplog(creds.get_username())
106 gp_db.state(GPOSTATE.UNAPPLY)
107 for gpo_guid in unapply_log(gp_db):
108 gp_db.set_guid(gpo_guid)
109 unapply_attributes = gp_db.list(gp_extensions)
110 for attr in unapply_attributes:
111 attr_obj = attr[-1](logger, test_ldb, gp_db, lp, attr[0], attr[1])
112 attr_obj.mapper()[attr[0]][0](attr[1]) # Set the old value
113 gp_db.delete(str(attr_obj), attr[0])
116 if __name__ == "__main__":
117 parser = optparse.OptionParser('samba_gpoupdate [options]')
118 sambaopts = options.SambaOptions(parser)
120 # Get the command line options
121 parser.add_option_group(sambaopts)
122 parser.add_option_group(options.VersionOptions(parser))
123 credopts = options.CredentialsOptions(parser)
124 parser.add_option('-H', '--url', dest='url', help='URL for the samdb')
125 parser.add_option('-X', '--unapply', help='Unapply Group Policy',
127 parser.add_option('-M', '--machine', help='Apply machine policy',
128 action='store_true', default=False)
129 parser.add_option_group(credopts)
131 # Set the options and the arguments
132 (opts, args) = parser.parse_args()
134 # Set the loadparm context
135 lp = sambaopts.get_loadparm()
141 # Initialize the session
142 creds = credopts.get_credentials(lp, fallback_machine=True)
143 session = system_session()
146 logger = logging.getLogger('samba_gpoupdate')
147 logger.addHandler(logging.StreamHandler(sys.stdout))
148 logger.setLevel(logging.CRITICAL)
149 log_level = lp.log_level()
151 logger.setLevel(logging.ERROR)
153 logger.setLevel(logging.WARNING)
155 logger.setLevel(logging.INFO)
157 logger.setLevel(logging.DEBUG)
159 cache_dir = lp.get('cache directory')
160 store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
164 if lp.get('server role') == 'active directory domain controller':
165 gp_extensions.append(gp_sec_ext(logger))
167 pass # User extensions
169 # Get a live instance of Samba
171 test_ldb = SamDB(url, session_info=session, credentials=creds, lp=lp)
176 apply_gp(lp, creds, test_ldb, logger, store, gp_extensions)
178 unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions)