RSA BSAFE Micro Edition Suite

Streamlined security for mobile and embedded devices

Search  Print

nbio_server.c

/* $Id: nbio_server.c,v 1.46 2005/07/12 22:53:48 ronl 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 "server_defaults.h"
#include "builtin_cert_pkey.h"      /* Built-in certificate and private key */
#include "debug_cb.h"                    /* BIO and SSL state dump callback */
#include "arguments.h"                      /* Program arguments processing */
#include "verify_cb.h"                                   /* Verify callback */
#include "non_blocking.h"                          /* Non-blocking function */

/* Global error output for program error reporting */
BIO *bio_err;

int main(int argc, char *argv[])
{
    int ret = R_ERROR_FAILED;                      /* Function return value */
    BIO *bio_ssl = NULL;                           /* BIO encapsulating SSL */
    BIO *bio_out = NULL;            /* Standard output for program feedback */
    BIO *bio_in = NULL;
    BIO *bio_con = NULL;                                  /* Connection BIO */
    char *port = SSL_SERVER_PORT_DEFAULT;            /* Default accept port */
    char *ciphers = NULL;                /* Cipher list, NULL means default */
    char *response = SSL_SERVER_RESPONSE_DEFAULT;     /* Response to client */
    static char buf[SSL_SERVER_DATA_BUFFER_LEN];  /* Application I/O buffer */
    int debug = 0;                              /* Print extra debug output */
    int state = 0;                                   /* Print the SSL state */
    int connections = SSL_SERVER_UNLIMITED_CONNECTIONS;
    int extras = 0;                         /* Extra command line arguments */
    int arg;                                            /* Argument counter */
    int offset;                                      /* Offset for the data */
    int len;                                /* Length of the request string */
    int bytes_read;                     /* Number of bytes read from client */
    int bytes_written;                 /* Number of bytes written to client */
    SSL_METHOD *meth=NULL;                  /* Pointer to server SSL method */
    SSL_CTX *ssl_ctx = NULL;                      /* Pointer to SSL context */
    R_LIB_CTX *lib_ctx = NULL;                /* Pointer to library context */
    SSLCERT *server_cert = NULL;           /* Pointer to server certificate */
    EVP_PKEY *pkey = NULL;                 /* Pointer to server private key */
    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 */

    /* 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 server arguments */
    if (server_parse_arguments(argc, argv, bio_err, bio_out, &port,
        &connections, &debug, &state, &meth, &extras, &off, &ciphers,
        &mode) == 1)
    {
        server_usage(bio_err, argv[0]);
        goto end;
    }

    /* Extra command line arguments are unwanted */
    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 usage and exit */
        server_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 server default method if it is not set */
    if (meth == NULL)
    {
        /* Select 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_server_method();
        BIO_printf(bio_out, "Doing TLSv1_server_method\n");
#elif (!defined(NO_SSL2) || defined(NO_SSL2IMPL)) && !defined(NO_SSL3)
        meth = SSLv23_server_method();
        BIO_printf(bio_out, "Doing SSLv23_server_method\n");
#elif !defined(NO_SSL3)
        meth = SSLv3_server_method();
        BIO_printf(bio_out, "Doing SSLv3_server_method\n");
#elif !defined(NO_SSL2)
        meth = SSLv2_server_method();
        BIO_printf(bio_out, "Doing SSLv2_server_method\n");
#else
        BIO_printf(bio_err, "Unable to set default client method.\n");
        goto end;
#endif
    }

    /* Create an 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);
    }

#ifndef SSLC_SMALL_CODE
    /*
     * Set the server temporary key generation mode. The temporary key
     * will be generated during the handshake.
     */
    if (!SSL_CTX_set_tmp_key_mode(ssl_ctx, SSL_TMP_512_DH|SSL_TMP_1024_DH|
        SSL_TMP_512_RSA, SSL_TMP_GENERATE_LATER))
    {
        BIO_printf(bio_err, "Unable to set temporary key mode\n");
        goto end;
    }
#endif /* !SSLC_SMALL_CODE */

    /* 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);

    BIO_printf(bio_out, "Server is using the builtin certificate\n");

    /* Load the built-in server certificate */
    server_cert = sslcert_get_certificate();

    /* Bind the certificate to the SSL context ssl_ctx */
    if (!SSL_CTX_use_certificate(ssl_ctx, server_cert))
    {
        BIO_printf(bio_err, "Unable to load server certificate\n");
        goto end;
    }

    BIO_printf(bio_out, "Server is using the builtin RSA key\n");

    /* Load the built-in server private key */
    pkey = get_rsa512_priv_server();

    /* Set the private key pkey for the SSL context ssl_ctx */
    if (!SSL_CTX_use_PrivateKey(ssl_ctx, pkey))
    {
        BIO_printf(bio_err, "Unable to load server private key\n");
        goto end;
    }

   /*
    * Check that the certificate and private key match. This is a common error
    * which should be avoided before starting the server, as without a
    * certificate and matching private key the SSL handshake cannot be
    * completed.
    */
    if (!SSL_CTX_check_private_key(ssl_ctx))
    {
        BIO_printf(bio_err, "Private key check failed\n");
        goto end;
    }

    /*
     * Create the SSL structure. 0 indicates the BIO will be for the SSL
     * server side protocol.
     */
    if ((bio_ssl = BIO_new_ssl(ssl_ctx, 0)) == NULL)
    {
        BIO_printf(bio_err, "Unable to create SSL structure\n");
        goto end;
    }

    /* Create a new accept BIO which listens on port */
    if ((bio_in = BIO_new_accept(port)) == NULL)
    {
        BIO_printf(bio_err, "Unable to create BIO on port: %s\n", port);
        goto end;
    }

    /*
     * When a new connection is accepted on bio_in, the SSL BIO in
     * bio_ssl is duplicated and has the new socket BIO pushed onto it.
     */
    BIO_set_accept_bios(bio_in, bio_ssl);

    if (debug)
    {
        BIO_set_cb(bio_in, bio_dump_cb);
        BIO_set_cb_arg(bio_in, (char *)bio_out);
    }

    /*
     * Sets up the accept socket by looking up the supplied host and port and
     * binding the socket to it
     */
    if (BIO_do_accept(bio_in) <= 0)
    {
        BIO_printf(bio_err, "Unable to set-up accept socket\n");
        goto end;
    }

    /*
     * Enter the web server loop which will allow a set number of connections
     * before exiting. The number of connections will be unlimited if the
     * connections number is negative.
     */
    while (connections != 0)
    {
        /* Decrement the connection count */
        connections--;

        BIO_printf(bio_out, "\nWaiting for new connection ...\n");

        /*
         * The first call to BIO_do_accept() sets up the socket for accepting
         * incoming connections. This call accepts the incoming connection.
         */
        if (BIO_do_accept(bio_in) <= 0)
        {
            BIO_printf(bio_err, "Unable to initialize server socket\n");
            goto end;
        }

        /* Pop the newly accepted connection */
        bio_con = BIO_pop(bio_in);

        /*
         * Switch on the non-blocking I/O flag for the connection BIO out
         */
        if (BIO_set_nbio(bio_con, 1) != 1)
        {
            BIO_printf(bio_err, "Unable to set non-blocking mode\n");
            goto end;
        }

        /*
         * Get the request from the client. As this sample uses non-blocking
         * sockets, we read data in until all bytes have been received,
         * or an error occurs. Note that the select_wait function is used
         * in a simplistic way.
         */
        for (;;)
        {
            /* Read a request from the client */
            bytes_read = BIO_read(bio_con, buf, sizeof(buf));

            if (bytes_read <= 0)
            {
                /*
                 * Indicates whether a temporary error occurred or a failure to
                 * complete the operation occurred
                 */
                if (BIO_should_retry(bio_con))
                {
                    if (debug)
                    {
                        BIO_printf(bio_out, "read DELAY\n");
                    }

                    /* Wait until the read can be accomplished */
                    select_wait(bio_con, debug, bio_out);
                    continue;
                }

                /* Exit the application if a read error occurred */
                goto end;
            }

            BIO_printf(bio_out, "RECEIVED: %s\n", buf);
            break;
        }

        /* Send a response to the client */
        BIO_printf(bio_out, "Sending response ...\n");

        /* Set the length of the response and the offset into it */
        len = Strlen(response);
        offset = 0;

        /*
         * Write the reply to the client. As this sample uses non-blocking
         * sockets, keep writing the data until completed and then close the
         * connection to the client. A more complete implementation would
         * use a protocol with message types and the number of Bytes to
         * send.
         */
        for (;;)
        {
            /* Send the data to the server and keep track of the data sent */
            bytes_written = BIO_write(bio_con, &response[offset], len);

          /*
                   * -1 indicates that an error occurred, or a temporary error, such as
                   * the server is busy, occurred and we need to retry later.
           */
            if (bytes_written <= 0)
            {
                /*
                 * Indicates whether a temporary error occurred or a failure
                 * to complete the operation occurred.
                 */
                if (BIO_should_retry(bio_con))
                {
                    if (debug)
                    {
                        BIO_printf(bio_out, "write DELAY\n");
                    }

                    /* Wait until the write can be accomplished */
                    select_wait(bio_in, debug, bio_out);
                    continue;
                }
                else
                {
                    /* Exit the application if a write error occurred */
                    goto end;
                }
            }

            offset += bytes_written;
            len -= bytes_written;

            /*
             * If there is no more data to write then sending the data has
             * been completed
             */
            if (len <= 0)
            {
                BIO_printf(bio_out, "Finished writing data to client\n");
                break;
            }
        }

        /* Finished so destroy the connection BIO */
        BIO_printf(bio_out, "Done\n");
        BIO_free_all(bio_con);
        bio_con = NULL;
    }

    ret = R_ERROR_NONE;

end:

    /* Display the error stack if an error occurred */
    if ((ret != R_ERROR_NONE) && (bio_err != NULL))
    {
        ERR_print_errors(bio_err);
    }

    /* Clean up */

    if (bio_con != NULL)
    {
        BIO_free_all(bio_con);
    }

    if (bio_in != NULL)
    {
        BIO_free(bio_in);
    }
    /*
     * If the malloc in BIO_new_accept() fails, bio_in will be NULL
     * but bio_ssl will have malloced memory which needs to be freed
     * If BIO_new_accept passes then the above free will also free 
     * memory allocated for bio_ssl.
     */
    else if (bio_ssl != NULL)
    {
        BIO_free(bio_ssl);
    }

    /*
     * The private key and certificate were generated as structures
     * in memory before being associated with the SSL. The application
     * therefore still has a reference to the private key and certificate
     * which must be freed now.
     */
    if (pkey != NULL)
    {
        SSLCERT_PKEY_free(pkey);
    }

    if (server_cert != NULL)
    {
        SSLCERT_free(server_cert);
    }

    if (ssl_ctx != NULL)
    {
        /*
         * Do not check for the return as no more memory freeing will be
         * performed
         */
        SSL_CTX_free(ssl_ctx);
    }

    /* Free the SSL library context */
    if (lib_ctx != NULL)
    {
        PRODUCT_LIBRARY_FREE(lib_ctx);
    }

    /* Free the BIO's for standard out and error */
    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