blob: 1271f6e406a1282769eed84676afc5700e4d02b5 [file] [log] [blame]
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001
2/*
3 * SSL/TLS OCSP-related functions
4 *
5 * Copyright (C) 2022 HAProxy Technologies, Remi Tricot-Le Breton <rlebreton@haproxy.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 * Acknowledgement:
13 * We'd like to specially thank the Stud project authors for a very clean
14 * and well documented code which helped us understand how the OpenSSL API
15 * ought to be used in non-blocking mode. This is one difficult part which
16 * is not easy to get from the OpenSSL doc, and reading the Stud code made
17 * it much more obvious than the examples in the OpenSSL package. Keep up
18 * the good works, guys !
19 *
20 * Stud is an extremely efficient and scalable SSL/TLS proxy which combines
21 * particularly well with haproxy. For more info about this project, visit :
22 * https://github.com/bumptech/stud
23 *
24 */
25
26/* Note: do NOT include openssl/xxx.h here, do it in openssl-compat.h */
27#define _GNU_SOURCE
28#include <ctype.h>
29#include <dirent.h>
30#include <errno.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include <sys/socket.h>
37#include <sys/stat.h>
38#include <sys/types.h>
39#include <netdb.h>
40#include <netinet/tcp.h>
41
42#include <import/ebpttree.h>
43#include <import/ebsttree.h>
44#include <import/lru.h>
45
46#include <haproxy/api.h>
47#include <haproxy/applet.h>
48#include <haproxy/arg.h>
49#include <haproxy/base64.h>
50#include <haproxy/channel.h>
51#include <haproxy/chunk.h>
52#include <haproxy/cli.h>
53#include <haproxy/connection.h>
54#include <haproxy/dynbuf.h>
55#include <haproxy/errors.h>
56#include <haproxy/fd.h>
57#include <haproxy/freq_ctr.h>
58#include <haproxy/frontend.h>
59#include <haproxy/global.h>
60#include <haproxy/http_rules.h>
61#include <haproxy/log.h>
62#include <haproxy/openssl-compat.h>
63#include <haproxy/pattern-t.h>
64#include <haproxy/proto_tcp.h>
65#include <haproxy/proxy.h>
66#include <haproxy/sample.h>
67#include <haproxy/sc_strm.h>
68#include <haproxy/quic_conn.h>
69#include <haproxy/quic_tp.h>
70#include <haproxy/server.h>
71#include <haproxy/shctx.h>
72#include <haproxy/ssl_ckch.h>
73#include <haproxy/ssl_crtlist.h>
74#include <haproxy/ssl_sock.h>
75#include <haproxy/ssl_utils.h>
76#include <haproxy/stats.h>
77#include <haproxy/stconn.h>
78#include <haproxy/stream-t.h>
79#include <haproxy/task.h>
80#include <haproxy/ticks.h>
81#include <haproxy/time.h>
82#include <haproxy/tools.h>
83#include <haproxy/vars.h>
84#include <haproxy/xxhash.h>
85#include <haproxy/istbuf.h>
86#include <haproxy/ssl_ocsp-t.h>
87#include <haproxy/http_client.h>
88
89
90/* ***** READ THIS before adding code here! *****
91 *
92 * Due to API incompatibilities between multiple OpenSSL versions and their
93 * derivatives, it's often tempting to add macros to (re-)define certain
94 * symbols. Please do not do this here, and do it in common/openssl-compat.h
95 * exclusively so that the whole code consistently uses the same macros.
96 *
97 * Whenever possible if a macro is missing in certain versions, it's better
98 * to conditionally define it in openssl-compat.h than using lots of ifdefs.
99 */
100
101#ifndef OPENSSL_NO_OCSP
102int ocsp_ex_index = -1;
103
104int ssl_sock_get_ocsp_arg_kt_index(int evp_keytype)
105{
106 switch (evp_keytype) {
107 case EVP_PKEY_RSA:
108 return 2;
109 case EVP_PKEY_DSA:
110 return 0;
111 case EVP_PKEY_EC:
112 return 1;
113 }
114
115 return -1;
116}
117
118/*
119 * Callback used to set OCSP status extension content in server hello.
120 */
121int ssl_sock_ocsp_stapling_cbk(SSL *ssl, void *arg)
122{
123 struct certificate_ocsp *ocsp;
124 struct ocsp_cbk_arg *ocsp_arg;
125 char *ssl_buf;
126 SSL_CTX *ctx;
127 EVP_PKEY *ssl_pkey;
128 int key_type;
129 int index;
130
131 ctx = SSL_get_SSL_CTX(ssl);
132 if (!ctx)
133 return SSL_TLSEXT_ERR_NOACK;
134
135 ocsp_arg = SSL_CTX_get_ex_data(ctx, ocsp_ex_index);
136 if (!ocsp_arg)
137 return SSL_TLSEXT_ERR_NOACK;
138
139 ssl_pkey = SSL_get_privatekey(ssl);
140 if (!ssl_pkey)
141 return SSL_TLSEXT_ERR_NOACK;
142
143 key_type = EVP_PKEY_base_id(ssl_pkey);
144
145 if (ocsp_arg->is_single && ocsp_arg->single_kt == key_type)
146 ocsp = ocsp_arg->s_ocsp;
147 else {
148 /* For multiple certs per context, we have to find the correct OCSP response based on
149 * the certificate type
150 */
151 index = ssl_sock_get_ocsp_arg_kt_index(key_type);
152
153 if (index < 0)
154 return SSL_TLSEXT_ERR_NOACK;
155
156 ocsp = ocsp_arg->m_ocsp[index];
157
158 }
159
160 if (!ocsp ||
161 !ocsp->response.area ||
162 !ocsp->response.data ||
163 (ocsp->expire < now.tv_sec))
164 return SSL_TLSEXT_ERR_NOACK;
165
166 ssl_buf = OPENSSL_malloc(ocsp->response.data);
167 if (!ssl_buf)
168 return SSL_TLSEXT_ERR_NOACK;
169
170 memcpy(ssl_buf, ocsp->response.area, ocsp->response.data);
171 SSL_set_tlsext_status_ocsp_resp(ssl, (unsigned char*)ssl_buf, ocsp->response.data);
172
173 return SSL_TLSEXT_ERR_OK;
174}
175
176#endif /* !defined(OPENSSL_NO_OCSP) */
177
178
179#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
180
181struct eb_root cert_ocsp_tree = EB_ROOT_UNIQUE;
182
183__decl_thread(HA_SPINLOCK_T ocsp_tree_lock);
184
185struct eb_root ocsp_update_tree = EB_ROOT; /* updatable ocsp responses sorted by next_update in absolute time */
186#define SSL_OCSP_UPDATE_DELAY_MAX 60*60 /* 1H */
187#define SSL_OCSP_UPDATE_DELAY_MIN 5*60 /* 5 minutes */
188#define SSL_OCSP_UPDATE_MARGIN 60 /* 1 minute */
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +0100189#define SSL_OCSP_HTTP_ERR_REPLAY 60 /* 1 minute */
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100190
191/* This function starts to check if the OCSP response (in DER format) contained
192 * in chunk 'ocsp_response' is valid (else exits on error).
193 * If 'cid' is not NULL, it will be compared to the OCSP certificate ID
194 * contained in the OCSP Response and exits on error if no match.
195 * If it's a valid OCSP Response:
196 * If 'ocsp' is not NULL, the chunk is copied in the OCSP response's container
197 * pointed by 'ocsp'.
198 * If 'ocsp' is NULL, the function looks up into the OCSP response's
199 * containers tree (using as index the ASN1 form of the OCSP Certificate ID extracted
200 * from the response) and exits on error if not found. Finally, If an OCSP response is
201 * already present in the container, it will be overwritten.
202 *
203 * Note: OCSP response containing more than one OCSP Single response is not
204 * considered valid.
205 *
206 * Returns 0 on success, 1 in error case.
207 */
208int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
209 struct certificate_ocsp *ocsp,
210 OCSP_CERTID *cid, char **err)
211{
212 OCSP_RESPONSE *resp;
213 OCSP_BASICRESP *bs = NULL;
214 OCSP_SINGLERESP *sr;
215 OCSP_CERTID *id;
216 unsigned char *p = (unsigned char *) ocsp_response->area;
217 int rc , count_sr;
218 ASN1_GENERALIZEDTIME *revtime, *thisupd, *nextupd = NULL;
219 int reason;
220 int ret = 1;
221#ifdef HAVE_ASN1_TIME_TO_TM
222 struct tm nextupd_tm = {0};
223#endif
224
225 resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p,
226 ocsp_response->data);
227 if (!resp) {
228 memprintf(err, "Unable to parse OCSP response");
229 goto out;
230 }
231
232 rc = OCSP_response_status(resp);
233 if (rc != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
234 memprintf(err, "OCSP response status not successful");
235 goto out;
236 }
237
238 bs = OCSP_response_get1_basic(resp);
239 if (!bs) {
240 memprintf(err, "Failed to get basic response from OCSP Response");
241 goto out;
242 }
243
244 count_sr = OCSP_resp_count(bs);
245 if (count_sr > 1) {
246 memprintf(err, "OCSP response ignored because contains multiple single responses (%d)", count_sr);
247 goto out;
248 }
249
250 sr = OCSP_resp_get0(bs, 0);
251 if (!sr) {
252 memprintf(err, "Failed to get OCSP single response");
253 goto out;
254 }
255
256 id = (OCSP_CERTID*)OCSP_SINGLERESP_get0_id(sr);
257
258 rc = OCSP_single_get0_status(sr, &reason, &revtime, &thisupd, &nextupd);
259 if (rc != V_OCSP_CERTSTATUS_GOOD && rc != V_OCSP_CERTSTATUS_REVOKED) {
260 memprintf(err, "OCSP single response: certificate status is unknown");
261 goto out;
262 }
263
264 if (!nextupd) {
265 memprintf(err, "OCSP single response: missing nextupdate");
266 goto out;
267 }
268
269 rc = OCSP_check_validity(thisupd, nextupd, OCSP_MAX_RESPONSE_TIME_SKEW, -1);
270 if (!rc) {
271 memprintf(err, "OCSP single response: no longer valid.");
272 goto out;
273 }
274
275 if (cid) {
276 if (OCSP_id_cmp(id, cid)) {
277 memprintf(err, "OCSP single response: Certificate ID does not match certificate and issuer");
278 goto out;
279 }
280 }
281
282 if (!ocsp) {
283 unsigned char key[OCSP_MAX_CERTID_ASN1_LENGTH];
284 unsigned char *p;
285
286 rc = i2d_OCSP_CERTID(id, NULL);
287 if (!rc) {
288 memprintf(err, "OCSP single response: Unable to encode Certificate ID");
289 goto out;
290 }
291
292 if (rc > OCSP_MAX_CERTID_ASN1_LENGTH) {
293 memprintf(err, "OCSP single response: Certificate ID too long");
294 goto out;
295 }
296
297 p = key;
298 memset(key, 0, OCSP_MAX_CERTID_ASN1_LENGTH);
299 i2d_OCSP_CERTID(id, &p);
300 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
301 ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, key, OCSP_MAX_CERTID_ASN1_LENGTH);
302 if (!ocsp) {
303 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
304 memprintf(err, "OCSP single response: Certificate ID does not match any certificate or issuer");
305 goto out;
306 }
307 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
308 }
309
310 /* According to comments on "chunk_dup", the
311 previous chunk buffer will be freed */
312 if (!chunk_dup(&ocsp->response, ocsp_response)) {
313 memprintf(err, "OCSP response: Memory allocation error");
314 goto out;
315 }
316
317#ifdef HAVE_ASN1_TIME_TO_TM
318 if (ASN1_TIME_to_tm(nextupd, &nextupd_tm) == 0) {
319 memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
320 goto out;
321 }
322 ocsp->expire = my_timegm(&nextupd_tm) - OCSP_MAX_RESPONSE_TIME_SKEW;
323#else
324 ocsp->expire = asn1_generalizedtime_to_epoch(nextupd) - OCSP_MAX_RESPONSE_TIME_SKEW;
325 if (ocsp->expire < 0) {
326 memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
327 goto out;
328 }
329#endif
330
331 ret = 0;
332out:
333 ERR_clear_error();
334
335 if (bs)
336 OCSP_BASICRESP_free(bs);
337
338 if (resp)
339 OCSP_RESPONSE_free(resp);
340
341 return ret;
342}
343/*
344 * External function use to update the OCSP response in the OCSP response's
345 * containers tree. The chunk 'ocsp_response' must contain the OCSP response
346 * to update in DER format.
347 *
348 * Returns 0 on success, 1 in error case.
349 */
350int ssl_sock_update_ocsp_response(struct buffer *ocsp_response, char **err)
351{
352 return ssl_sock_load_ocsp_response(ocsp_response, NULL, NULL, err);
353}
354
355
356
357#if !defined OPENSSL_IS_BORINGSSL
358/*
359 * Decrease the refcount of the struct ocsp_response and frees it if it's not
360 * used anymore. Also removes it from the tree if free'd.
361 */
362void ssl_sock_free_ocsp(struct certificate_ocsp *ocsp)
363{
364 if (!ocsp)
365 return;
366
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +0100367 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100368 ocsp->refcount--;
369 if (ocsp->refcount <= 0) {
370 ebmb_delete(&ocsp->key);
371 eb64_delete(&ocsp->next_update);
372 X509_free(ocsp->issuer);
373 ocsp->issuer = NULL;
374 sk_X509_pop_free(ocsp->chain, X509_free);
375 ocsp->chain = NULL;
376 chunk_destroy(&ocsp->response);
Remi Tricot-Le Breton648c83e2023-01-09 12:02:48 +0100377 if (ocsp->uri) {
378 ha_free(&ocsp->uri->area);
379 ha_free(&ocsp->uri);
380 }
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100381
382 free(ocsp);
383 }
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +0100384 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100385}
386
387
388/*
389 * This function dumps the details of an OCSP_CERTID. It is based on
390 * ocsp_certid_print in OpenSSL.
391 */
392static inline int ocsp_certid_print(BIO *bp, OCSP_CERTID *certid, int indent)
393{
394 ASN1_OCTET_STRING *piNameHash = NULL;
395 ASN1_OCTET_STRING *piKeyHash = NULL;
396 ASN1_INTEGER *pSerial = NULL;
397
398 if (OCSP_id_get0_info(&piNameHash, NULL, &piKeyHash, &pSerial, certid)) {
399
400 BIO_printf(bp, "%*sCertificate ID:\n", indent, "");
401 indent += 2;
402 BIO_printf(bp, "%*sIssuer Name Hash: ", indent, "");
403#ifndef USE_OPENSSL_WOLFSSL
404 i2a_ASN1_STRING(bp, piNameHash, 0);
405#else
406 wolfSSL_ASN1_STRING_print(bp, piNameHash);
407#endif
408 BIO_printf(bp, "\n%*sIssuer Key Hash: ", indent, "");
409#ifndef USE_OPENSSL_WOLFSSL
410 i2a_ASN1_STRING(bp, piKeyHash, 0);
411#else
412 wolfSSL_ASN1_STRING_print(bp, piNameHash);
413#endif
414 BIO_printf(bp, "\n%*sSerial Number: ", indent, "");
415 i2a_ASN1_INTEGER(bp, pSerial);
416 }
417 return 1;
418}
419
420/*
421 * Dump the details about an OCSP response in DER format stored in
422 * <ocsp_response> into buffer <out>.
423 * Returns 0 in case of success.
424 */
425int ssl_ocsp_response_print(struct buffer *ocsp_response, struct buffer *out)
426{
427 BIO *bio = NULL;
428 int write = -1;
429 OCSP_RESPONSE *resp;
430 const unsigned char *p;
431 int retval = -1;
432
433 if (!ocsp_response)
434 return -1;
435
436 if ((bio = BIO_new(BIO_s_mem())) == NULL)
437 return -1;
438
439 p = (const unsigned char*)ocsp_response->area;
440
441 resp = d2i_OCSP_RESPONSE(NULL, &p, ocsp_response->data);
442 if (!resp) {
443 chunk_appendf(out, "Unable to parse OCSP response");
444 goto end;
445 }
446
447#ifndef USE_OPENSSL_WOLFSSL
448 if (OCSP_RESPONSE_print(bio, resp, 0) != 0) {
449#else
450 if (wolfSSL_d2i_OCSP_RESPONSE_bio(bio, &resp) != 0) {
451#endif
452 struct buffer *trash = get_trash_chunk();
453 struct ist ist_block = IST_NULL;
454 struct ist ist_double_lf = IST_NULL;
455 static struct ist double_lf = IST("\n\n");
456
457 write = BIO_read(bio, trash->area, trash->size - 1);
458 if (write <= 0)
459 goto end;
460 trash->data = write;
461
462 /* Look for empty lines in the 'trash' buffer and add a space to
463 * the beginning to avoid having empty lines in the output
464 * (without changing the appearance of the information
465 * displayed).
466 */
467 ist_block = ist2(b_orig(trash), b_data(trash));
468
469 ist_double_lf = istist(ist_block, double_lf);
470
471 while (istlen(ist_double_lf)) {
472 /* istptr(ist_double_lf) points to the first \n of a
473 * \n\n pattern.
474 */
475 uint empty_line_offset = istptr(ist_double_lf) + 1 - istptr(ist_block);
476
477 /* Write up to the first '\n' of the "\n\n" pattern into
478 * the output buffer.
479 */
480 b_putblk(out, istptr(ist_block), empty_line_offset);
481 /* Add an extra space. */
482 b_putchr(out, ' ');
483
484 /* Keep looking for empty lines in the rest of the data. */
485 ist_block = istadv(ist_block, empty_line_offset);
486
487 ist_double_lf = istist(ist_block, double_lf);
488 }
489
490 retval = (b_istput(out, ist_block) <= 0);
491 }
492
493end:
494 if (bio)
495 BIO_free(bio);
496
497 OCSP_RESPONSE_free(resp);
498
499 return retval;
500}
501
502/*
503 * Dump the details of the OCSP response of ID <ocsp_certid> into buffer <out>.
504 * Returns 0 in case of success.
505 */
506int ssl_get_ocspresponse_detail(unsigned char *ocsp_certid, struct buffer *out)
507{
508 struct certificate_ocsp *ocsp;
509 int ret = 0;
510
511 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
512 ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, ocsp_certid, OCSP_MAX_CERTID_ASN1_LENGTH);
513 if (!ocsp) {
514 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
515 return -1;
516 }
517
518 ret = ssl_ocsp_response_print(&ocsp->response, out);
519
520 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
521
522 return ret;
523}
524
525
526/* IO handler of details "show ssl ocsp-response <id>".
527 * The current entry is taken from appctx->svcctx.
528 */
529static int cli_io_handler_show_ocspresponse_detail(struct appctx *appctx)
530{
531 struct buffer *trash = alloc_trash_chunk();
532 struct certificate_ocsp *ocsp = appctx->svcctx;
533
534 if (trash == NULL)
535 return 1;
536
537 if (ssl_ocsp_response_print(&ocsp->response, trash)) {
538 free_trash_chunk(trash);
539 return 1;
540 }
541
542 if (applet_putchk(appctx, trash) == -1)
543 goto yield;
544
545 appctx->svcctx = NULL;
546 if (trash)
547 free_trash_chunk(trash);
548 return 1;
549
550yield:
551 if (trash)
552 free_trash_chunk(trash);
553
554 return 0;
555}
556
557void ssl_sock_ocsp_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
558{
559 struct ocsp_cbk_arg *ocsp_arg;
560
561 if (ptr) {
562 ocsp_arg = ptr;
563
564 if (ocsp_arg->is_single) {
565 ssl_sock_free_ocsp(ocsp_arg->s_ocsp);
566 ocsp_arg->s_ocsp = NULL;
567 } else {
568 int i;
569
570 for (i = 0; i < SSL_SOCK_NUM_KEYTYPES; i++) {
571 ssl_sock_free_ocsp(ocsp_arg->m_ocsp[i]);
572 ocsp_arg->m_ocsp[i] = NULL;
573 }
574 }
575 free(ocsp_arg);
576 }
577}
578
579/*
580 * Extract the first OCSP URI (if any) contained in <cert> and write it into
581 * <out>.
582 * Returns 0 in case of success, 1 otherwise.
583 */
584int ssl_ocsp_get_uri_from_cert(X509 *cert, struct buffer *out, char **err)
585{
586 STACK_OF(OPENSSL_STRING) *ocsp_uri_stk = NULL;
587 int ret = 1;
588
589 if (!cert || !out)
590 goto end;
591
592 ocsp_uri_stk = X509_get1_ocsp(cert);
593 if (ocsp_uri_stk == NULL) {
594 memprintf(err, "%sNo OCSP URL stack!\n", *err ? *err : "");
595 goto end;
596 }
597
William Lallemand8bc00f82022-12-22 10:09:11 +0100598 if (!chunk_strcpy(out, sk_OPENSSL_STRING_value(ocsp_uri_stk, 0))) {
599 memprintf(err, "%sOCSP URI too long!\n", *err ? *err : "");
600 goto end;
601 }
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100602 if (b_data(out) == 0) {
603 memprintf(err, "%sNo OCSP URL!\n", *err ? *err : "");
604 goto end;
605 }
606
607 ret = 0;
608
609end:
610 X509_email_free(ocsp_uri_stk);
611 return ret;
612}
613
614/*
615 * Create the url and request body that make a proper OCSP request for the
616 * <certid>. The <req_url> parameter should already hold the OCSP URI that was
617 * extracted from the corresponding certificate. Depending on the size of the
618 * certid we will either append data to the <req_url> to create a proper URL
619 * that will be sent with a GET command, or the <req_body> will be constructed
620 * in case of a POST.
621 * Returns 0 in case of success.
622 */
623int ssl_ocsp_create_request_details(const OCSP_CERTID *certid, struct buffer *req_url,
624 struct buffer *req_body, char **err)
625{
626 int errcode = -1;
627 OCSP_REQUEST *ocsp;
628 struct buffer *bin_request = get_trash_chunk();
629 unsigned char *outbuf = (unsigned char*)b_orig(bin_request);
630
631 ocsp = OCSP_REQUEST_new();
632 if (ocsp == NULL) {
633 memprintf(err, "%sCan't create OCSP_REQUEST\n", *err ? *err : "");
634 goto end;
635 }
636
637 if (OCSP_request_add0_id(ocsp, (OCSP_CERTID*)certid) == NULL) {
638 memprintf(err, "%sOCSP_request_add0_id() error\n", *err ? *err : "");
639 goto end;
640 }
641
642 bin_request->data = i2d_OCSP_REQUEST(ocsp, &outbuf);
643 if (b_data(bin_request) <= 0) {
644 memprintf(err, "%si2d_OCSP_REQUEST() error\n", *err ? *err : "");
645 goto end;
646 }
647
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100648 /* HTTP based OCSP requests can use either the GET or the POST method to
649 * submit their requests. To enable HTTP caching, small requests (that
650 * after encoding are less than 255 bytes), MAY be submitted using GET.
651 * If HTTP caching is not important, or the request is greater than 255
652 * bytes, the request SHOULD be submitted using POST.
653 */
William Lallemandeb530202022-12-22 10:19:07 +0100654 if (b_data(bin_request) + b_data(req_url) < 0xff) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100655 struct buffer *b64buf = get_trash_chunk();
656 char *ret = NULL;
657 int base64_ret = 0;
658
659 chunk_strcat(req_url, "/");
660
661 base64_ret = a2base64(b_orig(bin_request), b_data(bin_request),
662 b_orig(b64buf), b_size(b64buf));
663
664 if (base64_ret < 0) {
665 memprintf(err, "%sa2base64() error\n", *err ? *err : "");
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100666 goto end;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100667 }
668
669 b64buf->data = base64_ret;
670
William Lallemandeb530202022-12-22 10:19:07 +0100671 ret = encode_chunk((char*)b_stop(req_url), b_orig(req_url) + b_size(req_url), '%',
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100672 query_encode_map, b64buf);
673 if (ret && *ret == '\0') {
William Lallemandeb530202022-12-22 10:19:07 +0100674 req_url->data = ret - b_orig(req_url);
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100675 errcode = 0;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100676 }
677 }
678 else {
679 chunk_cpy(req_body, bin_request);
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100680 errcode = 0;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100681 }
682
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100683
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100684end:
685 OCSP_REQUEST_free(ocsp);
686
687 return errcode;
688}
689
690/*
691 * Parse an OCSP_RESPONSE contained in <respbuf> and check its validity in
692 * regard to the contents of <ckch> or the <issuer> certificate.
693 * Certificate_ocsp structure does not keep a reference to the corresponding
694 * ckch_store so outside of a CLI context (see "send ssl ocsp-response"
695 * command), we only have an easy access to the issuer's certificate whose
696 * reference is held in the structure.
697 * Return 0 in case of success, 1 otherwise.
698 */
699int ssl_ocsp_check_response(STACK_OF(X509) *chain, X509 *issuer,
700 struct buffer *respbuf, char **err)
701{
702 int ret = 1;
703 int n;
704 OCSP_RESPONSE *response = NULL;
705 OCSP_BASICRESP *basic = NULL;
706 X509_STORE *store = NULL;
707 const unsigned char *start = (const unsigned char*)b_orig(respbuf);
708
709 if (!chain && !issuer) {
710 memprintf(err, "check_ocsp_response needs a certificate validation chain or an issuer certificate");
711 goto end;
712 }
713
714 response = d2i_OCSP_RESPONSE(NULL, &start, b_data(respbuf));
715 if (!response) {
716 memprintf(err, "d2i_OCSP_RESPONSE() failed");
717 goto end;
718 }
719
720 n = OCSP_response_status(response);
721
722 if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
723 memprintf(err, "OCSP response not successful (%d: %s)",
724 n, OCSP_response_status_str(n));
725 goto end;
726 }
727
728 basic = OCSP_response_get1_basic(response);
729 if (basic == NULL) {
730 memprintf(err, "OCSP_response_get1_basic() failed");
731 goto end;
732 }
733
Remi Tricot-Le Breton8bdd0052023-01-09 12:02:43 +0100734 /* Create a temporary store in which we add the certificate's chain
735 * certificates. We assume that all those certificates can be trusted
736 * because they were provided by the user.
737 * The only ssl item that needs to be verified here is the OCSP
738 * response.
739 */
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100740 store = X509_STORE_new();
741 if (!store) {
742 memprintf(err, "X509_STORE_new() failed");
743 goto end;
744 }
Remi Tricot-Le Breton8bdd0052023-01-09 12:02:43 +0100745
746 if (chain) {
747 int i = 0;
748 for (i = 0; i < sk_X509_num(chain); i++) {
749 X509 *cert = sk_X509_value(chain, i);
750 X509_STORE_add_cert(store, cert);
751 }
752 }
753
754 if (issuer)
755 X509_STORE_add_cert(store, issuer);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100756
Remi Tricot-Le Breton8bdd0052023-01-09 12:02:43 +0100757 if (OCSP_basic_verify(basic, chain, store, OCSP_TRUSTOTHER) != 1) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100758 memprintf(err, "OCSP_basic_verify() failed");
759 goto end;
760 }
761
762 ret = 0;
763
764end:
765 X509_STORE_free(store);
766 OCSP_RESPONSE_free(response);
767 OCSP_BASICRESP_free(basic);
768 return ret;
769}
770
771
772/*
773 * OCSP-UPDATE RELATED FUNCTIONS AND STRUCTURES
774 */
775
776struct task *ocsp_update_task __read_mostly = NULL;
777
778static struct ssl_ocsp_task_ctx {
779 struct certificate_ocsp *cur_ocsp;
780 struct httpclient *hc;
781 int flags;
782} ssl_ocsp_task_ctx;
783
784const struct http_hdr ocsp_request_hdrs[] = {
785 { IST("Content-Type"), IST("application/ocsp-request") },
786 { IST_NULL, IST_NULL }
787};
788
789static struct task *ssl_ocsp_update_responses(struct task *task, void *context, unsigned int state);
790
791/*
792 * Create the main OCSP update task that will iterate over the OCSP responses
793 * stored in ocsp_update_tree and send an OCSP request via the http_client
794 * applet to the corresponding OCSP responder. The task will then be in charge
795 * of processing the response, verifying it and resinserting it in the actual
796 * ocsp response tree if the response is valid.
797 * Returns 0 in case of success.
798 */
799int ssl_create_ocsp_update_task(char **err)
800{
801 if (ocsp_update_task)
802 return 0; /* Already created */
803
804 ocsp_update_task = task_new_anywhere();
805 if (!ocsp_update_task) {
806 memprintf(err, "parsing : failed to allocate global ocsp update task.");
807 return -1;
808 }
809
810 ocsp_update_task->process = ssl_ocsp_update_responses;
811 ocsp_update_task->context = NULL;
812
813 return 0;
814}
815
816static int ssl_ocsp_task_schedule()
817{
818 if (ocsp_update_task)
819 task_schedule(ocsp_update_task, now_ms);
820
821 return 0;
822}
823REGISTER_POST_CHECK(ssl_ocsp_task_schedule);
824
825void ssl_sock_free_ocsp(struct certificate_ocsp *ocsp);
826
827void ssl_destroy_ocsp_update_task(void)
828{
829 struct eb64_node *node, *next;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100830 if (!ocsp_update_task)
831 return;
832
833 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
834
835 node = eb64_first(&ocsp_update_tree);
836 while (node) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100837 next = eb64_next(node);
838 eb64_delete(node);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100839 node = next;
840 }
841
842 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
843
844 task_destroy(ocsp_update_task);
845 ocsp_update_task = NULL;
Remi Tricot-Le Breton14d7f0e2023-01-09 12:02:45 +0100846
847 ssl_sock_free_ocsp(ssl_ocsp_task_ctx.cur_ocsp);
848 ssl_ocsp_task_ctx.cur_ocsp = NULL;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100849}
850
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +0100851static inline void ssl_ocsp_set_next_update(struct certificate_ocsp *ocsp)
852{
853 int update_margin = (ocsp->expire >= SSL_OCSP_UPDATE_MARGIN) ? SSL_OCSP_UPDATE_MARGIN : 0;
854
855 ocsp->next_update.key = MIN(now.tv_sec + SSL_OCSP_UPDATE_DELAY_MAX,
856 ocsp->expire - update_margin);
857
858 /* An already existing valid OCSP response that expires within less than
859 * SSL_OCSP_UPDATE_DELAY_MIN or has no 'Next Update' field should not be
860 * updated more than once every 5 minutes in order to avoid continuous
861 * update of the same response. */
862 if (b_data(&ocsp->response))
863 ocsp->next_update.key = MAX(ocsp->next_update.key,
864 now.tv_sec + SSL_OCSP_UPDATE_DELAY_MIN);
865}
866
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100867/*
868 * Insert a certificate_ocsp structure into the ocsp_update_tree tree, in which
869 * entries are sorted by absolute date of the next update. The next_update key
870 * will be the smallest out of the actual expire value of the response and
871 * now+1H. This arbitrary 1H value ensures that ocsp responses are updated
872 * periodically even when they have a long expire time, while not overloading
873 * the system too much (in theory). Likewise, a minimum 5 minutes interval is
874 * defined in order to avoid updating too often responses that have a really
875 * short expire time or even no 'Next Update' at all.
876 */
877int ssl_ocsp_update_insert(struct certificate_ocsp *ocsp)
878{
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +0100879 /* Set next_update based on current time and the various OCSP
880 * minimum/maximum update times.
881 */
882 ssl_ocsp_set_next_update(ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100883
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +0100884 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
885 eb64_insert(&ocsp_update_tree, &ocsp->next_update);
886 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100887
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +0100888 return 0;
889}
890
891/*
892 * Reinsert an entry in the update tree. The entry's next update time can not
893 * occur before now+SSL_OCSP_HTTP_ERR_REPLAY.
894 * This is supposed to be used in case of http error (ocsp responder unreachable
895 * for instance). This ensures that the entry does not get reinserted at the
896 * beginning of the tree every time.
897 */
898int ssl_ocsp_update_insert_after_error(struct certificate_ocsp *ocsp)
899{
900 /* Set next_update based on current time and the various OCSP
901 * minimum/maximum update times.
902 */
903 ssl_ocsp_set_next_update(ocsp);
904
905 if (ocsp->next_update.key < now.tv_sec + SSL_OCSP_HTTP_ERR_REPLAY)
906 ocsp->next_update.key = now.tv_sec + SSL_OCSP_HTTP_ERR_REPLAY;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100907
908 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
909 eb64_insert(&ocsp_update_tree, &ocsp->next_update);
910 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
911
912 return 0;
913}
914
915void ocsp_update_response_stline_cb(struct httpclient *hc)
916{
917 struct task *task = hc->caller;
918
919 if (!task)
920 return;
921
922 ssl_ocsp_task_ctx.flags |= HC_F_RES_STLINE;
923 task_wakeup(task, TASK_WOKEN_MSG);
924}
925
926void ocsp_update_response_headers_cb(struct httpclient *hc)
927{
928 struct task *task = hc->caller;
929
930 if (!task)
931 return;
932
933 ssl_ocsp_task_ctx.flags |= HC_F_RES_HDR;
934 task_wakeup(task, TASK_WOKEN_MSG);
935}
936
937void ocsp_update_response_body_cb(struct httpclient *hc)
938{
939 struct task *task = hc->caller;
940
941 if (!task)
942 return;
943
944 ssl_ocsp_task_ctx.flags |= HC_F_RES_BODY;
945 task_wakeup(task, TASK_WOKEN_MSG);
946}
947
948void ocsp_update_response_end_cb(struct httpclient *hc)
949{
950 struct task *task = hc->caller;
951
952 if (!task)
953 return;
954
955 ssl_ocsp_task_ctx.flags |= HC_F_RES_END;
956 task_wakeup(task, TASK_WOKEN_MSG);
957}
958
959/*
960 * This is the main function of the ocsp auto update mechanism. It has two
961 * distinct parts and the branching to one or the other is completely based on
962 * the fact that the cur_ocsp pointer of the ssl_ocsp_task_ctx member is set.
963 *
964 * If the pointer is not set, we need to look at the first item of the update
965 * tree and see if it needs to be updated. If it does not we simply wait until
966 * the time is right and let the task asleep. If it does need to be updated, we
967 * simply build and send the corresponding ocsp request thanks to the
968 * http_client. The task is then sent to sleep with an expire time set to
969 * infinity. The http_client will wake it back up once the response is received
970 * (or a timeout occurs). Just note that during this whole process the
971 * cetificate_ocsp object corresponding to the entry being updated is taken out
972 * of the update tree and only stored in the ssl_ocsp_task_ctx context.
973 *
974 * Once the task is waken up by the http_client, it branches on the response
975 * processing part of the function which basically checks that the response is
976 * valid and inserts it into the ocsp_response tree. The task then goes back to
977 * sleep until another entry needs to be updated.
978 */
979static struct task *ssl_ocsp_update_responses(struct task *task, void *context, unsigned int state)
980{
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +0100981 unsigned int next_wakeup = 0;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100982 struct eb64_node *eb;
983 struct certificate_ocsp *ocsp;
984 struct httpclient *hc = NULL;
985 struct buffer *req_url = NULL;
986 struct buffer *req_body = NULL;
987 OCSP_CERTID *certid = NULL;
988 struct ssl_ocsp_task_ctx *ctx = &ssl_ocsp_task_ctx;
989
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100990 if (ctx->cur_ocsp) {
991 /* An update is in process */
992 ocsp = ctx->cur_ocsp;
993 hc = ctx->hc;
994 if (ctx->flags & HC_F_RES_STLINE) {
995 if (hc->res.status != 200) {
996 goto http_error;
997 }
998 ctx->flags &= ~HC_F_RES_STLINE;
999 }
1000
1001 if (ctx->flags & HC_F_RES_HDR) {
1002 struct http_hdr *hdr;
1003 int found = 0;
1004 /* Look for "Content-Type" header which should have
1005 * "application/ocsp-response" value. */
1006 for (hdr = hc->res.hdrs; isttest(hdr->v); hdr++) {
1007 if (isteqi(hdr->n, ist("Content-Type")) &&
1008 isteqi(hdr->v, ist("application/ocsp-response"))) {
1009 found = 1;
1010 break;
1011 }
1012 }
1013 if (!found) {
1014 goto http_error;
1015 }
1016 ctx->flags &= ~HC_F_RES_HDR;
1017 }
1018
1019 /* If the HC_F_RES_BODY is set, we still need for the
1020 * HC_F_RES_END flag to be set as well in order to be sure that
1021 * the body is complete. */
1022
1023 /* we must close only if F_RES_END is the last flag */
1024 if (ctx->flags & HC_F_RES_END) {
1025
1026 /* Process the body that must be complete since
1027 * HC_F_RES_END is set. */
1028 if (ctx->flags & HC_F_RES_BODY) {
1029 if (ssl_ocsp_check_response(ocsp->chain, ocsp->issuer, &hc->res.buf, NULL))
1030 goto http_error;
1031
1032 if (ssl_sock_update_ocsp_response(&hc->res.buf, NULL) != 0) {
1033 goto http_error;
1034 }
1035
1036 ctx->flags &= ~HC_F_RES_BODY;
1037 }
1038
1039 ctx->flags &= ~HC_F_RES_END;
1040
1041 /* Reinsert the entry into the update list so that it can be updated later */
1042 ssl_ocsp_update_insert(ocsp);
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001043 /* Release the reference kept on the updated ocsp response. */
1044 ssl_sock_free_ocsp(ctx->cur_ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001045 ctx->cur_ocsp = NULL;
1046
1047 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1048 /* Set next_wakeup to the new first entry of the tree */
1049 eb = eb64_first(&ocsp_update_tree);
1050 if (eb) {
1051 if (eb->key > now.tv_sec)
1052 next_wakeup = (eb->key - now.tv_sec)*1000;
1053 else
1054 next_wakeup = 0;
1055 }
1056 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1057 goto leave;
1058 }
1059
1060 /* We did not receive the HC_F_RES_END flag yet, wait for it
1061 * before trying to update a new ocsp response. */
1062 goto wait;
1063 } else {
1064 /* Look for next entry that needs to be updated. */
1065 const unsigned char *p = NULL;
1066
1067 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1068
1069 eb = eb64_first(&ocsp_update_tree);
1070 if (!eb) {
1071 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Breton1c647ad2023-01-12 09:49:10 +01001072 goto wait;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001073 }
1074
1075 if (eb->key > now.tv_sec) {
1076 next_wakeup = (eb->key - now.tv_sec)*1000;
1077 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1078 goto leave;
1079 }
1080
1081 ocsp = eb64_entry(eb, struct certificate_ocsp, next_update);
1082
1083 /* Take the current entry out of the update tree, it will be
1084 * reinserted after the response is processed. */
1085 eb64_delete(&ocsp->next_update);
1086
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001087 ++ocsp->refcount;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001088 ctx->cur_ocsp = ocsp;
1089
1090 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1091
1092 req_url = alloc_trash_chunk();
1093 if (!req_url) {
1094 goto leave;
1095 }
1096 req_body = alloc_trash_chunk();
1097 if (!req_body) {
1098 goto leave;
1099 }
1100
1101 p = ocsp->key_data;
1102
1103 d2i_OCSP_CERTID(&certid, &p, ocsp->key_length);
1104 if (!certid)
1105 goto leave;
1106
1107 /* Copy OCSP URI stored in ocsp structure into req_url */
1108 chunk_cpy(req_url, ocsp->uri);
1109
1110 /* Create ocsp request */
1111 if (ssl_ocsp_create_request_details(certid, req_url, req_body, NULL) != 0) {
1112 goto leave;
1113 }
1114
1115 /* Depending on the processing that occurred in
1116 * ssl_ocsp_create_request_details we could either have to send
1117 * a GET or a POST request. */
1118 hc = httpclient_new(task, b_data(req_body) ? HTTP_METH_POST : HTTP_METH_GET, ist2(b_orig(req_url), b_data(req_url)));
1119 if (!hc) {
1120 goto leave;
1121 }
1122
1123 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth,
1124 b_data(req_body) ? ocsp_request_hdrs : NULL,
William Lallemand70601c52022-12-22 14:34:01 +01001125 b_data(req_body) ? ist2(b_orig(req_body), b_data(req_body)) : IST_NULL) != ERR_NONE) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001126 goto leave;
1127 }
1128
1129 hc->ops.res_stline = ocsp_update_response_stline_cb;
1130 hc->ops.res_headers = ocsp_update_response_headers_cb;
1131 hc->ops.res_payload = ocsp_update_response_body_cb;
1132 hc->ops.res_end = ocsp_update_response_end_cb;
1133
1134 if (!httpclient_start(hc)) {
1135 goto leave;
1136 }
1137
1138 ctx->flags = 0;
1139 ctx->hc = hc;
1140
1141 /* We keep the lock, this indicates that an update is in process. */
1142 goto wait;
1143 }
1144
1145leave:
1146 if (ctx->cur_ocsp) {
1147 /* Something went wrong, reinsert the entry in the tree. */
1148 ssl_ocsp_update_insert(ctx->cur_ocsp);
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001149 /* Release the reference kept on the updated ocsp response. */
1150 ssl_sock_free_ocsp(ctx->cur_ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001151 ctx->cur_ocsp = NULL;
1152 }
1153 if (hc)
1154 httpclient_stop_and_destroy(hc);
1155 free_trash_chunk(req_url);
1156 free_trash_chunk(req_body);
1157 task->expire = tick_add(now_ms, next_wakeup);
1158 return task;
1159
1160wait:
1161 free_trash_chunk(req_url);
1162 free_trash_chunk(req_body);
1163 task->expire = TICK_ETERNITY;
1164 return task;
1165
1166http_error:
1167 /* Reinsert certificate into update list so that it can be updated later */
1168 if (ocsp)
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +01001169 ssl_ocsp_update_insert_after_error(ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001170
1171 if (hc)
1172 httpclient_stop_and_destroy(hc);
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001173 /* Release the reference kept on the updated ocsp response. */
1174 ssl_sock_free_ocsp(ctx->cur_ocsp);
Remi Tricot-Le Breton10f113e2023-01-12 09:49:11 +01001175 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1176 /* Set next_wakeup to the new first entry of the tree */
1177 eb = eb64_first(&ocsp_update_tree);
1178 if (eb) {
1179 if (eb->key > now.tv_sec)
1180 next_wakeup = (eb->key - now.tv_sec)*1000;
1181 else
1182 next_wakeup = 0;
1183 }
1184 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001185 ctx->cur_ocsp = NULL;
1186 ctx->hc = NULL;
1187 ctx->flags = 0;
1188 task->expire = tick_add(now_ms, next_wakeup);
1189 return task;
1190}
1191
1192
1193
1194
1195struct ocsp_cli_ctx {
1196 struct httpclient *hc;
1197 struct ckch_data *ckch_data;
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001198 X509 *ocsp_issuer;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001199 uint flags;
1200 uint do_update;
1201};
1202
1203
1204void cli_ocsp_res_stline_cb(struct httpclient *hc)
1205{
1206 struct appctx *appctx = hc->caller;
1207 struct ocsp_cli_ctx *ctx;
1208
1209 if (!appctx)
1210 return;
1211
1212 ctx = appctx->svcctx;
1213 ctx->flags |= HC_F_RES_STLINE;
1214 appctx_wakeup(appctx);
1215}
1216
1217void cli_ocsp_res_headers_cb(struct httpclient *hc)
1218{
1219 struct appctx *appctx = hc->caller;
1220 struct ocsp_cli_ctx *ctx;
1221
1222 if (!appctx)
1223 return;
1224
1225 ctx = appctx->svcctx;
1226 ctx->flags |= HC_F_RES_HDR;
1227 appctx_wakeup(appctx);
1228}
1229
1230void cli_ocsp_res_body_cb(struct httpclient *hc)
1231{
1232 struct appctx *appctx = hc->caller;
1233 struct ocsp_cli_ctx *ctx;
1234
1235 if (!appctx)
1236 return;
1237
1238 ctx = appctx->svcctx;
1239 ctx->flags |= HC_F_RES_BODY;
1240 appctx_wakeup(appctx);
1241}
1242
1243void cli_ocsp_res_end_cb(struct httpclient *hc)
1244{
1245 struct appctx *appctx = hc->caller;
1246 struct ocsp_cli_ctx *ctx;
1247
1248 if (!appctx)
1249 return;
1250
1251 ctx = appctx->svcctx;
1252 ctx->flags |= HC_F_RES_END;
1253 appctx_wakeup(appctx);
1254}
1255
1256static int cli_parse_update_ocsp_response(char **args, char *payload, struct appctx *appctx, void *private)
1257{
1258 int errcode = 0;
1259 char *err = NULL;
1260 struct ckch_store *ckch_store = NULL;
1261 X509 *cert = NULL;
1262 struct ocsp_cli_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
1263 struct httpclient *hc = NULL;
1264 struct buffer *req_url = NULL;
1265 struct buffer *req_body = NULL;
1266 OCSP_CERTID *certid = NULL;
1267
1268 if (!*args[3]) {
1269 memprintf(&err, "'update ssl ocsp-response' expects a filename\n");
1270 return cli_dynerr(appctx, err);
1271 }
1272
1273 req_url = alloc_trash_chunk();
1274 if (!req_url) {
1275 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1276 errcode |= ERR_ALERT | ERR_FATAL;
1277 goto end;
1278 }
1279
1280 req_body = alloc_trash_chunk();
1281 if (!req_body) {
1282 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1283 errcode |= ERR_ALERT | ERR_FATAL;
1284 goto end;
1285 }
1286
1287 /* The operations on the CKCH architecture are locked so we can
1288 * manipulate ckch_store and ckch_inst */
1289 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) {
1290 memprintf(&err, "%sCan't update the certificate!\nOperations on certificates are currently locked!\n", err ? err : "");
1291 errcode |= ERR_ALERT | ERR_FATAL;
1292 goto end;
1293 }
1294
1295 ckch_store = ckchs_lookup(args[3]);
1296
1297 if (!ckch_store) {
Remi Tricot-Le Breton14419eb2023-01-09 12:02:49 +01001298 memprintf(&err, "%sUnknown certificate! 'update ssl ocsp-response' expects an already known certificate file name.\n", err ? err : "");
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001299 errcode |= ERR_ALERT | ERR_FATAL;
1300 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1301 goto end;
1302 }
1303
1304 ctx->ckch_data = ckch_store->data;
1305
1306 cert = ckch_store->data->cert;
1307
1308 if (ssl_ocsp_get_uri_from_cert(cert, req_url, &err)) {
1309 errcode |= ERR_ALERT | ERR_FATAL;
1310 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1311 goto end;
1312 }
1313
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001314 /* Look for the ocsp issuer in the ckch_data or in the certificate
1315 * chain, the same way it is done in ssl_sock_load_ocsp. */
1316 ctx->ocsp_issuer = ctx->ckch_data->ocsp_issuer;
1317
1318 /* take issuer from chain over ocsp_issuer, is what is done historicaly */
1319 if (ctx->ckch_data->chain) {
1320 int i = 0;
1321 /* check if one of the certificate of the chain is the issuer */
1322 for (i = 0; i < sk_X509_num(ctx->ckch_data->chain); i++) {
1323 X509 *ti = sk_X509_value(ctx->ckch_data->chain, i);
1324 if (X509_check_issued(ti, cert) == X509_V_OK) {
1325 ctx->ocsp_issuer = ti;
1326 break;
1327 }
1328 }
1329 }
1330
1331 if (!ctx->ocsp_issuer) {
1332 memprintf(&err, "%sOCSP issuer not found\n", err ? err : "");
1333 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1334 goto end;
1335 }
1336
1337 X509_up_ref(ctx->ocsp_issuer);
1338
1339 certid = OCSP_cert_to_id(NULL, cert, ctx->ocsp_issuer);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001340 if (certid == NULL) {
1341 memprintf(&err, "%sOCSP_cert_to_id() error\n", err ? err : "");
1342 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1343 goto end;
1344 }
1345
1346 /* From here on the lock is not needed anymore. */
1347 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1348
1349 /* Create ocsp request */
1350 if (ssl_ocsp_create_request_details(certid, req_url, req_body, &err) != 0) {
1351 memprintf(&err, "%sCreate ocsp request error\n", err ? err : "");
1352 goto end;
1353 }
1354
1355 hc = httpclient_new(appctx, b_data(req_body) ? HTTP_METH_POST : HTTP_METH_GET, ist2(b_orig(req_url), b_data(req_url)));
1356 if (!hc) {
1357 memprintf(&err, "%sCan't allocate httpclient\n", err ? err : "");
1358 goto end;
1359 }
1360
1361 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, b_data(req_body) ? ocsp_request_hdrs : NULL,
1362 ist2(b_orig(req_body), b_data(req_body))) != ERR_NONE) {
1363 memprintf(&err, "%shttpclient_req_gen() error\n", err ? err : "");
1364 goto end;
1365 }
1366
1367 hc->ops.res_stline = cli_ocsp_res_stline_cb;
1368 hc->ops.res_headers = cli_ocsp_res_headers_cb;
1369 hc->ops.res_payload = cli_ocsp_res_body_cb;
1370 hc->ops.res_end = cli_ocsp_res_end_cb;
1371
1372 ctx->hc = hc; /* store the httpclient ptr in the applet */
1373 ctx->flags = 0;
1374
1375 if (!httpclient_start(hc)) {
1376 memprintf(&err, "%shttpclient_start() error\n", err ? err : "");
1377 goto end;
1378 }
1379
1380 free_trash_chunk(req_url);
Remi Tricot-Le Breton305a4f32023-01-23 15:57:13 +01001381 free_trash_chunk(req_body);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001382
1383 return 0;
1384
1385end:
1386 free_trash_chunk(req_url);
Remi Tricot-Le Breton305a4f32023-01-23 15:57:13 +01001387 free_trash_chunk(req_body);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001388
1389 if (errcode & ERR_CODE) {
1390 return cli_dynerr(appctx, memprintf(&err, "%sCan't send ocsp request for %s!\n", err ? err : "", args[3]));
1391 }
1392 return cli_dynmsg(appctx, LOG_NOTICE, err);
1393}
1394
1395static int cli_io_handler_update_ocsp_response(struct appctx *appctx)
1396{
1397 struct ocsp_cli_ctx *ctx = appctx->svcctx;
1398 struct httpclient *hc = ctx->hc;
1399
1400 if (ctx->flags & HC_F_RES_STLINE) {
1401 if (hc->res.status != 200) {
1402 chunk_printf(&trash, "OCSP response error (status %d)\n", hc->res.status);
1403 if (applet_putchk(appctx, &trash) == -1)
1404 goto more;
1405 goto end;
1406 }
1407 ctx->flags &= ~HC_F_RES_STLINE;
1408 }
1409
1410 if (ctx->flags & HC_F_RES_HDR) {
1411 struct http_hdr *hdr;
1412 int found = 0;
1413 /* Look for "Content-Type" header which should have
1414 * "application/ocsp-response" value. */
1415 for (hdr = hc->res.hdrs; isttest(hdr->v); hdr++) {
1416 if (isteqi(hdr->n, ist("Content-Type")) &&
1417 isteqi(hdr->v, ist("application/ocsp-response"))) {
1418 found = 1;
1419 break;
1420 }
1421 }
1422 if (!found) {
Remi Tricot-Le Breton083b2302023-01-23 15:57:14 +01001423 chunk_printf(&trash, "Missing 'Content-Type: application/ocsp-response' header\n");
1424 if (applet_putchk(appctx, &trash) == -1)
1425 goto more;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001426 goto end;
1427 }
1428 ctx->flags &= ~HC_F_RES_HDR;
1429 }
1430
1431 if (ctx->flags & HC_F_RES_BODY) {
1432 /* Wait until the full body is received and HC_F_RES_END flag is
1433 * set. */
1434 }
1435
1436 /* we must close only if F_END is the last flag */
1437 if (ctx->flags & HC_F_RES_END) {
1438 char *err = NULL;
1439
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001440 if (ssl_ocsp_check_response(ctx->ckch_data->chain, ctx->ocsp_issuer, &hc->res.buf, &err)) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001441 chunk_printf(&trash, "%s", err);
Remi Tricot-Le Breton305a4f32023-01-23 15:57:13 +01001442 free(err);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001443 if (applet_putchk(appctx, &trash) == -1)
1444 goto more;
1445 goto end;
1446 }
1447
1448 if (ssl_sock_update_ocsp_response(&hc->res.buf, &err) != 0) {
1449 chunk_printf(&trash, "%s", err);
Remi Tricot-Le Breton305a4f32023-01-23 15:57:13 +01001450 free(err);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001451 if (applet_putchk(appctx, &trash) == -1)
1452 goto more;
1453 goto end;
1454 }
1455
Remi Tricot-Le Breton305a4f32023-01-23 15:57:13 +01001456 free(err);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001457 chunk_reset(&trash);
1458
1459 if (ssl_ocsp_response_print(&hc->res.buf, &trash))
1460 goto end;
1461
1462 if (applet_putchk(appctx, &trash) == -1)
1463 goto more;
1464 ctx->flags &= ~HC_F_RES_BODY;
1465 ctx->flags &= ~HC_F_RES_END;
1466 goto end;
1467 }
1468
1469more:
1470 if (!ctx->flags)
1471 applet_have_no_more_data(appctx);
1472 return 0;
1473end:
1474 return 1;
1475}
1476
1477static void cli_release_update_ocsp_response(struct appctx *appctx)
1478{
1479 struct ocsp_cli_ctx *ctx = appctx->svcctx;
1480 struct httpclient *hc = ctx->hc;
1481
Remi Tricot-Le Breton71237a12023-01-10 11:21:40 +01001482 X509_free(ctx->ocsp_issuer);
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001483
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001484 /* Everything possible was printed on the CLI, we can destroy the client */
1485 httpclient_stop_and_destroy(hc);
1486
1487 return;
1488}
1489
1490
1491#endif /* !defined OPENSSL_IS_BORINGSSL */
1492
1493
1494#endif /* (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) */
1495
1496
1497static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
1498{
1499#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
1500 char *err = NULL;
1501 int i, j, ret;
1502
1503 if (!payload)
1504 payload = args[3];
1505
1506 /* Expect one parameter: the new response in base64 encoding */
1507 if (!*payload)
1508 return cli_err(appctx, "'set ssl ocsp-response' expects response in base64 encoding.\n");
1509
1510 /* remove \r and \n from the payload */
1511 for (i = 0, j = 0; payload[i]; i++) {
1512 if (payload[i] == '\r' || payload[i] == '\n')
1513 continue;
1514 payload[j++] = payload[i];
1515 }
1516 payload[j] = 0;
1517
1518 ret = base64dec(payload, j, trash.area, trash.size);
1519 if (ret < 0)
1520 return cli_err(appctx, "'set ssl ocsp-response' received invalid base64 encoded response.\n");
1521
1522 trash.data = ret;
1523 if (ssl_sock_update_ocsp_response(&trash, &err)) {
1524 if (err)
1525 return cli_dynerr(appctx, memprintf(&err, "%s.\n", err));
1526 else
1527 return cli_err(appctx, "Failed to update OCSP response.\n");
1528 }
1529
1530 return cli_msg(appctx, LOG_INFO, "OCSP Response updated!\n");
1531#else
1532 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1533#endif
1534
1535}
1536
1537/* parsing function for 'show ssl ocsp-response [id]'. If an entry is forced,
1538 * it's set into appctx->svcctx.
1539 */
1540static int cli_parse_show_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
1541{
1542#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
1543 if (*args[3]) {
1544 struct certificate_ocsp *ocsp = NULL;
1545 char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
1546 int key_length = OCSP_MAX_CERTID_ASN1_LENGTH;
1547 char *key_ptr = key;
1548
1549 if (strlen(args[3]) > OCSP_MAX_CERTID_ASN1_LENGTH*2) {
1550 return cli_err(appctx, "'show ssl ocsp-response' received a too big key.\n");
1551 }
1552
1553 if (!parse_binary(args[3], &key_ptr, &key_length, NULL)) {
1554 return cli_err(appctx, "'show ssl ocsp-response' received an invalid key.\n");
1555 }
1556
1557 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1558 ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, key, OCSP_MAX_CERTID_ASN1_LENGTH);
1559
1560 if (!ocsp) {
1561 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1562 return cli_err(appctx, "Certificate ID does not match any certificate.\n");
1563 }
1564 ++ocsp->refcount;
1565 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1566
1567 appctx->svcctx = ocsp;
1568 appctx->io_handler = cli_io_handler_show_ocspresponse_detail;
1569 }
1570
1571 return 0;
1572
1573#else
1574 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1575#endif
1576}
1577
1578/*
1579 * IO handler of "show ssl ocsp-response". The command taking a specific ID
1580 * is managed in cli_io_handler_show_ocspresponse_detail.
1581 * The current entry is taken from appctx->svcctx.
1582 */
1583static int cli_io_handler_show_ocspresponse(struct appctx *appctx)
1584{
1585#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
1586 struct buffer *trash = alloc_trash_chunk();
1587 struct buffer *tmp = NULL;
1588 struct ebmb_node *node;
1589 struct certificate_ocsp *ocsp = NULL;
1590 BIO *bio = NULL;
1591 int write = -1;
1592
1593 if (trash == NULL)
1594 return 1;
1595
1596 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1597
1598 tmp = alloc_trash_chunk();
1599 if (!tmp)
1600 goto end;
1601
1602 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1603 goto end;
1604
1605 if (!appctx->svcctx) {
1606 chunk_appendf(trash, "# Certificate IDs\n");
1607 node = ebmb_first(&cert_ocsp_tree);
1608 } else {
1609 node = &((struct certificate_ocsp *)appctx->svcctx)->key;
1610 }
1611
1612 while (node) {
1613 OCSP_CERTID *certid = NULL;
1614 const unsigned char *p = NULL;
1615 int i;
1616
1617 ocsp = ebmb_entry(node, struct certificate_ocsp, key);
1618
1619 /* Dump the key in hexadecimal */
1620 chunk_appendf(trash, "Certificate ID key : ");
1621 for (i = 0; i < ocsp->key_length; ++i) {
1622 chunk_appendf(trash, "%02x", ocsp->key_data[i]);
1623 }
1624 chunk_appendf(trash, "\n");
1625
1626 p = ocsp->key_data;
1627
1628 /* Decode the certificate ID (serialized into the key). */
1629 d2i_OCSP_CERTID(&certid, &p, ocsp->key_length);
1630 if (!certid)
1631 goto end;
1632
1633 /* Dump the CERTID info */
1634 ocsp_certid_print(bio, certid, 1);
1635 OCSP_CERTID_free(certid);
1636 write = BIO_read(bio, tmp->area, tmp->size-1);
1637 /* strip trailing LFs */
1638 while (write > 0 && tmp->area[write-1] == '\n')
1639 write--;
1640 tmp->area[write] = '\0';
1641
1642 chunk_appendf(trash, "%s\n", tmp->area);
1643
1644 node = ebmb_next(node);
1645 if (applet_putchk(appctx, trash) == -1)
1646 goto yield;
1647 }
1648
1649end:
1650 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1651 appctx->svcctx = NULL;
1652 free_trash_chunk(trash);
1653 free_trash_chunk(tmp);
1654 BIO_free(bio);
1655 return 1;
1656
1657yield:
1658 free_trash_chunk(trash);
1659 free_trash_chunk(tmp);
1660 BIO_free(bio);
1661
1662 ++ocsp->refcount;
1663 appctx->svcctx = ocsp;
1664 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1665 return 0;
1666#else
1667 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1668#endif
1669}
1670
1671
1672static struct cli_kw_list cli_kws = {{ },{
1673 { { "set", "ssl", "ocsp-response", NULL }, "set ssl ocsp-response <resp|payload> : update a certificate's OCSP Response from a base64-encode DER", cli_parse_set_ocspresponse, NULL },
1674
1675 { { "show", "ssl", "ocsp-response", NULL },"show ssl ocsp-response [id] : display the IDs of the OCSP responses used in memory, or the details of a single OCSP response", cli_parse_show_ocspresponse, cli_io_handler_show_ocspresponse, NULL },
1676#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
1677 { { "update", "ssl", "ocsp-response", NULL }, "update ssl ocsp-response <certfile> : send ocsp request and update stored ocsp response", cli_parse_update_ocsp_response, cli_io_handler_update_ocsp_response, cli_release_update_ocsp_response },
1678#endif
1679 { { NULL }, NULL, NULL, NULL }
1680}};
1681
1682INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1683
1684/*
1685 * Local variables:
1686 * c-indent-level: 8
1687 * c-basic-offset: 8
1688 * End:
1689 */