a5573cece265185627ecbd1ee34c0bef9c003b43
[samba.git] / source4 / scripting / bin / samba_gpoupdate
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
6
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.
11 #
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.
16 #
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/>.
19
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'''
23
24 import os
25 import fcntl
26 import sys
27 import tempfile
28 import subprocess
29 import tdb
30
31 sys.path.insert(0, "bin/python")
32
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
41
42
43 # Finds all GPO Files ending in inf
44 def gp_path_list(path):
45
46     GPO_LIST = []
47     for ext in gp_extensions:
48         GPO_LIST.append((ext, ext.list(path)))
49     return GPO_LIST
50
51
52 def gpo_parser(GPO_LIST, ldb, conn, attr_log):
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     '''
60
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)
66         else:
67             temp = ext.parse(thefile, ldb, conn, attr_log)
68     return ret
69
70
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         """
76
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
86
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)
95
96     def init_argsopts(self):
97         '''Set the options and the arguments'''
98         (opts, args) = self.parser.parse_args()
99
100         self.opts = opts
101         self.args = args
102
103     def init_credopts(self):
104         '''Set Credential operations'''
105         self.credopts = options.CredentialsOptions(self.parser)
106
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
115
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()
121
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()
128
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
135
136     def Get_lp_Content(self):
137         '''Return an instance of a local lp context'''
138         return self.lp
139
140     def Get_Creds(self):
141         '''Return an instance of a local creds'''
142         return self.creds
143
144
145 # Set up the GPO service
146 GPOService = GPOServiceSetup()
147 GPOService.InitializeService()
148
149 # Get the Samba Instance
150 test_ldb = GPOService.Get_LDB()
151
152 # Get The lp context
153 lp = GPOService.Get_lp_Content()
154
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)
168
169 # Get the CREDS
170 creds = GPOService.Get_Creds()
171
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')
177
178
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)
184
185
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
191
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)
196
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
200
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')
204
205 hierarchy_gpos = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn)
206 change_backlog = False
207
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
213
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
223
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)
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()
253