SSL_SESSION_set1_ticket ?

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

SSL_SESSION_set1_ticket ?

Jeremy Harris
Why is there not an SSL_SESSION_set1_ticket() ?

Having to store an entire ASN.1-coded session in a DB, at
some 1250 byte versus 160 for the ticket is suboptimal.


This is for client-side TLS1.2 resumption, when the clients
are separate processes and time-separated.

You can get ticket blobs via
SSL_SESSION_get0_ticket(SSL_get_session(ssl)).

You can get/set the entire session via i2d_SSL_SESSION(sess, ),
d2i_SSL_session(&sess, string, ), SSL_set_session(ssl, sess).

If it were possible to use an SSL_SESSION_set1_ticket,
what else would need to be set in the session?
--
Thanks,
  Jeremy
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni
A resumed session holds not just the ticket, but also the server
certificate, so that one examine the certificate and its saved
verification status, ...  And of course you need not just the
ticket, but also the master key (in the session object).

> On Mar 31, 2019, at 3:56 PM, Jeremy Harris <[hidden email]> wrote:
>
> Having to store an entire ASN.1-coded session in a DB, at
> some 1250 byte versus 160 for the ticket is suboptimal.
>
> This is for client-side TLS1.2 resumption, when the clients
> are separate processes and time-separated.

OpenSSL promises more state at the end of session resumption, and
so the necessary state is carried along.  For an MTA the size of
saved sessions is not a substantial barrier.

--
        Viktor.

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
Thanks for the explanation.  Next, serialise/deseralise
of the session is failing.  Test code:

  {
  SSL_SESSION * ss = SSL_get_session(ssl);

  uschar * sess_asn1;
  int len;

  len = i2d_SSL_SESSION(ss, &sess_asn1);

{
SSL_SESSION * ss = NULL;
if (!(d2i_SSL_SESSION(&ss, CUSS &sess_asn1, (long)len)))
  {
  DEBUG(D_tls)
    {
    ERR_error_string_n(ERR_get_error(),
      ssl_errstring, sizeof(ssl_errstring));
    debug_printf("decoding session: %s\n", ssl_errstring);
    }
  }
}


gets me:
"decoding session: error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag"

--
Cheers,
  Jeremy
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni


> On Apr 1, 2019, at 10:01 AM, Jeremy Harris <[hidden email]> wrote:
>
> Thanks for the explanation.  Next, serialise/deseralise
> of the session is failing.  Test code:
>
>  {
>  SSL_SESSION * ss = SSL_get_session(ssl);
>
>  uschar * sess_asn1;
>  int len;
>
>  len = i2d_SSL_SESSION(ss, &sess_asn1);

This is incorrect use of the api.  You need to provide a NULL
buffer, obtain the length, then call again, after allocating
a buffer of the requisite size.  Here's an example from the
DANE code in Postfix (likely similar code already in Exim):

        len = i2d_X509(cert, NULL);
        buf2 = buf = (unsigned char *) mymalloc(len);
        i2d_X509(cert, &buf2);

Note that i2d updates its second argument to point to the end of
the buffer just written, which supports append operations, but
means you also need a pointer to the original buffer, hence
the "buf2 = buf = ...".  The serialized data is sandwiched
between "buf" (start) and "buf2" (end).

--
--
        Viktor.

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
On 02/04/2019 00:31, Viktor Dukhovni wrote:
>>  len = i2d_SSL_SESSION(ss, &sess_asn1);
>
> This is incorrect use of the api.  You need to provide a NULL
> buffer, obtain the length, then call again, after allocating
> a buffer of the requisite size.

Thanks (again).  Perhaps a note of this in the manpage
would be good?  And any other i2d_* pages?
--
Cheers,
  Jeremy

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni
On Tue, Apr 02, 2019 at 10:54:43AM +0100, Jeremy Harris wrote:

> > This is incorrect use of the api.  You need to provide a NULL
> > buffer, obtain the length, then call again, after allocating
> > a buffer of the requisite size.
>
> Thanks (again).  Perhaps a note of this in the manpage
> would be good?  And any other i2d_* pages?

The 1.1.0 (and later) documentation reads:

    https://www.openssl.org/docs/man1.1.0/man3/d2i_SSL_SESSION.html

    These functions decode and encode an SSL_SESSION object. For
    encoding details see d2i_X509(3).

which leads to:

    https://www.openssl.org/docs/man1.1.0/man3/d2i_X509.html

    i2d_TYPE() encodes the structure pointed to by a into DER format.
    If ppout is not NULL, it writes the DER encoded data to the
    buffer at *ppout, and increments it to point after the data
    just written. If the return value is negative an error occurred,
    otherwise it returns the length of the encoded data.

    If *ppout is NULL memory will be allocated for a buffer and the
    encoded data written to it. In this case *ppout is not incremented
    and it points to the start of the data just written.

The "1.0.2" version (after running the word-wrapped HTML code
example through "indent") reads:

    https://www.openssl.org/docs/man1.0.2/man3/d2i_SSL_SESSION.html

    When using i2d_SSL_SESSION(), the memory location pointed to
    by pp must be large enough to hold the binary representation
    of the session. There is no known limit on the size of the
    created ASN1 representation, so the necessary amount of space
    should be obtained by first calling i2d_SSL_SESSION() with
    pp=NULL, and obtain the size needed, then allocate the memory
    and call i2d_SSL_SESSION() again. Note that this will advance
    the value contained in *pp so it is necessary to save a copy
    of the original allocation. For example:

        int i, j;
        char       *p, *temp;

        i = i2d_SSL_SESSION(sess, NULL);
        p = temp = malloc(i);
        j = i2d_SSL_SESSION(sess, &temp);
        assert(i == j);
        assert(p + i == temp);

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni
In reply to this post by Viktor Dukhovni
> On Apr 2, 2019, at 11:17 AM, Jeremy Harris <[hidden email]> wrote:
>
> If I understand right from rfc5077 the next record from the server after
> the server-hello should have been an empty session_ticket, if it was
> going to accept the resumed session.  But it goes on to a full handshake
> instead.
>
> Is there any way of finding out what it didn't like?

Does the server have a temporally stable ticket decryption key?
Is this Exim?  Is the server's SSL_CTX persistent and shared
across multiple connections?  IIRC Exim has a completely fresh
SSL stack initialized after fork for every client...

--
        Viktor.

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
On 02/04/2019 17:03, Viktor Dukhovni wrote:
> Does the server have a temporally stable ticket decryption key?
> Is this Exim?  Is the server's SSL_CTX persistent and shared
> across multiple connections?

Ah, right.  Unlike GnuTLS, the STEK is tied to the SSL_CTX and,
as you say, Exim initialises that fresh per connection.
Rearchitecting that is more effort than it's worth spending
on TLS 1.2, I think.
--
Thanks,
  Jeremy
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

OpenSSL - User mailing list
On 03/04/2019 22:16, Jeremy Harris wrote:
> On 02/04/2019 17:03, Viktor Dukhovni wrote:
>> Does the server have a temporally stable ticket decryption key?
>> Is this Exim?  Is the server's SSL_CTX persistent and shared
>> across multiple connections?
> Ah, right.  Unlike GnuTLS, the STEK is tied to the SSL_CTX and,
> as you say, Exim initialises that fresh per connection.
> Rearchitecting that is more effort than it's worth spending
> on TLS 1.2, I think.

As an Exim user (can already be seen in my mail headers), I always
wondered about the weird way that Exim (according to the docs/spec)
tries to reinit TLS for each message on a connection.

It seemed very much contrary to protocol, unlike the simple
approach of running TLS in one process, piping the plaintext
(E)SMTP stream to/from a succession of message processing processes,
which can be reforked without breaking the stream and without
ability to steal TLS keys through any security vulnerabilities.

When a non-TLS ESMTP process sends or receives the STARTTLS
command, it can exit with a magic exit code causing the non-TLS
parent process to fork a TLS process and spawn fresh ESMTP
processes.  Alternatively non-TLS ESMTP processes could run
through a dummy TLS process that can be instructed out-of-band
to start TLS negotiation at the right time.

This (could provide better batching of connection overhead than
session tickets, as it saves even the TCP SYN packets while
lowering load on the remote servers and all intervening
stateful firewalls.

Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S.  https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark.  Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni
In reply to this post by Jeremy Harris


> On Apr 3, 2019, at 4:16 PM, Jeremy Harris <[hidden email]> wrote:
>
>> Does the server have a temporally stable ticket decryption key?
>> Is this Exim?  Is the server's SSL_CTX persistent and shared
>> across multiple connections?
>
> Ah, right.  Unlike GnuTLS, the STEK is tied to the SSL_CTX and,
> as you say, Exim initialises that fresh per connection.
> Rearchitecting that is more effort than it's worth spending
> on TLS 1.2, I think.

Well, the *default* STEK is in the SSL_CTX, but that is not a
requirement, and you should use the default STEK, since it is
not automatically rolled over.

Postfix instantiates the ticket management callbacks, which
are registered per-ctx, but the associated key material is
then wherever the application (e.g. Postfix) decides to keep
them.

So you, and should, manage STEKs separately from the SSL_CTX,
and as appropriate tie the same keys to any appropriate SSL_CTX
by instantiating the appropriate callbacks.

The one thing to be mindful of is that if you have different
TLS policies across different SSL_CTX objects, they should
probably not share keys, otherwise it *may* be possible for
sessions to be established against a weak policy and then
inappropriately resumed against a service with a stronger
policy.  The solution is to assign different session id
contexts to SSL_CTX's that should not allow cross-resumption.

The SSL_CTX_set_session_id_context(3) allows you to specify
such a session id context.  I just looked at the documentation
of SSL_CTX_set_session_id_context(3), and is rather poor.
You just need to know that the context is binary data up
a maximum length you must not exceed, and the signature
of the function.  The description is largely wrong. :-(

--
        Viktor.

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni
On Wed, Apr 03, 2019 at 06:09:59PM -0400, Viktor Dukhovni wrote:

> > Ah, right.  Unlike GnuTLS, the STEK is tied to the SSL_CTX and,
> > as you say, Exim initialises that fresh per connection.
> > Rearchitecting that is more effort than it's worth spending
> > on TLS 1.2, I think.
>
> Well, the *default* STEK is in the SSL_CTX, but that is not a
> requirement, and you should use the default STEK, since it is
> not automatically rolled over.

[ Correction: ... should *not* use the default STEK, ... ]

For an example ticket callback implementation, see:

    https://github.com/vdukhovni/postfix/blob/master/postfix/src/tls/tls_server.c#L294-L337

On line 315:

    https://github.com/vdukhovni/postfix/blob/master/postfix/src/tls/tls_server.c#L315

either the matching keyset (current active for creating a new ticket,
either active or previous when decrypting an existing ticket) is
selected, and the requested HMAC_CTX and EVP_CIPHER_CTX structures
are initialized appropriately.  Keyset, because the HMAC and AES
keys are separate.  The ticket encryption algorithm chosen by Postfix
defaults to aes-256-cbc.  OpenSSL does not support AEAD for ticket
encryption.

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
In reply to this post by OpenSSL - User mailing list
On 03/04/2019 22:13, Jakob Bohm via openssl-users wrote:
> As an Exim user (can already be seen in my mail headers), I always
> wondered about the weird way that Exim (according to the docs/spec)
> tries to reinit TLS for each message on a connection.
>
> It seemed very much contrary to protocol, unlike the simple
> approach of running TLS in one process, piping the plaintext
> (E)SMTP stream to/from a succession of message processing processes,
> which can be reforked without breaking the stream and without
> ability to steal TLS keys through any security vulnerabilities.

http://exim.org/exim-html-current/doc/html/spec_html/ch-encrypted_smtp_connections_using_tlsssl.html#SECTmulmessam

"for sending using TLS Exim starts an additional proxy process for
handling the encryption, piping the unencrypted data stream from and to
the delivery processes"

--
Cheers,
  Jeremy

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
In reply to this post by Viktor Dukhovni
On 04/04/2019 16:16, Viktor Dukhovni wrote:
>> Well, the *default* STEK is in the SSL_CTX, but that is not a
>> requirement, and you should use the default STEK, since it is
>> not automatically rolled over.
>
> [ Correction: ... should *not* use the default STEK, ... ]

So I have the server side mostly running, with rollover and
overlap... but in the client, when a session is succesfully
resumed but past the overlap period, and the server issues
a new ticket - the client SSL_SESSION is apparently unchanged
(at least, the ASN.1 dump of it is identical to that loaded
for resumption prior to handshake).

How is an overlapped key update supposed to be handled by
a client?
--
Thanks,
  Jeremy
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Viktor Dukhovni
On Tue, Apr 09, 2019 at 11:52:59PM +0100, Jeremy Harris wrote:

> On 04/04/2019 16:16, Viktor Dukhovni wrote:
> >> Well, the *default* STEK is in the SSL_CTX, but that is not a
> >> requirement, and you should use the default STEK, since it is
> >> not automatically rolled over.
> >
> > [ Correction: ... should *not* use the default STEK, ... ]
>
> So I have the server side mostly running, with rollover and
> overlap... but in the client, when a session is succesfully
> resumed but past the overlap period, and the server issues
> a new ticket - the client SSL_SESSION is apparently unchanged
> (at least, the ASN.1 dump of it is identical to that loaded
> for resumption prior to handshake).
>
> How is an overlapped key update supposed to be handled by
> a client?

With TLS 1.0, 1.1 and 1.2, the the (always new IIRC) session object
associated with the connection object at the completion of each
handshake, will contain any fresh tickets issued by the server.

With TLS 1.3, the session tickets arrive post-handshake, and
so saving the session exactly when the handshake completes,
may not yet result in fresh tickets.

In Postfix, we implement the new session callbacks and then don't
care when the tickets arrive.  Whenever they do arrive the callback
happens, and the client session cache is updated at that point.

    https://github.com/vdukhovni/postfix/blob/master/postfix/src/tls/tls_client.c#L531-L556

The "external" (to OpenSSL) session cache implemented in Postfix,
has its own timeouts, and is periodically GC'd in the tlsmgr(8)
process discarding expired sessions.  There is one session per
lookup key, which captures not only the nexthop destination, endpoint
IP, peer HELO name (for split caches behind load-balancers), ...
but also a cryptographic hash of the TLS policy settings to avoid
accidental misuse.

For an in-memory cache, you could also bound the cache size, and
implement an LRU retired policy.  A priority search queue library
can provide the right abstraction.

--
        Viktor.
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
On 10/04/2019 01:25, Viktor Dukhovni wrote:
> With TLS 1.0, 1.1 and 1.2, the the (always new IIRC) session object
> associated with the connection object at the completion of each
> handshake, will contain any fresh tickets issued by the server.

That does not match my observation.
--
Cheers,
  Jeremy

Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Hubert Kario
On Wednesday, 10 April 2019 12:05:21 CEST Jeremy Harris wrote:
> On 10/04/2019 01:25, Viktor Dukhovni wrote:
> > With TLS 1.0, 1.1 and 1.2, the the (always new IIRC) session object
> > associated with the connection object at the completion of each
> > handshake, will contain any fresh tickets issued by the server.
>
> That does not match my observation.

that assumes that the server sends tickets in the first place... but the point
stands, the TLS 1.2 server cannot provide a session ticket to the client after
the handshake finished (client received server's Finished message), same for
even older protocols

--
Regards,
Hubert Kario
Senior Quality Engineer, QE BaseOS Security team
Web: www.cz.redhat.com
Red Hat Czech s.r.o., Purkyňova 115, 612 00  Brno, Czech Republic

signature.asc (849 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: SSL_SESSION_set1_ticket ?

Jeremy Harris
On 10/04/2019 11:15, Hubert Kario wrote:

> On Wednesday, 10 April 2019 12:05:21 CEST Jeremy Harris wrote:
>> On 10/04/2019 01:25, Viktor Dukhovni wrote:
>>> With TLS 1.0, 1.1 and 1.2, the the (always new IIRC) session object
>>> associated with the connection object at the completion of each
>>> handshake, will contain any fresh tickets issued by the server.
>>
>> That does not match my observation.
>
> that assumes that the server sends tickets in the first place... but the point
> stands, the TLS 1.2 server cannot provide a session ticket to the client after
> the handshake finished (client received server's Finished message), same for
> even older protocols

I'm not saying the new ticket arrived after the handshake.  I can
see the notification of it arriving during the handshake.  Yet
the session dumped via i2d... after the handshake is bitwise identical
to that given to d2i... , SSL_set_session before the handshake.
--
Cheers,
  Jeremy