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 errorsetreuidシステムコールは、引数が-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との互換のためにインプリメントされているよう・・・。