私の問題を説明するために、次の簡単な例を考えてみましょう。
サーバーのオペレーティングシステムは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
test
b
まず、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.c
:test1.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を使ってプログラムを実行したら、test1
userとして実行する必要がありますb
。
したがって、プログラムが呼び出されると、/usr/bin/mawk
ユーザーとしても実行する必要がありますb
。
したがって、後でawkスクリプトが呼び出される場合でも、userとして実行する必要がありcat
ます。したがって、ユーザーだけが使用できるコンテンツにアクセスできる必要があります。しかし、そのようなことは起こりませんでした。cat
b
cat
/botm/test/hide/test.txt
b
それでは、実際の状況を見てみましょう。
上記は単純化された例に過ぎません。複数のCおよびAWKプログラムを含むプロジェクトがあります。 C プログラムで AWK プログラムを呼び出して、いくつかのテキストを処理し、いくつかの出力を生成します。これには、電話cat
やその他のシステムツールが含まれる場合もあります。これらのプログラムの中には、www-data
そのプログラムによって生成された特定の一時ファイルにアクセスする権限がない(権限がないはずの)ユーザーとしてApacheサーバー上で実行されます。これがSETUIDが使用される理由です。そして、このワークフローはこのプロジェクト(2014年に始まり)でよく使われています。
今は、古いサーバー(操作)から新しいサーバー(動作しない)に移動しています。このメカニズムに依存しないようにプロジェクトを変更することは非常に大きな再設計になるので、今はそうしたくありません。
Cプログラム(SETUIDを含む)がシステムツール(、...)を呼び出すAWKスクリプトを呼び出すときにcat
SETUIDが保持されないのは
なぜですか?
以前のバージョンのシステムでは機能しました。
以下を追加するように編集されました。
AWKプログラムがまだ隠しファイルにアクセスできることを確認しました。 donが呼び出したファイルだけにアクセスsystem()
できません。
したがって、この動作を発見した理由は、パイプを使用してsystem()
実行コマンドを呼び出すことでした。 AWKやそれに似たものはありません。getline
mawk
sh
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を削除しません。 - make
mawk
または他の互換性のあるAWKインタプリタを-p
使用するときに使用されます/usr/bin/sh
。system()
- AWKプログラムを実行する別の方法を見つける
- これに依存しないように、すべてのプログラムを再設計してください。たとえば、C、Perl、またはPythonで実行した場合
sh
。
プロジェクト全体(およびこのワークフローを使用する他のいくつかのプロジェクト)を再設計するには、現在持っていない労力と時間が必要なので、どういうわけか完全なSETUID機能を復元する必要があります。
実際、その機能はAWKプログラムで簡単に再現でき、cat
私のプロジェクトの他のツールsystem()
([ -f
、、、、、、、、、、)も呼び出すことができます。ほとんどの場合、SETUIDなしで呼び出すことは許可されていません。cat
cp
mkdir
mv
sleep
wget
答え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);
}
こうして「変装」が完了し、sh
SETUIDは以前は不明になります。すべてが正常です。
これは、常に存在するように動作させるよりもsh
優れたソリューションです-p
。私が何をしているのかを知っていれば、SETUIDは維持されます。
私は次の理由で提案されたもののsetreuid()
代わりに使用します。setresuid()
setreuid()
十分setresuid()
GNU拡張です。