| RSA BSAFE Micro Edition Suite |
Streamlined security for mobile and embedded devices |
 
![]() |
/* $Id: simple.c,v 1.105.2.1 2005/11/22 00:30:15 lmalmborg 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. */ /* * Notes about compilation directives used in this code. * * The client program can minimize its memory usage by using compilation * directives to limit the code that is compiled. These directives are: * * NO_VERIFICATION == A verification routine that simply returns a * success code is used. This can introduce a * security vulnerability and is not generally * recommended. * NO_DISPLAY_CIPHER == Cipher name feedback is not printed out. This * causes no security issues. */ #include "r_prod.h" /* Load in the non-dynamic malloc routines if required */ #ifdef USE_UMALLOC #include "u_malloc.h" #include "u_malloc.c" #endif #include "verify_cb.h" /* * Enable these directives to reduce code size of this application. * Check the compilation directives note above for security implications. */ #if 0 #define NO_DISPLAY_CIPHER #endif #if 0 #define NO_VERIFICATION #endif /* * Macro constants */ #define SIMPLE_CLIENT_SERVER_PORT_DEFAULT "www.rsasecurity.com:443" #define SIMPLE_CLIENT_SERVER_REQUEST_STR "HEAD / HTTP/1.0\r\n\r\n" #define SIMPLE_DATA_BUFFER_LEN 512 int main(int argc, char *argv[]) { int ret = R_ERROR_FAILED; /* Program return code */ R_LIB_CTX *lib_ctx = NULL; /* Library context */ SSL_METHOD *meth = NULL; /* Protocol method */ static char *get_str = SIMPLE_CLIENT_SERVER_REQUEST_STR; unsigned char buf[SIMPLE_DATA_BUFFER_LEN]; /* Buffer for IO processing */ SIO_SOCK sock; /* Socket connection */ int i; int n; int no_verify = 0; /* Prevents meaningful verification of server cert */ int no_output = 0; /* Prevents reports of server data to stdout */ int badop = 0; /* Indicates if a command line argument is wrong */ SSL_CTX *ctx = NULL; /* The SSL_CTX from which to create all SSL's */ SSL *ssl = NULL; /* The SSL connection handle */ FILE *out; /* Standard out file */ FILE *err; /* Standard error file */ char *connect_host; /* A "hostname:port" string for server location */ char *cipher = NULL; /* Specific cipher suite list for handshake */ int loop; /* Loop counter */ int done; /* Flag to indicate the end of the server reply */ int loop_max; /* Number of repeated connections to one host */ static unsigned char rand_seed[] = "A bad seed for software PRNG"; int mode = R_LIB_CTX_FIPS140_MODE; /* Library's default mode */ #ifndef NO_DISPLAY_CIPHER int display_cipher; #endif #ifdef USE_RCERT /* The unit test rcert_simple uses this source file as well and it * requires the default resource list to operate as it uses both * sslcme and certcme code */ R_RES_LIST *resource_list = PRODUCT_DEFAULT_RESOURCE_LIST(); #else /* USE_RCERT */ #ifdef SSLC_SMALL_CODE /* Simple requires tiny resource list to operate as minimal client */ R_RES_LIST *resource_list = PRODUCT_SMALL_RESOURCE_LIST(); #else /* !SSLC_SMALL_CODE */ R_RES_LIST *resource_list = PRODUCT_DEFAULT_RESOURCE_LIST(); #endif /* SSLC_SMALL_CODE */ #endif /* USE_RCERT */ #ifdef USE_UMALLOC /* Use the replacement memory allocation routines */ R_set_mem_functions(user_malloc, user_realloc, user_free); #endif /* Standard out and error file pointer assignments */ out = stdout; err = stderr; /* By default, the host is specified to be the RSA site */ connect_host = SIMPLE_CLIENT_SERVER_PORT_DEFAULT; /* By default, make a single connection to the host */ loop_max = 1; /* Ignore the program name in the command line arguments */ argc--; argv++; /* Process the program options */ while (argc >= 1) { if (Strcmp(*argv, "-connect") == 0) { /* A new server is specified as the host */ if (--argc < 1) { badop = 1; break; } connect_host = *(++argv); } else if (Strcmp(*argv, "-cipher") == 0) { /* A new server is specified as the host */ if (--argc < 1) { badop = 1; break; } cipher = *(++argv); } else if (Strcmp(*argv, "-loop") == 0) { /* * A number of sequential connections to a single host is * set here. A negative value will mean no connections are * made. This is useful in order to check the behavior of * multiple client connections. */ if (--argc < 1) { badop = 1; break; } loop_max = atoi(*(++argv)); } else if (Strcmp(*argv, "-no_verify") == 0) { /* * Set the SSL options to ignore the results of the peer * certificate verification */ no_verify = 1; } else if (Strcmp(*argv, "-no_output") == 0) { /* * Do not write the data received from the server in response * to the client request made by this code */ no_output = 1; } else if (*argv[0] != '-') { /* * This is analogous to the '-connect' option except this provides * the host:port name directly as the switch */ connect_host = *argv; } else if (Strcmp(*argv, "-no_fips140") == 0) { mode = R_LIB_CTX_STANDARD_MODE; } else { /* * All other options (include -h, -?, -help, etc.) are * unknown are will result in a usage message and program exit */ fprintf(err, "Unknown option %s\n", *argv); fflush(err); badop = 1; break; } /* Decrement the argument counter and move to the next argument */ argc--; argv++; } /* Deal with problems with the command line arguments */ if (badop) { /* Print a usage message */ fprintf(err, "usage: simple [options]\n"); fprintf(err, " -connect <host>: connects to specified host\n"); fprintf(err, " -cipher <list> : use the specified cipher suite(s)\n"); fprintf(err, " -no_verify : no verification is done\n"); fprintf(err, " -loop <number> : loops for number times\n"); fprintf(err, " -no_output : no information is displayed\n"); fprintf(err, " -no_fips140 : do not use FIPS140 crypto resources\n"); fflush(err); /* Exit the program */ goto end; } /* Initialize the SSL library using the default or non FIPS140 resources */ if (PRODUCT_LIBRARY_NEW(resource_list, R_RES_FLAG_DEF, &lib_ctx) != R_ERROR_NONE) { fprintf(err, "Unable to create library context\n"); fflush(err); 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) { fprintf(err, "Unable to seed the PRNG\n"); fflush(err); goto end; } #ifndef NO_TLS1 /* Use TLS protocol version 1 */ meth = TLSv1_client_method(); fprintf(out, "Using the TLSv1 method\n"); fflush(out); #else /* Use SSL protocol version 3 */ meth = SSLv3_client_method(); fprintf(out, "Using the SSLv3 method\n"); fflush(out); #endif /* !NO_TLS1 */ /* Create the SSL_CTX with the method */ if ((ctx = SSL_CTX_new(meth)) == NULL) { fprintf(err, "Unable to create SSL_CTX\n"); fflush(err); 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(ctx, lib_ctx, mode); /* Set the cipher suite list if specified */ if (cipher != NULL) { SSL_CTX_set_cipher_list(ctx, cipher); } /* Enable all vendor bug compatibility options */ SSL_CTX_set_options(ctx, SSL_OP_ALL); /* * Reduce the initial size of the record write buffer. This is safe * to do in a client, but for interoperability in a server the buffer * is set to a higher default value. */ SSL_CTX_set_write_buf_size(ctx, 4 * 1024); /* Set the protocol behavior in response to verification result */ if (no_verify) { /* * This option means that the client connection will still attempt to * verify the certificate chain but then ignores the verification * result.(This is the default on the SSL_CTX.) */ SSL_CTX_set_verify_mode(ctx, SSL_VERIFY_NONE); } else { /* * This option means the client will fail the handshake if it is unable * to verify the certificate chain */ SSL_CTX_set_verify_mode(ctx, SSL_VERIFY_PEER); } #ifndef NO_VERIFICATION /* * Full application verification routines are available. A choice can be * made to use the functioning verification routine or a dummy routine that * simply routines a "successful verification" result. */ if (no_verify) { SSL_CTX_set_app_verify_cb(ctx, v_cb_dummy, NULL); } else { SSL_CTX_set_app_verify_cb(ctx, v_cb, NULL); } #else /* !NO_VERIFICATION */ /* * No functioning verification code is available in order to reduce static * memory size of the binary. The library will still try to verify incoming * server certificates so use the verification routine that always returns * true without doing any processing. No server verification will be * performed. */ SSL_CTX_set_app_verify_cb(ctx, v_cb_dummy, NULL); #endif /* !NO_VERIFICATION */ /* * The client connection loop. Make the required number of connections to a * single server. Each connection will be made with a new SSL structure * rather than resetting a single SSL for each connection attempt. */ for (loop = 0; loop < loop_max; loop++) { /* Free the existing SSL */ if (ssl != NULL) { SSL_free(ssl); } if ((ssl = SSL_new(ctx)) == NULL) { fprintf(err, "Unable to create SSL structure\n"); fflush(err); goto end; } fprintf(out, "Connecting to %s\n", connect_host); fflush(out); /* Make a socket connection to the server */ if ((sock = SIO_connect(connect_host, NULL, 0, &sock)) < 0) { /* Exit the connection loop */ fprintf(err, "Unable to make socket connection\n"); fflush(err); goto end; } fprintf(out, "Connected to %s\n", connect_host); fflush(out); /* Associate the socket file descriptor with the SSL */ SSL_set_fd(ssl, sock); /* Set the SSL to be a client-side connection */ SSL_set_connect_state(ssl); /* * Make a request of the server. SSL_write() will perform the * handshake sequence before writing the request string. */ n = Strlen(get_str); i = SSL_write(ssl, get_str, n); /* Handle errors from the write */ if (i < n) { fprintf(err, "SSL_write error %08lX\r\n", ERR_peek_error()); fflush(err); goto end; } #ifndef NO_DISPLAY_CIPHER display_cipher = 0; #endif done = 0; /* * Read the reply from the server until the connection breaks or there * is an error. The client does not know beforehand how much data the * server will send. */ for (;;) { /* Read a buffer of data from the server */ i = SSL_read(ssl, (char *)buf, sizeof(buf)); switch (SSL_get_error(ssl, i)) { case SSL_ERROR_SSL: fprintf(err, "Handshake failure - %lX\n", ERR_get_error()); fflush(err); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: /* * This error type will be returned for delay back information. * Retry the SSL read. */ continue; case SSL_ERROR_SYSCALL: case SSL_ERROR_ZERO_RETURN: done = 1; break; default: /* For example, successful read of a buffer of data */ break; } /* * Leave the read loop when the connection breaks or if there is a * system error. */ if (done) { break; } #ifndef NO_DISPLAY_CIPHER /* Write extra connection information to stdout */ if (!display_cipher) { display_cipher = 1; fprintf(out, "cipher=%s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); fprintf(out, "version=0x%x\n", SSL_version(ssl)); fflush(out); } #endif /* Write the data from the server to stdout */ if (no_output == 0) { fwrite(buf, 1, i, out); } } /* Shut down the connection */ SSL_shutdown(ssl); /* The socket is not closed */ SIO_close(sock); } ret = R_ERROR_NONE; end: if (ret != R_ERROR_NONE) { ERR_print_errors_fp(err); } if (ssl != NULL) { SSL_free(ssl); } if (ctx != NULL) { SSL_CTX_free(ctx); } #ifndef NO_VERIFICATION v_cb_cleanup(); #endif /* !NO_VERIFICATION */ if (lib_ctx != NULL) { PRODUCT_LIBRARY_FREE(lib_ctx); } #ifdef USE_UMALLOC fprintf(err, "UMALLOC: max used: %ld\n", user_mem_max()); fflush(err); #endif /* USE_UMALLOC */ return(R_ERROR_EXIT_CODE(ret)); }