Unixのset-user-IDメカニズムを説明できる人はいますか?このデザイン決定の根拠は何でしたか?効果的なユーザーIDメカニズムとどう違いますか?
答え1
おそらく、UNIXでは、ファイルに対する一般的な読み取り、書き込み、および実行権限を知っているでしょう。
ただし、多くのアプリケーションでは、このタイプの権限構造(たとえば、特定のユーザーに特定のファイルを読み取るための完全な権限を付与したり、ファイルをまったく読み取る権限を付与しないなど)はあまりにも粗雑です。したがって、Unixには別の許可ビットであるset-user-ID
ビットが含まれています。実行可能ファイルにこのビットが設定されると、所有者以外のユーザーがファイルを実行するたびに、そのユーザーは所有者の他のファイルにアクセスするときに所有者のすべてのファイルの読み取り/書き込み/実行権限を取得します。
ファイルのユーザー ID ビット設定を設定するには、次のように入力します。
chmod u+s filename
group-otherの実行権限も設定していることを確認してください。他のグループに対する読み取り権限もあれば良いでしょう。これはすべて1つの文で可能です
chmod 4755 filename
保存されたUIDとも呼ばれます。開始されたファイルにはSet-UIDビットがあり、保存されたUIDはファイル所有者のUIDになります。それ以外の場合、保存されたUIDは実際のUIDになります。
有効なuidとは何ですか?
このUIDは、特定のタスクを実行するためのプロセスの権限を評価するために使用されます。 EUIDは実際のUIDに変更でき、EUID!= 0の場合はスーパーユーザーUIDに変更できます。 EUID = 0の場合は、何でも変更できます。
はい
そのようなプログラムの例ですpasswd
。完全なリストを見ると、Set-UIDビットがあり、所有者が「root」であることがわかります。通常のユーザー(「mtk」など)で実行すると、次のようpasswd
になります。
Real-UID = mtk
Effective-UID = mtk
Saved-UID = root
答え2
man credentials
この状況では良い情報源です。この質問も参照してくださいだから。歴史的な説明についてはこちらをご覧くださいアーカイブされた投稿。
「UID設定」および「有効なUID」メカニズムを呼び出す代わりに、UIDの全体的な概念をメカニズムと呼びます。様々なUIDが存在する理由は、権限分離によるさまざまな問題によるものです。一般(権限のない)ユーザーでも、権限を持つユーザーのみが実行できるアクション(リソースへのアクセス)を実行する必要がある場合があります。これを容易に達成するために、プログラムはUIDを変更することができます。 3つのタイプがあります。
実際のUID - 所有プロセスのUID
有効なUID - プロセスが現在実行されているUID - 特定の瞬間にプロセスの実際の機能を決定します。これは
ps
ユーザーフィールドにも表示されます。保存されたセットUID - 実際のUIDと有効なUIDを切り替えるためのプレースホルダー
一般ユーザーができるので、最後のものが必要です。ただこれら3つの間を切り替えます。他の人はいません。setuidプログラムは通常、自分をロードしたユーザーが誰であるかを知る必要があります(そして実際のUIDはより多くの混乱を引き起こすので、変更しないでください)。
答え3
mtkの説明はとても良いです。
このpasswd
例は特権の上昇の1つです。 passwdは、ルートのみを変更できるファイルを変更する必要があるため、常にルートとして実行されます。これは、passwd実行可能ファイルがバッファオーバーフローのような事態を引き起こさないようにするため、賢い一般ユーザーが意図しない目的で使用できるようにすることが重要です。
もう1つの理由は、rootとしてログインした場合と同じ方法でユーザーを保護することですsu
。減らすか制限するエスカレーションではなく特定の操作に対する権限。たとえば、自分のエントリにアクセスする必要がなく、独自のエントリがあり、それが必要なすべてのデーモンサービス(ロガーなど)を起動する権限がある場合、suidを実行するとアクセスのみ可能です。私のものか他の人のもの。
実行ファイルにsuidビットが設定されていない場合でも、プログラム的にuidを設定できます。ただし、これはアップグレードには機能しません。つまり、通常のユーザーで、特定の時点で独自のuidを設定するプログラムを作成している場合、プログラムはrootに切り替えることはできません。私はこれがApacheがどのように機能するかを信じています。これは通常、1つのプロセスとしてルートによって開始され、次に子プロセスをフォークして、uidを許可されていないユーザー(「httpd」など)に切り替えます。これらのサブプロセスは、実際のWebサーバーが実行するアクションです。
答え4
上記の@mtkの答えには小さなエラーがあり、@Nor.Zは彼のコメントでこれを指摘しました。
資格情報のマニュアルページでは、実際のユーザーID(RUID)、有効なユーザーID(EUID)、および保存されたユーザーID(SUID)の概念について説明します。バラよりここ。
よく理解すると、基本的にプロセスの開始時にプロセスのEUIDとSUIDはプロセスのRUIDと同じに設定されます。グループも同じだ。ただし、プロセスを開始するバイナリの権限にset-user-IDビットが設定されている場合、EUIDはそしてSUIDは、rootなどの権限を持つユーザーであるバイナリ所有者のSUIDに設定されます。 RUIDは変更されません。後でプログラムがソースコードでEUIDを変更すると、ルートUIDもSUIDに保存され、SUIDから取得できるため、必要に応じてルートのUIDにリセットできます。次の例Cプログラムはこれを示しています(これYouTubeビデオ):
// filename: uid.c
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
uid_t ruid, euid, suid;
// Get the real, effective, and saved user IDs
if (getresuid(&ruid, &euid, &suid) == -1) {
perror("getresuid");
return 1;
}
// Display the user IDs
printf("Real UID: %d\n", ruid);
printf("Effective UID: %d\n", euid);
printf("Saved UID: %d\n", suid);
// file /etc/hosts is owned by root, and only root can open it
// in write mode. Normal/unprovileged users can only open it in read mode.
/*See if we can open the /etc/hosts file for reading and writing, as the EUID*/
printf("open: %d\n", open("/etc/hosts", O_RDWR));
/* access() tests what the RUID can do. We check 'writing' in this case */
printf("access: %d\n", access("/etc/hosts", W_OK));
printf("--\n");
// drop the privileges in the EUID by setting it with the RUID, and re-try
seteuid(ruid);
if (getresuid(&ruid, &euid, &suid) == -1) {
perror("getresuid");
return 1;
}
printf("Real UID: %d\n", ruid);
printf("Effective UID: %d\n", euid);
printf("Saved UID: %d\n", suid);
/*See if we can open the /etc/hosts file for reading and writing, as the EUID*/
printf("open: %d\n", open("/etc/hosts", O_RDWR));
/* access() tests what the RUID can do. We check 'writing' in this case */
printf("access: %d\n", access("/etc/hosts", W_OK));
return 0;
}
を使用してプログラムをコンパイルし、gcc uid.c -o uid
を使用して実行します./uid
。次のように出力されます。
Real UID: 1000
Effective UID: 1000
Saved UID: 1000
open: -1
access: -1
--
Real UID: 1000
Effective UID: 1000
Saved UID: 1000
open: -1
access: -1
オープンまたはアクセス戻りコード-1は、操作が失敗したことを示します。次に、プログラムの所有権をrootに変更し、sudo chown root ./uid
set-user-IDビットを設定しますsudo chmod u+s ./uid
。ls -lA
バイナリ所有者権限で実行する操作を確認し、「s」を記録するには、次の手順を実行します。uid
total 20
-rwsrwxr-x 1 root gamba 16272 dic 6 16:44 uid
-rw-rw-r-- 1 gamba gamba 1192 dic 6 16:17 uid.c
プログラムを再実行して上記と比較してください。
Real UID: 1000
Effective UID: 0
Saved UID: 0
open: 3
access: -1
--
Real UID: 1000
Effective UID: 1000
Saved UID: 0
open: -1
access: -1
ご覧のとおり、EUIDはそしてSUIDはルート(バイナリ所有者)のSUIDに設定されていますuid
!これにより、EUIDを使用したオープン操作は成功します(戻りコード3)、アクセス操作は失敗します(戻りコード-1)。これは、ファイルを読み取ることができる(許可なし)RUIDを使用しているためです/etc/hosts
。 「--」フラグの後にEUIDをRUIDに変更すると、両方の操作が再び失敗することがわかります。
最後に、バイナリの所有権をuid
一般/権限のないユーザー "gamba"にリセットすると、set-user-IDビットがリセットされます。しかし、ランナーを使用すると、sudo
次のような出力が得られます。
Real UID: 0
Effective UID: 1000
Saved UID: 1000
open: -1
access: 0
--
Real UID: 0
Effective UID: 0
Saved UID: 1000
access: 0
access: 0
ご覧のように、この例では set-user-ID ビットは権限のないユーザーに EUID を設定する効果があり、オープン操作が失敗します。実際のユーザー(この場合はroot)として作成されたため、アクセス操作は成功します(runnerを使用しているためsudo
)。この動作は、上記の回答で@goldilocksが指摘したように、特権ユーザー(rootなど)でプログラムを実行したときに誤って特定のファイルを変更したくない場合に便利です。