1*1f83e6f0Sbluhm /* $OpenBSD: server.c,v 1.7 2019/02/21 23:06:33 bluhm Exp $ */ 29231079cSbluhm /* 3*1f83e6f0Sbluhm * Copyright (c) 2018-2019 Alexander Bluhm <bluhm@openbsd.org> 49231079cSbluhm * 59231079cSbluhm * Permission to use, copy, modify, and distribute this software for any 69231079cSbluhm * purpose with or without fee is hereby granted, provided that the above 79231079cSbluhm * copyright notice and this permission notice appear in all copies. 89231079cSbluhm * 99231079cSbluhm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 109231079cSbluhm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 119231079cSbluhm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 129231079cSbluhm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 139231079cSbluhm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 149231079cSbluhm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 159231079cSbluhm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 169231079cSbluhm */ 179231079cSbluhm 189231079cSbluhm #include <sys/types.h> 199231079cSbluhm #include <sys/socket.h> 209231079cSbluhm 219231079cSbluhm #include <err.h> 229231079cSbluhm #include <netdb.h> 239231079cSbluhm #include <stdio.h> 247ca394abSbluhm #include <stdlib.h> 2522303e31Sbluhm #include <string.h> 269231079cSbluhm #include <unistd.h> 279231079cSbluhm 289231079cSbluhm #include <openssl/err.h> 299231079cSbluhm #include <openssl/ssl.h> 309231079cSbluhm 319231079cSbluhm #include "util.h" 329231079cSbluhm 339231079cSbluhm void __dead usage(void); 349231079cSbluhm 359231079cSbluhm void __dead 369231079cSbluhm usage(void) 379231079cSbluhm { 38*1f83e6f0Sbluhm fprintf(stderr, "usage: server [-Lsvv] [-C CA] [-c crt -k key] " 39*1f83e6f0Sbluhm "[-l cipers] [-p dhparam] [host port]\n"); 409231079cSbluhm exit(2); 419231079cSbluhm } 429231079cSbluhm 439231079cSbluhm int 449231079cSbluhm main(int argc, char *argv[]) 459231079cSbluhm { 469231079cSbluhm const SSL_METHOD *method; 479231079cSbluhm SSL_CTX *ctx; 489231079cSbluhm SSL *ssl; 497ca394abSbluhm BIO *abio, *cbio; 509231079cSbluhm SSL_SESSION *session; 51*1f83e6f0Sbluhm int ch, error, listciphers = 0, sessionreuse = 0, verify = 0; 52*1f83e6f0Sbluhm char buf[256], *dhparam = NULL; 53*1f83e6f0Sbluhm char *ca = NULL, *crt = NULL, *key = NULL, *ciphers = NULL; 54a8d85e88Sbluhm char *host_port, *host = "127.0.0.1", *port = "0"; 559231079cSbluhm 56*1f83e6f0Sbluhm while ((ch = getopt(argc, argv, "C:c:k:Ll:p:sv")) != -1) { 57a8d85e88Sbluhm switch (ch) { 58a8d85e88Sbluhm case 'C': 59a8d85e88Sbluhm ca = optarg; 60a8d85e88Sbluhm break; 61a8d85e88Sbluhm case 'c': 62a8d85e88Sbluhm crt = optarg; 63a8d85e88Sbluhm break; 64a8d85e88Sbluhm case 'k': 65a8d85e88Sbluhm key = optarg; 66a8d85e88Sbluhm break; 67*1f83e6f0Sbluhm case 'L': 68*1f83e6f0Sbluhm listciphers = 1; 69*1f83e6f0Sbluhm break; 70*1f83e6f0Sbluhm case 'l': 71*1f83e6f0Sbluhm ciphers = optarg; 72*1f83e6f0Sbluhm break; 73*1f83e6f0Sbluhm case 'p': 74*1f83e6f0Sbluhm dhparam = optarg; 75*1f83e6f0Sbluhm break; 767ca394abSbluhm case 's': 777ca394abSbluhm /* multiple reueses are possible */ 787ca394abSbluhm sessionreuse++; 797ca394abSbluhm break; 80a8d85e88Sbluhm case 'v': 81a8d85e88Sbluhm /* use twice to force client cert */ 82a8d85e88Sbluhm verify++; 83a8d85e88Sbluhm break; 84a8d85e88Sbluhm default: 85a8d85e88Sbluhm usage(); 86a8d85e88Sbluhm } 87a8d85e88Sbluhm } 88a8d85e88Sbluhm argc -= optind; 89a8d85e88Sbluhm argv += optind; 90a8d85e88Sbluhm if (argc == 2) { 91a8d85e88Sbluhm host = argv[0]; 92a8d85e88Sbluhm port = argv[1]; 93*1f83e6f0Sbluhm } else if (argc != 0 && !listciphers) { 949231079cSbluhm usage(); 959231079cSbluhm } 969231079cSbluhm if (asprintf(&host_port, strchr(host, ':') ? "[%s]:%s" : "%s:%s", 979231079cSbluhm host, port) == -1) 989231079cSbluhm err(1, "asprintf host port"); 99a8d85e88Sbluhm if ((crt == NULL && key != NULL) || (crt != NULL && key == NULL)) 100a8d85e88Sbluhm errx(1, "certificate and private key must be used together"); 101a8d85e88Sbluhm if (crt == NULL && asprintf(&crt, "%s.crt", host) == -1) 1029231079cSbluhm err(1, "asprintf crt"); 103a8d85e88Sbluhm if (key == NULL && asprintf(&key, "%s.key", host) == -1) 1049231079cSbluhm err(1, "asprintf key"); 1059231079cSbluhm 1069231079cSbluhm SSL_library_init(); 1079231079cSbluhm SSL_load_error_strings(); 10822303e31Sbluhm print_version(); 1099231079cSbluhm 1109231079cSbluhm /* setup method and context */ 111188261f9Sbluhm #if OPENSSL_VERSION_NUMBER >= 0x1010000f 112188261f9Sbluhm method = TLS_server_method(); 113188261f9Sbluhm if (method == NULL) 114188261f9Sbluhm err_ssl(1, "TLS_server_method"); 115188261f9Sbluhm #else 1169231079cSbluhm method = SSLv23_server_method(); 1179231079cSbluhm if (method == NULL) 1189231079cSbluhm err_ssl(1, "SSLv23_server_method"); 119188261f9Sbluhm #endif 1209231079cSbluhm ctx = SSL_CTX_new(method); 1219231079cSbluhm if (ctx == NULL) 1229231079cSbluhm err_ssl(1, "SSL_CTX_new"); 1239231079cSbluhm 124*1f83e6f0Sbluhm #if OPENSSL_VERSION_NUMBER >= 0x10100000 125*1f83e6f0Sbluhm /* needed to use DHE cipher with libressl */ 126*1f83e6f0Sbluhm if (SSL_CTX_set_dh_auto(ctx, 1) <= 0) 127*1f83e6f0Sbluhm err_ssl(1, "SSL_CTX_set_dh_auto"); 128*1f83e6f0Sbluhm #endif 129*1f83e6f0Sbluhm /* needed to use ADH, EDH, DHE cipher with openssl */ 130*1f83e6f0Sbluhm if (dhparam != NULL) { 131*1f83e6f0Sbluhm DH *dh; 132*1f83e6f0Sbluhm FILE *file; 133*1f83e6f0Sbluhm 134*1f83e6f0Sbluhm file = fopen(dhparam, "r"); 135*1f83e6f0Sbluhm if (file == NULL) 136*1f83e6f0Sbluhm err(1, "fopen %s", dhparam); 137*1f83e6f0Sbluhm dh = PEM_read_DHparams(file, NULL, NULL, NULL); 138*1f83e6f0Sbluhm if (dh == NULL) 139*1f83e6f0Sbluhm err_ssl(1, "PEM_read_DHparams"); 140*1f83e6f0Sbluhm if (SSL_CTX_set_tmp_dh(ctx, dh) <= 0) 141*1f83e6f0Sbluhm err_ssl(1, "SSL_CTX_set_tmp_dh"); 142*1f83e6f0Sbluhm fclose(file); 143*1f83e6f0Sbluhm } 144*1f83e6f0Sbluhm 1459231079cSbluhm /* needed when linking with OpenSSL 1.0.2p */ 1469231079cSbluhm if (SSL_CTX_set_ecdh_auto(ctx, 1) <= 0) 1479231079cSbluhm err_ssl(1, "SSL_CTX_set_ecdh_auto"); 1489231079cSbluhm 1499231079cSbluhm /* load server certificate */ 1509231079cSbluhm if (SSL_CTX_use_certificate_file(ctx, crt, SSL_FILETYPE_PEM) <= 0) 1519231079cSbluhm err_ssl(1, "SSL_CTX_use_certificate_file"); 1529231079cSbluhm if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0) 1539231079cSbluhm err_ssl(1, "SSL_CTX_use_PrivateKey_file"); 1549231079cSbluhm if (SSL_CTX_check_private_key(ctx) <= 0) 1559231079cSbluhm err_ssl(1, "SSL_CTX_check_private_key"); 1569231079cSbluhm 157a8d85e88Sbluhm /* request client certificate and verify it */ 158a8d85e88Sbluhm if (ca != NULL) { 159a8d85e88Sbluhm STACK_OF(X509_NAME) *x509stack; 160a8d85e88Sbluhm 161a8d85e88Sbluhm x509stack = SSL_load_client_CA_file(ca); 162a8d85e88Sbluhm if (x509stack == NULL) 163a8d85e88Sbluhm err_ssl(1, "SSL_load_client_CA_file"); 164a8d85e88Sbluhm SSL_CTX_set_client_CA_list(ctx, x509stack); 165a8d85e88Sbluhm if (SSL_CTX_load_verify_locations(ctx, ca, NULL) <= 0) 166a8d85e88Sbluhm err_ssl(1, "SSL_CTX_load_verify_locations"); 167a8d85e88Sbluhm } 168a8d85e88Sbluhm SSL_CTX_set_verify(ctx, 169a8d85e88Sbluhm verify == 0 ? SSL_VERIFY_NONE : 170a8d85e88Sbluhm verify == 1 ? SSL_VERIFY_PEER : 171a8d85e88Sbluhm SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 172a8d85e88Sbluhm verify_callback); 173a8d85e88Sbluhm 1747ca394abSbluhm if (sessionreuse) { 1757ca394abSbluhm uint32_t context; 1767ca394abSbluhm 1777ca394abSbluhm SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); 1787ca394abSbluhm context = arc4random(); 1797ca394abSbluhm if (SSL_CTX_set_session_id_context(ctx, 1807ca394abSbluhm (unsigned char *)&context, sizeof(context)) <= 0) 1817ca394abSbluhm err_ssl(1, "SSL_CTX_set_session_id_context"); 1827ca394abSbluhm } 1837ca394abSbluhm 184*1f83e6f0Sbluhm if (ciphers) { 185*1f83e6f0Sbluhm if (SSL_CTX_set_cipher_list(ctx, ciphers) <= 0) 186*1f83e6f0Sbluhm err_ssl(1, "SSL_CTX_set_cipher_list"); 187*1f83e6f0Sbluhm } 188*1f83e6f0Sbluhm 189*1f83e6f0Sbluhm if (listciphers) { 190*1f83e6f0Sbluhm ssl = SSL_new(ctx); 191*1f83e6f0Sbluhm if (ssl == NULL) 192*1f83e6f0Sbluhm err_ssl(1, "SSL_new"); 193*1f83e6f0Sbluhm print_ciphers(SSL_get_ciphers(ssl)); 194*1f83e6f0Sbluhm return 0; 195*1f83e6f0Sbluhm } 196*1f83e6f0Sbluhm 1977ca394abSbluhm /* setup bio for socket operations */ 1987ca394abSbluhm abio = BIO_new_accept(host_port); 1997ca394abSbluhm if (abio == NULL) 2007ca394abSbluhm err_ssl(1, "BIO_new_accept"); 2017ca394abSbluhm 2027ca394abSbluhm /* bind, listen */ 2037ca394abSbluhm if (BIO_do_accept(abio) <= 0) 2047ca394abSbluhm err_ssl(1, "BIO_do_accept setup"); 2057ca394abSbluhm printf("listen "); 2067ca394abSbluhm print_sockname(abio); 2077ca394abSbluhm 2087ca394abSbluhm /* fork to background and set timeout */ 2097ca394abSbluhm if (daemon(1, 1) == -1) 2107ca394abSbluhm err(1, "daemon"); 2117ca394abSbluhm if ((int)alarm(10) == -1) 2127ca394abSbluhm err(1, "alarm"); 2137ca394abSbluhm 2147ca394abSbluhm do { 2157ca394abSbluhm /* accept connection */ 2167ca394abSbluhm if (BIO_do_accept(abio) <= 0) 2177ca394abSbluhm err_ssl(1, "BIO_do_accept wait"); 2187ca394abSbluhm cbio = BIO_pop(abio); 2197ca394abSbluhm printf("accept "); 2207ca394abSbluhm print_sockname(cbio); 2217ca394abSbluhm printf("accept "); 2227ca394abSbluhm print_peername(cbio); 2237ca394abSbluhm 2247ca394abSbluhm /* do ssl server handshake */ 2259231079cSbluhm ssl = SSL_new(ctx); 2269231079cSbluhm if (ssl == NULL) 2279231079cSbluhm err_ssl(1, "SSL_new"); 2287ca394abSbluhm SSL_set_bio(ssl, cbio, cbio); 2299231079cSbluhm if ((error = SSL_accept(ssl)) <= 0) 2309231079cSbluhm err_ssl(1, "SSL_accept %d", error); 2317ca394abSbluhm printf("session %d: %s\n", sessionreuse, 2327ca394abSbluhm SSL_session_reused(ssl) ? "reuse" : "new"); 2337ca394abSbluhm if (fflush(stdout) != 0) 2347ca394abSbluhm err(1, "fflush stdout"); 2357ca394abSbluhm 2369231079cSbluhm 2379231079cSbluhm /* print session statistics */ 2389231079cSbluhm session = SSL_get_session(ssl); 2399231079cSbluhm if (session == NULL) 2409231079cSbluhm err_ssl(1, "SSL_get_session"); 2419231079cSbluhm if (SSL_SESSION_print_fp(stdout, session) <= 0) 2429231079cSbluhm err_ssl(1, "SSL_SESSION_print_fp"); 2439231079cSbluhm 2447ca394abSbluhm /* write server greeting and read client hello over TLS */ 2459231079cSbluhm strlcpy(buf, "greeting\n", sizeof(buf)); 2469231079cSbluhm printf(">>> %s", buf); 2479231079cSbluhm if (fflush(stdout) != 0) 2489231079cSbluhm err(1, "fflush stdout"); 2499231079cSbluhm if ((error = SSL_write(ssl, buf, 9)) <= 0) 2509231079cSbluhm err_ssl(1, "SSL_write %d", error); 2519231079cSbluhm if (error != 9) 2529231079cSbluhm errx(1, "write not 9 bytes greeting: %d", error); 2539231079cSbluhm if ((error = SSL_read(ssl, buf, 6)) <= 0) 2549231079cSbluhm err_ssl(1, "SSL_read %d", error); 2559231079cSbluhm if (error != 6) 2569231079cSbluhm errx(1, "read not 6 bytes hello: %d", error); 2579231079cSbluhm buf[6] = '\0'; 2589231079cSbluhm printf("<<< %s", buf); 2599231079cSbluhm if (fflush(stdout) != 0) 2609231079cSbluhm err(1, "fflush stdout"); 2619231079cSbluhm 2629231079cSbluhm /* shutdown connection */ 2639231079cSbluhm if ((error = SSL_shutdown(ssl)) < 0) 2649231079cSbluhm err_ssl(1, "SSL_shutdown unidirectional %d", error); 2659231079cSbluhm if (error <= 0) { 2669231079cSbluhm if ((error = SSL_shutdown(ssl)) <= 0) 2677ca394abSbluhm err_ssl(1, "SSL_shutdown bidirectional %d", 2687ca394abSbluhm error); 2699231079cSbluhm } 2709231079cSbluhm 2719231079cSbluhm SSL_free(ssl); 2727ca394abSbluhm } while (sessionreuse--); 2737ca394abSbluhm 2749231079cSbluhm SSL_CTX_free(ctx); 2759231079cSbluhm 2769231079cSbluhm printf("success\n"); 2779231079cSbluhm 2789231079cSbluhm return 0; 2799231079cSbluhm } 280