Manual pages: mcmcdiffmceditmcview

root/src/consaver/cons.saver.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. send_contents
  2. die
  3. main

   1 /*
   2    General purpose Linux console screen save/restore server
   3 
   4    This program should be setuid vcsa and /dev/vcsa* should be
   5    owned by the same user too.
   6 
   7    Original idea from Unix Interactive Tools version 3.2b (tty.c)
   8    This code requires root privileges.
   9    You may want to make the cons.saver setuid root.
  10    The code should be safe even if it is setuid but who knows?
  11 
  12    Partly rewritten by Jakub Jelinek <jakub@redhat.com>.
  13 
  14    Copyright (C) 1994-2025
  15    Free Software Foundation, Inc.
  16 
  17    This file is part of the Midnight Commander.
  18 
  19    The Midnight Commander is free software: you can redistribute it
  20    and/or modify it under the terms of the GNU General Public License as
  21    published by the Free Software Foundation, either version 3 of the License,
  22    or (at your option) any later version.
  23 
  24    The Midnight Commander is distributed in the hope that it will be useful,
  25    but WITHOUT ANY WARRANTY; without even the implied warranty of
  26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27    GNU General Public License for more details.
  28 
  29    You should have received a copy of the GNU General Public License
  30    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  31  */
  32 
  33 /* This code does _not_ need to be setuid root. However, it needs
  34    read/write access to /dev/vcsa* (which is privileged
  35    operation). You should create user vcsa, make cons.saver setuid
  36    user vcsa, and make all vcsa's owned by user vcsa.
  37 
  38    Seeing other peoples consoles is bad thing, but believe me, full
  39    root is even worse. */
  40 
  41 /** \file cons.saver.c
  42  *  \brief Source: general purpose Linux console screen save/restore server
  43  *
  44  *  This code does _not_ need to be setuid root. However, it needs
  45  *  read/write access to /dev/vcsa* (which is privileged
  46  *  operation). You should create user vcsa, make cons.saver setuid
  47  *  user vcsa, and make all vcsa's owned by user vcsa.
  48  *  Seeing other peoples consoles is bad thing, but believe me, full
  49  *  root is even worse.
  50  */
  51 
  52 #include <config.h>
  53 
  54 #ifndef _GNU_SOURCE
  55 #define _GNU_SOURCE
  56 #endif
  57 
  58 #include <stdlib.h>
  59 #include <stdio.h>
  60 #include <string.h>
  61 
  62 #include <sys/types.h>
  63 #include <sys/stat.h>
  64 #ifdef HAVE_SYS_IOCTL_H
  65 #include <sys/ioctl.h>
  66 #endif
  67 #ifdef HAVE_FCNTL_H
  68 #include <fcntl.h>
  69 #endif
  70 #include <termios.h>
  71 
  72 #include "lib/unixcompat.h"  // STDERR_FILENO, major(), minor()
  73 
  74 #define LINUX_CONS_SAVER_C
  75 #include "cons.saver.h"
  76 
  77 /*** global variables ****************************************************************************/
  78 
  79 /*** file scope macro definitions ****************************************************************/
  80 
  81 /*** file scope type declarations ****************************************************************/
  82 
  83 /*** forward declarations (file scope functions) *************************************************/
  84 
  85 /*** file scope variables ************************************************************************/
  86 
  87 /* --------------------------------------------------------------------------------------------- */
  88 /*** file scope functions ************************************************************************/
  89 /* --------------------------------------------------------------------------------------------- */
  90 
  91 static void
  92 send_contents (char *buffer, unsigned int columns, unsigned int rows)
     /* [previous][next][first][last][top][bottom][index][help]  */
  93 {
  94     unsigned char begin_line = 0, end_line = 0;
  95     unsigned int lastline, lc_index, x;
  96     unsigned char message, outbuf[1024], *p;
  97     unsigned short bytes;
  98 
  99     lc_index = 2 * rows * columns;
 100     for (lastline = rows; lastline > 0; lastline--)
 101         for (x = 0; x < columns; x++)
 102         {
 103             lc_index -= 2;
 104             if (buffer[lc_index] != ' ')
 105                 goto out;
 106         }
 107 out:
 108 
 109     message = CONSOLE_CONTENTS;
 110     if (write (1, &message, 1) != 1)
 111         return;
 112     if (read (0, &begin_line, 1) != 1)
 113         return;
 114     if (read (0, &end_line, 1) != 1)
 115         return;
 116     if (begin_line > lastline)
 117         begin_line = lastline;
 118     if (end_line > lastline)
 119         end_line = lastline;
 120 
 121     lc_index = (end_line - begin_line) * columns;
 122     bytes = lc_index;
 123 
 124     if (write (1, &bytes, 2) != 2)
 125         return;
 126     if (bytes == 0)
 127         return;
 128 
 129     p = outbuf;
 130     for (lc_index = 2 * begin_line * columns; lc_index < 2 * end_line * columns; lc_index += 2)
 131     {
 132         *p++ = buffer[lc_index];
 133         if (p == outbuf + sizeof (outbuf))
 134         {
 135             if (write (1, outbuf, sizeof (outbuf)) != sizeof (outbuf))
 136                 return;
 137             p = outbuf;
 138         }
 139     }
 140 
 141     if (p != outbuf)
 142         if (write (1, outbuf, p - outbuf) < (p - outbuf))
 143             return;
 144 }
 145 
 146 /* --------------------------------------------------------------------------------------------- */
 147 
 148 static void __attribute__ ((noreturn))
 149 die (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 150 {
 151     unsigned char zero = 0;
 152     ssize_t ret;
 153     ret = write (1, &zero, 1);
 154     (void) ret;
 155     exit (3);
 156 }
 157 
 158 /* --------------------------------------------------------------------------------------------- */
 159 /*** public functions ****************************************************************************/
 160 /* --------------------------------------------------------------------------------------------- */
 161 
 162 int
 163 main (int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help]  */
 164 {
 165     unsigned char action = 0, console_flag = 3;
 166     int console_fd, vcsa_fd, buffer_size;
 167     unsigned int console_minor;
 168     struct stat st;
 169     uid_t uid, euid;
 170     char *buffer, *tty_name, console_name[16], vcsa_name[16];
 171     struct winsize winsz;
 172 
 173     close (STDERR_FILENO);
 174 
 175     if (argc != 2)
 176         die ();
 177 
 178     tty_name = argv[1];
 179 
 180     // make sure the parameter is /dev/tty1 .. /dev/tty63, extract the number
 181     if (sscanf (tty_name, "/dev/tty%u", &console_minor) != 1)
 182         die ();
 183     if (console_minor < 1 || console_minor > 63)
 184         die ();
 185 
 186     // sscanf allows negative sign, leading zeros, integer overflow, or arbitrary suffix;
 187     // verify that the canonical form was given
 188     snprintf (console_name, sizeof (console_name), "/dev/tty%u", console_minor);
 189     if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
 190         die ();
 191 
 192     setsid ();
 193     uid = getuid ();
 194     euid = geteuid ();
 195 
 196     if (seteuid (uid) < 0)
 197         die ();
 198 
 199     console_fd = open (console_name, O_RDONLY);
 200     if (console_fd < 0)
 201         die ();
 202     if (fstat (console_fd, &st) < 0 || !S_ISCHR (st.st_mode))
 203         die ();
 204 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 205     if (major (st.st_rdev) != 4)
 206         die ();
 207     if (minor (st.st_rdev) != console_minor)
 208         die ();
 209 #endif
 210     if (st.st_uid != uid)
 211         die ();
 212 
 213     if (seteuid (euid) < 0)
 214         die ();
 215 
 216     snprintf (vcsa_name, sizeof (vcsa_name), "/dev/vcsa%u", console_minor);
 217     vcsa_fd = open (vcsa_name, O_RDWR);
 218     if (vcsa_fd < 0)
 219         die ();
 220     if (fstat (vcsa_fd, &st) < 0 || !S_ISCHR (st.st_mode))
 221         die ();
 222 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 223     if (major (st.st_rdev) != 7)
 224         die ();
 225     if (minor (st.st_rdev) != 128 + console_minor)
 226         die ();
 227 #endif
 228 
 229     if (seteuid (uid) < 0)
 230         die ();
 231 
 232     winsz.ws_col = winsz.ws_row = 0;
 233     if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0 || winsz.ws_col <= 0 || winsz.ws_row <= 0
 234         || winsz.ws_col >= 256 || winsz.ws_row >= 256)
 235         die ();
 236 
 237     buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
 238     buffer = calloc (buffer_size, 1);
 239     if (buffer == NULL)
 240         die ();
 241 
 242     if (write (1, &console_flag, 1) != 1)
 243         die ();
 244 
 245     while (console_flag && read (0, &action, 1) == 1)
 246     {
 247         switch (action)
 248         {
 249         case CONSOLE_DONE:
 250             console_flag = 0;
 251             continue;
 252         case CONSOLE_SAVE:
 253             if (seteuid (euid) < 0 || lseek (vcsa_fd, 0, 0) != 0 || fstat (console_fd, &st) < 0
 254                 || st.st_uid != uid || read (vcsa_fd, buffer, buffer_size) != buffer_size
 255                 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
 256                 memset (buffer, 0, buffer_size);
 257             if (seteuid (uid) < 0)
 258                 die ();
 259             break;
 260         case CONSOLE_RESTORE:
 261             if (seteuid (euid) >= 0 && lseek (vcsa_fd, 0, 0) == 0 && fstat (console_fd, &st) >= 0
 262                 && st.st_uid == uid)
 263                 if (write (vcsa_fd, buffer, buffer_size) != buffer_size)
 264                     die ();
 265             if (seteuid (uid) < 0)
 266                 die ();
 267             break;
 268         case CONSOLE_CONTENTS:
 269             send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
 270             break;
 271         default:
 272             break;
 273         }
 274 
 275         if (write (1, &console_flag, 1) != 1)
 276             die ();
 277     }
 278 
 279     exit (0);
 280 }
 281 
 282 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */