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