Esempio n. 1
0
/*
 * This is the main entry point into the example EST server.
 * This routine parses the command line options, reads in the
 * cert chains, creates an EST context, initializes the HTTP
 * layer (mongoose), and starts a simple TCP server to accept
 * incoming EST requests.
 */
int main (int argc, char **argv)
{
    char c;
    int i;

    X509 *x;
    EVP_PKEY * priv_key;
    BIO *certin;
    DH *dh;
    EST_ERROR rv;
    int sleep_delay = 0;
    int retry_period = 300;
    char vfile[255];
    int option_index = 0;
    pem_password_cb *priv_key_cb = NULL;

    static struct option long_options[] = {
        {"srp", 1, NULL, 0},
        {"enforce-csr", 0, NULL, 0},
        {"token", 1, 0, 0},
        {"keypass", 1, 0, 0},
        {"keypass_stdin", 1, 0, 0 },
        {"keypass_arg", 1, 0, 0 },
        {NULL, 0, NULL, 0}
    };

#ifdef WIN32
    InitializeCriticalSection(&enrollment_critical_section);
#endif

    /* Show usage if -h or --help options are specified */
    if ((argc == 1)
            || (argc == 2
                    && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))) {
        show_usage_and_exit();
    }

    while ((c = getopt_long(argc, argv, "?fhbwnovr:c:k:m:p:d:lt6", long_options,
        &option_index)) != -1) {
        switch (c) {
        case 0:
#if 0
            printf("option %s", long_options[option_index].name);
            if (optarg) {
                printf(" with arg %s", optarg);
            }
            printf("\n");
#endif
            if (!strncmp(long_options[option_index].name, "srp",
                strlen("srp"))) {
                srp = 1;
                strncpy(vfile, optarg, 255);
            }
            if (!strncmp(long_options[option_index].name, "enforce-csr",
                strlen("enforce-csr"))) {
                enforce_csr = 1;
            }
            if (!strncmp(long_options[option_index].name, "token",
                strlen("token"))) {
                http_token_auth = 1;
                memset(valid_token_value, 0, MAX_AUTH_TOKEN_LEN + 1);
                strncpy(&(valid_token_value[0]), optarg, MAX_AUTH_TOKEN_LEN);
            }
            if (!strncmp(long_options[option_index].name,"keypass_stdin", strlen("keypass_stdin"))) {
                priv_key_cb = PEM_def_callback;
            }
            if (!strncmp(long_options[option_index].name,"keypass_arg", strlen("keypass_arg"))) {
                strncpy(priv_key_pwd, optarg, MAX_PWD_LEN);
                priv_key_cb = string_password_cb;
            }
            break;
        case 'm':
            manual_enroll = 1;
            retry_period = atoi(optarg);
            break;
        case 'h':
            http_digest_auth = 1;
            break;
        case 'b':
            http_basic_auth = 1;
            break;
        case 'w':
            write_csr = 1;
            break;
        case 'n':
            http_auth_disable = 1;
            break;
        case 'o':
            disable_forced_http_auth = 1;
            break;
        case 'v':
            verbose = 1;
            break;
        case 'l':
            crl = 1;
            break;
        case 't':
            pop = 1;
            break;
        case '6':
            v6 = 1;
            break;
#ifndef DISABLE_PTHREADS
        case 'd':
            sleep_delay = atoi(optarg);
            break;
#endif
        case 'p':
            tcp_port = atoi(optarg);
            break;
        case 'c':
            strncpy(certfile, optarg, EST_MAX_FILE_LEN);
            break;
        case 'k':
            strncpy(keyfile, optarg, EST_MAX_FILE_LEN);
            break;
        case 'r':
            if (strnlen(optarg, MAX_REALM_LEN+1) > MAX_REALM_LEN) {
                printf("\nRealm value is too large.  Max is 32 characters\n");
                exit(1);
            }
            
            strncpy(realm, optarg, MAX_REALM_LEN);
            break;
        case 'f':
            /* turn FIPS on if user requested it
             * and exit if failure.
             */
            set_fips_return = FIPS_mode_set(1);
            if (set_fips_return != 1) {
                set_fips_error = ERR_get_error();
                printf("\nERROR WHILE SETTING FIPS MODE ON exiting ....\n");
                exit(1);
            } else {
                printf("\nRunning EST Sample Server with FIPS MODE = ON !\n");
            }
            ;
            break;
        default:
            show_usage_and_exit();
            break;
        }
    }
    argc -= optind;
    argv += optind;

    if (verbose) {
        print_version(stdout);
    }

    if (getenv("EST_CSR_ATTR")) {
        printf("\nUsing CSR Attributes: %s", getenv("EST_CSR_ATTR"));
    }

    if (!getenv("EST_CACERTS_RESP")) {
        printf("\nEST_CACERTS_RESP file not set, set this env variable to resolve");
        exit(1);
    }
    if (!getenv("EST_TRUSTED_CERTS")) {
        printf("\nEST_TRUSTED_CERTS file not set, set this env variable to resolve");
        exit(1);
    }

    /*
     * Read in the CA certificates
     */
    cacerts_len = read_binary_file(getenv("EST_CACERTS_RESP"), &cacerts_raw);
    if (cacerts_len <= 0) {
        printf("\nEST_CACERTS_RESP file could not be read\n");
        exit(1);
    }
    /*
     * Read in the trusted CA certificates for the local TLS context
     */
    if (getenv("EST_TRUSTED_CERTS")) {
        trustcerts_len = read_binary_file(getenv("EST_TRUSTED_CERTS"),
            &trustcerts);
        if (trustcerts_len <= 0) {
            printf("\nEST_TRUSTED_CERTS file could not be read\n");
            exit(1);
        }
    }

    est_apps_startup();

    /*
     * Read in the local server certificate
     */
    certin = BIO_new(BIO_s_file_internal());
    if (BIO_read_filename(certin, certfile) <= 0) {
        printf("\nUnable to read server certificate file %s\n", certfile);
        exit(1);
    }
    /*
     * This reads the file, which is expected to be PEM encoded.  If you're using
     * DER encoded certs, you would invoke d2i_X509_bio() instead.
     */
    x = PEM_read_bio_X509(certin, NULL, NULL, NULL);
    if (x == NULL) {
        printf("\nError while reading PEM encoded server certificate file %s\n",
            certfile);
        exit(1);
    }
    BIO_free(certin);

    /*
     * Read in the server's private key
     */

    priv_key = read_private_key(keyfile, priv_key_cb);
    if (priv_key == NULL) {
        printf("\nError while reading PEM encoded server private key file %s\n",
            keyfile);
        ERR_print_errors_fp(stderr);
        exit(1);
    }

    bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
    if (!bio_err) {
        printf("\nBIO not working\n");
        exit(1);
    }

    if (verbose) {
        est_init_logger(EST_LOG_LVL_INFO, NULL);
        est_enable_backtrace(1);
    } else {
        est_init_logger(EST_LOG_LVL_ERR, NULL);
    }
    ectx = est_server_init(trustcerts, trustcerts_len, cacerts_raw, cacerts_len,
        EST_CERT_FORMAT_PEM, realm, x, priv_key);
    if (!ectx) {
        printf("\nUnable to initialize EST context.  Aborting!!!\n");
        exit(1);
    }
    est_set_ex_data(ectx, &test_app_data);

    if (enforce_csr) {
        est_server_enforce_csrattr(ectx);
    }

    /*
     * Change the retry-after period.  This is not
     * necessary, it's only shown here as an example.
     */
    if (verbose)
        printf("\nRetry period being set to: %d \n", retry_period);
    est_server_set_retry_period(ectx, retry_period);

    if (crl) {
        est_enable_crl(ectx);
    }
    if (!pop) {
        if (verbose)
            printf("\nDisabling PoP check");
        est_server_disable_pop(ectx);
    }

    if (srp) {
        srp_db = SRP_VBASE_new(NULL);
        if (!srp_db) {
            printf("\nUnable allocate SRP verifier database.  Aborting!!!\n");
            exit(1);
        }
        if (SRP_VBASE_init(srp_db, vfile) != SRP_NO_ERROR) {
            printf("\nUnable initialize SRP verifier database.  Aborting!!!\n");
            exit(1);
        }

        if (est_server_enable_srp(ectx, &process_ssl_srp_auth)) {
            printf("\nUnable to enable SRP.  Aborting!!!\n");
            exit(1);
        }
    }

    if (est_set_ca_enroll_cb(ectx, &process_pkcs10_enrollment)) {
        printf(
            "\nUnable to set EST pkcs10 enrollment callback.  Aborting!!!\n");
        exit(1);
    }
    /*
     * We use the same handler for re-enrollment.  Our little toy
     * CA doesn't do anything special for re-enrollment.  A real
     * CA would need to implement the requirements in section
     * 4.2 of the EST draft.
     */
    if (est_set_ca_reenroll_cb(ectx, &process_pkcs10_enrollment)) {
        printf(
            "\nUnable to set EST pkcs10 enrollment callback.  Aborting!!!\n");
        exit(1);
    }
    if (est_set_csr_cb(ectx, &process_csrattrs_request)) {
        printf("\nUnable to set EST CSR Attributes callback.  Aborting!!!\n");
        exit(1);
    }
    if (!http_auth_disable) {
        if (est_set_http_auth_cb(ectx, &process_http_auth)) {
            printf("\nUnable to set EST HTTP AUTH callback.  Aborting!!!\n");
            exit(1);
        }
    }
    if (disable_forced_http_auth) {
        if (verbose)
            printf(
                "\nDisabling HTTP authentication when TLS client auth succeeds\n");
        if (est_set_http_auth_required(ectx, HTTP_AUTH_NOT_REQUIRED)) {
            printf("\nUnable to disable required HTTP auth.  Aborting!!!\n");
            exit(1);
        }
    }

    if (http_digest_auth) {
        
        MD5_CTX c;
        int len;
        static unsigned char ha1_input_buf[32*3+2];
        unsigned char md[17];
        int i;
        
        rv = est_server_set_auth_mode(ectx, AUTH_DIGEST);
        if (rv != EST_ERR_NONE) {
            printf(
                "\nUnable to enable HTTP digest authentication.  Aborting!!!\n");
            exit(1);
        }

        /*
         * Cache away the realm value and build the HA1
         */
        strncpy(digest_user[1], realm, MAX_REALM_LEN);

        len = sprintf((char *)ha1_input_buf, "%s:%s:%s", "estuser", realm, "estpwd");
        MD5_Init(&c);
        MD5_Update(&c, ha1_input_buf, len);
        MD5_Final((unsigned char *)md, &c);

        printf("\nDigest HA1 value = ");
        memset(digest_user[2], 0, 32);
        for(i = 0; i < 16; i++){
            
            sprintf(&(digest_user[2][i*2]),"%.2x", (unsigned char) md[i]);
            printf("%c%c", digest_user[2][i*2], digest_user[2][i*2+1]);
        }
        printf("\n");
    }

    if (http_basic_auth) {
        rv = est_server_set_auth_mode(ectx, AUTH_BASIC);
        if (rv != EST_ERR_NONE) {
            printf(
                "\nUnable to enable HTTP basic authentication.  Aborting!!!\n");
            exit(1);
        }
    }

    if (http_token_auth) {
        rv = est_server_set_auth_mode(ectx, AUTH_TOKEN);
        if (rv != EST_ERR_NONE) {
            printf(
                "\nUnable to enable HTTP token authentication.  Aborting!!!\n");
            exit(1);
        }
    }

    /*
     * Set DH parameters for TLS
     */
    dh = get_dh1024dsa();
    if (dh) {
        est_server_set_dh_parms(ectx, dh);
    }
    DH_free(dh);

    /*
     * Install thread locking mechanism for OpenSSL
     */
    mutex_buf = malloc(CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
    if (!mutex_buf) {
        printf("Cannot allocate mutexes");
        exit(1);
    }
    for (i = 0; i < CRYPTO_num_locks(); i++)
        MUTEX_SETUP(mutex_buf[i]);
    CRYPTO_set_id_callback(id_function);
    CRYPTO_set_locking_callback(locking_function);

    printf("\nLaunching EST server...\n");

    rv = est_server_start(ectx);
    if (rv != EST_ERR_NONE) {
        printf("\nFailed to init mg (rv=%d)\n", rv);
        exit(1);
    }

    /*
     * Start the simple server, which opens a TCP
     * socket, waits for incoming connections, and
     * invokes the EST handler for each connection.
     */
    start_simple_server(ectx, tcp_port, sleep_delay, v6);

    cleanup();
    EVP_PKEY_free(priv_key);
    X509_free(x);
    return 0;
}
Esempio n. 2
0
int main(int argc, char *argv[])
	{
	char *CApath=NULL,*CAfile=NULL;
	int badop=0;
	int bio_pair=0;
	int force=0;
	int tls1=0,ssl2=0,ssl3=0,ret=1;
	int client_auth=0;
	int server_auth=0,i;
	int app_verify=0;
	char *server_cert=TEST_SERVER_CERT;
	char *server_key=NULL;
	char *client_cert=TEST_CLIENT_CERT;
	char *client_key=NULL;
	SSL_CTX *s_ctx=NULL;
	SSL_CTX *c_ctx=NULL;
	SSL_METHOD *meth=NULL;
	SSL *c_ssl,*s_ssl;
	int number=1,reuse=0;
	long bytes=1L;
#ifndef OPENSSL_NO_DH
	DH *dh;
	int dhe1024 = 0, dhe1024dsa = 0;
#endif
	int no_dhe = 0;
	int print_time = 0;
	clock_t s_time = 0, c_time = 0;
	int comp = 0;
	COMP_METHOD *cm = NULL;

	verbose = 0;
	debug = 0;
	cipher = 0;

	bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);	

	CRYPTO_set_locking_callback(lock_dbg_cb);

	/* enable memory leak checking unless explicitly disabled */
	if (!((getenv("OPENSSL_DEBUG_MEMORY") != NULL) && (0 == strcmp(getenv("OPENSSL_DEBUG_MEMORY"), "off"))))
		{
		CRYPTO_malloc_debug_init();
		CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
		}
	else
		{
		/* OPENSSL_DEBUG_MEMORY=off */
		CRYPTO_set_mem_debug_functions(0, 0, 0, 0, 0);
		}
	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);

	RAND_seed(rnd_seed, sizeof rnd_seed);

	bio_stdout=BIO_new_fp(stdout,BIO_NOCLOSE);

	argc--;
	argv++;

	while (argc >= 1)
		{
		if	(strcmp(*argv,"-server_auth") == 0)
			server_auth=1;
		else if	(strcmp(*argv,"-client_auth") == 0)
			client_auth=1;
		else if	(strcmp(*argv,"-v") == 0)
			verbose=1;
		else if	(strcmp(*argv,"-d") == 0)
			debug=1;
		else if	(strcmp(*argv,"-reuse") == 0)
			reuse=1;
		else if	(strcmp(*argv,"-dhe1024") == 0)
			{
#ifndef OPENSSL_NO_DH
			dhe1024=1;
#else
			fprintf(stderr,"ignoring -dhe1024, since I'm compiled without DH\n");
#endif
			}
		else if	(strcmp(*argv,"-dhe1024dsa") == 0)
			{
#ifndef OPENSSL_NO_DH
			dhe1024dsa=1;
#else
			fprintf(stderr,"ignoring -dhe1024, since I'm compiled without DH\n");
#endif
			}
		else if	(strcmp(*argv,"-no_dhe") == 0)
			no_dhe=1;
		else if	(strcmp(*argv,"-ssl2") == 0)
			ssl2=1;
		else if	(strcmp(*argv,"-tls1") == 0)
			tls1=1;
		else if	(strcmp(*argv,"-ssl3") == 0)
			ssl3=1;
		else if	(strncmp(*argv,"-num",4) == 0)
			{
			if (--argc < 1) goto bad;
			number= atoi(*(++argv));
			if (number == 0) number=1;
			}
		else if	(strcmp(*argv,"-bytes") == 0)
			{
			if (--argc < 1) goto bad;
			bytes= atol(*(++argv));
			if (bytes == 0L) bytes=1L;
			i=strlen(argv[0]);
			if (argv[0][i-1] == 'k') bytes*=1024L;
			if (argv[0][i-1] == 'm') bytes*=1024L*1024L;
			}
		else if	(strcmp(*argv,"-cert") == 0)
			{
			if (--argc < 1) goto bad;
			server_cert= *(++argv);
			}
		else if	(strcmp(*argv,"-s_cert") == 0)
			{
			if (--argc < 1) goto bad;
			server_cert= *(++argv);
			}
		else if	(strcmp(*argv,"-key") == 0)
			{
			if (--argc < 1) goto bad;
			server_key= *(++argv);
			}
		else if	(strcmp(*argv,"-s_key") == 0)
			{
			if (--argc < 1) goto bad;
			server_key= *(++argv);
			}
		else if	(strcmp(*argv,"-c_cert") == 0)
			{
			if (--argc < 1) goto bad;
			client_cert= *(++argv);
			}
		else if	(strcmp(*argv,"-c_key") == 0)
			{
			if (--argc < 1) goto bad;
			client_key= *(++argv);
			}
		else if	(strcmp(*argv,"-cipher") == 0)
			{
			if (--argc < 1) goto bad;
			cipher= *(++argv);
			}
		else if	(strcmp(*argv,"-CApath") == 0)
			{
			if (--argc < 1) goto bad;
			CApath= *(++argv);
			}
		else if	(strcmp(*argv,"-CAfile") == 0)
			{
			if (--argc < 1) goto bad;
			CAfile= *(++argv);
			}
		else if	(strcmp(*argv,"-bio_pair") == 0)
			{
			bio_pair = 1;
			}
		else if	(strcmp(*argv,"-f") == 0)
			{
			force = 1;
			}
		else if	(strcmp(*argv,"-time") == 0)
			{
			print_time = 1;
			}
		else if	(strcmp(*argv,"-zlib") == 0)
			{
			comp = COMP_ZLIB;
			}
		else if	(strcmp(*argv,"-rle") == 0)
			{
			comp = COMP_RLE;
			}
		else if	(strcmp(*argv,"-app_verify") == 0)
			{
			app_verify = 1;
			}
		else
			{
			fprintf(stderr,"unknown option %s\n",*argv);
			badop=1;
			break;
			}
		argc--;
		argv++;
		}
	if (badop)
		{
bad:
		sv_usage();
		goto end;
		}

	if (!ssl2 && !ssl3 && !tls1 && number > 1 && !reuse && !force)
		{
		fprintf(stderr, "This case cannot work.  Use -f to perform "
			"the test anyway (and\n-d to see what happens), "
			"or add one of -ssl2, -ssl3, -tls1, -reuse\n"
			"to avoid protocol mismatch.\n");
		EXIT(1);
		}

	if (print_time)
		{
		if (!bio_pair)
			{
			fprintf(stderr, "Using BIO pair (-bio_pair)\n");
			bio_pair = 1;
			}
		if (number < 50 && !force)
			fprintf(stderr, "Warning: For accurate timings, use more connections (e.g. -num 1000)\n");
		}

/*	if (cipher == NULL) cipher=getenv("SSL_CIPHER"); */

	SSL_library_init();
	SSL_load_error_strings();

	if (comp == COMP_ZLIB) cm = COMP_zlib();
	if (comp == COMP_RLE) cm = COMP_rle();
	if (cm != NULL)
		{
		if (cm->type != NID_undef)
			SSL_COMP_add_compression_method(comp, cm);
		else
			{
			fprintf(stderr,
				"Warning: %s compression not supported\n",
				(comp == COMP_RLE ? "rle" :
					(comp == COMP_ZLIB ? "zlib" :
						"unknown")));
			ERR_print_errors_fp(stderr);
			}
		}

#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3)
	if (ssl2)
		meth=SSLv2_method();
	else 
	if (tls1)
		meth=TLSv1_method();
	else
	if (ssl3)
		meth=SSLv3_method();
	else
		meth=SSLv23_method();
#else
#ifdef OPENSSL_NO_SSL2
	meth=SSLv3_method();
#else
	meth=SSLv2_method();
#endif
#endif

	c_ctx=SSL_CTX_new(meth);
	s_ctx=SSL_CTX_new(meth);
	if ((c_ctx == NULL) || (s_ctx == NULL))
		{
		ERR_print_errors(bio_err);
		goto end;
		}

	if (cipher != NULL)
		{
		SSL_CTX_set_cipher_list(c_ctx,cipher);
		SSL_CTX_set_cipher_list(s_ctx,cipher);
		}

#ifndef OPENSSL_NO_DH
	if (!no_dhe)
		{
		if (dhe1024dsa)
			{
			/* use SSL_OP_SINGLE_DH_USE to avoid small subgroup attacks */
			SSL_CTX_set_options(s_ctx, SSL_OP_SINGLE_DH_USE);
			dh=get_dh1024dsa();
			}
		else if (dhe1024)
			dh=get_dh1024();
		else
			dh=get_dh512();
		SSL_CTX_set_tmp_dh(s_ctx,dh);
		DH_free(dh);
		}
#else
	(void)no_dhe;
#endif

#ifndef OPENSSL_NO_RSA
	SSL_CTX_set_tmp_rsa_callback(s_ctx,tmp_rsa_cb);
#endif

	if (!SSL_CTX_use_certificate_file(s_ctx,server_cert,SSL_FILETYPE_PEM))
		{
		ERR_print_errors(bio_err);
		}
	else if (!SSL_CTX_use_PrivateKey_file(s_ctx,
		(server_key?server_key:server_cert), SSL_FILETYPE_PEM))
		{
		ERR_print_errors(bio_err);
		goto end;
		}

	if (client_auth)
		{
		SSL_CTX_use_certificate_file(c_ctx,client_cert,
			SSL_FILETYPE_PEM);
		SSL_CTX_use_PrivateKey_file(c_ctx,
			(client_key?client_key:client_cert),
			SSL_FILETYPE_PEM);
		}

	if (	(!SSL_CTX_load_verify_locations(s_ctx,CAfile,CApath)) ||
		(!SSL_CTX_set_default_verify_paths(s_ctx)) ||
		(!SSL_CTX_load_verify_locations(c_ctx,CAfile,CApath)) ||
		(!SSL_CTX_set_default_verify_paths(c_ctx)))
		{
		/* fprintf(stderr,"SSL_load_verify_locations\n"); */
		ERR_print_errors(bio_err);
		/* goto end; */
		}

	if (client_auth)
		{
		BIO_printf(bio_err,"client authentication\n");
		SSL_CTX_set_verify(s_ctx,
			SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
			verify_callback);
		if (app_verify) 
			{
			SSL_CTX_set_cert_verify_callback(s_ctx, app_verify_callback, app_verify_arg);
			}
		}
	if (server_auth)
		{
		BIO_printf(bio_err,"server authentication\n");
		SSL_CTX_set_verify(c_ctx,SSL_VERIFY_PEER,
			verify_callback);
		if (app_verify) 
			{
			SSL_CTX_set_cert_verify_callback(s_ctx, app_verify_callback, app_verify_arg);
			}
		}
	
	{
		int session_id_context = 0;
		SSL_CTX_set_session_id_context(s_ctx, (void *)&session_id_context, sizeof session_id_context);
	}

	c_ssl=SSL_new(c_ctx);
	s_ssl=SSL_new(s_ctx);

#ifndef OPENSSL_NO_KRB5
	if (c_ssl  &&  c_ssl->kssl_ctx)
                {
                char	localhost[MAXHOSTNAMELEN+2];

		if (gethostname(localhost, sizeof localhost-1) == 0)
                        {
			localhost[sizeof localhost-1]='\0';
			if(strlen(localhost) == sizeof localhost-1)
				{
				BIO_printf(bio_err,"localhost name too long\n");
				goto end;
				}
			kssl_ctx_setstring(c_ssl->kssl_ctx, KSSL_SERVER,
                                localhost);
			}
		}
#endif    /* OPENSSL_NO_KRB5  */

	for (i=0; i<number; i++)
		{
		if (!reuse) SSL_set_session(c_ssl,NULL);
		if (bio_pair)
			ret=doit_biopair(s_ssl,c_ssl,bytes,&s_time,&c_time);
		else
			ret=doit(s_ssl,c_ssl,bytes);
		}

	if (!verbose)
		{
		print_details(c_ssl, "");
		}
	if ((number > 1) || (bytes > 1L))
		BIO_printf(bio_stdout, "%d handshakes of %ld bytes done\n",number,bytes);
	if (print_time)
		{
#ifdef CLOCKS_PER_SEC
		/* "To determine the time in seconds, the value returned
		 * by the clock function should be divided by the value
		 * of the macro CLOCKS_PER_SEC."
		 *                                       -- ISO/IEC 9899 */
		BIO_printf(bio_stdout, "Approximate total server time: %6.2f s\n"
			"Approximate total client time: %6.2f s\n",
			(double)s_time/CLOCKS_PER_SEC,
			(double)c_time/CLOCKS_PER_SEC);
#else
		/* "`CLOCKS_PER_SEC' undeclared (first use this function)"
		 *                            -- cc on NeXTstep/OpenStep */
		BIO_printf(bio_stdout,
			"Approximate total server time: %6.2f units\n"
			"Approximate total client time: %6.2f units\n",
			(double)s_time,
			(double)c_time);
#endif
		}

	SSL_free(s_ssl);
	SSL_free(c_ssl);

end:
	if (s_ctx != NULL) SSL_CTX_free(s_ctx);
	if (c_ctx != NULL) SSL_CTX_free(c_ctx);

	if (bio_stdout != NULL) BIO_free(bio_stdout);

#ifndef OPENSSL_NO_RSA
	free_tmp_rsa();
#endif
#ifndef OPENSSL_NO_ENGINE
	ENGINE_cleanup();
#endif
	CRYPTO_cleanup_all_ex_data();
	ERR_free_strings();
	ERR_remove_state(0);
	EVP_cleanup();
	CRYPTO_mem_leaks(bio_err);
	if (bio_err != NULL) BIO_free(bio_err);
	EXIT(ret);
	}