| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154 |
- {
- 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-2024, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
- }
- unit IdSSLOpenSSL;
- {
- Author: Gregor Ibic ([email protected])
- Copyright: (c) Gregor Ibic, Intelicom d.o.o and Indy Working Group.
- }
- {
- Indy OpenSSL now uses the standard OpenSSL libraries
- for pre-compiled win32 dlls, see:
- http://www.openssl.org/related/binaries.html
- recommended v0.9.8a or later
- }
- {
- Important information concerning OnVerifyPeer:
- Rev 1.39 of February 2005 deliberately broke the OnVerifyPeer interface,
- which (obviously?) only affects programs that implemented that callback
- as part of the SSL negotiation. Note that you really should always
- implement OnVerifyPeer, otherwise the certificate of the peer you are
- connecting to is NOT checked to ensure it is valid.
- Prior to this, if the SSL library detected a problem with a certificate
- or the Depth was insufficient (i.e. the "Ok" parameter in VerifyCallback
- is 0 / FALSE), then irrespective of whether your OnVerifyPeer returned True
- or False, the SSL connection would be deliberately failed.
- This created a problem in that even if there was only a very minor
- problem with one of the certificates in the chain (OnVerifyPeer is called
- once for each certificate in the certificate chain), which the user may
- have been happy to accept, the SSL negotiation would be failed. However,
- changing the code to allow the SSL connection when a user returned True
- for OnVerifyPeer would have meant that existing code which depended on
- automatic rejection of invalid certificates would then be accepting
- invalid certificates, which would have been an unacceptable security
- change.
- Consequently, OnVerifyPeer was changed to deliberately break existing code
- by adding an AOk parameter. To preserve the previous functionality, your
- OnVerifyPeer event should do "Result := AOk;". If you wish to consider
- accepting certificates that the SSL library has considered invalid, then
- in your OnVerifyPeer, make sure you satisfy yourself that the certificate
- really is valid and then set Result to True. In reality, in addition to
- checking AOk, you should always implement code that ensures you are only
- accepting certificates which are valid (at least from your point of view).
- Ciaran Costelloe, [email protected]
- }
- {
- RLebeau 1/12/2011: Breaking OnVerifyPeer event again, this time to add an
- additional AError parameter (patch courtesy of "jvlad", [email protected]).
- This helps user code distinquish between Self-signed and invalid certificates.
- }
- interface
- {$I IdCompilerDefines.inc}
- {$TYPEDADDRESS OFF}
- uses
- //facilitate inlining only.
- {$IFDEF WINDOWS}
- Windows,
- {$ENDIF}
- Classes,
- IdBuffer,
- IdCTypes,
- IdGlobal,
- IdException,
- IdStackConsts,
- IdSocketHandle,
- IdSSLOpenSSLHeaders,
- IdComponent,
- IdIOHandler,
- IdGlobalProtocols,
- IdTCPServer,
- IdThread,
- IdTCPConnection,
- IdIntercept,
- IdIOHandlerSocket,
- IdSSL,
- IdSocks,
- IdScheduler,
- IdYarn;
- type
- TIdSSLVersion = (sslvSSLv2, sslvSSLv23, sslvSSLv3, sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2);
- TIdSSLVersions = set of TIdSSLVersion;
- TIdSSLMode = (sslmUnassigned, sslmClient, sslmServer, sslmBoth);
- TIdSSLVerifyMode = (sslvrfPeer, sslvrfFailIfNoPeerCert, sslvrfClientOnce);
- TIdSSLVerifyModeSet = set of TIdSSLVerifyMode;
- TIdSSLCtxMode = (sslCtxClient, sslCtxServer);
- TIdSSLAction = (sslRead, sslWrite);
- const
- DEF_SSLVERSION = sslvTLSv1;
- DEF_SSLVERSIONS = [sslvTLSv1];
- P12_FILETYPE = 3;
- MAX_SSL_PASSWORD_LENGTH = 128;
- type
- TIdSSLULong = packed record
- case Byte of
- 0: (B1, B2, B3, B4: UInt8);
- 1: (W1, W2: UInt16);
- 2: (L1: Int32);
- 3: (C1: UInt32);
- end;
- TIdSSLEVP_MD = record
- Length: TIdC_UINT;
- MD: Array [0 .. EVP_MAX_MD_SIZE - 1] of TIdAnsiChar;
- end;
- TIdSSLByteArray = record
- Length: TIdC_UINT;
- Data: PByte;
- end;
- TIdX509 = class;
- TIdSSLIOHandlerSocketOpenSSL = class;
- TIdSSLCipher = class;
- TCallbackEvent = procedure(const AMsg: String) of object;
- TCallbackExEvent = procedure(ASender : TObject; const AsslSocket: PSSL;
- const AWhere, Aret: TIdC_INT; const AType, AMsg : String ) of object;
- TPasswordEvent = procedure(var Password: String) of object;
- TPasswordEventEx = procedure( ASender : TObject; var VPassword: String; const AIsWrite : Boolean) of object;
- TVerifyPeerEvent = function(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean of object;
- TIOHandlerNotify = procedure(ASender: TIdSSLIOHandlerSocketOpenSSL) of object;
- TIdSSLOptions = class(TPersistent)
- protected
- fsRootCertFile,
- fsCertFile,
- fsKeyFile,
- fsDHParamsFile: String;
- fMethod: TIdSSLVersion;
- fSSLVersions : TIdSSLVersions;
- fMode: TIdSSLMode;
- fVerifyDepth: Integer;
- fVerifyMode: TIdSSLVerifyModeSet;
- //fVerifyFile,
- fVerifyDirs: String;
- fCipherList: String;
- procedure AssignTo(Destination: TPersistent); override;
- procedure SetSSLVersions(const AValue : TIdSSLVersions);
- procedure SetMethod(const AValue : TIdSSLVersion);
- public
- constructor Create;
- // procedure Assign(ASource: TPersistent); override;
- published
- property RootCertFile: String read fsRootCertFile write fsRootCertFile;
- property CertFile: String read fsCertFile write fsCertFile;
- property KeyFile: String read fsKeyFile write fsKeyFile;
- property DHParamsFile: String read fsDHParamsFile write fsDHParamsFile;
- property Method: TIdSSLVersion read fMethod write SetMethod default DEF_SSLVERSION;
- property SSLVersions : TIdSSLVersions read fSSLVersions write SetSSLVersions default DEF_SSLVERSIONS;
- property Mode: TIdSSLMode read fMode write fMode;
- property VerifyMode: TIdSSLVerifyModeSet read fVerifyMode write fVerifyMode;
- property VerifyDepth: Integer read fVerifyDepth write fVerifyDepth;
- // property VerifyFile: String read fVerifyFile write fVerifyFile;
- property VerifyDirs: String read fVerifyDirs write fVerifyDirs;
- property CipherList: String read fCipherList write fCipherList;
- end;
- TIdSSLContext = class(TObject)
- protected
- fMethod: TIdSSLVersion;
- fSSLVersions : TIdSSLVersions;
- fMode: TIdSSLMode;
- fsRootCertFile, fsCertFile, fsKeyFile, fsDHParamsFile: String;
- fVerifyDepth: Integer;
- fVerifyMode: TIdSSLVerifyModeSet;
- // fVerifyFile: String;
- fVerifyDirs: String;
- fCipherList: String;
- fContext: PSSL_CTX;
- fStatusInfoOn: Boolean;
- // fPasswordRoutineOn: Boolean;
- fVerifyOn: Boolean;
- fSessionId: Integer;
- fCtxMode: TIdSSLCtxMode;
- procedure DestroyContext;
- function SetSSLMethod: PSSL_METHOD;
- procedure SetVerifyMode(Mode: TIdSSLVerifyModeSet; CheckRoutine: Boolean);
- function GetVerifyMode: TIdSSLVerifyModeSet;
- procedure InitContext(CtxMode: TIdSSLCtxMode);
- public
- {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} Parent: TObject;
- constructor Create;
- destructor Destroy; override;
- function Clone : TIdSSLContext;
- function LoadRootCert: Boolean;
- function LoadCert: Boolean;
- function LoadKey: Boolean;
- function LoadDHParams: Boolean;
- property StatusInfoOn: Boolean read fStatusInfoOn write fStatusInfoOn;
- // property PasswordRoutineOn: Boolean read fPasswordRoutineOn write fPasswordRoutineOn;
- property VerifyOn: Boolean read fVerifyOn write fVerifyOn;
- //THese can't be published in a TObject without a compiler warning.
- // published
- property SSLVersions : TIdSSLVersions read fSSLVersions write fSSLVersions;
- property Method: TIdSSLVersion read fMethod write fMethod;
- property Mode: TIdSSLMode read fMode write fMode;
- property RootCertFile: String read fsRootCertFile write fsRootCertFile;
- property CertFile: String read fsCertFile write fsCertFile;
- property CipherList: String read fCipherList write fCipherList;
- property KeyFile: String read fsKeyFile write fsKeyFile;
- property DHParamsFile: String read fsDHParamsFile write fsDHParamsFile;
- // property VerifyMode: TIdSSLVerifyModeSet read GetVerifyMode write SetVerifyMode;
- // property VerifyFile: String read fVerifyFile write fVerifyFile;
- property VerifyDirs: String read fVerifyDirs write fVerifyDirs;
- property VerifyMode: TIdSSLVerifyModeSet read fVerifyMode write fVerifyMode;
- property VerifyDepth: Integer read fVerifyDepth write fVerifyDepth;
- end;
- TIdSSLSocket = class(TObject)
- protected
- {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} fParent: TObject;
- fPeerCert: TIdX509;
- fSSL: PSSL;
- fSSLCipher: TIdSSLCipher;
- fSSLContext: TIdSSLContext;
- fHostName: String;
- function GetPeerCert: TIdX509;
- function GetSSLError(retCode: Integer): Integer;
- function GetSSLCipher: TIdSSLCipher;
- public
- constructor Create(Parent: TObject);
- destructor Destroy; override;
- procedure Accept(const pHandle: TIdStackSocketHandle);
- procedure Connect(const pHandle: TIdStackSocketHandle);
- function Send(const ABuffer : TIdBytes; AOffset, ALength: Integer): Integer;
- function Recv(var ABuffer : TIdBytes): Integer;
- function GetSessionID: TIdSSLByteArray;
- function GetSessionIDAsString:String;
- procedure SetCipherList(CipherList: String);
- //
- property PeerCert: TIdX509 read GetPeerCert;
- property Cipher: TIdSSLCipher read GetSSLCipher;
- property HostName: String read fHostName;
- end;
- // TIdSSLIOHandlerSocketOpenSSL and TIdServerIOHandlerSSLOpenSSL have some common
- // functions, but they do not have a common ancestor, so this interface helps
- // bridge the gap...
- IIdSSLOpenSSLCallbackHelper = interface(IInterface)
- ['{583F1209-10BA-4E06-8810-155FAEC415FE}']
- function GetPassword(const AIsWrite : Boolean): string;
- procedure StatusInfo(const ASSL: PSSL; AWhere, ARet: TIdC_INT; const AStatusStr: string);
- function VerifyPeer(ACertificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
- function GetIOHandlerSelf: TIdSSLIOHandlerSocketOpenSSL;
- end;
- TIdSSLIOHandlerSocketOpenSSL = class(TIdSSLIOHandlerSocketBase, IIdSSLOpenSSLCallbackHelper)
- protected
- fSSLContext: TIdSSLContext;
- fxSSLOptions: TIdSSLOptions;
- fSSLSocket: TIdSSLSocket;
- //fPeerCert: TIdX509;
- fOnStatusInfo: TCallbackEvent;
- FOnStatusInfoEx : TCallbackExEvent;
- fOnGetPassword: TPasswordEvent;
- fOnGetPasswordEx : TPasswordEventEx;
- fOnVerifyPeer: TVerifyPeerEvent;
- fSSLLayerClosed: Boolean;
- fOnBeforeConnect: TIOHandlerNotify;
- // function GetPeerCert: TIdX509;
- //procedure CreateSSLContext(axMode: TIdSSLMode);
- //
- procedure SetPassThrough(const Value: Boolean); override;
- procedure DoBeforeConnect(ASender: TIdSSLIOHandlerSocketOpenSSL); virtual;
- procedure DoStatusInfo(const AMsg: String); virtual;
- procedure DoStatusInfoEx(const AsslSocket: PSSL;
- const AWhere, Aret: TIdC_INT; const AWhereStr, ARetStr : String );
- procedure DoGetPassword(var Password: String); virtual;
- procedure DoGetPasswordEx(var VPassword: String; const AIsWrite : Boolean); virtual;
- function DoVerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean; virtual;
- function RecvEnc(var VBuffer: TIdBytes): Integer; override;
- function SendEnc(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; override;
- procedure Init;
- procedure OpenEncodedConnection; virtual;
- //some overrides from base classes
- procedure InitComponent; override;
- procedure ConnectClient; override;
- function CheckForError(ALastResult: Integer): Integer; override;
- procedure RaiseError(AError: Integer); override;
- { IIdSSLOpenSSLCallbackHelper }
- function GetPassword(const AIsWrite : Boolean): string;
- procedure StatusInfo(const ASslSocket: PSSL; AWhere, ARet: TIdC_INT; const AStatusStr: string);
- function VerifyPeer(ACertificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
- function GetIOHandlerSelf: TIdSSLIOHandlerSocketOpenSSL;
- public
- destructor Destroy; override;
- // TODO: add an AOwner parameter
- function Clone : TIdSSLIOHandlerSocketBase; override;
- procedure StartSSL; override;
- procedure AfterAccept; override;
- procedure Close; override;
- procedure Open; override;
- function Readable(AMSec: Integer = IdTimeoutDefault): Boolean; override;
- property SSLSocket: TIdSSLSocket read fSSLSocket write fSSLSocket;
- property OnBeforeConnect: TIOHandlerNotify read fOnBeforeConnect write fOnBeforeConnect;
- property SSLContext: TIdSSLContext read fSSLContext write fSSLContext;
- published
- property SSLOptions: TIdSSLOptions read fxSSLOptions write fxSSLOptions;
- property OnStatusInfo: TCallbackEvent read fOnStatusInfo write fOnStatusInfo;
- property OnStatusInfoEx: TCallbackExEvent read fOnStatusInfoEx write fOnStatusInfoEx;
- property OnGetPassword: TPasswordEvent read fOnGetPassword write fOnGetPassword;
- property OnGetPasswordEx : TPasswordEventEx read fOnGetPasswordEx write fOnGetPasswordEx;
- property OnVerifyPeer: TVerifyPeerEvent read fOnVerifyPeer write fOnVerifyPeer;
- end;
- TIdServerIOHandlerSSLOpenSSL = class(TIdServerIOHandlerSSLBase, IIdSSLOpenSSLCallbackHelper)
- protected
- fxSSLOptions: TIdSSLOptions;
- fSSLContext: TIdSSLContext;
- fOnStatusInfo: TCallbackEvent;
- FOnStatusInfoEx : TCallbackExEvent;
- fOnGetPassword: TPasswordEvent;
- fOnGetPasswordEx : TPasswordEventEx;
- fOnVerifyPeer: TVerifyPeerEvent;
- //
- //procedure CreateSSLContext(axMode: TIdSSLMode);
- //procedure CreateSSLContext;
- //
- procedure DoStatusInfo(const AMsg: String); virtual;
- procedure DoStatusInfoEx(const AsslSocket: PSSL;
- const AWhere, Aret: TIdC_INT; const AWhereStr, ARetStr : String );
- procedure DoGetPassword(var Password: String); virtual;
- //TPasswordEventEx
- procedure DoGetPasswordEx(var VPassword: String; const AIsWrite : Boolean); virtual;
- function DoVerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean; virtual;
- procedure InitComponent; override;
- { IIdSSLOpenSSLCallbackHelper }
- function GetPassword(const AIsWrite : Boolean): string;
- procedure StatusInfo(const ASslSocket: PSSL; AWhere, ARet: TIdC_INT; const AStatusStr: string);
- function VerifyPeer(ACertificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
- function GetIOHandlerSelf: TIdSSLIOHandlerSocketOpenSSL;
- public
- procedure Init; override;
- procedure Shutdown; override;
- // AListenerThread is a thread and not a yarn. Its the listener thread.
- function Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread;
- AYarn: TIdYarn): TIdIOHandler; override;
- // function Accept(ASocket: TIdSocketHandle; AThread: TIdThread) : TIdIOHandler; override;
- destructor Destroy; override;
- function MakeClientIOHandler : TIdSSLIOHandlerSocketBase; override;
- //
- function MakeFTPSvrPort : TIdSSLIOHandlerSocketBase; override;
- function MakeFTPSvrPasv : TIdSSLIOHandlerSocketBase; override;
- //
- property SSLContext: TIdSSLContext read fSSLContext;
- published
- property SSLOptions: TIdSSLOptions read fxSSLOptions write fxSSLOptions;
- property OnStatusInfo: TCallbackEvent read fOnStatusInfo write fOnStatusInfo;
- property OnStatusInfoEx: TCallbackExEvent read fOnStatusInfoEx write fOnStatusInfoEx;
- property OnGetPassword: TPasswordEvent read fOnGetPassword write fOnGetPassword;
- property OnGetPasswordEx : TPasswordEventEx read fOnGetPasswordEx write fOnGetPasswordEx;
- property OnVerifyPeer: TVerifyPeerEvent read fOnVerifyPeer write fOnVerifyPeer;
- end;
- TIdX509Name = class(TObject)
- protected
- fX509Name: PX509_NAME;
- function CertInOneLine: String;
- function GetHash: TIdSSLULong;
- function GetHashAsString: String;
- public
- constructor Create(aX509Name: PX509_NAME);
- //
- property Hash: TIdSSLULong read GetHash;
- property HashAsString: string read GetHashAsString;
- property OneLine: string read CertInOneLine;
- //
- property CertificateName: PX509_NAME read fX509Name;
- end;
- TIdX509Info = class(TObject)
- protected
- //Do not free this here because it belongs
- //to the X509 or something else.
- FX509 : PX509;
- public
- constructor Create( aX509: PX509);
- //
- property Certificate: PX509 read FX509;
- end;
- TIdX509Fingerprints = class(TIdX509Info)
- protected
- function GetMD5: TIdSSLEVP_MD;
- function GetMD5AsString:String;
- function GetSHA1: TIdSSLEVP_MD;
- function GetSHA1AsString:String;
- function GetSHA224 : TIdSSLEVP_MD;
- function GetSHA224AsString : String;
- function GetSHA256 : TIdSSLEVP_MD;
- function GetSHA256AsString : String;
- function GetSHA384 : TIdSSLEVP_MD;
- function GetSHA384AsString : String;
- function GetSHA512 : TIdSSLEVP_MD;
- function GetSHA512AsString : String;
- public
- property MD5 : TIdSSLEVP_MD read GetMD5;
- property MD5AsString : String read GetMD5AsString;
- {IMPORTANT!!!
- FIPS approves only these algorithms for hashing.
- SHA-1
- SHA-224
- SHA-256
- SHA-384
- SHA-512
- http://csrc.nist.gov/CryptoToolkit/tkhash.html
- }
- property SHA1 : TIdSSLEVP_MD read GetSHA1;
- property SHA1AsString : String read GetSHA1AsString;
- property SHA224 : TIdSSLEVP_MD read GetSHA224;
- property SHA224AsString : String read GetSHA224AsString;
- property SHA256 : TIdSSLEVP_MD read GetSHA256;
- property SHA256AsString : String read GetSHA256AsString;
- property SHA384 : TIdSSLEVP_MD read GetSHA384;
- property SHA384AsString : String read GetSHA384AsString;
- property SHA512 : TIdSSLEVP_MD read GetSHA512;
- property SHA512AsString : String read GetSHA512AsString;
- end;
- TIdX509SigInfo = class(TIdX509Info)
- protected
- function GetSignature : String;
- function GetSigType : TIdC_INT;
- function GetSigTypeAsString : String;
- public
- property Signature : String read GetSignature;
- property SigType : TIdC_INT read GetSigType ;
- property SigTypeAsString : String read GetSigTypeAsString;
- end;
- TIdX509 = class(TObject)
- protected
- FFingerprints : TIdX509Fingerprints;
- FSigInfo : TIdX509SigInfo;
- FCanFreeX509 : Boolean;
- FX509 : PX509;
- FSubject : TIdX509Name;
- FIssuer : TIdX509Name;
- FDisplayInfo : TStrings;
- function RSubject:TIdX509Name;
- function RIssuer:TIdX509Name;
- function RnotBefore:TDateTime;
- function RnotAfter:TDateTime;
- function RFingerprint:TIdSSLEVP_MD;
- function RFingerprintAsString:String;
- function GetSerialNumber: String;
- function GetVersion : TIdC_LONG;
- function GetDisplayInfo : TStrings;
- public
- Constructor Create(aX509: PX509; aCanFreeX509: Boolean = True); virtual;
- Destructor Destroy; override;
- property Version : TIdC_LONG read GetVersion;
- //
- property SigInfo : TIdX509SigInfo read FSigInfo;
- property Fingerprints : TIdX509Fingerprints read FFingerprints;
- //
- property Fingerprint: TIdSSLEVP_MD read RFingerprint;
- property FingerprintAsString: String read RFingerprintAsString;
- property Subject: TIdX509Name read RSubject;
- property Issuer: TIdX509Name read RIssuer;
- property notBefore: TDateTime read RnotBefore;
- property notAfter: TDateTime read RnotAfter;
- property SerialNumber : string read GetSerialNumber;
- property DisplayInfo : TStrings read GetDisplayInfo;
- //
- property Certificate: PX509 read FX509;
- end;
- TIdSSLCipher = class(TObject)
- protected
- FSSLSocket: TIdSSLSocket;
- function GetDescription: String;
- function GetName: String;
- function GetBits: Integer;
- function GetVersion: String;
- public
- constructor Create(AOwner: TIdSSLSocket);
- destructor Destroy; override;
- //These can't be published without a compiler warning.
- // published
- property Description: String read GetDescription;
- property Name: String read GetName;
- property Bits: Integer read GetBits;
- property Version: String read GetVersion;
- end;
- EIdOSSLCouldNotLoadSSLLibrary = class(EIdOpenSSLError);
- EIdOSSLModeNotSet = class(EIdOpenSSLError);
- EIdOSSLGetMethodError = class(EIdOpenSSLError);
- EIdOSSLCreatingSessionError = class(EIdOpenSSLError);
- EIdOSSLCreatingContextError = class(EIdOpenSSLAPICryptoError);
- EIdOSSLLoadingRootCertError = class(EIdOpenSSLAPICryptoError);
- EIdOSSLLoadingCertError = class(EIdOpenSSLAPICryptoError);
- EIdOSSLLoadingKeyError = class(EIdOpenSSLAPICryptoError);
- EIdOSSLLoadingDHParamsError = class(EIdOpenSSLAPICryptoError);
- EIdOSSLSettingCipherError = class(EIdOpenSSLError);
- EIdOSSLFDSetError = class(EIdOpenSSLAPISSLError);
- EIdOSSLDataBindingError = class(EIdOpenSSLAPISSLError);
- EIdOSSLAcceptError = class(EIdOpenSSLAPISSLError);
- EIdOSSLConnectError = class(EIdOpenSSLAPISSLError);
- {$IFNDEF OPENSSL_NO_TLSEXT}
- EIdOSSLSettingTLSHostNameError = class(EIdOpenSSLAPISSLError);
- {$ENDIF}
- function LoadOpenSSLLibrary: Boolean;
- procedure UnLoadOpenSSLLibrary;
- function OpenSSLVersion: string;
- implementation
- uses
- {$IFDEF HAS_UNIT_Generics_Collections}
- System.Generics.Collections,
- {$ENDIF}
- {$IFDEF USE_VCL_POSIX}
- Posix.SysTime,
- Posix.Time,
- Posix.Unistd,
- {$ENDIF}
- IdFIPS,
- IdResourceStringsCore,
- IdResourceStringsProtocols,
- IdResourceStringsOpenSSL,
- IdStack,
- IdStackBSDBase,
- IdAntiFreezeBase,
- IdExceptionCore,
- IdResourceStrings,
- IdThreadSafe,
- IdCustomTransparentProxy,
- IdURI,
- SysUtils,
- SyncObjs;
- type
- // TODO: TIdThreadSafeObjectList instead?
- {$IFDEF HAS_GENERICS_TThreadList}
- TIdCriticalSectionThreadList = TThreadList<TIdCriticalSection>;
- TIdCriticalSectionList = TList<TIdCriticalSection>;
- {$ELSE}
- // TODO: flesh out to match TThreadList<TIdCriticalSection> and TList<TIdCriticalSection> on non-Generics compilers
- TIdCriticalSectionThreadList = TThreadList;
- TIdCriticalSectionList = TList;
- {$ENDIF}
- // RLebeau 1/24/2019: defining this as a private implementation for now to
- // avoid a change in the public interface above. This should be rolled into
- // the public interface at some point...
- TIdSSLOptions_Internal = class(TIdSSLOptions)
- public
- {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} Parent: TObject;
- end;
- var
- SSLIsLoaded: TIdThreadSafeBoolean = nil;
- LockInfoCB: TIdCriticalSection = nil;
- LockPassCB: TIdCriticalSection = nil;
- LockVerifyCB: TIdCriticalSection = nil;
- CallbackLockList: TIdCriticalSectionThreadList = nil;
- procedure GetStateVars(const sslSocket: PSSL; AWhere, Aret: TIdC_INT; var VTypeStr, VMsg : String);
- {$IFDEF USE_INLINE}inline;{$ENDIF}
- begin
- case AWhere of
- SSL_CB_ALERT :
- begin
- VTypeStr := IndyFormat( RSOSSLAlert,[SSL_alert_type_string_long(Aret)]);
- VMsg := String(SSL_alert_type_string_long(Aret));
- end;
- SSL_CB_READ_ALERT :
- begin
- VTypeStr := IndyFormat(RSOSSLReadAlert,[SSL_alert_type_string_long(Aret)]);
- VMsg := String( SSL_alert_desc_string_long(Aret));
- end;
- SSL_CB_WRITE_ALERT :
- begin
- VTypeStr := IndyFormat(RSOSSLWriteAlert,[SSL_alert_type_string_long(Aret)]);
- VMsg := String( SSL_alert_desc_string_long(Aret));
- end;
- SSL_CB_ACCEPT_LOOP :
- begin
- VTypeStr := RSOSSLAcceptLoop;
- VMsg := String( SSL_state_string_long(sslSocket));
- end;
- SSL_CB_ACCEPT_EXIT :
- begin
- if ARet < 0 then begin
- VTypeStr := RSOSSLAcceptError;
- end else begin
- if ARet = 0 then begin
- VTypeStr := RSOSSLAcceptFailed;
- end else begin
- VTypeStr := RSOSSLAcceptExit;
- end;
- end;
- VMsg := String( SSL_state_string_long(sslSocket) );
- end;
- SSL_CB_CONNECT_LOOP :
- begin
- VTypeStr := RSOSSLConnectLoop;
- VMsg := String( SSL_state_string_long(sslSocket) );
- end;
- SSL_CB_CONNECT_EXIT :
- begin
- if ARet < 0 then begin
- VTypeStr := RSOSSLConnectError;
- end else begin
- if ARet = 0 then begin
- VTypeStr := RSOSSLConnectFailed
- end else begin
- VTypeStr := RSOSSLConnectExit;
- end;
- end;
- VMsg := String( SSL_state_string_long(sslSocket) );
- end;
- SSL_CB_HANDSHAKE_START :
- begin
- VTypeStr := RSOSSLHandshakeStart;
- VMsg := String( SSL_state_string_long(sslSocket) );
- end;
- SSL_CB_HANDSHAKE_DONE :
- begin
- VTypeStr := RSOSSLHandshakeDone;
- VMsg := String( SSL_state_string_long(sslSocket) );
- end;
- end;
- {var LW : TIdC_INT;
- begin
- VMsg := '';
- LW := Awhere and (not SSL_ST_MASK);
- if (LW and SSL_ST_CONNECT) > 0 then begin
- VWhereStr := 'SSL_connect:';
- end else begin
- if (LW and SSL_ST_ACCEPT) > 0 then begin
- VWhereStr := ' SSL_accept:';
- end else begin
- VWhereStr := ' undefined:';
- end;
- end;
- // IdSslStateStringLong
- if (Awhere and SSL_CB_LOOP) > 0 then begin
- VMsg := IdSslStateStringLong(sslSocket);
- end else begin
- if (Awhere and SSL_CB_ALERT) > 0 then begin
- if (Awhere and SSL_CB_READ > 0) then begin
- VWhereStr := VWhereStr + ' read:'+ IdSslAlertTypeStringLong(Aret);
- end else begin
- VWhereStr := VWhereStr + 'write:'+ IdSslAlertTypeStringLong(Aret);
- end;;
- VMsg := IdSslAlertDescStringLong(Aret);
- end else begin
- if (Awhere and SSL_CB_EXIT) > 0 then begin
- if ARet = 0 then begin
- VWhereStr := VWhereStr +'failed';
- VMsg := IdSslStateStringLong(sslSocket);
- end else begin
- if ARet < 0 then begin
- VWhereStr := VWhereStr +'error';
- VMsg := IdSslStateStringLong(sslSocket);
- end;
- end;
- end;
- end;
- end; }
- end;
- function PasswordCallback(buf: PIdAnsiChar; size: TIdC_INT; rwflag: TIdC_INT; userdata: Pointer): TIdC_INT; cdecl;
- {$IFDEF USE_MARSHALLED_PTRS}
- type
- TBytesPtr = ^TBytes;
- {$ENDIF}
- var
- Password: String;
- {$IFDEF STRING_IS_UNICODE}
- LPassword: TIdBytes;
- {$ENDIF}
- IdSSLContext: TIdSSLContext;
- LErr : Integer;
- LHelper: IIdSSLOpenSSLCallbackHelper;
- begin
- //Preserve last eror just in case OpenSSL is using it and we do something that
- //clobers it. CYA.
- LErr := GStack.WSGetLastError;
- try
- LockPassCB.Enter;
- try
- Password := ''; {Do not Localize}
- IdSSLContext := TIdSSLContext(userdata);
- if Supports(IdSSLContext.Parent, IIdSSLOpenSSLCallbackHelper, IInterface(LHelper)) then begin
- Password := LHelper.GetPassword(rwflag > 0);
- LHelper := nil;
- end;
- FillChar(buf^, size, 0);
- {$IFDEF STRING_IS_UNICODE}
- LPassword := IndyTextEncoding_OSDefault.GetBytes(Password);
- if Length(LPassword) > 0 then begin
- {$IFDEF USE_MARSHALLED_PTRS}
- TMarshal.Copy(TBytesPtr(@LPassword)^, 0, TPtrWrapper.Create(buf), IndyMin(Length(LPassword), size));
- {$ELSE}
- Move(LPassword[0], buf^, IndyMin(Length(LPassword), size));
- {$ENDIF}
- end;
- Result := Length(LPassword);
- {$ELSE}
- StrPLCopy(buf, Password, size);
- Result := Length(Password);
- {$ENDIF}
- buf[size-1] := #0; // RLebeau: truncate the password if needed
- finally
- LockPassCB.Leave;
- end;
- finally
- GStack.WSSetLastError(LErr);
- end;
- end;
- procedure InfoCallback(const sslSocket: PSSL; where, ret: TIdC_INT); cdecl;
- var
- IdSSLSocket: TIdSSLSocket;
- StatusStr : String;
- LErr : Integer;
- LHelper: IIdSSLOpenSSLCallbackHelper;
- begin
- {
- You have to save the value of WSGetLastError as some Operating System API
- function calls will reset that value and we can't know what a programmer will
- do in this event. We need the value of WSGetLastError so we can report
- an underlying socket error when the OpenSSL function returns.
- JPM.
- }
- LErr := GStack.WSGetLastError;
- try
- LockInfoCB.Enter;
- try
- IdSSLSocket := TIdSSLSocket(SSL_get_app_data(sslSocket));
- if Supports(IdSSLSocket.fParent, IIdSSLOpenSSLCallbackHelper, IInterface(LHelper)) then begin
- StatusStr := IndyFormat(RSOSSLStatusString, [String(SSL_state_string_long(sslSocket))]);
- LHelper.StatusInfo(sslSocket, where, ret, StatusStr);
- LHelper := nil;
- end;
- finally
- LockInfoCB.Leave;
- end;
- finally
- GStack.WSSetLastError(LErr);
- end;
- end;
- function TranslateInternalVerifyToSSL(Mode: TIdSSLVerifyModeSet): Integer;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := SSL_VERIFY_NONE;
- if sslvrfPeer in Mode then begin
- Result := Result or SSL_VERIFY_PEER;
- end;
- if sslvrfFailIfNoPeerCert in Mode then begin
- Result := Result or SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- end;
- if sslvrfClientOnce in Mode then begin
- Result := Result or SSL_VERIFY_CLIENT_ONCE;
- end;
- end;
- function VerifyCallback(Ok: TIdC_INT; ctx: PX509_STORE_CTX): TIdC_INT; cdecl;
- var
- hcert: PX509;
- Certificate: TIdX509;
- hSSL: PSSL;
- IdSSLSocket: TIdSSLSocket;
- // str: String;
- VerifiedOK: Boolean;
- Depth: Integer;
- Error: Integer;
- LOk: Boolean;
- LHelper: IIdSSLOpenSSLCallbackHelper;
- begin
- LockVerifyCB.Enter;
- try
- VerifiedOK := True;
- try
- hSSL := X509_STORE_CTX_get_app_data(ctx);
- if hSSL = nil then begin
- Result := Ok;
- Exit;
- end;
- hcert := X509_STORE_CTX_get_current_cert(ctx);
- Certificate := TIdX509.Create(hcert, False); // the certificate is owned by the store
- try
- IdSSLSocket := TIdSSLSocket(SSL_get_app_data(hSSL));
- Error := X509_STORE_CTX_get_error(ctx);
- Depth := X509_STORE_CTX_get_error_depth(ctx);
- if not ((Ok > 0) and (IdSSLSocket.fSSLContext.VerifyDepth >= Depth)) then begin
- Ok := 0;
- {if Error = X509_V_OK then begin
- Error := X509_V_ERR_CERT_CHAIN_TOO_LONG;
- end;}
- end;
- LOk := False;
- if Ok = 1 then begin
- LOk := True;
- end;
- if Supports(IdSSLSocket.fParent, IIdSSLOpenSSLCallbackHelper, IInterface(LHelper)) then begin
- VerifiedOK := LHelper.VerifyPeer(Certificate, LOk, Depth, Error);
- LHelper := nil;
- end;
- finally
- FreeAndNil(Certificate);
- end;
- except
- VerifiedOK := False;
- end;
- //if VerifiedOK and (Ok > 0) then begin
- if VerifiedOK {and (Ok > 0)} then begin
- Result := 1;
- end
- else begin
- Result := 0;
- end;
- // Result := Ok; // testing
- finally
- LockVerifyCB.Leave;
- end;
- end;
- //////////////////////////////////////////////////////
- // Utilities
- //////////////////////////////////////////////////////
- function IndySSL_load_client_CA_file(const AFileName: String) : PSTACK_OF_X509_NAME; forward;
- function IndySSL_CTX_use_PrivateKey_file(ctx: PSSL_CTX; const AFileName: String;
- AType: Integer): TIdC_INT; forward;
- function IndySSL_CTX_use_certificate_file(ctx: PSSL_CTX; const AFileName: String;
- AType: Integer): TIdC_INT; forward;
- function IndySSL_CTX_use_certificate_chain_file(ctx :PSSL_CTX;
- const AFileName: String) : TIdC_INT; forward;
- function IndyX509_STORE_load_locations(ctx: PX509_STORE;
- const AFileName, APathName: String): TIdC_INT; forward;
- function IndySSL_CTX_load_verify_locations(ctx: PSSL_CTX;
- const ACAFile, ACAPath: String): TIdC_INT; forward;
- function IndySSL_CTX_use_DHparams_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT; forward;
- // TODO
- {
- function d2i_DHparams_bio(bp: PBIO; x: PPointer): PDH; inline;
- begin
- Result := PDH(ASN1_d2i_bio(@DH_new, @d2i_DHparams, bp, x));
- end;
- }
- // SSL_CTX_use_PrivateKey_file() and SSL_CTX_use_certificate_file() do not
- // natively support PKCS12 certificates/keys, only PEM/ASN1, so load them
- // manually...
- function IndySSL_CTX_use_PrivateKey_file_PKCS12(ctx: PSSL_CTX; const AFileName: String): TIdC_INT;
- var
- LM: TMemoryStream;
- B: PBIO;
- LKey: PEVP_PKEY;
- LCert: PX509;
- P12: PPKCS12;
- CertChain: PSTACK_OF_X509;
- LPassword: array of TIdAnsiChar;
- LPasswordPtr: PIdAnsiChar;
- begin
- Result := 0;
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- B := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(B) then begin
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_BUF_LIB);
- Exit;
- end;
- try
- SetLength(LPassword, MAX_SSL_PASSWORD_LENGTH+1);
- LPassword[MAX_SSL_PASSWORD_LENGTH] := TIdAnsiChar(0);
- LPasswordPtr := PIdAnsiChar(LPassword);
- if Assigned(ctx^.default_passwd_callback) then begin
- ctx^.default_passwd_callback(LPasswordPtr, MAX_SSL_PASSWORD_LENGTH, 0, ctx^.default_passwd_callback_userdata);
- // TODO: check return value for failure
- end else begin
- // TODO: call PEM_def_callback(), like PEM_read_bio_X509() does
- // when default_passwd_callback is nil
- end;
- P12 := d2i_PKCS12_bio(B, nil);
- if not Assigned(P12) then begin
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_PKCS12_LIB);
- Exit;
- end;
- try
- CertChain := nil;
- if PKCS12_parse(P12, LPasswordPtr, LKey, LCert, @CertChain) <> 1 then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PKCS12_LIB);
- Exit;
- end;
- try
- Result := SSL_CTX_use_PrivateKey(ctx, LKey);
- finally
- sk_pop_free(CertChain, @X509_free);
- X509_free(LCert);
- EVP_PKEY_free(LKey);
- end;
- finally
- PKCS12_free(P12);
- end;
- finally
- BIO_free(B);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- function IndySSL_CTX_use_certificate_file_PKCS12(ctx: PSSL_CTX; const AFileName: String): TIdC_INT;
- var
- LM: TMemoryStream;
- B: PBIO;
- LCert: PX509;
- P12: PPKCS12;
- PKey: PEVP_PKEY;
- CertChain: PSTACK_OF_X509;
- LPassword: array of TIdAnsiChar;
- LPasswordPtr: PIdAnsiChar;
- begin
- Result := 0;
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- B := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(B) then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB);
- Exit;
- end;
- try
- SetLength(LPassword, MAX_SSL_PASSWORD_LENGTH+1);
- LPassword[MAX_SSL_PASSWORD_LENGTH] := TIdAnsiChar(0);
- LPasswordPtr := PIdAnsiChar(LPassword);
- if Assigned(ctx^.default_passwd_callback) then begin
- ctx^.default_passwd_callback(LPasswordPtr, MAX_SSL_PASSWORD_LENGTH, 0, ctx^.default_passwd_callback_userdata);
- // TODO: check return value for failure
- end else begin
- // TODO: call PEM_def_callback(), like PEM_read_bio_X509() does
- // when default_passwd_callback is nil
- end;
- P12 := d2i_PKCS12_bio(B, nil);
- if not Assigned(P12) then
- begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PKCS12_LIB);
- Exit;
- end;
- try
- CertChain := nil;
- if PKCS12_parse(P12, LPasswordPtr, PKey, LCert, @CertChain) <> 1 then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PKCS12_LIB);
- Exit;
- end;
- try
- Result := SSL_CTX_use_certificate(ctx, LCert);
- finally
- sk_pop_free(CertChain, @X509_free);
- X509_free(LCert);
- EVP_PKEY_free(PKey);
- end;
- finally
- PKCS12_free(P12);
- end;
- finally
- BIO_free(B);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- {
- IMPORTANT!!!
- OpenSSL can not handle Unicode file names at all. On Posix systems, UTF8 File
- names can be used with OpenSSL. The Windows operating system does not accept
- UTF8 file names at all so we have our own routines that will handle Unicode
- filenames. Most of this section of code is based on code in the OpenSSL .DLL
- which is copyrighted by the OpenSSL developers. Some of it is translated into
- Pascal and made some modifications so that it will handle Unicode filenames.
- }
- {$IFDEF STRING_IS_UNICODE}
- {$IFDEF WINDOWS}
- function Indy_unicode_X509_load_cert_crl_file(ctx: PX509_LOOKUP; const AFileName: String;
- const _type: TIdC_INT): TIdC_INT; forward;
- function Indy_unicode_X509_load_cert_file(ctx: PX509_LOOKUP; const AFileName: String;
- _type: TIdC_INT): TIdC_INT; forward;
- {
- This is for some file lookup definitions for a LOOKUP method that
- uses Unicode filenames instead of ASCII or UTF8. It is not meant
- to be portable at all.
- }
- function by_Indy_unicode_file_ctrl(ctx: PX509_LOOKUP; cmd: TIdC_INT;
- const argc: PAnsiChar; argl: TIdC_LONG; out ret: PAnsiChar): TIdC_INT;
- cdecl; forward;
- const
- Indy_x509_unicode_file_lookup: X509_LOOKUP_METHOD =
- (
- name: PAnsiChar('Load file into cache');
- new_item: nil; // * new */
- free: nil; // * free */
- init: nil; // * init */
- shutdown: nil; // * shutdown */
- ctrl: by_Indy_unicode_file_ctrl; // * ctrl */
- get_by_subject: nil; // * get_by_subject */
- get_by_issuer_serial: nil; // * get_by_issuer_serial */
- get_by_fingerprint: nil; // * get_by_fingerprint */
- get_by_alias: nil // * get_by_alias */
- );
- function Indy_Unicode_X509_LOOKUP_file(): PX509_LOOKUP_METHOD cdecl;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := @Indy_x509_unicode_file_lookup;
- end;
- function by_Indy_unicode_file_ctrl(ctx: PX509_LOOKUP; cmd: TIdC_INT;
- const argc: PAnsiChar; argl: TIdC_LONG; out ret: PAnsiChar): TIdC_INT; cdecl;
- var
- LOk: TIdC_INT;
- LFileName: String;
- begin
- LOk := 0;
- case cmd of
- X509_L_FILE_LOAD:
- begin
- // Note that typecasting an AnsiChar as a WideChar below is normally a crazy
- // thing to do. The thing is that the OpenSSL API is based on PAnsiChar, and
- // we are writing this function just for Unicode filenames. argc is actually
- // a PWideChar that has been coerced into a PAnsiChar so it can pass through
- // OpenSSL APIs...
- case argl of
- X509_FILETYPE_DEFAULT:
- begin
- LFileName := GetEnvironmentVariable(String(X509_get_default_cert_file_env));
- if LFileName = '' then begin
- LFileName := String(X509_get_default_cert_file);
- end;
- LOk := Ord(Indy_unicode_X509_load_cert_crl_file(ctx, LFileName, X509_FILETYPE_PEM) <> 0);
- if LOk = 0 then begin
- X509err(X509_F_BY_FILE_CTRL, X509_R_LOADING_DEFAULTS);
- end;
- end;
- X509_FILETYPE_PEM:
- begin
- LFileName := PWideChar(Pointer(argc));
- LOk := Ord(Indy_unicode_X509_load_cert_crl_file(ctx, LFileName, X509_FILETYPE_PEM) <> 0);
- end;
- else
- LFileName := PWideChar(Pointer(argc));
- LOk := Ord(Indy_unicode_X509_load_cert_file(ctx, LFileName, TIdC_INT(argl)) <> 0);
- end;
- end;
- end;
- Result := LOk;
- end;
- function Indy_unicode_X509_load_cert_file(ctx: PX509_LOOKUP; const AFileName: String;
- _type: TIdC_INT): TIdC_INT;
- var
- LM: TMemoryStream;
- Lin: PBIO;
- LX: PX509;
- i, count: Integer;
- begin
- Result := 0;
- count := 0;
- if AFileName = '' then begin
- Result := 1;
- Exit;
- end;
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- Lin := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(Lin) then begin
- X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_SYS_LIB);
- Exit;
- end;
- try
- case _type of
- X509_FILETYPE_PEM:
- begin
- repeat
- LX := PEM_read_bio_X509_AUX(Lin, nil, nil, nil);
- if not Assigned(LX) then begin
- if ((ERR_GET_REASON(ERR_peek_last_error())
- = PEM_R_NO_START_LINE) and (count > 0)) then begin
- ERR_clear_error();
- Break;
- end else begin
- X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_PEM_LIB);
- Exit;
- end;
- end;
- i := X509_STORE_add_cert(ctx^.store_ctx, LX);
- if i = 0 then begin
- Exit;
- end;
- Inc(count);
- X509_Free(LX);
- until False;
- Result := count;
- end;
- X509_FILETYPE_ASN1:
- begin
- LX := d2i_X509_bio(Lin, nil);
- if not Assigned(LX) then begin
- X509err(X509_F_X509_LOAD_CERT_FILE, ERR_R_ASN1_LIB);
- Exit;
- end;
- i := X509_STORE_add_cert(ctx^.store_ctx, LX);
- if i = 0 then begin
- Exit;
- end;
- Result := i;
- end;
- else
- X509err(X509_F_X509_LOAD_CERT_FILE, X509_R_BAD_X509_FILETYPE);
- Exit;
- end;
- finally
- BIO_free(Lin);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- function Indy_unicode_X509_load_cert_crl_file(ctx: PX509_LOOKUP; const AFileName: String;
- const _type: TIdC_INT): TIdC_INT;
- var
- LM: TMemoryStream;
- Linf: PSTACK_OF_X509_INFO;
- Litmp: PX509_INFO;
- Lin: PBIO;
- i, count: Integer;
- begin
- Result := 0;
- count := 0;
- LM := nil;
- if _type <> X509_FILETYPE_PEM then begin
- Result := Indy_unicode_X509_load_cert_file(ctx, AFileName, _type);
- Exit;
- end;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- Lin := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(Lin) then begin
- X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_SYS_LIB);
- Exit;
- end;
- try
- Linf := PEM_X509_INFO_read_bio(Lin, nil, nil, nil);
- finally
- BIO_free(Lin);
- end;
- finally
- FreeAndNil(LM);
- end;
- if not Assigned(Linf) then begin
- X509err(X509_F_X509_LOAD_CERT_CRL_FILE, ERR_R_PEM_LIB);
- Exit;
- end;
- try
- for i := 0 to sk_X509_INFO_num(Linf) - 1 do begin
- Litmp := sk_X509_INFO_value(Linf, i);
- if Assigned(Litmp^.x509) then begin
- X509_STORE_add_cert(ctx^.store_ctx, Litmp^.x509);
- Inc(count);
- end;
- if Assigned(Litmp^.crl) then begin
- X509_STORE_add_crl(ctx^.store_ctx, Litmp^.crl);
- Inc(count);
- end;
- end;
- finally
- sk_X509_INFO_pop_free(Linf, @X509_INFO_free);
- end;
- Result := count;
- end;
- procedure IndySSL_load_client_CA_file_err(var VRes: PSTACK_OF_X509_NAME);
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- if Assigned(VRes) then begin
- sk_X509_NAME_pop_free(VRes, @X509_NAME_free);
- VRes := nil;
- end;
- end;
- function xname_cmp(const a, b: PPX509_NAME): TIdC_INT; cdecl;
- begin
- Result := X509_NAME_cmp(a^, b^);
- end;
- function IndySSL_load_client_CA_file(const AFileName: String): PSTACK_OF_X509_NAME;
- var
- LM: TMemoryStream;
- LB: PBIO;
- Lsk: PSTACK_OF_X509_NAME;
- LX: PX509;
- LXN, LXNDup: PX509_NAME;
- Failed: Boolean;
- begin
- Result := nil;
- Failed := False;
- LX := nil;
- Lsk := sk_X509_NAME_new(@xname_cmp);
- if Assigned(Lsk) then begin
- try
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL_LOAD_CLIENT_CA_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- LB := BIO_new_mem_buf(LM.Memory, LM.Size);
- if Assigned(LB) then begin
- try
- try
- repeat
- LX := PEM_read_bio_X509(LB, nil, nil, nil);
- if LX = nil then begin
- Break;
- end;
- if not Assigned(Result) then begin
- Result := sk_X509_NAME_new_null;
- if not Assigned(Result) then begin
- SSLerr(SSL_F_SSL_LOAD_CLIENT_CA_FILE, ERR_R_MALLOC_FAILURE);
- Failed := True;
- Exit;
- end;
- end;
- LXN := X509_get_subject_name(LX);
- if not Assigned(LXN) then begin
- // error
- IndySSL_load_client_CA_file_err(Result);
- Failed := True;
- Exit;
- end;
- // * check for duplicates */
- LXNDup := X509_NAME_dup(LXN);
- if not Assigned(LXNDup) then begin
- // error
- IndySSL_load_client_CA_file_err(Result);
- Failed := True;
- Exit;
- end;
- if (sk_X509_NAME_find(Lsk, LXNDup) >= 0) then begin
- X509_NAME_free(LXNDup);
- end else begin
- sk_X509_NAME_push(Lsk, LXNDup);
- sk_X509_NAME_push(Result, LXNDup);
- end;
- X509_free(LX);
- LX := nil;
- until False;
- finally
- if Assigned(LX) then begin
- X509_free(LX);
- end;
- if Failed and Assigned(Result) then begin
- sk_X509_NAME_pop_free(Result, @X509_NAME_free);
- Result := nil;
- end;
- end;
- finally
- BIO_free(LB);
- end;
- end
- else begin
- SSLerr(SSL_F_SSL_LOAD_CLIENT_CA_FILE, ERR_R_MALLOC_FAILURE);
- end;
- finally
- FreeAndNil(LM);
- end;
- finally
- sk_X509_NAME_free(Lsk);
- end;
- end
- else begin
- SSLerr(SSL_F_SSL_LOAD_CLIENT_CA_FILE, ERR_R_MALLOC_FAILURE);
- end;
- if Assigned(Result) then begin
- ERR_clear_error;
- end;
- end;
- function IndySSL_CTX_use_PrivateKey_file(ctx: PSSL_CTX; const AFileName: String;
- AType: Integer): TIdC_INT;
- var
- LM: TMemoryStream;
- B: PBIO;
- LKey: PEVP_PKEY;
- j: TIdC_INT;
- begin
- Result := 0;
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- B := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(B) then begin
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, ERR_R_BUF_LIB);
- Exit;
- end;
- try
- case AType of
- SSL_FILETYPE_PEM:
- begin
- j := ERR_R_PEM_LIB;
- LKey := PEM_read_bio_PrivateKey(B, nil,
- ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- end;
- SSL_FILETYPE_ASN1:
- begin
- j := ERR_R_ASN1_LIB;
- LKey := d2i_PrivateKey_bio(B, nil);
- end;
- else
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, SSL_R_BAD_SSL_FILETYPE);
- Exit;
- end;
- if not Assigned(LKey) then begin
- SSLerr(SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE, j);
- Exit;
- end;
- Result := SSL_CTX_use_PrivateKey(ctx, LKey);
- EVP_PKEY_free(LKey);
- finally
- BIO_free(B);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- function IndySSL_CTX_use_certificate_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT;
- var
- LM: TMemoryStream;
- B: PBIO;
- LX: PX509;
- j: TIdC_INT;
- begin
- Result := 0;
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- B := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(B) then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB);
- Exit;
- end;
- try
- case AType of
- SSL_FILETYPE_ASN1:
- begin
- j := ERR_R_ASN1_LIB;
- LX := d2i_X509_bio(B, nil);
- end;
- SSL_FILETYPE_PEM:
- begin
- j := ERR_R_PEM_LIB;
- LX := PEM_read_bio_X509(B, nil, ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- end
- else begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, SSL_R_BAD_SSL_FILETYPE);
- Exit;
- end;
- end;
- if not Assigned(LX) then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, j);
- Exit;
- end;
- Result := SSL_CTX_use_certificate(ctx, LX);
- X509_free(LX);
- finally
- BIO_free(B);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- function IndySSL_CTX_use_certificate_chain_file(ctx :PSSL_CTX;
- const AFileName: String) : TIdC_INT;
- var
- LM: TMemoryStream;
- B: PBIO;
- LX: PX509;
- ca :PX509;
- r: TIdC_INT;
- LErr :TIdC_ULONG;
- begin
- Result := 0;
- ERR_clear_error(); //* clear error stack for
- //* SSL_CTX_use_certificate() */
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- B := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(B) then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB);
- Exit;
- end;
- try
- LX := PEM_read_bio_X509_AUX(B, nil, ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- if (Lx = nil) then begin
- SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB);
- end else begin
- Result := SSL_CTX_use_certificate(ctx, Lx);
- if (ERR_peek_error() <> 0) then begin
- Result := 0; //* Key/certificate mismatch doesn't imply
- //* ret==0 ... */
- end;
- if Result <> 0 then begin
- SSL_CTX_clear_chain_certs(ctx);
- repeat
- ca := PEM_read_bio_X509(B, nil,
- ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- if ca = nil then begin
- break;
- end;
- r := SSL_CTX_add0_chain_cert(ctx, ca);
- if (r = 0) then begin
- X509_free(ca);
- Result := 0;
- break;
- // goto end;
- end;
- //*
- //* Note that we must not free r if it was successfully added to
- //* the chain (while we must free the main certificate, since its
- //* reference count is increased by SSL_CTX_use_certificate).
- // */
- until False;
- if ca <> nil then begin
- //* When the while loop ends, it's usually just EOF. */
- LErr := ERR_peek_last_error();
- if (ERR_GET_LIB(Lerr) = ERR_LIB_PEM)
- and (ERR_GET_REASON(Lerr) = PEM_R_NO_START_LINE) then begin
- ERR_clear_error();
- end else begin
- Result := 0; //* some real error */
- end;
- end;
- end;
- //err:
- if LX <> nil then begin
- X509_free(LX);
- end;
- end;
- finally
- BIO_free(B);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- function IndyX509_STORE_load_locations(ctx: PX509_STORE;
- const AFileName, APathName: String): TIdC_INT;
- var
- lookup: PX509_LOOKUP;
- begin
- Result := 0;
- if AFileName <> '' then begin
- lookup := X509_STORE_add_lookup(ctx, Indy_Unicode_X509_LOOKUP_file);
- if not Assigned(lookup) then begin
- Exit;
- end;
- // RLebeau: the PAnsiChar(Pointer(...)) cast below looks weird, but it is
- // intentional. X509_LOOKUP_load_file() takes a PAnsiChar as input, but
- // we are using Unicode strings here. So casting the UnicodeString to a
- // raw Pointer and then passing that to X509_LOOKUP_load_file() as PAnsiChar.
- // Indy_Unicode_X509_LOOKUP_file will cast it back to PWideChar for processing...
- if (X509_LOOKUP_load_file(lookup, PAnsiChar(Pointer(AFileName)), X509_FILETYPE_PEM) <> 1) then begin
- Exit;
- end;
- end;
- if APathName <> '' then begin
- { TODO: Figure out how to do the hash dir lookup with a Unicode path. }
- if (X509_STORE_load_locations(ctx, nil, PAnsiChar(AnsiString(APathName))) <> 1) then begin
- Exit;
- end;
- end;
- if (AFileName = '') and (APathName = '') then begin
- Exit;
- end;
- Result := 1;
- end;
- function IndySSL_CTX_load_verify_locations(ctx: PSSL_CTX;
- const ACAFile, ACAPath: String): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := IndyX509_STORE_load_locations(ctx^.cert_store, ACAFile, ACAPath);
- end;
- function IndySSL_CTX_use_DHparams_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT;
- var
- LM: TMemoryStream;
- B: PBIO;
- LDH: PDH;
- j: Integer;
- begin
- Result := 0;
- LM := nil;
- try
- LM := TMemoryStream.Create;
- LM.LoadFromFile(AFileName);
- except
- // Surpress exception here since it's going to be called by the OpenSSL .DLL
- // Follow the OpenSSL .DLL Error conventions.
- SSLerr(SSL_F_SSL3_CTRL, ERR_R_SYS_LIB);
- LM.Free;
- Exit;
- end;
- try
- B := BIO_new_mem_buf(LM.Memory, LM.Size);
- if not Assigned(B) then begin
- SSLerr(SSL_F_SSL3_CTRL, ERR_R_BUF_LIB);
- Exit;
- end;
- try
- case AType of
- // TODO
- {
- SSL_FILETYPE_ASN1:
- begin
- j := ERR_R_ASN1_LIB;
- LDH := d2i_DHparams_bio(B, nil);
- end;
- }
- SSL_FILETYPE_PEM:
- begin
- j := ERR_R_DH_LIB;
- LDH := PEM_read_bio_DHparams(B, nil, ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- end
- else begin
- SSLerr(SSL_F_SSL3_CTRL, SSL_R_BAD_SSL_FILETYPE);
- Exit;
- end;
- end;
- if not Assigned(LDH) then begin
- SSLerr(SSL_F_SSL3_CTRL, j);
- Exit;
- end;
- Result := SSL_CTX_set_tmp_dh(ctx, LDH);
- DH_free(LDH);
- finally
- BIO_free(B);
- end;
- finally
- FreeAndNil(LM);
- end;
- end;
- {$ENDIF} // WINDOWS
- {$IFDEF UNIX}
- function IndySSL_load_client_CA_file(const AFileName: String) : PSTACK_OF_X509_NAME;
- {$IFDEF USE_MARSHALLED_PTRS}
- var
- M: TMarshaller;
- {$ENDIF}
- begin
- Result := SSL_load_client_CA_file(
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsUtf8(AFileName).ToPointer
- {$ELSE}
- PAnsiChar(UTF8String(AFileName))
- {$ENDIF}
- );
- end;
- function IndySSL_CTX_use_PrivateKey_file(ctx: PSSL_CTX; const AFileName: String;
- AType: Integer): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- {$IFDEF USE_MARSHALLED_PTRS}
- var
- M: TMarshaller;
- {$ENDIF}
- begin
- Result := SSL_CTX_use_PrivateKey_file(ctx,
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsUtf8(AFileName).ToPointer
- {$ELSE}
- PAnsiChar(UTF8String(AFileName))
- {$ENDIF}
- , AType);
- end;
- function IndySSL_CTX_use_certificate_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- {$IFDEF USE_MARSHALLED_PTRS}
- var
- M: TMarshaller;
- {$ENDIF}
- begin
- Result := SSL_CTX_use_certificate_file(ctx,
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsUtf8(AFileName).ToPointer
- {$ELSE}
- PAnsiChar(UTF8String(AFileName))
- {$ENDIF}
- , AType);
- end;
- function IndySSL_CTX_use_certificate_chain_file(ctx :PSSL_CTX;
- const AFileName: String) : TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- {$IFDEF USE_MARSHALLED_PTRS}
- var
- M: TMarshaller;
- {$ENDIF}
- begin
- Result := SSL_CTX_use_certificate_chain_file(ctx,
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsUtf8(AFileName).ToPointer
- {$ELSE}
- PAnsiChar(UTF8String(AFileName))
- {$ENDIF});
- end;
- {$IFDEF USE_MARSHALLED_PTRS}
- function AsUtf8OrNil(var M: TMarshaller; const S: String): Pointer;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- if S <> '' then begin
- Result := M.AsUtf8(S).ToPointer;
- end else begin
- Result := nil;
- end;
- end;
- {$ENDIF}
- function IndyX509_STORE_load_locations(ctx: PX509_STORE;
- const AFileName, APathName: String): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- {$IFDEF USE_MARSHALLED_PTRS}
- var
- M: TMarshaller;
- {$ENDIF}
- begin
- // RLebeau 4/18/2010: X509_STORE_load_locations() expects nil pointers
- // for unused values, but casting a string directly to a PAnsiChar
- // always produces a non-nil pointer, which causes X509_STORE_load_locations()
- // to fail. Need to cast the string to an intermediate Pointer so the
- // PAnsiChar cast is applied to the raw data and thus can be nil...
- //
- // RLebeau 8/18/2017: TMarshaller also produces a non-nil TPtrWrapper for
- // an empty string, so need to handle nil specially with marshalled
- // strings as well...
- //
- Result := X509_STORE_load_locations(ctx,
- {$IFDEF USE_MARSHALLED_PTRS}
- AsUtf8OrNil(M, AFileName),
- AsUtf8OrNil(M, APathName)
- {$ELSE}
- PAnsiChar(Pointer(UTF8String(AFileName))),
- PAnsiChar(Pointer(UTF8String(APathName)))
- {$ENDIF}
- );
- end;
- function IndySSL_CTX_load_verify_locations(ctx: PSSL_CTX;
- const ACAFile, ACAPath: String): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- // RLebeau: why are we calling X509_STORE_load_locations() directly
- // instead of just calling SSL_CTX_load_verify_locations() with
- // UTF-8 input?
- //Result := SSL_CTX_load_verify_locations(ctx,
- // {$IFDEF USE_MARSHALLED_PTRS}
- // AsUtf8OrNl(ACAFile),
- // AsUtf8OrNil(ACAPath)
- // {$ELSE}
- // PAnsiChar(Pointer(UTF8String(ACAFile))),
- // PAnsiChar(Pointer(UTF8String(ACAPath)))
- // {$ENDIF}
- //);
- Result := IndyX509_STORE_load_locations(ctx^.cert_store, ACAFile, ACAPath);
- end;
- function IndySSL_CTX_use_DHparams_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT;
- var
- B: PBIO;
- LDH: PDH;
- j: Integer;
- {$IFDEF USE_MARSHALLED_PTRS}
- M: TMarshaller;
- {$ENDIF}
- begin
- Result := 0;
- B := BIO_new_file(
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsUtf8(AFileName).ToPointer
- {$ELSE}
- PAnsiChar(UTF8String(AFileName))
- {$ENDIF}
- , 'r');
- if Assigned(B) then begin
- try
- case AType of
- // TODO
- {
- SSL_FILETYPE_ASN1:
- begin
- j := ERR_R_ASN1_LIB;
- LDH := d2i_DHparams_bio(B, nil);
- end;
- }
- SSL_FILETYPE_PEM:
- begin
- j := ERR_R_DH_LIB;
- LDH := PEM_read_bio_DHparams(B, nil, ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- end
- else begin
- SSLerr(SSL_F_SSL3_CTRL, SSL_R_BAD_SSL_FILETYPE);
- Exit;
- end;
- end;
- if not Assigned(LDH) then begin
- SSLerr(SSL_F_SSL3_CTRL, j);
- Exit;
- end;
- Result := SSL_CTX_set_tmp_dh(ctx, LDH);
- DH_free(LDH);
- finally
- BIO_free(B);
- end;
- end;
- end;
- {$ENDIF} // UNIX
- {$ELSE} // STRING_IS_UNICODE
- function IndySSL_load_client_CA_file(const AFileName: String) : PSTACK_OF_X509_NAME;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := SSL_load_client_CA_file(PAnsiChar(AFileName));
- end;
- function IndySSL_CTX_use_PrivateKey_file(ctx: PSSL_CTX; const AFileName: String;
- AType: Integer): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := SSL_CTX_use_PrivateKey_file(ctx, PAnsiChar(AFileName), AType);
- end;
- function IndySSL_CTX_use_certificate_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := SSL_CTX_use_certificate_file(ctx, PAnsiChar(AFileName), AType);
- end;
- function IndySSL_CTX_use_certificate_chain_file(ctx :PSSL_CTX;
- const AFileName: String) : TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := SSL_CTX_use_certificate_chain_file(ctx, PAnsiChar(AFileName));
- end;
- function IndyX509_STORE_load_locations(ctx: PX509_STORE;
- const AFileName, APathName: String): TIdC_INT;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- // RLebeau 4/18/2010: X509_STORE_load_locations() expects nil pointers
- // for unused values, but casting a string directly to a PAnsiChar
- // always produces a non-nil pointer, which causes X509_STORE_load_locations()
- // to fail. Need to cast the string to an intermediate Pointer so the
- // PAnsiChar cast is applied to the raw data and thus can be nil...
- //
- Result := X509_STORE_load_locations(ctx,
- PAnsiChar(Pointer(AFileName)),
- PAnsiChar(Pointer(APathName)));
- end;
- function IndySSL_CTX_load_verify_locations(ctx: PSSL_CTX;
- const ACAFile, ACAPath: String): TIdC_INT;
- begin
- // RLebeau 4/18/2010: X509_STORE_load_locations() expects nil pointers
- // for unused values, but casting a string directly to a PAnsiChar
- // always produces a non-nil pointer, which causes X509_STORE_load_locations()
- // to fail. Need to cast the string to an intermediate Pointer so the
- // PAnsiChar cast is applied to the raw data and thus can be nil...
- //
- Result := SSL_CTX_load_verify_locations(ctx,
- PAnsiChar(Pointer(ACAFile)),
- PAnsiChar(Pointer(ACAPath)));
- end;
- function IndySSL_CTX_use_DHparams_file(ctx: PSSL_CTX;
- const AFileName: String; AType: Integer): TIdC_INT;
- var
- B: PBIO;
- LDH: PDH;
- j: Integer;
- begin
- Result := 0;
- B := BIO_new_file(PAnsiChar(AFileName), 'r');
- if Assigned(B) then begin
- try
- case AType of
- // TODO
- {
- SSL_FILETYPE_ASN1:
- begin
- j := ERR_R_ASN1_LIB;
- LDH := d2i_DHparams_bio(B, nil);
- end;
- }
- SSL_FILETYPE_PEM:
- begin
- j := ERR_R_DH_LIB;
- LDH := PEM_read_bio_DHparams(B, nil, ctx^.default_passwd_callback,
- ctx^.default_passwd_callback_userdata);
- end
- else begin
- SSLerr(SSL_F_SSL3_CTRL, SSL_R_BAD_SSL_FILETYPE);
- Exit;
- end;
- end;
- if not Assigned(LDH) then begin
- SSLerr(SSL_F_SSL3_CTRL, j);
- Exit;
- end;
- Result := SSL_CTX_set_tmp_dh(ctx, LDH);
- DH_free(LDH);
- finally
- BIO_free(B);
- end;
- end;
- end;
- {$ENDIF}
- function AddMins(const DT: TDateTime; const Mins: Extended): TDateTime;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := DT + Mins / (60 * 24)
- end;
- function AddHrs(const DT: TDateTime; const Hrs: Extended): TDateTime;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := DT + Hrs / 24.0;
- end;
- {$IFDEF OPENSSL_SET_MEMORY_FUNCS}
- function IdMalloc(num: UInt32): Pointer cdecl;
- begin
- Result := AllocMem(num);
- end;
- function IdRealloc(addr: Pointer; num: UInt32): Pointer cdecl;
- begin
- Result := addr;
- ReallocMem(Result, num);
- end;
- procedure IdFree(addr: Pointer)cdecl;
- begin
- FreeMem(addr);
- end;
- procedure IdSslCryptoMallocInit;
- // replaces the actual alloc routines
- // this is useful if you are using a memory manager that can report on leaks
- // at shutdown time.
- var
- r: Integer;
- begin
- r := CRYPTO_set_mem_functions(@IdMalloc, @IdRealloc, @IdFree);
- Assert(r <> 0);
- end;
- {$ENDIF}
- {$IFNDEF OPENSSL_NO_BIO}
- procedure DumpCert(AOut: TStrings; AX509: PX509);
- var
- LMem: PBIO;
- LLen : TIdC_INT;
- LBufPtr : PIdAnsiChar;
- begin
- if Assigned(X509_print) then begin
- LMem := BIO_new(BIO_s_mem);
- if LMem <> nil then begin
- try
- X509_print(LMem, AX509);
- LLen := BIO_get_mem_data(LMem, LBufPtr);
- if (LLen > 0) and (LBufPtr <> nil) then begin
- AOut.Text := IndyTextEncoding_UTF8.GetString(
- {$IFNDEF VCL_6_OR_ABOVE}
- // RLebeau: for some reason, Delphi 5 causes a "There is no overloaded
- // version of 'GetString' that can be called with these arguments" compiler
- // error if the PByte type-cast is used, even though GetString() actually
- // expects a PByte as input. Must be a compiler bug, as it compiles fine
- // in Delphi 6. So, converting to TIdBytes until I find a better solution...
- RawToBytes(LBufPtr^, LLen)
- {$ELSE}
- PByte(LBufPtr), LLen
- {$ENDIF}
- );
- end;
- finally
- BIO_free(LMem);
- end;
- end;
- end;
- end;
- {$ELSE}
- procedure DumpCert(AOut: TStrings; AX509: PX509);
- begin
- end;
- {$ENDIF}
- {$IFNDEF WIN32_OR_WIN64}
- procedure _threadid_func(id : PCRYPTO_THREADID) cdecl;
- begin
- if Assigned(CRYPTO_THREADID_set_numeric) then begin
- CRYPTO_THREADID_set_numeric(id, TIdC_ULONG(CurrentThreadId));
- end;
- end;
- function _GetThreadID: TIdC_ULONG; cdecl;
- begin
- // TODO: Verify how well this will work with fibers potentially running from
- // thread to thread or many on the same thread.
- Result := TIdC_ULONG(CurrentThreadId);
- end;
- {$ENDIF}
- procedure SslLockingCallback(mode, n: TIdC_INT; Afile: PIdAnsiChar;
- line: TIdC_INT)cdecl;
- var
- Lock: TIdCriticalSection;
- LList: TIdCriticalSectionList;
- begin
- Assert(CallbackLockList <> nil);
- Lock := nil;
- LList := CallbackLockList.LockList;
- try
- if n < LList.Count then begin
- Lock := {$IFDEF HAS_GENERICS_TList}LList.Items[n]{$ELSE}TIdCriticalSection(LList.Items[n]){$ENDIF};
- end;
- finally
- CallbackLockList.UnlockList;
- end;
- Assert(Lock <> nil);
- if (mode and CRYPTO_LOCK) = CRYPTO_LOCK then begin
- Lock.Acquire;
- end else begin
- Lock.Release;
- end;
- end;
- procedure PrepareOpenSSLLocking;
- var
- i, cnt: Integer;
- Lock: TIdCriticalSection;
- LList: TIdCriticalSectionList;
- begin
- LList := CallbackLockList.LockList;
- try
- cnt := _CRYPTO_num_locks;
- for i := 0 to cnt - 1 do begin
- Lock := TIdCriticalSection.Create;
- try
- LList.Add(Lock);
- except
- Lock.Free;
- raise;
- end;
- end;
- finally
- CallbackLockList.UnlockList;
- end;
- end;
- // Note that I define UCTTime as PASN1_STRING
- function UTCTime2DateTime(UCTTime: PASN1_UTCTIME): TDateTime;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- var
- year: Word;
- month: Word;
- day: Word;
- hour: Word;
- min: Word;
- sec: Word;
- tz_h: Integer;
- tz_m: Integer;
- begin
- Result := 0;
- if UTC_Time_Decode(UCTTime, year, month, day, hour, min, sec, tz_h, tz_m) > 0 then begin
- Result := EncodeDate(year, month, day) + EncodeTime(hour, min, sec, 0);
- AddMins(Result, tz_m);
- AddHrs(Result, tz_h);
- Result := UTCTimeToLocalTime(Result);
- end;
- end;
- {
- function RSACallback(sslSocket: PSSL; e: Integer; KeyLength: Integer):PRSA; cdecl;
- const
- RSA: PRSA = nil;
- var
- SSLSocket: TSSLWSocket;
- IdSSLSocket: TIdSSLSocket;
- begin
- IdSSLSocket := TIdSSLSocket(IdSslGetAppData(sslSocket));
- if Assigned(IdSSLSocket) then begin
- IdSSLSocket.TriggerSSLRSACallback(KeyLength);
- end;
- Result := RSA_generate_key(KeyLength, RSA_F4, @RSAProgressCallback, ssl);
- end;
- }
- function LogicalAnd(A, B: Integer): Boolean;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := (A and B) = B;
- end;
- function BytesToHexString(APtr: Pointer; ALen: Integer): String;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- var
- i: Integer;
- LPtr: PByte;
- begin
- Result := '';
- LPtr := PByte(APtr);
- for i := 0 to (ALen - 1) do begin
- if i <> 0 then begin
- Result := Result + ':'; { Do not Localize }
- end;
- Result := Result + IndyFormat('%.2x', [LPtr^]);
- Inc(LPtr);
- end;
- end;
- function MDAsString(const AMD: TIdSSLEVP_MD): String;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- var
- i: Integer;
- begin
- Result := '';
- for i := 0 to AMD.Length - 1 do begin
- if i <> 0 then begin
- Result := Result + ':'; { Do not Localize }
- end;
- Result := Result + IndyFormat('%.2x', [Byte(AMD.MD[i])]);
- { do not localize }
- end;
- end;
- function LoadOpenSSLLibrary: Boolean;
- begin
- Assert(SSLIsLoaded <> nil);
- SSLIsLoaded.Lock;
- try
- if SSLIsLoaded.Value then begin
- Result := True;
- Exit;
- end;
- Result := IdSSLOpenSSLHeaders.Load;
- if not Result then begin
- Exit;
- end;
- {$IFDEF OPENSSL_SET_MEMORY_FUNCS}
- // has to be done before anything that uses memory
- IdSslCryptoMallocInit;
- {$ENDIF}
- // required eg to encrypt a private key when writing
- OpenSSL_add_all_ciphers;
- OpenSSL_add_all_digests;
- InitializeRandom;
- // IdSslRandScreen;
- SSL_load_error_strings;
- // Successful loading if true
- Result := SSLeay_add_ssl_algorithms > 0;
- if not Result then begin
- Exit;
- end;
- // Create locking structures, we need them for callback routines
- Assert(LockInfoCB = nil);
- LockInfoCB := TIdCriticalSection.Create;
- LockPassCB := TIdCriticalSection.Create;
- LockVerifyCB := TIdCriticalSection.Create;
- // Handle internal OpenSSL locking
- CallbackLockList := TIdCriticalSectionThreadList.Create;
- PrepareOpenSSLLocking;
- CRYPTO_set_locking_callback(@SslLockingCallback);
- {$IFNDEF WIN32_OR_WIN64}
- if Assigned(CRYPTO_THREADID_set_callback) then begin
- CRYPTO_THREADID_set_callback(@_threadid_func);
- end else begin
- CRYPTO_set_id_callback(@_GetThreadID);
- end;
- {$ENDIF}
- SSLIsLoaded.Value := True;
- Result := True;
- finally
- SSLIsLoaded.Unlock;
- end;
- end;
- procedure UnLoadOpenSSLLibrary;
- // allow the user to call unload directly?
- // will then need to implement reference count
- {$IFNDEF USE_OBJECT_ARC}
- var
- i: Integer;
- LList: TIdCriticalSectionList;
- {$ENDIF}
- begin
- // ssl was never loaded
- if Assigned(CRYPTO_set_locking_callback) then begin
- CRYPTO_set_locking_callback(nil);
- end;
- CleanupRandom; // <-- RLebeau: why is this here and not in IdSSLOpenSSLHeaders.Unload()?
- IdSSLOpenSSLHeaders.Unload;
- FreeAndNil(LockInfoCB);
- FreeAndNil(LockPassCB);
- FreeAndNil(LockVerifyCB);
- if Assigned(CallbackLockList) then begin
- {$IFDEF USE_OBJECT_ARC}
- CallbackLockList.Clear; // Items are auto-freed
- {$ELSE}
- LList := CallbackLockList.LockList;
- begin
- try
- for i := 0 to LList.Count - 1 do begin
- {$IFDEF HAS_GENERICS_TList}LList.Items[i]{$ELSE}TIdCriticalSection(LList.Items[i]){$ENDIF}.Free;
- end;
- LList.Clear;
- finally
- CallbackLockList.UnlockList;
- end;
- end;
- {$ENDIF}
- FreeAndNil(CallbackLockList);
- end;
- SSLIsLoaded.Value := False;
- end;
- function OpenSSLVersion: string;
- begin
- Result := '';
- // RLebeau 9/7/2015: even if LoadOpenSSLLibrary() fails, _SSLeay_version()
- // might have been loaded OK before the failure occured. LoadOpenSSLLibrary()
- // does not unload ..
- IdSSLOpenSSL.LoadOpenSSLLibrary;
- if Assigned(_SSLeay_version) then begin
- Result := String(_SSLeay_version(SSLEAY_VERSION));
- end;
- end;
- //////////////////////////////////////////////////////
- // TIdSSLOptions
- ///////////////////////////////////////////////////////
- constructor TIdSSLOptions.Create;
- begin
- inherited Create;
- fMethod := DEF_SSLVERSION;
- fSSLVersions := DEF_SSLVERSIONS;
- end;
- procedure TIdSSLOptions.SetMethod(const AValue: TIdSSLVersion);
- begin
- fMethod := AValue;
- if AValue = sslvSSLv23 then begin
- fSSLVersions := [sslvSSLv2,sslvSSLv3,sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2];
- end else begin
- fSSLVersions := [AValue];
- end;
- end;
- procedure TIdSSLOptions.SetSSLVersions(const AValue: TIdSSLVersions);
- begin
- fSSLVersions := AValue;
- if fSSLVersions = [sslvSSLv2] then begin
- fMethod := sslvSSLv2;
- end
- else if fSSLVersions = [sslvSSLv3] then begin
- fMethod := sslvSSLv3;
- end
- else if fSSLVersions = [sslvTLSv1] then begin
- fMethod := sslvTLSv1;
- end
- else if fSSLVersions = [sslvTLSv1_1 ] then begin
- fMethod := sslvTLSv1_1;
- end
- else if fSSLVersions = [sslvTLSv1_2 ] then begin
- fMethod := sslvTLSv1_2;
- end
- else begin
- fMethod := sslvSSLv23;
- if sslvSSLv23 in fSSLVersions then begin
- Exclude(fSSLVersions, sslvSSLv23);
- if fSSLVersions = [] then begin
- fSSLVersions := [sslvSSLv2,sslvSSLv3,sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2];
- end;
- end;
- end;
- end;
- procedure TIdSSLOptions.AssignTo(Destination: TPersistent);
- var
- LDest: TIdSSLOptions;
- begin
- if Destination is TIdSSLOptions then begin
- LDest := TIdSSLOptions(Destination);
- LDest.RootCertFile := RootCertFile;
- LDest.CertFile := CertFile;
- LDest.KeyFile := KeyFile;
- LDest.DHParamsFile := DHParamsFile;
- LDest.Method := Method;
- LDest.SSLVersions := SSLVersions;
- LDest.Mode := Mode;
- LDest.VerifyMode := VerifyMode;
- LDest.VerifyDepth := VerifyDepth;
- LDest.VerifyDirs := VerifyDirs;
- LDest.CipherList := CipherList;
- end else begin
- inherited AssignTo(Destination);
- end;
- end;
- ///////////////////////////////////////////////////////
- // TIdServerIOHandlerSSLOpenSSL
- ///////////////////////////////////////////////////////
- { TIdServerIOHandlerSSLOpenSSL }
- procedure TIdServerIOHandlerSSLOpenSSL.InitComponent;
- begin
- inherited InitComponent;
- fxSSLOptions := TIdSSLOptions_Internal.Create;
- TIdSSLOptions_Internal(fxSSLOptions).Parent := Self;
- end;
- destructor TIdServerIOHandlerSSLOpenSSL.Destroy;
- begin
- FreeAndNil(fxSSLOptions);
- inherited Destroy;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.Init;
- //see also TIdSSLIOHandlerSocketOpenSSL.Init
- begin
- //ensure Init isn't called twice
- Assert(fSSLContext = nil);
- fSSLContext := TIdSSLContext.Create;
- fSSLContext.Parent := Self;
- fSSLContext.RootCertFile := SSLOptions.RootCertFile;
- fSSLContext.CertFile := SSLOptions.CertFile;
- fSSLContext.KeyFile := SSLOptions.KeyFile;
- fSSLContext.DHParamsFile := SSLOptions.DHParamsFile;
- fSSLContext.fVerifyDepth := SSLOptions.fVerifyDepth;
- fSSLContext.fVerifyMode := SSLOptions.fVerifyMode;
- // fSSLContext.fVerifyFile := SSLOptions.fVerifyFile;
- fSSLContext.fVerifyDirs := SSLOptions.fVerifyDirs;
- fSSLContext.fCipherList := SSLOptions.fCipherList;
- fSSLContext.VerifyOn := Assigned(fOnVerifyPeer);
- fSSLContext.StatusInfoOn := Assigned(fOnStatusInfo) or Assigned(FOnStatusInfoEx);
- //fSSLContext.PasswordRoutineOn := Assigned(fOnGetPassword);
- fSSLContext.fMethod := SSLOptions.Method;
- fSSLContext.fMode := SSLOptions.Mode;
- fSSLContext.fSSLVersions := SSLOptions.SSLVersions;
- fSSLContext.InitContext(sslCtxServer);
- end;
- function TIdServerIOHandlerSSLOpenSSL.Accept(ASocket: TIdSocketHandle;
- // This is a thread and not a yarn. Its the listener thread.
- AListenerThread: TIdThread; AYarn: TIdYarn ): TIdIOHandler;
- var
- LIO: TIdSSLIOHandlerSocketOpenSSL;
- begin
- //using a custom scheduler, AYarn may be nil, so don't assert
- Assert(ASocket<>nil);
- Assert(fSSLContext<>nil);
- Assert(AListenerThread<>nil);
- Result := nil;
- LIO := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
- try
- LIO.PassThrough := True;
- LIO.Open;
- while not AListenerThread.Stopped do begin
- if ASocket.Select(250) then begin
- if (not AListenerThread.Stopped) and LIO.Binding.Accept(ASocket.Handle) then begin
- //we need to pass the SSLOptions for the socket from the server
- // TODO: wouldn't it be easier to just Assign() the server's SSLOptions
- // here? Do we really need to share ownership of it?
- // LIO.fxSSLOptions.Assign(fxSSLOptions);
- FreeAndNil(LIO.fxSSLOptions);
- LIO.IsPeer := True;
- LIO.fxSSLOptions := fxSSLOptions;
- LIO.fSSLSocket := TIdSSLSocket.Create(Self);
- LIO.fSSLContext := fSSLContext;
- // TODO: to enable server-side SNI, we need to:
- // - Set up an additional SSL_CTX for each different certificate;
- // - Add a servername callback to each SSL_CTX using SSL_CTX_set_tlsext_servername_callback();
- // - In the callback, retrieve the client-supplied servername with
- // SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name). Figure out the right
- // SSL_CTX to go with that host name, then switch the SSL object to that
- // SSL_CTX with SSL_set_SSL_CTX().
- // RLebeau 2/1/2022: note, the following call is basically a no-op for OpenSSL,
- // because PassThrough=True and fSSLContext are both assigned above, so there
- // is really nothing for TIdSSLIOHandlerSocketOpenSSL.Init() or
- // TIdSSLIOHandlerSocketOpenSSL.StartSSL() to do when called by
- // TIdSSLIOHandlerSocketOpenSSL.AfterAccept(). If anything, all this will
- // really do is update the Binding's IPVersion. But, calling this is consistent
- // with other server Accept() implementations, so we should do it here, too...
- LIO.AfterAccept;
- Result := LIO;
- LIO := nil;
- Break;
- end;
- end;
- end;
- finally
- FreeAndNil(LIO);
- end;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.DoStatusInfo(const AMsg: String);
- begin
- if Assigned(fOnStatusInfo) then begin
- fOnStatusInfo(AMsg);
- end;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.DoStatusInfoEx(const AsslSocket: PSSL;
- const AWhere, Aret: TIdC_INT; const AWhereStr, ARetStr: String);
- begin
- if Assigned(FOnStatusInfoEx) then begin
- FOnStatusInfoEx(Self,AsslSocket,AWhere,Aret,AWHereStr,ARetStr);
- end;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.DoGetPassword(var Password: String);
- begin
- if Assigned(fOnGetPassword) then begin
- fOnGetPassword(Password);
- end;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.DoGetPasswordEx(
- var VPassword: String; const AIsWrite: Boolean);
- begin
- if Assigned(fOnGetPasswordEx) then begin
- fOnGetPasswordEx(Self,VPassword,AIsWrite);
- end;
- end;
- function TIdServerIOHandlerSSLOpenSSL.DoVerifyPeer(Certificate: TIdX509;
- AOk: Boolean; ADepth, AError: Integer): Boolean;
- begin
- Result := True;
- if Assigned(fOnVerifyPeer) then begin
- Result := fOnVerifyPeer(Certificate, AOk, ADepth, AError);
- end;
- end;
- function TIdServerIOHandlerSSLOpenSSL.MakeFTPSvrPort : TIdSSLIOHandlerSocketBase;
- var
- LIO : TIdSSLIOHandlerSocketOpenSSL;
- begin
- LIO := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
- try
- LIO.PassThrough := True;
- LIO.OnGetPassword := DoGetPassword;
- LIO.OnGetPasswordEx := OnGetPasswordEx;
- LIO.IsPeer := True; // RLebeau 1/24/2019: is this still needed now?
- LIO.SSLOptions.Assign(SSLOptions);
- LIO.SSLOptions.Mode := sslmBoth;{or sslmClient}{doesn't really matter}
- LIO.SSLContext := SSLContext;
- except
- LIO.Free;
- raise;
- end;
- Result := LIO;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.Shutdown;
- begin
- FreeAndNil(fSSLContext);
- inherited Shutdown;
- end;
- function TIdServerIOHandlerSSLOpenSSL.MakeFTPSvrPasv : TIdSSLIOHandlerSocketBase;
- var
- LIO : TIdSSLIOHandlerSocketOpenSSL;
- begin
- LIO := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
- try
- LIO.PassThrough := True;
- LIO.OnGetPassword := DoGetPassword;
- LIO.OnGetPasswordEx := OnGetPasswordEx;
- LIO.IsPeer := True;
- LIO.SSLOptions.Assign(SSLOptions);
- LIO.SSLOptions.Mode := sslmBoth;{or sslmServer}
- LIO.SSLContext := nil;
- except
- LIO.Free;
- raise;
- end;
- Result := LIO;
- end;
- { IIdSSLOpenSSLCallbackHelper }
- function TIdServerIOHandlerSSLOpenSSL.GetPassword(const AIsWrite : Boolean): string;
- begin
- DoGetPasswordEx(Result, AIsWrite);
- if Result = '' then begin
- DoGetPassword(Result);
- end;
- end;
- procedure TIdServerIOHandlerSSLOpenSSL.StatusInfo(const ASslSocket: PSSL;
- AWhere, ARet: TIdC_INT; const AStatusStr: string);
- var
- LType, LMsg: string;
- begin
- DoStatusInfo(AStatusStr);
- if Assigned(fOnStatusInfoEx) then begin
- GetStateVars(ASslSocket, AWhere, ARet, LType, LMsg);
- DoStatusInfoEx(ASslSocket, AWhere, ARet, LType, LMsg);
- end;
- end;
- function TIdServerIOHandlerSSLOpenSSL.VerifyPeer(ACertificate: TIdX509;
- AOk: Boolean; ADepth, AError: Integer): Boolean;
- begin
- Result := DoVerifyPeer(ACertificate, AOk, ADepth, AError);
- end;
- function TIdServerIOHandlerSSLOpenSSL.GetIOHandlerSelf: TIdSSLIOHandlerSocketOpenSSL;
- begin
- Result := nil;
- end;
- ///////////////////////////////////////////////////////
- // TIdSSLIOHandlerSocketOpenSSL
- ///////////////////////////////////////////////////////
- function TIdServerIOHandlerSSLOpenSSL.MakeClientIOHandler: TIdSSLIOHandlerSocketBase;
- var
- LIO : TIdSSLIOHandlerSocketOpenSSL;
- begin
- LIO := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
- try
- LIO.PassThrough := True;
- // LIO.SSLOptions.Free;
- // LIO.SSLOptions := SSLOptions;
- // LIO.SSLContext := SSLContext;
- LIO.SSLOptions.Assign(SSLOptions);
- // LIO.SSLContext := SSLContext;
- LIO.SSLContext := nil;//SSLContext.Clone; // BGO: clone does not work, it must be either NIL, or SSLContext
- LIO.OnGetPassword := DoGetPassword;
- LIO.OnGetPasswordEx := OnGetPasswordEx;
- except
- LIO.Free;
- raise;
- end;
- Result := LIO;
- end;
- { TIdSSLIOHandlerSocketOpenSSL }
- procedure TIdSSLIOHandlerSocketOpenSSL.InitComponent;
- begin
- inherited InitComponent;
- IsPeer := False;
- fxSSLOptions := TIdSSLOptions_Internal.Create;
- TIdSSLOptions_Internal(fxSSLOptions).Parent := Self;
- fSSLLayerClosed := True;
- fSSLContext := nil;
- end;
- destructor TIdSSLIOHandlerSocketOpenSSL.Destroy;
- begin
- FreeAndNil(fSSLSocket);
- //we do not destroy these if their Parent is not Self
- //because these do not belong to us when we are in a server.
- if (fSSLContext <> nil) and (fSSLContext.Parent = Self) then begin
- FreeAndNil(fSSLContext);
- end;
- if (fxSSLOptions <> nil) and
- (fxSSLOptions is TIdSSLOptions_Internal) and
- (TIdSSLOptions_Internal(fxSSLOptions).Parent = Self) then
- begin
- FreeAndNil(fxSSLOptions);
- end;
- inherited Destroy;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.ConnectClient;
- var
- LPassThrough: Boolean;
- begin
- // RLebeau: initialize OpenSSL before connecting the socket...
- try
- Init;
- except
- on EIdOSSLCouldNotLoadSSLLibrary do begin
- if not PassThrough then raise;
- end;
- end;
- // RLebeau 1/11/07: In case a proxy is being used, pass through
- // any data from the base class unencrypted when setting up that
- // connection. We should do this anyway since SSL hasn't been
- // negotiated yet!
- LPassThrough := fPassThrough;
- fPassThrough := True;
- try
- inherited ConnectClient;
- finally
- fPassThrough := LPassThrough;
- end;
- DoBeforeConnect(Self);
- // CreateSSLContext(sslmClient);
- // CreateSSLContext(SSLOptions.fMode);
- StartSSL;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.StartSSL;
- begin
- if not PassThrough then begin
- OpenEncodedConnection;
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.Close;
- begin
- FreeAndNil(fSSLSocket);
- if fSSLContext <> nil then begin
- if fSSLContext.Parent = Self then begin
- FreeAndNil(fSSLContext);
- end else begin
- fSSLContext := nil;
- end;
- end;
- inherited Close;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.Open;
- begin
- FOpened := False;
- inherited Open;
- end;
- function TIdSSLIOHandlerSocketOpenSSL.Readable(AMSec: Integer = IdTimeoutDefault): Boolean;
- begin
- if not fPassThrough then
- begin
- Result := (fSSLSocket <> nil) and (ssl_pending(fSSLSocket.fSSL) > 0);
- if Result then Exit;
- end;
- Result := inherited Readable(AMSec);
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.SetPassThrough(const Value: Boolean);
- begin
- if fPassThrough <> Value then begin
- if not Value then begin
- if BindingAllocated then begin
- if Assigned(fSSLContext) then begin
- OpenEncodedConnection;
- end else begin
- raise EIdOSSLCouldNotLoadSSLLibrary.Create(RSOSSLCouldNotLoadSSLLibrary);
- end;
- end;
- end
- else begin
- // RLebeau 8/16/2019: need to call SSL_shutdown() here if the SSL/TLS session is active.
- // This is for FTP when handling CCC and REIN commands. The SSL/TLS session needs to be
- // shutdown cleanly on both ends without closing the underlying socket connection because
- // it is going to be used for continued unsecure communications!
- if (fSSLSocket <> nil) and (fSSLSocket.fSSL <> nil) then begin
- // if SSL_shutdown() returns 0, a "close notify" was sent to the peer and SSL_shutdown()
- // needs to be called again to receive the peer's "close notify" in response...
- if SSL_shutdown(fSSLSocket.fSSL) = 0 then begin
- SSL_shutdown(fSSLSocket.fSSL);
- end;
- end;
- {$IFDEF WIN32_OR_WIN64}
- // begin bug fix
- if BindingAllocated and IndyCheckWindowsVersion(6) then
- begin
- // disables Vista+ SSL_Read and SSL_Write timeout fix
- Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_RCVTIMEO, 0);
- Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, 0);
- end;
- // end bug fix
- {$ENDIF}
- end;
- fPassThrough := Value;
- end;
- end;
- function TIdSSLIOHandlerSocketOpenSSL.RecvEnc(var VBuffer: TIdBytes): Integer;
- begin
- Result := fSSLSocket.Recv(VBuffer);
- end;
- function TIdSSLIOHandlerSocketOpenSSL.SendEnc(const ABuffer: TIdBytes;
- const AOffset, ALength: Integer): Integer;
- begin
- Result := fSSLSocket.Send(ABuffer, AOffset, ALength);
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.AfterAccept;
- begin
- try
- inherited AfterAccept;
- // RLebeau: initialize OpenSSL after accepting a client socket...
- try
- Init;
- except
- on EIdOSSLCouldNotLoadSSLLibrary do begin
- if not PassThrough then raise;
- end;
- end;
- StartSSL;
- except
- Close;
- raise;
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.Init;
- //see also TIdServerIOHandlerSSLOpenSSL.Init
- begin
- if not Assigned(fSSLContext) then begin
- fSSLContext := TIdSSLContext.Create;
- fSSLContext.Parent := Self;
- fSSLContext.RootCertFile := SSLOptions.RootCertFile;
- fSSLContext.CertFile := SSLOptions.CertFile;
- fSSLContext.KeyFile := SSLOptions.KeyFile;
- fSSLContext.DHParamsFile := SSLOptions.DHParamsFile;
- fSSLContext.fVerifyDepth := SSLOptions.fVerifyDepth;
- fSSLContext.fVerifyMode := SSLOptions.fVerifyMode;
- // fSSLContext.fVerifyFile := SSLOptions.fVerifyFile;
- fSSLContext.fVerifyDirs := SSLOptions.fVerifyDirs;
- fSSLContext.fCipherList := SSLOptions.fCipherList;
- fSSLContext.VerifyOn := Assigned(fOnVerifyPeer);
- fSSLContext.StatusInfoOn := Assigned(fOnStatusInfo) or Assigned(fOnStatusInfoEx);
- //fSSLContext.PasswordRoutineOn := Assigned(fOnGetPassword);
- fSSLContext.fMethod := SSLOptions.Method;
- fSSLContext.fSSLVersions := SSLOptions.SSLVersions;
- fSSLContext.fMode := SSLOptions.Mode;
- fSSLContext.InitContext(sslCtxClient);
- end;
- end;
- //}
- procedure TIdSSLIOHandlerSocketOpenSSL.DoStatusInfo(const AMsg: String);
- begin
- if Assigned(fOnStatusInfo) then begin
- fOnStatusInfo(AMsg);
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.DoStatusInfoEx(
- const AsslSocket: PSSL; const AWhere, Aret: TIdC_INT; const AWhereStr,
- ARetStr: String);
- begin
- if Assigned(FOnStatusInfoEx) then begin
- FOnStatusInfoEx(Self,AsslSocket,AWhere,Aret,AWHereStr,ARetStr);
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.DoGetPassword(var Password: String);
- begin
- if Assigned(fOnGetPassword) then begin
- fOnGetPassword(Password);
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.DoGetPasswordEx(var VPassword: String;
- const AIsWrite: Boolean);
- begin
- if Assigned(fOnGetPasswordEx) then begin
- fOnGetPasswordEx(Self,VPassword,AIsWrite);
- end;
- end;
- function TIdSSLIOHandlerSocketOpenSSL.DoVerifyPeer(Certificate: TIdX509;
- AOk: Boolean; ADepth, AError: Integer): Boolean;
- begin
- Result := True;
- if Assigned(fOnVerifyPeer) then begin
- Result := fOnVerifyPeer(Certificate, AOk, ADepth, AError);
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.OpenEncodedConnection;
- var
- {$IFDEF WIN32_OR_WIN64}
- LTimeout: Integer;
- {$ENDIF}
- LMode: TIdSSLMode;
- LHost: string;
- // TODO: move the following to TIdSSLIOHandlerSocketBase...
- function GetURIHost: string;
- var
- LURI: TIdURI;
- begin
- Result := '';
- if URIToCheck <> '' then
- begin
- LURI := TIdURI.Create(URIToCheck);
- try
- Result := LURI.Host;
- finally
- LURI.Free;
- end;
- end;
- end;
- function GetProxyTargetHost: string;
- var
- // under ARC, convert a weak reference to a strong reference before working with it
- LTransparentProxy, LNextTransparentProxy: TIdCustomTransparentProxy;
- begin
- Result := '';
- // RLebeau: not reading from the property as it will create a
- // default Proxy object if one is not already assigned...
- LTransparentProxy := FTransparentProxy;
- if Assigned(LTransparentProxy) then
- begin
- if LTransparentProxy.Enabled then
- begin
- repeat
- LNextTransparentProxy := LTransparentProxy.ChainedProxy;
- if not Assigned(LNextTransparentProxy) then Break;
- if not LNextTransparentProxy.Enabled then Break;
- LTransparentProxy := LNextTransparentProxy;
- until False;
- Result := LTransparentProxy.Host;
- end;
- end;
- end;
- begin
- Assert(Binding<>nil);
- if not Assigned(fSSLSocket) then begin
- fSSLSocket := TIdSSLSocket.Create(Self);
- end;
- Assert(fSSLSocket.fSSLContext=nil);
- fSSLSocket.fSSLContext := fSSLContext;
- {$IFDEF WIN32_OR_WIN64}
- // begin bug fix
- if IndyCheckWindowsVersion(6) then
- begin
- // Note: Fix needed to allow SSL_Read and SSL_Write to timeout under
- // Vista+ when connection is dropped
- LTimeout := FReadTimeOut;
- if LTimeout <= 0 then begin
- LTimeout := 30000; // 30 seconds
- end;
- Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_RCVTIMEO, LTimeout);
- Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, LTimeout);
- end;
- // end bug fix
- {$ENDIF}
- // RLebeau 7/2/2015: do not rely on IsPeer to decide whether to call Connect()
- // or Accept(). SSLContext.Mode controls whether a client or server method is
- // used to handle the connection, so that same value should be used here as well.
- // A user encountered a scenario where he needed to connect a TIdTCPClient to a
- // TCP server on a hardware device, but run the client's SSLIOHandler as an SSL
- // server because the device was initiating the SSL handshake as an SSL client.
- // IsPeer was not designed to handle that scenario. Setting IsPeer to True
- // allowed Accept() to be called here, but at the cost of causing memory leaks
- // in TIdSSLIOHandlerSocketOpenSSL.Destroy() and TIdSSLIOHandlerSocketOpenSSL.Close()
- // in client components! IsPeer is intended to be set to True only in server
- // components...
- LMode := fSSLContext.Mode;
- if not (LMode in [sslmClient, sslmServer]) then begin
- // Mode must be sslmBoth (or else TIdSSLContext.SetSSLMethod() would have
- // raised an exception), so just fall back to previous behavior for now,
- // until we can figure out a better way to handle this scenario...
- if IsPeer then begin
- LMode := sslmServer;
- end else begin
- LMode := sslmClient;
- end;
- end;
- if LMode = sslmClient then begin
- LHost := GetURIHost;
- if LHost = '' then
- begin
- LHost := GetProxyTargetHost;
- if LHost = '' then begin
- LHost := Self.Host;
- end;
- end;
- fSSLSocket.fHostName := LHost;
- fSSLSocket.Connect(Binding.Handle);
- end else begin
- fSSLSocket.fHostName := '';
- fSSLSocket.Accept(Binding.Handle);
- end;
- fPassThrough := False;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.DoBeforeConnect(ASender: TIdSSLIOHandlerSocketOpenSSL);
- begin
- if Assigned(OnBeforeConnect) then begin
- OnBeforeConnect(Self);
- end;
- end;
- // TODO: add an AOwner parameter
- function TIdSSLIOHandlerSocketOpenSSL.Clone: TIdSSLIOHandlerSocketBase;
- var
- LIO : TIdSSLIOHandlerSocketOpenSSL;
- begin
- LIO := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
- try
- LIO.SSLOptions.Assign( SSLOptions );
- LIO.OnStatusInfo := DoStatusInfo;
- LIO.OnGetPassword := DoGetPassword;
- LIO.OnGetPasswordEx := OnGetPasswordEx;
- LIO.OnVerifyPeer := DoVerifyPeer;
- LIO.fSSLSocket := TIdSSLSocket.Create(Self);
- except
- LIO.Free;
- raise;
- end;
- Result := LIO;
- end;
- function TIdSSLIOHandlerSocketOpenSSL.CheckForError(ALastResult: Integer): Integer;
- //var
- // err: Integer;
- begin
- if PassThrough then begin
- Result := inherited CheckForError(ALastResult);
- end else begin
- Result := fSSLSocket.GetSSLError(ALastResult);
- if Result = SSL_ERROR_NONE then begin
- Result := 0;
- Exit;
- end;
- if Result = SSL_ERROR_SYSCALL then begin
- Result := inherited CheckForError(Integer(Id_SOCKET_ERROR));
- Exit;
- end;
- EIdOpenSSLAPISSLError.RaiseExceptionCode(Result, ALastResult, '');
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.RaiseError(AError: Integer);
- begin
- if (PassThrough) or (AError = Id_WSAESHUTDOWN) or (AError = Id_WSAECONNABORTED) or (AError = Id_WSAECONNRESET) then begin
- inherited RaiseError(AError);
- end else begin
- EIdOpenSSLAPISSLError.RaiseException(fSSLSocket.fSSL, AError, '');
- end;
- end;
- { IIdSSLOpenSSLCallbackHelper }
- function TIdSSLIOHandlerSocketOpenSSL.GetPassword(const AIsWrite : Boolean): string;
- begin
- DoGetPasswordEx(Result, AIsWrite);
- if Result = '' then begin
- DoGetPassword(Result);
- end;
- end;
- procedure TIdSSLIOHandlerSocketOpenSSL.StatusInfo(const ASslSocket: PSSL;
- AWhere, ARet: TIdC_INT; const AStatusStr: string);
- var
- LType, LMsg: string;
- begin
- DoStatusInfo(AStatusStr);
- if Assigned(fOnStatusInfoEx) then begin
- GetStateVars(ASslSocket, AWhere, ARet, LType, LMsg);
- DoStatusInfoEx(ASslSocket, AWhere, ARet, LType, LMsg);
- end;
- end;
- function TIdSSLIOHandlerSocketOpenSSL.VerifyPeer(ACertificate: TIdX509;
- AOk: Boolean; ADepth, AError: Integer): Boolean;
- begin
- Result := DoVerifyPeer(ACertificate, AOk, ADepth, AError);
- end;
- function TIdSSLIOHandlerSocketOpenSSL.GetIOHandlerSelf: TIdSSLIOHandlerSocketOpenSSL;
- begin
- Result := Self;
- end;
- { TIdSSLContext }
- constructor TIdSSLContext.Create;
- begin
- inherited Create;
- //an exception here probably means that you are using the wrong version
- //of the openssl libraries. refer to comments at the top of this file.
- if not LoadOpenSSLLibrary then begin
- raise EIdOSSLCouldNotLoadSSLLibrary.Create(RSOSSLCouldNotLoadSSLLibrary);
- end;
- fVerifyMode := [];
- fMode := sslmUnassigned;
- fSessionId := 1;
- end;
- destructor TIdSSLContext.Destroy;
- begin
- DestroyContext;
- inherited Destroy;
- end;
- procedure TIdSSLContext.DestroyContext;
- begin
- if fContext <> nil then begin
- SSL_CTX_free(fContext);
- fContext := nil;
- end;
- end;
- procedure TIdSSLContext.InitContext(CtxMode: TIdSSLCtxMode);
- var
- SSLMethod: PSSL_METHOD;
- error: TIdC_INT;
- // pCAname: PSTACK_X509_NAME;
- {$IFDEF USE_MARSHALLED_PTRS}
- M: TMarshaller;
- {$ENDIF}
- begin
- // Destroy the context first
- DestroyContext;
- if fMode = sslmUnassigned then begin
- if CtxMode = sslCtxServer then begin
- fMode := sslmServer;
- end else begin
- fMode := sslmClient;
- end
- end;
- // get SSL method function (SSL2, SSL23, SSL3, TLS)
- SSLMethod := SetSSLMethod;
- // create new SSL context
- fContext := SSL_CTX_new(SSLMethod);
- if fContext = nil then begin
- EIdOSSLCreatingContextError.RaiseException(RSSSLCreatingContextError);
- end;
- //set SSL Versions we will use
- // in OpenSSL 1.0.2g onwards, SSLv2 is disabled and not exported by default
- // at compile-time. If OpenSSL is compiled with "enable-ssl2" enabled so the
- // SSLv2_xxx_method() functions are exported, SSLv2 is still disabled by
- // default in the SSLv23_xxx_method() functions and must be enabled explicitly...
- if IsOpenSSL_SSLv2_Available then begin
- if not (sslvSSLv2 in SSLVersions) then begin
- SSL_CTX_set_options(fContext, SSL_OP_NO_SSLv2);
- end
- else if (fMethod = sslvSSLv23) then begin
- SSL_CTX_clear_options(fContext, SSL_OP_NO_SSLv2);
- end;
- end;
- // SSLv3 might also be disabled as well..
- if IsOpenSSL_SSLv3_Available then begin
- if not (sslvSSLv3 in SSLVersions) then begin
- SSL_CTX_set_options(fContext, SSL_OP_NO_SSLv3);
- end
- else if (fMethod = sslvSSLv23) then begin
- SSL_CTX_clear_options(fContext, SSL_OP_NO_SSLv3);
- end;
- end;
- // may as well do the same for all of them...
- if IsOpenSSL_TLSv1_0_Available then begin
- if not (sslvTLSv1 in SSLVersions) then begin
- SSL_CTX_set_options(fContext, SSL_OP_NO_TLSv1);
- end
- else if (fMethod = sslvSSLv23) then begin
- SSL_CTX_clear_options(fContext, SSL_OP_NO_TLSv1);
- end;
- end;
- {IMPORTANT!!! Do not set SSL_CTX_set_options SSL_OP_NO_TLSv1_1 and
- SSL_OP_NO_TLSv1_2 if that functionality is not available. OpenSSL 1.0 and
- earlier do not support those flags. Those flags would only cause
- an invalid MAC when doing SSL.}
- if IsOpenSSL_TLSv1_1_Available then begin
- if not (sslvTLSv1_1 in SSLVersions) then begin
- SSL_CTX_set_options(fContext, SSL_OP_NO_TLSv1_1);
- end
- else if (fMethod = sslvSSLv23) then begin
- SSL_CTX_clear_options(fContext, SSL_OP_NO_TLSv1_1);
- end;
- end;
- if IsOpenSSL_TLSv1_2_Available then begin
- if not (sslvTLSv1_2 in SSLVersions) then begin
- SSL_CTX_set_options(fContext, SSL_OP_NO_TLSv1_2);
- end
- else if (fMethod = sslvSSLv23) then begin
- SSL_CTX_clear_options(fContext, SSL_OP_NO_TLSv1_2);
- end;
- end;
- SSL_CTX_set_mode(fContext, SSL_MODE_AUTO_RETRY);
- // assign a password lookup routine
- // if PasswordRoutineOn then begin
- SSL_CTX_set_default_passwd_cb(fContext, @PasswordCallback);
- SSL_CTX_set_default_passwd_cb_userdata(fContext, Self);
- // end;
- SSL_CTX_set_default_verify_paths(fContext);
- // load key and certificate files
- if (RootCertFile <> '') or (VerifyDirs <> '') then begin {Do not Localize}
- if not LoadRootCert then begin
- EIdOSSLLoadingRootCertError.RaiseException(RSSSLLoadingRootCertError);
- end;
- end;
- if CertFile <> '' then begin {Do not Localize}
- if not LoadCert then begin
- EIdOSSLLoadingCertError.RaiseException(RSSSLLoadingCertError);
- end;
- end;
- if KeyFile <> '' then begin {Do not Localize}
- if not LoadKey then begin
- EIdOSSLLoadingKeyError.RaiseException(RSSSLLoadingKeyError);
- end;
- end;
- if DHParamsFile <> '' then begin {Do not Localize}
- if not LoadDHParams then begin
- EIdOSSLLoadingDHParamsError.RaiseException(RSSSLLoadingDHParamsError);
- end;
- end;
- if StatusInfoOn then begin
- SSL_CTX_set_info_callback(fContext, InfoCallback);
- end;
- //if_SSL_CTX_set_tmp_rsa_callback(hSSLContext, @RSACallback);
- if fCipherList <> '' then begin {Do not Localize}
- error := SSL_CTX_set_cipher_list(fContext,
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsAnsi(fCipherList).ToPointer
- {$ELSE}
- PAnsiChar(
- {$IFDEF STRING_IS_ANSI}
- fCipherList
- {$ELSE}
- AnsiString(fCipherList) // explicit cast to Ansi
- {$ENDIF}
- )
- {$ENDIF}
- );
- end else begin
- // RLebeau: don't override OpenSSL's default. As OpenSSL evolves, the
- // SSL_DEFAULT_CIPHER_LIST constant defined in the C/C++ SDK may change,
- // while Indy's define of it might take some time to catch up. We don't
- // want users using an older default with newer DLLs...
- (*
- error := SSL_CTX_set_cipher_list(fContext,
- {$IFDEF USE_MARSHALLED_PTRS}
- M.AsAnsi(SSL_DEFAULT_CIPHER_LIST).ToPointer
- {$ELSE}
- SSL_DEFAULT_CIPHER_LIST
- {$ENDIF}
- );
- *)
- error := 1;
- end;
- if error <= 0 then begin
- // TODO: should this be using EIdOSSLSettingCipherError.RaiseException() instead?
- raise EIdOSSLSettingCipherError.Create(RSSSLSettingCipherError);
- end;
- if fVerifyMode <> [] then begin
- SetVerifyMode(fVerifyMode, VerifyOn);
- end;
- if CtxMode = sslCtxServer then begin
- SSL_CTX_set_session_id_context(fContext, PByte(@fSessionId), SizeOf(fSessionId));
- end;
- // CA list
- if RootCertFile <> '' then begin {Do not Localize}
- SSL_CTX_set_client_CA_list(fContext, IndySSL_load_client_CA_file(RootCertFile));
- end
- // TODO: provide an event so users can apply their own settings as needed...
- end;
- procedure TIdSSLContext.SetVerifyMode(Mode: TIdSSLVerifyModeSet; CheckRoutine: Boolean);
- var
- Func: TSSL_CTX_set_verify_callback;
- begin
- if fContext<>nil then begin
- // SSL_CTX_set_default_verify_paths(fContext);
- if CheckRoutine then begin
- Func := VerifyCallback;
- end else begin
- Func := nil;
- end;
- SSL_CTX_set_verify(fContext, TranslateInternalVerifyToSSL(Mode), Func);
- SSL_CTX_set_verify_depth(fContext, fVerifyDepth);
- end;
- end;
- function TIdSSLContext.GetVerifyMode: TIdSSLVerifyModeSet;
- begin
- Result := fVerifyMode;
- end;
- {
- function TIdSSLContext.LoadVerifyLocations(FileName: String; Dirs: String): Boolean;
- begin
- Result := False;
- if (Dirs <> '') or (FileName <> '') then begin
- if IndySSL_CTX_load_verify_locations(fContext, FileName, Dirs) <= 0 then begin
- raise EIdOSSLCouldNotLoadSSLLibrary.Create(RSOSSLCouldNotLoadSSLLibrary);
- end;
- end;
- Result := True;
- end;
- }
- function SelectTLS1Method(const AMode : TIdSSLMode) : PSSL_METHOD;
- {$IFDEF USE_INLINE} inline; {$ENDIF}
- begin
- Result := nil;
- case AMode of
- sslmServer : begin
- if Assigned(TLSv1_server_method) then begin
- Result := TLSv1_server_method();
- end;
- end;
- sslmClient : begin
- if Assigned(TLSv1_client_method) then begin
- Result := TLSv1_client_method();
- end;
- end;
- else
- if Assigned(TLSv1_method) then begin
- Result := TLSv1_method();
- end;
- end;
- end;
- function TIdSSLContext.SetSSLMethod: PSSL_METHOD;
- begin
- Result := nil;
- if fMode = sslmUnassigned then begin
- raise EIdOSSLModeNotSet.Create(RSOSSLModeNotSet);
- end;
- case fMethod of
- sslvSSLv2:
- case fMode of
- sslmServer : begin
- if Assigned(SSLv2_server_method) then begin
- Result := SSLv2_server_method();
- end;
- end;
- sslmClient : begin
- if Assigned(SSLv2_client_method) then begin
- Result := SSLv2_client_method();
- end;
- end;
- else
- if Assigned(SSLv2_method) then begin
- Result := SSLv2_method();
- end;
- end;
- sslvSSLv23:
- case fMode of
- sslmServer : begin
- if Assigned(SSLv23_server_method) then begin
- Result := SSLv23_server_method();
- end;
- end;
- sslmClient : begin
- if Assigned(SSLv23_client_method) then begin
- Result := SSLv23_client_method();
- end;
- end;
- else
- if Assigned(SSLv23_method) then begin
- Result := SSLv23_method();
- end;
- end;
- sslvSSLv3:
- case fMode of
- sslmServer : begin
- if Assigned(SSLv3_server_method) then begin
- Result := SSLv3_server_method();
- end;
- end;
- sslmClient : begin
- if Assigned(SSLv3_client_method) then begin
- Result := SSLv3_client_method();
- end;
- end;
- else
- if Assigned(SSLv3_method) then begin
- Result := SSLv3_method();
- end;
- end;
- {IMPORTANT!!! fallback to TLS 1.0 if TLS 1.1 or 1.2 is not available.
- This is important because OpenSSL earlier than 1.0.1 does not support this
- functionality.
- Todo: Figure out a better fallback.
- }
- // TODO: get rid of this fallack! If the user didn't choose TLS 1.0, then
- // don't falback to it, just fail instead, like with all of the other SSL/TLS
- // versions...
- sslvTLSv1:
- Result := SelectTLS1Method(fMode);
- sslvTLSv1_1:
- case fMode of
- sslmServer : begin
- if Assigned(TLSv1_1_server_method) then begin
- Result := TLSv1_1_server_method();
- end else begin
- Result := SelectTLS1Method(fMode);
- end;
- end;
- sslmClient : begin
- if Assigned(TLSv1_1_client_method) then begin
- Result := TLSv1_1_client_method();
- end else begin
- Result := SelectTLS1Method(fMode);
- end;
- end;
- else
- if Assigned(TLSv1_1_method) then begin
- Result := TLSv1_1_method();
- end else begin
- Result := SelectTLS1Method(fMode);
- end;
- end;
- sslvTLSv1_2:
- case fMode of
- sslmServer : begin
- if Assigned(TLSv1_2_server_method) then begin
- Result := TLSv1_2_server_method();
- end else begin
- // TODO: fallback to TLSv1.1 if available?
- Result := SelectTLS1Method(fMode);
- end;
- end;
- sslmClient : begin
- if Assigned(TLSv1_2_client_method) then begin
- Result := TLSv1_2_client_method();
- end else begin
- // TODO: fallback to TLSv1.1 if available?
- Result := SelectTLS1Method(fMode);
- end;
- end;
- else
- if Assigned(TLSv1_2_method) then begin
- Result := TLSv1_2_method();
- end else begin
- // TODO: fallback to TLSv1.1 if available?
- Result := SelectTLS1Method(fMode);
- end;
- end;
- end;
- if Result = nil then begin
- raise EIdOSSLGetMethodError.Create(RSSSLGetMethodError);
- end;
- end;
- function TIdSSLContext.LoadRootCert: Boolean;
- begin
- Result := IndySSL_CTX_load_verify_locations(fContext, RootCertFile, VerifyDirs) > 0;
- end;
- function TIdSSLContext.LoadCert: Boolean;
- begin
- if PosInStrArray(ExtractFileExt(CertFile), ['.p12', '.pfx'], False) <> -1 then begin
- Result := IndySSL_CTX_use_certificate_file_PKCS12(fContext, CertFile) > 0;
- end else begin
- //OpenSSL 1.0.2 has a new function, SSL_CTX_use_certificate_chain_file
- //that handles a chain of certificates in a PEM file. That is prefered.
- if Assigned(SSL_CTX_use_certificate_chain_file) then begin
- Result := IndySSL_CTX_use_certificate_chain_file(fContext, CertFile) > 0;
- end else begin
- Result := IndySSL_CTX_use_certificate_file(fContext, CertFile, SSL_FILETYPE_PEM) > 0;
- end;
- end;
- end;
- function TIdSSLContext.LoadKey: Boolean;
- begin
- if PosInStrArray(ExtractFileExt(KeyFile), ['.p12', '.pfx'], False) <> -1 then begin
- Result := IndySSL_CTX_use_PrivateKey_file_PKCS12(fContext, KeyFile) > 0;
- end else begin
- Result := IndySSL_CTX_use_PrivateKey_file(fContext, KeyFile, SSL_FILETYPE_PEM) > 0;
- end;
- if Result then begin
- Result := SSL_CTX_check_private_key(fContext) > 0;
- end;
- end;
- function TIdSSLContext.LoadDHParams: Boolean;
- begin
- Result := IndySSL_CTX_use_DHparams_file(fContext, fsDHParamsFile, SSL_FILETYPE_PEM) > 0;
- end;
- //////////////////////////////////////////////////////////////
- function TIdSSLContext.Clone: TIdSSLContext;
- begin
- Result := TIdSSLContext.Create;
- Result.StatusInfoOn := StatusInfoOn;
- // property PasswordRoutineOn: Boolean read fPasswordRoutineOn write fPasswordRoutineOn;
- Result.VerifyOn := VerifyOn;
- Result.Method := Method;
- Result.SSLVersions := SSLVersions;
- Result.Mode := Mode;
- Result.RootCertFile := RootCertFile;
- Result.CertFile := CertFile;
- Result.KeyFile := KeyFile;
- Result.VerifyMode := VerifyMode;
- Result.VerifyDepth := VerifyDepth;
- end;
- { TIdSSLSocket }
- constructor TIdSSLSocket.Create(Parent: TObject);
- begin
- inherited Create;
- fParent := Parent;
- end;
- destructor TIdSSLSocket.Destroy;
- begin
- if fSSL <> nil then begin
- // TODO: should this be moved to TIdSSLContext instead? Is this here
- // just to make sure the SSL shutdown does not log any messages?
- {
- if (fSSLContext <> nil) and (fSSLContext.StatusInfoOn) and
- (fSSLContext.fContext <> nil) then begin
- SSL_CTX_set_info_callback(fSSLContext.fContext, nil);
- end;
- }
- //SSL_set_shutdown(fSSL, SSL_SENT_SHUTDOWN);
- SSL_shutdown(fSSL);
- SSL_free(fSSL);
- fSSL := nil;
- end;
- FreeAndNil(fSSLCipher);
- FreeAndNil(fPeerCert);
- inherited Destroy;
- end;
- function TIdSSLSocket.GetSSLError(retCode: Integer): Integer;
- begin
- // COMMENT!!!
- // I found out that SSL layer should not interpret errors, cause they will pop up
- // on the socket layer. Only thing that the SSL layer should consider is key
- // or protocol renegotiation. This is done by loop in read and write
- Result := SSL_get_error(fSSL, retCode);
- case Result of
- SSL_ERROR_NONE:
- Result := SSL_ERROR_NONE;
- SSL_ERROR_WANT_WRITE:
- Result := SSL_ERROR_WANT_WRITE;
- SSL_ERROR_WANT_READ:
- Result := SSL_ERROR_WANT_READ;
- SSL_ERROR_ZERO_RETURN:
- Result := SSL_ERROR_ZERO_RETURN;
- //Result := SSL_ERROR_NONE;
- {
- // ssl layer has been disconnected, it is not necessary that also
- // socked has been closed
- case Mode of
- sslemClient: begin
- case Action of
- sslWrite: begin
- if retCode = 0 then begin
- Result := 0;
- end
- else begin
- raise EIdException.Create(RSOSSLConnectionDropped); // TODO: create a new Exception class for this
- end;
- end;
- end;
- end;}
- //raise EIdException.Create(RSOSSLConnectionDropped); // TODO: create a new Exception class for this
- // X509_LOOKUP event is not really an error, just an event
- // SSL_ERROR_WANT_X509_LOOKUP:
- // raise EIdException.Create(RSOSSLCertificateLookup); // TODO: create a new Exception class for this
- SSL_ERROR_SYSCALL:
- Result := SSL_ERROR_SYSCALL;
- // Result := SSL_ERROR_NONE;
- {//raise EIdException.Create(RSOSSLInternal); // TODO: create a new Exception class for this
- if (retCode <> 0) or (DataLen <> 0) then begin
- raise EIdException.Create(RSOSSLConnectionDropped); // TODO: create a new Exception class for this
- end
- else begin
- Result := 0;
- end;}
- SSL_ERROR_SSL:
- // raise EIdException.Create(RSOSSLInternal); // TODO: create a new Exception class for this
- Result := SSL_ERROR_SSL;
- // Result := SSL_ERROR_NONE;
- end;
- end;
- procedure TIdSSLSocket.Accept(const pHandle: TIdStackSocketHandle);
- //Accept and Connect have a lot of duplicated code
- var
- error: Integer;
- StatusStr: String;
- LParentIO: TIdSSLIOHandlerSocketOpenSSL;
- LHelper: IIdSSLOpenSSLCallbackHelper;
- begin
- Assert(fSSL=nil);
- Assert(fSSLContext<>nil);
- fSSL := SSL_new(fSSLContext.fContext);
- if fSSL = nil then begin
- raise EIdOSSLCreatingSessionError.Create(RSSSLCreatingSessionError);
- end;
- error := SSL_set_app_data(fSSL, Self);
- if error <= 0 then begin
- EIdOSSLDataBindingError.RaiseException(fSSL, error, RSSSLDataBindingError);
- end;
- error := SSL_set_fd(fSSL, pHandle);
- if error <= 0 then begin
- EIdOSSLFDSetError.RaiseException(fSSL, error, RSSSLFDSetError);
- end;
- // RLebeau: if this socket's IOHandler was cloned, no need to reuse the
- // original IOHandler's active session ID, since this is a server socket
- // that generates its own sessions...
- //
- // RLebeau: is this actually true? Should we be reusing the original
- // IOHandler's active session ID regardless of whether this is a client
- // or server socket? What about FTP in non-passive mode, for example?
- {
- if (LParentIO <> nil) and (LParentIO.fSSLSocket <> nil) and
- (LParentIO.fSSLSocket <> Self) then
- begin
- SSL_copy_session_id(fSSL, LParentIO.fSSLSocket.fSSL);
- end;
- }
- error := SSL_accept(fSSL);
- if error <= 0 then begin
- EIdOSSLAcceptError.RaiseException(fSSL, error, RSSSLAcceptError);
- end;
- if Supports(fParent, IIdSSLOpenSSLCallbackHelper, IInterface(LHelper)) then begin
- LParentIO := LHelper.GetIOHandlerSelf;
- if LParentIO <> nil then begin
- StatusStr := 'Cipher: name = ' + Cipher.Name + '; ' + {Do not Localize}
- 'description = ' + Cipher.Description + '; ' + {Do not Localize}
- 'bits = ' + IntToStr(Cipher.Bits) + '; ' + {Do not Localize}
- 'version = ' + Cipher.Version + '; '; {Do not Localize}
- LParentIO.DoStatusInfo(StatusStr);
- end;
- LHelper := nil;
- end;
- end;
- procedure TIdSSLSocket.Connect(const pHandle: TIdStackSocketHandle);
- var
- error: Integer;
- StatusStr: String;
- LParentIO: TIdSSLIOHandlerSocketOpenSSL;
- LHelper: IIdSSLOpenSSLCallbackHelper;
- begin
- Assert(fSSL=nil);
- Assert(fSSLContext<>nil);
- if Supports(fParent, IIdSSLOpenSSLCallbackHelper, IInterface(LHelper)) then begin
- LParentIO := LHelper.GetIOHandlerSelf;
- end else begin
- LParentIO := nil;
- end;
- fSSL := SSL_new(fSSLContext.fContext);
- if fSSL = nil then begin
- raise EIdOSSLCreatingSessionError.Create(RSSSLCreatingSessionError);
- end;
- error := SSL_set_app_data(fSSL, Self);
- if error <= 0 then begin
- EIdOSSLDataBindingError.RaiseException(fSSL, error, RSSSLDataBindingError);
- end;
- error := SSL_set_fd(fSSL, pHandle);
- if error <= 0 then begin
- EIdOSSLFDSetError.RaiseException(fSSL, error, RSSSLFDSetError);
- end;
- // RLebeau: if this socket's IOHandler was cloned, reuse the
- // original IOHandler's active session ID...
- if (LParentIO <> nil) and (LParentIO.fSSLSocket <> nil) and
- (LParentIO.fSSLSocket <> Self) then
- begin
- SSL_copy_session_id(fSSL, LParentIO.fSSLSocket.fSSL);
- end;
- {$IFNDEF OPENSSL_NO_TLSEXT}
- error := SSL_set_tlsext_host_name(fSSL, fHostName);
- if error <= 0 then begin
- // RLebeau: for the time being, not raising an exception on error, as I don't
- // know which OpenSSL versions support this extension, and which error code(s)
- // are safe to ignore on those versions...
- //EIdOSSLSettingTLSHostNameError.RaiseException(fSSL, error, RSSSLSettingTLSHostNameError);
- end;
- {$ENDIF}
- error := SSL_connect(fSSL);
- if error <= 0 then begin
- // TODO: if sslv23 is being used, but sslv23 is not being used on the
- // remote side, SSL_connect() will fail. In that case, before giving up,
- // try re-connecting using a version-specific method for each enabled
- // version, maybe one will succeed...
- EIdOSSLConnectError.RaiseException(fSSL, error, RSSSLConnectError);
- end;
- // TODO: even if SSL_connect() returns success, the connection might
- // still be insecure if SSL_connect() detected that certificate validation
- // actually failed, but ignored it because SSL_VERIFY_PEER was disabled!
- // It would report such a failure via SSL_get_verify_result() instead of
- // returning an error code, so we should call SSL_get_verify_result() here
- // to make sure...
- if LParentIO <> nil then begin
- StatusStr := 'Cipher: name = ' + Cipher.Name + '; ' + {Do not Localize}
- 'description = ' + Cipher.Description + '; ' + {Do not Localize}
- 'bits = ' + IntToStr(Cipher.Bits) + '; ' + {Do not Localize}
- 'version = ' + Cipher.Version + '; '; {Do not Localize}
- LParentIO.DoStatusInfo(StatusStr);
- end;
- // TODO: enable this
- {
- var
- peercert: PX509;
- lHostName: AnsiString;
- peercert := SSL_get_peer_certificate(fSSL);
- try
- lHostName := AnsiString(fHostName);
- if (X509_check_host(peercert, PByte(PAnsiChar(lHostName)), Length(lHostName), 0) != 1) and
- (not certificate_host_name_override(peercert, PAnsiChar(lHostName)) then
- begin
- EIdOSSLCertificateError.RaiseException(fSSL, error, 'SSL certificate does not match host name');
- end;
- finally
- X509_free(peercert);
- end;
- }
- end;
- function TIdSSLSocket.Recv(var ABuffer: TIdBytes): Integer;
- var
- ret, err: Integer;
- begin
- repeat
- ret := SSL_read(fSSL, PByte(ABuffer), Length(ABuffer));
- if ret > 0 then begin
- Result := ret;
- Exit;
- end;
- err := GetSSLError(ret);
- if (err = SSL_ERROR_WANT_READ) or (err = SSL_ERROR_WANT_WRITE) then begin
- Continue;
- end;
- if err = SSL_ERROR_ZERO_RETURN then begin
- Result := 0;
- end else begin
- Result := ret;
- end;
- Exit;
- until False;
- end;
- function TIdSSLSocket.Send(const ABuffer: TIdBytes; AOffset, ALength: Integer): Integer;
- var
- ret, err: Integer;
- begin
- Result := 0;
- repeat
- ret := SSL_write(fSSL, @ABuffer[AOffset], ALength);
- if ret > 0 then begin
- Inc(Result, ret);
- Inc(AOffset, ret);
- Dec(ALength, ret);
- if ALength < 1 then begin
- Exit;
- end;
- Continue;
- end;
- err := GetSSLError(ret);
- if (err = SSL_ERROR_WANT_READ) or (err = SSL_ERROR_WANT_WRITE) then begin
- Continue;
- end;
- if err = SSL_ERROR_ZERO_RETURN then begin
- Result := 0;
- end else begin
- Result := ret;
- end;
- Exit;
- until False;
- end;
- function TIdSSLSocket.GetPeerCert: TIdX509;
- var
- LX509: PX509;
- begin
- if fPeerCert = nil then begin
- LX509 := SSL_get_peer_certificate(fSSL);
- if LX509 <> nil then begin
- fPeerCert := TIdX509.Create(LX509, False);
- end;
- end;
- Result := fPeerCert;
- end;
- function TIdSSLSocket.GetSSLCipher: TIdSSLCipher;
- begin
- if (fSSLCipher = nil) and (fSSL<>nil) then begin
- fSSLCipher := TIdSSLCipher.Create(Self);
- end;
- Result := fSSLCipher;
- end;
- function TIdSSLSocket.GetSessionID: TIdSSLByteArray;
- var
- pSession: PSSL_SESSION;
- begin
- Result.Length := 0;
- Result.Data := nil;
- if Assigned(SSL_get_session) and Assigned(SSL_SESSION_get_id) then
- begin
- if fSSL <> nil then begin
- pSession := SSL_get_session(fSSL);
- if pSession <> nil then begin
- Result.Data := PByte(SSL_SESSION_get_id(pSession, @Result.Length));
- end;
- end;
- end;
- end;
- function TIdSSLSocket.GetSessionIDAsString:String;
- var
- Data: TIdSSLByteArray;
- i: TIdC_UINT;
- LDataPtr: PByte;
- begin
- Result := ''; {Do not Localize}
- Data := GetSessionID;
- if Data.Length > 0 then begin
- for i := 0 to Data.Length-1 do begin
- // RLebeau: not all Delphi versions support indexed access using PByte
- LDataPtr := Data.Data;
- Inc(LDataPtr, I);
- Result := Result + IndyFormat('%.2x', [LDataPtr^]);{do not localize}
- end;
- end;
- end;
- procedure TIdSSLSocket.SetCipherList(CipherList: String);
- //var
- // tmpPStr: PAnsiChar;
- begin
- {
- fCipherList := CipherList;
- fCipherList_Ch := True;
- aCipherList := aCipherList+#0;
- if hSSL <> nil then f_SSL_set_cipher_list(hSSL, @aCipherList[1]);
- }
- end;
- ///////////////////////////////////////////////////////////////
- // X509 Certificate
- ///////////////////////////////////////////////////////////////
- { TIdX509Name }
- function TIdX509Name.CertInOneLine: String;
- var
- LOneLine: array[0..2048] of TIdAnsiChar;
- begin
- if FX509Name = nil then begin
- Result := ''; {Do not Localize}
- end else begin
- Result := String(X509_NAME_oneline(FX509Name, @LOneLine[0], SizeOf(LOneLine)));
- end;
- end;
- function TIdX509Name.GetHash: TIdSSLULong;
- begin
- if FX509Name = nil then begin
- FillChar(Result, SizeOf(Result), 0)
- end else begin
- Result.C1 := X509_NAME_hash(FX509Name);
- end;
- end;
- function TIdX509Name.GetHashAsString: String;
- begin
- Result := IndyFormat('%.8x', [Hash.L1]); {do not localize}
- end;
- constructor TIdX509Name.Create(aX509Name: PX509_NAME);
- begin
- Inherited Create;
- FX509Name := aX509Name;
- end;
- ///////////////////////////////////////////////////////////////
- // X509 Certificate
- ///////////////////////////////////////////////////////////////
- { TIdX509Info }
- constructor TIdX509Info.Create(aX509: PX509);
- begin
- inherited Create;
- FX509 := aX509;
- end;
- { TIdX509Fingerprints }
- function TIdX509Fingerprints.GetMD5: TIdSSLEVP_MD;
- begin
- CheckMD5Permitted;
- X509_digest(FX509, EVP_md5, PByte(@Result.MD), Result.Length);
- end;
- function TIdX509Fingerprints.GetMD5AsString: String;
- begin
- Result := MDAsString(MD5);
- end;
- function TIdX509Fingerprints.GetSHA1: TIdSSLEVP_MD;
- begin
- X509_digest(FX509, EVP_sha1, PByte(@Result.MD), Result.Length);
- end;
- function TIdX509Fingerprints.GetSHA1AsString: String;
- begin
- Result := MDAsString(SHA1);
- end;
- function TIdX509Fingerprints.GetSHA224 : TIdSSLEVP_MD;
- begin
- if Assigned(EVP_sha224) then begin
- X509_digest(FX509, EVP_sha224, PByte(@Result.MD), Result.Length);
- end else begin
- FillChar(Result, SizeOf(Result), 0);
- end;
- end;
- function TIdX509Fingerprints.GetSHA224AsString : String;
- begin
- if Assigned(EVP_sha224) then begin
- Result := MDAsString(SHA224);
- end else begin
- Result := '';
- end;
- end;
- function TIdX509Fingerprints.GetSHA256 : TIdSSLEVP_MD;
- begin
- if Assigned(EVP_sha256) then begin
- X509_digest(FX509, EVP_sha256, PByte(@Result.MD), Result.Length);
- end else begin
- FillChar(Result, SizeOf(Result), 0);
- end;
- end;
- function TIdX509Fingerprints.GetSHA256AsString : String;
- begin
- if Assigned(EVP_sha256) then begin
- Result := MDAsString(SHA256);
- end else begin
- Result := '';
- end;
- end;
- function TIdX509Fingerprints.GetSHA384 : TIdSSLEVP_MD;
- begin
- if Assigned(EVP_SHA384) then begin
- X509_digest(FX509, EVP_SHA384, PByte(@Result.MD), Result.Length);
- end else begin
- FillChar(Result, SizeOf(Result), 0);
- end;
- end;
- function TIdX509Fingerprints.GetSHA384AsString : String;
- begin
- if Assigned(EVP_SHA384) then begin
- Result := MDAsString(SHA384);
- end else begin
- Result := '';
- end;
- end;
- function TIdX509Fingerprints.GetSHA512 : TIdSSLEVP_MD;
- begin
- if Assigned(EVP_sha512) then begin
- X509_digest(FX509, EVP_sha512, PByte(@Result.MD), Result.Length);
- end else begin
- FillChar(Result, SizeOf(Result), 0);
- end;
- end;
- function TIdX509Fingerprints.GetSHA512AsString : String;
- begin
- if Assigned(EVP_sha512) then begin
- Result := MDAsString(SHA512);
- end else begin
- Result := '';
- end;
- end;
- { TIdX509SigInfo }
- function TIdX509SigInfo.GetSignature: String;
- begin
- Result := BytesToHexString(FX509^.signature^.data, FX509^.signature^.length);
- end;
- function TIdX509SigInfo.GetSigType: TIdC_INT;
- begin
- Result := X509_get_signature_type(FX509);
- end;
- function TIdX509SigInfo.GetSigTypeAsString: String;
- begin
- Result := String(OBJ_nid2ln(SigType));
- end;
- { TIdX509 }
- constructor TIdX509.Create(aX509: PX509; aCanFreeX509: Boolean = True);
- begin
- inherited Create;
- //don't create FDisplayInfo unless specifically requested.
- FDisplayInfo := nil;
- FX509 := aX509;
- FCanFreeX509 := aCanFreeX509;
- FFingerprints := TIdX509Fingerprints.Create(FX509);
- FSigInfo := TIdX509SigInfo.Create(FX509);
- FSubject := nil;
- FIssuer := nil;
- end;
- destructor TIdX509.Destroy;
- begin
- FreeAndNil(FDisplayInfo);
- FreeAndNil(FSubject);
- FreeAndNil(FIssuer);
- FreeAndNil(FFingerprints);
- FreeAndNil(FSigInfo);
- { If the X.509 certificate handle was obtained from a certificate
- store or from the SSL connection as a peer certificate, then DO NOT
- free it here! The memory is owned by the OpenSSL library and will
- crash the library if Indy tries to free its private memory here }
- if FCanFreeX509 then begin
- X509_free(FX509);
- end;
- inherited Destroy;
- end;
- function TIdX509.GetDisplayInfo: TStrings;
- begin
- if not Assigned(FDisplayInfo) then begin
- FDisplayInfo := TStringList.Create;
- DumpCert(FDisplayInfo, FX509);
- end;
- Result := FDisplayInfo;
- end;
- function TIdX509.GetSerialNumber: String;
- var
- LSN : PASN1_INTEGER;
- begin
- if FX509 <> nil then begin
- LSN := X509_get_serialNumber(FX509);
- Result := BytesToHexString(LSN.data, LSN.length);
- end else begin
- Result := '';
- end;
- end;
- function TIdX509.GetVersion : TIdC_LONG;
- begin
- Result := X509_get_version(FX509);
- end;
- function TIdX509.RSubject: TIdX509Name;
- var
- Lx509_name: PX509_NAME;
- Begin
- if not Assigned(FSubject) then begin
- if FX509 <> nil then begin
- Lx509_name := X509_get_subject_name(FX509);
- end else begin
- Lx509_name := nil;
- end;
- FSubject := TIdX509Name.Create(Lx509_name);
- end;
- Result := FSubject;
- end;
- function TIdX509.RIssuer: TIdX509Name;
- var
- Lx509_name: PX509_NAME;
- begin
- if not Assigned(FIssuer) then begin
- if FX509 <> nil then begin
- Lx509_name := X509_get_issuer_name(FX509);
- end else begin
- Lx509_name := nil;
- end;
- FIssuer := TIdX509Name.Create(Lx509_name);
- End;
- Result := FIssuer;
- end;
- function TIdX509.RFingerprint: TIdSSLEVP_MD;
- begin
- X509_digest(FX509, EVP_md5, PByte(@Result.MD), Result.Length);
- end;
- function TIdX509.RFingerprintAsString: String;
- begin
- Result := MDAsString(Fingerprint);
- end;
- function TIdX509.RnotBefore: TDateTime;
- begin
- if FX509 = nil then begin
- Result := 0
- end else begin
- //This is a safe typecast since PASN1_UTCTIME and PASN1_TIME are really
- //pointers to ASN1 strings since ASN1_UTCTIME amd ASM1_TIME are ASN1_STRING.
- Result := UTCTime2DateTime(PASN1_UTCTIME(X509_get_notBefore(FX509)));
- end;
- end;
- function TIdX509.RnotAfter:TDateTime;
- begin
- if FX509 = nil then begin
- Result := 0
- end else begin
- Result := UTCTime2DateTime(PASN1_UTCTIME(X509_get_notAfter(FX509)));
- end;
- end;
- ///////////////////////////////////////////////////////////////
- // TIdSSLCipher
- ///////////////////////////////////////////////////////////////
- constructor TIdSSLCipher.Create(AOwner: TIdSSLSocket);
- begin
- inherited Create;
- FSSLSocket := AOwner;
- end;
- destructor TIdSSLCipher.Destroy;
- begin
- inherited Destroy;
- end;
- function TIdSSLCipher.GetDescription;
- var
- Buf: array[0..1024] of TIdAnsiChar;
- begin
- Result := String(SSL_CIPHER_description(SSL_get_current_cipher(FSSLSocket.fSSL), @Buf[0], SizeOf(Buf)-1));
- end;
- function TIdSSLCipher.GetName:String;
- begin
- Result := String(SSL_CIPHER_get_name(SSL_get_current_cipher(FSSLSocket.fSSL)));
- end;
- function TIdSSLCipher.GetBits:TIdC_INT;
- begin
- SSL_CIPHER_get_bits(SSL_get_current_cipher(FSSLSocket.fSSL), Result);
- end;
- function TIdSSLCipher.GetVersion:String;
- begin
- Result := String(SSL_CIPHER_get_version(SSL_get_current_cipher(FSSLSocket.fSSL)));
- end;
- initialization
- Assert(SSLIsLoaded=nil);
- SSLIsLoaded := TIdThreadSafeBoolean.Create;
- RegisterSSL('OpenSSL','Indy Pit Crew', {do not localize}
- 'Copyright '+Char(169)+' 1993 - 2024'#10#13 + {do not localize}
- 'Chad Z. Hower (Kudzu) and the Indy Pit Crew. All rights reserved.', {do not localize}
- 'Open SSL Support DLL Delphi and C++Builder interface', {do not localize}
- 'http://www.indyproject.org/'#10#13 + {do not localize}
- 'Original Author - Gregor Ibic', {do not localize}
- TIdSSLIOHandlerSocketOpenSSL,
- TIdServerIOHandlerSSLOpenSSL);
- TIdSSLIOHandlerSocketOpenSSL.RegisterIOHandler;
- finalization
- // TODO: TIdSSLIOHandlerSocketOpenSSL.UnregisterIOHandler;
- UnLoadOpenSSLLibrary;
- //free the lock last as unload makes calls that use it
- FreeAndNil(SSLIsLoaded);
- end.
|