gpo: Add GPO unapply
[mdw/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 # 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():
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()
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         local_path = os.path.join(lp.get('realm').lower(), 'Policies', guid)
68         version = int(gpo.gpo_get_sysvol_gpt_version(os.path.join(lp.get("path", "sysvol"), local_path))[1])
69         if version != store.get_int(guid):
70             logger.info('GPO %s has changed' % guid)
71             gp_db.set_guid(guid)
72             store.start()
73             try:
74                 for ext in gp_extensions:
75                     ext.parse(ext.list(local_path), test_ldb, conn, gp_db, lp)
76             except:
77                 logger.error('Failed to parse gpo %s' % guid)
78                 store.cancel()
79                 continue
80             store.store(guid, '%i' % version)
81         store.commit()
82
83 def unapply_log(gp_db):
84     while True:
85         item = gp_db.apply_log_pop()
86         if item:
87             yield item
88         else:
89             break
90
91 def unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions):
92     gp_db = store.get_gplog(creds.get_username())
93     for gpo_guid in unapply_log(gp_db):
94         gp_db.set_guid(gpo_guid)
95         unapply_attributes = gp_db.list(gp_extensions)
96         for attr in unapply_attributes:
97             attr_obj = attr[-1](logger, test_ldb, gp_db, lp, attr[0], attr[1])
98             attr_obj.mapper()[attr[0]][0](attr[1]) # Set the old value
99             gp_db.delete(str(attr_obj), attr[0])
100         gp_db.commit()
101
102 if __name__ == "__main__":
103     parser = optparse.OptionParser('samba_gpoupdate [options]')
104     sambaopts = options.SambaOptions(parser)
105
106     # Get the command line options
107     parser.add_option_group(sambaopts)
108     parser.add_option_group(options.VersionOptions(parser))
109     credopts = options.CredentialsOptions(parser)
110     parser.add_option('-H', '--url', dest='url', help='URL for the samdb')
111     parser.add_option('-X', '--unapply', help='Unapply Group Policy', action='store_true')
112     parser.add_option_group(credopts)
113
114     # Set the options and the arguments
115     (opts, args) = parser.parse_args()
116
117     # Set the loadparm context
118     lp = sambaopts.get_loadparm()
119     if not opts.url:
120         url = lp.samdb_url()
121     else:
122         url = opts.url
123
124     # Initialize the session
125     creds = credopts.get_credentials(lp, fallback_machine=True)
126     session = system_session()
127
128     # Set up logging
129     logger = logging.getLogger('samba_gpoupdate')
130     logger.addHandler(logging.StreamHandler(sys.stdout))
131     logger.setLevel(logging.CRITICAL)
132     log_level = lp.log_level()
133     if log_level == 1:
134         logger.setLevel(logging.ERROR)
135     elif log_level == 2:
136         logger.setLevel(logging.WARNING)
137     elif log_level == 3:
138         logger.setLevel(logging.INFO)
139     elif log_level >= 4:
140         logger.setLevel(logging.DEBUG)
141
142     cache_dir = lp.get('cache directory')
143     store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb'))
144
145     gp_extensions = [gp_sec_ext(logger)]
146
147     # Get a live instance of Samba
148     test_ldb = SamDB(url, session_info=session, credentials=creds, lp=lp)
149
150     if not opts.unapply:
151         apply_gp(lp, creds, test_ldb, logger, store, gp_extensions)
152     else:
153         unapply_gp(lp, creds, test_ldb, logger, store, gp_extensions)
154