5

Linuxにはsetuidに似たCインターフェースがありますか?これにより、プログラムはユーザー名/パスワードなどを使用して別のユーザーに切り替えることができますか?setuidの問題は、スーパーユーザーのみが使用できることです。

ログインしたユーザーとしてジョブを実行する必要がある単純なWebサービスを実行しています。したがって、メインプロセスはrootとして実行され、ユーザーがログインした後、フォークしてsetuidを呼び出し、適切なuidに切り替えます。ただし、メインプロシージャをrootとして実行することに慣れていません。su私はむしろそれを別のユーザーとして実行し、 (ただし新しいプロセスを開始せずに)同様の別のユーザーに切り替えるメカニズムを持っていると思います。

4

2 に答える 2

10

まず、setuid()スーパーユーザー以外でも間違いなく使用できます。技術的には、Linuxで必要なのは、任意のユーザーに切り替えるCAP_SETUID(および/またはCAP_SETGID機能だけです。次に、実際のID(プロセスを実行したユーザー)、有効なID(setuid / setgidバイナリの所有者setuid())、および保存されたsetgid()IDの間でプロセスIDを変更できます。

ただし、そのいずれも実際にはあなたの状況に関連していません。

比較的単純ですが、非常に堅牢なソリューションがあります。スレッドを作成する前に、サービスデーモンによってフォークおよび実行されるsetuidルートヘルパーを用意し、Unixドメインソケットペアを使用してヘルパーとサービスの間で通信します。サービスは両方を渡します。その資格情報と、ユーザーバイナリが実行されるときにヘルパーにエンドポイントファイル記述子をパイプします。ヘルパーはすべてを安全にチェックし、すべてが正常であれば、指定されたパイプエンドポイントを標準入力、標準出力、および標準エラーに接続して、目的のユーザーヘルパーをフォークして実行します。

サービスができるだけ早くヘルパーを開始するための手順は次のとおりです。

  1. サービスとヘルパー間の特権通信に使用されるUnixドメインソケットペアを作成します。

  2. フォーク。

  3. 子では、ソケットペアの一方の端だけを残して、余分なファイル記述子をすべて閉じます。標準の入力、出力、およびエラーをにリダイレクトします/dev/null

  4. 親で、ソケットペアの子端を閉じます。

  5. 子で、特権ヘルパーバイナリを実行します。

  6. 親は単純なメッセージを送信します。データがまったくない可能性がありますが、その資格情報を含む補助メッセージが含まれています。

  7. ヘルパープログラムは、サービスからの最初のメッセージを待ちます。受信すると、資格情報を確認します。クレデンシャルがマスターを通過しない場合、すぐに終了します。

補助メッセージのクレデンシャルは、発信元のプロセス'、、、およびを定義UIDGIDますPID。プロセスはこれらを入力する必要がありますが、カーネルはそれらが真であることを確認します。ヘルパーはもちろん、それが期待どおりであることUIDを確認しGIDます(サービスが実行されているはずのアカウントに対応します)が、トリックは、/proc/PID/exeシンボリックリンクが指すファイルの統計を取得することです。これは、資格情報を送信したプロセスの真の実行可能ファイルです。インストールされているシステムサービスデーモン(システムバイナリディレクトリのroot:rootが所有)と同じであることを確認する必要があります。

この時点までのセキュリティを破る可能性のある非常に単純な攻撃があります。悪意のあるユーザーが独自のプログラムを作成し、ヘルパーバイナリを正しくフォークして実行し、最初のメッセージを真のクレデンシャルで送信する場合がありますが、ヘルパーが実際にクレデンシャルを参照しているものを確認する前に、自身を正しいシステムバイナリに置き換えます。 !!

その攻撃は、さらに3つのステップで簡単に打ち負かされます。

  1. ヘルパープログラムは、(暗号的に安全な)疑似乱数、たとえば1024ビットを生成し、それを親に送り返します。

  2. 親は番号を送り返しますが、補助メッセージにその資格情報を再度追加します。

  3. ヘルパープログラムは、、、およびが変更されていないこと、およびUID正しいサービスデーモンバイナリを指していることを確認します。(完全なチェックを繰り返すだけです。)GIDPID/proc/PID/exe

ステップ8で、ヘルパーは、ソケットのもう一方の端が実行すべきバイナリを実行していることをすでに確認しています。返送する必要のあるランダムなCookieを送信するということは、相手側が事前にメッセージをソケットに「詰め込む」ことができないことを意味します。もちろん、これは攻撃者が疑似乱数を事前に推測できないことを前提としています。注意したい場合は、から適切なCookieを読み取ることができますが、/dev/randomそれは限られたリソースであることを忘れないでください(カーネルで利用できる十分なランダム性がない場合はブロックされる可能性があります)。個人的には、から1024ビット(128バイト)と言って/dev/urandom、それを使用します。

この時点で、ヘルパーはソケットペアのもう一方の端がサービスデーモンであることを確認しました。ヘルパーは、サービスデーモンを信頼できる限り、制御メッセージを信頼できます。(これがサービスデーモンがユーザープロセスを生成する唯一のメカニズムであると想定しています。そうでない場合は、以降のすべてのメッセージで資格情報を再渡して、ヘルパーで毎回再確認する必要があります。)

サービスデーモンがユーザーバイナリを実行する場合は常に、

  1. 必要なパイプを作成します(1つはユーザーバイナリに標準入力を供給するためのもので、もう1つはユーザーバイナリから標準出力を取得するためのものです)。

  2. を含むヘルパーにメッセージを送信します

    • バイナリを実行するためのID。ユーザー(およびグループ)名、またはUIDとGIDのいずれか
    • バイナリへのパス
    • バイナリに指定されたコマンドラインパラメータ
    • データパイプのユーザーバイナリエンドポイントのファイル記述子を含む補助メッセージ

ヘルパーがそのようなメッセージを受け取るときはいつでも、それは分岐します。子では、標準の入力と出力を補助メッセージのファイル記述子に置き換え、IDをsetresgid()およびsetresuid()/またはinitgroups()で変更し、作業ディレクトリを適切な場所に変更して、ユーザーバイナリを実行します。親ヘルパープロセスは、補助メッセージ内のファイル記述子を閉じて、次のメッセージを待ちます。

ソケットからの入力がなくなるときにヘルパーが終了すると、サービスが終了するとヘルパーは自動的に終了します。

十分な関心があれば、サンプルコードを提供できます。正しく理解するための詳細がたくさんあるので、コードを書くのは少し面倒です。ただし、正しく記述されていれば、ApacheSuEXECなどよりも安全です。

于 2012-10-24T10:13:03.530 に答える
4

いいえ、ユーザー名とパスワードのみを使用してUIDを変更する方法はありません。(「パスワード」の概念は、カーネルによって認識されません。ユーザースペースにのみ存在します。)ルート以外のUIDから別のUIDに切り替えるには、中間ステップとして、通常はexec()-utingによってrootになる必要があります。 setuidバイナリ。

状況に応じたもう1つのオプションは、メインサーバーを非特権ユーザーとして実行し、rootとして実行されているバックエンドプロセスと通信することです。

于 2012-10-23T23:09:08.450 に答える