1 1.34 riastrad /* $NetBSD: ssl-bozo.c,v 1.34 2023/12/18 03:48:57 riastradh Exp $ */ 2 1.2 tls 3 1.13 mrg /* $eterna: ssl-bozo.c,v 1.15 2011/11/18 09:21:15 mrg Exp $ */ 4 1.1 tls 5 1.1 tls /* 6 1.33 mrg * Copyright (c) 1997-2023 Matthew R. Green 7 1.1 tls * All rights reserved. 8 1.1 tls * 9 1.1 tls * Redistribution and use in source and binary forms, with or without 10 1.1 tls * modification, are permitted provided that the following conditions 11 1.1 tls * are met: 12 1.1 tls * 1. Redistributions of source code must retain the above copyright 13 1.1 tls * notice, this list of conditions and the following disclaimer. 14 1.1 tls * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 tls * notice, this list of conditions and the following disclaimer and 16 1.1 tls * dedication in the documentation and/or other materials provided 17 1.1 tls * with the distribution. 18 1.1 tls * 19 1.1 tls * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 1.1 tls * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 1.1 tls * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 1.1 tls * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 1.1 tls * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 1.1 tls * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 1.1 tls * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26 1.1 tls * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 1.1 tls * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 tls * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 tls * SUCH DAMAGE. 30 1.1 tls * 31 1.1 tls */ 32 1.1 tls 33 1.18 mrg /* this code implements SSL and backend IO for bozohttpd */ 34 1.1 tls 35 1.7 mrg #include <stdarg.h> 36 1.7 mrg #include <stdio.h> 37 1.23 christos #include <string.h> 38 1.8 mrg #include <syslog.h> 39 1.7 mrg #include <unistd.h> 40 1.7 mrg 41 1.7 mrg #include "bozohttpd.h" 42 1.7 mrg 43 1.29 mrg #ifndef USE_ARG 44 1.29 mrg #define USE_ARG(x) /*LINTED*/(void)&(x) 45 1.29 mrg #endif 46 1.29 mrg 47 1.1 tls #ifndef NO_SSL_SUPPORT 48 1.1 tls 49 1.1 tls #include <openssl/ssl.h> 50 1.1 tls #include <openssl/err.h> 51 1.1 tls 52 1.19 christos #ifndef BOZO_SSL_CIPHERS 53 1.19 christos #define BOZO_SSL_CIPHERS \ 54 1.31 mrg "HIGH:" \ 55 1.31 mrg "-SHA:-ADH:" \ 56 1.31 mrg "-PSK-AES128-CCM:-PSK-AES256-CCM:" \ 57 1.31 mrg "-DHE-PSK-AES128-CCM8:-DHE-PSK-AES256-CCM8:" \ 58 1.31 mrg "-AES128-CCM8:-AES256-CCM8:" \ 59 1.31 mrg "-DHE-RSA-AES128-CCM8:-DHE-RSA-AES256-CCM8:" \ 60 1.31 mrg "-PSK-AES128-CCM8:-PSK-AES256-CCM8:" \ 61 1.31 mrg "-CAMELLIA128:-CAMELLIA256:" \ 62 1.31 mrg "-RSA-PSK-CHACHA20-POLY1305:" \ 63 1.19 christos "!aNULL:!eNULL:" \ 64 1.19 christos "!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" \ 65 1.19 christos "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:" \ 66 1.19 christos "!KRB5-DES-CBC3-SHA" 67 1.19 christos #endif 68 1.19 christos 69 1.7 mrg /* this structure encapsulates the ssl info */ 70 1.7 mrg typedef struct sslinfo_t { 71 1.7 mrg SSL_CTX *ssl_context; 72 1.7 mrg const SSL_METHOD *ssl_method; 73 1.7 mrg SSL *bozossl; 74 1.7 mrg char *certificate_file; 75 1.7 mrg char *privatekey_file; 76 1.19 christos char *ciphers; 77 1.7 mrg } sslinfo_t; 78 1.7 mrg 79 1.30 mrg /* Default to TLS 1.3. */ 80 1.30 mrg struct { 81 1.30 mrg unsigned proto; 82 1.30 mrg const char *name; 83 1.30 mrg } protos[] = { 84 1.30 mrg { TLS1_3_VERSION, "TLSv1.3" }, 85 1.30 mrg { TLS1_2_VERSION, "TLSv1.2" }, 86 1.30 mrg { TLS1_1_VERSION, "TLSv1.1" }, 87 1.30 mrg { 0, NULL }, 88 1.30 mrg }; 89 1.30 mrg 90 1.30 mrg static int 91 1.30 mrg bozo_ssl_proto(const char *name) 92 1.30 mrg { 93 1.30 mrg unsigned i; 94 1.30 mrg 95 1.30 mrg if (name) 96 1.30 mrg for (i = 0; protos[0].proto != 0; i++) 97 1.30 mrg if (strcasecmp(name, protos[i].name) == 0) 98 1.30 mrg return protos[i].proto; 99 1.30 mrg return protos[0].proto; 100 1.30 mrg } 101 1.30 mrg 102 1.30 mrg static const char * 103 1.30 mrg bozo_ssl_name(unsigned version) 104 1.30 mrg { 105 1.30 mrg unsigned i; 106 1.30 mrg 107 1.30 mrg for (i = 0; protos[0].proto != 0; i++) 108 1.30 mrg if (version == protos[i].proto) 109 1.30 mrg return protos[i].name; 110 1.30 mrg return protos[0].name; 111 1.30 mrg } 112 1.30 mrg 113 1.8 mrg /* 114 1.18 mrg * bozo_clear_ssl_queue: print the contents of the SSL error queue 115 1.8 mrg */ 116 1.18 mrg static void 117 1.18 mrg bozo_clear_ssl_queue(bozohttpd_t *httpd) 118 1.8 mrg { 119 1.18 mrg unsigned long sslcode = ERR_get_error(); 120 1.8 mrg 121 1.8 mrg do { 122 1.11 hannken static const char sslfmt[] = "SSL Error: %s:%s:%s"; 123 1.8 mrg 124 1.32 martin if (httpd->nolog) 125 1.32 martin continue; 126 1.32 martin 127 1.8 mrg if (httpd->logstderr || isatty(STDERR_FILENO)) { 128 1.8 mrg fprintf(stderr, sslfmt, 129 1.8 mrg ERR_lib_error_string(sslcode), 130 1.8 mrg ERR_func_error_string(sslcode), 131 1.8 mrg ERR_reason_error_string(sslcode)); 132 1.34 riastrad fputs("\n", stderr); 133 1.8 mrg } else { 134 1.8 mrg syslog(LOG_ERR, sslfmt, 135 1.8 mrg ERR_lib_error_string(sslcode), 136 1.8 mrg ERR_func_error_string(sslcode), 137 1.8 mrg ERR_reason_error_string(sslcode)); 138 1.8 mrg } 139 1.8 mrg } while (0 != (sslcode = ERR_get_error())); 140 1.18 mrg } 141 1.18 mrg 142 1.18 mrg /* 143 1.22 mrg * bozo_ssl_warn works just like bozowarn, plus the SSL error queue 144 1.18 mrg */ 145 1.18 mrg BOZO_PRINTFLIKE(2, 3) static void 146 1.18 mrg bozo_ssl_warn(bozohttpd_t *httpd, const char *fmt, ...) 147 1.18 mrg { 148 1.18 mrg va_list ap; 149 1.18 mrg 150 1.18 mrg va_start(ap, fmt); 151 1.32 martin if (!httpd->nolog) { 152 1.32 martin if (httpd->logstderr || isatty(STDERR_FILENO)) { 153 1.32 martin vfprintf(stderr, fmt, ap); 154 1.32 martin fputs("\n", stderr); 155 1.32 martin } else 156 1.32 martin vsyslog(LOG_ERR, fmt, ap); 157 1.32 martin } 158 1.18 mrg va_end(ap); 159 1.18 mrg 160 1.18 mrg bozo_clear_ssl_queue(httpd); 161 1.18 mrg } 162 1.18 mrg 163 1.18 mrg 164 1.18 mrg /* 165 1.22 mrg * bozo_ssl_err works just like bozoerr, plus the SSL error queue 166 1.18 mrg */ 167 1.18 mrg BOZO_PRINTFLIKE(3, 4) BOZO_DEAD static void 168 1.18 mrg bozo_ssl_err(bozohttpd_t *httpd, int code, const char *fmt, ...) 169 1.18 mrg { 170 1.18 mrg va_list ap; 171 1.18 mrg 172 1.18 mrg va_start(ap, fmt); 173 1.32 martin if (!httpd->nolog) { 174 1.32 martin if (httpd->logstderr || isatty(STDERR_FILENO)) { 175 1.32 martin vfprintf(stderr, fmt, ap); 176 1.32 martin fputs("\n", stderr); 177 1.32 martin } else 178 1.32 martin vsyslog(LOG_ERR, fmt, ap); 179 1.32 martin } 180 1.18 mrg va_end(ap); 181 1.18 mrg 182 1.18 mrg bozo_clear_ssl_queue(httpd); 183 1.8 mrg exit(code); 184 1.8 mrg } 185 1.8 mrg 186 1.18 mrg /* 187 1.18 mrg * bozo_check_error_queue: print warnings if the error isn't expected 188 1.18 mrg */ 189 1.18 mrg static void 190 1.18 mrg bozo_check_error_queue(bozohttpd_t *httpd, const char *tag, int ret) 191 1.18 mrg { 192 1.18 mrg if (ret > 0) 193 1.18 mrg return; 194 1.18 mrg 195 1.18 mrg const sslinfo_t *sslinfo = httpd->sslinfo; 196 1.18 mrg const int sslerr = SSL_get_error(sslinfo->bozossl, ret); 197 1.18 mrg 198 1.18 mrg if (sslerr != SSL_ERROR_ZERO_RETURN && 199 1.18 mrg sslerr != SSL_ERROR_SYSCALL && 200 1.18 mrg sslerr != SSL_ERROR_NONE) 201 1.18 mrg bozo_ssl_warn(httpd, "%s: SSL_ERROR %d", tag, sslerr); 202 1.18 mrg } 203 1.18 mrg 204 1.15 joerg static BOZO_PRINTFLIKE(2, 0) int 205 1.8 mrg bozo_ssl_printf(bozohttpd_t *httpd, const char * fmt, va_list ap) 206 1.7 mrg { 207 1.18 mrg char *buf; 208 1.18 mrg int nbytes; 209 1.7 mrg 210 1.18 mrg if ((nbytes = vasprintf(&buf, fmt, ap)) != -1) { 211 1.18 mrg const sslinfo_t *sslinfo = httpd->sslinfo; 212 1.18 mrg int ret = SSL_write(sslinfo->bozossl, buf, nbytes); 213 1.18 mrg bozo_check_error_queue(httpd, "write", ret); 214 1.18 mrg } 215 1.7 mrg 216 1.7 mrg free(buf); 217 1.7 mrg 218 1.7 mrg return nbytes; 219 1.7 mrg } 220 1.7 mrg 221 1.7 mrg static ssize_t 222 1.7 mrg bozo_ssl_read(bozohttpd_t *httpd, int fd, void *buf, size_t nbytes) 223 1.7 mrg { 224 1.17 mrg const sslinfo_t *sslinfo = httpd->sslinfo; 225 1.18 mrg int ret; 226 1.7 mrg 227 1.7 mrg USE_ARG(fd); 228 1.18 mrg ret = SSL_read(sslinfo->bozossl, buf, (int)nbytes); 229 1.18 mrg bozo_check_error_queue(httpd, "read", ret); 230 1.7 mrg 231 1.18 mrg return (ssize_t)ret; 232 1.7 mrg } 233 1.7 mrg 234 1.7 mrg static ssize_t 235 1.7 mrg bozo_ssl_write(bozohttpd_t *httpd, int fd, const void *buf, size_t nbytes) 236 1.7 mrg { 237 1.17 mrg const sslinfo_t *sslinfo = httpd->sslinfo; 238 1.18 mrg int ret; 239 1.7 mrg 240 1.7 mrg USE_ARG(fd); 241 1.18 mrg ret = SSL_write(sslinfo->bozossl, buf, (int)nbytes); 242 1.18 mrg bozo_check_error_queue(httpd, "write", ret); 243 1.1 tls 244 1.18 mrg return (ssize_t)ret; 245 1.7 mrg } 246 1.1 tls 247 1.1 tls void 248 1.7 mrg bozo_ssl_init(bozohttpd_t *httpd) 249 1.1 tls { 250 1.17 mrg sslinfo_t *sslinfo = httpd->sslinfo; 251 1.30 mrg int proto; 252 1.7 mrg 253 1.7 mrg if (sslinfo == NULL || !sslinfo->certificate_file) 254 1.1 tls return; 255 1.1 tls SSL_library_init(); 256 1.1 tls SSL_load_error_strings(); 257 1.1 tls 258 1.7 mrg sslinfo->ssl_method = SSLv23_server_method(); 259 1.7 mrg sslinfo->ssl_context = SSL_CTX_new(sslinfo->ssl_method); 260 1.1 tls 261 1.8 mrg if (NULL == sslinfo->ssl_context) 262 1.8 mrg bozo_ssl_err(httpd, EXIT_FAILURE, 263 1.8 mrg "SSL context creation failed"); 264 1.8 mrg 265 1.30 mrg proto = bozo_ssl_proto(httpd->ssl_min_proto); 266 1.30 mrg 267 1.30 mrg if (!SSL_CTX_set_min_proto_version(sslinfo->ssl_context, proto)) 268 1.19 christos bozo_ssl_err(httpd, EXIT_FAILURE, 269 1.30 mrg "Error setting minimum protocol version '%s'", 270 1.30 mrg bozo_ssl_name(proto)); 271 1.19 christos 272 1.19 christos if (!SSL_CTX_set_cipher_list(sslinfo->ssl_context, 273 1.19 christos sslinfo->ciphers ? sslinfo->ciphers : BOZO_SSL_CIPHERS)) 274 1.19 christos bozo_ssl_err(httpd, EXIT_FAILURE, 275 1.19 christos "Error setting cipher list '%s'", sslinfo->ciphers); 276 1.19 christos 277 1.14 elric if (1 != SSL_CTX_use_certificate_chain_file(sslinfo->ssl_context, 278 1.14 elric sslinfo->certificate_file)) 279 1.8 mrg bozo_ssl_err(httpd, EXIT_FAILURE, 280 1.8 mrg "Unable to use certificate file '%s'", 281 1.8 mrg sslinfo->certificate_file); 282 1.8 mrg 283 1.8 mrg if (1 != SSL_CTX_use_PrivateKey_file(sslinfo->ssl_context, 284 1.8 mrg sslinfo->privatekey_file, SSL_FILETYPE_PEM)) 285 1.8 mrg bozo_ssl_err(httpd, EXIT_FAILURE, 286 1.8 mrg "Unable to use private key file '%s'", 287 1.8 mrg sslinfo->privatekey_file); 288 1.1 tls 289 1.1 tls /* check consistency of key vs certificate */ 290 1.7 mrg if (!SSL_CTX_check_private_key(sslinfo->ssl_context)) 291 1.8 mrg bozo_ssl_err(httpd, EXIT_FAILURE, 292 1.8 mrg "Check private key failed"); 293 1.1 tls } 294 1.1 tls 295 1.18 mrg /* 296 1.18 mrg * returns non-zero for failure 297 1.18 mrg */ 298 1.18 mrg int 299 1.7 mrg bozo_ssl_accept(bozohttpd_t *httpd) 300 1.1 tls { 301 1.17 mrg sslinfo_t *sslinfo = httpd->sslinfo; 302 1.7 mrg 303 1.18 mrg if (sslinfo == NULL || !sslinfo->ssl_context) 304 1.18 mrg return 0; 305 1.18 mrg 306 1.26 mrg alarm(httpd->ssl_timeout); 307 1.26 mrg 308 1.18 mrg sslinfo->bozossl = SSL_new(sslinfo->ssl_context); 309 1.18 mrg if (sslinfo->bozossl == NULL) 310 1.22 mrg bozoerr(httpd, 1, "SSL_new failed"); 311 1.18 mrg 312 1.18 mrg SSL_set_rfd(sslinfo->bozossl, 0); 313 1.18 mrg SSL_set_wfd(sslinfo->bozossl, 1); 314 1.18 mrg 315 1.18 mrg const int ret = SSL_accept(sslinfo->bozossl); 316 1.18 mrg bozo_check_error_queue(httpd, "accept", ret); 317 1.18 mrg 318 1.26 mrg alarm(0); 319 1.26 mrg 320 1.26 mrg if (bozo_timeout_hit) { 321 1.26 mrg SSL_free(sslinfo->bozossl); 322 1.26 mrg sslinfo->bozossl = NULL; 323 1.26 mrg return 1; 324 1.26 mrg } 325 1.26 mrg 326 1.18 mrg return ret != 1; 327 1.1 tls } 328 1.1 tls 329 1.1 tls void 330 1.27 spz bozo_ssl_shutdown(bozohttpd_t *httpd) 331 1.27 spz { 332 1.27 spz const sslinfo_t *sslinfo = httpd->sslinfo; 333 1.27 spz 334 1.27 spz if (sslinfo && sslinfo->bozossl) 335 1.27 spz SSL_shutdown(sslinfo->bozossl); 336 1.27 spz } 337 1.27 spz 338 1.27 spz void 339 1.7 mrg bozo_ssl_destroy(bozohttpd_t *httpd) 340 1.1 tls { 341 1.17 mrg const sslinfo_t *sslinfo = httpd->sslinfo; 342 1.7 mrg 343 1.7 mrg if (sslinfo && sslinfo->bozossl) 344 1.7 mrg SSL_free(sslinfo->bozossl); 345 1.1 tls } 346 1.1 tls 347 1.19 christos static sslinfo_t * 348 1.19 christos bozo_get_sslinfo(bozohttpd_t *httpd) 349 1.19 christos { 350 1.19 christos sslinfo_t *sslinfo; 351 1.19 christos if (httpd->sslinfo) 352 1.19 christos return httpd->sslinfo; 353 1.19 christos sslinfo = bozomalloc(httpd, sizeof(*sslinfo)); 354 1.19 christos if (sslinfo == NULL) 355 1.22 mrg bozoerr(httpd, 1, "sslinfo allocation failed"); 356 1.19 christos memset(sslinfo, 0, sizeof(*sslinfo)); 357 1.19 christos return httpd->sslinfo = sslinfo; 358 1.19 christos } 359 1.19 christos 360 1.1 tls void 361 1.7 mrg bozo_ssl_set_opts(bozohttpd_t *httpd, const char *cert, const char *priv) 362 1.1 tls { 363 1.19 christos sslinfo_t *sslinfo = bozo_get_sslinfo(httpd); 364 1.7 mrg 365 1.21 mrg sslinfo->certificate_file = bozostrdup(httpd, NULL, cert); 366 1.21 mrg sslinfo->privatekey_file = bozostrdup(httpd, NULL, priv); 367 1.7 mrg debug((httpd, DEBUG_NORMAL, "using cert/priv files: %s & %s", 368 1.19 christos sslinfo->certificate_file, 369 1.19 christos sslinfo->privatekey_file)); 370 1.17 mrg if (!httpd->bindport) 371 1.28 mrg httpd->bindport = bozostrdup(httpd, NULL, BOZO_HTTPS_PORT); 372 1.19 christos } 373 1.19 christos 374 1.19 christos void 375 1.19 christos bozo_ssl_set_ciphers(bozohttpd_t *httpd, const char *ciphers) 376 1.19 christos { 377 1.19 christos sslinfo_t *sslinfo = bozo_get_sslinfo(httpd); 378 1.19 christos 379 1.21 mrg sslinfo->ciphers = bozostrdup(httpd, NULL, ciphers); 380 1.19 christos debug((httpd, DEBUG_NORMAL, "using ciphers: %s", sslinfo->ciphers)); 381 1.1 tls } 382 1.1 tls 383 1.7 mrg #endif /* NO_SSL_SUPPORT */ 384 1.7 mrg 385 1.25 mrg /* 386 1.25 mrg * These functions are always present, so that caller code can simply 387 1.25 mrg * use bozo_*() for IO, regardless of SSL. 388 1.25 mrg */ 389 1.7 mrg int 390 1.7 mrg bozo_printf(bozohttpd_t *httpd, const char *fmt, ...) 391 1.1 tls { 392 1.7 mrg va_list args; 393 1.7 mrg int cc; 394 1.1 tls 395 1.29 mrg USE_ARG(httpd); 396 1.29 mrg 397 1.7 mrg va_start(args, fmt); 398 1.7 mrg #ifndef NO_SSL_SUPPORT 399 1.17 mrg if (httpd->sslinfo) 400 1.7 mrg cc = bozo_ssl_printf(httpd, fmt, args); 401 1.17 mrg else 402 1.7 mrg #endif 403 1.17 mrg cc = vprintf(fmt, args); 404 1.7 mrg va_end(args); 405 1.7 mrg return cc; 406 1.1 tls } 407 1.1 tls 408 1.7 mrg ssize_t 409 1.7 mrg bozo_read(bozohttpd_t *httpd, int fd, void *buf, size_t len) 410 1.1 tls { 411 1.7 mrg #ifndef NO_SSL_SUPPORT 412 1.17 mrg if (httpd->sslinfo) 413 1.7 mrg return bozo_ssl_read(httpd, fd, buf, len); 414 1.7 mrg #endif 415 1.29 mrg USE_ARG(httpd); 416 1.7 mrg return read(fd, buf, len); 417 1.1 tls } 418 1.1 tls 419 1.7 mrg ssize_t 420 1.7 mrg bozo_write(bozohttpd_t *httpd, int fd, const void *buf, size_t len) 421 1.1 tls { 422 1.7 mrg #ifndef NO_SSL_SUPPORT 423 1.17 mrg if (httpd->sslinfo) 424 1.7 mrg return bozo_ssl_write(httpd, fd, buf, len); 425 1.7 mrg #endif 426 1.29 mrg USE_ARG(httpd); 427 1.7 mrg return write(fd, buf, len); 428 1.1 tls } 429 1.1 tls 430 1.7 mrg int 431 1.7 mrg bozo_flush(bozohttpd_t *httpd, FILE *fp) 432 1.1 tls { 433 1.7 mrg #ifndef NO_SSL_SUPPORT 434 1.17 mrg if (httpd->sslinfo) 435 1.18 mrg return 0; 436 1.7 mrg #endif 437 1.29 mrg USE_ARG(httpd); 438 1.7 mrg return fflush(fp); 439 1.1 tls } 440