netlib.narod.ru | < Назад | Оглавление | Далее > |
В предшествующем разделе был приведен пример проверки возможностей — фактически, одна и та же возможность проверялась дважды. Этой возможностью была CAP_SYS_NICE (строка 14104), которая управляет тем, должно ли быть позволено процессу устанавливать приоритеты (уровни требовательности) или политики планирования. Поскольку возможность CAP_SYS_NICE применяется не только к уровням требовательности, ее название не совсем правильно — хотя понятно, что установка планировщика — весьма близкое понятие, и что, скорее всего, вряд-ли одна возможность потребуется без другой.
Каждый процесс имеет три набора возможностей, хранящиеся в структуре struct task_struct (и определенные в строках с 16400 по 16401):
Действующий набор возможностей процесса — набор действий, которые ему разрешено выполнять в настоящее время; этот набор проверяется широко используемой функцией capable, которая определяется в строке 16738.
Разрешенный набор ограничивает возможности, которые могут быть предоставлены процессу в обычных условиях. Обычно этот набор не увеличивается, за одним исключением: если процесс имеет возможность CAP_SETPCAP, он может предоставить другим процессам любую возможность из своего разрешенного набора, даже если целевой процесс еще не имеет ее.
Если возможность находится в разрешенном наборе, но не находится в действующем наборе, процесс не имеет данную возможность непосредственно сейчас, но может получить ее, сделав запрос. К чему такое различие? Что ж, при первом освещении возможностей в начале этой главы мы кратко рассмотрели пример долговременного процесса, которому возможность требовалась только периодически, а не постоянно. Для того, чтобы процесс случайно не злоупотребил возможностью, он может дождаться, когда она ему потребуется, запросить ее, выполнить привилегированную операцию, а затем снова утратить возможность. Такой подход гораздо безопасней.
Наследуемый набор — не совсем то, что можно было бы предположить. Это не набор возможностей, которые родительский процесс передает дочерним процессам во время выполнения подпрограммы fork — в действительности в момент создания (т.е. непосредственно после выполнения fork) все три набора возможностей дочернего процесса совпадают с наборами родительского процесса. Наследуемый процесс выступает на сцену во время выполнения exec. Непосредственно перед вызовом exec наследуемый набор процесса помогает определить разрешенный и наследуемый наборы, которые процесс сохранит после выполнения exec — для более подробного ознакомления с этим процессом обратитесь к функции compute_creds (строка 9948). Обратите внимание, что сохранение возможности после выполнения exec лишь частично зависит от наследуемого набора процесса; это зависит также от разрядов возможностей, установленных в самом файле (или, по крайней мере, так планируется — это свойство реализовано еще не полностью).
Попутно отметим, что разрешенный набор всегда должен быть включать действующий и наследуемый наборы (или же совпадать с ними). (Строго говоря, это справедливо только по отношению к действующему набору. Один процесс может расширить наследуемый набор другого процесса так, что он больше не будет поднабором его разрешенного набора, но, насколько можно судить, это было бы бессмысленным, поэтому пока мы не будем рассматривать такую возможность.) Однако в отличие от того, что можно было бы предположить, действующий набор не обязательно должен включать наследуемый набор (или совпадать с ним). Т.е., после выполнения exec процесс может иметь возможность, которую он не имел до этого (хотя возможность должна входить в его разрешенный набор — т.е. она должна являться возможностью, которую оригинал мог бы получить для себя). Полагаю, что частично это связано с тем, что процессу не нужно временно получать возможность, которую ему негде использовать, разве что он может выполнить программу, которая в ней нуждается.
Возможности показаны на рис. 7.4. На этом рисунке показаны три набора возможностей для гипотетического процесса, причем разряды нумеруются справа налево. Процессу разрешено получить возможность CAP_KILL, которая позволяет ему прервать любой другой процесс, независимо от его владельца; но процесс пока не имеет этой возможности и не будет получать ее автоматически во время выполнения exec. В настоящий момент он имеет возможность вставлять и удалять модули ядра (используя CAP_SYS_MODULE), но также не будет получать ее во время выполнения exec. Процесс мог бы получить возможность CAP_SYS_NICE и будет получать ее во время выполнения exec (при условии, что соответствующие разряды возможностей файла установлены). И наконец, непосредственно сейчас процесс может изменить системное время (CAP_SYS_TIME) и будет сохранять эту возможность после выполнения exec (опять таки, при условии, что соответствующие разряды возможностей файла установлены). Ни этот процесс, ни один из тех, которые он может запустить с помощью функции exec, не может получить иные возможности, если только они не обеспечиваются каким-либо другим процессом, имеющим возможность CAP_SETPCAP.
Рис. 7.4. Наборы возможностей
Код, поддерживающий все эти свойства, в основном размещается в файле kernel/capability.c, начинающемся со строки 22460. Двумя основными функциями являются sys_capget (строка 22480), которая считывает возможности, и sys_capset (строка 22592), которая их устанавливает; эти функции освещаются далее в этом разделе. Как уже отмечалось, наследование возможностей после выполнения exec обеспечивается функцией compute_creds файла fs/exec.c (строка 9948).
Конечно, как правило, привилегированный процесс располагает всеми возможностями. Функция возможностей ядра обеспечивает привилегированный процесс структурированным способом избирательно предоставлять данному процессу только необходимые возможности, независимо от того, выполняется ли процесс в качестве привилегированного.
Интересное свойство возможностей состоит в том, что они могут использоваться для изменения «оттенка» системы. Например, установка возможности CAP_SYS_NICE для всех процессов позволила бы всем процессам поднимать свои приоритеты (и устанавливать свои планировщики, и т.п.). При изменении способа использования системы всеми процессами изменяется и сама система. Можете сами придумать новые возможности ядра, которые позволяют полнее использовать систему.
Одно, почти неуловимое преимущество возможностей заключается в том, что они проясняют исходный код. При проверке того, нужно ли разрешать текущему процессу устанавливать системное время, необходимость запрашивать вместо этого, будет ли текущий процесс запускаться привилегированным процессом — не слишком очевидный подход. Возможности позволяют более точно выразить, что имеется в виду. Существование возможностей даже помогает прояснить код, который запрашивает о идентификаторе пользователя или группы процесса, поскольку выполняющий это код предположительно интересует ответ на данный вопрос, а не возможные из него выводы. В противном случае, используя возможности, код запросил бы конкретно интересующие его данные. По мере дальнейшей интеграции возможностей в код ядра Linux это свойство станет более надежным.
13916: Здесь ядро распознает начало возможностей. Поскольку операторы #define снабжены развернутыми комментариями, мы не станем подробно останавливаться на каждом из них.
14153: Каждой возможности были просто присвоены последовательные целые числа, но поскольку они используются для адресации разрядов внутри целого значения без знака, с помощью макроса CAP_TO_MASK они преобразуются в степени числа 2.
14154: В основе установки и проверки возможностей лежит всего лишь набор простых манипуляций разрядами; некоторые макросы и встроенные функции, представленные в строках от этой до конца файла include/linux/capability.h, служат для прояснения манипуляций с разрядами.
22480: sys_capget принимает два аргумента: header типа cap_user_header_t (строка 13878) является указателем на структуру, определяющую версию используемых возможностей и обеспечивающую PID целевого процесса, dataptr типа cap_user_data_t (строка 13884) также является указателем на структуру — эта структура содержит действующий, разрешенный и наследуемый наборы. Посредством второго указателя sys_capget возвращает информацию.
22492: В случае несоответствия версий посредством указателя header sys_capget возвращает используемую версию, а затем — ошибку EINVAL (или EFAULT, если ей не удалось скопировать информацию о версии в область вызывающей функции).
22509: Идентифицирует процесс, о возможностях которого хочет узнать вызывающая функция; если pid не равен 0 или PID текущего процесса, sys_capget ищет его.
22520: Если ей удалось обнаружить целевой процесс, она копирует его возможности во временную переменную data.
22530: Если до сих пор все шло хорошо, она копирует возможности обратно в область пользователя в адрес, переданный аргументом dataptr. После этого она возвращает переменную error — как обычно, это 0, если все нормально, или, в противном случае — номер ошибки.
22592: Аргументы sys_capset почти полностью аналогичны аргументам функции sys_capget. Различие состоит в том, что теперь переменная data (которая больше не называется dataptr) является константой const. Подозреваю, что это было сделано для того, чтобы помешать изменению объекта, на который указывает data, но в действительности это предотвращает изменение самой переменной data, чтобы она не могла указывать на что-либо другое.
22600: Как и в случае с функцией sys_capget, функция sys_capset убеждается, что ядро и вызывающий процесс используют совместимые версии системы возможностей. Если это не так, она отклоняет попытку.
22613: Если значение pid является ненулевым, показывая, что вызывающий процесс желает установить возможности другого процесса, в большинстве случаев попытка должна быть отклонена. sys_capset разрешает попытку в любом случае, если вызывающий процесс имеет возможность CAP_SETPCAP и, следовательно, ему разрешается устанавливать возможности любого процесса. Первая часть этой проверки несколько слишком строга: она должна была бы также принимать pid, если он равен pid текущего процесса.
22616: Копирует новые возможности из области пользователя и возвращает ошибку, если это не удается.
22627: Аналогично коду sys_capget, начинающемуся в строке 22509, sys_capset идентифицирует процесс, о возможностях которого хочет узнать вызывающий процесс. Различие состоит в том, что в данном случае допускаются отрицательные значения pid для указания групп процессов (или же –1 указывает все процессы). В таком случае target по прежнему устанавливается в значение current, чтобы в последующих вычислениях использовались возможности текущего процесса.
22642: Теперь функция должна убедиться, что новые наборы возможностей принимают допустимые и внутренне непротиворечивые значения. Эта проверка удостоверяет, что наследуемый набор нового процесса не содержит ничего нового, если только новое свойство не входит в разрешенный набор вызывающего процесса. Таким образом, функция не пропускает никакие возможности, отсутствующие в вызывающем процессе.
22650: Аналогично, функция sys_capset убеждается, что разрешенный набор целевого процесса не содержит ничего, что в него не входило ранее, если только вызывающий процесс также не располагает этой возможностью. Таким образом, функция снова не пропускает никакие возможности, отсутствующие в вызывающем процессе.
22658: Вспомните, что действующий набор процесса должен быть поднабором его разрешенного набора. Это свойство поддерживается здесь.
22666: Теперь функция sys_capset готова выполнить запрошенные изменения. Отрицательное значение pid означает, что функция изменяет возможности более чем для одного процесса — для всех процессов, если значение pid равно –1; или для всех процессов в группе процессов, если pid имеет любое другое отрицательное значение. В этих случаях работа выполняется функциями cap_set_all (строка 22561) или cap_set_pg (строка 22539), соответственно; эти функции просто выполняют цикл по соответствующим наборам процессов, перезаписывая наборы возможностей для каждого из них так же, как для единственного процесса.
22676: Если pid имеет положительное значение (или 0, означающее текущий процесс), наборы возможностей присваиваются только целевому процессу.
netlib.narod.ru | < Назад | Оглавление | Далее > |