sslsockets.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by the Free Pascal development team
  4. SSL support for ssockets
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit sslsockets;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, sockets, ssockets, openssl, fpopenssl;
  16. Const
  17. SSLDataCount = 4; // 0 based.
  18. Type
  19. TVerifyCertificateEvent = Procedure(Sender : TObject; Allow : Boolean) of object;
  20. { TSSLSocketHandler }
  21. TSSLSocketHandler = class(TSocketHandler)
  22. private
  23. FRemoteHostName: String;
  24. FSSLLastErrorString: string;
  25. FCipherList: string;
  26. FVerifyPeerCert: Boolean;
  27. FOnVerifyCertificate: TVerifyCertificateEvent;
  28. FSSLType: TSSLType;
  29. FKeyPassword: string;
  30. FUsername: string;
  31. FPassword: string;
  32. FCertData : Array[0..4] of TSSLData;
  33. FSSL: TSSL;
  34. FCTX : TSSLContext;
  35. FSSLActive : Boolean;
  36. FSendHostAsSNI : Boolean;
  37. function GetSSLData(AIndex: Integer): TSSLData;
  38. procedure SetSSLData(AIndex: Integer; AValue: TSSLData);
  39. procedure SetSSLLastErrorString(AValue: string);
  40. protected
  41. Function FetchErrorInfo: Boolean;
  42. function CheckSSL(SSLResult: Integer): Boolean;
  43. function CheckSSL(SSLResult: Pointer): Boolean;
  44. function InitContext(NeedCertificate: Boolean): Boolean; virtual;
  45. function DoneContext: Boolean; virtual;
  46. function InitSslKeys: boolean;virtual;
  47. function DoVerifyCert:boolean;
  48. public
  49. constructor Create; override;
  50. Destructor Destroy; override;
  51. // Socket methods
  52. function Connect : Boolean; override;
  53. function Close : Boolean; override;
  54. function Accept : Boolean; override;
  55. function Shutdown(BiDirectional : Boolean): boolean; override;
  56. function Send(Const Buffer; Count: Integer): Integer; override;
  57. function Recv(Const Buffer; Count: Integer): Integer; override;
  58. function BytesAvailable: Integer; override;
  59. Function SSLActive: Boolean;
  60. function CreateSelfSignedCertificate(Const AHostName: string): Boolean; virtual;
  61. // Result of last CheckSSL call.
  62. Function SSLLastError: integer;
  63. property SSLLastErrorString: string read FSSLLastErrorString write SetSSLLastErrorString;
  64. published
  65. property SSLType: TSSLType read FSSLType write FSSLType;
  66. {:Password for decrypting of encoded certificate or key.}
  67. property Username: string read FUsername write FUsername;
  68. property Password: string read FPassword write FPassword;
  69. property KeyPassword: string read FKeyPassword write FKeyPassword;
  70. property CipherList: string read FCipherList write FCipherList;
  71. property Certificate : TSSLData Index 0 Read GetSSLData Write SetSSLData;
  72. property TrustedCertificate : TSSLData Index 1 Read GetSSLData Write SetSSLData;
  73. property PrivateKey : TSSLData Index 2 Read GetSSLData Write SetSSLData;
  74. property PFX: TSSLData Index 3 Read GetSSLData Write SetSSLData;
  75. property CertCA: TSSLData Index 4 Read GetSSLData Write SetSSLData;
  76. property VerifyPeerCert: Boolean read FVerifyPeerCert Write FVerifyPeerCert;
  77. Property SendHostAsSNI : Boolean Read FSendHostAsSNI Write FSendHostAsSNI;
  78. // In case a certificate must be generated as server, this is the hostname that will be used.
  79. property RemoteHostName : String Read FRemoteHostName Write FRemoteHostName;
  80. property OnVerifyCertificate: TVerifyCertificateEvent read FOnVerifyCertificate write FOnVerifyCertificate;
  81. end;
  82. implementation
  83. { TSocketHandler }
  84. Resourcestring
  85. SErrNoLibraryInit = 'Could not initialize OpenSSL library';
  86. Procedure MaybeInitSSLInterface;
  87. begin
  88. if not IsSSLloaded then
  89. if not InitSSLInterface then
  90. Raise EInOutError.Create(SErrNoLibraryInit);
  91. end;
  92. { TSSLSocketHandler }
  93. function TSSLSocketHandler.GetSSLData(AIndex: Integer): TSSLData;
  94. begin
  95. Result:=FCertData[AIndex];
  96. end;
  97. procedure TSSLSocketHandler.SetSSLData(AIndex: Integer; AValue: TSSLData);
  98. begin
  99. FCertData[AIndex].Assign(AValue);
  100. end;
  101. procedure TSSLSocketHandler.SetSSLLastErrorString(AValue: string);
  102. begin
  103. if FSSLLastErrorString=AValue then Exit;
  104. FSSLLastErrorString:=AValue;
  105. end;
  106. function TSSLSocketHandler.DoVerifyCert: boolean;
  107. begin
  108. Result:=True;
  109. If Assigned(OnVerifyCertificate) then
  110. OnVerifyCertificate(Self,Result);
  111. end;
  112. constructor TSSLSocketHandler.Create;
  113. Var
  114. I : Integer;
  115. begin
  116. inherited Create;
  117. FSendHostAsSNI:=True;
  118. MaybeInitSSLInterface;
  119. FCipherList:='DEFAULT';
  120. For I:=0 to SSLDataCount do
  121. FCertData[i]:=TSSLData.Create;
  122. end;
  123. Destructor TSSLSocketHandler.Destroy;
  124. Var
  125. I : Integer;
  126. begin
  127. FreeAndNil(FSSL);
  128. FreeAndNil(FCTX);
  129. inherited Destroy;
  130. For I:=0 to SSLDataCount do
  131. FreeAndNil(FCertData[i]);
  132. end;
  133. function TSSLSocketHandler.CreateSelfSignedCertificate(Const AHostName: string): Boolean;
  134. Const
  135. OneDay = 60*60*24;
  136. SixtyDays = 60*OneDay;
  137. var
  138. PK : PEVP_PKEY;
  139. X509 : PX509;
  140. RSA : PRSA;
  141. UTC : PASN1_UTCTIME;
  142. SN : PX509_NAME;
  143. B : PBIO;
  144. begin
  145. Result:=False;
  146. PK:=Nil;
  147. X509:=Nil;
  148. try
  149. PK:=EvpPkeynew;
  150. X509:=X509New;
  151. RSA:=RsaGenerateKey(1024,$10001,nil,nil);
  152. EvpPkeyAssign(PK,EVP_PKEY_RSA,RSA);
  153. X509SetVersion(X509,2);
  154. Asn1IntegerSet(X509getSerialNumber(X509),0);
  155. UTC:=Asn1UtctimeNew;
  156. try
  157. X509GmtimeAdj(UTC,-OneDay);
  158. X509SetNotBefore(X509,UTC);
  159. X509GmtimeAdj(UTC,SixtyDays);
  160. X509SetNotAfter(X509,UTC);
  161. finally
  162. Asn1UtctimeFree(UTC);
  163. end;
  164. X509SetPubkey(X509,PK);
  165. SN:=X509GetSubjectName(X509);
  166. X509NameAddEntryByTxt(SN,'C',$1001,'CZ',-1,-1,0);
  167. X509NameAddEntryByTxt(SN,'CN',$1001, AHostName,-1,-1,0);
  168. x509SetIssuerName(X509,SN);
  169. x509Sign(X509,PK,EvpGetDigestByName('SHA1'));
  170. B:=BioNew(BioSMem);
  171. try
  172. i2dX509Bio(B,X509);
  173. Certificate.Value:=BioToString(B);
  174. finally
  175. BioFreeAll(b);
  176. end;
  177. B:=BioNew(BioSMem);
  178. try
  179. i2dPrivatekeyBio(B,PK);
  180. Privatekey.Value:=BioToString(B);
  181. finally
  182. BioFreeAll(b);
  183. end;
  184. finally
  185. X509free(X509);
  186. EvpPkeyFree(PK);
  187. end;
  188. end;
  189. function TSSLSocketHandler.Connect: Boolean;
  190. begin
  191. Result:=Inherited Connect;
  192. if Result and InitContext(False) then
  193. begin
  194. Result:=CheckSSL(FSSL.SetFD(FSocket.Handle));
  195. if Result then
  196. begin
  197. if FSendHostAsSNI and (FSocket is TInetSocket) then
  198. FSSL.Ctrl(SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,PAnsiChar(AnsiString((FSocket as TInetSocket).Host)));
  199. Result:=CheckSSL(FSSL.Connect);
  200. if Result and VerifyPeerCert then
  201. Result:=(FSSL.VerifyResult<>0) or (not DoVerifyCert);
  202. if Result then
  203. FSSLActive:=True;
  204. end;
  205. end;
  206. end;
  207. function TSSLSocketHandler.Close: Boolean;
  208. begin
  209. Result:=Shutdown(False);
  210. end;
  211. Function TSSLSocketHandler.FetchErrorInfo : Boolean;
  212. var
  213. S : AnsiString;
  214. begin
  215. FSSLLastErrorString:='';
  216. FLastError:=ErrGetError;
  217. ErrClearError;
  218. Result:=(FLastError>=1);
  219. if not Result then
  220. begin
  221. S:=StringOfChar(#0,256);
  222. ErrErrorString(FLastError,S,256);
  223. FSSLLastErrorString:=s;
  224. end;
  225. end;
  226. function TSSLSocketHandler.CheckSSL(SSLResult : Integer) : Boolean;
  227. begin
  228. Result:=SSLResult>=1;
  229. if Not Result then
  230. begin
  231. FLastError:=SSLResult;
  232. FetchErrorInfo;
  233. end;
  234. end;
  235. function TSSLSocketHandler.CheckSSL(SSLResult: Pointer): Boolean;
  236. begin
  237. Result:=(SSLResult<>Nil);
  238. if not Result then
  239. Result:=FetchErrorInfo;
  240. end;
  241. function TSSLSocketHandler.DoneContext: Boolean;
  242. begin
  243. FreeAndNil(FSSL);
  244. FreeAndNil(FCTX);
  245. ErrRemoveState(0);
  246. FSSLActive:=False;
  247. Result:=True;
  248. end;
  249. Function HandleSSLPwd(buf : PAnsiChar; len:Integer; flags:Integer; UD : Pointer):Integer; cdecl;
  250. var
  251. Pwd: AnsiString;
  252. H : TSSLSocketHandler;
  253. begin
  254. if Not Assigned(UD) then
  255. PWD:=''
  256. else
  257. begin
  258. H:=TSSLSocketHandler(UD);
  259. Pwd:=H.KeyPassword;
  260. end;
  261. if (len<Length(Pwd)+1) then
  262. SetLength(Pwd,len-1);
  263. pwd:=pwd+#0;
  264. Result:=Length(Pwd);
  265. Move(Pointer(Pwd)^,Buf^,Result);
  266. end;
  267. function TSSLSocketHandler.InitSslKeys: boolean;
  268. begin
  269. Result:=(FCTX<>Nil);
  270. if not Result then
  271. Exit;
  272. if not Certificate.Empty then
  273. Result:=CheckSSL(FCTX.UseCertificate(Certificate));
  274. if Result and not PrivateKey.Empty then
  275. Result:=CheckSSL(FCTX.UsePrivateKey(PrivateKey));
  276. if Result and (CertCA.FileName<>'') then
  277. Result:=CheckSSL(FCTX.LoadVerifyLocations(CertCA.FileName,''));
  278. if Result and not PFX.Empty then
  279. Result:=CheckSSL(FCTX.LoadPFX(PFX,Self.KeyPassword));
  280. end;
  281. function TSSLSocketHandler.InitContext(NeedCertificate:Boolean): Boolean;
  282. Const
  283. VO : Array[Boolean] of Integer = (SSL_VERIFY_NONE,SSL_VERIFY_PEER);
  284. var
  285. s: AnsiString;
  286. begin
  287. Result:=DoneContext;
  288. if Not Result then
  289. Exit;
  290. try
  291. FCTX:=TSSLContext.Create(SSLType);
  292. Except
  293. CheckSSL(Nil);
  294. Result:=False;
  295. Exit;
  296. end;
  297. S:=FCipherList;
  298. FCTX.SetCipherList(S);
  299. FCTX.SetVerify(VO[FVerifypeerCert],Nil);
  300. FCTX.SetDefaultPasswdCb(@HandleSSLPwd);
  301. FCTX.SetDefaultPasswdCbUserdata(self);
  302. If NeedCertificate and Certificate.Empty and PFX.Empty then
  303. if Not CreateSelfSignedCertificate(RemoteHostName) then
  304. begin
  305. DoneContext;
  306. Exit(False);
  307. end;
  308. if Not InitSSLKeys then
  309. begin
  310. DoneContext;
  311. Exit(False);
  312. end;
  313. try
  314. FSSL:=TSSL.Create(FCTX);
  315. Result:=True;
  316. Except
  317. CheckSSL(Nil);
  318. DoneContext;
  319. Result:=False;
  320. end;
  321. end;
  322. function TSSLSocketHandler.Accept: Boolean;
  323. begin
  324. Result:=InitContext(True);
  325. if Result then
  326. begin
  327. Result:=CheckSSL(FSSL.setfd(Socket.Handle));
  328. if Result then
  329. Result:=CheckSSL(FSSL.Accept);
  330. end;
  331. FSSLActive:=Result;
  332. end;
  333. function TSSLSocketHandler.Shutdown(BiDirectional : Boolean): boolean;
  334. var
  335. r : integer;
  336. begin
  337. Result:=assigned(FSsl);
  338. if Result then
  339. If Not BiDirectional then
  340. Result:=CheckSSL(FSSL.Shutdown)
  341. else
  342. begin
  343. r:=FSSL.Shutdown;
  344. if r<>0 then
  345. Result:=CheckSSL(r)
  346. else
  347. begin
  348. Result:=fpShutdown(FSocket.Handle,1)=0;
  349. if Result then
  350. Result:=CheckSSL(FSsl.Shutdown);
  351. end
  352. end;
  353. If Result then
  354. Result:=DoneContext;
  355. end;
  356. function TSSLSocketHandler.Send(Const Buffer; Count: Integer): Integer;
  357. var
  358. e: integer;
  359. begin
  360. FLastError := 0;
  361. FSSLLastErrorString:='';
  362. repeat
  363. Result:=FSsl.Write(@Buffer,Count);
  364. e:=FSsl.GetError(Result);
  365. until Not (e in [SSL_ERROR_WANT_READ,SSL_ERROR_WANT_WRITE]);
  366. if (E=SSL_ERROR_ZERO_RETURN) then
  367. Result:=0
  368. else if (e<>0) then
  369. FLastError:=e;
  370. end;
  371. function TSSLSocketHandler.Recv(Const Buffer; Count: Integer): Integer;
  372. var
  373. e: integer;
  374. begin
  375. FLastError:=0;
  376. FSSLLastErrorString:= '';
  377. repeat
  378. Result:=FSSL.Read(@Buffer ,Count);
  379. e:=FSSL.GetError(Result);
  380. until Not (e in [SSL_ERROR_WANT_READ,SSL_ERROR_WANT_WRITE]);
  381. if (E=SSL_ERROR_ZERO_RETURN) then
  382. Result:=0
  383. else if (e<>0) then
  384. FLastError:=e;
  385. end;
  386. function TSSLSocketHandler.BytesAvailable: Integer;
  387. begin
  388. Result:= FSSL.Pending;
  389. end;
  390. Function TSSLSocketHandler.SSLActive: Boolean;
  391. begin
  392. Result:=FSSLActive;
  393. end;
  394. Function TSSLSocketHandler.SSLLastError: integer;
  395. begin
  396. Result:=FLastError;
  397. end;
  398. end.