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-2023
  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 <http://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 /*** file scope variables ************************************************************************/
  84 
  85 /*** file scope functions ************************************************************************/
  86 /* --------------------------------------------------------------------------------------------- */
  87 
  88 static void
  89 send_contents (char *buffer, unsigned int columns, unsigned int rows)
     /* [previous][next][first][last][top][bottom][index][help]  */
  90 {
  91     unsigned char begin_line = 0, end_line = 0;
  92     unsigned int lastline, lc_index, x;
  93     unsigned char message, outbuf[1024], *p;
  94     unsigned short bytes;
  95 
  96     lc_index = 2 * rows * columns;
  97     for (lastline = rows; lastline > 0; lastline--)
  98         for (x = 0; x < columns; x++)
  99         {
 100             lc_index -= 2;
 101             if (buffer[lc_index] != ' ')
 102                 goto out;
 103         }
 104   out:
 105 
 106     message = CONSOLE_CONTENTS;
 107     if (write (1, &message, 1) != 1)
 108         return;
 109     if (read (0, &begin_line, 1) != 1)
 110         return;
 111     if (read (0, &end_line, 1) != 1)
 112         return;
 113     if (begin_line > lastline)
 114         begin_line = lastline;
 115     if (end_line > lastline)
 116         end_line = lastline;
 117 
 118     lc_index = (end_line - begin_line) * columns;
 119     bytes = lc_index;
 120 
 121     if (write (1, &bytes, 2) != 2)
 122         return;
 123     if (bytes == 0)
 124         return;
 125 
 126     p = outbuf;
 127     for (lc_index = 2 * begin_line * columns; lc_index < 2 * end_line * columns; lc_index += 2)
 128     {
 129         *p++ = buffer[lc_index];
 130         if (p == outbuf + sizeof (outbuf))
 131         {
 132             if (write (1, outbuf, sizeof (outbuf)) != sizeof (outbuf))
 133                 return;
 134             p = outbuf;
 135         }
 136     }
 137 
 138     if (p != outbuf)
 139         if (write (1, outbuf, p - outbuf) < (p - outbuf))
 140             return;
 141 }
 142 
 143 /* --------------------------------------------------------------------------------------------- */
 144 
 145 static void __attribute__ ((noreturn)) die (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 146 {
 147     unsigned char zero = 0;
 148     ssize_t ret;
 149     ret = write (1, &zero, 1);
 150     (void) ret;
 151     exit (3);
 152 }
 153 
 154 /* --------------------------------------------------------------------------------------------- */
 155 /*** public functions ****************************************************************************/
 156 /* --------------------------------------------------------------------------------------------- */
 157 
 158 int
 159 main (int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help]  */
 160 {
 161     unsigned char action = 0, console_flag = 3;
 162     int console_fd, vcsa_fd, console_minor, buffer_size;
 163     struct stat st;
 164     uid_t uid, euid;
 165     char *buffer, *tty_name, console_name[16], vcsa_name[16];
 166     const char *p, *q;
 167     struct winsize winsz;
 168 
 169     close (STDERR_FILENO);
 170 
 171     if (argc != 2)
 172         die ();
 173 
 174     tty_name = argv[1];
 175     if (strnlen (tty_name, 15) == 15 || strncmp (tty_name, "/dev/", 5))
 176         die ();
 177 
 178     setsid ();
 179     uid = getuid ();
 180     euid = geteuid ();
 181 
 182     if (seteuid (uid) < 0)
 183         die ();
 184     console_fd = open (tty_name, O_RDONLY);
 185     if (console_fd < 0)
 186         die ();
 187     if (fstat (console_fd, &st) < 0 || !S_ISCHR (st.st_mode))
 188         die ();
 189 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 190     if ((st.st_rdev & 0xff00) != 0x0400)
 191         die ();
 192     console_minor = (int) (st.st_rdev & 0x00ff);
 193 #else
 194     console_minor = 1;          /* FIXME */
 195 #endif
 196     if (console_minor < 1 || console_minor > 63)
 197         die ();
 198     if (st.st_uid != uid)
 199         die ();
 200 
 201     switch (tty_name[5])
 202     {
 203         /* devfs */
 204     case 'v':
 205         p = "/dev/vc/%d";
 206         q = "/dev/vcc/a%d";
 207         break;
 208         /* /dev/ttyN */
 209     case 't':
 210         p = "/dev/tty%d";
 211         q = "/dev/vcsa%d";
 212         break;
 213     default:
 214         die ();
 215     }
 216 
 217     snprintf (console_name, sizeof (console_name), p, console_minor);
 218     if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
 219         die ();
 220 
 221     if (seteuid (euid) < 0)
 222         die ();
 223 
 224     snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
 225     vcsa_fd = open (vcsa_name, O_RDWR);
 226     if (vcsa_fd < 0)
 227         die ();
 228     if (fstat (vcsa_fd, &st) < 0 || !S_ISCHR (st.st_mode))
 229         die ();
 230 
 231     if (seteuid (uid) < 0)
 232         die ();
 233 
 234     winsz.ws_col = winsz.ws_row = 0;
 235     if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0
 236         || winsz.ws_col <= 0 || winsz.ws_row <= 0 || winsz.ws_col >= 256 || winsz.ws_row >= 256)
 237         die ();
 238 
 239     buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
 240     buffer = calloc (buffer_size, 1);
 241     if (buffer == NULL)
 242         die ();
 243 
 244     if (write (1, &console_flag, 1) != 1)
 245         die ();
 246 
 247     while (console_flag && read (0, &action, 1) == 1)
 248     {
 249         switch (action)
 250         {
 251         case CONSOLE_DONE:
 252             console_flag = 0;
 253             continue;
 254         case CONSOLE_SAVE:
 255             if (seteuid (euid) < 0
 256                 || lseek (vcsa_fd, 0, 0) != 0
 257                 || fstat (console_fd, &st) < 0 || st.st_uid != uid
 258                 || read (vcsa_fd, buffer, buffer_size) != buffer_size
 259                 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
 260                 memset (buffer, 0, buffer_size);
 261             if (seteuid (uid) < 0)
 262                 die ();
 263             break;
 264         case CONSOLE_RESTORE:
 265             if (seteuid (euid) >= 0
 266                 && lseek (vcsa_fd, 0, 0) == 0 && fstat (console_fd, &st) >= 0 && st.st_uid == uid)
 267                 if (write (vcsa_fd, buffer, buffer_size) != buffer_size)
 268                     die ();
 269             if (seteuid (uid) < 0)
 270                 die ();
 271             break;
 272         case CONSOLE_CONTENTS:
 273             send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
 274             break;
 275         default:
 276             break;
 277         }
 278 
 279         if (write (1, &console_flag, 1) != 1)
 280             die ();
 281     }
 282 
 283     exit (0);
 284 }
 285 
 286 /* --------------------------------------------------------------------------------------------- */

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