From 1cfd376a1dba5eeb3201d3836982234b3b55bbe4 Mon Sep 17 00:00:00 2001 From: bluhm Date: Fri, 9 Oct 2015 16:58:25 +0000 Subject: [PATCH] If syslogd is started with -S, it accepts TLS connections to receive encrypted messages. The server certificates are taken from /etc/ssl like relayd does. OK benno@ beck@ deraadt@ --- usr.sbin/syslogd/evbuffer_tls.c | 15 ++- usr.sbin/syslogd/evbuffer_tls.h | 3 +- usr.sbin/syslogd/privsep.c | 4 +- usr.sbin/syslogd/syslogd.8 | 24 +++- usr.sbin/syslogd/syslogd.c | 224 ++++++++++++++++++++++++++------ usr.sbin/syslogd/syslogd.h | 4 +- 6 files changed, 224 insertions(+), 50 deletions(-) diff --git a/usr.sbin/syslogd/evbuffer_tls.c b/usr.sbin/syslogd/evbuffer_tls.c index 0f7edbec8cc..c1a081fb7cf 100644 --- a/usr.sbin/syslogd/evbuffer_tls.c +++ b/usr.sbin/syslogd/evbuffer_tls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: evbuffer_tls.c,v 1.8 2015/09/20 21:49:54 bluhm Exp $ */ +/* $OpenBSD: evbuffer_tls.c,v 1.9 2015/10/09 16:58:25 bluhm Exp $ */ /* * Copyright (c) 2002-2004 Niels Provos @@ -259,6 +259,19 @@ buffertls_set(struct buffertls *buftls, struct bufferevent *bufev, buftls->bt_ctx = ctx; } +void +buffertls_accept(struct buffertls *buftls, int fd) +{ + struct bufferevent *bufev = buftls->bt_bufev; + + event_del(&bufev->ev_read); + event_del(&bufev->ev_write); + event_set(&bufev->ev_read, fd, EV_READ, buffertls_handshakecb, buftls); + event_set(&bufev->ev_write, fd, EV_WRITE, buffertls_handshakecb, + buftls); + bufferevent_add(&bufev->ev_read, bufev->timeout_read); +} + void buffertls_connect(struct buffertls *buftls, int fd) { diff --git a/usr.sbin/syslogd/evbuffer_tls.h b/usr.sbin/syslogd/evbuffer_tls.h index 3ce8083b042..fca3c211f6c 100644 --- a/usr.sbin/syslogd/evbuffer_tls.h +++ b/usr.sbin/syslogd/evbuffer_tls.h @@ -1,4 +1,4 @@ -/* $OpenBSD: evbuffer_tls.h,v 1.4 2015/09/10 18:32:06 bluhm Exp $ */ +/* $OpenBSD: evbuffer_tls.h,v 1.5 2015/10/09 16:58:25 bluhm Exp $ */ /* * Copyright (c) 2014-2015 Alexander Bluhm @@ -31,6 +31,7 @@ struct buffertls { void buffertls_set(struct buffertls *, struct bufferevent *, struct tls *, int); +void buffertls_accept(struct buffertls *, int); void buffertls_connect(struct buffertls *, int); #endif /* _EVBUFFER_TLS_H_ */ diff --git a/usr.sbin/syslogd/privsep.c b/usr.sbin/syslogd/privsep.c index 4af8794f63b..e89139b8dfe 100644 --- a/usr.sbin/syslogd/privsep.c +++ b/usr.sbin/syslogd/privsep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: privsep.c,v 1.54 2015/07/07 17:53:04 bluhm Exp $ */ +/* $OpenBSD: privsep.c,v 1.55 2015/10/09 16:58:25 bluhm Exp $ */ /* * Copyright (c) 2003 Anil Madhavapeddy @@ -184,6 +184,8 @@ priv_init(char *conf, int numeric, int lockfd, int nullfd, char *argv[]) close(fd_bind); if (fd_listen != -1) close(fd_listen); + if (fd_tls != -1) + close(fd_tls); for (i = 0; i < nunix; i++) if (fd_unix[i] != -1) close(fd_unix[i]); diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8 index d6a5c933b18..76bcc8a08de 100644 --- a/usr.sbin/syslogd/syslogd.8 +++ b/usr.sbin/syslogd/syslogd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: syslogd.8,v 1.38 2015/07/07 21:43:35 bluhm Exp $ +.\" $OpenBSD: syslogd.8,v 1.39 2015/10/09 16:58:25 bluhm Exp $ .\" .\" Copyright (c) 1983, 1986, 1991, 1993 .\" The Regents of the University of California. All rights reserved. @@ -30,7 +30,7 @@ .\" from: @(#)syslogd.8 8.1 (Berkeley) 6/6/93 .\" $NetBSD: syslogd.8,v 1.3 1996/01/02 17:41:48 perry Exp $ .\" -.Dd $Mdocdate: July 7 2015 $ +.Dd $Mdocdate: October 9 2015 $ .Dt SYSLOGD 8 .Os .Sh NAME @@ -45,6 +45,7 @@ .Op Fl f Ar config_file .Op Fl m Ar mark_interval .Op Fl p Ar log_socket +.Op Fl S Ar listen_address .Op Fl s Ar reporting_socket .Op Fl T Ar listen_address .Op Fl U Ar bind_address @@ -108,6 +109,25 @@ the symbolic local host name. Specify the pathname of an alternate log socket to be used instead; the default is .Pa /dev/log . +.It Fl S Ar listen_address +Create a TLS listen socket for receiving encrypted messages and +bind it to the specified address. +A port number may be specified using the +.Ar host:port +syntax. +The syslog server will attempt to look up a private key in +.Pa /etc/ssl/private/host:port.key +and a public certificate in +.Pa /etc/ssl/host:port.crt , +where +.Ar host +is the specified host name or IP address and +.Ar port +is the specified port if given on the command line. +If these files are not present, syslogd will continue to look in +.Pa /etc/ssl/private/host.key +and +.Pa /etc/ssl/host.crt . .It Fl s Ar reporting_socket Specify path to an .Dv AF_LOCAL diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index f821538aa61..5e4bda64934 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: syslogd.c,v 1.193 2015/10/09 16:44:55 bluhm Exp $ */ +/* $OpenBSD: syslogd.c,v 1.194 2015/10/09 16:58:25 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 over TCP and TLS by Alexander Bluhm + * IPv6, libevent, syslog over TCP and TLS by Alexander Bluhm */ #define MAXLINE 8192 /* maximum line length */ @@ -219,9 +219,13 @@ char *bind_host = NULL; /* bind UDP receive socket */ char *bind_port = NULL; char *listen_host = NULL; /* listen on TCP receive socket */ char *listen_port = NULL; +char *tls_hostport = NULL; /* listen on TLS receive socket */ +char *tls_host = NULL; +char *tls_port = NULL; char *path_ctlsock = NULL; /* Path to control socket */ -struct tls_config *tlsconfig = NULL; +struct tls *server_ctx; +struct tls_config *client_config, *server_config; const char *CAfile = "/etc/ssl/cert.pem"; /* file containing CA certificates */ int NoVerify = 0; /* do not verify TLS server x509 certificate */ int tcpbuf_dropped = 0; /* count messages dropped from TCP or TLS */ @@ -273,12 +277,14 @@ size_t ctl_reply_offset = 0; /* Number of bytes of reply written so far */ char *linebuf; int linesize; -int fd_ctlsock, fd_ctlconn, fd_klog, fd_sendsys, - fd_udp, fd_udp6, fd_bind, fd_listen, fd_unix[MAXUNIX]; +int fd_ctlsock, fd_ctlconn, fd_klog, fd_sendsys, fd_udp, fd_udp6, + fd_bind, fd_listen, fd_tls, fd_unix[MAXUNIX]; struct event *ev_ctlaccept, *ev_ctlread, *ev_ctlwrite; struct peer { + struct buffertls p_buftls; struct bufferevent *p_bufev; + struct tls *p_ctx; char *p_peername; char *p_hostname; int p_fd; @@ -341,15 +347,15 @@ int main(int argc, char *argv[]) { struct timeval to; - struct event *ev_klog, *ev_sendsys, - *ev_udp, *ev_udp6, *ev_bind, *ev_listen, *ev_unix, + struct event *ev_klog, *ev_sendsys, *ev_udp, *ev_udp6, + *ev_bind, *ev_listen, *ev_tls, *ev_unix, *ev_hup, *ev_int, *ev_quit, *ev_term, *ev_mark; const char *errstr; char *p; int ch, i; int lockpipe[2] = { -1, -1}, pair[2], nullfd, fd; - while ((ch = getopt(argc, argv, "46a:C:dFf:hm:np:s:T:U:uV")) != -1) + while ((ch = getopt(argc, argv, "46a:C:dFf:hm:np:S:s:T:U:uV")) != -1) switch (ch) { case '4': /* disable IPv6 */ Family = PF_INET; @@ -389,6 +395,13 @@ main(int argc, char *argv[]) case 'p': /* path */ path_unix[0] = optarg; break; + case 'S': /* allow tls and listen on address */ + tls_hostport = optarg; + if ((p = strdup(optarg)) == NULL) + err(1, "strdup tls address"); + if (loghost_parse(p, NULL, &tls_host, &tls_port) == -1) + errx(1, "bad tls address: %s", optarg); + break; case 's': path_ctlsock = optarg; break; @@ -470,6 +483,13 @@ main(int argc, char *argv[]) if (!Debug) die(0); } + fd_tls = -1; + if (tls_host && socket_bind("tls", tls_host, tls_port, 0, + &fd_tls, &fd_tls) == -1) { + logerrorx("socket listen tls"); + if (!Debug) + die(0); + } #ifndef SUN_LEN #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) @@ -518,39 +538,133 @@ main(int argc, char *argv[]) if (tls_init() == -1) { logerrorx("tls_init"); - } else if ((tlsconfig = tls_config_new()) == NULL) { - logerror("tls_config_new"); - } else if (NoVerify) { - tls_config_insecure_noverifycert(tlsconfig); - tls_config_insecure_noverifyname(tlsconfig); } else { + if ((client_config = tls_config_new()) == NULL) + logerror("tls_config_new client"); + if (tls_hostport) { + if ((server_config = tls_config_new()) == NULL) + logerror("tls_config_new server"); + if ((server_ctx = tls_server()) == NULL) { + logerror("tls_server"); + close(fd_tls); + fd_tls = -1; + } + } + } + if (client_config) { + if (NoVerify) { + tls_config_insecure_noverifycert(client_config); + tls_config_insecure_noverifyname(client_config); + } else { + struct stat sb; + + fd = -1; + p = NULL; + if ((fd = open(CAfile, O_RDONLY)) == -1) { + logerror("open CAfile"); + } else if (fstat(fd, &sb) == -1) { + logerror("fstat CAfile"); + } else if (sb.st_size > 50*1024*1024) { + logerrorx("CAfile larger than 50MB"); + } else if ((p = calloc(sb.st_size, 1)) == NULL) { + logerror("calloc CAfile"); + } else if (read(fd, p, sb.st_size) != sb.st_size) { + logerror("read CAfile"); + } else if (tls_config_set_ca_mem(client_config, p, + sb.st_size) == -1) { + logerrorx("tls_config_set_ca_mem"); + } else { + dprintf("CAfile %s, size %lld\n", + CAfile, sb.st_size); + } + free(p); + close(fd); + } + tls_config_set_protocols(client_config, TLS_PROTOCOLS_ALL); + if (tls_config_set_ciphers(client_config, "compat") != 0) + logerror("tls set client ciphers"); + } + if (server_config && server_ctx) { struct stat sb; + char *path; fd = -1; p = NULL; - if ((fd = open(CAfile, O_RDONLY)) == -1) { - logerror("open CAfile"); + path = NULL; + if (asprintf(&path, "/etc/ssl/private/%s.key", tls_hostport) + == -1 || (fd = open(path, O_RDONLY)) == -1) { + free(path); + path = NULL; + if (asprintf(&path, "/etc/ssl/private/%s.key", tls_host) + == -1 || (fd = open(path, O_RDONLY)) == -1) { + free(path); + path = NULL; + } + } + if (fd == -1) { + logerror("open keyfile"); } else if (fstat(fd, &sb) == -1) { - logerror("fstat CAfile"); - } else if (sb.st_size > 50*1024*1024) { - logerrorx("CAfile larger than 50MB"); + logerror("fstat keyfile"); + } else if (sb.st_size > 50*1024) { + logerrorx("keyfile larger than 50KB"); } else if ((p = calloc(sb.st_size, 1)) == NULL) { - logerror("calloc CAfile"); + logerror("calloc keyfile"); } else if (read(fd, p, sb.st_size) != sb.st_size) { - logerror("read CAfile"); - } else if (tls_config_set_ca_mem(tlsconfig, p, sb.st_size) - == -1) { - logerrorx("tls_config_set_ca_mem"); + logerror("read keyfile"); + } else if (tls_config_set_key_mem(server_config, p, + sb.st_size) == -1) { + logerrorx("tls_config_set_key_mem"); } else { - dprintf("CAfile %s, size %lld\n", CAfile, sb.st_size); + dprintf("Keyfile %s, size %lld\n", path, sb.st_size); } free(p); close(fd); - } - if (tlsconfig) { - tls_config_set_protocols(tlsconfig, TLS_PROTOCOLS_ALL); - if (tls_config_set_ciphers(tlsconfig, "compat") != 0) - logerror("tls set ciphers"); + free(path); + + fd = -1; + p = NULL; + path = NULL; + if (asprintf(&path, "/etc/ssl/%s.crt", tls_hostport) + == -1 || (fd = open(path, O_RDONLY)) == -1) { + free(path); + path = NULL; + if (asprintf(&path, "/etc/ssl/%s.crt", tls_host) + == -1 || (fd = open(path, O_RDONLY)) == -1) { + free(path); + path = NULL; + } + } + if (fd == -1) { + logerror("open certfile"); + } else if (fstat(fd, &sb) == -1) { + logerror("fstat certfile"); + } else if (sb.st_size > 50*1024) { + logerrorx("certfile larger than 50KB"); + } else if ((p = calloc(sb.st_size, 1)) == NULL) { + logerror("calloc certfile"); + } else if (read(fd, p, sb.st_size) != sb.st_size) { + logerror("read certfile"); + } else if (tls_config_set_cert_mem(server_config, p, + sb.st_size) == -1) { + logerrorx("tls_config_set_cert_mem"); + } else { + dprintf("Certfile %s, size %lld\n", + path, sb.st_size); + } + free(p); + close(fd); + free(path); + + tls_config_set_protocols(server_config, TLS_PROTOCOLS_ALL); + if (tls_config_set_ciphers(server_config, "compat") != 0) + logerror("tls set server ciphers"); + if (tls_configure(server_ctx, server_config) != 0) { + logerrorx("tls_configure server"); + tls_free(server_ctx); + server_ctx = NULL; + close(fd_tls); + fd_tls = -1; + } } dprintf("off & running....\n"); @@ -608,6 +722,7 @@ main(int argc, char *argv[]) (ev_udp6 = malloc(sizeof(struct event))) == NULL || (ev_bind = malloc(sizeof(struct event))) == NULL || (ev_listen = malloc(sizeof(struct event))) == NULL || + (ev_tls = malloc(sizeof(struct event))) == NULL || (ev_unix = reallocarray(NULL,nunix,sizeof(struct event))) == NULL || (ev_hup = malloc(sizeof(struct event))) == NULL || (ev_int = malloc(sizeof(struct event))) == NULL || @@ -630,6 +745,7 @@ main(int argc, char *argv[]) event_set(ev_bind, fd_bind, EV_READ|EV_PERSIST, udp_readcb, ev_bind); event_set(ev_listen, fd_listen, EV_READ|EV_PERSIST, tcp_acceptcb, ev_listen); + event_set(ev_tls, fd_tls, EV_READ|EV_PERSIST, tcp_acceptcb, ev_tls); for (i = 0; i < nunix; i++) event_set(&ev_unix[i], fd_unix[i], EV_READ|EV_PERSIST, unix_readcb, &ev_unix[i]); @@ -684,6 +800,8 @@ main(int argc, char *argv[]) event_add(ev_bind, NULL); if (fd_listen != -1) event_add(ev_listen, NULL); + if (fd_tls != -1) + event_add(ev_tls, NULL); for (i = 0; i < nunix; i++) if (fd_unix[i] != -1) event_add(&ev_unix[i], NULL); @@ -725,7 +843,7 @@ socket_bind(const char *proto, const char *host, const char *port, if (proto == NULL) proto = "udp"; if (port == NULL) - port = "syslog"; + port = strcmp(proto, "tls") == 0 ? "syslog-tls" : "syslog"; memset(&hints, 0, sizeof(hints)); hints.ai_family = Family; @@ -915,7 +1033,7 @@ reserve_accept4(int lfd, int event, struct event *ev, } void -tcp_acceptcb(int fd, short event, void *arg) +tcp_acceptcb(int lfd, short event, void *arg) { struct event *ev = arg; struct peer *p; @@ -923,9 +1041,10 @@ tcp_acceptcb(int fd, short event, void *arg) socklen_t sslen; char hostname[NI_MAXHOST], servname[NI_MAXSERV]; char *peername, ebuf[ERRBUFSIZE]; + int fd; sslen = sizeof(ss); - if ((fd = reserve_accept4(fd, event, ev, tcp_acceptcb, + if ((fd = reserve_accept4(lfd, event, ev, tcp_acceptcb, (struct sockaddr *)&ss, &sslen, SOCK_NONBLOCK)) == -1) { if (errno != ENFILE && errno != EMFILE && errno != EINTR && errno != EWOULDBLOCK && @@ -959,6 +1078,21 @@ tcp_acceptcb(int fd, short event, void *arg) close(fd); return; } + p->p_ctx = NULL; + if (lfd == fd_tls) { + if (tls_accept_socket(server_ctx, &p->p_ctx, fd) < 0) { + snprintf(ebuf, sizeof(ebuf), "tls_accept_socket \"%s\"", + peername); + logerrorctx(ebuf, server_ctx); + bufferevent_free(p->p_bufev); + free(p); + close(fd); + return; + } + buffertls_set(&p->p_buftls, p->p_bufev, p->p_ctx, fd); + buffertls_accept(&p->p_buftls, fd); + dprintf("tcp accept callback: tls context success\n"); + } if (!NoDNS && peername != hostname_unknown && priv_getnameinfo((struct sockaddr *)&ss, ss.ss_len, hostname, sizeof(hostname)) != 0) { @@ -972,8 +1106,8 @@ tcp_acceptcb(int fd, short event, void *arg) p->p_peername = peername; bufferevent_enable(p->p_bufev, EV_READ); - snprintf(ebuf, sizeof(ebuf), "syslogd: tcp logger \"%s\" accepted", - peername); + snprintf(ebuf, sizeof(ebuf), "syslogd: %s logger \"%s\" accepted", + p->p_ctx ? "tls" : "tcp", peername); logmsg(LOG_SYSLOG|LOG_INFO, ebuf, LocalHostName, ADDDATE); } @@ -1062,7 +1196,8 @@ tcp_readcb(struct bufferevent *bufev, void *arg) int len; while (EVBUFFER_LENGTH(bufev->input) > 0) { - dprintf("tcp logger \"%s\"", p->p_peername); + dprintf("%s logger \"%s\"", p->p_ctx ? "tls" : "tcp", + p->p_peername); msg = NULL; len = octet_counting(bufev->input, &msg, 1); if (len < 0) @@ -1100,12 +1235,15 @@ tcp_closecb(struct bufferevent *bufev, short event, void *arg) char ebuf[ERRBUFSIZE]; if (event & EVBUFFER_EOF) { - snprintf(ebuf, sizeof(ebuf), "syslogd: tcp logger \"%s\" " - "connection close", p->p_peername); + snprintf(ebuf, sizeof(ebuf), "syslogd: %s logger \"%s\" " + "connection close", p->p_ctx ? "tls" : "tcp", + p->p_peername); logmsg(LOG_SYSLOG|LOG_INFO, ebuf, LocalHostName, ADDDATE); } else { - snprintf(ebuf, sizeof(ebuf), "syslogd: tcp logger \"%s\" " - "connection error: %s", p->p_peername, strerror(errno)); + snprintf(ebuf, sizeof(ebuf), "syslogd: %s logger \"%s\" " + "connection error: %s", p->p_ctx ? "tls" : "tcp", + p->p_peername, + p->p_ctx ? tls_error(p->p_ctx) : strerror(errno)); logmsg(LOG_SYSLOG|LOG_NOTICE, ebuf, LocalHostName, ADDDATE); } @@ -1266,8 +1404,8 @@ tcp_connectcb(int fd, short event, void *arg) logerror(ebuf); goto error; } - if (tlsconfig && - tls_configure(f->f_un.f_forw.f_ctx, tlsconfig) == -1) { + if (client_config && + tls_configure(f->f_un.f_forw.f_ctx, client_config) == -1) { snprintf(ebuf, sizeof(ebuf), "tls_configure \"%s\"", f->f_un.f_forw.f_loghost); logerrorctx(ebuf, f->f_un.f_forw.f_ctx); @@ -1341,8 +1479,8 @@ usage(void) (void)fprintf(stderr, "usage: syslogd [-46dFhnuV] [-a path] [-C CAfile] [-f config_file]\n" - " [-m mark_interval] [-p log_socket] [-s reporting_socket]\n" - " [-T listen_address] [-U bind_address]\n"); + " [-m mark_interval] [-p log_socket] [-S listen_address]\n" + " [-s reporting_socket] [-T listen_address] [-U bind_address]\n"); exit(1); } diff --git a/usr.sbin/syslogd/syslogd.h b/usr.sbin/syslogd/syslogd.h index 28fd815acc0..37bcaedf885 100644 --- a/usr.sbin/syslogd/syslogd.h +++ b/usr.sbin/syslogd/syslogd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syslogd.h,v 1.19 2015/07/07 17:53:04 bluhm Exp $ */ +/* $OpenBSD: syslogd.h,v 1.20 2015/10/09 16:58:25 bluhm Exp $ */ /* * Copyright (c) 2003 Anil Madhavapeddy @@ -44,7 +44,7 @@ extern int nunix; extern char *path_unix[MAXUNIX]; extern char *path_ctlsock; extern int fd_ctlsock, fd_ctlconn, fd_klog, fd_sendsys; -extern int fd_udp, fd_udp6, fd_bind, fd_listen, fd_unix[MAXUNIX]; +extern int fd_udp, fd_udp6, fd_bind, fd_listen, fd_tls, fd_unix[MAXUNIX]; #define dprintf(_f...) do { if (Debug) printf(_f); } while (0) extern int Debug; -- 2.20.1