python2 reduction: Merge remaining compat code into common
[samba.git] / python / samba / gp_sec_ext.py
1 # gp_sec_ext kdc gpo policy
2 # Copyright (C) Luke Morrison <luc785@.hotmail.com> 2013
3 # Copyright (C) David Mulder <dmulder@suse.com> 2018
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 import os.path
19 from samba.gpclass import gp_inf_ext
20 from samba.auth import system_session
21 from samba.common import get_string
22 try:
23     from ldb import LdbError
24     from samba.samdb import SamDB
25 except ImportError:
26     pass
27
28 def mins_to_hours(val):
29     return '%d' % (int(val) / 60)
30
31 def days_to_hours(val):
32     return '%d' % (int(val) * 24)
33
34 def days2rel_nttime(val):
35     seconds = 60
36     minutes = 60
37     hours = 24
38     sam_add = 10000000
39     val = int(val)
40     return str(-(val * seconds * minutes * hours * sam_add))
41
42 class gp_krb_ext(gp_inf_ext):
43     apply_map = { 'MaxTicketAge':  'kdc:user_ticket_lifetime',
44                   'MaxServiceAge': 'kdc:service_ticket_lifetime',
45                   'MaxRenewAge':   'kdc:renewal_lifetime' }
46     def process_group_policy(self, deleted_gpo_list, changed_gpo_list):
47         if self.lp.get('server role') != 'active directory domain controller':
48             return
49         inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
50         for guid, settings in deleted_gpo_list:
51             self.gp_db.set_guid(guid)
52             for section in settings.keys():
53                 if section == str(self):
54                     for att, value in settings[section].items():
55                         self.set_kdc_tdb(att, value)
56                         self.gp_db.delete(section, att)
57                         self.gp_db.commit()
58
59         for gpo in changed_gpo_list:
60             if gpo.file_sys_path:
61                 self.gp_db.set_guid(gpo.name)
62                 path = os.path.join(gpo.file_sys_path, inf_file)
63                 inf_conf = self.parse(path)
64                 if not inf_conf:
65                     continue
66                 for section in inf_conf.sections():
67                     if section == str(self):
68                         for key, value in inf_conf.items(section):
69                             att = gp_krb_ext.apply_map[key]
70                             value_func = self.mapper().get(att)
71                             self.set_kdc_tdb(att, value_func(value))
72                             self.gp_db.commit()
73
74     def set_kdc_tdb(self, attribute, val):
75         old_val = self.gp_db.gpostore.get(attribute)
76         self.logger.info('%s was changed from %s to %s' % (attribute,
77                                                            old_val, val))
78         if val is not None:
79             self.gp_db.gpostore.store(attribute, get_string(val))
80             self.gp_db.store(str(self), attribute, get_string(old_val) \
81                     if old_val else None)
82         else:
83             self.gp_db.gpostore.delete(attribute)
84             self.gp_db.delete(str(self), attribute)
85
86     def mapper(self):
87         return {'kdc:user_ticket_lifetime': lambda val: val,
88                 'kdc:service_ticket_lifetime': mins_to_hours,
89                 'kdc:renewal_lifetime': days_to_hours,
90                 }
91
92     def __str__(self):
93         return 'Kerberos Policy'
94
95     def rsop(self, gpo):
96         output = {}
97         if self.lp.get('server role') != 'active directory domain controller':
98             return output
99         inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
100         if gpo.file_sys_path:
101             path = os.path.join(gpo.file_sys_path, inf_file)
102             inf_conf = self.parse(path)
103             if not inf_conf:
104                 return output
105             for section in inf_conf.sections():
106                 output[section] = {k: v for k, v in inf_conf.items(section) \
107                                       if gp_krb_ext.apply_map.get(k)}
108         return output
109
110
111 class gp_access_ext(gp_inf_ext):
112     '''This class takes the .inf file parameter (essentially a GPO file mapped
113     to a GUID), hashmaps it to the Samba parameter, which then uses an ldb
114     object to update the parameter to Samba4. Not registry oriented whatsoever.
115     '''
116
117     def __init__(self, *args):
118         super().__init__(*args)
119         try:
120             self.ldb = SamDB(self.lp.samdb_url(),
121                              session_info=system_session(),
122                              credentials=self.creds,
123                              lp=self.lp)
124         except (NameError, LdbError):
125             raise Exception('Failed to load SamDB for assigning Group Policy')
126
127     apply_map = { 'MinimumPasswordAge':     'minPwdAge',
128                   'MaximumPasswordAge':     'maxPwdAge',
129                   'MinimumPasswordLength':  'minPwdLength',
130                   'PasswordComplexity':     'pwdProperties' }
131     def process_group_policy(self, deleted_gpo_list, changed_gpo_list):
132         if self.lp.get('server role') != 'active directory domain controller':
133             return
134         inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
135         for guid, settings in deleted_gpo_list:
136             self.gp_db.set_guid(guid)
137             for section in settings.keys():
138                 if section == str(self):
139                     for att, value in settings[section].items():
140                         update_samba, _ = self.mapper().get(att)
141                         update_samba(att, value)
142                         self.gp_db.delete(section, att)
143                         self.gp_db.commit()
144
145         for gpo in changed_gpo_list:
146             if gpo.file_sys_path:
147                 self.gp_db.set_guid(gpo.name)
148                 path = os.path.join(gpo.file_sys_path, inf_file)
149                 inf_conf = self.parse(path)
150                 if not inf_conf:
151                     continue
152                 for section in inf_conf.sections():
153                     if section == str(self):
154                         for key, value in inf_conf.items(section):
155                             att = gp_access_ext.apply_map[key]
156                             (update_samba, value_func) = self.mapper().get(att)
157                             update_samba(att, value_func(value))
158                             self.gp_db.commit()
159
160     def ch_minPwdAge(self, attribute, val):
161         old_val = self.ldb.get_minPwdAge()
162         self.logger.info('KDC Minimum Password age was changed from %s to %s'
163                          % (old_val, val))
164         self.gp_db.store(str(self), attribute, str(old_val))
165         self.ldb.set_minPwdAge(val)
166
167     def ch_maxPwdAge(self, attribute, val):
168         old_val = self.ldb.get_maxPwdAge()
169         self.logger.info('KDC Maximum Password age was changed from %s to %s'
170                          % (old_val, val))
171         self.gp_db.store(str(self), attribute, str(old_val))
172         self.ldb.set_maxPwdAge(val)
173
174     def ch_minPwdLength(self, attribute, val):
175         old_val = self.ldb.get_minPwdLength()
176         self.logger.info(
177             'KDC Minimum Password length was changed from %s to %s'
178             % (old_val, val))
179         self.gp_db.store(str(self), attribute, str(old_val))
180         self.ldb.set_minPwdLength(val)
181
182     def ch_pwdProperties(self, attribute, val):
183         old_val = self.ldb.get_pwdProperties()
184         self.logger.info('KDC Password Properties were changed from %s to %s'
185                          % (old_val, val))
186         self.gp_db.store(str(self), attribute, str(old_val))
187         self.ldb.set_pwdProperties(val)
188
189     def mapper(self):
190         '''ldap value : samba setter'''
191         return {"minPwdAge": (self.ch_minPwdAge, days2rel_nttime),
192                 "maxPwdAge": (self.ch_maxPwdAge, days2rel_nttime),
193                 # Could be none, but I like the method assignment in
194                 # update_samba
195                 "minPwdLength": (self.ch_minPwdLength, lambda val: val),
196                 "pwdProperties": (self.ch_pwdProperties, lambda val: val),
197
198                 }
199
200     def __str__(self):
201         return 'System Access'
202
203     def rsop(self, gpo):
204         output = {}
205         if self.lp.get('server role') != 'active directory domain controller':
206             return output
207         inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
208         if gpo.file_sys_path:
209             path = os.path.join(gpo.file_sys_path, inf_file)
210             inf_conf = self.parse(path)
211             if not inf_conf:
212                 return output
213             for section in inf_conf.sections():
214                 output[section] = {k: v for k, v in inf_conf.items(section) \
215                                       if gp_access_ext.apply_map.get(k)}
216         return output