Отключает ли использование возможностей Linux LD_PRELOAD

В моей пользовательской среде предварительно загружена библиотека-перехватчик, которая запускает специальную реализацию вызовов bind(), connect() и т. д.

Проблема, которую я вижу, заключается в том, что всякий раз, когда приложение явно включает возможности с помощью команды setcap, выполнение приложения не позволяет предварительно загрузить библиотеку перехватчиков и вызывает libc по умолчанию connect().

Это ожидаемое поведение? Если да, что может быть причиной отключения LD_PRELOAD?

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


person Sunil Bojanapally    schedule 05.08.2013    source источник
comment
См. ответы на этот вопрос.   -  person scai    schedule 05.08.2013
comment
Вы можете написать программу-оболочку для целевого двоичного файла. Это более или менее должен быть setuid root. Он разветвит дочерний процесс, а затем выполнит целевой двоичный файл (с установленным LD_PRELOAD); целевой двоичный файл не имеет каких-либо файловых возможностей. Затем ваша библиотека предварительной загрузки связывается с дочерним процессом (например, через пару сокетов, скажем, fd 3), при этом дочерний процесс предоставляет необходимые возможности целевому процессу, а затем завершается (и библиотека предварительной загрузки пожинает дочерний процесс). Дайте мне знать, если вам нужен пример.   -  person Nominal Animal    schedule 06.08.2013
comment
@NominalAnimal Рад, если вы могли бы показать мне пример.   -  person Sunil Bojanapally    schedule 06.08.2013


Ответы (2)


Как ответил Оливер Мэтьюз, LD_PRELOAD отключен как для двоичных файлов setuid, так и для двоичных файлов, имеющих файловые возможности, по соображениям безопасности.

Чтобы предварительно загрузить библиотеку, сохраняя при этом возможности работы с файлами, у вас есть два варианта:

  1. Установить предварительно загруженную библиотеку setuid root

    (Динамический компоновщик Linux ld.so предварительно загружает библиотеки даже для двоичных файлов с setuid/file-capability, если библиотеки принадлежат пользователю root и помечены как set-uid.)

  2. Используйте корневую оболочку setuid

    Оболочка получает полные привилегии root (как реальные, так и эффективные идентификаторы пользователя и группы равны нулю) и сохраняет исходный реальный идентификатор пользователя и группы, например. переменная(ые) среды.

    Предварительно загруженная библиотека имеет конструктор, например.

    static void my_library_init(void) __attribute__((constructor));
    static void my_library_init(void)
    {
        /* ... */
    }
    

    который автоматически запускается до main() (но, возможно, после других конструкторов в других предварительно загруженных библиотеках или в библиотеках, от которых зависят предварительно загруженные библиотеки).

    Этот конструктор получает желаемые возможности, указанные либо через переменные среды (getenv(), cap_from_text()), либо через сам двоичный исполняемый файл (cap_from_file("/proc/self/exe")).

    Конструктор должен временно использовать prctl(PR_SET_KEEPCAPS, 1), чтобы сохранить возможности при изменении удостоверения, и сохранить возможности CAP_SETUID и CAP_SETGID, чтобы иметь возможность изменить удостоверение с root на пользователя и группу, указанные в переменных среды, прежде чем ограничить себя окончательным набором возможностей.

Оба варианта имеют очевидные соображения безопасности. Я рекомендую проверку работоспособности (и очистку LD_PRELOAD) в предварительно загруженном конструкторе библиотеки. Если что-то кажется подозрительным, используйте _exit(), чтобы немедленно прервать процесс.

В общем, я рекомендую первый вариант для простоты (как реализации, так и вопросов безопасности), но если есть какая-то причина, по которой его нельзя использовать, я могу предоставить доказательство концепции кода и для второго случая. (Я проверил, что оба варианта работают в Ubuntu 12.04.2 LTS с ядром x86-64 3.8.0-27, использующим файловую систему ext4.)

Надеюсь это поможет.

person Nominal Animal    schedule 06.08.2013
comment
Вариант 1, установка setuid root для предварительной загрузки библиотеки помогла мне в предварительной загрузке. Однако, когда я установил программируемость возможностей, которая имеет несколько изменений границ после exec, prctl(PR_SET_KEEPCAPS, 1L) не сохранила возможности. Выполнение моей тестовой программы с strace/gdb ./executable не смогло сохранить возможности и завершилось успешно, когда она запустилась с ./executable. Это связано с тем, что он имеет изменение границы с оболочки на strace/gdb, за которой следует ./executable. Хотелось бы знать, что заставляет способности не сохраняться при изменении границ. - person Sunil Bojanapally; 22.08.2013
comment
@SunEric: prctl(PR_SET_KEEPCAPS, 1L) действует только для следующего exec*(), так как PR_SET_KEEPCAPS всегда сбрасывается до 0 после вызова exec*(). Это упоминается на справочной странице man 2 prctl. - person Nominal Animal; 22.08.2013

Да, это из соображений безопасности (см. man sudo).

Вам придется обойти это, явно открыв библиотеку из вашего кода в начале main() с помощью dlopen (или обернув main или аналогичный).

person Oliver Matthews    schedule 05.08.2013
comment
Поправьте меня, если я ошибаюсь в понимании ld_preload: библиотека общих объектов предварительно загружается перед любыми стандартными библиотечными функциями. Если это так, то предварительно загружается необходимая библиотека, за которой следует libc.so; Поскольку вы предложили открыть библиотеку с помощью dlopen в приложении main(), разве символы уже не разрешены предварительно загруженной библиотекой или libc к моменту открытия dlopen? - person Sunil Bojanapally; 08.08.2013