summaryrefslogtreecommitdiff
path: root/keyexchange/isakmpd-20041012/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyexchange/isakmpd-20041012/monitor.c')
-rw-r--r--keyexchange/isakmpd-20041012/monitor.c1174
1 files changed, 0 insertions, 1174 deletions
diff --git a/keyexchange/isakmpd-20041012/monitor.c b/keyexchange/isakmpd-20041012/monitor.c
deleted file mode 100644
index bd14005..0000000
--- a/keyexchange/isakmpd-20041012/monitor.c
+++ /dev/null
@@ -1,1174 +0,0 @@
-/* $OpenBSD: monitor.c,v 1.29 2004/08/12 11:21:07 hshoexer Exp $ */
-
-/*
- * Copyright (c) 2003 Håkan Olsson. 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.
- *
- * 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 <sys/param.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#if defined (USE_POLICY)
-#include <regex.h>
-#include <keynote.h>
-#endif
-
-#include "sysdep.h"
-
-#include "conf.h"
-#include "log.h"
-#include "monitor.h"
-#include "policy.h"
-#include "ui.h"
-#include "util.h"
-#include "pf_key_v2.h"
-
-struct monitor_state {
- pid_t pid;
- int s;
- char root[MAXPATHLEN];
-} m_state;
-
-volatile sig_atomic_t sigchlded = 0;
-extern volatile sig_atomic_t sigtermed;
-static volatile sig_atomic_t cur_state = STATE_INIT;
-
-/* Private functions. */
-int m_write_int32(int, int32_t);
-int m_write_raw(int, char *, size_t);
-int m_read_int32(int, int32_t *);
-int m_read_raw(int, char *, size_t);
-void m_flush(int);
-
-static void m_priv_getfd(int);
-static void m_priv_getsocket(int);
-static void m_priv_setsockopt(int);
-static void m_priv_bind(int);
-static int m_priv_local_sanitize_path(char *, size_t, int);
-static int m_priv_check_sockopt(int, int);
-static int m_priv_check_bind(const struct sockaddr *, socklen_t);
-static void m_priv_increase_state(int);
-static void m_priv_test_state(int);
-
-static void m_priv_ui_init(int);
-static void m_priv_pfkey_open(int);
-
-/*
- * Public functions, unprivileged.
- */
-
-/* Setup monitor context, fork, drop child privs. */
-pid_t
-monitor_init(int debug)
-{
- struct passwd *pw;
- int p[2];
-
- memset(&m_state, 0, sizeof m_state);
-
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0)
- log_fatal("monitor_init: socketpair() failed");
-
- pw = getpwnam(ISAKMPD_PRIVSEP_USER);
- if (pw == NULL)
- log_fatal("monitor_init: getpwnam(\"%s\") failed",
- ISAKMPD_PRIVSEP_USER);
-
- m_state.pid = fork();
- m_state.s = p[m_state.pid ? 1 : 0];
- strlcpy(m_state.root, pw->pw_dir, sizeof m_state.root);
-
- LOG_DBG((LOG_SYSDEP, 30, "monitor_init: pid %d my fd %d", m_state.pid,
- m_state.s));
-
- /* The child process should drop privileges now. */
- if (!m_state.pid) {
- if (chroot(pw->pw_dir) != 0 || chdir("/") != 0)
- log_fatal("monitor_init: chroot failed");
-
- if (setgid(pw->pw_gid) != 0)
- log_fatal("monitor_init: setgid(%d) failed",
- pw->pw_gid);
-
- if (setuid(pw->pw_uid) != 0)
- log_fatal("monitor_init: setuid(%d) failed",
- pw->pw_uid);
-
- LOG_DBG((LOG_MISC, 10,
- "monitor_init: privileges dropped for child process"));
- } else {
- setproctitle("monitor [priv]");
- }
-
-
- /* With "-dd", stop and wait here. For gdb "attach" etc. */
- if (debug > 1) {
- log_print("monitor_init: stopped %s PID %d fd %d%s",
- m_state.pid ? "priv" : "child", getpid(), m_state.s,
- m_state.pid ? ", waiting for SIGCONT" : "");
- kill(getpid(), SIGSTOP); /* Wait here for SIGCONT. */
- if (m_state.pid)
- kill(m_state.pid, SIGCONT); /* Continue child. */
- }
-
- return m_state.pid;
-}
-
-void
-monitor_exit(int code)
-{
- if (m_state.pid != 0)
- kill(m_state.pid, SIGKILL);
-
- exit(code);
-}
-
-void
-monitor_ui_init(void)
-{
- int32_t err;
-
- if (m_write_int32(m_state.s, MONITOR_UI_INIT))
- goto errout;
-
- if (m_read_int32(m_state.s, &err))
- goto errout;
-
- if (err != 0) {
- log_fatal("monitor_ui_init: parent could not create FIFO "
- "\"%s\"", ui_fifo);
- exit(1);
- }
-
- ui_socket = mm_receive_fd(m_state.s);
- if (ui_socket < 0)
- log_fatal("monitor_ui_init: parent could not create FIFO "
- "\"%s\"", ui_fifo);
-
- return;
-
-errout:
- log_error("monitor_ui_init: problem talking to privileged process");
- return;
-}
-
-int
-monitor_pf_key_v2_open(void)
-{
- int32_t err;
-
- if (m_write_int32(m_state.s, MONITOR_PFKEY_OPEN))
- goto errout;
-
- if (m_read_int32(m_state.s, &err))
- goto errout;
-
- if (err < 0) {
- log_error("monitor_pf_key_v2_open: parent could not create "
- "PF_KEY socket");
- return -1;
- }
-
- pf_key_v2_socket = mm_receive_fd(m_state.s);
- if (pf_key_v2_socket < 0) {
- log_error("monitor_pf_key_v2_open: mm_receive_fd() failed: %s",
- strerror(errno));
- return -1;
- }
- return pf_key_v2_socket;
-
-errout:
- log_error("monitor_pf_key_v2_open: problem talking to privileged "
- "process");
- return -1;
-}
-
-int
-monitor_open(const char *path, int flags, mode_t mode)
-{
- int fd, mode32 = (int32_t) mode;
- int32_t err;
- char realpath[MAXPATHLEN];
-
- if (path[0] == '/')
- strlcpy(realpath, path, sizeof realpath);
- else
- snprintf(realpath, sizeof realpath, "%s/%s", m_state.root,
- path);
-
- /* Write data to priv process. */
- if (m_write_int32(m_state.s, MONITOR_GET_FD))
- goto errout;
-
- if (m_write_raw(m_state.s, realpath, strlen(realpath) + 1))
- goto errout;
-
- if (m_write_int32(m_state.s, flags))
- goto errout;
-
- if (m_write_int32(m_state.s, mode32))
- goto errout;
-
- if (m_read_int32(m_state.s, &err))
- goto errout;
-
- if (err != 0) {
- errno = (int) err;
- return -1;
- }
- /* Wait for response. */
- fd = mm_receive_fd(m_state.s);
- if (fd < 0) {
- log_error("monitor_open: mm_receive_fd () failed: %s",
- strerror(errno));
- return -1;
- }
- return fd;
-
-errout:
- log_error("monitor_open: problem talking to privileged process");
- return -1;
-}
-
-FILE *
-monitor_fopen(const char *path, const char *mode)
-{
- FILE *fp;
- int fd, flags = 0, saved_errno;
- mode_t mask, cur_umask;
-
- /* Only the child process is supposed to run this. */
- if (m_state.pid)
- log_fatal("[priv] bad call to monitor_fopen");
-
- switch (mode[0]) {
- case 'r':
- flags = (mode[1] == '+' ? O_RDWR : O_RDONLY);
- break;
- case 'w':
- flags = (mode[1] == '+' ? O_RDWR : O_WRONLY) | O_CREAT |
- O_TRUNC;
- break;
- case 'a':
- flags = (mode[1] == '+' ? O_RDWR : O_WRONLY) | O_CREAT |
- O_APPEND;
- break;
- default:
- log_fatal("monitor_fopen: bad call");
- }
-
- cur_umask = umask(0);
- (void)umask(cur_umask);
- mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
- mask &= ~cur_umask;
-
- fd = monitor_open(path, flags, mask);
- if (fd < 0)
- return NULL;
-
- /* Got the fd, attach a FILE * to it. */
- fp = fdopen(fd, mode);
- if (!fp) {
- log_error("monitor_fopen: fdopen() failed");
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- return NULL;
- }
- return fp;
-}
-
-int
-monitor_stat(const char *path, struct stat *sb)
-{
- int fd, r, saved_errno;
-
- /* O_NONBLOCK is needed for stat'ing fifos. */
- fd = monitor_open(path, O_RDONLY | O_NONBLOCK, 0);
- if (fd < 0)
- return -1;
-
- r = fstat(fd, sb);
- saved_errno = errno;
- close(fd);
- errno = saved_errno;
- return r;
-}
-
-int
-monitor_socket(int domain, int type, int protocol)
-{
- int s;
- int32_t err;
-
- if (m_write_int32(m_state.s, MONITOR_GET_SOCKET))
- goto errout;
-
- if (m_write_int32(m_state.s, (int32_t)domain))
- goto errout;
-
- if (m_write_int32(m_state.s, (int32_t)type))
- goto errout;
-
- if (m_write_int32(m_state.s, (int32_t)protocol))
- goto errout;
-
- if (m_read_int32(m_state.s, &err))
- goto errout;
-
- if (err != 0) {
- errno = (int)err;
- return -1;
- }
- /* Read result. */
- s = mm_receive_fd(m_state.s);
- if (s < 0) {
- log_error("monitor_socket: mm_receive_fd () failed: %s",
- strerror(errno));
- return -1;
- }
- return s;
-
-errout:
- log_error("monitor_socket: problem talking to privileged process");
- return -1;
-}
-
-int
-monitor_setsockopt(int s, int level, int optname, const void *optval,
- socklen_t optlen)
-{
- int32_t ret, err;
-
- if (m_write_int32(m_state.s, MONITOR_SETSOCKOPT))
- goto errout;
- if (mm_send_fd(m_state.s, s))
- goto errout;
-
- if (m_write_int32(m_state.s, (int32_t)level))
- goto errout;
- if (m_write_int32(m_state.s, (int32_t)optname))
- goto errout;
- if (m_write_int32(m_state.s, (int32_t)optlen))
- goto errout;
- if (m_write_raw(m_state.s, (char *)optval, (size_t)optlen))
- goto errout;
-
- if (m_read_int32(m_state.s, &err))
- goto errout;
-
- if (err != 0)
- errno = (int)err;
-
- if (m_read_int32(m_state.s, &ret))
- goto errout;
-
- return (int)ret;
-
-errout:
- log_print("monitor_setsockopt: read/write error");
- return -1;
-}
-
-int
-monitor_bind(int s, const struct sockaddr *name, socklen_t namelen)
-{
- int32_t ret, err;
-
- if (m_write_int32(m_state.s, MONITOR_BIND))
- goto errout;
- if (mm_send_fd(m_state.s, s))
- goto errout;
-
- if (m_write_int32(m_state.s, (int32_t)namelen))
- goto errout;
- if (m_write_raw(m_state.s, (char *)name, (size_t)namelen))
- goto errout;
-
- if (m_read_int32(m_state.s, &err))
- goto errout;
-
- if (err != 0)
- errno = (int)err;
-
- if (m_read_int32(m_state.s, &ret))
- goto errout;
-
- return (int)ret;
-
-errout:
- log_print("monitor_bind: read/write error");
- return -1;
-}
-
-struct monitor_dirents *
-monitor_opendir(const char *path)
-{
- char *buf, *cp;
- size_t bufsize;
- int fd, nbytes, entries;
- long base;
- struct stat sb;
- struct dirent *dp;
- struct monitor_dirents *direntries;
-
- fd = monitor_open(path, 0, O_RDONLY);
- if (fd < 0) {
- log_error("monitor_opendir: opendir(\"%s\") failed", path);
- return NULL;
- }
- /* Now build a list with all dirents from fd. */
- if (fstat(fd, &sb) < 0) {
- (void)close(fd);
- return NULL;
- }
- if (!S_ISDIR(sb.st_mode)) {
- (void)close(fd);
- errno = EACCES;
- return NULL;
- }
- bufsize = sb.st_size;
- if (bufsize < sb.st_blksize)
- bufsize = sb.st_blksize;
-
- buf = calloc(bufsize, sizeof(char));
- if (buf == NULL) {
- (void)close(fd);
- errno = EACCES;
- return NULL;
- }
- nbytes = getdirentries(fd, buf, bufsize, &base);
- if (nbytes <= 0) {
- (void)close(fd);
- free(buf);
- errno = EACCES;
- return NULL;
- }
- (void)close(fd);
-
- for (entries = 0, cp = buf; cp < buf + nbytes;) {
- dp = (struct dirent *)cp;
- cp += dp->d_reclen;
- entries++;
- }
-
- direntries = calloc(1, sizeof(struct monitor_dirents));
- if (direntries == NULL) {
- free(buf);
- errno = EACCES;
- return NULL;
- }
- direntries->dirents = calloc(entries + 1, sizeof(struct dirent *));
- if (direntries->dirents == NULL) {
- free(buf);
- free(direntries);
- errno = EACCES;
- return NULL;
- }
- direntries->current = 0;
-
- for (entries = 0, cp = buf; cp < buf + nbytes;) {
- dp = (struct dirent *)cp;
- direntries->dirents[entries++] = dp;
- cp += dp->d_reclen;
- }
- direntries->dirents[entries] = NULL;
-
- return direntries;
-}
-
-struct dirent *
-monitor_readdir(struct monitor_dirents *direntries)
-{
- if (direntries->dirents[direntries->current] != NULL)
- return direntries->dirents[direntries->current++];
-
- return NULL;
-}
-
-int
-monitor_closedir(struct monitor_dirents *direntries)
-{
- free(direntries->dirents);
- free(direntries);
-
- return 0;
-}
-
-void
-monitor_init_done(void)
-{
- if (m_write_int32(m_state.s, MONITOR_INIT_DONE))
- log_print("monitor_init_done: read/write error");
-
- return;
-}
-
-/*
- * Start of code running with privileges (the monitor process).
- */
-
-/* Help functions for monitor_loop(). */
-static void
-monitor_got_sigchld(int sig)
-{
- sigchlded = 1;
-}
-
-static void
-sig_pass_to_chld(int sig)
-{
- int oerrno = errno;
-
- if (m_state.pid != -1)
- kill(m_state.pid, sig);
- errno = oerrno;
-}
-
-/* This function is where the privileged process waits(loops) indefinitely. */
-void
-monitor_loop(int debug)
-{
- pid_t pid;
- fd_set *fds;
- size_t fdsn;
- int status, n, maxfd;
-
- if (!debug)
- log_to(0);
-
- maxfd = m_state.s + 1;
-
- fdsn = howmany(maxfd, NFDBITS) * sizeof(fd_mask);
- fds = (fd_set *)malloc(fdsn);
- if (!fds) {
- kill(m_state.pid, SIGTERM);
- log_fatal("monitor_loop: malloc (%lu) failed",
- (unsigned long)fdsn);
- return;
- }
- /* If the child dies, we should shutdown also. */
- signal(SIGCHLD, monitor_got_sigchld);
-
- /* SIGHUP, SIGUSR1 and SIGUSR2 will be forwarded to child. */
- signal(SIGHUP, sig_pass_to_chld);
- signal(SIGUSR1, sig_pass_to_chld);
- signal(SIGUSR2, sig_pass_to_chld);
-
- while (cur_state < STATE_QUIT) {
- /*
- * Currently, there is no need for us to hang around if the
- * child is in the process of shutting down.
- */
- if (sigtermed) {
- m_priv_increase_state(STATE_QUIT);
- kill(m_state.pid, SIGTERM);
- break;
- }
-
- if (sigchlded) {
- do {
- pid = waitpid(m_state.pid, &status, WNOHANG);
- } while (pid == -1 && errno == EINTR);
-
- if (pid == m_state.pid && (WIFEXITED(status) ||
- WIFSIGNALED(status))) {
- m_priv_increase_state(STATE_QUIT);
- break;
- }
- }
-
- memset(fds, 0, fdsn);
- FD_SET(m_state.s, fds);
-
- n = select(maxfd, fds, NULL, NULL, NULL);
- if (n == -1) {
- if (errno != EINTR) {
- log_error("select");
- sleep(1);
- }
- } else if (n)
- if (FD_ISSET(m_state.s, fds)) {
- int32_t msgcode;
- if (m_read_int32(m_state.s, &msgcode))
- m_flush(m_state.s);
- else
- switch (msgcode) {
- case MONITOR_GET_FD:
- m_priv_getfd(m_state.s);
- break;
-
- case MONITOR_UI_INIT:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_UI_INIT",
- __func__));
- m_priv_test_state(STATE_INIT);
- m_priv_ui_init(m_state.s);
- break;
-
- case MONITOR_PFKEY_OPEN:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_PFKEY_OPEN",
- __func__));
- m_priv_test_state(STATE_INIT);
- m_priv_pfkey_open(m_state.s);
- break;
-
- case MONITOR_GET_SOCKET:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_GET_SOCKET",
- __func__));
- m_priv_test_state(STATE_INIT);
- m_priv_getsocket(m_state.s);
- break;
-
- case MONITOR_SETSOCKOPT:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_SETSOCKOPT",
- __func__));
- m_priv_test_state(STATE_INIT);
- m_priv_setsockopt(m_state.s);
- break;
-
- case MONITOR_BIND:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_BIND",
- __func__));
- m_priv_test_state(STATE_INIT);
- m_priv_bind(m_state.s);
- break;
-
- case MONITOR_INIT_DONE:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_INIT_DONE",
- __func__));
- m_priv_test_state(STATE_INIT);
- m_priv_increase_state(
- STATE_RUNNING);
- break;
-
- case MONITOR_SHUTDOWN:
- LOG_DBG((LOG_MISC, 80,
- "%s: MONITOR_SHUTDOWN",
- __func__));
- m_priv_increase_state(
- STATE_QUIT);
- break;
-
- default:
- log_print("monitor_loop: "
- "got unknown code %d",
- msgcode);
- }
- }
- }
-
- free(fds);
- exit(0);
-}
-
-
-/* Privileged: called by monitor_loop. */
-static void
-m_priv_ui_init(int s)
-{
- int32_t err;
-
- ui_init();
-
- if (ui_socket >= 0)
- err = 0;
- else
- err = -1;
-
- if (m_write_int32(s, err))
- goto errout;
-
- if (ui_socket >= 0 && mm_send_fd(s, ui_socket)) {
- close(ui_socket);
- goto errout;
- }
-
- /* In case of stdin, we do not close the socket. */
- if (ui_socket > 0)
- close(ui_socket);
- return;
-
-errout:
- log_error("m_priv_ui_init: read/write operation failed");
- return;
-}
-
-/* Privileged: called by monitor_loop. */
-static void
-m_priv_pfkey_open(int s)
-{
- int fd;
- int32_t err;
-
- fd = pf_key_v2_open();
-
- if (fd < 0)
- err = -1;
- else
- err = 0;
-
- if (m_write_int32(s, err))
- goto errout;
-
- if (fd > 0 && mm_send_fd(s, fd)) {
- close(fd);
- goto errout;
- }
- close(fd);
-
- return;
-
-errout:
- log_error("m_priv_pfkey_open: read/write operation failed");
- return;
-}
-
-/* Privileged: called by monitor_loop. */
-static void
-m_priv_getfd(int s)
-{
- char path[MAXPATHLEN];
- int32_t v, err;
- int flags;
- mode_t mode;
-
- /*
- * We expect the following data on the socket:
- * u_int32_t pathlen
- * <variable> path
- * u_int32_t flags
- * u_int32_t mode
- */
-
- if (m_read_raw(s, path, MAXPATHLEN))
- goto errout;
-
- if (m_read_int32(s, &v))
- goto errout;
- flags = (int)v;
-
- if (m_read_int32(s, &v))
- goto errout;
- mode = (mode_t) v;
-
- if (m_priv_local_sanitize_path(path, sizeof path, flags) != 0) {
- err = EACCES;
- v = -1;
- } else {
- err = 0;
- v = (int32_t)open(path, flags, mode);
- if (v < 0)
- err = (int32_t)errno;
- }
-
- if (m_write_int32(s, err))
- goto errout;
-
- if (v > 0 && mm_send_fd(s, v)) {
- close(v);
- goto errout;
- }
- close(v);
- return;
-
-errout:
- log_error("m_priv_getfd: read/write operation failed");
- return;
-}
-
-/* Privileged: called by monitor_loop. */
-static void
-m_priv_getsocket(int s)
-{
- int domain, type, protocol;
- int32_t v, err;
-
- if (m_read_int32(s, &v))
- goto errout;
- domain = (int)v;
-
- if (m_read_int32(s, &v))
- goto errout;
- type = (int)v;
-
- if (m_read_int32(s, &v))
- goto errout;
- protocol = (int)v;
-
- err = 0;
- v = (int32_t)socket(domain, type, protocol);
- if (v < 0)
- err = (int32_t)errno;
-
- if (m_write_int32(s, err))
- goto errout;
-
- if (v > 0 && mm_send_fd(s, v)) {
- close(v);
- goto errout;
- }
- close(v);
- return;
-
-errout:
- log_error("m_priv_getsocket: read/write operation failed");
- return;
-}
-
-/* Privileged: called by monitor_loop. */
-static void
-m_priv_setsockopt(int s)
-{
- int sock, level, optname;
- char *optval = 0;
- socklen_t optlen;
- int32_t v, err;
-
- sock = mm_receive_fd(s);
- if (sock < 0)
- goto errout;
-
- if (m_read_int32(s, &level))
- goto errout;
-
- if (m_read_int32(s, &optname))
- goto errout;
-
- if (m_read_int32(s, &optlen))
- goto errout;
-
- optval = (char *)malloc(optlen);
- if (!optval)
- goto errout;
-
- if (m_read_raw(s, optval, optlen))
- goto errout;
-
- if (m_priv_check_sockopt(level, optname) != 0) {
- err = EACCES;
- v = -1;
- } else {
- err = 0;
- v = (int32_t)setsockopt(sock, level, optname, optval, optlen);
- if (v < 0)
- err = (int32_t)errno;
- }
-
- close(sock);
- sock = -1;
-
- if (m_write_int32(s, err))
- goto errout;
-
- if (m_write_int32(s, v))
- goto errout;
-
- free(optval);
- return;
-
-errout:
- log_print("m_priv_setsockopt: read/write error");
- if (optval)
- free(optval);
- if (sock >= 0)
- close(sock);
- return;
-}
-
-/* Privileged: called by monitor_loop. */
-static void
-m_priv_bind(int s)
-{
- int sock;
- struct sockaddr *name = 0;
- socklen_t namelen;
- int32_t v, err;
-
- sock = mm_receive_fd(s);
- if (sock < 0)
- goto errout;
-
- if (m_read_int32(s, &v))
- goto errout;
- namelen = (socklen_t) v;
-
- name = (struct sockaddr *)malloc(namelen);
- if (!name)
- goto errout;
-
- if (m_read_raw(s, (char *)name, (size_t)namelen))
- goto errout;
-
- if (m_priv_check_bind(name, namelen) != 0) {
- err = EACCES;
- v = -1;
- } else {
- err = 0;
- v = (int32_t)bind(sock, name, namelen);
- if (v < 0) {
- log_error("m_priv_bind: bind(%d,%p,%d) returned %d",
- sock, name, namelen, v);
- err = (int32_t)errno;
- }
- }
-
- close(sock);
- sock = -1;
-
- if (m_write_int32(s, err))
- goto errout;
-
- if (m_write_int32(s, v))
- goto errout;
-
- free(name);
- return;
-
-errout:
- log_print("m_priv_bind: read/write error");
- if (name)
- free(name);
- if (sock >= 0)
- close(sock);
- return;
-}
-
-/*
- * Help functions, used by both privileged and unprivileged code
- */
-
-/* Write a 32-bit value to a socket. */
-int
-m_write_int32(int s, int32_t value)
-{
- u_int32_t v;
-
- memcpy(&v, &value, sizeof v);
- return (write(s, &v, sizeof v) == -1);
-}
-
-/* Write a number of bytes of data to a socket. */
-int
-m_write_raw(int s, char *data, size_t dlen)
-{
- if (m_write_int32(s, (int32_t) dlen))
- return 1;
- return (write(s, data, dlen) == -1);
-}
-
-int
-m_read_int32(int s, int32_t *value)
-{
- u_int32_t v;
-
- if (read(s, &v, sizeof v) != sizeof v)
- return 1;
- memcpy(value, &v, sizeof v);
- return 0;
-}
-
-int
-m_read_raw(int s, char *data, size_t maxlen)
-{
- u_int32_t v;
- int r;
-
- if (m_read_int32(s, &v))
- return 1;
- if (v > maxlen)
- return 1;
- r = read(s, data, v);
- return (r == -1);
-}
-
-/* Drain all available input on a socket. */
-void
-m_flush(int s)
-{
- u_int8_t tmp;
- int one = 1;
-
- ioctl(s, FIONBIO, &one);/* Non-blocking */
- while (read(s, &tmp, 1) > 0);
- ioctl(s, FIONBIO, 0); /* Blocking */
-}
-
-/* Check that path/mode is permitted. */
-static int
-m_priv_local_sanitize_path(char *path, size_t pmax, int flags)
-{
- char *p;
-
- /*
- * We only permit paths starting with
- * /etc/isakmpd/ (read only)
- * /var/run/ (rw)
- */
-
- if (strlen(path) < strlen("/var/run/"))
- goto bad_path;
-
- /* Any path containing '..' is invalid. */
- for (p = path; *p && (p - path) < (int)pmax; p++)
- if (*p == '.' && *(p + 1) == '.')
- goto bad_path;
-
- /* For any write-mode, only a few paths are permitted. */
- if ((flags & O_ACCMODE) != O_RDONLY) {
- if (strncmp("/var/run/", path, strlen("/var/run/")) == 0)
- return 0;
- goto bad_path;
- }
- /* Any other path is read-only. */
- if (strncmp(ISAKMPD_ROOT, path, strlen(ISAKMPD_ROOT)) == 0 ||
- strncmp("/var/run/", path, strlen("/var/run/")) == 0)
- return 0;
-
-bad_path:
- log_print("m_priv_local_sanitize_path: illegal path \"%.1023s\", "
- "replaced with \"/dev/null\"", path);
- strlcpy(path, "/dev/null", pmax);
- return 1;
-}
-
-/* Check setsockopt */
-static int
-m_priv_check_sockopt(int level, int name)
-{
- switch (level) {
- /* These are allowed */
- case SOL_SOCKET:
- case IPPROTO_IP:
- case IPPROTO_IPV6:
- break;
-
- default:
- log_print("m_priv_check_sockopt: Illegal level %d", level);
- return 1;
- }
-
- switch (name) {
- /* These are allowed */
- case SO_REUSEPORT:
- case SO_REUSEADDR:
- case IP_AUTH_LEVEL:
- case IP_ESP_TRANS_LEVEL:
- case IP_ESP_NETWORK_LEVEL:
- case IP_IPCOMP_LEVEL:
- case IPV6_AUTH_LEVEL:
- case IPV6_ESP_TRANS_LEVEL:
- case IPV6_ESP_NETWORK_LEVEL:
- case IPV6_IPCOMP_LEVEL:
- break;
-
- default:
- log_print("m_priv_check_sockopt: Illegal option name %d",
- name);
- return 1;
- }
-
- return 0;
-}
-
-/* Check bind */
-static int
-m_priv_check_bind(const struct sockaddr *sa, socklen_t salen)
-{
- in_port_t port;
-
- if (sa == NULL) {
- log_print("NULL address");
- return 1;
- }
- if (sysdep_sa_len((struct sockaddr *)sa) != salen) {
- log_print("Length mismatch: %d %d",
- (int)sysdep_sa_len((struct sockaddr *)sa), (int)salen);
- return 1;
- }
- switch (sa->sa_family) {
- case AF_INET:
- if (salen != sizeof(struct sockaddr_in)) {
- log_print("Invalid inet address length");
- return 1;
- }
- port = ((const struct sockaddr_in *)sa)->sin_port;
- break;
- case AF_INET6:
- if (salen != sizeof(struct sockaddr_in6)) {
- log_print("Invalid inet6 address length");
- return 1;
- }
- port = ((const struct sockaddr_in6 *)sa)->sin6_port;
- break;
- default:
- log_print("Unknown address family");
- return 1;
- }
-
- port = ntohs(port);
-
- if (port != ISAKMP_PORT_DEFAULT && port < 1024) {
- log_print("Disallowed port %u", port);
- return 1;
- }
- return 0;
-}
-
-/* Increase state into less permissive mode */
-static void
-m_priv_increase_state(int state)
-{
- if (state <= cur_state)
- log_print("m_priv_increase_state: attempt to decrase state "
- "or match current state");
- if (state < STATE_INIT || state > STATE_QUIT)
- log_print("m_priv_increase_state: attempt to switch to "
- "invalid state");
- cur_state = state;
-}
-
-static void
-m_priv_test_state(int state)
-{
- if (cur_state != state)
- log_print("m_priv_test_state: Illegal state: %d != %d",
- (int)cur_state, state);
- return;
-}