13e07920fSDavid van Moolenbroek /* $NetBSD: tls.c,v 1.11 2013/05/27 23:15:51 christos Exp $ */
23e07920fSDavid van Moolenbroek
33e07920fSDavid van Moolenbroek /*-
43e07920fSDavid van Moolenbroek * Copyright (c) 2008 The NetBSD Foundation, Inc.
53e07920fSDavid van Moolenbroek * All rights reserved.
63e07920fSDavid van Moolenbroek *
73e07920fSDavid van Moolenbroek * This code is derived from software contributed to The NetBSD Foundation
83e07920fSDavid van Moolenbroek * by Martin Sch�tte.
93e07920fSDavid van Moolenbroek *
103e07920fSDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without
113e07920fSDavid van Moolenbroek * modification, are permitted provided that the following conditions
123e07920fSDavid van Moolenbroek * are met:
133e07920fSDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright
143e07920fSDavid van Moolenbroek * notice, this list of conditions and the following disclaimer.
153e07920fSDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright
163e07920fSDavid van Moolenbroek * notice, this list of conditions and the following disclaimer in the
173e07920fSDavid van Moolenbroek * documentation and/or other materials provided with the distribution.
183e07920fSDavid van Moolenbroek * 3. All advertising materials mentioning features or use of this software
193e07920fSDavid van Moolenbroek * must display the following acknowledgement:
203e07920fSDavid van Moolenbroek * This product includes software developed by the NetBSD
213e07920fSDavid van Moolenbroek * Foundation, Inc. and its contributors.
223e07920fSDavid van Moolenbroek * 4. Neither the name of The NetBSD Foundation nor the names of its
233e07920fSDavid van Moolenbroek * contributors may be used to endorse or promote products derived
243e07920fSDavid van Moolenbroek * from this software without specific prior written permission.
253e07920fSDavid van Moolenbroek *
263e07920fSDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
273e07920fSDavid van Moolenbroek * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
283e07920fSDavid van Moolenbroek * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
293e07920fSDavid van Moolenbroek * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
303e07920fSDavid van Moolenbroek * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
313e07920fSDavid van Moolenbroek * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
323e07920fSDavid van Moolenbroek * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
333e07920fSDavid van Moolenbroek * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
343e07920fSDavid van Moolenbroek * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
353e07920fSDavid van Moolenbroek * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
363e07920fSDavid van Moolenbroek * POSSIBILITY OF SUCH DAMAGE.
373e07920fSDavid van Moolenbroek */
383e07920fSDavid van Moolenbroek /*
393e07920fSDavid van Moolenbroek * tls.c TLS related code for syslogd
403e07920fSDavid van Moolenbroek *
413e07920fSDavid van Moolenbroek * implements the TLS init and handshake callbacks with all required
423e07920fSDavid van Moolenbroek * checks from http://tools.ietf.org/html/draft-ietf-syslog-transport-tls-13
433e07920fSDavid van Moolenbroek *
443e07920fSDavid van Moolenbroek * Martin Sch�tte
453e07920fSDavid van Moolenbroek */
463e07920fSDavid van Moolenbroek
473e07920fSDavid van Moolenbroek #include <sys/cdefs.h>
483e07920fSDavid van Moolenbroek __RCSID("$NetBSD: tls.c,v 1.11 2013/05/27 23:15:51 christos Exp $");
493e07920fSDavid van Moolenbroek
503e07920fSDavid van Moolenbroek #ifndef DISABLE_TLS
513e07920fSDavid van Moolenbroek #include "syslogd.h"
523e07920fSDavid van Moolenbroek #include "tls.h"
533e07920fSDavid van Moolenbroek #include <netinet/in.h>
543e07920fSDavid van Moolenbroek #include <ifaddrs.h>
553e07920fSDavid van Moolenbroek #include "extern.h"
563e07920fSDavid van Moolenbroek
573e07920fSDavid van Moolenbroek static unsigned getVerifySetting(const char *x509verifystring);
583e07920fSDavid van Moolenbroek
59*e1cdaee1SLionel Sambuc #if !defined(NDEBUG) && defined(__minix)
603e07920fSDavid van Moolenbroek /* to output SSL error codes */
613e07920fSDavid van Moolenbroek static const char *SSL_ERRCODE[] = {
623e07920fSDavid van Moolenbroek "SSL_ERROR_NONE",
633e07920fSDavid van Moolenbroek "SSL_ERROR_SSL",
643e07920fSDavid van Moolenbroek "SSL_ERROR_WANT_READ",
653e07920fSDavid van Moolenbroek "SSL_ERROR_WANT_WRITE",
663e07920fSDavid van Moolenbroek "SSL_ERROR_WANT_X509_LOOKUP",
673e07920fSDavid van Moolenbroek "SSL_ERROR_SYSCALL",
683e07920fSDavid van Moolenbroek "SSL_ERROR_ZERO_RETURN",
693e07920fSDavid van Moolenbroek "SSL_ERROR_WANT_CONNECT",
703e07920fSDavid van Moolenbroek "SSL_ERROR_WANT_ACCEPT"};
713e07920fSDavid van Moolenbroek /* TLS connection states -- keep in sync with symbols in .h */
723e07920fSDavid van Moolenbroek static const char *TLS_CONN_STATES[] = {
733e07920fSDavid van Moolenbroek "ST_NONE",
743e07920fSDavid van Moolenbroek "ST_TLS_EST",
753e07920fSDavid van Moolenbroek "ST_TCP_EST",
763e07920fSDavid van Moolenbroek "ST_CONNECTING",
773e07920fSDavid van Moolenbroek "ST_ACCEPTING",
783e07920fSDavid van Moolenbroek "ST_READING",
793e07920fSDavid van Moolenbroek "ST_WRITING",
803e07920fSDavid van Moolenbroek "ST_EOF",
813e07920fSDavid van Moolenbroek "ST_CLOSING0",
823e07920fSDavid van Moolenbroek "ST_CLOSING1",
833e07920fSDavid van Moolenbroek "ST_CLOSING2"};
84*e1cdaee1SLionel Sambuc #endif /* !defined(NDEBUG) && defined(__minix) */
853e07920fSDavid van Moolenbroek
863e07920fSDavid van Moolenbroek DH *get_dh1024(void);
873e07920fSDavid van Moolenbroek /* DH parameter precomputed with "openssl dhparam -C -2 1024" */
883e07920fSDavid van Moolenbroek #ifndef HEADER_DH_H
893e07920fSDavid van Moolenbroek #include <openssl/dh.h>
903e07920fSDavid van Moolenbroek #endif
913e07920fSDavid van Moolenbroek DH *
get_dh1024(void)923e07920fSDavid van Moolenbroek get_dh1024(void)
933e07920fSDavid van Moolenbroek {
943e07920fSDavid van Moolenbroek static const unsigned char dh1024_p[]={
953e07920fSDavid van Moolenbroek 0x94,0xBC,0xC4,0x71,0xD4,0xD3,0x2B,0x17,0x69,0xEA,0x82,0x1B,
963e07920fSDavid van Moolenbroek 0x0F,0x86,0x45,0x57,0xF8,0x86,0x2C,0xC8,0xF5,0x37,0x1F,0x1F,
973e07920fSDavid van Moolenbroek 0x12,0xDA,0x2C,0x62,0x4C,0xF6,0x95,0xF0,0xE4,0x6A,0x63,0x00,
983e07920fSDavid van Moolenbroek 0x32,0x54,0x5F,0xA9,0xAA,0x2E,0xD2,0xD3,0xA5,0x7A,0x4E,0xCF,
993e07920fSDavid van Moolenbroek 0xE8,0x2A,0xF6,0xAB,0xAF,0xD3,0x71,0x3E,0x75,0x9E,0x6B,0xF3,
1003e07920fSDavid van Moolenbroek 0x2E,0x6D,0x97,0x42,0xC2,0x45,0xC0,0x03,0xE1,0x17,0xA4,0x39,
1013e07920fSDavid van Moolenbroek 0xF6,0x36,0xA7,0x11,0xBD,0x30,0xF6,0x6F,0x21,0xBF,0x28,0xE4,
1023e07920fSDavid van Moolenbroek 0xF9,0xE1,0x1E,0x48,0x72,0x58,0xA9,0xC8,0x61,0x65,0xDB,0x66,
1033e07920fSDavid van Moolenbroek 0x36,0xA3,0x77,0x0A,0x81,0x79,0x2C,0x45,0x1E,0x97,0xA6,0xB1,
1043e07920fSDavid van Moolenbroek 0xD9,0x25,0x9C,0x28,0x96,0x91,0x40,0xF8,0xF6,0x86,0x11,0x9C,
1053e07920fSDavid van Moolenbroek 0x88,0xEC,0xA6,0xBA,0x9F,0x4F,0x85,0x43 };
1063e07920fSDavid van Moolenbroek static const unsigned char dh1024_g[]={ 0x02 };
1073e07920fSDavid van Moolenbroek DH *dh;
1083e07920fSDavid van Moolenbroek
1093e07920fSDavid van Moolenbroek if ((dh=DH_new()) == NULL)
1103e07920fSDavid van Moolenbroek return NULL;
1113e07920fSDavid van Moolenbroek dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
1123e07920fSDavid van Moolenbroek dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
1133e07920fSDavid van Moolenbroek if ((dh->p == NULL) || (dh->g == NULL)) {
1143e07920fSDavid van Moolenbroek DH_free(dh);
1153e07920fSDavid van Moolenbroek return NULL;
1163e07920fSDavid van Moolenbroek }
1173e07920fSDavid van Moolenbroek return dh;
1183e07920fSDavid van Moolenbroek }
1193e07920fSDavid van Moolenbroek
1203e07920fSDavid van Moolenbroek #define ST_CHANGE(x, y) do { \
1213e07920fSDavid van Moolenbroek if ((x) != (y)) { \
1223e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Change state: %s --> %s\n", \
1233e07920fSDavid van Moolenbroek TLS_CONN_STATES[x], TLS_CONN_STATES[y]); \
1243e07920fSDavid van Moolenbroek (x) = (y); \
1253e07920fSDavid van Moolenbroek } \
1263e07920fSDavid van Moolenbroek } while (/*CONSTCOND*/0)
1273e07920fSDavid van Moolenbroek
1283e07920fSDavid van Moolenbroek static unsigned
getVerifySetting(const char * x509verifystring)1293e07920fSDavid van Moolenbroek getVerifySetting(const char *x509verifystring)
1303e07920fSDavid van Moolenbroek {
1313e07920fSDavid van Moolenbroek if (!x509verifystring)
1323e07920fSDavid van Moolenbroek return X509VERIFY_ALWAYS;
1333e07920fSDavid van Moolenbroek
1343e07920fSDavid van Moolenbroek if (!strcasecmp(x509verifystring, "off"))
1353e07920fSDavid van Moolenbroek return X509VERIFY_NONE;
1363e07920fSDavid van Moolenbroek else if (!strcasecmp(x509verifystring, "opt"))
1373e07920fSDavid van Moolenbroek return X509VERIFY_IFPRESENT;
1383e07920fSDavid van Moolenbroek else
1393e07920fSDavid van Moolenbroek return X509VERIFY_ALWAYS;
1403e07920fSDavid van Moolenbroek }
1413e07920fSDavid van Moolenbroek /*
1423e07920fSDavid van Moolenbroek * init OpenSSL lib and one context.
1433e07920fSDavid van Moolenbroek * returns NULL if global context already exists.
1443e07920fSDavid van Moolenbroek * returns a status message on successfull init (to be free()d by caller).
1453e07920fSDavid van Moolenbroek * calls die() on serious error.
1463e07920fSDavid van Moolenbroek */
1473e07920fSDavid van Moolenbroek char*
init_global_TLS_CTX(void)1483e07920fSDavid van Moolenbroek init_global_TLS_CTX(void)
1493e07920fSDavid van Moolenbroek {
1503e07920fSDavid van Moolenbroek const char *keyfilename = tls_opt.keyfile;
1513e07920fSDavid van Moolenbroek const char *certfilename = tls_opt.certfile;
1523e07920fSDavid van Moolenbroek const char *CAfile = tls_opt.CAfile;
1533e07920fSDavid van Moolenbroek const char *CApath = tls_opt.CAdir;
1543e07920fSDavid van Moolenbroek
1553e07920fSDavid van Moolenbroek SSL_CTX *ctx;
1563e07920fSDavid van Moolenbroek unsigned x509verify = X509VERIFY_ALWAYS;
1573e07920fSDavid van Moolenbroek EVP_PKEY *pkey = NULL;
1583e07920fSDavid van Moolenbroek X509 *cert = NULL;
1593e07920fSDavid van Moolenbroek FILE *certfile = NULL;
1603e07920fSDavid van Moolenbroek FILE *keyfile = NULL;
1613e07920fSDavid van Moolenbroek unsigned long err;
1623e07920fSDavid van Moolenbroek char *fp = NULL, *cn = NULL;
1633e07920fSDavid van Moolenbroek
1643e07920fSDavid van Moolenbroek char statusmsg[1024];
1653e07920fSDavid van Moolenbroek
1663e07920fSDavid van Moolenbroek if (tls_opt.global_TLS_CTX) /* already initialized */
1673e07920fSDavid van Moolenbroek return NULL;
1683e07920fSDavid van Moolenbroek
1693e07920fSDavid van Moolenbroek x509verify = getVerifySetting(tls_opt.x509verify);
1703e07920fSDavid van Moolenbroek if (x509verify != X509VERIFY_ALWAYS)
1713e07920fSDavid van Moolenbroek loginfo("insecure configuration, peer authentication disabled");
1723e07920fSDavid van Moolenbroek
1733e07920fSDavid van Moolenbroek if (!(ctx = SSL_CTX_new(SSLv23_method()))) {
1743e07920fSDavid van Moolenbroek logerror("Unable to initialize OpenSSL: %s",
1753e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
1763e07920fSDavid van Moolenbroek die(0,0,NULL);
1773e07920fSDavid van Moolenbroek }
1783e07920fSDavid van Moolenbroek
1793e07920fSDavid van Moolenbroek if (!keyfilename)
1803e07920fSDavid van Moolenbroek keyfilename = DEFAULT_X509_KEYFILE;
1813e07920fSDavid van Moolenbroek if (!certfilename)
1823e07920fSDavid van Moolenbroek certfilename = DEFAULT_X509_CERTFILE;
1833e07920fSDavid van Moolenbroek
1843e07920fSDavid van Moolenbroek /* TODO: would it be better to use stat() for access checking? */
1853e07920fSDavid van Moolenbroek if (!(keyfile = fopen(keyfilename, "r"))
1863e07920fSDavid van Moolenbroek && !(certfile = fopen(certfilename, "r"))) {
1873e07920fSDavid van Moolenbroek errno = 0;
1883e07920fSDavid van Moolenbroek if (!tls_opt.gen_cert) {
1893e07920fSDavid van Moolenbroek logerror("TLS certificate files \"%s\" and \"%s\""
1903e07920fSDavid van Moolenbroek "not readable. Please configure them with "
1913e07920fSDavid van Moolenbroek "\"tls_cert\" and \"tls_key\" or set "
1923e07920fSDavid van Moolenbroek "\"tls_gen_cert=1\" to generate a new "
1933e07920fSDavid van Moolenbroek "certificate", keyfilename, certfilename);
1943e07920fSDavid van Moolenbroek die(0,0,NULL);
1953e07920fSDavid van Moolenbroek }
1963e07920fSDavid van Moolenbroek
1973e07920fSDavid van Moolenbroek loginfo("Generating a self-signed certificate and writing "
1983e07920fSDavid van Moolenbroek "files \"%s\" and \"%s\"", keyfilename, certfilename);
1993e07920fSDavid van Moolenbroek if (!mk_x509_cert(&cert, &pkey, TLS_GENCERT_BITS,
2003e07920fSDavid van Moolenbroek TLS_GENCERT_SERIAL, TLS_GENCERT_DAYS)) {
2013e07920fSDavid van Moolenbroek logerror("Unable to generate new certificate.");
2023e07920fSDavid van Moolenbroek die(0,0,NULL);
2033e07920fSDavid van Moolenbroek }
2043e07920fSDavid van Moolenbroek if (!write_x509files(pkey, cert,
2053e07920fSDavid van Moolenbroek keyfilename, certfilename)) {
2063e07920fSDavid van Moolenbroek logerror("Unable to write certificate to files \"%s\""
2073e07920fSDavid van Moolenbroek " and \"%s\"", keyfilename, certfilename);
2083e07920fSDavid van Moolenbroek /* not fatal */
2093e07920fSDavid van Moolenbroek }
2103e07920fSDavid van Moolenbroek }
2113e07920fSDavid van Moolenbroek if (keyfile)
2123e07920fSDavid van Moolenbroek (void)fclose(keyfile);
2133e07920fSDavid van Moolenbroek if (certfile)
2143e07920fSDavid van Moolenbroek (void)fclose(certfile);
2153e07920fSDavid van Moolenbroek errno = 0;
2163e07920fSDavid van Moolenbroek
2173e07920fSDavid van Moolenbroek /* if generated, then use directly */
2183e07920fSDavid van Moolenbroek if (cert && pkey) {
2193e07920fSDavid van Moolenbroek if (!SSL_CTX_use_PrivateKey(ctx, pkey)
2203e07920fSDavid van Moolenbroek || !SSL_CTX_use_certificate(ctx, cert)) {
2213e07920fSDavid van Moolenbroek logerror("Unable to use generated private "
2223e07920fSDavid van Moolenbroek "key and certificate: %s",
2233e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
2243e07920fSDavid van Moolenbroek die(0,0,NULL); /* any better reaction? */
2253e07920fSDavid van Moolenbroek }
2263e07920fSDavid van Moolenbroek } else {
2273e07920fSDavid van Moolenbroek /* load keys and certs from files */
2283e07920fSDavid van Moolenbroek if (!SSL_CTX_use_PrivateKey_file(ctx, keyfilename,
2293e07920fSDavid van Moolenbroek SSL_FILETYPE_PEM)
2303e07920fSDavid van Moolenbroek || !SSL_CTX_use_certificate_chain_file(ctx, certfilename)) {
2313e07920fSDavid van Moolenbroek logerror("Unable to load private key and "
2323e07920fSDavid van Moolenbroek "certificate from files \"%s\" and \"%s\": %s",
2333e07920fSDavid van Moolenbroek keyfilename, certfilename,
2343e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
2353e07920fSDavid van Moolenbroek die(0,0,NULL); /* any better reaction? */
2363e07920fSDavid van Moolenbroek }
2373e07920fSDavid van Moolenbroek }
2383e07920fSDavid van Moolenbroek if (!SSL_CTX_check_private_key(ctx)) {
2393e07920fSDavid van Moolenbroek logerror("Private key \"%s\" does not match "
2403e07920fSDavid van Moolenbroek "certificate \"%s\": %s",
2413e07920fSDavid van Moolenbroek keyfilename, certfilename,
2423e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
2433e07920fSDavid van Moolenbroek die(0,0,NULL);
2443e07920fSDavid van Moolenbroek }
2453e07920fSDavid van Moolenbroek
2463e07920fSDavid van Moolenbroek if (CAfile || CApath) {
2473e07920fSDavid van Moolenbroek if (SSL_CTX_load_verify_locations(ctx, CAfile, CApath) != 1) {
2483e07920fSDavid van Moolenbroek if (CAfile && CApath)
2493e07920fSDavid van Moolenbroek logerror("unable to load trust anchors from "
2503e07920fSDavid van Moolenbroek "\"%s\" and \"%s\": %s\n",
2513e07920fSDavid van Moolenbroek CAfile, CApath, ERR_error_string(
2523e07920fSDavid van Moolenbroek ERR_get_error(), NULL));
2533e07920fSDavid van Moolenbroek else
2543e07920fSDavid van Moolenbroek logerror("unable to load trust anchors from "
2553e07920fSDavid van Moolenbroek "\"%s\": %s\n", (CAfile?CAfile:CApath),
2563e07920fSDavid van Moolenbroek ERR_error_string(
2573e07920fSDavid van Moolenbroek ERR_get_error(), NULL));
2583e07920fSDavid van Moolenbroek } else {
2593e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "loaded trust anchors\n");
2603e07920fSDavid van Moolenbroek }
2613e07920fSDavid van Moolenbroek }
2623e07920fSDavid van Moolenbroek
2633e07920fSDavid van Moolenbroek /* options */
2643e07920fSDavid van Moolenbroek (void)SSL_CTX_set_options(ctx,
2653e07920fSDavid van Moolenbroek SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE);
2663e07920fSDavid van Moolenbroek (void)SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
2673e07920fSDavid van Moolenbroek
2683e07920fSDavid van Moolenbroek /* peer verification */
2693e07920fSDavid van Moolenbroek if ((x509verify == X509VERIFY_NONE)
2703e07920fSDavid van Moolenbroek || (x509verify == X509VERIFY_IFPRESENT))
2713e07920fSDavid van Moolenbroek /* ask for cert, but a client does not have to send one */
2723e07920fSDavid van Moolenbroek SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, check_peer_cert);
2733e07920fSDavid van Moolenbroek else
2743e07920fSDavid van Moolenbroek /* default: ask for cert and check it */
2753e07920fSDavid van Moolenbroek SSL_CTX_set_verify(ctx,
2763e07920fSDavid van Moolenbroek SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
2773e07920fSDavid van Moolenbroek check_peer_cert);
2783e07920fSDavid van Moolenbroek
2793e07920fSDavid van Moolenbroek if (SSL_CTX_set_tmp_dh(ctx, get_dh1024()) != 1)
2803e07920fSDavid van Moolenbroek logerror("SSL_CTX_set_tmp_dh() failed: %s",
2813e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
2823e07920fSDavid van Moolenbroek
2833e07920fSDavid van Moolenbroek /* make sure the OpenSSL error queue is empty */
2843e07920fSDavid van Moolenbroek while ((err = ERR_get_error()) != 0)
2853e07920fSDavid van Moolenbroek logerror("Unexpected OpenSSL error: %s",
2863e07920fSDavid van Moolenbroek ERR_error_string(err, NULL));
2873e07920fSDavid van Moolenbroek
2883e07920fSDavid van Moolenbroek
2893e07920fSDavid van Moolenbroek /* On successful init the status message is not logged immediately
2903e07920fSDavid van Moolenbroek * but passed to the caller. The reason is that init() can continue
2913e07920fSDavid van Moolenbroek * to initialize syslog-sign. When the status message is logged
2923e07920fSDavid van Moolenbroek * after that it will get a valid signature and not cause errors
2933e07920fSDavid van Moolenbroek * with signature verification.
2943e07920fSDavid van Moolenbroek */
2953e07920fSDavid van Moolenbroek if (cert || read_certfile(&cert, certfilename)) {
2963e07920fSDavid van Moolenbroek get_fingerprint(cert, &fp, NULL);
2973e07920fSDavid van Moolenbroek get_commonname(cert, &cn);
2983e07920fSDavid van Moolenbroek }
2993e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "loaded and checked own certificate\n");
3003e07920fSDavid van Moolenbroek snprintf(statusmsg, sizeof(statusmsg),
3013e07920fSDavid van Moolenbroek "Initialized TLS settings using library \"%s\". "
3023e07920fSDavid van Moolenbroek "Use certificate from file \"%s\" with CN \"%s\" "
3033e07920fSDavid van Moolenbroek "and fingerprint \"%s\"", SSLeay_version(SSLEAY_VERSION),
3043e07920fSDavid van Moolenbroek certfilename, cn, fp);
3053e07920fSDavid van Moolenbroek free(cn);
3063e07920fSDavid van Moolenbroek free(fp);
3073e07920fSDavid van Moolenbroek
3083e07920fSDavid van Moolenbroek tls_opt.global_TLS_CTX = ctx;
3093e07920fSDavid van Moolenbroek return strdup(statusmsg);
3103e07920fSDavid van Moolenbroek }
3113e07920fSDavid van Moolenbroek
3123e07920fSDavid van Moolenbroek
3133e07920fSDavid van Moolenbroek /*
3143e07920fSDavid van Moolenbroek * get fingerprint of cert
3153e07920fSDavid van Moolenbroek * returnstring will be allocated and should be free()d by the caller
3163e07920fSDavid van Moolenbroek * alg_name selects an algorithm, if it is NULL then DEFAULT_FINGERPRINT_ALG
3173e07920fSDavid van Moolenbroek * (should be "sha-1") will be used
3183e07920fSDavid van Moolenbroek * return value and non-NULL *returnstring indicate success
3193e07920fSDavid van Moolenbroek */
3203e07920fSDavid van Moolenbroek bool
get_fingerprint(const X509 * cert,char ** returnstring,const char * alg_name)3213e07920fSDavid van Moolenbroek get_fingerprint(const X509 *cert, char **returnstring, const char *alg_name)
3223e07920fSDavid van Moolenbroek {
3233e07920fSDavid van Moolenbroek #define MAX_ALG_NAME_LENGTH 8
3243e07920fSDavid van Moolenbroek unsigned char md[EVP_MAX_MD_SIZE];
3253e07920fSDavid van Moolenbroek char fp_val[4];
3263e07920fSDavid van Moolenbroek size_t memsize, i;
3273e07920fSDavid van Moolenbroek unsigned len;
3283e07920fSDavid van Moolenbroek const EVP_MD *digest;
3293e07920fSDavid van Moolenbroek const char *openssl_algname;
3303e07920fSDavid van Moolenbroek /* RFC nnnn uses hash function names from
3313e07920fSDavid van Moolenbroek * http://www.iana.org/assignments/hash-function-text-names/
3323e07920fSDavid van Moolenbroek * in certificate fingerprints.
3333e07920fSDavid van Moolenbroek * We have to map them to the hash function names used by OpenSSL.
3343e07920fSDavid van Moolenbroek * Actually we use the union of both namespaces to be RFC compliant
3353e07920fSDavid van Moolenbroek * and to let the user use "openssl -fingerprint ..."
3363e07920fSDavid van Moolenbroek *
3373e07920fSDavid van Moolenbroek * Intended behaviour is to prefer the IANA names,
3383e07920fSDavid van Moolenbroek * but allow the user to use OpenSSL names as well
3393e07920fSDavid van Moolenbroek * (e.g. for "RIPEMD160" wich has no IANA name)
3403e07920fSDavid van Moolenbroek */
3413e07920fSDavid van Moolenbroek static const struct hash_alg_namemap {
3423e07920fSDavid van Moolenbroek const char *iana;
3433e07920fSDavid van Moolenbroek const char *openssl;
3443e07920fSDavid van Moolenbroek } hash_alg_namemap[] = {
3453e07920fSDavid van Moolenbroek {"md2", "MD2" },
3463e07920fSDavid van Moolenbroek {"md5", "MD5" },
3473e07920fSDavid van Moolenbroek {"sha-1", "SHA1" },
3483e07920fSDavid van Moolenbroek {"sha-224", "SHA224"},
3493e07920fSDavid van Moolenbroek {"sha-256", "SHA256"},
3503e07920fSDavid van Moolenbroek {"sha-384", "SHA384"},
3513e07920fSDavid van Moolenbroek {"sha-512", "SHA512"}
3523e07920fSDavid van Moolenbroek };
3533e07920fSDavid van Moolenbroek
3543e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "get_fingerprint(cert@%p, return@%p, alg \"%s\")\n",
3553e07920fSDavid van Moolenbroek cert, returnstring, alg_name);
3563e07920fSDavid van Moolenbroek *returnstring = NULL;
3573e07920fSDavid van Moolenbroek
3583e07920fSDavid van Moolenbroek if (!alg_name)
3593e07920fSDavid van Moolenbroek alg_name = DEFAULT_FINGERPRINT_ALG;
3603e07920fSDavid van Moolenbroek openssl_algname = alg_name;
3613e07920fSDavid van Moolenbroek for (i = 0; i < A_CNT(hash_alg_namemap); i++)
3623e07920fSDavid van Moolenbroek if (!strcasecmp(alg_name, hash_alg_namemap[i].iana))
3633e07920fSDavid van Moolenbroek openssl_algname = hash_alg_namemap[i].openssl;
3643e07920fSDavid van Moolenbroek
3653e07920fSDavid van Moolenbroek if (!(digest = (const EVP_MD *) EVP_get_digestbyname(
3663e07920fSDavid van Moolenbroek __UNCONST(openssl_algname)))) {
3673e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "unknown digest algorithm %s\n",
3683e07920fSDavid van Moolenbroek openssl_algname);
3693e07920fSDavid van Moolenbroek return false;
3703e07920fSDavid van Moolenbroek }
3713e07920fSDavid van Moolenbroek if (!X509_digest(cert, digest, md, &len)) {
3723e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "cannot get %s digest\n", openssl_algname);
3733e07920fSDavid van Moolenbroek return false;
3743e07920fSDavid van Moolenbroek }
3753e07920fSDavid van Moolenbroek
3763e07920fSDavid van Moolenbroek /* 'normalise' and translate back to IANA name */
3773e07920fSDavid van Moolenbroek alg_name = openssl_algname = OBJ_nid2sn(EVP_MD_type(digest));
3783e07920fSDavid van Moolenbroek for (i = 0; i < A_CNT(hash_alg_namemap); i++)
3793e07920fSDavid van Moolenbroek if (!strcasecmp(openssl_algname, hash_alg_namemap[i].openssl))
3803e07920fSDavid van Moolenbroek alg_name = hash_alg_namemap[i].iana;
3813e07920fSDavid van Moolenbroek
3823e07920fSDavid van Moolenbroek /* needed memory: 3 string bytes for every binary byte with delimiter
3833e07920fSDavid van Moolenbroek * + max_iana_strlen with delimiter */
3843e07920fSDavid van Moolenbroek memsize = (len * 3) + strlen(alg_name) + 1;
3853e07920fSDavid van Moolenbroek MALLOC(*returnstring, memsize);
3863e07920fSDavid van Moolenbroek (void)strlcpy(*returnstring, alg_name, memsize);
3873e07920fSDavid van Moolenbroek (void)strlcat(*returnstring, ":", memsize);
3883e07920fSDavid van Moolenbroek /* append the fingeprint data */
3893e07920fSDavid van Moolenbroek for (i = 0; i < len; i++) {
3903e07920fSDavid van Moolenbroek (void)snprintf(fp_val, sizeof(fp_val),
3913e07920fSDavid van Moolenbroek "%02X:", (unsigned) md[i]);
3923e07920fSDavid van Moolenbroek (void)strlcat(*returnstring, fp_val, memsize);
3933e07920fSDavid van Moolenbroek }
3943e07920fSDavid van Moolenbroek return true;
3953e07920fSDavid van Moolenbroek }
3963e07920fSDavid van Moolenbroek
3973e07920fSDavid van Moolenbroek /*
3983e07920fSDavid van Moolenbroek * gets first CN from cert in returnstring (has to be freed by caller)
3993e07920fSDavid van Moolenbroek * on failure it returns false and *returnstring is NULL
4003e07920fSDavid van Moolenbroek */
4013e07920fSDavid van Moolenbroek bool
get_commonname(X509 * cert,char ** returnstring)4023e07920fSDavid van Moolenbroek get_commonname(X509 *cert, char **returnstring)
4033e07920fSDavid van Moolenbroek {
4043e07920fSDavid van Moolenbroek X509_NAME *x509name;
4053e07920fSDavid van Moolenbroek X509_NAME_ENTRY *entry;
4063e07920fSDavid van Moolenbroek unsigned char *ubuf;
4073e07920fSDavid van Moolenbroek int len, i;
4083e07920fSDavid van Moolenbroek
4093e07920fSDavid van Moolenbroek x509name = X509_get_subject_name(cert);
4103e07920fSDavid van Moolenbroek i = X509_NAME_get_index_by_NID(x509name, NID_commonName, -1);
4113e07920fSDavid van Moolenbroek if (i != -1) {
4123e07920fSDavid van Moolenbroek entry = X509_NAME_get_entry(x509name, i);
4133e07920fSDavid van Moolenbroek len = ASN1_STRING_to_UTF8(&ubuf,
4143e07920fSDavid van Moolenbroek X509_NAME_ENTRY_get_data(entry));
4153e07920fSDavid van Moolenbroek if (len > 0) {
4163e07920fSDavid van Moolenbroek MALLOC(*returnstring, (size_t)len+1);
4173e07920fSDavid van Moolenbroek strlcpy(*returnstring, (const char*)ubuf, len+1);
4183e07920fSDavid van Moolenbroek OPENSSL_free(ubuf);
4193e07920fSDavid van Moolenbroek return true;
4203e07920fSDavid van Moolenbroek }
4213e07920fSDavid van Moolenbroek OPENSSL_free(ubuf);
4223e07920fSDavid van Moolenbroek }
4233e07920fSDavid van Moolenbroek *returnstring = NULL;
4243e07920fSDavid van Moolenbroek return false;
4253e07920fSDavid van Moolenbroek }
4263e07920fSDavid van Moolenbroek /*
4273e07920fSDavid van Moolenbroek * test if cert matches as configured hostname or IP
4283e07920fSDavid van Moolenbroek * checks a 'really used' hostname and optionally a second expected subject
4293e07920fSDavid van Moolenbroek * against iPAddresses, dnsNames and commonNames
4303e07920fSDavid van Moolenbroek *
4313e07920fSDavid van Moolenbroek * TODO: wildcard matching for dnsNames is not implemented.
4323e07920fSDavid van Moolenbroek * in transport-tls that is a MAY, and I do not trust them anyway.
4333e07920fSDavid van Moolenbroek * but there might be demand for, so it's a todo item.
4343e07920fSDavid van Moolenbroek */
4353e07920fSDavid van Moolenbroek bool
match_hostnames(X509 * cert,const char * hostname,const char * subject)4363e07920fSDavid van Moolenbroek match_hostnames(X509 *cert, const char *hostname, const char *subject)
4373e07920fSDavid van Moolenbroek {
4383e07920fSDavid van Moolenbroek int i, len, num;
4393e07920fSDavid van Moolenbroek char *buf;
4403e07920fSDavid van Moolenbroek unsigned char *ubuf;
4413e07920fSDavid van Moolenbroek GENERAL_NAMES *gennames;
4423e07920fSDavid van Moolenbroek GENERAL_NAME *gn;
4433e07920fSDavid van Moolenbroek X509_NAME *x509name;
4443e07920fSDavid van Moolenbroek X509_NAME_ENTRY *entry;
4453e07920fSDavid van Moolenbroek ASN1_OCTET_STRING *asn1_ip, *asn1_cn_ip;
4463e07920fSDavid van Moolenbroek int crit, idx;
4473e07920fSDavid van Moolenbroek
4483e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "match_hostnames(%p, \"%s\", \"%s\")\n",
4493e07920fSDavid van Moolenbroek cert, hostname, subject);
4503e07920fSDavid van Moolenbroek
4513e07920fSDavid van Moolenbroek /* see if hostname is an IP */
4523e07920fSDavid van Moolenbroek if ((subject && (asn1_ip = a2i_IPADDRESS(subject )))
4533e07920fSDavid van Moolenbroek || (hostname && (asn1_ip = a2i_IPADDRESS(hostname))))
4543e07920fSDavid van Moolenbroek /* nothing */;
4553e07920fSDavid van Moolenbroek else
4563e07920fSDavid van Moolenbroek asn1_ip = NULL;
4573e07920fSDavid van Moolenbroek
4583e07920fSDavid van Moolenbroek if (!(gennames = X509_get_ext_d2i(cert, NID_subject_alt_name,
4593e07920fSDavid van Moolenbroek &crit, &idx))) {
4603e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "X509_get_ext_d2i() returned (%p,%d,%d) "
4613e07920fSDavid van Moolenbroek "--> no subjectAltName\n", gennames, crit, idx);
4623e07920fSDavid van Moolenbroek } else {
4633e07920fSDavid van Moolenbroek num = sk_GENERAL_NAME_num(gennames);
4643e07920fSDavid van Moolenbroek if (asn1_ip) {
4653e07920fSDavid van Moolenbroek /* first loop: check IPs */
4663e07920fSDavid van Moolenbroek for (i = 0; i < num; ++i) {
4673e07920fSDavid van Moolenbroek gn = sk_GENERAL_NAME_value(gennames, i);
4683e07920fSDavid van Moolenbroek if (gn->type == GEN_IPADD
4693e07920fSDavid van Moolenbroek && !ASN1_OCTET_STRING_cmp(asn1_ip,
4703e07920fSDavid van Moolenbroek gn->d.iPAddress))
4713e07920fSDavid van Moolenbroek return true;
4723e07920fSDavid van Moolenbroek }
4733e07920fSDavid van Moolenbroek }
4743e07920fSDavid van Moolenbroek /* second loop: check DNS names */
4753e07920fSDavid van Moolenbroek for (i = 0; i < num; ++i) {
4763e07920fSDavid van Moolenbroek gn = sk_GENERAL_NAME_value(gennames, i);
4773e07920fSDavid van Moolenbroek if (gn->type == GEN_DNS) {
4783e07920fSDavid van Moolenbroek buf = (char *)ASN1_STRING_data(gn->d.ia5);
4793e07920fSDavid van Moolenbroek len = ASN1_STRING_length(gn->d.ia5);
4803e07920fSDavid van Moolenbroek if (!strncasecmp(subject, buf, len)
4813e07920fSDavid van Moolenbroek || !strncasecmp(hostname, buf, len))
4823e07920fSDavid van Moolenbroek return true;
4833e07920fSDavid van Moolenbroek }
4843e07920fSDavid van Moolenbroek }
4853e07920fSDavid van Moolenbroek }
4863e07920fSDavid van Moolenbroek
4873e07920fSDavid van Moolenbroek /* check commonName; not sure if more than one CNs possible, but we
4883e07920fSDavid van Moolenbroek * will look at all of them */
4893e07920fSDavid van Moolenbroek x509name = X509_get_subject_name(cert);
4903e07920fSDavid van Moolenbroek i = X509_NAME_get_index_by_NID(x509name, NID_commonName, -1);
4913e07920fSDavid van Moolenbroek while (i != -1) {
4923e07920fSDavid van Moolenbroek entry = X509_NAME_get_entry(x509name, i);
4933e07920fSDavid van Moolenbroek len = ASN1_STRING_to_UTF8(&ubuf,
4943e07920fSDavid van Moolenbroek X509_NAME_ENTRY_get_data(entry));
4953e07920fSDavid van Moolenbroek if (len > 0) {
4963e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "found CN: %.*s\n", len, ubuf);
4973e07920fSDavid van Moolenbroek /* hostname */
4983e07920fSDavid van Moolenbroek if ((subject && !strncasecmp(subject,
4993e07920fSDavid van Moolenbroek (const char*)ubuf, len))
5003e07920fSDavid van Moolenbroek || (hostname && !strncasecmp(hostname,
5013e07920fSDavid van Moolenbroek (const char*)ubuf, len))) {
5023e07920fSDavid van Moolenbroek OPENSSL_free(ubuf);
5033e07920fSDavid van Moolenbroek return true;
5043e07920fSDavid van Moolenbroek }
5053e07920fSDavid van Moolenbroek OPENSSL_free(ubuf);
5063e07920fSDavid van Moolenbroek /* IP -- convert to ASN1_OCTET_STRING and compare then
5073e07920fSDavid van Moolenbroek * so that "10.1.2.3" and "10.01.02.03" are equal */
5083e07920fSDavid van Moolenbroek if ((asn1_ip)
5093e07920fSDavid van Moolenbroek && subject
5103e07920fSDavid van Moolenbroek && (asn1_cn_ip = a2i_IPADDRESS(subject))
5113e07920fSDavid van Moolenbroek && !ASN1_OCTET_STRING_cmp(asn1_ip, asn1_cn_ip)) {
5123e07920fSDavid van Moolenbroek return true;
5133e07920fSDavid van Moolenbroek }
5143e07920fSDavid van Moolenbroek }
5153e07920fSDavid van Moolenbroek i = X509_NAME_get_index_by_NID(x509name, NID_commonName, i);
5163e07920fSDavid van Moolenbroek }
5173e07920fSDavid van Moolenbroek return false;
5183e07920fSDavid van Moolenbroek }
5193e07920fSDavid van Moolenbroek
5203e07920fSDavid van Moolenbroek /*
5213e07920fSDavid van Moolenbroek * check if certificate matches given fingerprint
5223e07920fSDavid van Moolenbroek */
5233e07920fSDavid van Moolenbroek bool
match_fingerprint(const X509 * cert,const char * fingerprint)5243e07920fSDavid van Moolenbroek match_fingerprint(const X509 *cert, const char *fingerprint)
5253e07920fSDavid van Moolenbroek {
5263e07920fSDavid van Moolenbroek #define MAX_ALG_NAME_LENGTH 8
5273e07920fSDavid van Moolenbroek char alg[MAX_ALG_NAME_LENGTH];
5283e07920fSDavid van Moolenbroek char *certfingerprint;
5293e07920fSDavid van Moolenbroek char *p;
5303e07920fSDavid van Moolenbroek const char *q;
5313e07920fSDavid van Moolenbroek
5323e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "match_fingerprint(cert@%p, fp \"%s\")\n",
5333e07920fSDavid van Moolenbroek cert, fingerprint);
5343e07920fSDavid van Moolenbroek if (!fingerprint)
5353e07920fSDavid van Moolenbroek return false;
5363e07920fSDavid van Moolenbroek
5373e07920fSDavid van Moolenbroek /* get algorithm */
5383e07920fSDavid van Moolenbroek p = alg;
5393e07920fSDavid van Moolenbroek q = fingerprint;
5403e07920fSDavid van Moolenbroek while (*q != ':' && *q != '\0' && p < alg + MAX_ALG_NAME_LENGTH)
5413e07920fSDavid van Moolenbroek *p++ = *q++;
5423e07920fSDavid van Moolenbroek *p = '\0';
5433e07920fSDavid van Moolenbroek
5443e07920fSDavid van Moolenbroek if (!get_fingerprint(cert, &certfingerprint, alg)) {
5453e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "cannot get %s digest\n", alg);
5463e07920fSDavid van Moolenbroek return false;
5473e07920fSDavid van Moolenbroek }
5483e07920fSDavid van Moolenbroek if (strncmp(certfingerprint, fingerprint, strlen(certfingerprint))) {
5493e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "fail: fingerprints do not match\n");
5503e07920fSDavid van Moolenbroek free(certfingerprint);
5513e07920fSDavid van Moolenbroek return false;
5523e07920fSDavid van Moolenbroek }
5533e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "accepted: fingerprints match\n");
5543e07920fSDavid van Moolenbroek free(certfingerprint);
5553e07920fSDavid van Moolenbroek return true;
5563e07920fSDavid van Moolenbroek }
5573e07920fSDavid van Moolenbroek
5583e07920fSDavid van Moolenbroek /*
5593e07920fSDavid van Moolenbroek * check if certificate matches given certificate file
5603e07920fSDavid van Moolenbroek */
5613e07920fSDavid van Moolenbroek bool
match_certfile(const X509 * cert1,const char * certfilename)5623e07920fSDavid van Moolenbroek match_certfile(const X509 *cert1, const char *certfilename)
5633e07920fSDavid van Moolenbroek {
5643e07920fSDavid van Moolenbroek X509 *cert2;
5653e07920fSDavid van Moolenbroek char *fp1, *fp2;
5663e07920fSDavid van Moolenbroek bool rc = false;
5673e07920fSDavid van Moolenbroek errno = 0;
5683e07920fSDavid van Moolenbroek
5693e07920fSDavid van Moolenbroek if (read_certfile(&cert2, certfilename)
5703e07920fSDavid van Moolenbroek && get_fingerprint(cert1, &fp1, NULL)
5713e07920fSDavid van Moolenbroek && get_fingerprint(cert2, &fp2, NULL)) {
5723e07920fSDavid van Moolenbroek if (!strcmp(fp1, fp2))
5733e07920fSDavid van Moolenbroek rc = true;
5743e07920fSDavid van Moolenbroek FREEPTR(fp1);
5753e07920fSDavid van Moolenbroek FREEPTR(fp2);
5763e07920fSDavid van Moolenbroek }
5773e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "match_certfile(cert@%p, file \"%s\") "
5783e07920fSDavid van Moolenbroek "returns %d\n", cert1, certfilename, rc);
5793e07920fSDavid van Moolenbroek return rc;
5803e07920fSDavid van Moolenbroek }
5813e07920fSDavid van Moolenbroek
5823e07920fSDavid van Moolenbroek /*
5833e07920fSDavid van Moolenbroek * reads X.509 certificate from file
5843e07920fSDavid van Moolenbroek * caller has to free it later with 'OPENSSL_free(cert);'
5853e07920fSDavid van Moolenbroek */
5863e07920fSDavid van Moolenbroek bool
read_certfile(X509 ** cert,const char * certfilename)5873e07920fSDavid van Moolenbroek read_certfile(X509 **cert, const char *certfilename)
5883e07920fSDavid van Moolenbroek {
5893e07920fSDavid van Moolenbroek FILE *certfile;
5903e07920fSDavid van Moolenbroek errno = 0;
5913e07920fSDavid van Moolenbroek
5923e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "read_certfile(%p, \"%s\")\n",
5933e07920fSDavid van Moolenbroek cert, certfilename);
5943e07920fSDavid van Moolenbroek if (!cert || !certfilename)
5953e07920fSDavid van Moolenbroek return false;
5963e07920fSDavid van Moolenbroek
5973e07920fSDavid van Moolenbroek if (!(certfile = fopen(certfilename, "rb"))) {
5983e07920fSDavid van Moolenbroek logerror("Unable to open certificate file: %s", certfilename);
5993e07920fSDavid van Moolenbroek return false;
6003e07920fSDavid van Moolenbroek }
6013e07920fSDavid van Moolenbroek
6023e07920fSDavid van Moolenbroek /* either PEM or DER */
6033e07920fSDavid van Moolenbroek if (!(*cert = PEM_read_X509(certfile, NULL, NULL, NULL))
6043e07920fSDavid van Moolenbroek && !(*cert = d2i_X509_fp(certfile, NULL))) {
6053e07920fSDavid van Moolenbroek DPRINTF((D_TLS), "Unable to read certificate from %s\n",
6063e07920fSDavid van Moolenbroek certfilename);
6073e07920fSDavid van Moolenbroek (void)fclose(certfile);
6083e07920fSDavid van Moolenbroek return false;
6093e07920fSDavid van Moolenbroek }
6103e07920fSDavid van Moolenbroek else {
6113e07920fSDavid van Moolenbroek DPRINTF((D_TLS), "Read certificate from %s\n", certfilename);
6123e07920fSDavid van Moolenbroek (void)fclose(certfile);
6133e07920fSDavid van Moolenbroek return true;
6143e07920fSDavid van Moolenbroek }
6153e07920fSDavid van Moolenbroek }
6163e07920fSDavid van Moolenbroek
6173e07920fSDavid van Moolenbroek /* used for incoming connections in check_peer_cert() */
6183e07920fSDavid van Moolenbroek int
accept_cert(const char * reason,struct tls_conn_settings * conn_info,char * cur_fingerprint,char * cur_subjectline)6193e07920fSDavid van Moolenbroek accept_cert(const char* reason, struct tls_conn_settings *conn_info,
6203e07920fSDavid van Moolenbroek char *cur_fingerprint, char *cur_subjectline)
6213e07920fSDavid van Moolenbroek {
6223e07920fSDavid van Moolenbroek /* When using DSA keys the callback gets called twice.
6233e07920fSDavid van Moolenbroek * This flag avoids multiple log messages for the same connection.
6243e07920fSDavid van Moolenbroek */
6253e07920fSDavid van Moolenbroek if (!conn_info->accepted)
6263e07920fSDavid van Moolenbroek loginfo("Established connection and accepted %s certificate "
6273e07920fSDavid van Moolenbroek "from %s due to %s. Subject is \"%s\", fingerprint is"
6283e07920fSDavid van Moolenbroek " \"%s\"", conn_info->incoming ? "server" : "client",
6293e07920fSDavid van Moolenbroek conn_info->hostname, reason, cur_subjectline,
6303e07920fSDavid van Moolenbroek cur_fingerprint);
6313e07920fSDavid van Moolenbroek
6323e07920fSDavid van Moolenbroek if (cur_fingerprint && !conn_info->fingerprint)
6333e07920fSDavid van Moolenbroek conn_info->fingerprint = cur_fingerprint;
6343e07920fSDavid van Moolenbroek else
6353e07920fSDavid van Moolenbroek FREEPTR(cur_fingerprint);
6363e07920fSDavid van Moolenbroek
6373e07920fSDavid van Moolenbroek if (cur_subjectline && !conn_info->subject)
6383e07920fSDavid van Moolenbroek conn_info->subject = cur_subjectline;
6393e07920fSDavid van Moolenbroek else
6403e07920fSDavid van Moolenbroek FREEPTR(cur_subjectline);
6413e07920fSDavid van Moolenbroek
6423e07920fSDavid van Moolenbroek conn_info->accepted = true;
6433e07920fSDavid van Moolenbroek return 1;
6443e07920fSDavid van Moolenbroek }
6453e07920fSDavid van Moolenbroek int
deny_cert(struct tls_conn_settings * conn_info,char * cur_fingerprint,char * cur_subjectline)6463e07920fSDavid van Moolenbroek deny_cert(struct tls_conn_settings *conn_info,
6473e07920fSDavid van Moolenbroek char *cur_fingerprint, char *cur_subjectline)
6483e07920fSDavid van Moolenbroek {
6493e07920fSDavid van Moolenbroek if (!conn_info->accepted)
6503e07920fSDavid van Moolenbroek loginfo("Deny %s certificate from %s. "
6513e07920fSDavid van Moolenbroek "Subject is \"%s\", fingerprint is \"%s\"",
6523e07920fSDavid van Moolenbroek conn_info->incoming ? "client" : "server",
6533e07920fSDavid van Moolenbroek conn_info->hostname,
6543e07920fSDavid van Moolenbroek cur_subjectline, cur_fingerprint);
6553e07920fSDavid van Moolenbroek else
6563e07920fSDavid van Moolenbroek logerror("Error with TLS %s certificate authentication, "
6573e07920fSDavid van Moolenbroek "already approved certificate became invalid. "
6583e07920fSDavid van Moolenbroek "Subject is \"%s\", fingerprint is \"%s\"",
6593e07920fSDavid van Moolenbroek conn_info->incoming ? "client" : "server",
6603e07920fSDavid van Moolenbroek cur_subjectline, cur_fingerprint);
6613e07920fSDavid van Moolenbroek FREEPTR(cur_fingerprint);
6623e07920fSDavid van Moolenbroek FREEPTR(cur_subjectline);
6633e07920fSDavid van Moolenbroek return 0;
6643e07920fSDavid van Moolenbroek }
6653e07920fSDavid van Moolenbroek
6663e07920fSDavid van Moolenbroek /*
6673e07920fSDavid van Moolenbroek * Callback after OpenSSL has verified a peer certificate,
6683e07920fSDavid van Moolenbroek * gets called for every certificate in a chain (starting with root CA).
6693e07920fSDavid van Moolenbroek * preverify_ok indicates a valid trust path (necessary),
6703e07920fSDavid van Moolenbroek * then we check whether the hostname or configured subject matches the cert.
6713e07920fSDavid van Moolenbroek */
6723e07920fSDavid van Moolenbroek int
check_peer_cert(int preverify_ok,X509_STORE_CTX * ctx)6733e07920fSDavid van Moolenbroek check_peer_cert(int preverify_ok, X509_STORE_CTX *ctx)
6743e07920fSDavid van Moolenbroek {
6753e07920fSDavid van Moolenbroek char *cur_subjectline = NULL;
6763e07920fSDavid van Moolenbroek char *cur_fingerprint = NULL;
6773e07920fSDavid van Moolenbroek char cur_issuerline[256];
6783e07920fSDavid van Moolenbroek SSL *ssl;
6793e07920fSDavid van Moolenbroek X509 *cur_cert;
6803e07920fSDavid van Moolenbroek int cur_err, cur_depth;
6813e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info;
6823e07920fSDavid van Moolenbroek struct peer_cred *cred, *tmp_cred;
6833e07920fSDavid van Moolenbroek
6843e07920fSDavid van Moolenbroek /* read context info */
6853e07920fSDavid van Moolenbroek cur_cert = X509_STORE_CTX_get_current_cert(ctx);
6863e07920fSDavid van Moolenbroek cur_err = X509_STORE_CTX_get_error(ctx);
6873e07920fSDavid van Moolenbroek cur_depth = X509_STORE_CTX_get_error_depth(ctx);
6883e07920fSDavid van Moolenbroek ssl = X509_STORE_CTX_get_ex_data(ctx,
6893e07920fSDavid van Moolenbroek SSL_get_ex_data_X509_STORE_CTX_idx());
6903e07920fSDavid van Moolenbroek conn_info = SSL_get_app_data(ssl);
6913e07920fSDavid van Moolenbroek
6923e07920fSDavid van Moolenbroek /* some info */
6933e07920fSDavid van Moolenbroek (void)get_commonname(cur_cert, &cur_subjectline);
6943e07920fSDavid van Moolenbroek (void)get_fingerprint(cur_cert, &cur_fingerprint, NULL);
6953e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "check cert for connection with %s. "
6963e07920fSDavid van Moolenbroek "depth is %d, preverify is %d, subject is %s, fingerprint "
6973e07920fSDavid van Moolenbroek "is %s, conn_info@%p%s\n", conn_info->hostname, cur_depth,
6983e07920fSDavid van Moolenbroek preverify_ok, cur_subjectline, cur_fingerprint, conn_info,
6993e07920fSDavid van Moolenbroek (conn_info->accepted ? ", cb was already called" : ""));
7003e07920fSDavid van Moolenbroek
7013e07920fSDavid van Moolenbroek if (Debug && !preverify_ok) {
7023e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "openssl verify error:"
7033e07920fSDavid van Moolenbroek "num=%d:%s:depth=%d:%s\t\n", cur_err,
7043e07920fSDavid van Moolenbroek X509_verify_cert_error_string(cur_err),
7053e07920fSDavid van Moolenbroek cur_depth, cur_subjectline);
7063e07920fSDavid van Moolenbroek if (cur_err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) {
7073e07920fSDavid van Moolenbroek X509_NAME_oneline(
7083e07920fSDavid van Moolenbroek X509_get_issuer_name(ctx->current_cert),
7093e07920fSDavid van Moolenbroek cur_issuerline, sizeof(cur_issuerline));
7103e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "openssl verify error:missing "
7113e07920fSDavid van Moolenbroek "cert for issuer=%s\n", cur_issuerline);
7123e07920fSDavid van Moolenbroek }
7133e07920fSDavid van Moolenbroek }
7143e07920fSDavid van Moolenbroek
7153e07920fSDavid van Moolenbroek /*
7163e07920fSDavid van Moolenbroek * quite a lot of variables here,
7173e07920fSDavid van Moolenbroek * the big if/elseif covers all possible combinations.
7183e07920fSDavid van Moolenbroek *
7193e07920fSDavid van Moolenbroek * here is a list, ordered like the conditions below:
7203e07920fSDavid van Moolenbroek * - conn_info->x509verify
7213e07920fSDavid van Moolenbroek * X509VERIFY_NONE: do not verify certificates,
7223e07920fSDavid van Moolenbroek * only log its subject and fingerprint
7233e07920fSDavid van Moolenbroek * X509VERIFY_IFPRESENT: if we got her, then a cert is present,
7243e07920fSDavid van Moolenbroek * so check it normally
7253e07920fSDavid van Moolenbroek * X509VERIFY_ALWAYS: normal certificate check
7263e07920fSDavid van Moolenbroek * - cur_depth:
7273e07920fSDavid van Moolenbroek * > 0: peer provided CA cert. remember if its valid,
7283e07920fSDavid van Moolenbroek * but always accept, because most checks work on depth 0
7293e07920fSDavid van Moolenbroek * == 0: the peer's own cert. check this for final decision
7303e07920fSDavid van Moolenbroek * - preverify_ok:
7313e07920fSDavid van Moolenbroek * true: valid certificate chain from a trust anchor to this cert
7323e07920fSDavid van Moolenbroek * false: no valid and trusted certificate chain
7333e07920fSDavid van Moolenbroek * - conn_info->incoming:
7343e07920fSDavid van Moolenbroek * true: we are the server, means we authenticate against all
7353e07920fSDavid van Moolenbroek * allowed attributes in tls_opt
7363e07920fSDavid van Moolenbroek * false: otherwise we are client and conn_info has all attributes
7373e07920fSDavid van Moolenbroek * to check
7383e07920fSDavid van Moolenbroek * - conn_info->fingerprint (only if !conn_info->incoming)
7393e07920fSDavid van Moolenbroek * NULL: no fingerprint configured, only check certificate chain
7403e07920fSDavid van Moolenbroek * !NULL: a peer cert with this fingerprint is trusted
7413e07920fSDavid van Moolenbroek *
7423e07920fSDavid van Moolenbroek */
7433e07920fSDavid van Moolenbroek /* shortcut */
7443e07920fSDavid van Moolenbroek if (cur_depth != 0) {
7453e07920fSDavid van Moolenbroek FREEPTR(cur_fingerprint);
7463e07920fSDavid van Moolenbroek FREEPTR(cur_subjectline);
7473e07920fSDavid van Moolenbroek return 1;
7483e07920fSDavid van Moolenbroek }
7493e07920fSDavid van Moolenbroek
7503e07920fSDavid van Moolenbroek if (conn_info->x509verify == X509VERIFY_NONE)
7513e07920fSDavid van Moolenbroek return accept_cert("disabled verification", conn_info,
7523e07920fSDavid van Moolenbroek cur_fingerprint, cur_subjectline);
7533e07920fSDavid van Moolenbroek
7543e07920fSDavid van Moolenbroek /* implicit: (cur_depth == 0)
7553e07920fSDavid van Moolenbroek * && (conn_info->x509verify != X509VERIFY_NONE) */
7563e07920fSDavid van Moolenbroek if (conn_info->incoming) {
7573e07920fSDavid van Moolenbroek if (preverify_ok)
7583e07920fSDavid van Moolenbroek return accept_cert("valid certificate chain",
7593e07920fSDavid van Moolenbroek conn_info, cur_fingerprint, cur_subjectline);
7603e07920fSDavid van Moolenbroek
7613e07920fSDavid van Moolenbroek /* else: now check allowed client fingerprints/certs */
7623e07920fSDavid van Moolenbroek SLIST_FOREACH(cred, &tls_opt.fprint_head, entries) {
7633e07920fSDavid van Moolenbroek if (match_fingerprint(cur_cert, cred->data)) {
7643e07920fSDavid van Moolenbroek return accept_cert("matching fingerprint",
7653e07920fSDavid van Moolenbroek conn_info, cur_fingerprint,
7663e07920fSDavid van Moolenbroek cur_subjectline);
7673e07920fSDavid van Moolenbroek }
7683e07920fSDavid van Moolenbroek }
7693e07920fSDavid van Moolenbroek SLIST_FOREACH_SAFE(cred, &tls_opt.cert_head,
7703e07920fSDavid van Moolenbroek entries, tmp_cred) {
7713e07920fSDavid van Moolenbroek if (match_certfile(cur_cert, cred->data))
7723e07920fSDavid van Moolenbroek return accept_cert("matching certfile",
7733e07920fSDavid van Moolenbroek conn_info, cur_fingerprint,
7743e07920fSDavid van Moolenbroek cur_subjectline);
7753e07920fSDavid van Moolenbroek }
7763e07920fSDavid van Moolenbroek return deny_cert(conn_info, cur_fingerprint, cur_subjectline);
7773e07920fSDavid van Moolenbroek }
7783e07920fSDavid van Moolenbroek
7793e07920fSDavid van Moolenbroek /* implicit: (cur_depth == 0)
7803e07920fSDavid van Moolenbroek * && (conn_info->x509verify != X509VERIFY_NONE)
7813e07920fSDavid van Moolenbroek * && !conn_info->incoming */
7823e07920fSDavid van Moolenbroek if (!conn_info->incoming && preverify_ok) {
7833e07920fSDavid van Moolenbroek /* certificate chain OK. check subject/hostname */
7843e07920fSDavid van Moolenbroek if (match_hostnames(cur_cert, conn_info->hostname,
7853e07920fSDavid van Moolenbroek conn_info->subject))
7863e07920fSDavid van Moolenbroek return accept_cert("matching hostname/subject",
7873e07920fSDavid van Moolenbroek conn_info, cur_fingerprint, cur_subjectline);
7883e07920fSDavid van Moolenbroek else
7893e07920fSDavid van Moolenbroek return deny_cert(conn_info, cur_fingerprint,
7903e07920fSDavid van Moolenbroek cur_subjectline);
7913e07920fSDavid van Moolenbroek } else if (!conn_info->incoming && !preverify_ok) {
7923e07920fSDavid van Moolenbroek /* chain not OK. check fingerprint/subject/hostname */
7933e07920fSDavid van Moolenbroek if (match_fingerprint(cur_cert, conn_info->fingerprint))
7943e07920fSDavid van Moolenbroek return accept_cert("matching fingerprint", conn_info,
7953e07920fSDavid van Moolenbroek cur_fingerprint, cur_subjectline);
7963e07920fSDavid van Moolenbroek else if (match_certfile(cur_cert, conn_info->certfile))
7973e07920fSDavid van Moolenbroek return accept_cert("matching certfile", conn_info,
7983e07920fSDavid van Moolenbroek cur_fingerprint, cur_subjectline);
7993e07920fSDavid van Moolenbroek else
8003e07920fSDavid van Moolenbroek return deny_cert(conn_info, cur_fingerprint,
8013e07920fSDavid van Moolenbroek cur_subjectline);
8023e07920fSDavid van Moolenbroek }
8033e07920fSDavid van Moolenbroek
8043e07920fSDavid van Moolenbroek FREEPTR(cur_fingerprint);
8053e07920fSDavid van Moolenbroek FREEPTR(cur_subjectline);
8063e07920fSDavid van Moolenbroek return 0;
8073e07920fSDavid van Moolenbroek }
8083e07920fSDavid van Moolenbroek
8093e07920fSDavid van Moolenbroek /*
8103e07920fSDavid van Moolenbroek * Create TCP sockets for incoming TLS connections.
8113e07920fSDavid van Moolenbroek * To be used like socksetup(), hostname and port are optional,
8123e07920fSDavid van Moolenbroek * returns bound stream sockets.
8133e07920fSDavid van Moolenbroek */
8143e07920fSDavid van Moolenbroek struct socketEvent *
socksetup_tls(const int af,const char * bindhostname,const char * port)8153e07920fSDavid van Moolenbroek socksetup_tls(const int af, const char *bindhostname, const char *port)
8163e07920fSDavid van Moolenbroek {
8173e07920fSDavid van Moolenbroek struct addrinfo hints, *res, *r;
8183e07920fSDavid van Moolenbroek int error, maxs;
8193e07920fSDavid van Moolenbroek const int on = 1;
8203e07920fSDavid van Moolenbroek struct socketEvent *s, *socks;
8213e07920fSDavid van Moolenbroek
8223e07920fSDavid van Moolenbroek if(!tls_opt.server
8233e07920fSDavid van Moolenbroek || !tls_opt.global_TLS_CTX)
8243e07920fSDavid van Moolenbroek return NULL;
8253e07920fSDavid van Moolenbroek
8263e07920fSDavid van Moolenbroek memset(&hints, 0, sizeof(hints));
8273e07920fSDavid van Moolenbroek hints.ai_flags = AI_PASSIVE;
8283e07920fSDavid van Moolenbroek hints.ai_family = af;
8293e07920fSDavid van Moolenbroek hints.ai_socktype = SOCK_STREAM;
8303e07920fSDavid van Moolenbroek
8313e07920fSDavid van Moolenbroek error = getaddrinfo(bindhostname, (port ? port : "syslog-tls"),
8323e07920fSDavid van Moolenbroek &hints, &res);
8333e07920fSDavid van Moolenbroek if (error) {
8343e07920fSDavid van Moolenbroek logerror("%s", gai_strerror(error));
8353e07920fSDavid van Moolenbroek errno = 0;
8363e07920fSDavid van Moolenbroek die(0, 0, NULL);
8373e07920fSDavid van Moolenbroek }
8383e07920fSDavid van Moolenbroek
8393e07920fSDavid van Moolenbroek /* Count max number of sockets we may open */
8403e07920fSDavid van Moolenbroek for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
8413e07920fSDavid van Moolenbroek continue;
8423e07920fSDavid van Moolenbroek socks = malloc((maxs+1) * sizeof(*socks));
8433e07920fSDavid van Moolenbroek if (!socks) {
8443e07920fSDavid van Moolenbroek logerror("Unable to allocate memory for sockets");
8453e07920fSDavid van Moolenbroek die(0, 0, NULL);
8463e07920fSDavid van Moolenbroek }
8473e07920fSDavid van Moolenbroek
8483e07920fSDavid van Moolenbroek socks->fd = 0; /* num of sockets counter at start of array */
8493e07920fSDavid van Moolenbroek s = socks + 1;
8503e07920fSDavid van Moolenbroek for (r = res; r; r = r->ai_next) {
8513e07920fSDavid van Moolenbroek if ((s->fd = socket(r->ai_family, r->ai_socktype,
8523e07920fSDavid van Moolenbroek r->ai_protocol)) == -1) {
8533e07920fSDavid van Moolenbroek logerror("socket() failed: %s", strerror(errno));
8543e07920fSDavid van Moolenbroek continue;
8553e07920fSDavid van Moolenbroek }
8563e07920fSDavid van Moolenbroek s->af = r->ai_family;
857ebfedea0SLionel Sambuc #if defined(__minix) && defined(INET6)
8583e07920fSDavid van Moolenbroek if (r->ai_family == AF_INET6
8593e07920fSDavid van Moolenbroek && setsockopt(s->fd, IPPROTO_IPV6, IPV6_V6ONLY,
8603e07920fSDavid van Moolenbroek &on, sizeof(on)) == -1) {
8613e07920fSDavid van Moolenbroek logerror("setsockopt(IPV6_V6ONLY) failed: %s",
8623e07920fSDavid van Moolenbroek strerror(errno));
8633e07920fSDavid van Moolenbroek close(s->fd);
8643e07920fSDavid van Moolenbroek continue;
8653e07920fSDavid van Moolenbroek }
866ebfedea0SLionel Sambuc #endif /* defined(__minix) && defined(INET6) */
8673e07920fSDavid van Moolenbroek if (setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR,
8683e07920fSDavid van Moolenbroek &on, sizeof(on)) == -1) {
8693e07920fSDavid van Moolenbroek DPRINTF(D_NET, "Unable to setsockopt(): %s\n",
8703e07920fSDavid van Moolenbroek strerror(errno));
8713e07920fSDavid van Moolenbroek }
8723e07920fSDavid van Moolenbroek if ((error = bind(s->fd, r->ai_addr, r->ai_addrlen)) == -1) {
8733e07920fSDavid van Moolenbroek logerror("bind() failed: %s", strerror(errno));
8743e07920fSDavid van Moolenbroek /* is there a better way to handle a EADDRINUSE? */
8753e07920fSDavid van Moolenbroek close(s->fd);
8763e07920fSDavid van Moolenbroek continue;
8773e07920fSDavid van Moolenbroek }
8783e07920fSDavid van Moolenbroek if (listen(s->fd, TLSBACKLOG) == -1) {
8793e07920fSDavid van Moolenbroek logerror("listen() failed: %s", strerror(errno));
8803e07920fSDavid van Moolenbroek close(s->fd);
8813e07920fSDavid van Moolenbroek continue;
8823e07920fSDavid van Moolenbroek }
8833e07920fSDavid van Moolenbroek s->ev = allocev();
8843e07920fSDavid van Moolenbroek event_set(s->ev, s->fd, EV_READ | EV_PERSIST,
8853e07920fSDavid van Moolenbroek dispatch_socket_accept, s->ev);
8863e07920fSDavid van Moolenbroek EVENT_ADD(s->ev);
8873e07920fSDavid van Moolenbroek
8883e07920fSDavid van Moolenbroek socks->fd = socks->fd + 1; /* num counter */
8893e07920fSDavid van Moolenbroek s++;
8903e07920fSDavid van Moolenbroek }
8913e07920fSDavid van Moolenbroek
8923e07920fSDavid van Moolenbroek if (socks->fd == 0) {
8933e07920fSDavid van Moolenbroek free (socks);
8943e07920fSDavid van Moolenbroek if(Debug)
8953e07920fSDavid van Moolenbroek return NULL;
8963e07920fSDavid van Moolenbroek else
8973e07920fSDavid van Moolenbroek die(0, 0, NULL);
8983e07920fSDavid van Moolenbroek }
8993e07920fSDavid van Moolenbroek if (res)
9003e07920fSDavid van Moolenbroek freeaddrinfo(res);
9013e07920fSDavid van Moolenbroek
9023e07920fSDavid van Moolenbroek return socks;
9033e07920fSDavid van Moolenbroek }
9043e07920fSDavid van Moolenbroek
9053e07920fSDavid van Moolenbroek /*
9063e07920fSDavid van Moolenbroek * Dispatch routine for non-blocking SSL_connect()
9073e07920fSDavid van Moolenbroek * Has to be idempotent in case of TLS_RETRY (~ EAGAIN),
9083e07920fSDavid van Moolenbroek * so we can continue a slow handshake.
9093e07920fSDavid van Moolenbroek */
9103e07920fSDavid van Moolenbroek /*ARGSUSED*/
9113e07920fSDavid van Moolenbroek void
dispatch_SSL_connect(int fd,short event,void * arg)9123e07920fSDavid van Moolenbroek dispatch_SSL_connect(int fd, short event, void *arg)
9133e07920fSDavid van Moolenbroek {
9143e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info = (struct tls_conn_settings *) arg;
9153e07920fSDavid van Moolenbroek SSL *ssl = conn_info->sslptr;
9163e07920fSDavid van Moolenbroek int rc, error;
9173e07920fSDavid van Moolenbroek sigset_t newmask, omask;
9183e07920fSDavid van Moolenbroek struct timeval tv;
9193e07920fSDavid van Moolenbroek
9203e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
9213e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "dispatch_SSL_connect(conn_info@%p, fd %d)\n",
9223e07920fSDavid van Moolenbroek conn_info, fd);
9233e07920fSDavid van Moolenbroek assert(conn_info->state == ST_TCP_EST
9243e07920fSDavid van Moolenbroek || conn_info->state == ST_CONNECTING);
9253e07920fSDavid van Moolenbroek
9263e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_CONNECTING);
9273e07920fSDavid van Moolenbroek rc = SSL_connect(ssl);
9283e07920fSDavid van Moolenbroek if (0 >= rc) {
9293e07920fSDavid van Moolenbroek error = tls_examine_error("SSL_connect()",
9303e07920fSDavid van Moolenbroek conn_info->sslptr, NULL, rc);
9313e07920fSDavid van Moolenbroek switch (error) {
9323e07920fSDavid van Moolenbroek case TLS_RETRY_READ:
9333e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_READ,
9343e07920fSDavid van Moolenbroek dispatch_SSL_connect, conn_info);
9353e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->retryevent);
9363e07920fSDavid van Moolenbroek break;
9373e07920fSDavid van Moolenbroek case TLS_RETRY_WRITE:
9383e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_WRITE,
9393e07920fSDavid van Moolenbroek dispatch_SSL_connect, conn_info);
9403e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->retryevent);
9413e07920fSDavid van Moolenbroek break;
9423e07920fSDavid van Moolenbroek default: /* should not happen,
9433e07920fSDavid van Moolenbroek * ... but does if the cert is not accepted */
9443e07920fSDavid van Moolenbroek logerror("Cannot establish TLS connection "
9453e07920fSDavid van Moolenbroek "to \"%s\" -- TLS handshake aborted "
9463e07920fSDavid van Moolenbroek "before certificate authentication.",
9473e07920fSDavid van Moolenbroek conn_info->hostname);
9483e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_NONE);
9493e07920fSDavid van Moolenbroek conn_info->reconnect = 5 * TLS_RECONNECT_SEC;
9503e07920fSDavid van Moolenbroek tv.tv_sec = conn_info->reconnect;
9513e07920fSDavid van Moolenbroek tv.tv_usec = 0;
9523e07920fSDavid van Moolenbroek schedule_event(&conn_info->event, &tv,
9533e07920fSDavid van Moolenbroek tls_reconnect, conn_info);
9543e07920fSDavid van Moolenbroek break;
9553e07920fSDavid van Moolenbroek }
9563e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
9573e07920fSDavid van Moolenbroek return;
9583e07920fSDavid van Moolenbroek }
9593e07920fSDavid van Moolenbroek /* else */
9603e07920fSDavid van Moolenbroek conn_info->reconnect = TLS_RECONNECT_SEC;
9613e07920fSDavid van Moolenbroek event_set(conn_info->event, fd, EV_READ, dispatch_tls_eof, conn_info);
9623e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->event);
9633e07920fSDavid van Moolenbroek
9643e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "TLS connection established.\n");
9653e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TLS_EST);
9663e07920fSDavid van Moolenbroek
9673e07920fSDavid van Moolenbroek send_queue(0, 0, get_f_by_conninfo(conn_info));
9683e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
9693e07920fSDavid van Moolenbroek }
9703e07920fSDavid van Moolenbroek
9713e07920fSDavid van Moolenbroek /*
9723e07920fSDavid van Moolenbroek * establish TLS connection
9733e07920fSDavid van Moolenbroek */
9743e07920fSDavid van Moolenbroek bool
tls_connect(struct tls_conn_settings * conn_info)9753e07920fSDavid van Moolenbroek tls_connect(struct tls_conn_settings *conn_info)
9763e07920fSDavid van Moolenbroek {
9773e07920fSDavid van Moolenbroek struct addrinfo hints, *res, *res1;
9783e07920fSDavid van Moolenbroek int error, rc, sock;
9793e07920fSDavid van Moolenbroek const int one = 1;
9803e07920fSDavid van Moolenbroek char buf[MAXLINE];
9813e07920fSDavid van Moolenbroek SSL *ssl = NULL;
9823e07920fSDavid van Moolenbroek
9833e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "tls_connect(conn_info@%p)\n", conn_info);
9843e07920fSDavid van Moolenbroek assert(conn_info->state == ST_NONE);
9853e07920fSDavid van Moolenbroek
9863e07920fSDavid van Moolenbroek if(!tls_opt.global_TLS_CTX)
9873e07920fSDavid van Moolenbroek return false;
9883e07920fSDavid van Moolenbroek
9893e07920fSDavid van Moolenbroek memset(&hints, 0, sizeof(hints));
9903e07920fSDavid van Moolenbroek hints.ai_family = AF_UNSPEC;
9913e07920fSDavid van Moolenbroek hints.ai_socktype = SOCK_STREAM;
9923e07920fSDavid van Moolenbroek hints.ai_protocol = 0;
9933e07920fSDavid van Moolenbroek hints.ai_flags = AI_CANONNAME;
9943e07920fSDavid van Moolenbroek error = getaddrinfo(conn_info->hostname,
9953e07920fSDavid van Moolenbroek (conn_info->port ? conn_info->port : "syslog-tls"), &hints, &res);
9963e07920fSDavid van Moolenbroek if (error) {
9973e07920fSDavid van Moolenbroek logerror("%s", gai_strerror(error));
9983e07920fSDavid van Moolenbroek return false;
9993e07920fSDavid van Moolenbroek }
10003e07920fSDavid van Moolenbroek
10013e07920fSDavid van Moolenbroek sock = -1;
10023e07920fSDavid van Moolenbroek for (res1 = res; res1; res1 = res1->ai_next) {
10033e07920fSDavid van Moolenbroek if ((sock = socket(res1->ai_family, res1->ai_socktype,
10043e07920fSDavid van Moolenbroek res1->ai_protocol)) == -1) {
10053e07920fSDavid van Moolenbroek DPRINTF(D_NET, "Unable to open socket.\n");
10063e07920fSDavid van Moolenbroek continue;
10073e07920fSDavid van Moolenbroek }
10083e07920fSDavid van Moolenbroek if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
10093e07920fSDavid van Moolenbroek &one, sizeof(one)) == -1) {
10103e07920fSDavid van Moolenbroek DPRINTF(D_NET, "Unable to setsockopt(): %s\n",
10113e07920fSDavid van Moolenbroek strerror(errno));
10123e07920fSDavid van Moolenbroek }
10133e07920fSDavid van Moolenbroek if (connect(sock, res1->ai_addr, res1->ai_addrlen) == -1) {
10143e07920fSDavid van Moolenbroek DPRINTF(D_NET, "Unable to connect() to %s: %s\n",
10153e07920fSDavid van Moolenbroek res1->ai_canonname, strerror(errno));
10163e07920fSDavid van Moolenbroek close(sock);
10173e07920fSDavid van Moolenbroek sock = -1;
10183e07920fSDavid van Moolenbroek continue;
10193e07920fSDavid van Moolenbroek }
10203e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TCP_EST);
10213e07920fSDavid van Moolenbroek
10223e07920fSDavid van Moolenbroek if (!(ssl = SSL_new(tls_opt.global_TLS_CTX))) {
10233e07920fSDavid van Moolenbroek ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
10243e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Unable to establish TLS: %s\n", buf);
10253e07920fSDavid van Moolenbroek close(sock);
10263e07920fSDavid van Moolenbroek sock = -1;
10273e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_NONE);
10283e07920fSDavid van Moolenbroek continue;
10293e07920fSDavid van Moolenbroek }
10303e07920fSDavid van Moolenbroek if (!SSL_set_fd(ssl, sock)) {
10313e07920fSDavid van Moolenbroek ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
10323e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Unable to connect TLS to socket: %s\n",
10333e07920fSDavid van Moolenbroek buf);
10343e07920fSDavid van Moolenbroek FREE_SSL(ssl);
10353e07920fSDavid van Moolenbroek close(sock);
10363e07920fSDavid van Moolenbroek sock = -1;
10373e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_NONE);
10383e07920fSDavid van Moolenbroek continue;
10393e07920fSDavid van Moolenbroek }
10403e07920fSDavid van Moolenbroek
10413e07920fSDavid van Moolenbroek SSL_set_app_data(ssl, conn_info);
10423e07920fSDavid van Moolenbroek SSL_set_connect_state(ssl);
10433e07920fSDavid van Moolenbroek while ((rc = ERR_get_error()) != 0) {
10443e07920fSDavid van Moolenbroek ERR_error_string_n(rc, buf, sizeof(buf));
10453e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Found SSL error in queue: %s\n", buf);
10463e07920fSDavid van Moolenbroek }
10473e07920fSDavid van Moolenbroek errno = 0; /* reset to be sure we get the right one later on */
10483e07920fSDavid van Moolenbroek
10493e07920fSDavid van Moolenbroek if ((fcntl(sock, F_SETFL, O_NONBLOCK)) == -1) {
10503e07920fSDavid van Moolenbroek DPRINTF(D_NET, "Unable to fcntl(sock, O_NONBLOCK): "
10513e07920fSDavid van Moolenbroek "%s\n", strerror(errno));
10523e07920fSDavid van Moolenbroek }
10533e07920fSDavid van Moolenbroek
10543e07920fSDavid van Moolenbroek /* now we have a TCP connection, so assume we can
10553e07920fSDavid van Moolenbroek * use that and do not have to try another res */
10563e07920fSDavid van Moolenbroek conn_info->sslptr = ssl;
10573e07920fSDavid van Moolenbroek
10583e07920fSDavid van Moolenbroek assert(conn_info->state == ST_TCP_EST);
10593e07920fSDavid van Moolenbroek assert(conn_info->event);
10603e07920fSDavid van Moolenbroek assert(conn_info->retryevent);
10613e07920fSDavid van Moolenbroek
10623e07920fSDavid van Moolenbroek freeaddrinfo(res);
10633e07920fSDavid van Moolenbroek dispatch_SSL_connect(sock, 0, conn_info);
10643e07920fSDavid van Moolenbroek return true;
10653e07920fSDavid van Moolenbroek }
10663e07920fSDavid van Moolenbroek /* still no connection after for loop */
10673e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_NET), "Unable to establish a TCP connection to %s\n",
10683e07920fSDavid van Moolenbroek conn_info->hostname);
10693e07920fSDavid van Moolenbroek freeaddrinfo(res);
10703e07920fSDavid van Moolenbroek
10713e07920fSDavid van Moolenbroek assert(conn_info->state == ST_NONE);
10723e07920fSDavid van Moolenbroek if (sock != -1)
10733e07920fSDavid van Moolenbroek close(sock);
10743e07920fSDavid van Moolenbroek if (ssl) {
10753e07920fSDavid van Moolenbroek SSL_shutdown(ssl);
10763e07920fSDavid van Moolenbroek SSL_free(ssl);
10773e07920fSDavid van Moolenbroek }
10783e07920fSDavid van Moolenbroek return false;
10793e07920fSDavid van Moolenbroek }
10803e07920fSDavid van Moolenbroek
10813e07920fSDavid van Moolenbroek int
tls_examine_error(const char * functionname,const SSL * ssl,struct tls_conn_settings * tls_conn,const int rc)10823e07920fSDavid van Moolenbroek tls_examine_error(const char *functionname, const SSL *ssl,
10833e07920fSDavid van Moolenbroek struct tls_conn_settings *tls_conn, const int rc)
10843e07920fSDavid van Moolenbroek {
10853e07920fSDavid van Moolenbroek int ssl_error, err_error;
10863e07920fSDavid van Moolenbroek
10873e07920fSDavid van Moolenbroek ssl_error = SSL_get_error(ssl, rc);
10883e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "%s returned rc %d and error %s: %s\n", functionname,
10893e07920fSDavid van Moolenbroek rc, SSL_ERRCODE[ssl_error], ERR_error_string(ssl_error, NULL));
10903e07920fSDavid van Moolenbroek switch (ssl_error) {
10913e07920fSDavid van Moolenbroek case SSL_ERROR_WANT_READ:
10923e07920fSDavid van Moolenbroek return TLS_RETRY_READ;
10933e07920fSDavid van Moolenbroek case SSL_ERROR_WANT_WRITE:
10943e07920fSDavid van Moolenbroek return TLS_RETRY_WRITE;
10953e07920fSDavid van Moolenbroek case SSL_ERROR_SYSCALL:
10963e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "SSL_ERROR_SYSCALL: ");
10973e07920fSDavid van Moolenbroek err_error = ERR_get_error();
10983e07920fSDavid van Moolenbroek if ((rc == -1) && (err_error == 0)) {
10993e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "socket I/O error: %s\n",
11003e07920fSDavid van Moolenbroek strerror(errno));
11013e07920fSDavid van Moolenbroek } else if ((rc == 0) && (err_error == 0)) {
11023e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "unexpected EOF from %s\n",
11033e07920fSDavid van Moolenbroek tls_conn ? tls_conn->hostname : NULL);
11043e07920fSDavid van Moolenbroek } else {
11053e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "no further info\n");
11063e07920fSDavid van Moolenbroek }
11073e07920fSDavid van Moolenbroek return TLS_PERM_ERROR;
11083e07920fSDavid van Moolenbroek case SSL_ERROR_ZERO_RETURN:
11093e07920fSDavid van Moolenbroek logerror("TLS connection closed by %s",
11103e07920fSDavid van Moolenbroek tls_conn ? tls_conn->hostname : NULL);
11113e07920fSDavid van Moolenbroek return TLS_PERM_ERROR;
11123e07920fSDavid van Moolenbroek case SSL_ERROR_SSL:
11133e07920fSDavid van Moolenbroek logerror("internal SSL error, error queue gives %s",
11143e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
11153e07920fSDavid van Moolenbroek return TLS_PERM_ERROR;
11163e07920fSDavid van Moolenbroek default:
11173e07920fSDavid van Moolenbroek break;
11183e07920fSDavid van Moolenbroek }
11193e07920fSDavid van Moolenbroek if (tls_conn)
11203e07920fSDavid van Moolenbroek tls_conn->errorcount++;
11213e07920fSDavid van Moolenbroek /* TODO: is this ever reached? */
11223e07920fSDavid van Moolenbroek return TLS_TEMP_ERROR;
11233e07920fSDavid van Moolenbroek }
11243e07920fSDavid van Moolenbroek
11253e07920fSDavid van Moolenbroek
11263e07920fSDavid van Moolenbroek bool
parse_tls_destination(const char * p,struct filed * f,size_t linenum)11273e07920fSDavid van Moolenbroek parse_tls_destination(const char *p, struct filed *f, size_t linenum)
11283e07920fSDavid van Moolenbroek {
11293e07920fSDavid van Moolenbroek const char *q;
11303e07920fSDavid van Moolenbroek
11313e07920fSDavid van Moolenbroek if ((*p++ != '@') || *p++ != '[') {
11323e07920fSDavid van Moolenbroek logerror("parse_tls_destination() on non-TLS action "
11333e07920fSDavid van Moolenbroek "in config line %zu", linenum);
11343e07920fSDavid van Moolenbroek return false;
11353e07920fSDavid van Moolenbroek }
11363e07920fSDavid van Moolenbroek
11373e07920fSDavid van Moolenbroek if (!(q = strchr(p, ']'))) {
11383e07920fSDavid van Moolenbroek logerror("Unterminated [ "
11393e07920fSDavid van Moolenbroek "in config line %zu", linenum);
11403e07920fSDavid van Moolenbroek return false;
11413e07920fSDavid van Moolenbroek }
11423e07920fSDavid van Moolenbroek
11433e07920fSDavid van Moolenbroek if (!(f->f_un.f_tls.tls_conn =
11443e07920fSDavid van Moolenbroek calloc(1, sizeof(*f->f_un.f_tls.tls_conn)))
11453e07920fSDavid van Moolenbroek || !(f->f_un.f_tls.tls_conn->event = allocev())
11463e07920fSDavid van Moolenbroek || !(f->f_un.f_tls.tls_conn->retryevent = allocev())) {
11473e07920fSDavid van Moolenbroek if (f->f_un.f_tls.tls_conn)
11483e07920fSDavid van Moolenbroek free(f->f_un.f_tls.tls_conn->event);
11493e07920fSDavid van Moolenbroek free(f->f_un.f_tls.tls_conn);
11503e07920fSDavid van Moolenbroek logerror("Couldn't allocate memory for TLS config");
11513e07920fSDavid van Moolenbroek return false;
11523e07920fSDavid van Moolenbroek }
11533e07920fSDavid van Moolenbroek /* default values */
11543e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->x509verify = X509VERIFY_ALWAYS;
11553e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->reconnect = TLS_RECONNECT_SEC;
11563e07920fSDavid van Moolenbroek
11573e07920fSDavid van Moolenbroek if (!(copy_string(&(f->f_un.f_tls.tls_conn->hostname), p, q))) {
11583e07920fSDavid van Moolenbroek logerror("Unable to read TLS server name"
11593e07920fSDavid van Moolenbroek "in config line %zu", linenum);
11603e07920fSDavid van Moolenbroek free_tls_conn(f->f_un.f_tls.tls_conn);
11613e07920fSDavid van Moolenbroek return false;
11623e07920fSDavid van Moolenbroek }
11633e07920fSDavid van Moolenbroek p = ++q;
11643e07920fSDavid van Moolenbroek
11653e07920fSDavid van Moolenbroek if (*p == ':') {
11663e07920fSDavid van Moolenbroek p++; q++;
11673e07920fSDavid van Moolenbroek while (isalnum((unsigned char)*q))
11683e07920fSDavid van Moolenbroek q++;
11693e07920fSDavid van Moolenbroek if (!(copy_string(&(f->f_un.f_tls.tls_conn->port), p, q))) {
11703e07920fSDavid van Moolenbroek logerror("Unable to read TLS port or service name"
11713e07920fSDavid van Moolenbroek " after ':' in config line %zu", linenum);
11723e07920fSDavid van Moolenbroek free_tls_conn(f->f_un.f_tls.tls_conn);
11733e07920fSDavid van Moolenbroek return false;
11743e07920fSDavid van Moolenbroek }
11753e07920fSDavid van Moolenbroek p = q;
11763e07920fSDavid van Moolenbroek }
11773e07920fSDavid van Moolenbroek /* allow whitespace for readability? */
11783e07920fSDavid van Moolenbroek while (isblank((unsigned char)*p))
11793e07920fSDavid van Moolenbroek p++;
11803e07920fSDavid van Moolenbroek if (*p == '(') {
11813e07920fSDavid van Moolenbroek p++;
11823e07920fSDavid van Moolenbroek while (*p != ')') {
11833e07920fSDavid van Moolenbroek if (copy_config_value_quoted("subject=\"",
11843e07920fSDavid van Moolenbroek &(f->f_un.f_tls.tls_conn->subject), &p)
11853e07920fSDavid van Moolenbroek || copy_config_value_quoted("fingerprint=\"",
11863e07920fSDavid van Moolenbroek &(f->f_un.f_tls.tls_conn->fingerprint), &p)
11873e07920fSDavid van Moolenbroek || copy_config_value_quoted("cert=\"",
11883e07920fSDavid van Moolenbroek &(f->f_un.f_tls.tls_conn->certfile), &p)) {
11893e07920fSDavid van Moolenbroek /* nothing */
11903e07920fSDavid van Moolenbroek } else if (!strcmp(p, "verify=")) {
11913e07920fSDavid van Moolenbroek q = p += sizeof("verify=")-1;
11923e07920fSDavid van Moolenbroek /* "" are optional */
11933e07920fSDavid van Moolenbroek if (*p == '\"') { p++; q++; }
11943e07920fSDavid van Moolenbroek while (isalpha((unsigned char)*q)) q++;
11953e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->x509verify =
11963e07920fSDavid van Moolenbroek getVerifySetting(p);
11973e07920fSDavid van Moolenbroek if (*q == '\"') q++; /* "" are optional */
11983e07920fSDavid van Moolenbroek p = q;
11993e07920fSDavid van Moolenbroek } else {
12003e07920fSDavid van Moolenbroek logerror("unknown keyword %s "
12013e07920fSDavid van Moolenbroek "in config line %zu", p, linenum);
12023e07920fSDavid van Moolenbroek }
12033e07920fSDavid van Moolenbroek while (*p == ',' || isblank((unsigned char)*p))
12043e07920fSDavid van Moolenbroek p++;
12053e07920fSDavid van Moolenbroek if (*p == '\0') {
12063e07920fSDavid van Moolenbroek logerror("unterminated ("
12073e07920fSDavid van Moolenbroek "in config line %zu", linenum);
12083e07920fSDavid van Moolenbroek }
12093e07920fSDavid van Moolenbroek }
12103e07920fSDavid van Moolenbroek }
12113e07920fSDavid van Moolenbroek
12123e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_PARSE),
12133e07920fSDavid van Moolenbroek "got TLS config: host %s, port %s, "
12143e07920fSDavid van Moolenbroek "subject: %s, certfile: %s, fingerprint: %s\n",
12153e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->hostname,
12163e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->port,
12173e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->subject,
12183e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->certfile,
12193e07920fSDavid van Moolenbroek f->f_un.f_tls.tls_conn->fingerprint);
12203e07920fSDavid van Moolenbroek return true;
12213e07920fSDavid van Moolenbroek }
12223e07920fSDavid van Moolenbroek
12233e07920fSDavid van Moolenbroek /*
12243e07920fSDavid van Moolenbroek * Dispatch routine (triggered by timer) to reconnect to a lost TLS server
12253e07920fSDavid van Moolenbroek */
12263e07920fSDavid van Moolenbroek /*ARGSUSED*/
12273e07920fSDavid van Moolenbroek void
tls_reconnect(int fd,short event,void * arg)12283e07920fSDavid van Moolenbroek tls_reconnect(int fd, short event, void *arg)
12293e07920fSDavid van Moolenbroek {
12303e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info = (struct tls_conn_settings *) arg;
12313e07920fSDavid van Moolenbroek
12323e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL|D_EVENT), "tls_reconnect(conn_info@%p, "
12333e07920fSDavid van Moolenbroek "server %s)\n", conn_info, conn_info->hostname);
12343e07920fSDavid van Moolenbroek if (conn_info->sslptr) {
12353e07920fSDavid van Moolenbroek conn_info->shutdown = true;
12363e07920fSDavid van Moolenbroek free_tls_sslptr(conn_info);
12373e07920fSDavid van Moolenbroek }
12383e07920fSDavid van Moolenbroek assert(conn_info->state == ST_NONE);
12393e07920fSDavid van Moolenbroek
12403e07920fSDavid van Moolenbroek if (!tls_connect(conn_info)) {
12413e07920fSDavid van Moolenbroek if (conn_info->reconnect > TLS_RECONNECT_GIVEUP) {
12423e07920fSDavid van Moolenbroek logerror("Unable to connect to TLS server %s, "
12433e07920fSDavid van Moolenbroek "giving up now", conn_info->hostname);
12443e07920fSDavid van Moolenbroek message_queue_freeall(get_f_by_conninfo(conn_info));
12453e07920fSDavid van Moolenbroek /* free the message queue; but do not free the
12463e07920fSDavid van Moolenbroek * tls_conn_settings nor change the f_type to F_UNUSED.
12473e07920fSDavid van Moolenbroek * that way one can still trigger a reconnect
12483e07920fSDavid van Moolenbroek * with a SIGUSR1
12493e07920fSDavid van Moolenbroek */
12503e07920fSDavid van Moolenbroek } else {
12513e07920fSDavid van Moolenbroek struct timeval tv;
12523e07920fSDavid van Moolenbroek logerror("Unable to connect to TLS server %s, "
12533e07920fSDavid van Moolenbroek "try again in %d sec", conn_info->hostname,
12543e07920fSDavid van Moolenbroek conn_info->reconnect);
12553e07920fSDavid van Moolenbroek tv.tv_sec = conn_info->reconnect;
12563e07920fSDavid van Moolenbroek tv.tv_usec = 0;
12573e07920fSDavid van Moolenbroek schedule_event(&conn_info->event, &tv,
12583e07920fSDavid van Moolenbroek tls_reconnect, conn_info);
12593e07920fSDavid van Moolenbroek TLS_RECONNECT_BACKOFF(conn_info->reconnect);
12603e07920fSDavid van Moolenbroek }
12613e07920fSDavid van Moolenbroek } else {
12623e07920fSDavid van Moolenbroek assert(conn_info->state == ST_TLS_EST
12633e07920fSDavid van Moolenbroek || conn_info->state == ST_CONNECTING
12643e07920fSDavid van Moolenbroek || conn_info->state == ST_NONE);
12653e07920fSDavid van Moolenbroek }
12663e07920fSDavid van Moolenbroek }
12673e07920fSDavid van Moolenbroek /*
12683e07920fSDavid van Moolenbroek * Dispatch routine for accepting TLS connections.
12693e07920fSDavid van Moolenbroek * Has to be idempotent in case of TLS_RETRY (~ EAGAIN),
12703e07920fSDavid van Moolenbroek * so we can continue a slow handshake.
12713e07920fSDavid van Moolenbroek */
12723e07920fSDavid van Moolenbroek /*ARGSUSED*/
12733e07920fSDavid van Moolenbroek void
dispatch_tls_accept(int fd,short event,void * arg)12743e07920fSDavid van Moolenbroek dispatch_tls_accept(int fd, short event, void *arg)
12753e07920fSDavid van Moolenbroek {
12763e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info = (struct tls_conn_settings *) arg;
12773e07920fSDavid van Moolenbroek int rc, error;
12783e07920fSDavid van Moolenbroek struct TLS_Incoming_Conn *tls_in;
12793e07920fSDavid van Moolenbroek sigset_t newmask, omask;
12803e07920fSDavid van Moolenbroek
12813e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL),
12823e07920fSDavid van Moolenbroek "dispatch_tls_accept(conn_info@%p, fd %d)\n", conn_info, fd);
12833e07920fSDavid van Moolenbroek assert(conn_info->event);
12843e07920fSDavid van Moolenbroek assert(conn_info->retryevent);
12853e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
12863e07920fSDavid van Moolenbroek
12873e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_ACCEPTING);
12883e07920fSDavid van Moolenbroek rc = SSL_accept(conn_info->sslptr);
12893e07920fSDavid van Moolenbroek if (0 >= rc) {
12903e07920fSDavid van Moolenbroek error = tls_examine_error("SSL_accept()",
12913e07920fSDavid van Moolenbroek conn_info->sslptr, NULL, rc);
12923e07920fSDavid van Moolenbroek switch (error) {
12933e07920fSDavid van Moolenbroek case TLS_RETRY_READ:
12943e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_READ,
12953e07920fSDavid van Moolenbroek dispatch_tls_accept, conn_info);
12963e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->retryevent);
12973e07920fSDavid van Moolenbroek break;
12983e07920fSDavid van Moolenbroek case TLS_RETRY_WRITE:
12993e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_WRITE,
13003e07920fSDavid van Moolenbroek dispatch_tls_accept, conn_info);
13013e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->retryevent);
13023e07920fSDavid van Moolenbroek break;
13033e07920fSDavid van Moolenbroek default: /* should not happen */
13043e07920fSDavid van Moolenbroek free_tls_conn(conn_info);
13053e07920fSDavid van Moolenbroek break;
13063e07920fSDavid van Moolenbroek }
13073e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
13083e07920fSDavid van Moolenbroek return;
13093e07920fSDavid van Moolenbroek }
13103e07920fSDavid van Moolenbroek /* else */
13113e07920fSDavid van Moolenbroek CALLOC(tls_in, sizeof(*tls_in));
13123e07920fSDavid van Moolenbroek CALLOC(tls_in->inbuf, (size_t)TLS_MIN_LINELENGTH);
13133e07920fSDavid van Moolenbroek
13143e07920fSDavid van Moolenbroek tls_in->tls_conn = conn_info;
13153e07920fSDavid van Moolenbroek tls_in->socket = SSL_get_fd(conn_info->sslptr);
13163e07920fSDavid van Moolenbroek tls_in->inbuf[0] = '\0';
13173e07920fSDavid van Moolenbroek tls_in->inbuflen = TLS_MIN_LINELENGTH;
13183e07920fSDavid van Moolenbroek SLIST_INSERT_HEAD(&TLS_Incoming_Head, tls_in, entries);
13193e07920fSDavid van Moolenbroek
13203e07920fSDavid van Moolenbroek event_set(conn_info->event, tls_in->socket, EV_READ | EV_PERSIST,
13213e07920fSDavid van Moolenbroek dispatch_tls_read, tls_in);
13223e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->event);
13233e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TLS_EST);
13243e07920fSDavid van Moolenbroek
13253e07920fSDavid van Moolenbroek loginfo("established TLS connection from %s with certificate "
13263e07920fSDavid van Moolenbroek "%s (%s)", conn_info->hostname, conn_info->subject,
13273e07920fSDavid van Moolenbroek conn_info->fingerprint);
13283e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
13293e07920fSDavid van Moolenbroek /*
13303e07920fSDavid van Moolenbroek * We could also listen to EOF kevents -- but I do not think
13313e07920fSDavid van Moolenbroek * that would be useful, because we still had to read() the buffer
13323e07920fSDavid van Moolenbroek * before closing the socket.
13333e07920fSDavid van Moolenbroek */
13343e07920fSDavid van Moolenbroek }
13353e07920fSDavid van Moolenbroek
13363e07920fSDavid van Moolenbroek /*
13373e07920fSDavid van Moolenbroek * Dispatch routine for accepting TCP connections and preparing
13383e07920fSDavid van Moolenbroek * the tls_conn_settings object for a following SSL_accept().
13393e07920fSDavid van Moolenbroek */
13403e07920fSDavid van Moolenbroek /*ARGSUSED*/
13413e07920fSDavid van Moolenbroek void
dispatch_socket_accept(int fd,short event,void * ev)13423e07920fSDavid van Moolenbroek dispatch_socket_accept(int fd, short event, void *ev)
13433e07920fSDavid van Moolenbroek {
13443e07920fSDavid van Moolenbroek #ifdef LIBWRAP
13453e07920fSDavid van Moolenbroek struct request_info req;
13463e07920fSDavid van Moolenbroek #endif
13473e07920fSDavid van Moolenbroek struct sockaddr_storage frominet;
13483e07920fSDavid van Moolenbroek socklen_t addrlen;
13493e07920fSDavid van Moolenbroek int newsock, rc;
13503e07920fSDavid van Moolenbroek sigset_t newmask, omask;
13513e07920fSDavid van Moolenbroek SSL *ssl;
13523e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info;
13533e07920fSDavid van Moolenbroek char hbuf[NI_MAXHOST];
13543e07920fSDavid van Moolenbroek char *peername;
13553e07920fSDavid van Moolenbroek
13563e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_NET), "incoming TCP connection\n");
13573e07920fSDavid van Moolenbroek if (!tls_opt.global_TLS_CTX) {
13583e07920fSDavid van Moolenbroek logerror("global_TLS_CTX not initialized!");
13593e07920fSDavid van Moolenbroek return;
13603e07920fSDavid van Moolenbroek }
13613e07920fSDavid van Moolenbroek
13623e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
13633e07920fSDavid van Moolenbroek addrlen = sizeof(frominet);
13643e07920fSDavid van Moolenbroek if ((newsock = accept(fd, (struct sockaddr *)&frominet,
13653e07920fSDavid van Moolenbroek &addrlen)) == -1) {
13663e07920fSDavid van Moolenbroek logerror("Error in accept(): %s", strerror(errno));
13673e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
13683e07920fSDavid van Moolenbroek return;
13693e07920fSDavid van Moolenbroek }
13703e07920fSDavid van Moolenbroek /* TODO: do we want an IP or a hostname? maybe even both? */
13713e07920fSDavid van Moolenbroek if ((rc = getnameinfo((struct sockaddr *)&frominet, addrlen,
13723e07920fSDavid van Moolenbroek hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
13733e07920fSDavid van Moolenbroek DPRINTF(D_NET, "could not get peername: %s", gai_strerror(rc));
13743e07920fSDavid van Moolenbroek peername = NULL;
13753e07920fSDavid van Moolenbroek }
13763e07920fSDavid van Moolenbroek else {
13773e07920fSDavid van Moolenbroek size_t len = strlen(hbuf) + 1;
13783e07920fSDavid van Moolenbroek MALLOC(peername, len);
13793e07920fSDavid van Moolenbroek (void)memcpy(peername, hbuf, len);
13803e07920fSDavid van Moolenbroek }
13813e07920fSDavid van Moolenbroek
13823e07920fSDavid van Moolenbroek #ifdef LIBWRAP
13833e07920fSDavid van Moolenbroek request_init(&req, RQ_DAEMON, appname, RQ_FILE, newsock, NULL);
13843e07920fSDavid van Moolenbroek fromhost(&req);
13853e07920fSDavid van Moolenbroek if (!hosts_access(&req)) {
13863e07920fSDavid van Moolenbroek logerror("access from %s denied by hosts_access", peername);
13873e07920fSDavid van Moolenbroek shutdown(newsock, SHUT_RDWR);
13883e07920fSDavid van Moolenbroek close(newsock);
13893e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
13903e07920fSDavid van Moolenbroek return;
13913e07920fSDavid van Moolenbroek }
13923e07920fSDavid van Moolenbroek #endif
13933e07920fSDavid van Moolenbroek
13943e07920fSDavid van Moolenbroek if ((fcntl(newsock, F_SETFL, O_NONBLOCK)) == -1) {
13953e07920fSDavid van Moolenbroek DPRINTF(D_NET, "Unable to fcntl(sock, O_NONBLOCK): %s\n",
13963e07920fSDavid van Moolenbroek strerror(errno));
13973e07920fSDavid van Moolenbroek }
13983e07920fSDavid van Moolenbroek
13993e07920fSDavid van Moolenbroek if (!(ssl = SSL_new(tls_opt.global_TLS_CTX))) {
14003e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Unable to establish TLS: %s\n",
14013e07920fSDavid van Moolenbroek ERR_error_string(ERR_get_error(), NULL));
14023e07920fSDavid van Moolenbroek close(newsock);
14033e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
14043e07920fSDavid van Moolenbroek return;
14053e07920fSDavid van Moolenbroek }
14063e07920fSDavid van Moolenbroek if (!SSL_set_fd(ssl, newsock)) {
14073e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Unable to connect TLS to socket %d: %s\n",
14083e07920fSDavid van Moolenbroek newsock, ERR_error_string(ERR_get_error(), NULL));
14093e07920fSDavid van Moolenbroek SSL_free(ssl);
14103e07920fSDavid van Moolenbroek close(newsock);
14113e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
14123e07920fSDavid van Moolenbroek return;
14133e07920fSDavid van Moolenbroek }
14143e07920fSDavid van Moolenbroek
14153e07920fSDavid van Moolenbroek if (!(conn_info = calloc(1, sizeof(*conn_info)))
14163e07920fSDavid van Moolenbroek || !(conn_info->event = allocev())
14173e07920fSDavid van Moolenbroek || !(conn_info->retryevent = allocev())) {
14183e07920fSDavid van Moolenbroek if (conn_info)
14193e07920fSDavid van Moolenbroek free(conn_info->event);
14203e07920fSDavid van Moolenbroek free(conn_info);
14213e07920fSDavid van Moolenbroek SSL_free(ssl);
14223e07920fSDavid van Moolenbroek close(newsock);
14233e07920fSDavid van Moolenbroek logerror("Unable to allocate memory to accept incoming "
14243e07920fSDavid van Moolenbroek "TLS connection from %s", peername);
14253e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
14263e07920fSDavid van Moolenbroek return;
14273e07920fSDavid van Moolenbroek }
14283e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_NONE);
14293e07920fSDavid van Moolenbroek /* store connection details inside ssl object, used to verify
14303e07920fSDavid van Moolenbroek * cert and immediately match against hostname */
14313e07920fSDavid van Moolenbroek conn_info->hostname = peername;
14323e07920fSDavid van Moolenbroek conn_info->sslptr = ssl;
14333e07920fSDavid van Moolenbroek conn_info->x509verify = getVerifySetting(tls_opt.x509verify);
14343e07920fSDavid van Moolenbroek conn_info->incoming = true;
14353e07920fSDavid van Moolenbroek SSL_set_app_data(ssl, conn_info);
14363e07920fSDavid van Moolenbroek SSL_set_accept_state(ssl);
14373e07920fSDavid van Moolenbroek
14383e07920fSDavid van Moolenbroek assert(conn_info->event);
14393e07920fSDavid van Moolenbroek assert(conn_info->retryevent);
14403e07920fSDavid van Moolenbroek
14413e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TCP_EST);
14423e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "socket connection from %s accept()ed with fd %d, "
14433e07920fSDavid van Moolenbroek "calling SSL_accept()...\n", peername, newsock);
14443e07920fSDavid van Moolenbroek dispatch_tls_accept(newsock, 0, conn_info);
14453e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
14463e07920fSDavid van Moolenbroek }
14473e07920fSDavid van Moolenbroek
14483e07920fSDavid van Moolenbroek /*
14493e07920fSDavid van Moolenbroek * Dispatch routine to read from outgoing TCP/TLS sockets.
14503e07920fSDavid van Moolenbroek *
14513e07920fSDavid van Moolenbroek * I do not know if libevent can tell us the difference
14523e07920fSDavid van Moolenbroek * between available data and an EOF. But it does not matter
14533e07920fSDavid van Moolenbroek * because there should not be any incoming data.
14543e07920fSDavid van Moolenbroek * So we close the connection either because the peer closed its
14553e07920fSDavid van Moolenbroek * side or because the peer broke the protocol by sending us stuff ;-)
14563e07920fSDavid van Moolenbroek */
14573e07920fSDavid van Moolenbroek void
dispatch_tls_eof(int fd,short event,void * arg)14583e07920fSDavid van Moolenbroek dispatch_tls_eof(int fd, short event, void *arg)
14593e07920fSDavid van Moolenbroek {
14603e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info = (struct tls_conn_settings *) arg;
14613e07920fSDavid van Moolenbroek sigset_t newmask, omask;
14623e07920fSDavid van Moolenbroek struct timeval tv;
14633e07920fSDavid van Moolenbroek
14643e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
14653e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_EVENT|D_CALL), "dispatch_eof_tls(%d, %d, %p)\n",
14663e07920fSDavid van Moolenbroek fd, event, arg);
14673e07920fSDavid van Moolenbroek assert(conn_info->state == ST_TLS_EST);
14683e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_EOF);
14693e07920fSDavid van Moolenbroek DEL_EVENT(conn_info->event);
14703e07920fSDavid van Moolenbroek
14713e07920fSDavid van Moolenbroek free_tls_sslptr(conn_info);
14723e07920fSDavid van Moolenbroek
14733e07920fSDavid van Moolenbroek /* this overwrites the EV_READ event */
14743e07920fSDavid van Moolenbroek tv.tv_sec = conn_info->reconnect;
14753e07920fSDavid van Moolenbroek tv.tv_usec = 0;
14763e07920fSDavid van Moolenbroek schedule_event(&conn_info->event, &tv, tls_reconnect, conn_info);
14773e07920fSDavid van Moolenbroek TLS_RECONNECT_BACKOFF(conn_info->reconnect);
14783e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
14793e07920fSDavid van Moolenbroek }
14803e07920fSDavid van Moolenbroek
14813e07920fSDavid van Moolenbroek /*
14823e07920fSDavid van Moolenbroek * Dispatch routine to read from TCP/TLS sockets.
14833e07920fSDavid van Moolenbroek * NB: This gets called when the TCP socket has data available, thus
14843e07920fSDavid van Moolenbroek * we can call SSL_read() on it. But that does not mean the SSL buffer
14853e07920fSDavid van Moolenbroek * holds a complete record and SSL_read() lets us read any data now.
14863e07920fSDavid van Moolenbroek */
14873e07920fSDavid van Moolenbroek /*ARGSUSED*/
14883e07920fSDavid van Moolenbroek void
dispatch_tls_read(int fd_lib,short event,void * arg)14893e07920fSDavid van Moolenbroek dispatch_tls_read(int fd_lib, short event, void *arg)
14903e07920fSDavid van Moolenbroek {
14913e07920fSDavid van Moolenbroek struct TLS_Incoming_Conn *c = (struct TLS_Incoming_Conn *) arg;
14923e07920fSDavid van Moolenbroek int fd = c->socket;
14933e07920fSDavid van Moolenbroek int error;
14943e07920fSDavid van Moolenbroek int rc;
14953e07920fSDavid van Moolenbroek sigset_t newmask, omask;
14963e07920fSDavid van Moolenbroek bool retrying;
14973e07920fSDavid van Moolenbroek
14983e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
14993e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_EVENT|D_CALL), "active TLS socket %d\n", fd);
15003e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "calling SSL_read(%p, %p, %zu)\n", c->tls_conn->sslptr,
15013e07920fSDavid van Moolenbroek &(c->inbuf[c->read_pos]), c->inbuflen - c->read_pos);
15023e07920fSDavid van Moolenbroek retrying = (c->tls_conn->state == ST_READING);
15033e07920fSDavid van Moolenbroek ST_CHANGE(c->tls_conn->state, ST_READING);
15043e07920fSDavid van Moolenbroek rc = SSL_read(c->tls_conn->sslptr, &(c->inbuf[c->read_pos]),
15053e07920fSDavid van Moolenbroek c->inbuflen - c->read_pos);
15063e07920fSDavid van Moolenbroek if (rc <= 0) {
15073e07920fSDavid van Moolenbroek error = tls_examine_error("SSL_read()", c->tls_conn->sslptr,
15083e07920fSDavid van Moolenbroek c->tls_conn, rc);
15093e07920fSDavid van Moolenbroek switch (error) {
15103e07920fSDavid van Moolenbroek case TLS_RETRY_READ:
15113e07920fSDavid van Moolenbroek /* normal event loop will call us again */
15123e07920fSDavid van Moolenbroek break;
15133e07920fSDavid van Moolenbroek case TLS_RETRY_WRITE:
15143e07920fSDavid van Moolenbroek if (!retrying)
15153e07920fSDavid van Moolenbroek event_del(c->tls_conn->event);
15163e07920fSDavid van Moolenbroek event_set(c->tls_conn->retryevent, fd,
15173e07920fSDavid van Moolenbroek EV_WRITE, dispatch_tls_read, c);
15183e07920fSDavid van Moolenbroek EVENT_ADD(c->tls_conn->retryevent);
15193e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
15203e07920fSDavid van Moolenbroek return;
15213e07920fSDavid van Moolenbroek case TLS_TEMP_ERROR:
15223e07920fSDavid van Moolenbroek if (c->tls_conn->errorcount < TLS_MAXERRORCOUNT)
15233e07920fSDavid van Moolenbroek break;
15243e07920fSDavid van Moolenbroek /* FALLTHROUGH */
15253e07920fSDavid van Moolenbroek case TLS_PERM_ERROR:
15263e07920fSDavid van Moolenbroek /* there might be data in the inbuf, so only
15273e07920fSDavid van Moolenbroek * mark for closing after message retrieval */
15283e07920fSDavid van Moolenbroek c->closenow = true;
15293e07920fSDavid van Moolenbroek break;
15303e07920fSDavid van Moolenbroek default:
15313e07920fSDavid van Moolenbroek break;
15323e07920fSDavid van Moolenbroek }
15333e07920fSDavid van Moolenbroek } else {
15343e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "SSL_read() returned %d\n", rc);
15353e07920fSDavid van Moolenbroek c->errorcount = 0;
15363e07920fSDavid van Moolenbroek c->read_pos += rc;
15373e07920fSDavid van Moolenbroek }
15383e07920fSDavid van Moolenbroek if (retrying)
15393e07920fSDavid van Moolenbroek EVENT_ADD(c->tls_conn->event);
15403e07920fSDavid van Moolenbroek tls_split_messages(c);
15413e07920fSDavid van Moolenbroek if (c->closenow) {
15423e07920fSDavid van Moolenbroek free_tls_conn(c->tls_conn);
15433e07920fSDavid van Moolenbroek FREEPTR(c->inbuf);
15443e07920fSDavid van Moolenbroek SLIST_REMOVE(&TLS_Incoming_Head, c, TLS_Incoming_Conn, entries);
15453e07920fSDavid van Moolenbroek free(c);
15463e07920fSDavid van Moolenbroek } else
15473e07920fSDavid van Moolenbroek ST_CHANGE(c->tls_conn->state, ST_TLS_EST);
15483e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
15493e07920fSDavid van Moolenbroek }
15503e07920fSDavid van Moolenbroek
15513e07920fSDavid van Moolenbroek /* moved message splitting out of dispatching function.
15523e07920fSDavid van Moolenbroek * now we can call it recursively.
15533e07920fSDavid van Moolenbroek *
15543e07920fSDavid van Moolenbroek * TODO: the code for oversized messages still needs testing,
15553e07920fSDavid van Moolenbroek * especially for the skipping case.
15563e07920fSDavid van Moolenbroek */
15573e07920fSDavid van Moolenbroek void
tls_split_messages(struct TLS_Incoming_Conn * c)15583e07920fSDavid van Moolenbroek tls_split_messages(struct TLS_Incoming_Conn *c)
15593e07920fSDavid van Moolenbroek {
15603e07920fSDavid van Moolenbroek /* define only to make it better readable */
15613e07920fSDavid van Moolenbroek #define MSG_END_OFFSET (c->cur_msg_start + c->cur_msg_len)
15623e07920fSDavid van Moolenbroek size_t offset = 0;
15633e07920fSDavid van Moolenbroek size_t msglen = 0;
15643e07920fSDavid van Moolenbroek char *newbuf;
15653e07920fSDavid van Moolenbroek char buf_char;
15663e07920fSDavid van Moolenbroek
15673e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL|D_DATA), "tls_split_messages() -- "
15683e07920fSDavid van Moolenbroek "incoming status is msg_start %zu, msg_len %zu, pos %zu\n",
15693e07920fSDavid van Moolenbroek c->cur_msg_start, c->cur_msg_len, c->read_pos);
15703e07920fSDavid van Moolenbroek
15713e07920fSDavid van Moolenbroek if (!c->read_pos)
15723e07920fSDavid van Moolenbroek return;
15733e07920fSDavid van Moolenbroek
15743e07920fSDavid van Moolenbroek if (c->dontsave && c->read_pos < MSG_END_OFFSET) {
15753e07920fSDavid van Moolenbroek c->cur_msg_len -= c->read_pos;
15763e07920fSDavid van Moolenbroek c->read_pos = 0;
15773e07920fSDavid van Moolenbroek } else if (c->dontsave && c->read_pos == MSG_END_OFFSET) {
15783e07920fSDavid van Moolenbroek c->cur_msg_start = c->cur_msg_len = c->read_pos = 0;
15793e07920fSDavid van Moolenbroek c->dontsave = false;
15803e07920fSDavid van Moolenbroek } else if (c->dontsave && c->read_pos > MSG_END_OFFSET) {
15813e07920fSDavid van Moolenbroek /* move remaining input to start of buffer */
15823e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "move inbuf of length %zu by %zu chars\n",
15833e07920fSDavid van Moolenbroek c->read_pos - (MSG_END_OFFSET),
15843e07920fSDavid van Moolenbroek MSG_END_OFFSET);
15853e07920fSDavid van Moolenbroek memmove(&c->inbuf[0],
15863e07920fSDavid van Moolenbroek &c->inbuf[MSG_END_OFFSET],
15873e07920fSDavid van Moolenbroek c->read_pos - (MSG_END_OFFSET));
15883e07920fSDavid van Moolenbroek c->read_pos -= (MSG_END_OFFSET);
15893e07920fSDavid van Moolenbroek c->cur_msg_start = c->cur_msg_len = 0;
15903e07920fSDavid van Moolenbroek c->dontsave = false;
15913e07920fSDavid van Moolenbroek }
15923e07920fSDavid van Moolenbroek if (c->read_pos < MSG_END_OFFSET) {
15933e07920fSDavid van Moolenbroek return;
15943e07920fSDavid van Moolenbroek }
15953e07920fSDavid van Moolenbroek
15963e07920fSDavid van Moolenbroek /* read length prefix, always at start of buffer */
15973e07920fSDavid van Moolenbroek while (isdigit((unsigned char)c->inbuf[offset])
15983e07920fSDavid van Moolenbroek && offset < c->read_pos) {
15993e07920fSDavid van Moolenbroek msglen *= 10;
16003e07920fSDavid van Moolenbroek msglen += c->inbuf[offset] - '0';
16013e07920fSDavid van Moolenbroek offset++;
16023e07920fSDavid van Moolenbroek }
16033e07920fSDavid van Moolenbroek if (offset == c->read_pos) {
16043e07920fSDavid van Moolenbroek /* next invocation will have more data */
16053e07920fSDavid van Moolenbroek return;
16063e07920fSDavid van Moolenbroek }
16073e07920fSDavid van Moolenbroek if (c->inbuf[offset] == ' ') {
16083e07920fSDavid van Moolenbroek c->cur_msg_len = msglen;
16093e07920fSDavid van Moolenbroek c->cur_msg_start = offset + 1;
16103e07920fSDavid van Moolenbroek if (MSG_END_OFFSET+1 > c->inbuflen) { /* +1 for the '\0' */
16113e07920fSDavid van Moolenbroek newbuf = realloc(c->inbuf, MSG_END_OFFSET+1);
16123e07920fSDavid van Moolenbroek if (newbuf) {
16133e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "Reallocated inbuf\n");
16143e07920fSDavid van Moolenbroek c->inbuflen = MSG_END_OFFSET+1;
16153e07920fSDavid van Moolenbroek c->inbuf = newbuf;
16163e07920fSDavid van Moolenbroek } else {
16173e07920fSDavid van Moolenbroek logerror("Couldn't reallocate buffer, "
16183e07920fSDavid van Moolenbroek "will skip this message");
16193e07920fSDavid van Moolenbroek c->dontsave = true;
16203e07920fSDavid van Moolenbroek c->cur_msg_len -= c->read_pos;
16213e07920fSDavid van Moolenbroek c->cur_msg_start = 0;
16223e07920fSDavid van Moolenbroek c->read_pos = 0;
16233e07920fSDavid van Moolenbroek }
16243e07920fSDavid van Moolenbroek }
16253e07920fSDavid van Moolenbroek } else {
16263e07920fSDavid van Moolenbroek /* found non-digit in prefix */
16273e07920fSDavid van Moolenbroek /* Question: would it be useful to skip this message and
16283e07920fSDavid van Moolenbroek * try to find next message by looking for its beginning?
16293e07920fSDavid van Moolenbroek * IMHO not.
16303e07920fSDavid van Moolenbroek */
16313e07920fSDavid van Moolenbroek logerror("Unable to handle TLS length prefix. "
16323e07920fSDavid van Moolenbroek "Protocol error? Closing connection now.");
16333e07920fSDavid van Moolenbroek /* only set flag -- caller has to close then */
16343e07920fSDavid van Moolenbroek c->closenow = true;
16353e07920fSDavid van Moolenbroek return;
16363e07920fSDavid van Moolenbroek }
16373e07920fSDavid van Moolenbroek /* read one syslog message */
16383e07920fSDavid van Moolenbroek if (c->read_pos >= MSG_END_OFFSET) {
16393e07920fSDavid van Moolenbroek /* process complete msg */
16403e07920fSDavid van Moolenbroek assert(MSG_END_OFFSET+1 <= c->inbuflen);
16413e07920fSDavid van Moolenbroek /* message in c->inbuf is not NULL-terminated,
16423e07920fSDavid van Moolenbroek * so this avoids a complete copy */
16433e07920fSDavid van Moolenbroek buf_char = c->inbuf[MSG_END_OFFSET];
16443e07920fSDavid van Moolenbroek c->inbuf[MSG_END_OFFSET] = '\0';
16453e07920fSDavid van Moolenbroek printline(c->tls_conn->hostname, &c->inbuf[c->cur_msg_start],
16463e07920fSDavid van Moolenbroek RemoteAddDate ? ADDDATE : 0);
16473e07920fSDavid van Moolenbroek c->inbuf[MSG_END_OFFSET] = buf_char;
16483e07920fSDavid van Moolenbroek
16493e07920fSDavid van Moolenbroek if (MSG_END_OFFSET == c->read_pos) {
16503e07920fSDavid van Moolenbroek /* no unprocessed data in buffer --> reset to empty */
16513e07920fSDavid van Moolenbroek c->cur_msg_start = c->cur_msg_len = c->read_pos = 0;
16523e07920fSDavid van Moolenbroek } else {
16533e07920fSDavid van Moolenbroek /* move remaining input to start of buffer */
16543e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "move inbuf of length %zu by %zu "
16553e07920fSDavid van Moolenbroek "chars\n", c->read_pos - (MSG_END_OFFSET),
16563e07920fSDavid van Moolenbroek MSG_END_OFFSET);
16573e07920fSDavid van Moolenbroek memmove(&c->inbuf[0], &c->inbuf[MSG_END_OFFSET],
16583e07920fSDavid van Moolenbroek c->read_pos - (MSG_END_OFFSET));
16593e07920fSDavid van Moolenbroek c->read_pos -= (MSG_END_OFFSET);
16603e07920fSDavid van Moolenbroek c->cur_msg_start = c->cur_msg_len = 0;
16613e07920fSDavid van Moolenbroek }
16623e07920fSDavid van Moolenbroek }
16633e07920fSDavid van Moolenbroek
16643e07920fSDavid van Moolenbroek /* shrink inbuf if too large */
16653e07920fSDavid van Moolenbroek if ((c->inbuflen > TLS_PERSIST_LINELENGTH)
16663e07920fSDavid van Moolenbroek && (c->read_pos < TLS_LARGE_LINELENGTH)) {
16673e07920fSDavid van Moolenbroek newbuf = realloc(c->inbuf, TLS_LARGE_LINELENGTH);
16683e07920fSDavid van Moolenbroek if (newbuf) {
16693e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "Shrink inbuf\n");
16703e07920fSDavid van Moolenbroek c->inbuflen = TLS_LARGE_LINELENGTH;
16713e07920fSDavid van Moolenbroek c->inbuf = newbuf;
16723e07920fSDavid van Moolenbroek } else {
16733e07920fSDavid van Moolenbroek logerror("Couldn't shrink inbuf");
16743e07920fSDavid van Moolenbroek /* no change necessary */
16753e07920fSDavid van Moolenbroek }
16763e07920fSDavid van Moolenbroek }
16773e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "return with status: msg_start %zu, msg_len %zu, "
16783e07920fSDavid van Moolenbroek "pos %zu\n", c->cur_msg_start, c->cur_msg_len, c->read_pos);
16793e07920fSDavid van Moolenbroek
16803e07920fSDavid van Moolenbroek /* try to read another message */
16813e07920fSDavid van Moolenbroek if (c->read_pos > 10)
16823e07920fSDavid van Moolenbroek tls_split_messages(c);
16833e07920fSDavid van Moolenbroek return;
16843e07920fSDavid van Moolenbroek }
16853e07920fSDavid van Moolenbroek
16863e07920fSDavid van Moolenbroek /*
16873e07920fSDavid van Moolenbroek * wrapper for dispatch_tls_send()
16883e07920fSDavid van Moolenbroek *
16893e07920fSDavid van Moolenbroek * send one line with tls
16903e07920fSDavid van Moolenbroek * f has to be of typ TLS
16913e07920fSDavid van Moolenbroek *
16923e07920fSDavid van Moolenbroek * returns false if message cannot be sent right now,
16933e07920fSDavid van Moolenbroek * caller is responsible to enqueue it
16943e07920fSDavid van Moolenbroek * returns true if message passed to dispatch_tls_send()
16953e07920fSDavid van Moolenbroek * delivery is not garantueed, but likely
16963e07920fSDavid van Moolenbroek */
16973e07920fSDavid van Moolenbroek #define DEBUG_LINELENGTH 40
16983e07920fSDavid van Moolenbroek bool
tls_send(struct filed * f,char * line,size_t len,struct buf_queue * qentry)16993e07920fSDavid van Moolenbroek tls_send(struct filed *f, char *line, size_t len, struct buf_queue *qentry)
17003e07920fSDavid van Moolenbroek {
17013e07920fSDavid van Moolenbroek struct tls_send_msg *smsg;
17023e07920fSDavid van Moolenbroek
17033e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "tls_send(f=%p, line=\"%.*s%s\", "
17043e07920fSDavid van Moolenbroek "len=%zu) to %sconnected dest.\n", f,
17053e07920fSDavid van Moolenbroek (int)(len > DEBUG_LINELENGTH ? DEBUG_LINELENGTH : len),
17063e07920fSDavid van Moolenbroek line, (len > DEBUG_LINELENGTH ? "..." : ""),
17073e07920fSDavid van Moolenbroek len, f->f_un.f_tls.tls_conn->sslptr ? "" : "un");
17083e07920fSDavid van Moolenbroek
17093e07920fSDavid van Moolenbroek if(f->f_un.f_tls.tls_conn->state == ST_TLS_EST) {
17103e07920fSDavid van Moolenbroek /* send now */
17113e07920fSDavid van Moolenbroek if (!(smsg = calloc(1, sizeof(*smsg)))) {
17123e07920fSDavid van Moolenbroek logerror("Unable to allocate memory, drop message");
17133e07920fSDavid van Moolenbroek return false;
17143e07920fSDavid van Moolenbroek }
17153e07920fSDavid van Moolenbroek smsg->f = f;
17163e07920fSDavid van Moolenbroek smsg->line = line;
17173e07920fSDavid van Moolenbroek smsg->linelen = len;
17183e07920fSDavid van Moolenbroek (void)NEWREF(qentry->msg);
17193e07920fSDavid van Moolenbroek smsg->qentry = qentry;
17203e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "now sending line: \"%.*s\"\n",
17213e07920fSDavid van Moolenbroek (int)smsg->linelen, smsg->line);
17223e07920fSDavid van Moolenbroek dispatch_tls_send(0, 0, smsg);
17233e07920fSDavid van Moolenbroek return true;
17243e07920fSDavid van Moolenbroek } else {
17253e07920fSDavid van Moolenbroek /* other socket operation active, send later */
17263e07920fSDavid van Moolenbroek DPRINTF(D_DATA, "connection not ready to send: \"%.*s\"\n",
17273e07920fSDavid van Moolenbroek (int)len, line);
17283e07920fSDavid van Moolenbroek return false;
17293e07920fSDavid van Moolenbroek }
17303e07920fSDavid van Moolenbroek }
17313e07920fSDavid van Moolenbroek
17323e07920fSDavid van Moolenbroek /*ARGSUSED*/
17333e07920fSDavid van Moolenbroek void
dispatch_tls_send(int fd,short event,void * arg)17343e07920fSDavid van Moolenbroek dispatch_tls_send(int fd, short event, void *arg)
17353e07920fSDavid van Moolenbroek {
17363e07920fSDavid van Moolenbroek struct tls_send_msg *smsg = (struct tls_send_msg *) arg;
17373e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info = smsg->f->f_un.f_tls.tls_conn;
17383e07920fSDavid van Moolenbroek struct filed *f = smsg->f;
17393e07920fSDavid van Moolenbroek int rc, error;
17403e07920fSDavid van Moolenbroek sigset_t newmask, omask;
17413e07920fSDavid van Moolenbroek bool retrying;
17423e07920fSDavid van Moolenbroek struct timeval tv;
17433e07920fSDavid van Moolenbroek
17443e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
17453e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL), "dispatch_tls_send(f=%p, buffer=%p, "
17463e07920fSDavid van Moolenbroek "line@%p, len=%zu, offset=%zu) to %sconnected dest.\n",
17473e07920fSDavid van Moolenbroek smsg->f, smsg->qentry->msg, smsg->line,
17483e07920fSDavid van Moolenbroek smsg->linelen, smsg->offset,
17493e07920fSDavid van Moolenbroek conn_info->sslptr ? "" : "un");
17503e07920fSDavid van Moolenbroek assert(conn_info->state == ST_TLS_EST
17513e07920fSDavid van Moolenbroek || conn_info->state == ST_WRITING);
17523e07920fSDavid van Moolenbroek
17533e07920fSDavid van Moolenbroek retrying = (conn_info->state == ST_WRITING);
17543e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_WRITING);
17553e07920fSDavid van Moolenbroek rc = SSL_write(conn_info->sslptr,
17563e07920fSDavid van Moolenbroek (smsg->line + smsg->offset),
17573e07920fSDavid van Moolenbroek (smsg->linelen - smsg->offset));
17583e07920fSDavid van Moolenbroek if (0 >= rc) {
17593e07920fSDavid van Moolenbroek error = tls_examine_error("SSL_write()",
17603e07920fSDavid van Moolenbroek conn_info->sslptr,
17613e07920fSDavid van Moolenbroek conn_info, rc);
17623e07920fSDavid van Moolenbroek switch (error) {
17633e07920fSDavid van Moolenbroek case TLS_RETRY_READ:
17643e07920fSDavid van Moolenbroek /* collides with eof event */
17653e07920fSDavid van Moolenbroek if (!retrying)
17663e07920fSDavid van Moolenbroek event_del(conn_info->event);
17673e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_READ,
17683e07920fSDavid van Moolenbroek dispatch_tls_send, smsg);
17693e07920fSDavid van Moolenbroek RETRYEVENT_ADD(conn_info->retryevent);
17703e07920fSDavid van Moolenbroek break;
17713e07920fSDavid van Moolenbroek case TLS_RETRY_WRITE:
17723e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_WRITE,
17733e07920fSDavid van Moolenbroek dispatch_tls_send, smsg);
17743e07920fSDavid van Moolenbroek RETRYEVENT_ADD(conn_info->retryevent);
17753e07920fSDavid van Moolenbroek break;
17763e07920fSDavid van Moolenbroek case TLS_PERM_ERROR:
17773e07920fSDavid van Moolenbroek /* no need to check active events */
17783e07920fSDavid van Moolenbroek free_tls_send_msg(smsg);
17793e07920fSDavid van Moolenbroek free_tls_sslptr(conn_info);
17803e07920fSDavid van Moolenbroek tv.tv_sec = conn_info->reconnect;
17813e07920fSDavid van Moolenbroek tv.tv_usec = 0;
17823e07920fSDavid van Moolenbroek schedule_event(&conn_info->event, &tv,
17833e07920fSDavid van Moolenbroek tls_reconnect, conn_info);
17843e07920fSDavid van Moolenbroek TLS_RECONNECT_BACKOFF(conn_info->reconnect);
17853e07920fSDavid van Moolenbroek break;
17863e07920fSDavid van Moolenbroek default:
17873e07920fSDavid van Moolenbroek break;
17883e07920fSDavid van Moolenbroek }
17893e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
17903e07920fSDavid van Moolenbroek return;
17913e07920fSDavid van Moolenbroek } else if ((size_t)rc < smsg->linelen) {
17923e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_DATA), "TLS: SSL_write() wrote %d out of %zu "
17933e07920fSDavid van Moolenbroek "bytes\n", rc, (smsg->linelen - smsg->offset));
17943e07920fSDavid van Moolenbroek smsg->offset += rc;
17953e07920fSDavid van Moolenbroek /* try again */
17963e07920fSDavid van Moolenbroek if (retrying)
17973e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->event);
17983e07920fSDavid van Moolenbroek dispatch_tls_send(0, 0, smsg);
17993e07920fSDavid van Moolenbroek return;
18003e07920fSDavid van Moolenbroek } else if ((size_t)rc == (smsg->linelen - smsg->offset)) {
18013e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_DATA), "TLS: SSL_write() complete\n");
18023e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TLS_EST);
18033e07920fSDavid van Moolenbroek free_tls_send_msg(smsg);
18043e07920fSDavid van Moolenbroek send_queue(0, 0, f);
18053e07920fSDavid van Moolenbroek
18063e07920fSDavid van Moolenbroek } else {
18073e07920fSDavid van Moolenbroek /* should not be reached */
18083e07920fSDavid van Moolenbroek /*LINTED constcond */
18093e07920fSDavid van Moolenbroek assert(0);
18103e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_DATA), "unreachable code after SSL_write()\n");
18113e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TLS_EST);
18123e07920fSDavid van Moolenbroek free_tls_send_msg(smsg);
18133e07920fSDavid van Moolenbroek send_queue(0, 0, f);
18143e07920fSDavid van Moolenbroek }
18153e07920fSDavid van Moolenbroek if (retrying && conn_info->event->ev_events)
18163e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->event);
18173e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
18183e07920fSDavid van Moolenbroek }
18193e07920fSDavid van Moolenbroek
18203e07920fSDavid van Moolenbroek /*
18213e07920fSDavid van Moolenbroek * Close a SSL connection and its queue and its tls_conn.
18223e07920fSDavid van Moolenbroek */
18233e07920fSDavid van Moolenbroek void
free_tls_conn(struct tls_conn_settings * conn_info)18243e07920fSDavid van Moolenbroek free_tls_conn(struct tls_conn_settings *conn_info)
18253e07920fSDavid van Moolenbroek {
18263e07920fSDavid van Moolenbroek DPRINTF(D_MEM, "free_tls_conn(conn_info@%p) with sslptr@%p\n",
18273e07920fSDavid van Moolenbroek conn_info, conn_info->sslptr);
18283e07920fSDavid van Moolenbroek
18293e07920fSDavid van Moolenbroek if (conn_info->sslptr) {
18303e07920fSDavid van Moolenbroek conn_info->shutdown = true;
18313e07920fSDavid van Moolenbroek free_tls_sslptr(conn_info);
18323e07920fSDavid van Moolenbroek }
18333e07920fSDavid van Moolenbroek assert(conn_info->state == ST_NONE);
18343e07920fSDavid van Moolenbroek
18353e07920fSDavid van Moolenbroek FREEPTR(conn_info->port);
18363e07920fSDavid van Moolenbroek FREEPTR(conn_info->subject);
18373e07920fSDavid van Moolenbroek FREEPTR(conn_info->hostname);
18383e07920fSDavid van Moolenbroek FREEPTR(conn_info->certfile);
18393e07920fSDavid van Moolenbroek FREEPTR(conn_info->fingerprint);
18403e07920fSDavid van Moolenbroek DEL_EVENT(conn_info->event);
18413e07920fSDavid van Moolenbroek DEL_EVENT(conn_info->retryevent);
18423e07920fSDavid van Moolenbroek FREEPTR(conn_info->event);
18433e07920fSDavid van Moolenbroek FREEPTR(conn_info->retryevent);
18443e07920fSDavid van Moolenbroek FREEPTR(conn_info);
18453e07920fSDavid van Moolenbroek DPRINTF(D_MEM2, "free_tls_conn(conn_info@%p) returns\n", conn_info);
18463e07920fSDavid van Moolenbroek }
18473e07920fSDavid van Moolenbroek
18483e07920fSDavid van Moolenbroek /*
18493e07920fSDavid van Moolenbroek * Dispatch routine for non-blocking TLS shutdown
18503e07920fSDavid van Moolenbroek */
18513e07920fSDavid van Moolenbroek /*ARGSUSED*/
18523e07920fSDavid van Moolenbroek void
dispatch_SSL_shutdown(int fd,short event,void * arg)18533e07920fSDavid van Moolenbroek dispatch_SSL_shutdown(int fd, short event, void *arg)
18543e07920fSDavid van Moolenbroek {
18553e07920fSDavid van Moolenbroek struct tls_conn_settings *conn_info = (struct tls_conn_settings *) arg;
18563e07920fSDavid van Moolenbroek int rc, error;
18573e07920fSDavid van Moolenbroek sigset_t newmask, omask;
18583e07920fSDavid van Moolenbroek bool retrying;
18593e07920fSDavid van Moolenbroek
18603e07920fSDavid van Moolenbroek BLOCK_SIGNALS(omask, newmask);
18613e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_CALL),
18623e07920fSDavid van Moolenbroek "dispatch_SSL_shutdown(conn_info@%p, fd %d)\n", conn_info, fd);
18633e07920fSDavid van Moolenbroek retrying = ((conn_info->state == ST_CLOSING0)
18643e07920fSDavid van Moolenbroek || (conn_info->state == ST_CLOSING1)
18653e07920fSDavid van Moolenbroek || (conn_info->state == ST_CLOSING2));
18663e07920fSDavid van Moolenbroek if (!retrying)
18673e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_CLOSING0);
18683e07920fSDavid van Moolenbroek
18693e07920fSDavid van Moolenbroek rc = SSL_shutdown(conn_info->sslptr);
18703e07920fSDavid van Moolenbroek if (rc == 1) { /* shutdown complete */
18713e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_NET), "Closed TLS connection to %s\n",
18723e07920fSDavid van Moolenbroek conn_info->hostname);
18733e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TCP_EST); /* check this */
18743e07920fSDavid van Moolenbroek conn_info->accepted = false;
18753e07920fSDavid van Moolenbroek /* closing TCP comes below */
18763e07920fSDavid van Moolenbroek } else if (rc == 0) { /* unidirectional, now call a 2nd time */
18773e07920fSDavid van Moolenbroek /* problem: when connecting as a client to rsyslogd this
18783e07920fSDavid van Moolenbroek * loops and I keep getting rc == 0
18793e07920fSDavid van Moolenbroek * maybe I hit this bug?
18803e07920fSDavid van Moolenbroek * http://www.mail-archive.com/openssl-dev@openssl.org/msg24105.html
18813e07920fSDavid van Moolenbroek *
18823e07920fSDavid van Moolenbroek * anyway, now I use three closing states to make sure I abort
18833e07920fSDavid van Moolenbroek * after two rc = 0.
18843e07920fSDavid van Moolenbroek */
18853e07920fSDavid van Moolenbroek if (conn_info->state == ST_CLOSING0) {
18863e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_CLOSING1);
18873e07920fSDavid van Moolenbroek dispatch_SSL_shutdown(fd, 0, conn_info);
18883e07920fSDavid van Moolenbroek } else if (conn_info->state == ST_CLOSING1) {
18893e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_CLOSING2);
18903e07920fSDavid van Moolenbroek dispatch_SSL_shutdown(fd, 0, conn_info);
18913e07920fSDavid van Moolenbroek } else if (conn_info->state == ST_CLOSING2) {
18923e07920fSDavid van Moolenbroek /* abort shutdown, jump to close TCP below */
18933e07920fSDavid van Moolenbroek } else
18943e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "Unexpected connection state %d\n",
18953e07920fSDavid van Moolenbroek conn_info->state);
18963e07920fSDavid van Moolenbroek /* and abort here too*/
18973e07920fSDavid van Moolenbroek } else if (rc == -1 && conn_info->shutdown ) {
18983e07920fSDavid van Moolenbroek (void)tls_examine_error("SSL_shutdown()",
18993e07920fSDavid van Moolenbroek conn_info->sslptr, NULL, rc);
19003e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_NET), "Ignore error in SSL_shutdown()"
19013e07920fSDavid van Moolenbroek " and force connection shutdown.");
19023e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TCP_EST);
19033e07920fSDavid van Moolenbroek conn_info->accepted = false;
19043e07920fSDavid van Moolenbroek } else if (rc == -1 && !conn_info->shutdown ) {
19053e07920fSDavid van Moolenbroek error = tls_examine_error("SSL_shutdown()",
19063e07920fSDavid van Moolenbroek conn_info->sslptr, NULL, rc);
19073e07920fSDavid van Moolenbroek switch (error) {
19083e07920fSDavid van Moolenbroek case TLS_RETRY_READ:
19093e07920fSDavid van Moolenbroek if (!retrying)
19103e07920fSDavid van Moolenbroek event_del(conn_info->event);
19113e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_READ,
19123e07920fSDavid van Moolenbroek dispatch_SSL_shutdown, conn_info);
19133e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->retryevent);
19143e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
19153e07920fSDavid van Moolenbroek return;
19163e07920fSDavid van Moolenbroek case TLS_RETRY_WRITE:
19173e07920fSDavid van Moolenbroek if (!retrying)
19183e07920fSDavid van Moolenbroek event_del(conn_info->event);
19193e07920fSDavid van Moolenbroek event_set(conn_info->retryevent, fd, EV_WRITE,
19203e07920fSDavid van Moolenbroek dispatch_SSL_shutdown, conn_info);
19213e07920fSDavid van Moolenbroek EVENT_ADD(conn_info->retryevent);
19223e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
19233e07920fSDavid van Moolenbroek return;
19243e07920fSDavid van Moolenbroek default:
19253e07920fSDavid van Moolenbroek /* force close() on the TCP connection */
19263e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_TCP_EST);
19273e07920fSDavid van Moolenbroek conn_info->accepted = false;
19283e07920fSDavid van Moolenbroek break;
19293e07920fSDavid van Moolenbroek }
19303e07920fSDavid van Moolenbroek }
19313e07920fSDavid van Moolenbroek if ((conn_info->state != ST_TLS_EST)
19323e07920fSDavid van Moolenbroek && (conn_info->state != ST_NONE)
19333e07920fSDavid van Moolenbroek && (conn_info->state != ST_CLOSING0)
19343e07920fSDavid van Moolenbroek && (conn_info->state != ST_CLOSING1)) {
19353e07920fSDavid van Moolenbroek int sock = SSL_get_fd(conn_info->sslptr);
19363e07920fSDavid van Moolenbroek
19373e07920fSDavid van Moolenbroek if (shutdown(sock, SHUT_RDWR) == -1)
19383e07920fSDavid van Moolenbroek logerror("Cannot shutdown socket");
19393e07920fSDavid van Moolenbroek DEL_EVENT(conn_info->retryevent);
19403e07920fSDavid van Moolenbroek DEL_EVENT(conn_info->event);
19413e07920fSDavid van Moolenbroek
19423e07920fSDavid van Moolenbroek if (close(sock) == -1)
19433e07920fSDavid van Moolenbroek logerror("Cannot close socket");
19443e07920fSDavid van Moolenbroek DPRINTF((D_TLS|D_NET), "Closed TCP connection to %s\n",
19453e07920fSDavid van Moolenbroek conn_info->hostname);
19463e07920fSDavid van Moolenbroek ST_CHANGE(conn_info->state, ST_NONE);
19473e07920fSDavid van Moolenbroek FREE_SSL(conn_info->sslptr);
19483e07920fSDavid van Moolenbroek }
19493e07920fSDavid van Moolenbroek RESTORE_SIGNALS(omask);
19503e07920fSDavid van Moolenbroek }
19513e07920fSDavid van Moolenbroek
19523e07920fSDavid van Moolenbroek /*
19533e07920fSDavid van Moolenbroek * Close a SSL object
19543e07920fSDavid van Moolenbroek */
19553e07920fSDavid van Moolenbroek void
free_tls_sslptr(struct tls_conn_settings * conn_info)19563e07920fSDavid van Moolenbroek free_tls_sslptr(struct tls_conn_settings *conn_info)
19573e07920fSDavid van Moolenbroek {
19583e07920fSDavid van Moolenbroek int sock;
19593e07920fSDavid van Moolenbroek DPRINTF(D_MEM, "free_tls_sslptr(conn_info@%p)\n", conn_info);
19603e07920fSDavid van Moolenbroek
19613e07920fSDavid van Moolenbroek if (!conn_info->sslptr) {
19623e07920fSDavid van Moolenbroek assert(conn_info->incoming == 1
19633e07920fSDavid van Moolenbroek || conn_info->state == ST_NONE);
19643e07920fSDavid van Moolenbroek return;
19653e07920fSDavid van Moolenbroek } else {
19663e07920fSDavid van Moolenbroek sock = SSL_get_fd(conn_info->sslptr);
19673e07920fSDavid van Moolenbroek dispatch_SSL_shutdown(sock, 0, conn_info);
19683e07920fSDavid van Moolenbroek }
19693e07920fSDavid van Moolenbroek }
19703e07920fSDavid van Moolenbroek
19713e07920fSDavid van Moolenbroek /* write self-generated certificates */
19723e07920fSDavid van Moolenbroek bool
write_x509files(EVP_PKEY * pkey,X509 * cert,const char * keyfilename,const char * certfilename)19733e07920fSDavid van Moolenbroek write_x509files(EVP_PKEY *pkey, X509 *cert,
19743e07920fSDavid van Moolenbroek const char *keyfilename, const char *certfilename)
19753e07920fSDavid van Moolenbroek {
19763e07920fSDavid van Moolenbroek FILE *certfile, *keyfile;
19773e07920fSDavid van Moolenbroek
19783e07920fSDavid van Moolenbroek if (!(umask(0177),(keyfile = fopen(keyfilename, "a")))) {
19793e07920fSDavid van Moolenbroek logerror("Unable to write to file \"%s\"", keyfilename);
19803e07920fSDavid van Moolenbroek return false;
19813e07920fSDavid van Moolenbroek }
19823e07920fSDavid van Moolenbroek if (!(umask(0122),(certfile = fopen(certfilename, "a")))) {
19833e07920fSDavid van Moolenbroek logerror("Unable to write to file \"%s\"", certfilename);
19843e07920fSDavid van Moolenbroek (void)fclose(keyfile);
19853e07920fSDavid van Moolenbroek return false;
19863e07920fSDavid van Moolenbroek }
19873e07920fSDavid van Moolenbroek if (!PEM_write_PrivateKey(keyfile, pkey, NULL, NULL, 0, NULL, NULL))
19883e07920fSDavid van Moolenbroek logerror("Unable to write key to \"%s\"", keyfilename);
19893e07920fSDavid van Moolenbroek if (!X509_print_fp(certfile, cert)
19903e07920fSDavid van Moolenbroek || !PEM_write_X509(certfile, cert))
19913e07920fSDavid van Moolenbroek logerror("Unable to write certificate to \"%s\"",
19923e07920fSDavid van Moolenbroek certfilename);
19933e07920fSDavid van Moolenbroek
19943e07920fSDavid van Moolenbroek (void)fclose(keyfile);
19953e07920fSDavid van Moolenbroek (void)fclose(certfile);
19963e07920fSDavid van Moolenbroek return true;
19973e07920fSDavid van Moolenbroek }
19983e07920fSDavid van Moolenbroek
19993e07920fSDavid van Moolenbroek
20003e07920fSDavid van Moolenbroek /* adds all local IP addresses as subjectAltNames to cert x.
20013e07920fSDavid van Moolenbroek * getifaddrs() should be quite portable among BSDs and Linux
20023e07920fSDavid van Moolenbroek * but if not available the whole function can simply be removed.
20033e07920fSDavid van Moolenbroek */
20043e07920fSDavid van Moolenbroek bool
x509_cert_add_subjectAltName(X509 * cert,X509V3_CTX * ctx)20053e07920fSDavid van Moolenbroek x509_cert_add_subjectAltName(X509 *cert, X509V3_CTX *ctx)
20063e07920fSDavid van Moolenbroek {
20073e07920fSDavid van Moolenbroek struct ifaddrs *ifa = NULL, *ifp = NULL;
20083e07920fSDavid van Moolenbroek char ip[100];
20093e07920fSDavid van Moolenbroek char subjectAltName[2048];
20103e07920fSDavid van Moolenbroek int idx = 0;
20113e07920fSDavid van Moolenbroek socklen_t salen;
20123e07920fSDavid van Moolenbroek X509_EXTENSION *ext;
20133e07920fSDavid van Moolenbroek #ifdef notdef
20143e07920fSDavid van Moolenbroek STACK_OF(X509_EXTENSION) *extlist;
20153e07920fSDavid van Moolenbroek extlist = sk_X509_EXTENSION_new_null();
20163e07920fSDavid van Moolenbroek #endif
20173e07920fSDavid van Moolenbroek
20183e07920fSDavid van Moolenbroek if (getifaddrs (&ifp) == -1) {
20193e07920fSDavid van Moolenbroek logerror("Unable to get list of local interfaces");
20203e07920fSDavid van Moolenbroek return false;
20213e07920fSDavid van Moolenbroek }
20223e07920fSDavid van Moolenbroek
20233e07920fSDavid van Moolenbroek idx = snprintf(subjectAltName, sizeof(subjectAltName),
20243e07920fSDavid van Moolenbroek "DNS:%s", LocalFQDN);
20253e07920fSDavid van Moolenbroek
20263e07920fSDavid van Moolenbroek for (ifa = ifp; ifa; ifa = ifa->ifa_next) {
20273e07920fSDavid van Moolenbroek if(!ifa->ifa_addr)
20283e07920fSDavid van Moolenbroek continue;
20293e07920fSDavid van Moolenbroek
20303e07920fSDavid van Moolenbroek /* only IP4 and IP6 addresses, but filter loopbacks */
20313e07920fSDavid van Moolenbroek if (ifa->ifa_addr->sa_family == AF_INET) {
20323e07920fSDavid van Moolenbroek struct sockaddr_in *addr =
20333e07920fSDavid van Moolenbroek (struct sockaddr_in *)ifa->ifa_addr;
20343e07920fSDavid van Moolenbroek if (addr->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
20353e07920fSDavid van Moolenbroek continue;
20363e07920fSDavid van Moolenbroek salen = sizeof(struct sockaddr_in);
20373e07920fSDavid van Moolenbroek } else if (ifa->ifa_addr->sa_family == AF_INET6) {
20383e07920fSDavid van Moolenbroek struct in6_addr *addr6 =
20393e07920fSDavid van Moolenbroek &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
20403e07920fSDavid van Moolenbroek if (IN6_IS_ADDR_LOOPBACK(addr6))
20413e07920fSDavid van Moolenbroek continue;
20423e07920fSDavid van Moolenbroek salen = sizeof(struct sockaddr_in6);
20433e07920fSDavid van Moolenbroek } else
20443e07920fSDavid van Moolenbroek continue;
20453e07920fSDavid van Moolenbroek
20463e07920fSDavid van Moolenbroek if (getnameinfo(ifa->ifa_addr, salen, ip, sizeof(ip),
20473e07920fSDavid van Moolenbroek NULL, 0, NI_NUMERICHOST)) {
20483e07920fSDavid van Moolenbroek continue;
20493e07920fSDavid van Moolenbroek }
20503e07920fSDavid van Moolenbroek
20513e07920fSDavid van Moolenbroek /* add IP to list */
20523e07920fSDavid van Moolenbroek idx += snprintf(&subjectAltName[idx],
20533e07920fSDavid van Moolenbroek sizeof(subjectAltName)-idx, ", IP:%s", ip);
20543e07920fSDavid van Moolenbroek }
20553e07920fSDavid van Moolenbroek freeifaddrs (ifp);
20563e07920fSDavid van Moolenbroek
20573e07920fSDavid van Moolenbroek ext = X509V3_EXT_conf_nid(NULL, ctx,
20583e07920fSDavid van Moolenbroek NID_subject_alt_name, subjectAltName);
20593e07920fSDavid van Moolenbroek X509_add_ext(cert, ext, -1);
20603e07920fSDavid van Moolenbroek X509_EXTENSION_free(ext);
20613e07920fSDavid van Moolenbroek
20623e07920fSDavid van Moolenbroek return true;
20633e07920fSDavid van Moolenbroek }
20643e07920fSDavid van Moolenbroek
20653e07920fSDavid van Moolenbroek /*
20663e07920fSDavid van Moolenbroek * generates a private key and a X.509 certificate
20673e07920fSDavid van Moolenbroek */
20683e07920fSDavid van Moolenbroek bool
mk_x509_cert(X509 ** x509p,EVP_PKEY ** pkeyp,int bits,int serial,int days)20693e07920fSDavid van Moolenbroek mk_x509_cert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int days)
20703e07920fSDavid van Moolenbroek {
20713e07920fSDavid van Moolenbroek X509 *cert;
20723e07920fSDavid van Moolenbroek EVP_PKEY *pk;
20733e07920fSDavid van Moolenbroek DSA *dsa;
20743e07920fSDavid van Moolenbroek X509_NAME *name = NULL;
20753e07920fSDavid van Moolenbroek X509_EXTENSION *ex = NULL;
20763e07920fSDavid van Moolenbroek X509V3_CTX ctx;
20773e07920fSDavid van Moolenbroek
20783e07920fSDavid van Moolenbroek DPRINTF((D_CALL|D_TLS), "mk_x509_cert(%p, %p, %d, %d, %d)\n",
20793e07920fSDavid van Moolenbroek x509p, pkeyp, bits, serial, days);
20803e07920fSDavid van Moolenbroek
20813e07920fSDavid van Moolenbroek if (pkeyp && *pkeyp)
20823e07920fSDavid van Moolenbroek pk = *pkeyp;
20833e07920fSDavid van Moolenbroek else if ((pk = EVP_PKEY_new()) == NULL) {
20843e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "EVP_PKEY_new() failed\n");
20853e07920fSDavid van Moolenbroek return false;
20863e07920fSDavid van Moolenbroek }
20873e07920fSDavid van Moolenbroek
20883e07920fSDavid van Moolenbroek if (x509p && *x509p)
20893e07920fSDavid van Moolenbroek cert = *x509p;
20903e07920fSDavid van Moolenbroek else if ((cert = X509_new()) == NULL) {
20913e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "X509_new() failed\n");
20923e07920fSDavid van Moolenbroek return false;
20933e07920fSDavid van Moolenbroek }
20943e07920fSDavid van Moolenbroek
20953e07920fSDavid van Moolenbroek dsa = DSA_generate_parameters(bits, NULL, 0,
20963e07920fSDavid van Moolenbroek NULL, NULL, NULL, NULL);
20973e07920fSDavid van Moolenbroek if (!DSA_generate_key(dsa)) {
20983e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "DSA_generate_key() failed\n");
20993e07920fSDavid van Moolenbroek return false;
21003e07920fSDavid van Moolenbroek }
21013e07920fSDavid van Moolenbroek if (!EVP_PKEY_assign_DSA(pk, dsa)) {
21023e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "EVP_PKEY_assign_DSA() failed\n");
21033e07920fSDavid van Moolenbroek return false;
21043e07920fSDavid van Moolenbroek }
21053e07920fSDavid van Moolenbroek
21063e07920fSDavid van Moolenbroek X509_set_version(cert, 3);
21073e07920fSDavid van Moolenbroek ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
21083e07920fSDavid van Moolenbroek X509_gmtime_adj(X509_get_notBefore(cert), 0);
21093e07920fSDavid van Moolenbroek X509_gmtime_adj(X509_get_notAfter(cert), (long)60 * 60 * 24 * days);
21103e07920fSDavid van Moolenbroek
21113e07920fSDavid van Moolenbroek if (!X509_set_pubkey(cert, pk)) {
21123e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "X509_set_pubkey() failed\n");
21133e07920fSDavid van Moolenbroek return false;
21143e07920fSDavid van Moolenbroek }
21153e07920fSDavid van Moolenbroek
21163e07920fSDavid van Moolenbroek /*
21173e07920fSDavid van Moolenbroek * This function creates and adds the entry, working out the correct
21183e07920fSDavid van Moolenbroek * string type and performing checks on its length. Normally we'd check
21193e07920fSDavid van Moolenbroek * the return value for errors...
21203e07920fSDavid van Moolenbroek */
21213e07920fSDavid van Moolenbroek name = X509_get_subject_name(cert);
21223e07920fSDavid van Moolenbroek /*
21233e07920fSDavid van Moolenbroek X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
21243e07920fSDavid van Moolenbroek (unsigned char *)"The NetBSD Project", -1, -1, 0);
21253e07920fSDavid van Moolenbroek X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC,
21263e07920fSDavid van Moolenbroek (unsigned char *)"syslogd", -1, -1, 0);
21273e07920fSDavid van Moolenbroek */
21283e07920fSDavid van Moolenbroek X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
21293e07920fSDavid van Moolenbroek (unsigned char *) LocalFQDN, -1, -1, 0);
21303e07920fSDavid van Moolenbroek X509_set_issuer_name(cert, name);
21313e07920fSDavid van Moolenbroek
21323e07920fSDavid van Moolenbroek /*
21333e07920fSDavid van Moolenbroek * Add extension using V3 code: we can set the config file as NULL
21343e07920fSDavid van Moolenbroek * because we wont reference any other sections.
21353e07920fSDavid van Moolenbroek */
21363e07920fSDavid van Moolenbroek X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
21373e07920fSDavid van Moolenbroek
21383e07920fSDavid van Moolenbroek ex = X509V3_EXT_conf_nid(NULL, &ctx, NID_netscape_comment,
21393e07920fSDavid van Moolenbroek __UNCONST("auto-generated by the NetBSD syslogd"));
21403e07920fSDavid van Moolenbroek X509_add_ext(cert, ex, -1);
21413e07920fSDavid van Moolenbroek X509_EXTENSION_free(ex);
21423e07920fSDavid van Moolenbroek
21433e07920fSDavid van Moolenbroek ex = X509V3_EXT_conf_nid(NULL, &ctx, NID_netscape_ssl_server_name,
21443e07920fSDavid van Moolenbroek LocalFQDN);
21453e07920fSDavid van Moolenbroek X509_add_ext(cert, ex, -1);
21463e07920fSDavid van Moolenbroek X509_EXTENSION_free(ex);
21473e07920fSDavid van Moolenbroek
21483e07920fSDavid van Moolenbroek ex = X509V3_EXT_conf_nid(NULL, &ctx, NID_netscape_cert_type,
21493e07920fSDavid van Moolenbroek __UNCONST("server, client"));
21503e07920fSDavid van Moolenbroek X509_add_ext(cert, ex, -1);
21513e07920fSDavid van Moolenbroek X509_EXTENSION_free(ex);
21523e07920fSDavid van Moolenbroek
21533e07920fSDavid van Moolenbroek ex = X509V3_EXT_conf_nid(NULL, &ctx, NID_key_usage,
21543e07920fSDavid van Moolenbroek __UNCONST("keyAgreement, keyEncipherment, "
21553e07920fSDavid van Moolenbroek "nonRepudiation, digitalSignature"));
21563e07920fSDavid van Moolenbroek X509_add_ext(cert, ex, -1);
21573e07920fSDavid van Moolenbroek X509_EXTENSION_free(ex);
21583e07920fSDavid van Moolenbroek
21593e07920fSDavid van Moolenbroek ex = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints,
21603e07920fSDavid van Moolenbroek __UNCONST("critical,CA:FALSE"));
21613e07920fSDavid van Moolenbroek X509_add_ext(cert, ex, -1);
21623e07920fSDavid van Moolenbroek X509_EXTENSION_free(ex);
21633e07920fSDavid van Moolenbroek
21643e07920fSDavid van Moolenbroek (void)x509_cert_add_subjectAltName(cert, &ctx);
21653e07920fSDavid van Moolenbroek
21663e07920fSDavid van Moolenbroek if (!X509_sign(cert, pk, EVP_dss1())) {
21673e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "X509_sign() failed\n");
21683e07920fSDavid van Moolenbroek return false;
21693e07920fSDavid van Moolenbroek }
21703e07920fSDavid van Moolenbroek if (X509_verify(cert, pk) != 1) {
21713e07920fSDavid van Moolenbroek DPRINTF(D_TLS, "X509_verify() failed\n");
21723e07920fSDavid van Moolenbroek return false;
21733e07920fSDavid van Moolenbroek }
21743e07920fSDavid van Moolenbroek
21753e07920fSDavid van Moolenbroek *x509p = cert;
21763e07920fSDavid van Moolenbroek *pkeyp = pk;
21773e07920fSDavid van Moolenbroek return true;
21783e07920fSDavid van Moolenbroek }
21793e07920fSDavid van Moolenbroek
21803e07920fSDavid van Moolenbroek void
free_tls_send_msg(struct tls_send_msg * msg)21813e07920fSDavid van Moolenbroek free_tls_send_msg(struct tls_send_msg *msg)
21823e07920fSDavid van Moolenbroek {
21833e07920fSDavid van Moolenbroek if (!msg) {
21843e07920fSDavid van Moolenbroek DPRINTF((D_DATA), "invalid tls_send_msg_free(NULL)\n");
21853e07920fSDavid van Moolenbroek return;
21863e07920fSDavid van Moolenbroek }
21873e07920fSDavid van Moolenbroek DELREF(msg->qentry->msg);
21883e07920fSDavid van Moolenbroek (void)message_queue_remove(msg->f, msg->qentry);
21893e07920fSDavid van Moolenbroek FREEPTR(msg->line);
21903e07920fSDavid van Moolenbroek FREEPTR(msg);
21913e07920fSDavid van Moolenbroek }
21923e07920fSDavid van Moolenbroek #endif /* !DISABLE_TLS */
2193