Update early data API for writing to unauthenticated clients

Change the early data API so that the server must use
SSL_write_early_data() to write to an unauthenticated client.

Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2737)
This commit is contained in:
Matt Caswell 2017-03-02 15:49:33 +00:00
parent 0665b4edae
commit 09f2887482
4 changed files with 53 additions and 44 deletions

View File

@ -34,7 +34,9 @@ These functions are used to send and recieve early data. Early data can be sent
by the client immediately after its initial ClientHello without having to wait
for the server to complete the handshake. Early data can only be sent if a
session has previously been established with the server, and the server is known
to support it.
to support it. Additionally these functions can be used to send data from the
server to the client when the client has not yet completed the authentication
stage of the handshake.
Early data has weaker security properties than other data sent over an SSL/TLS
connection. In particular the data is not forward secret and the server has no
@ -51,10 +53,6 @@ unauthenticated at this point.
A server or client can determine whether the full handshake has been completed
or not by calling L<SSL_is_init_finished(3)>.
[[TODO(TLS1.3): The server uses SSL_write_ex()/SSL_write() to send data to an
unauthenticated client. Should we create a separate function for this to avoid
accidents??]]
On the client side the function SSL_SESSION_get_max_early_data() can be used to
determine whether a session established with a server can be used to send early
data. If the session cannot be used then this function will return 0. Otherwise
@ -67,14 +65,15 @@ information on how to write bytes to the underlying connection, and how to
handle any errors that may arise. This page will detail the differences between
SSL_write_early_data() and L<SSL_write_ex(3)>.
SSL_write_early_data() must be the first IO function called on a new connection,
i.e. it must occur before any calls to L<SSL_write_ex(3)>, L<SSL_read_ex(3)>,
L<SSL_connect(3)>, L<SSL_do_handshake(3)> or other similar functions. It may be
called multiple times to stream data to the server, but the total number of
bytes written must not exceed the value returned from
SSL_SESSION_get_max_early_data(). Once the initial SSL_write_early_data() call
has completed successfully the client may interleave calls to L<SSL_read_ex(3)>
and L<SSL_read(3)> with calls to SSL_write_early_data() as required.
When called by a client, SSL_write_early_data() must be the first IO function
called on a new connection, i.e. it must occur before any calls to
L<SSL_write_ex(3)>, L<SSL_read_ex(3)>, L<SSL_connect(3)>, L<SSL_do_handshake(3)>
or other similar functions. It may be called multiple times to stream data to
the server, but the total number of bytes written must not exceed the value
returned from SSL_SESSION_get_max_early_data(). Once the initial
SSL_write_early_data() call has completed successfully the client may interleave
calls to L<SSL_read_ex(3)> and L<SSL_read(3)> with calls to
SSL_write_early_data() as required.
If SSL_write_early_data() fails you should call L<SSL_get_error(3)> to determine
the correct course of action, as for L<SSL_write_ex(3)>.
@ -85,8 +84,6 @@ L<SSL_do_handshake(3)>. Alternatively you can call a standard write function
such as L<SSL_write_ex(3)>, which will transparently complete the connection and
write the requested data.
Only clients may call SSL_write_early_data().
A server may choose to ignore early data that has been sent to it. Once the
connection has been completed you can determine whether the server accepted or
rejected the early data by calling SSL_get_early_data_status(). This will return
@ -127,20 +124,26 @@ or if the early data was rejected.
=back
Once the initial SSL_read_early_data() call has completed successfully the
server may interleave calls to L<SSL_write_ex(3)> and L<SSL_write(3)> with calls
to SSL_read_early_data() as required. As noted above data sent via
L<SSL_write_ex(3)> or L<SSL_write(3)> in this way is sent to an unauthenticated
client.
Once the initial SSL_read_early_data() call has completed successfully (i.e. it
has returned SSL_READ_EARLY_DATA_SUCCESS or SSL_READ_EARLY_DATA_FINISH) then the
server may choose to write data immediately to the unauthenticated client using
SSL_write_early_data(). If SSL_read_early_data() returned
SSL_READ_EARLY_DATA_FINISH then in some situations (e.g. if the client only
support TLSv1.2) the handshake may have already been completed and calls
to SSL_write_early_data() are not allowed. Call L<SSL_is_init_finished(3)> to
determine whether the handshake has completed or not. If the handshake is still
in progress then the server may interleave calls to SSL_write_early_data() with
calls to SSL_read_early_data() as required.
Servers must not call L<SSL_read_ex(3)> or L<SSL_read(3)> until
SSL_read_early_data() has returned with SSL_READ_EARLY_DATA_FINISH. Once it has
done so the connection to the client still needs to be completed. Complete the
connection by calling a function such as L<SSL_accept(3)> or
L<SSL_do_handshake(3)>. Alternatively you can call a standard read function such
as L<SSL_read_ex(3)>, which will transparently complete the connection and read
the requested data. Note that it is an error to attempt to complete the
connection before SSL_read_early_data() has returned SSL_READ_EARLY_DATA_FINISH.
Servers must not call L<SSL_read_ex(3)>, L<SSL_read(3)>, L<SSL_write_ex(3)> or
L<SSL_write(3)> until SSL_read_early_data() has returned with
SSL_READ_EARLY_DATA_FINISH. Once it has done so the connection to the client
still needs to be completed. Complete the connection by calling a function such
as L<SSL_accept(3)> or L<SSL_do_handshake(3)>. Alternatively you can call a
standard read function such as L<SSL_read_ex(3)>, which will transparently
complete the connection and read the requested data. Note that it is an error to
attempt to complete the connection before SSL_read_early_data() has returned
SSL_READ_EARLY_DATA_FINISH.
Only servers may call SSL_read_early_data().

View File

@ -1760,7 +1760,8 @@ int ssl_write_internal(SSL *s, const void *buf, size_t num, size_t *written)
if (!ssl_write_early_finish(s))
return 0;
} else if (s->early_data_state == SSL_EARLY_DATA_CONNECT_RETRY
|| s->early_data_state == SSL_EARLY_DATA_ACCEPT_RETRY) {
|| s->early_data_state == SSL_EARLY_DATA_ACCEPT_RETRY
|| s->early_data_state == SSL_EARLY_DATA_READ_RETRY) {
SSLerr(SSL_F_SSL_WRITE_INTERNAL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
@ -1820,17 +1821,14 @@ int SSL_write_early_data(SSL *s, const void *buf, size_t num, size_t *written)
{
int ret;
if (s->server) {
SSLerr(SSL_F_SSL_WRITE_EARLY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
switch (s->early_data_state) {
case SSL_EARLY_DATA_NONE:
if (!SSL_in_before(s)
if (s->server
|| !SSL_in_before(s)
|| s->session == NULL
|| s->session->ext.max_early_data == 0) {
SSLerr(SSL_F_SSL_WRITE_EARLY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
SSLerr(SSL_F_SSL_WRITE_EARLY_DATA,
ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
/* fall through */
@ -1851,8 +1849,15 @@ int SSL_write_early_data(SSL *s, const void *buf, size_t num, size_t *written)
s->early_data_state = SSL_EARLY_DATA_WRITE_RETRY;
return ret;
case SSL_EARLY_DATA_READ_RETRY:
/* We are a server writing to an unauthenticated client */
s->early_data_state = SSL_EARLY_DATA_UNAUTH_WRITING;
ret = SSL_write_ex(s, buf, num, written);
s->early_data_state = SSL_EARLY_DATA_READ_RETRY;
return ret;
default:
SSLerr(SSL_F_SSL_WRITE_EARLY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
SSLerr(SSL_F_SSL_WRITE_EARLY_DATA, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
}

View File

@ -621,6 +621,7 @@ typedef enum {
SSL_EARLY_DATA_CONNECTING,
SSL_EARLY_DATA_WRITE_RETRY,
SSL_EARLY_DATA_WRITING,
SSL_EARLY_DATA_UNAUTH_WRITING,
SSL_EARLY_DATA_FINISHED_WRITING,
SSL_EARLY_DATA_ACCEPT_RETRY,
SSL_EARLY_DATA_ACCEPTING,

View File

@ -1624,10 +1624,10 @@ static int test_early_data_read_write(void)
}
/*
* Server should be able to write normal data, and client should be able to
* Server should be able to write data, and client should be able to
* read it.
*/
if (!SSL_write_ex(serverssl, MSG2, strlen(MSG2), &written)
if (!SSL_write_early_data(serverssl, MSG2, strlen(MSG2), &written)
|| written != strlen(MSG2)) {
printf("Failed writing message 2\n");
goto end;
@ -1647,7 +1647,7 @@ static int test_early_data_read_write(void)
goto end;
}
/* Server should still be able read early data after writing normal data */
/* Server should still be able read early data after writing data */
if (SSL_read_early_data(serverssl, buf, sizeof(buf), &readbytes)
!= SSL_READ_EARLY_DATA_SUCCESS
|| readbytes != strlen(MSG3)
@ -1656,8 +1656,8 @@ static int test_early_data_read_write(void)
goto end;
}
/* Write more normal data from server and read it from client */
if (!SSL_write_ex(serverssl, MSG4, strlen(MSG4), &written)
/* Write more data from server and read it from client */
if (!SSL_write_early_data(serverssl, MSG4, strlen(MSG4), &written)
|| written != strlen(MSG4)) {
printf("Failed writing message 4\n");
goto end;
@ -1700,7 +1700,7 @@ static int test_early_data_read_write(void)
goto end;
}
/* Client and server should not be able to write early data now */
/* Client and server should not be able to write/read early data now */
if (SSL_write_early_data(clientssl, MSG6, strlen(MSG6), &written)) {
printf("Unexpected success writing early data\n");
goto end;
@ -1778,7 +1778,7 @@ static int test_early_data_read_write(void)
goto end;
}
/* Client and server should not be able to write early data now */
/* Client and server should not be able to write/read early data now */
if (SSL_write_early_data(clientssl, MSG6, strlen(MSG6), &written)) {
printf("Unexpected success writing early data (2)\n");
goto end;