2011-03-10 12 views
8

He encontrado que en Linux, al hacer mi propia llamada al rt_sigqueue syscall, puedo poner lo que quiera en los campos si_uid y si_pid y la llamada tiene éxito y felizmente entrega los valores incorrectos. Naturalmente, las restricciones en el envío de señales proporcionan cierta protección contra este tipo de falsificación, pero me preocupa que pueda ser peligroso confiar en esta información. ¿Hay alguna buena documentación sobre el tema que pueda leer? ¿Por qué Linux permite el comportamiento obviamente incorrecto de dejar que el que llama especifique los parámetros siginfo en lugar de generarlos en el espacio del kernel? Parece absurdo, especialmente porque pueden requerirse llamadas a los sistemas adicionales (y, por lo tanto, el costo de rendimiento) a fin de obtener el uid/gid en el espacio de usuario.¿Los datos en siginfo son confiables?

Editar: Basado en mi lectura de POSIX (énfasis añadido por mí):

Si si_code es SI_USER o SI_QUEUE, [XSI] o cualquier valor menor o igual a 0, entonces la señal fue generado por un proceso y si_pid y si_uid se establecerán en la identificación del proceso y la identificación del usuario real del remitente, respectivamente.

Creo que este comportamiento de Linux es no conforme y es un error grave.

+0

Parece que no le gustó SO. Tal vez deberías probar la lista de distribución del kernel de Linux: http://www.kernel.org/pub/linux/docs/lkml/ –

Respuesta

5

Esa sección de la página POSIX se cita también enumera lo si-code medios, y aquí es el significado:

SI_QUEUE 
    The signal was sent by the sigqueue() function. 

Esa sección pasa a decir:

Si la señal no fue generada por uno de las funciones o eventos enumerados anteriormente , si_code se debe establecer a uno de los valores específicos de la señal descritos en XBD, oa valor definido en la implementación que es no igual a ninguno de los valores definidos arriba.

No se infringe nada si solo la función sigqueue() usa SI_QUEUE. Su escenario involucra código que no sea la función sigqueue() usando SI_QUEUE La pregunta es si POSIX visualiza un sistema operativo que impone que solo una función de biblioteca especificada (a diferencia de alguna función que no es una función de biblioteca definida POSIX) tenga permiso para realizar una llamada al sistema con ciertas características Creo que la respuesta es no".

EDITAR partir de 2011-03-26, 14:00 PST:

Esta edición es en respuesta a comentario de hace ocho horas R .. 's, ya que la página no dejaría me deja un comentario suficientemente voluminoso:

Creo que básicamente tienes razón. Pero o bien un sistema cumple con POSIX o no lo está. Si una función que no es de la biblioteca realiza un syscall que da como resultado una combinación no compatible de uid, pid y 'si_code', entonces la segunda declaración que cité deja en claro que la llamada en sí misma no es compatible. Uno puede interpretar esto de dos maneras. Una forma es: "Si un usuario rompe esta regla, entonces hace que el sistema no cumpla". Pero tienes razón, creo que es una tontería. ¿De qué sirve un sistema cuando cualquier usuario no privilegiado puede hacerlo no conforme?La solución, como yo lo veo, es de alguna manera hacer que el sistema sepa que no es la biblioteca 'sigqueue()' que hace la llamada al sistema, entonces el núcleo mismo debe establecer 'si_code' en algo que no sea 'SI_QUEUE', y dejar el uid y pid como los configuraste. En mi opinión, deberías plantear esto a la gente del kernel. Sin embargo, pueden tener dificultades; No conozco ninguna forma segura para que ellos detecten si un syscall está hecho por una función de biblioteca particular, viendo cómo funciona la biblioteca. casi por definición, son simplemente envoltorios de conveniencia alrededor de los syscalls. Y esa puede ser la posición que toman, y sé que será una desilusión.

(voluminoso) EDITAR como de 2011-03-26, 18:00 PST:

de nuevo debido a las limitaciones en longitud comentario.

Esto es en respuesta a R .. del comentario de hace una hora aproximadamente.

Soy un poco nuevo en el tema de syscall, así que por favor tengan paciencia conmigo.

Por "kernel sysqueue syscall", ¿te refieres a la llamada `__NR_rt_sigqueueinfo '? Esa es la única que he encontrado cuando hice esto:

grep -Ri 'NR.*queue' /usr/include 

Si ese es el caso, creo que no soy la comprensión de su punto original. El kernel permitirá que (no root) use SI-QUEUE con un pid y un uid falsificados sin error. Si tengo el lado emisor codificado así:

#include <sys/syscall.h> 
#include <sys/types.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

int main(int argc, 
     char **argv 
     ) 
{ 
    long john_silver; 

    siginfo_t my_siginfo; 

    if(argc!=2) 
    { 
    fprintf(stderr,"missing pid argument\n"); 

    exit(1); 
    } 

    john_silver=strtol(argv[1],NULL,0); 

    if(kill(john_silver,SIGUSR1)) 
    { 
    fprintf(stderr,"kill() fail\n"); 

    exit(1); 
    } 

    sleep(1); 

    my_siginfo.si_signo=SIGUSR1; 
    my_siginfo.si_code=SI_QUEUE; 
    my_siginfo.si_pid=getpid(); 
    my_siginfo.si_uid=getuid(); 
    my_siginfo.si_value.sival_int=41; 

    if(syscall(__NR_rt_sigqueueinfo,john_silver,SIGUSR1,&my_siginfo)) 
    { 
    perror("syscall()"); 

    exit(1); 
    } 

    sleep(1); 

    my_siginfo.si_signo=SIGUSR2; 
    my_siginfo.si_code=SI_QUEUE; 
    my_siginfo.si_pid=getpid()+1; 
    my_siginfo.si_uid=getuid()+1; 
    my_siginfo.si_value.sival_int=42; 

    if(syscall(__NR_rt_sigqueueinfo,john_silver,SIGUSR2,&my_siginfo)) 
    { 
    perror("syscall()"); 

    exit(1); 
    } 

    return 0; 

} /* main() */ 

y el lado receptor codificado así:

#include <sys/types.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

int signaled_flag=0; 

siginfo_t received_information; 

void 
my_handler(int  signal_number, 
      siginfo_t *signal_information, 
      void  *we_ignore_this 
     ) 
{ 
    memmove(&received_information, 
      signal_information, 
      sizeof(received_information) 
     ); 

    signaled_flag=1; 

} /* my_handler() */ 

/*--------------------------------------------------------------------------*/ 

int 
main(void) 
{ 
    pid_t   myself; 

    struct sigaction the_action; 

    myself=getpid(); 

    printf("signal receiver is process %d\n",myself); 

    the_action.sa_sigaction=my_handler; 
    sigemptyset(&the_action.sa_mask); 
    the_action.sa_flags=SA_SIGINFO; 

    if(sigaction(SIGUSR1,&the_action,NULL)) 
    { 
    fprintf(stderr,"sigaction(SIGUSR1) fail\n"); 

    exit(1); 
    } 

    if(sigaction(SIGUSR2,&the_action,NULL)) 
    { 
    fprintf(stderr,"sigaction(SIGUSR2) fail\n"); 

    exit(1); 
    } 

    for(;;) 
    { 
    while(!signaled_flag) 
    { 
     sleep(1); 
    } 

    printf("si_signo: %d\n",received_information.si_signo); 
    printf("si_pid : %d\n",received_information.si_pid ); 
    printf("si_uid : %d\n",received_information.si_uid ); 

    if(received_information.si_signo==SIGUSR2) 
    { 
     break; 
    } 

    signaled_flag=0; 
    } 

    return 0; 

} /* main() */ 

A continuación, puedo correr (no root) el lado de recepción de este modo:

wally:~/tmp/20110326$ receive 
signal receiver is process 9023 
si_signo: 10 
si_pid : 9055 
si_uid : 4000 
si_signo: 10 
si_pid : 9055 
si_uid : 4000 
si_signo: 12 
si_pid : 9056 
si_uid : 4001 
wally:~/tmp/20110326$ 

Y vea esto (no raíz) en el envío:

wally:~/tmp/20110326$ send 9023 
wally:~/tmp/20110326$ 

Como puede ver, el tercer evento ha falsificado pid y uid. ¿No es eso a lo que originalmente te opusiste? No hay EINVAL o EPERM a la vista. Creo que estoy confundido

+0

No veo cómo esto contradice el párrafo citado y sus requisitos de que el uid y el pid sean válido cuando 'si_code' toma uno de los valores que indican una señal generada por el proceso. Interpreto el "debe" en el texto que he citado como indicativo de que la aplicación puede confiar en que la restricción siempre se cumple cuando se invoca un controlador de señal, independientemente de la interfaz utilizada para enviar la señal y si es estándar o no. –

+0

No pude dejar todo mi comentario aquí; la página decía que no tenía suficientes caracteres. Por favor, mira la edición de mi respuesta anterior. –

+0

Creo que la cuestión de si es conforme depende de si el uid/pid es válido cuando 'si_code' es' SI_QUEUE' o uno de los otros valores especiales es un requisito del mecanismo * delivery * de la señal del sistema o un requisito sobre ' función sigqueue' Por lo que puedo decir, es el primero no el último. En cualquier caso, estoy bastante confundido acerca de por qué el kernel lo está dejando a la aplicación (y confiando en ello) para completar lo que deberían ser campos restringidos por privilegios ... –

0

Acepto que si_uid y si_pid deberían ser confiables, y si no lo son, es un error. Sin embargo, esto solo es necesario si la señal es SIGCHLD generada por un cambio de estado de un proceso hijo, o si si_code es SI_USER o SI_QUEUE, o si el sistema admite la opción XSI y si_code <= 0. Linux/glibc también pasa los valores si_uid y si_pid en otros casos; estos a menudo no son confiables pero eso no es un problema de conformidad POSIX.

Por supuesto, para kill() la señal no se puede poner en cola, en cuyo caso el siginfo_t no proporciona ninguna información adicional.

La razón por la cual rt_sigqueueinfo permite más que solo SI_QUEUE es probablemente permitir la implementación de E/S asíncronas POSIX, colas de mensajes y temporizadores por proceso con soporte mínimo para kernel. La implementación de estos en el área de usuario requiere la capacidad de enviar una señal con SI_ASYNCIO, SI_MESGQ y SI_TIMER respectivamente.No sé cómo glibc asigna los recursos para poner en cola la señal de antemano; para mí parece que no es así y simplemente espera que rt_sigqueueinfo no falle. POSIX claramente prohíbe descartar una notificación de vencimiento del temporizador (finalización de E/S asincrónica, llegada de mensaje en una cola de mensajes) porque demasiadas señales están en cola en el momento de la caducidad; la implementación debería haber rechazado la creación o el registro si no había suficientes recursos. Los objetos se han definido cuidadosamente de modo que cada solicitud de E/S, cola de mensajes o temporizador pueden tener como máximo una señal en vuelo a la vez.

+0

Pero 'rt_sigqueueinfo' impone' si_code <= 0', en cuyo caso se requiere que uid y pid sean válidos, ¿no? –

+0

Sí, si 'si_code <= 0' y el sistema admite la opción XSI, uid y pid deben ser válidos. Sin embargo, no es necesario que los códigos como 'SI_QUEUE' o' SI_TIMER' sean negativos o nulos. Además de la confusión, FreeBSD no admite esta parte de la opción XSI: establece 'si_code = 0' (también conocido como' SI_NOINFO') si no hay información adicional disponible; uid y pid no son válidos en este caso. – jilles

+0

Pero en Linux estos valores son negativos. –

Cuestiones relacionadas