blob: 53afaae8685502440732a21f1e61aadd654e3157 [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 */
189
190/* This function starts to check if the OCSP response (in DER format) contained
191 * in chunk 'ocsp_response' is valid (else exits on error).
192 * If 'cid' is not NULL, it will be compared to the OCSP certificate ID
193 * contained in the OCSP Response and exits on error if no match.
194 * If it's a valid OCSP Response:
195 * If 'ocsp' is not NULL, the chunk is copied in the OCSP response's container
196 * pointed by 'ocsp'.
197 * If 'ocsp' is NULL, the function looks up into the OCSP response's
198 * containers tree (using as index the ASN1 form of the OCSP Certificate ID extracted
199 * from the response) and exits on error if not found. Finally, If an OCSP response is
200 * already present in the container, it will be overwritten.
201 *
202 * Note: OCSP response containing more than one OCSP Single response is not
203 * considered valid.
204 *
205 * Returns 0 on success, 1 in error case.
206 */
207int ssl_sock_load_ocsp_response(struct buffer *ocsp_response,
208 struct certificate_ocsp *ocsp,
209 OCSP_CERTID *cid, char **err)
210{
211 OCSP_RESPONSE *resp;
212 OCSP_BASICRESP *bs = NULL;
213 OCSP_SINGLERESP *sr;
214 OCSP_CERTID *id;
215 unsigned char *p = (unsigned char *) ocsp_response->area;
216 int rc , count_sr;
217 ASN1_GENERALIZEDTIME *revtime, *thisupd, *nextupd = NULL;
218 int reason;
219 int ret = 1;
220#ifdef HAVE_ASN1_TIME_TO_TM
221 struct tm nextupd_tm = {0};
222#endif
223
224 resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p,
225 ocsp_response->data);
226 if (!resp) {
227 memprintf(err, "Unable to parse OCSP response");
228 goto out;
229 }
230
231 rc = OCSP_response_status(resp);
232 if (rc != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
233 memprintf(err, "OCSP response status not successful");
234 goto out;
235 }
236
237 bs = OCSP_response_get1_basic(resp);
238 if (!bs) {
239 memprintf(err, "Failed to get basic response from OCSP Response");
240 goto out;
241 }
242
243 count_sr = OCSP_resp_count(bs);
244 if (count_sr > 1) {
245 memprintf(err, "OCSP response ignored because contains multiple single responses (%d)", count_sr);
246 goto out;
247 }
248
249 sr = OCSP_resp_get0(bs, 0);
250 if (!sr) {
251 memprintf(err, "Failed to get OCSP single response");
252 goto out;
253 }
254
255 id = (OCSP_CERTID*)OCSP_SINGLERESP_get0_id(sr);
256
257 rc = OCSP_single_get0_status(sr, &reason, &revtime, &thisupd, &nextupd);
258 if (rc != V_OCSP_CERTSTATUS_GOOD && rc != V_OCSP_CERTSTATUS_REVOKED) {
259 memprintf(err, "OCSP single response: certificate status is unknown");
260 goto out;
261 }
262
263 if (!nextupd) {
264 memprintf(err, "OCSP single response: missing nextupdate");
265 goto out;
266 }
267
268 rc = OCSP_check_validity(thisupd, nextupd, OCSP_MAX_RESPONSE_TIME_SKEW, -1);
269 if (!rc) {
270 memprintf(err, "OCSP single response: no longer valid.");
271 goto out;
272 }
273
274 if (cid) {
275 if (OCSP_id_cmp(id, cid)) {
276 memprintf(err, "OCSP single response: Certificate ID does not match certificate and issuer");
277 goto out;
278 }
279 }
280
281 if (!ocsp) {
282 unsigned char key[OCSP_MAX_CERTID_ASN1_LENGTH];
283 unsigned char *p;
284
285 rc = i2d_OCSP_CERTID(id, NULL);
286 if (!rc) {
287 memprintf(err, "OCSP single response: Unable to encode Certificate ID");
288 goto out;
289 }
290
291 if (rc > OCSP_MAX_CERTID_ASN1_LENGTH) {
292 memprintf(err, "OCSP single response: Certificate ID too long");
293 goto out;
294 }
295
296 p = key;
297 memset(key, 0, OCSP_MAX_CERTID_ASN1_LENGTH);
298 i2d_OCSP_CERTID(id, &p);
299 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
300 ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, key, OCSP_MAX_CERTID_ASN1_LENGTH);
301 if (!ocsp) {
302 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
303 memprintf(err, "OCSP single response: Certificate ID does not match any certificate or issuer");
304 goto out;
305 }
306 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
307 }
308
309 /* According to comments on "chunk_dup", the
310 previous chunk buffer will be freed */
311 if (!chunk_dup(&ocsp->response, ocsp_response)) {
312 memprintf(err, "OCSP response: Memory allocation error");
313 goto out;
314 }
315
316#ifdef HAVE_ASN1_TIME_TO_TM
317 if (ASN1_TIME_to_tm(nextupd, &nextupd_tm) == 0) {
318 memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
319 goto out;
320 }
321 ocsp->expire = my_timegm(&nextupd_tm) - OCSP_MAX_RESPONSE_TIME_SKEW;
322#else
323 ocsp->expire = asn1_generalizedtime_to_epoch(nextupd) - OCSP_MAX_RESPONSE_TIME_SKEW;
324 if (ocsp->expire < 0) {
325 memprintf(err, "OCSP single response: Invalid \"Next Update\" time");
326 goto out;
327 }
328#endif
329
330 ret = 0;
331out:
332 ERR_clear_error();
333
334 if (bs)
335 OCSP_BASICRESP_free(bs);
336
337 if (resp)
338 OCSP_RESPONSE_free(resp);
339
340 return ret;
341}
342/*
343 * External function use to update the OCSP response in the OCSP response's
344 * containers tree. The chunk 'ocsp_response' must contain the OCSP response
345 * to update in DER format.
346 *
347 * Returns 0 on success, 1 in error case.
348 */
349int ssl_sock_update_ocsp_response(struct buffer *ocsp_response, char **err)
350{
351 return ssl_sock_load_ocsp_response(ocsp_response, NULL, NULL, err);
352}
353
354
355
356#if !defined OPENSSL_IS_BORINGSSL
357/*
358 * Decrease the refcount of the struct ocsp_response and frees it if it's not
359 * used anymore. Also removes it from the tree if free'd.
360 */
361void ssl_sock_free_ocsp(struct certificate_ocsp *ocsp)
362{
363 if (!ocsp)
364 return;
365
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +0100366 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100367 ocsp->refcount--;
368 if (ocsp->refcount <= 0) {
369 ebmb_delete(&ocsp->key);
370 eb64_delete(&ocsp->next_update);
371 X509_free(ocsp->issuer);
372 ocsp->issuer = NULL;
373 sk_X509_pop_free(ocsp->chain, X509_free);
374 ocsp->chain = NULL;
375 chunk_destroy(&ocsp->response);
Remi Tricot-Le Breton648c83e2023-01-09 12:02:48 +0100376 if (ocsp->uri) {
377 ha_free(&ocsp->uri->area);
378 ha_free(&ocsp->uri);
379 }
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100380
381 free(ocsp);
382 }
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +0100383 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100384}
385
386
387/*
388 * This function dumps the details of an OCSP_CERTID. It is based on
389 * ocsp_certid_print in OpenSSL.
390 */
391static inline int ocsp_certid_print(BIO *bp, OCSP_CERTID *certid, int indent)
392{
393 ASN1_OCTET_STRING *piNameHash = NULL;
394 ASN1_OCTET_STRING *piKeyHash = NULL;
395 ASN1_INTEGER *pSerial = NULL;
396
397 if (OCSP_id_get0_info(&piNameHash, NULL, &piKeyHash, &pSerial, certid)) {
398
399 BIO_printf(bp, "%*sCertificate ID:\n", indent, "");
400 indent += 2;
401 BIO_printf(bp, "%*sIssuer Name Hash: ", indent, "");
402#ifndef USE_OPENSSL_WOLFSSL
403 i2a_ASN1_STRING(bp, piNameHash, 0);
404#else
405 wolfSSL_ASN1_STRING_print(bp, piNameHash);
406#endif
407 BIO_printf(bp, "\n%*sIssuer Key Hash: ", indent, "");
408#ifndef USE_OPENSSL_WOLFSSL
409 i2a_ASN1_STRING(bp, piKeyHash, 0);
410#else
411 wolfSSL_ASN1_STRING_print(bp, piNameHash);
412#endif
413 BIO_printf(bp, "\n%*sSerial Number: ", indent, "");
414 i2a_ASN1_INTEGER(bp, pSerial);
415 }
416 return 1;
417}
418
419/*
420 * Dump the details about an OCSP response in DER format stored in
421 * <ocsp_response> into buffer <out>.
422 * Returns 0 in case of success.
423 */
424int ssl_ocsp_response_print(struct buffer *ocsp_response, struct buffer *out)
425{
426 BIO *bio = NULL;
427 int write = -1;
428 OCSP_RESPONSE *resp;
429 const unsigned char *p;
430 int retval = -1;
431
432 if (!ocsp_response)
433 return -1;
434
435 if ((bio = BIO_new(BIO_s_mem())) == NULL)
436 return -1;
437
438 p = (const unsigned char*)ocsp_response->area;
439
440 resp = d2i_OCSP_RESPONSE(NULL, &p, ocsp_response->data);
441 if (!resp) {
442 chunk_appendf(out, "Unable to parse OCSP response");
443 goto end;
444 }
445
446#ifndef USE_OPENSSL_WOLFSSL
447 if (OCSP_RESPONSE_print(bio, resp, 0) != 0) {
448#else
449 if (wolfSSL_d2i_OCSP_RESPONSE_bio(bio, &resp) != 0) {
450#endif
451 struct buffer *trash = get_trash_chunk();
452 struct ist ist_block = IST_NULL;
453 struct ist ist_double_lf = IST_NULL;
454 static struct ist double_lf = IST("\n\n");
455
456 write = BIO_read(bio, trash->area, trash->size - 1);
457 if (write <= 0)
458 goto end;
459 trash->data = write;
460
461 /* Look for empty lines in the 'trash' buffer and add a space to
462 * the beginning to avoid having empty lines in the output
463 * (without changing the appearance of the information
464 * displayed).
465 */
466 ist_block = ist2(b_orig(trash), b_data(trash));
467
468 ist_double_lf = istist(ist_block, double_lf);
469
470 while (istlen(ist_double_lf)) {
471 /* istptr(ist_double_lf) points to the first \n of a
472 * \n\n pattern.
473 */
474 uint empty_line_offset = istptr(ist_double_lf) + 1 - istptr(ist_block);
475
476 /* Write up to the first '\n' of the "\n\n" pattern into
477 * the output buffer.
478 */
479 b_putblk(out, istptr(ist_block), empty_line_offset);
480 /* Add an extra space. */
481 b_putchr(out, ' ');
482
483 /* Keep looking for empty lines in the rest of the data. */
484 ist_block = istadv(ist_block, empty_line_offset);
485
486 ist_double_lf = istist(ist_block, double_lf);
487 }
488
489 retval = (b_istput(out, ist_block) <= 0);
490 }
491
492end:
493 if (bio)
494 BIO_free(bio);
495
496 OCSP_RESPONSE_free(resp);
497
498 return retval;
499}
500
501/*
502 * Dump the details of the OCSP response of ID <ocsp_certid> into buffer <out>.
503 * Returns 0 in case of success.
504 */
505int ssl_get_ocspresponse_detail(unsigned char *ocsp_certid, struct buffer *out)
506{
507 struct certificate_ocsp *ocsp;
508 int ret = 0;
509
510 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
511 ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, ocsp_certid, OCSP_MAX_CERTID_ASN1_LENGTH);
512 if (!ocsp) {
513 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
514 return -1;
515 }
516
517 ret = ssl_ocsp_response_print(&ocsp->response, out);
518
519 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
520
521 return ret;
522}
523
524
525/* IO handler of details "show ssl ocsp-response <id>".
526 * The current entry is taken from appctx->svcctx.
527 */
528static int cli_io_handler_show_ocspresponse_detail(struct appctx *appctx)
529{
530 struct buffer *trash = alloc_trash_chunk();
531 struct certificate_ocsp *ocsp = appctx->svcctx;
532
533 if (trash == NULL)
534 return 1;
535
536 if (ssl_ocsp_response_print(&ocsp->response, trash)) {
537 free_trash_chunk(trash);
538 return 1;
539 }
540
541 if (applet_putchk(appctx, trash) == -1)
542 goto yield;
543
544 appctx->svcctx = NULL;
545 if (trash)
546 free_trash_chunk(trash);
547 return 1;
548
549yield:
550 if (trash)
551 free_trash_chunk(trash);
552
553 return 0;
554}
555
556void ssl_sock_ocsp_free_func(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
557{
558 struct ocsp_cbk_arg *ocsp_arg;
559
560 if (ptr) {
561 ocsp_arg = ptr;
562
563 if (ocsp_arg->is_single) {
564 ssl_sock_free_ocsp(ocsp_arg->s_ocsp);
565 ocsp_arg->s_ocsp = NULL;
566 } else {
567 int i;
568
569 for (i = 0; i < SSL_SOCK_NUM_KEYTYPES; i++) {
570 ssl_sock_free_ocsp(ocsp_arg->m_ocsp[i]);
571 ocsp_arg->m_ocsp[i] = NULL;
572 }
573 }
574 free(ocsp_arg);
575 }
576}
577
578/*
579 * Extract the first OCSP URI (if any) contained in <cert> and write it into
580 * <out>.
581 * Returns 0 in case of success, 1 otherwise.
582 */
583int ssl_ocsp_get_uri_from_cert(X509 *cert, struct buffer *out, char **err)
584{
585 STACK_OF(OPENSSL_STRING) *ocsp_uri_stk = NULL;
586 int ret = 1;
587
588 if (!cert || !out)
589 goto end;
590
591 ocsp_uri_stk = X509_get1_ocsp(cert);
592 if (ocsp_uri_stk == NULL) {
593 memprintf(err, "%sNo OCSP URL stack!\n", *err ? *err : "");
594 goto end;
595 }
596
William Lallemand8bc00f82022-12-22 10:09:11 +0100597 if (!chunk_strcpy(out, sk_OPENSSL_STRING_value(ocsp_uri_stk, 0))) {
598 memprintf(err, "%sOCSP URI too long!\n", *err ? *err : "");
599 goto end;
600 }
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100601 if (b_data(out) == 0) {
602 memprintf(err, "%sNo OCSP URL!\n", *err ? *err : "");
603 goto end;
604 }
605
606 ret = 0;
607
608end:
609 X509_email_free(ocsp_uri_stk);
610 return ret;
611}
612
613/*
614 * Create the url and request body that make a proper OCSP request for the
615 * <certid>. The <req_url> parameter should already hold the OCSP URI that was
616 * extracted from the corresponding certificate. Depending on the size of the
617 * certid we will either append data to the <req_url> to create a proper URL
618 * that will be sent with a GET command, or the <req_body> will be constructed
619 * in case of a POST.
620 * Returns 0 in case of success.
621 */
622int ssl_ocsp_create_request_details(const OCSP_CERTID *certid, struct buffer *req_url,
623 struct buffer *req_body, char **err)
624{
625 int errcode = -1;
626 OCSP_REQUEST *ocsp;
627 struct buffer *bin_request = get_trash_chunk();
628 unsigned char *outbuf = (unsigned char*)b_orig(bin_request);
629
630 ocsp = OCSP_REQUEST_new();
631 if (ocsp == NULL) {
632 memprintf(err, "%sCan't create OCSP_REQUEST\n", *err ? *err : "");
633 goto end;
634 }
635
636 if (OCSP_request_add0_id(ocsp, (OCSP_CERTID*)certid) == NULL) {
637 memprintf(err, "%sOCSP_request_add0_id() error\n", *err ? *err : "");
638 goto end;
639 }
640
641 bin_request->data = i2d_OCSP_REQUEST(ocsp, &outbuf);
642 if (b_data(bin_request) <= 0) {
643 memprintf(err, "%si2d_OCSP_REQUEST() error\n", *err ? *err : "");
644 goto end;
645 }
646
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100647 /* HTTP based OCSP requests can use either the GET or the POST method to
648 * submit their requests. To enable HTTP caching, small requests (that
649 * after encoding are less than 255 bytes), MAY be submitted using GET.
650 * If HTTP caching is not important, or the request is greater than 255
651 * bytes, the request SHOULD be submitted using POST.
652 */
William Lallemandeb530202022-12-22 10:19:07 +0100653 if (b_data(bin_request) + b_data(req_url) < 0xff) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100654 struct buffer *b64buf = get_trash_chunk();
655 char *ret = NULL;
656 int base64_ret = 0;
657
658 chunk_strcat(req_url, "/");
659
660 base64_ret = a2base64(b_orig(bin_request), b_data(bin_request),
661 b_orig(b64buf), b_size(b64buf));
662
663 if (base64_ret < 0) {
664 memprintf(err, "%sa2base64() error\n", *err ? *err : "");
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100665 goto end;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100666 }
667
668 b64buf->data = base64_ret;
669
William Lallemandeb530202022-12-22 10:19:07 +0100670 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 +0100671 query_encode_map, b64buf);
672 if (ret && *ret == '\0') {
William Lallemandeb530202022-12-22 10:19:07 +0100673 req_url->data = ret - b_orig(req_url);
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100674 errcode = 0;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100675 }
676 }
677 else {
678 chunk_cpy(req_body, bin_request);
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100679 errcode = 0;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100680 }
681
Remi Tricot-Le Bretonc389b042023-01-02 15:01:16 +0100682
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100683end:
684 OCSP_REQUEST_free(ocsp);
685
686 return errcode;
687}
688
689/*
690 * Parse an OCSP_RESPONSE contained in <respbuf> and check its validity in
691 * regard to the contents of <ckch> or the <issuer> certificate.
692 * Certificate_ocsp structure does not keep a reference to the corresponding
693 * ckch_store so outside of a CLI context (see "send ssl ocsp-response"
694 * command), we only have an easy access to the issuer's certificate whose
695 * reference is held in the structure.
696 * Return 0 in case of success, 1 otherwise.
697 */
698int ssl_ocsp_check_response(STACK_OF(X509) *chain, X509 *issuer,
699 struct buffer *respbuf, char **err)
700{
701 int ret = 1;
702 int n;
703 OCSP_RESPONSE *response = NULL;
704 OCSP_BASICRESP *basic = NULL;
705 X509_STORE *store = NULL;
706 const unsigned char *start = (const unsigned char*)b_orig(respbuf);
707
708 if (!chain && !issuer) {
709 memprintf(err, "check_ocsp_response needs a certificate validation chain or an issuer certificate");
710 goto end;
711 }
712
713 response = d2i_OCSP_RESPONSE(NULL, &start, b_data(respbuf));
714 if (!response) {
715 memprintf(err, "d2i_OCSP_RESPONSE() failed");
716 goto end;
717 }
718
719 n = OCSP_response_status(response);
720
721 if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
722 memprintf(err, "OCSP response not successful (%d: %s)",
723 n, OCSP_response_status_str(n));
724 goto end;
725 }
726
727 basic = OCSP_response_get1_basic(response);
728 if (basic == NULL) {
729 memprintf(err, "OCSP_response_get1_basic() failed");
730 goto end;
731 }
732
Remi Tricot-Le Breton8bdd0052023-01-09 12:02:43 +0100733 /* Create a temporary store in which we add the certificate's chain
734 * certificates. We assume that all those certificates can be trusted
735 * because they were provided by the user.
736 * The only ssl item that needs to be verified here is the OCSP
737 * response.
738 */
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100739 store = X509_STORE_new();
740 if (!store) {
741 memprintf(err, "X509_STORE_new() failed");
742 goto end;
743 }
Remi Tricot-Le Breton8bdd0052023-01-09 12:02:43 +0100744
745 if (chain) {
746 int i = 0;
747 for (i = 0; i < sk_X509_num(chain); i++) {
748 X509 *cert = sk_X509_value(chain, i);
749 X509_STORE_add_cert(store, cert);
750 }
751 }
752
753 if (issuer)
754 X509_STORE_add_cert(store, issuer);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100755
Remi Tricot-Le Breton8bdd0052023-01-09 12:02:43 +0100756 if (OCSP_basic_verify(basic, chain, store, OCSP_TRUSTOTHER) != 1) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100757 memprintf(err, "OCSP_basic_verify() failed");
758 goto end;
759 }
760
761 ret = 0;
762
763end:
764 X509_STORE_free(store);
765 OCSP_RESPONSE_free(response);
766 OCSP_BASICRESP_free(basic);
767 return ret;
768}
769
770
771/*
772 * OCSP-UPDATE RELATED FUNCTIONS AND STRUCTURES
773 */
774
775struct task *ocsp_update_task __read_mostly = NULL;
776
777static struct ssl_ocsp_task_ctx {
778 struct certificate_ocsp *cur_ocsp;
779 struct httpclient *hc;
780 int flags;
781} ssl_ocsp_task_ctx;
782
783const struct http_hdr ocsp_request_hdrs[] = {
784 { IST("Content-Type"), IST("application/ocsp-request") },
785 { IST_NULL, IST_NULL }
786};
787
788static struct task *ssl_ocsp_update_responses(struct task *task, void *context, unsigned int state);
789
790/*
791 * Create the main OCSP update task that will iterate over the OCSP responses
792 * stored in ocsp_update_tree and send an OCSP request via the http_client
793 * applet to the corresponding OCSP responder. The task will then be in charge
794 * of processing the response, verifying it and resinserting it in the actual
795 * ocsp response tree if the response is valid.
796 * Returns 0 in case of success.
797 */
798int ssl_create_ocsp_update_task(char **err)
799{
800 if (ocsp_update_task)
801 return 0; /* Already created */
802
803 ocsp_update_task = task_new_anywhere();
804 if (!ocsp_update_task) {
805 memprintf(err, "parsing : failed to allocate global ocsp update task.");
806 return -1;
807 }
808
809 ocsp_update_task->process = ssl_ocsp_update_responses;
810 ocsp_update_task->context = NULL;
811
812 return 0;
813}
814
815static int ssl_ocsp_task_schedule()
816{
817 if (ocsp_update_task)
818 task_schedule(ocsp_update_task, now_ms);
819
820 return 0;
821}
822REGISTER_POST_CHECK(ssl_ocsp_task_schedule);
823
824void ssl_sock_free_ocsp(struct certificate_ocsp *ocsp);
825
826void ssl_destroy_ocsp_update_task(void)
827{
828 struct eb64_node *node, *next;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100829 if (!ocsp_update_task)
830 return;
831
832 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
833
834 node = eb64_first(&ocsp_update_tree);
835 while (node) {
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100836 next = eb64_next(node);
837 eb64_delete(node);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100838 node = next;
839 }
840
841 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
842
843 task_destroy(ocsp_update_task);
844 ocsp_update_task = NULL;
Remi Tricot-Le Breton14d7f0e2023-01-09 12:02:45 +0100845
846 ssl_sock_free_ocsp(ssl_ocsp_task_ctx.cur_ocsp);
847 ssl_ocsp_task_ctx.cur_ocsp = NULL;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100848}
849
850/*
851 * Insert a certificate_ocsp structure into the ocsp_update_tree tree, in which
852 * entries are sorted by absolute date of the next update. The next_update key
853 * will be the smallest out of the actual expire value of the response and
854 * now+1H. This arbitrary 1H value ensures that ocsp responses are updated
855 * periodically even when they have a long expire time, while not overloading
856 * the system too much (in theory). Likewise, a minimum 5 minutes interval is
857 * defined in order to avoid updating too often responses that have a really
858 * short expire time or even no 'Next Update' at all.
859 */
860int ssl_ocsp_update_insert(struct certificate_ocsp *ocsp)
861{
862 int update_margin = (ocsp->expire >= SSL_OCSP_UPDATE_MARGIN) ? SSL_OCSP_UPDATE_MARGIN : 0;
863
864 ocsp->next_update.key = MIN(now.tv_sec + SSL_OCSP_UPDATE_DELAY_MAX,
865 ocsp->expire - update_margin);
866
867 /* An already existing valid OCSP response that expires within less than
868 * SSL_OCSP_UPDATE_DELAY_MIN or has no 'Next Update' field should not be
869 * updated more than once every 5 minutes in order to avoid continuous
870 * update of the same response. */
871 if (b_data(&ocsp->response))
Remi Tricot-Le Bretonbdd84c52023-01-12 09:49:08 +0100872 ocsp->next_update.key = MAX(ocsp->next_update.key,
873 now.tv_sec + SSL_OCSP_UPDATE_DELAY_MIN);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100874
875 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
876 eb64_insert(&ocsp_update_tree, &ocsp->next_update);
877 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
878
879 return 0;
880}
881
882void ocsp_update_response_stline_cb(struct httpclient *hc)
883{
884 struct task *task = hc->caller;
885
886 if (!task)
887 return;
888
889 ssl_ocsp_task_ctx.flags |= HC_F_RES_STLINE;
890 task_wakeup(task, TASK_WOKEN_MSG);
891}
892
893void ocsp_update_response_headers_cb(struct httpclient *hc)
894{
895 struct task *task = hc->caller;
896
897 if (!task)
898 return;
899
900 ssl_ocsp_task_ctx.flags |= HC_F_RES_HDR;
901 task_wakeup(task, TASK_WOKEN_MSG);
902}
903
904void ocsp_update_response_body_cb(struct httpclient *hc)
905{
906 struct task *task = hc->caller;
907
908 if (!task)
909 return;
910
911 ssl_ocsp_task_ctx.flags |= HC_F_RES_BODY;
912 task_wakeup(task, TASK_WOKEN_MSG);
913}
914
915void ocsp_update_response_end_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_END;
923 task_wakeup(task, TASK_WOKEN_MSG);
924}
925
926/*
927 * This is the main function of the ocsp auto update mechanism. It has two
928 * distinct parts and the branching to one or the other is completely based on
929 * the fact that the cur_ocsp pointer of the ssl_ocsp_task_ctx member is set.
930 *
931 * If the pointer is not set, we need to look at the first item of the update
932 * tree and see if it needs to be updated. If it does not we simply wait until
933 * the time is right and let the task asleep. If it does need to be updated, we
934 * simply build and send the corresponding ocsp request thanks to the
935 * http_client. The task is then sent to sleep with an expire time set to
936 * infinity. The http_client will wake it back up once the response is received
937 * (or a timeout occurs). Just note that during this whole process the
938 * cetificate_ocsp object corresponding to the entry being updated is taken out
939 * of the update tree and only stored in the ssl_ocsp_task_ctx context.
940 *
941 * Once the task is waken up by the http_client, it branches on the response
942 * processing part of the function which basically checks that the response is
943 * valid and inserts it into the ocsp_response tree. The task then goes back to
944 * sleep until another entry needs to be updated.
945 */
946static struct task *ssl_ocsp_update_responses(struct task *task, void *context, unsigned int state)
947{
948 unsigned int next_wakeup;
949 struct eb64_node *eb;
950 struct certificate_ocsp *ocsp;
951 struct httpclient *hc = NULL;
952 struct buffer *req_url = NULL;
953 struct buffer *req_body = NULL;
954 OCSP_CERTID *certid = NULL;
955 struct ssl_ocsp_task_ctx *ctx = &ssl_ocsp_task_ctx;
956
957 /* This arbitrary 10s time should only be used when an error occurred
958 * during an ocsp response processing. */
959 next_wakeup = 10000;
960
961 if (ctx->cur_ocsp) {
962 /* An update is in process */
963 ocsp = ctx->cur_ocsp;
964 hc = ctx->hc;
965 if (ctx->flags & HC_F_RES_STLINE) {
966 if (hc->res.status != 200) {
967 goto http_error;
968 }
969 ctx->flags &= ~HC_F_RES_STLINE;
970 }
971
972 if (ctx->flags & HC_F_RES_HDR) {
973 struct http_hdr *hdr;
974 int found = 0;
975 /* Look for "Content-Type" header which should have
976 * "application/ocsp-response" value. */
977 for (hdr = hc->res.hdrs; isttest(hdr->v); hdr++) {
978 if (isteqi(hdr->n, ist("Content-Type")) &&
979 isteqi(hdr->v, ist("application/ocsp-response"))) {
980 found = 1;
981 break;
982 }
983 }
984 if (!found) {
985 goto http_error;
986 }
987 ctx->flags &= ~HC_F_RES_HDR;
988 }
989
990 /* If the HC_F_RES_BODY is set, we still need for the
991 * HC_F_RES_END flag to be set as well in order to be sure that
992 * the body is complete. */
993
994 /* we must close only if F_RES_END is the last flag */
995 if (ctx->flags & HC_F_RES_END) {
996
997 /* Process the body that must be complete since
998 * HC_F_RES_END is set. */
999 if (ctx->flags & HC_F_RES_BODY) {
1000 if (ssl_ocsp_check_response(ocsp->chain, ocsp->issuer, &hc->res.buf, NULL))
1001 goto http_error;
1002
1003 if (ssl_sock_update_ocsp_response(&hc->res.buf, NULL) != 0) {
1004 goto http_error;
1005 }
1006
1007 ctx->flags &= ~HC_F_RES_BODY;
1008 }
1009
1010 ctx->flags &= ~HC_F_RES_END;
1011
1012 /* Reinsert the entry into the update list so that it can be updated later */
1013 ssl_ocsp_update_insert(ocsp);
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001014 /* Release the reference kept on the updated ocsp response. */
1015 ssl_sock_free_ocsp(ctx->cur_ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001016 ctx->cur_ocsp = NULL;
1017
1018 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1019 /* Set next_wakeup to the new first entry of the tree */
1020 eb = eb64_first(&ocsp_update_tree);
1021 if (eb) {
1022 if (eb->key > now.tv_sec)
1023 next_wakeup = (eb->key - now.tv_sec)*1000;
1024 else
1025 next_wakeup = 0;
1026 }
1027 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1028 goto leave;
1029 }
1030
1031 /* We did not receive the HC_F_RES_END flag yet, wait for it
1032 * before trying to update a new ocsp response. */
1033 goto wait;
1034 } else {
1035 /* Look for next entry that needs to be updated. */
1036 const unsigned char *p = NULL;
1037
1038 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1039
1040 eb = eb64_first(&ocsp_update_tree);
1041 if (!eb) {
1042 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1043 goto leave;
1044 }
1045
1046 if (eb->key > now.tv_sec) {
1047 next_wakeup = (eb->key - now.tv_sec)*1000;
1048 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1049 goto leave;
1050 }
1051
1052 ocsp = eb64_entry(eb, struct certificate_ocsp, next_update);
1053
1054 /* Take the current entry out of the update tree, it will be
1055 * reinserted after the response is processed. */
1056 eb64_delete(&ocsp->next_update);
1057
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001058 ++ocsp->refcount;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001059 ctx->cur_ocsp = ocsp;
1060
1061 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1062
1063 req_url = alloc_trash_chunk();
1064 if (!req_url) {
1065 goto leave;
1066 }
1067 req_body = alloc_trash_chunk();
1068 if (!req_body) {
1069 goto leave;
1070 }
1071
1072 p = ocsp->key_data;
1073
1074 d2i_OCSP_CERTID(&certid, &p, ocsp->key_length);
1075 if (!certid)
1076 goto leave;
1077
1078 /* Copy OCSP URI stored in ocsp structure into req_url */
1079 chunk_cpy(req_url, ocsp->uri);
1080
1081 /* Create ocsp request */
1082 if (ssl_ocsp_create_request_details(certid, req_url, req_body, NULL) != 0) {
1083 goto leave;
1084 }
1085
1086 /* Depending on the processing that occurred in
1087 * ssl_ocsp_create_request_details we could either have to send
1088 * a GET or a POST request. */
1089 hc = httpclient_new(task, b_data(req_body) ? HTTP_METH_POST : HTTP_METH_GET, ist2(b_orig(req_url), b_data(req_url)));
1090 if (!hc) {
1091 goto leave;
1092 }
1093
1094 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth,
1095 b_data(req_body) ? ocsp_request_hdrs : NULL,
William Lallemand70601c52022-12-22 14:34:01 +01001096 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 +01001097 goto leave;
1098 }
1099
1100 hc->ops.res_stline = ocsp_update_response_stline_cb;
1101 hc->ops.res_headers = ocsp_update_response_headers_cb;
1102 hc->ops.res_payload = ocsp_update_response_body_cb;
1103 hc->ops.res_end = ocsp_update_response_end_cb;
1104
1105 if (!httpclient_start(hc)) {
1106 goto leave;
1107 }
1108
1109 ctx->flags = 0;
1110 ctx->hc = hc;
1111
1112 /* We keep the lock, this indicates that an update is in process. */
1113 goto wait;
1114 }
1115
1116leave:
1117 if (ctx->cur_ocsp) {
1118 /* Something went wrong, reinsert the entry in the tree. */
1119 ssl_ocsp_update_insert(ctx->cur_ocsp);
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001120 /* Release the reference kept on the updated ocsp response. */
1121 ssl_sock_free_ocsp(ctx->cur_ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001122 ctx->cur_ocsp = NULL;
1123 }
1124 if (hc)
1125 httpclient_stop_and_destroy(hc);
1126 free_trash_chunk(req_url);
1127 free_trash_chunk(req_body);
1128 task->expire = tick_add(now_ms, next_wakeup);
1129 return task;
1130
1131wait:
1132 free_trash_chunk(req_url);
1133 free_trash_chunk(req_body);
1134 task->expire = TICK_ETERNITY;
1135 return task;
1136
1137http_error:
1138 /* Reinsert certificate into update list so that it can be updated later */
1139 if (ocsp)
1140 ssl_ocsp_update_insert(ocsp);
1141
1142 if (hc)
1143 httpclient_stop_and_destroy(hc);
Remi Tricot-Le Breton57f60c22023-01-09 12:02:42 +01001144 /* Release the reference kept on the updated ocsp response. */
1145 ssl_sock_free_ocsp(ctx->cur_ocsp);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001146 ctx->cur_ocsp = NULL;
1147 ctx->hc = NULL;
1148 ctx->flags = 0;
1149 task->expire = tick_add(now_ms, next_wakeup);
1150 return task;
1151}
1152
1153
1154
1155
1156struct ocsp_cli_ctx {
1157 struct httpclient *hc;
1158 struct ckch_data *ckch_data;
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001159 X509 *ocsp_issuer;
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001160 uint flags;
1161 uint do_update;
1162};
1163
1164
1165void cli_ocsp_res_stline_cb(struct httpclient *hc)
1166{
1167 struct appctx *appctx = hc->caller;
1168 struct ocsp_cli_ctx *ctx;
1169
1170 if (!appctx)
1171 return;
1172
1173 ctx = appctx->svcctx;
1174 ctx->flags |= HC_F_RES_STLINE;
1175 appctx_wakeup(appctx);
1176}
1177
1178void cli_ocsp_res_headers_cb(struct httpclient *hc)
1179{
1180 struct appctx *appctx = hc->caller;
1181 struct ocsp_cli_ctx *ctx;
1182
1183 if (!appctx)
1184 return;
1185
1186 ctx = appctx->svcctx;
1187 ctx->flags |= HC_F_RES_HDR;
1188 appctx_wakeup(appctx);
1189}
1190
1191void cli_ocsp_res_body_cb(struct httpclient *hc)
1192{
1193 struct appctx *appctx = hc->caller;
1194 struct ocsp_cli_ctx *ctx;
1195
1196 if (!appctx)
1197 return;
1198
1199 ctx = appctx->svcctx;
1200 ctx->flags |= HC_F_RES_BODY;
1201 appctx_wakeup(appctx);
1202}
1203
1204void cli_ocsp_res_end_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_END;
1214 appctx_wakeup(appctx);
1215}
1216
1217static int cli_parse_update_ocsp_response(char **args, char *payload, struct appctx *appctx, void *private)
1218{
1219 int errcode = 0;
1220 char *err = NULL;
1221 struct ckch_store *ckch_store = NULL;
1222 X509 *cert = NULL;
1223 struct ocsp_cli_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
1224 struct httpclient *hc = NULL;
1225 struct buffer *req_url = NULL;
1226 struct buffer *req_body = NULL;
1227 OCSP_CERTID *certid = NULL;
1228
1229 if (!*args[3]) {
1230 memprintf(&err, "'update ssl ocsp-response' expects a filename\n");
1231 return cli_dynerr(appctx, err);
1232 }
1233
1234 req_url = alloc_trash_chunk();
1235 if (!req_url) {
1236 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1237 errcode |= ERR_ALERT | ERR_FATAL;
1238 goto end;
1239 }
1240
1241 req_body = alloc_trash_chunk();
1242 if (!req_body) {
1243 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1244 errcode |= ERR_ALERT | ERR_FATAL;
1245 goto end;
1246 }
1247
1248 /* The operations on the CKCH architecture are locked so we can
1249 * manipulate ckch_store and ckch_inst */
1250 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock)) {
1251 memprintf(&err, "%sCan't update the certificate!\nOperations on certificates are currently locked!\n", err ? err : "");
1252 errcode |= ERR_ALERT | ERR_FATAL;
1253 goto end;
1254 }
1255
1256 ckch_store = ckchs_lookup(args[3]);
1257
1258 if (!ckch_store) {
Remi Tricot-Le Breton14419eb2023-01-09 12:02:49 +01001259 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 +01001260 errcode |= ERR_ALERT | ERR_FATAL;
1261 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1262 goto end;
1263 }
1264
1265 ctx->ckch_data = ckch_store->data;
1266
1267 cert = ckch_store->data->cert;
1268
1269 if (ssl_ocsp_get_uri_from_cert(cert, req_url, &err)) {
1270 errcode |= ERR_ALERT | ERR_FATAL;
1271 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1272 goto end;
1273 }
1274
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001275 /* Look for the ocsp issuer in the ckch_data or in the certificate
1276 * chain, the same way it is done in ssl_sock_load_ocsp. */
1277 ctx->ocsp_issuer = ctx->ckch_data->ocsp_issuer;
1278
1279 /* take issuer from chain over ocsp_issuer, is what is done historicaly */
1280 if (ctx->ckch_data->chain) {
1281 int i = 0;
1282 /* check if one of the certificate of the chain is the issuer */
1283 for (i = 0; i < sk_X509_num(ctx->ckch_data->chain); i++) {
1284 X509 *ti = sk_X509_value(ctx->ckch_data->chain, i);
1285 if (X509_check_issued(ti, cert) == X509_V_OK) {
1286 ctx->ocsp_issuer = ti;
1287 break;
1288 }
1289 }
1290 }
1291
1292 if (!ctx->ocsp_issuer) {
1293 memprintf(&err, "%sOCSP issuer not found\n", err ? err : "");
1294 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1295 goto end;
1296 }
1297
1298 X509_up_ref(ctx->ocsp_issuer);
1299
1300 certid = OCSP_cert_to_id(NULL, cert, ctx->ocsp_issuer);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001301 if (certid == NULL) {
1302 memprintf(&err, "%sOCSP_cert_to_id() error\n", err ? err : "");
1303 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1304 goto end;
1305 }
1306
1307 /* From here on the lock is not needed anymore. */
1308 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1309
1310 /* Create ocsp request */
1311 if (ssl_ocsp_create_request_details(certid, req_url, req_body, &err) != 0) {
1312 memprintf(&err, "%sCreate ocsp request error\n", err ? err : "");
1313 goto end;
1314 }
1315
1316 hc = httpclient_new(appctx, b_data(req_body) ? HTTP_METH_POST : HTTP_METH_GET, ist2(b_orig(req_url), b_data(req_url)));
1317 if (!hc) {
1318 memprintf(&err, "%sCan't allocate httpclient\n", err ? err : "");
1319 goto end;
1320 }
1321
1322 if (httpclient_req_gen(hc, hc->req.url, hc->req.meth, b_data(req_body) ? ocsp_request_hdrs : NULL,
1323 ist2(b_orig(req_body), b_data(req_body))) != ERR_NONE) {
1324 memprintf(&err, "%shttpclient_req_gen() error\n", err ? err : "");
1325 goto end;
1326 }
1327
1328 hc->ops.res_stline = cli_ocsp_res_stline_cb;
1329 hc->ops.res_headers = cli_ocsp_res_headers_cb;
1330 hc->ops.res_payload = cli_ocsp_res_body_cb;
1331 hc->ops.res_end = cli_ocsp_res_end_cb;
1332
1333 ctx->hc = hc; /* store the httpclient ptr in the applet */
1334 ctx->flags = 0;
1335
1336 if (!httpclient_start(hc)) {
1337 memprintf(&err, "%shttpclient_start() error\n", err ? err : "");
1338 goto end;
1339 }
1340
1341 free_trash_chunk(req_url);
1342
1343 return 0;
1344
1345end:
1346 free_trash_chunk(req_url);
1347
1348 if (errcode & ERR_CODE) {
1349 return cli_dynerr(appctx, memprintf(&err, "%sCan't send ocsp request for %s!\n", err ? err : "", args[3]));
1350 }
1351 return cli_dynmsg(appctx, LOG_NOTICE, err);
1352}
1353
1354static int cli_io_handler_update_ocsp_response(struct appctx *appctx)
1355{
1356 struct ocsp_cli_ctx *ctx = appctx->svcctx;
1357 struct httpclient *hc = ctx->hc;
1358
1359 if (ctx->flags & HC_F_RES_STLINE) {
1360 if (hc->res.status != 200) {
1361 chunk_printf(&trash, "OCSP response error (status %d)\n", hc->res.status);
1362 if (applet_putchk(appctx, &trash) == -1)
1363 goto more;
1364 goto end;
1365 }
1366 ctx->flags &= ~HC_F_RES_STLINE;
1367 }
1368
1369 if (ctx->flags & HC_F_RES_HDR) {
1370 struct http_hdr *hdr;
1371 int found = 0;
1372 /* Look for "Content-Type" header which should have
1373 * "application/ocsp-response" value. */
1374 for (hdr = hc->res.hdrs; isttest(hdr->v); hdr++) {
1375 if (isteqi(hdr->n, ist("Content-Type")) &&
1376 isteqi(hdr->v, ist("application/ocsp-response"))) {
1377 found = 1;
1378 break;
1379 }
1380 }
1381 if (!found) {
1382 fprintf(stderr, "Missing 'Content-Type: application/ocsp-response' header\n");
1383 goto end;
1384 }
1385 ctx->flags &= ~HC_F_RES_HDR;
1386 }
1387
1388 if (ctx->flags & HC_F_RES_BODY) {
1389 /* Wait until the full body is received and HC_F_RES_END flag is
1390 * set. */
1391 }
1392
1393 /* we must close only if F_END is the last flag */
1394 if (ctx->flags & HC_F_RES_END) {
1395 char *err = NULL;
1396
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001397 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 +01001398 chunk_printf(&trash, "%s", err);
1399 if (applet_putchk(appctx, &trash) == -1)
1400 goto more;
1401 goto end;
1402 }
1403
1404 if (ssl_sock_update_ocsp_response(&hc->res.buf, &err) != 0) {
1405 chunk_printf(&trash, "%s", err);
1406 if (applet_putchk(appctx, &trash) == -1)
1407 goto more;
1408 goto end;
1409 }
1410
1411 chunk_reset(&trash);
1412
1413 if (ssl_ocsp_response_print(&hc->res.buf, &trash))
1414 goto end;
1415
1416 if (applet_putchk(appctx, &trash) == -1)
1417 goto more;
1418 ctx->flags &= ~HC_F_RES_BODY;
1419 ctx->flags &= ~HC_F_RES_END;
1420 goto end;
1421 }
1422
1423more:
1424 if (!ctx->flags)
1425 applet_have_no_more_data(appctx);
1426 return 0;
1427end:
1428 return 1;
1429}
1430
1431static void cli_release_update_ocsp_response(struct appctx *appctx)
1432{
1433 struct ocsp_cli_ctx *ctx = appctx->svcctx;
1434 struct httpclient *hc = ctx->hc;
1435
Remi Tricot-Le Breton71237a12023-01-10 11:21:40 +01001436 X509_free(ctx->ocsp_issuer);
Remi Tricot-Le Breton15dc0e22023-01-09 12:02:41 +01001437
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +01001438 /* Everything possible was printed on the CLI, we can destroy the client */
1439 httpclient_stop_and_destroy(hc);
1440
1441 return;
1442}
1443
1444
1445#endif /* !defined OPENSSL_IS_BORINGSSL */
1446
1447
1448#endif /* (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) */
1449
1450
1451static int cli_parse_set_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
1452{
1453#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
1454 char *err = NULL;
1455 int i, j, ret;
1456
1457 if (!payload)
1458 payload = args[3];
1459
1460 /* Expect one parameter: the new response in base64 encoding */
1461 if (!*payload)
1462 return cli_err(appctx, "'set ssl ocsp-response' expects response in base64 encoding.\n");
1463
1464 /* remove \r and \n from the payload */
1465 for (i = 0, j = 0; payload[i]; i++) {
1466 if (payload[i] == '\r' || payload[i] == '\n')
1467 continue;
1468 payload[j++] = payload[i];
1469 }
1470 payload[j] = 0;
1471
1472 ret = base64dec(payload, j, trash.area, trash.size);
1473 if (ret < 0)
1474 return cli_err(appctx, "'set ssl ocsp-response' received invalid base64 encoded response.\n");
1475
1476 trash.data = ret;
1477 if (ssl_sock_update_ocsp_response(&trash, &err)) {
1478 if (err)
1479 return cli_dynerr(appctx, memprintf(&err, "%s.\n", err));
1480 else
1481 return cli_err(appctx, "Failed to update OCSP response.\n");
1482 }
1483
1484 return cli_msg(appctx, LOG_INFO, "OCSP Response updated!\n");
1485#else
1486 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1487#endif
1488
1489}
1490
1491/* parsing function for 'show ssl ocsp-response [id]'. If an entry is forced,
1492 * it's set into appctx->svcctx.
1493 */
1494static int cli_parse_show_ocspresponse(char **args, char *payload, struct appctx *appctx, void *private)
1495{
1496#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
1497 if (*args[3]) {
1498 struct certificate_ocsp *ocsp = NULL;
1499 char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
1500 int key_length = OCSP_MAX_CERTID_ASN1_LENGTH;
1501 char *key_ptr = key;
1502
1503 if (strlen(args[3]) > OCSP_MAX_CERTID_ASN1_LENGTH*2) {
1504 return cli_err(appctx, "'show ssl ocsp-response' received a too big key.\n");
1505 }
1506
1507 if (!parse_binary(args[3], &key_ptr, &key_length, NULL)) {
1508 return cli_err(appctx, "'show ssl ocsp-response' received an invalid key.\n");
1509 }
1510
1511 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1512 ocsp = (struct certificate_ocsp *)ebmb_lookup(&cert_ocsp_tree, key, OCSP_MAX_CERTID_ASN1_LENGTH);
1513
1514 if (!ocsp) {
1515 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1516 return cli_err(appctx, "Certificate ID does not match any certificate.\n");
1517 }
1518 ++ocsp->refcount;
1519 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1520
1521 appctx->svcctx = ocsp;
1522 appctx->io_handler = cli_io_handler_show_ocspresponse_detail;
1523 }
1524
1525 return 0;
1526
1527#else
1528 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1529#endif
1530}
1531
1532/*
1533 * IO handler of "show ssl ocsp-response". The command taking a specific ID
1534 * is managed in cli_io_handler_show_ocspresponse_detail.
1535 * The current entry is taken from appctx->svcctx.
1536 */
1537static int cli_io_handler_show_ocspresponse(struct appctx *appctx)
1538{
1539#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
1540 struct buffer *trash = alloc_trash_chunk();
1541 struct buffer *tmp = NULL;
1542 struct ebmb_node *node;
1543 struct certificate_ocsp *ocsp = NULL;
1544 BIO *bio = NULL;
1545 int write = -1;
1546
1547 if (trash == NULL)
1548 return 1;
1549
1550 HA_SPIN_LOCK(OCSP_LOCK, &ocsp_tree_lock);
1551
1552 tmp = alloc_trash_chunk();
1553 if (!tmp)
1554 goto end;
1555
1556 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1557 goto end;
1558
1559 if (!appctx->svcctx) {
1560 chunk_appendf(trash, "# Certificate IDs\n");
1561 node = ebmb_first(&cert_ocsp_tree);
1562 } else {
1563 node = &((struct certificate_ocsp *)appctx->svcctx)->key;
1564 }
1565
1566 while (node) {
1567 OCSP_CERTID *certid = NULL;
1568 const unsigned char *p = NULL;
1569 int i;
1570
1571 ocsp = ebmb_entry(node, struct certificate_ocsp, key);
1572
1573 /* Dump the key in hexadecimal */
1574 chunk_appendf(trash, "Certificate ID key : ");
1575 for (i = 0; i < ocsp->key_length; ++i) {
1576 chunk_appendf(trash, "%02x", ocsp->key_data[i]);
1577 }
1578 chunk_appendf(trash, "\n");
1579
1580 p = ocsp->key_data;
1581
1582 /* Decode the certificate ID (serialized into the key). */
1583 d2i_OCSP_CERTID(&certid, &p, ocsp->key_length);
1584 if (!certid)
1585 goto end;
1586
1587 /* Dump the CERTID info */
1588 ocsp_certid_print(bio, certid, 1);
1589 OCSP_CERTID_free(certid);
1590 write = BIO_read(bio, tmp->area, tmp->size-1);
1591 /* strip trailing LFs */
1592 while (write > 0 && tmp->area[write-1] == '\n')
1593 write--;
1594 tmp->area[write] = '\0';
1595
1596 chunk_appendf(trash, "%s\n", tmp->area);
1597
1598 node = ebmb_next(node);
1599 if (applet_putchk(appctx, trash) == -1)
1600 goto yield;
1601 }
1602
1603end:
1604 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1605 appctx->svcctx = NULL;
1606 free_trash_chunk(trash);
1607 free_trash_chunk(tmp);
1608 BIO_free(bio);
1609 return 1;
1610
1611yield:
1612 free_trash_chunk(trash);
1613 free_trash_chunk(tmp);
1614 BIO_free(bio);
1615
1616 ++ocsp->refcount;
1617 appctx->svcctx = ocsp;
1618 HA_SPIN_UNLOCK(OCSP_LOCK, &ocsp_tree_lock);
1619 return 0;
1620#else
1621 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1622#endif
1623}
1624
1625
1626static struct cli_kw_list cli_kws = {{ },{
1627 { { "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 },
1628
1629 { { "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 },
1630#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
1631 { { "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 },
1632#endif
1633 { { NULL }, NULL, NULL, NULL }
1634}};
1635
1636INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1637
1638/*
1639 * Local variables:
1640 * c-indent-level: 8
1641 * c-basic-offset: 8
1642 * End:
1643 */