X-Git-Url: http://git.samba.org/samba.git/?p=nivanova%2Fsamba-autobuild%2F.git;a=blobdiff_plain;f=python%2Fsamba%2Fgpclass.py;h=00330eb5ecb4d840f6c24bf109433b882ad45e9d;hp=a73cde4c3795bdf2d806b16488690e17cf551560;hb=ac56f870182054942aff462bf226d52edd927468;hpb=4a7ccbeab7656f96e2d7cadb9be44526c52910f7 diff --git a/python/samba/gpclass.py b/python/samba/gpclass.py index a73cde4c379..00330eb5ecb 100644 --- a/python/samba/gpclass.py +++ b/python/samba/gpclass.py @@ -44,7 +44,8 @@ except ImportError: class gp_log: ''' Log settings overwritten by gpo apply - The gp_log is an xml file that stores a history of gpo changes (and the original setting value). + The gp_log is an xml file that stores a history of gpo changes (and the + original setting value). The log is organized like so: @@ -69,19 +70,22 @@ class gp_log: - Each guid value contains a list of extensions, which contain a list of attributes. The guid value - represents a GPO. The attributes are the values of those settings prior to the application of - the GPO. - The list of guids is enclosed within a user name, which represents the user the settings were - applied to. This user may be the samaccountname of the local computer, which implies that these - are machine policies. - The applylog keeps track of the order in which the GPOs were applied, so that they can be rolled - back in reverse, returning the machine to the state prior to policy application. + Each guid value contains a list of extensions, which contain a list of + attributes. The guid value represents a GPO. The attributes are the values + of those settings prior to the application of the GPO. + The list of guids is enclosed within a user name, which represents the user + the settings were applied to. This user may be the samaccountname of the + local computer, which implies that these are machine policies. + The applylog keeps track of the order in which the GPOs were applied, so + that they can be rolled back in reverse, returning the machine to the state + prior to policy application. ''' def __init__(self, user, gpostore, db_log=None): ''' Initialize the gp_log - param user - the username (or machine name) that policies are being applied to - param gpostore - the GPOStorage obj which references the tdb which contains gp_logs + param user - the username (or machine name) that policies are + being applied to + param gpostore - the GPOStorage obj which references the tdb which + contains gp_logs param db_log - (optional) a string to initialize the gp_log ''' self._state = GPOSTATE.APPLY @@ -91,24 +95,27 @@ class gp_log: self.gpdb = etree.fromstring(db_log) else: self.gpdb = etree.Element('gp') - self.user = self.gpdb.find('user[@name="%s"]' % user) - if self.user is None: - self.user = etree.SubElement(self.gpdb, 'user') - self.user.attrib['name'] = user + self.user = user + user_obj = self.gpdb.find('user[@name="%s"]' % user) + if user_obj is None: + user_obj = etree.SubElement(self.gpdb, 'user') + user_obj.attrib['name'] = user def state(self, value): ''' Policy application state param value - APPLY, ENFORCE, or UNAPPLY - The behavior of the gp_log depends on whether we are applying policy, enforcing policy, - or unapplying policy. During an apply, old settings are recorded in the log. During an - enforce, settings are being applied but the gp_log does not change. During an unapply, - additions to the log should be ignored (since function calls to apply settings are actually + The behavior of the gp_log depends on whether we are applying policy, + enforcing policy, or unapplying policy. During an apply, old settings + are recorded in the log. During an enforce, settings are being applied + but the gp_log does not change. During an unapply, additions to the log + should be ignored (since function calls to apply settings are actually reverting policy), but removals from the log are allowed. ''' # If we're enforcing, but we've unapplied, apply instead if value == GPOSTATE.ENFORCE: - apply_log = self.user.find('applylog') + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + apply_log = user_obj.find('applylog') if apply_log is None or len(apply_log) == 0: self._state = GPOSTATE.APPLY else: @@ -118,16 +125,19 @@ class gp_log: def set_guid(self, guid): ''' Log to a different GPO guid - param guid - guid value of the GPO from which we're applying policy + param guid - guid value of the GPO from which we're applying + policy ''' - self.guid = self.user.find('guid[@value="%s"]' % guid) - if self.guid is None: - self.guid = etree.SubElement(self.user, 'guid') - self.guid.attrib['value'] = guid + self.guid = guid + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + obj = user_obj.find('guid[@value="%s"]' % guid) + if obj is None: + obj = etree.SubElement(user_obj, 'guid') + obj.attrib['value'] = guid if self._state == GPOSTATE.APPLY: - apply_log = self.user.find('applylog') + apply_log = user_obj.find('applylog') if apply_log is None: - apply_log = etree.SubElement(self.user, 'applylog') + apply_log = etree.SubElement(user_obj, 'applylog') item = etree.SubElement(apply_log, 'guid') item.attrib['count'] = '%d' % (len(apply_log)-1) item.attrib['value'] = guid @@ -136,45 +146,53 @@ class gp_log: ''' Pop a GPO guid from the applylog return - last applied GPO guid - Removes the GPO guid last added to the list, which is the most recently applied GPO. + Removes the GPO guid last added to the list, which is the most recently + applied GPO. ''' - apply_log = self.user.find('applylog') + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + apply_log = user_obj.find('applylog') if apply_log is not None: ret = apply_log.find('guid[@count="%d"]' % (len(apply_log)-1)) if ret is not None: apply_log.remove(ret) return ret.attrib['value'] - if len(apply_log) == 0 and apply_log in self.user: - self.user.remove(apply_log) + if len(apply_log) == 0 and apply_log in user_obj: + user_obj.remove(apply_log) return None def store(self, gp_ext_name, attribute, old_val): ''' Store an attribute in the gp_log param gp_ext_name - Name of the extension applying policy param attribute - The attribute being modified - param old_val - The value of the attribute prior to policy application + param old_val - The value of the attribute prior to policy + application ''' if self._state == GPOSTATE.UNAPPLY or self._state == GPOSTATE.ENFORCE: return None - assert self.guid is not None, "gpo guid was not set" - ext = self.guid.find('gp_ext[@name="%s"]' % gp_ext_name) + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + guid_obj = user_obj.find('guid[@value="%s"]' % self.guid) + assert guid_obj is not None, "gpo guid was not set" + ext = guid_obj.find('gp_ext[@name="%s"]' % gp_ext_name) if ext is None: - ext = etree.SubElement(self.guid, 'gp_ext') + ext = etree.SubElement(guid_obj, 'gp_ext') ext.attrib['name'] = gp_ext_name attr = ext.find('attribute[@name="%s"]' % attribute) if attr is None: attr = etree.SubElement(ext, 'attribute') attr.attrib['name'] = attribute - attr.text = old_val + attr.text = old_val def retrieve(self, gp_ext_name, attribute): ''' Retrieve a stored attribute from the gp_log param gp_ext_name - Name of the extension which applied policy param attribute - The attribute being retrieved - return - The value of the attribute prior to policy application + return - The value of the attribute prior to policy + application ''' - assert self.guid is not None, "gpo guid was not set" - ext = self.guid.find('gp_ext[@name="%s"]' % gp_ext_name) + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + guid_obj = user_obj.find('guid[@value="%s"]' % self.guid) + assert guid_obj is not None, "gpo guid was not set" + ext = guid_obj.find('gp_ext[@name="%s"]' % gp_ext_name) if ext is not None: attr = ext.find('attribute[@name="%s"]' % attribute) if attr is not None: @@ -182,44 +200,50 @@ class gp_log: return None def list(self, gp_extensions): - ''' Return a list of attributes, their previous values, and functions to set them - param gp_extensions - list of extension objects, for retrieving attr to func mappings - return - list of (attr, value, apply_func) tuples for unapplying policy + ''' Return a list of attributes, their previous values, and functions + to set them + param gp_extensions - list of extension objects, for retrieving attr to + func mappings + return - list of (attr, value, apply_func) tuples for + unapplying policy ''' - assert self.guid is not None, "gpo guid was not set" + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + guid_obj = user_obj.find('guid[@value="%s"]' % self.guid) + assert guid_obj is not None, "gpo guid was not set" ret = [] data_maps = {} for gp_ext in gp_extensions: data_maps.update(gp_ext.apply_map()) - exts = self.guid.findall('gp_ext') + exts = guid_obj.findall('gp_ext') if exts is not None: for ext in exts: - ext_map = {val[0]: val[1] for (key, val) in data_maps[ext.attrib['name']].items()} + ext_map = {val[0]: val[1] for (key, val) in \ + data_maps[ext.attrib['name']].items()} attrs = ext.findall('attribute') for attr in attrs: - ret.append((attr.attrib['name'], attr.text, ext_map[attr.attrib['name']])) + ret.append((attr.attrib['name'], attr.text, + ext_map[attr.attrib['name']])) return ret def delete(self, gp_ext_name, attribute): ''' Remove an attribute from the gp_log - param gp_ext_name - name of extension from which to remove the attribute + param gp_ext_name - name of extension from which to remove the + attribute param attribute - attribute to remove ''' - assert self.guid is not None, "gpo guid was not set" - ext = self.guid.find('gp_ext[@name="%s"]' % gp_ext_name) + user_obj = self.gpdb.find('user[@name="%s"]' % self.user) + guid_obj = user_obj.find('guid[@value="%s"]' % self.guid) + assert guid_obj is not None, "gpo guid was not set" + ext = guid_obj.find('gp_ext[@name="%s"]' % gp_ext_name) if ext is not None: attr = ext.find('attribute[@name="%s"]' % attribute) if attr is not None: ext.remove(attr) if len(ext) == 0: - self.guid.remove(ext) + guid_obj.remove(ext) def commit(self): ''' Write gp_log changes to disk ''' - if len(self.guid) == 0 and self.guid in self.user: - self.user.remove(self.guid) - if len(self.user) == 0 and self.user in self.gpdb: - self.gpdb.remove(self.user) self.gpostore.store(self.username, etree.tostring(self.gpdb, 'utf-8')) class GPOStorage: @@ -304,37 +328,71 @@ class inf_to(): def __str__(self): pass +class inf_to_kdc_tdb(inf_to): + def mins_to_hours(self): + return '%d' % (int(self.val)/60) + + def days_to_hours(self): + return '%d' % (int(self.val)*24) + + 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, + 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) + else: + self.gp_db.gpostore.delete(self.attribute) + self.gp_db.delete(str(self), 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), + } + + def __str__(self): + return 'Kerberos Policy' + class inf_to_ldb(inf_to): - '''This class takes the .inf file parameter (essentially a GPO file mapped to a GUID), - hashmaps it to the Samba parameter, which then uses an ldb object to update the - parameter to Samba4. Not registry oriented whatsoever. + '''This class takes the .inf file parameter (essentially a GPO file mapped + to a GUID), hashmaps it to the Samba parameter, which then uses an ldb + object to update the parameter to Samba4. Not registry oriented whatsoever. ''' def ch_minPwdAge(self, val): old_val = self.ldb.get_minPwdAge() - self.logger.info('KDC Minimum Password age was changed from %s to %s' % (old_val, val)) + self.logger.info('KDC Minimum Password age was changed from %s to %s' \ + % (old_val, val)) self.gp_db.store(str(self), self.attribute, old_val) self.ldb.set_minPwdAge(val) def ch_maxPwdAge(self, val): old_val = self.ldb.get_maxPwdAge() - self.logger.info('KDC Maximum Password age was changed from %s to %s' % (old_val, val)) + self.logger.info('KDC Maximum Password age was changed from %s to %s' \ + % (old_val, val)) self.gp_db.store(str(self), self.attribute, old_val) self.ldb.set_maxPwdAge(val) def ch_minPwdLength(self, val): old_val = self.ldb.get_minPwdLength() - self.logger.info('KDC Minimum Password length was changed from %s to %s' % (old_val, val)) + self.logger.info( + 'KDC Minimum Password length was changed from %s to %s' \ + % (old_val, val)) self.gp_db.store(str(self), self.attribute, old_val) self.ldb.set_minPwdLength(val) def ch_pwdProperties(self, val): old_val = self.ldb.get_pwdProperties() - self.logger.info('KDC Password Properties were changed from %s to %s' % (old_val, val)) + self.logger.info('KDC Password Properties were changed from %s to %s' \ + % (old_val, val)) self.gp_db.store(str(self), self.attribute, old_val) self.ldb.set_pwdProperties(val) - def nttime2unix(self): + def days2rel_nttime(self): seconds = 60 minutes = 60 hours = 24 @@ -345,9 +403,10 @@ class inf_to_ldb(inf_to): def mapper(self): '''ldap value : samba setter''' - return { "minPwdAge" : (self.ch_minPwdAge, self.nttime2unix), - "maxPwdAge" : (self.ch_maxPwdAge, self.nttime2unix), - # Could be none, but I like the method assignment in update_samba + 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), @@ -372,7 +431,8 @@ class gp_sec_ext(gp_ext): return "Security GPO extension" def list(self, rootpath): - return os.path.join(rootpath, "MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf") + return os.path.join(rootpath, + "MACHINE/Microsoft/Windows NT/SecEdit/GptTmpl.inf") def listmachpol(self, rootpath): return os.path.join(rootpath, "Machine/Registry.pol") @@ -381,11 +441,28 @@ class gp_sec_ext(gp_ext): return os.path.join(rootpath, "User/Registry.pol") def apply_map(self): - return {"System Access": {"MinimumPasswordAge": ("minPwdAge", inf_to_ldb), - "MaximumPasswordAge": ("maxPwdAge", inf_to_ldb), - "MinimumPasswordLength": ("minPwdLength", inf_to_ldb), - "PasswordComplexity": ("pwdProperties", inf_to_ldb), - } + return {"System Access": {"MinimumPasswordAge": ("minPwdAge", + inf_to_ldb), + "MaximumPasswordAge": ("maxPwdAge", + inf_to_ldb), + "MinimumPasswordLength": ("minPwdLength", + inf_to_ldb), + "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 read_inf(self, path, conn): @@ -417,7 +494,8 @@ class gp_sec_ext(gp_ext): (att, setter) = current_section.get(key) value = value.encode('ascii', 'ignore') ret = True - setter(self.logger, self.ldb, self.gp_db, self.lp, att, value).update_samba() + setter(self.logger, self.ldb, self.gp_db, self.lp, att, + value).update_samba() self.gp_db.commit() return ret @@ -431,8 +509,10 @@ class gp_sec_ext(gp_ext): try: blist = afile.split('/') idx = afile.lower().split('/').index('machine') - for case in [blist[idx].upper(), blist[idx].capitalize(), blist[idx].lower()]: - bfile = '/'.join(blist[:idx]) + '/' + case + '/' + '/'.join(blist[idx+1:]) + for case in [blist[idx].upper(), blist[idx].capitalize(), + blist[idx].lower()]: + bfile = '/'.join(blist[:idx]) + '/' + case + '/' + \ + '/'.join(blist[idx+1:]) try: return self.read_inf(bfile, conn) except NTSTATUSError: