在python中将子连接到父通信

时间:2016-03-28 20:17:04

标签: python linux security process elevated-privileges

我的python程序需要提升权限,因此由root(使用setuid-binary-wrapper)启动。

为了尽可能减少攻击面(以及编码错误的影响),我决定将我的代码分成两部分:一部分将作为root执行,另一部分将执行regular user权限。问题是,代码是相互依赖的,因此需要secure two-way communication

我不知道这是否是正确的方法(欢迎其他想法),但我决定使用two processes - 一个具有提升权限的父进程和一个具有常规权限的子进程用户

想法:

  • 父进程由root启动并保留其权限
  • 父进程生成一个子进程,该进程将丢弃给普通用户(子进程无法重新获得root权限)
  • 子进程执行大部分工作,但是如果它需要执行具有root权限的内容,它会告诉父进程为它执行此操作

问题:

  • subprocess(。Popen)是否足够? multiprocessing会更适合吗?

  • 子进程和父进程如何以交互式安全方式进行通信(subprocess.PIPE是否安全)?

  • 您知道这种情况下的任何简单代码示例吗?

----------------------------------------------- --------------------------

根据Gil Hamilton的建议,我想出了以下代码

仍有一些问题:

  • 这样安全吗?我是否需要删除文件描述符等其他内容或os.setuid(<unprivileged UID>)是否足够?
  • 一般情况下,如果进程下降到这样的特定用户,该用户是否能够干扰丢弃的进程内存?

privileged.py

#!/bin/python
from multiprocessing import Process, Pipe
from unprivileged import Unprivileged

if __name__ == '__main__':
  privilegedProcessPipeEnd, unprivilegedProcessPipeEnd = Pipe()
  unprivilegedProcess = Process(target=Unprivileged(unprivilegedProcessPipeEnd).operate)
  unprivilegedProcess.start()

  print(privilegedProcessPipeEnd.recv())
  privilegedProcessPipeEnd.send("ok")
  print(privilegedProcessPipeEnd.recv())
  privilegedProcessPipeEnd.send("nok")

  privilegedProcessPipeEnd.close()
  unprivilegedProcessPipeEnd.close()
  unprivilegedProcess.join()

unprivileged.py

import os

class Unprivileged:

  def __init__(self, unprivilegedProcessPipeEnd):
    self._unprivilegedProcessPipeEnd = unprivilegedProcessPipeEnd

  def operate(self):
    invokerUid = os.getuid()

    if invokerUid == 0:
      # started by root; TODO: drop to predefined standard user
      # os.setuid(standardUid)
      pass
    else:
      # started by a regular user through a setuid-binary 
      os.setuid(invokerUid) # TODO: drop to predefined standard user (save invokerUid for future stuff)

    # os.setuid(0) # not permitted anymore, cannot become root again

    print("os.getuid(): " + str(os.getuid()))

    self._unprivilegedProcessPipeEnd.send("invoke privilegedFunction1")
    print(self._unprivilegedProcessPipeEnd.recv())
    self._unprivilegedProcessPipeEnd.send("invoke privilegedFunction2")
    print(self._unprivilegedProcessPipeEnd.recv())

    return

main.c(setuid-wrapper program):

#include <unistd.h>
#define SCRIPT_PATH "/home/u1/project/src/privileged.py"

int
main(int argc,
     char **argv) {
  return execv(SCRIPT_PATH, argv);
}

/* compile and run like this:
$ gcc -std=c99 main.c -o main
# chown root:root main
# chmod 6771 main
$ chmod +x /home/u1/project/src/privileged.py
$ ./main
*/

1 个答案:

答案 0 :(得分:1)

这可以通过Popen完成,但它有点笨拙的IMO,因为您无法控制Popen的流程转换。如果您依靠UID来减弱权限,则需要fork,然后在子项中调整您的UID,然后再调用其他子代码。

(没有正当理由你不能将你的子代码放在你用Popen调用的单独程序中并让它调整其UID作为第一步,它在我看来只是一种奇怪的方式来构造它。)

我建议您查看使用multiprocessing模块。该模块可以轻松创建一个新进程(它将为您处理fork)。然后,您可以轻松地放入调整UID的代码(见下文),然后您可以在相同的“代码库”中运行子代码。也就是说,您不一定需要来调用单独的程序。

multiprocessing模块还提供了自己的Pipe对象以及Queue对象,这两个对象都是进程间通信机制。两者都是安全的 - 在某种意义上说,没有外部用户可以加入它们(没有root权限)。但是,当然如果您的非特权子进程受到损害,它可以将任何想要的内容发送给父进程,这样您的特权父进程仍然需要验证/审核其输入。

multiprocessing模块的文档提供了几个简单的示例,可以帮助您入门。创建后,使用管道就像读取和写入文件一样简单。

至于调整UID,在调用您想要作为非特权用户运行的代码之前,这只是对子进程中os.setuid的单个调用。有关详细信息,请阅读setuid(2)credentials(7)手册页。