setuid包装器的注意事项

时间:2010-11-18 07:00:58

标签: c setuid

我编写的Python扩展需要root访问才能执行单个硬件初始化调用。我宁愿不以root身份运行整个脚本只是为了我的扩展中的这一个调用,所以我想编写一个包装器来执行此初始化,然后再下载到用户权限并运行实际脚本。

我打算让这个包装器通过sudo运行,例如

$ sudo devwrap python somescript.py

我正在考虑类似的事情(更新以修复一些错误):

int main(int argc, char * argv[])
{
  if(argc < 2) return 0;

  int res = do_hardware_init();
  if(res != OK_VALUE)
  {
    // Print error message
    return HW_ERR;
  }

  const char *sudo_uid = getenv("SUDO_UID");
  if(sudo_uid)
  {
      int real_uid = (int) strtol(sudo_uid, NULL, 0);
      setuid(real_uid);
  }

  return execvp(argv[1], &argv[1]); // No return if successful

}

所以我有三个问题:

  1. 这看起来很健康吗?我通常不需要弄乱* uid()调用,所以我不熟悉常见的陷阱。 execvp电话看起来也有些奇怪,但据我所知,它在正确的地方有争议。)
  2. execvp手册页说“应该不应该直接通过应用程序访问环境数组” - 这会使getenv调用成为一个坏主意吗?
  3. 是否有比execvp更好的通话,所以我可以做sudo devwrap somescript.py(注意没有“python”)

1 个答案:

答案 0 :(得分:1)

  1. 理智的......更多以下。
  2. 使用getenv()不会直接访问environ数组 - 这是干净的。直接访问environ数组意味着'strcpy(buffer,environ [3])`或类似的东西。
  3. 如果脚本以shebang(也许是#!/usr/bin/env python)开头,你可以做你想做的事情 - 当然somescript.py必须是可执行的。
  4. 我在第一部分看到的问题取决于您如何处理硬件初始化中的错误。如果省略的错误处理没有退出,那么当你没有通过'sudo'运行时,你可以获得核心转储(或段错误),因为你在空指针上运行strtol()。如果do_hardware_init()本身在失败时退出,则没有问题,除非用户找到从“sudo”破坏环境的方法。我真的认为你应该验证环境,如果没有合理地设置SUDO_UID,则退出并出现错误。 root想要运行此扩展吗?

    我没有查看sudo的规范,看它确实设置了SUDO_UID环境变量 - 我认为你是正确的。

    用户输入此内容有什么后果?

    sudo devwrap ls
    

    它执行硬件初始化,重置UID,然后运行ls - 可能没有太大的危害,但可能不是你想到的。那有关系吗?你能控制一下吗?

    如果参数计数小于2,您可能应该退出错误,而不是成功退出。


    要求人们通过'sudo'运行扩展是非常不寻常的。

    您确定没有其他方法可以实现吗?初始化有哪些要求?它是针对所有流程执行一次,还是每个流程执行一次(因此链接是至关重要的)?


    你能简单地让devwrap程序SUID root吗?然后,您需要以不同方式重置UID:

    setgid(getgid());
    setuid(getuid());
    

    在执行命令之前删除任何SGID-ness和任何SUID-ness。这很难造成重大伤害。如果在没有SGID的情况下安装程序,则setgid()调用是强制性的,这一点尚不清楚,但它也没有任何危害。