Hacer que funcione xconsole (v.02)

TÍTULO:			Hacer que funcione xconsole (v.02)
VERSIÓN LFS:	Cualquiera
AUTOR:			Matthias S. Benkmann  <m.s.b@gmx.net>
TRADUCTOR:	Proyecto LFS-ES

SINOPSIS:
	xconsole me dió unos cuantos problemas. No funcionaba si se lanzaba
     	como un usuario normal, incluso haciendo setuid root. Y si leía la
	entrada de datos desde /dev/console (por defecto) interceptaba
	pulsaciones de teclas destinadas a ventanas xterm. Si tienes los
	mismos problemas, lee este receta. 


RECETA:

Inspeccioné un sistema SuSE y ví que ellos no hacen que xconsole lea los datos
de /dev/console. En su lugar, estaba configurado para leerlos de un FIFO 
/dev/xconsole al que pasaba los datos syslogd. Esto tiene la ventaja de que se
puede emplear syslog.conf para controlar exactamente qué mensajes quieres que
visualice xconsole.

Para configurarlo de esta manera, haz lo siguiente:

1. Crea el FIFO /dev/xconsole

     mkfifo /dev/xconsole
     chmod 640 /dev/xconsole
     chown root.tty /dev/xconsole

2. Configura syslogd para que escriba mensajes en /dev/xconsole añadiendo una
   línea del tipo siguiente a /etc/syslog.conf
   
      *.err;auth,authpriv.none		|/dev/xconsole

   Esto escribirá todos los mensajes con prioridad igual a err o superior en 
   /dev/xconsole, con la excepción de los mensajes de auth y authpriv (ya que
   estos pueden contener datos secretos).


Ahora puedes lanzar xconsole haciendo

     xconsole -file /dev/xconsole
     
y empleará el FIFO.

Lo anterior corrige esos problemas extraños con la captura no deseada de
pulsaciones de teclas. Aún queda el problema de que xconsole solo funciona
cuando lo lanza root. Ni haciendo setuid root ni setgid tty se corrige.
Mirando en el código fuente se puede ver porqué. xconsole usa la función 
access() para comprobar si puede acceder al fichero del que debe leer.
Por desgracia, access() sólo comprueba el uid auténtico y no el uid que el
proceso está empleando, que es lo único que cambia setuid. La razón para este
comportamiento consiste probablemente en que de otro modo podría emplearse un
xconsole con setuid root para espiar a otros usuarios. Para soslayar este
problema, escribí el siguiente programa:

------------------- principio de startxconsole.c ----------------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int main()
{
  int i, fd;
  struct passwd* pw;
  char* xauth;
  char* env[32];
  
  /* haz XAUTHORITY el del directorio home auténtico del usuario en lugar de
  dejarle especificar cualquier otra cosa */
  pw=getpwuid(getuid());
  if (pw==NULL) {perror("getpwuid"); return 1;};
  xauth=malloc(strlen(pw->pw_dir)+128);
  if (xauth==NULL) {perror("malloc"); return 1;};
  strcpy(xauth,"XAUTHORITY=");
  strcat(xauth,pw->pw_dir);
  strcat(xauth,"/.Xauthority");
  
  /* limpia el entorno (excepto para el path estándar, DISPLAY y XAUTHORITY) */
  env[0]="PATH=/bin:/usr/bin";
  env[1]="DISPLAY=:0.0";
  env[2]=xauth;
  env[3]=NULL;
  
  /* inicializa los administradores de señales a sus defectos */
  for (i = 1; i < NSIG; i++) signal (i, SIG_DFL); 
  
  /* stdin="/dev/null" (para evitar que los usuarios pasen datos no seguros) */
  close(0);
  if ( open("/dev/null",O_RDONLY) != 0 )
  {
    fprintf(stderr,"Unable to redirect /dev/null -> stdin\n");
    return 1;
  };
  
  /* cierra todos los fds excepto stdin,stdout,stderr (siendo paranoico) */
  i = getdtablesize ();
  for (fd = 3; fd < i; ++fd) close (fd);
  
  /* hacer los ids auténticos iguales a los ids que se están usando */
  setreuid(geteuid(),geteuid());
  setregid(getegid(),getegid()); 
  execle("/usr/X11R6/bin/xconsole","xconsole","-file","/dev/xconsole",
  "-notify","-verbose","-geometry","-0-0",NULL,env);
  perror("/usr/X11R6/bin/xconsole");
  return 1;
};
------------------- fin de startxconsole.c ----------------------------------

Compílalo con  

   gcc -W -Wall -O2 -o startxconsole startxconsole.c
 

startxconsole hace que setuid y setgid funcionen como se espera. Así que se
puede hacer lo siguiente:

   chgrp tty startxconsole
   chmod g+s startxconsole
   
y podrás lanzar startxconsole como un usuario normal (probablemente lo puedas
poner en xinitrc y arrancará xconsole).

Hay que hacer notar que, por motivos de seguridad, startxconsole incluye una
órden xconsole fija. De esta manera los usuarios no pueden pasarle sus propios
argumentos a xconsole. Si se les dejara hacerlo, podrían indicar el dispositivo
tty de otros usuarios como -file y espiarlos. Además, startxconsole siempre
coloca la ubicación del fichero .Xauthority (mediante la variable de entorno 
XAUTHORITY) apuntando a <home>/.Xauthority donde <home> es el directorio home
del usuario que lo lanza. Esto asegura que un usuario no puede utilizar 
XAUTHORITY para lograr un acceso de lectura a un fichero que no le pertenece. 
DISPLAY es siempre :0.0.
Las demás variables de entorno se borran (o mejor dicho, no se pasan a
xconsole), los administradores de señales se colocan en sus valores por
defecto, stdin se hace igual a /dev/null y se cierran los demás descriptores 
de fichero con la excepción de stdout y stderr. Esto debería eliminar
cualquier posibilidad de que un usuario le pase datos no seguros a xconsole.