| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: nbio_client.c,v 1.45 2005/04/11 12:28:11 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" /* Default defines for client samples */ #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_out = NULL; /* Standard output for program feedback */ BIO *bio_ssl = NULL; /* BIO for non-blocking connection */ /* "host:port" string of the server location */ char *host = SSL_CLIENT_HOST_PORT_DEFAULT; char *ciphers = NULL; /* Cipher list, NULL means default */ char *server_req; /* Request for the server - this is the data sent */ int len; /* Length of the request string */ int offset; /* Offset for the data sent */ static char buf[SSL_CLIENT_DATA_BUFFER_LEN]; /* Buffer for server data */ int debug = 0; /* Print extra debug output */ int state = 0; /* Print the SSL state */ int extras = 0; /* Extra command line arguments */ int bytes_read; /* Number bytes read from server */ int bytes_written; /* Number bytes written to server */ SSL_METHOD *meth = NULL; /* Pointer to server SSL method */ SSL_CTX *ssl_ctx = NULL; /* Pointer to SSL context */ SSL *ssl = NULL; /* SSL reference for setting debug callback */ R_LIB_CTX *lib_ctx = NULL; /* Pointer to library context */ int arg; /* Argument counter */ 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; } /* 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; } } /* Print the program usage messages */ 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 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); } /* 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); /* * Set the verification callback used by the application. This replaces * the internal verification. Note that v_cb_dummy is a verification * callback which ignores any input. */ SSL_CTX_set_app_verify_cb(ssl_ctx, v_cb_dummy, NULL); /* * Set the verification mode for the context. Verification is not required * here, so set it to none. */ SSL_CTX_set_verify_mode(ssl_ctx, SSL_VERIFY_NONE); /* * Create a BIO that handles the connection and set up of transparent SSL * handling which encapsulates the socket setup. */ if ((bio_ssl = BIO_new_ssl_connect(ssl_ctx)) == NULL) { BIO_printf(bio_err, "Unable to create an SSL object\n"); goto end; } /* Set the host name value for the connection BIO bio_ssl */ BIO_set_conn_hostname(bio_ssl, host); /* * Switch on the non-blocking I/O flag for the connection BIO bio_ssl */ if (BIO_set_nbio(bio_ssl, 1) != 1) { BIO_printf(bio_err, "Unable to set non-blocking mode\n"); goto end; } /* * Set debug on both the SSL and the BIO in order to obtain output both * during the handshake as well as after the handshake has finished */ if (debug) { BIO_get_ssl(bio_ssl, &ssl); BIO_set_cb(bio_ssl, bio_dump_cb); BIO_set_cb_arg(bio_ssl, (char *)bio_out); BIO_set_cb(SSL_get_rbio(ssl), bio_dump_cb); BIO_set_cb_arg(SSL_get_rbio(ssl), (char *)bio_out); } /* * The command to send to the web server is a simple HTTP command to get * the top-level page */ server_req = SSL_CLIENT_REQUEST_DEFAULT; len = Strlen(server_req); for (;;) { /* * Do a handshake with the server to create a connection, can subsequently * make changes to the type of socket connection as desired. */ retVal = BIO_do_connect(bio_ssl); if (retVal == 1) { /* The handshake has completed successfully */ break; } /* * -1 indicates that an error occurred, or a temporary error, such as * the server is busy, occurred and we need to retry later. */ if (retVal <= 0) { /* * Indicates whether a temporary error occurred or a failure to * complete the operation occurred */ if ((ret = BIO_should_retry(bio_ssl))) { if (debug) { BIO_printf(bio_out, "write DELAY\n"); } /* Wait until the write can be accomplished */ select_wait(bio_ssl, debug, bio_out); continue; } else { /* If not a retry then it is an error */ ret = R_ERROR_FAILED; goto end; } } } /* * Set the KEEPALIVE socket option, need to get socket fd first * Also check the option was correctly set */ retVal = BIO_get_fd(bio_ssl, &sock_fd); 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"); } } /* * Send the request to the server. As this sample uses non-blocking * sockets, we send data in until all Bytes have been written to the * server, or an error occurs. Note that the select_wait function is used * in a simplistic way. Also note that the SSL BIO "hides" the handshake * messages that are sent to the server to establish the secure channel. */ offset = 0; for (;;) { /* Send the request to the server */ bytes_written = BIO_write(bio_ssl, &(server_req[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 ((ret = BIO_should_retry(bio_ssl))) { if (debug) { BIO_printf(bio_out, "write DELAY\n"); } /* Wait until the write can be accomplished */ select_wait(bio_ssl, debug, bio_out); continue; } else { /* If not a retry then it is an error */ ret = R_ERROR_FAILED; goto end; } } /* The write request succeeded in writing some Bytes */ offset += bytes_written; len -= bytes_written; /* * If there is no more data to write, the request sending has been * completed */ if (len <= 0) { BIO_printf(bio_out, "Finished writing request to server\n"); break; } } /* * Read the reply from the server and display it on stdout. As this sample * uses non-blocking sockets, read data in until the server closes the * connection. A more complete implementation would have a protocol with * message types and Bytes sent. */ for (;;) { bytes_read = BIO_read(bio_ssl, buf, sizeof(buf)); /* A zero return indicates end-of-file (close-of-socket) */ if (bytes_read == 0) { break; } if (bytes_read < 0) { /* * Check to see if the reason was something that should be retried * later. */ if (BIO_should_retry(bio_ssl)) { if (debug) { BIO_printf(bio_out, "read DELAY\n"); } /* Wait until the server has sent information */ select_wait(bio_ssl, debug, bio_out); continue; } else { /* * If not a retry then it is an error, or the server has * closed the connection */ goto end; } } BIO_printf(bio_out, "RECEIVED: %s\n", buf); } /* Report that the request and reply were successful */ BIO_printf(bio_out, "Connection completed successfully\n"); ret = R_ERROR_NONE; end: /* If there was an error display the error stack */ if ((ret != R_ERROR_NONE) && (bio_err != NULL)) { ERR_print_errors(bio_err); } /* Clean up time */ if (bio_ssl != NULL) { BIO_free_all(bio_ssl); } /* Free the SSL_CTX */ if (ssl_ctx != NULL) { SSL_CTX_free(ssl_ctx); } /* Clean up the library */ PRODUCT_LIBRARY_FREE(lib_ctx); /* Free the standard output and error BIOs */ if (bio_out != NULL) { BIO_free_all(bio_out); } if (bio_err != NULL) { BIO_free(bio_err); bio_err = NULL; } return(R_ERROR_EXIT_CODE(ret)); }