Merge dc62bf4bca into fff9e1eac8
commit
f5f8e35a01
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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 name="tcp">A simple TCP client/server pair</a>
|
||||
|
||||
|
|
@ -1596,3 +1600,90 @@ Expected behavior:
|
|||
|
||||
Please contact wolfSSL at support@wolfssl.com with any questions, bug fixes,
|
||||
or suggested feature additions.
|
||||
|
||||
|
||||
## <a name="chainverifycb">Chain Verify Callback</a>
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
#include <wolfssl/error-ssl.h>
|
||||
|
||||
#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 */
|
||||
Loading…
Reference in New Issue