ruid/suid/euid/fsuid
プロセスは、タスク構造体下にcredメンバーに、以下の4つのIDを有しています。uidは実idで、このid単位でプロセス数/オープンしているファイル数/ペンディングしているシグナル数などをuser_struct構造体下で管理することになります。
これらのIDを設定するため、以下の3つのシステムコールがあります。
setuidシステムコールは、rootの場合、ruid/euid/fsuid/suidの全ては設定します。これはこのプロセスその物を、そのid権限のプロセスとする事を意味し、rootで動作しているログインプロセスからログインした時に、そのユーザIDの親ともなるセッションプロセスを作成する。と言ったケースが考えられます。
注目すべきは、実ユーザ変更時と、実行ユーザ変更で実ユーザと異なるとき、保存ユーザを実行ユーザにしていることです。これはsetuidシステムコールのように元のユーザ権限を無くしてしまう事になります。
特権モードは、上記設定が無条件に行われます。そうでない時変更するidは、実ユーザ/実行ユーザ/保存ユーザのどれかでなければなりません。これがユーザidの変更処理において、一般的利用に適したシステムコールかと思われます。
setreuidシステムコールは、なんかbsdとの互換のためにインプリメントされているよう・・・。
struct task_struct {
:
const struct cred __rcu *cred;
:
}
struct cred {
:
uid_t uid; /* real UID of the task */
uid_t suid; /* saved UID of the task */
uid_t euid; /* effective UID of the task */
uid_t fsuid; /* UID for VFS ops */
:
}
suidは、あるユーザから別のユーザになり、そして元のユーザに戻ると言った場合(通常rootから通常ユーザに移行するケース)です。uidを変更する場合、suid/euidのどちらかにしか変更できないからです。euidは実際の動作の権限で、fsuidはファイル操作の権限となります。これらのIDを設定するため、以下の3つのシステムコールがあります。
SYSCALL_DEFINE1(setuid, uid_t, uid) SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
setuidシステムコールは、rootの場合、ruid/euid/fsuid/suidの全ては設定します。これはこのプロセスその物を、そのid権限のプロセスとする事を意味し、rootで動作しているログインプロセスからログインした時に、そのユーザIDの親ともなるセッションプロセスを作成する。と言ったケースが考えられます。
SYSCALL_DEFINE1(setuid, uid_t, uid)
{
const struct cred *old;
struct cred *new;
int retval;
new = prepare_creds();
if (!new)
return -ENOMEM;
old = current_cred();
retval = -EPERM;
if (nsown_capable(CAP_SETUID)) {
new->suid = new->uid = uid;
if (uid != old->uid) {
retval = set_user(new);
if (retval < 0)
goto error;
}
} else if (uid != old->uid && uid != new->suid) {
goto error;
}
new->fsuid = new->euid = uid;
retval = security_task_fix_setuid(new, old, LSM_SETID_ID);
if (retval < 0)
goto error;
return commit_creds(new);
error:
abort_creds(new);
return retval;
}
通常ユーザの場合、変更するuidが、suidでないと更新できません。この事は通常ユーザは他のユーザに移行出来ないと言うことです。
[root@localhost kitamura]# cat setuid.c
#include "stdio.h"
#include "stdlib.h"
void main()
{
printf("%d\n", getuid());
if (setuid(1001)) {
printf("error\n");
exit(0);
}
printf("%d\n", getuid());
if (setuid(1002)) {
printf("error\n");
exit(0);
}
printf("%d\n", getuid());
}
[root@localhost kitamura]# ./setuid
0
1001
error
setreuidシステムコールは、引数が-1でない実ユーザ/実行ユーザを変更します。通常特権では、実ユーザは、実行ユーザへのみ、実行ユーザは、実ユーザか保存ユーザへの変更が可能です。注目すべきは、実ユーザ変更時と、実行ユーザ変更で実ユーザと異なるとき、保存ユーザを実行ユーザにしていることです。これはsetuidシステムコールのように元のユーザ権限を無くしてしまう事になります。
SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
{
const struct cred *old;
struct cred *new;
int retval;
new = prepare_creds();
if (!new)
return -ENOMEM;
old = current_cred();
retval = -EPERM;
if (ruid != (uid_t) -1) {
new->uid = ruid;
if (old->uid != ruid &&
old->euid != ruid &&
!nsown_capable(CAP_SETUID))
goto error;
}
if (euid != (uid_t) -1) {
new->euid = euid;
if (old->uid != euid &&
old->euid != euid &&
old->suid != euid &&
!nsown_capable(CAP_SETUID))
goto error;
}
if (new->uid != old->uid) {
retval = set_user(new);
if (retval < 0)
goto error;
}
if (ruid != (uid_t) -1 ||
(euid != (uid_t) -1 && euid != old->uid))
new->suid = new->euid;
new->fsuid = new->euid;
retval = security_task_fix_setuid(new, old, LSM_SETID_RE);
if (retval < 0)
goto error;
return commit_creds(new);
error:
abort_creds(new);
return retval;
}
setresuidシステムコールは、その引数が-1でない実ユーザ/実行ユーザ/保存ユーザを、ファイルユーザを実行ユーザに設定します。特権モードは、上記設定が無条件に行われます。そうでない時変更するidは、実ユーザ/実行ユーザ/保存ユーザのどれかでなければなりません。これがユーザidの変更処理において、一般的利用に適したシステムコールかと思われます。
SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
{
const struct cred *old;
struct cred *new;
int retval;
new = prepare_creds();
if (!new)
return -ENOMEM;
old = current_cred();
retval = -EPERM;
if (!nsown_capable(CAP_SETUID)) {
if (ruid != (uid_t) -1 && ruid != old->uid &&
ruid != old->euid && ruid != old->suid)
goto error;
if (euid != (uid_t) -1 && euid != old->uid &&
euid != old->euid && euid != old->suid)
goto error;
if (suid != (uid_t) -1 && suid != old->uid &&
suid != old->euid && suid != old->suid)
goto error;
}
if (ruid != (uid_t) -1) {
new->uid = ruid;
if (ruid != old->uid) {
retval = set_user(new);
if (retval < 0)
goto error;
}
}
if (euid != (uid_t) -1)
new->euid = euid;
if (suid != (uid_t) -1)
new->suid = suid;
new->fsuid = new->euid;
retval = security_task_fix_setuid(new, old, LSM_SETID_RES);
if (retval < 0)
goto error;
return commit_creds(new);
error:
abort_creds(new);
return retval;
}
補足
task下のcredを取得するのに、new = prepare_creds()/old = current_cred()とし、newのidを更新し、oldのidは比較時に参照すると、まどろっこしいことをしているのは、credがrcuで管理されているからです。setreuidシステムコールは、なんかbsdとの互換のためにインプリメントされているよう・・・。





