| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: ssl_client.c,v 1.55 2005/04/11 12:28:12 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" /* The default values for client samples */ #include "debug_cb.h" /* The BIO and SSL state dump callback */ #include "arguments.h" /* The program arguments processing */ #include "print_con_info.h" /* The print connection information */ #ifndef SSLC_SMALL_CODE int verification_setup(SSL_CTX *ctx,BIO *bio); #endif /* SSLC_SMALL_CODE */ BIO *bio_err; int main(int argc, char *argv[]) { int ret = R_ERROR_FAILED; /* The function return value */ BIO *sock_bio; /* The connect BIO for the SSL */ BIO *bio_out = NULL; /* The BIO for standard output */ int done = 0; int temp; /* A temporary variable to use for misc. operations */ int bytes_read; /* The number of Bytes read from server */ int bytes_written; /* The number of Bytes written to server */ int count; /* Number of peer certificates received */ int debug = 0; /* Flag to allow extra debug information */ int state = 0; /* Print the SSL state */ char *host = SSL_CLIENT_HOST_PORT_DEFAULT; /* The port for server */ char *path = SSL_CLIENT_REQUEST_DEFAULT; /* The URL to retrieve */ char *ciphers = NULL; /* The cipher list. NULL means default */ int extras = 0; /* Extra command line arguments */ int arg; /* The argument counter */ static char buf[SSL_CLIENT_DATA_BUFFER_LEN]; SSL *ssl=NULL; SSL_METHOD *meth = NULL; /* The pointer for the server SSL method */ SSL_CTX *ssl_ctx = NULL; /* The pointer to the SSL context */ R_LIB_CTX *lib_ctx = NULL; /* The pointer to the 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 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; } /* An error for extra command line arguments */ 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; } /* 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 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); #ifndef SSLC_SMALL_CODE /* Set up the verification callbacks. See ssl_verify.c. */ verification_setup(ssl_ctx, bio_out); #endif /* SSLC_SMALL_CODE */ /* * Create the SSL structure. Defaults are inherited from the SSL_CTX. * Options are usually set against the SSL_CTX, and only those that * require connection-specific changes are set against the SSL. */ if ((ssl = SSL_new(ssl_ctx)) == NULL) { BIO_printf(bio_err, "Unable to create SSL\n"); goto end; } /* * An SSL exists so set the SSL to be a client-side implementation. When an * SSL_do_handshake() is performed it will know which handshake function to * run. If SSL_connect() or SSL_accept() is used, the side of the * connection will be set as part of the handshake process. * SSL_set_accept_state() is used to set the SSL to do a server-side * handshake. */ SSL_set_connect_state(ssl); /* * Create a BIO to handle the set up and connection of a connection * socket. The connect BIO encapsulates a socket and some state * information to hide the details of a socket connection. This may also be * done manually using the "standard" socket API calls. This call does not * make a connection to the host and just sets the host details in the BIO. */ if ((sock_bio = BIO_new_connect(host)) == NULL) { BIO_printf(bio_err, "Unable to create BIO connection\n"); goto end; } /* * Use the newly created connect BIO. At this point, the connect BIO has * not checked whether the hostname is valid. The check and the connection * are made the first time the SSL protocol attempts to read/write some * data. Call BIO_do_handshake() or BIO_do_connect() to perform the * "connect operations". */ SSL_set_bio(ssl, sock_bio, sock_bio); /* * In debug mode add the BIO callback function to output all data * that passes through the BIO */ if (debug) { BIO_set_cb(sock_bio, bio_dump_cb); BIO_set_cb_arg(sock_bio, (char *)bio_out); } /* Main client handshake loop */ while (!done) { /* * Perform the handshake. This activates the underlying connect BIO * and a socket connection will be made. */ temp = SSL_do_handshake(ssl); /* The SSL_get_error() call categorizes errors into groups */ switch (SSL_get_error(ssl, temp)) { case SSL_ERROR_NONE: /* The handshake has finished successfully */ done = 1; break; case SSL_ERROR_SSL: /* Handshake error - report and exit */ BIO_printf(bio_err, "Handshake failure\n"); goto end; case SSL_ERROR_SYSCALL: /* * System call error. This error is different from * the SSL_ERROR_SSL in that errno (under unix) has the numeric * error value, and it is not converted into text. If doing an * SSL_read() or SSL_write() there is no recorded error in the * error logging. This is because the error could be a retry * error of which the library is unaware. */ BIO_printf(bio_err, "System call error = %d\n", R_os_get_last_sys_error()); goto end; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: /* * Perform the handshake again. These errors are normally only * reported when doing non-blocking I/O. */ #if !defined(NO_SLEEP) BIO_printf(bio_out, "sleep(1)\n"); R_sleep(1); #endif break; case SSL_ERROR_ZERO_RETURN: /* * A read(2)/write(2) system call returned 0 (usually because * the socket was closed). If the socket is closed, the protocol * has failed. */ BIO_printf(bio_out, "socket closed\n"); goto end; } } /* * Completion of the handshake, can subsequently make changes to the * type of socket connection as desired. Get the socket fd first, set * the KEEPALIVE socket option, then check option was correctly set. */ sock_fd = SSL_get_fd(ssl); 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"); } } /* * The SSL handshake is finished at this point. For demonstration purposes * print out all connection information. */ BIO_printf(bio_out, "SSL connection completed\n"); /* * The peer may have returned a 'certificate chain'. Normally this * is the server certificate. If the CA certificate was sent, it * must be available locally in order to verify that the CA certificate * is true or the application must decide to trust it. */ count = SSL_get_peer_cert_chain_count(ssl); BIO_printf(bio_out, "\nCertificate Chain: %d items\n", count); /* * An application can walk the chain and print certificate details * at this point */ /* Print details on the SSL connection. Ignore the return code. */ print_connection_info(bio_out, ssl); /* A connection exists. Request some information and read the response. */ /* * Determine the number of Bytes to write. This does not include the * trailing null (no special reason for this). */ temp = Strlen(path); /* * For blocking I/O this call will return with an error or with the total * number of Bytes written. For non-blocking I/O it is possible the * function will return -1 for a blocked write operation and the write * should be in a retry loop. */ bytes_written = SSL_write(ssl, path, temp); /* * Write the data and exit on error. In real applications, additional error * handling would be performed. */ if (bytes_written <= 0) { BIO_printf(bio_err, "Write error\n"); goto end; } /* * Read data in blocks of the size of the buffer from the other side * of the protocol */ for (;;) { /* Read an application buffer of data from the server */ bytes_read = SSL_read(ssl, buf, sizeof(buf)); /* * Perform minimal error processing. Exit the read loop only if * the connection breaks (i == 0) or there is an error (i < 0). * In this case, this is the crude method that the client uses * to determine it has finished reading data. */ if (bytes_read <= 0) { break; } /* Write the data to stdout */ BIO_write(bio_out, buf, bytes_read); } /* * Send an SSL_shutdown() message. If called again, the connection * attempts to read a shutdown message from the remote end. Normally * either the shutdown message is received or the socket is closed. * If the other end does not understand shutdown messages (for example * with some versions of MSIE), neither of these occur. */ SSL_shutdown(ssl); /* Set the program success flag */ ret = R_ERROR_NONE; end: /* Error handling */ if ((ret != R_ERROR_NONE) && (bio_err != NULL)) { /* Display the error stack on standard error */ ERR_print_errors(bio_err); } /* Clean up the allocated structures */ if (ssl != NULL) { SSL_free(ssl); } if (ssl_ctx != NULL) { /* * Do not check the return code. No more work is done in * order to free this memory. */ SSL_CTX_free(ssl_ctx); } if (lib_ctx != NULL) { PRODUCT_LIBRARY_FREE(lib_ctx); } /* Free the BIOs used for standard outputs */ 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)); }