我有一个以root身份启动的守护进程(因此它可以绑定到低端口)。在初始化之后,我非常希望出于安全原因而放弃root权限。
有人能指出我在C中的已知正确的代码片段吗?
我已阅读过这些手册页,我在不同的应用程序中查看了各种各样的实现,它们各不相同,其中一些非常复杂。这是与安全相关的代码,我真的不想重新发明其他人犯的错误。我正在寻找的是一个最佳实践,已知良好的,可移植的库函数,我可以使用它知道它将正确。这样的事情存在吗?
供参考:我是从根本开始的;我需要改变以在不同的uid和gid下运行;我需要正确设置补充组;之后我不需要改回root权限。
答案 0 :(得分:46)
要删除所有权限(用户和组),您需要在用户之前删除该组。鉴于userid
和groupid
包含您要删除的用户和组的ID,并假设有效ID也是root,这可以通过调用setuid()和{来完成{3}}:
if (getuid() == 0) {
/* process is running as root, drop privileges */
if (setgid(groupid) != 0)
fatal("setgid: Unable to drop group privileges: %s", strerror(errno));
if (setuid(userid) != 0)
fatal("setuid: Unable to drop user privileges: %S", strerror(errno));
}
如果你是偏执狂,你可以尝试恢复你的root权限,这应该会失败。如果没有失败,你就救助了:
if (setuid(0) != -1)
fatal("ERROR: Managed to regain root privileges?");
此外,如果您仍然偏执,您可能也想要setgid()和seteuid(),但它不是必需的,因为setuid()和setgid()已经设置了所有ID该过程归root所有。
补充组列表是一个问题,因为没有POSIX函数来设置补充组(有setegid(),但没有setgroups())。您可以使用BSD和Linux扩展getgroups(),这会让您感到担忧。
您还应该setgroups()或任何其他目录,以便该进程不会保留在根目录中。
由于你的问题一般是关于Unix的,所以这是非常通用的方法。请注意,在Linux中,这不再是首选方法。在当前的Linux版本中,您应该在可执行文件上设置chdir("/")
,并以普通用户身份运行它。无需root访问权限。
答案 1 :(得分:16)
您正在寻找这篇文章:
POS36-C. Observe correct revocation order while relinquishing privileges
不确定如何在不重复该页面内容的情况下最好地提供一些信息......
答案 2 :(得分:2)
这是我最擅长的:
#define _GNU_SOURCE // for secure_getenv()
int drop_root_privileges(void) { // returns 0 on success and -1 on failure
gid_t gid;
uid_t uid;
// no need to "drop" the privileges that you don't have in the first place!
if (getuid() != 0) {
return 0;
}
// when your program is invoked with sudo, getuid() will return 0 and you
// won't be able to drop your privileges
if ((uid = getuid()) == 0) {
const char *sudo_uid = secure_getenv("SUDO_UID");
if (sudo_uid == NULL) {
printf("environment variable `SUDO_UID` not found\n");
return -1;
}
errno = 0;
uid = (uid_t) strtoll(sudo_uid, NULL, 10);
if (errno != 0) {
perror("under-/over-flow in converting `SUDO_UID` to integer");
return -1;
}
}
// again, in case your program is invoked using sudo
if ((gid = getgid()) == 0) {
const char *sudo_gid = secure_getenv("SUDO_GID");
if (sudo_gid == NULL) {
printf("environment variable `SUDO_GID` not found\n");
return -1;
}
errno = 0;
gid = (gid_t) strtoll(sudo_gid, NULL, 10);
if (errno != 0) {
perror("under-/over-flow in converting `SUDO_GID` to integer");
return -1;
}
}
if (setgid(gid) != 0) {
perror("setgid");
return -1;
}
if (setuid(uid) != 0) {
perror("setgid");
return -1;
}
// change your directory to somewhere else, just in case if you are in a
// root-owned one (e.g. /root)
if (chdir("/") != 0) {
perror("chdir");
return -1;
}
// check if we successfully dropped the root privileges
if (setuid(0) == 0 || seteuid(0) == 0) {
printf("could not drop root privileges!\n");
return -1;
}
return 0;
}