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
  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, console_minor, buffer_size;
 167     struct stat st;
 168     uid_t uid, euid;
 169     char *buffer, *tty_name, console_name[16], vcsa_name[16];
 170     const char *p, *q;
 171     struct winsize winsz;
 172 
 173     close (STDERR_FILENO);
 174 
 175     if (argc != 2)
 176         die ();
 177 
 178     tty_name = argv[1];
 179     if (strnlen (tty_name, 15) == 15 || strncmp (tty_name, "/dev/", 5))
 180         die ();
 181 
 182     setsid ();
 183     uid = getuid ();
 184     euid = geteuid ();
 185 
 186     if (seteuid (uid) < 0)
 187         die ();
 188     console_fd = open (tty_name, O_RDONLY);
 189     if (console_fd < 0)
 190         die ();
 191     if (fstat (console_fd, &st) < 0 || !S_ISCHR (st.st_mode))
 192         die ();
 193 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 194     if ((st.st_rdev & 0xff00) != 0x0400)
 195         die ();
 196     console_minor = (int) (st.st_rdev & 0x00ff);
 197 #else
 198     console_minor = 1;  // FIXME
 199 #endif
 200     if (console_minor < 1 || console_minor > 63)
 201         die ();
 202     if (st.st_uid != uid)
 203         die ();
 204 
 205     switch (tty_name[5])
 206     {
 207     case 'v':
 208         // devfs
 209         p = "/dev/vc/%d";
 210         q = "/dev/vcc/a%d";
 211         break;
 212     case 't':
 213         // /dev/ttyN
 214         p = "/dev/tty%d";
 215         q = "/dev/vcsa%d";
 216         break;
 217     default:
 218         die ();
 219     }
 220 
 221     snprintf (console_name, sizeof (console_name), p, console_minor);
 222     if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
 223         die ();
 224 
 225     if (seteuid (euid) < 0)
 226         die ();
 227 
 228     snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
 229     vcsa_fd = open (vcsa_name, O_RDWR);
 230     if (vcsa_fd < 0)
 231         die ();
 232     if (fstat (vcsa_fd, &st) < 0 || !S_ISCHR (st.st_mode))
 233         die ();
 234 
 235     if (seteuid (uid) < 0)
 236         die ();
 237 
 238     winsz.ws_col = winsz.ws_row = 0;
 239     if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0 || winsz.ws_col <= 0 || winsz.ws_row <= 0
 240         || winsz.ws_col >= 256 || winsz.ws_row >= 256)
 241         die ();
 242 
 243     buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
 244     buffer = calloc (buffer_size, 1);
 245     if (buffer == NULL)
 246         die ();
 247 
 248     if (write (1, &console_flag, 1) != 1)
 249         die ();
 250 
 251     while (console_flag && read (0, &action, 1) == 1)
 252     {
 253         switch (action)
 254         {
 255         case CONSOLE_DONE:
 256             console_flag = 0;
 257             continue;
 258         case CONSOLE_SAVE:
 259             if (seteuid (euid) < 0 || lseek (vcsa_fd, 0, 0) != 0 || fstat (console_fd, &st) < 0
 260                 || st.st_uid != uid || read (vcsa_fd, buffer, buffer_size) != buffer_size
 261                 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
 262                 memset (buffer, 0, buffer_size);
 263             if (seteuid (uid) < 0)
 264                 die ();
 265             break;
 266         case CONSOLE_RESTORE:
 267             if (seteuid (euid) >= 0 && lseek (vcsa_fd, 0, 0) == 0 && fstat (console_fd, &st) >= 0
 268                 && st.st_uid == uid)
 269                 if (write (vcsa_fd, buffer, buffer_size) != buffer_size)
 270                     die ();
 271             if (seteuid (uid) < 0)
 272                 die ();
 273             break;
 274         case CONSOLE_CONTENTS:
 275             send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
 276             break;
 277         default:
 278             break;
 279         }
 280 
 281         if (write (1, &console_flag, 1) != 1)
 282             die ();
 283     }
 284 
 285     exit (0);
 286 }
 287 
 288 /* --------------------------------------------------------------------------------------------- */

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