| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $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)); }