如何在Perl进程之间共享OpenSSL会话?

时间:2017-11-16 22:42:23

标签: perl ssl openssl lwp

我正在使用Perl通过TLS连接到一些(非常)低功耗的硬件设备。第一次握手可能需要大约10-15秒才能完成!重用会话(来自相同的Perl进程)要快得多,但当下一个作业连接到同一设备时,新进程必须建立具有相同延迟的新会话。我想在我的进程之间共享会话缓存,但我遇到了问题(和段错误!)。

我有一个测试脚本(连接到openssl s_server -www)和一个IO::Socket::SSL::Session_Cache包装器,它使用Sereal将缓存对象写入磁盘。尽管在缓存中找到现有会话,它不会重复使用,有时在尝试向缓存添加其他条目时会出现段错误。

use 5.20.1; use warnings;

use LWP::UserAgent;
use IO::Socket::SSL;
# $IO::Socket::SSL::DEBUG = 2;

request_with_new_ua();
request_with_new_ua();
request_with_new_ua();

sub request_with_new_ua {
   say "make request";
   my $ua = LWP::UserAgent->new;
   $ua->ssl_opts(
      verify_hostname => 0,
      SSL_session_cache => Inline::SessionStore->new,
   );
   process_response($ua->get('https://localhost:4433'));
}

sub process_response {
   my $res = shift;
   say "> $_" for grep /Session|Master/, split /\n/, $res->as_string;
}

BEGIN {
   package Inline::SessionStore;

   use 5.20.1; use warnings;
   use Moo;
   use experimental qw(signatures);
   use Sereal::Encoder;
   use Sereal::Decoder;
   use Try::Tiny;
   use Path::Tiny;

   has session_cache => ( is => 'rw' );

   my $encoder = Sereal::Encoder->new;
   my $decoder = Sereal::Decoder->new;
   my $file = path('/tmp/ssl-session-cache');

   sub get_session ($self, $key) {
      say "get session $key";
      my $cache;
      try {
         $cache = $decoder->decode($file->slurp_raw);
         say "got cache from file, ".ref $cache;
      } catch {
         say $_ unless /No such file/;
         $cache = IO::Socket::SSL::Session_Cache->new(128);
         say "made new cache";
      };
      $self->session_cache($cache);

      my $ret = $cache->get_session($key);
      say "found session $ret" if $ret;
      return $ret;
   }

   sub add_session {
      my $self = shift;
      say"add session " . join ' - ', @_;
      my $session = $self->session_cache->add_session(@_);

      $file->spew_raw($encoder->encode($self->session_cache));
      return $session;
   }

   sub del_session {
      my $self = shift;
      say "del session " . join ' - ', @_;
      $self->session_cache->del_session(@_);

      $file->spew_raw($encoder->encode($self->session_cache));
   }

   1;
}

输出:

 rm -f /tmp/ssl-session-cache && perl wes.swp/workbench.pl
make request
get session localhost:4433
made new cache
add session localhost:4433 - 23864624
> SSL-Session:
>     Session-ID: 
>     Session-ID-ctx: 01000000
>     Master-Key: DDF335492BFE2A7BA7674A656E72005865859D89249D597302F338D01C5776E2C94B61E6BCBED6114DFDA5AAEECD22EA
make request
get session localhost:4433
got cache from file, IO::Socket::SSL::Session_Cache
found session 23864624
add session localhost:4433 - 23864624  # trying to re-add the session??
> SSL-Session:
>     Session-ID: 
>     Session-ID-ctx: 01000000
>     Master-Key: 4FE17B7FE9B4DE0A711C659FC333F686AD41840740B9D10E67A972D5A27D1720F0470329DA63DE65C1B023A1E2F0CC89
make request
get session localhost:4433
got cache from file, IO::Socket::SSL::Session_Cache
found session 23864624
add session localhost:4433 - 23864624
> SSL-Session:
>     Session-ID: 
>     Session-ID-ctx: 01000000
>     Master-Key: C76C52E5ECC13B0BB4FA887B381779B6F686A73DDFBEA06B33336537EC6AE39290370C07505BCD8B552CA874CD6E4089

我觉得我接近让它上班,但我错过了一些东西。

1 个答案:

答案 0 :(得分:3)

我不认为有一种方法可以在进程之间使用IO :: Socket :: SSL / Net :: SSLeay或Crypt :: SSLeay(它们是LWP的新旧SSL后端)。

您尝试在代码中使用的会话缓存引用了OpenSSL库内部的SESSION对象。在Perl级别序列化缓存不会从OpenSSL库内部序列化部件,而只会包含指向内部结构的指针。由于这些指针仅对进程的当前状态有效,因此在不同的进程或进程状态内再次反序列化将因此导致在最佳情况下指向无处的悬空指针或在最坏情况下指向某些其他数据,因此将导致在分段错误或内部数据损坏。

在Net :: SSLeay中,有i2d_SSL_SESSION和d2i_SSL_SESSION函数,理论上可用于正确序列化SESSION对象。但我怀疑在当前的实施中是否有用。