From: Andrew Dinh Date: Mon, 23 Sep 2024 06:25:24 +0000 (-0700) Subject: Add a guide for demo QUIC blocking server X-Git-Tag: openssl-3.5.0-alpha1~356 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9dfacaa82a518c8c459dd6e33a7dd205129692f4;p=thirdparty%2Fopenssl.git Add a guide for demo QUIC blocking server Reviewed-by: Matt Caswell Reviewed-by: Neil Horman (Merged from https://github.com/openssl/openssl/pull/25361) --- diff --git a/demos/guide/quic-server-block.c b/demos/guide/quic-server-block.c index f451ce7c6dc..a424fbfddb3 100644 --- a/demos/guide/quic-server-block.c +++ b/demos/guide/quic-server-block.c @@ -231,7 +231,7 @@ static int run_quic_server(SSL_CTX *ctx, int fd) while (SSL_read_ex(conn, buf, sizeof(buf), &nread) > 0) { if (SSL_write_ex(conn, buf, nread, &nwritten) > 0 && nwritten == nread) - break; + continue; fprintf(stderr, "Error echoing client input"); break; } diff --git a/doc/build.info b/doc/build.info index 2ccdae3ad38..5141aac8b10 100644 --- a/doc/build.info +++ b/doc/build.info @@ -5027,6 +5027,10 @@ DEPEND[html/man7/ossl-guide-quic-multi-stream.html]=man7/ossl-guide-quic-multi-s GENERATE[html/man7/ossl-guide-quic-multi-stream.html]=man7/ossl-guide-quic-multi-stream.pod DEPEND[man/man7/ossl-guide-quic-multi-stream.7]=man7/ossl-guide-quic-multi-stream.pod GENERATE[man/man7/ossl-guide-quic-multi-stream.7]=man7/ossl-guide-quic-multi-stream.pod +DEPEND[html/man7/ossl-guide-quic-server-block.html]=man7/ossl-guide-quic-server-block.pod +GENERATE[html/man7/ossl-guide-quic-server-block.html]=man7/ossl-guide-quic-server-block.pod +DEPEND[man/man7/ossl-guide-quic-server-block.7]=man7/ossl-guide-quic-server-block.pod +GENERATE[man/man7/ossl-guide-quic-server-block.7]=man7/ossl-guide-quic-server-block.pod DEPEND[html/man7/ossl-guide-tls-client-block.html]=man7/ossl-guide-tls-client-block.pod GENERATE[html/man7/ossl-guide-tls-client-block.html]=man7/ossl-guide-tls-client-block.pod DEPEND[man/man7/ossl-guide-tls-client-block.7]=man7/ossl-guide-tls-client-block.pod @@ -5267,6 +5271,7 @@ html/man7/ossl-guide-quic-client-block.html \ html/man7/ossl-guide-quic-client-non-block.html \ html/man7/ossl-guide-quic-introduction.html \ html/man7/ossl-guide-quic-multi-stream.html \ +html/man7/ossl-guide-quic-server-block.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 \ @@ -5420,6 +5425,7 @@ man/man7/ossl-guide-quic-client-block.7 \ man/man7/ossl-guide-quic-client-non-block.7 \ man/man7/ossl-guide-quic-introduction.7 \ man/man7/ossl-guide-quic-multi-stream.7 \ +man/man7/ossl-guide-quic-server-block.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 \ diff --git a/doc/man7/ossl-guide-introduction.pod b/doc/man7/ossl-guide-introduction.pod index 7ab41164da1..9dc540e764c 100644 --- a/doc/man7/ossl-guide-introduction.pod +++ b/doc/man7/ossl-guide-introduction.pod @@ -85,6 +85,8 @@ The pages in the guide are as follows: =item L: Writing a simple blocking QUIC client +=item L: Writing a simple blocking QUIC server + =item L: Writing a simple multi-stream QUIC client =item L: Writing a simple nonblocking QUIC client diff --git a/doc/man7/ossl-guide-quic-server-block.pod b/doc/man7/ossl-guide-quic-server-block.pod new file mode 100644 index 00000000000..7711cb014e2 --- /dev/null +++ b/doc/man7/ossl-guide-quic-server-block.pod @@ -0,0 +1,282 @@ +=pod + +=begin comment + +NB: Changes to the source code samples in this file should also be reflected in +demos/guide/quic-server-block.c + +=end comment + +=head1 NAME + +ossl-guide-quic-server-block +- OpenSSL Guide: Writing a simple blocking QUIC server + +=head1 SIMPLE BLOCKING QUIC SERVER EXAMPLE + +This page will present various source code samples demonstrating how to write a +simple, non-concurrent, QUIC "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. + +The server only accepts HTTP/1.0 requests, which is non-standard and will not +be supported by real world servers. This is for demonstration purposes only. + +Both the accepting 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 QUIC server is available in +the B directory of the OpenSSL source distribution in the file +B. It is also available online at +L. + +We assume that you already have OpenSSL installed on your system; that you +already have some fundamental understanding of OpenSSL concepts and QUIC (see +L and L); +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 UDP/IP and sockets. + +=head2 Creating the SSL_CTX and SSL objects + +The first step is to create an B object for our server. We use the +L function for this purpose. We pass as an argument the return +value of the function L. You should use this method +whenever you are writing a QUIC server. + + /* + * An SSL_CTX holds shared configuration information for multiple + * subsequent per-client SSL connections. We specifically load a QUIC + * server method here. + */ + ctx = SSL_CTX_new(OSSL_QUIC_server_method()); + if (ctx == NULL) + goto err; + +Servers need a private key and certificate. Intermediate issuer CA +certificates are often 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), but we do verify the consistency of the private key with the +corresponding certificate. + + /* + * 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, cert_path) <= 0) { + fprintf(stderr, "couldn't load certificate file: %s\n", cert_path); + goto err; + } + + /* + * 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, key_path, SSL_FILETYPE_PEM) <= 0) { + fprintf(stderr, "couldn't load key file: %s\n", key_path); + goto err; + } + +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 certificate, 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 function and pass the B 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); + + +QUIC also dictates using Application-Layer Protocol Negotiation (ALPN) to select +an application protocol. We use L for this +purpose. We can pass a callback which will be called for each connection to +select an ALPN the server considers acceptable. + + /* Setup ALPN negotiation callback to decide which ALPN is accepted. */ + SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL); + +In this case, we only accept "http/1.0" and "hq-interop". + + /* + * ALPN strings for TLS handshake. Only 'http/1.0' and 'hq-interop' + * are accepted. + */ + static const unsigned char alpn_ossltest[] = { + 8, 'h', 't', 't', 'p', '/', '1', '.', '0', + 10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p', + }; + + static int select_alpn(SSL *ssl, const unsigned char **out, + unsigned char *out_len, const unsigned char *in, + unsigned int in_len, void *arg) + { + if (SSL_select_next_proto((unsigned char **)out, out_len, alpn_ossltest, + sizeof(alpn_ossltest), in, + in_len) == OPENSSL_NPN_NEGOTIATED) + return SSL_TLSEXT_ERR_OK; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + +That is all the setup that we need to do for the B. Next, we create a +UDP socket and bind to it on localhost. + + /* Retrieve the file descriptor for a new UDP socket */ + if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + fprintf(stderr, "cannot create socket"); + goto err; + } + + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + + /* Bind to the new UDP socket on localhost */ + if (bind(fd, (const struct sockaddr *)&sa, sizeof(sa)) < 0) { + fprintf(stderr, "cannot bind to %u\n", port); + BIO_closesocket(fd); + goto err; + } + +To run the QUIC server, we create an B to listen for incoming +connections. We provide it with the bound UDP port to then explicitly begin +listening for new connections. + + /* + * Create a new QUIC listener. Listeners, and other QUIC objects, default + * to operating in blocking mode. The configured behaviour is inherited by + * child objects. + */ + if ((listener = SSL_new_listener(ctx, 0)) == NULL) { + goto err; + } + + /* Provide the listener with our UDP socket. */ + if (!SSL_set_fd(listener, fd)) + goto err; + + /* Begin listening. */ + if (!SSL_listen(listener)) + goto err; + +=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. +L will return an accepted connection within a fresh +SSL, in which the handshake will have already occurred. + + /* Block while waiting for a client connection */ + conn = SSL_accept_connection(listener, 0); + if (conn == NULL) { + fprintf(stderr, "error while accepting connection\n"); + goto err; + } + +With the handshake complete, the server echoes client input back to the client +in a loop. + + while (SSL_read_ex(conn, buf, sizeof(buf), &nread) > 0) { + if (SSL_write_ex(conn, buf, nread, &nwritten) > 0 && + nwritten == nread) { + continue; + } + fprintf(stderr, "Error echoing client input"); + break; + } + +Once the client closes its connection, we signal the end of the stream by using +L. This will send a final Finished packet to the +client. + + /* Signal the end of the stream. */ + if (SSL_stream_conclude(conn, 0) != 1) { + fprintf(stderr, "Unable to conclude stream\n"); + SSL_free(conn); + goto err; + } + +We then shut down the connection with L, which may need +to be called multiple times to ensure the connection is shutdown completely. + + while (SSL_shutdown_ex(conn, 0, &shutdown_args, + sizeof(SSL_SHUTDOWN_EX_ARGS)) != 1) { + fprintf(stderr, "Re-attempting SSL shutdown\n"); + } + +Finally, we free the SSL connection, and the server is now ready to accept the +next client connection. + + SSL_free(conn); + +=head2 Final clean up + +If the server somehow manages to break out of the infinite loop and +be ready to exit, it would deallocate the constructed B. + + SSL_free(listener); + +And in the main function, it would deallocate the constructed B. + + SSL_CTX_free(ctx); + BIO_closesocket(fd); + res = EXIT_SUCCESS; + return res; + +=head1 SEE ALSO + +L, L, +L, L, +L, L, +L + +=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. + +=cut