diff --git a/.github/examples-manifest.yml b/.github/examples-manifest.yml
index 1f0950d82..119a84756 100644
--- a/.github/examples-manifest.yml
+++ b/.github/examples-manifest.yml
@@ -134,6 +134,17 @@ profiles:
--enable-earlydata --enable-keygen --enable-des3 --enable-static --enable-shared
cflags: "-DHAVE_SECRET_CALLBACK"
+ tlschainverifycb:
+ # tls plus --enable-chain-verify-cb, which no release carries yet. Kept
+ # separate because configure exits 1 on an unrecognized --enable-*, so
+ # putting it in `tls` would fail that whole dir on the stable tag.
+ flags: >-
+ --enable-tls13 --enable-ech --enable-writedup --enable-pkcallbacks
+ --enable-postauth --enable-cryptocb --enable-opensslall --enable-session-ticket
+ --enable-earlydata --enable-keygen --enable-des3 --enable-chain-verify-cb
+ --enable-static --enable-shared
+ cflags: "-DHAVE_SECRET_CALLBACK"
+
dtls:
# earlydata requires session tickets or PSK: without session-ticket,
# configure dies with "cannot enable earlydata without enabling session
@@ -959,6 +970,27 @@ examples:
# {client,server}-tls-uart (needs /dev/ttyUSB0), and the four *-perf
# benchmarks. All still build.
+ # Split out of tls above purely so the other 22 targets keep testing the
+ # released library. --enable-chain-verify-cb reached wolfSSL master in
+ # wolfSSL/wolfssl#11367 and no release contains it yet; configure exits 1 on
+ # an unrecognized --enable-*, so on a stable tag this profile cannot even be
+ # built. Drop the pin and fold this back into tls once a release has it.
+ - id: tls-chainverifycb
+ path: tls
+ profile: tlschainverifycb
+ wolfssl_ref: master
+ # Only the pair, not the whole dir: everything else here is already built
+ # and run by the tls entry against both refs.
+ build: [make, client-tls-chainverifycb, server-tls]
+ run:
+ # Sends a fixed message rather than reading stdin, so no stdin here and
+ # the server never sees "shutdown".
+ - pair:
+ server: [./server-tls]
+ client: [./client-tls-chainverifycb, 127.0.0.1]
+ server_exit: killed
+ expect: "server: I hear ya fa shizzle"
+
- id: dtls
path: dtls
profile: dtls
diff --git a/.github/scripts/manifest.py b/.github/scripts/manifest.py
index 52f0a5748..0de35c70c 100644
--- a/.github/scripts/manifest.py
+++ b/.github/scripts/manifest.py
@@ -340,7 +340,15 @@ def cmd_wolfssl_matrix(data, refs, tier, shas=None):
rebuild -- otherwise every example pays a full wolfSSL build.
"""
pinned = dict(zip(refs, shas)) if shas else {}
- profiles = sorted({e["profile"] for e in live_entries(data, tier)})
+ # Exactly the (profile, ref) pairs some entry asks for. A pinned entry must
+ # seed its own pair, and a profile only pinned entries use must NOT be built
+ # for the refs they exclude: configure exits 1 on an unrecognized
+ # --enable-*, so a profile naming a flag a ref predates fails to build.
+ wanted = {
+ (e["profile"], ref)
+ for e in live_entries(data, tier)
+ for ref in entry_refs(e, refs)
+ }
out = [
{
"profile": name,
@@ -350,28 +358,8 @@ def cmd_wolfssl_matrix(data, refs, tier, shas=None):
"cflags": data["profiles"][name].get("cflags", ""),
"overlay": data["profiles"][name].get("overlay", ""),
}
- for name in profiles
- for ref in refs
+ for name, ref in sorted(wanted)
]
- # A pinned example needs its (profile, ref) seeded too, or its job pays a
- # full wolfSSL build on every run.
- for e in live_entries(data, tier):
- ref = e.get("wolfssl_ref")
- if ref and not any(
- o["profile"] == e["profile"] and o["wolfssl_ref"] == ref for o in out
- ):
- out.append(
- {
- "profile": e["profile"],
- "wolfssl_ref": ref,
- "wolfssl_sha": pinned.get(ref, ref),
- "flags": " ".join(
- data["profiles"][e["profile"]].get("flags", "").split()
- ),
- "cflags": data["profiles"][e["profile"]].get("cflags", ""),
- "overlay": data["profiles"][e["profile"]].get("overlay", ""),
- }
- )
print(json.dumps(out))
diff --git a/.gitignore b/.gitignore
index 145f1b5f0..9f9ce5e8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -91,6 +91,7 @@ android/wolfssljni-ndk-sample/proguard-project.txt
/tls/server-tls13-earlydata
/tls/client-tls-bio
/tls/client-tls-cacb
+/tls/client-tls-chainverifycb
/tls/client-tls-callback
/tls/client-tls-cryptocb
/tls/client-tls-ecdhe
diff --git a/tls/Makefile b/tls/Makefile
index 28b8060d1..51ee8ee97 100644
--- a/tls/Makefile
+++ b/tls/Makefile
@@ -99,6 +99,7 @@ debug: all
%-threaded: CFLAGS+=-pthread
%-writedup: CFLAGS+=-pthread
memory-tls: CFLAGS+=-pthread
+client-tls-chainverifycb: CFLAGS+=-pthread
# compile tcp examples without the LIBS variable
%-tcp: LIBS=
diff --git a/tls/README.md b/tls/README.md
index 5b0513d6d..a74de7306 100644
--- a/tls/README.md
+++ b/tls/README.md
@@ -119,6 +119,7 @@ into.
3. [Running](#run-ecc)
6. [Encrypted Client Hello](#ech)
+7. [Chain Verify Callback](#chainverifycb)
@@ -182,6 +183,9 @@ For `client-tls-writedup` and `server-tls-writedup`, it is required that
wolfSSL be configured with the `--enable-writedup` flag. Remember to build
and install wolfSSL after configuring it with this flag.
+For `client-tls-chainverifycb`, it is required that wolfSSL be configured
+with the `--enable-chain-verify-cb` flag.
+
## A simple TCP client/server pair
@@ -1596,3 +1600,90 @@ Expected behavior:
Please contact wolfSSL at support@wolfssl.com with any questions, bug fixes,
or suggested feature additions.
+
+
+## Chain Verify Callback
+
+A chain verify callback replaces wolfSSL's peer certificate verification
+completely, so an external root of trust - an HSM or secure element holding
+the anchors - can make the trust decision instead.
+
+To run this example build wolfSSL with the feature:
+
+```sh
+./configure --enable-chain-verify-cb && make && sudo make install
+```
+
+`client-tls-chainverifycb` is a TLS client that loads no CA certificate at
+all. It installs a chain verify callback, which wolfSSL calls with the
+server's certificates as raw DER once it has decoded them. wolfSSL builds no
+chain, verifies no signature and checks no date or host name: the trust
+decision is entirely the application's.
+
+Here the application is a small "trust service" on its own thread, standing
+in for the HSM. The callback copies the chain, hands it to the service and
+returns `CHAIN_VERIFY_WANT_E`, which suspends the handshake. The client keeps
+re-entering `wolfSSL_connect()`; each time the callback is asked again, and
+once the service has a verdict the handshake completes or fails.
+
+What to look for:
+
+* `wolfSSL_CTX_SetChainVerifyCb()` is the only verification-related setup.
+ There is no `wolfSSL_CTX_load_verify_locations()`.
+* `chain_verify_cb()` verifies nothing itself. On the first call it copies the
+ DER buffers, which are only valid for the duration of the call, and defers.
+* `service_verify()` is what the root of trust does with the chain: walk it
+ from the certificate nearest the anchor down to the server's own. It uses a
+ CertManager the SSL object knows nothing about; an HSM would use its own
+ store.
+* The connect loop treats `CHAIN_VERIFY_WANT_E` like `WANT_READ`: something to
+ wait for, then call `wolfSSL_connect()` again.
+
+Start a server presenting this repository's `certs/server-cert.pem`, for
+example the one in the wolfSSL source tree. `-d` stops it asking for a client
+certificate, and it serves one connection and exits:
+
+```sh
+cd wolfssl
+./examples/server/server -d -p 11111 \
+ -c ../wolfssl-examples/certs/server-cert.pem \
+ -k ../wolfssl-examples/certs/server-key.pem
+```
+
+Then, from this directory:
+
+```sh
+make client-tls-chainverifycb
+./client-tls-chainverifycb
+trust service: anchors loaded from ../certs/ca-cert.pem, running on its own thread
+client: no CA loaded, chain verify callback installed
+callback: 2 certificate(s) handed to the trust service, deferring
+callback: no verdict yet, deferring again
+callback: trust service accepted the chain
+client: handshake done, TLSv1.2
+server: I hear you fa shizzle!
+```
+
+Two switches show the other outcomes:
+
+* `-n` installs no callback. With no CA loaded the handshake fails with
+ `ASN_NO_SIGNER_E` (-188): wolfSSL's own verification has nothing to trust,
+ which is exactly why the callback exists.
+* `-x` makes the service reject every chain. The handshake fails with
+ `CHAIN_VERIFY_CB_E` (-524) and the server receives a single
+ `bad_certificate` alert; the reason the service gave is never sent to it.
+
+`-a anchor.pem` points the service at a different trust anchor, and the host
+and port default to `127.0.0.1 11111`.
+
+wolfSSL still decodes every certificate before the callback runs, so malformed
+DER fails the handshake without the callback seeing it, and it still enforces
+the minimum key sizes on the server's own key. Everything about trust is the
+callback's. It is consulted even under `WOLFSSL_VERIFY_NONE`.
+
+DTLS, raw public keys and verifying a stapled OCSP response are not supported
+together with the callback: the setters refuse them, and a connection that
+uses one of them anyway fails with `CHAIN_VERIFY_UNSUPPORTED_E` before the
+callback is called.
+
+See `ChainVerifyCb` in `wolfssl/ssl.h` for the full contract.
diff --git a/tls/client-tls-chainverifycb.c b/tls/client-tls-chainverifycb.c
new file mode 100644
index 000000000..4e4fc7096
--- /dev/null
+++ b/tls/client-tls-chainverifycb.c
@@ -0,0 +1,508 @@
+/* client-tls-chainverifycb.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
+ */
+
+/* A TLS client whose root of trust lives outside wolfSSL.
+ *
+ * No CA is loaded into the WOLFSSL_CTX. A chain verify callback hands the
+ * server's certificates, as raw DER, to a "trust service" that runs on its
+ * own thread and owns the trust anchors. It stands in for an HSM or secure
+ * element that holds the root of trust and does the chain verification. The
+ * callback defers with CHAIN_VERIFY_WANT_E while the service works, and the
+ * handshake resumes when wolfSSL_connect() is called again with a verdict
+ * available.
+ *
+ * Requires wolfSSL built with --enable-chain-verify-cb.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 11111
+#define DEFAULT_ANCHOR "../certs/ca-cert.pem"
+#define MAX_CHAIN 8
+#define SERVICE_DELAY_MS 300 /* pretend the anchors are slow to reach */
+#define POLL_MS 100 /* how often the handshake is re-entered */
+#define MAXDATASIZE 4096
+
+#ifdef WOLFSSL_CHAIN_VERIFY_CB
+
+/* The root of trust. The SSL object never sees these anchors; the service
+ * verifies chains against them on its own thread. One job at a time is
+ * enough for one connection. */
+typedef struct TrustService {
+ pthread_t thread;
+ pthread_mutex_t lock;
+ pthread_cond_t cond;
+ unsigned char* anchor; /* PEM trust anchor(s) */
+ long anchorSz;
+ int rejectAll; /* demo switch: refuse every chain */
+ int stop;
+
+ enum { JOB_NONE, JOB_PENDING, JOB_DONE } state;
+ unsigned char* der[MAX_CHAIN];
+ unsigned int derSz[MAX_CHAIN];
+ int count;
+ int verdict; /* 0 accepted, otherwise a wolfSSL error */
+} TrustService;
+
+static void job_clear(TrustService* svc)
+{
+ int i;
+ for (i = 0; i < svc->count; i++) {
+ free(svc->der[i]);
+ svc->der[i] = NULL;
+ }
+ svc->count = 0;
+ svc->state = JOB_NONE;
+}
+
+/* Verify the chain the way an HSM holding the anchors would: walk from the
+ * certificate nearest the anchors down to the server's own, each one checked
+ * against what is trusted so far and then trusted for the next step. */
+static int service_verify(const TrustService* svc)
+{
+ WOLFSSL_CERT_MANAGER* cm;
+ int i;
+ int ret;
+
+ cm = wolfSSL_CertManagerNew();
+ if (cm == NULL)
+ return MEMORY_E;
+
+ ret = wolfSSL_CertManagerLoadCABuffer(cm, svc->anchor, svc->anchorSz,
+ WOLFSSL_FILETYPE_PEM);
+ for (i = svc->count - 1; (ret == WOLFSSL_SUCCESS) && (i >= 1); i--) {
+ ret = wolfSSL_CertManagerVerifyBuffer(cm, svc->der[i], svc->derSz[i],
+ WOLFSSL_FILETYPE_ASN1);
+ if (ret == WOLFSSL_SUCCESS) {
+ ret = wolfSSL_CertManagerLoadCABuffer(cm, svc->der[i],
+ svc->derSz[i], WOLFSSL_FILETYPE_ASN1);
+ }
+ }
+ if (ret == WOLFSSL_SUCCESS) {
+ ret = wolfSSL_CertManagerVerifyBuffer(cm, svc->der[0], svc->derSz[0],
+ WOLFSSL_FILETYPE_ASN1);
+ }
+ wolfSSL_CertManagerFree(cm);
+
+ return (ret == WOLFSSL_SUCCESS) ? 0 : ret;
+}
+
+static void* service_thread(void* arg)
+{
+ TrustService* svc = (TrustService*)arg;
+ int verdict;
+
+ pthread_mutex_lock(&svc->lock);
+ while (!svc->stop) {
+ if (svc->state != JOB_PENDING) {
+ pthread_cond_wait(&svc->cond, &svc->lock);
+ continue;
+ }
+ pthread_mutex_unlock(&svc->lock);
+
+ usleep(SERVICE_DELAY_MS * 1000);
+ verdict = svc->rejectAll ? ASN_NO_SIGNER_E : service_verify(svc);
+
+ pthread_mutex_lock(&svc->lock);
+ svc->verdict = verdict;
+ svc->state = JOB_DONE;
+ pthread_cond_broadcast(&svc->cond);
+ }
+ pthread_mutex_unlock(&svc->lock);
+ return NULL;
+}
+
+static int service_start(TrustService* svc, const char* anchorFile,
+ int rejectAll)
+{
+ FILE* f;
+ long sz;
+
+ memset(svc, 0, sizeof(*svc));
+ svc->rejectAll = rejectAll;
+
+ f = fopen(anchorFile, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "trust service: cannot open %s: %s\n", anchorFile,
+ strerror(errno));
+ return -1;
+ }
+ if ((fseek(f, 0, SEEK_END) != 0) || ((sz = ftell(f)) <= 0) ||
+ (fseek(f, 0, SEEK_SET) != 0)) {
+ fprintf(stderr, "trust service: cannot size %s\n", anchorFile);
+ fclose(f);
+ return -1;
+ }
+ svc->anchor = (unsigned char*)malloc((size_t)sz);
+ if ((svc->anchor == NULL) ||
+ (fread(svc->anchor, 1, (size_t)sz, f) != (size_t)sz)) {
+ fprintf(stderr, "trust service: cannot read %s\n", anchorFile);
+ free(svc->anchor);
+ svc->anchor = NULL;
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+ svc->anchorSz = sz;
+
+ pthread_mutex_init(&svc->lock, NULL);
+ pthread_cond_init(&svc->cond, NULL);
+ if (pthread_create(&svc->thread, NULL, service_thread, svc) != 0) {
+ fprintf(stderr, "trust service: cannot start thread\n");
+ pthread_cond_destroy(&svc->cond);
+ pthread_mutex_destroy(&svc->lock);
+ free(svc->anchor);
+ svc->anchor = NULL;
+ return -1;
+ }
+ printf("trust service: anchors loaded from %s, running on its own thread\n",
+ anchorFile);
+ return 0;
+}
+
+static void service_stop(TrustService* svc)
+{
+ pthread_mutex_lock(&svc->lock);
+ svc->stop = 1;
+ pthread_cond_broadcast(&svc->cond);
+ pthread_mutex_unlock(&svc->lock);
+ pthread_join(svc->thread, NULL);
+ job_clear(svc);
+ free(svc->anchor);
+ pthread_cond_destroy(&svc->cond);
+ pthread_mutex_destroy(&svc->lock);
+}
+
+/* Give the service a little time to answer. A real application would go
+ * back to its event loop instead and re-enter the handshake when told to. */
+static void service_poll(TrustService* svc)
+{
+ struct timespec until;
+
+ clock_gettime(CLOCK_REALTIME, &until);
+ until.tv_nsec += POLL_MS * 1000000L;
+ if (until.tv_nsec >= 1000000000L) {
+ until.tv_sec++;
+ until.tv_nsec -= 1000000000L;
+ }
+ pthread_mutex_lock(&svc->lock);
+ if (svc->state == JOB_PENDING)
+ pthread_cond_timedwait(&svc->cond, &svc->lock, &until);
+ pthread_mutex_unlock(&svc->lock);
+}
+
+/* The chain verify callback. wolfSSL has already decoded the certificates;
+ * it has built no chain and verified nothing. certs[0] is the server's own
+ * certificate, the rest is the chain it sent. The buffers are only valid for
+ * the duration of the call, so they are copied before the service gets them.
+ */
+static int chain_verify_cb(WOLFSSL* ssl, const WOLFSSL_BUFFER_INFO* certs,
+ int certsSz, void* ctx)
+{
+ TrustService* svc = (TrustService*)ctx;
+ int ret = 0;
+ int i;
+
+ (void)ssl;
+
+ pthread_mutex_lock(&svc->lock);
+ switch (svc->state) {
+ case JOB_NONE:
+ if (certsSz > MAX_CHAIN) {
+ ret = MAX_CHAIN_ERROR;
+ break;
+ }
+ for (i = 0; i < certsSz; i++) {
+ svc->der[i] = (unsigned char*)malloc(certs[i].length);
+ if (svc->der[i] == NULL) {
+ ret = MEMORY_E;
+ break;
+ }
+ memcpy(svc->der[i], certs[i].buffer, certs[i].length);
+ svc->derSz[i] = certs[i].length;
+ svc->count = i + 1;
+ }
+ if (ret != 0) {
+ job_clear(svc);
+ break;
+ }
+ svc->state = JOB_PENDING;
+ pthread_cond_broadcast(&svc->cond);
+ printf("callback: %d certificate(s) handed to the trust service, "
+ "deferring\n", certsSz);
+ ret = CHAIN_VERIFY_WANT_E;
+ break;
+
+ case JOB_PENDING:
+ printf("callback: no verdict yet, deferring again\n");
+ ret = CHAIN_VERIFY_WANT_E;
+ break;
+
+ case JOB_DONE:
+ ret = svc->verdict;
+ if (ret == 0) {
+ printf("callback: trust service accepted the chain\n");
+ }
+ else {
+ printf("callback: trust service rejected the chain: %s\n",
+ wolfSSL_ERR_reason_error_string((unsigned long)ret));
+ }
+ job_clear(svc);
+ break;
+ }
+ pthread_mutex_unlock(&svc->lock);
+
+ return ret;
+}
+
+static int wait_socket(int sock, int forRead)
+{
+ fd_set fds;
+ struct timeval tv;
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ return select(sock + 1, forRead ? &fds : NULL, forRead ? NULL : &fds,
+ NULL, &tv);
+}
+
+static int tcp_connect(const char* host, int port)
+{
+ struct sockaddr_in addr;
+ int sock;
+ int flags;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ fprintf(stderr, "socket: %s\n", strerror(errno));
+ return -1;
+ }
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons((unsigned short)port);
+ if (inet_pton(AF_INET, host, &addr.sin_addr) != 1) {
+ fprintf(stderr, "invalid address %s\n", host);
+ close(sock);
+ return -1;
+ }
+ if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
+ fprintf(stderr, "connect to %s:%d: %s\n", host, port, strerror(errno));
+ close(sock);
+ return -1;
+ }
+ /* Non-blocking from here on, so the handshake returns to us whenever it
+ * waits for the network or for the trust service. */
+ flags = fcntl(sock, F_GETFL, 0);
+ if ((flags == -1) || (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1)) {
+ fprintf(stderr, "cannot set non-blocking: %s\n", strerror(errno));
+ close(sock);
+ return -1;
+ }
+ return sock;
+}
+
+static void usage(void)
+{
+ printf("client-tls-chainverifycb [-n] [-x] [-a anchor.pem] [host] [port]\n"
+ " -n install no callback: with no CA loaded the handshake must "
+ "fail\n"
+ " -x make the trust service reject every chain\n"
+ " -a trust anchor file for the service (default %s)\n",
+ DEFAULT_ANCHOR);
+}
+
+int main(int argc, char** argv)
+{
+ TrustService svc;
+ WOLFSSL_CTX* ctx = NULL;
+ WOLFSSL* ssl = NULL;
+ const char* host = DEFAULT_HOST;
+ const char* anchorFile = DEFAULT_ANCHOR;
+ int port = DEFAULT_PORT;
+ int noCallback = 0;
+ int rejectAll = 0;
+ int hostSet = 0;
+ int sock = -1;
+ int ret = 0;
+ int err = 0;
+ int exitCode = EXIT_FAILURE;
+ int i;
+ char buf[MAXDATASIZE];
+ const char msg[] = "Hello from client-tls-chainverifycb";
+
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-n") == 0)
+ noCallback = 1;
+ else if (strcmp(argv[i], "-x") == 0)
+ rejectAll = 1;
+ else if ((strcmp(argv[i], "-a") == 0) && (i + 1 < argc))
+ anchorFile = argv[++i];
+ else if (strcmp(argv[i], "-h") == 0) {
+ usage();
+ return EXIT_SUCCESS;
+ }
+ else if (argv[i][0] == '-') {
+ usage();
+ return EXIT_FAILURE;
+ }
+ else if (!hostSet) {
+ host = argv[i];
+ hostSet = 1;
+ }
+ else
+ port = atoi(argv[i]);
+ }
+
+ /* A server that gives up on the handshake may close the socket while we
+ * are still writing; report that as an error instead of dying. */
+ signal(SIGPIPE, SIG_IGN);
+
+ if (service_start(&svc, anchorFile, rejectAll) != 0)
+ return EXIT_FAILURE;
+
+ wolfSSL_Init();
+
+ ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
+ if (ctx == NULL) {
+ fprintf(stderr, "wolfSSL_CTX_new failed\n");
+ goto cleanup;
+ }
+
+ /* Deliberately no wolfSSL_CTX_load_verify_locations(): the SSL object
+ * holds no trust anchors at all. */
+ if (!noCallback) {
+ ret = wolfSSL_CTX_SetChainVerifyCb(ctx, chain_verify_cb);
+ if (ret != WOLFSSL_SUCCESS) {
+ fprintf(stderr, "wolfSSL_CTX_SetChainVerifyCb failed: %s\n",
+ wolfSSL_ERR_reason_error_string((unsigned long)ret));
+ goto cleanup;
+ }
+ printf("client: no CA loaded, chain verify callback installed\n");
+ }
+ else {
+ printf("client: no CA loaded, no callback: expecting the handshake "
+ "to fail\n");
+ }
+
+ sock = tcp_connect(host, port);
+ if (sock < 0)
+ goto cleanup;
+
+ ssl = wolfSSL_new(ctx);
+ if (ssl == NULL) {
+ fprintf(stderr, "wolfSSL_new failed\n");
+ goto cleanup;
+ }
+ wolfSSL_set_fd(ssl, sock);
+ wolfSSL_SetChainVerifyCtx(ssl, &svc);
+
+ /* Drive the handshake. Three things make wolfSSL_connect() return early:
+ * waiting for the socket, and the callback waiting for the service. */
+ for (;;) {
+ ret = wolfSSL_connect(ssl);
+ if (ret == WOLFSSL_SUCCESS)
+ break;
+ err = wolfSSL_get_error(ssl, ret);
+ if (err == WOLFSSL_ERROR_WANT_READ)
+ wait_socket(sock, 1);
+ else if (err == WOLFSSL_ERROR_WANT_WRITE)
+ wait_socket(sock, 0);
+ else if (err == CHAIN_VERIFY_WANT_E)
+ service_poll(&svc);
+ else
+ break;
+ }
+ if (ret != WOLFSSL_SUCCESS) {
+ printf("client: handshake failed: %d (%s)\n", err,
+ wolfSSL_ERR_reason_error_string((unsigned long)err));
+ if (noCallback && (err == ASN_NO_SIGNER_E))
+ exitCode = EXIT_SUCCESS; /* the expected outcome of -n */
+ if (rejectAll && (err == CHAIN_VERIFY_CB_E))
+ exitCode = EXIT_SUCCESS; /* the expected outcome of -x */
+ goto cleanup;
+ }
+ printf("client: handshake done, %s\n", wolfSSL_get_version(ssl));
+
+ do {
+ ret = wolfSSL_write(ssl, msg, (int)strlen(msg));
+ err = wolfSSL_get_error(ssl, ret);
+ } while ((ret <= 0) && (err == WOLFSSL_ERROR_WANT_WRITE));
+ if (ret <= 0) {
+ fprintf(stderr, "wolfSSL_write failed: %d\n", err);
+ goto cleanup;
+ }
+
+ do {
+ ret = wolfSSL_read(ssl, buf, sizeof(buf) - 1);
+ err = wolfSSL_get_error(ssl, ret);
+ if ((ret <= 0) && (err == WOLFSSL_ERROR_WANT_READ))
+ wait_socket(sock, 1);
+ } while ((ret <= 0) && (err == WOLFSSL_ERROR_WANT_READ));
+ if (ret <= 0) {
+ fprintf(stderr, "wolfSSL_read failed: %d\n", err);
+ goto cleanup;
+ }
+ buf[ret] = '\0';
+ printf("server: %s\n", buf);
+ exitCode = EXIT_SUCCESS;
+
+cleanup:
+ if (ssl != NULL) {
+ wolfSSL_shutdown(ssl);
+ wolfSSL_free(ssl);
+ }
+ if (sock >= 0)
+ close(sock);
+ if (ctx != NULL)
+ wolfSSL_CTX_free(ctx);
+ service_stop(&svc);
+ wolfSSL_Cleanup();
+ return exitCode;
+}
+
+#else
+
+int main(void)
+{
+ fprintf(stderr, "wolfSSL was built without --enable-chain-verify-cb\n");
+ return EXIT_FAILURE;
+}
+
+#endif /* WOLFSSL_CHAIN_VERIFY_CB */