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

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