И наконец, если привилегированная программа сталкивается с непредвиденной ситуацией, обычно она должна сделать одно из двух: либо завершиться, либо, если это сервер, отклонить клиентский запрос. Попытки исправить неожиданную проблему обычно вынуждают делать предположения, которые в некоторых случаях могут быть необоснованными, что приводит к созданию лазеек в безопасности. В таких ситуациях более безопасными являются завершение программы или запись сообщения в журнал и отклонение запроса клиента.
Привилегированные программы могут работать с системными ресурсами, недоступными обычным пользователям. В случае взлома такой программы безопасность системы может оказаться под ударом. В данной главе был дан целый ряд рекомендаций по написанию привилегированных приложений. Эти рекомендации имеют двойное назначение: во-первых, снизить вероятность взлома программы с повышенными привилегиями и, во-вторых, минимизировать ущерб в случае, если этого не удалось избежать.
В [Viega & McGraw, 2002] рассматривается целый ряд тем, связанных с проектированием и реализацией безопасного программного обеспечения. Общие сведения о безопасности в UNIX-системах, а также целую главу, посвященную методикам безопасного программирования, можно найти в книге [Garfinkel et al., 2003]. Тема компьютерной безопасности также затронута в [Bishop, 2005], хотя книга того же автора, [Bishop, 2003], дает более широкий охват. [Peikari & Chuvakin, 2004] делает акцент на различных способах атаки системы. В книгах [Erickson, 2008] и [Anley, 2007] дается исчерпывающий разбор вредоносных программ (экспоитов) и предоставляется достаточно информации, благодаря которой мудрый программист сможет от них защититься. В документе [Chen et al., 2002] описывается и анализируется модель установки пользовательских идентификаторов в UNIX. Документ [Tsafrir et al., 2008] пересматривает и дополняет обсуждение различных аспектов, изложенных в [Chen et al., 2002]. [Drepper, 2009] содержит богатый набор советов о безопасном и защитном программировании в Linux.
Несколько источников информации о написании безопасных программ можно найти в Сети.
• Мэтт Бишоп сделал целый ряд публикаций, связанных с безопасностью. Они доступны по адресу nob.cs.ucdavis.edu/~bishop/secprog. Наиболее интересная из них носит название «Как написать программу, устанавливающую UID» (англ. How to Write a Setuid Program; изначально опубликована в журнале;login, № 12 (1), январь/февраль 1896 г.). Эта статья, несмотря на свой возраст, содержит огромное количество полезных советов.
• Руководство по безопасному программированию для Linux и Unix (англ. Secure Programming for Linux and Unix HOWTO) доступно по адресу www.dwheeler.com/secure-programs/.
• Перечень полезных рекомендаций по написанию программ, устанавливающих пользовательские идентификаторы, находится на сайте www.homeport.org/~adam/setuid.7.html.
38.1. Войдите в систему как обычный, непривилегированный пользователь, создайте исполняемый файл (или скопируйте уже существующий — например, /bin/sleep) и установите ему бит, позволяющий менять идентификатор пользователя (chmod u+s). Попробуйте отредактировать этот файл (например, cat >> file). Введите команду ls — l и посмотрите, что произошло с правами доступа. В чем причина?
38.2. Напишите программу, устанавливающую идентификатор администратора, похожую на команду sudo(8). Она должна принимать параметры и аргументы командной строки следующего вида:
$ ./douser [-u user] program-file arg1 arg2…
Программа douser запускает файл program-file, передавая ему аргументы так, как будто он был запущен пользователем user (если опустить параметр — u, пользователем по умолчанию должен быть администратор). Перед выполнением файла program-file программа douser должна запросить у пользователя пароль и сверить введенную строку с содержимым стандартного файла для хранения паролей (см. листинг 8.2). После этого она должна установить все идентификаторы UID и GID, принадлежащие заданному пользователю.
39. Система возможностей
Эта глава посвящена системе возможностей в Linux, которая разбивает традиционную для UNIX структуру привилегий («все или ничего») на отдельные сегменты, которые могут включаться и отключаться независимо друг от друга. Использование возможностей позволяет программе выполнять только строго определенные привилегированные операции.
Традиционная для UNIX структура привилегий разделяет процессы на две категории: те, чей действующий пользовательский идентификатор равен 0 (то есть принадлежащие администратору) и которые обходят любые проверки прав доступа, и все остальные, права доступа которых проверяются в соответствии с их пользовательскими и групповыми идентификаторами.