[Synapse] Issues with TTCPBlockSocket.DoSSLConnect and OpenSSL. lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- guest60499 · 2017-01-30 20:21 Based on testing it appears Synapse doesn't complete the handshaking. The listener is started as follows: Code: [Select]socat openssl-listen :2000,reuseaddr,cert=../keys/id_rsa.pem,cafile=../keys/id_rsa.crt stdio It's possible to use socat to connect back to myself. TTCPBlockSocket will connect and initiate a connection but will not proceed further than that. Attempting to connect to a website via HTTPS doesn't fail, it seems like the connection just stops before it is fully connected. Attempting to connect to a machine's SSH server will give you a version string. Sending anything will cause the server to fail with "unknown protocol." It seems like Synapse doesn't provide a way to add a certificate file to the trust database, which is why I attempted to connect to a website. I can't find out what the error code means as LastErrorDesc is empty. The code was implemented referring to the following: 1, 2, 3. I also searched the forums but didn't come up with much. Code: [Select]Unable to connect with SSL. -2: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit form; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,  ssl_openssl, blcksock; type  TTCPClient = class(TThread)  private    FHost: String;    FPort: Integer;    Socket: TTCPBlockSocket;  protected    procedure Execute; override;    procedure SocketStatus(Sender: TObject;                         Reason: THookSocketReason;                           const Value: AnsiString);  public    constructor Create(CreateSuspended: Boolean;                       const StackSize: SizeUInt=DefaultStackSize);    property Host: String read FHost write FHost;    property Port: Integer read FPort write FPort;  end;   { TMainForm }   TMainForm = class(TForm)    procedure FormCreate(Sender: TObject);  private    TCPClient: TTCPClient;  public  end; var  MainForm: TMainForm; implementation {$R *.lfm} { TTCPClient } procedure TTCPClient.Execute;label  ExitLoop;var  Line: String;begin  Socket.Connect(FHost, IntToStr(FPort));  Socket.SSLDoConnect;   if Socket.LastError <> 0 then  begin     WriteLn('Unable to connect with SSL.');     WriteLn(Socket.LastError, ': ', Socket.LastErrorDesc);  end;   repeat    while Socket.CanRead(1000) do    begin      Line := Socket.RecvString(1);    if Line = '' then         goto ExitLoop;      WriteLn(Line);    end;  until False; ExitLoop:  WriteLn('Done.');end; procedure TTCPClient.SocketStatus(Sender: TObject;                                Reason: THookSocketReason;                                  const Value: AnsiString);var  Status: String;begin  case Reason of    HR_ResolvingBegin:      Status := 'ResolvingBegin';    HR_ResolvingEnd:      Status := 'ResolvingEnd';    HR_SocketCreate:    Status := 'SocketCreate';    HR_SocketClose:      Status := 'SocketClose';    HR_Connect:      Status := 'Connect';    HR_CanRead:      Status := 'CanRead';    HR_CanWrite:      Status := 'CanWrite';    HR_ReadCount:      Status := 'ReadCount';    HR_WriteCount:      Status := 'WriteCount';    HR_Wait:      Status := 'Wait';    HR_Error:      Status := 'Error';  end;end; constructor TTCPClient.Create(CreateSuspended: Boolean;                            const StackSize: SizeUInt=DefaultStackSize);begin  inherited Create(CreateSuspended, StackSize);  Socket := TTCPBlockSocket.CreateWithSSL(TSSLOpenSSL);   Socket.OnStatus := @SocketStatus;  Socket.ConvertLineEnd := True;end; { TMainForm } procedure TMainForm.FormCreate(Sender: TObject);begin  TCPClient := TTCPClient.Create(True);  TCPClient.Host := 'localhost';  TCPClient.Port := 2000;  TCPClient.Start;end; end. ---------------------------------------------------------------------- rvk · 2017-01-30 22:35 Just to make sure everything is in the right place... does the following code work for you: One button and memo on the form with openssl-libraries in place: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses  ssl_openssl, blcksock; procedure TForm1.Button1Click(Sender: TObject);var  Socket: TTCPBlockSocket;  HTTPHeader: String;begin  Socket := TTCPBlockSocket.Create;  try    Socket.Connect('www.microsoft.com', '443');    Socket.SSLDoConnect;  if Socket.LastError = 0 then    begin      HTTPHeader :=        'GET /nl-nl/ HTTP/1.0' + CRLF +        'Host: www.microsoft.com:443' + CRLF +        'Keep-Alive: 300' + CRLF +        'Connection: keep-alive' + CRLF + CRLF;      Socket.SendString(HTTPHeader);      Memo1.Lines.Add(Socket.RecvBufferStr(4096, 1000));      Memo1.Lines.Add('');      Memo1.Lines.Add('Library used: ' + Socket.SSL.LibName);      Memo1.Lines.Add('Version: ' + Socket.SSL.LibVersion);    end;  finally    Socket.Free;  end;end; ---------------------------------------------------------------------- guest60499 · 2017-01-31 18:10 Unfortunately I receive no output. It just occurred to me to ensure the shared object file was in the project directory, as they might not be found in the LDPATH. One of the reasons is that Linux distributions tend to append numerical prefixes to allow installation of multiple system libraries. Reading unit ssl_openssl pointed me towards unit ssl_openssl_lib, which specifies the names of the libraries: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  {$IFNDEF MSWINDOWS}    {$IFDEF DARWIN}    DLLSSLName: string = 'libssl.dylib';    DLLUtilName: string = 'libcrypto.dylib';    {$ELSE}     {$IFDEF OS2}      {$IFDEF OS2GCC}    DLLSSLName: string = 'kssl.dll';    DLLUtilName: string = 'kcrypto.dll';      {$ELSE OS2GCC}    DLLSSLName: string = 'ssl.dll';    DLLUtilName: string = 'crypto.dll';      {$ENDIF OS2GCC}     {$ELSE OS2}    DLLSSLName: string = 'libssl.so';    DLLUtilName: string = 'libcrypto.so';     {$ENDIF OS2}    {$ENDIF}  {$ELSE}  DLLSSLName: string = 'ssleay32.dll';  DLLSSLName2: string = 'libssl32.dll';  DLLUtilName: string = 'libeay32.dll';  {$ENDIF}{$ENDIF}  Having compiled the temporary project, the block with the names "libssl.so" and "libcrypto.so" is highlighted. I located the system libraries and copied them into the project folder and compiled and ran the project. As mentioned, nothing interesting happened. ---------------------------------------------------------------------- rvk · 2017-01-31 18:35 Code: [Select]DLLSSLName = 'libssl.so'; DLLUtilName = 'libcrypto.so'; Yeah, that's why I let you run that simple example. Those libraries need to be found. So you just have a openssl problem. Did you copy the exact libssl.so or still libssl1.0.0.so or something? libssl.so in your project directory doesn't work. See edit below. Normally you create a symbolic link with the name libssl.so to the real libssl.so file. Like: Code: [Select]# ls /usr/lib/i386-linux-gnu/libssl*.so lrwxrwxrwx 1 root root 15 Jan 27 01:19 /usr/lib/i386-linux- gnu/libssl.so -> libssl.so.1.0.0 # cat /etc/ld.so.conf.d/i386-linux-gnu.conf # Multiarch support /lib/i386-linux-gnu /usr/lib/i386-linux-gnu /lib/i586-linux-gnu /usr/lib/i586-linux-gnuSo my guess is you didn't install libsslx.y.z and libssl-dev correctly. i.e. sudo apt-get install libssl1.0.0 libssl-dev And where did you get the files? Are you sure you took the 32 bit ones if you compile your project in 32 bit (and 64 bit otherwise)? What does the Socket.SSL.LibName and Socket.SSL.LibVersion give you? Is it blank (no openssl found) or does it show your libssl.so version? Edit: O, and B.T.W. Linux doesn't check your project directory for the library. See: http://wiki.freepascal.org/Lazarus/FPC_Libraries QuoteLinux A dynamic library filename always has the form 'lib'+packagename+'.so'+version. For example: libz.so.1 and libz.so.1.2.2. Linux searches a library in this order: first in the paths of the environment variable LD_LIBRARY_PATHthen in /libthen /usr/libfinally the paths of /etc/ld.so.confUsing ldconfig after copying your library file into a lib directory may help with caching issues. To share memory (GetMem/FreeMem, strings) with other libraries (not written in FPC) under Linux, you should use the unit cmem. This unit must be added as the very first unit in the uses section of the project main source file (typically .lpr), so that its initialization section is called before any other unit can allocate memory. So you need to create a symbolic linked file libssl.so in one of these directories to you libsslx.y.z.so version libssl. And then run ldconfig. After that loading the library should work. ---------------------------------------------------------------------- guest60499 · 2017-01-31 20:03 Thanks for the info rvk, it turns out I needed libssl-dev (under Debian). I'm glad to know that FreePascal does the sane thing when checking for libraries. Not that I assumed it didn't, necessarily, I was just very confused by the behavior... Very grateful, thanks! EDIT: As a note to anyone reading, setting CertCAFile to the path of the server's .crt will allow the verification of a self-signed certificate. For use with a socat server you will need to set verify=0 as I don't think Synapse supports offering a key as part of the client handshake.