From 883fe2a618731100cbecb9d7da55fd62b384d3bf Mon Sep 17 00:00:00 2001 From: matthieu Date: Sun, 26 Nov 2006 16:59:50 +0000 Subject: [PATCH] OpenBSD modification: privilege separation. --- app/xconsole/Makefile.am | 12 ++ app/xconsole/config.h.in | 60 +++++++++ app/xconsole/privsep.c | 254 +++++++++++++++++++++++++++++++++++++++ app/xconsole/xconsole.c | 56 ++++++++- 4 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 app/xconsole/privsep.c diff --git a/app/xconsole/Makefile.am b/app/xconsole/Makefile.am index de2cd2d64..b6245d530 100644 --- a/app/xconsole/Makefile.am +++ b/app/xconsole/Makefile.am @@ -27,6 +27,11 @@ xconsole_LDADD = $(XCONSOLE_LIBS) xconsole_SOURCES = \ xconsole.c +if USE_PRIVSEP +xconsole_SOURCES += \ + privsep.c +endif + appman_PRE = \ xconsole.man @@ -80,3 +85,10 @@ SUFFIXES += .$(APP_MAN_SUFFIX) .man .man.$(APP_MAN_SUFFIX): sed $(MAN_SUBSTS) < $< > $@ + +.PHONY: ChangeLog + +ChangeLog: + GIT_DIR=${srcdir}/.git git-log --name-only > ChangeLog + +dist-hook: ChangeLog diff --git a/app/xconsole/config.h.in b/app/xconsole/config.h.in index c364eda45..e5866a4ee 100644 --- a/app/xconsole/config.h.in +++ b/app/xconsole/config.h.in @@ -1,5 +1,56 @@ /* config.h.in. Generated from configure.ac by autoheader. */ +/* ENOSYS: Not implemented */ +#undef BROKEN_SETRESGID + +/* ENOSYS: Not implemented */ +#undef BROKEN_SETRESUID + +/* Define to 1 if you have the 'openpty' function. */ +#undef HAS_OPENPTY + +/* Define to 1 if you have the 'setproctitle function. */ +#undef HAS_SETPROCTITLE + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PTY_H + +/* Define to 1 if you have the `setresgid' function. */ +#undef HAVE_SETRESGID + +/* Define to 1 if you have the `setresuid' function. */ +#undef HAVE_SETRESUID + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTIL_H + /* Name of package */ #undef PACKAGE @@ -18,5 +69,14 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 to use privilege separation */ +#undef USE_PRIVSEP + /* Version number of package */ #undef VERSION + +/* Unprivileged userid */ +#undef XCONSOLE_USER diff --git a/app/xconsole/privsep.c b/app/xconsole/privsep.c new file mode 100644 index 000000000..2fc1931a7 --- /dev/null +++ b/app/xconsole/privsep.c @@ -0,0 +1,254 @@ +/* $OpenBSD: privsep.c,v 1.1 2006/11/26 16:59:50 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAS_UTIL_H +#include +#endif +#ifdef HAS_PTY_H +#inlude +#endif + +enum cmd_types { + PRIV_OPEN_PTY, + PRIV_REDIRECT_CONSOLE, +}; + + +static int priv_fd = -1; + +static void +send_fd(int socket, 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(socket, &msg, 0)) == -1) + warn("%s: sendmsg(%d)", __func__, socket); + if (n != sizeof(int)) + warnx("%s: sendmsg: expected sent 1 got %ld", + __func__, (long)n); +} + +static int +receive_fd(int socket) +{ + 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(socket, &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 (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 HAS_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); + } +} diff --git a/app/xconsole/xconsole.c b/app/xconsole/xconsole.c index 1e2a41a6e..e3b67cf24 100644 --- a/app/xconsole/xconsole.c +++ b/app/xconsole/xconsole.c @@ -28,6 +28,10 @@ in this Software without prior written authorization from The Open Group. /* $XFree86: xc/programs/xconsole/xconsole.c,v 3.31tsi Exp $ */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include @@ -61,6 +65,21 @@ extern char *_XawTextGetSTRING(TextWidget ctx, XawTextPosition left, #include #include #include +#ifdef HAS_OPENPTY +# ifdef HAS_UTIL_H +# include +# endif +# ifdef HAS_PTY_H +# include +# endif +#endif +#ifdef USE_PRIVSEP +# include +#endif + +extern int priv_init(uid_t, gid_t); +extern int priv_openpty(int *, int *); +extern int priv_set_console(int); /* Fix ISC brain damage. When using gcc fdopen isn't declared in . */ #if defined(ISC) && __STDC__ && !defined(ISC30) @@ -275,8 +294,14 @@ OpenConsole(void) { # ifdef TIOCCONS int on = 1; +#ifdef USE_PRIVSEP + if (priv_set_console(tty_fd) != -1) + input = fdopen (pty_fd, "r"); +#else if (ioctl (tty_fd, TIOCCONS, (char *) &on) != -1) input = fdopen (pty_fd, "r"); +#endif + # else # ifndef Lynx int consfd = open("/dev/console", O_RDONLY); @@ -646,11 +671,30 @@ main(int argc, char *argv[]) Arg arglist[10]; Cardinal num_args; +#ifdef USE_PRIVSEP + struct passwd *pw; +#endif + XtSetLanguageProc(NULL,NULL,NULL); top = XtInitialize ("xconsole", "XConsole", options, XtNumber (options), &argc, argv); XtGetApplicationResources (top, (XtPointer)&app_resources, resources, XtNumber (resources), NULL, 0); +#ifdef USE_PRIVSEP + /* Revoke privileges if any */ + if (getuid() == 0) { + /* Running as root */ + pw = getpwnam(XCONSOLE_USER); + if (!pw) { + fprintf(stderr, "%s user not found\n", XCONSOLE_USER); + exit(2); + } + if (priv_init(pw->pw_uid, pw->pw_gid) < 0) { + fprintf(stderr, "priv_init failed\n"); + exit(2); + } + } +#endif if (app_resources.daemon) if (fork ()) exit (0); @@ -822,7 +866,17 @@ ScrollLine(Widget w) static int get_pty(int *pty, int *tty, char *ttydev, char *ptydev) { -#if defined (SVR4) || defined (USE_PTS) +#ifdef USE_PRIVSEP + if (priv_openpty(pty, tty) < 0) { + return 1; + } + return 0; +#elif HAS_OPENPTY + if (openpty(pty, tty, NULL, NULL, NULL) == -1) { + return 1; + } + return 0; +#elif defined (SVR4) || defined (USE_PTS) #if defined (_AIX) if ((*pty = open ("/dev/ptc", O_RDWR)) < 0) #else