diff options
-rw-r--r-- | demos/guide/Makefile | 12 | ||||
-rw-r--r-- | demos/guide/tls-server-block.c | 284 | ||||
-rw-r--r-- | doc/build.info | 6 | ||||
-rw-r--r-- | doc/man7/ossl-guide-introduction.pod | 2 | ||||
-rw-r--r-- | doc/man7/ossl-guide-tls-client-block.pod | 3 | ||||
-rw-r--r-- | doc/man7/ossl-guide-tls-introduction.pod | 10 | ||||
-rw-r--r-- | doc/man7/ossl-guide-tls-server-block.pod | 329 |
7 files changed, 641 insertions, 5 deletions
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 <string.h> + +/* Include the appropriate header file for SOCK_STREAM */ +#ifdef _WIN32 /* Windows */ +# include <stdarg.h> +# include <winsock2.h> +#else /* Linux/Unix */ +# include <err.h> +# include <sys/socket.h> +# include <sys/select.h> +#endif + +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +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; +} diff --git a/doc/build.info b/doc/build.info index cb9c76b..f84364d 100644 --- a/doc/build.info +++ b/doc/build.info @@ -4919,6 +4919,10 @@ DEPEND[html/man7/ossl-guide-tls-introduction.html]=man7/ossl-guide-tls-introduct GENERATE[html/man7/ossl-guide-tls-introduction.html]=man7/ossl-guide-tls-introduction.pod DEPEND[man/man7/ossl-guide-tls-introduction.7]=man7/ossl-guide-tls-introduction.pod GENERATE[man/man7/ossl-guide-tls-introduction.7]=man7/ossl-guide-tls-introduction.pod +DEPEND[html/man7/ossl-guide-tls-server-block.html]=man7/ossl-guide-tls-server-block.pod +GENERATE[html/man7/ossl-guide-tls-server-block.html]=man7/ossl-guide-tls-server-block.pod +DEPEND[man/man7/ossl-guide-tls-server-block.7]=man7/ossl-guide-tls-server-block.pod +GENERATE[man/man7/ossl-guide-tls-server-block.7]=man7/ossl-guide-tls-server-block.pod DEPEND[html/man7/ossl_store-file.html]=man7/ossl_store-file.pod GENERATE[html/man7/ossl_store-file.html]=man7/ossl_store-file.pod DEPEND[man/man7/ossl_store-file.7]=man7/ossl_store-file.pod @@ -5135,6 +5139,7 @@ html/man7/ossl-guide-quic-multi-stream.html \ html/man7/ossl-guide-tls-client-block.html \ html/man7/ossl-guide-tls-client-non-block.html \ html/man7/ossl-guide-tls-introduction.html \ +html/man7/ossl-guide-tls-server-block.html \ html/man7/ossl_store-file.html \ html/man7/ossl_store.html \ html/man7/passphrase-encoding.html \ @@ -5279,6 +5284,7 @@ man/man7/ossl-guide-quic-multi-stream.7 \ man/man7/ossl-guide-tls-client-block.7 \ man/man7/ossl-guide-tls-client-non-block.7 \ man/man7/ossl-guide-tls-introduction.7 \ +man/man7/ossl-guide-tls-server-block.7 \ man/man7/ossl_store-file.7 \ man/man7/ossl_store.7 \ man/man7/passphrase-encoding.7 \ diff --git a/doc/man7/ossl-guide-introduction.pod b/doc/man7/ossl-guide-introduction.pod index b6c1f95..4fd94f0 100644 --- a/doc/man7/ossl-guide-introduction.pod +++ b/doc/man7/ossl-guide-introduction.pod @@ -79,6 +79,8 @@ The pages in the guide are as follows: =item L<ossl-guide-tls-client-non-block(7)>: Writing a simple nonblocking TLS client +=item L<ossl-guide-tls-server-block(7)>: Writing a simple blocking TLS server + =item L<ossl-guide-quic-introduction(7)>: An introduction to QUIC in OpenSSL =item L<ossl-guide-quic-client-block(7)>: Writing a simple blocking QUIC client diff --git a/doc/man7/ossl-guide-tls-client-block.pod b/doc/man7/ossl-guide-tls-client-block.pod index 8f72fe3..34c6b36 100644 --- a/doc/man7/ossl-guide-tls-client-block.pod +++ b/doc/man7/ossl-guide-tls-client-block.pod @@ -564,6 +564,9 @@ intermediate CAs, or the issuer is simply unrecognised). See L<ossl-guide-tls-client-non-block(7)> to read a tutorial on how to modify the client developed on this page to support a nonblocking socket. +See L<ossl-guide-tls-server-block(7)> for a tutorial on how to implement a +simple TLS server handling one client at a time over a blocking socket. + See L<ossl-guide-quic-client-block(7)> to read a tutorial on how to modify the client developed on this page to support QUIC instead of TLS. diff --git a/doc/man7/ossl-guide-tls-introduction.pod b/doc/man7/ossl-guide-tls-introduction.pod index fd8184e..d7c05f6 100644 --- a/doc/man7/ossl-guide-tls-introduction.pod +++ b/doc/man7/ossl-guide-tls-introduction.pod @@ -230,7 +230,8 @@ circumstances where you want them to be different. It is up to the application programmer to create the B<BIO> objects that are needed and supply them to the B<SSL> object. See -L<ossl-guide-tls-client-block(7)> for further information. +L<ossl-guide-tls-client-block(7)> and L<ossl-guide-tls-server-block(7)> for +usage examples. Finally, an endpoint can establish a "session" with its peer. The session holds various TLS parameters about the connection between the client and the server. @@ -295,15 +296,18 @@ object. =head1 FURTHER READING -See L<ossl-guide-tls-client-block(7)> to see an example of applying these +See L<ossl-guide-tls-client-block(7)> for an example of how to apply these concepts in order to write a simple TLS client based on a blocking socket. +See L<ossl-guide-tls-server-block(7)> for an example of how to apply these +concepts in order to write a simple TLS server handling one client at a time +over a blocking socket. See L<ossl-guide-quic-introduction(7)> for an introduction to QUIC in OpenSSL. =head1 SEE ALSO L<ossl-guide-introduction(7)>, L<ossl-guide-libraries-introduction(7)>, L<ossl-guide-libssl-introduction(7)>, L<ossl-guide-tls-client-block(7)>, -L<ossl-guide-quic-introduction(7)> +L<ossl-guide-tls-server-block(7)>, L<ossl-guide-quic-introduction(7)> =head1 COPYRIGHT diff --git a/doc/man7/ossl-guide-tls-server-block.pod b/doc/man7/ossl-guide-tls-server-block.pod new file mode 100644 index 0000000..e6bd2b0 --- /dev/null +++ b/doc/man7/ossl-guide-tls-server-block.pod @@ -0,0 +1,329 @@ +=pod + +=begin comment + +NB: Changes to the source code samples in this file should also be reflected in +demos/guide/tls-server-block.c + +=end comment + +=head1 NAME + +ossl-guide-tls-server-block +- OpenSSL Guide: Writing a simple blocking TLS server + +=head1 SIMPLE BLOCKING TLS SERVER EXAMPLE + +This page will present various source code samples demonstrating how to write a +simple, non-concurrent, TLS "echo" server application which accepts one client +connection at a time, echoing input from the client back to the same client. +Once the current client disconnects, the next client connection is accepted. + +Both the acceptor socket and client connections are "blocking". A more typical +server might use nonblocking sockets with an event loop and callbacks for I/O +events. + +The complete source code for this example blocking TLS server is available in +the B<demos/guide> directory of the OpenSSL source distribution in the file +B<tls-server-block.c>. It is also available online at +L<https://github.com/openssl/openssl/blob/master/demos/guide/tls-server-block.c>. + +We assume that you already have OpenSSL installed on your system; that you +already have some fundamental understanding of OpenSSL concepts and TLS (see +L<ossl-guide-libraries-introduction(7)> and L<ossl-guide-tls-introduction(7)>); +and that you know how to write and build C code and link it against the +libcrypto and libssl libraries that are provided by OpenSSL. It also assumes +that you have a basic understanding of TCP/IP and sockets. + +=head2 Creating the SSL_CTX and SSL objects + +The first step is to create an B<SSL_CTX> object for our server. We use the +L<SSL_CTX_new(3)> function for this purpose. We could alternatively use +L<SSL_CTX_new_ex(3)> if we want to associate the B<SSL_CTX> with a particular +B<OSSL_LIB_CTX> (see L<ossl-guide-libraries-introduction(7)> to learn about +B<OSSL_LIB_CTX>). We pass as an argument the return value of the function +L<TLS_server_method(3)>. You should use this method whenever you are writing a +TLS server. This method will automatically use TLS version negotiation to select +the highest version of the protocol that is mutually supported by both the +server and the client. + + /* + * 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"); + } + +We would also like to restrict the TLS versions that we are willing to accept to +TLSv1.2 or above. TLS protocol versions earlier than that are generally to be +avoided where possible. We can do that using +L<SSL_CTX_set_min_proto_version(3)>: + + /* + * 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"); + } + +Next we configure some option flags, see L<SSL_CTX_set_options(3)> for details: + + /* + * 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); + +Servers need a private key and certificate. Though anonymous ciphers (no +server certificate) are possible in TLS 1.2, they are rarely applicable, and +are not currently defined for TLS 1.3. Additional intermediate issuer CA +certificates are often also required, and both the server (end-entity or EE) +certificate and the issuer ("chain") certificates are most easily configured in +a single "chain file". Below we load such a chain file (the EE certificate +must appear first), and then load the corresponding private key, checking that +it matches the server certificate. No checks are performed to check the +integrity of the chain (CA signatures or certificate expiration dates, for +example). + + /* + * 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. + */ + 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???"); + } + +Next we enable session caching, which makes it possible for clients to more +efficiently make additional TLS connections after completing an initial full +TLS handshake. With TLS 1.3, session resumption typically still performs a fresh +key agreement, but the certificate exchange is avoided. + + /* + * 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); + +Most servers, including this one, do not solicit client certificates. We +therefore do not need a "trust store" and allow the handshake to complete even +when the client does not present a certificate. Note: Even if a client did +present a trusted ceritificate, for it to be useful, the server application +would still need custom code to use the verified identity to grant nondefault +access to that particular client. Some servers grant access to all clients +with certificates from a private CA, this then requires processing of +certificate revocation lists to deauthorise a client. It is often simpler and +more secure to instead keep a list of authorised public keys. + +Though this is the default setting, we explicitly call the +L<SSL_CTX_set_verify(3)> function and pass the B<SSL_VERIFY_NONE> value to it. +The final argument to this function is a callback that you can optionally +supply to override the default handling for certificate verification. Most +applications do not need to do this so this can safely be set to NULL to get +the default handling. + + /* + * 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); + +That is all the setup that we need to do for the B<SSL_CTX>. Next we create an +acceptor BIO on which to accept client connections. This just records the +intended port (and optional "host:" prefix), without actually creating the +socket. This delayed processing allows the programmer to specify additional +behaviours before the listening socket is actually created. + + /* + * 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"); + } + +Servers almost always want to use the "SO_REUSEADDR" option to avoid startup +failures if there are still lingering client connections, so we do that before +making the B<first> call to L<BIO_do_accept(3)> which creates the listening +socket, without accepting a client connection. Subsequent calls to the same +function will accept new connections. + + 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"); + } + +=head2 Server loop + +The server now enters a "forever" loop handling one client connection at a +time. Before each connection we clear the OpenSSL error stack, so that any +error reports are related to just the new connection. + + /* Pristine error stack for each new connection */ + ERR_clear_error(); + +At this point the server blocks to accept the next client: + + /* Wait for the next client to connect */ + if (BIO_do_accept(acceptor_bio) <= 0) { + /* Client went away before we accepted the connection */ + continue; + } + +On success the accepted client connection has been wrapped in a fresh BIO and +pushed onto the end of the acceptor BIO chain. We pop it off returning the +acceptor BIO to its initial state. + + /* Pop the client connection from the BIO chain */ + client_bio = BIO_pop(acceptor_bio); + fprintf(stderr, "New client connection accepted\n"); + +Next, we create an B<SSL> object by calling the B<SSL_new(3)> function and +passing the B<SSL_CTX> we created as an argument. The client connection BIO is +configured as the I/O conduit for this SSL handle. SSL_set_bio transfers +ownership of the BIO or BIOs involved (our B<client_bio>) to the SSL handle. + + /* 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); + +And now we're ready to attempt the SSL handshake. With a blocking socket +OpenSSL will perform all the read and write operations required to complete the +handshake (or detect and report a failure) before returning. + + /* 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; + } + +With the handshake complete, the server loops echoing client input back to the +client: + + 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; + } + +Once the client closes its connection, we report the number of bytes sent to +B<stderr> and free the SSL handle, which also frees the B<client_bio> and +closes the underlying socket. + + fprintf(stderr, "Client connection closed, %zu bytes sent\n", total); + SSL_free(ssl); + +The server is now ready to accept the next client connection. + +=head2 Final clean up + +If the server could somehow manage to break out of the infinite loop, and +be ready to exit, it would first deallocate the constructed B<SSL_CTX>. + + /* + * Unreachable placeholder cleanup code, the above loop runs forever. + */ + SSL_CTX_free(ctx); + return EXIT_SUCCESS; + +=head1 SEE ALSO + +L<ossl-guide-introduction(7)>, L<ossl-guide-libraries-introduction(7)>, +L<ossl-guide-libssl-introduction(7)>, L<ossl-guide-tls-introduction(7)>, +L<ossl-guide-tls-client-non-block(7)>, L<ossl-guide-quic-client-block(7)> + +=head1 COPYRIGHT + +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 +L<https://www.openssl.org/source/license.html>. + +=cut |