Conversation
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.
There was a problem hiding this comment.
Pull request overview
Adds a new ocsp/responder/ set of examples showcasing the wolfSSL OCSP Responder API, including in-memory request/response generation, a minimal HTTP responder, and an nginx+SCGI deployment pattern using wolfCLU.
Changes:
- Added two C examples: raw DER OCSP request/response and a minimal HTTP POST responder.
- Added shared PEM->DER file loading helpers and a local Makefile to build the examples.
- Added nginx+SCGI deployment example (config + run script) and documented how to run all examples.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| ocsp/responder/README.md | Documents the new responder examples and how to build/run them. |
| ocsp/responder/ocsp-request-response.c | In-memory OCSP request encode + response sign + verification flow example. |
| ocsp/responder/ocsp-responder-http.c | Minimal socket-based HTTP responder that returns signed OCSP responses. |
| ocsp/responder/ocsp-load-certs.h | Shared file/cert/key loading helpers for the C examples. |
| ocsp/responder/nginx-scgi/run.sh | Script to stand up wolfCLU SCGI backend + nginx frontend for OCSP. |
| ocsp/responder/nginx-scgi/nginx-ocsp.conf | Example nginx config to SCGI-pass OCSP requests to wolfCLU. |
| ocsp/responder/Makefile | Builds the responder examples against an installed wolfSSL. |
| .gitignore | Ignores the newly built responder example binaries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add missing <time.h> 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()
| } | ||
|
|
||
| /* Receive full HTTP request. */ | ||
| static int RecvHttp(int fd, byte* buf, int bufSz) |
There was a problem hiding this comment.
BLOCK-2: RecvHttp blocks indefinitely when client sends no Content-Length and keeps connection open
- File:
ocsp/responder/ocsp-responder-http.c:106-134 - Function:
RecvHttp - Category: [bug]
- Confidence: High
Description: When a client sends an HTTP request without a Content-Length header (or sends headers without \r\n\r\n), the recv loop will block indefinitely waiting for more data — the server is single-threaded so this stalls all OCSP processing. There is no read timeout set on the socket. A misbehaving or slow client will hang the entire responder.
While the code is described as "not production-hardened," even for an example/demo server, a single curl or telnet connection that stays open would make the responder completely unresponsive. This is a functional correctness issue for anyone running the example.
Code:
while (total < bufSz - 1) {
int n = (int)recv(fd, buf + total, (size_t)(bufSz - 1 - total), 0);
if (n <= 0) break;
...
}Recommendation: Set a SO_RCVTIMEO on the client socket after accept():
| static int RecvHttp(int fd, byte* buf, int bufSz) | |
| /* In the while(running) loop, after accept(): */ | |
| { | |
| struct timeval tv; | |
| tv.tv_sec = 5; | |
| tv.tv_usec = 0; | |
| setsockopt(clientfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); | |
| } |
| printf("OCSP responder listening on port %d\n", port); | ||
|
|
||
| while (running) { | ||
| byte httpBuf[BUF_SZ]; |
There was a problem hiding this comment.
Consider making these static globals? Each are 64KB
| { | ||
| const byte* p = (const byte*)data; | ||
| int remaining = sz; | ||
| while (remaining > 0) { |
There was a problem hiding this comment.
SUGGEST-2: send() returning 0 can cause infinite loop in SendAll
- File:
ocsp/responder/ocsp-responder-http.c:178-180 - Function:
SendAll - Category: [bug]
- Confidence: Medium
Description: send() can return 0 on a non-blocking socket or under certain conditions. The current check if (n < 0) return -1; would cause an infinite loop if send returns 0 repeatedly, since remaining never decreases. While the socket is blocking by default, this is fragile.
Code:
int n = (int)send(fd, p, (size_t)remaining, 0);
if (n < 0) return -1;Recommendation:
| while (remaining > 0) { | |
| if (n <= 0) return -1; |
| keyFile = argv[3]; | ||
|
|
||
| wolfSSL_Init(); | ||
| { |
There was a problem hiding this comment.
SUGGEST-3: Missing SIGPIPE handling in HTTP server
- File:
ocsp/responder/ocsp-responder-http.c:240-247 - Function:
main - Category: [bug]
- Confidence: Medium
Description: When a client disconnects while the server is writing a response, send() will generate SIGPIPE which kills the process by default. The signal handlers set up for SIGINT/SIGTERM do not cover this. This means a client that connects and immediately disconnects can crash the responder.
Recommendation: Add signal(SIGPIPE, SIG_IGN); or set SA_NOSIGPIPE/MSG_NOSIGNAL:
| { | |
| signal(SIGPIPE, SIG_IGN); |
| if (hdrEnd) { | ||
| char* cl; | ||
| headerEnd = (int)(hdrEnd - (char*)buf) + 4; | ||
| cl = strstr((char*)buf, "Content-Length:"); |
There was a problem hiding this comment.
SUGGEST-4: HTTP Content-Length header parsing is case-sensitive with only two variants
- File:
ocsp/responder/ocsp-responder-http.c:121-122,155-156 - Functions:
RecvHttp,ParsePost - Category: [convention]
- Confidence: Low
Description: The code checks for "Content-Length:" and "content-length:" but RFC 7230 specifies HTTP header field names are case-insensitive. Other valid forms like "CONTENT-LENGTH:" or "Content-length:" would be missed. For an example this is probably acceptable, but since wolfSSL users may copy this pattern, a case-insensitive search would be more robust.
Recommendation: Consider a simple case-insensitive strstr helper, or add a brief comment noting this intentional limitation.
|
|
||
| printf("\nShutdown.\n"); | ||
|
|
||
| cleanup: |
There was a problem hiding this comment.
SUGGEST-5: ocsp-responder-http.c main always returns 0 on error paths
- File:
ocsp/responder/ocsp-responder-http.c:345-352 - Function:
main - Category: [bug]
- Confidence: High
Description: When an error occurs during setup (loading certs, parsing, creating responder, binding socket, etc.), the code jumps to cleanup: which always return 0. This means the process exits with success status even on fatal errors. Compare with ocsp-request-response.c which correctly returns ret.
Code:
cleanup:
if (sockfd >= 0) close(sockfd);
if (responder) wc_OcspResponder_free(responder);
if (caCertInit) wc_FreeDecodedCert(&caCert);
free(caCertDer);
free(caKeyDer);
wolfSSL_Cleanup();
return 0; /* Always 0, even on error */Recommendation: Track error state and return non-zero on failure:
| cleanup: | |
| wolfSSL_Cleanup(); | |
| return ret; |
(and initialize int ret = -1; at declaration, set ret = 0 before the main loop, similar to the pattern in ocsp-request-response.c)
|
|
||
| ret = wolfSSL_CertManagerLoadCABuffer(cm, caCertDer, caCertDerSz, | ||
| SSL_FILETYPE_ASN1); | ||
| if (ret != WOLFSSL_SUCCESS) { |
There was a problem hiding this comment.
NIT-1: ocsp-request-response.c return code inconsistency on success path
- File:
ocsp/responder/ocsp-request-response.c:310-313 - Function:
main - Category: [convention]
- Confidence: Medium
Description: After wolfSSL_CertManagerLoadCABuffer succeeds, ret holds WOLFSSL_SUCCESS (which is 1). The code then checks ret != WOLFSSL_SUCCESS and continues, but ret remains WOLFSSL_SUCCESS. If wc_NewOCSP fails at line 315, the goto to cleanup returns ret which would be 1 (success) instead of an error. The ret = 0 at line 387 fixes this for the normal path, but error paths between lines 310-320 could return a misleading value.
Recommendation: Reset ret = 0 after the wolfSSL_CertManagerLoadCABuffer success check, or set ret = -1 in the wc_NewOCSP failure block (which the code already does at line 318).
|
|
||
| CC = gcc | ||
| WOLFSSL_INSTALL_DIR = /usr/local | ||
| CFLAGS = -Wall -g -I$(WOLFSSL_INSTALL_DIR)/include |
There was a problem hiding this comment.
NIT-2: Makefile missing -Wextra and -Werror for development
- File:
ocsp/responder/Makefile:9 - Category: [style]
- Confidence: Low
Description: The Makefile uses only -Wall for warnings. Adding -Wextra would catch additional issues during development. This is consistent with some other examples in the repo that also only use -Wall, so it follows convention.
Recommendation: No change needed; just noting for awareness.
Three examples demonstrating the new OCSP Responder API:
ocsp-request-response.c - Pure API usage: encode DER OCSP requests
from certificates, generate signed responses, and verify them
in memory without networking.
ocsp-responder-http.c - Minimal HTTP server that accepts POST
requests with DER OCSP payloads and returns signed responses.
nginx-scgi/ - Production-style deployment using nginx as HTTP
frontend with wolfclu running as an SCGI backend for OCSP
processing.