赞
踩
在内核级别,用户 ID 和 组 ID 是每个线程的属性,然而,POSIX 标准要求同一个进程里的多个线程共享相同的认证信息(credentials)。
在 NPTL (Native POSIX Threads Library)线程实现中,通过对某些系统调用进行封装,从而支持了这一要求。这里的封装函数包括 setuid(),它通过信号技术确保了当其中某个线程改变认证信息后,其他所有线程也会跟着改变对应的认证信息。
在 glibc 中,setuid() 最终会调用 __nptl_setxid(),其定义在 nptl/allocatestack.c 中:
- int
- attribute_hidden
- __nptl_setxid (struct xid_command *cmdp)
- {
- ...
- list_for_each (runp, &__stack_user)
- {
- struct pthread *t = list_entry (runp, struct pthread, list);
- if (t == self)
- continue;
-
- signalled += setxid_signal_thread (cmdp, t);
- }
- ...
- }
上述函数调用了 setxid_signal_thread() 来给其他线程发信号,其定义在 nptl/allocatestack.c 中:
- static int
- setxid_signal_thread (struct xid_command *cmdp, struct pthread *t)
- {
- if ((t->cancelhandling & SETXID_BITMASK) == 0)
- return 0;
-
- int val;
- pid_t pid = __getpid ();
- INTERNAL_SYSCALL_DECL (err);
- val = INTERNAL_SYSCALL_CALL (tgkill, err, pid, t->tid, SIGSETXID);
-
- /* If this failed, it must have had not started yet or else exited. */
- if (!INTERNAL_SYSCALL_ERROR_P (val, err))
- {
- atomic_increment (&cmdp->cntr);
- return 1;
- }
- else
- return 0;
- }
可以看到上述函数通过 tgkill() 系统调用给当前进程的 t->tid 线程发了一个 SIGSETXID 信号。
SIGSETXID 信号的处理函数是 sighandler_setxid(),其定义在 ./nptl/nptl-init.c 中:
- /* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to
- tell each thread to call the respective setxid syscall on itself. This is
- the handler. */
- static void
- sighandler_setxid (int sig, siginfo_t *si, void *ctx)
- {
- int result;
- ...
- /* Safety check. It would be possible to call this function for
- other signals and send a signal from another process. This is not
- correct and might even be a security problem. Try to catch as
- many incorrect invocations as possible. */
- if (sig != SIGSETXID
- || si->si_pid != __getpid ()
- || si->si_code != SI_TKILL)
- return;
-
- INTERNAL_SYSCALL_DECL (err);
- result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, err, 3, __xidcmd->id[0],
- __xidcmd->id[1], __xidcmd->id[2]);
- ...
- }
上述函数通过 INTERNAL_SYSCALL_NCS() 宏进行了系统调用,其中 __xidcmd->syscall_no 是 setuid() 的系统调用号。
不过,如果在线程中 通过 syscall() 来旁路系统调用后,上述 POSIX 要求就不满足了,例如:
- //setuid(1001);
- syscall(SYS_setuid, 1001);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。