Цель issetugid?

Согласно справочным страницам для issetugid, вызов должен либо (1) предупреждать об изменениях uid/gid; или (2) предупреждение о возможной зараженной среде. Название функции предполагает третью цель.

Первый вопрос: какова цель?

Когда я смотрю на доступные реализации (например, в системе Linux в виде библиотеки, поскольку ядро ​​Linux не предоставляет API), я обнаруживаю следующее:

if (getuid() != geteuid()) return 1; 
if (getgid() != getegid()) return 1; 
return 0; 

На Солярисе это выглядит так:

return ((curproc->p_flag & SUGID) != 0);

Я немного подозрительный, но это отчасти потому, что трудно понять, какие функции, такие как geteuid и getegid, возвращают на всех платформах - например, BSD, Linux, Unix и Solaris.

Второй вопрос: является ли код Linux семантически эквивалентным коду Solaris?

Третий вопрос: реализованы ли geteuid и getegid на разных платформах одинаково? Как насчет систем, в которых я играю с тремя идентификаторами — реальным, эффективным и сохраненным?

Четвертый вопрос: здесь важен только эффективный идентификатор?

Если процесс запускается с UID = 0 и временно теряет привилегии, то в игру вступает идентификатор saved. Процесс, который временно удаляет root, не нуждается в exec и не должен быть испорчен.

Пятый вопрос: является ли процесс, который временно удаляет root, испорченным?

Шестой вопрос: следует ли считать испорченным процесс, эффективным идентификатором которого является сохраненный идентификатор?


person jww    schedule 18.05.2013    source источник
comment
Заявленная основная причина для вызова issetugid — гарантировать безопасное поведение при использовании в программах setuid или setgid или программах, которые запускаются с большими привилегиями после успешного выполнения exec. Возможно, вы делаете это более сложным, чем оно есть на самом деле.   -  person jim mcnamara    schedule 18.05.2013
comment
Спасибо, Джим. У меня неприятная ситуация — я проверяю код, использующий эту функцию. В Linux библиотека предоставляет API, а ядро ​​— нет. Но я не уверен, что он должен делать (поскольку он нестандартный), и делает ли он это эффективно (Демистификация Setuid).   -  person jww    schedule 18.05.2013


Ответы (2)


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

1) Цель issetugid() — сообщить библиотекам, используются ли они в программе, запущенной с повышенными привилегиями, чтобы они могли избежать рискованного поведения, такого как доверие к переменным среды LD_LIBRARY_PATH, NLSPATH и т. д., которые позволяют вызывающей стороне загружать модули. которые могут злоупотреблять повышенными привилегиями. Вы можете увидеть некоторые исторические обсуждения этого вопроса, например, в этой тред об ошибках безопасности ncurses 4.1 .

2) Этот код кажется менее безопасным, чем версии BSD и Solaris, поскольку он не учитывает сохраненные биты setid.

3) Вероятно, они имеют разные реализации на разных ядрах — посмотрите исходный код платформы, чтобы узнать.

4, 5 и 6) Нет, да, да — процесс, который может изменить свой euid или egid обратно на более высокие уровни, по-прежнему не должен доверять переменным среды, которые заставляют его загружать предоставленный пользователем код для их использования.

person alanc    schedule 19.05.2013
comment
~alanc - Шесть вопросов - это слишком много для системы, предназначенной для одного вопроса... Да, согласен. Это был компромисс между одним постом с несколькими вопросами или несколькими постами (с большим количеством копий/вставок) с одним вопросом. - person jww; 20.05.2013
comment
спасибо за ответ на вопрос 2 и 4. У меня были такие же чувства. Кроме того, считаете ли вы расу проблемой (это был другой, менее важный вопрос, поскольку Linux (и другие ОС) полны рас). - person jww; 20.05.2013
comment
Если код многопоточный, то да, проверка текущих результатов getuid/geteuid/и т.д. как показано в #2, может соревноваться с setuid/seteuid/и т.д. звонки и возвращать разные ответы в зависимости от того, кто выиграет гонку. - person alanc; 20.05.2013

Я не знаю issetugid(), но я могу научиться, читая справочные страницы BSD или Solaris. Функция исходит из OpenBSD.

1) Руководство по OpenBSD для issetugid(2) говорит: «Функция issetugid() возвращает 1, если процесс был сделан setuid или setgid в результате последнего или других предыдущих системных вызовов execve(). В противном случае она возвращает 0». Затем он предлагает использовать issetugid(), чтобы проверить, безопасно ли открывать файлы, указанные в переменных среды.

2) Нет, ваш код Linux и Solaris не эквивалентен. Процесс, выполняющий setuid, может установить свой реальный uid в эффективный uid без очистки переменных среды. Например, uid_t uid = geteuid(); setresuid(uid, uid, uid); установит как реальный uid, так и сохраненный uid в эффективный uid. Тогда ваш issetugid() в Linux вернет 0, а issetugid() в Solaris вернет 1.

Solaris проверяет флаг процесса SUGID во время выполнения. Illumos, бесплатный форк Solaris, устанавливает SUGID в src/uts/common/os/exec.c при выполнении файла. OpenBSD имеет аналогичную логику. В руководстве OpenBSD говорится:

Если дочерний процесс выполняет новый исполняемый файл, будет определен новый статус issetugid. Этот статус основан на разрешениях uid, euid, gid и egid существующего процесса, а также на режимах исполняемого файла. Если режимами нового исполняемого файла являются setuid или setgid, или если существующий процесс выполняет новый образ с uid != euid или gid != egid, новый процесс будет считаться issetugid.

Solaris и OpenBSD сравнивают идентификаторы во время выполнения. Ваш код Linux откладывает сравнение до вызова issetugid(), так что это не эквивалентно.

3) Кажется, что функции geteuid() и getegid() везде делают одно и то же; они просто возвращают эффективный идентификатор пользователя и эффективный идентификатор группы.

4) Сохраненные идентификаторы не имеют значения. Процесс мог изменить эти идентификаторы без очистки переменных среды. Ни один из реальных, эффективных или сохраненных идентификаторов не говорит нам, кто установил переменные среды для текущего процесса.

5) По крайней мере, в OpenBSD и Solaris процесс, временно удаляющий root, не становится испорченным. На странице руководства OpenBSD говорится:

На результат системного вызова issetugid() не влияют вызовы setuid(), setgid() или другие подобные вызовы. В случае fork() дочерний процесс наследует тот же статус.

На состояние issetugid() влияет только функция execve().

Когда процесс временно удаляет root с помощью setuid() или seteuid(), он не выполняет файл, поэтому его значение issetugid() не изменяется.

Но FreeBSD, DragonFly BSD и NetBSD определяют issetugid() более строго. Руководство по FreeBSD для issetugid( 2) говорит,

Процесс считается испорченным, если он был создан в результате системного вызова execve(2), в котором были установлены биты setuid или setgid (и в результате были предоставлены дополнительные привилегии), или если он изменил какие-либо из своих реальных, эффективных или сохраненные идентификаторы пользователя или группы с момента начала выполнения.

В этих системах процесс, удаляющий корень, принудительно устанавливает значение issetugid() равным 1.

6) Нет, эффективный идентификатор, равный сохраненному идентификатору, не портит процесс. Если бы это было так, то каждый процесс был бы испорчен, потому что каждый процесс имеет свой сохраненный идентификатор, установленный на его эффективный идентификатор во время выполнения.

person George Koehler    schedule 26.05.2015