Printout Header
RSS Feed

Attribute für AD User : pwdLastSet


Das Active Directory Attribut pwdLastSet zeigt den exakten Zeitpunkt, an dem das Passwort eines Accounts zum letzten Mal geändert wurde. Es spielt dabei keine Rolle, ob der Benutzer das Passwort selbst geändert hat oder ob es von einem Administrator zurückgesetzt wurde.

pwdLastSet


LDAP Name pwdLastSet
Datentyp Integer8 (64 bit signed numeric)
Multivalue (Array) Nein
System Flags

0x10

Search Flags 0x0
Im Global Catalog? Nein
Attribute ID 1.2.840.113556.1.4.96
AD DB Attributname Pwd-Last-Set
ADSI datatype 10 - LargeInteger
LDAP syntax 1.2.840.113556.1.4.906 - Microsoft Large Integer
Wird verwendet in ... > W2K
Schema Doku Microsoft - MSDN


Falls der Wert von pwdLastSet gleich 0 ist, dann bedeutet das, dass der Benutzer sein Passwort bei der nächsten Anmeldung ändern muss:

AD Database Structure

Der pwdLastSet Wert ist ein Microsoft Large Integer, es handelt sich hier um numerische Werte von 8 Byte (64 bit) Länge - aus diesem Grund werden sie oft auch Integer8 Werte genannt:

Minimum value:
-9223372036854775808 (-2^63)  or
hex 0x8000000000000000

Maximum value:

9223372036854775807 (2^63 - 1) or
hex 0x7FFFFFFFFFFFFFFF


Es gibt einen anderen Artikel im SelfADSI Tutorial über Microsoft Integer8 Werte, die Datum/Zeit-Stempel oder Zeitintervalle repräsentieren.

Der Wert, der in einem lastLogon Attribut gespeichert ist, repräsentiert ein Datum und ein Uhrzeit, ausgedrückt in 100-Nanosekunden-Schritten seit dem 1. Januar 1600, 0:00 Uhr.

Nebenbei erwähnt wird diese Spezifikation auch in der Microsoft FileTime Struktur verwendet. Zusätzlich dazu sollte man wissen, dass alle Datums- und Zeitangaben in der Active Directory Datenbank intern im UTC Zeitformat gespeichert werden (Universal Coordinated Time) - das ist (fast) das gleiche wie die frühere Greenwich Meantime (GMT). Wenn Sie also ein System in der mitteleuropäische Zeitzone zur Sommerzeit betreiben (MEZ, das ist GMT + 2), so müssen Sie die Integer8 Attributwerte entsprechend umrechnen, wenn Sie sie in lokaler Zeit sehen wollen.

Wenn Sie nun das pwdLastSet Attribut eines bestimmten Benutzers lesen wollen, so müssen Sie zuerst mit dem zurückgegebenen Large Integer Wert umgehen können, der aus zwei Teilwerten besteht: Dem HighPart und dem LowPart. Diese Teilwerte sind durch das ADSI Interface für diesen Datentyp zugreifbar. Aber: Sie müssen beim Auslesen eines Large Integer/Integer8 Attributes in einm ADSI Script unbedingt mit einem vorangehenden 'Set' ein Objekt erzeugen. Ansonsten kann man auf die entsprechenden ADSI Interface-Eigenschaften 'HighPart' und 'LowPart' nicht zugreifen.

Umwandlung eins pwdLastSet Wertes in eine lesbare Datum-/Zeitangabe

Hier kommt also ein Script, indem ein Integer8 Wert in ein Datum plus Uhrzeit umgewandelt werden kann, inklusive der Anpassung an die lokale Zeitzone (die Abweichung von UTC ermitteln wir aus der Registry):

'Sie müssen hier den Distinguished Name eines Objekts aus Ihrer eigene Umgebung angeben! Set obj = GetObject("LDAP://cn=Administrator,cn=Users,dc=cerrotorre,dc=de") set llValue = obj.Get("pwdLastSet") 'nicht vergessen: mit 'SET' ein Object erzeugen WScript.Echo LargeIntegerToDate(llValue) Function LargeIntegerToDate(value) 'nimmmt einen Microsoft LargeInteger Wert (Intger8) und gibt das entsprechende Datum plus Uhrzeit zurück 'erst die lokale Zeitabweichung aus der Registry auslesen Set sho = CreateObject("Wscript.Shell") timeShiftValue = sho.RegRead("HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias") If IsArray(timeShiftValue) Then timeShift = 0 For i = 0 To UBound(timeShiftValue) timeShift = timeShift + (timeShiftValue(i) * 256^i) Next Else timeShift = timeShiftValue End If 'der Large Integer wird mit zwei Long Werten gehandhabt (HighPart und LowPart) i8High = value.HighPart i8Low = value.LowPart If (i8Low < 0) Then i8High = i8High + 1 End If 'das Datum und die Uhrzeit ausrechnen: 100-Nanosecond-Schritte seit dem 1. Januar 1601 If (i8High = 0) And (i8Low = 0) Then LargeIntegerToDate = #1/1/1601# Else LargeIntegerToDate = #1/1/1601# + (((i8High * 2^32) + i8Low)/600000000 - timeShift)/1440 End If End Function

Alle Benutzer suchen, deren Passwort sich seit 6 Monaten nicht geändert haben

Und hier ein weiteres Script, indem wir umgekehrt die Umwandlung eines Datums/Zeit-Wertes in den entsprechenden Integer8-Wert meistern müssen - wir wollen alle Benutzer finden, deren Passwort sich seit 6 Monaten nicht geändert hat. Um den korrekten LDAP Filter zu bauen, brauchen wir den Large Integer Wert für das Datum/die Uhrzeit von vor sechs Monaten... Wenn Sie nicht genau wissen, wie die Suchfunktion dieses Scripts arbeitet: Es gibt einen detaillierten Artikel hier im SelfADSI Tutorial, indem die LDAP Suche mit ADO Techniken erklärt wird.

'Sie müssen hier andere Namen und Anmeldedaten aus Ihrer eigene Umgebung angeben! serverName = "nadrash.cerrotorre.de" baseStr = "dc=cerrotorre,dc=de" userName = "philipp@cerrotorre.de" userPass = "secret" 'Datum+Zeit von vor 6 Monaten ermitteln, DateAdd ist eine Standardfunktion von VBScript, wir könnten auch Tag, Jahr usw ermitteln sixMonthsBefore= DateAdd("m", -6, Now) 'Zeigt den Integer8 Wert des betreffenden Datums+Uhrzeit... sixMonthsBeforeValue = DateToLargeIntegerString(sixMonthsBefore) 'Beide Werte anzeigen lassen: WScript.Echo "Timestamp von vor sechs Monaten: " & sixMonthsBefore & " => " & sixMonthsBeforeValue 'Nun eine Liste aller Benutzer ausgeben lassen, deren Passwort sich innerhalb der letzen Woche nicht geändert hat: filterStr = "(&(objectclass=user)(pwdLastSet<=" & sixMonthsBeforeValue & "))" 'Suche Ausführen! Set ado = CreateObject("ADODB.Connection") 'Neue ADO Connection erzeugen ado.Provider = "ADSDSOObject" 'Die ADSI-Schnittstelle verwenden ado.Properties("User ID") = userName 'Credentials angeben - wenn sie diese zwei Zeilen weglassen, ... ado.Properties("Password") = userPass '... wird die aktuellen Anmeldedaten verwendet ado.Properties("Encrypt Password") = True ado.Open "ADS-Search" 'Beliebigen Namen für die Connection vergeben Set adoCmd = CreateObject("ADODB.Command") 'Neues ADO-Kommando erzeugen adoCmd.ActiveConnection = ado 'Zuordnung zur bestehenden ADO-Connection adoCmd.Properties("Page Size") = 1000 'Parameter für Paged Result Suche auf 1000 setzen (=AD Standard) adoCmd.Properties("Cache Results") = True adoCmd.CommandText = "<LDAP://"& serverName & "/" & baseStr &">;" & filterStr & ";distinguishedName;subtree" Set objectList = adoCmd.Execute 'Suche durchführen While Not objectList.EOF WScript.Echo objectList.Fields("distinguishedName") 'Output: Distinguished Name dieser Objekte objectList.MoveNext 'zum nächsten gefundenen Objekt Wend Function DateToLargeIntegerString(value) 'nimmt ein Datun+Uhrzeit und gibt den entsprechenden Microsoft LargeInteger Wert (Intger8) zurück 'erst die lokale Zeitabweichung aus der Registry auslesen Set sho = CreateObject("Wscript.Shell") timeShiftValue = sho.RegRead("HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias") If IsArray(timeShiftValue) Then timeShift = 0 For i = 0 To UBound(timeShiftValue) timeShift = timeShift + (timeShiftValue(i) * 256^i) Next Else timeShift = timeShiftValue End If 'die Zeit von lokaler Zeit in UTC umrechnen value = DateAdd("n", timeShift, value) 'wieviele Sekunden seit 1601 sind vergangen? secs = DateDiff("s", #1/1/1601#, value) 'in Nano-Sekunden umrechnen DateToLargeIntegerString = CStr(secs) & "0000000" End Function