Clarify usage of st_rdev. Use it if HAVE_STRUCT_STAT_ST_RDEV is defined.
[midnight-commander.git] / src / consaver / cons.saver.c
blob58821f3af3b2bdf7fc0ad54f86c5bd98cb1b3e70
1 /*
2 General purpose Linux console screen save/restore server
4 This program should be setuid vcsa and /dev/vcsa* should be
5 owned by the same user too.
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?
12 Partly rewritten by Jakub Jelinek <jakub@redhat.com>.
14 Copyright (C) 1994-2016
15 Free Software Foundation, Inc.
17 This file is part of the Midnight Commander.
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.
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.
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/>.
33 /* This code does _not_ need to be setuid root. However, it needs
34 read/write access to /dev/vcsa* (which is priviledged
35 operation). You should create user vcsa, make cons.saver setuid
36 user vcsa, and make all vcsa's owned by user vcsa.
38 Seeing other peoples consoles is bad thing, but believe me, full
39 root is even worse. */
41 /** \file cons.saver.c
42 * \brief Source: general purpose Linux console screen save/restore server
44 * This code does _not_ need to be setuid root. However, it needs
45 * read/write access to /dev/vcsa* (which is priviledged
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.
52 #include <config.h>
54 #ifndef _GNU_SOURCE
55 #define _GNU_SOURCE
56 #endif
58 #include <stdlib.h>
59 #include <stdio.h>
60 #include <string.h>
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>
72 #include "lib/unixcompat.h" /* STDERR_FILENO */
74 #define LINUX_CONS_SAVER_C
75 #include "cons.saver.h"
77 /*** global variables ****************************************************************************/
79 /*** file scope macro definitions ****************************************************************/
81 /*** file scope type declarations ****************************************************************/
83 /*** file scope variables ************************************************************************/
85 /*** file scope functions ************************************************************************/
86 /* --------------------------------------------------------------------------------------------- */
88 static void
89 send_contents (char *buffer, unsigned int columns, unsigned int rows)
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;
96 lc_index = 2 * rows * columns;
97 for (lastline = rows; lastline > 0; lastline--)
98 for (x = 0; x < columns; x++)
100 lc_index -= 2;
101 if (buffer[lc_index] != ' ')
102 goto out;
104 out:
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;
118 lc_index = (end_line - begin_line) * columns;
119 bytes = lc_index;
121 if (write (1, &bytes, 2) != 2)
122 return;
123 if (bytes == 0)
124 return;
126 p = outbuf;
127 for (lc_index = 2 * begin_line * columns; lc_index < 2 * end_line * columns; lc_index += 2)
129 *p++ = buffer[lc_index];
130 if (p == outbuf + sizeof (outbuf))
132 if (write (1, outbuf, sizeof (outbuf)) != sizeof (outbuf))
133 return;
134 p = outbuf;
138 if (p != outbuf)
139 if (write (1, outbuf, p - outbuf) < (p - outbuf))
140 return;
143 /* --------------------------------------------------------------------------------------------- */
145 static void __attribute__ ((noreturn)) die (void)
147 unsigned char zero = 0;
148 ssize_t ret;
149 ret = write (1, &zero, 1);
150 (void) ret;
151 exit (3);
154 /* --------------------------------------------------------------------------------------------- */
155 /*** public functions ****************************************************************************/
156 /* --------------------------------------------------------------------------------------------- */
159 main (int argc, char **argv)
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;
169 close (STDERR_FILENO);
171 if (argc != 2)
172 die ();
174 tty_name = argv[1];
175 if (strnlen (tty_name, 15) == 15 || strncmp (tty_name, "/dev/", 5))
176 die ();
178 setsid ();
179 uid = getuid ();
180 euid = geteuid ();
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 ();
201 switch (tty_name[5])
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 ();
217 snprintf (console_name, sizeof (console_name), p, console_minor);
218 if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
219 die ();
221 if (seteuid (euid) < 0)
222 die ();
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 ();
231 if (seteuid (uid) < 0)
232 die ();
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 ();
239 buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
240 buffer = calloc (buffer_size, 1);
241 if (buffer == NULL)
242 die ();
244 if (write (1, &console_flag, 1) != 1)
245 die ();
247 while (console_flag && read (0, &action, 1) == 1)
249 switch (action)
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;
279 if (write (1, &console_flag, 1) != 1)
280 die ();
283 exit (0);
286 /* --------------------------------------------------------------------------------------------- */