diff options
Diffstat (limited to 'keyexchange/isakmpd-20041012/monitor.c')
-rw-r--r-- | keyexchange/isakmpd-20041012/monitor.c | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/monitor.c b/keyexchange/isakmpd-20041012/monitor.c new file mode 100644 index 0000000..bd14005 --- /dev/null +++ b/keyexchange/isakmpd-20041012/monitor.c @@ -0,0 +1,1174 @@ +/* $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; +} |