From b0ceceb0c822dac2e03e5c2d629857198fb1ef59 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 7 Apr 2026 14:11:36 +0200 Subject: [PATCH 1/4] Add OCSP responder examples Three examples demonstrating the new OCSP Responder API: 1. ocsp-request-response.c - Pure API usage: encode DER OCSP requests from certificates, generate signed responses, and verify them in memory without networking. 2. ocsp-responder-http.c - Minimal HTTP server that accepts POST requests with DER OCSP payloads and returns signed responses. 3. nginx-scgi/ - Production-style deployment using nginx as HTTP frontend with wolfclu running as an SCGI backend for OCSP processing. --- .gitignore | 2 + ocsp/responder/Makefile | 24 ++ ocsp/responder/README.md | 109 ++++++ ocsp/responder/nginx-scgi/nginx-ocsp.conf | 47 +++ ocsp/responder/nginx-scgi/run.sh | 183 ++++++++++ ocsp/responder/ocsp-load-certs.h | 86 +++++ ocsp/responder/ocsp-request-response.c | 419 ++++++++++++++++++++++ ocsp/responder/ocsp-responder-http.c | 332 +++++++++++++++++ 8 files changed, 1202 insertions(+) create mode 100644 ocsp/responder/Makefile create mode 100644 ocsp/responder/README.md create mode 100644 ocsp/responder/nginx-scgi/nginx-ocsp.conf create mode 100755 ocsp/responder/nginx-scgi/run.sh create mode 100644 ocsp/responder/ocsp-load-certs.h create mode 100644 ocsp/responder/ocsp-request-response.c create mode 100644 ocsp/responder/ocsp-responder-http.c diff --git a/.gitignore b/.gitignore index 36c5375d..fe6bd376 100644 --- a/.gitignore +++ b/.gitignore @@ -313,6 +313,8 @@ hash/sha512-hash ocsp/ocsp_nonblock/ocsp_nonblock ocsp/stapling/ocsp-client ocsp/stapling/ocsp-server +ocsp/responder/ocsp-request-response +ocsp/responder/ocsp-responder-http sslkeylog.log diff --git a/ocsp/responder/Makefile b/ocsp/responder/Makefile new file mode 100644 index 00000000..a66301e9 --- /dev/null +++ b/ocsp/responder/Makefile @@ -0,0 +1,24 @@ +# Makefile for OCSP Responder Examples +# +# wolfSSL must be built with: +# ./configure --enable-ocsp --enable-ocsp-responder +# make && sudo make install + +CC = gcc +WOLFSSL_INSTALL_DIR = /usr/local +CFLAGS = -Wall -g -I$(WOLFSSL_INSTALL_DIR)/include +LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl + +# Uncomment for static linking: +# LIBS = $(WOLFSSL_INSTALL_DIR)/lib/libwolfssl.a -lm -lpthread + +SRC = $(wildcard *.c) +TARGETS = $(patsubst %.c, %, $(SRC)) + +all: $(TARGETS) + +%: %.c + $(CC) -o $@ $< $(CFLAGS) $(LIBS) + +clean: + rm -f $(TARGETS) diff --git a/ocsp/responder/README.md b/ocsp/responder/README.md new file mode 100644 index 00000000..e405ce4d --- /dev/null +++ b/ocsp/responder/README.md @@ -0,0 +1,109 @@ +# OCSP Responder Examples + +Examples demonstrating the wolfSSL OCSP Responder API added in +[wolfSSL/wolfssl#9761](https://github.com/wolfSSL/wolfssl/pull/9761). + +## Prerequisites + +Build and install wolfSSL with OCSP responder support: + +```sh +cd wolfssl +./configure --enable-ocsp --enable-ocsp-responder +make +sudo make install +sudo ldconfig +``` + +## Examples + +### 1. Raw DER Request/Response (`ocsp-request-response.c`) + +Demonstrates the core API without any networking: + +- Parse a certificate and build a DER-encoded OCSP request + (`wc_InitOcspRequest`, `wc_EncodeOcspRequest`) +- Create an `OcspResponder`, register a signer, and set certificate statuses + (`wc_OcspResponder_new`, `wc_OcspResponder_AddSigner`, + `wc_OcspResponder_SetCertStatus`) +- Generate a signed OCSP response from the request + (`wc_OcspResponder_WriteResponse`) +- Verify the response against a `WOLFSSL_CERT_MANAGER` + (`wc_CheckCertOcspResponse`) +- Show REVOKED status and error response generation + +```sh +make ocsp-request-response +./ocsp-request-response +``` + +Uses the wolfSSL test certs in `../../certs/` by default. + +### 2. Minimal HTTP Responder (`ocsp-responder-http.c`) + +A tiny HTTP server that accepts POST requests containing DER OCSP requests and +returns DER OCSP responses. Kept as small as possible. + +```sh +make ocsp-responder-http + +# Start the responder, marking the server cert as GOOD +./ocsp-responder-http 8080 ../../certs/ca-cert.pem ../../certs/ca-key.pem \ + ../../certs/server-cert.pem + +# Test with OpenSSL (in another terminal) +openssl ocsp -issuer ../../certs/ca-cert.pem -cert ../../certs/server-cert.pem \ + -url http://127.0.0.1:8080/ -no_nonce +``` + +Any certificate files listed after the CA key have their serial numbers +registered as CERT_GOOD. Certificates not registered will get CERT_UNKNOWN. + +### 3. nginx + wolfclu SCGI (`nginx-scgi/`) + +Production-style deployment: nginx handles HTTP and forwards raw OCSP request +bodies to wolfclu over SCGI. nginx provides TLS termination, access control, +logging, and load balancing while wolfclu focuses on OCSP processing. + +Requirements: +- [wolfCLU](https://github.com/wolfSSL/wolfCLU) built and installed +- nginx with SCGI support (enabled by default) + +``` ++---------+ HTTP POST +-------+ SCGI +---------+ +| Client |------------>| nginx |-------->| wolfclu | +|(openssl) |<------------| :8080 |<--------| :8081 | ++---------+ OCSP resp +-------+ +---------+ +``` + +Quick start: + +```sh +cd nginx-scgi +./run.sh +``` + +Or run manually: + +```sh +# Terminal 1: Start wolfclu SCGI backend +wolfssl ocsp -scgi -port 8081 \ + -rsigner ../../certs/ca-cert.pem \ + -rkey ../../certs/ca-key.pem \ + -CA ../../certs/ca-cert.pem + +# Terminal 2: Start nginx +nginx -c $(pwd)/nginx-scgi/nginx-ocsp.conf + +# Terminal 3: Test +openssl ocsp -issuer ../../certs/ca-cert.pem -cert ../../certs/server-cert.pem \ + -url http://127.0.0.1:8080/ -no_nonce +``` + +The `nginx-ocsp.conf` file can be customized for your environment. See the +comments in the file for standalone vs. installed nginx usage. + +## Shared Code + +`ocsp-load-certs.h` contains file loading utilities (`LoadFile`, `LoadCertDer`, +`LoadKeyDer`) shared between the C examples. diff --git a/ocsp/responder/nginx-scgi/nginx-ocsp.conf b/ocsp/responder/nginx-scgi/nginx-ocsp.conf new file mode 100644 index 00000000..d6a4e268 --- /dev/null +++ b/ocsp/responder/nginx-scgi/nginx-ocsp.conf @@ -0,0 +1,47 @@ +# nginx configuration for proxying OCSP requests to wolfclu via SCGI. +# +# Install: copy this to /etc/nginx/sites-enabled/ or include it from +# nginx.conf, then reload nginx. Alternatively run a standalone nginx: +# +# nginx -c $(pwd)/nginx-ocsp.conf -p $(pwd) +# +# Adjust paths/ports below as needed. + +# Run as a foreground process for testing (comment out for daemon mode). +daemon off; +# Uncomment and set if running without root: +# pid /tmp/nginx-ocsp.pid; + +events { + worker_connections 64; +} + +http { + # Where nginx writes temp files when running standalone. + client_body_temp_path /tmp/nginx-ocsp-body; + proxy_temp_path /tmp/nginx-ocsp-proxy; + fastcgi_temp_path /tmp/nginx-ocsp-fastcgi; + uwsgi_temp_path /tmp/nginx-ocsp-uwsgi; + scgi_temp_path /tmp/nginx-ocsp-scgi; + + access_log /dev/stdout; + error_log /dev/stderr info; + + server { + listen 8080; + + location / { + # Forward OCSP requests to wolfclu running in SCGI mode. + scgi_pass 127.0.0.1:8081; + + # Standard SCGI parameter: tell the backend the content length. + include scgi_params; + + # Ensure the OCSP request body is forwarded. + scgi_param CONTENT_LENGTH $content_length; + scgi_param CONTENT_TYPE $content_type; + scgi_param REQUEST_METHOD $request_method; + scgi_param REQUEST_URI $request_uri; + } + } +} diff --git a/ocsp/responder/nginx-scgi/run.sh b/ocsp/responder/nginx-scgi/run.sh new file mode 100755 index 00000000..ad42ee9c --- /dev/null +++ b/ocsp/responder/nginx-scgi/run.sh @@ -0,0 +1,183 @@ +#!/bin/sh +# +# run.sh - Start an OCSP responder using wolfclu (SCGI) behind nginx. +# +# This script: +# 1. Starts wolfclu in SCGI mode on port 8081 +# 2. Starts nginx on port 8080, forwarding to wolfclu via SCGI +# 3. Sends a test OCSP query using wolfssl's built-in test certs +# +# Prerequisites: +# - wolfssl built with: --enable-ocsp --enable-ocsp-responder +# - wolfclu built and installed +# - nginx installed with SCGI support (default in most packages) +# +# Usage: +# ./run.sh [options] +# +# Options: +# --ca-cert CA certificate (default: wolfSSL test ca-cert.pem) +# --ca-key CA private key (default: wolfSSL test ca-key.pem) +# --index OpenSSL-format index.txt (optional) +# --port nginx listen port (default: 8080) +# --scgi-port wolfclu SCGI port (default: 8081) + +set -e + +# Defaults - use wolfSSL test certificates +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +CA_CERT="${SCRIPT_DIR}/../../../certs/ca-cert.pem" +CA_KEY="${SCRIPT_DIR}/../../../certs/ca-key.pem" +INDEX_FILE="" +HTTP_PORT=8080 +SCGI_PORT=8081 +WOLFCLU_PID="" +NGINX_PID="" + +cleanup() { + echo "" + echo "Shutting down..." + [ -n "$WOLFCLU_PID" ] && kill "$WOLFCLU_PID" 2>/dev/null || true + [ -n "$NGINX_PID" ] && kill "$NGINX_PID" 2>/dev/null || true + wait 2>/dev/null || true + [ -n "$WORK_DIR" ] && rm -rf "$WORK_DIR" + echo "Done." +} +trap cleanup EXIT INT TERM + +# Parse arguments +while [ $# -gt 0 ]; do + case "$1" in + --ca-cert) CA_CERT="$2"; shift 2 ;; + --ca-key) CA_KEY="$2"; shift 2 ;; + --index) INDEX_FILE="$2"; shift 2 ;; + --port) HTTP_PORT="$2"; shift 2 ;; + --scgi-port) SCGI_PORT="$2"; shift 2 ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Validate files exist +if [ ! -f "$CA_CERT" ]; then + echo "Error: CA cert not found: $CA_CERT" + exit 1 +fi +if [ ! -f "$CA_KEY" ]; then + echo "Error: CA key not found: $CA_KEY" + exit 1 +fi + +# Check for required tools +if ! command -v wolfssl >/dev/null 2>&1; then + echo "Error: 'wolfssl' (wolfCLU) not found in PATH" + echo "Build wolfCLU from https://github.com/wolfSSL/wolfCLU" + exit 1 +fi +if ! command -v nginx >/dev/null 2>&1; then + echo "Error: nginx not found in PATH" + exit 1 +fi + +echo "=== OCSP Responder: nginx + wolfclu (SCGI) ===" +echo "" +echo "CA cert: $CA_CERT" +echo "CA key: $CA_KEY" +echo "HTTP port: $HTTP_PORT (nginx)" +echo "SCGI port: $SCGI_PORT (wolfclu)" +echo "" + +# --- Step 1: Start wolfclu OCSP responder in SCGI mode --- +echo "Starting wolfclu OCSP responder (SCGI on port $SCGI_PORT)..." + +WOLFCLU_ARGS="-scgi -port $SCGI_PORT -rsigner $CA_CERT -rkey $CA_KEY -CA $CA_CERT" +if [ -n "$INDEX_FILE" ]; then + WOLFCLU_ARGS="$WOLFCLU_ARGS -index $INDEX_FILE" +fi + +wolfssl ocsp $WOLFCLU_ARGS & +WOLFCLU_PID=$! +sleep 1 + +if ! kill -0 "$WOLFCLU_PID" 2>/dev/null; then + echo "Error: wolfclu failed to start" + exit 1 +fi +echo "wolfclu started (PID $WOLFCLU_PID)" + +# --- Step 2: Generate nginx config with correct ports --- +WORK_DIR="$(mktemp -d "$SCRIPT_DIR/tmp.XXXXXX")" +NGINX_CONF="$WORK_DIR/nginx-ocsp.conf" +cat > "$NGINX_CONF" </dev/null; then + echo "Error: nginx failed to start" + exit 1 +fi +echo "nginx started (PID $NGINX_PID)" + +echo "" +echo "=== OCSP responder is running ===" +echo "" +echo "Test with wolfssl:" +echo " wolfssl ocsp -issuer $CA_CERT -cert ../../certs/server-cert.pem \\" +echo " -url http://127.0.0.1:$HTTP_PORT/" +echo "" +echo "Test with openssl:" +echo " openssl ocsp -issuer $CA_CERT -cert ../../certs/server-cert.pem \\" +echo " -url http://127.0.0.1:$HTTP_PORT/ -resp_text" +echo "" +echo "Press Ctrl-C to stop." +echo "" + +# Wait for either process to exit +wait diff --git a/ocsp/responder/ocsp-load-certs.h b/ocsp/responder/ocsp-load-certs.h new file mode 100644 index 00000000..df5bf572 --- /dev/null +++ b/ocsp/responder/ocsp-load-certs.h @@ -0,0 +1,86 @@ +/* ocsp-load-certs.h + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * 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 3 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-1335, USA + */ + +/* Shared file/cert loading helpers for the OCSP responder examples. */ + +#ifndef OCSP_LOAD_CERTS_H +#define OCSP_LOAD_CERTS_H + +#include /* WC_MAYBE_UNUSED */ +#include /* wc_CertPemToDer, wc_KeyPemToDer */ +#include +#include + +/* Load file into malloc'd buffer. Caller must free(*buf). */ +static WC_MAYBE_UNUSED byte* LoadFile(const char* path, int* outSz) +{ + FILE* f; + long sz; + byte* buf; + + f = fopen(path, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + sz = ftell(f); + fseek(f, 0, SEEK_SET); + if (sz <= 0) { fclose(f); return NULL; } + buf = (byte*)malloc((size_t)sz); + if (!buf) { fclose(f); return NULL; } + *outSz = (int)fread(buf, 1, (size_t)sz, f); + fclose(f); + return buf; +} + +/* Load PEM certificate, convert to DER. Caller must free() result. */ +static WC_MAYBE_UNUSED byte* LoadCertDer(const char* path, int* derSz) +{ + byte *pem, *der; + int pemSz = 0, ret; + + pem = LoadFile(path, &pemSz); + if (!pem) return NULL; + der = (byte*)malloc((size_t)pemSz); + if (!der) { free(pem); return NULL; } + ret = wc_CertPemToDer(pem, pemSz, der, pemSz, CERT_TYPE); + free(pem); + if (ret <= 0) { free(der); return NULL; } + *derSz = ret; + return der; +} + +/* Load PEM private key, convert to DER. Caller must free() result. */ +static WC_MAYBE_UNUSED byte* LoadKeyDer(const char* path, int* derSz) +{ + byte *pem, *der; + int pemSz = 0, ret; + + pem = LoadFile(path, &pemSz); + if (!pem) return NULL; + der = (byte*)malloc((size_t)pemSz); + if (!der) { free(pem); return NULL; } + ret = wc_KeyPemToDer(pem, pemSz, der, pemSz, NULL); + free(pem); + if (ret <= 0) { free(der); return NULL; } + *derSz = ret; + return der; +} + +#endif /* OCSP_LOAD_CERTS_H */ diff --git a/ocsp/responder/ocsp-request-response.c b/ocsp/responder/ocsp-request-response.c new file mode 100644 index 00000000..784d754e --- /dev/null +++ b/ocsp/responder/ocsp-request-response.c @@ -0,0 +1,419 @@ +/* ocsp-request-response.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * 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 3 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-1335, USA + */ + +/* Example: Generate and verify raw DER OCSP requests and responses using the + * wolfSSL OCSP Responder API. No networking - pure encode/decode in memory. */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#include +#include +#include + +#include +#include + +#if defined(HAVE_OCSP) && defined(HAVE_OCSP_RESPONDER) && \ + !defined(NO_FILESYSTEM) + +#include "ocsp-load-certs.h" + +/* Default certificate paths - these are the wolfSSL test certs */ +#ifndef CA_CERT +#define CA_CERT "../../certs/ca-cert.pem" +#endif +#ifndef CA_KEY +#define CA_KEY "../../certs/ca-key.pem" +#endif +#ifndef SERVER_CERT +#define SERVER_CERT "../../certs/server-cert.pem" +#endif + +#define MAX_DER_SZ 4096 +#define MAX_RESP_SZ 4096 + +static void HexDump(const char* label, const unsigned char* data, int sz) +{ + int i; + printf("%s (%d bytes):\n ", label, sz); + for (i = 0; i < sz && i < 64; i++) { + printf("%02x", data[i]); + if ((i + 1) % 32 == 0) + printf("\n "); + } + if (sz > 64) + printf("..."); + printf("\n"); +} + +int main(int argc, char** argv) +{ + int ret; + + /* Certificate buffers */ + byte* caCertDer = NULL; + int caCertDerSz = 0; + byte* caKeyDer = NULL; + int caKeyDerSz = 0; + byte* serverCertDer = NULL; + int serverCertDerSz = 0; + + /* DecodedCert for the server cert (to build the OCSP request) */ + DecodedCert serverCert; + int serverCertInit = 0; + + /* DecodedCert for the CA cert (to get its subject for SetCertStatus) */ + DecodedCert caCert; + int caCertInit = 0; + char caSubject[256] = {0}; + word32 caSubjectSz = sizeof(caSubject); + + /* OCSP request */ + OcspRequest* req = NULL; + unsigned char reqDer[MAX_DER_SZ]; + int reqDerSz; + + /* OCSP responder */ + OcspResponder* responder = NULL; + + /* OCSP response */ + unsigned char respDer[MAX_RESP_SZ]; + word32 respDerSz; + + /* For verification */ + WOLFSSL_CERT_MANAGER* cm = NULL; + WOLFSSL_OCSP* ocsp = NULL; + + const char* caCertFile = CA_CERT; + const char* caKeyFile = CA_KEY; + const char* serverCertFile = SERVER_CERT; + + (void)argc; + (void)argv; + + printf("=== OCSP Request/Response Example ===\n\n"); + + ret = wolfSSL_Init(); + if (ret != WOLFSSL_SUCCESS) { + printf("wolfSSL_Init failed\n"); + return 1; + } + + /* --- Step 1: Load certificates --- */ + printf("Step 1: Loading certificates...\n"); + + caCertDer = LoadCertDer(caCertFile, &caCertDerSz); + if (!caCertDer) { + printf("Error loading CA cert: %s\n", caCertFile); + ret = -1; + goto cleanup; + } + printf(" CA cert: %d bytes DER\n", caCertDerSz); + + caKeyDer = LoadKeyDer(caKeyFile, &caKeyDerSz); + if (!caKeyDer) { + printf("Error loading CA key: %s\n", caKeyFile); + ret = -1; + goto cleanup; + } + printf(" CA key: %d bytes DER\n", caKeyDerSz); + + serverCertDer = LoadCertDer(serverCertFile, &serverCertDerSz); + if (!serverCertDer) { + printf("Error loading server cert: %s\n", serverCertFile); + ret = -1; + goto cleanup; + } + printf(" Server cert: %d bytes DER\n\n", serverCertDerSz); + + /* --- Step 2: Parse the server cert and build an OCSP request --- */ + printf("Step 2: Building OCSP request from server certificate...\n"); + + /* Parse with the CA loaded so that issuerKeyHash gets populated. + * The issuerKeyHash is derived from the CA's public key and is needed + * for the OCSP request to match the responder's signer. */ + { + WOLFSSL_CERT_MANAGER* tmpCm = wolfSSL_CertManagerNew(); + if (tmpCm == NULL) { + printf("Error creating temp CertManager\n"); + ret = -1; + goto cleanup; + } + ret = wolfSSL_CertManagerLoadCABuffer(tmpCm, caCertDer, + caCertDerSz, SSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + printf("Error loading CA into temp CertManager: %d\n", ret); + wolfSSL_CertManagerFree(tmpCm); + goto cleanup; + } + wc_InitDecodedCert(&serverCert, serverCertDer, + (word32)serverCertDerSz, NULL); + serverCertInit = 1; + ret = wc_ParseCert(&serverCert, CERT_TYPE, 1, tmpCm); + wolfSSL_CertManagerFree(tmpCm); + } + if (ret != 0) { + printf("Error parsing/verifying server cert: %d\n", ret); + goto cleanup; + } + + /* Allocate and initialize the OCSP request from the parsed cert. + * wc_InitOcspRequest fills in issuerHash, issuerKeyHash, and serial + * from the DecodedCert automatically. */ + req = wc_OcspRequest_new(NULL); + if (req == NULL) { + printf("Error allocating OCSP request\n"); + ret = -1; + goto cleanup; + } + + ret = wc_InitOcspRequest(req, &serverCert, 0, NULL); + if (ret != 0) { + printf("Error initializing OCSP request: %d\n", ret); + goto cleanup; + } + + /* Encode the OCSP request into DER format */ + reqDerSz = wc_EncodeOcspRequest(req, reqDer, sizeof(reqDer)); + if (reqDerSz <= 0) { + printf("Error encoding OCSP request: %d\n", reqDerSz); + ret = reqDerSz; + goto cleanup; + } + + HexDump(" OCSP Request DER", reqDer, reqDerSz); + printf("\n"); + + /* --- Step 3: Set up the OCSP responder --- */ + printf("Step 3: Setting up OCSP responder...\n"); + + /* Parse the CA cert to get its subject name */ + wc_InitDecodedCert(&caCert, caCertDer, (word32)caCertDerSz, NULL); + caCertInit = 1; + ret = wc_ParseCert(&caCert, CERT_TYPE, 0, NULL); + if (ret != 0) { + printf("Error parsing CA cert: %d\n", ret); + goto cleanup; + } + + ret = wc_GetDecodedCertSubject(&caCert, caSubject, &caSubjectSz); + if (ret != 0) { + printf("Error getting CA subject: %d\n", ret); + goto cleanup; + } + printf(" CA Subject: %s\n", caSubject); + + /* Create the responder. The second argument controls whether the CA cert + * is included in responses (1=yes). */ + responder = wc_OcspResponder_new(NULL, 1); + if (responder == NULL) { + printf("Error creating OCSP responder\n"); + ret = -1; + goto cleanup; + } + + /* Register the CA cert and key as a signer. When the CA signs directly + * (not using a delegated responder cert), pass NULL/0 for the last two + * parameters. */ + ret = wc_OcspResponder_AddSigner(responder, + caCertDer, (word32)caCertDerSz, + caKeyDer, (word32)caKeyDerSz, + NULL, 0); + if (ret != 0) { + printf("Error adding signer: %d\n", ret); + goto cleanup; + } + printf(" Signer added (CA as direct signer)\n"); + + /* Set the status for the server certificate's serial number. + * In production, you would populate this from a database or CRL. + * Here we mark it as CERT_GOOD with 24-hour validity. */ + { + byte serial[32]; + word32 serialSz = sizeof(serial); + + ret = wc_GetDecodedCertSerial(&serverCert, serial, &serialSz); + if (ret != 0) { + printf("Error getting server cert serial: %d\n", ret); + goto cleanup; + } + + HexDump(" Server cert serial", serial, (int)serialSz); + + ret = wc_OcspResponder_SetCertStatus(responder, + caSubject, caSubjectSz, + serial, serialSz, + CERT_GOOD, + 0, /* revocationTime (unused) */ + WC_CRL_REASON_UNSPECIFIED, + 86400 /* validityPeriod secs */); + if (ret != 0) { + printf("Error setting cert status: %d\n", ret); + goto cleanup; + } + printf(" Certificate status set to GOOD (24h validity)\n\n"); + } + + /* --- Step 4: Generate OCSP response --- */ + printf("Step 4: Generating OCSP response...\n"); + + respDerSz = sizeof(respDer); + ret = wc_OcspResponder_WriteResponse(responder, + reqDer, (word32)reqDerSz, + respDer, &respDerSz); + if (ret != 0) { + printf("Error generating OCSP response: %d\n", ret); + goto cleanup; + } + + HexDump(" OCSP Response DER", respDer, (int)respDerSz); + printf("\n"); + + /* --- Step 5: Verify the OCSP response --- */ + printf("Step 5: Verifying OCSP response...\n"); + + cm = wolfSSL_CertManagerNew(); + if (cm == NULL) { + printf("Error creating CertManager\n"); + ret = -1; + goto cleanup; + } + + ret = wolfSSL_CertManagerLoadCABuffer(cm, caCertDer, caCertDerSz, + SSL_FILETYPE_ASN1); + if (ret != WOLFSSL_SUCCESS) { + printf("Error loading CA into CertManager: %d\n", ret); + goto cleanup; + } + + ocsp = wc_NewOCSP(cm); + if (ocsp == NULL) { + printf("Error creating OCSP verifier\n"); + ret = -1; + goto cleanup; + } + + ret = wc_CheckCertOcspResponse(ocsp, &serverCert, + respDer, (int)respDerSz, NULL); + if (ret == 0) { + printf(" OCSP response verification: SUCCESS\n"); + } + else { + printf(" OCSP response verification FAILED: %d\n", ret); + } + + /* --- Step 6: Demonstrate a REVOKED status --- */ + printf("\nStep 6: Demonstrating REVOKED status...\n"); + { + byte serial[32]; + word32 serialSz = sizeof(serial); + + ret = wc_GetDecodedCertSerial(&serverCert, serial, &serialSz); + if (ret != 0) { + printf("Error getting server cert serial: %d\n", ret); + goto cleanup; + } + + /* Update the status to REVOKED */ + ret = wc_OcspResponder_SetCertStatus(responder, + caSubject, caSubjectSz, + serial, serialSz, + CERT_REVOKED, + time(NULL), /* revocationTime */ + WC_CRL_REASON_KEY_COMPROMISE, + 0); + if (ret != 0) { + printf("Error setting revoked status: %d\n", ret); + goto cleanup; + } + + respDerSz = sizeof(respDer); + ret = wc_OcspResponder_WriteResponse(responder, + reqDer, (word32)reqDerSz, + respDer, &respDerSz); + if (ret != 0) { + printf("Error generating revoked response: %d\n", ret); + goto cleanup; + } + + HexDump(" REVOKED OCSP Response DER", respDer, (int)respDerSz); + + /* Verify - should indicate revoked */ + ret = wc_CheckCertOcspResponse(ocsp, &serverCert, + respDer, (int)respDerSz, NULL); + printf(" Verification result: %d (non-zero = revoked/failed)\n", ret); + } + + /* --- Step 7: Demonstrate error response --- */ + printf("\nStep 7: Generating OCSP error response (unauthorized)...\n"); + { + respDerSz = sizeof(respDer); + ret = wc_OcspResponder_WriteErrorResponse(OCSP_UNAUTHORIZED, + respDer, &respDerSz); + if (ret != 0) { + printf("Error encoding error response: %d\n", ret); + goto cleanup; + } + HexDump(" Error Response DER", respDer, (int)respDerSz); + } + + printf("\n=== Example complete ===\n"); + ret = 0; + +cleanup: + if (ocsp) + wc_FreeOCSP(ocsp); + if (cm) + wolfSSL_CertManagerFree(cm); + if (responder) + wc_OcspResponder_free(responder); + if (req) + wc_OcspRequest_free(req); + if (serverCertInit) + wc_FreeDecodedCert(&serverCert); + if (caCertInit) + wc_FreeDecodedCert(&caCert); + free(caCertDer); + free(caKeyDer); + free(serverCertDer); + + wolfSSL_Cleanup(); + return ret; +} + +#else + +int main(int argc, char** argv) +{ + (void)argc; + (void)argv; + printf("This example requires --enable-ocsp --enable-ocsp-responder\n"); + return 0; +} + +#endif /* HAVE_OCSP && HAVE_OCSP_RESPONDER && !NO_FILESYSTEM */ diff --git a/ocsp/responder/ocsp-responder-http.c b/ocsp/responder/ocsp-responder-http.c new file mode 100644 index 00000000..d48828f3 --- /dev/null +++ b/ocsp/responder/ocsp-responder-http.c @@ -0,0 +1,332 @@ +/* ocsp-responder-http.c + * + * Copyright (C) 2006-2026 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * 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 3 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-1335, USA + */ + +/* Minimal HTTP OCSP responder. Accepts POST requests with DER-encoded OCSP + * requests and returns DER-encoded OCSP responses. Intentionally kept as + * small as possible. Not production-hardened. + * + * Usage: + * ./ocsp-responder-http [cert-to-mark-good ...] + * + * Any certificates listed after the CA key will have their serial numbers + * registered as CERT_GOOD. Serials not registered get CERT_UNKNOWN. + */ + +#ifdef HAVE_CONFIG_H + #include +#endif + +#ifndef WOLFSSL_USER_SETTINGS + #include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(HAVE_OCSP) && defined(HAVE_OCSP_RESPONDER) && \ + !defined(NO_FILESYSTEM) + +#include "ocsp-load-certs.h" + +#include +#include +#include +#include + +#define BUF_SZ 65536 + +static volatile int running = 1; + +static void sigHandler(int sig) +{ + (void)sig; + running = 0; +} + +/* Register a certificate's serial as CERT_GOOD in the responder by loading + * and parsing it to extract the serial number. */ +static int AddGoodCert(OcspResponder* resp, const char* caSubject, + word32 caSubjectSz, const char* certFile) +{ + byte* certDer; + int certDerSz = 0; + DecodedCert dc; + byte serial[32]; + word32 serialSz = sizeof(serial); + int ret; + + certDer = LoadCertDer(certFile, &certDerSz); + if (!certDer) + return -1; + + wc_InitDecodedCert(&dc, certDer, (word32)certDerSz, NULL); + ret = wc_ParseCert(&dc, CERT_TYPE, 0, NULL); + if (ret != 0) { + wc_FreeDecodedCert(&dc); + free(certDer); + return ret; + } + + ret = wc_GetDecodedCertSerial(&dc, serial, &serialSz); + wc_FreeDecodedCert(&dc); + free(certDer); + if (ret != 0) + return ret; + + return wc_OcspResponder_SetCertStatus(resp, caSubject, caSubjectSz, + serial, serialSz, CERT_GOOD, 0, WC_CRL_REASON_UNSPECIFIED, + 86400); +} + +/* Receive full HTTP request. */ +static int RecvHttp(int fd, byte* buf, int bufSz) +{ + int total = 0, contentLen = 0, headerEnd = 0; + + while (total < bufSz - 1) { + int n = (int)recv(fd, buf + total, (size_t)(bufSz - 1 - total), 0); + if (n <= 0) break; + total += n; + buf[total] = '\0'; + + if (!headerEnd) { + char* hdrEnd = strstr((char*)buf, "\r\n\r\n"); + if (hdrEnd) { + char* cl; + headerEnd = (int)(hdrEnd - (char*)buf) + 4; + cl = strstr((char*)buf, "Content-Length:"); + if (!cl) cl = strstr((char*)buf, "content-length:"); + if (cl) contentLen = atoi(cl + 15); + } + } + if (headerEnd && total >= headerEnd + contentLen) + break; + } + return total; +} + +/* Extract POST body from HTTP request */ +static int ParsePost(const byte* http, int httpSz, + const byte** body, int* bodySz) +{ + const char* hdr = (const char*)http; + const char* end; + const char* cl; + int offset; + + *body = NULL; + *bodySz = 0; + + if (strncmp(hdr, "POST ", 5) != 0) + return -1; + + end = strstr(hdr, "\r\n\r\n"); + if (!end) return -1; + offset = (int)(end - hdr) + 4; + + cl = strstr(hdr, "Content-Length:"); + if (!cl) cl = strstr(hdr, "content-length:"); + if (cl) + *bodySz = atoi(cl + 15); + else + *bodySz = httpSz - offset; + + if (offset + *bodySz > httpSz) + return -1; + + *body = http + offset; + return 0; +} + +static void SendOcspResp(int fd, const byte* resp, int respSz) +{ + char hdr[256]; + int hdrLen; + + hdrLen = snprintf(hdr, sizeof(hdr), + "HTTP/1.0 200 OK\r\n" + "Content-Type: application/ocsp-response\r\n" + "Content-Length: %d\r\n" + "\r\n", respSz); + + send(fd, hdr, (size_t)hdrLen, 0); + send(fd, resp, (size_t)respSz, 0); +} + +static void SendHttpError(int fd, int code, const char* msg) +{ + char buf[256]; + int len = snprintf(buf, sizeof(buf), + "HTTP/1.0 %d %s\r\nContent-Length: 0\r\n\r\n", code, msg); + send(fd, buf, (size_t)len, 0); +} + +int main(int argc, char** argv) +{ + int port; + const char* certFile; + const char* keyFile; + byte *caCertDer = NULL, *caKeyDer = NULL; + int caCertDerSz = 0, caKeyDerSz = 0; + OcspResponder* responder = NULL; + DecodedCert caCert; + int caCertInit = 0; + char caSubject[256]; + word32 caSubjectSz = sizeof(caSubject); + int sockfd, clientfd, opt = 1, i; + struct sockaddr_in addr; + + if (argc < 4) { + printf("Usage: %s [good-cert ...]\n\n" + " port Listen port\n" + " ca-cert CA certificate (PEM)\n" + " ca-key CA private key (PEM)\n" + " good-cert Certificate(s) to mark as GOOD\n", + argv[0]); + return 1; + } + + port = atoi(argv[1]); + certFile = argv[2]; + keyFile = argv[3]; + + wolfSSL_Init(); + { + struct sigaction sa; + sa.sa_handler = sigHandler; + sa.sa_flags = 0; /* No SA_RESTART so accept() returns on signal */ + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + } + + caCertDer = LoadCertDer(certFile, &caCertDerSz); + caKeyDer = LoadKeyDer(keyFile, &caKeyDerSz); + if (!caCertDer || !caKeyDer) { + fprintf(stderr, "Error loading cert/key\n"); + goto cleanup; + } + + wc_InitDecodedCert(&caCert, caCertDer, (word32)caCertDerSz, NULL); + caCertInit = 1; + if (wc_ParseCert(&caCert, CERT_TYPE, 0, NULL) != 0) { + fprintf(stderr, "Error parsing CA cert\n"); + goto cleanup; + } + + if (wc_GetDecodedCertSubject(&caCert, caSubject, &caSubjectSz) != 0) { + fprintf(stderr, "Error getting CA subject\n"); + goto cleanup; + } + + responder = wc_OcspResponder_new(NULL, 1); + if (!responder) { + fprintf(stderr, "Error creating responder\n"); + goto cleanup; + } + + if (wc_OcspResponder_AddSigner(responder, caCertDer, (word32)caCertDerSz, + caKeyDer, (word32)caKeyDerSz, + NULL, 0) != 0) { + fprintf(stderr, "Error adding signer\n"); + goto cleanup; + } + + /* Register any extra cert arguments as GOOD */ + for (i = 4; i < argc; i++) { + if (AddGoodCert(responder, caSubject, caSubjectSz, argv[i]) == 0) + printf("Registered GOOD: %s\n", argv[i]); + else + fprintf(stderr, "Warning: could not register %s\n", argv[i]); + } + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons((unsigned short)port); + + if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + perror("bind"); + goto cleanup; + } + listen(sockfd, 5); + printf("OCSP responder listening on port %d\n", port); + + while (running) { + byte httpBuf[BUF_SZ]; + byte respBuf[BUF_SZ]; + word32 respSz; + const byte* ocspReq; + int ocspReqSz, recvLen; + + clientfd = accept(sockfd, NULL, NULL); + if (clientfd < 0) continue; + + recvLen = RecvHttp(clientfd, httpBuf, BUF_SZ); + if (recvLen <= 0 || + ParsePost(httpBuf, recvLen, &ocspReq, &ocspReqSz) != 0) { + SendHttpError(clientfd, 400, "Bad Request"); + close(clientfd); + continue; + } + + respSz = sizeof(respBuf); + if (wc_OcspResponder_WriteResponse(responder, ocspReq, + (word32)ocspReqSz, respBuf, &respSz) != 0) { + respSz = sizeof(respBuf); + wc_OcspResponder_WriteErrorResponse(OCSP_INTERNAL_ERROR, + respBuf, &respSz); + } + + SendOcspResp(clientfd, respBuf, (int)respSz); + close(clientfd); + } + + close(sockfd); + printf("\nShutdown.\n"); + +cleanup: + if (responder) wc_OcspResponder_free(responder); + if (caCertInit) wc_FreeDecodedCert(&caCert); + free(caCertDer); + free(caKeyDer); + wolfSSL_Cleanup(); + return 0; +} + +#else + +int main(int argc, char** argv) +{ + (void)argc; (void)argv; + printf("This example requires --enable-ocsp --enable-ocsp-responder\n"); + return 0; +} + +#endif /* HAVE_OCSP && HAVE_OCSP_RESPONDER && !NO_FILESYSTEM */ From 1f6aa9c2591b76e6bf9aa1a8a518bb629d941c37 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Wed, 8 Apr 2026 11:30:25 +0200 Subject: [PATCH 2/4] Address code review feedback for OCSP responder examples - Add missing include for time(NULL) usage - Replace atoi() with strtol() and validate Content-Length in RecvHttp and ParsePost to reject negative/overflowing values - Add SendAll() helper to handle partial send() writes - Check return values of socket(), setsockopt(), and listen() --- ocsp/responder/ocsp-request-response.c | 1 + ocsp/responder/ocsp-responder-http.c | 56 ++++++++++++++++++++------ 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/ocsp/responder/ocsp-request-response.c b/ocsp/responder/ocsp-request-response.c index 784d754e..8c0a1551 100644 --- a/ocsp/responder/ocsp-request-response.c +++ b/ocsp/responder/ocsp-request-response.c @@ -37,6 +37,7 @@ #include #include +#include #if defined(HAVE_OCSP) && defined(HAVE_OCSP_RESPONDER) && \ !defined(NO_FILESYSTEM) diff --git a/ocsp/responder/ocsp-responder-http.c b/ocsp/responder/ocsp-responder-http.c index d48828f3..b0e15fdb 100644 --- a/ocsp/responder/ocsp-responder-http.c +++ b/ocsp/responder/ocsp-responder-http.c @@ -120,10 +120,14 @@ static int RecvHttp(int fd, byte* buf, int bufSz) headerEnd = (int)(hdrEnd - (char*)buf) + 4; cl = strstr((char*)buf, "Content-Length:"); if (!cl) cl = strstr((char*)buf, "content-length:"); - if (cl) contentLen = atoi(cl + 15); + if (cl) { + long val = strtol(cl + 15, NULL, 10); + if (val > 0 && val < bufSz) + contentLen = (int)val; + } } } - if (headerEnd && total >= headerEnd + contentLen) + if (headerEnd && contentLen > 0 && total >= headerEnd + contentLen) break; } return total; @@ -150,10 +154,15 @@ static int ParsePost(const byte* http, int httpSz, cl = strstr(hdr, "Content-Length:"); if (!cl) cl = strstr(hdr, "content-length:"); - if (cl) - *bodySz = atoi(cl + 15); - else + if (cl) { + long val = strtol(cl + 15, NULL, 10); + if (val <= 0 || val > httpSz - offset) + return -1; + *bodySz = (int)val; + } + else { *bodySz = httpSz - offset; + } if (offset + *bodySz > httpSz) return -1; @@ -162,6 +171,19 @@ static int ParsePost(const byte* http, int httpSz, return 0; } +static int SendAll(int fd, const void* data, int sz) +{ + const byte* p = (const byte*)data; + int remaining = sz; + while (remaining > 0) { + int n = (int)send(fd, p, (size_t)remaining, 0); + if (n < 0) return -1; + p += n; + remaining -= n; + } + return sz; +} + static void SendOcspResp(int fd, const byte* resp, int respSz) { char hdr[256]; @@ -173,8 +195,8 @@ static void SendOcspResp(int fd, const byte* resp, int respSz) "Content-Length: %d\r\n" "\r\n", respSz); - send(fd, hdr, (size_t)hdrLen, 0); - send(fd, resp, (size_t)respSz, 0); + SendAll(fd, hdr, hdrLen); + SendAll(fd, resp, respSz); } static void SendHttpError(int fd, int code, const char* msg) @@ -182,7 +204,7 @@ static void SendHttpError(int fd, int code, const char* msg) char buf[256]; int len = snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\r\nContent-Length: 0\r\n\r\n", code, msg); - send(fd, buf, (size_t)len, 0); + SendAll(fd, buf, len); } int main(int argc, char** argv) @@ -197,7 +219,7 @@ int main(int argc, char** argv) int caCertInit = 0; char caSubject[256]; word32 caSubjectSz = sizeof(caSubject); - int sockfd, clientfd, opt = 1, i; + int sockfd = -1, clientfd, opt = 1, i; struct sockaddr_in addr; if (argc < 4) { @@ -265,7 +287,14 @@ int main(int argc, char** argv) } sockfd = socket(AF_INET, SOCK_STREAM, 0); - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + if (sockfd < 0) { + perror("socket"); + goto cleanup; + } + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + perror("setsockopt"); + goto cleanup; + } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; @@ -275,7 +304,10 @@ int main(int argc, char** argv) perror("bind"); goto cleanup; } - listen(sockfd, 5); + if (listen(sockfd, 5) < 0) { + perror("listen"); + goto cleanup; + } printf("OCSP responder listening on port %d\n", port); while (running) { @@ -308,10 +340,10 @@ int main(int argc, char** argv) close(clientfd); } - close(sockfd); printf("\nShutdown.\n"); cleanup: + if (sockfd >= 0) close(sockfd); if (responder) wc_OcspResponder_free(responder); if (caCertInit) wc_FreeDecodedCert(&caCert); free(caCertDer); From 04ff1c7ddddb484d4ffdb59007842784fd1032e6 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Thu, 9 Apr 2026 19:05:41 +0200 Subject: [PATCH 3/4] Address PR #570 review feedback for OCSP responder examples - Add SO_RCVTIMEO (5s) on accepted client sockets to prevent indefinite blocking from incomplete requests - Move 64KB httpBuf/respBuf from stack to static globals - Fix SendAll infinite loop when send() returns 0 (check n <= 0) - Ignore SIGPIPE to prevent crash on client disconnect during writes - Use case-insensitive Content-Length header matching per RFC 7230 - Track error state and return nonzero from main on fatal errors - Reset ret after wolfSSL_CertManagerLoadCABuffer to avoid leaking WOLFSSL_SUCCESS (1) into error paths in ocsp-request-response.c - Add -Wextra to Makefile CFLAGS --- ocsp/responder/Makefile | 2 +- ocsp/responder/ocsp-request-response.c | 2 + ocsp/responder/ocsp-responder-http.c | 51 +++++++++++++++++++++----- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/ocsp/responder/Makefile b/ocsp/responder/Makefile index a66301e9..6f50fc4d 100644 --- a/ocsp/responder/Makefile +++ b/ocsp/responder/Makefile @@ -6,7 +6,7 @@ CC = gcc WOLFSSL_INSTALL_DIR = /usr/local -CFLAGS = -Wall -g -I$(WOLFSSL_INSTALL_DIR)/include +CFLAGS = -Wall -Wextra -g -I$(WOLFSSL_INSTALL_DIR)/include LIBS = -L$(WOLFSSL_INSTALL_DIR)/lib -lwolfssl # Uncomment for static linking: diff --git a/ocsp/responder/ocsp-request-response.c b/ocsp/responder/ocsp-request-response.c index 8c0a1551..92ba4008 100644 --- a/ocsp/responder/ocsp-request-response.c +++ b/ocsp/responder/ocsp-request-response.c @@ -172,6 +172,7 @@ int main(int argc, char** argv) wolfSSL_CertManagerFree(tmpCm); goto cleanup; } + ret = 0; /* Reset from WOLFSSL_SUCCESS (1) to 0 for error paths */ wc_InitDecodedCert(&serverCert, serverCertDer, (word32)serverCertDerSz, NULL); serverCertInit = 1; @@ -311,6 +312,7 @@ int main(int argc, char** argv) printf("Error loading CA into CertManager: %d\n", ret); goto cleanup; } + ret = 0; /* Reset from WOLFSSL_SUCCESS (1) to 0 for error paths */ ocsp = wc_NewOCSP(cm); if (ocsp == NULL) { diff --git a/ocsp/responder/ocsp-responder-http.c b/ocsp/responder/ocsp-responder-http.c index b0e15fdb..0eb09ae0 100644 --- a/ocsp/responder/ocsp-responder-http.c +++ b/ocsp/responder/ocsp-responder-http.c @@ -52,6 +52,8 @@ #include "ocsp-load-certs.h" +#include + #include #include #include @@ -61,6 +63,22 @@ static volatile int running = 1; +/* Case-insensitive substring search (for HTTP headers per RFC 7230) */ +static char* FindHeaderCI(const char* haystack, const char* needle) +{ + size_t nLen = strlen(needle); + while (*haystack) { + if (strncasecmp(haystack, needle, nLen) == 0) + return (char*)haystack; + haystack++; + } + return NULL; +} + +/* Large buffers as static globals to avoid 128KB on the stack each iteration */ +static byte httpBuf[BUF_SZ]; +static byte respBuf[BUF_SZ]; + static void sigHandler(int sig) { (void)sig; @@ -118,8 +136,7 @@ static int RecvHttp(int fd, byte* buf, int bufSz) if (hdrEnd) { char* cl; headerEnd = (int)(hdrEnd - (char*)buf) + 4; - cl = strstr((char*)buf, "Content-Length:"); - if (!cl) cl = strstr((char*)buf, "content-length:"); + cl = FindHeaderCI((char*)buf, "Content-Length:"); if (cl) { long val = strtol(cl + 15, NULL, 10); if (val > 0 && val < bufSz) @@ -152,8 +169,7 @@ static int ParsePost(const byte* http, int httpSz, if (!end) return -1; offset = (int)(end - hdr) + 4; - cl = strstr(hdr, "Content-Length:"); - if (!cl) cl = strstr(hdr, "content-length:"); + cl = FindHeaderCI(hdr, "Content-Length:"); if (cl) { long val = strtol(cl + 15, NULL, 10); if (val <= 0 || val > httpSz - offset) @@ -177,7 +193,7 @@ static int SendAll(int fd, const void* data, int sz) int remaining = sz; while (remaining > 0) { int n = (int)send(fd, p, (size_t)remaining, 0); - if (n < 0) return -1; + if (n <= 0) return -1; p += n; remaining -= n; } @@ -219,7 +235,7 @@ int main(int argc, char** argv) int caCertInit = 0; char caSubject[256]; word32 caSubjectSz = sizeof(caSubject); - int sockfd = -1, clientfd, opt = 1, i; + int sockfd = -1, clientfd, opt = 1, i, ret = 0; struct sockaddr_in addr; if (argc < 4) { @@ -244,12 +260,17 @@ int main(int argc, char** argv) sigemptyset(&sa.sa_mask); sigaction(SIGINT, &sa, NULL); sigaction(SIGTERM, &sa, NULL); + + /* Ignore SIGPIPE so client disconnections during writes don't crash */ + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); } caCertDer = LoadCertDer(certFile, &caCertDerSz); caKeyDer = LoadKeyDer(keyFile, &caKeyDerSz); if (!caCertDer || !caKeyDer) { fprintf(stderr, "Error loading cert/key\n"); + ret = -1; goto cleanup; } @@ -257,17 +278,20 @@ int main(int argc, char** argv) caCertInit = 1; if (wc_ParseCert(&caCert, CERT_TYPE, 0, NULL) != 0) { fprintf(stderr, "Error parsing CA cert\n"); + ret = -1; goto cleanup; } if (wc_GetDecodedCertSubject(&caCert, caSubject, &caSubjectSz) != 0) { fprintf(stderr, "Error getting CA subject\n"); + ret = -1; goto cleanup; } responder = wc_OcspResponder_new(NULL, 1); if (!responder) { fprintf(stderr, "Error creating responder\n"); + ret = -1; goto cleanup; } @@ -275,6 +299,7 @@ int main(int argc, char** argv) caKeyDer, (word32)caKeyDerSz, NULL, 0) != 0) { fprintf(stderr, "Error adding signer\n"); + ret = -1; goto cleanup; } @@ -289,10 +314,12 @@ int main(int argc, char** argv) sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); + ret = -1; goto cleanup; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt"); + ret = -1; goto cleanup; } memset(&addr, 0, sizeof(addr)); @@ -302,24 +329,30 @@ int main(int argc, char** argv) if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { perror("bind"); + ret = -1; goto cleanup; } if (listen(sockfd, 5) < 0) { perror("listen"); + ret = -1; goto cleanup; } printf("OCSP responder listening on port %d\n", port); while (running) { - byte httpBuf[BUF_SZ]; - byte respBuf[BUF_SZ]; word32 respSz; const byte* ocspReq; int ocspReqSz, recvLen; + struct timeval tv; clientfd = accept(sockfd, NULL, NULL); if (clientfd < 0) continue; + /* Set receive timeout so incomplete requests don't block forever */ + tv.tv_sec = 5; + tv.tv_usec = 0; + setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + recvLen = RecvHttp(clientfd, httpBuf, BUF_SZ); if (recvLen <= 0 || ParsePost(httpBuf, recvLen, &ocspReq, &ocspReqSz) != 0) { @@ -349,7 +382,7 @@ int main(int argc, char** argv) free(caCertDer); free(caKeyDer); wolfSSL_Cleanup(); - return 0; + return ret; } #else From b79c987789ce6fe19e4a8cbd5c39e27e7be0c02e Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz <49391366+julek-wolfssl@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:17:01 +0200 Subject: [PATCH 4/4] Update ocsp/responder/nginx-scgi/run.sh Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ocsp/responder/nginx-scgi/run.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ocsp/responder/nginx-scgi/run.sh b/ocsp/responder/nginx-scgi/run.sh index ad42ee9c..09fde5c8 100755 --- a/ocsp/responder/nginx-scgi/run.sh +++ b/ocsp/responder/nginx-scgi/run.sh @@ -92,12 +92,12 @@ echo "" # --- Step 1: Start wolfclu OCSP responder in SCGI mode --- echo "Starting wolfclu OCSP responder (SCGI on port $SCGI_PORT)..." -WOLFCLU_ARGS="-scgi -port $SCGI_PORT -rsigner $CA_CERT -rkey $CA_KEY -CA $CA_CERT" +set -- -scgi -port "$SCGI_PORT" -rsigner "$CA_CERT" -rkey "$CA_KEY" -CA "$CA_CERT" if [ -n "$INDEX_FILE" ]; then - WOLFCLU_ARGS="$WOLFCLU_ARGS -index $INDEX_FILE" + set -- "$@" -index "$INDEX_FILE" fi -wolfssl ocsp $WOLFCLU_ARGS & +wolfssl ocsp "$@" & WOLFCLU_PID=$! sleep 1