gpo: Extract Kerberos policy from Security extension
[samba.git] / python / samba / gp_sec_ext.py
index d74dd252fc13a79bf1d9dbcc4e6631a86098eea6..fde1f22269296fbfd5c946c506cd7a751cf5ba79 100644 (file)
 
 import os.path
 from samba.gpclass import gp_ext_setter, gp_inf_ext
+from samba.auth import system_session
+from samba.compat import get_string
+try:
+    from ldb import LdbError
+    from samba.samdb import SamDB
+except ImportError:
+    pass
 
 
-class inf_to_kdc_tdb(gp_ext_setter):
-    def mins_to_hours(self):
-        return '%d' % (int(self.val) / 60)
+class gp_krb_ext(gp_inf_ext):
+    apply_map = { 'MaxTicketAge':  'kdc:user_ticket_lifetime',
+                  'MaxServiceAge': 'kdc:service_ticket_lifetime',
+                  'MaxRenewAge':   'kdc:renewal_lifetime' }
+    def process_group_policy(self, deleted_gpo_list, changed_gpo_list):
+        if self.lp.get('server role') != 'active directory domain controller':
+            return
+        inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+        for gpo in deleted_gpo_list:
+            self.gp_db.set_guid(gpo[0])
+            for section in gpo[1].keys():
+                if section == str(self):
+                    for att, value in gpo[1][section].items():
+                        update_samba, _ = self.mapper().get(att)
+                        update_samba(att, value)
+                        self.gp_db.delete(section, att)
+                        self.gp_db.commit()
 
-    def days_to_hours(self):
-        return '%d' % (int(self.val) * 24)
+        for gpo in changed_gpo_list:
+            if gpo.file_sys_path:
+                self.gp_db.set_guid(gpo.name)
+                path = os.path.join(gpo.file_sys_path, inf_file)
+                inf_conf = self.parse(path)
+                if not inf_conf:
+                    continue
+                for section in inf_conf.sections():
+                    if section == str(self):
+                        for key, value in inf_conf.items(section):
+                            att = gp_krb_ext.apply_map[key]
+                            (update_samba, value_func) = self.mapper().get(att)
+                            update_samba(att, value_func(value))
+                            self.gp_db.commit()
 
-    def set_kdc_tdb(self, val):
-        old_val = self.gp_db.gpostore.get(self.attribute)
-        self.logger.info('%s was changed from %s to %s' % (self.attribute,
+    def mins_to_hours(self, val):
+        return '%d' % (int(val) / 60)
+
+    def days_to_hours(self, val):
+        return '%d' % (int(val) * 24)
+
+    def set_kdc_tdb(self, attribute, val):
+        old_val = self.gp_db.gpostore.get(attribute)
+        self.logger.info('%s was changed from %s to %s' % (attribute,
                                                            old_val, val))
         if val is not None:
-            self.gp_db.gpostore.store(self.attribute, val)
-            self.gp_db.store(str(self), self.attribute, old_val)
+            self.gp_db.gpostore.store(attribute, get_string(val))
+            self.gp_db.store(str(self), attribute, get_string(old_val) \
+                    if old_val else None)
         else:
-            self.gp_db.gpostore.delete(self.attribute)
-            self.gp_db.delete(str(self), self.attribute)
+            self.gp_db.gpostore.delete(attribute)
+            self.gp_db.delete(str(self), attribute)
 
     def mapper(self):
-        return {'kdc:user_ticket_lifetime': (self.set_kdc_tdb, self.explicit),
-                 'kdc:service_ticket_lifetime': (self.set_kdc_tdb,
-                                                 self.mins_to_hours),
-                 'kdc:renewal_lifetime': (self.set_kdc_tdb,
-                                          self.days_to_hours),
+        return {'kdc:user_ticket_lifetime': (self.set_kdc_tdb,
+                                             lambda val: val),
+                'kdc:service_ticket_lifetime': (self.set_kdc_tdb,
+                                                self.mins_to_hours),
+                'kdc:renewal_lifetime': (self.set_kdc_tdb,
+                                         self.days_to_hours),
                 }
 
     def __str__(self):
         return 'Kerberos Policy'
 
+    def rsop(self, gpo):
+        output = {}
+        inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+        if gpo.file_sys_path:
+            path = os.path.join(gpo.file_sys_path, inf_file)
+            inf_conf = self.parse(path)
+            if not inf_conf:
+                return output
+            for section in inf_conf.sections():
+                output[section] = {k: v for k, v in inf_conf.items(section) \
+                                      if gp_krb_ext.apply_map.get(k)}
+        return output
+
 
 class inf_to_ldb(gp_ext_setter):
     '''This class takes the .inf file parameter (essentially a GPO file mapped
@@ -55,6 +109,16 @@ class inf_to_ldb(gp_ext_setter):
     object to update the parameter to Samba4. Not registry oriented whatsoever.
     '''
 
+    def __init__(self, logger, gp_db, lp, creds, key, value):
+        super(inf_to_ldb, self).__init__(logger, gp_db, lp, creds, key, value)
+        try:
+            self.ldb = SamDB(self.lp.samdb_url(),
+                             session_info=system_session(),
+                             credentials=self.creds,
+                             lp=self.lp)
+        except (NameError, LdbError):
+            raise Exception('Failed to load SamDB for assigning Group Policy')
+
     def ch_minPwdAge(self, val):
         old_val = self.ldb.get_minPwdAge()
         self.logger.info('KDC Minimum Password age was changed from %s to %s'
@@ -96,11 +160,11 @@ class inf_to_ldb(gp_ext_setter):
     def mapper(self):
         '''ldap value : samba setter'''
         return {"minPwdAge": (self.ch_minPwdAge, self.days2rel_nttime),
-                 "maxPwdAge": (self.ch_maxPwdAge, self.days2rel_nttime),
-                 # Could be none, but I like the method assignment in
-                 # update_samba
-                 "minPwdLength": (self.ch_minPwdLength, self.explicit),
-                 "pwdProperties": (self.ch_pwdProperties, self.explicit),
+                "maxPwdAge": (self.ch_maxPwdAge, self.days2rel_nttime),
+                # Could be none, but I like the method assignment in
+                # update_samba
+                "minPwdLength": (self.ch_minPwdLength, self.explicit),
+                "pwdProperties": (self.ch_pwdProperties, self.explicit),
 
                 }
 
@@ -119,16 +183,6 @@ class gp_sec_ext(gp_inf_ext):
     def __str__(self):
         return "Security GPO extension"
 
-    def list(self, rootpath):
-        return os.path.join(rootpath,
-                            "MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf")
-
-    def listmachpol(self, rootpath):
-        return os.path.join(rootpath, "Machine/Registry.pol")
-
-    def listuserpol(self, rootpath):
-        return os.path.join(rootpath, "User/Registry.pol")
-
     def apply_map(self):
         return {"System Access": {"MinimumPasswordAge": ("minPwdAge",
                                                          inf_to_ldb),
@@ -139,18 +193,64 @@ class gp_sec_ext(gp_inf_ext):
                                   "PasswordComplexity": ("pwdProperties",
                                                          inf_to_ldb),
                                   },
-                "Kerberos Policy": {"MaxTicketAge": (
-                                        "kdc:user_ticket_lifetime",
-                                        inf_to_kdc_tdb
-                                    ),
-                                    "MaxServiceAge": (
-                                        "kdc:service_ticket_lifetime",
-                                        inf_to_kdc_tdb
-                                    ),
-                                    "MaxRenewAge": (
-                                        "kdc:renewal_lifetime",
-                                        inf_to_kdc_tdb
-                                    ),
-                                    }
                 }
 
+    def process_group_policy(self, deleted_gpo_list, changed_gpo_list):
+        if self.lp.get('server role') != 'active directory domain controller':
+            return
+        inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+        apply_map = self.apply_map()
+        for gpo in deleted_gpo_list:
+            self.gp_db.set_guid(gpo[0])
+            for section in gpo[1].keys():
+                current_section = apply_map.get(section)
+                if not current_section:
+                    continue
+                for key, value in gpo[1][section].items():
+                    setter = None
+                    for _, tup in current_section.items():
+                        if tup[0] == key:
+                            setter = tup[1]
+                    if setter:
+                        value = value.encode('ascii', 'ignore') \
+                             if value else value
+                        setter(self.logger, self.gp_db, self.lp, self.creds,
+                               key, value).delete()
+                        self.gp_db.delete(section, key)
+                        self.gp_db.commit()
+
+        for gpo in changed_gpo_list:
+            if gpo.file_sys_path:
+                self.gp_db.set_guid(gpo.name)
+                path = os.path.join(gpo.file_sys_path, inf_file)
+                inf_conf = self.parse(path)
+                if not inf_conf:
+                    continue
+                for section in inf_conf.sections():
+                    current_section = apply_map.get(section)
+                    if not current_section:
+                        continue
+                    for key, value in inf_conf.items(section):
+                        if current_section.get(key):
+                            (att, setter) = current_section.get(key)
+                            value = value.encode('ascii', 'ignore')
+                            setter(self.logger, self.gp_db, self.lp,
+                                   self.creds, att, value).update_samba()
+                            self.gp_db.commit()
+
+    def rsop(self, gpo):
+        output = {}
+        inf_file = 'MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf'
+        apply_map = self.apply_map()
+        if gpo.file_sys_path:
+            path = os.path.join(gpo.file_sys_path, inf_file)
+            inf_conf = self.parse(path)
+            if not inf_conf:
+                return output
+            for section in inf_conf.sections():
+                current_section = apply_map.get(section)
+                if not current_section:
+                    continue
+                output[section] = {k: v for k, v in inf_conf.items(section) \
+                                      if current_section.get(k)}
+        return output