From: bluhm Date: Sun, 18 Jan 2015 19:37:59 +0000 (+0000) Subject: Implement sending syslog messages over TLS. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=b60e1f73b6566ceae714960a532385331d2fc9e5;p=openbsd Implement sending syslog messages over TLS. OK reyk@ --- diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile index 98add7b6ba4..236d683964b 100644 --- a/usr.sbin/syslogd/Makefile +++ b/usr.sbin/syslogd/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.6 2014/10/05 18:14:01 bluhm Exp $ +# $OpenBSD: Makefile,v 1.7 2015/01/18 19:37:59 bluhm Exp $ PROG= syslogd -SRCS= syslogd.c ttymsg.c privsep.c privsep_fdpass.c ringbuf.c +SRCS= syslogd.c ttymsg.c privsep.c privsep_fdpass.c ringbuf.c evbuffer_tls.c MAN= syslogd.8 syslog.conf.5 -LDADD= -levent -DPADD= ${LIBEVENT} +LDADD= -levent -ltls -lssl -lcrypto +DPADD= ${LIBEVENT} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} .include diff --git a/usr.sbin/syslogd/evbuffer_tls.c b/usr.sbin/syslogd/evbuffer_tls.c new file mode 100644 index 00000000000..d593489a214 --- /dev/null +++ b/usr.sbin/syslogd/evbuffer_tls.c @@ -0,0 +1,357 @@ +/* $OpenBSD: evbuffer_tls.c,v 1.1 2015/01/18 19:37:59 bluhm Exp $ */ + +/* + * Copyright (c) 2002-2004 Niels Provos + * Copyright (c) 2014-2015 Alexander Bluhm + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "evbuffer_tls.h" + +/* prototypes */ + +void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *); +int evtls_read(struct evbuffer *, int, int, struct tls *); +int evtls_write(struct evbuffer *, int, struct tls *); + +static int +bufferevent_add(struct event *ev, int timeout) +{ + struct timeval tv, *ptv = NULL; + + if (timeout) { + timerclear(&tv); + tv.tv_sec = timeout; + ptv = &tv; + } + + return (event_add(ev, ptv)); +} + +static void +buffertls_readcb(int fd, short event, void *arg) +{ + struct buffertls *buftls = arg; + struct bufferevent *bufev = buftls->bt_bufev; + struct tls *ctx = buftls->bt_ctx; + int res = 0; + short what = EVBUFFER_READ; + size_t len; + int howmuch = -1; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto error; + } + + /* + * If we have a high watermark configured then we don't want to + * read more data than would make us reach the watermark. + */ + if (bufev->wm_read.high != 0) { + howmuch = bufev->wm_read.high - EVBUFFER_LENGTH(bufev->input); + /* we might have lowered the watermark, stop reading */ + if (howmuch <= 0) { + struct evbuffer *buf = bufev->input; + event_del(&bufev->ev_read); + evbuffer_setcb(buf, + bufferevent_read_pressure_cb, bufev); + return; + } + } + + res = evtls_read(bufev->input, fd, howmuch, ctx); + switch (res) { + case TLS_READ_AGAIN: + event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, + buftls); + goto reschedule; + case TLS_WRITE_AGAIN: + event_set(&bufev->ev_read, fd, EV_WRITE, buffertls_readcb, + buftls); + goto reschedule; + case -1: + if (errno == EAGAIN || errno == EINTR) + goto reschedule; + /* error case */ + what |= EVBUFFER_ERROR; + break; + case 0: + /* eof case */ + what |= EVBUFFER_EOF; + break; + } + if (res <= 0) + goto error; + + event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls); + bufferevent_add(&bufev->ev_read, bufev->timeout_read); + + /* See if this callbacks meets the water marks */ + len = EVBUFFER_LENGTH(bufev->input); + if (bufev->wm_read.low != 0 && len < bufev->wm_read.low) + return; + if (bufev->wm_read.high != 0 && len >= bufev->wm_read.high) { + struct evbuffer *buf = bufev->input; + event_del(&bufev->ev_read); + + /* Now schedule a callback for us when the buffer changes */ + evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev); + } + + /* Invoke the user callback - must always be called last */ + if (bufev->readcb != NULL) + (*bufev->readcb)(bufev, bufev->cbarg); + return; + + reschedule: + bufferevent_add(&bufev->ev_read, bufev->timeout_read); + return; + + error: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +static void +buffertls_writecb(int fd, short event, void *arg) +{ + struct buffertls *buftls = arg; + struct bufferevent *bufev = buftls->bt_bufev; + struct tls *ctx = buftls->bt_ctx; + int res = 0; + short what = EVBUFFER_WRITE; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto error; + } + + if (EVBUFFER_LENGTH(bufev->output) != 0) { + res = evtls_write(bufev->output, fd, ctx); + switch (res) { + case TLS_READ_AGAIN: + event_set(&bufev->ev_write, fd, EV_READ, + buffertls_writecb, buftls); + goto reschedule; + case TLS_WRITE_AGAIN: + event_set(&bufev->ev_write, fd, EV_WRITE, + buffertls_writecb, buftls); + goto reschedule; + case -1: + if (errno == EAGAIN || errno == EINTR || + errno == EINPROGRESS) + goto reschedule; + /* error case */ + what |= EVBUFFER_ERROR; + break; + case 0: + /* eof case */ + what |= EVBUFFER_EOF; + break; + } + if (res <= 0) + goto error; + } + + event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); + if (EVBUFFER_LENGTH(bufev->output) != 0) + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + + /* + * Invoke the user callback if our buffer is drained or below the + * low watermark. + */ + if (bufev->writecb != NULL && + EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low) + (*bufev->writecb)(bufev, bufev->cbarg); + + return; + + reschedule: + if (EVBUFFER_LENGTH(bufev->output) != 0) + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + return; + + error: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +static void +buffertls_connectcb(int fd, short event, void *arg) +{ + struct buffertls *buftls = arg; + struct bufferevent *bufev = buftls->bt_bufev; + struct tls *ctx = buftls->bt_ctx; + const char *hostname = buftls->bt_hostname; + int res = 0; + short what = EVBUFFER_CONNECT; + + if (event == EV_TIMEOUT) { + what |= EVBUFFER_TIMEOUT; + goto error; + } + + res = tls_connect_socket(ctx, fd, hostname); + switch (res) { + case TLS_READ_AGAIN: + event_set(&bufev->ev_write, fd, EV_READ, + buffertls_connectcb, buftls); + goto reschedule; + case TLS_WRITE_AGAIN: + event_set(&bufev->ev_write, fd, EV_WRITE, + buffertls_connectcb, buftls); + goto reschedule; + case -1: + if (errno == EAGAIN || errno == EINTR || + errno == EINPROGRESS) + goto reschedule; + /* error case */ + what |= EVBUFFER_ERROR; + break; + } + if (res < 0) + goto error; + + /* + * There might be data available in the tls layer. Try + * an read operation and setup the callbacks. Call the read + * callback after enabling the write callback to give the + * read error handler a chance to disable the write event. + */ + event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); + if (EVBUFFER_LENGTH(bufev->output) != 0) + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + buffertls_readcb(fd, 0, buftls); + + return; + + reschedule: + bufferevent_add(&bufev->ev_write, bufev->timeout_write); + return; + + error: + (*bufev->errorcb)(bufev, what, bufev->cbarg); +} + +void +buffertls_set(struct buffertls *buftls, struct bufferevent *bufev, + struct tls *ctx, int fd) +{ + bufferevent_setfd(bufev, fd); + event_set(&bufev->ev_read, fd, EV_READ, buffertls_readcb, buftls); + event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_writecb, buftls); + buftls->bt_bufev = bufev; + buftls->bt_ctx = ctx; +} + +void +buffertls_connect(struct buffertls *buftls, int fd, const char *hostname) +{ + struct bufferevent *bufev = buftls->bt_bufev; + + event_del(&bufev->ev_read); + event_del(&bufev->ev_write); + + buftls->bt_hostname = hostname; + buffertls_connectcb(fd, 0, buftls); +} + +/* + * Reads data from a file descriptor into a buffer. + */ + +#define EVBUFFER_MAX_READ 4096 + +int +evtls_read(struct evbuffer *buf, int fd, int howmuch, struct tls *ctx) +{ + u_char *p; + size_t len, oldoff = buf->off; + int n = EVBUFFER_MAX_READ; + + if (ioctl(fd, FIONREAD, &n) == -1 || n <= 0) { + n = EVBUFFER_MAX_READ; + } else if (n > EVBUFFER_MAX_READ && n > howmuch) { + /* + * It's possible that a lot of data is available for + * reading. We do not want to exhaust resources + * before the reader has a chance to do something + * about it. If the reader does not tell us how much + * data we should read, we artifically limit it. + */ + if ((size_t)n > buf->totallen << 2) + n = buf->totallen << 2; + if (n < EVBUFFER_MAX_READ) + n = EVBUFFER_MAX_READ; + } + if (howmuch < 0 || howmuch > n) + howmuch = n; + + /* If we don't have FIONREAD, we might waste some space here */ + if (evbuffer_expand(buf, howmuch) == -1) + return (-1); + + /* We can append new data at this point */ + p = buf->buffer + buf->off; + + n = tls_read(ctx, p, howmuch, &len); + if (n < 0 || len == 0) + return (n); + + buf->off += len; + + /* Tell someone about changes in this buffer */ + if (buf->off != oldoff && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + + return (len); +} + +int +evtls_write(struct evbuffer *buffer, int fd, struct tls *ctx) +{ + size_t len; + int n; + + n = tls_write(ctx, buffer->buffer, buffer->off, &len); + if (n < 0 || len == 0) + return (n); + + evbuffer_drain(buffer, len); + + return (len); +} diff --git a/usr.sbin/syslogd/evbuffer_tls.h b/usr.sbin/syslogd/evbuffer_tls.h new file mode 100644 index 00000000000..ba81f80c322 --- /dev/null +++ b/usr.sbin/syslogd/evbuffer_tls.h @@ -0,0 +1,37 @@ +/* $OpenBSD: evbuffer_tls.h,v 1.1 2015/01/18 19:37:59 bluhm Exp $ */ + +/* + * Copyright (c) 2014-2015 Alexander Bluhm + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _EVBUFFER_TLS_H_ +#define _EVBUFFER_TLS_H_ + +#define EVBUFFER_CONNECT 0x80 + +struct bufferevent; +struct tls; + +struct buffertls { + struct bufferevent *bt_bufev; + struct tls *bt_ctx; + const char *bt_hostname; +}; + +void buffertls_set(struct buffertls *, struct bufferevent *, struct tls *, + int); +void buffertls_connect(struct buffertls *, int, const char *); + +#endif /* _EVBUFFER_TLS_H_ */ diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index c94936e342d..915cba56475 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: syslogd.c,v 1.142 2015/01/16 06:40:21 deraadt Exp $ */ +/* $OpenBSD: syslogd.c,v 1.143 2015/01/18 19:37:59 bluhm Exp $ */ /* * Copyright (c) 1983, 1988, 1993, 1994 @@ -50,7 +50,7 @@ * extensive changes by Ralph Campbell * more extensive changes by Eric Allman (again) * memory buffer logging by Damien Miller - * IPv6, libevent, sending via TCP by Alexander Bluhm + * IPv6, libevent, sending over TCP and TLS by Alexander Bluhm */ #define MAXLINE 1024 /* maximum line length */ @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ #include #include "syslogd.h" +#include "evbuffer_tls.h" char *ConfFile = _PATH_LOGCONF; const char ctty[] = _PATH_CONSOLE; @@ -135,9 +137,11 @@ struct filed { struct { char f_loghost[1+4+3+1+HOST_NAME_MAX+1+1+NI_MAXSERV]; /* @proto46://[hostname]:servname\0 */ - struct sockaddr_storage f_addr; + struct sockaddr_storage f_addr; + struct buffertls f_buftls; struct bufferevent *f_bufev; - int f_reconnectwait; + struct tls *f_ctx; + int f_reconnectwait; } f_forw; /* forwarding address */ char f_fname[PATH_MAX]; struct { @@ -182,11 +186,12 @@ int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */ #define F_MEMBUF 7 /* memory buffer */ #define F_PIPE 8 /* pipe to external program */ #define F_FORWTCP 9 /* remote machine via TCP */ +#define F_FORWTLS 10 /* remote machine via TLS */ char *TypeNames[] = { "UNUSED", "FILE", "TTY", "CONSOLE", "FORWUDP", "USERS", "WALL", "MEMBUF", - "PIPE", "FORWTCP", + "PIPE", "FORWTCP", "FORWTLS", }; SIMPLEQ_HEAD(filed_list, filed) Files; @@ -271,6 +276,7 @@ void tcp_readcb(struct bufferevent *, void *); void tcp_writecb(struct bufferevent *, void *); void tcp_errorcb(struct bufferevent *, short, void *); void tcp_connectcb(int, short, void *); +struct tls *tls_socket(struct filed *); void die_signalcb(int, short, void *); void mark_timercb(int, short, void *); void init_signalcb(int, short, void *); @@ -715,8 +721,7 @@ tcp_readcb(struct bufferevent *bufev, void *arg) * Drop data received from the forward log server. */ dprintf("loghost \"%s\" did send %zu bytes back\n", - f->f_un.f_forw.f_loghost, - EVBUFFER_LENGTH(f->f_un.f_forw.f_bufev->input)); + f->f_un.f_forw.f_loghost, EVBUFFER_LENGTH(bufev->input)); evbuffer_drain(bufev->input, -1); } @@ -745,10 +750,16 @@ tcp_errorcb(struct bufferevent *bufev, short event, void *arg) else snprintf(ebuf, sizeof(ebuf), "syslogd: loghost \"%s\" connection error: %s", - f->f_un.f_forw.f_loghost, strerror(errno)); + f->f_un.f_forw.f_loghost, f->f_un.f_forw.f_ctx ? + tls_error(f->f_un.f_forw.f_ctx) : strerror(errno)); dprintf("%s\n", ebuf); /* The SIGHUP handler may also close the socket, so invalidate it. */ + if (f->f_un.f_forw.f_ctx) { + tls_close(f->f_un.f_forw.f_ctx); + tls_free(f->f_un.f_forw.f_ctx); + f->f_un.f_forw.f_ctx = NULL; + } close(f->f_file); f->f_file = -1; @@ -768,6 +779,7 @@ tcp_connectcb(int fd, short event, void *arg) { struct filed *f = arg; struct bufferevent *bufev = f->f_un.f_forw.f_bufev; + struct tls *ctx; struct timeval to; int s; @@ -780,8 +792,9 @@ tcp_connectcb(int fd, short event, void *arg) if ((s = tcp_socket(f)) == -1) goto retry; + dprintf("tcp connect callback: socket success, event %#x\n", event); + f->f_file = s; - dprintf("tcp connect callback: success, event %#x\n", event); bufferevent_setfd(bufev, s); bufferevent_setcb(bufev, tcp_readcb, tcp_writecb, tcp_errorcb, f); /* @@ -789,7 +802,20 @@ tcp_connectcb(int fd, short event, void *arg) * the socket to detect connection close and errors. */ bufferevent_enable(bufev, EV_READ|EV_WRITE); - f->f_file = s; + + if (f->f_type == F_FORWTLS) { + if ((ctx = tls_socket(f)) == NULL) { + close(f->f_file); + f->f_file = -1; + goto retry; + } + dprintf("tcp connect callback: TLS context success\n"); + f->f_un.f_forw.f_ctx = ctx; + + buffertls_set(&f->f_un.f_forw.f_buftls, bufev, ctx, s); + /* XXX no host given */ + buffertls_connect(&f->f_un.f_forw.f_buftls, s, NULL); + } return; @@ -808,6 +834,46 @@ tcp_connectcb(int fd, short event, void *arg) evtimer_add(&bufev->ev_write, &to); } +struct tls * +tls_socket(struct filed *f) +{ + static struct tls_config *config; + struct tls *ctx; + char ebuf[100]; + + if (config == NULL) { + if (tls_init() < 0) { + snprintf(ebuf, sizeof(ebuf), "tls_init \"%s\"", + f->f_un.f_forw.f_loghost); + logerror(ebuf); + return (NULL); + } + if ((config = tls_config_new()) == NULL) { + snprintf(ebuf, sizeof(ebuf), "tls_config_new \"%s\"", + f->f_un.f_forw.f_loghost); + logerror(ebuf); + return (NULL); + } + /* XXX No verify for now, ca certs are outside of privsep. */ + tls_config_insecure_noverifyhost(config); + tls_config_insecure_noverifycert(config); + } + if ((ctx = tls_client()) == NULL) { + snprintf(ebuf, sizeof(ebuf), "tls_client \"%s\"", + f->f_un.f_forw.f_loghost); + logerror(ebuf); + return (NULL); + } + if (tls_configure(ctx, config) < 0) { + snprintf(ebuf, sizeof(ebuf), "tls_configure \"%s\": %s", + f->f_un.f_forw.f_loghost, tls_error(ctx)); + logerror(ebuf); + tls_free(ctx); + return (NULL); + } + return (ctx); +} + void usage(void) { @@ -1114,6 +1180,7 @@ fprintlog(struct filed *f, int flags, char *msg) break; case F_FORWTCP: + case F_FORWTLS: dprintf(" %s\n", f->f_un.f_forw.f_loghost); if (EVBUFFER_LENGTH(f->f_un.f_forw.f_bufev->output) >= MAX_TCPBUF) @@ -1402,6 +1469,12 @@ init(void) fprintlog(f, 0, (char *)NULL); switch (f->f_type) { + case F_FORWTLS: + if (f->f_un.f_forw.f_ctx) { + tls_close(f->f_un.f_forw.f_ctx); + tls_free(f->f_un.f_forw.f_ctx); + } + /* FALLTHROUGH */ case F_FORWTCP: /* XXX Save messages in output buffer for reconnect. */ bufferevent_free(f->f_un.f_forw.f_bufev); @@ -1542,6 +1615,7 @@ init(void) case F_FORWUDP: case F_FORWTCP: + case F_FORWTLS: printf("%s", f->f_un.f_forw.f_loghost); break; @@ -1604,9 +1678,10 @@ cfline(char *line, char *prog) { int i, pri; size_t rb_len; - char *bp, *p, *q, *proto, *host, *port; + char *bp, *p, *q, *proto, *host, *port, *ipproto; char buf[MAXLINE], ebuf[100]; struct filed *xf, *f, *d; + struct timeval to; dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog); @@ -1714,11 +1789,13 @@ cfline(char *line, char *prog) } if (proto == NULL) proto = "udp"; + ipproto = proto; if (strcmp(proto, "udp") == 0) { if (fd_udp == -1) proto = "udp6"; if (fd_udp6 == -1) proto = "udp4"; + ipproto = proto; } else if (strcmp(proto, "udp4") == 0) { if (fd_udp == -1) { snprintf(ebuf, sizeof(ebuf), "no udp4 \"%s\"", @@ -1736,6 +1813,12 @@ cfline(char *line, char *prog) } else if (strcmp(proto, "tcp") == 0 || strcmp(proto, "tcp4") == 0 || strcmp(proto, "tcp6") == 0) { ; + } else if (strcmp(proto, "tls") == 0) { + ipproto = "tcp"; + } else if (strcmp(proto, "tls4") == 0) { + ipproto = "tcp4"; + } else if (strcmp(proto, "tls6") == 0) { + ipproto = "tcp6"; } else { snprintf(ebuf, sizeof(ebuf), "bad protocol \"%s\"", f->f_un.f_forw.f_loghost); @@ -1749,14 +1832,15 @@ cfline(char *line, char *prog) break; } if (port == NULL) - port = "syslog"; + port = strncmp(proto, "tls", 3) == 0 ? + "syslog-tls" : "syslog"; if (strlen(port) >= NI_MAXSERV) { snprintf(ebuf, sizeof(ebuf), "port too long \"%s\"", f->f_un.f_forw.f_loghost); logerror(ebuf); break; } - if (priv_getaddrinfo(proto, host, port, + if (priv_getaddrinfo(ipproto, host, port, (struct sockaddr*)&f->f_un.f_forw.f_addr, sizeof(f->f_un.f_forw.f_addr)) != 0) { snprintf(ebuf, sizeof(ebuf), "bad hostname \"%s\"", @@ -1775,7 +1859,7 @@ cfline(char *line, char *prog) break; } f->f_type = F_FORWUDP; - } else if (strncmp(proto, "tcp", 3) == 0) { + } else if (strncmp(ipproto, "tcp", 3) == 0) { if ((f->f_un.f_forw.f_bufev = bufferevent_new(-1, tcp_readcb, tcp_writecb, tcp_errorcb, f)) == NULL) { snprintf(ebuf, sizeof(ebuf), @@ -1784,8 +1868,23 @@ cfline(char *line, char *prog) logerror(ebuf); break; } - f->f_type = F_FORWTCP; - tcp_connectcb(-1, 0, f); + if (strncmp(proto, "tls", 3) == 0) { + f->f_type = F_FORWTLS; + } else { + f->f_type = F_FORWTCP; + } + /* + * If we try to connect to a TLS server immediately + * syslogd gets an SIGPIPE as the signal handlers have + * not been set up. Delay the connection until the + * event loop is started. We can reuse the write event + * for that as bufferevent is still disabled. + */ + to.tv_sec = 0; + to.tv_usec = 1; + evtimer_set(&f->f_un.f_forw.f_bufev->ev_write, + tcp_connectcb, f); + evtimer_add(&f->f_un.f_forw.f_bufev->ev_write, &to); } break;