2010-07-28 145 views
45

我有一个以root身份启动的守护程序(因此它可以绑定到低端口)。初始化后,我非常想让它为了安全起见而放弃root权限。删除root权限

任何人都可以指向我在已知的正确的在C代码片段会做到这一点?

我已经阅读过手册页,我已经在不同的应用程序中看到了这个不同的实现,它们都是不同的,有些非常复杂。这是与安全相关的代码,我真的不希望重蹈其他人的同样错误的重犯。我正在寻找的是一种最佳实践,即已知的,可移植的便携式库函数,我可以使用这些函数以获得正确的知识。这样的事情存在吗?

仅供参考:我以root身份启动;我需要改变在不同的用户和gid下运行;我需要让补充小组正确设置;之后我不需要改回根权限。

+5

这在unixes之间有很大的不同 - 有没有特别的?如果你需要一个“便携式”解决方案,它会很麻烦,你最好抓住例如来自OpenSSH的permanent_set_uid()函数 - 在uidswap中。c文件 – nos 2010-07-28 22:05:34

回答

43

为了删除所有权限(用户和组),您需要在用户之前删除组。

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)); 
} 

如果你是偏执狂:鉴于useridgroupid包含用户和要删除该组的ID,并假设有效ID是也根,这是通过调用setuid()setgid()完成,你可以尝试恢复你的root权限,这应该会失败。如果没有失败,你救助:

if (setuid(0) != -1) 
    fatal("ERROR: Managed to regain root privileges?"); 

另外,如果你还是偏执,你可能要seteuid()setegid()太多,但它不应该是必要的,因为的setuid()和setgid()已经设置了所有ID,如果该进程属于root。

补充组列表是一个问题,因为没有POSIX函数来设置补充组(有getgroups(),但没有setgroups())。有一个BSD和Linux扩展setgroups(),你可以使用,它涉及到你。

您还应该chdir("/")或任何其他目录,以便进程不会保留在根目录中。

由于你的问题是关于Unix的一般情况,这是非常普遍的方法。请注意,在Linux中,这不再是首选方法。在当前的Linux版本中,您应该将CAP_NET_BIND_SERVICE capability设置为可执行文件,并以普通用户身份运行它。不需要root权限。

+1

你也想设置gid - 这可能会因unix的不同而有所不同,因为setuid/setgid实际上与真实和已保存的id不一致 – nos 2010-07-28 22:06:45

+1

这确实需要一个可移植的解决方案,所以没有Linux能力fu允许,恐怕。我已经尝试了setuid()和setgid()的简单方法;它不会正确设置组(如果您不调用setgroups(),显然您最终可能仍然是某些根组的成员!)。 – 2010-07-28 22:14:54

+0

@nos谢谢。扩大到覆盖群体。如果进程由root拥有(如OP所述),或者它是setuid-root,则setuid()和setgid()已经设置了所有ID(真实,有效和保存)。这是在说明书中。否则,实现将不符合POSIX。 – Juliano 2010-07-28 22:59:59

1

这是我能做些什么最好:

#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; 
}