setuid


setuidシステムコールは、rootのCAP_SETUIDを有するなら、uid=euid=fsuid=suid=uidとし、有していない通常タスクは、euid=fsuid=uidとします。ただし通常タスクのuidは、タスクのuid/suidでなければなりません。

ログインプロセスのようにroot権限で動作し、その中で他の権限へ移行するような実装を想定したものですが、rootから一般権限へ移行した場合、uid/euid/fsuid/suidの全てのクレデンシャルは同一となり、従って一般権限プロセスから他の権限へ移行するのできません。

これはセキュリティ的に理にかなっていますが、そうなると、!nsown_capable(CAP_SETUID)の処理が必要であるかと言うことです。

root権限の必要なコマンドは、その処理全てでroot権限が必要という事でなく、その部分的な処理で、スタックオーバフローとか、他の外部コマンドを実行するとか、権限が奪われるセキュリティ上、脆弱となります。

で、そのような処理では、脆弱的な箇所では一般権限に移行し、後、root権限へ戻る。と言った実装を可能とするものです。(たぶん)

上記実装を実現するため、uid/euid/fsuid/suidの個別単位で設定できるsetuidシステムコールの設定条件に準じた別のシステムコールが実装されています。
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;
}

補足

ファイル属性のsuidは、一般権限からroot権限へ移行しますが、システムコールから実現する事は不可能です。本実装は、カーネルマターで、execシステムからコールされるprepare_binprm()で、inodeのi_uidをプロセスのeuidに直接設定し、uid/fsuid/suidは、security_bprm_set_creds()でカーネルのセキュリティに応じて設定されます。未セキュリティカーネルでは、uid/fsuid/suid=euidとします。
int prepare_binprm(struct linux_binprm *bprm)
{
       umode_t mode;
       struct inode * inode = bprm->file->f_path.dentry->d_inode;
       int retval;

       mode = inode->i_mode;
       if (bprm->file->f_op == NULL)
               return -EACCES;

       bprm->cred->euid = current_euid();
       bprm->cred->egid = current_egid();

       if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
               if (mode & S_ISUID) {
                       bprm->per_clear |= PER_CLEAR_ON_SETID;
                       bprm->cred->euid = inode->i_uid;
               }

               if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
                       bprm->per_clear |= PER_CLEAR_ON_SETID;
                       bprm->cred->egid = inode->i_gid;
               }
       }

       retval = security_bprm_set_creds(bprm);
       if (retval)
               return retval;
       bprm->cred_prepared = 1;

       memset(bprm->buf, 0, BINPRM_BUF_SIZE);
       return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}


最終更新 2015/07/01 16:48:03 - north
(2012/11/29 18:30:46 作成)


検索

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