From owner-svn-src-all@freebsd.org Thu Oct 6 14:55:17 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 2BFFBBE92F4; Thu, 6 Oct 2016 14:55:17 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 0561940E; Thu, 6 Oct 2016 14:55:16 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u96EtGP5029463; Thu, 6 Oct 2016 14:55:16 GMT (envelope-from cem@FreeBSD.org) Received: (from cem@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u96EtGZC029462; Thu, 6 Oct 2016 14:55:16 GMT (envelope-from cem@FreeBSD.org) Message-Id: <201610061455.u96EtGZC029462@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: cem set sender to cem@FreeBSD.org using -f From: "Conrad E. Meyer" Date: Thu, 6 Oct 2016 14:55:16 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r306761 - head/usr.bin/write X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 06 Oct 2016 14:55:17 -0000 Author: cem Date: Thu Oct 6 14:55:15 2016 New Revision: 306761 URL: https://svnweb.freebsd.org/changeset/base/306761 Log: write(1): Capsicumify Enter Capsicum capability sandbox pretty early in this setuid program. Some minor modifications were needed to cache directory fds and use relative lookups. Rights restriction of the stdio descriptors is unfortunately pretty messy because we need an ioctl capability not present in the current libcapsicum helpers (FIODGNAME). Reviewed by: ed Sponsored by: Dell EMC Isilon Differential Revision: https://reviews.freebsd.org/D7999 Modified: head/usr.bin/write/write.c Modified: head/usr.bin/write/write.c ============================================================================== --- head/usr.bin/write/write.c Thu Oct 6 14:42:06 2016 (r306760) +++ head/usr.bin/write/write.c Thu Oct 6 14:55:15 2016 (r306761) @@ -46,12 +46,16 @@ static char sccsid[] = "@(#)write.c 8.1 __FBSDID("$FreeBSD$"); #include +#include +#include #include #include -#include #include + +#include #include #include +#include #include #include #include @@ -64,23 +68,76 @@ __FBSDID("$FreeBSD$"); #include void done(int); -void do_write(char *, char *, uid_t); +void do_write(int, char *, char *, const char *); static void usage(void); -int term_chk(char *, int *, time_t *, int); +int term_chk(int, char *, int *, time_t *, int); void wr_fputs(wchar_t *s); -void search_utmp(char *, char *, char *, uid_t); +void search_utmp(int, char *, char *, char *, uid_t); int utmp_chk(char *, char *); int main(int argc, char **argv) { + unsigned long cmds[] = { TIOCGETA, TIOCGWINSZ, FIODGNAME }; + cap_rights_t rights; + struct passwd *pwd; time_t atime; uid_t myuid; int msgsok, myttyfd; char tty[MAXPATHLEN], *mytty; + const char *login; + int devfd; (void)setlocale(LC_CTYPE, ""); + devfd = open(_PATH_DEV, O_RDONLY); + if (devfd < 0) + err(1, "open(/dev)"); + cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_IOCTL, CAP_LOOKUP, + CAP_PWRITE); + if (cap_rights_limit(devfd, &rights) < 0 && errno != ENOSYS) + err(1, "can't limit devfd rights"); + + /* + * Can't use capsicum helpers here because we need the additional + * FIODGNAME ioctl. + */ + cap_rights_init(&rights, CAP_FCNTL, CAP_FSTAT, CAP_IOCTL, CAP_READ, + CAP_WRITE); + if ((cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS) || + (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) || + (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS) || + (cap_ioctls_limit(STDIN_FILENO, cmds, nitems(cmds)) < 0 && errno != ENOSYS) || + (cap_ioctls_limit(STDOUT_FILENO, cmds, nitems(cmds)) < 0 && errno != ENOSYS) || + (cap_ioctls_limit(STDERR_FILENO, cmds, nitems(cmds)) < 0 && errno != ENOSYS) || + (cap_fcntls_limit(STDIN_FILENO, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) || + (cap_fcntls_limit(STDOUT_FILENO, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) || + (cap_fcntls_limit(STDERR_FILENO, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS)) + err(1, "can't limit stdio rights"); + + caph_cache_catpages(); + caph_cache_tzdata(); + + /* + * Cache UTX database fds. + */ + setutxent(); + + /* + * Determine our login name before we reopen() stdout + * and before entering capability sandbox. + */ + myuid = getuid(); + if ((login = getlogin()) == NULL) { + if ((pwd = getpwuid(myuid))) + login = pwd->pw_name; + else + login = "???"; + } + + if (cap_enter() < 0 && errno != ENOSYS) + err(1, "cap_enter"); + while (getopt(argc, argv, "") != -1) usage(); argc -= optind; @@ -99,29 +156,27 @@ main(int argc, char **argv) errx(1, "can't find your tty's name"); if (!strncmp(mytty, _PATH_DEV, strlen(_PATH_DEV))) mytty += strlen(_PATH_DEV); - if (term_chk(mytty, &msgsok, &atime, 1)) + if (term_chk(devfd, mytty, &msgsok, &atime, 1)) exit(1); if (!msgsok) errx(1, "you have write permission turned off"); - myuid = getuid(); - /* check args */ switch (argc) { case 1: - search_utmp(argv[0], tty, mytty, myuid); - do_write(tty, mytty, myuid); + search_utmp(devfd, argv[0], tty, mytty, myuid); + do_write(devfd, tty, mytty, login); break; case 2: if (!strncmp(argv[1], _PATH_DEV, strlen(_PATH_DEV))) argv[1] += strlen(_PATH_DEV); if (utmp_chk(argv[0], argv[1])) errx(1, "%s is not logged in on %s", argv[0], argv[1]); - if (term_chk(argv[1], &msgsok, &atime, 1)) + if (term_chk(devfd, argv[1], &msgsok, &atime, 1)) exit(1); if (myuid && !msgsok) errx(1, "%s has messages disabled on %s", argv[0], argv[1]); - do_write(argv[1], mytty, myuid); + do_write(devfd, argv[1], mytty, login); break; default: usage(); @@ -170,7 +225,7 @@ utmp_chk(char *user, char *tty) * writing from, unless that's the only terminal with messages enabled. */ void -search_utmp(char *user, char *tty, char *mytty, uid_t myuid) +search_utmp(int devfd, char *user, char *tty, char *mytty, uid_t myuid) { struct utmpx *u; time_t bestatime, atime; @@ -185,7 +240,7 @@ search_utmp(char *user, char *tty, char if (u->ut_type == USER_PROCESS && strcmp(user, u->ut_user) == 0) { ++nloggedttys; - if (term_chk(u->ut_line, &msgsok, &atime, 0)) + if (term_chk(devfd, u->ut_line, &msgsok, &atime, 0)) continue; /* bad term? skip */ if (myuid && !msgsok) continue; /* skip ttys with msgs off */ @@ -219,15 +274,13 @@ search_utmp(char *user, char *tty, char * and the access time */ int -term_chk(char *tty, int *msgsokP, time_t *atimeP, int showerror) +term_chk(int devfd, char *tty, int *msgsokP, time_t *atimeP, int showerror) { struct stat s; - char path[MAXPATHLEN]; - (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); - if (stat(path, &s) < 0) { + if (fstatat(devfd, tty, &s, 0) < 0) { if (showerror) - warn("%s", path); + warn("%s%s", _PATH_DEV, tty); return(1); } *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ @@ -239,26 +292,21 @@ term_chk(char *tty, int *msgsokP, time_t * do_write - actually make the connection */ void -do_write(char *tty, char *mytty, uid_t myuid) +do_write(int devfd, char *tty, char *mytty, const char *login) { - const char *login; char *nows; - struct passwd *pwd; time_t now; - char path[MAXPATHLEN], host[MAXHOSTNAMELEN]; + char host[MAXHOSTNAMELEN]; wchar_t line[512]; + int fd; - /* Determine our login name before we reopen() stdout */ - if ((login = getlogin()) == NULL) { - if ((pwd = getpwuid(myuid))) - login = pwd->pw_name; - else - login = "???"; - } - - (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); - if ((freopen(path, "w", stdout)) == NULL) - err(1, "%s", path); + fd = openat(devfd, tty, O_WRONLY); + if (fd < 0) + err(1, "openat(%s%s)", _PATH_DEV, tty); + fclose(stdout); + stdout = fdopen(fd, "w"); + if (stdout == NULL) + err(1, "%s%s", _PATH_DEV, tty); (void)signal(SIGINT, done); (void)signal(SIGHUP, done);