11

Linuxでは、syscallを自分で呼び出すことで、 andフィールドrt_sigqueueに好きなものを入力でき、呼び出しが成功して、間違った値が表示されることがわかりました。当然のことながら、信号の送信に対するuidの制限は、この種のなりすましに対するある程度の保護を提供しますが、この情報に依存することは危険である可能性があるのではないかと心配しています。私が読むことができるトピックに関する良いドキュメントはありますか?Linuxが、カーネル空間でパラメーターを生成するのではなく、呼び出し元にパラメーターを指定させるという明らかに誤った動作を許可するのはなぜですか?特に、ユーザースペースでuid / gidを取得するために、追加のsys呼び出し(したがってパフォーマンスコスト)が必要になる可能性があるため、これは無意味に思えます。si_uidsi_pidsiginfo

編集:POSIXの私の読書に基づいて(私によって追加された強調):

si_codeがSI_USERまたはSI_QUEUE、[XSI]、または0以下の任意の値の場合、信号はプロセスによって生成され、si_pidおよびsi_uidはそれぞれ送信者のプロセスIDおよび実際のユーザーIDに設定されます。

Linuxによるこの動作は不適合であり、重大なバグであると思います。

4

2 に答える 2

5

引用する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現在の編集:

この編集は、 8時間前のR ..のコメントに対応しています。このページでは、十分な量のコメントを残すことができなかったためです。

基本的に正しいと思います。ただし、システムがPOSIXに準拠しているか、準拠していないかのどちらかです。非ライブラリ関数がsyscallを実行し、その結果uid、pid、および'si_code'の組み合わせが非準拠になる場合、引用した2番目のステートメントは、呼び出し自体が準拠していないことを明確にします。これは2つの方法で解釈できます。1つの方法は、「ユーザーがこのルールに違反した場合、システムを非準拠にする」というものです。しかし、あなたは正しいです、私はそれがばかげていると思います。非特権ユーザーがシステムを非準拠にすることができる場合、システムはどのように役立ちますか?私が見ているように、修正は、システムコールを行うライブラリ「sigqueue()」ではないことをシステムに認識させることです。カーネル自体は、「si_code」を「SI_QUEUE」以外に設定し、 uidとpidを設定します。私の意見では、あなたはカーネルの人々とこれを上げる必要があります。ただし、問題が発生する可能性があります。ライブラリがどのように機能するかを見て、特定のライブラリ関数によってシステムコールが行われたかどうかを検出するための安全な方法を私は知りません。ほとんど定義上、システムコールの単なる便利なラッパーです。そしてそれは彼らがとる立場かもしれません、それは私が失望することを知っています。

(膨大な)2011年3月26日、18:00 PST現在の編集:

コメントの長さに制限があるためです。

これは、約1時間前のR..のコメントへの応答です。

私はsyscallの件名に少し慣れていないので、ご容赦ください。

「カーネルsysqueueシステムコール」とは、「__ NR_rt_sigqueueinfo」呼び出しを意味しますか?これを行ったときに見つけたのはこれだけです。

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

そうだとすれば、私はあなたの本来のポイントを理解していないと思います。カーネルは、(ルート以外の)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() */

次に、受信側を(ルートではなく)実行できます。

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$ 

そして、送信側でこれ(非ルート)を参照してください:

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

ご覧のとおり、3番目のイベントはpidとuidを偽装しています。それはあなたが最初に反対したことではありませんか?ないEINVALEPERM、見えない。私は混乱していると思います。

于 2011-03-26T11:38:54.793 に答える
0

私はそれに同意しsi_uidsi_pid信頼できるはずです。そうでない場合、それはバグです。ただし、これが必要になるのは、シグナルがSIGCHLD子プロセスの状態変化によって生成される場合、またはsi_codeSI_USERまたはSI_QUEUEの場合、またはシステムが XSI オプション および をサポートしている場合のみですsi_code <= 0。Linux/glibc は、その他の場合もsi_uidandsi_pid値を渡します。これらは多くの場合信頼できませんが、これは POSIX 準拠の問題ではありません。

もちろん、kill()シグナルがキューに入れられない場合もあります。その場合、siginfo_tは追加情報を提供しません。

rt_sigqueueinfoそれ以上のことを許可する理由SI_QUEUEは、おそらく、最小限のカーネル サポートで POSIX 非同期 I/O、メッセージ キュー、およびプロセスごとのタイマーを実装できるようにするためです。SI_ASYNCIOこれらをユーザーランドに実装するには、それぞれ、 、SI_MESGQおよびでシグナルを送信する機能が必要SI_TIMERです。glibc が事前にシグナルをキューに入れるためのリソースをどのように割り当てるかはわかりません。私にはそうではないように見えますが、単に望んでいますrt_sigqueueinfo失敗しません。POSIX では、タイマーの期限切れ (非同期 I/O の完了、メッセージ キューへのメッセージの到着) 通知を破棄することを明確に禁止しています。リソースが不十分な場合、実装は作成または登録を拒否する必要があります。オブジェクトは、各 I/O 要求、メッセージ キュー、またはタイマーが一度に処理中のシグナルを最大 1 つ持つことができるように、慎重に定義されています。

于 2011-05-08T13:45:45.843 に答える