gpo: Test that unapply works
[nivanova/samba-autobuild/.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 # Edited by David Mulder May 2017
7
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.
12 #
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.
17 #
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/>.
20
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'''
24
25 import os
26 import sys
27
28 sys.path.insert(0, "bin/python")
29
30 import optparse
31 from samba import getopt as options
32 from samba.gpclass import *
33 from samba.net import Net
34 from samba.dcerpc import nbt
35 from samba import smb
36 import logging
37
38 ''' Fetch the hostname of a writable DC '''
39 def get_dc_hostname(creds, lp):
40     net = Net(creds=creds, lp=lp)
41     cldap_ret = net.finddc(domain=lp.get('realm'), flags=(nbt.NBT_SERVER_LDAP |
42         nbt.NBT_SERVER_DS))
43     return cldap_ret.pdc_dns_name
44
45 ''' Fetch a list of GUIDs for applicable GPOs '''
46 def get_gpo_list(dc_hostname, creds, lp):
47     gpos = []
48     ads = gpo.ADS_STRUCT(dc_hostname, lp, creds)
49     if ads.connect():
50         gpos = ads.get_gpo_list(creds.get_username())
51     return gpos
52
53 def apply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
54     gp_db = store.get_gplog(creds.get_username())
55     dc_hostname = get_dc_hostname(creds, lp)
56     try:
57         conn =  smb.SMB(dc_hostname, 'sysvol', lp=lp, creds=creds)
58     except:
59         logger.error('Error connecting to \'%s\' using SMB' % dc_hostname)
60         raise
61     gpos = get_gpo_list(dc_hostname, creds, lp)
62
63     for gpo_obj in gpos:
64         guid = gpo_obj.name
65         if guid == 'Local Policy':
66             continue
67         path = os.path.join(lp.get('realm').lower(), 'Policies', guid)
68         local_path = os.path.join(lp.get("path", "sysvol"), path)
69         version = int(gpo.gpo_get_sysvol_gpt_version(local_path)[1])
70         if version != store.get_int(guid):
71             logger.info('GPO %s has changed' % guid)
72             gp_db.state(GPOSTATE.APPLY)
73         else:
74             gp_db.state(GPOSTATE.ENFORCE)
75         gp_db.set_guid(guid)
76         store.start()
77         try:
78             for ext in gp_extensions:
79                 ext.parse(ext.list(path), test_ldb, conn, gp_db, lp)
80         except:
81             logger.error('Failed to parse gpo %s' % guid)
82             store.cancel()
83             continue
84         store.store(guid, '%i' % version)
85         store.commit()
86
87 def unapply_log(gp_db):
88     while True:
89         item = gp_db.apply_log_pop()
90         if item:
91             yield item
92         else:
93             break
94
95 def unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
96     gp_db = store.get_gplog(creds.get_username())
97     gp_db.state(GPOSTATE.UNAPPLY)
98     for gpo_guid in unapply_log(gp_db):
99         gp_db.set_guid(gpo_guid)
100         unapply_attributes = gp_db.list(gp_extensions)
101         for attr in unapply_attributes:
102             attr_obj = attr[-1](logger, test_ldb, gp_db, lp, attr[0], attr[1])
103             attr_obj.mapper()[attr[0]][0](attr[1]) # Set the old value
104             gp_db.delete(str(attr_obj), attr[0])
105         gp_db.commit()
106
107 if __name__ == "__main__":
108     parser = optparse.OptionParser('samba_gpoupdate [options]')
109     sambaopts = options.SambaOptions(parser)
110
111     # Get the command line options
112     parser.add_option_group(sambaopts)
113     parser.add_option_group(options.VersionOptions(parser))
114     credopts = options.CredentialsOptions(parser)
115     parser.add_option('-H', '--url', dest='url', help='URL for the samdb')
116     parser.add_option('-X', '--unapply', help='Unapply Group Policy',
117                       action='store_true')
118     parser.add_option_group(credopts)
119
120     # Set the options and the arguments
121     (opts, args) = parser.parse_args()
122
123     # Set the loadparm context
124     lp = sambaopts.get_loadparm()
125     if not opts.url:
126         url = lp.samdb_url()
127     else:
128         url = opts.url
129
130     # Initialize the session
131     creds = credopts.get_credentials(lp, fallback_machine=True)
132     session = system_session()
133
134     # Set up logging
135     logger = logging.getLogger('samba_gpoupdate')
136     logger.addHandler(logging.StreamHandler(sys.stdout))
137     logger.setLevel(logging.CRITICAL)
138     log_level = lp.log_level()
139     if log_level == 1:
140         logger.setLevel(logging.ERROR)
141     elif log_level == 2:
142         logger.setLevel(logging.WARNING)
143     elif log_level == 3:
144         logger.setLevel(logging.INFO)
145     elif log_level >= 4:
146         logger.setLevel(logging.DEBUG)
147
148     cache_dir = lp.get('cache directory')
149     store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
150
151     gp_extensions = [gp_sec_ext(logger)]
152
153     # Get a live instance of Samba
154     test_ldb = SamDB(url, session_info=session, credentials=creds, lp=lp)
155
156     if not opts.unapply:
157         apply_gp(lp, creds, test_ldb, logger, store, gp_extensions)
158     else:
159         unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions)
160