Моя программа на Python требует повышенных привилегий и поэтому запускается с правами root (с setuid-binary-wrapper).
Чтобы свести поверхность атаки (и влияние ошибок кода) к минимуму, я решил разделить свой код на две части: одна часть должна выполняться как root
, а другая — с разрешениями regular user
. Проблема в том, что код взаимозависим и поэтому нуждается в secure two-way communication
.
Я не знаю, правильный ли это подход (другие идеи приветствуются), но я решил пойти с two processes
- один родительский процесс с повышенными привилегиями и один дочерний процесс с привилегиями обычного пользователя.
Идея:
- Родительский процесс запускается пользователем root и сохраняет свои привилегии
- Родительский процесс порождает дочерний процесс, который переходит к обычному пользователю (дочерний процесс не может восстановить привилегии root)
- Дочерний процесс делает большую часть работы, но если ему нужно что-то выполнить с root-правами, он говорит родительскому процессу сделать это за него.
Вопросы:
Достаточно ли для этого
subprocess
(.Popen)? Может лиmultiprocessing
подойти лучше?Как дочерний процесс и родительский процесс могут взаимодействовать интерактивным и безопасным способом (безопасен ли
subprocess.PIPE
)?Знаете ли вы какие-нибудь простые примеры кода для этого сценария?
-------------------------------------------------------------------------
Основываясь на предложениях Гила Гамильтона, я придумал следующий код
Остались некоторые вопросы:
- Это безопасно? Нужно ли мне удалять дополнительные элементы, такие как файловые дескрипторы, или достаточно
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-обертки):
#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
*/
unprivilegedProcessPipeEnd
в родительском (привилегированном процессе) и закрытьprivilegedProcessPipeEnd
в дочернем, чтобы каждая сторона имела только свой собственный процесс. конец трубы открыт. - person Gil Hamilton   schedule 29.03.2016/dev/null
, если вам это не нравится. - person Gil Hamilton   schedule 29.03.2016setuid
для установки идентификатора пользователяMCH
, то пользовательMCH
сможет использоватьgdb
илиstrace
для непривилегированного потомка. Следовательно, они могут делать с памятью процесса практически все, что захотят. Вы всегда можете установить выделенный идентификатор пользователя-администратора (например,bin
иdaemon
в стандартной установке Linux) иsetuid
для этого пользователя. - person Gil Hamilton   schedule 29.03.2016