在 Ubuntu 14.04 中,passwd
可执行文件是
-rwsr-xr-x 1 root root 47032 gen 27 01:50 /usr/bin/passwd
可执行文件ping
是
-rwsr-xr-x 1 root root 44168 mag 7 2014 /bin/ping
因此(对于两者)运行进程的 uid 应该是 result root
,即使它们是从普通用户运行的。如果我passwd
从运行user1
,事实上,我得到
$ ps -aux | grep passwd
root 4317 0.0 0.0 85940 2004 pts/0 S+ 10:24 0:00 passwd
但如果我逃避ping
它user1
就不一样了:
$ ps -aux | grep ping
user1 4362 0.0 0.0 6500 632 pts/0 S+ 10:29 0:00 ping 192.168.8.1
为什么进程的 uidroot
在第一种情况下被设置为而不是在第二种情况下?
答案1
@rui-f-ribeiro 言论的大体意义是正确的,但细节则不然。细节很重要。 Ubuntu 使用这些软件包:
ping 实用程序重置名为 的函数中的权限limit_capabilities
,该函数由 ping 和 ping6 共享。相关的代码块如下所示:
if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
perror("ping: prctl");
exit(-1);
}
if (setuid(getuid()) < 0) {
perror("setuid");
exit(-1);
}
if (prctl(PR_SET_KEEPCAPS, 0) < 0) {
perror("ping: prctl");
exit(-1);
}
cap_free(cap_p);
cap_free(cap_cur_p);
#endif
uid = getuid();
euid = geteuid();
#ifndef CAPABILITIES
if (seteuid(uid)) {
perror("ping: setuid");
exit(-1);
}
#endif
也就是说(阅读源代码),ping
执行多个特权操作并放弃特权 - 但可以将其构建为根据个人喜好以不同的方式运行。
有趣的是,变更日志指出:
iputils (3:20121221-2) unstable; urgency=low
* Enable the CAP_NET_RAW capability and strip the setuid bit on ping and
ping6 binaries if possible.
故事passwd
相似,但细节不同。它是阴影工具套件,可能会删除以下权限change_root
:
/* Drop privileges */
if ( (setregid (getgid (), getgid ()) != 0)
|| (setreuid (getuid (), getuid ()) != 0)) {
fprintf (stderr, _("%s: failed to drop privileges (%s)\n"),
Prog, strerror (errno));
exit (EXIT_FAILURE);
}
但它只在特殊情况下才会这样做:
/*
* process_root_flag - chroot if given the --root option
*
* This shall be called before accessing the passwd, group, shadow,
* gshadow, useradd's default, login.defs files (non exhaustive list)
* or authenticating the caller.
*
* The audit, syslog, or locale files shall be open before
*/
在通常情况下,它确保它拥有特权并且不会删除它们(因为没有其他不需要特权的事情可做):
if (setuid (0) != 0) {
(void) fputs (_("Cannot change ID to root.\n"), stderr);
SYSLOG ((LOG_ERR, "can't setuid(0)"));
closelog ();
exit (E_NOPERM);
}
大多数实用程序不会重置 setuid/setgid 行为,假定它们未安装这些权限。
答案2
正如 @schily 所说,在 ping 实用程序(和其他实用程序)中,root 权限在不再需要后会被删除。这样做是出于安全原因。
从 ping.c - main() - 通过 getuid 和 setuid 调用删除用户 root。
getuid() 获取当前用户,root 执行 setuid() 将更改进程的 uid。
/*
* Pull this stuff up front so we can drop root if desired.
*/
if (!(proto = getprotobyname("icmp"))) {
(void)fprintf(stderr, "ping: unknown protocol icmp.\n");
exit(2);
}
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
if (errno==EPERM) {
fprintf(stderr, "ping: ping must run as root\n");
}
else perror("ping: socket");
exit(2);
}
#ifdef SAFE_TO_DROP_ROOT
setuid(getuid()); /* HERE RETURNING TO THE USER */
#endif
答案3
第二个进程的 uid 已被重置,因为在打开套接字后不再需要 root。
当您检查时,该passwd
实用程序仍然需要 root 权限。
如果您想验证这一点,则需要检查源代码,因为 uid 的重置可能完成得太快,以至于其他人有机会在重置之前验证 uid。