ruid/suid/euid/fsuid


プロセスは、タスク構造体下にcredメンバーに、以下の4つのIDを有しています。uidは実idで、このid単位でプロセス数/オープンしているファイル数/ペンディングしているシグナル数などをuser_struct構造体下で管理することになります。
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との互換のためにインプリメントされているよう・・・。

最終更新 2012/12/16 19:17:08 - north
(2012/12/16 19:17:08 作成)


検索

アクセス数
3689310
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。