RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

ssl_client.c

/* $Id: ssl_client.c,v 1.55 2005/04/11 12:28:12 hfrancis Exp $ */

/*
 * Copyright (C) 1999-2003 RSA Security Inc. All rights reserved.
 *
 * This work contains proprietary information of RSA Security.
 * Distribution is limited to authorized licensees of RSA
 * Security. Any unauthorized reproduction, distribution or
 * modification of this work is strictly prohibited.
 */

/*
 * A source of pseudo random numbers is required for various aspects of the
 * security protocol and components included in this product. Failure to
 * appropriately seed the Pseudo Random Number Generator (PRNG) will
 * seriously impact the security provided. Your application should provide this
 * random seed.
 *
 * The exact requirements for this seeding process may depend upon your
 * application and the environment for which your application is designed.
 * See RFC 1750 - Randomness Recommendations for Security.
 */

#include "r_prod.h"
#include "client_defaults.h"       /* The default values for client samples */
#include "debug_cb.h"                /* The BIO and SSL state dump callback */
#include "arguments.h"                  /* The program arguments processing */
#include "print_con_info.h"             /* The print connection information */

#ifndef SSLC_SMALL_CODE
int verification_setup(SSL_CTX *ctx,BIO *bio);
#endif /* SSLC_SMALL_CODE */

BIO *bio_err;

int main(int argc, char *argv[])
{
    int ret = R_ERROR_FAILED;                  /* The function return value */
    BIO *sock_bio;                           /* The connect BIO for the SSL */
    BIO *bio_out = NULL;                     /* The BIO for standard output */
    int done = 0;
    int temp;           /* A temporary variable to use for misc. operations */
    int bytes_read;                 /* The number of Bytes read from server */
    int bytes_written;             /* The number of Bytes written to server */
    int count;                      /* Number of peer certificates received */
    int debug = 0;                 /* Flag to allow extra debug information */
    int state = 0;                                   /* Print the SSL state */
    char *host = SSL_CLIENT_HOST_PORT_DEFAULT;       /* The port for server */
    char *path = SSL_CLIENT_REQUEST_DEFAULT;         /* The URL to retrieve */
    char *ciphers = NULL;            /* The cipher list. NULL means default */
    int extras = 0;                         /* Extra command line arguments */
    int arg;                                        /* The argument counter */
    static char buf[SSL_CLIENT_DATA_BUFFER_LEN];
    SSL *ssl=NULL;
    SSL_METHOD *meth = NULL;       /* The pointer for the server SSL method */
    SSL_CTX *ssl_ctx = NULL;              /* The pointer to the SSL context */
    R_LIB_CTX *lib_ctx = NULL;        /* The pointer to the library context */
    int off = 0;                                      /* Additional options */
    static unsigned char rand_seed[] = "A bad seed for software PRNG";
    int mode = R_LIB_CTX_FIPS140_MODE;            /* Library's default mode */
    int sock_fd = 0;     /* File descriptor for socket connection to server */
    int retVal = 0;                          /* Return value from API calls */
    int sockOptVal = 0;                 /* Buffer for setting socket option */
    unsigned long optLen = 0;                /* Length of the socket option */

    /* Create an output channel */
    if ((bio_err = BIO_new_fp(stderr, BIO_NOCLOSE)) == NULL)
    {
        goto end;
    }
    BIO_set_flags(bio_err, BIO_FLAGS_FLUSH_ON_WRITE);

    /* Create an output channel */
    if ((bio_out = BIO_new_fp(stdout, BIO_NOCLOSE)) == NULL)
    {
        goto end;
    }
    BIO_set_flags(bio_out, BIO_FLAGS_FLUSH_ON_WRITE);

    /* Parse the client application arguments */
    if (client_parse_arguments(argc, argv, bio_err, bio_out, &host,
        &debug, &state, &meth, &extras, &off, &ciphers, &mode) == 1)
    {
        client_usage(bio_err, argv[0]);
        goto end;
    }

    /* An error for extra command line arguments */
    if (extras > 0)
    {
        /* Report the first command line problem */
        for (arg = 1; arg < argc; arg++)
        {
            if (argv[arg] != NULL)
            {
                BIO_printf(bio_err, "\nUnknown argument : %s\n", argv[arg]);
                break;
            }
        }

        /* Report program usage and exit */
        client_usage(bio_err, argv[0]);
        goto end;
    }

    /* Initialize the SSL library using the default resources */
    if (PRODUCT_LIBRARY_NEW(PRODUCT_DEFAULT_RESOURCE_LIST(), R_RES_FLAG_DEF,
        &lib_ctx) != R_ERROR_NONE)
    {
        BIO_printf(bio_err, "Unable to create library context\n");
        goto end;
    }

    /*
         * This demonstrates how to seed the software PRNG of the SSL library.
         * Seeding information gathered using software methods is not the best
         * source, so do not use the following example of application-specified
         * entropy in production. RNG hardware is considered the best source of
         * random information.
     */
    if (R_rand_seed(R_rand_get_default(), rand_seed, sizeof(rand_seed)) == 0)
    {
        BIO_printf(bio_err, "Unable to seed the PRNG\n");
        goto end;
    }

#ifndef SSLC_SMALL_CODE
    SSL_load_error_strings();
#endif /* !SSLC_SMALL_CODE */

    /* Set the client default method if it is not set */
    if (meth == NULL)
    {
        /*
         * Select the protocol version in the following order:
         * - Use pure TLSv1 if it is the only protocol version available
         * - Use SSLv3 support optionally in an SSLv2 handshake
         *   (for maximum compatibility) (if possible)
         * - Use pure SSLv3
         * - Use pure SSLv2
         */
#if defined(NO_SSL2) && defined(NO_SSL3) && !defined(NO_TLS1)
        meth = TLSv1_client_method();
        BIO_printf(bio_out, "Doing TLSv1_client_method\n");
#elif (!defined(NO_SSL2) || defined(NO_SSL2IMPL)) && !defined(NO_SSL3)
        meth = SSLv23_client_method();
        BIO_printf(bio_out, "Doing SSLv23_client_method\n");
#elif !defined(NO_SSL3)
        meth = SSLv3_client_method();
        BIO_printf(bio_out, "Doing SSLv3_client_method\n");
#elif !defined(NO_SSL2)
        meth = SSLv2_client_method();
        BIO_printf(bio_out, "Doing SSLv2_client_method\n");
#else
        BIO_printf(bio_err, "Unable to set default client method.\n");
        goto end;
#endif
    }

    /* Create the SSL context structure */
    if ((ssl_ctx = SSL_CTX_new(meth)) == NULL)
    {
        BIO_printf(bio_err, "Unable to create SSL context\n");
        goto end;
    }

    /*
     * Set the mode of operation of the context.
     *
     * Note this is only applicable to libraries that support FIPS/non-FIPS
     * modes of operations.
     */
    (void)SSL_CTX_set_R_LIB_CTX(ssl_ctx, lib_ctx, mode);

    /* Set the cipher list if specified. Otherwise use the default. */
    if (ciphers != NULL)
    {
        SSL_CTX_set_cipher_list(ssl_ctx, ciphers);
    }

    /* Set the SSL information callback to print the SSL state */
    if (state)
    {
        SSL_CTX_set_info_cb(ssl_ctx, ssl_state_info_cb);
    }

    /* Enable all vendor bug compatibility options */
    SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | off);

#ifndef SSLC_SMALL_CODE
    /* Set up the verification callbacks. See ssl_verify.c. */
    verification_setup(ssl_ctx, bio_out);
#endif /* SSLC_SMALL_CODE */

    /*
     * Create the SSL structure. Defaults are inherited from the SSL_CTX.
     * Options are usually set against the SSL_CTX, and only those that
     * require connection-specific changes are set against the SSL.
     */
    if ((ssl = SSL_new(ssl_ctx)) == NULL)
    {
        BIO_printf(bio_err, "Unable to create SSL\n");
        goto end;
    }

    /*
     * An SSL exists so set the SSL to be a client-side implementation. When an
     * SSL_do_handshake() is performed it will know which handshake function to
     * run. If SSL_connect() or SSL_accept() is used, the side of the
     * connection will be set as part of the handshake process.
     * SSL_set_accept_state() is used to set the SSL to do a server-side
     * handshake.
     */
    SSL_set_connect_state(ssl);

    /*
     * Create a BIO to handle the set up and connection of a connection
     * socket. The connect BIO encapsulates a socket and some state
     * information to hide the details of a socket connection. This may also be
     * done manually using the "standard" socket API calls. This call does not
     * make a connection to the host and just sets the host details in the BIO.
     */
    if ((sock_bio = BIO_new_connect(host)) == NULL)
    {
        BIO_printf(bio_err, "Unable to create BIO connection\n");
        goto end;
    }

    /*
     * Use the newly created connect BIO. At this point, the connect BIO has
     * not checked whether the hostname is valid. The check and the connection
     * are made the first time the SSL protocol attempts to read/write some
     * data. Call BIO_do_handshake() or BIO_do_connect() to perform the
     * "connect operations".
     */
    SSL_set_bio(ssl, sock_bio, sock_bio);

    /*
     * In debug mode add the BIO callback function to output all data
     * that passes through the BIO
     */
    if (debug)
    {
        BIO_set_cb(sock_bio, bio_dump_cb);
        BIO_set_cb_arg(sock_bio, (char *)bio_out);
    }

    /* Main client handshake loop */

    while (!done)
    {
        /*
         * Perform the handshake. This activates the underlying connect BIO
         * and a socket connection will be made.
         */
        temp = SSL_do_handshake(ssl);

        /* The SSL_get_error() call categorizes errors into groups */
        switch (SSL_get_error(ssl, temp))
        {
        case SSL_ERROR_NONE:
            /* The handshake has finished successfully */
            done = 1;
            break;
        case SSL_ERROR_SSL:
            /* Handshake error - report and exit */
            BIO_printf(bio_err, "Handshake failure\n");
            goto end;
        case SSL_ERROR_SYSCALL:
            /*
             * System call error. This error is different from
             * the SSL_ERROR_SSL in that errno (under unix) has the numeric
             * error value, and it is not converted into text. If doing an
             * SSL_read() or SSL_write() there is no recorded error in the
             * error logging. This is because the error could be a retry
             * error of which the library is unaware.
             */
            BIO_printf(bio_err, "System call error = %d\n",
                R_os_get_last_sys_error());
            goto end;
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
        case SSL_ERROR_WANT_CONNECT:
            /*
             * Perform the handshake again. These errors are normally only
             * reported when doing non-blocking I/O.
             */
#if !defined(NO_SLEEP)
            BIO_printf(bio_out, "sleep(1)\n");
            R_sleep(1);
#endif
            break;
        case SSL_ERROR_ZERO_RETURN:
            /*
             * A read(2)/write(2) system call returned 0 (usually because
             * the socket was closed). If the socket is closed, the protocol
             * has failed.
             */
            BIO_printf(bio_out, "socket closed\n");
            goto end;
        }
    }

    /*
     * Completion of the handshake, can subsequently make changes to the 
     * type of socket connection as desired. Get the socket fd first, set 
     * the KEEPALIVE socket option, then check option was correctly set.
     */
    sock_fd = SSL_get_fd(ssl);
    sockOptVal = 1; /* non zero value to turn option on */
    optLen = sizeof(sockOptVal);
    retVal = SIO_setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, &sockOptVal, optLen);
    if (retVal != 0)
    {
        /* If fail, report it but continue on */
        BIO_printf(bio_err, "Setting KEEPALIVE socket option failure\n");
    }
    retVal = SIO_getsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, &sockOptVal, &optLen);
    if (retVal != 0)
    {
        /* If fail, report it but continue on */
        BIO_printf(bio_err, "Getting KEEPALIVE socket option failure\n");
    }
    else
    {
        if (sockOptVal == 0)
        {
        /* If fail, report it but continue on */
        BIO_printf(bio_err, "KEEPALIVE socket option was not correctly set\n");
        }
    }


    /*
     * The SSL handshake is finished at this point. For demonstration purposes
     * print out all connection information.
     */
    BIO_printf(bio_out, "SSL connection completed\n");

    /*
     * The peer may have returned a 'certificate chain'. Normally this
     * is the server certificate. If the CA certificate was sent, it
     * must be available locally in order to verify that the CA certificate
     * is true or the application must decide to trust it.
     */
    count = SSL_get_peer_cert_chain_count(ssl);
    BIO_printf(bio_out, "\nCertificate Chain: %d items\n", count);

    /*
     * An application can walk the chain and print certificate details
     * at this point
     */

    /* Print details on the SSL connection. Ignore the return code. */
    print_connection_info(bio_out, ssl);

    /* A connection exists. Request some information and read the response. */

    /*
     * Determine the number of Bytes to write. This does not include the
     * trailing null (no special reason for this).
     */
    temp = Strlen(path);

    /*
     * For blocking I/O this call will return with an error or with the total
     * number of Bytes written. For non-blocking I/O it is possible the
     * function will return -1 for a blocked write operation and the write
     * should be in a retry loop.
     */
    bytes_written = SSL_write(ssl, path, temp);

    /*
     * Write the data and exit on error. In real applications, additional error
     * handling would be performed.
     */
    if (bytes_written <= 0)
    {
        BIO_printf(bio_err, "Write error\n");
        goto end;
    }

    /*
     * Read data in blocks of the size of the buffer from the other side
     * of the protocol
     */
    for (;;)
    {
        /* Read an application buffer of data from the server */
        bytes_read = SSL_read(ssl, buf, sizeof(buf));

        /*
         * Perform minimal error processing. Exit the read loop only if
         * the connection breaks (i == 0) or there is an error (i < 0).
         * In this case, this is the crude method that the client uses
         * to determine it has finished reading data.
         */
        if (bytes_read <= 0)
        {
            break;
        }

        /* Write the data to stdout */
        BIO_write(bio_out, buf, bytes_read);
    }

    /*
     * Send an SSL_shutdown() message. If called again, the connection
     * attempts to read a shutdown message from the remote end. Normally
     * either the shutdown message is received or the socket is closed.
     * If the other end does not understand shutdown messages (for example
     * with some versions of MSIE), neither of these occur.
     */
    SSL_shutdown(ssl);

    /* Set the program success flag */
    ret = R_ERROR_NONE;

end:

    /* Error handling */
    if ((ret != R_ERROR_NONE) && (bio_err != NULL))
    {
        /* Display the error stack on standard error */
        ERR_print_errors(bio_err);
    }

    /* Clean up the allocated structures */
    if (ssl != NULL)
    {
        SSL_free(ssl);
    }

    if (ssl_ctx != NULL)
    {
        /*
         * Do not check the return code. No more work is done in
         * order to free this memory.
         */
        SSL_CTX_free(ssl_ctx);
    }

    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }

    /* Free the BIOs used for standard outputs */
    if (bio_out != NULL)
    {
        BIO_free(bio_out);
    }

    if (bio_err != NULL)
    {
        BIO_free(bio_err);
        bio_err = NULL;
    }

    return(R_ERROR_EXIT_CODE(ret));
}

Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 072-001001-2100-001-000 - 2.1