[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