123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- {
- This file is part of the Free Component Library (FCL)
- Copyright (c) 1999-2000 by the Free Pascal development team
- SSL support for ssockets
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- unit sslsockets;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, sockets, ssockets, openssl, fpopenssl;
- Const
- SSLDataCount = 4; // 0 based.
- Type
- TVerifyCertificateEvent = Procedure(Sender : TObject; Allow : Boolean) of object;
- { TSSLSocketHandler }
- TSSLSocketHandler = class(TSocketHandler)
- private
- FRemoteHostName: String;
- FSSLLastErrorString: string;
- FCipherList: string;
- FVerifyPeerCert: Boolean;
- FOnVerifyCertificate: TVerifyCertificateEvent;
- FSSLType: TSSLType;
- FKeyPassword: string;
- FUsername: string;
- FPassword: string;
- FCertData : Array[0..4] of TSSLData;
- FSSL: TSSL;
- FCTX : TSSLContext;
- FSSLActive : Boolean;
- FSendHostAsSNI : Boolean;
- function GetSSLData(AIndex: Integer): TSSLData;
- procedure SetSSLData(AIndex: Integer; AValue: TSSLData);
- procedure SetSSLLastErrorString(AValue: string);
- protected
- Function FetchErrorInfo: Boolean;
- function CheckSSL(SSLResult: Integer): Boolean;
- function CheckSSL(SSLResult: Pointer): Boolean;
- function InitContext(NeedCertificate: Boolean): Boolean; virtual;
- function DoneContext: Boolean; virtual;
- function InitSslKeys: boolean;virtual;
- function DoVerifyCert:boolean;
- public
- constructor Create; override;
- Destructor Destroy; override;
- // Socket methods
- function Connect : Boolean; override;
- function Close : Boolean; override;
- function Accept : Boolean; override;
- function Shutdown(BiDirectional : Boolean): boolean; override;
- function Send(Const Buffer; Count: Integer): Integer; override;
- function Recv(Const Buffer; Count: Integer): Integer; override;
- function BytesAvailable: Integer; override;
- Function SSLActive: Boolean;
- function CreateSelfSignedCertificate(Const AHostName: string): Boolean; virtual;
- // Result of last CheckSSL call.
- Function SSLLastError: integer;
- property SSLLastErrorString: string read FSSLLastErrorString write SetSSLLastErrorString;
- published
- property SSLType: TSSLType read FSSLType write FSSLType;
- {:Password for decrypting of encoded certificate or key.}
- property Username: string read FUsername write FUsername;
- property Password: string read FPassword write FPassword;
- property KeyPassword: string read FKeyPassword write FKeyPassword;
- property CipherList: string read FCipherList write FCipherList;
- property Certificate : TSSLData Index 0 Read GetSSLData Write SetSSLData;
- property TrustedCertificate : TSSLData Index 1 Read GetSSLData Write SetSSLData;
- property PrivateKey : TSSLData Index 2 Read GetSSLData Write SetSSLData;
- property PFX: TSSLData Index 3 Read GetSSLData Write SetSSLData;
- property CertCA: TSSLData Index 4 Read GetSSLData Write SetSSLData;
- property VerifyPeerCert: Boolean read FVerifyPeerCert Write FVerifyPeerCert;
- Property SendHostAsSNI : Boolean Read FSendHostAsSNI Write FSendHostAsSNI;
- // In case a certificate must be generated as server, this is the hostname that will be used.
- property RemoteHostName : String Read FRemoteHostName Write FRemoteHostName;
- property OnVerifyCertificate: TVerifyCertificateEvent read FOnVerifyCertificate write FOnVerifyCertificate;
- end;
- implementation
- { TSocketHandler }
- Resourcestring
- SErrNoLibraryInit = 'Could not initialize OpenSSL library';
- Procedure MaybeInitSSLInterface;
- begin
- if not IsSSLloaded then
- if not InitSSLInterface then
- Raise EInOutError.Create(SErrNoLibraryInit);
- end;
- { TSSLSocketHandler }
- function TSSLSocketHandler.GetSSLData(AIndex: Integer): TSSLData;
- begin
- Result:=FCertData[AIndex];
- end;
- procedure TSSLSocketHandler.SetSSLData(AIndex: Integer; AValue: TSSLData);
- begin
- FCertData[AIndex].Assign(AValue);
- end;
- procedure TSSLSocketHandler.SetSSLLastErrorString(AValue: string);
- begin
- if FSSLLastErrorString=AValue then Exit;
- FSSLLastErrorString:=AValue;
- end;
- function TSSLSocketHandler.DoVerifyCert: boolean;
- begin
- Result:=True;
- If Assigned(OnVerifyCertificate) then
- OnVerifyCertificate(Self,Result);
- end;
- constructor TSSLSocketHandler.Create;
- Var
- I : Integer;
- begin
- inherited Create;
- FSendHostAsSNI:=True;
- MaybeInitSSLInterface;
- FCipherList:='DEFAULT';
- For I:=0 to SSLDataCount do
- FCertData[i]:=TSSLData.Create;
- end;
- Destructor TSSLSocketHandler.Destroy;
- Var
- I : Integer;
- begin
- FreeAndNil(FSSL);
- FreeAndNil(FCTX);
- inherited Destroy;
- For I:=0 to SSLDataCount do
- FreeAndNil(FCertData[i]);
- end;
- function TSSLSocketHandler.CreateSelfSignedCertificate(Const AHostName: string): Boolean;
- Const
- OneDay = 60*60*24;
- SixtyDays = 60*OneDay;
- var
- PK : PEVP_PKEY;
- X509 : PX509;
- RSA : PRSA;
- UTC : PASN1_UTCTIME;
- SN : PX509_NAME;
- B : PBIO;
- begin
- Result:=False;
- PK:=Nil;
- X509:=Nil;
- try
- PK:=EvpPkeynew;
- X509:=X509New;
- RSA:=RsaGenerateKey(1024,$10001,nil,nil);
- EvpPkeyAssign(PK,EVP_PKEY_RSA,RSA);
- X509SetVersion(X509,2);
- Asn1IntegerSet(X509getSerialNumber(X509),0);
- UTC:=Asn1UtctimeNew;
- try
- X509GmtimeAdj(UTC,-OneDay);
- X509SetNotBefore(X509,UTC);
- X509GmtimeAdj(UTC,SixtyDays);
- X509SetNotAfter(X509,UTC);
- finally
- Asn1UtctimeFree(UTC);
- end;
- X509SetPubkey(X509,PK);
- SN:=X509GetSubjectName(X509);
- X509NameAddEntryByTxt(SN,'C',$1001,'CZ',-1,-1,0);
- X509NameAddEntryByTxt(SN,'CN',$1001, AHostName,-1,-1,0);
- x509SetIssuerName(X509,SN);
- x509Sign(X509,PK,EvpGetDigestByName('SHA1'));
- B:=BioNew(BioSMem);
- try
- i2dX509Bio(B,X509);
- Certificate.Value:=BioToString(B);
- finally
- BioFreeAll(b);
- end;
- B:=BioNew(BioSMem);
- try
- i2dPrivatekeyBio(B,PK);
- Privatekey.Value:=BioToString(B);
- finally
- BioFreeAll(b);
- end;
- finally
- X509free(X509);
- EvpPkeyFree(PK);
- end;
- end;
- function TSSLSocketHandler.Connect: Boolean;
- begin
- Result:=Inherited Connect;
- if Result and InitContext(False) then
- begin
- Result:=CheckSSL(FSSL.SetFD(FSocket.Handle));
- if Result then
- begin
- if FSendHostAsSNI and (FSocket is TInetSocket) then
- FSSL.Ctrl(SSL_CTRL_SET_TLSEXT_HOSTNAME,TLSEXT_NAMETYPE_host_name,PAnsiChar(AnsiString((FSocket as TInetSocket).Host)));
- Result:=CheckSSL(FSSL.Connect);
- if Result and VerifyPeerCert then
- Result:=(FSSL.VerifyResult<>0) or (not DoVerifyCert);
- if Result then
- FSSLActive:=True;
- end;
- end;
- end;
- function TSSLSocketHandler.Close: Boolean;
- begin
- Result:=Shutdown(False);
- end;
- Function TSSLSocketHandler.FetchErrorInfo : Boolean;
- var
- S : AnsiString;
- begin
- FSSLLastErrorString:='';
- FLastError:=ErrGetError;
- ErrClearError;
- Result:=(FLastError>=1);
- if not Result then
- begin
- S:=StringOfChar(#0,256);
- ErrErrorString(FLastError,S,256);
- FSSLLastErrorString:=s;
- end;
- end;
- function TSSLSocketHandler.CheckSSL(SSLResult : Integer) : Boolean;
- begin
- Result:=SSLResult>=1;
- if Not Result then
- begin
- FLastError:=SSLResult;
- FetchErrorInfo;
- end;
- end;
- function TSSLSocketHandler.CheckSSL(SSLResult: Pointer): Boolean;
- begin
- Result:=(SSLResult<>Nil);
- if not Result then
- Result:=FetchErrorInfo;
- end;
- function TSSLSocketHandler.DoneContext: Boolean;
- begin
- FreeAndNil(FSSL);
- FreeAndNil(FCTX);
- ErrRemoveState(0);
- FSSLActive:=False;
- Result:=True;
- end;
- Function HandleSSLPwd(buf : PAnsiChar; len:Integer; flags:Integer; UD : Pointer):Integer; cdecl;
- var
- Pwd: AnsiString;
- H : TSSLSocketHandler;
- begin
- if Not Assigned(UD) then
- PWD:=''
- else
- begin
- H:=TSSLSocketHandler(UD);
- Pwd:=H.KeyPassword;
- end;
- if (len<Length(Pwd)+1) then
- SetLength(Pwd,len-1);
- pwd:=pwd+#0;
- Result:=Length(Pwd);
- Move(Pointer(Pwd)^,Buf^,Result);
- end;
- function TSSLSocketHandler.InitSslKeys: boolean;
- begin
- Result:=(FCTX<>Nil);
- if not Result then
- Exit;
- if not Certificate.Empty then
- Result:=CheckSSL(FCTX.UseCertificate(Certificate));
- if Result and not PrivateKey.Empty then
- Result:=CheckSSL(FCTX.UsePrivateKey(PrivateKey));
- if Result and (CertCA.FileName<>'') then
- Result:=CheckSSL(FCTX.LoadVerifyLocations(CertCA.FileName,''));
- if Result and not PFX.Empty then
- Result:=CheckSSL(FCTX.LoadPFX(PFX,Self.KeyPassword));
- end;
- function TSSLSocketHandler.InitContext(NeedCertificate:Boolean): Boolean;
- Const
- VO : Array[Boolean] of Integer = (SSL_VERIFY_NONE,SSL_VERIFY_PEER);
- var
- s: AnsiString;
- begin
- Result:=DoneContext;
- if Not Result then
- Exit;
- try
- FCTX:=TSSLContext.Create(SSLType);
- Except
- CheckSSL(Nil);
- Result:=False;
- Exit;
- end;
- S:=FCipherList;
- FCTX.SetCipherList(S);
- FCTX.SetVerify(VO[FVerifypeerCert],Nil);
- FCTX.SetDefaultPasswdCb(@HandleSSLPwd);
- FCTX.SetDefaultPasswdCbUserdata(self);
- If NeedCertificate and Certificate.Empty and PFX.Empty then
- if Not CreateSelfSignedCertificate(RemoteHostName) then
- begin
- DoneContext;
- Exit(False);
- end;
- if Not InitSSLKeys then
- begin
- DoneContext;
- Exit(False);
- end;
- try
- FSSL:=TSSL.Create(FCTX);
- Result:=True;
- Except
- CheckSSL(Nil);
- DoneContext;
- Result:=False;
- end;
- end;
- function TSSLSocketHandler.Accept: Boolean;
- begin
- Result:=InitContext(True);
- if Result then
- begin
- Result:=CheckSSL(FSSL.setfd(Socket.Handle));
- if Result then
- Result:=CheckSSL(FSSL.Accept);
- end;
- FSSLActive:=Result;
- end;
- function TSSLSocketHandler.Shutdown(BiDirectional : Boolean): boolean;
- var
- r : integer;
- begin
- Result:=assigned(FSsl);
- if Result then
- If Not BiDirectional then
- Result:=CheckSSL(FSSL.Shutdown)
- else
- begin
- r:=FSSL.Shutdown;
- if r<>0 then
- Result:=CheckSSL(r)
- else
- begin
- Result:=fpShutdown(FSocket.Handle,1)=0;
- if Result then
- Result:=CheckSSL(FSsl.Shutdown);
- end
- end;
- If Result then
- Result:=DoneContext;
- end;
- function TSSLSocketHandler.Send(Const Buffer; Count: Integer): Integer;
- var
- e: integer;
- begin
- FLastError := 0;
- FSSLLastErrorString:='';
- repeat
- Result:=FSsl.Write(@Buffer,Count);
- e:=FSsl.GetError(Result);
- until Not (e in [SSL_ERROR_WANT_READ,SSL_ERROR_WANT_WRITE]);
- if (E=SSL_ERROR_ZERO_RETURN) then
- Result:=0
- else if (e<>0) then
- FLastError:=e;
- end;
- function TSSLSocketHandler.Recv(Const Buffer; Count: Integer): Integer;
- var
- e: integer;
- begin
- FLastError:=0;
- FSSLLastErrorString:= '';
- repeat
- Result:=FSSL.Read(@Buffer ,Count);
- e:=FSSL.GetError(Result);
- until Not (e in [SSL_ERROR_WANT_READ,SSL_ERROR_WANT_WRITE]);
- if (E=SSL_ERROR_ZERO_RETURN) then
- Result:=0
- else if (e<>0) then
- FLastError:=e;
- end;
- function TSSLSocketHandler.BytesAvailable: Integer;
- begin
- Result:= FSSL.Pending;
- end;
- Function TSSLSocketHandler.SSLActive: Boolean;
- begin
- Result:=FSSLActive;
- end;
- Function TSSLSocketHandler.SSLLastError: integer;
- begin
- Result:=FLastError;
- end;
- end.
|