gpo: Add gpo tests
[nivanova/samba-autobuild/.git] / python / samba / gpclass.py
1 # Reads important GPO parameters and updates Samba
2 # Copyright (C) Luke Morrison <luc785@.hotmail.com> 2013
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17
18 import sys
19 import os
20 import tdb
21 sys.path.insert(0, "bin/python")
22 import samba.gpo as gpo
23 import optparse
24 import ldb
25 from samba.auth import system_session
26 import samba.getopt as options
27 from samba.samdb import SamDB
28 from samba.netcmd import gpo as gpo_user
29 import codecs
30 from samba import NTSTATUSError
31 from ConfigParser import ConfigParser
32 from StringIO import StringIO
33 from abc import ABCMeta, abstractmethod
34
35 class Backlog:
36     def __init__(self, sysvol_log):
37         if os.path.isfile(sysvol_log):
38             self.backlog = tdb.open(sysvol_log)
39         else:
40             self.backlog = tdb.Tdb(sysvol_log, 0, tdb.DEFAULT, os.O_CREAT|os.O_RDWR)
41         self.backlog.transaction_start()
42
43     def version(self, guid):
44         try:
45             old_version = int(self.backlog.get(guid))
46         except TypeError:
47             old_version = -1
48         return old_version
49
50     def store(self, guid, version):
51         self.backlog.store(guid, '%i' % version)
52
53     def commit(self):
54         self.backlog.transaction_commit()
55
56     def __del__(self):
57         self.backlog.close()
58
59 class gp_ext(object):
60     __metaclass__ = ABCMeta
61
62     @abstractmethod
63     def list(self, rootpath):
64         pass
65
66     @abstractmethod
67     def parse(self, afile, ldb, conn, lp):
68         pass
69
70     @abstractmethod
71     def __str__(self):
72         pass
73
74
75 class inf_to():
76     __metaclass__ = ABCMeta
77
78     def __init__(self, logger, ldb, lp, attribute, val):
79         self.logger = logger
80         self.ldb = ldb
81         self.attribute = attribute
82         self.val = val
83         self.lp = lp
84
85     def explicit(self):
86         return self.val
87
88     def update_samba(self):
89         (upd_sam, value) = self.mapper().get(self.attribute)
90         upd_sam(value())
91
92     @abstractmethod
93     def mapper(self):
94         pass
95
96 class inf_to_ldb(inf_to):
97     '''This class takes the .inf file parameter (essentially a GPO file mapped to a GUID),
98     hashmaps it to the Samba parameter, which then uses an ldb object to update the
99     parameter to Samba4. Not registry oriented whatsoever.
100     '''
101
102     def ch_minPwdAge(self, val):
103         self.logger.info('KDC Minimum Password age was changed from %s to %s' % (self.ldb.get_minPwdAge(), val))
104         self.ldb.set_minPwdAge(val)
105
106     def ch_maxPwdAge(self, val):
107         self.logger.info('KDC Maximum Password age was changed from %s to %s' % (self.ldb.get_maxPwdAge(), val))
108         self.ldb.set_maxPwdAge(val)
109
110     def ch_minPwdLength(self, val):
111         self.logger.info('KDC Minimum Password length was changed from %s to %s' % (self.ldb.get_minPwdLength(), val))
112         self.ldb.set_minPwdLength(val)
113
114     def ch_pwdProperties(self, val):
115         self.logger.info('KDC Password Properties were changed from %s to %s' % (self.ldb.get_pwdProperties(), val))
116         self.ldb.set_pwdProperties(val)
117
118     def nttime2unix(self):
119         seconds = 60
120         minutes = 60
121         hours = 24
122         sam_add = 10000000
123         val = (self.val)
124         val = int(val)
125         return  str(-(val * seconds * minutes * hours * sam_add))
126
127     def mapper(self):
128         '''ldap value : samba setter'''
129         return { "minPwdAge" : (self.ch_minPwdAge, self.nttime2unix),
130                  "maxPwdAge" : (self.ch_maxPwdAge, self.nttime2unix),
131                  # Could be none, but I like the method assignment in update_samba
132                  "minPwdLength" : (self.ch_minPwdLength, self.explicit),
133                  "pwdProperties" : (self.ch_pwdProperties, self.explicit),
134
135                }
136
137
138 class gp_sec_ext(gp_ext):
139     '''This class does the following two things:
140         1) Identifies the GPO if it has a certain kind of filepath,
141         2) Finally parses it.
142     '''
143
144     count = 0
145
146     def __init__(self, logger):
147         self.logger = logger
148
149     def __str__(self):
150         return "Security GPO extension"
151
152     def list(self, rootpath):
153         return os.path.join(rootpath, "MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf")
154
155     def listmachpol(self, rootpath):
156         return os.path.join(rootpath, "Machine/Registry.pol")
157
158     def listuserpol(self, rootpath):
159         return os.path.join(rootpath, "User/Registry.pol")
160
161     def populate_inf(self):
162         return {"System Access": {"MinimumPasswordAge": ("minPwdAge", inf_to_ldb),
163                                   "MaximumPasswordAge": ("maxPwdAge", inf_to_ldb),
164                                   "MinimumPasswordLength": ("minPwdLength", inf_to_ldb),
165                                   "PasswordComplexity": ("pwdProperties", inf_to_ldb),
166                                  }
167                }
168
169     def read_inf(self, path, conn):
170         ret = False
171         inftable = self.populate_inf()
172
173         policy = conn.loadfile(path.replace('/', '\\'))
174         current_section = None
175
176         # So here we would declare a boolean,
177         # that would get changed to TRUE.
178         #
179         # If at any point in time a GPO was applied,
180         # then we return that boolean at the end.
181
182         inf_conf = ConfigParser()
183         inf_conf.optionxform=str
184         try:
185             inf_conf.readfp(StringIO(policy))
186         except:
187             inf_conf.readfp(StringIO(policy.decode('utf-16')))
188
189         for section in inf_conf.sections():
190             current_section = inftable.get(section)
191             if not current_section:
192                 continue
193             for key, value in inf_conf.items(section):
194                 if current_section.get(key):
195                     (att, setter) = current_section.get(key)
196                     value = value.encode('ascii', 'ignore')
197                     ret = True
198                     setter(self.logger, self.ldb, self.lp, att, value).update_samba()
199         return ret
200
201     def parse(self, afile, ldb, conn, lp):
202         self.ldb = ldb
203         self.lp = lp
204
205         # Fixing the bug where only some Linux Boxes capitalize MACHINE
206         if afile.endswith('inf'):
207             try:
208                 blist = afile.split('/')
209                 idx = afile.lower().split('/').index('machine')
210                 for case in [blist[idx].upper(), blist[idx].capitalize(), blist[idx].lower()]:
211                     bfile = '/'.join(blist[:idx]) + '/' + case + '/' + '/'.join(blist[idx+1:])
212                     try:
213                         return self.read_inf(bfile, conn)
214                     except NTSTATUSError:
215                         continue
216             except ValueError:
217                 try:
218                     return self.read_inf(afile, conn)
219                 except:
220                     return None
221