def s4_srv_record_create(s4connector, object): _d=ud.function('s4_srv_record_create') dnsRecords=[] zoneDn, zoneName=__create_default_s4_zone_dn(s4connector, object) relativeDomainName=object['attributes'].get('relativeDomainName') relativeDomainName=univention.s4connector.s4.compatible_list(relativeDomainName) # ucr set connector/s4/mapping/dns/srv_record/_ldap._tcp.test.local/location='100 0 389 foobar.test.local.' # ucr set connector/s4/mapping/dns/srv_record/_ldap._tcp.test.local/location='100 0 389 foobar.test.local. 100 0 389 foobar2.test.local.' ucr_locations = s4connector.configRegistry.get('connector/s4/mapping/dns/srv_record/%s.%s/location' % (relativeDomainName[0].lower(),zoneName[0].lower())) ud.debug(ud.LDAP, ud.INFO, 's4_srv_record_create: ucr_locations for connector/s4/mapping/dns/srv_record/%s.%s/location: %s' % (relativeDomainName[0].lower(),zoneName[0].lower(),ucr_locations)) if ucr_locations: if ucr_locations.lower() == 'ignore': return # Convert ucr variable priority=None; weight=None; port=None; target=None for v in ucr_locations.split(' '): # Check explicit for None, because the int values may be 0 if priority == None: priority=int(v) elif weight == None: weight=int(v) elif port == None: port=int(v) elif not target: target=__remove_dot(v) if priority != None and weight != None and port != None and target: ud.debug(ud.LDAP, ud.INFO, 'priority=%d weight=%d port=%d target=%s' % (priority,weight,port,target)) s=SRVRecord(target, port, priority, weight) dnsRecords.append(ndr_pack(s)) priority=None; weight=None; port=None; target=None else: __pack_sRVrecord(object, dnsRecords) dnsNodeDn=s4_dns_node_base_create(s4connector, object, dnsRecords)
def __create_s4_forward_zone(s4connector, zoneDn, zoneName): al=[] al.append(('objectClass', ['top', 'dnsZone'])) al.append(('DC', univention.s4connector.s4.compatible_list(zoneName))) ud.debug(ud.LDAP, ud.INFO, '_dns_zone_forward_con_create: dn: %s' % zoneDn) ud.debug(ud.LDAP, ud.INFO, '_dns_zone_forward_con_create: al: %s' % al) s4connector.lo_s4.lo.add_s(zoneDn, al)
def con2ucs (s4connector, key, object): _d=ud.function('dns: con2ucs') ud.debug(ud.LDAP, ud.INFO, 'dc con2ucs: Object (%s): %s' % (object['dn'], object)) # Search sambaDomainname object via sambaSID sambadomainnameObject = univention.admin.handlers.settings.sambadomain.lookup(None, s4connector.lo, 'sambaSID=%s' % object['attributes'].get('objectSid', [])[0]) if len(sambadomainnameObject) > 1: ud.debug(ud.LDAP, ud.WARN, 'dc con2ucs: Found more than one sambaDomainname object with sambaSID %s' % object['attributes'].get('objectSid', [])[0]) elif len(sambadomainnameObject) == 1: # Use the first sambaDomain sambadomainnameObject = sambadomainnameObject[0] # Do we modify this UCS object modify = False sync_times = [ ('maxPasswordAge', 'maxPwdAge'), ('minPasswordAge', 'minPwdAge'), ('lockoutDuration', 'lockoutDuration') ] for (ucs_attr, s4_attr) in sync_times: ucs_time = _unixTimeInverval2seconds(sambadomainnameObject.get(ucs_attr, 0)) s4_time = _nano2s(long(object['attributes'].get(s4_attr, [0])[0]) * -1) if ucs_time != s4_time: sambadomainnameObject[ucs_attr] = [str(s4_time), 'seconds'] modify = True sync_integers = [ ('passwordHistory', 'pwdHistoryLength'), ('passwordLength', 'minPwdLength') ] for (ucs_attr, s4_attr) in sync_integers: ucs_val = sambadomainnameObject.get(ucs_attr, 0) s4_val = object['attributes'].get(s4_attr, [None])[0] if ucs_val != s4_val: sambadomainnameObject[ucs_attr] = s4_val modify = True if modify: sambadomainnameObject.modify() if s4connector.configRegistry.is_true('connector/s4/mapping/gpo', True): # Search DC object via ldap search dn,attr = s4connector.lo.search('objectClass=*', scope='base')[0] ml = [] ucs_val = attr.get('msGPOLink') s4_val = object['attributes'].get('gPLink') if ucs_val != s4_val: if not 'msGPO' in attr.get('objectClass', []): ml.append(('objectClass', '', 'msGPO')) ml.append(('msGPOLink', ucs_val, s4_val)) if ml: s4connector.lo.modify(dn, ml) return True
def __pack_mxRecord(object, dnsRecords): for mXRecord in object['attributes'].get('mXRecord', []): if mXRecord: ud.debug(ud.LDAP, ud.INFO, '__pack_mxRecord: %s' % mXRecord) mXRecord=univention.s4connector.s4.compatible_modstring(mXRecord) mx=mXRecord.split(' ') priority=mx[0] name=mx[1] mx_record=MXRecord(name, int(priority)) dnsRecords.append(ndr_pack(mx_record)) ud.debug(ud.LDAP, ud.INFO, '__pack_mxRecord: %s' % ndr_pack(mx_record))
def ucs_host_record_create(s4connector, object): _d=ud.function('ucs_host_record_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_host_record_create: object: %s' % object) zoneName, relativeDomainName=__split_s4_dn(object['dn']) # unpack the host record a=__unpack_aRecord(object) # Does a host record for this zone already exist? searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: superordinate=s4connector_get_superordinate('dns/host_record', s4connector.lo, searchResult[0][0]) newRecord= univention.admin.handlers.dns.host_record.object(None, s4connector.lo, position=None, dn=searchResult[0][0], superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() if set(newRecord['a']) != set(a): newRecord['a']=a newRecord.modify() else: ud.debug(ud.LDAP, ud.INFO, 'ucs_host_record_create: do not modify host record') else: zoneDN='zoneName=%s,%s' % (zoneName, s4connector.property['dns'].ucs_default_dn) ud.debug(ud.LDAP, ud.INFO, 'ucs_host_record_create: zoneDN: %s' % zoneDN) superordinate=s4connector_get_superordinate('dns/host_record', s4connector.lo, zoneDN) ud.debug(ud.LDAP, ud.INFO, 'ucs_host_record_create: superordinate: %s' % superordinate) position=univention.admin.uldap.position(zoneDN) newRecord= univention.admin.handlers.dns.host_record.object(None, s4connector.lo, position, dn=None, superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() newRecord['name']=relativeDomainName newRecord['a']=a newRecord.create()
def ucs2con (s4connector, key, object): _d=ud.function('dns: ucs2con') dns_type=_identify_dns_ucs_object(s4connector, object) if not dns_type: # unknown object -> ignore ud.debug(ud.LDAP, ud.INFO, 'dns ucs2con: Ignore unkown dns object: %s' % object['dn']) return True ud.debug(ud.LDAP, ud.INFO, 'dns ucs2con: Object (%s) is from type %s' % (object['dn'], dns_type)) if dns_type == 'forward_zone' or dns_type == 'reverse_zone': if object['modtype'] in ['add', 'modify']: s4_zone_create(s4connector, object) elif object['modtype'] in ['delete']: s4_zone_delete(s4connector, object) # ignore move elif dns_type == 'host_record': if object['modtype'] in ['add', 'modify']: s4_host_record_create(s4connector, object) elif object['modtype'] in ['delete']: s4_dns_node_base_delete(s4connector, object) # ignore move elif dns_type == 'alias': if object['modtype'] in ['add', 'modify']: s4_cname_create(s4connector, object) elif object['modtype'] in ['delete']: s4_dns_node_base_delete(s4connector, object) # ignore move elif dns_type == 'srv_record': if object['modtype'] in ['add', 'modify']: s4_srv_record_create(s4connector, object) elif object['modtype'] in ['delete']: s4_dns_node_base_delete(s4connector, object) # ignore move elif dns_type == 'ptr_record': if object['modtype'] in ['add', 'modify']: s4_ptr_record_create(s4connector, object) elif object['modtype'] in ['delete']: s4_dns_node_base_delete(s4connector, object) # ignore move return True
def ucs_srv_record_delete(s4connector, object): _d=ud.function('ucs_srv_record_delete') ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_delete: object: %s' % object) zoneName, relativeDomainName=__split_s4_dn(object['dn']) searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: superordinate=s4connector_get_superordinate('dns/srv_record', s4connector.lo, searchResult[0][0]) newRecord= univention.admin.handlers.dns.srv_record.object(None, s4connector.lo, position=None, dn=searchResult[0][0], superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() newRecord.delete() else: ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_delete: Object was not found, filter was: ((&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName)) return True
def sid_to_s4(s4connector, key, object): ud.debug(ud.LDAP, ud.INFO, "sid_to_s4 object: %s" % object) sidAttribute='sambaSID' if s4connector.configRegistry.is_false('connector/s4/mapping/sid', False): ud.debug(ud.LDAP, ud.INFO, 'sid_to_s4: SID mapping is disabled via UCR: connector/s4/mapping/sid') sidAttribute='univentionSamba4SID' else: # This case will be handled by direct mapping return # object dn was already mapped to the s4 DN: s4_dn = object['dn'] modlist = [] # search the ucs object via if not object['attributes'].has_key(sidAttribute): ud.debug(ud.LDAP, ud.INFO, 'sid_to_s4: UCS object does not have a %s' % sidAttribute) return sambaSID = object['attributes'][sidAttribute] # get the ad sid (s4_dn, s4_attributes) = s4connector.lo_s4.lo.search_s(s4_dn, ldap.SCOPE_BASE, '(objectSid=*)', ['objectSid'] )[0] objectSid = s4_attributes.get('objectSid') if objectSid: # decoded_s4_sid = univention.s4connector.s4.decode_sid(objectSid[0]) s4_objectSid = ndr_unpack(security.dom_sid, objectSid[0]) decoded_s4_sid = str(s4_objectSid) if decoded_s4_sid == sambaSID[0]: ud.debug(ud.LDAP, ud.INFO, 'sid_to_s4: objectSid and %s are equal' % sidAttribute) return ### change objectSID # http://serverfault.com/questions/53717/how-can-i-change-the-sid-of-a-user-account-in-the-active-directory # http://technet.microsoft.com/en-us/library/cc961998.aspx ud.debug(ud.LDAP, ud.INFO, 'sid_to_s4: changing objectSid from %s to %s' % (decoded_s4_sid, sambaSID[0]) ) new_objectSid_ndr = ndr_pack(security.dom_sid(sambaSID[0])) modlist.append((ldap.MOD_REPLACE, 'objectSid', new_objectSid_ndr)) # objectSid modification for an Samba4 object is only possible with the "provision" control: LDB_CONTROL_PROVISION_OID = '1.3.6.1.4.1.7165.4.3.16' controls = [ LDAPControl(LDB_CONTROL_PROVISION_OID,criticality=0) ] s4connector.lo_s4.lo.modify_ext_s(s4_dn, modlist, serverctrls=controls) pass
def sid_to_s4_mapping(s4connector, key, object): ud.debug(ud.LDAP, ud.INFO, "sid_to_s4_mapping") sidAttribute='sambaSID' if s4connector.configRegistry.is_false('connector/s4/mapping/sid', False): ud.debug(ud.LDAP, ud.INFO, 'sid_to_s4: SID mapping is disabled via UCR: connector/s4/mapping/sid') sidAttribute='univentionSamba4SID' sambaSID = object['attributes'][sidAttribute] # Two diffrent cases are possible, the user sid contains the # domain sid or not. if sambaSID[0].startswith('S-'): new_objectSid_ndr = ndr_pack(security.dom_sid('%s' % (sambaSID[0]))) else: new_objectSid_ndr = ndr_pack(security.dom_sid('%s-%s' % (s4connector.s4_sid, sambaSID[0]))) return [new_objectSid_ndr]
def _unixTimeInverval2seconds(unixTime): if type(unixTime) != type([]): return unixTime if len(unixTime) != 2: ud.debug(ud.LDAP, ud.WARN, 'dc _unixTimeInverval2seconds: Not a valid time format: %s' % unixTime) return 0 if unixTime[1] == 'seconds': return unixTime[0] elif unixTime[1] == 'minutes': return unixTime[0] * 60 elif unixTime[1] == 'hours': return unixTime[0] * 3600 # 60 * 60 elif unixTime[1] == 'days': return unixTime[0] * 86400 # 60 * 60 * 24 else: ud.debug(ud.LDAP, ud.WARN, 'dc _unixTimeInverval2seconds: Not a valid time unit: %s' % unixTime) return 0
def con2ucs (s4connector, key, object): _d=ud.function('dns: con2ucs') ud.debug(ud.LDAP, ud.INFO, 'dns con2ucs: Object (%s): %s' % (object['dn'], object)) dns_type=_identify_dns_con_object(s4connector, object) if not dns_type: # unknown object -> ignore ud.debug(ud.LDAP, ud.INFO, 'dns con2ucs: Ignore unkown dns object: %s' % object['dn']) return True ud.debug(ud.LDAP, ud.INFO, 'dns con2ucs: Object (%s) is from type %s' % (object['dn'], dns_type)) if dns_type == 'host_record': if object['modtype'] in ['add', 'modify']: ucs_host_record_create(s4connector, object) elif object['modtype'] in ['delete']: ucs_host_record_delete(s4connector, object) # ignore move elif dns_type == 'ptr_record': if object['modtype'] in ['add', 'modify']: ucs_ptr_record_create(s4connector, object) elif object['modtype'] in ['delete']: ucs_ptr_record_create(s4connector, object) # ignore move elif dns_type == 'alias': if object['modtype'] in ['add', 'modify']: ucs_cname_create(s4connector, object) elif object['modtype'] in ['delete']: ucs_cname_create(s4connector, object) # ignore move elif dns_type == 'srv_record': if object['modtype'] in ['add', 'modify']: ucs_srv_record_create(s4connector, object) elif object['modtype'] in ['delete']: ucs_srv_record_delete(s4connector, object) # ignore move if dns_type in ['forward_zone', 'reverse_zone']: if object['modtype'] in ['add', 'modify']: ucs_zone_create(s4connector, object, dns_type) elif object['modtype'] in ['delete']: ucs_zone_delete(s4connector, object, dns_type) # ignore move return True
def password_sync_ucs_to_s4(s4connector, key, object): _d = ud.function('ldap.s4.password_sync_ucs_to_s4') ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4 called") modify = False old_ucs_object = object.get('old_ucs_object', {}) new_ucs_object = object.get('new_ucs_object', {}) if old_ucs_object or new_ucs_object: for attr in ['sambaLMPassword', 'sambaNTPassword', 'sambaPwdLastSet', 'sambaPwdMustChange', 'krb5PrincipalName', 'krb5Key', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd', 'univentionService']: old_values = set(old_ucs_object.get(attr, [])) new_values = set(new_ucs_object.get(attr, [])) if old_values != new_values: modify = True break else: # add mode modify = True if not modify: ud.debug(ud.LDAP, ud.INFO, 'password_sync_ucs_to_s4: the password for %s has not been changed. Skipping password sync.' % (object['dn'])) return try: ud.debug(ud.LDAP, ud.INFO, "Object DN=%s" % object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, "Object DN not printable") ucs_object = s4connector._object_mapping(key, object, 'con') try: ud.debug(ud.LDAP, ud.INFO, " UCS DN = %s" % ucs_object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, " UCS DN not printable") try: ucs_object_attributes = s4connector.lo.get(ucs_object['dn'], ['sambaLMPassword', 'sambaNTPassword', 'sambaPwdLastSet', 'sambaPwdMustChange', 'krb5PrincipalName', 'krb5Key', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd', 'univentionService'], required=True) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.PROCESS, "password_sync_ucs_to_s4: The UCS object (%s) was not found. The object was removed." % ucs_object['dn']) return services = ucs_object_attributes.get('univentionService', []) if 'Samba 4' in services: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: %s is a S4 server, skip password sync" % ucs_object['dn']) return sambaPwdLastSet = None if 'sambaPwdLastSet' in ucs_object_attributes: sambaPwdLastSet = int(ucs_object_attributes['sambaPwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdLastSet: %s" % sambaPwdLastSet) if 'sambaPwdMustChange' in ucs_object_attributes: sambaPwdMustChange = int(ucs_object_attributes['sambaPwdMustChange'][0]) ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs_to_s4: Ignoring sambaPwdMustChange: %s" % sambaPwdMustChange) ucsLMhash = ucs_object_attributes.get('sambaLMPassword', [None])[0] ucsNThash = ucs_object_attributes.get('sambaNTPassword', [None])[0] krb5Principal = ucs_object_attributes.get('krb5PrincipalName', [None])[0] krb5Key = ucs_object_attributes.get('krb5Key', []) if not ucsNThash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaNTPassword missing in UCS LDAP, trying krb5Key") ucsNThash = extract_NThash_from_krb5key(krb5Key) if not ucsNThash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get NT Password-Hash from UCS LDAP") # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Password-Hash from UCS: %s" % ucsNThash) s4_object_attributes = s4connector.lo_s4.get(compatible_modstring(object['dn']), ['pwdLastSet', 'objectSid']) pwdLastSet = None if 'pwdLastSet' in s4_object_attributes: pwdLastSet = int(s4_object_attributes['pwdLastSet'][0]) objectSid = univention.s4connector.s4.decode_sid(s4_object_attributes['objectSid'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet from S4 : %s" % pwdLastSet) # rid = None # if s4_object_attributes.has_key('objectSid'): # rid = str(univention.s4connector.s4.decode_sid(s4_object_attributes['objectSid'][0]).split('-')[-1]) pwd_set = False filter_expr = format_escaped('(objectSid={0!e})', objectSid) res = s4connector.lo_s4.search(filter=filter_expr, attr=['unicodePwd', 'userPrincipalName', 'supplementalCredentials', 'msDS-KeyVersionNumber', 'dBCSPwd']) s4_search_attributes = res[0][1] unicodePwd_attr = s4_search_attributes.get('unicodePwd', [None])[0] dBCSPwd_attr = s4_search_attributes.get('dBCSPwd', [None])[0] userPrincipalName_attr = s4_search_attributes.get('userPrincipalName', [None])[0] supplementalCredentials = s4_search_attributes.get('supplementalCredentials', [None])[0] msDS_KeyVersionNumber = s4_search_attributes.get('msDS-KeyVersionNumber', [0])[0] # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Password-Hash from S4: %s" % unicodePwd_attr) s4NThash = None if unicodePwd_attr: s4NThash = binascii.b2a_hex(unicodePwd_attr).upper() else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get NT Password-Hash from S4") s4LMhash = None if dBCSPwd_attr: s4LMhash = binascii.b2a_hex(dBCSPwd_attr).upper() else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get LM Password-Hash from S4") modlist = [] if krb5Principal != userPrincipalName_attr: if krb5Principal: if not userPrincipalName_attr: # new and not old modlist.append((ldap.MOD_ADD, 'userPrincipalName', krb5Principal)) else: # new and old differ if krb5Principal.lower() != userPrincipalName_attr.lower(): ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs_to_s4: userPrincipalName != krb5Principal: '%s' != '%s'" % (userPrincipalName_attr, krb5Principal)) modlist.append((ldap.MOD_REPLACE, 'userPrincipalName', krb5Principal)) else: if userPrincipalName_attr: # old and not new modlist.append((ldap.MOD_DELETE, 'userPrincipalName', userPrincipalName_attr)) if not ucsNThash == s4NThash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: NT Hash S4: %s NT Hash UCS: %s" % (s4NThash, ucsNThash)) # Now if ucsNThash is empty there should at least some timestamp in UCS, # otherwise it's probably not a good idea to remove the unicodePwd. # Usecase: LDB module on ucs_3.0-0-ucsschool slaves creates XP computers/windows in UDM without password if ucsNThash or sambaPwdLastSet: pwd_set = True unicodePwd_new = None if ucsNThash: try: unicodePwd_new = binascii.a2b_hex(ucsNThash) except TypeError as exc: if ucsNThash.startswith("NO PASSWORD"): pwd_set = False else: raise if pwd_set and unicodePwd_new: modlist.append((ldap.MOD_REPLACE, 'unicodePwd', unicodePwd_new)) if not ucsLMhash == s4LMhash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: LM Hash S4: %s LM Hash UCS: %s" % (s4LMhash, ucsLMhash)) pwd_set = True if ucsLMhash: dBCSPwd_new = binascii.a2b_hex(ucsLMhash) modlist.append((ldap.MOD_REPLACE, 'dBCSPwd', dBCSPwd_new)) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: dBCSPwd should be removed in Samba 4 which is no longer possible, see Bug https://forge.univention.org/bugzilla/show_bug.cgi?id=49905") # modlist.append((ldap.MOD_DELETE, 'dBCSPwd', dBCSPwd_attr)) if pwd_set or not supplementalCredentials: if krb5Principal: # encoding of Samba4 supplementalCredentials if krb5Key: supplementalCredentials_new = calculate_supplementalCredentials(krb5Key, supplementalCredentials) if supplementalCredentials_new: modlist.append((ldap.MOD_REPLACE, 'supplementalCredentials', supplementalCredentials_new)) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: no supplementalCredentials_new") # if supplementalCredentials: # modlist.append((ldap.MOD_REPLACE, 'msDS-KeyVersionNumber', krb5KeyVersionNumber)) # else: # modlist.append((ldap.MOD_ADD, 'msDS-KeyVersionNumber', krb5KeyVersionNumber)) if sambaPwdLastSet is None: sambaPwdLastSet = int(time.time()) newpwdlastset = str(univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) elif sambaPwdLastSet in [0, 1]: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: samba pwd expired, set newpwdLastSet to 0") newpwdlastset = 0 else: newpwdlastset = univention.s4connector.s4.samba2s4_time(sambaPwdLastSet) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet in modlist: %s" % newpwdlastset) modlist.append((ldap.MOD_REPLACE, 'pwdLastSet', str(newpwdlastset))) modlist.append((ldap.MOD_REPLACE, 'badPwdCount', '0')) modlist.append((ldap.MOD_REPLACE, 'badPasswordTime', '0')) modlist.append((ldap.MOD_REPLACE, 'lockoutTime', '0')) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: No password change to sync to S4 ") # check pwdLastSet if sambaPwdLastSet is not None: if sambaPwdLastSet in [0, 1]: newpwdlastset = 0 else: newpwdlastset = univention.s4connector.s4.samba2s4_time(sambaPwdLastSet) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdLastSet: %d" % sambaPwdLastSet) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: newpwdlastset : %s" % newpwdlastset) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet (AD): %s" % pwdLastSet) if newpwdlastset != pwdLastSet and abs(newpwdlastset - pwdLastSet) >= 10000000: modlist.append((ldap.MOD_REPLACE, 'pwdLastSet', str(newpwdlastset))) # TODO: Password History ctrl_bypass_password_hash = LDAPControl('1.3.6.1.4.1.7165.4.3.12', criticality=0) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: modlist: %s" % modlist) if modlist: s4connector.lo_s4.lo.modify_ext_s(compatible_modstring(object['dn']), modlist, serverctrls=[ctrl_bypass_password_hash])
def password_sync_ucs_to_s4(s4connector, key, object): _d=ud.function('ldap.s4.password_sync_ucs_to_s4') ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4 called") compatible_modstring = univention.s4connector.s4.compatible_modstring try: ud.debug(ud.LDAP, ud.INFO, "Object DN=%s" % object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, "Object DN not printable") ucs_object = s4connector._object_mapping(key, object, 'con') try: ud.debug(ud.LDAP, ud.INFO, " UCS DN = %s" % ucs_object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, " UCS DN not printable") try: res = s4connector.lo.lo.search(base=ucs_object['dn'], scope='base', attr=['sambaLMPassword', 'sambaNTPassword','sambaPwdLastSet','sambaPwdMustChange', 'krb5PrincipalName', 'krb5Key', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd', 'univentionService']) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.PROCESS, "password_sync_ucs_to_s4: The UCS object (%s) was not found. The object was removed." % ucs_object['dn']) return services=res[0][1].get('univentionService', []) if 'Samba 4' in services: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: %s is a S4 server, skip password sync" % ucs_object['dn']) return sambaPwdLastSet = None if res[0][1].has_key('sambaPwdLastSet'): sambaPwdLastSet = long(res[0][1]['sambaPwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdLastSet: %s" % sambaPwdLastSet) sambaPwdMustChange = -1 if res[0][1].has_key('sambaPwdMustChange'): sambaPwdMustChange = long(res[0][1]['sambaPwdMustChange'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdMustChange: %s" % sambaPwdMustChange) ucsLMhash = res[0][1].get('sambaLMPassword', [None])[0] ucsNThash = res[0][1].get('sambaNTPassword', [None])[0] krb5Principal = res[0][1].get('krb5PrincipalName', [None])[0] krb5Key = res[0][1].get('krb5Key', []) if not ucsNThash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaNTPassword missing in UCS LDAP, trying krb5Key") ucsNThash = extract_NThash_from_krb5key(krb5Key) if not ucsNThash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get NT Password-Hash from UCS LDAP") # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Password-Hash from UCS: %s" % ucsNThash) res=s4connector.lo_s4.lo.search_s(univention.s4connector.s4.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)',['pwdLastSet','objectSid']) pwdLastSet = None if res[0][1].has_key('pwdLastSet'): pwdLastSet = long(res[0][1]['pwdLastSet'][0]) objectSid = univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet from S4 : %s" % pwdLastSet) # rid = None # if res[0][1].has_key('objectSid'): # rid = str(univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]).split('-')[-1]) pwd_set = False res=s4connector.lo_s4.lo.search_s(s4connector.lo_s4.base, ldap.SCOPE_SUBTREE, compatible_modstring('(objectSid=%s)' % objectSid), ['unicodePwd', 'userPrincipalName', 'supplementalCredentials', 'msDS-KeyVersionNumber', 'dBCSPwd']) unicodePwd_attr = res[0][1].get('unicodePwd', [None])[0] dBCSPwd_attr = res[0][1].get('dBCSPwd', [None])[0] userPrincipalName_attr = res[0][1].get('userPrincipalName', [None])[0] supplementalCredentials = res[0][1].get('supplementalCredentials', [None])[0] msDS_KeyVersionNumber = res[0][1].get('msDS-KeyVersionNumber', [0])[0] # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Password-Hash from S4: %s" % unicodePwd_attr) s4NThash = None if unicodePwd_attr: s4NThash = binascii.b2a_hex(unicodePwd_attr).upper() else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get NT Password-Hash from S4") s4LMhash = None if dBCSPwd_attr: s4LMhash = binascii.b2a_hex(dBCSPwd_attr).upper() else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get LM Password-Hash from S4") modlist=[] if krb5Principal != userPrincipalName_attr: if krb5Principal: if not userPrincipalName_attr: ## new and not old modlist.append((ldap.MOD_ADD, 'userPrincipalName', krb5Principal)) else: ## new and old differ if krb5Principal.lower() != userPrincipalName_attr.lower(): ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs_to_s4: userPrincipalName != krb5Principal: '%s' != '%s'" % (userPrincipalName_attr, krb5Principal)) modlist.append((ldap.MOD_REPLACE, 'userPrincipalName', krb5Principal)) else: if userPrincipalName_attr: ## old and not new modlist.append((ldap.MOD_DELETE, 'userPrincipalName', userPrincipalName_attr)) if not ucsNThash == s4NThash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: NT Hash S4: %s NT Hash UCS: %s" % (s4NThash, ucsNThash)) ## Now if ucsNThash is empty there should at least some timestamp in UCS, ## otherwise it's probably not a good idea to remove the unicodePwd. ## Usecase: LDB module on ucs_3.0-0-ucsschool slaves creates XP computers/windows in UDM without password if ucsNThash or sambaPwdLastSet: pwd_set = True if unicodePwd_attr: modlist.append((ldap.MOD_DELETE, 'unicodePwd', unicodePwd_attr)) if ucsNThash: unicodePwd_new = binascii.a2b_hex(ucsNThash) modlist.append((ldap.MOD_ADD, 'unicodePwd', unicodePwd_new)) if not ucsLMhash == s4LMhash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: LM Hash S4: %s LM Hash UCS: %s" % (s4LMhash, ucsLMhash)) pwd_set = True if dBCSPwd_attr: modlist.append((ldap.MOD_DELETE, 'dBCSPwd', dBCSPwd_attr)) if ucsLMhash: dBCSPwd_new = binascii.a2b_hex(ucsLMhash) modlist.append((ldap.MOD_ADD, 'dBCSPwd', dBCSPwd_new)) if pwd_set or not supplementalCredentials: if krb5Principal: ## encoding of Samba4 supplementalCredentials if supplementalCredentials: modlist.append((ldap.MOD_DELETE, 'supplementalCredentials', supplementalCredentials)) if krb5Key: supplementalCredentials_new = calculate_supplementalCredentials(krb5Key, supplementalCredentials) if supplementalCredentials_new: modlist.append((ldap.MOD_ADD, 'supplementalCredentials', supplementalCredentials_new)) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: no supplementalCredentials_new") #if supplementalCredentials: # modlist.append((ldap.MOD_REPLACE, 'msDS-KeyVersionNumber', krb5KeyVersionNumber)) #else: # modlist.append((ldap.MOD_ADD, 'msDS-KeyVersionNumber', krb5KeyVersionNumber)) if sambaPwdMustChange >= 0 and sambaPwdMustChange < time.time(): # password expired, must be changed on next login ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: samba pwd expired, set newpwdLastSet to 0") newpwdlastset = "0" else: if sambaPwdLastSet == None: sambaPwdLastSet = int(time.time()) newpwdlastset = str(univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) elif sambaPwdLastSet in [0, 1]: newpwdlastset = "0" else: newpwdlastset = str(univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdlastset in modlist: %s" % newpwdlastset) modlist.append((ldap.MOD_REPLACE, 'pwdlastset', newpwdlastset)) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: No password change to sync to S4 ") # check pwdLastSet if sambaPwdLastSet != None: newpwdlastset = str(univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdLastSet: %d" % sambaPwdLastSet) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: newpwdlastset : %s" % newpwdlastset) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet (AD): %s" % pwdLastSet) if sambaPwdLastSet in [0, 1]: modlist.append((ldap.MOD_REPLACE, 'pwdlastset', "0")) elif pwdLastSet != newpwdlastset: modlist.append((ldap.MOD_REPLACE, 'pwdlastset', newpwdlastset)) ## TODO: Password History ctrl_bypass_password_hash = LDAPControl('1.3.6.1.4.1.7165.4.3.12',criticality=0) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: modlist: %s" % modlist) if modlist: s4connector.lo_s4.lo.modify_ext_s(compatible_modstring(object['dn']), modlist, serverctrls=[ ctrl_bypass_password_hash ])
def test_debug_closed(): ud.debug(ud.MAIN, ud.ALL, "No crash") assert True
def sid_to_ucs_mapping(s4connector, key, s4_object): ud.debug(ud.LDAP, ud.INFO, "sid_to_ucs_mapping") object_sid=s4_object['attributes']['objectSid'][0] return object_sid.split('-')[-1]
# License with the Debian GNU/Linux or Univention distribution in file # /usr/share/common-licenses/AGPL-3; if not, see # <http://www.gnu.org/licenses/>. import univention.debug2 as ud ud.init('/tmp/univention.debug2.log', 1, 1) ud.set_level(ud.PROCESS, ud.ERROR) ud.set_level(ud.LISTENER, ud.WARN) ud.set_level(ud.NETWORK, ud.PROCESS) ud.set_level(ud.LDAP, ud.INFO) ud.set_level(ud.ADMIN, ud.ALL) for lvl in [ud.ERROR, ud.WARN, ud.PROCESS, ud.INFO, ud.ALL]: for mod in [ud.ADMIN, ud.PROCESS, ud.LISTENER, ud.NETWORK, ud.LDAP]: ud.debug(mod, lvl, '==> send msg to %s with level %s' % (mod, lvl)) ud.set_level(ud.ADMIN, ud.ERROR) ud.debug(ud.ADMIN, ud.ERROR, '==> admin error') ud.debug(ud.ADMIN, ud.WARN, '==> admin warn') ud.debug(ud.ADMIN, ud.PROCESS, '==> admin process') ud.debug(ud.ADMIN, ud.INFO, '==> admin info') ud.debug(ud.ADMIN, ud.ALL, '==> admin all') ud.set_level(ud.LDAP, ud.INFO) ud.debug(ud.LDAP, ud.ERROR, '==> ldap error') ud.debug(ud.LDAP, ud.WARN, '==> ldap warn') ud.debug(ud.LDAP, ud.PROCESS, '==> ldap process') ud.debug(ud.LDAP, ud.INFO, '==> ldap info') ud.debug(ud.LDAP, ud.ALL, '==> ldap all')
def lockout_sync_s4_to_ucs(s4connector, key, ucs_object): """ Sync account locking *state* from Samba/AD to OpenLDAP: sync Samba/AD (lockoutTime != 0) -> OpenLDAP sambaAcctFlags ("L") and Samba/AD badPasswordTime -> OpenLDAP sambaBadPasswordTime """ function_name = 'lockout_sync_s4_to_ucs' _d = ud.function('ldap.s4.%s' % function_name) ud.debug(ud.LDAP, ud.INFO, "%s called" % function_name) if ucs_object['modtype'] not in ('modify', 'add'): return modlist = [] try: ucs_object_attributes = s4connector.lo.get(ucs_object['dn'], ['sambaAcctFlags', 'sambaBadPasswordTime'], required=True) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.WARN, "%s: The UCS object (%s) was not found. The object was removed." % (function_name, ucs_object['dn'])) return sambaAcctFlags = ucs_object_attributes.get('sambaAcctFlags', [''])[0] sambaBadPasswordTime = ucs_object_attributes.get('sambaBadPasswordTime', ["0"])[0] lockoutTime = ucs_object['attributes'].get('lockoutTime', ['0'])[0] if lockoutTime != "0": if "L" not in sambaAcctFlags: acctFlags = univention.admin.samba.acctFlags(sambaAcctFlags) new_sambaAcctFlags = acctFlags.set('L') ud.debug(ud.LDAP, ud.PROCESS, "%s: Marking Samba account as locked in OpenLDAP" % (function_name,)) modlist.append(('sambaAcctFlags', sambaAcctFlags, new_sambaAcctFlags)) badPasswordTime = ucs_object['attributes'].get('badPasswordTime', ["0"])[0] if badPasswordTime != sambaBadPasswordTime: ud.debug(ud.LDAP, ud.PROCESS, "%s: Copying badPasswordTime from S4: %s" % (function_name, badPasswordTime)) if sambaBadPasswordTime: ud.debug(ud.LDAP, ud.INFO, "%s: Old sambaBadPasswordTime: %s" % (function_name, sambaBadPasswordTime)) modlist.append(('sambaBadPasswordTime', sambaBadPasswordTime, badPasswordTime)) else: if "L" in sambaAcctFlags: acctFlags = univention.admin.samba.acctFlags(sambaAcctFlags) new_sambaAcctFlags = acctFlags.unset('L') ud.debug(ud.LDAP, ud.PROCESS, "%s: Marking Samba account as unlocked in OpenLDAP" % (function_name,)) modlist.append(('sambaAcctFlags', sambaAcctFlags, new_sambaAcctFlags)) if sambaBadPasswordTime and sambaBadPasswordTime != "0": ud.debug(ud.LDAP, ud.PROCESS, "%s: Unsetting sambaBadPasswordTime: %s" % (function_name, sambaBadPasswordTime)) modlist.append(('sambaBadPasswordTime', sambaBadPasswordTime, "0")) if modlist: ud.debug(ud.LDAP, ud.ALL, "%s: modlist: %s" % (function_name, modlist)) s4connector.lo.lo.modify(ucs_object['dn'], modlist)
def sid_to_ucs(s4connector, key, s4_object): ud.debug(ud.LDAP, ud.INFO, "sid_to_ucs S4 object: %s" % s4_object) ud.debug(ud.LDAP, ud.INFO, "sid_to_ucs S4 key: %s" % key) sidAttribute='sambaSID' if s4connector.configRegistry.is_false('connector/s4/mapping/sid', False): ud.debug(ud.LDAP, ud.INFO, 'sid_to_ucs: SID mapping is disabled via UCR: connector/s4/mapping/sid') sidAttribute='univentionSamba4SID' else: # This case will be handled by direct mapping return # modlist ml = [] # object dn is already mapped to the UCS DN: if not s4_object.get('dn'): return # ignore ucs_dn = s4_object['dn'] ud.debug(ud.LDAP, ud.INFO, "sid_to_s4: UCS DN %s" % ucs_dn) if s4_object.has_key('attributes') and s4_object['attributes'].has_key('objectSid'): ud.debug(ud.LDAP, ud.INFO, 'sid_to_ucs: objectSid found: %s' % s4_object['attributes']['objectSid']) else: ud.debug(ud.LDAP, ud.INFO, 'sid_to_ucs: objectSid not found in attributes!') return (ucs_dn, ucs_attributes) = s4connector.lo.lo.search(base=ucs_dn, scope='base', attr=[sidAttribute, 'objectClass'])[0] if not ucs_dn: ud.debug(ud.LDAP, ud.WARN, 'sid_to_ucs: UCS object (%s) not found' % ucs_dn) return objectSid = s4_object['attributes'].get('objectSid') sambaSID = ucs_attributes.get(sidAttribute) if not sambaSID or objectSid != sambaSID: ml.append((sidAttribute, sambaSID, s4_object['attributes'].get('objectSid'))) if 'user' in s4_object['attributes'].get('objectClass', []): if not 'sambaSamAccount' in ucs_attributes.get('objectClass'): ml.append(('objectClass',ucs_attributes.get('objectClass'), ucs_attributes.get('objectClass')+['sambaSamAccount'])) if 'group' in s4_object['attributes'].get('objectClass', []): if not 'sambaGroupMapping' in ucs_attributes.get('objectClass'): ml.append(('objectClass',ucs_attributes.get('objectClass'), ucs_attributes.get('objectClass')+['sambaGroupMapping'])) if ml: ud.debug(ud.LDAP, ud.INFO, 'sid_to_ucs: modlist = %s' % ml) s4connector.lo.lo.modify(ucs_dn, ml) return
def password_sync_ucs(connector, key, object): _d=ud.function('ldap.ad.password_sync_ucs') # externes Programm zum Überptragen des Hash aufrufen # per ldapmodify pwdlastset auf -1 setzen compatible_modstring = univention.connector.ad.compatible_modstring try: ud.debug(ud.LDAP, ud.INFO, "Object DN=%s" % object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, "Object DN not printable") ucs_object = connector._object_mapping(key, object, 'con') try: ud.debug(ud.LDAP, ud.INFO, " UCS DN = %s" % ucs_object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, " UCS DN not printable") try: res = connector.lo.lo.search(base=ucs_object['dn'], scope='base', attr=['sambaLMPassword', 'sambaNTPassword','sambaPwdLastSet']) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.PROCESS, "password_sync_ucs: The UCS object (%s) was not found. The object was removed." % ucs_object['dn']) return sambaPwdLastSet = None if res[0][1].has_key('sambaPwdLastSet'): sambaPwdLastSet = long(res[0][1]['sambaPwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: sambaPwdLastSet: %s" % sambaPwdLastSet) pwd = None if res[0][1].has_key('sambaNTPassword'): pwd=res[0][1]['sambaNTPassword'][0] else: pwd='NO PASSWORDXXXXXX' ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs: Failed to get NT Hash from UCS") if res[0][1].has_key('sambaLMPassword'): pwd+=res[0][1]['sambaLMPassword'][0] else: pwd+='NO PASSWORDXXXXX' ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs: Failed to get LM Hash from UCS") res=connector.lo_ad.lo.search_s(univention.connector.ad.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)',['pwdLastSet','objectSid']) pwdLastSet = None if res[0][1].has_key('pwdLastSet'): pwdLastSet = long(res[0][1]['pwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: pwdLastSet from AD : %s" % pwdLastSet) rid = None if res[0][1].has_key('objectSid'): rid = str(univention.connector.ad.decode_sid(res[0][1]['objectSid'][0]).split('-')[-1]) # Only sync passwords from UCS to AD when the password timestamp in UCS is newer if connector.baseConfig.is_true('%s/ad/password/timestamp/check' % connector.CONFIGBASENAME, False): ad_password_last_set = 0 # If sambaPwdLast was set to 1 the password must be changed on next login. In this # case the timestamp is ignored and the password will be synced. This behaviour can # be disbled by setting connector/ad/password/timestamp/syncreset/ucs to false. This # might be necessary if the connector is configured in read mode and the password will be # synced in two ways: Bug #22653 if sambaPwdLastSet > 1 or ( sambaPwdLastSet <= 2 and connector.baseConfig.is_false('%s/ad/password/timestamp/syncreset/ucs' % connector.CONFIGBASENAME, False)): ad_password_last_set = univention.connector.ad.ad2samba_time(pwdLastSet) if sambaPwdLastSet: if long(ad_password_last_set) >= long(sambaPwdLastSet): # skip ud.debug(ud.LDAP, ud.PROCESS, "password_sync: Don't sync the password from UCS to AD because the AD password equal or is newer.") ud.debug(ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) return ud.debug(ud.LDAP, ud.INFO, "password_sync: Sync the passwords from UCS to AD.") ud.debug(ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) pwd_set = False pwd_ad_res = get_password_from_ad(connector, rid) pwd_ad = '' if len(pwd_ad_res) >3 and _get_integer(pwd_ad_res[4:]) == 0: pwd_ad = pwd_ad_res[12:].split(':')[1].strip().upper() else: ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs: Failed to get Password-Hash from AD") res = '' ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: Hash AD: %s Hash UCS: %s"%(pwd_ad,pwd)) if not pwd == pwd_ad: pwd_set = True res = set_password_in_ad(connector, object['attributes']['sAMAccountName'][0], pwd) if not pwd_set or len(res) >3 and _get_integer(res[4:]) == 0 : newpwdlastset = "-1" # if pwd was set in ad we need to set pwdlastset to -1 or it will be 0 #if sambaPwdMustChange >= 0 and sambaPwdMustChange < time.time(): # # password expired, must be changed on next login # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: samba pwd expired, set newpwdLastSet to 0") # newpwdlastset = "0" if sambaPwdLastSet <= 1: newpwdlastset = "0" # User must change his password elif pwdLastSet and int(pwdLastSet) > 0 and not pwd_set: newpwdlastset = "1" if long(newpwdlastset) != 1: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: pwdlastset in modlist: %s" % newpwdlastset) connector.lo_ad.lo.modify_s(compatible_modstring(object['dn']), [(ldap.MOD_REPLACE, 'pwdlastset', newpwdlastset)]) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: don't modify pwdlastset") else: ud.debug(ud.LDAP, ud.ERROR, "password_sync_ucs: Failed to sync Password from AD ")
def password_sync_s4_to_ucs(s4connector, key, ucs_object, modifyUserPassword=True): _d=ud.function('ldap.s4.password_sync_s4_to_ucs') ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs called") object=s4connector._object_mapping(key, ucs_object, 'ucs') res=s4connector.lo_s4.lo.search_s(univention.s4connector.s4.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)',['objectSid','pwdLastSet']) pwdLastSet = None if res[0][1].has_key('pwdLastSet'): pwdLastSet = long(res[0][1]['pwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: pwdLastSet from S4: %s (%s)" % (pwdLastSet,res)) objectSid = univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]) # rid = None # if res[0][1].has_key('objectSid'): # rid = str(univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]).split('-')[-1]) res=s4connector.lo_s4.lo.search_s(s4connector.lo_s4.base, ldap.SCOPE_SUBTREE, univention.s4connector.s4.compatible_modstring('(objectSid=%s)' % objectSid), ['unicodePwd', 'supplementalCredentials', 'msDS-KeyVersionNumber', 'dBCSPwd']) unicodePwd_attr = res[0][1].get('unicodePwd', [None])[0] if unicodePwd_attr: ntPwd = binascii.b2a_hex(unicodePwd_attr).upper() lmPwd = '' dBCSPwd = res[0][1].get('dBCSPwd', [None])[0] if dBCSPwd: lmPwd = binascii.b2a_hex(dBCSPwd).upper() supplementalCredentials = res[0][1].get('supplementalCredentials', [None])[0] msDS_KeyVersionNumber = res[0][1].get('msDS-KeyVersionNumber', [0])[0] ntPwd_ucs = '' lmPwd_ucs = '' krb5Principal = '' userPassword = '' modlist=[] res=s4connector.lo.search(base=ucs_object['dn'], attr=['sambaPwdMustChange', 'sambaPwdLastSet','sambaNTPassword', 'sambaLMPassword', 'krb5PrincipalName', 'krb5Key', 'krb5KeyVersionNumber', 'userPassword', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd']) if res[0][1].has_key('sambaNTPassword'): ntPwd_ucs = res[0][1]['sambaNTPassword'][0] if res[0][1].has_key('sambaLMPassword'): lmPwd_ucs = res[0][1]['sambaLMPassword'][0] if res[0][1].has_key('krb5PrincipalName'): krb5Principal=res[0][1]['krb5PrincipalName'][0] if res[0][1].has_key('userPassword'): userPassword=res[0][1]['userPassword'][0] sambaPwdLastSet = None if res[0][1].has_key('sambaPwdLastSet'): sambaPwdLastSet=res[0][1]['sambaPwdLastSet'][0] ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet: %s" % sambaPwdLastSet) sambaPwdMustChange = '' if res[0][1].has_key('sambaPwdMustChange'): sambaPwdMustChange=res[0][1]['sambaPwdMustChange'][0] ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdMustChange: %s" % sambaPwdMustChange) krb5Key_ucs=res[0][1].get('krb5Key', []) userPassword_ucs=res[0][1].get('userPassword', [None])[0] krb5KeyVersionNumber=res[0][1].get('krb5KeyVersionNumber', [None])[0] pwd_changed = False if ntPwd != ntPwd_ucs: pwd_changed = True modlist.append(('sambaNTPassword', ntPwd_ucs, str(ntPwd))) if lmPwd != lmPwd_ucs: pwd_changed = True modlist.append(('sambaLMPassword', lmPwd_ucs, str(lmPwd))) if pwd_changed: if krb5Principal: ## decoding of Samba4 supplementalCredentials krb5Key_new = calculate_krb5key(unicodePwd_attr, supplementalCredentials, int(msDS_KeyVersionNumber)) modlist.append(('krb5Key', krb5Key_ucs, krb5Key_new)) if int(msDS_KeyVersionNumber) != int(krb5KeyVersionNumber): modlist.append(('krb5KeyVersionNumber', krb5KeyVersionNumber, msDS_KeyVersionNumber)) ## Append modification as well to modlist, to apply in one transaction if modifyUserPassword: modlist.append(('userPassword', userPassword_ucs, '{K5KEY}')) # Remove the POSIX and Kerberos password expiry interval if res[0][1].has_key('shadowLastChange'): modlist.append(('shadowLastChange', res[0][1]['shadowLastChange'][0], None)) if res[0][1].has_key('shadowMax'): modlist.append(('shadowMax', res[0][1]['shadowMax'][0], None)) if res[0][1].has_key('krb5PasswordEnd'): modlist.append(('krb5PasswordEnd', res[0][1]['krb5PasswordEnd'][0], None)) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: No password change to sync to UCS") if pwd_changed and (pwdLastSet or pwdLastSet == 0): newSambaPwdMustChange = sambaPwdMustChange if pwdLastSet == 0: # pwd change on next login newSambaPwdMustChange = str(pwdLastSet) newSambaPwdLastSet = str(pwdLastSet) else: newSambaPwdLastSet = str(univention.s4connector.s4.s42samba_time(pwdLastSet)) userobject = s4connector.get_ucs_object('user', ucs_object['dn']) if not userobject: ud.debug(ud.LDAP, ud.ERROR, "password_sync_s4_to_ucs: couldn't get user-object from UCS") return False sambaPwdMustChange=sambaPwdMustChange.strip() if not sambaPwdMustChange.isdigit(): pass elif pwd_changed or (long(sambaPwdMustChange) < time.time() and not pwdLastSet == 0): pwhistoryPolicy = userobject.loadPolicyObject('policies/pwhistory') try: expiryInterval=int(pwhistoryPolicy['expiryInterval']) newSambaPwdMustChange = str(long(newSambaPwdLastSet)+(expiryInterval*3600*24) ) except: # FIXME: which exception is to be caught? # expiryInterval is empty or no legal int-string pwhistoryPolicy['expiryInterval']='' expiryInterval=-1 newSambaPwdMustChange = '' ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: pwhistoryPolicy: expiryInterval: %s" % expiryInterval) if sambaPwdLastSet: if sambaPwdLastSet != newSambaPwdLastSet: modlist.append(('sambaPwdLastSet', sambaPwdLastSet, newSambaPwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet in modlist (replace): %s" % newSambaPwdLastSet) else: modlist.append(('sambaPwdLastSet', '', newSambaPwdLastSet )) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet in modlist (set): %s" % newSambaPwdLastSet) if sambaPwdMustChange != newSambaPwdMustChange: # change if password has changed or "change pwd on next login" is not set # set sambaPwdMustChange regarding to the univention-policy if sambaPwdMustChange: modlist.append(('sambaPwdMustChange', sambaPwdMustChange, newSambaPwdMustChange)) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdMustChange in modlist (replace): %s" % newSambaPwdMustChange) else: modlist.append(('sambaPwdMustChange', '', newSambaPwdMustChange)) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdMustChange in modlist (set): %s" % newSambaPwdMustChange) if len(modlist)>0: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: modlist: %s" % modlist) s4connector.lo.lo.modify(ucs_object['dn'], modlist) else: ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs_s4_to_ucs: Failed to get Password-Hash from S4")
def ucs_srv_record_create(s4connector, object): _d = ud.function('ucs_srv_record_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: object: %s' % object) zoneName, relativeDomainName = __split_s4_dn(object['dn']) # unpack the host record srv = __unpack_sRVrecord(object) # ucr set connector/s4/mapping/dns/srv_record/_ldap._tcp.test.local/location='100 0 389 foobar.test.local. 100 0 389 foobar2.test.local.' ucr_locations = s4connector.configRegistry.get( 'connector/s4/mapping/dns/srv_record/%s.%s/location' % (relativeDomainName.lower(), zoneName.lower())) ud.debug( ud.LDAP, ud.INFO, 'ucs_srv_record_create: ucr_locations for connector/s4/mapping/dns/srv_record/%s.%s/location: %s' % (relativeDomainName.lower(), zoneName.lower(), ucr_locations)) if ucr_locations and ucr_locations.lower() == 'ignore': return # Does a host record for this zone already exist? searchResult = s4connector.lo.search( filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: superordinate = s4connector_get_superordinate('dns/srv_record', s4connector.lo, searchResult[0][0]) newRecord = univention.admin.handlers.dns.srv_record.object( None, s4connector.lo, position=None, dn=searchResult[0][0], superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() if ucr_locations: ud.debug( ud.LDAP, ud.INFO, 'ucs_srv_record_create: do not write SRV record back from S4 to UCS because location of SRV record have been overwritten by UCR' ) else: ud.debug( ud.LDAP, ud.INFO, 'ucs_srv_record_create: location: %s' % newRecord['location']) ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: srv : %s' % srv) srv.sort() newRecord['location'].sort() if srv != newRecord['location']: newRecord['location'] = srv newRecord.modify() else: ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: do not modify host record') else: zoneDN = 'zoneName=%s,%s' % ( zoneName, s4connector.property['dns'].ucs_default_dn) superordinate = s4connector_get_superordinate('dns/srv_record', s4connector.lo, zoneDN) ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: superordinate: %s' % superordinate) position = univention.admin.uldap.position(zoneDN) newRecord = univention.admin.handlers.dns.srv_record.object( None, s4connector.lo, position, dn=None, superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() # Make syntax UDM compatible service = string.join(relativeDomainName.split('.')[:-1], '.') if service.startswith('_'): service = service[1:] protocol = relativeDomainName.split('.')[-1] if protocol.startswith('_'): protocol = protocol[1:] ud.debug( ud.LDAP, ud.INFO, 'SRV create: service="%s" protocol="%s"' % (service, protocol)) newRecord['name'] = [service, protocol] newRecord['location'] = srv newRecord.create()
def password_sync(connector, key, ucs_object): _d=ud.function('ldap.ad.password_sync') # externes Programm zum holen des Hash aufrufen # "kerberos_now" object=connector._object_mapping(key, ucs_object, 'ucs') res=connector.lo_ad.lo.search_s(univention.connector.ad.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)',['objectSid','pwdLastSet']) pwdLastSet = None if res[0][1].has_key('pwdLastSet'): pwdLastSet = long(res[0][1]['pwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync: pwdLastSet from AD: %s (%s)" % (pwdLastSet,res)) rid = None if res[0][1].has_key('objectSid'): rid = str(univention.connector.ad.decode_sid(res[0][1]['objectSid'][0]).split('-')[-1]) ucs_result=connector.lo.search(base=ucs_object['dn'], attr=['sambaPwdLastSet','sambaNTPassword', 'sambaLMPassword', 'krb5PrincipalName', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd']) sambaPwdLastSet = None if ucs_result[0][1].has_key('sambaPwdLastSet'): sambaPwdLastSet=ucs_result[0][1]['sambaPwdLastSet'][0] ud.debug(ud.LDAP, ud.INFO, "password_sync: sambaPwdLastSet: %s" % sambaPwdLastSet) if connector.baseConfig.is_true('%s/ad/password/timestamp/check' % connector.CONFIGBASENAME, False): # Only sync the passwords from AD to UCS when the pwdLastSet timestamps in AD are newer ad_password_last_set = 0 # If pwdLastSet was set to 0 the password must be changed on next login. In this # case the timestamp is ignored and the password will be synced. This behaviour can # be disabled by setting connector/ad/password/timestamp/syncreset/ad to false. This # might be necessary if the connector is configured in read mode and the password will be # synced in two ways: Bug #22653 if (pwdLastSet > 1) or (pwdLastSet in [0,1] and connector.baseConfig.is_false('%s/ad/password/timestamp/syncreset/ad' % connector.CONFIGBASENAME, False)): ad_password_last_set = univention.connector.ad.ad2samba_time(pwdLastSet) if sambaPwdLastSet: if long(sambaPwdLastSet) >= long(ad_password_last_set) and long(sambaPwdLastSet) != 1: # skip ud.debug(ud.LDAP, ud.PROCESS, "password_sync: Don't sync the passwords from AD to UCS because the UCS password is equal or newer.") ud.debug(ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) return ud.debug(ud.LDAP, ud.INFO, "password_sync: Sync the passwords from AD to UCS.") ud.debug(ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) res = get_password_from_ad(connector, rid) if len(res) >3 and _get_integer(res[4:]) == 0: ntPwd_ucs = '' lmPwd_ucs = '' krb5Principal = '' userPassword = '' data = res[12:].split(':')[1].strip() ntPwd = data[:32] lmPwd = data[32:] modlist=[] if ucs_result[0][1].has_key('sambaLMPassword'): lmPwd_ucs = ucs_result[0][1]['sambaLMPassword'][0] if ucs_result[0][1].has_key('sambaNTPassword'): ntPwd_ucs = ucs_result[0][1]['sambaNTPassword'][0] if ucs_result[0][1].has_key('krb5PrincipalName'): krb5Principal=ucs_result[0][1]['krb5PrincipalName'][0] if ucs_result[0][1].has_key('userPassword'): userPassword=ucs_result[0][1]['userPassword'][0] pwd_changed = False if lmPwd.upper() != lmPwd_ucs.upper(): if not lmPwd in ['00000000000000000000000000000000', 'NO PASSWORD*********************']: pwd_changed = True modlist.append(('sambaLMPassword', lmPwd_ucs, str(lmPwd.upper()))) if ntPwd.upper() != ntPwd_ucs.upper(): if ntPwd in ['00000000000000000000000000000000', 'NO PASSWORD*********************']: ud.debug(ud.LDAP, ud.WARN, "password_sync: AD connector password daemon retured 0 for the nt hash. Please check the AD settings.") else: ud.debug(ud.LDAP, ud.WARN, "password_sync: %s" % ntPwd) pwd_changed = True modlist.append(('sambaNTPassword', ntPwd_ucs, str(ntPwd.upper()))) if krb5Principal: connector.lo.lo.lo.modify_s(univention.connector.ad.compatible_modstring(ucs_object['dn']), [(ldap.MOD_REPLACE, 'krb5Key', nt_password_to_arcfour_hmac_md5(ntPwd.upper()))]) if pwd_changed: connector.lo.lo.lo.modify_s(univention.connector.ad.compatible_modstring(ucs_object['dn']), [(ldap.MOD_REPLACE, 'userPassword', '{K5KEY}')]) # Remove the POSIX and Kerberos password expiry interval if ucs_result[0][1].has_key('shadowLastChange'): modlist.append(('shadowLastChange', ucs_result[0][1]['shadowLastChange'][0], None)) if ucs_result[0][1].has_key('shadowMax'): modlist.append(('shadowMax', ucs_result[0][1]['shadowMax'][0], None)) if ucs_result[0][1].has_key('krb5PasswordEnd'): modlist.append(('krb5PasswordEnd', ucs_result[0][1]['krb5PasswordEnd'][0], None)) if pwdLastSet or pwdLastSet == 0: newSambaPwdLastSet = str(univention.connector.ad.ad2samba_time(pwdLastSet)) if sambaPwdLastSet: if sambaPwdLastSet != newSambaPwdLastSet: modlist.append(('sambaPwdLastSet', sambaPwdLastSet, newSambaPwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: sambaPwdLastSet in modlist (replace): %s" % newSambaPwdLastSet) else: modlist.append(('sambaPwdLastSet', '', newSambaPwdLastSet )) ud.debug(ud.LDAP, ud.INFO, "password_sync: sambaPwdLastSet in modlist (set): %s" % newSambaPwdLastSet) if len(modlist)>0: connector.lo.lo.modify(ucs_object['dn'], modlist) else: ud.debug(ud.LDAP, ud.ERROR, "password_sync: sync failed, no result from AD" )
def ucs_srv_record_create(s4connector, object): _d=ud.function('ucs_srv_record_create') ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: object: %s' % object) zoneName, relativeDomainName=__split_s4_dn(object['dn']) # unpack the host record srv=__unpack_sRVrecord(object) # ucr set connector/s4/mapping/dns/srv_record/_ldap._tcp.test.local/location='100 0 389 foobar.test.local. 100 0 389 foobar2.test.local.' ucr_locations = s4connector.configRegistry.get('connector/s4/mapping/dns/srv_record/%s.%s/location' % (relativeDomainName.lower(),zoneName.lower())) ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: ucr_locations for connector/s4/mapping/dns/srv_record/%s.%s/location: %s' % (relativeDomainName.lower(),zoneName.lower(),ucr_locations)) if ucr_locations and ucr_locations.lower() == 'ignore': return # Does a host record for this zone already exist? searchResult=s4connector.lo.search(filter='(&(relativeDomainName=%s)(zoneName=%s))' % (relativeDomainName, zoneName), unique=1) if len(searchResult) > 0: superordinate=s4connector_get_superordinate('dns/srv_record', s4connector.lo, searchResult[0][0]) newRecord= univention.admin.handlers.dns.srv_record.object(None, s4connector.lo, position=None, dn=searchResult[0][0], superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() if ucr_locations: ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: do not write SRV record back from S4 to UCS because location of SRV record have been overwritten by UCR') else: ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: location: %s' % newRecord['location']) ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: srv : %s' % srv) srv.sort() newRecord['location'].sort() if srv != newRecord['location']: newRecord['location']=srv newRecord.modify() else: ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: do not modify host record') else: zoneDN='zoneName=%s,%s' % (zoneName, s4connector.property['dns'].ucs_default_dn) superordinate=s4connector_get_superordinate('dns/srv_record', s4connector.lo, zoneDN) ud.debug(ud.LDAP, ud.INFO, 'ucs_srv_record_create: superordinate: %s' % superordinate) position=univention.admin.uldap.position(zoneDN) newRecord= univention.admin.handlers.dns.srv_record.object(None, s4connector.lo, position, dn=None, superordinate=superordinate, attributes=[], update_zone=False) newRecord.open() # Make syntax UDM compatible service=string.join(relativeDomainName.split('.')[:-1], '.') if service.startswith('_'): service=service[1:] protocol=relativeDomainName.split('.')[-1] if protocol.startswith('_'): protocol=protocol[1:] ud.debug(ud.LDAP, ud.INFO, 'SRV create: service="%s" protocol="%s"' % (service, protocol)) newRecord['name']=[service, protocol] newRecord['location']=srv newRecord.create()
def password_sync_ucs_to_s4(s4connector, key, object): _d = ud.function('ldap.s4.password_sync_ucs_to_s4') ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4 called") compatible_modstring = univention.s4connector.s4.compatible_modstring try: ud.debug(ud.LDAP, ud.INFO, "Object DN=%s" % object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, "Object DN not printable") ucs_object = s4connector._object_mapping(key, object, 'con') try: ud.debug(ud.LDAP, ud.INFO, " UCS DN = %s" % ucs_object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, " UCS DN not printable") try: res = s4connector.lo.lo.search( base=ucs_object['dn'], scope='base', attr=[ 'sambaLMPassword', 'sambaNTPassword', 'sambaPwdLastSet', 'sambaPwdMustChange', 'krb5PrincipalName', 'krb5Key', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd', 'univentionService' ]) except ldap.NO_SUCH_OBJECT: ud.debug( ud.LDAP, ud.PROCESS, "password_sync_ucs_to_s4: The UCS object (%s) was not found. The object was removed." % ucs_object['dn']) return services = res[0][1].get('univentionService', []) if 'Samba 4' in services: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: %s is a S4 server, skip password sync" % ucs_object['dn']) return sambaPwdLastSet = None if res[0][1].has_key('sambaPwdLastSet'): sambaPwdLastSet = long(res[0][1]['sambaPwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdLastSet: %s" % sambaPwdLastSet) sambaPwdMustChange = -1 if res[0][1].has_key('sambaPwdMustChange'): sambaPwdMustChange = long(res[0][1]['sambaPwdMustChange'][0]) ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdMustChange: %s" % sambaPwdMustChange) ucsLMhash = res[0][1].get('sambaLMPassword', [None])[0] ucsNThash = res[0][1].get('sambaNTPassword', [None])[0] krb5Principal = res[0][1].get('krb5PrincipalName', [None])[0] krb5Key = res[0][1].get('krb5Key', []) if not ucsNThash: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaNTPassword missing in UCS LDAP, trying krb5Key" ) ucsNThash = extract_NThash_from_krb5key(krb5Key) if not ucsNThash: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get NT Password-Hash from UCS LDAP" ) # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Password-Hash from UCS: %s" % ucsNThash) res = s4connector.lo_s4.lo.search_s( univention.s4connector.s4.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)', ['pwdLastSet', 'objectSid']) pwdLastSet = None if res[0][1].has_key('pwdLastSet'): pwdLastSet = long(res[0][1]['pwdLastSet'][0]) objectSid = univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet from S4 : %s" % pwdLastSet) # rid = None # if res[0][1].has_key('objectSid'): # rid = str(univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]).split('-')[-1]) pwd_set = False res = s4connector.lo_s4.lo.search_s( s4connector.lo_s4.base, ldap.SCOPE_SUBTREE, compatible_modstring('(objectSid=%s)' % objectSid), [ 'unicodePwd', 'userPrincipalName', 'supplementalCredentials', 'msDS-KeyVersionNumber', 'dBCSPwd' ]) unicodePwd_attr = res[0][1].get('unicodePwd', [None])[0] dBCSPwd_attr = res[0][1].get('dBCSPwd', [None])[0] userPrincipalName_attr = res[0][1].get('userPrincipalName', [None])[0] supplementalCredentials = res[0][1].get('supplementalCredentials', [None])[0] msDS_KeyVersionNumber = res[0][1].get('msDS-KeyVersionNumber', [0])[0] # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Password-Hash from S4: %s" % unicodePwd_attr) s4NThash = None if unicodePwd_attr: s4NThash = binascii.b2a_hex(unicodePwd_attr).upper() else: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get NT Password-Hash from S4") s4LMhash = None if dBCSPwd_attr: s4LMhash = binascii.b2a_hex(dBCSPwd_attr).upper() else: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: Failed to get LM Password-Hash from S4") modlist = [] if krb5Principal != userPrincipalName_attr: if krb5Principal: if not userPrincipalName_attr: ## new and not old modlist.append( (ldap.MOD_ADD, 'userPrincipalName', krb5Principal)) else: ## new and old differ if krb5Principal.lower() != userPrincipalName_attr.lower(): ud.debug( ud.LDAP, ud.WARN, "password_sync_ucs_to_s4: userPrincipalName != krb5Principal: '%s' != '%s'" % (userPrincipalName_attr, krb5Principal)) modlist.append( (ldap.MOD_REPLACE, 'userPrincipalName', krb5Principal)) else: if userPrincipalName_attr: ## old and not new modlist.append((ldap.MOD_DELETE, 'userPrincipalName', userPrincipalName_attr)) if not ucsNThash == s4NThash: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: NT Hash S4: %s NT Hash UCS: %s" % (s4NThash, ucsNThash)) ## Now if ucsNThash is empty there should at least some timestamp in UCS, ## otherwise it's probably not a good idea to remove the unicodePwd. ## Usecase: LDB module on ucs_3.0-0-ucsschool slaves creates XP computers/windows in UDM without password if ucsNThash or sambaPwdLastSet: pwd_set = True if unicodePwd_attr: modlist.append( (ldap.MOD_DELETE, 'unicodePwd', unicodePwd_attr)) if ucsNThash: unicodePwd_new = binascii.a2b_hex(ucsNThash) modlist.append((ldap.MOD_ADD, 'unicodePwd', unicodePwd_new)) if not ucsLMhash == s4LMhash: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: LM Hash S4: %s LM Hash UCS: %s" % (s4LMhash, ucsLMhash)) pwd_set = True if dBCSPwd_attr: modlist.append((ldap.MOD_DELETE, 'dBCSPwd', dBCSPwd_attr)) if ucsLMhash: dBCSPwd_new = binascii.a2b_hex(ucsLMhash) modlist.append((ldap.MOD_ADD, 'dBCSPwd', dBCSPwd_new)) if pwd_set or not supplementalCredentials: if krb5Principal: ## encoding of Samba4 supplementalCredentials if supplementalCredentials: modlist.append((ldap.MOD_DELETE, 'supplementalCredentials', supplementalCredentials)) if krb5Key: supplementalCredentials_new = calculate_supplementalCredentials( krb5Key, supplementalCredentials) if supplementalCredentials_new: modlist.append((ldap.MOD_ADD, 'supplementalCredentials', supplementalCredentials_new)) else: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: no supplementalCredentials_new" ) #if supplementalCredentials: # modlist.append((ldap.MOD_REPLACE, 'msDS-KeyVersionNumber', krb5KeyVersionNumber)) #else: # modlist.append((ldap.MOD_ADD, 'msDS-KeyVersionNumber', krb5KeyVersionNumber)) if sambaPwdMustChange >= 0 and sambaPwdMustChange < time.time(): # password expired, must be changed on next login ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: samba pwd expired, set newpwdLastSet to 0" ) newpwdlastset = "0" else: if sambaPwdLastSet == None: sambaPwdLastSet = int(time.time()) newpwdlastset = str( univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) elif sambaPwdLastSet in [0, 1]: newpwdlastset = "0" else: newpwdlastset = str( univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdlastset in modlist: %s" % newpwdlastset) modlist.append((ldap.MOD_REPLACE, 'pwdlastset', newpwdlastset)) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: No password change to sync to S4 ") # check pwdLastSet if sambaPwdLastSet != None: newpwdlastset = str( univention.s4connector.s4.samba2s4_time(sambaPwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: sambaPwdLastSet: %d" % sambaPwdLastSet) ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: newpwdlastset : %s" % newpwdlastset) ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: pwdLastSet (AD): %s" % pwdLastSet) if sambaPwdLastSet in [0, 1]: modlist.append((ldap.MOD_REPLACE, 'pwdlastset', "0")) elif pwdLastSet != newpwdlastset: modlist.append((ldap.MOD_REPLACE, 'pwdlastset', newpwdlastset)) ## TODO: Password History ctrl_bypass_password_hash = LDAPControl('1.3.6.1.4.1.7165.4.3.12', criticality=0) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs_to_s4: modlist: %s" % modlist) if modlist: s4connector.lo_s4.lo.modify_ext_s( compatible_modstring(object['dn']), modlist, serverctrls=[ctrl_bypass_password_hash])
def password_sync_s4_to_ucs(s4connector, key, ucs_object, modifyUserPassword=True): _d = ud.function('ldap.s4.password_sync_s4_to_ucs') ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs called") object = s4connector._object_mapping(key, ucs_object, 'ucs') res = s4connector.lo_s4.lo.search_s( univention.s4connector.s4.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)', ['objectSid', 'pwdLastSet']) pwdLastSet = None if res[0][1].has_key('pwdLastSet'): pwdLastSet = long(res[0][1]['pwdLastSet'][0]) ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: pwdLastSet from S4: %s (%s)" % (pwdLastSet, res)) objectSid = univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]) # rid = None # if res[0][1].has_key('objectSid'): # rid = str(univention.s4connector.s4.decode_sid(res[0][1]['objectSid'][0]).split('-')[-1]) res = s4connector.lo_s4.lo.search_s( s4connector.lo_s4.base, ldap.SCOPE_SUBTREE, univention.s4connector.s4.compatible_modstring( '(objectSid=%s)' % objectSid), [ 'unicodePwd', 'supplementalCredentials', 'msDS-KeyVersionNumber', 'dBCSPwd' ]) unicodePwd_attr = res[0][1].get('unicodePwd', [None])[0] if unicodePwd_attr: ntPwd = binascii.b2a_hex(unicodePwd_attr).upper() lmPwd = '' dBCSPwd = res[0][1].get('dBCSPwd', [None])[0] if dBCSPwd: lmPwd = binascii.b2a_hex(dBCSPwd).upper() supplementalCredentials = res[0][1].get('supplementalCredentials', [None])[0] msDS_KeyVersionNumber = res[0][1].get('msDS-KeyVersionNumber', [0])[0] ntPwd_ucs = '' lmPwd_ucs = '' krb5Principal = '' userPassword = '' modlist = [] res = s4connector.lo.search(base=ucs_object['dn'], attr=[ 'sambaPwdMustChange', 'sambaPwdLastSet', 'sambaNTPassword', 'sambaLMPassword', 'krb5PrincipalName', 'krb5Key', 'krb5KeyVersionNumber', 'userPassword', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd' ]) if res[0][1].has_key('sambaNTPassword'): ntPwd_ucs = res[0][1]['sambaNTPassword'][0] if res[0][1].has_key('sambaLMPassword'): lmPwd_ucs = res[0][1]['sambaLMPassword'][0] if res[0][1].has_key('krb5PrincipalName'): krb5Principal = res[0][1]['krb5PrincipalName'][0] if res[0][1].has_key('userPassword'): userPassword = res[0][1]['userPassword'][0] sambaPwdLastSet = None if res[0][1].has_key('sambaPwdLastSet'): sambaPwdLastSet = res[0][1]['sambaPwdLastSet'][0] ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet: %s" % sambaPwdLastSet) sambaPwdMustChange = '' if res[0][1].has_key('sambaPwdMustChange'): sambaPwdMustChange = res[0][1]['sambaPwdMustChange'][0] ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdMustChange: %s" % sambaPwdMustChange) krb5Key_ucs = res[0][1].get('krb5Key', []) userPassword_ucs = res[0][1].get('userPassword', [None])[0] krb5KeyVersionNumber = res[0][1].get('krb5KeyVersionNumber', [None])[0] pwd_changed = False if ntPwd != ntPwd_ucs: pwd_changed = True modlist.append(('sambaNTPassword', ntPwd_ucs, str(ntPwd))) if lmPwd != lmPwd_ucs: pwd_changed = True modlist.append(('sambaLMPassword', lmPwd_ucs, str(lmPwd))) if pwd_changed: if krb5Principal: ## decoding of Samba4 supplementalCredentials krb5Key_new = calculate_krb5key(unicodePwd_attr, supplementalCredentials, int(msDS_KeyVersionNumber)) modlist.append(('krb5Key', krb5Key_ucs, krb5Key_new)) if int(msDS_KeyVersionNumber) != int(krb5KeyVersionNumber): modlist.append( ('krb5KeyVersionNumber', krb5KeyVersionNumber, msDS_KeyVersionNumber)) ## Append modification as well to modlist, to apply in one transaction if modifyUserPassword: modlist.append(('userPassword', userPassword_ucs, '{K5KEY}')) # Remove the POSIX and Kerberos password expiry interval if res[0][1].has_key('shadowLastChange'): modlist.append(('shadowLastChange', res[0][1]['shadowLastChange'][0], None)) if res[0][1].has_key('shadowMax'): modlist.append(('shadowMax', res[0][1]['shadowMax'][0], None)) if res[0][1].has_key('krb5PasswordEnd'): modlist.append( ('krb5PasswordEnd', res[0][1]['krb5PasswordEnd'][0], None)) else: ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: No password change to sync to UCS") if pwd_changed and (pwdLastSet or pwdLastSet == 0): newSambaPwdMustChange = sambaPwdMustChange if pwdLastSet == 0: # pwd change on next login newSambaPwdMustChange = str(pwdLastSet) newSambaPwdLastSet = str(pwdLastSet) else: newSambaPwdLastSet = str( univention.s4connector.s4.s42samba_time(pwdLastSet)) userobject = s4connector.get_ucs_object( 'user', ucs_object['dn']) if not userobject: ud.debug( ud.LDAP, ud.ERROR, "password_sync_s4_to_ucs: couldn't get user-object from UCS" ) return False sambaPwdMustChange = sambaPwdMustChange.strip() if not sambaPwdMustChange.isdigit(): pass elif pwd_changed or (long(sambaPwdMustChange) < time.time() and not pwdLastSet == 0): pwhistoryPolicy = userobject.loadPolicyObject( 'policies/pwhistory') try: expiryInterval = int(pwhistoryPolicy['expiryInterval']) newSambaPwdMustChange = str( long(newSambaPwdLastSet) + (expiryInterval * 3600 * 24)) except: # FIXME: which exception is to be caught? # expiryInterval is empty or no legal int-string pwhistoryPolicy['expiryInterval'] = '' expiryInterval = -1 newSambaPwdMustChange = '' ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: pwhistoryPolicy: expiryInterval: %s" % expiryInterval) if sambaPwdLastSet: if sambaPwdLastSet != newSambaPwdLastSet: modlist.append(('sambaPwdLastSet', sambaPwdLastSet, newSambaPwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet in modlist (replace): %s" % newSambaPwdLastSet) else: modlist.append(('sambaPwdLastSet', '', newSambaPwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet in modlist (set): %s" % newSambaPwdLastSet) if sambaPwdMustChange != newSambaPwdMustChange: # change if password has changed or "change pwd on next login" is not set # set sambaPwdMustChange regarding to the univention-policy if sambaPwdMustChange: modlist.append(('sambaPwdMustChange', sambaPwdMustChange, newSambaPwdMustChange)) ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdMustChange in modlist (replace): %s" % newSambaPwdMustChange) else: modlist.append( ('sambaPwdMustChange', '', newSambaPwdMustChange)) ud.debug( ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdMustChange in modlist (set): %s" % newSambaPwdMustChange) if len(modlist) > 0: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: modlist: %s" % modlist) s4connector.lo.lo.modify(ucs_object['dn'], modlist) else: ud.debug( ud.LDAP, ud.WARN, "password_sync_ucs_s4_to_ucs: Failed to get Password-Hash from S4")
def calculate_krb5key(unicodePwd, supplementalCredentials, kvno=0): up_blob = unicodePwd sc_blob = supplementalCredentials keys = [] keytypes = [] context = heimdal.context() if up_blob: # ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: up_blob: %s" % binascii.b2a_base64(up_blob)) assert len(up_blob) == 16 key = heimdal.keyblock_raw(context, 23, up_blob) keys.append(heimdal.asn1_encode_key(key, None, kvno)) if sc_blob: # ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: sc_blob: %s" % binascii.b2a_base64(sc_blob)) try: sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob) for p in sc.sub.packages: krb = None ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: parsing %s blob" % p.name) if p.name == "Primary:Kerberos": krb_blob = binascii.unhexlify(p.data) krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 3 for k in krb.ctr.keys: if k.keytype not in keytypes: ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ctr3.key.keytype: %s" % k.keytype) try: key = heimdal.keyblock_raw(context, k.keytype, k.value) krb5SaltObject = heimdal.salt_raw(context, krb.ctr.salt.string) keys.append(heimdal.asn1_encode_key(key, krb5SaltObject, kvno)) keytypes.append(k.keytype) except: if k.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) for this if k.value == up_blob: # the known case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring arc4 NThash with special keytype %s in %s" % (k.keytype, p.name)) else: # unknown special case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring unknown key with special keytype %s in %s" % (k.keytype, p.name)) else: traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_krb5key: krb5Key with keytype %s could not be parsed in %s. Ignoring this keytype." % (k.keytype, p.name)) elif p.name == "Primary:Kerberos-Newer-Keys": krb_blob = binascii.unhexlify(p.data) krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 4 for k in krb.ctr.keys: if k.keytype not in keytypes: ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ctr4.key.keytype: %s" % k.keytype) try: key = heimdal.keyblock_raw(context, k.keytype, k.value) krb5SaltObject = heimdal.salt_raw(context, krb.ctr.salt.string) keys.append(heimdal.asn1_encode_key(key, krb5SaltObject, kvno)) keytypes.append(k.keytype) except: if k.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) for this if k.value == up_blob: # the known case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring arc4 NThash with special keytype %s in %s" % (k.keytype, p.name)) else: # unknown special case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring unknown key with special keytype %s in %s" % (k.keytype, p.name)) else: traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_krb5key: krb5Key with keytype %s could not be parsed in %s. Ignoring this keytype." % (k.keytype, p.name)) except Exception: import sys exc = sys.exc_info()[1] if isinstance(exc.args, type(())) and len(exc.args) == 2 and exc.args[1] == 'Buffer Size Error': ud.debug(ud.LDAP, ud.WARN, "calculate_krb5key: '%s' while unpacking supplementalCredentials:: %s" % (exc, binascii.b2a_base64(sc_blob))) ud.debug(ud.LDAP, ud.WARN, "calculate_krb5key: the krb5Keys from the PrimaryKerberosBlob could not be parsed. Continuing anyway.") else: traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_krb5key: the krb5Keys from the PrimaryKerberosBlob could not be parsed. Continuing anyway.") return keys
def password_sync(connector, key, ucs_object): _d = ud.function('ldap.ad.password_sync') # noqa: F841 # externes Programm zum holen des Hash aufrufen # "kerberos_now" object = connector._object_mapping(key, ucs_object, 'ucs') res = connector.lo_ad.lo.search_s( univention.connector.ad.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)', ['objectSid', 'pwdLastSet']) if connector.isInCreationList(object['dn']): connector.removeFromCreationList(object['dn']) ud.debug( ud.LDAP, ud.INFO, "password_sync: Synchronisation of password has been canceled. Object was just created." ) return pwdLastSet = None if 'pwdLastSet' in res[0][1]: pwdLastSet = int(res[0][1]['pwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync: pwdLastSet from AD: %s (%s)" % (pwdLastSet, res)) if 'objectSid' in res[0][1]: str( univention.connector.ad.decode_sid( res[0][1]['objectSid'][0]).split('-')[-1]) ucs_result = connector.lo.search(base=ucs_object['dn'], attr=[ 'sambaPwdLastSet', 'sambaNTPassword', 'krb5PrincipalName', 'krb5Key', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd' ]) sambaPwdLastSet = None if 'sambaPwdLastSet' in ucs_result[0][1]: sambaPwdLastSet = ucs_result[0][1]['sambaPwdLastSet'][0] ud.debug(ud.LDAP, ud.INFO, "password_sync: sambaPwdLastSet: %s" % sambaPwdLastSet) if connector.baseConfig.is_true( '%s/ad/password/timestamp/check' % connector.CONFIGBASENAME, False): # Only sync the passwords from AD to UCS when the pwdLastSet timestamps in AD are newer ad_password_last_set = 0 # If pwdLastSet was set to 0 the password must be changed on next login. In this # case the timestamp is ignored and the password will be synced. This behavior can # be disabled by setting connector/ad/password/timestamp/syncreset/ad to false. This # might be necessary if the connector is configured in read mode and the password will be # synced in two ways: Bug #22653 if (pwdLastSet > 1) or (pwdLastSet in [0, 1] and connector.baseConfig.is_false( '%s/ad/password/timestamp/syncreset/ad' % connector.CONFIGBASENAME, False)): ad_password_last_set = univention.connector.ad.ad2samba_time( pwdLastSet) if sambaPwdLastSet: if int(sambaPwdLastSet) >= int(ad_password_last_set) and int( sambaPwdLastSet) != 1: # skip ud.debug( ud.LDAP, ud.PROCESS, "password_sync: Don't sync the passwords from AD to UCS because the UCS password is equal or newer." ) ud.debug( ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) return ud.debug(ud.LDAP, ud.INFO, "password_sync: Sync the passwords from AD to UCS.") ud.debug( ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) try: nt_hash, krb5Key = get_password_from_ad( connector, univention.connector.ad.compatible_modstring(object['dn'])) except Exception as e: ud.debug( ud.LDAP, ud.PROCESS, "password_sync: get_password_from_ad failed with %s, retry with reconnect" % str(e)) nt_hash, krb5Key = get_password_from_ad( connector, univention.connector.ad.compatible_modstring(object['dn']), reconnect=True) old_krb5end = ucs_result[0][1].get('krb5PasswordEnd', [None])[0] old_shadowMax = ucs_result[0][1].get('shadowMax', [None])[0] old_shadowLastChange = ucs_result[0][1].get('shadowLastChange', [None])[0] modlist = [] if nt_hash: ntPwd_ucs = '' krb5Principal = '' ntPwd = nt_hash if 'sambaNTPassword' in ucs_result[0][1]: ntPwd_ucs = ucs_result[0][1]['sambaNTPassword'][0] if 'krb5PrincipalName' in ucs_result[0][1]: krb5Principal = ucs_result[0][1]['krb5PrincipalName'][0] pwd_changed = False if ntPwd.upper() != ntPwd_ucs.upper(): if ntPwd in [ '00000000000000000000000000000000', 'NO PASSWORD*********************' ]: ud.debug( ud.LDAP, ud.WARN, "password_sync: AD connector password daemon returned 0 for the nt hash. Please check the AD settings." ) else: pwd_changed = True modlist.append( ('sambaNTPassword', ntPwd_ucs, str(ntPwd.upper()))) if krb5Principal: connector.lo.lo.modify_s( univention.connector.ad.compatible_modstring( ucs_object['dn']), [(ldap.MOD_REPLACE, 'krb5Key', nt_password_to_arcfour_hmac_md5(ntPwd.upper()))]) if pwd_changed: if krb5Key: krb5Key_ucs = ucs_result[0][1]['krb5Key'][0] modlist.append(('krb5Key', krb5Key_ucs, krb5Key)) connector.lo.lo.modify_s( univention.connector.ad.compatible_modstring(ucs_object['dn']), [(ldap.MOD_REPLACE, 'userPassword', '{K5KEY}')]) # update shadowLastChange new_shadowLastChange = str(int(time.time()) / 3600 / 24) if pwdLastSet != 0: modlist.append(('shadowLastChange', old_shadowLastChange, new_shadowLastChange)) ud.debug( ud.LDAP, ud.INFO, "password_sync: update shadowLastChange to %s for %s" % (new_shadowLastChange, ucs_object['dn'])) # get pw policy new_shadowMax = None new_krb5end = None policies = connector.lo.getPolicies(ucs_object['dn']) policy = policies.get('univentionPolicyPWHistory', {}).get('univentionPWExpiryInterval') if policy: ud.debug( ud.LDAP, ud.INFO, "password_sync: password expiry for %s is %s" % (ucs_object['dn'], policy)) policy_value = policy.get('value', [None])[0] if policy_value: new_shadowMax = policy_value new_krb5end = time.strftime( "%Y%m%d000000Z", time.gmtime((int(time.time()) + (int(policy_value) * 3600 * 24)))) # update shadowMax (set to value of univentionPWExpiryInterval, otherwise delete) and # krb5PasswordEnd (set to today + univentionPWExpiryInterval, otherwise delete) if (old_shadowMax or new_shadowMax) and (pwdLastSet != 0): ud.debug( ud.LDAP, ud.INFO, "password_sync: update shadowMax to %s for %s" % (new_shadowMax, ucs_object['dn'])) modlist.append(('shadowMax', old_shadowMax, new_shadowMax)) if (old_krb5end or new_krb5end) and (pwdLastSet != 0): ud.debug( ud.LDAP, ud.INFO, "password_sync: update krb5PasswordEnd to %s for %s" % (new_krb5end, ucs_object['dn'])) modlist.append(('krb5PasswordEnd', old_krb5end, new_krb5end)) else: ud.debug(ud.LDAP, ud.ERROR, "password_sync: sync failed, no result from AD") # update sambaPwdLastSet if pwdLastSet or pwdLastSet == 0: newSambaPwdLastSet = str( univention.connector.ad.ad2samba_time(pwdLastSet)) if sambaPwdLastSet: if sambaPwdLastSet != newSambaPwdLastSet: modlist.append( ('sambaPwdLastSet', sambaPwdLastSet, newSambaPwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync: sambaPwdLastSet in modlist (replace): %s" % newSambaPwdLastSet) else: modlist.append(('sambaPwdLastSet', '', newSambaPwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync: sambaPwdLastSet in modlist (set): %s" % newSambaPwdLastSet) if pwdLastSet == 0: expiry = int(time.time()) new_krb5end = time.strftime("%Y%m%d000000Z", time.gmtime(expiry)) if old_krb5end: ud.debug( ud.LDAP, ud.INFO, "password_sync: krb5PasswordEnd in modlist (replace): %s" % new_krb5end) modlist.append(('krb5PasswordEnd', old_krb5end, new_krb5end)) else: ud.debug( ud.LDAP, ud.INFO, "password_sync: krb5PasswordEnd in modlist (set): %s" % new_krb5end) modlist.append(('krb5PasswordEnd', '', new_krb5end)) if old_shadowMax: ud.debug(ud.LDAP, ud.INFO, "password_sync: shadowMax in modlist (replace): 0") modlist.append(('shadowMax', old_shadowMax, '1')) else: ud.debug(ud.LDAP, ud.INFO, "password_sync: shadowMax in modlist (set): 0") modlist.append(('shadowMax', '', '1')) two_days_ago = long(time.time()) - (86400 * 2) new_shadowLastChange = str(two_days_ago / 3600 / 24) if old_shadowLastChange: ud.debug( ud.LDAP, ud.INFO, "password_sync: shadowLastChange in modlist (replace): %s" % new_shadowLastChange) modlist.append(('shadowLastChange', old_shadowLastChange, new_shadowLastChange)) else: ud.debug( ud.LDAP, ud.INFO, "password_sync: shadowMax in modlist (set): %s" % new_shadowLastChange) modlist.append(('shadowLastChange', '', new_shadowLastChange)) if len(modlist) > 0: connector.lo.lo.modify(ucs_object['dn'], modlist)
def password_sync_s4_to_ucs(s4connector, key, ucs_object, modifyUserPassword=True): _d = ud.function('ldap.s4.password_sync_s4_to_ucs') ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs called") if ucs_object['modtype'] == 'modify': if 'pwdLastSet' not in ucs_object.get('changed_attributes', []): ud.debug(ud.LDAP, ud.INFO, 'password_sync_s4_to_ucs: the password for %s has not been changed. Skipping password sync.' % (ucs_object['dn'])) return object = s4connector._object_mapping(key, ucs_object, 'ucs') s4_object_attributes = s4connector.lo_s4.get(compatible_modstring(object['dn']), ['objectSid', 'pwdLastSet']) if s4connector.isInCreationList(object['dn']): s4connector.removeFromCreationList(object['dn']) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: Synchronisation of password has been canceled. Object was just created.") return pwdLastSet = None if 'pwdLastSet' in s4_object_attributes: pwdLastSet = int(s4_object_attributes['pwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: pwdLastSet from S4: %s (%s)" % (pwdLastSet, s4_object_attributes)) objectSid = univention.s4connector.s4.decode_sid(s4_object_attributes['objectSid'][0]) # rid = None # if s4_object_attributes.has_key('objectSid'): # rid = str(univention.s4connector.s4.decode_sid(s4_object_attributes['objectSid'][0]).split('-')[-1]) filter_expr = format_escaped('(objectSid={0!e})', objectSid) res = s4connector.lo_s4.search(filter=filter_expr, attr=['unicodePwd', 'supplementalCredentials', 'msDS-KeyVersionNumber', 'dBCSPwd']) s4_search_attributes = res[0][1] unicodePwd_attr = s4_search_attributes.get('unicodePwd', [None])[0] if unicodePwd_attr: ntPwd = binascii.b2a_hex(unicodePwd_attr).upper() lmPwd = '' dBCSPwd = s4_search_attributes.get('dBCSPwd', [None])[0] if dBCSPwd: lmPwd = binascii.b2a_hex(dBCSPwd).upper() supplementalCredentials = s4_search_attributes.get('supplementalCredentials', [None])[0] msDS_KeyVersionNumber = s4_search_attributes.get('msDS-KeyVersionNumber', [0])[0] ntPwd_ucs = '' lmPwd_ucs = '' krb5Principal = '' userPassword = '' modlist = [] ucs_object_attributes = s4connector.lo.get(ucs_object['dn'], ['sambaPwdMustChange', 'sambaPwdLastSet', 'sambaNTPassword', 'sambaLMPassword', 'krb5PrincipalName', 'krb5Key', 'krb5KeyVersionNumber', 'userPassword', 'shadowLastChange', 'shadowMax', 'krb5PasswordEnd', 'univentionService']) services = ucs_object_attributes.get('univentionService', []) if 'S4 SlavePDC' in services: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: %s is a S4 SlavePDC server, skip password sync" % ucs_object['dn']) return if 'sambaNTPassword' in ucs_object_attributes: ntPwd_ucs = ucs_object_attributes['sambaNTPassword'][0] if 'sambaLMPassword' in ucs_object_attributes: lmPwd_ucs = ucs_object_attributes['sambaLMPassword'][0] if 'krb5PrincipalName' in ucs_object_attributes: krb5Principal = ucs_object_attributes['krb5PrincipalName'][0] if 'userPassword' in ucs_object_attributes: userPassword = ucs_object_attributes['userPassword'][0] sambaPwdLastSet = None if 'sambaPwdLastSet' in ucs_object_attributes: sambaPwdLastSet = ucs_object_attributes['sambaPwdLastSet'][0] ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet: %s" % sambaPwdLastSet) sambaPwdMustChange = '' if 'sambaPwdMustChange' in ucs_object_attributes: sambaPwdMustChange = ucs_object_attributes['sambaPwdMustChange'][0] ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: Found sambaPwdMustChange: %s" % sambaPwdMustChange) krb5Key_ucs = ucs_object_attributes.get('krb5Key', []) userPassword_ucs = ucs_object_attributes.get('userPassword', [None])[0] krb5KeyVersionNumber = ucs_object_attributes.get('krb5KeyVersionNumber', [None])[0] pwd_changed = False if ntPwd != ntPwd_ucs: pwd_changed = True modlist.append(('sambaNTPassword', ntPwd_ucs, str(ntPwd))) if lmPwd != lmPwd_ucs: pwd_changed = True modlist.append(('sambaLMPassword', lmPwd_ucs, str(lmPwd))) if pwd_changed: if krb5Principal: # decoding of Samba4 supplementalCredentials krb5Key_new = calculate_krb5key(unicodePwd_attr, supplementalCredentials, int(msDS_KeyVersionNumber)) modlist.append(('krb5Key', krb5Key_ucs, krb5Key_new)) if int(msDS_KeyVersionNumber) != int(krb5KeyVersionNumber): modlist.append(('krb5KeyVersionNumber', krb5KeyVersionNumber, msDS_KeyVersionNumber)) # Append modification as well to modlist, to apply in one transaction if modifyUserPassword: modlist.append(('userPassword', userPassword_ucs, '{K5KEY}')) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: No password change to sync to UCS") try: old_pwdLastSet = object['old_s4_object']['pwdLastSet'][0] except (KeyError, IndexError): old_pwdLastSet = None if pwdLastSet != old_pwdLastSet: ud.debug(ud.LDAP, ud.ALL, "password_sync_s4_to_ucs: updating shadowLastChange") old_shadowLastChange = ucs_object_attributes.get('shadowLastChange', [None])[0] new_shadowLastChange = old_shadowLastChange # shadowMax (set to value of univentionPWExpiryInterval, otherwise delete) # krb5PasswordEnd (set to today + univentionPWExpiryInterval, otherwise delete) old_shadowMax = ucs_object_attributes.get('shadowMax', [None])[0] new_shadowMax = old_shadowMax old_krb5end = ucs_object_attributes.get('krb5PasswordEnd', [None])[0] new_krb5end = old_krb5end pwdLastSet_unix = univention.s4connector.s4.s42samba_time(pwdLastSet) newSambaPwdLastSet = str(pwdLastSet_unix) if pwdLastSet == 0: # pwd change on next login new_shadowMax = '1' expiry = int(time.time()) new_krb5end = time.strftime("%Y%m%d000000Z", time.gmtime(expiry)) else: # not pwd change on next login new_shadowLastChange = str(pwdLastSet_unix / 3600 / 24) userobject = s4connector.get_ucs_object(key, ucs_object['dn']) if not userobject: ud.debug(ud.LDAP, ud.ERROR, "password_sync_s4_to_ucs: couldn't get user-object from UCS") return False pwhistoryPolicy = userobject.loadPolicyObject('policies/pwhistory') try: expiryInterval = int(pwhistoryPolicy['expiryInterval']) except (TypeError, ValueError): # expiryInterval is empty or no legal int-string pwhistoryPolicy['expiryInterval'] = '' expiryInterval = -1 ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: password expiryInterval for %s is %s" % (ucs_object['dn'], expiryInterval)) if expiryInterval in (-1, 0): new_shadowMax = '' new_krb5end = '' else: new_shadowMax = str(expiryInterval) new_krb5end = time.strftime("%Y%m%d000000Z", time.gmtime((pwdLastSet_unix + (int(expiryInterval) * 3600 * 24)))) if new_shadowLastChange != old_shadowLastChange: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: update shadowLastChange to %s for %s" % (new_shadowLastChange, ucs_object['dn'])) modlist.append(('shadowLastChange', old_shadowLastChange, new_shadowLastChange)) if new_shadowMax != old_shadowMax: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: update shadowMax to %s for %s" % (new_shadowMax, ucs_object['dn'])) modlist.append(('shadowMax', old_shadowMax, new_shadowMax)) if new_krb5end != old_krb5end: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: update krb5PasswordEnd to %s for %s" % (new_krb5end, ucs_object['dn'])) modlist.append(('krb5PasswordEnd', old_krb5end, new_krb5end)) if sambaPwdLastSet: if sambaPwdLastSet != newSambaPwdLastSet: modlist.append(('sambaPwdLastSet', sambaPwdLastSet, newSambaPwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet in modlist (replace): %s" % newSambaPwdLastSet) else: modlist.append(('sambaPwdLastSet', '', newSambaPwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: sambaPwdLastSet in modlist (set): %s" % newSambaPwdLastSet) if sambaPwdMustChange: modlist.append(('sambaPwdMustChange', sambaPwdMustChange, '')) ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: Removing sambaPwdMustChange") if len(modlist) > 0: ud.debug(ud.LDAP, ud.INFO, "password_sync_s4_to_ucs: modlist: %s" % modlist) s4connector.lo.lo.modify(ucs_object['dn'], modlist) else: ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs_s4_to_ucs: Failed to get Password-Hash from S4")
def calculate_krb5keys(supplementalCredentialsblob): spl = supplementalCredentialsblob #cleartext_hex = None keys = [] keytypes = [] kvno = 0 context = heimdal.context() # for i in range(0, spl.sub.num_packages): # pkg = spl.sub.packages[i] # if pkg.name != "Primary:CLEARTEXT": # continue # cleartext_hex = pkg.data krb5_old_hex = None for i in range(0, spl.sub.num_packages): pkg = spl.sub.packages[i] if pkg.name != "Primary:Kerberos": continue krb5_old_hex = pkg.data if krb5_old_hex is not None: krb5_old_raw = binascii.a2b_hex(krb5_old_hex) krb5_old = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb5_old_raw, allow_remaining=True) assert krb5_old.version == 3 for k in krb5_old.ctr.keys: if k.keytype not in keytypes: ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ctr3.key.keytype: %s" % k.keytype) try: key = heimdal.keyblock_raw(context, k.keytype, k.value) krb5SaltObject = heimdal.salt_raw(context, krb5_old.ctr.salt.string) keys.append( heimdal.asn1_encode_key(key, krb5SaltObject, kvno)) keytypes.append(k.keytype) except: if k.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) for this ud.debug( ud.LDAP, ud.INFO, "calculate_krb5key: ignoring unknown key with special keytype %s in %s" % (k.keytype, pkg.name)) else: traceback.print_exc() ud.debug( ud.LDAP, ud.ERROR, "calculate_krb5key: krb5Key with keytype %s could not be parsed in %s. Ignoring this keytype." % (k.keytype, pkg.name)) krb5_new_hex = None for i in range(0, spl.sub.num_packages): pkg = spl.sub.packages[i] if pkg.name != "Primary:Kerberos-Newer-Keys": continue krb5_new_hex = pkg.data if krb5_new_hex is not None: krb_blob = binascii.unhexlify(krb5_new_hex) krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 4 for k in krb.ctr.keys: if k.keytype not in keytypes: ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ctr4.key.keytype: %s" % k.keytype) try: key = heimdal.keyblock_raw(context, k.keytype, k.value) krb5SaltObject = heimdal.salt_raw(context, krb.ctr.salt.string) keys.append( heimdal.asn1_encode_key(key, krb5SaltObject, kvno)) keytypes.append(k.keytype) except: if k.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) for this ud.debug( ud.LDAP, ud.INFO, "calculate_krb5key: ignoring unknown key with special keytype %s in %s" % (k.keytype, pkg.name)) else: traceback.print_exc() ud.debug( ud.LDAP, ud.ERROR, "calculate_krb5key: krb5Key with keytype %s could not be parsed in %s. Ignoring this keytype." % (k.keytype, pkg.name)) return keys
def lockout_sync_ucs_to_s4(s4connector, key, object): """ Sync unlock *modification* from OpenLDAP to Samba/AD: sync OpenLDAP ("L" not in sambaAcctFlags) -> Samba/AD lockoutTime = 0 sync OpenLDAP ("L" in sambaAcctFlags) -> Samba/AD lockoutTime = sambaBadPasswordTime and OpenLDAP sambaBadPasswordTime -> Samba/AD badPasswordTime """ function_name = 'lockout_sync_ucs_to_s4' _d = ud.function('ldap.s4.%s' % function_name) ud.debug(ud.LDAP, ud.INFO, "%s called" % function_name) if object['modtype'] not in ('modify', 'add'): return new_ucs_object = object.get('new_ucs_object', {}) if not new_ucs_object: # only set by sync_from_ucs in MODIFY case return old_ucs_object = object.get('old_ucs_object', {}) if not old_ucs_object: # only set by sync_from_ucs in MODIFY case return new_sambaAcctFlags = new_ucs_object.get('sambaAcctFlags', [''])[0] is_locked = "L" in new_sambaAcctFlags old_sambaAcctFlags = old_ucs_object.get('sambaAcctFlags', [''])[0] was_locked = "L" in old_sambaAcctFlags if is_locked == was_locked: # Require a change in the pickled state return modlist = [] if not is_locked: s4_object_attributes = s4connector.lo_s4.get(compatible_modstring(object['dn']), ['lockoutTime', 'badPasswordTime']) if 'lockoutTime' not in s4_object_attributes: return lockoutTime = s4_object_attributes['lockoutTime'][0] if lockoutTime == "0": return # Now object.get('new_ucs_object') may be a stale pickled state, so let's lookup the current OpenLDAP object state # Unfortunately "object" doesn't hold the current OpenLDAP DN, so we need to map back first ucs_object = s4connector._object_mapping(key, object) try: ucs_object_attributes = s4connector.lo.get(ucs_object['dn'], ['sambaAcctFlags', 'sambaBadPasswordTime'], required=True) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.WARN, "%s: The UCS object (%s) was not found. The object was removed." % (function_name, ucs_object['dn'])) return sambaAcctFlags = ucs_object_attributes.get('sambaAcctFlags', [''])[0] if "L" in sambaAcctFlags: ## currently locked again return sambaBadPasswordTime = ucs_object_attributes.get('sambaBadPasswordTime', [''])[0] if sambaBadPasswordTime and sambaBadPasswordTime != "0": ud.debug(ud.LDAP, ud.ERROR, "%s: The UCS object (%s) is unlocked, but sambaBadPasswordTime is set." % (function_name, ucs_object['dn'])) return # Ok here we have: # 1. Account currently not locked in OpenLDAP but in Samba/AD # 2. Lockout state has changed to unlocked at some pickled point in the past modlist.append((ldap.MOD_REPLACE, "lockoutTime", "0")) modlist.append((ldap.MOD_REPLACE, "badPasswordTime", "0")) ud.debug(ud.LDAP, ud.PROCESS, "%s: Marking account as unlocked in Samba/AD" % (function_name,)) else: s4_object_attributes = s4connector.lo_s4.get(compatible_modstring(object['dn']), ['lockoutTime', 'badPasswordTime']) lockoutTime = s4_object_attributes.get('lockoutTime', ['0'])[0] # Now object.get('new_ucs_object') may be a stale pickled state, so let's lookup the current OpenLDAP object state # Unfortunately "object" doesn't hold the current OpenLDAP DN, so we need to map back first ucs_object = s4connector._object_mapping(key, object) try: ucs_object_attributes = s4connector.lo.get(ucs_object['dn'], ['sambaAcctFlags', 'sambaBadPasswordTime'], required=True) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.WARN, "%s: The UCS object (%s) was not found. The object was removed." % (function_name, ucs_object['dn'])) return sambaAcctFlags = ucs_object_attributes.get('sambaAcctFlags', [''])[0] if "L" not in sambaAcctFlags: ## currently not locked any longer return sambaBadPasswordTime = ucs_object_attributes.get('sambaBadPasswordTime', [''])[0] if not sambaBadPasswordTime: ud.debug(ud.LDAP, ud.ERROR, "%s: The UCS object (%s) is locked, but sambaBadPasswordTime is missing." % (function_name, ucs_object['dn'])) return if sambaBadPasswordTime == "0": ud.debug(ud.LDAP, ud.ERROR, "%s: The UCS object (%s) is locked, but sambaBadPasswordTime is 0." % (function_name, ucs_object['dn'])) return if sambaBadPasswordTime == lockoutTime: ## already locked return # Ok here we have: # 1. Account currently locked in OpenLDAP but not in Samba/AD # 2. Lockout state has changed to locked at some pickled point in the past modlist.append((ldap.MOD_REPLACE, "lockoutTime", sambaBadPasswordTime)) modlist.append((ldap.MOD_REPLACE, "badPasswordTime", sambaBadPasswordTime)) ud.debug(ud.LDAP, ud.PROCESS, "%s: Marking account as locked in Samba/AD" % (function_name,)) ud.debug(ud.LDAP, ud.INFO, "%s: Setting lockoutTime to the value of sambaBadPasswordTime: %s" % (function_name, sambaBadPasswordTime)) if modlist: ud.debug(ud.LDAP, ud.ALL, "%s: modlist: %s" % (function_name, modlist)) s4connector.lo_s4.lo.modify_ext_s(compatible_modstring(object['dn']), modlist)
def ucs2con(s4connector, key, object): _d = ud.function('dc: ucs2con') ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: Object (%s): %s' % (object['dn'], object)) s4base_dn, s4base_attr = s4connector.lo_s4.lo.search_s( s4connector.s4_ldap_base, ldap.SCOPE_BASE, '(objectClass=*)')[0] ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: S4 object: %s' % (s4base_dn)) ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: S4 object: %s' % (s4base_attr)) if 'univentionBase' in object['attributes'].get('objectClass'): # DC object → sync GPO if s4connector.configRegistry.is_true('connector/s4/mapping/gpo', True): ucs_val = object['attributes'].get( 'msGPOLink', [None])[0] # msGPOLink is a single value s4_val = s4base_attr.get('msGPOLink') if ucs_val != s4_val: s4connector.lo_s4.lo.modify_s(s4connector.s4_ldap_base, [ (ldap.MOD_REPLACE, 'gPLink', univention.s4connector.s4.compatible_modstring(ucs_val)) ]) elif 'sambaDomain' in object['attributes'].get('objectClass'): # Samba Domain object ml = [] sync_times = [('sambaMaxPwdAge', 'maxPwdAge'), ('sambaMinPwdAge', 'minPwdAge'), ('sambaLockoutDuration', 'lockoutDuration')] for (ucs_attr, s4_attr) in sync_times: ucs_time = long(object['attributes'].get(ucs_attr, [0])[0]) s4_time = _nano2s(long(s4base_attr.get(s4_attr, [0])[0]) * -1) ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: ucs_time (%s): %s' % (ucs_attr, ucs_time)) ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: s4-time (%s): %s' % (s4_attr, s4_time)) if ucs_time != s4_time: if ucs_time > 0: s4_time = str(_s2nano(ucs_time) * -1) else: s4_time = "0" ml.append((ldap.MOD_REPLACE, s4_attr, [s4_time])) sync_integers = [('sambaPwdHistoryLength', 'pwdHistoryLength'), ('sambaMinPwdLength', 'minPwdLength'), ('univentionSamba4pwdProperties', 'pwdProperties')] for (ucs_attr, s4_attr) in sync_integers: ucs_val = object['attributes'].get(ucs_attr, str(0)) s4_val = s4base_attr.get(s4_attr, [0])[0] if ucs_val != s4_val: ml.append((ldap.MOD_REPLACE, s4_attr, ucs_val)) if ml: ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: S4 object modlist: %s' % (ml)) s4connector.lo_s4.lo.modify_s( s4connector.s4_ldap_base, univention.s4connector.s4.compatible_modlist(ml)) return True
def password_sync_ucs(connector, key, object): _d = ud.function('ldap.ad.password_sync_ucs') # noqa: F841 # externes Programm zum Überptragen des Hash aufrufen # per ldapmodify pwdlastset auf -1 setzen compatible_modstring = univention.connector.ad.compatible_modstring try: ud.debug(ud.LDAP, ud.INFO, "Object DN=%s" % object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, "Object DN not printable") ucs_object = connector._object_mapping(key, object, 'con') try: ud.debug(ud.LDAP, ud.INFO, " UCS DN = %s" % ucs_object['dn']) except: # FIXME: which exception is to be caught? ud.debug(ud.LDAP, ud.INFO, " UCS DN not printable") try: res = connector.lo.lo.search( base=ucs_object['dn'], scope='base', attr=['sambaLMPassword', 'sambaNTPassword', 'sambaPwdLastSet']) except ldap.NO_SUCH_OBJECT: ud.debug( ud.LDAP, ud.PROCESS, "password_sync_ucs: The UCS object (%s) was not found. The object was removed." % ucs_object['dn']) return sambaPwdLastSet = None if 'sambaPwdLastSet' in res[0][1]: sambaPwdLastSet = int(res[0][1]['sambaPwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: sambaPwdLastSet: %s" % sambaPwdLastSet) pwd = None if 'sambaNTPassword' in res[0][1]: pwd = res[0][1]['sambaNTPassword'][0] else: pwd = 'NO PASSWORDXXXXXX' ud.debug(ud.LDAP, ud.WARN, "password_sync_ucs: Failed to get NT Hash from UCS") if pwd in ['NO PASSWORDXXXXXX', 'NO PASSWORD*********************']: ud.debug( ud.LDAP, ud.PROCESS, "The sambaNTPassword hash is set to %s. Skip the synchronisation of this hash to AD." % pwd) res = connector.lo_ad.lo.search_s( univention.connector.ad.compatible_modstring(object['dn']), ldap.SCOPE_BASE, '(objectClass=*)', ['pwdLastSet', 'objectSid']) pwdLastSet = None if 'pwdLastSet' in res[0][1]: pwdLastSet = int(res[0][1]['pwdLastSet'][0]) ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: pwdLastSet from AD : %s" % pwdLastSet) if 'objectSid' in res[0][1]: str( univention.connector.ad.decode_sid( res[0][1]['objectSid'][0]).split('-')[-1]) # Only sync passwords from UCS to AD when the password timestamp in UCS is newer if connector.baseConfig.is_true( '%s/ad/password/timestamp/check' % connector.CONFIGBASENAME, False): ad_password_last_set = 0 # If sambaPwdLast was set to 1 the password must be changed on next login. In this # case the timestamp is ignored and the password will be synced. This behavior can # be disabled by setting connector/ad/password/timestamp/syncreset/ucs to false. This # might be necessary if the connector is configured in read mode and the password will be # synced in two ways: Bug #22653 if sambaPwdLastSet > 1 or ( sambaPwdLastSet <= 2 and connector.baseConfig.is_false( '%s/ad/password/timestamp/syncreset/ucs' % connector.CONFIGBASENAME, False)): ad_password_last_set = univention.connector.ad.ad2samba_time( pwdLastSet) if sambaPwdLastSet: if int(ad_password_last_set) >= int(sambaPwdLastSet): # skip ud.debug( ud.LDAP, ud.PROCESS, "password_sync: Don't sync the password from UCS to AD because the AD password equal or is newer." ) ud.debug( ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug( ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) return ud.debug(ud.LDAP, ud.INFO, "password_sync: Sync the passwords from UCS to AD.") ud.debug( ud.LDAP, ud.INFO, "password_sync: AD pwdlastset: %s (original (%s))" % (ad_password_last_set, pwdLastSet)) ud.debug(ud.LDAP, ud.INFO, "password_sync: UCS pwdlastset: %s" % (sambaPwdLastSet)) pwd_set = False try: nt_hash, krb5Key = get_password_from_ad( connector, univention.connector.ad.compatible_modstring(object['dn'])) except NTSTATUSError as e: ud.debug( ud.LDAP, ud.PROCESS, "password_sync_ucs: get_password_from_ad failed with %s, retry with reconnect" % str(e)) nt_hash, krb5Key = get_password_from_ad( connector, univention.connector.ad.compatible_modstring(object['dn']), reconnect=True) if not nt_hash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: No password hash could be read from AD") res = '' ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: Hash AD: %s Hash UCS: %s" % (nt_hash, pwd)) if not pwd == nt_hash: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: Hash AD and Hash UCS differ") pwd_set = True try: res = set_password_in_ad(connector, object['attributes']['sAMAccountName'][0], pwd) except NTSTATUSError as e: ud.debug( ud.LDAP, ud.PROCESS, "password_sync: set_password_in_ad failed with %s, retry with reconnect" % str(e)) res = set_password_in_ad(connector, object['attributes']['sAMAccountName'][0], pwd, reconnect=True) newpwdlastset = "-1" # if pwd was set in ad we need to set pwdlastset to -1 or it will be 0 # if sambaPwdMustChange >= 0 and sambaPwdMustChange < time.time(): # password expired, must be changed on next login # ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: samba pwd expired, set newpwdLastSet to 0") # newpwdlastset = "0" if sambaPwdLastSet <= 1: newpwdlastset = "0" # User must change his password elif pwdLastSet and int(pwdLastSet) > 0 and not pwd_set: newpwdlastset = "1" if int(newpwdlastset) != 1: ud.debug( ud.LDAP, ud.INFO, "password_sync_ucs: pwdlastset in modlist: %s" % newpwdlastset) connector.lo_ad.lo.modify_s(compatible_modstring( object['dn']), [(ldap.MOD_REPLACE, 'pwdlastset', newpwdlastset)]) else: ud.debug(ud.LDAP, ud.INFO, "password_sync_ucs: don't modify pwdlastset")
def sid_to_ucs(s4connector, key, s4_object): ud.debug(ud.LDAP, ud.INFO, "sid_to_ucs S4 object: %s" % s4_object) ud.debug(ud.LDAP, ud.INFO, "sid_to_ucs S4 key: %s" % key) sidAttribute = 'sambaSID' if s4connector.configRegistry.is_false('connector/s4/mapping/sid', False): ud.debug( ud.LDAP, ud.INFO, 'sid_to_ucs: SID mapping is disabled via UCR: connector/s4/mapping/sid' ) sidAttribute = 'univentionSamba4SID' else: # This case will be handled by direct mapping return # modlist ml = [] # object dn is already mapped to the UCS DN: if not s4_object.get('dn'): return # ignore ucs_dn = s4_object['dn'] ud.debug(ud.LDAP, ud.INFO, "sid_to_s4: UCS DN %s" % ucs_dn) if 'attributes' in s4_object and 'objectSid' in s4_object['attributes']: ud.debug( ud.LDAP, ud.INFO, 'sid_to_ucs: objectSid found: %s' % s4_object['attributes']['objectSid']) else: ud.debug(ud.LDAP, ud.INFO, 'sid_to_ucs: objectSid not found in attributes!') return (ucs_dn, ucs_attributes) = s4connector.lo.lo.search( base=ucs_dn, scope='base', attr=[sidAttribute, 'objectClass'])[0] if not ucs_dn: ud.debug(ud.LDAP, ud.WARN, 'sid_to_ucs: UCS object (%s) not found' % ucs_dn) return objectSid = s4_object['attributes'].get('objectSid') sambaSID = ucs_attributes.get(sidAttribute) if not sambaSID or objectSid != sambaSID: ml.append( (sidAttribute, sambaSID, s4_object['attributes'].get('objectSid'))) if 'user' in s4_object['attributes'].get('objectClass', []): if 'sambaSamAccount' not in ucs_attributes.get('objectClass'): ml.append( ('objectClass', ucs_attributes.get('objectClass'), ucs_attributes.get('objectClass') + ['sambaSamAccount'])) if 'group' in s4_object['attributes'].get('objectClass', []): if 'sambaGroupMapping' not in ucs_attributes.get('objectClass'): ml.append(('objectClass', ucs_attributes.get('objectClass'), ucs_attributes.get('objectClass') + ['sambaGroupMapping'])) if ml: ud.debug(ud.LDAP, ud.INFO, 'sid_to_ucs: modlist = %s' % ml) s4connector.lo.lo.modify(ucs_dn, ml) return
def get_password_from_ad(connector, user_dn, reconnect=False): _d = ud.function('ldap.ad.get_password_from_ad') # noqa: F841 ud.debug(ud.LDAP, ud.INFO, "get_password_from_ad: Read password from AD: %s" % user_dn) nt_hash = None if not connector.drs or reconnect: connector.open_drs_connection() req8 = drsuapi.DsGetNCChangesRequest8() req8.destination_dsa_guid = misc.GUID(connector.computer_guid) req8.source_dsa_invocation_id = misc.GUID(connector.computer_guid) req8.naming_context = drsuapi.DsReplicaObjectIdentifier() req8.naming_context.dn = user_dn req8.replica_flags = 0 req8.max_object_count = 402 req8.max_ndr_size = 402116 req8.extended_op = drsuapi.DRSUAPI_EXOP_REPL_SECRET req8.fsmo_info = 0 while True: (level, ctr) = connector.drs.DsGetNCChanges(connector.drsuapi_handle, 8, req8) rid = None unicode_blob = None keys = [] if ctr.first_object is None: break for i in ctr.first_object.object.attribute_ctr.attributes: if str(i.attid) == "589970": # DRSUAPI_ATTID_objectSid if i.value_ctr.values: for j in i.value_ctr.values: sid = ndr_unpack(security.dom_sid, j.blob) _tmp, rid = sid.split() if str(i.attid) == "589914": # DRSUAPI_ATTID_unicodePwd if i.value_ctr.values: for j in i.value_ctr.values: unicode_blob = j.blob ud.debug( ud.LDAP, ud.INFO, "get_password_from_ad: Found unicodePwd blob") if i.attid == drsuapi.DRSUAPI_ATTID_supplementalCredentials and connector.baseConfig.is_true( '%s/ad/mapping/user/password/kerberos/enabled' % connector.CONFIGBASENAME, False): if i.value_ctr.values: for j in i.value_ctr.values: ud.debug( ud.LDAP, ud.INFO, "get_password_from_ad: Found supplementalCredentials blob" ) spl = decrypt_supplementalCredentials( connector, j.blob) keys = calculate_krb5keys(spl) if rid and unicode_blob: nt_hash = decrypt(connector.drs.user_session_key, unicode_blob, rid).upper() if ctr.more_data == 0: break ud.debug(ud.LDAP, ud.INFO, "get_password_from_ad: AD Hash: %s" % nt_hash) return nt_hash, keys
def sid_to_ucs_mapping(s4connector, key, s4_object): ud.debug(ud.LDAP, ud.INFO, "sid_to_ucs_mapping") object_sid = s4_object['attributes']['objectSid'][0] return object_sid.split('-')[-1]
def calculate_supplementalCredentials(ucs_krb5key, old_supplementalCredentials): old_krb = {} if old_supplementalCredentials: sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, old_supplementalCredentials) for p in sc.sub.packages: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: parsing %s blob" % p.name) if p.name == "Primary:Kerberos": krb_blob = binascii.unhexlify(p.data) try: krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 3 old_krb['ctr3'] = krb.ctr for k in krb.ctr.keys: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: ctr3.key.keytype: %s" % k.keytype) except: ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: ndr_unpack of S4 Primary:Kerberos blob failed. Traceback:") traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: Continuing anyway, Primary:Kerberos (DES keys) blob will be missing in supplementalCredentials ctr3.old_keys.") elif p.name == "Primary:Kerberos-Newer-Keys": krb_blob = binascii.unhexlify(p.data) try: krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 4 old_krb['ctr4'] = krb.ctr for k in krb.ctr.keys: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: ctr4.key.keytype: %s" % k.keytype) except: ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: ndr_unpack of S4 Primary:Kerberos-Newer-Keys blob failed. Traceback:") traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: Continuing anyway, Primary:Kerberos-Newer-Keys (AES and DES keys) blob will be missing in supplementalCredentials ctr4.old_keys.") krb5_aes256 = '' krb5_aes128 = '' krb5_des_md5 = '' krb5_des_crc = '' krb_ctr3_salt = '' krb_ctr4_salt = '' for k in ucs_krb5key: (keyblock, salt, kvno) = heimdal.asn1_decode_key(k) key_data = keyblock.keyvalue() saltstring = salt.saltvalue() enctype = keyblock.keytype() enctype_id = enctype.toint() ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: krb5_keytype: %s (%d)" % (enctype, enctype_id)) if enctype_id == 18: krb5_aes256 = key_data if not krb_ctr4_salt: krb_ctr4_salt = saltstring elif enctype_id == 17: krb5_aes128 = key_data if not krb_ctr4_salt: krb_ctr4_salt = saltstring elif enctype_id == 3: krb5_des_md5 = key_data if not krb_ctr3_salt: krb_ctr3_salt = saltstring elif enctype_id == 1: krb5_des_crc = key_data if not krb_ctr3_salt: krb_ctr3_salt = saltstring ## build new drsblobs.supplementalCredentialsBlob sc_blob = None cred_List = [] package_names = [] ## Primary:Kerberos-Newer-Keys : AES keys if krb5_aes256 or krb5_aes128: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: building Primary:Kerberos-Newer-Keys blob") kerberosKey4list = [] if krb5_aes256: assert len(krb5_aes256) == 32 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 18 next_key.value = krb5_aes256 next_key.value_len = len(krb5_aes256) kerberosKey4list.append(next_key) if krb5_aes128: assert len(krb5_aes128) == 16 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 17 next_key.value = krb5_aes128 next_key.value_len = len(krb5_aes128) kerberosKey4list.append(next_key) if krb5_des_md5: assert len(krb5_des_md5) == 8 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 3 next_key.value = krb5_des_md5 next_key.value_len = len(krb5_des_md5) kerberosKey4list.append(next_key) if krb5_des_crc: assert len(krb5_des_crc) == 8 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 1 next_key.value = krb5_des_crc next_key.value_len = len(krb5_des_crc) kerberosKey4list.append(next_key) salt4 = drsblobs.package_PrimaryKerberosString() salt4.string = krb_ctr4_salt ctr4 = drsblobs.package_PrimaryKerberosCtr4() ctr4.salt = salt4 ctr4.num_keys = len(kerberosKey4list) ctr4.keys = kerberosKey4list if old_krb.get('ctr4'): ## Backup old_keys to s4_old_keys s4_num_old_keys = old_krb['ctr4'].num_old_keys s4_old_keys = [] for key in old_krb['ctr4'].old_keys: s4_old_keys.append(key) ## keys -> old_keys if len(old_krb['ctr4'].keys) != ctr4.num_keys: cleaned_old_keys = [] for key in old_krb['ctr4'].keys: if key.keytype == 4294967156: ## in all known cases W2k8 AD uses keytype 4294967156 (=-140L) to include the arc4 hash ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys filtering keytype %s from old_keys" % key.keytype) continue else: # TODO: can we do something better at this point to make old_keys == num_keys ? cleaned_old_keys.append(key) ctr4.old_keys = cleaned_old_keys ctr4.num_old_keys = len(cleaned_old_keys) else: ctr4.old_keys = old_krb['ctr4'].keys ctr4.num_old_keys = old_krb['ctr4'].num_keys ## s4_old_keys -> older_keys if ctr4.num_old_keys != ctr4.num_older_keys: cleaned_older_keys = [] for key in s4_old_keys: if key.keytype == 4294967156: ## in all known cases W2k8 AD uses keytype 4294967156 (=-140L) to include the arc4 hash ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys filtering keytype %s from older_keys" % key.keytype) continue else: # TODO: can we do something better at this point to make old_keys == num_keys ? cleaned_older_keys.append(key) ctr4.older_keys = cleaned_older_keys ctr4.num_older_keys = len(cleaned_older_keys) else: ctr4.older_keys = s4_old_keys ctr4.num_older_keys = s4_num_old_keys if ctr4.num_old_keys != 0 and ctr4.num_old_keys != ctr4.num_keys: # TODO: Recommended policy is to fill up old_keys to match num_keys, this will result in a traceback, can we do something better? ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_keys = %s" % ctr4.num_keys) for k in ctr4.keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.key.keytype: %s" % k.keytype) ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_old_keys = %s" % ctr4.num_old_keys) for k in ctr4.old_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.old_key.keytype: %s" % k.keytype) if ctr4.num_older_keys != 0 and ctr4.num_older_keys != ctr4.num_old_keys: # TODO: Recommended policy is to fill up old_keys to match num_keys, this will result in a traceback, can we do something better? ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_old_keys = %s" % ctr4.num_old_keys) for k in ctr4.old_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.old_key.keytype: %s" % k.keytype) ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_older_keys = %s" % ctr4.num_older_keys) for k in ctr4.older_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.older_key.keytype: %s" % k.keytype) krb_Primary_Kerberos_Newer = drsblobs.package_PrimaryKerberosBlob() krb_Primary_Kerberos_Newer.version = 4 krb_Primary_Kerberos_Newer.ctr = ctr4 krb_blob_Primary_Kerberos_Newer = ndr_pack(krb_Primary_Kerberos_Newer) creddata_Primary_Kerberos_Newer = binascii.hexlify(krb_blob_Primary_Kerberos_Newer) credname_Primary_Kerberos_Newer = "Primary:Kerberos-Newer-Keys" cred_Primary_Kerberos_Newer = drsblobs.supplementalCredentialsPackage() cred_Primary_Kerberos_Newer.name = credname_Primary_Kerberos_Newer cred_Primary_Kerberos_Newer.name_len = len(credname_Primary_Kerberos_Newer) cred_Primary_Kerberos_Newer.data = creddata_Primary_Kerberos_Newer cred_Primary_Kerberos_Newer.data_len = len(creddata_Primary_Kerberos_Newer) cred_Primary_Kerberos_Newer.reserved = 1 cred_List.append(cred_Primary_Kerberos_Newer) package_names.append('Kerberos-Newer-Keys') ## Primary:Kerberos : MD5 and CRC keys if krb5_des_md5 or krb5_des_crc: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: building Primary:Kerberos blob") kerberosKey3list = [] if krb5_des_md5: next_key = drsblobs.package_PrimaryKerberosKey3() next_key.keytype = 3 next_key.value = krb5_des_md5 next_key.value_len = len(krb5_des_md5) kerberosKey3list.append(next_key) if krb5_des_crc: next_key = drsblobs.package_PrimaryKerberosKey3() next_key.keytype = 1 next_key.value = krb5_des_crc next_key.value_len = len(krb5_des_crc) kerberosKey3list.append(next_key) salt = drsblobs.package_PrimaryKerberosString() salt.string = krb_ctr3_salt ctr3 = drsblobs.package_PrimaryKerberosCtr3() ctr3.salt = salt ctr3.num_keys = len(kerberosKey3list) ctr3.keys = kerberosKey3list if old_krb.get('ctr3'): ## keys -> old_keys if len(old_krb['ctr3'].keys) != ctr3.num_keys: cleaned_ctr3_old_keys = [] for key in old_krb['ctr3'].keys: if key.keytype == 4294967156: ## in all known cases W2k8 AD uses keytype 4294967156 (=-140L) to include the arc4 hash ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: Primary:Kerberos filtering keytype %s from old_keys" % key.keytype) continue else: # TODO: can we do something better at this point to make old_keys == num_keys ? cleaned_ctr3_old_keys.append(key) ctr3.old_keys = cleaned_ctr3_old_keys ctr3.num_old_keys = len(cleaned_ctr3_old_keys) else: ctr3.old_keys = old_krb['ctr3'].keys ctr3.num_old_keys = old_krb['ctr3'].num_keys if ctr3.num_old_keys != 0 and ctr3.num_old_keys != ctr3.num_keys: # TODO: Recommended policy is to fill up old_keys to match num_keys, this will result in a traceback, can we do something better? ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos num_keys = %s" % ctr3.num_keys) for k in ctr4.keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr3.key.keytype: %s" % k.keytype) ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos num_old_keys = %s" % ctr3.num_old_keys) for k in ctr4.old_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr3.old_key.keytype: %s" % k.keytype) krb = drsblobs.package_PrimaryKerberosBlob() krb.version = 3 krb.ctr = ctr3 krb3_blob = ndr_pack(krb) creddata_Primary_Kerberos = binascii.hexlify(krb3_blob) credname_Primary_Kerberos = "Primary:Kerberos" cred_Primary_Kerberos = drsblobs.supplementalCredentialsPackage() cred_Primary_Kerberos.name = credname_Primary_Kerberos cred_Primary_Kerberos.name_len = len(credname_Primary_Kerberos) cred_Primary_Kerberos.data = creddata_Primary_Kerberos cred_Primary_Kerberos.data_len = len(creddata_Primary_Kerberos) cred_Primary_Kerberos.reserved = 1 cred_List.append(cred_Primary_Kerberos) package_names.append('Kerberos') if package_names: package_names_carray = (ctypes.c_char_p * len(package_names))(*package_names) package_names_PyCObject = _PyCObject_FromVoidPtr(ctypes.cast(package_names_carray, ctypes.POINTER(ctypes.c_char_p)), None) krb_Packages = drsblobs.package_PackagesBlob() krb_Packages.names = package_names_PyCObject krb_blob_Packages = ndr_pack(krb_Packages) # krb_blob_Packages = '\0'.join(package_names).encode('utf-16le') # this pretty much simulates it cred_PackagesBlob_data = binascii.hexlify(krb_blob_Packages).upper() cred_PackagesBlob_name = "Packages" cred_PackagesBlob = drsblobs.supplementalCredentialsPackage() cred_PackagesBlob.name = cred_PackagesBlob_name cred_PackagesBlob.name_len = len(cred_PackagesBlob_name) cred_PackagesBlob.data = cred_PackagesBlob_data cred_PackagesBlob.data_len = len(cred_PackagesBlob_data) cred_PackagesBlob.reserved = 2 cred_List.insert(-1, cred_PackagesBlob) sub = drsblobs.supplementalCredentialsSubBlob() sub.num_packages = len(cred_List) sub.packages = cred_List sub.signature = drsblobs.SUPPLEMENTAL_CREDENTIALS_SIGNATURE sub.prefix = drsblobs.SUPPLEMENTAL_CREDENTIALS_PREFIX sc = drsblobs.supplementalCredentialsBlob() sc.sub = sub sc_blob = ndr_pack(sc) return sc_blob
def calculate_supplementalCredentials(ucs_krb5key, old_supplementalCredentials): old_krb = {} if old_supplementalCredentials: sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, old_supplementalCredentials) for p in sc.sub.packages: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: parsing %s blob" % p.name) if p.name == "Primary:Kerberos": krb_blob = binascii.unhexlify(p.data) try: krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 3 old_krb['ctr3'] = krb.ctr for k in krb.ctr.keys: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: ctr3.key.keytype: %s" % k.keytype) except: ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: ndr_unpack of S4 Primary:Kerberos blob failed. Traceback:") traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: Continuing anyway, Primary:Kerberos (DES keys) blob will be missing in supplementalCredentials ctr3.old_keys.") elif p.name == "Primary:Kerberos-Newer-Keys": krb_blob = binascii.unhexlify(p.data) try: krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 4 old_krb['ctr4'] = krb.ctr for k in krb.ctr.keys: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: ctr4.key.keytype: %s" % k.keytype) except: ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: ndr_unpack of S4 Primary:Kerberos-Newer-Keys blob failed. Traceback:") traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_supplementalCredentials: Continuing anyway, Primary:Kerberos-Newer-Keys (AES and DES keys) blob will be missing in supplementalCredentials ctr4.old_keys.") krb5_aes256 = '' krb5_aes128 = '' krb5_des_md5 = '' krb5_des_crc = '' krb_ctr3_salt = '' krb_ctr4_salt = '' for k in ucs_krb5key: (keyblock, salt, kvno) = heimdal.asn1_decode_key(k) key_data = keyblock.keyvalue() saltstring = salt.saltvalue() enctype = keyblock.keytype() enctype_id = enctype.toint() if enctype_id not in krb5_context.etype_ids: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ignoring unsupported krb5_keytype: (%d)" % (enctype_id,)) continue ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: krb5_keytype: %s (%d)" % (enctype, enctype_id)) if enctype_id == 18: krb5_aes256 = key_data if not krb_ctr4_salt: krb_ctr4_salt = saltstring elif enctype_id == 17: krb5_aes128 = key_data if not krb_ctr4_salt: krb_ctr4_salt = saltstring elif enctype_id == 3: krb5_des_md5 = key_data if not krb_ctr3_salt: krb_ctr3_salt = saltstring elif enctype_id == 1: krb5_des_crc = key_data if not krb_ctr3_salt: krb_ctr3_salt = saltstring # build new drsblobs.supplementalCredentialsBlob sc_blob = None cred_List = [] package_names = [] # Primary:Kerberos-Newer-Keys : AES keys if krb5_aes256 or krb5_aes128: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: building Primary:Kerberos-Newer-Keys blob") kerberosKey4list = [] if krb5_aes256: assert len(krb5_aes256) == 32 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 18 next_key.value = krb5_aes256 next_key.value_len = len(krb5_aes256) kerberosKey4list.append(next_key) if krb5_aes128: assert len(krb5_aes128) == 16 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 17 next_key.value = krb5_aes128 next_key.value_len = len(krb5_aes128) kerberosKey4list.append(next_key) if krb5_des_md5: assert len(krb5_des_md5) == 8 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 3 next_key.value = krb5_des_md5 next_key.value_len = len(krb5_des_md5) kerberosKey4list.append(next_key) if krb5_des_crc: assert len(krb5_des_crc) == 8 next_key = drsblobs.package_PrimaryKerberosKey4() next_key.keytype = 1 next_key.value = krb5_des_crc next_key.value_len = len(krb5_des_crc) kerberosKey4list.append(next_key) salt4 = drsblobs.package_PrimaryKerberosString() salt4.string = krb_ctr4_salt ctr4 = drsblobs.package_PrimaryKerberosCtr4() ctr4.salt = salt4 ctr4.num_keys = len(kerberosKey4list) ctr4.keys = kerberosKey4list if old_krb.get('ctr4'): # Backup old_keys to s4_old_keys s4_num_old_keys = old_krb['ctr4'].num_old_keys s4_old_keys = [] for key in old_krb['ctr4'].old_keys: s4_old_keys.append(key) # keys -> old_keys if len(old_krb['ctr4'].keys) != ctr4.num_keys: cleaned_old_keys = [] for key in old_krb['ctr4'].keys: if key.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) to include the arc4 hash ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys filtering keytype %s from old_keys" % key.keytype) continue else: # TODO: can we do something better at this point to make old_keys == num_keys ? cleaned_old_keys.append(key) ctr4.old_keys = cleaned_old_keys ctr4.num_old_keys = len(cleaned_old_keys) else: ctr4.old_keys = old_krb['ctr4'].keys ctr4.num_old_keys = old_krb['ctr4'].num_keys # s4_old_keys -> older_keys if ctr4.num_old_keys != ctr4.num_older_keys: cleaned_older_keys = [] for key in s4_old_keys: if key.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) to include the arc4 hash ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys filtering keytype %s from older_keys" % key.keytype) continue else: # TODO: can we do something better at this point to make old_keys == num_keys ? cleaned_older_keys.append(key) ctr4.older_keys = cleaned_older_keys ctr4.num_older_keys = len(cleaned_older_keys) else: ctr4.older_keys = s4_old_keys ctr4.num_older_keys = s4_num_old_keys if ctr4.num_old_keys != 0 and ctr4.num_old_keys != ctr4.num_keys: # TODO: Recommended policy is to fill up old_keys to match num_keys, this will result in a traceback, can we do something better? ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_keys = %s" % ctr4.num_keys) for k in ctr4.keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.key.keytype: %s" % k.keytype) ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_old_keys = %s" % ctr4.num_old_keys) for k in ctr4.old_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.old_key.keytype: %s" % k.keytype) if ctr4.num_older_keys != 0 and ctr4.num_older_keys != ctr4.num_old_keys: # TODO: Recommended policy is to fill up old_keys to match num_keys, this will result in a traceback, can we do something better? ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_old_keys = %s" % ctr4.num_old_keys) for k in ctr4.old_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.old_key.keytype: %s" % k.keytype) ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos-Newer-Keys num_older_keys = %s" % ctr4.num_older_keys) for k in ctr4.older_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr4.older_key.keytype: %s" % k.keytype) krb_Primary_Kerberos_Newer = drsblobs.package_PrimaryKerberosBlob() krb_Primary_Kerberos_Newer.version = 4 krb_Primary_Kerberos_Newer.ctr = ctr4 krb_blob_Primary_Kerberos_Newer = ndr_pack(krb_Primary_Kerberos_Newer) creddata_Primary_Kerberos_Newer = binascii.hexlify(krb_blob_Primary_Kerberos_Newer) credname_Primary_Kerberos_Newer = "Primary:Kerberos-Newer-Keys" cred_Primary_Kerberos_Newer = drsblobs.supplementalCredentialsPackage() cred_Primary_Kerberos_Newer.name = credname_Primary_Kerberos_Newer cred_Primary_Kerberos_Newer.name_len = len(credname_Primary_Kerberos_Newer) cred_Primary_Kerberos_Newer.data = creddata_Primary_Kerberos_Newer cred_Primary_Kerberos_Newer.data_len = len(creddata_Primary_Kerberos_Newer) cred_Primary_Kerberos_Newer.reserved = 1 cred_List.append(cred_Primary_Kerberos_Newer) package_names.append('Kerberos-Newer-Keys') # Primary:Kerberos : MD5 and CRC keys if krb5_des_md5 or krb5_des_crc: ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: building Primary:Kerberos blob") kerberosKey3list = [] if krb5_des_md5: next_key = drsblobs.package_PrimaryKerberosKey3() next_key.keytype = 3 next_key.value = krb5_des_md5 next_key.value_len = len(krb5_des_md5) kerberosKey3list.append(next_key) if krb5_des_crc: next_key = drsblobs.package_PrimaryKerberosKey3() next_key.keytype = 1 next_key.value = krb5_des_crc next_key.value_len = len(krb5_des_crc) kerberosKey3list.append(next_key) salt = drsblobs.package_PrimaryKerberosString() salt.string = krb_ctr3_salt ctr3 = drsblobs.package_PrimaryKerberosCtr3() ctr3.salt = salt ctr3.num_keys = len(kerberosKey3list) ctr3.keys = kerberosKey3list if old_krb.get('ctr3'): # keys -> old_keys if len(old_krb['ctr3'].keys) != ctr3.num_keys: cleaned_ctr3_old_keys = [] for key in old_krb['ctr3'].keys: if key.keytype == 4294967156: # in all known cases W2k8 AD uses keytype 4294967156 (=-140L) to include the arc4 hash ud.debug(ud.LDAP, ud.INFO, "calculate_supplementalCredentials: Primary:Kerberos filtering keytype %s from old_keys" % key.keytype) continue else: # TODO: can we do something better at this point to make old_keys == num_keys ? cleaned_ctr3_old_keys.append(key) ctr3.old_keys = cleaned_ctr3_old_keys ctr3.num_old_keys = len(cleaned_ctr3_old_keys) else: ctr3.old_keys = old_krb['ctr3'].keys ctr3.num_old_keys = old_krb['ctr3'].num_keys if ctr3.num_old_keys != 0 and ctr3.num_old_keys != ctr3.num_keys: # TODO: Recommended policy is to fill up old_keys to match num_keys, this will result in a traceback, can we do something better? ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos num_keys = %s" % ctr3.num_keys) for k in ctr3.keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr3.key.keytype: %s" % k.keytype) ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: Primary:Kerberos num_old_keys = %s" % ctr3.num_old_keys) for k in ctr3.old_keys: ud.debug(ud.LDAP, ud.WARN, "calculate_supplementalCredentials: ctr3.old_key.keytype: %s" % k.keytype) krb = drsblobs.package_PrimaryKerberosBlob() krb.version = 3 krb.ctr = ctr3 krb3_blob = ndr_pack(krb) creddata_Primary_Kerberos = binascii.hexlify(krb3_blob) credname_Primary_Kerberos = "Primary:Kerberos" cred_Primary_Kerberos = drsblobs.supplementalCredentialsPackage() cred_Primary_Kerberos.name = credname_Primary_Kerberos cred_Primary_Kerberos.name_len = len(credname_Primary_Kerberos) cred_Primary_Kerberos.data = creddata_Primary_Kerberos cred_Primary_Kerberos.data_len = len(creddata_Primary_Kerberos) cred_Primary_Kerberos.reserved = 1 cred_List.append(cred_Primary_Kerberos) package_names.append('Kerberos') if package_names: krb_blob_Packages = '\0'.join(package_names).encode('utf-16le') cred_PackagesBlob_data = binascii.hexlify(krb_blob_Packages).upper() cred_PackagesBlob_name = "Packages" cred_PackagesBlob = drsblobs.supplementalCredentialsPackage() cred_PackagesBlob.name = cred_PackagesBlob_name cred_PackagesBlob.name_len = len(cred_PackagesBlob_name) cred_PackagesBlob.data = cred_PackagesBlob_data cred_PackagesBlob.data_len = len(cred_PackagesBlob_data) cred_PackagesBlob.reserved = 2 cred_List.insert(-1, cred_PackagesBlob) sub = drsblobs.supplementalCredentialsSubBlob() sub.num_packages = len(cred_List) sub.packages = cred_List sub.signature = drsblobs.SUPPLEMENTAL_CREDENTIALS_SIGNATURE sub.prefix = drsblobs.SUPPLEMENTAL_CREDENTIALS_PREFIX sc = drsblobs.supplementalCredentialsBlob() sc.sub = sub sc_blob = ndr_pack(sc) ud.debug(ud.LDAP, ud.ALL, "calculate_supplementalCredentials: sc:\n%s" % ndr_print(sc)) return sc_blob
def calculate_krb5key(unicodePwd, supplementalCredentials, kvno=0): up_blob = unicodePwd sc_blob = supplementalCredentials keys = [] keytypes = [] context = heimdal.context() if up_blob: #ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: up_blob: %s" % binascii.b2a_base64(up_blob)) assert len(up_blob) == 16 key = heimdal.keyblock_raw(context, 23, up_blob) keys.append(heimdal.asn1_encode_key(key, None, kvno)) if sc_blob: #ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: sc_blob: %s" % binascii.b2a_base64(sc_blob)) try: sc = ndr_unpack(drsblobs.supplementalCredentialsBlob, sc_blob) for p in sc.sub.packages: krb = None ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: parsing %s blob" % p.name) if p.name == "Primary:Kerberos": krb_blob = binascii.unhexlify(p.data) krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 3 for k in krb.ctr.keys: if k.keytype not in keytypes: ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ctr3.key.keytype: %s" % k.keytype) try: key = heimdal.keyblock_raw(context, k.keytype, k.value) krb5SaltObject = heimdal.salt_raw(context, krb.ctr.salt.string) keys.append(heimdal.asn1_encode_key(key, krb5SaltObject, kvno)) keytypes.append(k.keytype) except: if k.keytype == 4294967156: ## in all known cases W2k8 AD uses keytype 4294967156 (=-140L) for this if k.value == up_blob: ## the known case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring arc4 NThash with special keytype %s in %s" % (k.keytype, p.name)) else: ## unknown special case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring unknown key with special keytype %s in %s" % (k.keytype, p.name)) else: traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_krb5key: krb5Key with keytype %s could not be parsed in %s. Ignoring this keytype." % (k.keytype, p.name)) elif p.name == "Primary:Kerberos-Newer-Keys": krb_blob = binascii.unhexlify(p.data) krb = ndr_unpack(drsblobs.package_PrimaryKerberosBlob, krb_blob) assert krb.version == 4 for k in krb.ctr.keys: if k.keytype not in keytypes: ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ctr4.key.keytype: %s" % k.keytype) try: key = heimdal.keyblock_raw(context, k.keytype, k.value) krb5SaltObject = heimdal.salt_raw(context, krb.ctr.salt.string) keys.append(heimdal.asn1_encode_key(key, krb5SaltObject, kvno)) keytypes.append(k.keytype) except: if k.keytype == 4294967156: ## in all known cases W2k8 AD uses keytype 4294967156 (=-140L) for this if k.value == up_blob: ## the known case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring arc4 NThash with special keytype %s in %s" % (k.keytype, p.name)) else: ## unknown special case ud.debug(ud.LDAP, ud.INFO, "calculate_krb5key: ignoring unknown key with special keytype %s in %s" % (k.keytype, p.name)) else: traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_krb5key: krb5Key with keytype %s could not be parsed in %s. Ignoring this keytype." % (k.keytype, p.name)) except Exception: import sys exc = sys.exc_info()[1] if type(exc.args) == type(()) and len(exc.args) == 2 and exc.args[1] == 'Buffer Size Error': ud.debug(ud.LDAP, ud.WARN, "calculate_krb5key: '%s' while unpacking supplementalCredentials:: %s" % ( exc, binascii.b2a_base64(sc_blob) ) ) ud.debug(ud.LDAP, ud.WARN, "calculate_krb5key: the krb5Keys from the PrimaryKerberosBlob could not be parsed. Continuing anyway.") else: traceback.print_exc() ud.debug(ud.LDAP, ud.ERROR, "calculate_krb5key: the krb5Keys from the PrimaryKerberosBlob could not be parsed. Continuing anyway.") return keys
def __get_zone_name(object): zoneName=object['attributes'].get('zoneName') if not zoneName: ud.debug(ud.LDAP, ud.WARN, 'Failed to get zone name for object %s' % (object['dn'])) raise return zoneName
def ntsd_to_ucs(s4connector, key, s4_object): ud.debug(ud.LDAP, ud.INFO, "ntsd_to_ucs S4 object: %s" % s4_object) ud.debug(ud.LDAP, ud.INFO, "ntsd_to_ucs S4 key: %s" % key) # modlist ml = [] # search Samba DS expicitly for hidden attribute # object dn is already mapped to the UCS DN: s4_dn = s4_object.get('dn') if not s4_dn: return # ignore try: s4_attributes = s4connector.lo_s4.get(s4_dn, attr=['nTSecurityDescriptor'], required=True) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.WARN, 'ntsd_to_ucs: S4 object (%s) not found' % s4_dn) return ntsd_ndr = s4_attributes.get('nTSecurityDescriptor') if not ntsd_ndr: ud.debug(ud.LDAP, ud.INFO, 'ntsd_to_ucs: nTSecurityDescriptor not found in attributes!') return # search in UCS/OpenLDAP DS to determine modify/add ucs_dn = s4_dn try: ucs_attributes = s4connector.lo.get(ucs_dn, attr=['msNTSecurityDescriptor']) except ldap.NO_SUCH_OBJECT: ud.debug(ud.LDAP, ud.WARN, 'sid_to_ucs: UCS object (%s) not found' % ucs_dn) return domain_sid = security.dom_sid(s4connector.s4_sid) s4_ntsd_sddl = decode_sd_in_ndr_to_sddl(domain_sid, ntsd_ndr[0]) ucs_ntsd_sddl = ucs_attributes.get('msNTSecurityDescriptor', [None])[0] if not ucs_ntsd_sddl or ucs_ntsd_sddl != s4_ntsd_sddl: ml.append(('msNTSecurityDescriptor', ucs_ntsd_sddl, s4_ntsd_sddl)) if ml: ud.debug(ud.LDAP, ud.INFO, 'ntsd_to_ucs: modlist = %s' % ml) serverctrls = [PostReadControl(True, ['entryUUID', 'entryCSN'])] response = {} s4connector.lo.lo.modify(ucs_dn, ml, serverctrls=serverctrls, response=response) for c in response.get('ctrls', []): # If the modify actually did something if c.controlType == PostReadControl.controlType: entryUUID = c.entry['entryUUID'][0] entryCSN = c.entry['entryCSN'][0] s4connector._remember_entryCSN_commited_by_connector(entryUUID, entryCSN)
def con2ucs(s4connector, key, object): _d = ud.function('dns: con2ucs') ud.debug(ud.LDAP, ud.INFO, 'dc con2ucs: Object (%s): %s' % (object['dn'], object)) # Search sambaDomainname object via sambaSID sambadomainnameObject = univention.admin.handlers.settings.sambadomain.lookup( None, s4connector.lo, 'sambaSID=%s' % object['attributes'].get('objectSid', [])[0]) if len(sambadomainnameObject) > 1: ud.debug( ud.LDAP, ud.WARN, 'dc con2ucs: Found more than one sambaDomainname object with sambaSID %s' % object['attributes'].get('objectSid', [])[0]) elif len(sambadomainnameObject) == 1: # Use the first sambaDomain sambadomainnameObject = sambadomainnameObject[0] # Do we modify this UCS object modify = False sync_times = [('maxPasswordAge', 'maxPwdAge'), ('minPasswordAge', 'minPwdAge'), ('lockoutDuration', 'lockoutDuration')] for (ucs_attr, s4_attr) in sync_times: ucs_time = _unixTimeInverval2seconds( sambadomainnameObject.get(ucs_attr, 0)) s4_time = _nano2s( long(object['attributes'].get(s4_attr, [0])[0]) * -1) if ucs_time != s4_time: sambadomainnameObject[ucs_attr] = [str(s4_time), 'seconds'] modify = True sync_integers = [('passwordHistory', 'pwdHistoryLength'), ('passwordLength', 'minPwdLength'), ('domainPwdProperties', 'pwdProperties')] for (ucs_attr, s4_attr) in sync_integers: ucs_val = sambadomainnameObject.get(ucs_attr, 0) s4_val = object['attributes'].get(s4_attr, [None])[0] if ucs_val != s4_val: sambadomainnameObject[ucs_attr] = s4_val modify = True if modify: sambadomainnameObject.modify() if s4connector.configRegistry.is_true('connector/s4/mapping/gpo', True): # Search DC object via ldap search dn, attr = s4connector.lo.search('objectClass=*', scope='base')[0] ml = [] ucs_val = attr.get('msGPOLink') s4_val = object['attributes'].get('gPLink') if ucs_val != s4_val: if 'msGPO' not in attr.get('objectClass', []): ml.append(('objectClass', '', 'msGPO')) ml.append(('msGPOLink', ucs_val, s4_val)) if ml: s4connector.lo.modify(dn, ml) return True
def ucs2con (s4connector, key, object): _d=ud.function('dc: ucs2con') ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: Object (%s): %s' % (object['dn'], object)) s4base_dn,s4base_attr = s4connector.lo_s4.lo.search_s(s4connector.s4_ldap_base, ldap.SCOPE_BASE, '(objectClass=*)')[0] ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: S4 object: %s' % (s4base_dn)) ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: S4 object: %s' % (s4base_attr)) if 'univentionBase' in object['attributes'].get('objectClass'): # DC object → sync GPO if s4connector.configRegistry.is_true('connector/s4/mapping/gpo', True): ucs_val = object['attributes'].get('msGPOLink', [None])[0] # msGPOLink is a single value s4_val = s4base_attr.get('msGPOLink') if ucs_val != s4_val: s4connector.lo_s4.lo.modify_s(s4connector.s4_ldap_base, [(ldap.MOD_REPLACE, 'gPLink', univention.s4connector.s4.compatible_modstring(ucs_val))]) elif 'sambaDomain' in object['attributes'].get('objectClass'): # Samba Domain object ml = [] sync_times = [ ('sambaMaxPwdAge', 'maxPwdAge'), ('sambaMinPwdAge', 'minPwdAge'), ('sambaLockoutDuration', 'lockoutDuration') ] for (ucs_attr, s4_attr) in sync_times: ucs_time = long(object['attributes'].get(ucs_attr, [0])[0]) s4_time = _nano2s(long(s4base_attr.get(s4_attr, [0])[0]) * -1) ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: ucs_time (%s): %s' % (ucs_attr, ucs_time)) ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: s4-time (%s): %s' % (s4_attr, s4_time)) if ucs_time != s4_time: ml.append( (ldap.MOD_REPLACE, s4_attr, [ str(_s2nano(ucs_time) * -1) ] ) ) sync_integers = [ ('sambaPwdHistoryLength', 'pwdHistoryLength'), ('sambaMinPwdLength', 'minPwdLength') ] for (ucs_attr, s4_attr) in sync_integers: ucs_val = object['attributes'].get(ucs_attr, str(0)) s4_val = s4base_attr.get(s4_attr, [0])[0] if ucs_val != s4_val: ml.append( (ldap.MOD_REPLACE, s4_attr, ucs_val ) ) if ml: ud.debug(ud.LDAP, ud.INFO, 'dc ucs2con: S4 object modlist: %s' % (ml)) s4connector.lo_s4.lo.modify_s(s4connector.s4_ldap_base, univention.s4connector.s4.compatible_modlist(ml)) return True
# License with the Debian GNU/Linux or Univention distribution in file # /usr/share/common-licenses/AGPL-3; if not, see # <http://www.gnu.org/licenses/>. import univention.debug2 as ud ud.init( '/tmp/univention.debug2.log', 1, 1) ud.set_level( ud.PROCESS, ud.ERROR ) ud.set_level( ud.LISTENER, ud.WARN ) ud.set_level( ud.NETWORK, ud.PROCESS ) ud.set_level( ud.LDAP, ud.INFO ) ud.set_level( ud.ADMIN, ud.ALL ) for lvl in [ ud.ERROR, ud.WARN, ud.PROCESS, ud.INFO, ud.ALL ]: for mod in [ ud.ADMIN, ud.PROCESS, ud.LISTENER, ud.NETWORK, ud.LDAP ]: ud.debug( mod, lvl, '==> send msg to %s with level %s' % (mod, lvl) ) ud.set_level( ud.ADMIN, ud.ERROR ) ud.debug( ud.ADMIN, ud.ERROR, '==> admin error' ) ud.debug( ud.ADMIN, ud.WARN, '==> admin warn' ) ud.debug( ud.ADMIN, ud.PROCESS, '==> admin process' ) ud.debug( ud.ADMIN, ud.INFO, '==> admin info' ) ud.debug( ud.ADMIN, ud.ALL, '==> admin all' ) ud.set_level( ud.LDAP, ud.INFO ) ud.debug( ud.LDAP, ud.ERROR, '==> ldap error' ) ud.debug( ud.LDAP, ud.WARN, '==> ldap warn' ) ud.debug( ud.LDAP, ud.PROCESS, '==> ldap process' ) ud.debug( ud.LDAP, ud.INFO, '==> ldap info' ) ud.debug( ud.LDAP, ud.ALL, '==> ldap all' )