freebsd-src/crypto/openssl/apps/s_time.c
Pierre Pronchery b077aed33b Merge OpenSSL 3.0.9
Migrate to OpenSSL 3.0 in advance of FreeBSD 14.0.  OpenSSL 1.1.1 (the
version we were previously using) will be EOL as of 2023-09-11.

Most of the base system has already been updated for a seamless switch
to OpenSSL 3.0.  For many components we've added
`-DOPENSSL_API_COMPAT=0x10100000L` to CFLAGS to specify the API version,
which avoids deprecation warnings from OpenSSL 3.0.  Changes have also
been made to avoid OpenSSL APIs that were already deprecated in OpenSSL
1.1.1.  The process of updating to contemporary APIs can continue after
this merge.

Additional changes are still required for libarchive and Kerberos-
related libraries or tools; workarounds will immediately follow this
commit.  Fixes are in progress in the upstream projects and will be
incorporated when those are next updated.

There are some performance regressions in benchmarks (certain tests in
`openssl speed`) and in some OpenSSL consumers in ports (e.g.  haproxy).
Investigation will continue for these.

Netflix's testing showed no functional regression and a rather small,
albeit statistically significant, increase in CPU consumption with
OpenSSL 3.0.

Thanks to ngie@ and des@ for updating base system components, to
antoine@ and bofh@ for ports exp-runs and port fixes/workarounds, and to
Netflix and everyone who tested prior to commit or contributed to this
update in other ways.

PR:		271615
PR:		271656 [exp-run]
Relnotes:	Yes
Sponsored by:	The FreeBSD Foundation
2023-06-23 18:53:36 -04:00

474 lines
14 KiB
C

/*
* Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/opensslconf.h>
#ifndef OPENSSL_NO_SOCK
#include "apps.h"
#include "progs.h"
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include "s_apps.h"
#include <openssl/err.h>
#include "internal/sockets.h"
#if !defined(OPENSSL_SYS_MSDOS)
# include <unistd.h>
#endif
#define SSL_CONNECT_NAME "localhost:4433"
#define SECONDS 30
#define SECONDSSTR "30"
static SSL *doConnection(SSL *scon, const char *host, SSL_CTX *ctx);
/*
* Define a HTTP get command globally.
* Also define the size of the command, this is two bytes less than
* the size of the string because the %s is replaced by the URL.
*/
static const char fmt_http_get_cmd[] = "GET %s HTTP/1.0\r\n\r\n";
static const size_t fmt_http_get_cmd_size = sizeof(fmt_http_get_cmd) - 2;
typedef enum OPTION_choice {
OPT_COMMON,
OPT_CONNECT, OPT_CIPHER, OPT_CIPHERSUITES, OPT_CERT, OPT_NAMEOPT, OPT_KEY,
OPT_CAPATH, OPT_CAFILE, OPT_CASTORE,
OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE,
OPT_NEW, OPT_REUSE, OPT_BUGS, OPT_VERIFY, OPT_TIME, OPT_SSL3,
OPT_WWW, OPT_TLS1, OPT_TLS1_1, OPT_TLS1_2, OPT_TLS1_3,
OPT_PROV_ENUM
} OPTION_CHOICE;
const OPTIONS s_time_options[] = {
OPT_SECTION("General"),
{"help", OPT_HELP, '-', "Display this summary"},
OPT_SECTION("Connection"),
{"connect", OPT_CONNECT, 's',
"Where to connect as post:port (default is " SSL_CONNECT_NAME ")"},
{"new", OPT_NEW, '-', "Just time new connections"},
{"reuse", OPT_REUSE, '-', "Just time connection reuse"},
{"bugs", OPT_BUGS, '-', "Turn on SSL bug compatibility"},
{"cipher", OPT_CIPHER, 's', "TLSv1.2 and below cipher list to be used"},
{"ciphersuites", OPT_CIPHERSUITES, 's',
"Specify TLSv1.3 ciphersuites to be used"},
#ifndef OPENSSL_NO_SSL3
{"ssl3", OPT_SSL3, '-', "Just use SSLv3"},
#endif
#ifndef OPENSSL_NO_TLS1
{"tls1", OPT_TLS1, '-', "Just use TLSv1.0"},
#endif
#ifndef OPENSSL_NO_TLS1_1
{"tls1_1", OPT_TLS1_1, '-', "Just use TLSv1.1"},
#endif
#ifndef OPENSSL_NO_TLS1_2
{"tls1_2", OPT_TLS1_2, '-', "Just use TLSv1.2"},
#endif
#ifndef OPENSSL_NO_TLS1_3
{"tls1_3", OPT_TLS1_3, '-', "Just use TLSv1.3"},
#endif
{"verify", OPT_VERIFY, 'p',
"Turn on peer certificate verification, set depth"},
{"time", OPT_TIME, 'p', "Seconds to collect data, default " SECONDSSTR},
{"www", OPT_WWW, 's', "Fetch specified page from the site"},
OPT_SECTION("Certificate"),
{"nameopt", OPT_NAMEOPT, 's', "Certificate subject/issuer name printing options"},
{"cert", OPT_CERT, '<', "Cert file to use, PEM format assumed"},
{"key", OPT_KEY, '<', "File with key, PEM; default is -cert file"},
{"cafile", OPT_CAFILE, '<', "PEM format file of CA's"},
{"CAfile", OPT_CAFILE, '<', "PEM format file of CA's"},
{"CApath", OPT_CAPATH, '/', "PEM format directory of CA's"},
{"CAstore", OPT_CASTORE, ':', "URI to store of CA's"},
{"no-CAfile", OPT_NOCAFILE, '-',
"Do not load the default certificates file"},
{"no-CApath", OPT_NOCAPATH, '-',
"Do not load certificates from the default certificates directory"},
{"no-CAstore", OPT_NOCASTORE, '-',
"Do not load certificates from the default certificates store URI"},
OPT_PROV_OPTIONS,
{NULL}
};
#define START 0
#define STOP 1
static double tm_Time_F(int s)
{
return app_tminterval(s, 1);
}
int s_time_main(int argc, char **argv)
{
char buf[1024 * 8];
SSL *scon = NULL;
SSL_CTX *ctx = NULL;
const SSL_METHOD *meth = NULL;
char *CApath = NULL, *CAfile = NULL, *CAstore = NULL;
char *cipher = NULL, *ciphersuites = NULL;
char *www_path = NULL;
char *host = SSL_CONNECT_NAME, *certfile = NULL, *keyfile = NULL, *prog;
double totalTime = 0.0;
int noCApath = 0, noCAfile = 0, noCAstore = 0;
int maxtime = SECONDS, nConn = 0, perform = 3, ret = 1, i, st_bugs = 0;
long bytes_read = 0, finishtime = 0;
OPTION_CHOICE o;
int min_version = 0, max_version = 0, ver, buf_len, fd;
size_t buf_size;
meth = TLS_client_method();
prog = opt_init(argc, argv, s_time_options);
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
case OPT_EOF:
case OPT_ERR:
opthelp:
BIO_printf(bio_err, "%s: Use -help for summary.\n", prog);
goto end;
case OPT_HELP:
opt_help(s_time_options);
ret = 0;
goto end;
case OPT_CONNECT:
host = opt_arg();
break;
case OPT_REUSE:
perform = 2;
break;
case OPT_NEW:
perform = 1;
break;
case OPT_VERIFY:
verify_args.depth = opt_int_arg();
BIO_printf(bio_err, "%s: verify depth is %d\n",
prog, verify_args.depth);
break;
case OPT_CERT:
certfile = opt_arg();
break;
case OPT_NAMEOPT:
if (!set_nameopt(opt_arg()))
goto end;
break;
case OPT_KEY:
keyfile = opt_arg();
break;
case OPT_CAPATH:
CApath = opt_arg();
break;
case OPT_CAFILE:
CAfile = opt_arg();
break;
case OPT_NOCAPATH:
noCApath = 1;
break;
case OPT_NOCAFILE:
noCAfile = 1;
break;
case OPT_CASTORE:
CAstore = opt_arg();
break;
case OPT_NOCASTORE:
noCAstore = 1;
break;
case OPT_CIPHER:
cipher = opt_arg();
break;
case OPT_CIPHERSUITES:
ciphersuites = opt_arg();
break;
case OPT_BUGS:
st_bugs = 1;
break;
case OPT_TIME:
maxtime = opt_int_arg();
break;
case OPT_WWW:
www_path = opt_arg();
buf_size = strlen(www_path) + fmt_http_get_cmd_size;
if (buf_size > sizeof(buf)) {
BIO_printf(bio_err, "%s: -www option is too long\n", prog);
goto end;
}
break;
case OPT_SSL3:
min_version = SSL3_VERSION;
max_version = SSL3_VERSION;
break;
case OPT_TLS1:
min_version = TLS1_VERSION;
max_version = TLS1_VERSION;
break;
case OPT_TLS1_1:
min_version = TLS1_1_VERSION;
max_version = TLS1_1_VERSION;
break;
case OPT_TLS1_2:
min_version = TLS1_2_VERSION;
max_version = TLS1_2_VERSION;
break;
case OPT_TLS1_3:
min_version = TLS1_3_VERSION;
max_version = TLS1_3_VERSION;
break;
case OPT_PROV_CASES:
if (!opt_provider(o))
goto end;
break;
}
}
/* No extra arguments. */
argc = opt_num_rest();
if (argc != 0)
goto opthelp;
if (cipher == NULL)
cipher = getenv("SSL_CIPHER");
if ((ctx = SSL_CTX_new(meth)) == NULL)
goto end;
SSL_CTX_set_quiet_shutdown(ctx, 1);
if (SSL_CTX_set_min_proto_version(ctx, min_version) == 0)
goto end;
if (SSL_CTX_set_max_proto_version(ctx, max_version) == 0)
goto end;
if (st_bugs)
SSL_CTX_set_options(ctx, SSL_OP_ALL);
if (cipher != NULL && !SSL_CTX_set_cipher_list(ctx, cipher))
goto end;
if (ciphersuites != NULL && !SSL_CTX_set_ciphersuites(ctx, ciphersuites))
goto end;
if (!set_cert_stuff(ctx, certfile, keyfile))
goto end;
if (!ctx_set_verify_locations(ctx, CAfile, noCAfile, CApath, noCApath,
CAstore, noCAstore)) {
ERR_print_errors(bio_err);
goto end;
}
if (!(perform & 1))
goto next;
printf("Collecting connection statistics for %d seconds\n", maxtime);
/* Loop and time how long it takes to make connections */
bytes_read = 0;
finishtime = (long)time(NULL) + maxtime;
tm_Time_F(START);
for (;;) {
if (finishtime < (long)time(NULL))
break;
if ((scon = doConnection(NULL, host, ctx)) == NULL)
goto end;
if (www_path != NULL) {
buf_len = BIO_snprintf(buf, sizeof(buf), fmt_http_get_cmd,
www_path);
if (buf_len <= 0 || SSL_write(scon, buf, buf_len) <= 0)
goto end;
while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
bytes_read += i;
}
SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
BIO_closesocket(SSL_get_fd(scon));
nConn += 1;
if (SSL_session_reused(scon)) {
ver = 'r';
} else {
ver = SSL_version(scon);
if (ver == TLS1_VERSION)
ver = 't';
else if (ver == SSL3_VERSION)
ver = '3';
else
ver = '*';
}
fputc(ver, stdout);
fflush(stdout);
SSL_free(scon);
scon = NULL;
}
totalTime += tm_Time_F(STOP); /* Add the time for this iteration */
i = (int)((long)time(NULL) - finishtime + maxtime);
printf
("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n",
nConn, totalTime, ((double)nConn / totalTime), bytes_read);
printf
("%d connections in %ld real seconds, %ld bytes read per connection\n",
nConn, (long)time(NULL) - finishtime + maxtime,
nConn > 0 ? bytes_read / nConn : 0l);
/*
* Now loop and time connections using the same session id over and over
*/
next:
if (!(perform & 2))
goto end;
printf("\n\nNow timing with session id reuse.\n");
/* Get an SSL object so we can reuse the session id */
if ((scon = doConnection(NULL, host, ctx)) == NULL) {
BIO_printf(bio_err, "Unable to get connection\n");
goto end;
}
if (www_path != NULL) {
buf_len = BIO_snprintf(buf, sizeof(buf), fmt_http_get_cmd, www_path);
if (buf_len <= 0 || SSL_write(scon, buf, buf_len) <= 0)
goto end;
while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
continue;
}
SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
if ((fd = SSL_get_fd(scon)) >= 0)
BIO_closesocket(fd);
nConn = 0;
totalTime = 0.0;
finishtime = (long)time(NULL) + maxtime;
printf("starting\n");
bytes_read = 0;
tm_Time_F(START);
for (;;) {
if (finishtime < (long)time(NULL))
break;
if ((doConnection(scon, host, ctx)) == NULL)
goto end;
if (www_path != NULL) {
buf_len = BIO_snprintf(buf, sizeof(buf), fmt_http_get_cmd,
www_path);
if (buf_len <= 0 || SSL_write(scon, buf, buf_len) <= 0)
goto end;
while ((i = SSL_read(scon, buf, sizeof(buf))) > 0)
bytes_read += i;
}
SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
if ((fd = SSL_get_fd(scon)) >= 0)
BIO_closesocket(fd);
nConn += 1;
if (SSL_session_reused(scon)) {
ver = 'r';
} else {
ver = SSL_version(scon);
if (ver == TLS1_VERSION)
ver = 't';
else if (ver == SSL3_VERSION)
ver = '3';
else
ver = '*';
}
fputc(ver, stdout);
fflush(stdout);
}
totalTime += tm_Time_F(STOP); /* Add the time for this iteration */
printf
("\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n",
nConn, totalTime, ((double)nConn / totalTime), bytes_read);
if (nConn > 0)
printf
("%d connections in %ld real seconds, %ld bytes read per connection\n",
nConn, (long)time(NULL) - finishtime + maxtime, bytes_read / nConn);
else
printf("0 connections in %ld real seconds\n",
(long)time(NULL) - finishtime + maxtime);
ret = 0;
end:
SSL_free(scon);
SSL_CTX_free(ctx);
return ret;
}
/*-
* doConnection - make a connection
*/
static SSL *doConnection(SSL *scon, const char *host, SSL_CTX *ctx)
{
BIO *conn;
SSL *serverCon;
int i;
if ((conn = BIO_new(BIO_s_connect())) == NULL)
return NULL;
if (BIO_set_conn_hostname(conn, host) <= 0
|| BIO_set_conn_mode(conn, BIO_SOCK_NODELAY) <= 0) {
BIO_free(conn);
return NULL;
}
if (scon == NULL) {
serverCon = SSL_new(ctx);
if (serverCon == NULL) {
BIO_free(conn);
return NULL;
}
} else {
serverCon = scon;
SSL_set_connect_state(serverCon);
}
SSL_set_bio(serverCon, conn, conn);
/* ok, lets connect */
i = SSL_connect(serverCon);
if (i <= 0) {
BIO_printf(bio_err, "ERROR\n");
if (verify_args.error != X509_V_OK)
BIO_printf(bio_err, "verify error:%s\n",
X509_verify_cert_error_string(verify_args.error));
else
ERR_print_errors(bio_err);
if (scon == NULL)
SSL_free(serverCon);
return NULL;
}
#if defined(SOL_SOCKET) && defined(SO_LINGER)
{
struct linger no_linger;
int fd;
no_linger.l_onoff = 1;
no_linger.l_linger = 0;
fd = SSL_get_fd(serverCon);
if (fd >= 0)
(void)setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&no_linger,
sizeof(no_linger));
}
#endif
return serverCon;
}
#endif /* OPENSSL_NO_SOCK */