From f4b4a185b546044150821f1929e5cd6fd0dfba99 Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Mon, 27 May 2024 21:35:56 +1000 Subject: MVP demo TLS server - No concurrency, one client-at-a-time - Blocking - No client certs - Fixed chain and key file names - Minimal support for session resumption Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Todd Short (Merged from https://github.com/openssl/openssl/pull/24505) --- demos/guide/Makefile | 12 +- demos/guide/tls-server-block.c | 284 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 demos/guide/tls-server-block.c (limited to 'demos') diff --git a/demos/guide/Makefile b/demos/guide/Makefile index 29a0fd5..943d2f3 100644 --- a/demos/guide/Makefile +++ b/demos/guide/Makefile @@ -5,6 +5,7 @@ # LD_LIBRARY_PATH=../.. ./tls-client-block www.example.com 443 TESTS = tls-client-block \ + tls-server-block \ quic-client-block \ quic-multi-stream \ tls-client-non-block \ @@ -14,21 +15,28 @@ CFLAGS = -I../../include -g -Wall LDFLAGS = -L../.. LDLIBS = -lcrypto -lssl -all: $(TESTS) +all: $(TESTS) chain tls-client-block: tls-client-block.o +tls-server-block: tls-server-block.o quic-client-block: quic-client-block.o quic-multi-stream: quic-multi-stream.o tls-client-non-block: tls-client-non-block.o quic-client-non-block: quic-client-non-block.o +chain: chain.pem +pkey.pem: + openssl genpkey -algorithm rsa -out pkey.pem -pkeyopt rsa_keygen_bits:2048 +chain.pem: pkey.pem + openssl req -x509 -new -key pkey.pem -days 36500 -subj / -out chain.pem + $(TESTS): $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) clean: $(RM) $(TESTS) *.o -.PHONY: test +.PHONY: test chain test: all @echo "\nTLS and QUIC tests:" @echo "skipped" diff --git a/demos/guide/tls-server-block.c b/demos/guide/tls-server-block.c new file mode 100644 index 0000000..45246ce --- /dev/null +++ b/demos/guide/tls-server-block.c @@ -0,0 +1,284 @@ +/* + * Copyright 2024 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 + */ + +/* + * NB: Changes to this file should also be reflected in + * doc/man7/ossl-guide-tls-server-block.pod + */ + +#include + +/* Include the appropriate header file for SOCK_STREAM */ +#ifdef _WIN32 /* Windows */ +# include +# include +#else /* Linux/Unix */ +# include +# include +# include +#endif + +#include +#include +#include + +static const char cache_id[] = "OpenSSL Demo Server"; + +#ifdef _WIN32 +static const char *progname; + +static void vwarnx(const char *fmt, va_list ap) +{ + if (progname != NULL) + fprintf(stderr, "%s: ", progname); + vfprintf(stderr, fmt, ap); + putc('\n', stderr); +} + +static void errx(int status, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + exit(status); +} + +static void warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} +#endif + +/* Minimal TLS echo server. */ +int main(int argc, char *argv[]) +{ + int res = EXIT_FAILURE; + long opts; + const char *hostport; + SSL_CTX *ctx = NULL; + BIO *acceptor_bio; + +#ifdef _WIN32 + progname = argv[0]; +#endif + + if (argc != 2) + errx(res, "Usage: %s [host:]port", argv[0]); + hostport = argv[1]; + + /* + * An SSL_CTX holds shared configuration information for multiple + * subsequent per-client SSL connections. + */ + ctx = SSL_CTX_new(TLS_server_method()); + if (ctx == NULL) { + ERR_print_errors_fp(stderr); + errx(res, "Failed to create server SSL_CTX"); + } + + /* + * TLS versions older than TLS 1.2 are deprecated by IETF and SHOULD + * be avoided if possible. + */ + if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(res, "Failed to set the minimum TLS protocol version"); + } + +#if 0 + /* + * In applications (e.g. SMTP) where most clients are performing + * unauthenticated opportunistic TLS it may make sense to set the security + * level to 0, allowing weaker encryption parameters, which are still + * stronger than a potential cleartext fallback. + * + * The default security level is 2 (as of OpenSSL 3.2), which is roughly + * equivalent to that of 112 bit symmetric keys, or 2048-bit RSA or + * finite-field Diffie-Hellman keys. Notably, non-zero security levels no + * longer allow the use of SHA-1 in certificate signatures, key exchange + * or in the TLS 1.[01] PRF (so TLS 1.0 and 1.1 require security level 0). + */ + SSL_CTX_set_security_level(ctx, 0); +#endif + + /* + * Tolerate clients hanging up without a TLS "shutdown". Appropriate in all + * application protocols which perform their own message "framing", and + * don't rely on TLS to defend against "truncation" attacks. + */ + opts = SSL_OP_IGNORE_UNEXPECTED_EOF; + + /* + * Block potential CPU-exhaustion attacks by clients that request frequent + * renegotiation. This is of course only effective if there are existing + * limits on initial full TLS handshake or connection rates. + */ + opts |= SSL_OP_NO_RENEGOTIATION; + + /* + * Most servers elect to use their own cipher preference rather than that of + * the client. + */ + opts |= SSL_OP_CIPHER_SERVER_PREFERENCE; + + /* Apply the selection options */ + SSL_CTX_set_options(ctx, opts); + + /* + * Load the server's certificate *chain* file (PEM format), which includes + * not only the leaf (end-entity) server certificate, but also any + * intermediate issuer-CA certificates. The leaf certificate must be the + * first certificate in the file. + * + * In advanced use-cases this can be called multiple times, once per public + * key algorithm for which the server has a corresponding certificate. + * However, the corresponding private key (see below) must be loaded first, + * *before* moving on to the next chain file. + * + * The requisite files "chain.pem" and "pkey.pem" can be generated by running + * "make chain" in this directory. If the server will be executed from some + * other directory, move or copy the files there. + */ + if (SSL_CTX_use_certificate_chain_file(ctx, "chain.pem") <= 0) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(res, "Failed to load the server certificate chain file"); + } + + /* + * Load the corresponding private key, this also checks that the private + * key matches the just loaded end-entity certificate. It does not check + * whether the certificate chain is valid, the certificates could be + * expired, or may otherwise fail to form a chain that a client can validate. + */ + if (SSL_CTX_use_PrivateKey_file(ctx, "pkey.pem", SSL_FILETYPE_PEM) <= 0) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(res, "Error loading the server private key file, " + "possible key/cert mismatch???"); + } + + /* + * Servers that want to enable session resumption must specify a cache id + * byte array, that identifies the server application, and reduces the + * chance of inappropriate cache sharing. + */ + SSL_CTX_set_session_id_context(ctx, (void *)cache_id, sizeof(cache_id)); + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); + + /* + * How many client TLS sessions to cache. The default is + * SSL_SESSION_CACHE_MAX_SIZE_DEFAULT (20k in recent OpenSSL versions), + * which may be too small or too large. + */ + SSL_CTX_sess_set_cache_size(ctx, 1024); + + /* + * Sessions older than this are considered a cache miss even if still in + * the cache. The default is two hours. Busy servers whose clients make + * many connections in a short burst may want a shorter timeout, on lightly + * loaded servers with sporadic connections from any given client, a longer + * time may be appropriate. + */ + SSL_CTX_set_timeout(ctx, 3600); + + /* + * Clients rarely employ certificate-based authentication, and so we don't + * require "mutual" TLS authentication (indeed there's no way to know + * whether or how the client authenticated the server, so the term "mutual" + * is potentially misleading). + * + * Since we're not soliciting or processing client certificates, we don't + * need to configure a trusted-certificate store, so no call to + * SSL_CTX_set_default_verify_paths() is needed. The server's own + * certificate chain is assumed valid. + */ + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + + /* + * Create a listener socket wrapped in a BIO. + * The first call to BIO_do_accept() initialises the socket + */ + acceptor_bio = BIO_new_accept(hostport); + if (acceptor_bio == NULL) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(res, "Error creating acceptor bio"); + } + + BIO_set_bind_mode(acceptor_bio, BIO_BIND_REUSEADDR); + if (BIO_do_accept(acceptor_bio) <= 0) { + SSL_CTX_free(ctx); + ERR_print_errors_fp(stderr); + errx(res, "Error setting up acceptor socket"); + } + + /* Wait for incoming connection */ + for (;;) { + BIO *client_bio; + SSL *ssl; + unsigned char buf[8192]; + size_t nread; + size_t nwritten; + size_t total = 0; + + /* Pristine error stack for each new connection */ + ERR_clear_error(); + + /* Wait for the next client to connect */ + if (BIO_do_accept(acceptor_bio) <= 0) { + /* Client went away before we accepted the connection */ + continue; + } + + /* Pop the client connection from the BIO chain */ + client_bio = BIO_pop(acceptor_bio); + fprintf(stderr, "New client connection accepted\n"); + + /* Associate a new SSL handle with the new connection */ + if ((ssl = SSL_new(ctx)) == NULL) { + ERR_print_errors_fp(stderr); + warnx("Error creating SSL handle for new connection"); + BIO_free(client_bio); + continue; + } + SSL_set_bio(ssl, client_bio, client_bio); + + /* Attempt an SSL handshake with the client */ + if (SSL_accept(ssl) <= 0) { + ERR_print_errors_fp(stderr); + warnx("Error performing SSL handshake with client"); + SSL_free(ssl); + continue; + } + + while (SSL_read_ex(ssl, buf, sizeof(buf), &nread) > 0) { + if (SSL_write_ex(ssl, buf, nread, &nwritten) > 0 && + nwritten == nread) { + total += nwritten; + continue; + } + warnx("Error echoing client input"); + break; + } + fprintf(stderr, "Client connection closed, %zu bytes sent\n", total); + SSL_free(ssl); + } + + /* + * Unreachable placeholder cleanup code, the above loop runs forever. + */ + SSL_CTX_free(ctx); + return EXIT_SUCCESS; +} -- cgit v1.1