diff options
author | Can Erkin Acar <canacar@cvs.openbsd.org> | 2003-10-22 18:51:56 +0000 |
---|---|---|
committer | Can Erkin Acar <canacar@cvs.openbsd.org> | 2003-10-22 18:51:56 +0000 |
commit | 6e363212d16d26425498972bb2e01ed7035bb463 (patch) | |
tree | 1c2ab9e5b252d08ee7b6a0214c70019bb4b75e72 /sbin | |
parent | 6693f734d4d99c32579a9cef43ff0f9a39e4dc62 (diff) |
privilege seperated pflogd
_pflogd user and group must be created for proper operation.
ok frantzen@ henning@ mcbride@ deraadt@
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/pflogd/Makefile | 4 | ||||
-rw-r--r-- | sbin/pflogd/pflogd.c | 116 | ||||
-rw-r--r-- | sbin/pflogd/pflogd.h | 41 | ||||
-rw-r--r-- | sbin/pflogd/privsep.c | 268 | ||||
-rw-r--r-- | sbin/pflogd/privsep_fdpass.c | 120 |
5 files changed, 492 insertions, 57 deletions
diff --git a/sbin/pflogd/Makefile b/sbin/pflogd/Makefile index 79902152951..31c795b00c3 100644 --- a/sbin/pflogd/Makefile +++ b/sbin/pflogd/Makefile @@ -1,11 +1,11 @@ -# $OpenBSD: Makefile,v 1.4 2003/03/01 06:11:20 cloder Exp $ +# $OpenBSD: Makefile,v 1.5 2003/10/22 18:51:55 canacar Exp $ CFLAGS+=-Wall -Werror -Wmissing-prototypes -Wshadow LDADD+= -lpcap -lutil DPAPP+= ${LIBPCAP} ${LIBUTIL} PROG= pflogd -SRCS= pflogd.c +SRCS= pflogd.c privsep.c privsep_fdpass.c MAN= pflogd.8 .include <bsd.prog.mk> diff --git a/sbin/pflogd/pflogd.c b/sbin/pflogd/pflogd.c index fea633e7ce6..a73ed438692 100644 --- a/sbin/pflogd/pflogd.c +++ b/sbin/pflogd/pflogd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pflogd.c,v 1.22 2003/09/26 16:14:33 deraadt Exp $ */ +/* $OpenBSD: pflogd.c,v 1.23 2003/10/22 18:51:55 canacar Exp $ */ /* * Copyright (c) 2001 Theo de Raadt @@ -31,6 +31,7 @@ */ #include <sys/types.h> +#include <sys/ioctl.h> #include <sys/file.h> #include <sys/stat.h> #include <stdio.h> @@ -45,15 +46,7 @@ #include <stdarg.h> #include <fcntl.h> #include <util.h> - -#define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */ -#define PCAP_TO_MS 500 /* pcap read timeout (ms) */ -#define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */ -#define PCAP_OPT_FIL 0 /* filter optimization */ -#define FLUSH_DELAY 60 /* flush delay */ - -#define PFLOGD_LOG_FILE "/var/log/pflog" -#define PFLOGD_DEFAULT_IF "pflog0" +#include "pflogd.h" pcap_t *hpcap; pcap_dumper_t *dpcap; @@ -148,37 +141,47 @@ sig_alrm(int sig) gotsig_alrm = 1; } -int -init_pcap(void) +void +set_pcap_filter(void) { struct bpf_program bprog; - pcap_t *oldhpcap = hpcap; + if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) + logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); + else { + if (pcap_setfilter(hpcap, &bprog) < 0) + logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); + pcap_freecode(&bprog); + } +} + +int +init_pcap(void) +{ hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); if (hpcap == NULL) { logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); - hpcap = oldhpcap; + hpcap = NULL; return (-1); } - if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) - logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); - else if (pcap_setfilter(hpcap, &bprog) < 0) - logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); - if (filter != NULL) - free(filter); - if (pcap_datalink(hpcap) != DLT_PFLOG) { logmsg(LOG_ERR, "Invalid datalink type"); pcap_close(hpcap); - hpcap = oldhpcap; + hpcap = NULL; return (-1); } - if (oldhpcap) - pcap_close(oldhpcap); + set_pcap_filter(); snaplen = pcap_snapshot(hpcap); + + /* lock */ + if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) { + logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); + return (-1); + } + return (0); } @@ -187,7 +190,7 @@ reset_dump(void) { struct pcap_file_header hdr; struct stat st; - int tmpsnap; + int fd; FILE *fp; if (hpcap == NULL) @@ -201,17 +204,18 @@ reset_dump(void) * Basically reimplement pcap_dump_open() because it truncates * files and duplicates headers and such. */ - fp = fopen(filename, "a+"); + fd = priv_open_log(); + if (fd < 0) + return (1); + + fp = fdopen(fd, "a+"); + if (fp == NULL) { - snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", - filename, pcap_strerror(errno)); - logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); + logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); return (1); } if (fstat(fileno(fp), &st) == -1) { - snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", - filename, pcap_strerror(errno)); - logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); + logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); return (1); } @@ -222,10 +226,9 @@ reset_dump(void) if (st.st_size == 0) { if (snaplen != pcap_snapshot(hpcap)) { logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); - if (init_pcap()) { - logmsg(LOG_ERR, "Failed to initialize"); - if (hpcap == NULL) return (-1); - logmsg(LOG_NOTICE, "Using old settings"); + if (priv_set_snaplen(snaplen)) { + logmsg(LOG_WARNING, + "Failed, using old settings"); } } hdr.magic = TCPDUMP_MAGIC; @@ -259,22 +262,16 @@ reset_dump(void) "Invalid/incompatible log file, move it away"); fclose(fp); return (1); - } + } if (hdr.snaplen != snaplen) { logmsg(LOG_WARNING, - "Existing file specifies a snaplen of %u, using it", + "Existing file has different snaplen %u, using it", hdr.snaplen); - tmpsnap = snaplen; - snaplen = hdr.snaplen; - if (init_pcap()) { - logmsg(LOG_ERR, "Failed to re-initialize"); - if (hpcap == 0) - return (-1); - logmsg(LOG_NOTICE, - "Using old settings, offset: %llu", - (unsigned long long)st.st_size); + if (priv_set_snaplen(hdr.snaplen)) { + logmsg(LOG_WARNING, + "Failed, using old settings, offset %llu", + (unsigned long long) st.st_size); } - snaplen = tmpsnap; } } @@ -327,24 +324,33 @@ main(int argc, char **argv) (void)umask(S_IRWXG | S_IRWXO); - signal(SIGTERM, sig_close); - signal(SIGINT, sig_close); - signal(SIGQUIT, sig_close); - signal(SIGALRM, sig_alrm); - signal(SIGHUP, sig_hup); - alarm(delay); - + /* filter will be used by the privileged process */ if (argc) { filter = copy_argv(argv); if (filter == NULL) logmsg(LOG_NOTICE, "Failed to form filter expression"); } + /* initialize pcap before dropping privileges */ if (init_pcap()) { logmsg(LOG_ERR, "Exiting, init failure"); exit(1); } + /* Privilege separation begins here */ + if (priv_init()) { + logmsg(LOG_ERR, "unable to privsep"); + exit(1); + } + + /* Process is now unprivileged and inside a chroot */ + signal(SIGTERM, sig_close); + signal(SIGINT, sig_close); + signal(SIGQUIT, sig_close); + signal(SIGALRM, sig_alrm); + signal(SIGHUP, sig_hup); + alarm(delay); + if (reset_dump()) { logmsg(LOG_ERR, "Failed to open log file %s", filename); pcap_close(hpcap); diff --git a/sbin/pflogd/pflogd.h b/sbin/pflogd/pflogd.h new file mode 100644 index 00000000000..765adb9a791 --- /dev/null +++ b/sbin/pflogd/pflogd.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2003 Can Erkin Acar + * + * 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. + */ + +#include <pcap.h> + +#define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */ +#define PCAP_TO_MS 500 /* pcap read timeout (ms) */ +#define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */ +#define PCAP_OPT_FIL 0 /* filter optimization */ +#define FLUSH_DELAY 60 /* flush delay */ + +#define PFLOGD_LOG_FILE "/var/log/pflog" +#define PFLOGD_DEFAULT_IF "pflog0" + +void logmsg(int priority, const char *message, ...); + +/* Privilege separation */ +int priv_init(void); +int priv_set_snaplen(int snaplen); +int priv_open_log(void); +pcap_t *pcap_open_live_fd(int fd, int snaplen, char *ebuf); + +void set_pcap_filter(void); +/* File descriptor send/recv */ +void send_fd(int, int); +int receive_fd(int); + +extern int Debug; diff --git a/sbin/pflogd/privsep.c b/sbin/pflogd/privsep.c new file mode 100644 index 00000000000..5b72a705ffe --- /dev/null +++ b/sbin/pflogd/privsep.c @@ -0,0 +1,268 @@ +/* $OpenBSD: privsep.c,v 1.1 2003/10/22 18:51:55 canacar Exp $ */ + +/* + * Copyright (c) 2003 Can Erkin Acar + * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> + * + * 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. + */ +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/ioctl.h> + +#include <net/if.h> +#include <net/bpf.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <pcap.h> +#include <pcap-int.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include "pflogd.h" + +enum cmd_types { + PRIV_SET_SNAPLEN, /* set the snaplength */ + PRIV_OPEN_LOG, /* open logfile for appending */ +}; + +static int priv_fd = -1; +static pid_t child_pid; + +volatile sig_atomic_t gotsig_chld = 0; + +static void sig_pass_to_chld(int); +static void sig_chld(int); +static void must_read(int, void *, size_t); +static void must_write(int, void *, size_t); +static int set_snaplen(int snap); + +/* bpf filter expression common to parent and child */ +extern char *filter; +extern char *errbuf; +extern char *filename; +extern pcap_t *hpcap; + +/* based on syslogd privsep */ +int +priv_init(void) +{ + int i, fd, socks[2], cmd; + int snaplen, ret; + struct passwd *pw; + + /* Create sockets */ + if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) + err(1, "socketpair() failed"); + + pw = getpwnam("_pflogd"); + if (pw == NULL) + errx(1, "unknown user _pflogd"); + + child_pid = fork(); + if (child_pid < 0) + err(1, "fork() failed"); + + if (!child_pid) { + /* Child - drop privileges and return */ + if (chroot(pw->pw_dir) != 0) + err(1, "unable to chroot"); + chdir("/"); + if (setegid(pw->pw_gid) == -1) + err(1, "setegid() failed"); + if (setgid(pw->pw_gid) == -1) + err(1, "setgid() failed"); + if (seteuid(pw->pw_uid) == -1) + err(1, "seteuid() failed"); + if (setuid(pw->pw_uid) == -1) + err(1, "setuid() failed"); + close(socks[0]); + priv_fd = socks[1]; + return 0; + } + + /* Father */ + for (i = 1; i <= _NSIG; i++) + signal(i, SIG_DFL); + + /* Pass ALRM/TERM/HUP through to child, and accept CHLD */ + signal(SIGALRM, sig_pass_to_chld); + signal(SIGTERM, sig_pass_to_chld); + signal(SIGHUP, sig_pass_to_chld); + signal(SIGCHLD, sig_chld); + + setproctitle("[priv]"); + close(socks[1]); + + while (!gotsig_chld) { + must_read(socks[0], &cmd, sizeof(int)); + switch (cmd) { + case PRIV_SET_SNAPLEN: + logmsg(LOG_DEBUG, + "[priv]: msg PRIV_SET_SNAPLENGTH received"); + must_read(socks[0], &snaplen, sizeof(int)); + + ret = set_snaplen(snaplen); + if (ret) { + logmsg(LOG_NOTICE, + "[priv]: set_snaplen failed for snaplen %d", + snaplen); + } + + must_write(socks[0], &ret, sizeof(int)); + break; + + case PRIV_OPEN_LOG: + logmsg(LOG_DEBUG, + "[priv]: msg PRIV_OPEN_LOG received"); + + /* XXX */ + fd = open(filename, O_RDWR|O_APPEND|O_NONBLOCK, 0); + if (fd < 0) + logmsg(LOG_NOTICE, + "[priv]: failed to open %s: %s", + filename, strerror(errno)); + send_fd(socks[0], fd); + close(fd); + break; + + default: + logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); + _exit(1); + /* NOTREACHED */ + } + } + + _exit(1); +} + +/* this is called from parent */ +static int +set_snaplen(int snap) +{ + if (hpcap == NULL) + return (1); + + hpcap->snapshot = snap; + set_pcap_filter(); + + return 0; +} + + +/* + * send the snaplength to privileged process + */ +int +priv_set_snaplen(int snaplen) +{ + int cmd, ret; + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion", __func__); + + cmd = PRIV_SET_SNAPLEN; + + must_write(priv_fd, &cmd, sizeof(int)); + must_write(priv_fd, &snaplen, sizeof(int)); + + must_read(priv_fd, &ret, sizeof(int)); + + /* also set hpcap->snapshot in child */ + if (ret == 0) + hpcap->snapshot = snaplen; + + return (ret); +} + +/* Open log-file */ +int +priv_open_log(void) +{ + int cmd, fd; + + if (priv_fd < 0) + errx(1, "%s: called from privileged portion\n", __func__); + + cmd = PRIV_OPEN_LOG; + must_write(priv_fd, &cmd, sizeof(int)); + fd = receive_fd(priv_fd); + + return (fd); +} + +/* If priv parent gets a TERM or HUP, pass it through to child instead */ +static void +sig_pass_to_chld(int sig) +{ + kill(child_pid, sig); +} + +/* if parent gets a SIGCHLD, it will exit */ +static void +sig_chld(int sig) +{ + gotsig_chld = 1; +} + +/* Read data with the assertion that it all must come through, or + * else abort the process. Based on atomicio() from openssh. */ +static void +must_read(int fd, void *buf, size_t n) +{ + char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = read(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + _exit(0); + default: + pos += res; + } + } +} + +/* Write data with the assertion that it all has to be written, or + * else abort the process. Based on atomicio() from openssh. */ +static void +must_write(int fd, void *buf, size_t n) +{ + char *s = buf; + ssize_t res, pos = 0; + + while (n > pos) { + res = write(fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + case 0: + _exit(0); + default: + pos += res; + } + } +} diff --git a/sbin/pflogd/privsep_fdpass.c b/sbin/pflogd/privsep_fdpass.c new file mode 100644 index 00000000000..166b6930b3b --- /dev/null +++ b/sbin/pflogd/privsep_fdpass.c @@ -0,0 +1,120 @@ +/* $OpenBSD: privsep_fdpass.c,v 1.1 2003/10/22 18:51:55 canacar Exp $ */ + +/* + * Copyright 2001 Niels Provos <provos@citi.umich.edu> + * All rights reserved. + * + * Copyright (c) 2002 Matthieu Herrb + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 + * COPYRIGHT HOLDERS OR CONTRIBUTORS 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 <sys/param.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "pflogd.h" + +void +send_fd(int sock, int fd) +{ + struct msghdr msg; + char tmp[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct iovec vec; + int result = 0; + ssize_t n; + + memset(&msg, 0, sizeof(msg)); + + if (fd >= 0) { + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof(int)); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = fd; + } else { + result = errno; + } + + vec.iov_base = &result; + vec.iov_len = sizeof(int); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + + if ((n = sendmsg(sock, &msg, 0)) == -1) + warn("%s: sendmsg(%d)", __func__, sock); + if (n != sizeof(int)) + warnx("%s: sendmsg: expected sent 1 got %ld", + __func__, (long)n); +} + +int +receive_fd(int sock) +{ + struct msghdr msg; + char tmp[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct iovec vec; + ssize_t n; + int result; + int fd; + + memset(&msg, 0, sizeof(msg)); + vec.iov_base = &result; + vec.iov_len = sizeof(int); + msg.msg_iov = &vec; + msg.msg_iovlen = 1; + msg.msg_control = tmp; + msg.msg_controllen = sizeof(tmp); + + if ((n = recvmsg(sock, &msg, 0)) == -1) + warn("%s: recvmsg", __func__); + if (n != sizeof(int)) + warnx("%s: recvmsg: expected received 1 got %ld", + __func__, (long)n); + if (result == 0) { + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg->cmsg_type != SCM_RIGHTS) + warnx("%s: expected type %d got %d", __func__, + SCM_RIGHTS, cmsg->cmsg_type); + fd = (*(int *)CMSG_DATA(cmsg)); + return fd; + } else { + errno = result; + return -1; + } +} |