1 /* $OpenBSD: s_time.c,v 1.13 2015/10/10 22:28:51 doug Exp $ */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  *
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  *
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58 
59 /*-----------------------------------------
60    s_time - SSL client connection timer program
61    Written and donated by Larry Streepy <streepy@healthcare.com>
62   -----------------------------------------*/
63 
64 #include <sys/types.h>
65 #include <sys/socket.h>
66 
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <limits.h>
70 #include <string.h>
71 #include <unistd.h>
72 #include <poll.h>
73 
74 #include "apps.h"
75 
76 #include <openssl/err.h>
77 #include <openssl/pem.h>
78 #include <openssl/ssl.h>
79 #include <openssl/x509.h>
80 
81 #include "s_apps.h"
82 
83 #define SSL_CONNECT_NAME	"localhost:4433"
84 
85 #define BUFSIZZ 1024*10
86 
87 #define MYBUFSIZ 1024*8
88 
89 #undef min
90 #undef max
91 #define min(a,b) (((a) < (b)) ? (a) : (b))
92 #define max(a,b) (((a) > (b)) ? (a) : (b))
93 
94 #define SECONDS	30
95 extern int verify_depth;
96 extern int verify_error;
97 
98 static void s_time_usage(void);
99 static SSL *doConnection(SSL * scon);
100 
101 static SSL_CTX *tm_ctx = NULL;
102 static const SSL_METHOD *s_time_meth = NULL;
103 static long bytes_read = 0;
104 
105 struct {
106 	int bugs;
107 	char *CAfile;
108 	char *CApath;
109 	char *certfile;
110 	char *cipher;
111 	char *host;
112 	char *keyfile;
113 	int maxtime;
114 	int nbio;
115 	int no_shutdown;
116 	int perform;
117 	int verify;
118 	int verify_depth;
119 	char *www_path;
120 } s_time_config;
121 
122 struct option s_time_options[] = {
123 	{
124 		.name = "bugs",
125 		.desc = "Enable workarounds for known SSL/TLS bugs",
126 		.type = OPTION_FLAG,
127 		.opt.flag = &s_time_config.bugs,
128 	},
129 	{
130 		.name = "CAfile",
131 		.argname = "file",
132 		.desc = "File containing trusted certificates in PEM format",
133 		.type = OPTION_ARG,
134 		.opt.arg = &s_time_config.CAfile,
135 	},
136 	{
137 		.name = "CApath",
138 		.argname = "path",
139 		.desc = "Directory containing trusted certificates",
140 		.type = OPTION_ARG,
141 		.opt.arg = &s_time_config.CApath,
142 	},
143 	{
144 		.name = "cert",
145 		.argname = "file",
146 		.desc = "Client certificate to use, if one is requested",
147 		.type = OPTION_ARG,
148 		.opt.arg = &s_time_config.certfile,
149 	},
150 	{
151 		.name = "cipher",
152 		.argname = "list",
153 		.desc = "List of cipher suites to send to the server",
154 		.type = OPTION_ARG,
155 		.opt.arg = &s_time_config.cipher,
156 	},
157 	{
158 		.name = "connect",
159 		.argname = "host:port",
160 		.desc = "Host and port to connect to (default "
161 		    SSL_CONNECT_NAME ")",
162 		.type = OPTION_ARG,
163 		.opt.arg = &s_time_config.host,
164 	},
165 	{
166 		.name = "key",
167 		.argname = "file",
168 		.desc = "Client private key to use, if one is required",
169 		.type = OPTION_ARG,
170 		.opt.arg = &s_time_config.keyfile,
171 	},
172 	{
173 		.name = "nbio",
174 		.desc = "Use non-blocking I/O",
175 		.type = OPTION_FLAG,
176 		.opt.flag = &s_time_config.nbio,
177 	},
178 	{
179 		.name = "new",
180 		.desc = "Use a new session ID for each connection",
181 		.type = OPTION_VALUE,
182 		.opt.value = &s_time_config.perform,
183 		.value = 1,
184 	},
185 	{
186 		.name = "no_shutdown",
187 		.desc = "Shut down the connection without notifying the server",
188 		.type = OPTION_FLAG,
189 		.opt.flag = &s_time_config.no_shutdown,
190 	},
191 	{
192 		.name = "reuse",
193 		.desc = "Reuse the same session ID for each connection",
194 		.type = OPTION_VALUE,
195 		.opt.value = &s_time_config.perform,
196 		.value = 2,
197 	},
198 	{
199 		.name = "time",
200 		.argname = "seconds",
201 		.desc = "Duration to perform timing tests for (default 30)",
202 		.type = OPTION_ARG_INT,
203 		.opt.value = &s_time_config.maxtime,
204 	},
205 	{
206 		.name = "verify",
207 		.argname = "depth",
208 		.desc = "Enable peer certificate verification with given depth",
209 		.type = OPTION_ARG_INT,
210 		.opt.value = &s_time_config.verify_depth,
211 	},
212 	{
213 		.name = "www",
214 		.argname = "page",
215 		.desc = "Page to GET from the server (default none)",
216 		.type = OPTION_ARG,
217 		.opt.arg = &s_time_config.www_path,
218 	},
219 	{ NULL },
220 };
221 
222 static void
223 s_time_usage(void)
224 {
225 	fprintf(stderr,
226 	    "usage: s_time "
227 	    "[-bugs] [-CAfile file] [-CApath directory] [-cert file]\n"
228 	    "    [-cipher cipherlist] [-connect host:port] [-key keyfile]\n"
229 	    "    [-nbio] [-new] [-no_shutdown] [-reuse] [-time seconds]\n"
230 	    "    [-verify depth] [-www page]\n\n");
231 	options_usage(s_time_options);
232 }
233 
234 /***********************************************************************
235  * TIME - time functions
236  */
237 #define START	0
238 #define STOP	1
239 
240 static double
241 tm_Time_F(int s)
242 {
243 	return app_tminterval(s, 1);
244 }
245 
246 /***********************************************************************
247  * MAIN - main processing area for client
248  *			real name depends on MONOLITH
249  */
250 int
251 s_time_main(int argc, char **argv)
252 {
253 	double totalTime = 0.0;
254 	int nConn = 0;
255 	SSL *scon = NULL;
256 	long finishtime = 0;
257 	int ret = 1, i;
258 	char buf[1024 * 8];
259 	int ver;
260 
261 	if (single_execution) {
262 		if (pledge("stdio inet rpath", NULL) == -1) {
263 			perror("pledge");
264 			exit(1);
265 		}
266 	}
267 
268 	s_time_meth = SSLv23_client_method();
269 
270 	verify_depth = 0;
271 
272 	memset(&s_time_config, 0, sizeof(s_time_config));
273 
274 	s_time_config.host = SSL_CONNECT_NAME;
275 	s_time_config.maxtime = SECONDS;
276 	s_time_config.perform = 3;
277 	s_time_config.verify = SSL_VERIFY_NONE;
278 	s_time_config.verify_depth = -1;
279 
280 	if (options_parse(argc, argv, s_time_options, NULL, NULL) != 0) {
281 		s_time_usage();
282 		goto end;
283 	}
284 
285 	if (s_time_config.verify_depth >= 0) {
286 		s_time_config.verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
287 		verify_depth = s_time_config.verify_depth;
288 		BIO_printf(bio_err, "verify depth is %d\n", verify_depth);
289 	}
290 
291 	if (s_time_config.www_path != NULL &&
292 	    strlen(s_time_config.www_path) > MYBUFSIZ - 100) {
293 		BIO_printf(bio_err, "-www option too long\n");
294 		goto end;
295 	}
296 
297 	if ((tm_ctx = SSL_CTX_new(s_time_meth)) == NULL)
298 		return (1);
299 
300 	SSL_CTX_set_quiet_shutdown(tm_ctx, 1);
301 
302 	if (s_time_config.bugs)
303 		SSL_CTX_set_options(tm_ctx, SSL_OP_ALL);
304 
305 	if (s_time_config.cipher != NULL) {
306 		if (!SSL_CTX_set_cipher_list(tm_ctx, s_time_config.cipher)) {
307 			BIO_printf(bio_err, "error setting cipher list\n");
308 			ERR_print_errors(bio_err);
309 			goto end;
310 		}
311 	}
312 
313 	SSL_CTX_set_verify(tm_ctx, s_time_config.verify, NULL);
314 
315 	if (!set_cert_stuff(tm_ctx, s_time_config.certfile,
316 	    s_time_config.keyfile))
317 		goto end;
318 
319 	if ((!SSL_CTX_load_verify_locations(tm_ctx, s_time_config.CAfile,
320 	    s_time_config.CApath)) ||
321 	    (!SSL_CTX_set_default_verify_paths(tm_ctx))) {
322 		/*
323 		 * BIO_printf(bio_err,"error setting default verify
324 		 * locations\n");
325 		 */
326 		ERR_print_errors(bio_err);
327 		/* goto end; */
328 	}
329 
330 	if (!(s_time_config.perform & 1))
331 		goto next;
332 	printf("Collecting connection statistics for %d seconds\n",
333 	    s_time_config.maxtime);
334 
335 	/* Loop and time how long it takes to make connections */
336 
337 	bytes_read = 0;
338 	finishtime = (long) time(NULL) + s_time_config.maxtime;
339 	tm_Time_F(START);
340 	for (;;) {
341 		if (finishtime < (long) time(NULL))
342 			break;
343 		if ((scon = doConnection(NULL)) == NULL)
344 			goto end;
345 
346 		if (s_time_config.www_path != NULL) {
347 			int retval = snprintf(buf, sizeof buf,
348 			    "GET %s HTTP/1.0\r\n\r\n", s_time_config.www_path);
349 			if ((size_t)retval >= sizeof buf) {
350 				fprintf(stderr, "URL too long\n");
351 				goto end;
352 			}
353 			SSL_write(scon, buf, strlen(buf));
354 			while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
355 				bytes_read += i;
356 		}
357 		if (s_time_config.no_shutdown)
358 			SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN |
359 			    SSL_RECEIVED_SHUTDOWN);
360 		else
361 			SSL_shutdown(scon);
362 		shutdown(SSL_get_fd(scon), SHUT_RDWR);
363 		close(SSL_get_fd(scon));
364 
365 		nConn += 1;
366 		if (SSL_session_reused(scon))
367 			ver = 'r';
368 		else {
369 			ver = SSL_version(scon);
370 			if (ver == TLS1_VERSION)
371 				ver = 't';
372 			else if (ver == SSL3_VERSION)
373 				ver = '3';
374 			else if (ver == SSL2_VERSION)
375 				ver = '2';
376 			else
377 				ver = '*';
378 		}
379 		fputc(ver, stdout);
380 		fflush(stdout);
381 
382 		SSL_free(scon);
383 		scon = NULL;
384 	}
385 	totalTime += tm_Time_F(STOP);	/* Add the time for this iteration */
386 
387 	i = (int) ((long) time(NULL) - finishtime + s_time_config.maxtime);
388 	printf("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n", nConn, totalTime, ((double) nConn / totalTime), bytes_read);
389 	printf("%d connections in %ld real seconds, %ld bytes read per connection\n", nConn, (long) time(NULL) - finishtime + s_time_config.maxtime, bytes_read / nConn);
390 
391 	/*
392 	 * Now loop and time connections using the same session id over and
393 	 * over
394 	 */
395 
396 next:
397 	if (!(s_time_config.perform & 2))
398 		goto end;
399 	printf("\n\nNow timing with session id reuse.\n");
400 
401 	/* Get an SSL object so we can reuse the session id */
402 	if ((scon = doConnection(NULL)) == NULL) {
403 		fprintf(stderr, "Unable to get connection\n");
404 		goto end;
405 	}
406 	if (s_time_config.www_path != NULL) {
407 		int retval = snprintf(buf, sizeof buf,
408 		    "GET %s HTTP/1.0\r\n\r\n", s_time_config.www_path);
409 		if ((size_t)retval >= sizeof buf) {
410 			fprintf(stderr, "URL too long\n");
411 			goto end;
412 		}
413 		SSL_write(scon, buf, strlen(buf));
414 		while (SSL_read(scon, buf, sizeof(buf)) > 0);
415 	}
416 	if (s_time_config.no_shutdown)
417 		SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN |
418 		    SSL_RECEIVED_SHUTDOWN);
419 	else
420 		SSL_shutdown(scon);
421 	shutdown(SSL_get_fd(scon), SHUT_RDWR);
422 	close(SSL_get_fd(scon));
423 
424 	nConn = 0;
425 	totalTime = 0.0;
426 
427 	finishtime = (long) time(NULL) + s_time_config.maxtime;
428 
429 	printf("starting\n");
430 	bytes_read = 0;
431 	tm_Time_F(START);
432 
433 	for (;;) {
434 		if (finishtime < (long) time(NULL))
435 			break;
436 		if ((doConnection(scon)) == NULL)
437 			goto end;
438 
439 		if (s_time_config.www_path) {
440 			int retval = snprintf(buf, sizeof buf,
441 			    "GET %s HTTP/1.0\r\n\r\n", s_time_config.www_path);
442 			if ((size_t)retval >= sizeof buf) {
443 				fprintf(stderr, "URL too long\n");
444 				goto end;
445 			}
446 			SSL_write(scon, buf, strlen(buf));
447 			while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
448 				bytes_read += i;
449 		}
450 		if (s_time_config.no_shutdown)
451 			SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN |
452 			    SSL_RECEIVED_SHUTDOWN);
453 		else
454 			SSL_shutdown(scon);
455 		shutdown(SSL_get_fd(scon), SHUT_RDWR);
456 		close(SSL_get_fd(scon));
457 
458 		nConn += 1;
459 		if (SSL_session_reused(scon))
460 			ver = 'r';
461 		else {
462 			ver = SSL_version(scon);
463 			if (ver == TLS1_VERSION)
464 				ver = 't';
465 			else if (ver == SSL3_VERSION)
466 				ver = '3';
467 			else if (ver == SSL2_VERSION)
468 				ver = '2';
469 			else
470 				ver = '*';
471 		}
472 		fputc(ver, stdout);
473 		fflush(stdout);
474 	}
475 	totalTime += tm_Time_F(STOP);	/* Add the time for this iteration */
476 
477 
478 	printf("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n", nConn, totalTime, ((double) nConn / totalTime), bytes_read);
479 	printf("%d connections in %ld real seconds, %ld bytes read per connection\n", nConn, (long) time(NULL) - finishtime + s_time_config.maxtime, bytes_read / nConn);
480 
481 	ret = 0;
482 end:
483 	if (scon != NULL)
484 		SSL_free(scon);
485 
486 	if (tm_ctx != NULL) {
487 		SSL_CTX_free(tm_ctx);
488 		tm_ctx = NULL;
489 	}
490 
491 	return (ret);
492 }
493 
494 /***********************************************************************
495  * doConnection - make a connection
496  * Args:
497  *		scon	= earlier ssl connection for session id, or NULL
498  * Returns:
499  *		SSL *	= the connection pointer.
500  */
501 static SSL *
502 doConnection(SSL * scon)
503 {
504 	struct pollfd pfd[1];
505 	SSL *serverCon;
506 	BIO *conn;
507 	long verify_error;
508 	int i;
509 
510 	if ((conn = BIO_new(BIO_s_connect())) == NULL)
511 		return (NULL);
512 
513 /*	BIO_set_conn_port(conn,port);*/
514 	BIO_set_conn_hostname(conn, s_time_config.host);
515 
516 	if (scon == NULL)
517 		serverCon = SSL_new(tm_ctx);
518 	else {
519 		serverCon = scon;
520 		SSL_set_connect_state(serverCon);
521 	}
522 
523 	SSL_set_bio(serverCon, conn, conn);
524 
525 	/* ok, lets connect */
526 	for (;;) {
527 		i = SSL_connect(serverCon);
528 		if (BIO_sock_should_retry(i)) {
529 			BIO_printf(bio_err, "DELAY\n");
530 
531 			i = SSL_get_fd(serverCon);
532 			pfd[0].fd = i;
533 			pfd[0].events = POLLIN;
534 			poll(pfd, 1, -1);
535 			continue;
536 		}
537 		break;
538 	}
539 	if (i <= 0) {
540 		BIO_printf(bio_err, "ERROR\n");
541 		verify_error = SSL_get_verify_result(serverCon);
542 		if (verify_error != X509_V_OK)
543 			BIO_printf(bio_err, "verify error:%s\n",
544 			    X509_verify_cert_error_string(verify_error));
545 		else
546 			ERR_print_errors(bio_err);
547 		if (scon == NULL)
548 			SSL_free(serverCon);
549 		return NULL;
550 	}
551 	return serverCon;
552 }
553