mirror of
https://github.com/MidnightCommander/mc
synced 2025-01-03 18:14:25 +03:00
* cons.saver.c: Partly rewritten cons.saver.c.
This commit is contained in:
parent
2d48b95137
commit
919aa12d7d
@ -1,3 +1,7 @@
|
|||||||
|
2004-09-10 Jakub Jelinek <jakub@redhat.com>
|
||||||
|
|
||||||
|
* cons.saver.c: Partly rewritten cons.saver.c.
|
||||||
|
|
||||||
2004-09-28 Andrew V. Samoilov <sav@bcs.zp.ua>
|
2004-09-28 Andrew V. Samoilov <sav@bcs.zp.ua>
|
||||||
|
|
||||||
* util.c (convert_controls): Fix possible buffer overflow
|
* util.c (convert_controls): Fix possible buffer overflow
|
||||||
@ -224,7 +228,7 @@
|
|||||||
|
|
||||||
From Petr Hadraba <hadrabap@volny.cz>
|
From Petr Hadraba <hadrabap@volny.cz>
|
||||||
|
|
||||||
2004-09-10 Jakub Jelinek <jakub@redhat.com>
|
|
||||||
|
|
||||||
* view.c (get_byte): Fix avoid dying if file is too large
|
* view.c (get_byte): Fix avoid dying if file is too large
|
||||||
to fit into memory.
|
to fit into memory.
|
||||||
|
431
src/cons.saver.c
431
src/cons.saver.c
@ -1,9 +1,13 @@
|
|||||||
#ifdef __linux__
|
/* This program should be setuid vcsa and /dev/vcsa* should be
|
||||||
|
owned by the same user too.
|
||||||
|
Partly rewritten by Jakub Jelinek <jakub@redhat.com>. */
|
||||||
|
|
||||||
/* General purpose Linux console screen save/restore server
|
/* General purpose Linux console screen save/restore server
|
||||||
Copyright (C) 1994 Janne Kukonlehto <jtklehto@stekt.oulu.fi>
|
Copyright (C) 1994 Janne Kukonlehto <jtklehto@stekt.oulu.fi>
|
||||||
Original idea from Unix Interactive Tools version 3.2b (tty.c)
|
Original idea from Unix Interactive Tools version 3.2b (tty.c)
|
||||||
This code requires root privileges.
|
This code requires root privileges.
|
||||||
|
You may want to make the cons.saver setuid root.
|
||||||
|
The code should be safe even if it is setuid but who knows?
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,300 +23,201 @@
|
|||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||||
|
|
||||||
/* cons.saver is run by MC to save and restore screen contents on Linux
|
/* This code does _not_ need to be setuid root. However, it needs
|
||||||
virtual console. This is done by using file /dev/vcsaN or /dev/vcc/aN,
|
read/write access to /dev/vcsa* (which is priviledged
|
||||||
where N is the number of the virtual console.
|
operation). You should create user vcsa, make cons.saver setuid
|
||||||
In a properly set up system, /dev/vcsaN should become accessible
|
user vcsa, and make all vcsa's owned by user vcsa.
|
||||||
by the user when the user logs in on the corresponding console
|
|
||||||
/dev/ttyN or /dev/vc/N. In this case, cons.saver doesn't need to be
|
|
||||||
suid root. However, if /dev/vcsaN is not accessible by the user,
|
|
||||||
cons.saver can be made setuid root - this program is designed to be
|
|
||||||
safe even in this case. */
|
|
||||||
|
|
||||||
#include <config.h>
|
Seeing other peoples consoles is bad thing, but believe me, full
|
||||||
#include <sys/types.h>
|
root is even worse. */
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
#ifndef _GNU_SOURCE
|
||||||
#include <fcntl.h>
|
#define _GNU_SOURCE
|
||||||
#include <errno.h>
|
|
||||||
#ifdef HAVE_UNISTD_H
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h> /* For isdigit() */
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
#define LINUX_CONS_SAVER_C
|
#define LINUX_CONS_SAVER_C
|
||||||
#include "cons.saver.h"
|
#include "cons.saver.h"
|
||||||
|
|
||||||
#define cmd_input 0
|
void
|
||||||
#define cmd_output 1
|
send_contents (char *buffer, unsigned int columns, unsigned int rows)
|
||||||
|
|
||||||
static int console_minor = 0;
|
|
||||||
static char *buffer = NULL;
|
|
||||||
static int buffer_size = 0;
|
|
||||||
static int columns, rows;
|
|
||||||
static int vcs_fd;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get window size for the given terminal.
|
|
||||||
* Return 0 for success, -1 otherwise.
|
|
||||||
*/
|
|
||||||
static int tty_getsize (int console_fd)
|
|
||||||
{
|
{
|
||||||
struct winsize winsz;
|
unsigned char begin_line = 0, end_line = 0;
|
||||||
|
unsigned int lastline, index, x;
|
||||||
|
unsigned char message, outbuf[1024], *p;
|
||||||
|
unsigned short bytes;
|
||||||
|
|
||||||
winsz.ws_col = winsz.ws_row = 0;
|
index = 2 * rows * columns;
|
||||||
ioctl (console_fd, TIOCGWINSZ, &winsz);
|
for (lastline = rows; lastline > 0; lastline--)
|
||||||
if (winsz.ws_col && winsz.ws_row) {
|
for (x = 0; x < columns; x++)
|
||||||
columns = winsz.ws_col;
|
{
|
||||||
rows = winsz.ws_row;
|
index -= 2;
|
||||||
return 0;
|
if (buffer [index] != ' ')
|
||||||
}
|
goto out;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
|
||||||
return -1;
|
message = CONSOLE_CONTENTS;
|
||||||
}
|
write (1, &message, 1);
|
||||||
|
|
||||||
/*
|
read (0, &begin_line, 1);
|
||||||
* Do various checks to make sure that the supplied filename is
|
read (0, &end_line, 1);
|
||||||
* a suitable tty device. If check_console is set, check that we
|
if (begin_line > lastline)
|
||||||
* are dealing with a Linux virtual console.
|
begin_line = lastline;
|
||||||
* Return 0 for success, -1 otherwise.
|
if (end_line > lastline)
|
||||||
*/
|
end_line = lastline;
|
||||||
static int check_file (char *filename, int check_console)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
struct stat stat_buf;
|
|
||||||
|
|
||||||
/* Avoiding race conditions: use of fstat makes sure that
|
index = (end_line - begin_line) * columns;
|
||||||
both 'open' and 'stat' operate on the same file */
|
bytes = index;
|
||||||
|
if (index != bytes)
|
||||||
|
bytes = 0;
|
||||||
|
write (1, &bytes, 2);
|
||||||
|
if (! bytes)
|
||||||
|
return;
|
||||||
|
|
||||||
fd = open (filename, O_RDWR);
|
p = outbuf;
|
||||||
if (fd == -1)
|
for (index = 2 * begin_line * columns;
|
||||||
return -1;
|
index < 2 * end_line * columns;
|
||||||
|
index += 2)
|
||||||
do {
|
|
||||||
if (fstat (fd, &stat_buf) == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Must be character device */
|
|
||||||
if (!S_ISCHR (stat_buf.st_mode)){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_console){
|
|
||||||
/* Major number must be 4 */
|
|
||||||
if ((stat_buf.st_rdev & 0xff00) != 0x0400){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Minor number must be between 1 and 63 */
|
|
||||||
console_minor = (int) (stat_buf.st_rdev & 0x00ff);
|
|
||||||
if (console_minor < 1 || console_minor > 63){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Must be owned by the user */
|
|
||||||
if (stat_buf.st_uid != getuid ()){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Everything seems to be okay */
|
|
||||||
return fd;
|
|
||||||
} while (0);
|
|
||||||
|
|
||||||
close (fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if the supplied filename is a Linux virtual console.
|
|
||||||
* Return 0 if successful, -1 otherwise.
|
|
||||||
* Since the tty name is supplied by the user and cons.saver can be
|
|
||||||
* a setuid program, many checks have to be done to prevent possible
|
|
||||||
* security compromise.
|
|
||||||
*/
|
|
||||||
static int detect_console (char *tty_name)
|
|
||||||
{
|
|
||||||
char console_name [16];
|
|
||||||
static char vcs_name [16];
|
|
||||||
int console_fd;
|
|
||||||
|
|
||||||
/* Must be console */
|
|
||||||
console_fd = check_file (tty_name, 1);
|
|
||||||
if (console_fd == -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (tty_getsize (console_fd) == -1) {
|
|
||||||
close (console_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
close (console_fd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Only allow /dev/ttyMINOR and /dev/vc/MINOR where MINOR is the minor
|
|
||||||
* device number of the console, set in check_file()
|
|
||||||
*/
|
|
||||||
switch (tty_name[5])
|
|
||||||
{
|
{
|
||||||
case 'v':
|
*p++ = buffer [index];
|
||||||
snprintf (console_name, sizeof (console_name), "/dev/vc/%d",
|
if (p == outbuf + sizeof (outbuf))
|
||||||
console_minor);
|
{
|
||||||
if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
|
write (1, outbuf, sizeof (outbuf));
|
||||||
return -1;
|
p = outbuf;
|
||||||
break;
|
}
|
||||||
case 't':
|
|
||||||
snprintf (console_name, sizeof (console_name), "/dev/tty%d",
|
|
||||||
console_minor);
|
|
||||||
if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf (vcs_name, sizeof (vcs_name), "/dev/vcsa%d", console_minor);
|
if (p != outbuf)
|
||||||
vcs_fd = check_file (vcs_name, 0);
|
write (1, outbuf, p - outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
/* Try devfs name */
|
void __attribute__ ((noreturn))
|
||||||
if (vcs_fd == -1) {
|
die (void)
|
||||||
snprintf (vcs_name, sizeof (vcs_name), "/dev/vcc/a%d", console_minor);
|
{
|
||||||
vcs_fd = check_file (vcs_name, 0);
|
unsigned char zero = 0;
|
||||||
|
write (1, &zero, 1);
|
||||||
|
exit (3);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
unsigned char action = 0, console_flag = 3;
|
||||||
|
int console_fd, vcsa_fd, console_minor, buffer_size;
|
||||||
|
struct stat st;
|
||||||
|
uid_t uid, euid;
|
||||||
|
char *buffer, *tty_name, console_name [16], vcsa_name [16], *p, *q;
|
||||||
|
struct winsize winsz;
|
||||||
|
|
||||||
|
close (2);
|
||||||
|
|
||||||
|
if (argc != 2)
|
||||||
|
die ();
|
||||||
|
|
||||||
|
tty_name = argv [1];
|
||||||
|
if (strnlen (tty_name, 15) == 15
|
||||||
|
|| strncmp (tty_name, "/dev/", 5))
|
||||||
|
die ();
|
||||||
|
|
||||||
|
setsid ();
|
||||||
|
uid = getuid ();
|
||||||
|
euid = geteuid ();
|
||||||
|
|
||||||
|
if (seteuid (uid) < 0)
|
||||||
|
die ();
|
||||||
|
console_fd = open (tty_name, O_RDONLY);
|
||||||
|
if (console_fd < 0)
|
||||||
|
die ();
|
||||||
|
if (fstat (console_fd, &st) < 0 || ! S_ISCHR (st.st_mode))
|
||||||
|
die ();
|
||||||
|
if ((st.st_rdev & 0xff00) != 0x0400)
|
||||||
|
die ();
|
||||||
|
console_minor = (int) (st.st_rdev & 0x00ff);
|
||||||
|
if (console_minor < 1 || console_minor > 63)
|
||||||
|
die ();
|
||||||
|
if (st.st_uid != uid)
|
||||||
|
die ();
|
||||||
|
|
||||||
|
switch (tty_name [5])
|
||||||
|
{
|
||||||
|
/* devfs */
|
||||||
|
case 'v': p = "/dev/vc/%d"; q = "/dev/vcc/a%d"; break;
|
||||||
|
/* /dev/ttyN */
|
||||||
|
case 't': p = "/dev/tty%d"; q = "/dev/vcsa%d"; break;
|
||||||
|
default: die (); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vcs_fd == -1)
|
snprintf (console_name, sizeof (console_name), p, console_minor);
|
||||||
return -1;
|
if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
|
||||||
|
die ();
|
||||||
|
|
||||||
return 0;
|
if (seteuid (euid) < 0)
|
||||||
}
|
die ();
|
||||||
|
|
||||||
static void save_console (void)
|
snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
|
||||||
{
|
vcsa_fd = open (vcsa_name, O_RDWR);
|
||||||
lseek (vcs_fd, 0, SEEK_SET);
|
if (vcsa_fd < 0)
|
||||||
read (vcs_fd, buffer, buffer_size);
|
die ();
|
||||||
}
|
if (fstat (vcsa_fd, &st) < 0 || ! S_ISCHR (st.st_mode))
|
||||||
|
die ();
|
||||||
|
|
||||||
static void restore_console (void)
|
if (seteuid (uid) < 0)
|
||||||
{
|
die ();
|
||||||
lseek (vcs_fd, 0, SEEK_SET);
|
|
||||||
write (vcs_fd, buffer, buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_contents (void)
|
winsz.ws_col = winsz.ws_row = 0;
|
||||||
{
|
if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0
|
||||||
unsigned char begin_line=0, end_line=0;
|
|| winsz.ws_col <= 0 || winsz.ws_row <= 0
|
||||||
int index;
|
|| winsz.ws_col >= 256 || winsz.ws_row >= 256)
|
||||||
unsigned char message;
|
die ();
|
||||||
unsigned short bytes;
|
|
||||||
const int bytes_per_char = 2;
|
|
||||||
|
|
||||||
/* Inform the invoker that we can handle this command */
|
buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
|
||||||
message = CONSOLE_CONTENTS;
|
buffer = calloc (buffer_size, 1);
|
||||||
write (cmd_output, &message, 1);
|
if (buffer == NULL)
|
||||||
|
die ();
|
||||||
|
|
||||||
/* Read the range of lines wanted */
|
write (1, &console_flag, 1);
|
||||||
read (cmd_input, &begin_line, 1);
|
|
||||||
read (cmd_input, &end_line, 1);
|
|
||||||
if (begin_line > rows)
|
|
||||||
begin_line = rows;
|
|
||||||
if (end_line > rows)
|
|
||||||
end_line = rows;
|
|
||||||
|
|
||||||
/* Tell the invoker how many bytes it will be */
|
while (console_flag && read (0, &action, 1) == 1)
|
||||||
bytes = (end_line - begin_line) * columns;
|
{
|
||||||
write (cmd_output, &bytes, 2);
|
switch (action)
|
||||||
|
{
|
||||||
/* Send the contents */
|
|
||||||
for (index = (2 + begin_line * columns) * bytes_per_char;
|
|
||||||
index < (2 + end_line * columns) * bytes_per_char;
|
|
||||||
index += bytes_per_char)
|
|
||||||
write (cmd_output, buffer + index, 1);
|
|
||||||
|
|
||||||
/* All done */
|
|
||||||
}
|
|
||||||
|
|
||||||
int main (int argc, char **argv)
|
|
||||||
{
|
|
||||||
unsigned char action = 0;
|
|
||||||
int stderr_fd;
|
|
||||||
|
|
||||||
/* 0 - not a console, 3 - supported Linux console */
|
|
||||||
signed char console_flag = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Make sure stderr points to a valid place
|
|
||||||
*/
|
|
||||||
close (2);
|
|
||||||
stderr_fd = open ("/dev/tty", O_RDWR);
|
|
||||||
|
|
||||||
/* This may well happen if program is running non-root */
|
|
||||||
if (stderr_fd == -1)
|
|
||||||
stderr_fd = open ("/dev/null", O_RDWR);
|
|
||||||
|
|
||||||
if (stderr_fd == -1)
|
|
||||||
exit (1);
|
|
||||||
|
|
||||||
if (stderr_fd != 2)
|
|
||||||
while (dup2 (stderr_fd, 2) == -1 && errno == EINTR)
|
|
||||||
;
|
|
||||||
|
|
||||||
if (argc != 2){
|
|
||||||
/* Wrong number of arguments */
|
|
||||||
|
|
||||||
write (cmd_output, &console_flag, 1);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lose the control terminal */
|
|
||||||
setsid ();
|
|
||||||
|
|
||||||
/* Check that the argument is a Linux console */
|
|
||||||
if (detect_console (argv [1]) == -1) {
|
|
||||||
/* Not a console -> no need for privileges */
|
|
||||||
setuid (getuid ());
|
|
||||||
} else {
|
|
||||||
console_flag = 3;
|
|
||||||
/* Allocate buffer for screen image */
|
|
||||||
buffer_size = 4 + 2 * columns * rows;
|
|
||||||
buffer = (char*) malloc (buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inform the invoker about the result of the tests */
|
|
||||||
write (cmd_output, &console_flag, 1);
|
|
||||||
|
|
||||||
/* Read commands from the invoker */
|
|
||||||
while (console_flag && read (cmd_input, &action, 1)){
|
|
||||||
/* Handle command */
|
|
||||||
switch (action){
|
|
||||||
case CONSOLE_DONE:
|
case CONSOLE_DONE:
|
||||||
console_flag = 0;
|
console_flag = 0;
|
||||||
continue; /* Break while loop instead of switch clause */
|
continue;
|
||||||
case CONSOLE_SAVE:
|
case CONSOLE_SAVE:
|
||||||
save_console ();
|
if (seteuid (euid) < 0
|
||||||
break;
|
|| lseek (vcsa_fd, 0, 0) != 0
|
||||||
|
|| fstat (console_fd, &st) < 0 || st.st_uid != uid
|
||||||
|
|| read (vcsa_fd, buffer, buffer_size) != buffer_size
|
||||||
|
|| fstat (console_fd, &st) < 0 || st.st_uid != uid)
|
||||||
|
memset (buffer, 0, buffer_size);
|
||||||
|
if (seteuid (uid) < 0)
|
||||||
|
die ();
|
||||||
|
break;
|
||||||
case CONSOLE_RESTORE:
|
case CONSOLE_RESTORE:
|
||||||
restore_console ();
|
if (seteuid (euid) >= 0
|
||||||
break;
|
&& lseek (vcsa_fd, 0, 0) == 0
|
||||||
|
&& fstat (console_fd, &st) >= 0 && st.st_uid == uid)
|
||||||
|
write (vcsa_fd, buffer, buffer_size);
|
||||||
|
if (seteuid (uid) < 0)
|
||||||
|
die ();
|
||||||
|
break;
|
||||||
case CONSOLE_CONTENTS:
|
case CONSOLE_CONTENTS:
|
||||||
send_contents ();
|
send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
|
||||||
break;
|
break;
|
||||||
} /* switch (action) */
|
}
|
||||||
|
|
||||||
/* Inform the invoker that command has been handled */
|
|
||||||
write (cmd_output, &console_flag, 1);
|
|
||||||
} /* while (read ...) */
|
|
||||||
|
|
||||||
if (buffer)
|
write (1, &console_flag, 1);
|
||||||
free (buffer);
|
}
|
||||||
return 0;
|
|
||||||
|
exit (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#error "The Linux console screen saver works only on Linux"
|
|
||||||
|
|
||||||
#endif /* __linux__ */
|
|
||||||
|
Loading…
Reference in New Issue
Block a user