X-Git-Url: https://ruderich.org/simon/gitweb/?p=ptyas%2Fptyas.git;a=blobdiff_plain;f=ptyas.c;h=e8e1ba7de1eb9f7a296d14683b061ba306b7d771;hp=2d0614d8865936de8548f3d497d53298ebd668f1;hb=2bd62914a0887c368ddba71f0fbcd0a01084b4b6;hpb=f345aeadd2754d2cd1fd4c616f9a88ef8c9ea513 diff --git a/ptyas.c b/ptyas.c index 2d0614d..e8e1ba7 100644 --- a/ptyas.c +++ b/ptyas.c @@ -2,20 +2,20 @@ * Run the login shell or command as the given user in a new pty to prevent * terminal injection attacks. * - * Copyright (C) 2016-2018 Simon Ruderich + * Copyright (C) 2016-2019 Simon Ruderich * * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Affero General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . */ #define _GNU_SOURCE @@ -82,9 +82,11 @@ static void open_pty_or_die(int *pty_master, int *pty_slave, uid_t uid) { if (*pty_slave == -1) { die("open slave tty"); } - /* The user must be able to write to the new TTY. Normally grantpt() would + /* + * The user must be able to write to the new TTY. Normally grantpt() would * do this for us, but we don't trust the user and thus don't want to pass - * the pty_master to a process running under that uid. */ + * the pty_master to a process running under that uid. + */ if (chown(slave_path, uid, (gid_t)-1) != 0) { die("chown slave tty"); } @@ -115,12 +117,29 @@ static int snprintf_or_assert(char *str, size_t size, const char *format, ...) { static void drop_privileges_or_die(uid_t uid, gid_t gid) { /* Drop all supplementary group IDs. */ +#ifdef __FreeBSD__ + { + /* FreeBSD uses the first gid to set the egid of the process. */ + gid_t egid = gid; + if (setgroups(1, &egid) != 0) { + die("setgroups"); + } + if (getgroups(1, &egid) != 1) { + die_fmt("failed to drop all supplementary groups\n"); + } + if (egid != gid) { + die_fmt("failed to drop all supplementary groups (egid): %d %d\n", + egid, gid); + } + } +#else if (setgroups(0, NULL) != 0) { die("setgroups"); } if (getgroups(0, NULL) != 0) { - die_fmt("failed to drop all supplementary groups"); + die_fmt("failed to drop all supplementary groups\n"); } +#endif /* Dropping groups may require privileges, do that first. */ if (setresgid(gid, gid, gid) != 0) { @@ -143,12 +162,12 @@ static void drop_privileges_or_die(uid_t uid, gid_t gid) { } if ( uid != ruid || uid != euid || uid != suid || gid != rgid || gid != egid || gid != sgid) { - die_fmt("failed to drop privileges"); + die_fmt("failed to drop privileges\n"); } } /* Just to be safe. */ if (setuid(0) != -1) { - die_fmt("failed to drop privileges (setuid)"); + die_fmt("failed to drop privileges (setuid)\n"); } } @@ -268,10 +287,12 @@ static void proxy_input_between_ttys(int pty_master, int ctty, volatile pid_t *p static volatile pid_t pid_to_wait_for; static int pid_to_wait_for_status; -static void sigchld_handler() { +static void sigchld_handler(int signal) { int status; pid_t pid; + (void)signal; + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (pid == pid_to_wait_for) { /* Mark that our child has died and we should exit as well. */ @@ -282,6 +303,28 @@ static void sigchld_handler() { } } +/* + * SIGWINCH handler to handle resizes of the outer terminal. + * + * Errors are ignored without message because printing in signal handlers is + * problematic (no FILE * usable due to locks) and there's not much we can do + * at this point. + */ +static int sigwinch_ctty = -1; +static int sigwinch_slave = -1; + +static void sigwinch_handler(int signal) { + (void)signal; + + struct winsize size; + if (ioctl(sigwinch_ctty, TIOCGWINSZ, &size) == -1) { + return; + } + if (ioctl(sigwinch_slave, TIOCSWINSZ, &size) == -1) { + return; + } +} + int main(int argc, char **argv) { char *exec_argv_shell[] = { NULL, NULL }; /* filled below */ @@ -418,14 +461,26 @@ int main(int argc, char **argv) { } quit_with_matching_code(status); } - close_or_die(pty_slave); + /* Don't close pty_slave here as it's used in sigwinch_handler(). */ + + sigwinch_ctty = ctty; + sigwinch_slave = pty_slave; + + struct sigaction action_sigwinch = { + .sa_handler = sigwinch_handler, + }; + sigemptyset(&action_sigwinch.sa_mask); + if (sigaction(SIGWINCH, &action_sigwinch, NULL) != 0) { + die("sigaction SIGWINCH"); + } pid_to_wait_for = pid; - struct sigaction action = { + struct sigaction action_sigchld = { .sa_handler = sigchld_handler, }; - if (sigaction(SIGCHLD, &action, NULL) != 0) { - die("sigaction"); + sigemptyset(&action_sigchld.sa_mask); + if (sigaction(SIGCHLD, &action_sigchld, NULL) != 0) { + die("sigaction SIGCHLD"); } if (sigprocmask(SIG_SETMASK, &sigset_old, NULL) != 0) {