Skip to content

Commit

Permalink
RT3724 Add asynchronous event processing
Browse files Browse the repository at this point in the history
Rebranding of SSL_ERROR_WANT_X509_LOOKUP as SSL_ERROR_WANT_EVENT, making
event type to wait for visible in SSL->rwstate, letting TLS_SRP have its
own event type instead of piggybacking on SSL_X509_LOOKUP.
Adding task for decryption of client key exchange response
Adding task for generating client certificate verify message
Adding task for signing of server key exchange message

(cherry picked from commit 64658cf)

Conflicts:
	apps/s_server.c
	include/openssl/ssl.h
	include/openssl/ssl3.h
	ssl/s3_clnt.c
	ssl/s3_lib.c
	ssl/s3_srvr.c
	ssl/ssl_locl.h
  • Loading branch information
Stefan Eissing authored and tmshort committed Jun 17, 2015
1 parent 312529e commit 92914ac
Show file tree
Hide file tree
Showing 24 changed files with 1,735 additions and 274 deletions.
8 changes: 4 additions & 4 deletions apps/s_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1811,8 +1811,8 @@ int s_client_main(int argc, char **argv)
read_ssl = 1;
write_ssl = 0;
break;
case SSL_ERROR_WANT_X509_LOOKUP:
BIO_printf(bio_c_out, "write X BLOCK\n");
case SSL_ERROR_WANT_EVENT:
BIO_printf(bio_c_out, "event %d BLOCK\n", SSL_want(con));
break;
case SSL_ERROR_ZERO_RETURN:
if (cbuf_len != 0) {
Expand Down Expand Up @@ -1899,8 +1899,8 @@ int s_client_main(int argc, char **argv)
if ((read_tty == 0) && (write_ssl == 0))
write_ssl = 1;
break;
case SSL_ERROR_WANT_X509_LOOKUP:
BIO_printf(bio_c_out, "read X BLOCK\n");
case SSL_ERROR_WANT_EVENT:
BIO_printf(bio_c_out, "event %d BLOCK\n", SSL_want(con));
break;
case SSL_ERROR_SYSCALL:
ret = get_last_socket_error();
Expand Down
99 changes: 47 additions & 52 deletions apps/s_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -1954,6 +1954,33 @@ static void print_stats(BIO *bio, SSL_CTX *ssl_ctx)
SSL_CTX_sess_get_cache_size(ssl_ctx));
}

static int sv_check_event_wait(SSL *con, int retcode)
{
switch (SSL_get_error(con, retcode)) {
case SSL_ERROR_WANT_EVENT:
switch (SSL_want(con)) {
#ifndef OPENSSL_NO_SRP
case SSL_EVENT_SRP_CLIENTHELLO:
BIO_printf(bio_s_out, "LOOKUP renego during write\n");
srp_callback_parm.user = SRP_VBASE_get_by_user(srp_callback_parm.vb, srp_callback_parm.login);
if (srp_callback_parm.user)
BIO_printf(bio_s_out, "LOOKUP done %s\n", srp_callback_parm.user->info);
else
BIO_printf(bio_s_out, "LOOKUP not successful\n");
break;
#endif
default:
/* this is currently a busy wait, maybe not spam bio_s_out...
BIO_printf(bio_s_out,"Wait Event: %d\n", SSL_want(con)); */
break;
}
break;
default:
break;
}
return (retcode);
}

static int sv_body(char *hostname, int s, int stype, unsigned char *context)
{
char *buf = NULL;
Expand Down Expand Up @@ -2234,28 +2261,16 @@ static int sv_body(char *hostname, int s, int stype, unsigned char *context)
}
#endif
k = SSL_write(con, &(buf[l]), (unsigned int)i);
#ifndef OPENSSL_NO_SRP
while (SSL_get_error(con, k) == SSL_ERROR_WANT_X509_LOOKUP) {
BIO_printf(bio_s_out, "LOOKUP renego during write\n");
srp_callback_parm.user =
SRP_VBASE_get_by_user(srp_callback_parm.vb,
srp_callback_parm.login);
if (srp_callback_parm.user)
BIO_printf(bio_s_out, "LOOKUP done %s\n",
srp_callback_parm.user->info);
else
BIO_printf(bio_s_out, "LOOKUP not successful\n");
k = SSL_write(con, &(buf[l]), (unsigned int)i);
}
#endif
switch (SSL_get_error(con, k)) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_X509_LOOKUP:
BIO_printf(bio_s_out, "Write BLOCK\n");
break;
case SSL_ERROR_WANT_EVENT:
sv_check_event_wait(con, k);
break;
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
BIO_printf(bio_s_out, "ERROR\n");
Expand Down Expand Up @@ -2292,20 +2307,6 @@ static int sv_body(char *hostname, int s, int stype, unsigned char *context)
} else {
again:
i = SSL_read(con, (char *)buf, bufsize);
#ifndef OPENSSL_NO_SRP
while (SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP) {
BIO_printf(bio_s_out, "LOOKUP renego during read\n");
srp_callback_parm.user =
SRP_VBASE_get_by_user(srp_callback_parm.vb,
srp_callback_parm.login);
if (srp_callback_parm.user)
BIO_printf(bio_s_out, "LOOKUP done %s\n",
srp_callback_parm.user->info);
else
BIO_printf(bio_s_out, "LOOKUP not successful\n");
i = SSL_read(con, (char *)buf, bufsize);
}
#endif
switch (SSL_get_error(con, i)) {
case SSL_ERROR_NONE:
#ifdef CHARSET_EBCDIC
Expand All @@ -2319,6 +2320,9 @@ static int sv_body(char *hostname, int s, int stype, unsigned char *context)
case SSL_ERROR_WANT_READ:
BIO_printf(bio_s_out, "Read BLOCK\n");
break;
case SSL_ERROR_WANT_EVENT:
sv_check_event_wait(con,i);
break;
case SSL_ERROR_SYSCALL:
case SSL_ERROR_SSL:
BIO_printf(bio_s_out, "ERROR\n");
Expand Down Expand Up @@ -2370,32 +2374,23 @@ static int init_ssl_connection(SSL *con)
#endif
unsigned char *exportedkeymat;

i = SSL_accept(con);
int waiting = 1;
while (waiting) {
i = SSL_accept(con);
switch (SSL_get_error(con, i)) {
case SSL_ERROR_WANT_EVENT:
sv_check_event_wait(con, i);
#ifdef CERT_CB_TEST_RETRY
{
while (i <= 0 && SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP
&& SSL_state(con) == SSL3_ST_SR_CLNT_HELLO_C) {
BIO_printf(bio_err,
"LOOKUP from certificate callback during accept\n");
i = SSL_accept(con);
}
}
if (SSL_state(con) == SSL3_ST_SR_CLNT_HELLO_C)
BIO_printf(bio_err,
"LOOKUP from certificate callback during accept\n");
#endif
#ifndef OPENSSL_NO_SRP
while (i <= 0 && SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP) {
BIO_printf(bio_s_out, "LOOKUP during accept %s\n",
srp_callback_parm.login);
srp_callback_parm.user =
SRP_VBASE_get_by_user(srp_callback_parm.vb,
srp_callback_parm.login);
if (srp_callback_parm.user)
BIO_printf(bio_s_out, "LOOKUP done %s\n",
srp_callback_parm.user->info);
else
BIO_printf(bio_s_out, "LOOKUP not successful\n");
i = SSL_accept(con);
break;
default:
waiting = 0;
break;
}
}
#endif

if (i <= 0) {
if (BIO_sock_should_retry(i)) {
Expand Down
2 changes: 1 addition & 1 deletion doc/ssl/SSL_CTX_set_client_cert_cb.pod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ certificate will be installed into B<ssl>, see the NOTES and BUGS sections.
If no certificate should be set, "0" has to be returned and no certificate
will be sent. A negative return value will suspend the handshake and the
handshake function will return immediately. L<SSL_get_error(3)|SSL_get_error(3)>
will return SSL_ERROR_WANT_X509_LOOKUP to indicate, that the handshake was
will return SSL_ERROR_WANT_EVENT to indicate, that the handshake was
suspended. The next call to the handshake function will again lead to the call
of client_cert_cb(). It is the job of the client_cert_cb() to store information
about the state of the last call, if required to continue.
Expand Down
120 changes: 120 additions & 0 deletions doc/ssl/SSL_CTX_set_schedule_task_cb.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
=pod

=head1 NAME

SSL_CTX_set_schedule_task_cb, SSL_CTX_get_schedule_task_cb - handle task callback function

=head1 SYNOPSIS

#include <openssl/ssl.h>

typedef void SSL_task_fn(SSL *, SSL_task_ctx *ctx);
typedef int (*SSL_schedule_task_cb)(SSL *ssl, int event_type, SSL_task_ctx *ctx, SSL_task_fn *fn);

void SSL_CTX_set_schedule_task_cb(SSL_CTX *ctx, SSL_schedule_task_cb cb);
SSL_schedule_task_cb SSL_CTX_get_schedule_task_cb(SSL_CTX *ctx);

=head1 DESCRIPTION

SSL_CTX_set_schedule_task_cb() sets the B<SSL_schedule_task_cb()> callback, that is
called when a task needs to be performed for an SSL object.

When B<SSL_schedule_task_cb()> is NULL, no callback function is used.

SSL_CTX_get_schedule_task_cb() returns a pointer to the currently set callback
function.

SSL_schedule_task_cb() is the application defined callback. It is invoked whenever
the SSL object has a task to perform that might be time consuming, especially during
the connect/accept handshake period. Applications that wish to mulitplex several
SSL connects may prefer to handle such tasks in separate threads.

If no SSL_schedule_task_cb() is registered all such tasks are directly executed
by the SSL object.

=head1 CALLBACK ARGUMENTS

The callback is invoked with four arguments: B<SSL*> as the SSL object that needs
the task to be performed. B<event_type> is the event that will signal that the
task is done (see SSL_EVENT_* constants). This value is informative only and the
callback might use it to select which tasks it wants to schedule or refuse.

B<ctx> is an opaque parameter that needs to be passed to the task function and,
finally, B<fn> is the supplied function that needs to be called.

=head1 CALLBACK RETURN VALUES

If a callback is registered, its return values are interpreted as follows:

=over 4

=item E<gt>0

The scheduling was successful.

=item Z<>0

The callback refuses to schedule the task. The SSL object must execute the
task itself instead. This de facto results in the same behaviour as if no
callback had been registered. This allows the application to only schedule
a subset of the tasks in another thread (switching on the event_type, for
example).

=item E<lt>0

The scheduling failed. The SSL object will treat this as an error. If this
happens during handshake, for example, the handshake will fail.

=head1 NOTES

=head2 ASYNCHRONOUS HANDLING

While the callback might directly call the supplied task function, the most
common use case is expected to be that the task function will run in a
separate thread.

The SSL method called by the application (e.g. connect()/accept()), will in
this case return with a value E<lt>0 and the application needs to check
B<SSL_get_error>() for the cause. When a task was scheduled, B<SSL_get_error>()
will return B<SSL_ERROR_WANT_EVENT> to indicate that it waits for an event
to happen (here, the event that signals the end of the task).

The application may call the SSL method again, but this will give the same
results until the event has happened. How the application code may synchronize
with its callback is not a concern of OpenSSL.

=head2 OBJECT LIFETIMES

The arguments to the callback will continue to exist until the task function
has been called. However, once the task function returns, the supplied B<SSL*>
might have been deallocated.

An application that accepts a task, but then decides not to run it, needs to
signal the event with a negative result value. Otherwise resources allocated
to the SSL object might never be freed.

=head2 TASK/EVENT TYPES

The following tasks, indicated by their completion event, are currently available
for SSL. Not all tasks will occur during an SSL connection. Tasks vary by
client, server, protocol version, renegotiation attempts and other things. Also,
the list of tasks might vary with each release.

=item B<SSL_EVENT_KEY_EXCH_DECRYPT_DONE>

The task performs the decryption of the exchanged keys on the server side.
Supported for RSA encrypted keys.

=item B<SSL_EVENT_SETUP_CERT_VRFY_DONE>

The task signs the certificate verification message on the client side.

=item B<SSL_EVENT_KEY_EXCH_MSG_SIGNED>

The task signs the key verification message before it is sent to the client.

=head1 SEE ALSO

L<ssl(3)|ssl(3)>, L<SSL_signal_event(3)|SSL_signal_event(3)>

=cut
21 changes: 17 additions & 4 deletions doc/ssl/SSL_get_error.pod
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,23 @@ can be used.

=item SSL_ERROR_WANT_X509_LOOKUP

The operation did not complete because an application callback set by
SSL_CTX_set_client_cert_cb() has asked to be called again.
The TLS/SSL I/O function should be called again later.
Details depend on the application.
Identical value to SSL_ERROR_WANT_EVENT, maintained for backward compatibility.

=item SSL_ERROR_WANT_EVENT

The operation did not complete because processing was stopped and can only
proceed when a certain event happens. After it did happen, the TLS/SSL I/O
function needs to be called again.

(Ideally, the application would suspend/block its processing thread until
the event happens (or a timeout triggers). OpenSSL currently opens no
framework/utility for this and leaves this up to the application and its
installed callback functions.)

An example of such an event is the client certificate selection. An
application callback set by SSL_CTX_set_client_cert_cb() has asked to
be called again, ideally after the user selected/another task retrieved
the certificate to be used.

=item SSL_ERROR_SYSCALL

Expand Down
60 changes: 60 additions & 0 deletions doc/ssl/SSL_signal_event.pod
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
=pod

=head1 NAME

SSL_signal_event - let SSL know that an event has happened and that processing may continue

=head1 SYNOPSIS

#include <openssl/ssl.h>

int SSL_signal_event(const SSL *ssl, int event, int retcode);
int SSL_signal_event_err(const SSL *ssl, int event, int func, int reason, const char *file, int line);

=head1 DESCRIPTION

SSL_signal_event() notifies the SSL engine that an asynchronous event has happened.

SSL offers callbacks to the application that allow asynchronous processing. In such
cases, methods called by the application may return with -1 and SSL_get_error() will
then indicate SSL_ERROR_WANT_EVENT. The actual event SSL is waiting for can be
retrieved with SSL_want().

This will continue to be returned until the proper event is being signalled, usually
by the application that finished the processing.

SSL_signal_event_err() notifies the SSL engine that an asynchronous event has failed
The result will be -1 and the other information is set via SSLErr() in the calling
thread.

=head1 NOTES

It is safe to call SSL_signal_event() from the callback that is starting the event.
It is safe to make several calls for the same event, however only the first will
influence the result. All calls afterwards will be silently ignored.

The parameter B<retcode> is > 0 for a successfully handled event and <= 0 for
a failure in event handling. How a failure is affecting further SSL processing is
defined per event.

=head1 RETURN VALUES

The following return values can currently occur for SSL_signal_event():

=over 4

=item Z<>0

The operation failed; check the error stack to find out the reason.

=item Z<>1

The operation succeeded.

=back

=head1 SEE ALSO

L<ssl_want(3)|ssl_want(3)>, L<SSL_get_error(3)|SSL_get_error(3)>

=cut

0 comments on commit 92914ac

Please sign in to comment.