| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: cache_server.c,v 1.34 2005/07/14 04:28:59 gsingh 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" /* The default values for server samples */ #include "builtin_cert_pkey.h" /* The built-in certificate & private key */ #include "debug_cb.h" /* The BIO and SSL state dump callback */ #include "arguments.h" /* The program arguments processing */ #include "app_cache.h" /* The external session cache */ /* * Macro constants */ #define SSL_CACHE_SERVER_QUIT_CMD "exit" /* * Global Variable Definitions */ BIO *bio_err; /* * Local function prototype declarations */ static void cache_server_usage(BIO *bio); static void print_connection_source(BIO *accept_bio, BIO *bio_out); int main(int argc, char *argv[]) { int ret = R_ERROR_FAILED; /* The function return value */ BIO *bio = NULL; BIO *bio_out = NULL; BIO *abio = NULL; int bytes_read; /* The number of Bytes read from client */ int ssl_err; /* The ssl error identifier */ char *port = SSL_SERVER_PORT_DEFAULT; /* The port to accept clients */ char *ciphers = NULL; /*The cipher list. Null means default */ static char buf[SSL_SERVER_DATA_BUFFER_LEN]; /* Application I/O buffer */ int debug = 0; /* The switch for extra debug output */ int state = 0; /* The print the SSL state */ int extras = 0; /* The extra command line arguments */ int connections = SSL_SERVER_UNLIMITED_CONNECTIONS; /* Connection count */ SSL *ssl = NULL; /* The ssl connection object */ SSL_METHOD *meth = NULL; /* The pointer to server SSL method */ SSL_CTX *ssl_ctx = NULL; /* The pointer to SSL context */ R_LIB_CTX *lib_ctx = NULL; /* The pointer to library context */ SSLCERT *server_cert = NULL; /* The pointer to server certificate */ EVP_PKEY *pkey = NULL; /* The pointer to server private key */ SESS_CACHE *session_cache = NULL; /* The SSL session cache */ int arg = 0; /* The command line argument counter */ int bad_arg = 0; /* The flag for improper argument */ int no_flush = 0; /* Flag to stop session cache flushing */ int no_cache = 0; /* The flag to stop session caching */ long session_timeout = 0; /* The valid session lifetime */ int cache_mode; /* The session cache mode */ int cmd_len; /* The length of expected command data */ 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 general server arguments */ if (server_parse_arguments(argc, argv, bio_err, bio_out, &port, &connections, &debug, &state, &meth, &extras, &off, &ciphers, &mode) == 1) { /* An error in the general arguments so report usage and exit */ server_usage(bio_err, argv[0]); cache_server_usage(bio_err); goto end; } /* Parse application-specific server arguments */ if (extras > 0) { for (arg = 1; arg < argc; arg++) { /* Skip the NULL arguments */ if (argv[arg] == NULL) { continue; } /* Process specific arguments */ if (Strcmp(argv[arg], "-no_cache") == 0) { /* Stop flushing the session cache */ no_cache = 1; } else if (Strcmp(argv[arg], "-no_flush") == 0) { /* Stop flushing the session cache */ no_flush = 1; } else if (Strcmp(argv[arg], "-timeout") == 0) { /* Assign a default session timeout */ if (++arg >= argc) { bad_arg = 1; } else { session_timeout = atol(argv[arg]); } } else { bad_arg = 1; } if (bad_arg == 1) { /* Report specific problem */ BIO_printf(bio_err, "\nUnknown argument : %s\n", argv[arg]); /* Report program usage and exit */ server_usage(bio_err, argv[0]); cache_server_usage(bio_err); 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 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_server_method(); BIO_printf(bio_out, "Doing TLSv1_server_method\n"); #elif !defined(NO_SSL2) && !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 server 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); /* Load the built-in server certificate */ BIO_printf(bio_out, "Server is using the built-in certificate\n"); server_cert = sslcert_get_certificate(); if (!SSL_CTX_use_certificate(ssl_ctx, server_cert)) { BIO_printf(bio_err, "Unable to load server certificate\n"); goto end; } /* Load the built-in server private key */ BIO_printf(bio_out, "Server is using the built-in RSA key\n"); pkey = get_rsa512_priv_server(); 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; } /* Set the external session caching code */ if (!no_cache) { #ifdef SSLC_SMALL_CODE /* * For multithreaded programs the session cache must be protected * against simultaneous updates. For these programs you must add a call * to R_lock_set_cb() to provide a thread locking mechanism. Typically * the locking mechanism is platform-dependent. See the * multithreading sample code for calls to APP_locking_cb_add(). */ /* Initialize the local session cache */ if (APP_session_cache_init(&session_cache) == 0) { BIO_printf(bio_err, "Session cache initialization failed\n"); goto end; } /* Set the session cache callback */ SSL_CTX_set_session_cache_cb(ssl_ctx, APP_session_cache_cb); /* Set the session cache structure as the callback argument */ SSL_CTX_set_session_cache_cb_arg(ssl_ctx, session_cache); #endif /* SSLC_SMALL_CODE */ /* Set the caching flags on the SSL_CTX */ cache_mode = SSL_SESS_CACHE_SERVER; if (no_flush) { cache_mode |= SSL_SESS_CACHE_NO_AUTO_CLEAR; } SSL_CTX_set_session_cache_mode(ssl_ctx, cache_mode); } /* Set the given session timeout on the SSL_CTX */ SSL_CTX_set_timeout(ssl_ctx, session_timeout); /* Set up an accept BIO to simplify the process of setting up a server */ if ((abio = BIO_new_accept(port)) == NULL) { BIO_printf(bio_err, "Unable to create accept BIO\n"); goto end; } else { BIO_printf(bio_out, "Creating an accept BIO\n"); } /* Allow for easy restart of the server accept socket */ BIO_set_bind_mode(abio, SIO_BIND_REUSEADDR); /* * The first call to BIO_do_accept() sets up the socket for accepting * incoming connections by establishing a listener */ if (BIO_do_accept(abio) <= 0) { BIO_printf(bio_err, "Unable to initialize server socket\n"); goto end; } /* * In debug mode add a BIO callback to output all data passing through the * connection. This can be done on the accept socket because the callback * reference is passed on to the accepting socket. */ if (debug) { BIO_set_cb(abio, bio_dump_cb); BIO_set_cb_arg(abio, (char *)bio_out); } /* * Create the SSL structure. Defaults are inherited from the SSL_CTX. * Options are usually set against the SSL_CTX with those requiring * modification set against the SSL. */ if ((ssl = SSL_new(ssl_ctx)) == NULL) { BIO_printf(bio_err, "Unable to create SSL structure\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 number of connections is negative. */ while (connections != 0) { BIO_flush(bio_out); BIO_flush(bio_err); /* Decrement the connection count */ connections--; BIO_printf(bio_out, "\nWaiting for new connection ...\n"); /* Accept a new client socket connection */ if (BIO_do_accept(abio) < 0) { /* The accept of a new socket failed */ BIO_printf(bio_err, "Accept of client connection failed\n"); ERR_print_errors(bio_err); /* Attempt a new socket connection */ continue; } /* Pop the newly generated BIO from the listener BIO */ bio = BIO_pop(abio); /* Print feedback about the socket connection */ print_connection_source(bio, bio_out); /* * The BIO is taken by SSL_set_bio(). It does not need to be freed * as this is performed during the SSL clean up. */ SSL_set_bio(ssl, bio, bio); /* * Instruct the SSL layer to perform the server-side of the protocol * when SSL_do_handshake() is called. SSL_do_handshake() is implicitly * called by SSL_read()/SSL_write(). Note that SSL_connect() or * SSL_accept() explicitly do the client- or server-side handshake * functions. */ SSL_set_accept_state(ssl); /* * If the cached session identifier is not cleared, the SSL uses the * protocol version of the last connection. Here the session * identifier is cleared and reverts to the protocol specified by the * SSL_CTX. */ SSL_set_session(ssl, NULL); /* * Reset the SSL connection deals to be in an "initial" state * ready to begin another handshake. */ SSL_clear(ssl); /* * Call SSL_read() to perform the full handshake. Read less than a full * buffer here to allow for processing later. */ bytes_read = SSL_read(ssl, buf, SSL_SERVER_DATA_BUFFER_LEN - 1); /* * Categorize the return value from the SSL call to determine how to * deal with the error. This call also works when the SSL return code * does not indicate an error in which case SSL_ERROR_NONE is always * returned here. */ ssl_err = SSL_get_error(ssl, bytes_read); switch (ssl_err) { case SSL_ERROR_NONE: /* Handshake finished, so continue parsing the data */ break; case SSL_ERROR_SSL: /* Handshake error, report and exit */ BIO_printf(bio_out, "Handshake failure\n"); ERR_print_errors(bio_out); goto close_connection; case SSL_ERROR_ZERO_RETURN: break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_SYSCALL: default: /* * System call error. This error is different from the * SSL_ERROR_SSL in that errno (under unix) has the numeric error * value, and is not converted into text. If calling SSL_read() * or SSL_write() there is no recorded error in the error logging * because the error could be able to be retried, about which the * library is unaware. */ BIO_printf(bio_out, "System call error (ssl_err=%d,ret=%d,errno=%d)\n", ssl_err, bytes_read, R_os_get_last_sys_error()); ERR_print_errors(bio_out); goto close_connection; break; } /* Process the data read from the client */ /* * NULL-terminate the buffer of data read from the client. This sample * assumes that the server has just read text data. */ buf[bytes_read] = '\0'; /* Report feedback to the standard output */ BIO_printf(bio_out, "Received from client '%s'\n", buf); /* Check for a quit signal from the client */ cmd_len = Strlen(SSL_CACHE_SERVER_QUIT_CMD); if (Strncmp(SSL_CACHE_SERVER_QUIT_CMD, buf, cmd_len) == 0) { /* Exit immediately from the connection loop */ BIO_printf(bio_out, "Received quit from client\n"); connections = 0; } /* No reply to client is made here */ close_connection: /* Close the SSL connection */ SSL_shutdown(ssl); /* Report the connection close to standard output */ BIO_printf(bio_out, "Done\n"); BIO_flush(bio_out); } /* Set program success */ ret = R_ERROR_NONE; end: /* On error output the error stack */ if ((ret != R_ERROR_NONE) && (bio_err != NULL)) { ERR_print_errors(bio_err); } /* Free memory for all structures */ if (ssl != NULL) { SSL_free(ssl); } if (abio != NULL) { BIO_free(abio); } if (!no_cache) { /* In debug mode output the cache statistics */ if (debug) { APP_session_cache_stats(session_cache, bio_out); } /* Finalize the application cache */ if (APP_session_cache_final(session_cache)) { session_cache = NULL; } /* The finalization routine frees all the lock names */ } /* * 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 BIOs for standard out and error */ BIO_flush(bio_out); BIO_flush(bio_err); 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)); } static void cache_server_usage(BIO *bio) { BIO_printf(bio, "Where specific arguments are :\n"); BIO_printf(bio, "-no_cache - No session caching\n"); BIO_printf(bio, "-no_flush - No session cache flushing\n"); BIO_printf(bio, "-timeout arg - Reset the default session lifetime\n"); return; } static void print_connection_source(BIO *accept_bio, BIO *bio_out) { int sock; /* The socket file descriptor */ unsigned long len; /* The length of socket data structure */ struct sockaddr_in name; /* The socket data structure */ static char hostbuf[128]; /* The buffer for the host name string */ int hostlen = 0; /* The length of the hostname string */ int i; /* Extract the file descriptor from the BIO */ BIO_get_fd(accept_bio, &sock); /* Make a socket call to retrieve the socket address details */ len = (unsigned long)sizeof(name); i = SIO_getsockname(sock, (struct sockaddr *) &name, &len); /* Retrieve the peer host name from the address */ if (i == 0) { /* Initialize the hostname string to be empty */ hostbuf[0] = '\0'; hostlen = SIO_gethostbyaddr((unsigned char *) &name, len, hostbuf, sizeof(hostbuf)); } else { BIO_printf(bio_out, "getsockname failed - %d\n", R_os_get_last_sys_error()); } /* Report the host name if it was found */ if (hostlen != 0) { BIO_printf(bio_out, "New connection from %s\n", hostbuf); } else { BIO_printf(bio_out, "New connection\n"); } return; }