IdAuthenticationSSPI.pas 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10079: IdAuthenticationSSPI.pas
  11. {
  12. { Rev 1.2 17.7.2003 ã. 22:27:44 DBondzhev
  13. { Added domain name for authorizing against MS Proxy
  14. }
  15. {
  16. { Rev 1.1 01.2.2003 ã. 11:54:16 DBondzhev
  17. }
  18. {
  19. { Rev 1.0 2002.11.12 10:31:20 PM czhower
  20. }
  21. {
  22. Implementation of the NTLM authentication with SSPI
  23. Author: Alex Brainman
  24. Copyright: (c) Chad Z. Hower and The Winshoes Working Group.
  25. }
  26. unit IdAuthenticationSSPI;
  27. {$DEFINE SET_ENCRYPT_IN_FT_WITH_GETPROCADDRESS_FUDGE}
  28. interface
  29. uses
  30. IdAuthentication, Windows, classes, SysUtils,
  31. IdSSPI;
  32. const
  33. SEC_E_OK = 0;
  34. SEC_E_INSUFFICIENT_MEMORY = HRESULT($80090300);
  35. SEC_E_INVALID_HANDLE = HRESULT($80090301);
  36. SEC_E_UNSUPPORTED_FUNCTION = HRESULT($80090302);
  37. SEC_E_TARGET_UNKNOWN = HRESULT($80090303);
  38. SEC_E_INTERNAL_ERROR = HRESULT($80090304);
  39. SEC_E_SECPKG_NOT_FOUND = HRESULT($80090305);
  40. SEC_E_NOT_OWNER = HRESULT($80090306);
  41. SEC_E_CANNOT_INSTALL = HRESULT($80090307);
  42. SEC_E_INVALID_TOKEN = HRESULT($80090308);
  43. SEC_E_CANNOT_PACK = HRESULT($80090309);
  44. SEC_E_QOP_NOT_SUPPORTED = HRESULT($8009030A);
  45. SEC_E_NO_IMPERSONATION = HRESULT($8009030B);
  46. SEC_E_LOGON_DENIED = HRESULT($8009030C);
  47. SEC_E_UNKNOWN_CREDENTIALS = HRESULT($8009030D);
  48. SEC_E_NO_CREDENTIALS = HRESULT($8009030E);
  49. SEC_E_MESSAGE_ALTERED = HRESULT($8009030F);
  50. SEC_E_OUT_OF_SEQUENCE = HRESULT($80090310);
  51. SEC_E_NO_AUTHENTICATING_AUTHORITY = HRESULT($80090311);
  52. SEC_I_CONTINUE_NEEDED = HRESULT($00090312);
  53. SEC_I_COMPLETE_NEEDED = HRESULT($00090313);
  54. SEC_I_COMPLETE_AND_CONTINUE = HRESULT($00090314);
  55. SEC_I_LOCAL_LOGON = HRESULT($00090315);
  56. SEC_E_BAD_PKGID = HRESULT($80090316);
  57. SEC_E_CONTEXT_EXPIRED = HRESULT($80090317);
  58. SEC_E_INCOMPLETE_MESSAGE = HRESULT($80090318);
  59. SEC_E_INCOMPLETE_CREDENTIALS = HRESULT($80090320);
  60. SEC_E_BUFFER_TOO_SMALL = HRESULT($80090321);
  61. SEC_I_INCOMPLETE_CREDENTIALS = HRESULT($00090320);
  62. SEC_I_RENEGOTIATE = HRESULT($00090321);
  63. SEC_E_WRONG_PRINCIPAL = HRESULT($80090322);
  64. SEC_I_NO_LSA_CONTEXT = HRESULT($00090323);
  65. SEC_E_TIME_SKEW = HRESULT($80090324);
  66. SEC_E_UNTRUSTED_ROOT = HRESULT($80090325);
  67. SEC_E_ILLEGAL_MESSAGE = HRESULT($80090326);
  68. SEC_E_CERT_UNKNOWN = HRESULT($80090327);
  69. SEC_E_CERT_EXPIRED = HRESULT($80090328);
  70. SEC_E_ENCRYPT_FAILURE = HRESULT($80090329);
  71. SEC_E_DECRYPT_FAILURE = HRESULT($80090330);
  72. SEC_E_ALGORITHM_MISMATCH = HRESULT($80090331);
  73. SEC_E_SECURITY_QOS_FAILED = HRESULT($80090332);
  74. type
  75. { ESSPIException }
  76. ESSPIException = class(Exception)
  77. public
  78. class function GetErrorMessageByNo(aErrorNo: LongWord): string;
  79. public
  80. constructor CreateError(
  81. aFailedFuncName: string; anErrorNo: Longint = SEC_E_OK);
  82. end;
  83. ESSPIInterfaceInitFailed = class(ESSPIException);
  84. { TSSPIInterface }
  85. TSSPIInterface = class(TObject)
  86. private
  87. fLoadPending, fIsAvailable: Boolean;
  88. fPFunctionTable: PSecurityFunctionTableA;
  89. fDLLHandle: THandle;
  90. procedure releaseFunctionTable;
  91. procedure checkAvailable;
  92. function getFunctionTable: SecurityFunctionTableA;
  93. public
  94. class procedure RaiseIfError(
  95. aStatus: SECURITY_STATUS; aFunctionName: string);
  96. function IsAvailable: Boolean;
  97. property FunctionTable: SecurityFunctionTableA read getFunctionTable;
  98. public
  99. constructor Create;
  100. destructor Destroy; override;
  101. end;
  102. { TSSPIPackages }
  103. TSSPIPackage = class(TObject)
  104. private
  105. fPSecPkginfo: PSecPkgInfo;
  106. function getPSecPkgInfo: PSecPkgInfo;
  107. function getMaxToken: ULONG;
  108. function getName: string;
  109. public
  110. property MaxToken: ULONG read getMaxToken;
  111. property Name: string read getName;
  112. public
  113. constructor Create(aPSecPkginfo: PSecPkgInfo);
  114. end;
  115. TCustomSSPIPackage = class(TSSPIPackage)
  116. private
  117. fInfo: PSecPkgInfo;
  118. public
  119. constructor Create(aPkgName: string);
  120. destructor Destroy; override;
  121. end;
  122. TSSPINTLMPackage = class(TCustomSSPIPackage)
  123. public
  124. constructor Create;
  125. end;
  126. { TSSPICredentials }
  127. TSSPICredentialsUse = (scuInBound, scuOutBound, scuBoth);
  128. TSSPICredentials = class(TObject)
  129. private
  130. fPackage: TSSPIPackage;
  131. fHandle: CredHandle;
  132. fUse: TSSPICredentialsUse;
  133. fAcquired: Boolean;
  134. fExpiry: TimeStamp;
  135. function getHandle: PCredHandle;
  136. procedure setUse(aValue: TSSPICredentialsUse);
  137. protected
  138. procedure CheckAcquired;
  139. procedure CheckNotAcquired;
  140. procedure DoAcquire(pszPrincipal: PSEC_CHAR; pvLogonId, pAuthData: PVOID);
  141. procedure DoRelease; virtual;
  142. public
  143. procedure Release;
  144. property Package: TSSPIPackage read fPackage;
  145. property Handle: PCredHandle read getHandle;
  146. property Use: TSSPICredentialsUse read fUse write setUse;
  147. property Acquired: Boolean read fAcquired;
  148. public
  149. constructor Create(aPackage: TSSPIPackage);
  150. destructor Destroy; override;
  151. end;
  152. { TSSPIWinNTCredentials }
  153. TSSPIWinNTCredentials = class(TSSPICredentials)
  154. protected
  155. public
  156. procedure Acquire(
  157. aUse: TSSPICredentialsUse); overload;
  158. procedure Acquire(
  159. aUse: TSSPICredentialsUse; aDomain,
  160. aUserName, aPassword: string); overload;
  161. end;
  162. { TSSPIContext }
  163. TSSPIContext = class(TObject)
  164. private
  165. fCredentials: TSSPICredentials;
  166. fHandle: CtxtHandle;
  167. fHasHandle: Boolean;
  168. fExpiry: TimeStamp;
  169. function getHandle: PCtxtHandle;
  170. function getExpiry: TimeStamp;
  171. procedure updateHasContextAndCheckForError(
  172. const aFuncResult: SECURITY_STATUS; const aFuncName: string;
  173. const aErrorsToIgnore: array of SECURITY_STATUS);
  174. protected
  175. procedure CheckHasHandle;
  176. procedure CheckCredentials;
  177. function DoInitialize(
  178. aTokenSourceName: PChar;
  179. var aIn, aOut: SecBufferDesc;
  180. const errorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS;
  181. procedure DoRelease; virtual;
  182. function GetRequestedFlags: ULONG; virtual; abstract;
  183. procedure SetEstablishedFlags(aFlags: ULONG); virtual; abstract;
  184. function GetAuthenticated: Boolean; virtual; abstract;
  185. property HasHandle: Boolean read fHasHandle;
  186. public
  187. procedure Release;
  188. property Credentials: TSSPICredentials read fCredentials;
  189. property Handle: PCtxtHandle read getHandle;
  190. property Authenticated: Boolean read GetAuthenticated;
  191. property Expiry: TimeStamp read getExpiry;
  192. public
  193. constructor Create(aCredentials: TSSPICredentials);
  194. destructor Destroy; override;
  195. end;
  196. { TSSPIConnectionContext }
  197. TCustomSSPIConnectionContext = class(TSSPIContext)
  198. private
  199. fStatus: SECURITY_STATUS;
  200. fOutBuffDesc, fInBuffDesc: SecBufferDesc;
  201. fInBuff: SecBuffer;
  202. protected
  203. procedure DoRelease; override;
  204. function GetAuthenticated: Boolean; override;
  205. function DoUpdateAndGenerateReply(
  206. var aIn, aOut: SecBufferDesc;
  207. const aErrorsToIgnore: array of SECURITY_STATUS
  208. ): SECURITY_STATUS; virtual; abstract;
  209. public
  210. function UpdateAndGenerateReply(
  211. const aFromPeerToken: string; var aToPeerToken: string): Boolean;
  212. public
  213. constructor Create(aCredentials: TSSPICredentials);
  214. end;
  215. TSSPIClientConnectionContext = class(TCustomSSPIConnectionContext)
  216. private
  217. fTargetName: string;
  218. fReqReguested, fReqEstablished: ULONG;
  219. protected
  220. function GetRequestedFlags: ULONG; override;
  221. procedure SetEstablishedFlags(aFlags: ULONG); override;
  222. function DoUpdateAndGenerateReply(
  223. var aIn, aOut: SecBufferDesc;
  224. const aErrorsToIgnore: array of SECURITY_STATUS
  225. ): SECURITY_STATUS; override;
  226. public
  227. function GenerateInitialChalenge(
  228. const aTargetName: string; var aToPeerToken: string): Boolean;
  229. public
  230. constructor Create(aCredentials: TSSPICredentials);
  231. end;
  232. TIndySSPINTLMClient = class(TObject)
  233. protected
  234. fNTLMPackage: TSSPINTLMPackage;
  235. fCredentials: TSSPIWinNTCredentials;
  236. fContext: TSSPIClientConnectionContext;
  237. public
  238. procedure SetCredentials(aDomain, aUserName, aPassword: string);
  239. procedure SetCredentialsAsCurrentUser;
  240. function InitAndBuildType1Message: string;
  241. function UpdateAndBuildType3Message(aServerType2Message: string): string;
  242. public
  243. constructor Create;
  244. destructor Destroy; override;
  245. end;
  246. TIdSSPINTLMAuthentication = class(TIdAuthentication)
  247. protected
  248. FNTLMInfo: string;
  249. FSSPIClient: TIndySSPINTLMClient;
  250. procedure SetDomain(const Value: String);
  251. function GetDomain: String;
  252. procedure SetUserName(const Value: String); override;
  253. function GetSteps: Integer; override;
  254. function DoNext: TIdAuthWhatsNext; override;
  255. public
  256. constructor Create; override;
  257. function Authentication: string; override;
  258. function KeepAlive: Boolean; override;
  259. procedure Reset; override;
  260. property Domain: String read GetDomain write SetDomain;
  261. end;
  262. implementation
  263. uses
  264. IdGlobal,
  265. IdException,
  266. IdCoderMIME,
  267. IdResourceStrings, Math, IdHeaderList;
  268. var
  269. g: TSSPIInterface;
  270. { ESSPIException }
  271. class function ESSPIException.GetErrorMessageByNo
  272. (aErrorNo: LongWord): string;
  273. begin
  274. case HRESULT(aErrorNo) of
  275. SEC_E_OK: Result := RSHTTPSSPISuccess;
  276. SEC_E_INSUFFICIENT_MEMORY:
  277. Result := RSHTTPSSPINotEnoughMem;
  278. SEC_E_INVALID_HANDLE:
  279. Result := RSHTTPSSPIInvalidHandle;
  280. SEC_E_UNSUPPORTED_FUNCTION:
  281. Result := RSHTTPSSPIFuncNotSupported;
  282. SEC_E_TARGET_UNKNOWN:
  283. Result := RSHTTPSSPIUnknownTarget;
  284. SEC_E_INTERNAL_ERROR:
  285. Result := RSHTTPSSPIInternalError;
  286. SEC_E_SECPKG_NOT_FOUND:
  287. Result := RSHTTPSSPISecPackageNotFound;
  288. SEC_E_NOT_OWNER:
  289. Result := RSHTTPSSPINotOwner;
  290. SEC_E_CANNOT_INSTALL:
  291. Result := RSHTTPSSPIPackageCannotBeInstalled;
  292. SEC_E_INVALID_TOKEN:
  293. Result := RSHTTPSSPIInvalidToken;
  294. SEC_E_CANNOT_PACK:
  295. Result := RSHTTPSSPICannotPack;
  296. SEC_E_QOP_NOT_SUPPORTED:
  297. Result := RSHTTPSSPIQOPNotSupported;
  298. SEC_E_NO_IMPERSONATION:
  299. Result := RSHTTPSSPINoImpersonation;
  300. SEC_E_LOGON_DENIED:
  301. Result := RSHTTPSSPILoginDenied;
  302. SEC_E_UNKNOWN_CREDENTIALS:
  303. Result := RSHTTPSSPIUnknownCredentials;
  304. SEC_E_NO_CREDENTIALS:
  305. Result := RSHTTPSSPINoCredentials;
  306. SEC_E_MESSAGE_ALTERED:
  307. Result := RSHTTPSSPIMessageAltered;
  308. SEC_E_OUT_OF_SEQUENCE:
  309. Result := RSHTTPSSPIOutOfSequence;
  310. SEC_E_NO_AUTHENTICATING_AUTHORITY:
  311. Result := RSHTTPSSPINoAuthAuthority;
  312. SEC_I_CONTINUE_NEEDED:
  313. Result := RSHTTPSSPIContinueNeeded;
  314. SEC_I_COMPLETE_NEEDED:
  315. Result := RSHTTPSSPICompleteNeeded;
  316. SEC_I_COMPLETE_AND_CONTINUE:
  317. Result :=RSHTTPSSPICompleteContinueNeeded;
  318. SEC_I_LOCAL_LOGON:
  319. Result := RSHTTPSSPILocalLogin;
  320. SEC_E_BAD_PKGID:
  321. Result := RSHTTPSSPIBadPackageID;
  322. SEC_E_CONTEXT_EXPIRED:
  323. Result := RSHTTPSSPIContextExpired;
  324. SEC_E_INCOMPLETE_MESSAGE:
  325. Result := RSHTTPSSPIIncompleteMessage;
  326. SEC_E_INCOMPLETE_CREDENTIALS:
  327. Result := RSHTTPSSPIIncompleteCredentialNotInit;
  328. SEC_E_BUFFER_TOO_SMALL:
  329. Result := RSHTTPSSPIBufferTooSmall;
  330. SEC_I_INCOMPLETE_CREDENTIALS:
  331. Result := RSHTTPSSPIIncompleteCredentialsInit;
  332. SEC_I_RENEGOTIATE:
  333. Result := RSHTTPSSPIRengotiate;
  334. SEC_E_WRONG_PRINCIPAL:
  335. Result := RSHTTPSSPIWrongPrincipal;
  336. SEC_I_NO_LSA_CONTEXT:
  337. Result := RSHTTPSSPINoLSACode;
  338. SEC_E_TIME_SKEW:
  339. Result := RSHTTPSSPITimeScew;
  340. SEC_E_UNTRUSTED_ROOT:
  341. Result := RSHTTPSSPIUntrustedRoot;
  342. SEC_E_ILLEGAL_MESSAGE:
  343. Result := RSHTTPSSPIIllegalMessage;
  344. SEC_E_CERT_UNKNOWN:
  345. Result := RSHTTPSSPICertUnknown;
  346. SEC_E_CERT_EXPIRED:
  347. Result := RSHTTPSSPICertExpired;
  348. SEC_E_ENCRYPT_FAILURE:
  349. Result := RSHTTPSSPIEncryptionFailure;
  350. SEC_E_DECRYPT_FAILURE:
  351. Result := RSHTTPSSPIDecryptionFailure;
  352. SEC_E_ALGORITHM_MISMATCH:
  353. Result := RSHTTPSSPIAlgorithmMismatch;
  354. SEC_E_SECURITY_QOS_FAILED:
  355. Result := RSHTTPSSPISecurityQOSFailure;
  356. else
  357. Result := RSHTTPSSPIUnknwonError;
  358. end;
  359. end;
  360. constructor ESSPIException.CreateError
  361. (aFailedFuncName: string; anErrorNo: Longint = SEC_E_OK);
  362. begin
  363. if anErrorNo = SEC_E_OK then
  364. inherited Create(aFailedFuncName)
  365. else
  366. inherited CreateFmt(
  367. RSHTTPSSPIErrorMsg,
  368. [aFailedFuncName, anErrorNo, anErrorNo, GetErrorMessageByNo(anErrorNo)]);
  369. end;
  370. { TSSPIInterface }
  371. procedure TSSPIInterface.releaseFunctionTable;
  372. begin
  373. if fPFunctionTable <> nil then begin
  374. fPFunctionTable := nil;
  375. end;
  376. end;
  377. procedure TSSPIInterface.checkAvailable;
  378. begin
  379. if not IsAvailable then
  380. raise ESSPIInterfaceInitFailed.Create(
  381. RSHTTPSSPIInterfaceInitFailed);
  382. end;
  383. function TSSPIInterface.getFunctionTable: SecurityFunctionTableA;
  384. begin
  385. checkAvailable;
  386. Result := fPFunctionTable^;
  387. end;
  388. class procedure TSSPIInterface.RaiseIfError
  389. (aStatus: SECURITY_STATUS; aFunctionName: string);
  390. begin
  391. if not SEC_SUCCESS(aStatus) then
  392. raise ESSPIException.CreateError(aFunctionName, aStatus);
  393. end;
  394. function TSSPIInterface.IsAvailable: Boolean;
  395. procedure loadDLL;
  396. const
  397. SECURITY_DLL_NT = 'security.dll'; {Do not translate}
  398. SECURITY_DLL_95 = 'secur32.dll'; {Do not translate}
  399. ENCRYPT_MESSAGE = 'EncryptMessage'; {Do not translate}
  400. DECRYPT_MESSAGE = 'DecryptMessage'; {Do not translate}
  401. var
  402. dllName: string;
  403. entrypoint: INIT_SECURITY_INTERFACE_A;
  404. begin
  405. fIsAvailable := False;
  406. if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
  407. { Windows95 SSPI dll }
  408. dllName := SECURITY_DLL_95
  409. else
  410. { WindowsNT & Windows2000 SSPI dll }
  411. dllName := SECURITY_DLL_NT;
  412. { load SSPI dll }
  413. fDLLHandle := LoadLibrary(@dllName[1]);
  414. if fDLLHandle > 0 then begin
  415. { get InitSecurityInterface entry point
  416. and call it to fetch SPPI function table}
  417. entrypoint := GetProcAddress(fDLLHandle, SECURITY_ENTRYPOINTA);
  418. fPFunctionTable := entrypoint;
  419. { let's see what SSPI functions are available
  420. and if we can continue on with the set }
  421. with fPFunctionTable^ do begin
  422. fIsAvailable :=
  423. Assigned(QuerySecurityPackageInfoA) and
  424. Assigned(FreeContextBuffer) and
  425. Assigned(DeleteSecurityContext) and
  426. Assigned(FreeCredentialHandle) and
  427. Assigned(AcquireCredentialsHandleA) and
  428. Assigned(InitializeSecurityContextA) and
  429. Assigned(AcceptSecurityContext) and
  430. Assigned(ImpersonateSecurityContext) and
  431. Assigned(RevertSecurityContext) and
  432. Assigned(QueryContextAttributesA) and
  433. Assigned(MakeSignature) and
  434. Assigned(VerifySignature);
  435. {$IFDEF SET_ENCRYPT_IN_FT_WITH_GETPROCADDRESS_FUDGE}
  436. { fudge for Encrypt/DecryptMessage }
  437. if (not Assigned(EncryptMessage)) and (GetProcAddress(fDLLHandle, ENCRYPT_MESSAGE) <> nil) then
  438. EncryptMessage := GetProcAddress(fDLLHandle, ENCRYPT_MESSAGE);
  439. if (not Assigned(DecryptMessage)) and (GetProcAddress(fDLLHandle, DECRYPT_MESSAGE) <> nil) then
  440. DecryptMessage := GetProcAddress(fDLLHandle, DECRYPT_MESSAGE);
  441. {$ENDIF}
  442. end;
  443. end;
  444. end;
  445. begin
  446. if fIsAvailable then
  447. Result := True
  448. else begin
  449. if fLoadPending then begin
  450. releaseFunctionTable;
  451. loadDLL;
  452. fLoadPending := False;
  453. end;
  454. Result := fIsAvailable;
  455. end;
  456. end;
  457. constructor TSSPIInterface.Create;
  458. begin
  459. inherited Create;
  460. fLoadPending := True;
  461. fIsAvailable := False;
  462. fPFunctionTable := nil;
  463. end;
  464. destructor TSSPIInterface.Destroy;
  465. begin
  466. releaseFunctionTable;
  467. FreeLibrary(fDLLHandle);
  468. inherited Destroy;
  469. end;
  470. { TSSPIPackage }
  471. function TSSPIPackage.getPSecPkgInfo: PSecPkgInfo;
  472. begin
  473. if fPSecPkginfo = nil then
  474. raise ESSPIException.Create(RSHTTPSSPINoPkgInfoSpecified);
  475. Result := fPSecPkginfo;
  476. end;
  477. function TSSPIPackage.getMaxToken: ULONG;
  478. begin
  479. Result := getPSecPkgInfo^.cbMaxToken;
  480. end;
  481. function TSSPIPackage.getName: string;
  482. begin
  483. Result := StrPas(getPSecPkgInfo^.Name);
  484. end;
  485. constructor TSSPIPackage.Create(aPSecPkginfo: PSecPkgInfo);
  486. begin
  487. inherited Create;
  488. fPSecPkginfo := aPSecPkginfo;
  489. end;
  490. { TCustomSSPIPackage }
  491. constructor TCustomSSPIPackage.Create(aPkgName: string);
  492. begin
  493. g.RaiseIfError(
  494. g.FunctionTable.QuerySecurityPackageInfoA(PChar(aPkgName), @fInfo),
  495. 'QuerySecurityPackageInfoA'); {Do not translate}
  496. inherited Create(fInfo);
  497. end;
  498. destructor TCustomSSPIPackage.Destroy;
  499. begin
  500. if fInfo <> nil then
  501. g.RaiseIfError(
  502. g.FunctionTable.FreeContextBuffer(fInfo), 'FreeContextBuffer'); {Do not translate}
  503. inherited Destroy;
  504. end;
  505. { TSSPINTLMPackage }
  506. constructor TSSPINTLMPackage.Create;
  507. begin
  508. inherited Create(NTLMSP_NAME);
  509. end;
  510. { TSSPICredentials }
  511. procedure TSSPICredentials.CheckAcquired;
  512. begin
  513. if not fAcquired then
  514. raise ESSPIException.Create(RSHTTPSSPINoCredentialHandle);
  515. end;
  516. procedure TSSPICredentials.CheckNotAcquired;
  517. begin
  518. if fAcquired then
  519. raise ESSPIException.Create(
  520. RSHTTPSSPICanNotChangeCredentials);
  521. end;
  522. procedure TSSPICredentials.DoAcquire
  523. (pszPrincipal: PSEC_CHAR; pvLogonId, pAuthData: PVOID);
  524. var
  525. cu: ULONG;
  526. begin
  527. Release;
  528. case Use of
  529. scuInBound:
  530. cu := SECPKG_CRED_INBOUND;
  531. scuOutBound:
  532. cu := SECPKG_CRED_OUTBOUND;
  533. scuBoth:
  534. cu := SECPKG_CRED_BOTH;
  535. else
  536. raise ESSPIException.Create(RSHTTPSSPIUnknwonCredentialUse);
  537. end;
  538. g.RaiseIfError(
  539. g.FunctionTable.AcquireCredentialsHandleA(
  540. pszPrincipal, PSEC_CHAR(Package.Name), cu, pvLogonId, pAuthData, nil, nil,
  541. @fHandle, @fExpiry),
  542. 'AcquireCredentialsHandleA'); {Do not translater}
  543. fAcquired := True;
  544. end;
  545. procedure TSSPICredentials.DoRelease;
  546. begin
  547. g.RaiseIfError(
  548. g.FunctionTable.FreeCredentialHandle(@fHandle),
  549. 'FreeCredentialHandle'); {Do not translate}
  550. SecInvalidateHandle(@fHandle);
  551. end;
  552. procedure TSSPICredentials.Release;
  553. begin
  554. if fAcquired then begin
  555. DoRelease;
  556. fAcquired := False;
  557. end;
  558. end;
  559. function TSSPICredentials.getHandle: PCredHandle;
  560. begin
  561. CheckAcquired;
  562. Result := @fHandle;
  563. end;
  564. procedure TSSPICredentials.setUse(aValue: TSSPICredentialsUse);
  565. begin
  566. if fUse <> aValue then begin
  567. CheckNotAcquired;
  568. fUse := aValue;
  569. end;
  570. end;
  571. constructor TSSPICredentials.Create(aPackage: TSSPIPackage);
  572. begin
  573. inherited Create;
  574. fPackage := aPackage;
  575. fUse := scuOutBound;
  576. fAcquired := False;
  577. end;
  578. destructor TSSPICredentials.Destroy;
  579. begin
  580. Release;
  581. inherited Destroy;
  582. end;
  583. { TSSPIWinNTCredentials }
  584. procedure TSSPIWinNTCredentials.Acquire(aUse: TSSPICredentialsUse);
  585. begin
  586. Acquire(aUse, '', '', ''); {Do not translate}
  587. end;
  588. procedure TSSPIWinNTCredentials.Acquire
  589. (aUse: TSSPICredentialsUse; aDomain, aUserName, aPassword: string);
  590. var
  591. ai: SEC_WINNT_AUTH_IDENTITY;
  592. pai: PVOID;
  593. begin
  594. Use := aUse;
  595. if (Length(aDomain) > 0) and (Length(aUserName) > 0) then begin
  596. with ai do begin
  597. User := PChar(aUserName);
  598. UserLength := Length(aUserName);
  599. Domain := PChar(aDomain);
  600. DomainLength := Length(aDomain);
  601. Password := PChar(aPassword);
  602. PasswordLength := Length(aPassword);
  603. Flags := SEC_WINNT_AUTH_IDENTITY_ANSI;
  604. end;
  605. pai := @ai;
  606. end else
  607. pai := nil;
  608. DoAcquire(nil, nil, pai);
  609. end;
  610. { TSSPIContext }
  611. constructor TSSPIContext.Create(aCredentials: TSSPICredentials);
  612. begin
  613. inherited Create;
  614. fCredentials := aCredentials;
  615. fHasHandle := False;
  616. end;
  617. destructor TSSPIContext.Destroy;
  618. begin
  619. Release;
  620. inherited Destroy;
  621. end;
  622. procedure TSSPIContext.updateHasContextAndCheckForError(
  623. const aFuncResult: SECURITY_STATUS; const aFuncName: string;
  624. const aErrorsToIgnore: array of SECURITY_STATUS);
  625. var
  626. doRaise: Boolean;
  627. i: Integer;
  628. begin
  629. doRaise := not SEC_SUCCESS(aFuncResult);
  630. if doRaise then
  631. for i := Low(aErrorsToIgnore) to High(aErrorsToIgnore) do
  632. if aFuncResult = aErrorsToIgnore[i] then begin
  633. doRaise := False;
  634. break;
  635. end;
  636. if doRaise then
  637. raise ESSPIException.CreateError(aFuncName, aFuncResult);
  638. if not fHasHandle then
  639. fHasHandle := True;
  640. end;
  641. function TSSPIContext.DoInitialize
  642. (aTokenSourceName: PChar;
  643. var aIn, aOut: SecBufferDesc;
  644. const errorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS;
  645. var
  646. tmp: PCtxtHandle;
  647. tmp2: PSecBufferDesc;
  648. r: ULONG;
  649. begin
  650. if fHasHandle then begin
  651. tmp := @fHandle;
  652. tmp2 := @aIn;
  653. end else begin
  654. tmp := nil;
  655. tmp2 := nil;
  656. end;
  657. Result :=
  658. g.FunctionTable.InitializeSecurityContextA(
  659. Credentials.Handle, tmp, aTokenSourceName,
  660. GetRequestedFlags, 0, SECURITY_NATIVE_DREP, tmp2, 0,
  661. @fHandle, @aOut, @r, @fExpiry
  662. );
  663. updateHasContextAndCheckForError(
  664. Result, 'InitializeSecurityContextA', errorsToIgnore); {Do not translate}
  665. SetEstablishedFlags(r);
  666. end;
  667. procedure TSSPIContext.DoRelease;
  668. begin
  669. g.RaiseIfError(
  670. g.FunctionTable.DeleteSecurityContext(@fHandle), 'DeleteSecurityContext'); {Do not translate}
  671. end;
  672. procedure TSSPIContext.Release;
  673. begin
  674. if HasHandle then begin
  675. DoRelease;
  676. fHasHandle := False;
  677. end;
  678. end;
  679. procedure TSSPIContext.CheckHasHandle;
  680. begin
  681. if not HasHandle then
  682. raise ESSPIException.Create(RSHTTPSSPINoCredentialHandle);
  683. end;
  684. procedure TSSPIContext.CheckCredentials;
  685. begin
  686. if (not Assigned(Credentials)) or (not Credentials.Acquired) then
  687. raise ESSPIException.Create(RSHTTPSSPIDoAuquireCredentialHandle);
  688. end;
  689. function TSSPIContext.getExpiry: TimeStamp;
  690. begin
  691. CheckHasHandle;
  692. Result := fExpiry;
  693. end;
  694. function TSSPIContext.getHandle: PCtxtHandle;
  695. begin
  696. CheckHasHandle;
  697. Result := @fHandle;
  698. end;
  699. { TCustomSSPIConnectionContext }
  700. procedure TCustomSSPIConnectionContext.DoRelease;
  701. begin
  702. inherited DoRelease;
  703. fStatus := SEC_E_INVALID_HANDLE; // just to put something other then SEC_E_OK
  704. end;
  705. function TCustomSSPIConnectionContext.GetAuthenticated: Boolean;
  706. begin
  707. CheckHasHandle;
  708. Result := fStatus = SEC_E_OK;
  709. end;
  710. function TCustomSSPIConnectionContext.UpdateAndGenerateReply
  711. (const aFromPeerToken: string; var aToPeerToken: string): Boolean;
  712. var
  713. fOutBuff: SecBuffer;
  714. begin
  715. Result := False;
  716. { check credentials }
  717. CheckCredentials;
  718. { prepare input buffer }
  719. with fInBuff do begin
  720. cbBuffer := Length(aFromPeerToken);
  721. pvBuffer := @(aFromPeerToken[1]);
  722. end;
  723. { prepare output buffer }
  724. with fOutBuff do begin
  725. BufferType := SECBUFFER_TOKEN;
  726. cbBuffer := Credentials.Package.MaxToken;
  727. pvBuffer := AllocMem(cbBuffer);
  728. end;
  729. with fOutBuffDesc do begin
  730. ulVersion := SECBUFFER_VERSION;
  731. cBuffers := 1;
  732. pBuffers := @fOutBuff;
  733. end;
  734. try
  735. { do processing }
  736. fStatus := DoUpdateAndGenerateReply(fInBuffDesc, fOutBuffDesc, []);
  737. { complete token if applicable }
  738. case fStatus of
  739. SEC_I_COMPLETE_NEEDED,
  740. SEC_I_COMPLETE_AND_CONTINUE:
  741. begin
  742. if not Assigned(g.FunctionTable.CompleteAuthToken) then
  743. begin
  744. raise ESSPIException.Create(RSHTTPSSPICompleteTokenNotSupported);
  745. end;
  746. fStatus := g.FunctionTable.CompleteAuthToken(Handle, @fOutBuffDesc);
  747. g.RaiseIfError(fStatus, 'CompleteAuthToken'); {Do not translate}
  748. end;
  749. end;
  750. Result :=
  751. (fStatus = SEC_I_CONTINUE_NEEDED) or
  752. (fStatus = SEC_I_COMPLETE_AND_CONTINUE) or
  753. (fOutBuff.cbBuffer > 0);
  754. if Result then
  755. with fOutBuff do
  756. SetString(aToPeerToken, PChar(pvBuffer), cbBuffer);
  757. finally
  758. FreeMem(fOutBuff.pvBuffer);
  759. end;
  760. end;
  761. constructor TCustomSSPIConnectionContext.Create(aCredentials: TSSPICredentials);
  762. begin
  763. inherited Create(aCredentials);
  764. with fInBuff do begin
  765. BufferType := SECBUFFER_TOKEN;
  766. end;
  767. with fInBuffDesc do begin
  768. ulVersion := SECBUFFER_VERSION;
  769. cBuffers := 1;
  770. pBuffers := @fInBuff;
  771. end;
  772. with fOutBuffDesc do begin
  773. ulVersion := SECBUFFER_VERSION;
  774. cBuffers := 1;
  775. end;
  776. end;
  777. { TSSPIClientConnectionContext }
  778. constructor TSSPIClientConnectionContext.Create(aCredentials: TSSPICredentials);
  779. begin
  780. inherited Create(aCredentials);
  781. fTargetName := ''; {Do not translate}
  782. end;
  783. function TSSPIClientConnectionContext.GetRequestedFlags: ULONG;
  784. begin
  785. Result := fReqReguested;
  786. end;
  787. procedure TSSPIClientConnectionContext.SetEstablishedFlags(aFlags: ULONG);
  788. begin
  789. fReqEstablished := aFlags;
  790. end;
  791. function TSSPIClientConnectionContext.DoUpdateAndGenerateReply
  792. (var aIn, aOut: SecBufferDesc;
  793. const aErrorsToIgnore: array of SECURITY_STATUS): SECURITY_STATUS;
  794. begin
  795. Result := DoInitialize(PChar(fTargetName), aIn, aOut, []);
  796. end;
  797. function TSSPIClientConnectionContext.GenerateInitialChalenge
  798. (const aTargetName: string; var aToPeerToken: string): Boolean;
  799. begin
  800. Release;
  801. fTargetName := aTargetName;
  802. Result := UpdateAndGenerateReply('', aToPeerToken); {Do not translate}
  803. end;
  804. { TIndySSPINTLMClient }
  805. constructor TIndySSPINTLMClient.Create;
  806. begin
  807. inherited Create;
  808. fNTLMPackage := TSSPINTLMPackage.Create;
  809. fCredentials := TSSPIWinNTCredentials.Create(fNTLMPackage);
  810. fContext := TSSPIClientConnectionContext.Create(fCredentials);
  811. end;
  812. destructor TIndySSPINTLMClient.Destroy;
  813. begin
  814. fContext.Free;
  815. fCredentials.Free;
  816. fNTLMPackage.Free;
  817. inherited Destroy;
  818. end;
  819. procedure TIndySSPINTLMClient.SetCredentials
  820. (aDomain, aUserName, aPassword: string);
  821. begin
  822. fCredentials.Acquire(scuOutBound, aDomain, aUserName, aPassword);
  823. end;
  824. procedure TIndySSPINTLMClient.SetCredentialsAsCurrentUser;
  825. begin
  826. fCredentials.Acquire(scuOutBound);
  827. end;
  828. function TIndySSPINTLMClient.InitAndBuildType1Message: string;
  829. begin
  830. fContext.GenerateInitialChalenge('', Result);
  831. end;
  832. function TIndySSPINTLMClient.UpdateAndBuildType3Message
  833. (aServerType2Message: string): string;
  834. begin
  835. fContext.UpdateAndGenerateReply(aServerType2Message, Result);
  836. end;
  837. { TIdSSPINTLMAuthentication }
  838. constructor TIdSSPINTLMAuthentication.Create;
  839. begin
  840. inherited Create;
  841. FSSPIClient := TIndySSPINTLMClient.Create;
  842. Domain := IndyGetHostName;
  843. end;
  844. function TIdSSPINTLMAuthentication.DoNext: TIdAuthWhatsNext;
  845. begin
  846. result := wnDoRequest;
  847. case FCurrentStep of
  848. 0:
  849. begin
  850. {if (Length(Username) > 0) and (Length(Password) > 0) then
  851. begin}
  852. result := wnDoRequest;
  853. FCurrentStep := 1;
  854. {end
  855. else begin
  856. result := wnAskTheProgram;
  857. end;}
  858. end;
  859. 1:
  860. begin
  861. FCurrentStep := 2;
  862. result := wnDoRequest;
  863. end;
  864. 3:
  865. begin
  866. FCurrentStep := 4;
  867. result := wnDoRequest;
  868. end;
  869. 4:
  870. begin
  871. FCurrentStep := 0;
  872. If Username = '' then
  873. result := wnAskTheProgram
  874. else begin
  875. result := wnFail;
  876. Username := '';
  877. Password := '';
  878. Domain := IndyGetHostName;
  879. end;
  880. end;
  881. end;
  882. end;
  883. function TIdSSPINTLMAuthentication.Authentication: string;
  884. var
  885. S: string;
  886. begin
  887. result := '';
  888. case FCurrentStep of
  889. 1:
  890. begin
  891. if Length(Username) = 0 then
  892. FSSPIClient.SetCredentialsAsCurrentUser
  893. else
  894. FSSPIClient.SetCredentials(Domain, Username, Password);
  895. result := 'NTLM ' + TIdEncoderMIME.EncodeString(FSSPIClient.InitAndBuildType1Message); {Do not translate}
  896. FNTLMInfo := ''; {Do not translate}
  897. end;
  898. 2:
  899. begin
  900. if Length(FNTLMInfo) = 0 then
  901. begin
  902. FNTLMInfo := ReadAuthInfo('NTLM'); {Do not translate}
  903. Fetch(FNTLMInfo);
  904. end;
  905. if Length(FNTLMInfo) = 0 then
  906. begin
  907. Reset;
  908. Abort;
  909. end;
  910. S := TIdDecoderMIME.DecodeString(FNTLMInfo);
  911. result := 'NTLM ' + TIdEncoderMIME.EncodeString(FSSPIClient.UpdateAndBuildType3Message(S)); {Do not translate}
  912. FCurrentStep := 3;
  913. Inc(FAuthRetries);
  914. end;
  915. 3: begin
  916. FCurrentStep := 4;
  917. end;
  918. end;
  919. end;
  920. procedure TIdSSPINTLMAuthentication.Reset;
  921. begin
  922. inherited Reset;
  923. FCurrentStep := 0;
  924. end;
  925. function TIdSSPINTLMAuthentication.KeepAlive: Boolean;
  926. begin
  927. result := FCurrentStep >= 1;
  928. end;
  929. function TIdSSPINTLMAuthentication.GetSteps: Integer;
  930. begin
  931. result := 3;
  932. end;
  933. procedure TIdSSPINTLMAuthentication.SetDomain(const Value: String);
  934. begin
  935. Params.Values['Domain'] := Value;
  936. end;
  937. function TIdSSPINTLMAuthentication.GetDomain: String;
  938. begin
  939. result := Params.Values['Domain'];
  940. end;
  941. procedure TIdSSPINTLMAuthentication.SetUserName(const Value: String);
  942. Var
  943. S: String;
  944. begin
  945. S := Value;
  946. if IndyPos('\', S) > 0 then begin
  947. Domain := Copy(S, 1, IndyPos('\', S) - 1);
  948. Delete(S, 1, Length(Domain) + 1);
  949. end;
  950. inherited SetUserName(S);
  951. end;
  952. initialization
  953. g := TSSPIInterface.Create;
  954. if g.IsAvailable then
  955. RegisterAuthenticationMethod('NTLM', TIdSSPINTLMAuthentication); {Do not translate}
  956. finalization
  957. g.Free;
  958. end.