Почему исходный код Bash не нуждается в бите выполнения?

57

С помощью Bash sourceможно выполнить скрипт без установленного бита выполнения. Это задокументированное и ожидаемое поведение, но не против ли это использования бита выполнения?

Я знаю, sourceэто не создает подоболочки.

Motte001
источник
2
Тот факт, что chmodвы можете установить права доступа (включая `x) с восьмеричным числом, дает некоторое представление о том, с какой эры это происходит. Я не удивлюсь, если бы он начинался как быстрый и грязный индикатор «это двоичный файл, который вы можете выполнить», со времен, предшествовавших тому, как была изобретена она, но у меня нет никаких доказательств этого
вставил
9
С другой стороны, когда SUID вступает в игру, различные уровни защиты для разных категорий пользователей становятся более важными. Если хотите, читайте мою программу SUID. Но если вы выполняете его, просто читая его, силы SUID не приходят вместе с ним
инфикс
@infixed Загрузчик программ Linux даже не будет смотреть на шебанг, если не установлен бит выполнения. (Чтобы немного подрезать мой собственный рог: см. Здесь .)
Кайл Стрэнд,
У вас также может быть + xr, но это немного странно, потому что обычно можно прочитать двоичный файл, введя код в запущенный процесс
Niklas B.
@KyleStrand путем « если вы запустите его с помощью простого прочтения его, SUID силы не идут вместе с ним» я что - то вдоль линий предполагаяcp /sbin/suidexecutable /tmp/mycopy; /tmp/mycopy
инфиксальный

Ответы:

36

Баш - переводчик; он принимает входные данные и делает все, что хочет. Не нужно прислушиваться к исполняемому биту. Фактически, Bash является переносимым и может работать в операционных системах и файловых системах, которые не имеют понятия о исполняемом бите.

Что заботится о исполняемом бите, так это ядро ​​операционной системы. Когда ядро ​​Linux выполняет exec, например, проверку того, что файловая система не смонтирована с noexecопцией, оно проверяет исполняемый бит программного файла и обеспечивает выполнение любых требований, предъявляемых модулями безопасности (такими как SELinux или AppArmor).

Обратите внимание, что исполняемый бит является довольно произвольным видом управления. Например, в системе Linux x86-64 вы можете обойти проверку ядром исполняемого бита, явно вызвав /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2интерпретатор :

cp /bin/ls /tmp/
chmod -x /tmp/ls
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /tmp/ls

Это несколько похоже на получение исходного кода Bash в Bash, за исключением того, что ld.soэто интерпретатор, а выполняемый код - это машинный код в формате ELF.

200_success
источник
3
Использование загрузчика в качестве «интерпретатора» «байт-кода», который оказывается просто исполняемым двоичным файлом… потрясающе. Спасибо тебе за это.
Кайл Стрэнд,
@KyleStrand может быть несколько уровней косвенности (разница между хранимой программой и загруженным процессом и тем, как отображается память, различные супервизоры, виртуальные машины и т. Д.), Но в принципе машинный код может выполняться непосредственно процессором (без микрокода, и т.д.) и, следовательно, машинный код не интерпретируется: аппаратное обеспечение понимает его как есть - это родной язык - переводчики не нужны.
Jfs
2
@JFSebastian Да, мы знаем, что это нативный код, поэтому «интерпретатор» в кавычках. Работа ld.soсостоит в том, чтобы сделать динамическое связывание.
200_success
@JFSebastian Что сказал 200_success. Кроме того, это то, что я имел в виду под «настоящим исполняемым двоичным файлом», и я также поместил «байт-код» в кавычки (поскольку AFAIK «байт-код» обычно не используется для ссылки на фактический скомпилированный нативно исполняемый двоичный код). Также неплохое имя пользователя.
Кайл Стрэнд,
@JFSebastian Я верю, что современные x86-подобные процессоры на самом деле являются внутренними RISC, а набор команд CISC старого стиля в основном управляет выполнением микрокода соответствующих команд RISC, где одна инструкция CISC может привести к выполнению многих команд RISC. Таким образом, в некотором смысле называть то, что процессор делает с машинным кодом RISC, «интерпретацией» является, если не полностью правильным с технической точки зрения, то, по крайней мере, допустимой мысленной моделью. AES-NI , пожалуй, один из самых экстремальных примеров: одна инструкция CISC на раунд AES!
CVn
57

sourceили эквивалентная, но стандартная точка. не выполняет сценарий, а читает команды из файла сценария, а затем выполняет их построчно в текущей среде оболочки.

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

Бит выполнения требуется только при запуске скрипта. Здесь оболочка будет fork()новый процесс, затем с помощью execve()функции для создания нового образа процесса из сценария, который должен быть обычным, исполняемым файлом.

cuonglm
источник
5
@alexis: я не уверен, что действительно следую. Если вы это сделаете bash < script, вы получите по сути тот же результат, что и source script. Какую защиту обеспечивает проверка битов выполнения?
Заметный компилятор
27
@alexis, это не работа переводчика - проверять права доступа к сценариям, которые он интерпретирует. Ничто не делает этого - ни Python, ни Ruby, ни виртуальная машина Java, ни другие случайные оболочки. Бит выполнения определяет, может ли execvсемейство системных вызовов OS * использоваться с исполняемым файлом, а не будет ли его выполнять интерпретатор. Зачем вводить людей в заблуждение (и нарушать способность оценивать код, передаваемый из нефайловых источников), нарушая соглашения?
Чарльз Даффи
14
@alexis, ... кроме того, есть ценный смысл иметь библиотеку оболочки, которая не является исполняемой: она говорит: «Я библиотека, а не команда - исходи из меня, не выполняй меня». В противном случае вам потребуется написать код для обнаружения и устранения неправильного использования кода, предназначенного только для источника, но, не имея разрешения + x, вы можете быть уверены, что пользователи не смогут (не будут) злоупотреблять библиотекой таким образом.
Чарльз Даффи
6
@alexis «это было решение авторов» Только в том смысле, что любое другое возможное количество кода, которое не было включено, было решением. Оболочка открывает файл для чтения, для чтения команд из него. Я не ожидал, что он проверит разрешение на чтение, он просто попытается открыть файл и потерпит неудачу, если не сможет. Аналогично, он не проверяет разрешение на запись или выполнение, поскольку эти проверки не имеют отношения к чтению файла.
Рэнди Оррисон
2
@alexis Это используется на практике, хотя, например, с помощью python virtualenv, скрипт для его активации предназначен для получения, а не для выполнения, и, следовательно bin/activate, не имеет исполняемого бита. Скрипты могут быть библиотеками или чем-то подобным. Я думаю, наличие .sh также может быть сигналом, но иметь минимум пушек - это хорошо: приятно, что ./bin/activateвместо этого невозможно бежать случайно. bin/activate
daboross
20

Исполняемый бит (в отличие от остальных) в файлах, отличных от unsetuid, не является механизмом безопасности. Все, что вы можете прочитать, вы можете запускать косвенно, а Linux позволит вам косвенно читать все, что вы можете запустить, но не можете читать напрямую (этого должно быть достаточно, чтобы пробить дыру в концепции неустановленного (g) uid x-bit как мера безопасности).

Это скорее для удобства: пусть система запускает его для меня напрямую, если бит установлен, в противном случае мне нужно сделать это косвенно ( bash the_script;или некоторый взлом, чтобы получить образ памяти исполняемого файла без разрешения на чтение ).

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

Очевидно, однако, что многие разработчики разделяемых библиотек разделяют ваше мышление, и, следовательно, многие системы требуют, чтобы разделяемые библиотеки, которые по сути являются нативным эквивалентом неисчерпаемых оболочек, были помечены как исполняемые для того, чтобы их можно было использовать. См. Почему исполняемые библиотеки общего доступа? ,

PSkocik
источник
3
@ Motte001 Они могут выполнить эти вложения, если они действительно хотят. Это удобство.
PSkocik
2
Исполняемый бит не для безопасности: если у меня есть файл, я свободен для chmodнего и делаю его исполняемым. Он предназначен для сортировки данных из программ, поэтому вопрос ОП является обоснованным.
Алексис
1
@ Motte001 Это еще одна функция безопасности браузера / почтового приложения с графическим интерфейсом - можно легко решить, что двойной щелчок на любом файле, имя которого заканчивается на «.sh», будет выполнен в терминале (в стиле Windows) или, наоборот, щелчок любого файла в каталоге «загруженные вложения» вызовет встроенное предварительное приложение в песочнице. Этот xбит - просто дополнительное место, где он может прочитать / написать подсказку о том, что делать.
IMSoP
3
Одна из причин, по которой нам нужен исполняемый бит, - запуск программ setuid, особенно тех, которые принадлежат пользователю root. Хотя вы, безусловно, можете выполнять их самостоятельно, вам нужна ОС для их запуска, чтобы у них были необходимые разрешения. В некоторых других случаях это действительно просто удобство.
Валид Хан
2
«Linux позволит вам читать все, что вы можете запустить через / proc» - Ну, мое стандартное ядро ​​Debian не делает: /tmp$ cp /bin/cat ./cat ; chmod a-rw ./cat ; ./cat & cp /proc/$!/exe /tmp/cat2->cp: cannot stat ‘/proc/16260/exe’: Permission denied
ilkkachu
9

Это хороший вопрос! Unix использует исполняемый бит, чтобы различать программы и данные. ОС не требует бита выполнения, так как исходный сценарий не передается в ОС для выполнения в качестве нового процесса. Но оболочка обрабатывает исходный скрипт как программу и ищет $PATHфайл, который вы хотите получить. Таким образом, самой оболочке могло потребоваться разрешение на выполнение для исходных файлов; но это не так.

Вопрос должен был подняться давно. Дизайн оболочки Bourne был результатом «длинной последовательности изменений, диалогов, дискуссий» среди обитателей Bell Labs, и многие дизайнерские решения обсуждались SR Bourne и другими на протяжении многих лет. К сожалению, мой быстрый взгляд не нашел обсуждения исходной функции (в мою защиту трудно гуглить за это). Я обнаружил, что "." Команда не появляется в этом раннем введении в оболочку самого Борна, но она присутствует в более зрелой версии Версии 7 .

Отсутствие авторитета, вот моя собственная интерпретация:

  1. Команда ., иначе говоря source, является текстовым включением (как #includeв препроцессоре C) в исходный код исполняемого скрипта или интерактивного сеанса. Таким образом, включенный файл, возможно, не «исполняется».

  2. Философия Unix всегда заключалась в том, чтобы дать программистам достаточно веревки, чтобы повеситься. Слишком много ручных и произвольных ограничений просто мешают. Лишь сравнительно недавно некоторые распространенные дистрибутивы rm -r /отказываются делать то, что вы просите. (Эта команда говорит, rmчтобы удалить все на вашем компьютере. Не пытайтесь сделать это как root! Или еще лучше, совсем нет.) Так что, возможно, Bourne at al. просто решил, что когда вы пытаетесь найти файл, вы должны знать, что вы делаете. Это также позволяет избежать ненужной работы, и циклы имели большое значение в то время.

Alexis
источник
Но определенно не следует делать то, что сказал @cat.
TOOGAM
Я удивлен, что он не был помечен и удален @TOOGAM, но я добавил / s!
кот
Там я не знаю, что написал @cat, но я еще больше убедил ребенка в ответе. (Но давай, это уже достаточно ясно из контекста.)
Алексис
4

Что касается ОС, то файл, содержащий скрипт оболочки, - это просто данные. Если вы передаете имя такого файла данных sourceкоманде или передаете его в командной строке для вызова оболочки bash, все, что видит ОС, это строка, которая совпадает с именем файла, содержащего данные.

Как бит выполнения будет вообще уместен в этом случае?

Стивен М. Уэбб
источник
3

Различие важно, потому что у вас может быть файл команд оболочки, который бесполезен в качестве исполняемого файла, но полезен только при использовании источника. Для этого файла вы можете отключить бит выполнения, и тогда он никогда не будет доступен, если явно в исходной команде. Причина такой вещи - иметь побочные эффекты на оболочке, из которой она запускается. Для конкретного примера у меня есть скрипт с именем fix_path, который просматривает и изменяет путь.

КАРТА
источник
1

На всякий случай, если кто-то заинтересован в дальнейшем изучении и / или разъяснении: в почти POSIX-совместимой оболочке, реализованной некоторое время назад, внутренняя работа функций exec_program () и builtin_source () очень показательна. В этих функциях вы видите, в чем разница между ними:

https://github.com/rsenn/shish/blob/master/src/builtin/builtin_source.c

https://github.com/rsenn/shish/blob/master/src/exec/exec_program.c

в основном, источник может рассматриваться как оболочка, временно перенаправляющая свой внутренний файловый дескриптор, с которого он анализирует скрипт оболочки (терминал в интерактивном режиме). так что это очень похоже на другие перенаправления, такие как <input_file.txtи, >>append_to_something.listи они просто должны открывать и закрывать файлы.

таким образом, выполнение выполняется execve()системным вызовом, для которого бит выполнения является обязательным.

Я помню, как видел некоторые системы, которые разрешают выполнение двоичных файлов ELF / a.out, но через выполнение "/lib/ld-dynamic-linker.so" и с двоичной программой (без бита exec) в качестве первого аргумента. Я считаю, что это было на некоторых машинах DEC Alpha или VAX (это мог быть SCO Unix?)

rsenn
источник
-2

Другая точка зрения:

Исходный скрипт в основном состоит из встроенных командных оболочек и вызовов программ. Встроенные оболочки (включая sourceих) являются частями оболочки, и оболочка должна быть исполняемой в первую очередь. Каждая вызываемая программа (то есть ELF, другой скрипт с shebang) должна иметь установленный бит выполнения, иначе она не будет работать.

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

Камиль Мачоровски
источник
Встроенные оболочки не получены из сценария оболочки. Обычно они являются частью исполняемого файла оболочки. В ksh93, zsh и нескольких других оболочках они могут быть расширениями оболочки.
fpmurphy
@ fpmurphy1 Разве это не мое предложение "встроенные функции оболочки (...) являются частями оболочки"?
Камиль Мачоровски