Index: privsep.c --- privsep.c.orig +++ privsep.c @@ -0,0 +1,268 @@ +/* $OpenBSD: privsep.c,v 1.7 2015/11/14 17:12:14 matthieu Exp $ */ +/* + * Copyright 2001 Niels Provos + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UTIL_H +#include +#endif +#ifdef HAVE_PTY_H +#inlude +#endif + +#include "xconsole.h" + +enum cmd_types { + PRIV_OPEN_PTY, + PRIV_REDIRECT_CONSOLE, +}; + + +static int priv_fd = -1; + +static void +send_fd(int s, int fd) +{ + struct msghdr msg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + struct cmsghdr *cmsg; + struct iovec vec; + int result = 0; + ssize_t n; + + memset(&msg, 0, sizeof(msg)); + + if (fd >= 0) { + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + 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(s, &msg, 0)) == -1) + warn("%s: sendmsg(%d)", __func__, s); + if (n != sizeof(int)) + warnx("%s: sendmsg: expected sent 1 got %ld", + __func__, (long)n); +} + +static int +receive_fd(int s) +{ + struct msghdr msg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + 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 = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + if ((n = recvmsg(s, &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; + } +} + +int +priv_init(uid_t uid, gid_t gid) +{ + int i, on, result, fd; + int pty, tty; + pid_t pid; + int socks[2]; + int cmd; + + /* Create sockets */ + if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) { + return -1; + } + pid = fork(); + if (pid < 0) { + /* can't fork */ + return -1; + } + if (pid != 0) { + /* Father - drop privileges and return */ + if (uid != -1 && gid != -1) { + if (setgroups(1, &gid) == -1) + return -1; + + if (setresgid(gid, gid, gid) == -1) + return -1; + if (setresuid(uid, uid, uid) == -1) + return -1; + } + close(socks[0]); + priv_fd = socks[1]; + return 0; + } + /* son */ + for (i = 1; i <= _NSIG; i++) + signal(i, SIG_DFL); +#ifdef HAVE_SETPROCTILE + setproctitle("[priv]"); +#endif + close(socks[1]); + + while (1) { + if (read(socks[0], &cmd, sizeof(int)) == 0) { + exit(0); + } + switch (cmd) { + + case PRIV_OPEN_PTY: + if (openpty(&pty, &tty, NULL, NULL, NULL) == -1) { + warn("%s: openpty", __func__); + pty = -1; + tty = -1; + } + send_fd(socks[0], pty); + send_fd(socks[0], tty); + if (pty != -1) + close(pty); + if (tty != -1) + close(tty); + break; + case PRIV_REDIRECT_CONSOLE: + on = 1; + fd = receive_fd(socks[0]); + result = ioctl(fd, TIOCCONS, (char *) &on); + if (result < 0) { + warn("%s: ioctl(TIOCCONS)", __func__); + } + write(socks[0], &result, sizeof(int)); + close(fd); + break; + default: + errx(1, "%s: unknown command %d", __func__, cmd); + break; + } + } + _exit(1); +} + +/* Open pseudo-tty */ +int +priv_openpty(int *pty_ptr, int *tty_ptr) +{ + int cmd; + int pty, tty; + + if (priv_fd != -1) { + cmd = PRIV_OPEN_PTY; + write(priv_fd, &cmd, sizeof(int)); + pty = receive_fd(priv_fd); + tty = receive_fd(priv_fd); + if (tty < 0 || pty < 0) { + fprintf(stderr, "openpty: %d %d %d\n", + pty, tty, errno); + return -1; + } + if (pty_ptr != NULL) + *pty_ptr = pty; + if (tty_ptr != NULL) + *tty_ptr = tty; + return 0; + } else + return openpty(pty_ptr, tty_ptr, NULL, NULL, NULL); +} + +/* Redirect console output */ +int +priv_set_console(int fd) +{ + int cmd; + int on, result; + + if (priv_fd != -1) { + cmd = PRIV_REDIRECT_CONSOLE; + write(priv_fd, &cmd, sizeof(int)); + send_fd(priv_fd, fd); + read(priv_fd, &result, sizeof(int)); + return result; + } else { + on = 1; + return ioctl(fd, TIOCCONS, &on); + } +}