]> ruderich.org/simon Gitweb - ptyas/ptyas.git/blobdiff - ptyas.c
Add support for FreeBSD
[ptyas/ptyas.git] / ptyas.c
diff --git a/ptyas.c b/ptyas.c
index e6ecd558405be523fe971a2f747bac905c1d00a9..e8e1ba7de1eb9f7a296d14683b061ba306b7d771 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");
     }
@@ -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) {