Skip to content
Open
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
83 changes: 47 additions & 36 deletions doc/modules/ROOT/pages/3.tutorials/3d.tls-context.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ include::example$snippets/3d_tls_context.cpp[tag=assume]
== Introduction

The cpp:tls_context[] class is a portable abstraction for TLS configuration that
works across multiple TLS backends (WolfSSL, OpenSSL, mbedTLS, etc.). It
works across the TLS backends (OpenSSL and WolfSSL). It
handles:

* Loading certificates and private keys
Expand All @@ -41,12 +41,20 @@ for establishing secure connections.

[NOTE]
====
Two implemented features depend on how WolfSSL was built.
`set_default_verify_paths()` needs `WOLFSSL_SYS_CA_CERTS` to load the system
store. `set_verify_callback()` needs `WOLFSSL_ALWAYS_VERIFY_CB`; without it,
installing a callback fails the handshake with
`std::errc::function_not_supported` rather than failing open. Both are
enabled by `--enable-opensslextra`.
On WolfSSL, some features depend on how the library was built:

* `set_default_verify_paths()` needs `WOLFSSL_SYS_CA_CERTS`
(`--enable-sys-ca-certs`, on by default) to load the system store.
* `set_verify_callback()` needs `WOLFSSL_ALWAYS_VERIFY_CB`, which
`--enable-opensslextra` defines.
* `set_alpn()` needs `HAVE_ALPN` (`--enable-alpn`).
* CRL checking needs `HAVE_CRL` (`--enable-crl`).
* Verifying an IP literal passed to `tls_stream::set_hostname()` needs
both `OPENSSL_EXTRA` and `WOLFSSL_IP_ALT_NAME`
(`--enable-opensslextra --enable-ip-alt-name`).

Except for the system store, a missing feature fails the handshake with
`std::errc::function_not_supported` rather than failing open.
====

== Construction
Expand All @@ -55,10 +63,11 @@ A cpp:tls_context[] is a shared handle to an opaque implementation. Copies share
the same underlying state, making it easy to pass contexts by value and share
them across multiple TLS streams.

IMPORTANT: Don't modify a context after creating a stream from it. The
configuration is captured when the first stream is constructed, so later
changes reach neither existing nor new streams. If you need a different
configuration, create a separate context.
IMPORTANT: Don't modify a context after creating a stream from it. Most
settings are captured at the first handshake of any stream using it;
others, such as callbacks, are read later. A change may therefore apply
partly or not at all. If you need a different configuration, create a
separate context.

[source,cpp]
----
Expand Down Expand Up @@ -111,7 +120,7 @@ into a single password-protected file:
include::example$snippets/3d_tls_context.cpp[tag=pkcs12_file,indent=0]
----

NOTE: The bundle is decoded when the first stream is created; a wrong
NOTE: The bundle is decoded at the first handshake; a wrong
passphrase or malformed bundle surfaces as a handshake failure.
Intermediate certificates in the bundle are loaded and sent during the
handshake on both backends.
Expand Down Expand Up @@ -154,16 +163,20 @@ For HTTPS clients connecting to public servers, use the system's CA store:
include::example$snippets/3d_tls_context.cpp[tag=system_trust,indent=0]
----

This uses the operating system's trusted certificates:
What "system store" means depends on the backend:

* Linux: `/etc/ssl/certs` or distribution-specific paths
* macOS: System Keychain
* Windows: Windows Certificate Store

On OpenSSL the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables are
honored. On WolfSSL this requires a build with `WOLFSSL_SYS_CA_CERTS`;
without it, supply trust anchors explicitly with `load_verify_file()` or
`add_certificate_authority()` (below).
* OpenSSL: the CA file and directory OpenSSL was built with, under its
`OPENSSLDIR` (typically `/etc/ssl` on Linux). The `SSL_CERT_FILE` and
`SSL_CERT_DIR` environment variables override them. OpenSSL does not
read the macOS Keychain or the Windows Certificate Store. On those
platforms, supply trust anchors with `load_verify_file()` unless that
OpenSSL installation ships its own CA bundle.
* WolfSSL: on Linux it reads the first readable CA directory among
`/etc/ssl/certs` and the `/etc/pki` locations. On macOS it reads the
user, admin, and system trust settings. On Windows it reads the Root and
CA stores. This requires a build with `WOLFSSL_SYS_CA_CERTS`. Without it, supply
trust anchors explicitly with `load_verify_file()` or
`add_certificate_authority()` (below).

=== Custom CA Bundle

Expand All @@ -174,10 +187,6 @@ For internal PKI or testing, load a custom CA bundle:
include::example$snippets/3d_tls_context.cpp[tag=ca_bundle,indent=0]
----

NOTE: With OpenSSL, `load_verify_file()` registers only the *first*
certificate from a multi-cert bundle (WolfSSL handles multi-cert files);
add the rest with repeated `add_certificate_authority()` calls.

=== CA Directory

Add a directory of CA certificates:
Expand Down Expand Up @@ -210,9 +219,6 @@ You can combine multiple trust sources:
include::example$snippets/3d_tls_context.cpp[tag=combine_trust,indent=0]
----

NOTE: The `set_default_verify_paths()` call above contributes nothing in
this release. Only the explicitly added CA is actually trusted.

== Protocol Configuration

Control which TLS versions and cipher suites are allowed for connections.
Expand Down Expand Up @@ -300,6 +306,10 @@ This enables:
present (required for virtual hosting)
* **Hostname matching**: Validates the certificate matches the expected host

Hostname matching applies only when the verify mode is `peer` or
`require_peer`. Under the default `none`, a certificate for a different
host is accepted.

=== Chain Depth

Limit how many intermediate certificates are allowed:
Expand Down Expand Up @@ -369,8 +379,9 @@ include::example$snippets/3d_tls_context.cpp[tag=crl_load,indent=0]
CRLs must be refreshed periodically as they expire.

NOTE: On WolfSSL, CRL checking requires a `HAVE_CRL` build; without it,
supplying a CRL or a non-disabled policy fails the handshake with
`std::errc::function_not_supported`.
a revocation policy other than `disabled` fails the handshake with
`std::errc::function_not_supported`. A CRL supplied while the policy
stays `disabled` is ignored.

=== Bootstrap vs. Hardened Connections

Expand Down Expand Up @@ -483,12 +494,12 @@ to detect errors:
include::example$snippets/3d_tls_context.cpp[tag=error_handling,indent=0]
----

Common errors include:

* File not found or permission denied
* Invalid certificate or key format
* Key doesn't match certificate
* Wrong passphrase for encrypted key or PKCS#12
The setters only record configuration, so few of them can fail. Those
that read a file report one that cannot be opened, and `set_alpn()`
rejects a malformed protocol name. Content is decoded when the native
context is first built. An invalid certificate or key format, a key that
doesn't match its certificate, or a wrong passphrase therefore surfaces
as a handshake failure.

== Next Steps

Expand Down
80 changes: 48 additions & 32 deletions doc/modules/ROOT/pages/4.guide/4l.tls.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,21 @@ revocation (`add_crl()` / `add_crl_file()` with

[NOTE]
====
Two implemented features depend on how WolfSSL was built.
`set_default_verify_paths()` requires `WOLFSSL_SYS_CA_CERTS` to load the
system store; without it, supply trust anchors explicitly.
`set_verify_callback()` requires `WOLFSSL_ALWAYS_VERIFY_CB` — without it,
installing a callback fails the handshake with
On WolfSSL, some features depend on how the library was built:

* `set_default_verify_paths()` needs `WOLFSSL_SYS_CA_CERTS`
(`--enable-sys-ca-certs`, on by default) to load the system store;
without it, supply trust anchors explicitly.
* `set_verify_callback()` needs `WOLFSSL_ALWAYS_VERIFY_CB`, which
`--enable-opensslextra` defines.
* `set_alpn()` needs `HAVE_ALPN` (`--enable-alpn`).
* CRL checking needs `HAVE_CRL` (`--enable-crl`).
* Verifying an IP literal passed to `set_hostname()` needs both
`OPENSSL_EXTRA` and `WOLFSSL_IP_ALT_NAME`
(`--enable-opensslextra --enable-ip-alt-name`).

Except for the system store, a missing feature fails the handshake with
`std::errc::function_not_supported` rather than failing open (see below).
Both are enabled by `--enable-opensslextra`.
====

The typical flow:
Expand All @@ -79,8 +87,10 @@ The cpp:tls_context[] class stores TLS configuration: certificates, keys, trust
anchors, protocol settings, and verification options. Contexts are shared
handles—copies share the same underlying state.

IMPORTANT: Don't modify a context after creating streams from it. The
configuration is captured when the first stream is constructed.
IMPORTANT: Don't modify a context after creating streams from it. Most
settings are captured at the first handshake of any stream using it;
others, such as callbacks, are read later. A change may therefore apply
partly or not at all.

=== Creating a Context

Expand Down Expand Up @@ -127,7 +137,7 @@ PKCS#12 (`.pfx` or `.p12`) files bundle certificate, key, and chain together:
include::example$snippets/4l_tls.cpp[tag=pkcs12,indent=0]
----

NOTE: The bundle is decoded when the first stream is created; a malformed
NOTE: The bundle is decoded at the first handshake; a malformed
bundle or wrong passphrase surfaces as a handshake failure. Intermediate
certificates in the bundle are loaded and sent during the handshake on
both backends.
Expand All @@ -138,17 +148,22 @@ Configure which Certificate Authorities (CAs) to trust for peer verification.

==== System Trust Store

Use the operating system's default CA certificates:
Use the system's default CA certificates:

[source,cpp]
----
include::example$snippets/4l_tls.cpp[tag=system_trust,indent=0]
----

On OpenSSL the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables are
honored. The WolfSSL backend requires a build with `WOLFSSL_SYS_CA_CERTS`.
Without it this call has no effect, and a CA bundle must be supplied
explicitly via `load_verify_file()` or `add_certificate_authority()`.
What this loads depends on the backend. OpenSSL reads the CA file and
directory it was built with, or those named by the `SSL_CERT_FILE` and
`SSL_CERT_DIR` environment variables. It does not read the macOS Keychain
or the Windows Certificate Store. WolfSSL reads the platform store:
`/etc/ssl/certs` and similar on Linux, the trust settings on macOS, and
the Root and CA stores on Windows. It requires a build with
`WOLFSSL_SYS_CA_CERTS`. Where the system store is unavailable, this call
has no effect, and a CA bundle must be supplied explicitly via
`load_verify_file()` or `add_certificate_authority()`.

==== Custom CA Certificates

Expand All @@ -157,11 +172,11 @@ explicitly via `load_verify_file()` or `add_certificate_authority()`.
include::example$snippets/4l_tls.cpp[tag=custom_cas,indent=0]
----

NOTE: With OpenSSL, `load_verify_file()` registers only the *first*
certificate from a multi-cert bundle (WolfSSL handles multi-cert files);
add others with repeated `add_certificate_authority()` calls. For
`add_verify_path()`, OpenSSL requires the hashed-filename layout produced
by `openssl rehash`, while WolfSSL loads every file in the directory.
NOTE: Every valid certificate in a bundle is trusted. If one fails to
load, OpenSSL fails the handshake, while WolfSSL skips that certificate
and fails the handshake only when none load. For `add_verify_path()`, OpenSSL requires the
hashed-filename layout produced by `openssl rehash`, while WolfSSL loads
every file in the directory.

=== Protocol Configuration

Expand Down Expand Up @@ -245,10 +260,19 @@ This does two things:
to present (important for virtual hosting)
2. Verifies the server certificate matches this hostname

The hostname is checked only when the verify mode is `peer` or
`require_peer`. Under the default `none`, a certificate for a different
host is accepted.

An IP literal (IPv4 or IPv6) is verified against the certificate's
iPAddress entries instead of its DNS names. No SNI is sent, because RFC
6066 excludes literals.

NOTE: On WolfSSL, verifying an IP literal requires a build with both
`OPENSSL_EXTRA` and `WOLFSSL_IP_ALT_NAME`. Without them, setting an IP
literal fails the handshake with `std::errc::function_not_supported`
under every verify mode, including `none`.

==== Verification Depth

Limit the certificate chain depth:
Expand Down Expand Up @@ -305,8 +329,9 @@ revoked is rejected. Under `hard_fail`, an undeterminable status is also
rejected. `disabled` (the default) skips revocation entirely.

NOTE: On WolfSSL, CRL checking requires a `HAVE_CRL` build. Without it,
supplying a CRL or a non-disabled policy fails the handshake with
a revocation policy other than `disabled` fails the handshake with
`std::errc::function_not_supported` rather than skip the check silently.
A CRL supplied while the policy stays `disabled` is ignored.

== TLS Streams

Expand Down Expand Up @@ -442,18 +467,9 @@ include::example$snippets/4l_tls.cpp[tag=stream_overloads,indent=0]

Complete example connecting to an HTTPS server:

[WARNING]
====
This example uses `set_default_verify_paths()`, which does *not* currently
load any trust anchors, so it verifies nothing against a real public server.
To verify the server today, replace that call with an explicit CA bundle,
for example:

[source,cpp]
----
include::example$snippets/4l_tls.cpp[tag=ca_bundle,indent=0]
----
====
NOTE: On WolfSSL builds without `WOLFSSL_SYS_CA_CERTS`,
`set_default_verify_paths()` cannot load the system store; supply the
server's CA explicitly with `load_verify_file()` instead.

[source,cpp]
----
Expand Down
49 changes: 26 additions & 23 deletions include/boost/corosio/tls_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,17 @@ tls_context_data const& get_tls_context_data(tls_context const&) noexcept;
share the same underlying state. This allows contexts to be passed
by value and shared across multiple TLS streams.

This class abstracts the configuration phase of TLS across multiple
backend implementations, among them OpenSSL, WolfSSL, mbedTLS and
Schannel. Portable code therefore works regardless of which TLS
library is linked.
This class abstracts the configuration phase of TLS across the
OpenSSL and WolfSSL backends. Portable code therefore works
regardless of which TLS library is linked.

@par Modification After Stream Creation

Modifying a context after creating a TLS stream from it
results in undefined behavior. The context's configuration is
captured when the first stream is constructed, and subsequent
modifications are not reflected in existing or new streams
sharing the context.
results in undefined behavior. Most settings are captured at the
first handshake of any stream using it; others, such as callbacks,
are read later. A modification may therefore apply partly or not at
all.

If different configurations are needed, create separate context
objects.
Expand Down Expand Up @@ -492,15 +491,17 @@ class BOOST_COROSIO_DECL tls_context

/** Add a certificate authority for peer verification.

Adds a single CA certificate to the trust store used for verifying
peer certificates. Call this multiple times to add multiple CAs,
or use `load_verify_file()` for a bundle.
Adds one or more CA certificates to the trust store used for
verifying peer certificates. The data may contain multiple
concatenated PEM certificates.

@param ca The CA certificate data in PEM format.

@return Success. The certificate is recorded and decoded when the
native context is first built; a malformed certificate
surfaces as a handshake failure.
@return Success. The certificates are recorded and decoded when
the native context is first built. Whether a malformed
certificate fails the handshake depends on the backend. On
OpenSSL, any one does. WolfSSL skips certificates it cannot
use and fails only when none load.

@see load_verify_file
@see set_default_verify_paths
Expand All @@ -517,7 +518,8 @@ class BOOST_COROSIO_DECL tls_context

@return Success, or an error if the file could not be read. The
certificates are decoded when the native context is first
built; malformed certificates surface as a handshake failure.
built. Malformed certificates are handled as described for
`add_certificate_authority()`.

@par Example
@par !example load_verify_file
Expand Down Expand Up @@ -555,8 +557,8 @@ class BOOST_COROSIO_DECL tls_context

/** Use the system default CA certificate store.

Configures the context to use the operating system's default
trust store for peer certificate verification. This is the
Configures the context to use the backend's default trust store
for peer certificate verification. This is the
recommended approach for HTTPS clients connecting to public
servers.

Expand All @@ -571,10 +573,11 @@ class BOOST_COROSIO_DECL tls_context
must reject unverified peers should therefore also use
`set_verify_mode( tls_verify_mode::peer )`.

@note The OpenSSL backend honors the `SSL_CERT_FILE` and
`SSL_CERT_DIR` environment variables. The WolfSSL backend
requires a build with `WOLFSSL_SYS_CA_CERTS`; without it the
system store is unavailable and this call has no effect.
@note OpenSSL reads the CA file and directory it was built with,
or those named by `SSL_CERT_FILE` and `SSL_CERT_DIR`. It does
not read the macOS Keychain or the Windows Certificate Store.
WolfSSL reads the platform store and requires a build with
`WOLFSSL_SYS_CA_CERTS`; without it this call has no effect.

@par Example
@par !example set_default_verify_paths
Expand Down Expand Up @@ -836,8 +839,8 @@ class BOOST_COROSIO_DECL tls_context

@note CRLs are consulted only when a revocation policy is set via
@ref set_revocation_policy. On WolfSSL, CRL checking requires a
build with `HAVE_CRL`; without it, supplying a CRL or a
revocation policy fails the handshake with
build with `HAVE_CRL`; without it, a revocation policy
other than `disabled` fails the handshake with
`std::errc::function_not_supported`.

@see add_crl_file
Expand Down
Loading
Loading