restemplate/indy/Protocols/IdAuthenticationSSPI.pas

1333 lines
42 KiB
Plaintext

{
$Project$
$Workfile$
$Revision$
$DateUTC$
$Id$
This file is part of the Indy (Internet Direct) project, and is offered
under the dual-licensing agreement described on the Indy website.
(http://www.indyproject.org/)
Copyright:
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
$Log$
}
{
Rev 1.3 6/11/2004 9:33:58 AM DSiders
Added "Do not Localize" comments.
Rev 1.2 13.1.2004 ã. 17:26:06 DBondzhev
Added Domain property
Rev 1.1 4/12/2003 10:24:04 PM GGrieve
Fix to Compile
Rev 1.0 11/14/2002 02:13:50 PM JPMugaas
}
unit IdAuthenticationSSPI;
{
Implementation of the NTLM authentication with SSPI
Author: Alex Brainman
Copyright: (c) Chad Z. Hower and The Winshoes Working Group.
}
{$DEFINE SET_ENCRYPT_IN_FT_WITH_GETPROCADDRESS_FUDGE}
interface
{$i IdCompilerDefines.inc}
uses
IdGlobal,
IdAuthentication,
IdCoder,
Windows,
SysUtils,
IdSSPI;
const
SEC_E_OK = 0;
{$EXTERNALSYM SEC_E_OK}
SEC_E_INSUFFICIENT_MEMORY = HRESULT($80090300);
{$EXTERNALSYM SEC_E_INSUFFICIENT_MEMORY}
SEC_E_INVALID_HANDLE = HRESULT($80090301);
{$EXTERNALSYM SEC_E_INVALID_HANDLE}
SEC_E_UNSUPPORTED_FUNCTION = HRESULT($80090302);
{$EXTERNALSYM SEC_E_UNSUPPORTED_FUNCTION}
SEC_E_TARGET_UNKNOWN = HRESULT($80090303);
{$EXTERNALSYM SEC_E_TARGET_UNKNOWN}
SEC_E_INTERNAL_ERROR = HRESULT($80090304);
{$EXTERNALSYM SEC_E_INTERNAL_ERROR}
SEC_E_SECPKG_NOT_FOUND = HRESULT($80090305);
{$EXTERNALSYM SEC_E_SECPKG_NOT_FOUND}
SEC_E_NOT_OWNER = HRESULT($80090306);
{$EXTERNALSYM SEC_E_NOT_OWNER}
SEC_E_CANNOT_INSTALL = HRESULT($80090307);
{$EXTERNALSYM SEC_E_CANNOT_INSTALL}
SEC_E_INVALID_TOKEN = HRESULT($80090308);
{$EXTERNALSYM SEC_E_INVALID_TOKEN}
SEC_E_CANNOT_PACK = HRESULT($80090309);
{$EXTERNALSYM SEC_E_CANNOT_PACK}
SEC_E_QOP_NOT_SUPPORTED = HRESULT($8009030A);
{$EXTERNALSYM SEC_E_QOP_NOT_SUPPORTED}
SEC_E_NO_IMPERSONATION = HRESULT($8009030B);
{$EXTERNALSYM SEC_E_NO_IMPERSONATION}
SEC_E_LOGON_DENIED = HRESULT($8009030C);
{$EXTERNALSYM SEC_E_LOGON_DENIED}
SEC_E_UNKNOWN_CREDENTIALS = HRESULT($8009030D);
{$EXTERNALSYM SEC_E_UNKNOWN_CREDENTIALS}
SEC_E_NO_CREDENTIALS = HRESULT($8009030E);
{$EXTERNALSYM SEC_E_NO_CREDENTIALS}
SEC_E_MESSAGE_ALTERED = HRESULT($8009030F);
{$EXTERNALSYM SEC_E_MESSAGE_ALTERED}
SEC_E_OUT_OF_SEQUENCE = HRESULT($80090310);
{$EXTERNALSYM SEC_E_OUT_OF_SEQUENCE}
SEC_E_NO_AUTHENTICATING_AUTHORITY = HRESULT($80090311);
{$EXTERNALSYM SEC_E_NO_AUTHENTICATING_AUTHORITY}
SEC_I_CONTINUE_NEEDED = HRESULT($00090312);
{$EXTERNALSYM SEC_I_CONTINUE_NEEDED}
SEC_I_COMPLETE_NEEDED = HRESULT($00090313);
{$EXTERNALSYM SEC_I_COMPLETE_NEEDED}
SEC_I_COMPLETE_AND_CONTINUE = HRESULT($00090314);
{$EXTERNALSYM SEC_I_COMPLETE_AND_CONTINUE}
SEC_I_LOCAL_LOGON = HRESULT($00090315);
{$EXTERNALSYM SEC_I_LOCAL_LOGON}
SEC_E_BAD_PKGID = HRESULT($80090316);
{$EXTERNALSYM SEC_E_BAD_PKGID}
SEC_E_CONTEXT_EXPIRED = HRESULT($80090317);
{$EXTERNALSYM SEC_E_CONTEXT_EXPIRED}
SEC_E_INCOMPLETE_MESSAGE = HRESULT($80090318);
{$EXTERNALSYM SEC_E_INCOMPLETE_MESSAGE}
SEC_E_INCOMPLETE_CREDENTIALS = HRESULT($80090320);
{$EXTERNALSYM SEC_E_INCOMPLETE_CREDENTIALS}
SEC_E_BUFFER_TOO_SMALL = HRESULT($80090321);
{$EXTERNALSYM SEC_E_BUFFER_TOO_SMALL}
SEC_I_INCOMPLETE_CREDENTIALS = HRESULT($00090320);
{$EXTERNALSYM SEC_I_INCOMPLETE_CREDENTIALS}
SEC_I_RENEGOTIATE = HRESULT($00090321);
{$EXTERNALSYM SEC_I_RENEGOTIATE}
SEC_E_WRONG_PRINCIPAL = HRESULT($80090322);
{$EXTERNALSYM SEC_E_WRONG_PRINCIPAL}
SEC_I_NO_LSA_CONTEXT = HRESULT($00090323);
{$EXTERNALSYM SEC_I_NO_LSA_CONTEXT}
SEC_E_TIME_SKEW = HRESULT($80090324);
{$EXTERNALSYM SEC_E_TIME_SKEW}
SEC_E_UNTRUSTED_ROOT = HRESULT($80090325);
{$EXTERNALSYM SEC_E_UNTRUSTED_ROOT}
SEC_E_ILLEGAL_MESSAGE = HRESULT($80090326);
{$EXTERNALSYM SEC_E_ILLEGAL_MESSAGE}
SEC_E_CERT_UNKNOWN = HRESULT($80090327);
{$EXTERNALSYM SEC_E_CERT_UNKNOWN}
SEC_E_CERT_EXPIRED = HRESULT($80090328);
{$EXTERNALSYM SEC_E_CERT_EXPIRED}
SEC_E_ENCRYPT_FAILURE = HRESULT($80090329);
{$EXTERNALSYM SEC_E_ENCRYPT_FAILURE}
SEC_E_DECRYPT_FAILURE = HRESULT($80090330);
{$EXTERNALSYM SEC_E_DECRYPT_FAILURE}
SEC_E_ALGORITHM_MISMATCH = HRESULT($80090331);
{$EXTERNALSYM SEC_E_ALGORITHM_MISMATCH}
SEC_E_SECURITY_QOS_FAILED = HRESULT($80090332);
{$EXTERNALSYM SEC_E_SECURITY_QOS_FAILED}
SEC_E_UNFINISHED_CONTEXT_DELETED = HRESULT($80090333);
{$EXTERNALSYM SEC_E_UNFINISHED_CONTEXT_DELETED}
SEC_E_NO_TGT_REPLY = HRESULT($80090334);
{$EXTERNALSYM SEC_E_NO_TGT_REPLY}
SEC_E_NO_IP_ADDRESSES = HRESULT($80090335);
{$EXTERNALSYM SEC_E_NO_IP_ADDRESSES}
SEC_E_WRONG_CREDENTIAL_HANDLE = HRESULT($80090336);
{$EXTERNALSYM SEC_E_WRONG_CREDENTIAL_HANDLE}
SEC_E_CRYPTO_SYSTEM_INVALID = HRESULT($80090337);
{$EXTERNALSYM SEC_E_CRYPTO_SYSTEM_INVALID}
SEC_E_MAX_REFERRALS_EXCEEDED = HRESULT($80090338);
{$EXTERNALSYM SEC_E_MAX_REFERRALS_EXCEEDED}
SEC_E_MUST_BE_KDC = HRESULT($80090339);
{$EXTERNALSYM SEC_E_MUST_BE_KDC}
SEC_E_STRONG_CRYPTO_NOT_SUPPORTED = HRESULT($8009033A);
{$EXTERNALSYM SEC_E_STRONG_CRYPTO_NOT_SUPPORTED}
SEC_E_TOO_MANY_PRINCIPALS = HRESULT($8009033B);
{$EXTERNALSYM SEC_E_TOO_MANY_PRINCIPALS}
SEC_E_NO_PA_DATA = HRESULT($8009033C);
{$EXTERNALSYM SEC_E_NO_PA_DATA}
SEC_E_PKINIT_NAME_MISMATCH = HRESULT($8009033D);
{$EXTERNALSYM SEC_E_PKINIT_NAME_MISMATCH}
SEC_E_SMARTCARD_LOGON_REQUIRED = HRESULT($8009033E);
{$EXTERNALSYM SEC_E_SMARTCARD_LOGON_REQUIRED}
SEC_E_SHUTDOWN_IN_PROGRESS = HRESULT($8009033F);
{$EXTERNALSYM SEC_E_SHUTDOWN_IN_PROGRESS}
SEC_E_KDC_INVALID_REQUEST = HRESULT($80090340);
{$EXTERNALSYM SEC_E_KDC_INVALID_REQUEST}
SEC_E_KDC_UNABLE_TO_REFER = HRESULT($80090341);
{$EXTERNALSYM SEC_E_KDC_UNABLE_TO_REFER}
SEC_E_KDC_UNKNOWN_ETYPE = HRESULT($80090342);
{$EXTERNALSYM SEC_E_KDC_UNKNOWN_ETYPE}
SEC_E_UNSUPPORTED_PREAUTH = HRESULT($80090343);
{$EXTERNALSYM SEC_E_UNSUPPORTED_PREAUTH}
SEC_E_DELEGATION_REQUIRED = HRESULT($80090345);
{$EXTERNALSYM SEC_E_DELEGATION_REQUIRED}
SEC_E_BAD_BINDINGS = HRESULT($80090346);
{$EXTERNALSYM SEC_E_BAD_BINDINGS}
SEC_E_MULTIPLE_ACCOUNTS = HRESULT($80090347);
{$EXTERNALSYM SEC_E_MULTIPLE_ACCOUNTS}
SEC_E_NO_KERB_KEY = HRESULT($80090348);
{$EXTERNALSYM SEC_E_NO_KERB_KEY}
SEC_E_CERT_WRONG_USAGE = HRESULT($80090349);
{$EXTERNALSYM SEC_E_CERT_WRONG_USAGE}
SEC_E_DOWNGRADE_DETECTED = HRESULT($80090350);
{$EXTERNALSYM SEC_E_DOWNGRADE_DETECTED}
SEC_E_SMARTCARD_CERT_REVOKED = HRESULT($80090351);
{$EXTERNALSYM SEC_E_SMARTCARD_CERT_REVOKED}
SEC_E_ISSUING_CA_UNTRUSTED = HRESULT($80090352);
{$EXTERNALSYM SEC_E_ISSUING_CA_UNTRUSTED}
SEC_E_REVOCATION_OFFLINE_C = HRESULT($80090353);
{$EXTERNALSYM SEC_E_REVOCATION_OFFLINE_C}
SEC_E_PKINIT_CLIENT_FAILURE = HRESULT($80090354);
{$EXTERNALSYM SEC_E_PKINIT_CLIENT_FAILURE}
SEC_E_SMARTCARD_CERT_EXPIRED = HRESULT($80090355);
{$EXTERNALSYM SEC_E_SMARTCARD_CERT_EXPIRED}
SEC_E_NO_S4U_PROT_SUPPORT = HRESULT($80090356);
{$EXTERNALSYM SEC_E_NO_S4U_PROT_SUPPORT}
SEC_E_CROSSREALM_DELEGATION_FAILURE = HRESULT($80090357);
{$EXTERNALSYM SEC_E_CROSSREALM_DELEGATION_FAILURE}
SEC_E_REVOCATION_OFFLINE_KDC = HRESULT($80090358);
{$EXTERNALSYM SEC_E_REVOCATION_OFFLINE_KDC}
SEC_E_ISSUING_CA_UNTRUSTED_KDC = HRESULT($80090359);
{$EXTERNALSYM SEC_E_ISSUING_CA_UNTRUSTED_KDC}
SEC_E_KDC_CERT_EXPIRED = HRESULT($8009035A);
{$EXTERNALSYM SEC_E_KDC_CERT_EXPIRED}
SEC_E_KDC_CERT_REVOKED = HRESULT($8009035B);
{$EXTERNALSYM SEC_E_KDC_CERT_REVOKED}
SEC_I_SIGNATURE_NEEDED = HRESULT($0009035C);
{$EXTERNALSYM SEC_I_SIGNATURE_NEEDED}
SEC_E_INVALID_PARAMETER = HRESULT($8009035D);
{$EXTERNALSYM SEC_E_INVALID_PARAMETER}
SEC_E_DELEGATION_POLICY = HRESULT($8009035E);
{$EXTERNALSYM SEC_E_DELEGATION_POLICY}
SEC_E_POLICY_NLTM_ONLY = HRESULT($8009035F);
{$EXTERNALSYM SEC_E_POLICY_NLTM_ONLY}
SEC_I_NO_RENEGOTIATION = HRESULT($00090360);
{$EXTERNALSYM SEC_I_NO_RENEGOTIATION}
SEC_E_NO_CONTEXT = HRESULT($80090361);
{$EXTERNALSYM SEC_E_NO_CONTEXT}
SEC_E_PKU2U_CERT_FAILURE = HRESULT($80090362);
{$EXTERNALSYM SEC_E_PKU2U_CERT_FAILURE}
SEC_E_MUTUAL_AUTH_FAILED = HRESULT($80090363);
{$EXTERNALSYM SEC_E_MUTUAL_AUTH_FAILED}
type
ESSPIException = class(Exception)
public
// Params must be in this order to avoid conflict with CreateHelp
// constructor in CBuilder as CB does not differentiate constructors
// by name as Delphi does
constructor CreateError(const AErrorNo: Integer; const AFailedFuncName: string);
//
class function GetErrorMessageByNo(AErrorNo: UInt32): string;
end;
ESSPIInterfaceInitFailed = class(ESSPIException);
{ TSSPIInterface }
TSSPIInterface = class(TObject)
private
fLoadPending, fIsAvailable: Boolean;
fPFunctionTable: PSecurityFunctionTable;
fDLLHandle: THandle;
procedure ReleaseFunctionTable;
procedure CheckAvailable;
function GetFunctionTable: SecurityFunctionTable;
public
class procedure RaiseIfError(aStatus: SECURITY_STATUS; const aFunctionName: string);
function IsAvailable: Boolean;
property FunctionTable: SecurityFunctionTable read GetFunctionTable;
public
constructor Create;
destructor Destroy; override;
end;
{ TSSPIPackages }
TSSPIPackage = class(TObject)
private
fPSecPkginfo: PSecPkgInfo;
function GetPSecPkgInfo: PSecPkgInfo;
function GetMaxToken: ULONG;
function GetName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF};
public
property MaxToken: ULONG read GetMaxToken;
property Name: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF} read GetName;
public
constructor Create(aPSecPkginfo: PSecPkgInfo);
end;
TCustomSSPIPackage = class(TSSPIPackage)
private
fInfo: PSecPkgInfo;
public
constructor Create(const aPkgName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF});
destructor Destroy; override;
end;
TSSPINTLMPackage = class(TCustomSSPIPackage)
public
constructor Create;
end;
{ TSSPICredentials }
TSSPICredentialsUse = (scuInBound, scuOutBound, scuBoth);
TSSPICredentials = class(TObject)
private
fPackage: TSSPIPackage;
fHandle: CredHandle;
fUse: TSSPICredentialsUse;
fAcquired: Boolean;
fExpiry: TimeStamp;
function GetHandle: PCredHandle;
procedure SetUse(aValue: TSSPICredentialsUse);
protected
procedure CheckAcquired;
procedure CheckNotAcquired;
procedure DoAcquire(pszPrincipal: {$IFDEF SSPI_UNICODE}PSEC_WCHAR{$ELSE}PSEC_CHAR{$ENDIF}; pvLogonId, pAuthData: PVOID);
procedure DoRelease; virtual;
public
procedure Release;
property Package: TSSPIPackage read fPackage;
property Handle: PCredHandle read GetHandle;
property Use: TSSPICredentialsUse read fUse write SetUse;
property Acquired: Boolean read fAcquired;
public
constructor Create(aPackage: TSSPIPackage);
destructor Destroy; override;
end;
{ TSSPIWinNTCredentials }
TSSPIWinNTCredentials = class(TSSPICredentials)
protected
public
procedure Acquire(aUse: TSSPICredentialsUse); overload;
procedure Acquire(aUse: TSSPICredentialsUse;
const aDomain, aUserName, aPassword: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF}); overload;
end;
{ TSSPIContext }
TSSPIContext = class(TObject)
private
fCredentials: TSSPICredentials;
fHandle: CtxtHandle;
fHasHandle: Boolean;
fExpiry: TimeStamp;
function GetHandle: PCtxtHandle;
function GetExpiry: TimeStamp;
procedure UpdateHasContextAndCheckForError(
const aFuncResult: SECURITY_STATUS; const aFuncName: string;
const aErrorsToIgnore: array of SECURITY_STATUS);
protected
procedure CheckHasHandle;
procedure CheckCredentials;
function DoInitialize(const aTokenSourceName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF};
var aIn, aOut: SecBufferDesc;
const errorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS;
procedure DoRelease; virtual;
function GetRequestedFlags: ULONG; virtual; abstract;
procedure SetEstablishedFlags(aFlags: ULONG); virtual; abstract;
function GetAuthenticated: Boolean; virtual; abstract;
property HasHandle: Boolean read fHasHandle;
public
procedure Release;
property Credentials: TSSPICredentials read fCredentials;
property Handle: PCtxtHandle read GetHandle;
property Authenticated: Boolean read GetAuthenticated;
property Expiry: TimeStamp read GetExpiry;
public
constructor Create(aCredentials: TSSPICredentials);
destructor Destroy; override;
end;
{ TSSPIConnectionContext }
TCustomSSPIConnectionContext = class(TSSPIContext)
private
fStatus: SECURITY_STATUS;
fOutBuffDesc, fInBuffDesc: SecBufferDesc;
fInBuff: SecBuffer;
protected
procedure DoRelease; override;
function GetAuthenticated: Boolean; override;
function DoUpdateAndGenerateReply(var aIn, aOut: SecBufferDesc;
const aErrorsToIgnore: array of SECURITY_STATUS
): SECURITY_STATUS; virtual; abstract;
public
constructor Create(ACredentials: TSSPICredentials);
function UpdateAndGenerateReply(
const aFromPeerToken: TIdBytes; var aToPeerToken: TIdBytes): Boolean;
end;
TSSPIClientConnectionContext = class(TCustomSSPIConnectionContext)
private
fTargetName: string;
fReqReguested, fReqEstablished: ULONG;
protected
function GetRequestedFlags: ULONG; override;
procedure SetEstablishedFlags(aFlags: ULONG); override;
function DoUpdateAndGenerateReply(var aIn, aOut: SecBufferDesc;
const aErrorsToIgnore: array of SECURITY_STATUS
): SECURITY_STATUS; override;
public
function GenerateInitialChallenge(const aTargetName: string;
var aToPeerToken: TIdBytes): Boolean;
public
constructor Create(aCredentials: TSSPICredentials);
end;
TIndySSPINTLMClient = class(TObject)
protected
fNTLMPackage: TSSPINTLMPackage;
fCredentials: TSSPIWinNTCredentials;
fContext: TSSPIClientConnectionContext;
public
procedure SetCredentials(const aDomain, aUserName, aPassword: string);
procedure SetCredentialsAsCurrentUser;
function InitAndBuildType1Message: TIdBytes;
function UpdateAndBuildType3Message(const aServerType2Message: TIdBytes): TIdBytes;
public
constructor Create;
destructor Destroy; override;
end;
TIdSSPINTLMAuthentication = class(TIdAuthentication)
protected
FNTLMInfo: string;
FSSPIClient: TIndySSPINTLMClient;
procedure SetDomain(const Value: String);
function GetDomain: String;
procedure SetUserName(const Value: String); override;
function GetSteps: Integer; override;
function DoNext: TIdAuthWhatsNext; override;
public
constructor Create; override;
destructor Destroy; override;
function Authentication: string; override;
function KeepAlive: Boolean; override;
property Domain: String read GetDomain write SetDomain;
end;
// RLebeau 4/17/10: this forces C++Builder to link to this unit so
// RegisterAuthenticationMethod can be called correctly at program startup...
{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT}
{$HPPEMIT LINKUNIT}
{$ELSE}
{$HPPEMIT '#pragma link "IdAuthenticationSSPI"'}
{$ENDIF}
implementation
uses
IdGlobalCore,
IdGlobalProtocols,
IdException,
IdCoderMIME,
IdResourceStringsSSPI,
IdHeaderList;
var
gSSPIInterface: TSSPIInterface = nil;
gAuthRegistered: Boolean = False;
{ ESSPIException }
class function ESSPIException.GetErrorMessageByNo(aErrorNo: UInt32): string;
begin
case HRESULT(aErrorNo) of
SEC_E_OK:
Result := RSHTTPSSPISuccess;
SEC_E_INSUFFICIENT_MEMORY:
Result := RSHTTPSSPINotEnoughMem;
SEC_E_INVALID_HANDLE:
Result := RSHTTPSSPIInvalidHandle;
SEC_E_UNSUPPORTED_FUNCTION:
Result := RSHTTPSSPIFuncNotSupported;
SEC_E_TARGET_UNKNOWN:
Result := RSHTTPSSPIUnknownTarget;
SEC_E_INTERNAL_ERROR:
Result := RSHTTPSSPIInternalError;
SEC_E_SECPKG_NOT_FOUND:
Result := RSHTTPSSPISecPackageNotFound;
SEC_E_NOT_OWNER:
Result := RSHTTPSSPINotOwner;
SEC_E_CANNOT_INSTALL:
Result := RSHTTPSSPIPackageCannotBeInstalled;
SEC_E_INVALID_TOKEN:
Result := RSHTTPSSPIInvalidToken;
SEC_E_CANNOT_PACK:
Result := RSHTTPSSPICannotPack;
SEC_E_QOP_NOT_SUPPORTED:
Result := RSHTTPSSPIQOPNotSupported;
SEC_E_NO_IMPERSONATION:
Result := RSHTTPSSPINoImpersonation;
SEC_E_LOGON_DENIED:
Result := RSHTTPSSPILoginDenied;
SEC_E_UNKNOWN_CREDENTIALS:
Result := RSHTTPSSPIUnknownCredentials;
SEC_E_NO_CREDENTIALS:
Result := RSHTTPSSPINoCredentials;
SEC_E_MESSAGE_ALTERED:
Result := RSHTTPSSPIMessageAltered;
SEC_E_OUT_OF_SEQUENCE:
Result := RSHTTPSSPIOutOfSequence;
SEC_E_NO_AUTHENTICATING_AUTHORITY:
Result := RSHTTPSSPINoAuthAuthority;
SEC_I_CONTINUE_NEEDED:
Result := RSHTTPSSPIContinueNeeded;
SEC_I_COMPLETE_NEEDED:
Result := RSHTTPSSPICompleteNeeded;
SEC_I_COMPLETE_AND_CONTINUE:
Result :=RSHTTPSSPICompleteContinueNeeded;
SEC_I_LOCAL_LOGON:
Result := RSHTTPSSPILocalLogin;
SEC_E_BAD_PKGID:
Result := RSHTTPSSPIBadPackageID;
SEC_E_CONTEXT_EXPIRED:
Result := RSHTTPSSPIContextExpired;
SEC_E_INCOMPLETE_MESSAGE:
Result := RSHTTPSSPIIncompleteMessage;
SEC_E_INCOMPLETE_CREDENTIALS:
Result := RSHTTPSSPIIncompleteCredentialNotInit;
SEC_E_BUFFER_TOO_SMALL:
Result := RSHTTPSSPIBufferTooSmall;
SEC_I_INCOMPLETE_CREDENTIALS:
Result := RSHTTPSSPIIncompleteCredentialsInit;
SEC_I_RENEGOTIATE:
Result := RSHTTPSSPIRengotiate;
SEC_E_WRONG_PRINCIPAL:
Result := RSHTTPSSPIWrongPrincipal;
SEC_I_NO_LSA_CONTEXT:
Result := RSHTTPSSPINoLSACode;
SEC_E_TIME_SKEW:
Result := RSHTTPSSPITimeScew;
SEC_E_UNTRUSTED_ROOT:
Result := RSHTTPSSPIUntrustedRoot;
SEC_E_ILLEGAL_MESSAGE:
Result := RSHTTPSSPIIllegalMessage;
SEC_E_CERT_UNKNOWN:
Result := RSHTTPSSPICertUnknown;
SEC_E_CERT_EXPIRED:
Result := RSHTTPSSPICertExpired;
SEC_E_ENCRYPT_FAILURE:
Result := RSHTTPSSPIEncryptionFailure;
SEC_E_DECRYPT_FAILURE:
Result := RSHTTPSSPIDecryptionFailure;
SEC_E_ALGORITHM_MISMATCH:
Result := RSHTTPSSPIAlgorithmMismatch;
SEC_E_SECURITY_QOS_FAILED:
Result := RSHTTPSSPISecurityQOSFailure;
SEC_E_UNFINISHED_CONTEXT_DELETED :
Result := RSHTTPSSPISecCtxWasDelBeforeUpdated;
SEC_E_NO_TGT_REPLY :
Result := RSHTTPSSPIClientNoTGTReply;
SEC_E_NO_IP_ADDRESSES :
Result := RSHTTPSSPILocalNoIPAddr;
SEC_E_WRONG_CREDENTIAL_HANDLE :
Result := RSHTTPSSPIWrongCredHandle;
SEC_E_CRYPTO_SYSTEM_INVALID :
Result := RSHTTPSSPICryptoSysInvalid;
SEC_E_MAX_REFERRALS_EXCEEDED :
Result := RSHTTPSSPIMaxTicketRef;
SEC_E_MUST_BE_KDC :
Result := RSHTTPSSPIMustBeKDC;
SEC_E_STRONG_CRYPTO_NOT_SUPPORTED :
Result := RSHTTPSSPIStrongCryptoNotSupported;
SEC_E_TOO_MANY_PRINCIPALS :
Result := RSHTTPSSPIKDCReplyTooManyPrincipals;
SEC_E_NO_PA_DATA :
Result := RSHTTPSSPINoPAData;
SEC_E_PKINIT_NAME_MISMATCH :
Result := RSHTTPSSPIPKInitNameMismatch;
SEC_E_SMARTCARD_LOGON_REQUIRED :
Result := RSHTTPSSPISmartcardLogonReq;
SEC_E_SHUTDOWN_IN_PROGRESS :
Result := RSHTTPSSPISysShutdownInProg;
SEC_E_KDC_INVALID_REQUEST :
Result := RSHTTPSSPIKDCInvalidRequest;
SEC_E_KDC_UNABLE_TO_REFER :
Result := RSHTTPSSPIKDCUnableToRefer;
SEC_E_KDC_UNKNOWN_ETYPE :
Result := RSHTTPSSPIKDCETypeUnknown;
SEC_E_UNSUPPORTED_PREAUTH :
Result := RSHTTPSSPIUnsupPreauth;
SEC_E_DELEGATION_REQUIRED :
Result := RSHTTPSSPIDeligationReq;
SEC_E_BAD_BINDINGS :
Result := RSHTTPSSPIBadBindings;
SEC_E_MULTIPLE_ACCOUNTS :
Result := RSHTTPSSPIMultipleAccounts;
SEC_E_NO_KERB_KEY :
Result := RSHTTPSSPINoKerbKey;
SEC_E_CERT_WRONG_USAGE :
Result := RSHTTPSSPICertWrongUsage;
SEC_E_DOWNGRADE_DETECTED :
Result := RSHTTPSSPIDowngradeDetected;
SEC_E_SMARTCARD_CERT_REVOKED :
Result := RSHTTPSSPISmartcardCertRevoked;
SEC_E_ISSUING_CA_UNTRUSTED :
Result := RSHTTPSSPIIssuingCAUntrusted;
SEC_E_REVOCATION_OFFLINE_C :
Result := RSHTTPSSPIRevocationOffline;
SEC_E_PKINIT_CLIENT_FAILURE :
Result := RSHTTPSSPIPKInitClientFailure;
SEC_E_SMARTCARD_CERT_EXPIRED :
Result := RSHTTPSSPISmartcardExpired;
SEC_E_NO_S4U_PROT_SUPPORT :
Result := RSHTTPSSPINoS4UProtSupport;
SEC_E_CROSSREALM_DELEGATION_FAILURE :
Result := RSHTTPSSPICrossRealmDeligationFailure;
SEC_E_REVOCATION_OFFLINE_KDC :
Result := RSHTTPSSPIRevocationOfflineKDC;
SEC_E_ISSUING_CA_UNTRUSTED_KDC :
Result := RSHTTPSSPICAUntrustedKDC;
SEC_E_KDC_CERT_EXPIRED :
Result := RSHTTPSSPIKDCCertExpired;
SEC_E_KDC_CERT_REVOKED :
Result := RSHTTPSSPIKDCCertRevoked;
SEC_I_SIGNATURE_NEEDED :
Result := RSHTTPSSPISignatureNeeded;
SEC_E_INVALID_PARAMETER :
Result := RSHTTPSSPIInvalidParameter;
SEC_E_DELEGATION_POLICY :
Result := RSHTTPSSPIDeligationPolicy;
SEC_E_POLICY_NLTM_ONLY :
Result := RSHTTPSSPIPolicyNTLMOnly;
SEC_I_NO_RENEGOTIATION :
Result := RSHTTPSSPINoRenegotiation;
SEC_E_NO_CONTEXT :
Result := RSHTTPSSPINoContext;
SEC_E_PKU2U_CERT_FAILURE :
Result := RSHTTPSSPIPKU2UCertFailure;
SEC_E_MUTUAL_AUTH_FAILED :
Result := RSHTTPSSPIMutualAuthFailed;
else
Result := RSHTTPSSPIUnknwonError;
end;
end;
constructor ESSPIException.CreateError(const AErrorNo: Integer; const AFailedFuncName: string);
begin
if AErrorNo = SEC_E_OK then begin
inherited Create(AFailedFuncName);
end else begin
inherited CreateFmt(RSHTTPSSPIErrorMsg,
[AFailedFuncName, AErrorNo, AErrorNo, GetErrorMessageByNo(AErrorNo)]);
end;
end;
{ TSSPIInterface }
procedure TSSPIInterface.ReleaseFunctionTable;
begin
if fPFunctionTable <> nil then begin
fPFunctionTable := nil;
end;
end;
procedure TSSPIInterface.CheckAvailable;
begin
if not IsAvailable then begin
raise ESSPIInterfaceInitFailed.Create(RSHTTPSSPIInterfaceInitFailed);
end;
end;
function TSSPIInterface.GetFunctionTable: SecurityFunctionTable;
begin
CheckAvailable;
Result := fPFunctionTable^;
end;
class procedure TSSPIInterface.RaiseIfError(aStatus: SECURITY_STATUS;
const aFunctionName: string);
begin
if not SEC_SUCCESS(aStatus) then begin
raise ESSPIException.CreateError(aStatus, aFunctionName);
end;
end;
function TSSPIInterface.IsAvailable: Boolean;
procedure LoadDLL;
const
SECURITY_DLL_NT = 'security.dll'; {Do not translate}
SECURITY_DLL_95 = 'secur32.dll'; {Do not translate}
ENCRYPT_MESSAGE = 'EncryptMessage'; {Do not translate}
DECRYPT_MESSAGE = 'DecryptMessage'; {Do not translate}
var
dllName: string;
entrypoint: INIT_SECURITY_INTERFACE;
begin
fIsAvailable := False;
if IndyWindowsPlatform = VER_PLATFORM_WIN32_WINDOWS then
{ Windows95 SSPI dll }
dllName := SECURITY_DLL_95
else
{ WindowsNT & Windows2000 SSPI dll }
dllName := SECURITY_DLL_NT;
{ load SSPI dll }
//In Windows, you should use SafeLoadLibrary instead of the LoadLibrary API
//call because LoadLibrary messes with the FPU control word.
fDLLHandle := SafeLoadLibrary(dllName);
if fDLLHandle > 0 then begin
{ get InitSecurityInterface entry point
and call it to fetch SPPI function table}
entrypoint := GetProcAddress(fDLLHandle, SECURITY_ENTRYPOINT);
fPFunctionTable := entrypoint();
{ let's see what SSPI functions are available
and if we can continue on with the set }
fIsAvailable :=
Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.QuerySecurityPackageInfoW{$ELSE}fPFunctionTable^.QuerySecurityPackageInfoA{$ENDIF}) and
Assigned(fPFunctionTable^.FreeContextBuffer) and
Assigned(fPFunctionTable^.DeleteSecurityContext) and
Assigned(fPFunctionTable^.FreeCredentialsHandle) and
Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.AcquireCredentialsHandleW{$ELSE}fPFunctionTable^.AcquireCredentialsHandleA{$ENDIF}) and
Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.InitializeSecurityContextW{$ELSE}fPFunctionTable^.InitializeSecurityContextA{$ENDIF}) and
Assigned(fPFunctionTable^.AcceptSecurityContext) and
Assigned(fPFunctionTable^.ImpersonateSecurityContext) and
Assigned(fPFunctionTable^.RevertSecurityContext) and
Assigned({$IFDEF SSPI_UNICODE}fPFunctionTable^.QueryContextAttributesW{$ELSE}fPFunctionTable^.QueryContextAttributesA{$ENDIF}) and
Assigned(fPFunctionTable^.MakeSignature) and
Assigned(fPFunctionTable^.VerifySignature);
{$IFDEF SET_ENCRYPT_IN_FT_WITH_GETPROCADDRESS_FUDGE}
{ fudge for Encrypt/DecryptMessage }
if not Assigned(fPFunctionTable^.EncryptMessage) then begin
fPFunctionTable^.EncryptMessage := GetProcAddress(fDLLHandle, ENCRYPT_MESSAGE);
end;
if not Assigned(fPFunctionTable^.DecryptMessage) then begin
fPFunctionTable^.DecryptMessage := GetProcAddress(fDLLHandle, DECRYPT_MESSAGE);
end;
{$ENDIF}
end;
end;
begin
if not fIsAvailable then begin
if fLoadPending then begin
ReleaseFunctionTable;
LoadDLL;
fLoadPending := False;
end;
end;
Result := fIsAvailable;
end;
constructor TSSPIInterface.Create;
begin
inherited Create;
fLoadPending := True;
fIsAvailable := False;
fPFunctionTable := nil;
end;
destructor TSSPIInterface.Destroy;
begin
ReleaseFunctionTable;
FreeLibrary(fDLLHandle);
inherited Destroy;
end;
{ TSSPIPackage }
constructor TSSPIPackage.Create(aPSecPkginfo: PSecPkgInfo);
begin
inherited Create;
fPSecPkginfo := aPSecPkginfo;
end;
function TSSPIPackage.GetPSecPkgInfo: PSecPkgInfo;
begin
if not Assigned(fPSecPkginfo) then begin
raise ESSPIException.Create(RSHTTPSSPINoPkgInfoSpecified);
end;
Result := fPSecPkginfo;
end;
function TSSPIPackage.GetMaxToken: ULONG;
begin
Result := GetPSecPkgInfo^.cbMaxToken;
end;
function TSSPIPackage.GetName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF};
begin
Result := GetPSecPkgInfo^.Name;
end;
{ TCustomSSPIPackage }
constructor TCustomSSPIPackage.Create(const aPkgName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF});
begin
gSSPIInterface.RaiseIfError(
{$IFDEF SSPI_UNICODE}
gSSPIInterface.FunctionTable.QuerySecurityPackageInfoW(PWideChar(aPkgName), @fInfo),
'QuerySecurityPackageInfoW' {Do not translate}
{$ELSE}
gSSPIInterface.FunctionTable.QuerySecurityPackageInfoA(PAnsiChar(aPkgName), @fInfo),
'QuerySecurityPackageInfoA' {Do not translate}
{$ENDIF}
);
inherited Create(fInfo);
end;
destructor TCustomSSPIPackage.Destroy;
begin
if fInfo <> nil then begin
gSSPIInterface.RaiseIfError(
gSSPIInterface.FunctionTable.FreeContextBuffer(fInfo), 'FreeContextBuffer'); {Do not localize}
end;
inherited Destroy;
end;
{ TSSPINTLMPackage }
constructor TSSPINTLMPackage.Create;
begin
inherited Create(NTLMSP_NAME);
end;
{ TSSPICredentials }
constructor TSSPICredentials.Create(aPackage: TSSPIPackage);
begin
inherited Create;
fPackage := aPackage;
fUse := scuOutBound;
fAcquired := False;
end;
procedure TSSPICredentials.CheckAcquired;
begin
if not fAcquired then begin
raise ESSPIException.Create(RSHTTPSSPINoCredentialHandle);
end;
end;
procedure TSSPICredentials.CheckNotAcquired;
begin
if fAcquired then begin
raise ESSPIException.Create(RSHTTPSSPICanNotChangeCredentials);
end;
end;
procedure TSSPICredentials.DoAcquire
(pszPrincipal: {$IFDEF SSPI_UNICODE}PSEC_WCHAR{$ELSE}PSEC_CHAR{$ENDIF}; pvLogonId, pAuthData: PVOID);
var
cu: ULONG;
begin
Release;
case Use of
scuInBound:
cu := SECPKG_CRED_INBOUND;
scuOutBound:
cu := SECPKG_CRED_OUTBOUND;
scuBoth:
cu := SECPKG_CRED_BOTH;
else
raise ESSPIException.Create(RSHTTPSSPIUnknwonCredentialUse);
end;
gSSPIInterface.RaiseIfError(
gSSPIInterface.FunctionTable.{$IFDEF SSPI_UNICODE}AcquireCredentialsHandleW{$ELSE}AcquireCredentialsHandleA{$ENDIF}(
pszPrincipal, {$IFDEF SSPI_UNICODE}PSEC_WCHAR{$ELSE}PSEC_CHAR{$ENDIF}(Package.Name), cu, pvLogonId, pAuthData, nil, nil,
@fHandle, @fExpiry),
{$IFDEF SSPI_UNICODE}
'AcquireCredentialsHandleW' {Do not translater}
{$ELSE}
'AcquireCredentialsHandleA' {Do not translater}
{$ENDIF}
);
fAcquired := True;
end;
procedure TSSPICredentials.DoRelease;
begin
gSSPIInterface.RaiseIfError(
gSSPIInterface.FunctionTable.FreeCredentialsHandle(@fHandle),
'FreeCredentialsHandle'); {Do not translate}
SecInvalidateHandle(fHandle);
end;
procedure TSSPICredentials.Release;
begin
if fAcquired then begin
DoRelease;
fAcquired := False;
end;
end;
function TSSPICredentials.GetHandle: PCredHandle;
begin
CheckAcquired;
Result := @fHandle;
end;
procedure TSSPICredentials.SetUse(aValue: TSSPICredentialsUse);
begin
if fUse <> aValue then begin
CheckNotAcquired;
fUse := aValue;
end;
end;
destructor TSSPICredentials.Destroy;
begin
Release;
inherited Destroy;
end;
{ TSSPIWinNTCredentials }
procedure TSSPIWinNTCredentials.Acquire(aUse: TSSPICredentialsUse);
begin
Acquire(aUse, '', '', ''); {Do not translate}
end;
procedure TSSPIWinNTCredentials.Acquire(aUse: TSSPICredentialsUse;
const aDomain, aUserName, aPassword: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF});
var
ai: SEC_WINNT_AUTH_IDENTITY;
pai: PVOID;
begin
Use := aUse;
if (Length(aDomain) > 0) and (Length(aUserName) > 0) then begin
{$IFDEF SSPI_UNICODE}
ai.User := PUSHORT(PWideChar(aUserName));
ai.UserLength := Length(aUserName);
ai.Domain := PUSHORT(PWideChar(aDomain));
ai.DomainLength := Length(aDomain);
ai.Password := PUSHORT(PWideChar(aPassword));
ai.PasswordLength := Length(aPassword);
ai.Flags := SEC_WINNT_AUTH_IDENTITY_UNICODE;
{$ELSE}
ai.User := PUCHAR(PAnsiChar(aUserName));
ai.UserLength := Length(aUserName);
ai.Domain := PUCHAR(PAnsiChar(aDomain));
ai.DomainLength := Length(aDomain);
ai.Password := PUCHAR(PAnsiChar(aPassword));
ai.PasswordLength := Length(aPassword);
ai.Flags := SEC_WINNT_AUTH_IDENTITY_ANSI;
{$ENDIF}
pai := @ai;
end else
begin
pai := nil;
end;
DoAcquire(nil, nil, pai);
end;
{ TSSPIContext }
constructor TSSPIContext.Create(aCredentials: TSSPICredentials);
begin
inherited Create;
fCredentials := aCredentials;
fHasHandle := False;
end;
destructor TSSPIContext.Destroy;
begin
Release;
inherited Destroy;
end;
procedure TSSPIContext.UpdateHasContextAndCheckForError(
const aFuncResult: SECURITY_STATUS; const aFuncName: string;
const aErrorsToIgnore: array of SECURITY_STATUS);
var
doRaise: Boolean;
i: Integer;
begin
doRaise := not SEC_SUCCESS(aFuncResult);
if doRaise then begin
for i := Low(aErrorsToIgnore) to High(aErrorsToIgnore) do begin
if aFuncResult = aErrorsToIgnore[i] then begin
doRaise := False;
Break;
end;
end;
end;
if doRaise then begin
raise ESSPIException.CreateError(aFuncResult, aFuncName);
end;
fHasHandle := True;
end;
function TSSPIContext.DoInitialize(const aTokenSourceName: {$IFDEF SSPI_UNICODE}TIdUnicodeString{$ELSE}AnsiString{$ENDIF};
var aIn, aOut: SecBufferDesc;
const errorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS;
var
tmp: PCtxtHandle;
tmp2: PSecBufferDesc;
r: ULONG;
begin
if fHasHandle then begin
tmp := @fHandle;
tmp2 := @aIn;
end else begin
tmp := nil;
tmp2 := nil;
end;
Result :=
gSSPIInterface.FunctionTable.{$IFDEF SSPI_UNICODE}InitializeSecurityContextW{$ELSE}InitializeSecurityContextA{$ENDIF}(
Credentials.Handle, tmp,
{$IFDEF SSPI_UNICODE}PWideChar{$ELSE}PAnsiChar{$ENDIF}(aTokenSourceName),
GetRequestedFlags, 0, SECURITY_NATIVE_DREP, tmp2, 0,
@fHandle, @aOut, @r, @fExpiry
);
UpdateHasContextAndCheckForError(Result,
{$IFDEF SSPI_UNICODE}'InitializeSecurityContextW'{$ELSE}'InitializeSecurityContextA'{$ENDIF}, {Do not translate}
errorsToIgnore);
SetEstablishedFlags(r);
end;
procedure TSSPIContext.DoRelease;
begin
gSSPIInterface.RaiseIfError(
gSSPIInterface.FunctionTable.DeleteSecurityContext(@fHandle), 'DeleteSecurityContext'); {Do not translate}
end;
procedure TSSPIContext.Release;
begin
if HasHandle then begin
DoRelease;
fHasHandle := False;
end;
end;
procedure TSSPIContext.CheckHasHandle;
begin
if not HasHandle then begin
raise ESSPIException.Create(RSHTTPSSPINoCredentialHandle);
end;
end;
procedure TSSPIContext.CheckCredentials;
begin
if (not Assigned(Credentials)) or (not Credentials.Acquired) then begin
raise ESSPIException.Create(RSHTTPSSPIDoAuquireCredentialHandle);
end;
end;
function TSSPIContext.GetExpiry: TimeStamp;
begin
CheckHasHandle;
Result := fExpiry;
end;
function TSSPIContext.GetHandle: PCtxtHandle;
begin
CheckHasHandle;
Result := @fHandle;
end;
{ TCustomSSPIConnectionContext }
procedure TCustomSSPIConnectionContext.DoRelease;
begin
inherited DoRelease;
fStatus := SEC_E_INVALID_HANDLE; // just to put something other then SEC_E_OK
end;
function TCustomSSPIConnectionContext.GetAuthenticated: Boolean;
begin
CheckHasHandle;
Result := fStatus = SEC_E_OK;
end;
function TCustomSSPIConnectionContext.UpdateAndGenerateReply
(const aFromPeerToken: TIdBytes; var aToPeerToken: TIdBytes): Boolean;
var
fOutBuff: SecBuffer;
begin
Result := False;
{ check credentials }
CheckCredentials;
{ prepare input buffer }
fInBuff.cbBuffer := Length(aFromPeerToken);
//Assert(Length(aFromPeerToken)>0);
if fInBuff.cbBuffer > 0 then begin
fInBuff.pvBuffer := @aFromPeerToken[0];
end;
{ prepare output buffer }
fOutBuff.BufferType := SECBUFFER_TOKEN;
fOutBuff.cbBuffer := Credentials.Package.MaxToken;
fOutBuff.pvBuffer := AllocMem(fOutBuff.cbBuffer);
fOutBuffDesc.ulVersion := SECBUFFER_VERSION;
fOutBuffDesc.cBuffers := 1;
fOutBuffDesc.pBuffers := @fOutBuff;
try
{ do processing }
fStatus := DoUpdateAndGenerateReply(fInBuffDesc, fOutBuffDesc, []);
{ complete token if applicable }
case fStatus of
SEC_I_COMPLETE_NEEDED,
SEC_I_COMPLETE_AND_CONTINUE:
begin
if not Assigned(gSSPIInterface.FunctionTable.CompleteAuthToken) then begin
raise ESSPIException.Create(RSHTTPSSPICompleteTokenNotSupported);
end;
fStatus := gSSPIInterface.FunctionTable.CompleteAuthToken(Handle, @fOutBuffDesc);
gSSPIInterface.RaiseIfError(fStatus, 'CompleteAuthToken'); {Do not translate}
end;
end;
Result :=
(fStatus = SEC_I_CONTINUE_NEEDED) or
(fStatus = SEC_I_COMPLETE_AND_CONTINUE) or
(fOutBuff.cbBuffer > 0);
if Result then begin
aToPeerToken := RawToBytes(fOutBuff.pvBuffer^, fOutBuff.cbBuffer);
end;
finally
FreeMem(fOutBuff.pvBuffer);
end;
end;
constructor TCustomSSPIConnectionContext.Create(aCredentials: TSSPICredentials);
begin
inherited Create(aCredentials);
fInBuff.BufferType := SECBUFFER_TOKEN;
fInBuffDesc.ulVersion := SECBUFFER_VERSION;
fInBuffDesc.cBuffers := 1;
fInBuffDesc.pBuffers := @fInBuff;
fOutBuffDesc.ulVersion := SECBUFFER_VERSION;
fOutBuffDesc.cBuffers := 1;
end;
{ TSSPIClientConnectionContext }
constructor TSSPIClientConnectionContext.Create(aCredentials: TSSPICredentials);
begin
inherited Create(aCredentials);
fTargetName := ''; {Do not translate}
end;
function TSSPIClientConnectionContext.GetRequestedFlags: ULONG;
begin
Result := fReqReguested;
end;
procedure TSSPIClientConnectionContext.SetEstablishedFlags(aFlags: ULONG);
begin
fReqEstablished := aFlags;
end;
function TSSPIClientConnectionContext.DoUpdateAndGenerateReply
(var aIn, aOut: SecBufferDesc;
const aErrorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS;
begin
Result := DoInitialize(fTargetName, aIn, aOut, []);
end;
function TSSPIClientConnectionContext.GenerateInitialChallenge
(const aTargetName: string; var aToPeerToken: TIdBytes): Boolean;
begin
Release;
fTargetName := aTargetName;
Result := UpdateAndGenerateReply(nil, aToPeerToken); {Do not translate}
end;
{ TIndySSPINTLMClient }
constructor TIndySSPINTLMClient.Create;
begin
inherited Create;
fNTLMPackage := TSSPINTLMPackage.Create;
fCredentials := TSSPIWinNTCredentials.Create(fNTLMPackage);
fContext := TSSPIClientConnectionContext.Create(fCredentials);
end;
destructor TIndySSPINTLMClient.Destroy;
begin
FreeAndNil(fContext);
FreeAndNil(fCredentials);
FreeAndNil(fNTLMPackage);
inherited Destroy;
end;
procedure TIndySSPINTLMClient.SetCredentials(const aDomain, aUserName, aPassword: string);
begin
fCredentials.Acquire(scuOutBound, aDomain, aUserName, aPassword);
end;
procedure TIndySSPINTLMClient.SetCredentialsAsCurrentUser;
begin
fCredentials.Acquire(scuOutBound);
end;
function TIndySSPINTLMClient.InitAndBuildType1Message: TIdBytes;
begin
fContext.GenerateInitialChallenge('', Result);
end;
function TIndySSPINTLMClient.UpdateAndBuildType3Message(const aServerType2Message: TIdBytes): TIdBytes;
begin
fContext.UpdateAndGenerateReply(aServerType2Message, Result);
end;
{ TIdSSPINTLMAuthentication }
constructor TIdSSPINTLMAuthentication.Create;
begin
inherited Create;
FSSPIClient := TIndySSPINTLMClient.Create;
Domain := IndyComputerName;
end;
function TIdSSPINTLMAuthentication.DoNext: TIdAuthWhatsNext;
begin
Result := wnDoRequest;
case FCurrentStep of
0:
begin
{if (Length(Username) > 0) and (Length(Password) > 0) then
begin}
Result := wnDoRequest;
FCurrentStep := 1;
{end
else begin
result := wnAskTheProgram;
end;}
end;
1:
begin
FCurrentStep := 2;
Result := wnDoRequest;
end;
//Authentication does the 2>3 progression
3:
begin
FCurrentStep := 4;
Result := wnDoRequest;
end;
4:
begin
FCurrentStep := 0;
if Username = '' then begin
Result := wnAskTheProgram;
end else begin
Result := wnFail;
Username := '';
Password := '';
Domain := IndyComputerName;
end;
end;
end;
end;
function TIdSSPINTLMAuthentication.Authentication: string;
var
buf: TIdBytes;
begin
Result := '';
buf := nil;
case FCurrentStep of
1:
begin
if Length(Username) = 0 then begin
FSSPIClient.SetCredentialsAsCurrentUser;
end else begin
FSSPIClient.SetCredentials(Domain, Username, Password);
end;
Result := 'NTLM ' + TIdEncoderMIME.EncodeBytes(FSSPIClient.InitAndBuildType1Message); {Do not translate}
FNTLMInfo := ''; {Do not translate}
end;
2:
begin
if Length(FNTLMInfo) = 0 then begin
FNTLMInfo := ReadAuthInfo('NTLM'); {Do not translate}
Fetch(FNTLMInfo);
end;
if Length(FNTLMInfo) = 0 then begin
Reset;
Abort;
end;
buf := TIdDecoderMIME.DecodeBytes(FNTLMInfo);
Result := 'NTLM ' + TIdEncoderMIME.EncodeBytes(FSSPIClient.UpdateAndBuildType3Message(buf)); {Do not translate}
FCurrentStep := 3;
end;
3: begin
FCurrentStep := 4;
end;
end;
end;
function TIdSSPINTLMAuthentication.KeepAlive: Boolean;
begin
Result := FCurrentStep >= 1;
end;
function TIdSSPINTLMAuthentication.GetSteps: Integer;
begin
Result := 3;
end;
procedure TIdSSPINTLMAuthentication.SetDomain(const Value: String);
begin
Params.Values['Domain'] := Value; {do not localize}
end;
function TIdSSPINTLMAuthentication.GetDomain: String;
begin
Result := Params.Values['Domain']; {do not localize}
end;
procedure TIdSSPINTLMAuthentication.SetUserName(const Value: String);
Var
S: String;
begin
S := Value;
if IndyPos('\', S) > 0 then begin
Domain := Copy(S, 1, IndyPos('\', S) - 1);
Delete(S, 1, Length(Domain) + 1);
end;
inherited SetUserName(S);
end;
destructor TIdSSPINTLMAuthentication.Destroy;
begin
FreeAndNil(FSSPIClient);
inherited;
end;
initialization
gSSPIInterface := TSSPIInterface.Create;
if gSSPIInterface.IsAvailable then begin
RegisterAuthenticationMethod('NTLM', TIdSSPINTLMAuthentication); {do not localize}
RegisterAuthenticationMethod('Negotiate', TIdSSPINTLMAuthentication); {do not localize}
gAuthRegistered := True;
end;
finalization
if gAuthRegistered then begin
UnregisterAuthenticationMethod('NTLM'); {do not localize}
UnregisterAuthenticationMethod('Negotiate'); {do not localize}
end;
FreeAndNil(gSSPIInterface);
end.