Einzelnen Beitrag anzeigen

Benutzerbild von Sherlock
Sherlock

Registriert seit: 10. Jan 2006
Ort: Offenbach
3.760 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Kerberos für SOAP per WSDL Import

  Alt 5. Mär 2015, 15:08
Sodele, ich würde das hier gerne weiterspinnen. Meine Recherchen im Netz führen mich zu der Erkenntnis, daß es niemanden gibt, der bereits erfolgreich mit Delphi in OS ein Kerberos Ticket angefordert hat. Alle verweisen auf die üblichen Verdächtigen, die bei SO, oder eben der msdn verlinkt sind. Knackpunkt ist folgendes:
Es ist eben nicht damit getan ein paar API-Funktionen aufzurufen. Ein Kerberos-Ticket muss man sich auch per UDP-Kommunikation mit dem Kerberos-Server verdienen. Das führt dazu, daß selbst die zahlreichen voneinander abgeschrieben Beispile in den C-Dialekten an der Stelle äusserst schwammig werden. Ich halte Kerberos mittlerweile für eine Art weissen Wal, den jeder angeblich mal gesehen hat

Die API-Aufrufe bekommt man mit Hilfe von ein bis zwei Indy-Units einigermaßen zusammengeklöppelt. Aber selbst dann muss der korrekte SPN von Aussen gesetzt werden, Es gibt keine API, die einem das abnimmt. Hier die Source meines kleinen Testprojekts:

Delphi-Quellcode:
uses IdSSPI, IdAuthenticationSSPI

procedure TForm2.Button1Click(Sender: TObject);
var
  secfunc: SecurityFunctionTableA;
  sec_Entry: SECURITY_STATUS;
  pszTargetName: PSEC_CHAR;
  hCredential: SecHandle;
  tsExpiry: TimeStamp;
  hNewContext: CtxtHandle;
  Output: SecBufferDesc;
  token: SecBuffer;
  fContextAttr: ULONG;
  pPkgInfo: PSecPkgInfoA;
  TokenPointer: PByteArray;
  InitSecurityInterfaceA: function: PSecurityFunctionTableA; stdcall;
begin
  try
    TokenPointer := nil;
    InitSecurityInterfaceA := GetProcAddress(GetModuleHandle('secur32.dll'), 'InitSecurityInterfaceA');
    if Assigned(InitSecurityInterfaceA) then
      secfunc := InitSecurityInterfaceA^;

    sec_State:= secfunc.QuerySecurityPackageInfoA(
      PAnsiChar('Kerberos'),
      @pPkgInfo
      );

    if sec_State = SEC_E_OK then
      sec_State := secfunc.AcquireCredentialsHandleA(
        nil,
        pPkgInfo^.Name,
        SECPKG_CRED_OUTBOUND,
        nil,
        nil,
        nil,
        nil,
        @hCredential,
        @tsExpiry
        );

    Output.ulVersion := SECBUFFER_VERSION;
    Output.cBuffers := 1;
    Output.pBuffers := @token;

    GetMem(TokenPointer, ppkginfo^.cbMaxToken);

    token.cbBuffer := ppkginfo^.cbMaxToken;
    token.BufferType := SECBUFFER_TOKEN;
    token.pvBuffer := tokenpointer;

    if sec_State = SEC_E_OK then
      sec_State := secfunc.InitializeSecurityContextA(
        @hCredential,
        nil,
        PAnsiChar('RestrictedKrbHost/FM-DC01.mydomain.int'), // Muss man eben irgendwie selbst herausfinden
        ISC_REQ_DELEGATE + ISC_REQ_MUTUAL_AUTH,
        0,
        SECURITY_NATIVE_DREP,
        nil,
        0,
        @hNewContext,
        @Output,
        @fContextAttr,
        @tsExpiry
        );

    if sec_State = SEC_I_CONTINUE_NEEDED then // Derzeit ist genau das der Status
    begin
      // Output irgendwie an den Kerberos-Server senden und die Anwort mit erneutem Aufruf von
      // InitializeSecurityContextA verwursten
    end;

    if sec_State = SEC_E_OK then
    begin
      ShowMessage('YAY!');
    end
    else
      case sec_State of
        SEC_E_OK: ShowMessage('YAY!');
        SEC_I_COMPLETE_AND_CONTINUE: ShowMessage('SEC_I_COMPLETE_AND_CONTINUE');
        SEC_I_COMPLETE_NEEDED: ShowMessage('SEC_I_COMPLETE_NEEDED');
        SEC_I_CONTINUE_NEEDED: ShowMessage('SEC_I_CONTINUE_NEEDED');
        SEC_I_INCOMPLETE_CREDENTIALS: ShowMessage('SEC_I_INCOMPLETE_CREDENTIALS');
        SEC_E_INSUFFICIENT_MEMORY: ShowMessage('SEC_E_INSUFFICIENT_MEMORY');
        SEC_E_INTERNAL_ERROR: ShowMessage('SEC_E_INTERNAL_ERROR');
        SEC_E_INVALID_HANDLE: ShowMessage('SEC_E_INVALID_HANDLE');
        SEC_E_INVALID_TOKEN: ShowMessage('SEC_E_INVALID_TOKEN');
        SEC_E_LOGON_DENIED: ShowMessage('SEC_E_LOGON_DENIED');
        SEC_E_NO_AUTHENTICATING_AUTHORITY: ShowMessage('SEC_E_NO_AUTHENTICATING_AUTHORITY');
        SEC_E_NO_CREDENTIALS: ShowMessage('SEC_E_NO_CREDENTIALS');
        SEC_E_TARGET_UNKNOWN: ShowMessage('SEC_E_TARGET_UNKNOWN');
        SEC_E_UNSUPPORTED_FUNCTION: ShowMessage('SEC_E_UNSUPPORTED_FUNCTION');
        SEC_E_WRONG_PRINCIPAL: ShowMessage('SEC_E_WRONG_PRINCIPAL');
      else
        ShowMessage('UNKNOWN ERROR Code. Last Error:' + IntToStr(GetLastError));
      end;
  finally
    secfunc.FreeCredentialsHandle(@hCredential);
    secfunc.FreeContextBuffer(pPkgInfo);
    FreeMem(TokenPointer);
  end;
end;
Wer hat sowas mal wirklich vollumfänglich implementiert? (Der SOAP-Teil aus meinem Startpost kann erstmal getrost vergessen werden)

Sherlock
Oliver
Geändert von Sherlock (Morgen um 16:78 Uhr) Grund: Weil ich es kann
  Mit Zitat antworten Zitat