Re: OpenSSL read/write timeouts



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);
}
.



Relevant Pages

  • RE: SSL MITM not on port 443
    ... Have you ever done what you're trying to do on a "normal" SSL web ... My recommendation would be to set up a web server in your lab ... hopes that the client will accept that certificate. ... SSL MITM not on port 443 ...
    (Pen-Test)
  • Re: Antw: Re: LDAP Authentication Problem
    ... TLSv1 und wird auf einen SSL Client Hello Request mit TLSv1 nicht ... antworten anstatt ein SSLv3 Server Hello. ... the LDAP PAM module and the shadow package. ...
    (de.comp.sys.novell)
  • Re: OWA 2003 w/ Smart Card Authentication.
    ... Exchange 2003 server via ActivSync. ... the IIS certificate. ... Whether or not authentication will succeed is completely dictated by ... Server's SSL certificate must be configured on root of v-server via ...
    (microsoft.public.exchange.connectivity)
  • Re: OWA 2003 w/ Smart Card Authentication.
    ... Exchange 2003 server via ActivSync. ... the IIS certificate. ... Whether or not authentication will succeed is completely dictated by ... Server's SSL certificate must be configured on root of v-server via ...
    (microsoft.public.exchange.connectivity)
  • RE: SSL and IPS (was RE: ssh and ids)
    ... along with the certificate needed for SSL. ... > certificate keyfile or the Diffie-Hellman exponents. ... that the server is who they say they are. ... The client sends the server the client's SSL version number, ...
    (Focus-IDS)