Как выполнять манипуляции с файлом / каталогом с учетом прав пользователя?

У меня есть серверное приложение, которое будет работать под системной учетной записью, потому что в любой момент времени оно будет обрабатывать запросы от имени любого пользователя в системе. Эти запросы состоят из инструкций по управлению файловой системой.

Вот в чем загвоздка: программа должна помнить о привилегиях этого конкретного пользователя при выполнении действий. Например, joe не должен иметь возможность изменять /home/larry, если его права доступа 755.

В настоящее время моя стратегия такова

  • Получить владельца / группу файла
  • Сравните его с идентификатором пользователя / идентификатором группы пользователя, пытающегося выполнить действие.
  • Если они совпадают (или не совпадают), используйте соответствующую часть поля разрешений в файле, чтобы разрешить или запретить действие.

Это мудро? Есть ли более простой способ сделать это?

Сначала я думал о том, чтобы несколько экземпляров приложения работали под учетными записями пользователя, но это не вариант, потому что тогда только один из экземпляров может прослушивать данный TCP-порт.


person Nathan Osman    schedule 31.01.2011    source источник


Ответы (3)


Взгляните на самбу, чтобы увидеть, как это можно сделать. Демон samba запускается от имени пользователя root, но при первой возможности разветвляется и принимает учетные данные обычного пользователя.

В системах Unix есть два отдельных набора учетных данных: реальные идентификаторы пользователя / группы и эффективные идентификаторы пользователя / группы. Реальный набор определяет, кем вы являетесь на самом деле, а эффективный набор определяет, к чему вы можете получить доступ. Вы можете изменить эффективный uid / gid по своему усмотрению, если вы используете root, включая обычного пользователя, и обратно, поскольку ваши настоящие идентификаторы пользователя / группы остаются root во время перехода. Таким образом, альтернативный способ сделать это в одном процессе - использовать seteuid/gid для применения разрешений разных пользователей туда и обратно по мере необходимости. Если демон вашего сервера работает от имени пользователя root или имеет CAP_SETUID, это будет разрешено.

Однако обратите внимание, что если у вас есть возможность переключать эффективный uid / gid по прихоти и ваше приложение подрывается, тогда эта подрывная деятельность может, например, переключить эффективный uid / gid обратно на 0, и у вас может быть серьезная уязвимость в системе безопасности. Вот почему разумно как можно скорее навсегда отказаться от всех привилегий, включая ваш настоящий пользовательский uid / gid.

По этой причине нормально и безопаснее иметь один прослушивающий сокет, работающий от имени пользователя root, а затем отключиться и изменить как реальный, так и эффективный идентификаторы пользователей, вызвав setuid. Тогда он не может вернуться назад. У вашего разветвленного процесса будет сокет, который был accept()ed, поскольку это вилка. Каждый процесс просто закрывает ненужные файловые дескрипторы; сокеты остаются активными, поскольку на них ссылаются файловые дескрипторы в противоположных процессах.

Вы также можете попытаться обеспечить соблюдение разрешений, изучив их по отдельности, но я надеюсь, что очевидно, что это потенциально подвержено ошибкам, имеет множество крайних случаев и с большей вероятностью пойдет не так (например, это не будет работать со списками ACL POSIX. если вы специально не реализуете это тоже).

Итак, у вас есть три варианта:

  1. Вилка и _5 _ / _ 6_ нужному пользователю. Если требуется связь, используйте pipe(2) или socketpair(2) перед форк-вилкой.
  2. Не используйте форк и _9 _ / _ 10_ по мере необходимости (менее безопасный: более вероятно, что ваш сервер может быть скомпрометирован случайно).
  3. Не связывайтесь с системными учетными данными; выполнить принудительное применение разрешений вручную (менее безопасно: вероятность неправильной авторизации выше).

Если вам нужно связаться с демоном, то, хотя это может быть сложнее сделать это через сокет или канал, первый вариант действительно является правильным безопасным способом сделать это. См., Например, как ssh разделяет привилегии. Вы также можете подумать, можете ли вы изменить свою архитектуру, чтобы вместо какого-либо взаимодействия процесс мог просто использовать некоторую память или дисковое пространство.

Вы упомянули, что рассматривали возможность запуска отдельного процесса для каждого пользователя, но вам нужен один прослушивающий TCP-порт. Вы все еще можете это сделать. Просто пусть главный демон прослушивает порт TCP и отправляет запросы каждому пользовательскому демону и обменивается данными по мере необходимости (например, через сокеты домена Unix). Фактически это было бы почти то же самое, что иметь разветвляющийся мастер-демон; Думаю, последнее оказалось бы проще реализовать.

Дальнейшее чтение: credentials(7) справочная страница. Также обратите внимание, что в Linux есть файловая система uid / gids; это почти то же самое, что и эффективный uid / gids, за исключением других вещей, таких как отправка сигналов. Если у ваших пользователей нет доступа к оболочке и они не могут запускать произвольный код, вам не нужно беспокоиться о разнице.

person Robie Basak    schedule 13.02.2011
comment
Спасибо за очень хорошо написанный ответ! Можно ли создать главный процесс, который прослушивает порт, а затем перенаправляет данные другому процессу через другое соединение сокета? Это то, что вы говорите в предпоследнем абзаце? - person Nathan Osman; 14.02.2011
comment
Да, верно, и вы могли бы это сделать. Однако, исходя из всего, что вы сказали, я почти уверен, что вам было бы проще и проще реализовать мастер-демон прослушивания разветвления и иметь права отбрасывания дочерних элементов. Разветвленный процесс автоматически получит доступ к сокету запроса, как и главный демон; не нужно сообщать эту часть. - person Robie Basak; 14.02.2011
comment
Хорошо. Я ищу решение, которое также будет хорошо работать с Qt, хотя это не является обязательным требованием. Поэтому я принимаю это во внимание при выборе правильного подхода. - person Nathan Osman; 14.02.2011
comment
Насколько я знаю, выполнение этого с fork() будет отлично работать с Qt, но, конечно, тогда он перестанет быть кроссплатформенным, поскольку в Windows нет такой концепции. - person Robie Basak; 14.02.2011
comment
Спасибо! Я принял ваш ответ и назначил награду - ваш ответ был чрезвычайно полезным. - person Nathan Osman; 16.02.2011

Я бы получил свой сервер fork () и немедленно setuid(uid) отказался бы от привилегий root. Тогда любые манипуляции с файлами будут осуществляться от имени пользователя, которым вы стали. Поскольку вы являетесь дочерним по отношению к серверу, вы все равно будете иметь дочерний сокет accept () ed, в котором будет продолжаться запрос (и я предполагаю, что ответ). Это (очевидно) требует привилегий root для демона.

В этом случае передача дескрипторов файлов между процессами кажется излишне сложной, поскольку у дочернего элемента уже есть дескриптор «запроса».

person CoreyStup    schedule 31.01.2011
comment
Хммм ... это определенно возможно. - person Nathan Osman; 31.01.2011

Позвольте одному серверу работать на предварительно заданном порте сервера и порождать дочерние процессы для пользователей, которые входят в систему. Дочерние процессы должны отказаться от привилегий и олицетворять пользователя, вошедшего в систему. Теперь дочерние процессы больше не могут причинить вреда.

person Daniel    schedule 31.01.2011
comment
Да, но как соединения передаются с сервера дочерним процессам? - person Nathan Osman; 31.01.2011
comment
Я считаю, что это работает, позволяя корневому процессу настроить сокет, а затем просто передать дескриптор файла дочернему процессу. Я не знаю, как именно это делается, но по крайней мере PostgreSQL работает аналогичным образом, где для каждого соединения создается дочерний процесс для его обработки. И я не могу поверить, что весь трафик должен проходить через основной процесс, поэтому там будет проходить какой-то fd. - person Daniel; 31.01.2011
comment
Ну ... Я разберусь. Это действительно усложняет ситуацию, потому что тогда дочерние процессы должны взаимодействовать с родителем. - person Nathan Osman; 31.01.2011