externer LDAP Connect mit erweiterter Rückmeldung bei falschen Userdaten

  • ich möchte mich von einem PC außerhalb meiner AD-DOMAIN am LDAP mit Benutzername und Kennwort anmelden und benötige die erweiterten Rückmeldungen wie z.B.:

    525 - user not found
    52e - invalid credentials
    530 - not permitted to logon at this time
    532 - password expired
    533 - account disabled
    701 - account expired
    773 - user must reset password

    um einen LDAP-CONNECT hinzubekommen, habe ich bislang drei Varianten gefunden:

    1. per ADSDSOObject – dies gibt mir aber im Fehlerfall nicht die nötigen Infos zurück - ich hab zumindest keine Möglichkeit gefunden

    2. per $objDSO.OpenDSObject – dies gibt mir ebenfalls keine erweiterten RETURN-Meldungen zurück - ich hab zumindest keine Möglichkeit gefunden

    3. per DLLCALL auf die WLAPD32.DLL

    hierzu habe ich mit folgenden Quellcode experimentiert

    [autoit]


    #include <array.au3>
    #include <WinAPIError.au3>
    #include <WINAPI.au3>
    $oMyError = ObjEvent("AutoIt.Error", "ComError")
    Global $objDSO = ObjGet ("LDAP:")
    Global $Host = "xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx"
    Global $Port = "389"

    [/autoit][autoit][/autoit][autoit]

    Global $fkt
    Const $ADS_SECURE_AUTHENTICATION = 1
    Const $ADS_USE_SEALING = 64 ;'0x40
    Const $ADS_USE_SIGNING = 128 ;'0x80
    Const $ADS_SERVER_BIND= 512 ;0x200

    [/autoit][autoit][/autoit][autoit]

    Const $LDAP_OPT_ERROR_NUMBER = 0x31
    Const $LDAP_OPT_ERROR_STRING = 0x32
    Const $LDAP_OPT_SERVER_ERROR = 0x33
    Const $LDAP_OPT_SERVER_EXT_ERROR = 0x34
    Const $LDAP_OPT_PROTOCOL_VERSION = 0x11

    [/autoit][autoit][/autoit][autoit]

    Const $LDAP_AUTH_NEGOTIATE = 0x0400
    Const $LDAP_AUTH_DIGEST = 0x4000
    Const $LDAP_AUTH_OTHERKIND = 0x86
    Const $LDAP_AUTH_SIMPLE = 0x80
    Const $LDAP_AUTH_NTLM = 0x01000

    [/autoit][autoit][/autoit][autoit]

    Const $SEC_WINNT_AUTH_IDENTITY_UNICODE = 0x2
    Const $SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1

    [/autoit][autoit][/autoit][autoit]

    Global $username = "username"
    Global $Passwort = "secret"
    Global $domain = "DOMAIN"

    [/autoit][autoit][/autoit][autoit]

    Global $ldapldll = DllOpen("WLDAP32.DLL")

    [/autoit][autoit][/autoit][autoit]

    $ldapinit = ldapinit()
    ;errorpruefung($ldapinit, $fkt)
    $ldapsetOption = ldap_set_option()
    ;errorpruefung($ldapsetOption, $fkt)
    $ldapcon = ldap_simple_bind_s()
    errorpruefung($ldapcon, $fkt)
    $ldapgetoption = ldap_get_option()
    errorpruefung($ldapcon, $fkt)

    [/autoit][autoit][/autoit][autoit]

    ldap_unbind()
    DllClose("WLDAP32.DLL")
    ConsoleWrite(@CRLF)
    exit

    [/autoit][autoit][/autoit][autoit]

    Func ldapinit()
    $Hostptr = DllStructCreate("char[" & (StringLen($Host) + 1) & "]")
    DllStructSetData($Hostptr,1,$Host)
    $fkt = "ldap_open"
    Return DllCall($ldapldll, "ptr", $fkt, "ptr",DllStructGetPtr($Hostptr), "ULONG", "")
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func ldap_simple_bind_s()
    $fkt = "ldap_simple_bind_s"
    $dn = DllStructCreate("char[" & (StringLen($domain & "\" & $username) + 1) & "]")
    DllStructSetData($dn,1, $domain & "\" & $username)
    $passwd = DllStructCreate("char[" & (StringLen($Passwort) + 1) & "]")
    DllStructSetData($passwd,1, $Passwort)
    Return DllCall($ldapldll, "ULONG", $fkt, "ptr", $ldapinit[0], "ptr",DllStructGetPtr($dn), "ptr",DllStructGetPtr($passwd), "ULONG", $LDAP_AUTH_SIMPLE)
    ;_WinAPI_ShowError(Hex(_WinAPI_GetLastError()) & ": " & _WinAPI_GetLastErrorMessage())
    ;Return $ldapcon
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func ldap_get_option()
    ;If $ldapcon[0] <> 0 Then
    $ldaperr = DllStructCreate("char[255]")
    DllStructSetData($ldaperr,1, 0)
    $fkt = "ldap_get_option"
    $ergebnis = DllCall($ldapldll, "ULONG", $fkt, "ptr", $ldapinit[0], "int", $LDAP_OPT_SERVER_ERROR, "ptr", DllStructGetPtr($ldaperr))
    ;$fkt = "LdapGetLastError"
    ;$lasterror = DllCall($ldapldll, "ULONG", $fkt)
    ;errorpruefung($lasterror, $fkt)
    ;ConsoleWrite(Hex(_WinAPI_GetLastError()) & ": " & _WinAPI_GetLastErrorMessage())
    $b = DllStructCreate("DWORD",DllStructGetPtr($ldaperr,1))
    errorpruefung($ldaperr, $fkt)
    errorpruefung($b, $fkt)
    Return $ergebnis
    ;_ArrayDisplay($ergebnis)
    ;EndIf
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func ldap_set_option()
    ;If $ldapcon[0] <> 0 Then
    $ldaperr = DllStructCreate("ULONG")
    DllStructSetData($ldaperr,1, 3)
    $fkt = "ldap_set_option"
    ;$ldaperr = 0
    $ergebnis = DllCall($ldapldll, "ULONG", $fkt, "ptr", $ldapinit[0], "int", $LDAP_OPT_PROTOCOL_VERSION, "ptr", DllStructGetPtr($ldaperr))
    If @error <> 0 Then ConsoleWrite(Hex(_WinAPI_GetLastError()) & ": " & _WinAPI_GetLastErrorMessage())
    errorpruefung($ldaperr, $fkt)
    Return $ergebnis
    ;_ArrayDisplay($ergebnis)
    ;EndIf
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func ldap_unbind()
    $fkt = "ldap_unbind"
    $ldapunbind = DllCall($ldapldll, "ULONG", $fkt, "ptr", $ldapinit[0])
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    ;$ldapfree = DllStructCreate("char[" & (StringLen($LDAP_OPT_SERVER_ERROR) + 1) & "]")
    ;DllStructSetData($ldapfree,1, $LDAP_OPT_SERVER_ERROR)
    ;$fkt = "ldap_memfree"
    ;DllCall($ldapldll, "ptr", $fkt, "ptr", DllStructGetPtr($ldapfree))

    [/autoit][autoit][/autoit][autoit]

    ;$fkt = "LdapGetLastError"
    ;$lasterror = DllCall($ldapldll, "ULONG", $fkt)
    ;errorpruefung($lasterror, $fkt)

    [/autoit][autoit][/autoit][autoit]

    Func errorpruefung($ID, $Name = "")
    If IsArray($ID) THEN
    ConsoleWrite(@CRLF & $Name & " ")
    For $I = 0 To UBound($ID)-1
    ConsoleWrite(@CRLF & $I & ": " & $ID[$I] & " - " & hex($ID[$I],8) & " - " & dec(hex($ID[$I],8)) & " " & DllStructGetData($ID, $I))
    Next
    ConsoleWrite(@CRLF)
    ;_ArrayDisplay($ID)
    Else
    ConsoleWrite(@CRLF & $Name & " " & DllStructGetData($ID, 1) & " - " & hex(DllStructGetData($ID, 1),8) & " " & DllStructGetData($ID, 2) & " - " & hex(DllStructGetData($ID, 2),8) & @CRLF)
    EndIf
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func ComError()
    if IsObj($oMyError) then
    $Fehlertext = ("Name: " & @TAB & $oMyError.description & @CRLF & _
    "Beschreibung: " & @TAB & $oMyError.windescription & @CRLF & _
    "Fehlernummer: " & @TAB & hex($oMyError.number,8) & @CRLF & _
    "Fehlernummer1: " & @TAB & $oMyError.number & @CRLF & _
    "Letzter Fehler: " & @TAB & $oMyError.lastdllerror & @CRLF & _
    "Zeile: " & @TAB & $oMyError.scriptline & @CRLF & _
    "Quelle: " & @TAB & $oMyError.source & @CRLF & _
    "err.helpfile is: " & @TAB & $oMyError.helpfile & @CRLF & _
    "err.helpcontext is: " & @TAB & $oMyError.helpcontext )
    SetError($Fehlertext)
    ConsoleWrite(@CRLF & $Fehlertext & @CRLF)
    ;SetError(Hex($oError.Number))
    else
    SetError(1)
    endif
    EndFunc

    [/autoit]


    - bei Eingabe eines richtigen Usernamens + Kennwort bekomme ich ERROR 0x0 zurück – also alles OK.
    - bei Falscheingabe bekomme ich ERROR 0x31 zurück – also LDAP_INVALID_CREDENTIALS – also auch OK.

    Wenn ich nun ldap_get_option mit LDAP_OPT_SERVER_ERROR abfrage, bekomme ich leider keine auswertbare Antwort – die letzen 4 HEX-Werte sind immer 1828, egal, ob der Benutzer nicht existiert oder das Passwort falsch ist oder der Benutzer deaktiviert wurde oder oder… Ich bekomme auch keinen String zurück, sondern nur einen HEX-Wert

    Wie komme ich zu dem erweiterten Rückgabestring, der z.B. bei Java oder PHP so aussieht:
    javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C09030B, comment: AcceptSecurityContext error, data 525, v893
    Hier steht fast ganz hinten der Wert 525 - also in dem Bsp. User not found

    Die Microsoftseiten sind z.B. hier für ldap_get_option und hier für LDAP_OPT_SERVER_ERROR

    Ich muss nicht mit DLL-CALL arbeiten, ich benötige nur den erweiterten Rückgabestring in AUTOIT. Gibt’s noch andere Methoden, mit AUTOIT auf LDAP von extern zuzugreifen UND den erweiterten Rückgabestring zu bekommen?
    Der LDAP-Server muss die Antwort geben, da ein Linux-System von unserem LDAP auch die Antwort bekommt.
    Mir scheint hier, daß ich ein Problem habe, den TCHAR-Rückgabestring auszuwerten - an den komm ich einfach nicht ran...

    Ich bitte um Hilfe…

    Sorry, wenn mein Quellcode nicht gleich verständlich ist - ich bin Anfänger auf dem Gebiet DLLCall - das ist alles mein Werk der letzten 5 verzweifelten Tage...

    2 Mal editiert, zuletzt von card0384 (7. März 2010 um 12:52)

  • Ja, hab ich, das UDF nutzt angesprochene Variante 1 - ADsDSOObject.
    Da bekomm ich aber den erweiterten Fehler nicht zurück.
    Ich bekomm als Rückmeldung nur "Geht" oder "Geht nicht".
    Ich muss aber "Warum geht es nicht" zurückbekommen. Lt. MS geht dies nur mittels LDAP_OPT_SERVER_ERROR

    Für mich ist wichtig, ob z.B. das Passwort abgelaufen oder der Nutzer deaktiviert oder der Nutzer unbekannt oder das Passwort falsch ist.
    Je nach Returncode muss ich unterschiedliche Aktionen ausführen - z.B. bei Passwort abgelaufen die Routine zum Passwort ändern durchführen oder bei anderen Fehlern dem Nutzer die entsprechenden Hinweise ausgeben.

    Oder kennt jemand eine andere Variante?

    Einmal editiert, zuletzt von card0384 (5. März 2010 um 13:47)

  • ist kein Spezi hier, der sich mit dll-call auskennt und eine Idee hat, wie ich an den String komme?
    Der String muss lt. Microsoft ein TCHAR sein, gibts da Besonderheiten in Autoit?

  • Ich hab nen Network-Sniffer auf meine Anfrage "gehetzt" und der ergab im Protokollstapel den Text:

    BindResponse: Status: Invalid Credentials, MatchedDN: NULL, ErrorMessage: 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 52e, vece

    Der Text kam bereits beim ldap_simple_bind_s

    Und da steht ja bereits mein gewünschtes Ergebniss drin - ganz hinten vor "vece", der data-Sector :rolleyes:

    Wie kann ich den Text (String) im Autoit sichtbar machen?

    AutoIt gibt mir nur
    0: 49 - 00000031 - 49 0
    1: 0x0000000000EC16C8 - 00EC16C8 - 15472328 0
    2: 0x0000000003B9E910 - 03B9E910 - 62515472 0
    3: 0x0000000003B9E970 - 03B9E970 - 62515568 0
    4: 128 - 00000080 - 128 0

    für mein Array zurück, dies ist aber das Ergebniss - nicht der Error-String.
    Wie kann ich den Errorstring in eine Variable packen?

    Die Funktion:

    [autoit]

    Func ldap_simple_bind_s()
    $fkt = "ldap_simple_bind_s"
    $dn = DllStructCreate("char[" & (StringLen($domain & "\" & $username) + 1) & "]")
    DllStructSetData($dn,1, $domain & "\" & $username)
    $passwd = DllStructCreate("char[" & (StringLen($Passwort) + 1) & "]")
    DllStructSetData($passwd,1, $Passwort)
    $ldapcon = DllCall($ldapldll, "ULONG", $fkt, "ptr", $ldapinit[0], "ptr",DllStructGetPtr($dn), "ptr",DllStructGetPtr($passwd), "ULONG", $LDAP_AUTH_SIMPLE)
    ConsoleWrite(@CRLF & Hex(_WinAPI_GetLastError()) & ": " & _WinAPI_GetLastErrorMessage())
    Return $ldapcon
    EndFunc

    [/autoit]

    Gibt mir bei ConsoleWrite nur "erfolgreich beendet" zurück - stimmt ja auch der DllCall war ja auch erfolgreich (nur nicht in der Art des Ergebnisses)
    Wie bekomm ich die Fehlermeldung, die im TCP-IP-String zurückgegeben wird, in Autoit sichtbar?

    Hat das etwas mit dem DllCallbackRegister zu tun?
    Oder mit diesem ominösem lparam oder wparam (hab ich immer mal gelesen ohne zu wissen, was dies ist)?
    Oder lieg ich da auf dem Holzweg?
    Davon hab ich leider noch keine Ahnung...

    Die Informationen sind DA, ich bekomm sie nur nicht sichtbar...
    Bitte helft mir, ich bin schon so weit in dem Sumpf drin - ich freu mich über jede Hilfe-Leine ;)

    Einmal editiert, zuletzt von card0384 (8. März 2010 um 15:27)

  • Es gibt noch ne kleine Ergänzung:
    Ich stelle gerade fest, das der Netzwerksniffer auch bei folgendem Script die erweiterten Fehlermeldung aufzeichnet:

    [autoit]

    Global $objDSO = ObjGet ("LDAP:")
    Const $ADS_SERVER_BIND= 512 ;0x200
    Global $username = "DOMAIN\Username"
    Global $Passwort = "secret"
    $obj1 = $objDSO.OpenDSObject("LDAP://xxx.xxx.xxx.xxx", $username, $Passwort, $ADS_SERVER_BIND)

    [/autoit]

    Die Rückmeldung mit falschen Userdaten, die das LDAP an das Autoit-Script sendet:

    Result: Status: Invalid Credentials, MatchedDN: NULL, ErrorMessage: 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 52e, vece

    Auch hier bekomme ich aber leider die Meldung im Autoit nicht sichtbar - hat hier jemand ne Idee?
    Ich brauch den Hex-Code, welcher in der Result-Zeile rechts neben "data" und links neben "vece" steht

    Gibts ne andere Methode statt $objDSO.OpenDSObject für das LDAP-Objekt, wie ich den Fehlercomment darstellen kann?
    Da bräuchte ich den ganzen DllCall überhaupt nicht ausführen...

    Einmal editiert, zuletzt von card0384 (8. März 2010 um 16:59)

  • Von hinten durch die Brust in's Auge habe ich es doch geschafft. Details findest Du hier . Ich habe das ganze bereits in Version 0.38 des AD UDFs eingebaut.