Достоверны ли данные в siginfo? - PullRequest
8 голосов
/ 11 марта 2011

Я обнаружил, что в Linux, совершая свой собственный вызов системного вызова rt_sigqueue, я могу поместить все, что мне нравится, в поля si_uid и si_pid, и вызов завершается успешно и с радостью доставляет неправильные значения.Естественно, ограничения uid на отправку сигналов обеспечивают некоторую защиту от такого рода спуфинга, но я боюсь, что полагаться на эту информацию опасно.Есть ли какая-нибудь хорошая документация по теме, которую я мог бы прочитать?Почему Linux допускает явно некорректное поведение, позволяющее вызывающей стороне указывать параметры siginfo, а не генерировать их в пространстве ядра?Это кажется бессмысленным, особенно потому, что для получения uid / gid в пользовательском пространстве могут потребоваться дополнительные системные вызовы (и, следовательно, затраты на производительность).

Редактировать: На основании моего чтения POSIX (выделено мной):

Если si_code равен SI_USER или SI_QUEUE, [XSI] или любое значение меньше или равно 0, то сигнал был сгенерирован процессом иsi_pid и si_uid должны быть установлены для идентификатора процесса и реального идентификатора пользователя отправителя, соответственно.

Я считаю, что это поведение в Linux является несовместимым и представляет собой серьезную ошибку.

Ответы [ 2 ]

5 голосов
/ 26 марта 2011

В этом разделе страницы POSIX, которую вы цитируете, также указано, что означает si-code, и вот значение:

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

В этом разделе говорится:

Если сигнал не был сгенерирован одним из перечисленных функций или событий выше, si_code должен быть установлен либо к одному из значений сигнала описано в XBD, или к определенное реализацией значение, которое не равно ни одному из определенных значений выше.

Ничего не нарушается, если только функция sigqueue() использует SI_QUEUE. Ваш сценарий включает в себя код, отличный от функции sigqueue(), использующий SI_QUEUE Вопрос заключается в том, предусматривает ли POSIX операционную систему, обеспечивающую, чтобы разрешалась только определенная библиотечная функция (в отличие от некоторой функции, которая не является библиотечной функцией, определенной в POSIX)? совершить системный вызов с определенными характеристиками. Я считаю, что ответ «нет».

РЕДАКТИРОВАТЬ с 2011-03-26, 14:00 PST:

Это изменение является ответом на комментарий R .. от восьми часов назад, поскольку страница не позволила мне оставить достаточно объемный комментарий:

Я думаю, ты в принципе прав. Но либо система совместима с POSIX, либо нет. Если небиблиотечная функция выполняет системный вызов, что приводит к несовместимой комбинации uid, pid и 'si_code', то второе приведенное мной утверждение проясняет, что сам вызов не соответствует. Можно интерпретировать это двумя способами. Один из способов заключается в следующем: «Если пользователь нарушает это правило, он делает систему несовместимой». Но ты прав, я думаю, что это глупо. Что хорошего в системе, когда любой непривилегированный пользователь может сделать ее несовместимой? Как я понимаю, исправление состоит в том, чтобы система знала, что это не библиотека 'sigqueue ()', выполняющая системный вызов, тогда само ядро ​​должно установить для 'si_code' значение, отличное от 'SI_QUEUE', и оставить UID и PID, как вы установите их. На мой взгляд, вы должны поднять это с ребятами из ядра. Однако у них могут возникнуть трудности; Я не знаю какого-либо безопасного способа для них определить, был ли выполнен системный вызов определенной библиотечной функцией, видя, как функционирует библиотека. почти по определению, это просто удобные обертки вокруг системных вызовов. И это может быть позиция, которую они занимают, что, я знаю, будет разочарованием.

(объем) РЕДАКТИРОВАТЬ на 2011-03-26, 18:00 PST:

Снова из-за ограничений на длину комментария.

Это ответ на комментарий R .. около часа назад.

Я немного новичок в теме системного вызова, поэтому, пожалуйста, потерпите меня.

Под "ядром sysqueue syscall" вы подразумеваете вызов `__NR_rt_sigqueueinfo '? Это единственное, что я нашел, когда сделал это:

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

Если это так, я думаю, я не понимаю вашу первоначальную мысль. Ядро позволит (без полномочий root) использовать SI-QUEUE с поддельным pid и uid без ошибок. Если у меня есть отправляющая сторона, закодированная таким образом:

#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() */

и получающая сторона закодирована таким образом:

#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() */

Затем я могу запустить (без полномочий root) получающую сторону таким образом:

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$ 

И посмотрите это (не root) на конце отправки:

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

Как видите, третье событие имеет поддельные pid и uid. Разве это не то, против чего вы изначально возражали? Там нет EINVAL или EPERM в поле зрения. Я думаю, я в замешательстве.

0 голосов
/ 08 мая 2011

Я согласен, что si_uid и si_pid должны быть заслуживающими доверия, а если нет, то это ошибка. Однако это требуется только в том случае, если сигнал SIGCHLD генерируется изменением состояния дочернего процесса, или если si_code равен SI_USER или SI_QUEUE, или если система поддерживает параметр XSI и si_code <= 0. Linux / glibc также передает значения si_uid и si_pid в других случаях; они часто не заслуживают доверия, но это не проблема соответствия POSIX.

Конечно, для kill() сигнал не может быть поставлен в очередь, и в этом случае siginfo_t не предоставляет никакой дополнительной информации.

Причина, по которой rt_sigqueueinfo допускает нечто большее, чем просто SI_QUEUE, вероятно, заключается в том, что он позволяет реализовывать асинхронный ввод-вывод POSIX, очереди сообщений и таймеры для каждого процесса с минимальной поддержкой ядра. Для их реализации в пользовательской среде требуется возможность отправки сигнала с SI_ASYNCIO, SI_MESGQ и SI_TIMER соответственно. Я не знаю, как glibc распределяет ресурсы для постановки в очередь сигнала заранее; мне кажется, что это не так и просто надеется, rt_sigqueueinfo не подведет. POSIX явно запрещает сбрасывать уведомление об истечении таймера (завершение асинхронного ввода-вывода, прибытие сообщения в очередь сообщений), поскольку слишком много сигналов находится в очереди во время истечения срока; реализация должна была отклонить создание или регистрацию, если было недостаточно ресурсов. Объекты были определены так, чтобы каждый запрос ввода-вывода, очередь сообщений или таймер могли иметь не более одного сигнала в полете за раз.

...