gpo: Make the gpclass more easily extensible
[Samba.git] / source4 / scripting / bin / samba_gpoupdate
blob3a6ed99d7b8125a243edaf1eb01edd03797e8eab
1 #!/usr/bin/env python
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
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 '''This script reads a log file of previous GPO, gets all GPO from sysvol
21 and sorts them by container. Then, it applies the ones that haven't been
22 applied, have changed, or is in the right container'''
24 import os
25 import fcntl
26 import sys
27 import tempfile
28 import subprocess
29 import tdb
31 sys.path.insert(0, "bin/python")
33 import samba
34 import optparse
35 from samba import getopt as options
36 from samba.gpclass import *
37 from samba.net import Net
38 from samba.dcerpc import nbt
39 from samba import smb
40 import logging
43 # Finds all GPO Files ending in inf
44 def gp_path_list(path):
46 GPO_LIST = []
47 for ext in gp_extensions:
48 GPO_LIST.append((ext, ext.list(path)))
49 return GPO_LIST
52 def gpo_parser(GPO_LIST, ldb, conn, attr_log, lp):
53 '''The API method to parse the GPO
54 :param GPO_LIST:
55 :param ldb: Live instance of an LDB object AKA Samba
56 :param conn: Live instance of a CIFS connection
57 :param attr_log: backlog path for GPO and attribute to be written
58 no return except a newly updated Samba
59 '''
61 ret = False
62 for entry in GPO_LIST:
63 (ext, thefile) = entry
64 if ret == False:
65 ret = ext.parse(thefile, ldb, conn, attr_log, lp)
66 else:
67 temp = ext.parse(thefile, ldb, conn, attr_log, lp)
68 return ret
71 class GPOServiceSetup:
72 def __init__(self):
73 """Initialize all components necessary to return instances of
74 a Samba lp context (smb.conf) and Samba LDB context
75 """
77 self.parser = optparse.OptionParser("samba_gpoupdate [options]")
78 self.sambaopts = options.SambaOptions(self.parser)
79 self.credopts = None
80 self.opts = None
81 self.args = None
82 self.lp = None
83 self.smbconf = None
84 self.creds = None
85 self.url = None
87 # Setters or Initializers
88 def init_parser(self):
89 '''Get the command line options'''
90 self.parser.add_option_group(self.sambaopts)
91 self.parser.add_option_group(options.VersionOptions(self.parser))
92 self.init_credopts()
93 self.parser.add_option("-H", dest="url", help="URL for the samdb")
94 self.parser.add_option_group(self.credopts)
96 def init_argsopts(self):
97 '''Set the options and the arguments'''
98 (opts, args) = self.parser.parse_args()
100 self.opts = opts
101 self.args = args
103 def init_credopts(self):
104 '''Set Credential operations'''
105 self.credopts = options.CredentialsOptions(self.parser)
107 def init_lp(self):
108 '''Set the loadparm context'''
109 self.lp = self.sambaopts.get_loadparm()
110 self.smbconf = self.lp.configfile
111 if (not self.opts.url):
112 self.url = self.lp.samdb_url()
113 else:
114 self.url = self.opts.url
116 def init_session(self):
117 '''Initialize the session'''
118 self.creds = self.credopts.get_credentials(self.lp,
119 fallback_machine=True)
120 self.session = system_session()
122 def InitializeService(self):
123 '''Inializer for the thread'''
124 self.init_parser()
125 self.init_argsopts()
126 self.init_lp()
127 self.init_session()
129 # Getters
130 def Get_LDB(self):
131 '''Return a live instance of Samba'''
132 SambaDB = SamDB(self.url, session_info=self.session,
133 credentials=self.creds, lp=self.lp)
134 return SambaDB
136 def Get_lp_Content(self):
137 '''Return an instance of a local lp context'''
138 return self.lp
140 def Get_Creds(self):
141 '''Return an instance of a local creds'''
142 return self.creds
145 # Set up the GPO service
146 GPOService = GPOServiceSetup()
147 GPOService.InitializeService()
149 # Get the Samba Instance
150 test_ldb = GPOService.Get_LDB()
152 # Get The lp context
153 lp = GPOService.Get_lp_Content()
155 # Set up logging
156 logger = logging.getLogger('samba_gpoupdate')
157 logger.addHandler(logging.StreamHandler(sys.stdout))
158 logger.setLevel(logging.CRITICAL)
159 log_level = lp.log_level()
160 if log_level == 1:
161 logger.setLevel(logging.ERROR)
162 elif log_level == 2:
163 logger.setLevel(logging.WARNING)
164 elif log_level == 3:
165 logger.setLevel(logging.INFO)
166 elif log_level >= 4:
167 logger.setLevel(logging.DEBUG)
169 # Get the CREDS
170 creds = GPOService.Get_Creds()
172 # Read the readable backLog into a hashmap
173 # then open writable backLog in same location
174 BackLoggedGPO = None
175 sys_log = '%s/%s' % (lp.get("path", "sysvol"), 'gpo.tdb')
176 attr_log = '%s/%s' % (lp.get("path", "sysvol"), 'attrlog.txt')
179 if os.path.isfile(sys_log):
180 BackLog = tdb.open(sys_log)
181 else:
182 BackLog = tdb.Tdb(sys_log, 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR)
183 BackLoggedGPO = scan_log(BackLog)
186 # We need to know writable DC to setup SMB connection
187 net = Net(creds=creds, lp=lp)
188 cldap_ret = net.finddc(domain=lp.get('realm'), flags=(nbt.NBT_SERVER_LDAP |
189 nbt.NBT_SERVER_DS))
190 dc_hostname = cldap_ret.pdc_dns_name
192 try:
193 conn = smb.SMB(dc_hostname, 'sysvol', lp=lp, creds=creds)
194 except Exception, e:
195 raise Exception("Error connecting to '%s' using SMB" % dc_hostname, e)
197 # Get the dn of the domain, and the dn of readable/writable DC
198 global_dn = test_ldb.domain_dn()
199 DC_OU = "OU=Domain Controllers" + ',' + global_dn
201 # Set up a List of the GUID for all GPO's
202 guid_list = [x['name'] for x in conn.list('%s/Policies' % lp.get("realm").lower())]
203 SYSV_PATH = '%s/%s/%s' % (lp.get("path", "sysvol"), lp.get("realm"), 'Policies')
205 hierarchy_gpos = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
206 change_backlog = False
208 # Take a local list of all current GPO list and run it against previous GPO's
209 # to see if something has changed. If so reset default and re-apply GPO.
210 Applicable_GPO = []
211 for i in hierarchy_gpos:
212 Applicable_GPO += i
214 # Flag gets set when
215 GPO_Changed = False
216 GPO_Deleted = check_deleted(Applicable_GPO, BackLoggedGPO)
217 if (GPO_Deleted):
218 # Null the backlog
219 BackLoggedGPO = {}
220 # Reset defaults then overwrite them
221 Reset_Defaults(test_ldb)
222 GPO_Changed = False
224 BackLog.transaction_start()
225 for guid_eval in hierarchy_gpos:
226 guid = guid_eval[0]
227 gp_extensions = [gp_sec_ext(logger)]
228 local_path = '%s/Policies' % lp.get("realm").lower() + '/' + guid + '/'
229 version = int(gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1])
230 try:
231 old_version = int(BackLoggedGPO.get(guid))
232 except:
233 old_version = -1
234 gpolist = gp_path_list(local_path)
235 if version != old_version:
236 GPO_Changed = True
237 # If the GPO has a dn that is applicable to Samba
238 if guid_eval[1]:
239 # If it has a GPO file that could apply to Samba
240 if gpolist[0][1]:
241 # If it we have not read it before and is not empty
242 # Rewrite entire logfile here
243 if (version != 0) and GPO_Changed == True:
244 logger.info('GPO %s has changed' % guid)
245 try:
246 change_backlog = gpo_parser(gpolist, test_ldb, conn, attr_log, lp)
247 except:
248 logger.error('Failed to parse gpo %s' % guid)
249 continue
250 BackLog.store(guid, '%i' % version)
251 BackLog.transaction_commit()
252 BackLog.close()