RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

simple.c

/* $Id: simple.c,v 1.105.2.1 2005/11/22 00:30:15 lmalmborg 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.
 */

/*
 * Notes about compilation directives used in this code.
 *
 * The client program can minimize its memory usage by using compilation
 * directives to limit the code that is compiled. These directives are:
 *
 * NO_VERIFICATION   == A verification routine that simply returns a
 *                      success code is used. This can introduce a
 *                      security vulnerability and is not generally
 *                      recommended.
 * NO_DISPLAY_CIPHER == Cipher name feedback is not printed out. This
 *                      causes no security issues.
 */


#include "r_prod.h"

/* Load in the non-dynamic malloc routines if required */
#ifdef USE_UMALLOC
#include "u_malloc.h"
#include "u_malloc.c"
#endif

#include "verify_cb.h"

/*
 * Enable these directives to reduce code size of this application.
 * Check the compilation directives note above for security implications.
 */
#if 0
#define NO_DISPLAY_CIPHER
#endif

#if 0
#define NO_VERIFICATION
#endif

/*
 * Macro constants
 */

#define SIMPLE_CLIENT_SERVER_PORT_DEFAULT   "www.rsasecurity.com:443"

#define SIMPLE_CLIENT_SERVER_REQUEST_STR    "HEAD / HTTP/1.0\r\n\r\n"

#define SIMPLE_DATA_BUFFER_LEN              512

int main(int argc, char *argv[])
{
    int ret = R_ERROR_FAILED;                        /* Program return code */
    R_LIB_CTX *lib_ctx = NULL;                           /* Library context */
    SSL_METHOD *meth = NULL;                             /* Protocol method */
    static char *get_str = SIMPLE_CLIENT_SERVER_REQUEST_STR;
    unsigned char buf[SIMPLE_DATA_BUFFER_LEN];  /* Buffer for IO processing */
    SIO_SOCK sock;                                     /* Socket connection */
    int i;
    int n;
    int no_verify = 0;   /* Prevents meaningful verification of server cert */
    int no_output = 0;         /* Prevents reports of server data to stdout */
    int badop = 0;         /* Indicates if a command line argument is wrong */
    SSL_CTX *ctx = NULL;      /* The SSL_CTX from which to create all SSL's */
    SSL *ssl = NULL;                           /* The SSL connection handle */
    FILE *out;                                         /* Standard out file */
    FILE *err;                                       /* Standard error file */
    char *connect_host;     /* A "hostname:port" string for server location */
    char *cipher = NULL;        /* Specific cipher suite list for handshake */
    int loop;                                               /* Loop counter */
    int done;               /* Flag to indicate the end of the server reply */
    int loop_max;             /* Number of repeated connections to one host */
    static unsigned char rand_seed[] = "A bad seed for software PRNG";
    int mode = R_LIB_CTX_FIPS140_MODE;            /* Library's default mode */
#ifndef NO_DISPLAY_CIPHER
    int display_cipher;
#endif

#ifdef USE_RCERT
    /* The unit test rcert_simple uses this source file as well and it
     * requires the default resource list to operate as it uses both 
     * sslcme and certcme code */
    R_RES_LIST *resource_list = PRODUCT_DEFAULT_RESOURCE_LIST();
#else /* USE_RCERT */
#ifdef SSLC_SMALL_CODE
    /* Simple requires tiny resource list to operate as minimal client */
    R_RES_LIST *resource_list = PRODUCT_SMALL_RESOURCE_LIST();
#else /* !SSLC_SMALL_CODE */
    R_RES_LIST *resource_list = PRODUCT_DEFAULT_RESOURCE_LIST();
#endif /* SSLC_SMALL_CODE */
#endif /* USE_RCERT */


#ifdef USE_UMALLOC
    /* Use the replacement memory allocation routines */
    R_set_mem_functions(user_malloc, user_realloc, user_free);
#endif

    /* Standard out and error file pointer assignments */
    out = stdout;
    err = stderr;

    /* By default, the host is specified to be the RSA site */
    connect_host = SIMPLE_CLIENT_SERVER_PORT_DEFAULT;

    /* By default, make a single connection to the host */
    loop_max = 1;

    /* Ignore the program name in the command line arguments */
    argc--;
    argv++;

    /* Process the program options */
    while (argc >= 1)
    {
        if (Strcmp(*argv, "-connect") == 0)
        {
            /* A new server is specified as the host */
            if (--argc < 1)
            {
                badop = 1;
                break;
            }

            connect_host = *(++argv);
        }
        else if (Strcmp(*argv, "-cipher") == 0)
        {
            /* A new server is specified as the host */
            if (--argc < 1)
            {
                badop = 1;
                break;
            }

            cipher = *(++argv);
        }
        else if (Strcmp(*argv, "-loop") == 0)
        {
            /*
             * A number of sequential connections to a single host is
             * set here. A negative value will mean no connections are
             * made. This is useful in order to check the behavior of
             * multiple client connections.
             */
            if (--argc < 1)
            {
                badop = 1;
                break;
            }

            loop_max = atoi(*(++argv));
        }
        else if (Strcmp(*argv, "-no_verify") == 0)
        {
            /*
             * Set the SSL options to ignore the results of the peer
             * certificate verification
             */
            no_verify = 1;
        }
        else if (Strcmp(*argv, "-no_output") == 0)
        {
            /*
             * Do not write the data received from the server in response
             * to the client request made by this code
             */
            no_output = 1;
        }
        else if (*argv[0] != '-')
        {
            /*
             * This is analogous to the '-connect' option except this provides
             * the host:port name directly as the switch
             */
            connect_host = *argv;
        }
        else if (Strcmp(*argv, "-no_fips140") == 0)
        {
            mode = R_LIB_CTX_STANDARD_MODE;
        }
        else
        {
            /*
             * All other options (include -h, -?, -help, etc.) are
             * unknown are will result in a usage message and program exit
             */
            fprintf(err, "Unknown option %s\n", *argv);
            fflush(err);
            badop = 1;
            break;
        }

        /* Decrement the argument counter and move to the next argument */
        argc--;
        argv++;
    }

    /* Deal with problems with the command line arguments */
    if (badop)
    {
        /* Print a usage message */
        fprintf(err, "usage: simple [options]\n");
        fprintf(err, " -connect <host>: connects to specified host\n");
        fprintf(err, " -cipher <list> : use the specified cipher suite(s)\n");
        fprintf(err, " -no_verify     : no verification is done\n");
        fprintf(err, " -loop <number> : loops for number times\n");
        fprintf(err, " -no_output     : no information is displayed\n");
        fprintf(err, " -no_fips140    : do not use FIPS140 crypto resources\n");
        fflush(err);

        /* Exit the program */
        goto end;
    }

    /* Initialize the SSL library using the default or non FIPS140 resources */
    if (PRODUCT_LIBRARY_NEW(resource_list, R_RES_FLAG_DEF, &lib_ctx) !=
        R_ERROR_NONE)
    {
        fprintf(err, "Unable to create library context\n");
        fflush(err);
        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)
    {
        fprintf(err, "Unable to seed the PRNG\n");
        fflush(err);
        goto end;
    }

#ifndef NO_TLS1
    /* Use TLS protocol version 1 */
    meth = TLSv1_client_method();
    fprintf(out, "Using the TLSv1 method\n");
    fflush(out);
#else
    /* Use SSL protocol version 3 */
    meth = SSLv3_client_method();
    fprintf(out, "Using the SSLv3 method\n");
    fflush(out);
#endif /* !NO_TLS1 */

    /* Create the SSL_CTX with the method */
    if ((ctx = SSL_CTX_new(meth)) == NULL)
    {
        fprintf(err, "Unable to create SSL_CTX\n");
        fflush(err);
        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(ctx, lib_ctx, mode);

    /* Set the cipher suite list if specified */
    if (cipher != NULL)
    {
        SSL_CTX_set_cipher_list(ctx, cipher);
    }

    /* Enable all vendor bug compatibility options */
    SSL_CTX_set_options(ctx, SSL_OP_ALL);

    /*
     * Reduce the initial size of the record write buffer. This is safe
     * to do in a client, but for interoperability in a server the buffer
     * is set to a higher default value.
     */
    SSL_CTX_set_write_buf_size(ctx, 4 * 1024);

    /* Set the protocol behavior in response to verification result */
    if (no_verify)
    {
        /*
         * This option means that the client connection will still attempt to
         * verify the certificate chain but then ignores the verification
         * result.(This is the default on the SSL_CTX.)
         */
        SSL_CTX_set_verify_mode(ctx, SSL_VERIFY_NONE);
    }
    else
    {
        /*
         * This option means the client will fail the handshake if it is unable
         * to verify the certificate chain
         */
        SSL_CTX_set_verify_mode(ctx, SSL_VERIFY_PEER);
    }

#ifndef NO_VERIFICATION
    /*
     * Full application verification routines are available. A choice can be
     * made to use the functioning verification routine or a dummy routine that
     * simply routines a "successful verification" result.
     */
    if (no_verify)
    {
        SSL_CTX_set_app_verify_cb(ctx, v_cb_dummy, NULL);
    }
    else
    {
        SSL_CTX_set_app_verify_cb(ctx, v_cb, NULL);
    }
#else /* !NO_VERIFICATION */
    /*
     * No functioning verification code is available in order to reduce static
     * memory size of the binary. The library will still try to verify incoming
     * server certificates so use the verification routine that always returns
     * true without doing any processing. No server verification will be
     * performed.
     */
    SSL_CTX_set_app_verify_cb(ctx, v_cb_dummy, NULL);
#endif /* !NO_VERIFICATION */

    /*
     * The client connection loop. Make the required number of connections to a
     * single server. Each connection will be made with a new SSL structure
     * rather than resetting a single SSL for each connection attempt.
     */
    for (loop = 0; loop < loop_max; loop++)
    {
        /* Free the existing SSL */
        if (ssl != NULL)
        {
            SSL_free(ssl);
        }

        if ((ssl = SSL_new(ctx)) == NULL)
        {
            fprintf(err, "Unable to create SSL structure\n");
            fflush(err);
            goto end;
        }

        fprintf(out, "Connecting to %s\n", connect_host);
        fflush(out);

        /* Make a socket connection to the server */
        if ((sock = SIO_connect(connect_host, NULL, 0, &sock)) < 0)
        {
            /* Exit the connection loop */
            fprintf(err, "Unable to make socket connection\n");
            fflush(err);
            goto end;
        }

        fprintf(out, "Connected to %s\n", connect_host);
        fflush(out);

        /* Associate the socket file descriptor with the SSL */
        SSL_set_fd(ssl, sock);

        /* Set the SSL to be a client-side connection */
        SSL_set_connect_state(ssl);

        /*
         * Make a request of the server. SSL_write() will perform the
         * handshake sequence before writing the request string.
         */
        n = Strlen(get_str);
        i = SSL_write(ssl, get_str, n);

        /* Handle errors from the write */
        if (i < n)
        {
            fprintf(err, "SSL_write error %08lX\r\n", ERR_peek_error());
            fflush(err);
            goto end;
        }

#ifndef NO_DISPLAY_CIPHER
        display_cipher = 0;
#endif
        done = 0;

        /*
         * Read the reply from the server until the connection breaks or there
         * is an error. The client does not know beforehand how much data the
         * server will send.
         */
        for (;;)
        {
            /* Read a buffer of data from the server */
            i = SSL_read(ssl, (char *)buf, sizeof(buf));

            switch (SSL_get_error(ssl, i))
            {
            case SSL_ERROR_SSL:
                fprintf(err, "Handshake failure - %lX\n", ERR_get_error());
                fflush(err);
                break;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
                /*
                 * This error type will be returned for delay back information.
                 * Retry the SSL read.
                 */
                continue;
            case SSL_ERROR_SYSCALL:
            case SSL_ERROR_ZERO_RETURN:
                done = 1;
                break;
            default:
                /* For example, successful read of a buffer of data */
                break;
            }

            /*
             * Leave the read loop when the connection breaks or if there is a
             * system error.
             */
            if (done)
            {
                break;
            }

#ifndef NO_DISPLAY_CIPHER
            /* Write extra connection information to stdout */
            if (!display_cipher)
            {
                display_cipher = 1;
                fprintf(out, "cipher=%s\n",
                    SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)));
                fprintf(out, "version=0x%x\n", SSL_version(ssl));
                fflush(out);
            }
#endif

            /* Write the data from the server to stdout */
            if (no_output == 0)
            {
                fwrite(buf, 1, i, out);
            }
        }

        /* Shut down the connection */
        SSL_shutdown(ssl);

        /* The socket is not closed */
        SIO_close(sock);
    }

    ret = R_ERROR_NONE;

end:

    if (ret != R_ERROR_NONE)
    {
        ERR_print_errors_fp(err);
    }

    if (ssl != NULL)
    {
        SSL_free(ssl);
    }

    if (ctx != NULL)
    {
        SSL_CTX_free(ctx);
    }

#ifndef NO_VERIFICATION
    v_cb_cleanup();
#endif /* !NO_VERIFICATION */

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

#ifdef USE_UMALLOC
    fprintf(err, "UMALLOC: max used: %ld\n", user_mem_max());
    fflush(err);
#endif /* USE_UMALLOC */

    return(R_ERROR_EXIT_CODE(ret));
}

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