Repository URL to install this package:
Version:
3.0.0 ▾
|
{
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.