dea413b1f06fd3e860ac8e608bcfc5b9afa1a3cc
[midnight-commander.git] / src / consaver / cons.saver.c
blobdea413b1f06fd3e860ac8e608bcfc5b9afa1a3cc
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, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
15 2006, 2007, 2011
16 The Free Software Foundation, Inc.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /* This code does _not_ need to be setuid root. However, it needs
35 read/write access to /dev/vcsa* (which is priviledged
36 operation). You should create user vcsa, make cons.saver setuid
37 user vcsa, and make all vcsa's owned by user vcsa.
39 Seeing other peoples consoles is bad thing, but believe me, full
40 root is even worse. */
42 /** \file cons.saver.c
43 * \brief Source: general purpose Linux console screen save/restore server
45 * This code does _not_ need to be setuid root. However, it needs
46 * read/write access to /dev/vcsa* (which is priviledged
47 * operation). You should create user vcsa, make cons.saver setuid
48 * user vcsa, and make all vcsa's owned by user vcsa.
49 * Seeing other peoples consoles is bad thing, but believe me, full
50 * root is even worse.
53 #include <config.h>
55 #ifndef _GNU_SOURCE
56 #define _GNU_SOURCE
57 #endif
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <string.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #ifdef HAVE_SYS_IOCTL_H
66 #include <sys/ioctl.h>
67 #endif
68 #include <fcntl.h>
69 #include <termios.h>
70 #include <unistd.h>
72 #define LINUX_CONS_SAVER_C
73 #include "cons.saver.h"
75 /*** global variables ****************************************************************************/
77 /*** file scope macro definitions ****************************************************************/
79 /*** file scope type declarations ****************************************************************/
81 /*** file scope variables ************************************************************************/
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
86 static void
87 send_contents (char *buffer, unsigned int columns, unsigned int rows)
89 unsigned char begin_line = 0, end_line = 0;
90 unsigned int lastline, lc_index, x;
91 unsigned char message, outbuf[1024], *p;
92 unsigned short bytes;
94 lc_index = 2 * rows * columns;
95 for (lastline = rows; lastline > 0; lastline--)
96 for (x = 0; x < columns; x++)
98 lc_index -= 2;
99 if (buffer[lc_index] != ' ')
100 goto out;
102 out:
104 message = CONSOLE_CONTENTS;
105 if (write (1, &message, 1) != 1)
106 return;
107 if (read (0, &begin_line, 1) != 1)
108 return;
109 if (read (0, &end_line, 1) != 1)
110 return;
111 if (begin_line > lastline)
112 begin_line = lastline;
113 if (end_line > lastline)
114 end_line = lastline;
116 lc_index = (end_line - begin_line) * columns;
117 bytes = lc_index;
119 if (write (1, &bytes, 2) != 2)
120 return;
121 if (bytes == 0)
122 return;
124 p = outbuf;
125 for (lc_index = 2 * begin_line * columns; lc_index < 2 * end_line * columns; lc_index += 2)
127 *p++ = buffer[lc_index];
128 if (p == outbuf + sizeof (outbuf))
130 if (write (1, outbuf, sizeof (outbuf)) != sizeof (outbuf))
131 return;
132 p = outbuf;
136 if (p != outbuf)
137 if (write (1, outbuf, p - outbuf) < (p - outbuf))
138 return;
141 /* --------------------------------------------------------------------------------------------- */
143 static void __attribute__ ((noreturn)) die (void)
145 unsigned char zero = 0;
146 ssize_t ret;
147 ret = write (1, &zero, 1);
148 exit (3);
151 /* --------------------------------------------------------------------------------------------- */
152 /*** public functions ****************************************************************************/
153 /* --------------------------------------------------------------------------------------------- */
156 main (int argc, char **argv)
158 unsigned char action = 0, console_flag = 3;
159 int console_fd, vcsa_fd, console_minor, buffer_size;
160 struct stat st;
161 uid_t uid, euid;
162 char *buffer, *tty_name, console_name[16], vcsa_name[16];
163 const char *p, *q;
164 struct winsize winsz;
166 close (2);
168 if (argc != 2)
169 die ();
171 tty_name = argv[1];
172 if (strnlen (tty_name, 15) == 15 || strncmp (tty_name, "/dev/", 5))
173 die ();
175 setsid ();
176 uid = getuid ();
177 euid = geteuid ();
179 if (seteuid (uid) < 0)
180 die ();
181 console_fd = open (tty_name, O_RDONLY);
182 if (console_fd < 0)
183 die ();
184 if (fstat (console_fd, &st) < 0 || !S_ISCHR (st.st_mode))
185 die ();
186 if ((st.st_rdev & 0xff00) != 0x0400)
187 die ();
188 console_minor = (int) (st.st_rdev & 0x00ff);
189 if (console_minor < 1 || console_minor > 63)
190 die ();
191 if (st.st_uid != uid)
192 die ();
194 switch (tty_name[5])
196 /* devfs */
197 case 'v':
198 p = "/dev/vc/%d";
199 q = "/dev/vcc/a%d";
200 break;
201 /* /dev/ttyN */
202 case 't':
203 p = "/dev/tty%d";
204 q = "/dev/vcsa%d";
205 break;
206 default:
207 die ();
208 break;
211 snprintf (console_name, sizeof (console_name), p, console_minor);
212 if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
213 die ();
215 if (seteuid (euid) < 0)
216 die ();
218 snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
219 vcsa_fd = open (vcsa_name, O_RDWR);
220 if (vcsa_fd < 0)
221 die ();
222 if (fstat (vcsa_fd, &st) < 0 || !S_ISCHR (st.st_mode))
223 die ();
225 if (seteuid (uid) < 0)
226 die ();
228 winsz.ws_col = winsz.ws_row = 0;
229 if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0
230 || winsz.ws_col <= 0 || winsz.ws_row <= 0 || winsz.ws_col >= 256 || winsz.ws_row >= 256)
231 die ();
233 buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
234 buffer = calloc (buffer_size, 1);
235 if (buffer == NULL)
236 die ();
238 if (write (1, &console_flag, 1) != 1)
239 die ();
241 while (console_flag && read (0, &action, 1) == 1)
243 switch (action)
245 case CONSOLE_DONE:
246 console_flag = 0;
247 continue;
248 case CONSOLE_SAVE:
249 if (seteuid (euid) < 0
250 || lseek (vcsa_fd, 0, 0) != 0
251 || fstat (console_fd, &st) < 0 || st.st_uid != uid
252 || read (vcsa_fd, buffer, buffer_size) != buffer_size
253 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
254 memset (buffer, 0, buffer_size);
255 if (seteuid (uid) < 0)
256 die ();
257 break;
258 case CONSOLE_RESTORE:
259 if (seteuid (euid) >= 0
260 && lseek (vcsa_fd, 0, 0) == 0 && fstat (console_fd, &st) >= 0 && st.st_uid == uid)
261 if (write (vcsa_fd, buffer, buffer_size) != buffer_size)
262 die ();
263 if (seteuid (uid) < 0)
264 die ();
265 break;
266 case CONSOLE_CONTENTS:
267 send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
268 break;
271 if (write (1, &console_flag, 1) != 1)
272 die ();
275 exit (0);
278 /* --------------------------------------------------------------------------------------------- */