[Info-vax] OpenSSL read/write timeouts
Victor
victor.esin at gmail.com
Thu Jan 15 00:32:50 EST 2009
Rich Jordan:
> Hoping someone here has done this, but if not I'll pester the OpenSSL
> lists; so far google searches are not turning up useful results
> (though perhaps the lack of same is an answer in itself).
>
> Is there a way to put a timeout on a blocking read/write in OpenSSL?
> The only timeout documentation I can find is for an SSL session
> timeout (default 300 seconds), but nothing for a timeout on an actual
> I/O operation using SSL_READ() or SSL_WRITE().
>
> Do you have to handle it at the lower socket layer? Or roll your own
> with polling and traps using non-blocking read/write functions (read
> timeouts are the critical one for this app, but both would be nice).
>
Yes, we do it through the non-blocking calls. It happened to be a
small enough solution to stop looking for anything else. See the
example below, but don't take it too seriously as it was
just a fast and dirty (but working) proof of the concept for our
Windows-types.
/*
* ++
* FACILITY:
*
* Simplest SSL Client + "Socket BIO"
*
* ABSTRACT:
*
* This is an example of a SSL client with minimum functionality.
* This client uses Socket BIO.
* The socket APIs are used to handle TCP/IP operations.
*
* This SSL client verifies the server's certificate against the
CA
* certificate loaded in the client.
*
* This SSL client does not load its own certificate and key
because
* the SSL server does not request & verify the client
certificate.
*
* ENVIRONMENT:
*
* OpenVMS Alpha V7.2-2
* TCP/IP Services V5.0A or higher
*
* AUTHOR:
*
* Taka Shinagawa, OpenVMS Security Group
*
* CREATION DATE:
*
* 1-Jan-2002
*
* --
*/
/* Assumptions, Build, Configuration, and Execution Instructions */
/*
* ASSUMPTIONS:
*
* The following are assumed to be true for the
* execution of this program to succeed:
*
* - SSL is installed and started on this system.
*
* - this server program, and its accompanying client
* program are run on the same system, but in different
* processes.
*
* - the certificate and keys referenced by this program
* reside in the same directory as this program. There
* is a command procedure, SSL$EXAMPLES_SETUP.COM, to
* help set up the certificates and keys.
*
*
* BUILD INSTRUCTIONS:
*
* To build this example program use commands of the form,
*
* For a 32-bit application using only SSL APIs needs to run the
following commands for SSL_APP.C .
*
-----------------------------------------------------------------
* $CC/POINTER_SIZE=32/PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES
SSL_APP.C
* $LINK SSL_APP.OBJ, VMS_DECC_OPTIONS.OPT/OPT
*
-----------------------------------------------------------------
* VMS_DECC_OPTIONS.OPT should include the following lines.
* -------------------------------------------------
* SYS$LIBRARY:SSL$LIBCRYPTO_SHR32.EXE/SHARE
* SYS$LIBRARY:SSL$LIBSSL_SHR32.EXE/SHARE
* -------------------------------------------------
*
* Creating a 64-bit application of SSL_APP.C should run the
following commands.
*
-----------------------------------------------------------------
* $CC/POINTER_SIZE=64/PREFIX_LIBRARY_ENTRIES=ALL_ENTRIES
SSL_APP.C
* $LINK SSL_APP.OBJ, VMS_DECC_OPTIONS.OPT/OPT
*
-----------------------------------------------------------------
* VMS_DECC_OPTIONS.OPT should include the following lines.
* -------------------------------------------------
* SYS$LIBRARY:SSL$LIBCRYPTO_SHR.EXE/SHARE
* SYS$LIBRARY:SSL$LIBSSL_SHR.EXE/SHARE
* -------------------------------------------------
*
*
* CONFIGURATION INSTRUCTIONS:
*
*
* RUN INSTRUCTIONS:
*
* To run this example program:
*
* 1) Start the server program on this system,
*
* $ run server
*
* 2) Start the client program on this same system,
*
* $ run client
*
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#ifdef __VMS
#include <socket.h>
#include <inet.h>
#include <in.h>
#include <stropts.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define RETURN_NULL(x) if ((x)==NULL) exit (1)
#define RETURN_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define RETURN_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr);
exit(1); }
static int verify_callback(int ok, X509_STORE_CTX *ctx);
#define RSA_CLIENT_CERT "ssl$root:[ca_ncc]ncc7.pem"
#define RSA_CLIENT_KEY "ssl$root:[ca_ncc]ncc7.pem"
#define RSA_CLIENT_CA_CERT "ssl$root:[ca_ncc]ncc_cacert.pem"
#define RSA_CLIENT_CA_PATH "sys$common:[syshlp.examples.ssl]"
#define ON 1
#define OFF 0
typedef enum {
SOCKET_IS_NONBLOCKING,
SOCKET_IS_BLOCKING,
SOCKET_HAS_TIMED_OUT,
SOCKET_HAS_BEEN_CLOSED,
SOCKET_OPERATION_OK
} timeout_state;
static int check_socket_and_wait_for_timeout (
int sock_fd,
int writing) // 0 - read; 1 - write, 2 - connect
{
fd_set rset, wset;
struct timeval tv = {10,0};
int rc;
/* Guard against closed socket */
if (sock_fd < 0)
return SOCKET_HAS_BEEN_CLOSED;
/* Construct the arguments to select */
FD_ZERO(&rset);
FD_SET(sock_fd, &rset);
wset = rset;
/* See if the socket is ready */
switch (writing)
{
case 0:
rc = select(sock_fd+1, &rset, NULL, NULL, &tv);
break;
case 1:
rc = select(sock_fd+1, NULL, &wset, NULL, &tv);
break;
case 2:
rc = select(sock_fd+1, &rset, &wset, NULL, &tv);
break;
}
/* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK
otherwise
(when we are able to write or when there's something to
read) */
return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK;
}
void main()
{
int err, ret, sockstate, len, fl, one=1;
int verify_client = ON; /* To verify a client certificate,
set ON */
int sock;
struct sockaddr_in server_addr;
char *str;
char buf [4096];
char hello[80];
SSL_CTX *ctx;
SSL *ssl;
SSL_METHOD *meth;
X509 *server_cert;
BIO *sbio = NULL;
EVP_PKEY *pkey;
// short int s_port = 5555;
// const char *s_ipaddr = "10.32.9.19";
short int s_port = 4669;
const char *s_ipaddr = "10.32.9.33";
/*----------------------------------------------------------*/
printf ("Message to be sent to the SSL server: ");
fgets (hello, 80, stdin);
/* Load encryption & hashing algorithms for the SSL program */
SSL_library_init();
/* Load the error strings for SSL & CRYPTO APIs */
SSL_load_error_strings();
/* Create a SSL_METHOD structure (choose a SSL/TLS protocol
version) */
meth = SSLv3_method();
/* Create a SSL_CTX structure */
ctx = SSL_CTX_new(meth);
RETURN_NULL(ctx);
/
*-------------------------------------------------------------------------
*/
if(verify_client == ON)
{
/* Load the client certificate into the SSL_CTX
structure */
if (SSL_CTX_use_certificate_file(ctx, RSA_CLIENT_CERT,
SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(1);
}
/* Load the private-key corresponding to the client
certificate */
if (SSL_CTX_use_PrivateKey_file(ctx, RSA_CLIENT_KEY,
SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(1);
}
/* Check if the client certificate and private-key
matches */
if (!SSL_CTX_check_private_key(ctx)) {
fprintf(stderr,"Private key does not match the
certificate public key\n");
exit(1);
}
}
/* Load the RSA CA certificate into the SSL_CTX structure */
if (!SSL_CTX_load_verify_locations(ctx, RSA_CLIENT_CA_CERT,
NULL)) {
ERR_print_errors_fp(stderr);
exit(1);
}
/* Set to require peer (server) certificate verification */
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
SSL_CTX_set_verify_depth(ctx,1);
/*
----------------------------------------------------------------- */
/* Set up a TCP socket */
sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
RETURN_ERR(sock, "socket");
fl = ioctl (sock, FIONBIO, &one);
if (fl == -1)
perror ("ioctl FIONBIO");
memset (&server_addr, '\0', sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(s_port); /*
Server Port number */
server_addr.sin_addr.s_addr = inet_addr(s_ipaddr); /* Server
IP */
/* Establish a TCP/IP connection to the SSL client */
err = connect(sock, (struct sockaddr*) &server_addr, sizeof
(server_addr));
sockstate = check_socket_and_wait_for_timeout(sock, 2);
if (sockstate != SOCKET_OPERATION_OK)
{
RETURN_ERR(err, "connect");
exit;
}
/* ----------------------------------------------- */
/* A SSL structure is created */
ssl = SSL_new (ctx);
RETURN_NULL(ssl);
SSL_set_fd(ssl, sock); /* Set the socket for SSL */
BIO_set_nbio(SSL_get_rbio(ssl), 1);
BIO_set_nbio(SSL_get_wbio(ssl), 1);
SSL_set_connect_state(ssl);
/* Actually negotiate SSL connection */
/* XXX If SSL_connect() returns 0, it's also a failure. */
sockstate = 0;
/* fl = fcntl (sock, F_GETFL, 0);
if (fl == -1)
perror ("fcntl GETFL");
printf ("nonblock=%x before connect\n", fl & O_NONBLOCK);
*/
do {
ret = SSL_connect(ssl);
err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_READ) {
sockstate = check_socket_and_wait_for_timeout
(sock, 0);
} else if (err == SSL_ERROR_WANT_WRITE) {
sockstate = check_socket_and_wait_for_timeout
(sock, 1);
} else {
sockstate = SOCKET_OPERATION_OK;
}
if (sockstate == SOCKET_HAS_TIMED_OUT) {
printf ("The connect operation timed out\n");
break;
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
printf ("\nderlying socket has been closed.
\n");
break;
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
break;
}
} while (err == SSL_ERROR_WANT_READ || err ==
SSL_ERROR_WANT_WRITE);
if (ret <= 0 && err != SSL_ERROR_WANT_READ && err !=
SSL_ERROR_WANT_WRITE) {
RETURN_SSL(err);
}
/* Informational output (optional) */
printf ("SSL connection using %s\n", SSL_get_cipher (ssl));
/* Get the server's certificate (optional) */
server_cert = SSL_get_peer_certificate (ssl);
if (server_cert != NULL)
{
printf ("Server certificate:\n");
str = X509_NAME_oneline(X509_get_subject_name
(server_cert),0,0);
RETURN_NULL(str);
printf ("\t subject: %s\n", str);
free (str);
str = X509_NAME_oneline(X509_get_issuer_name
(server_cert),0,0);
RETURN_NULL(str);
printf ("\t issuer: %s\n", str);
free(str);
X509_free (server_cert);
}
else
printf("The SSL server does not have certificate.\n");
/* Receive data from the SSL client */
/* fl = fcntl (sock, F_GETFL, 0);
if (fl == -1)
perror ("fcntl GETFL");
printf ("nonblock=%x before read\n", fl & O_NONBLOCK);
*/
do {
err = 0;
len = SSL_read (ssl, buf, sizeof(buf)-1);
err = SSL_get_error(ssl, len);
if (err == SSL_ERROR_WANT_READ) {
sockstate = check_socket_and_wait_for_timeout
(sock, 0);
} else if (err == SSL_ERROR_WANT_WRITE) {
sockstate = check_socket_and_wait_for_timeout
(sock, 1);
} else {
sockstate = SOCKET_OPERATION_OK;
}
if (sockstate == SOCKET_HAS_TIMED_OUT) {
printf ("The read operation timed out\n");
break;
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
break;
}
} while (err == SSL_ERROR_WANT_READ || err ==
SSL_ERROR_WANT_WRITE);
if (len <= 0) {
RETURN_SSL(err);
}
buf[len] = '\0';
printf ("Received %d chars:'%s'\n", len, buf);
/*--------------- DATA EXCHANGE - send message and receive
reply. ---------------*/
/* fl = fcntl (sock, F_GETFL, 0);
if (fl == -1)
perror ("fcntl GETFL");
printf ("nonblock=%x before write\n", fl & O_NONBLOCK);
*/
do {
err = 0;
len = SSL_write(ssl, hello, strlen(hello));
err = SSL_get_error (ssl, len);
if (err == SSL_ERROR_WANT_READ) {
sockstate = check_socket_and_wait_for_timeout
(sock, 0);
} else if (err == SSL_ERROR_WANT_WRITE) {
sockstate = check_socket_and_wait_for_timeout
(sock, 1);
} else {
sockstate = SOCKET_OPERATION_OK;
}
if (sockstate == SOCKET_HAS_TIMED_OUT) {
printf ("The write operation timed out\n");
break;
} else if (sockstate == SOCKET_HAS_BEEN_CLOSED) {
printf ("Underlying socket has been closed.
\n");
break;
} else if (sockstate == SOCKET_IS_NONBLOCKING) {
break;
}
} while (err == SSL_ERROR_WANT_READ || err ==
SSL_ERROR_WANT_WRITE);
if (len <= 0)
RETURN_SSL(err);
/*--------------- SSL closure ---------------*/
/* Shutdown the client side of the SSL connection */
err = SSL_shutdown(ssl);
RETURN_SSL(err);
/* Terminate communication on a socket */
err = close(sock);
RETURN_ERR(err, "close");
/* Free the SSL structure */
SSL_free(ssl);
/* Free the SSL_CTX structure */
SSL_CTX_free(ctx);
}
More information about the Info-vax
mailing list