スティッキービット下のexec
サンプル
[root@north dir]# cat cmd.c
#include <stdio.h>
#include <unistd.h>
void get_id();
void main()
{
get_id("cmd");
}
void get_id(char* name)
{
printf("%-7s ", name);
printf("pid :%-4d ", getpid());
printf("uid :%-4d ", getuid());
printf("euid:%-4d ", geteuid());
printf("gid :%-4d ", getgid());
printf("egid:%-4d\n", getegid());
}
[root@north dir]# cat fork.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
void get_id(char* name);
void main(int argc, char *argv[])
{
int pid, stat;
get_id("parent");
pid = fork();
if (!pid) {
get_id("child");
execlp("./cmd.out", "cmd", NULL);
printf("no exec\n");
}
else {
waitpid(pid, &stat, 0);
}
}
void get_id(char* name)
{
printf("%-7s ", name);
printf("pid :%-4d ", getpid());
printf("uid :%-4d ", getuid());
printf("euid:%-4d ", geteuid());
printf("gid :%-4d ", getgid());
printf("egid:%-4d\n", getegid());
}
ユーザ/グループID
[root@north dir]# su -c id rootuid=0(root) gid=0(root) groups=0(root)
[root@north dir]# su -c id aaa
uid=200(aaa) gid=1003(aaa) groups=1003(aaa)
[root@north dir]# su -c id bbb
uid=1004(bbb) gid=1004(bbb) groups=1004(bbb)
root:x:0 :0 aaa :x:1003:1003 bbb :x:1004:1004
ファイルグループ変更
[root@north dir]# ls -l cmd.out- rwxr-xr-x 1 root root 7560 3月 8 14:07 cmd.out
[root@north dir]# chgrp bbb cmd.out
[root@north dir]# ls -l cmd.out
- rwxr-xr-x 1 root bbb 7560 3月 8 14:07 cmd.out
[root@north dir]# su -c ./fork.out aaa
parent pid :2783 uid :200 euid:200 gid :1003 egid:1003
child pid :2784 uid :200 euid:200 gid :1003 egid:1003
cmd pid :2784 uid :200 euid:200 gid :1003 egid:1003 <-cmdでのidはcmdを起動したプロセス(child)のid
S_ISUID
[root@north dir]# chmod u+s cmd.out[root@north dir]# ls -l cmd.out
- rwsr-xr-x 1 root bbb 7560 3月 8 14:34 cmd.out
parent pid :2839 uid :200 euid:200 gid :1003 egid:1003
child pid :2840 uid :200 euid:200 gid :1003 egid:1003
cmd pid :2840 uid :200 euid:0 gid :1003 egid:1003 <-euid:cmd.outユーザrootのinode->i_uid=0
S_ISGID
[root@north dir]# chmod g+xs cmd.out[root@north dir]# ls -l cmd.out
- rwsr-sr-x 1 root bbb 7560 3月 8 14:34 cmd.out
parent pid :2851 uid :200 euid:200 gid :1003 egid:1003
child pid :2852 uid :200 euid:200 gid :1003 egid:1003
cmd pid :2852 uid :200 euid:0 gid :1003 egid:1004 <-egid:cmdファイルのinode->i_gid
グループ実行不可ならS_ISGIDの効果無し
[root@north dir]# chmod g-xs cmd.out[root@north dir]# chmod g+s cmd.out
[root@north dir]# ls -l cmd.out
- rwsr-Sr-x 1 root bbb 7560 3月 8 14:34 cmd.out
parent pid :3285 uid :200 euid:200 gid :1003 egid:1003
child pid :3286 uid :200 euid:200 gid :1003 egid:1003
cmd pid :3286 uid :200 euid:0 gid :1003 egid:1003 <-g+sであってもg-xなら実行でのinode->i_gidは設定されない
捕捉
ファイル参照はeuid/egid下での実装となる。 MNT_NOSUIDのマウントファイルシステムはS_ISUID/S_ISGIDでも、inodeのidは設定されない。カーネル
struct task_struct {
:
const struct cred __rcu *cred;
:
} current;
struct cred {
atomic_t usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
atomic_t subscribers; /* number of processes subscribed */
void *put_addr;
unsigned magic;
#define CRED_MAGIC 0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
kuid_t uid; /* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */
unsigned securebits; /* SUID-less security management */
kernel_cap_t cap_inheritable; /* caps our children can inherit */
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
struct key *thread_keyring; /* keyring private to this thread */
struct key *request_key_auth; /* assumed request_key authority */
struct thread_group_cred *tgcred; /* thread-group shared credentials */
#endif
#ifdef CONFIG_SECURITY
void *security; /* subjective LSM security */
#endif
struct user_struct *user; /* real user ID subscription */
struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
struct group_info *group_info; /* supplementary groups for euid/fsgid */
struct rcu_head rcu; /* RCU deletion hook */
};
int do_execve(const char *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp,
struct pt_regs *regs)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execve_common(filename, argv, envp, regs);
}
static int do_execve_common(const char *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp,
struct pt_regs *regs)
{
struct linux_binprm *bprm;
struct file *file;
struct files_struct *displaced;
bool clear_in_exec;
int retval;
const struct cred *cred = current_cred();
if ((current->flags & PF_NPROC_EXCEEDED) &&
atomic_read(&cred->user->processes) > rlimit(RLIMIT_NPROC)) {
retval = -EAGAIN;
goto out_ret;
}
current->flags &= ~PF_NPROC_EXCEEDED;
retval = unshare_files(&displaced);
if (retval)
goto out_ret;
retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
goto out_files;
retval = prepare_bprm_creds(bprm);
if (retval)
goto out_free;
retval = check_unsafe_exec(bprm);
if (retval < 0)
goto out_free;
clear_in_exec = retval;
current->in_execve = 1;
file = open_exec(filename);
retval = PTR_ERR(file);
if (IS_ERR(file))
goto out_unmark;
sched_exec();
bprm->file = file;
bprm->filename = filename;
bprm->interp = filename;
retval = bprm_mm_init(bprm);
if (retval)
goto out_file;
bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
goto out;
bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
goto out;
retval = prepare_binprm(bprm);
if (retval < 0)
goto out;
retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
goto out;
bprm->exec = bprm->p;
retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out;
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
retval = search_binary_handler(bprm,regs);
if (retval < 0)
goto out;
current->fs->in_exec = 0;
current->in_execve = 0;
acct_update_integrals(current);
free_bprm(bprm);
if (displaced)
put_files_struct(displaced);
return retval;
out:
if (bprm->mm) {
acct_arg_size(bprm, 0);
mmput(bprm->mm);
}
out_file:
if (bprm->file) {
allow_write_access(bprm->file);
fput(bprm->file);
}
out_unmark:
if (clear_in_exec)
current->fs->in_exec = 0;
current->in_execve = 0;
out_free:
free_bprm(bprm);
out_files:
if (displaced)
reset_files_struct(displaced);
out_ret:
return retval;
}
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) && !current->no_new_privs) {
if (mode & S_ISUID) {
if (!kuid_has_mapping(bprm->cred->user_ns, inode->i_uid))
return -EPERM;
bprm->per_clear |= PER_CLEAR_ON_SETID;
bprm->cred->euid = inode->i_uid;
}
if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
if (!kgid_has_mapping(bprm->cred->user_ns, inode->i_gid))
return -EPERM;
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);
}






