Нарушение доступа Indy / Libssl32 в SSL_accept при остановке TIdTCPServer - PullRequest
0 голосов
/ 03 июля 2018

Я использую Delphi 10.1 Update 2 и Indy 10.6.2.5341.

Мы наблюдаем нарушения прав доступа в SSL_accept. Это происходит, если TIdTCPServer настроен с использованием SSL, и существует открытое соединение, которое еще не согласовало TLS, если TIdTCPServer остановлен.

Это похоже на проблему в Libssl32 или Indy. Это может быть просто воспроизведено с помощью следующего кода и Putty с использованием соединения RAW. Кто-нибудь знает решение (или обходной путь) для предотвращения этих сбоев?

procedure TSslCrash.HandlerOnExecute(AContext: TIdContext);
begin
  //
end;

procedure TSslCrash.HandlerOnConnect(AContext: TIdContext);
begin
  TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := False;
end;

procedure TSslCrash.ButtonStartClick(Sender: TObject);
begin
  LServer := TIdTCPServer.Create;
  LIOHandler := TIdServerIOHandlerSSLOpenSSL.Create;

  LIOHandler.SSLOptions.Mode := sslmServer;
  LIOHandler.SSLOptions.Method := sslvTLSv1_2;
  LIOHandler.SSLOptions.VerifyMode := [];
  LIOHandler.SSLOptions.VerifyDepth := 0;
  LIOHandler.SSLOptions.CertFile := 'localhost.crt';
  LIOHandler.SSLOptions.RootCertFile := 'localhost.crt';
  LIOHandler.SSLOptions.KeyFile := 'localhost.key';

  LServer.Bindings.Add.Port := 10000;
  LServer.IOHandler := LIOHandler;
  LServer.OnExecute := HandlerOnExecute;
  LServer.OnConnect := HandlerOnConnect;
  LServer.Active := True;

  //Now open a RAW connection with Putty on port 10000 and keep it open
end;

procedure TSslCrash.ButtonStopClick(Sender: TObject);
begin
  if Assigned(LServer) then begin
    LServer.Active := False;  //This causes an AV in TIdSSLSocket.Accept

    FreeAndNil(LIOHandler);
    FreeAndNil(LServer);
  end;
end;

1 Ответ

0 голосов
/ 03 июля 2018

Когда Putty подключен в режиме Raw, рукопожатие SSL / TLS не выполняется, поэтому SSL_accept() застревает в ожидании запроса на подтверждение связи, который никогда не приходит.

Когда TIdTCPServer деактивируется, он отключает активные соединения с сокетами, не выполняя любые операции блокирования сокетов, выполняемые в других потоках. В случае SSL_accept() это должно разблокировать его, чтобы он мог выйти с кодом ошибки, который TIdSSLSocket.Accept() может затем обнаружить и обернуть в повышенное исключение (EIdOSSLUnderlyingCryptoError, EIdOSSLAcceptError, EIdSocketError и т. Д. В зависимости от природа кода ошибки) в контексте клиентского потока, ожидающего завершения рукопожатия.

Однако, когда TIdTCPServer отключает соединение с сокетом во время деактивации, вызывается TIdTCPConnection.Disconnect(), который вызывает TIdIOHandler.Close(), который TIdSSLIOhandlerSocketOpenSSL переопределил для освобождения своего внутреннего объекта TIdSSLSocket - того же самого объекта, который вызывает SSL_accept(). Таким образом, вполне вероятно, что базовый объект OpenSSL SSL освобождается в TIdSSLSocket.Destroy() (который вызывает SSL_shutdown() и SSL_free()) в контексте деактивирующего потока, при этом все еще активно используется в TIdSSLObject.Accept() (который вызывает SSL_accept()) в контексте клиентского потока, что приводит к нарушению прав доступа.

Ничего не поделаешь, не изменив исходный код Indy. Например, возможно, замените TIdCustomTCPServer.DoTerminateContext() на AContext.Binding.CloseSocket() вместо AContext.Connection.Disconnect(False), чтобы сам IOHandler не был закрыт, а только нижележащим сокетом (аналогично тому, что TIdCustomTCPServer.StopListening() делает при завершении прослушивания потоков accept()).

Я открыл для вас билет в трекере Indy:

# 218: нарушение прав доступа в SSL_accept () при деактивации TIdTCPServer

...