| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: sock_client.c,v 1.48 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 values for client samples */ #include "debug_cb.h" /* BIO and SSL state dump callback */ #include "arguments.h" /* Program arguments processing */ #include "print_con_info.h" /* Print connection information */ 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 */ char *host = NULL; /* "host:port" string of the server location */ char *ciphers = NULL; /* Cipher list, NULL means default */ /* Request for the server - this is the data sent */ char *server_req = SSL_CLIENT_REQUEST_DEFAULT; int len; /* Length of the request string */ 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 arg; /* Argument counter */ int bytes_read; /* Number bytes read from server */ int bytes_written; /* Number bytes written to server */ SIO_SOCK sock = INVALID_SOCKET; /* Socket connection to the server */ SSL *ssl = NULL; /* SSL connection object */ 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 */ 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 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 client application arguments */ if (client_parse_arguments(argc, argv, bio_err, bio_out, &host, &debug, &state, &meth, &extras, &off, &ciphers, &mode) == 1) { /* Exit immediately on failure to parse command line */ client_usage(bio_err, argv[0]); goto end; } /* Extra command line arguments are an error */ 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 program usage and exit */ client_usage(bio_err, argv[0]); goto end; } /* Set a server location if it is not already set */ if (host == NULL) { host = SSL_CLIENT_HOST_PORT_DEFAULT; } /* 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_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); /* Create a socket connection to the server */ if (SIO_connect(host, NULL, 0, &sock) < 0) { BIO_printf(bio_err, "Unable to create socket connection\n"); goto end; } /* * Prepare a simple HTTP command string to send to the server. It will * get the server's top-level page if it is a web server. The socket * server sample will return a simple string message. */ len = Strlen(server_req); /* Create a new SSL structure */ if ((ssl = (SSL *)SSL_new(ssl_ctx)) == NULL) { BIO_printf(bio_err, "Unable to create SSL structure\n"); goto end; } /* Assign the SSL's read and write source to be the socket connection */ SSL_set_fd(ssl, sock); /* * Set the KEEPALIVE socket option and check option was correctly set. */ sockOptVal = 1; /* non zero value to turn option on */ optLen = sizeof(sockOptVal); retVal = SIO_setsockopt(sock, 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, 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"); } } /* Set the client state on the SSL */ SSL_set_connect_state(ssl); /* * In debug mode add the callback to print BIO data to the read BIO of the * SSL. This will give a trace of all data read by the SSL. */ if (debug) { BIO_set_cb(SSL_get_rbio(ssl), bio_dump_cb); BIO_set_cb_arg(SSL_get_rbio(ssl), (char *)bio_out); } /* * Send the request to the server. SSL_read()/SSL_write() will perform * a handshake with the server to establish the secure channel before * sending the data (that is, the server request string). */ bytes_written = SSL_write(ssl, server_req, len); /* * A blocking socket will write all Bytes or will return an error. * Report the error case and exit immediately. */ if (bytes_written <= 0) { BIO_printf(bio_err, "SSL write to server failed\n"); goto end; } /* Read the server's reply and display to the standard output BIO */ for (;;) { /* * Read a buffer full of data or a record from the server; whichever * is smaller. Leave space for a NULL character at the end of the * buffer. */ bytes_read = SSL_read(ssl, buf, SSL_CLIENT_DATA_BUFFER_LEN - 1); /* * A blocking connection will return zero if an SSL shutdown * is received. */ if (bytes_read == 0) { BIO_printf(bio_out, "Server connection closed\n"); break; } /* Report and exit immediately if there is an error */ if (bytes_read < 0) { BIO_printf(bio_err, "SSL read from server failed\n"); goto end; } /* * Ensure the buffer ends in a NULL and then print it to the * standard output. The assumption is that the buffer is text data. */ buf[bytes_read] = '\0'; BIO_printf(bio_out, "RECEIVED: %s\n", buf); } /* Report that the request and reply were successful */ BIO_printf(bio_out, "Connection completed succesfully\n"); /* Print details on the SSL connection. */ print_connection_info(bio_out, ssl); /* Set the program success code */ ret = R_ERROR_NONE; end: if ((ret != R_ERROR_NONE) && (bio_err != NULL)) { /* Display the error stack on error */ ERR_print_errors(bio_err); } /* Shutdown down the SSL connection then free the SSL */ if (ssl != NULL) { SSL_shutdown(ssl); SSL_free(ssl); } /* Close the socket to the server */ if (sock != INVALID_SOCKET) { /* Close the socket to the server */ SIO_close(sock); } /* Free the SSL_CTX */ if (ssl_ctx != NULL) { SSL_CTX_free(ssl_ctx); } /* Clean up the library */ if (lib_ctx != NULL) { 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)); }