Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/examples-manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,16 @@ examples:
stdin: "shutdown\n"
server_exit: clean
expect: "Server: I hear ya fa shizzle"
# server-cert.pem has SAN DNS:example.com, so wolfSSL skips the CN and the
# client's domain check would already reject it for www.wolfssl.com.
# OU=ECC (printed via KEEP_PEER_CERT, set by opensslall) also shows
# explicitly that the server picked the non-default vhost from the SNI.
- pair:
server: [./server-tls-sni]
client: [./client-tls-sni, 127.0.0.1, www.wolfssl.com]
stdin: "shutdown\n"
server_exit: clean
expect: "OU=ECC"
- pair:
server: [./server-tls-threaded]
client: [./client-tls, 127.0.0.1]
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ android/wolfssljni-ndk-sample/proguard-project.txt
/tls/server-tls-writedup
/tls/client-tls13-certauth-clienthello
/tls/server-tls13-certauth-clienthello
/tls/server-tls-sni
/tls/client-tls-sni
/tls/client-ech
/tls/client-ech-local
/tls/server-ech-local
Expand Down
93 changes: 93 additions & 0 deletions tls/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ into.
3. [Running](#run-ecc)

6. [Encrypted Client Hello](#ech)
7. [Server Name Indication](#sni)



Expand Down Expand Up @@ -1488,6 +1489,98 @@ The printed base64 config can then be passed directly to `client-ech-local`:
./client-ech-local <base64-encoded-config>
```

## <a name="sni">Server Name Indication</a>

`client-tls-sni` and `server-tls-sni` demonstrate Server Name Indication
(SNI, RFC 6066): the client names the host it wants in the ClientHello, and a
server hosting several names on one port presents the matching certificate.

`server-tls-sni` listens on port 11111 and serves two names, each from its own
`WOLFSSL_CTX`:

| Host name | Certificate |
|---|---|
| `example.com` (default) | `../certs/server-cert.pem` (RSA) |
| `www.wolfssl.com` | `../certs/server-ecc.pem` (ECC) |

Before calling `wolfSSL_accept()` the server peeks at the ClientHello with
`MSG_PEEK`, extracts the name with `wolfSSL_SNI_GetFromBuffer()` and creates
the `WOLFSSL` from the matching context. wolfSSL then checks the name against
the one registered with `wolfSSL_CTX_UseSNI()` and applies the policy set with
`wolfSSL_CTX_SNI_SetOptions()`. After the handshake the server prints
`wolfSSL_SNI_GetRequest()` and `wolfSSL_SNI_Status()`.

`client-tls-sni` sends the name with `wolfSSL_UseSNI()` and requires the
server certificate to be valid for it with `wolfSSL_check_domain_name()`.
Without that check the client would accept any certificate its CAs signed,
whatever name it asked for.

The server takes the policy as its only argument:

| Policy | Option bits | Unknown name | No SNI |
|---|---|---|---|
| `strict` | none | fatal `unrecognized_name` alert | default host |
| `continue` (default) | `WOLFSSL_SNI_CONTINUE_ON_MISMATCH` | default host, status `NO_MATCH` | default host |
| `answer` | `WOLFSSL_SNI_ANSWER_ON_MISMATCH` | default host, status `FAKE_MATCH` | default host |
| `abort` | `WOLFSSL_SNI_ABORT_ON_ABSENCE` + continue | default host, status `NO_MATCH` | fatal alert |

SNI is enabled by default on x86_64, x86, aarch64 and amd64; elsewhere build
wolfSSL with `--enable-sni`. The client also prints the server certificate's
subject when wolfSSL has `KEEP_PEER_CERT` (set by `--enable-opensslextra`).

Build:

```sh
make server-tls-sni client-tls-sni
```

Run the server in one terminal:

```sh
./server-tls-sni
Serving example.com with ../certs/server-cert.pem
Serving www.wolfssl.com with ../certs/server-ecc.pem
Waiting for a connection...
```

Run the client in a second terminal with the name to request:

```sh
./client-tls-sni 127.0.0.1 www.wolfssl.com
Connecting with SNI "www.wolfssl.com"
Connected with TLSv1.3, cipher TLS13-AES256-GCM-SHA384
Message for server: hello
Server: I hear ya fa shizzle!
```

The server reports which host it chose and how wolfSSL matched the name:

```
ClientHello requests "www.wolfssl.com", using host www.wolfssl.com
Client connected successfully
SNI request: www.wolfssl.com
SNI status: WOLFSSL_SNI_REAL_MATCH
Client: hello
```

Ask for a name the server does not host to see both sides reject it. Under the
default policy the server answers with the `example.com` certificate, and the
client refuses it:

```sh
./client-tls-sni 127.0.0.1 other.com
Connecting with SNI "other.com"
ERROR: failed to connect to wolfSSL: -322 (peer subject name mismatch)
```

Under `./server-tls-sni strict` the server rejects the name itself and the
client gets `-313 (received alert fatal error)`. Omit the host name
(`./client-tls-sni 127.0.0.1`) to connect without SNI; only the `abort`
policy rejects that.

Send `shutdown` as the message to stop the server. A rejected client does not
stop it.

## TLS Example with Post-Handshake Authentication

See `client-tls-posthsauth.c` and `server-tls-posthsauth.c`. These server and client applications show how to do a handshake without the server authenticating the client. Then after the handshake is complete, the server requests authentication and the client authenticates itself to the server. This is mutual authentication with a faster handshake because the client authentication is done later. This can lead to a better user experience if there are conditions where the client need not be authenticated.
Expand Down
264 changes: 264 additions & 0 deletions tls/client-tls-sni.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
/* client-tls-sni.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL. (formerly known as CyaSSL)
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/

/* TLS v1.3 client demonstrating Server Name Indication (SNI, RFC 6066).
*
* The client tells the server which host name it wants in the ClientHello,
* so a server hosting several names on one address can present the matching
* certificate. The client then checks that the certificate it received is
* valid for that same name.
*
* APIs shown:
* wolfSSL_UseSNI() send a host name for this connection
* (wolfSSL_CTX_UseSNI() does it for every
* connection made from a context)
* wolfSSL_check_domain_name() require the server certificate to match
*
* Pair with server-tls-sni, which serves two names:
* example.com RSA certificate (../certs/server-cert.pem)
* www.wolfssl.com ECC certificate (../certs/server-ecc.pem)
* Run without a host name to see how the server handles an absent SNI.
*
* Requires wolfSSL built with SNI, which is on by default.
*/

/* the usual suspects */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* socket includes */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

/* wolfSSL */
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/ssl.h>
#include <wolfssl/wolfio.h>
#include <wolfssl/wolfcrypt/error-crypt.h>

#define DEFAULT_PORT 11111

/* Both server certificates chain to different CAs, so load both */
#define CA_FILE_RSA "../certs/ca-cert.pem"
#define CA_FILE_ECC "../certs/ca-ecc-cert.pem"

#if defined(WOLFSSL_TLS13) && defined(HAVE_SNI)

int main(int argc, char** argv)
{
int ret = 0;
int sockfd = SOCKET_INVALID;
struct sockaddr_in servAddr;
char buff[256];
size_t len;
const char* host = (argc == 3) ? argv[2] : NULL;

/* declare wolfSSL objects */
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;

/* Check for proper calling convention */
if (argc < 2 || argc > 3) {
printf("usage: %s <IPv4 address> [host name]\n", argv[0]);
printf(" host name: sent as SNI and required to match the server\n"
" certificate. Omit it to connect without SNI.\n");
return 0;
}

/* Create a socket that uses an internet IPv4 address,
* Sets the socket to be stream based (TCP),
* 0 means choose the default protocol. */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "ERROR: failed to create the socket\n");
ret = -1; goto exit;
}

/* Initialize the server address struct with zeros */
memset(&servAddr, 0, sizeof(servAddr));

/* Fill in the server address */
servAddr.sin_family = AF_INET; /* using IPv4 */
servAddr.sin_port = htons(DEFAULT_PORT); /* on DEFAULT_PORT */

/* Get the server IPv4 address from the command line call */
if (inet_pton(AF_INET, argv[1], &servAddr.sin_addr) != 1) {
fprintf(stderr, "ERROR: invalid address\n");
ret = -1; goto exit;
}

/* Connect to the server */
if ((ret = connect(sockfd, (struct sockaddr*) &servAddr, sizeof(servAddr)))
== -1) {
fprintf(stderr, "ERROR: failed to connect\n");
goto exit;
}

/*---------------------------------*/
/* Start of wolfSSL initialization and configuration */
/*---------------------------------*/
#if 0
wolfSSL_Debugging_ON();
#endif

/* Initialize wolfSSL */
if ((ret = wolfSSL_Init()) != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: Failed to initialize the library\n");
goto exit;
}

/* Create and initialize WOLFSSL_CTX */
if ((ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())) == NULL) {
fprintf(stderr, "ERROR: failed to create WOLFSSL_CTX\n");
ret = -1; goto exit;
}

/* Load the CA certificates for both server certificates */
if ((ret = wolfSSL_CTX_load_verify_locations(ctx, CA_FILE_RSA, NULL))
!= WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: failed to load %s, please check the file.\n",
CA_FILE_RSA);
goto exit;
}
if ((ret = wolfSSL_CTX_load_verify_locations(ctx, CA_FILE_ECC, NULL))
!= WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: failed to load %s, please check the file.\n",
CA_FILE_ECC);
goto exit;
}

/* Create a WOLFSSL object */
if ((ssl = wolfSSL_new(ctx)) == NULL) {
fprintf(stderr, "ERROR: failed to create WOLFSSL object\n");
ret = -1; goto exit;
}

if (host != NULL) {
/* Ask for this host name in the ClientHello */
if ((ret = wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, host,
(unsigned short)strlen(host)))
!= WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: wolfSSL_UseSNI failed\n");
goto exit;
}

/* And insist that the certificate we get back is for that name.
* Without this, any certificate the CA signed would be accepted. */
if ((ret = wolfSSL_check_domain_name(ssl, host)) != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: wolfSSL_check_domain_name failed\n");
goto exit;
}
printf("Connecting with SNI \"%s\"\n", host);
}
else {
printf("Connecting without SNI\n");
}

/* Attach wolfSSL to the socket */
if ((ret = wolfSSL_set_fd(ssl, sockfd)) != WOLFSSL_SUCCESS) {
fprintf(stderr, "ERROR: Failed to set the file descriptor\n");
goto exit;
}

/* Connect to wolfSSL on the server side */
if ((ret = wolfSSL_connect(ssl)) != WOLFSSL_SUCCESS) {
int err = wolfSSL_get_error(ssl, ret);
fprintf(stderr, "ERROR: failed to connect to wolfSSL: %d (%s)\n",
err, wolfSSL_ERR_reason_error_string((unsigned long)err));
goto exit;
}

printf("Connected with %s, cipher %s\n", wolfSSL_get_version(ssl),
wolfSSL_get_cipher_name(ssl));

#ifdef KEEP_PEER_CERT
/* Show which certificate the server chose for our SNI */
{
WOLFSSL_X509* peer = wolfSSL_get_peer_certificate(ssl);
if (peer != NULL) {
char subject[256];
if (wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(peer),
subject, sizeof(subject)) != NULL) {
printf("Server certificate subject: %s\n", subject);
}
wolfSSL_X509_free(peer);
}
}
#endif

/* Get a message for the server from stdin */
printf("Message for server: ");
memset(buff, 0, sizeof(buff));
if (fgets(buff, sizeof(buff), stdin) == NULL) {
fprintf(stderr, "ERROR: failed to get message for server\n");
ret = -1; goto exit;
}
len = strnlen(buff, sizeof(buff));

/* Send the message to the server */
if ((ret = wolfSSL_write(ssl, buff, (int)len)) != (int)len) {
fprintf(stderr, "ERROR: failed to write entire message\n");
fprintf(stderr, "%d bytes of %d bytes were sent", ret, (int) len);
goto exit;
}

/* Read the server data into our buff array */
memset(buff, 0, sizeof(buff));
if ((ret = wolfSSL_read(ssl, buff, sizeof(buff)-1)) < 0) {
fprintf(stderr, "ERROR: failed to read\n");
goto exit;
}

/* Print to stdout any data the server sends */
printf("Server: %s\n", buff);

/* Return reporting a success */
ret = 0;

exit:
/* Cleanup and return */
if (sockfd != SOCKET_INVALID)
close(sockfd); /* Close the connection to the server */
if (ssl)
wolfSSL_free(ssl); /* Free the wolfSSL object */
if (ctx)
wolfSSL_CTX_free(ctx); /* Free the wolfSSL context object */
wolfSSL_Cleanup(); /* Cleanup the wolfSSL environment */

return ret;
}

#else

int main(int argc, char** argv)
{
(void)argc;
(void)argv;
printf("Example requires TLS v1.3 and SNI (build wolfSSL with "
"--enable-sni)\n");
return 0;
}

#endif
Loading
Loading