CプログラムのAWKスクリプトからcatを呼び出すときにSETUIDが無効です。

CプログラムのAWKスクリプトからcatを呼び出すときにSETUIDが無効です。

私の問題を説明するために、次の簡単な例を考えてみましょう。
サーバーのオペレーティングシステムはDebian 11 armhfです。

私たちはディレクトリにあります/botm/test/

drwx------ 2 b b 4096 Sep 27 19:27 hide
-rwsr-xr-x 1 b b 8260 Sep 27 19:54 test1
-rwsr-xr-x 1 b b 8264 Sep 27 19:58 test2
-rw-r--r-- 1 b b   56 Sep 27 19:52 test1.awk
-rw-r--r-- 1 b b  172 Sep 27 19:54 test1.c
-rw-r--r-- 1 b b  186 Sep 27 19:58 test2.c

ご覧のとおり、所有者だけがbディレクトリにアクセスできます/botm/test/hide

今私たちはユーザーなので、test
ファイルにアクセスできません/botm/test/hide/test.txt

$ cat hide/test.txt
cat: hide/test.txt: Permission denied

[OK]プログラムがSETUIDビットを設定test1しました。そのため、ユーザーにもかかわらず、これらのプログラムをユーザーのように実行できます。test2
testb

まず、test2.c

/* test2.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
  int r;
  r = execl("/usr/bin/cat","/usr/bin/cat","/botm/test/hide/test.txt", (char *)0);
  printf("%d\n",r);
  return r;
}
$ ./test2
hidden content
$ 

期待どおりに動作します。

今、、test1.ctest1.awk

/* test1.c */
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
  int r;
  r = execl("/usr/bin/mawk","/usr/bin/mawk","-f","/botm/test/test1.awk", (char *)0);
  printf("%d\n",r);
  return r;
}
# test1.awk
BEGIN {
  system("cat /botm/test/hide/test.txt");
};
$ ./test1
cat: /botm/test/hide/test.txt: Permission denied
$

驚くべきことに、これはうまくいきません。

私の期待:
SETUIDを使ってプログラムを実行したら、test1userとして実行する必要がありますb
したがって、プログラムが呼び出されると、/usr/bin/mawkユーザーとしても実行する必要がありますb
したがって、後でawkスクリプトが呼び出される場合でも、userとして実行する必要がありcatます。したがって、ユーザーだけが使用できるコンテンツにアクセスできる必要があります。しかし、そのようなことは起こりませんでした。catb
cat/botm/test/hide/test.txtb

それでは、実際の状況を見てみましょう。
上記は単純化された例に過ぎません。複数のCおよびAWKプログラムを含むプロジェクトがあります。 C プログラムで AWK プログラムを呼び出して、いくつかのテキストを処理し、いくつかの出力を生成します。これには、電話catやその他のシステムツールが含まれる場合もあります。これらのプログラムの中には、www-dataそのプログラムによって生成された特定の一時ファイルにアクセスする権限がない(権限がないはずの)ユーザーとしてApacheサーバー上で実行されます。これがSETUIDが使用される理由です。そして、このワークフローはこのプロジェクト(2014年に始まり)でよく使われています。

今は、古いサーバー(操作)から新しいサーバー(動作しない)に移動しています。このメカニズムに依存しないようにプロジェクトを変更することは非常に大きな再設計になるので、今はそうしたくありません。

Cプログラム(SETUIDを含む)がシステムツール(、...)を呼び出すAWKスクリプトを呼び出すときにcatSETUIDが保持されないのは
なぜですか?
以前のバージョンのシステムでは機能しました。

以下を追加するように編集されました。

AWKプログラムがまだ隠しファイルにアクセスできることを確認しました。 donが呼び出したファイルだけにアクセスsystem()できません。

したがって、この動作を発見した理由は、パイプを使用してsystem()実行コマンドを呼び出すことでした。 AWKやそれに似たものはありません。getlinemawksh
exec

shとして呼び出さないと、SETUIDは削除されます-p
これは新しい行動です。この部分はman sh古いサーバーには存在しません。

           -p priviliged    Do not attempt to reset effective uid if it does not match uid. This is not set by default to help avoid incorrect usage by
                            setuid root programs via system(3) or popen(3).

したがって、プログラムを再実行するには、次の少なくとも 1 つを達成する必要があります。

  • makeは/usr/bin/shデフォルトでSETUIDを削除しません。
  • makemawkまたは他の互換性のあるAWKインタプリタを-p使用するときに使用されます/usr/bin/shsystem()
  • AWKプログラムを実行する別の方法を見つける
  • これに依存しないように、すべてのプログラムを再設計してください。たとえば、C、Perl、またはPythonで実行した場合sh

プロジェクト全体(およびこのワークフローを使用する他のいくつかのプロジェクト)を再設計するには、現在持っていない労力と時間が必要なので、どういうわけか完全なSETUID機能を復元する必要があります。

実際、その機能はAWKプログラムで簡単に再現でき、cat私のプロジェクトの他のツールsystem()[ -f、、、、、、、、、、)も呼び出すことができます。ほとんどの場合、SETUIDなしで呼び出すことは許可されていません。catcpmkdirmvsleepwget

答え1

この同様の質問に対する答えは次のとおりです。
https://unix.stackexchange.com/a/565254/543092
提案がありますsetresuid()

私のプロジェクトでは、AWKスクリプトは常にCプログラムから呼び出されるので、これが私が使用できるソリューションです。

私は次のことをしました:

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char **argv)
{
    uid_t euid;
    int r;
    euid = geteuid();
    r = setreuid(euid, euid);
    if (r != 0)
        return (r = errno);
    r = execl("/usr/bin/mawk","/usr/bin/mawk","-f","/botm/test/test1.awk", (char *)0);
    printf("%d\n",r);
}

こうして「変装」が完了し、shSETUIDは以前は不明になります。すべてが正常です。

これは、常に存在するように動作させるよりもsh優れたソリューションです-p。私が何をしているのかを知っていれば、SETUIDは維持されます。

私は次の理由で提案されたもののsetreuid()代わりに使用します。setresuid()

  • setreuid()十分
  • setresuid()GNU拡張です。

関連情報