#!/usr/bin/env python # Copyright Luke Morrison July 2013 # Co-Edited by Matthieu Pattou July 2013 from original August 2013 # Edited by Garming Sam Feb. 2014 # Edited by Luke Morrison April 2014 # 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 . '''This script reads a log file of previous GPO, gets all GPO from sysvol and sorts them by container. Then, it applies the ones that haven't been applied, have changed, or is in the right container''' import os import fcntl import sys import tempfile import subprocess import tdb sys.path.insert(0, "bin/python") import samba import optparse from samba import getopt as options from samba.gpclass import * from samba.net import Net from samba.dcerpc import nbt from samba import smb import logging # Finds all GPO Files ending in inf def gp_path_list(path): GPO_LIST = [] for ext in gp_extensions: GPO_LIST.append((ext, ext.list(path))) return GPO_LIST def gpo_parser(GPO_LIST, ldb, conn, attr_log, lp): '''The API method to parse the GPO :param GPO_LIST: :param ldb: Live instance of an LDB object AKA Samba :param conn: Live instance of a CIFS connection :param attr_log: backlog path for GPO and attribute to be written no return except a newly updated Samba ''' ret = False for entry in GPO_LIST: (ext, thefile) = entry if ret == False: ret = ext.parse(thefile, ldb, conn, attr_log, lp) else: temp = ext.parse(thefile, ldb, conn, attr_log, lp) return ret class GPOServiceSetup: def __init__(self): """Initialize all components necessary to return instances of a Samba lp context (smb.conf) and Samba LDB context """ self.parser = optparse.OptionParser("samba_gpoupdate [options]") self.sambaopts = options.SambaOptions(self.parser) self.credopts = None self.opts = None self.args = None self.lp = None self.smbconf = None self.creds = None self.url = None # Setters or Initializers def init_parser(self): '''Get the command line options''' self.parser.add_option_group(self.sambaopts) self.parser.add_option_group(options.VersionOptions(self.parser)) self.init_credopts() self.parser.add_option("-H", dest="url", help="URL for the samdb") self.parser.add_option_group(self.credopts) def init_argsopts(self): '''Set the options and the arguments''' (opts, args) = self.parser.parse_args() self.opts = opts self.args = args def init_credopts(self): '''Set Credential operations''' self.credopts = options.CredentialsOptions(self.parser) def init_lp(self): '''Set the loadparm context''' self.lp = self.sambaopts.get_loadparm() self.smbconf = self.lp.configfile if (not self.opts.url): self.url = self.lp.samdb_url() else: self.url = self.opts.url def init_session(self): '''Initialize the session''' self.creds = self.credopts.get_credentials(self.lp, fallback_machine=True) self.session = system_session() def InitializeService(self): '''Inializer for the thread''' self.init_parser() self.init_argsopts() self.init_lp() self.init_session() # Getters def Get_LDB(self): '''Return a live instance of Samba''' SambaDB = SamDB(self.url, session_info=self.session, credentials=self.creds, lp=self.lp) return SambaDB def Get_lp_Content(self): '''Return an instance of a local lp context''' return self.lp def Get_Creds(self): '''Return an instance of a local creds''' return self.creds # Set up the GPO service GPOService = GPOServiceSetup() GPOService.InitializeService() # Get the Samba Instance test_ldb = GPOService.Get_LDB() # Get The lp context lp = GPOService.Get_lp_Content() # Set up logging logger = logging.getLogger('samba_gpoupdate') logger.addHandler(logging.StreamHandler(sys.stdout)) logger.setLevel(logging.CRITICAL) log_level = lp.log_level() if log_level == 1: logger.setLevel(logging.ERROR) elif log_level == 2: logger.setLevel(logging.WARNING) elif log_level == 3: logger.setLevel(logging.INFO) elif log_level >= 4: logger.setLevel(logging.DEBUG) # Get the CREDS creds = GPOService.Get_Creds() # Read the readable backLog into a hashmap # then open writable backLog in same location BackLoggedGPO = None sys_log = '%s/%s' % (lp.get("path", "sysvol"), 'gpo.tdb') attr_log = '%s/%s' % (lp.get("path", "sysvol"), 'attrlog.txt') if os.path.isfile(sys_log): BackLog = tdb.open(sys_log) else: BackLog = tdb.Tdb(sys_log, 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR) BackLoggedGPO = scan_log(BackLog) # We need to know writable DC to setup SMB connection net = Net(creds=creds, lp=lp) cldap_ret = net.finddc(domain=lp.get('realm'), flags=(nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)) dc_hostname = cldap_ret.pdc_dns_name try: conn = smb.SMB(dc_hostname, 'sysvol', lp=lp, creds=creds) except Exception, e: raise Exception("Error connecting to '%s' using SMB" % dc_hostname, e) # Get the dn of the domain, and the dn of readable/writable DC global_dn = test_ldb.domain_dn() DC_OU = "OU=Domain Controllers" + ',' + global_dn # Set up a List of the GUID for all GPO's guid_list = [x['name'] for x in conn.list('%s/Policies' % lp.get("realm").lower())] SYSV_PATH = '%s/%s/%s' % (lp.get("path", "sysvol"), lp.get("realm"), 'Policies') hierarchy_gpos = establish_hierarchy(test_ldb, guid_list, DC_OU, global_dn) change_backlog = False # Take a local list of all current GPO list and run it against previous GPO's # to see if something has changed. If so reset default and re-apply GPO. Applicable_GPO = [] for i in hierarchy_gpos: Applicable_GPO += i # Flag gets set when GPO_Changed = False GPO_Deleted = check_deleted(Applicable_GPO, BackLoggedGPO) if (GPO_Deleted): # Null the backlog BackLoggedGPO = {} # Reset defaults then overwrite them Reset_Defaults(test_ldb) GPO_Changed = False BackLog.transaction_start() for guid_eval in hierarchy_gpos: guid = guid_eval[0] gp_extensions = [gp_sec_ext(logger)] local_path = '%s/Policies' % lp.get("realm").lower() + '/' + guid + '/' version = int(gpo.gpo_get_sysvol_gpt_version(lp.get("path", "sysvol") + '/' + local_path)[1]) try: old_version = int(BackLoggedGPO.get(guid)) except: old_version = -1 gpolist = gp_path_list(local_path) if version != old_version: GPO_Changed = True # If the GPO has a dn that is applicable to Samba if guid_eval[1]: # If it has a GPO file that could apply to Samba if gpolist[0][1]: # If it we have not read it before and is not empty # Rewrite entire logfile here if (version != 0) and GPO_Changed == True: logger.info('GPO %s has changed' % guid) try: change_backlog = gpo_parser(gpolist, test_ldb, conn, attr_log, lp) except: logger.error('Failed to parse gpo %s' % guid) continue BackLog.store(guid, '%i' % version) BackLog.transaction_commit() BackLog.close()