]> ruderich.org/simon Gitweb - ptyas/ptyas.git/blobdiff - ptyas.c
README: rename to README.adoc
[ptyas/ptyas.git] / ptyas.c
diff --git a/ptyas.c b/ptyas.c
index 9ffe998a7f51c25deedfae106ddb166c68b676ba..25f49aa2fc81ebb3d935ec87403857d8606a25a2 100644 (file)
--- 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-2017  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 <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 #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");
     }
@@ -222,8 +224,10 @@ static void proxy_input_between_ttys(int pty_master, int ctty, volatile pid_t *p
             break;
         }
 
-        /* Handle errors first. (Data available before the error occurred
-         * might be dropped, but shouldn't matter here.) */
+        /*
+         * Handle errors first. (Data available before the error occurred
+         * might be dropped, but shouldn't matter here.)
+         */
         if (fds[0].revents & (POLLERR | POLLNVAL)) {
             fprintf(stderr, "poll: error on master: %d\n", fds[0].revents);
             break;
@@ -266,10 +270,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. */
@@ -280,6 +286,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 */
@@ -359,9 +387,11 @@ int main(int argc, char **argv) {
         if (pid == -1) {
             die("fork child");
         } else if (pid == 0) {
-            /* Drop the privileges just now so that the other user doesn't get
+            /*
+             * Drop the privileges just now so that the other user doesn't get
              * access to the master TTY or the session leader (which might
-             * have additional privileges). */
+             * have additional privileges).
+             */
             drop_privileges_or_die(uid, gid);
 
             dup2_or_die(pty_slave, STDIN_FILENO);
@@ -376,10 +406,12 @@ int main(int argc, char **argv) {
             }
             const char *home = passwd->pw_dir;
 
-            // Ignore errors here as we don't want to die on non-existent home
-            // directories to allow running as any user (think "/nonexistent"
-            // as home) and an error message will be annoying to ignore when
-            // running this command in scripts.
+            /*
+             * Ignore errors here as we don't want to die on non-existent home
+             * directories to allow running as any user (think "/nonexistent"
+             * as home) and an error message will be annoying to ignore when
+             * running this command in scripts.
+             */
             chdir(home);
 
             char envp_user[strlen("USER=") + strlen(user) + 1];
@@ -412,14 +444,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) {
@@ -450,8 +494,10 @@ int main(int argc, char **argv) {
         die("tcsetattr restore");
     }
 
-    /* Wait until we got the status code from our child. poll() might already
-     * exit after POLLHUP while we haven't collected the child yet. */
+    /*
+     * Wait until we got the status code from our child. poll() might already
+     * exit after POLLHUP while we haven't collected the child yet.
+     */
     if (sigprocmask(SIG_BLOCK, &sigset, &sigset_old) != 0) {
         die("sigprocmask block sigchld loop");
     }