¿Por qué la fuente de Bash no necesita el bit de ejecución?

57

Con Bash's sourcees posible ejecutar un script sin un conjunto de bits de ejecución. Este es un comportamiento documentado y esperado, pero ¿no está en contra del uso de un bit de ejecución?

Lo sé, eso sourceno crea una subshell.

Motte001
fuente
2
El hecho de que chmodpueda permitirle establecer permisos (incluido `x) con un número octal le da una idea de la época de la que proviene. No me sorprendería que comenzara como un indicador rápido y sucio de "este es un archivo binario que puede ejecutar", desde los días previos a la invención de she-bang, pero no tengo evidencia de eso
infijo
99
Por otra parte, cuando SUID entra en juego, los diferentes niveles de protección para diferentes categorías de usuarios se vuelven más importantes. Siga adelante y lea mi programa SUID si lo desea. Pero si lo ejecuta a través de la simple lectura de ella, los poderes SUID no vienen junto con él
infijo
@infixed El cargador de programas de Linux ni siquiera mirará el shebang a menos que se establezca el bit de ejecución. (Para tocar un poco mi propia bocina: ver aquí .)
Kyle Strand
También puede tener + xr, pero eso es un poco extraño porque generalmente es posible leer el binario inyectando código en el proceso en ejecución
Niklas B.
@KyleStrand por "si lo ejecuta a través de la simple lectura de ella, los poderes SUID no vienen junto con él" Estaba imaginando algo en la línea decp /sbin/suidexecutable /tmp/mycopy; /tmp/mycopy
infijo

Respuestas:

36

Bash es un intérprete; acepta entrada y hace lo que quiera. No necesita prestar atención al bit ejecutable. De hecho, Bash es portátil y puede ejecutarse en sistemas operativos y sistemas de archivos que no tienen ningún concepto de bit ejecutable.

Lo que importa del bit ejecutable es el núcleo del sistema operativo. Cuando el kernel de Linux realiza exec, por ejemplo, comprueba que el sistema de archivos no está montado con una noexecopción, comprueba el bit ejecutable del archivo de programa y hace cumplir los requisitos impuestos por los módulos de seguridad (como SELinux o AppArmor).

Tenga en cuenta que el bit ejecutable es un tipo de control bastante discrecional. En un sistema Linux x86-64, por ejemplo, puede omitir la verificación del núcleo del bit ejecutable invocando explícitamente /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2como intérprete :

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

Esto es algo análogo al abastecimiento del código fuente de Bash en Bash, excepto que ld.soes el intérprete, y el código que ejecuta es código de máquina en formato ELF.

200_success
fuente
3
Usar el cargador como un "intérprete" de "bytecode" que resulta ser un binario ejecutable real ... increíble. Gracias por eso.
Kyle Strand
@KyleStrand puede haber varios niveles de indirección (la diferencia entre el programa almacenado y el proceso cargado y cómo se asigna la memoria, varios supervisores, máquinas virtuales, etc.) pero, en principio, el código de máquina puede ejecutarse directamente por la CPU (sin microcódigo, etc.) y, por lo tanto, el código de la máquina no se interpreta: el hardware lo entiende como es, es el idioma nativo, no se necesitan intérpretes.
jfs
2
@JFSebastian Sí, sabemos que es código nativo, por lo tanto, "intérprete" está entre comillas. El trabajo de ld.soes hacer enlaces dinámicos.
200_success
@JFSebastian Lo que dijo 200_success. Además, eso es lo que quise decir con "binario ejecutable real", y también puse "bytecode" entre comillas (ya que AFAIK "bytecode" generalmente no se usa para referirse al código binario ejecutable compilado de forma nativa real). Además, buen nombre de usuario.
Kyle Strand
@JFSebastian Creo que las CPU modernas similares a x86 son en realidad RISC internamente, con el conjunto de instrucciones CISC de estilo antiguo que básicamente impulsa la ejecución del microcódigo de las instrucciones RISC correspondientes, donde una instrucción CISC puede hacer que se ejecuten muchas instrucciones RISC. Entonces, en cierto sentido, llamar a lo que hace la CPU con el código de máquina RISC "interpretar" es, si no es completamente correcto desde un punto de vista técnico, entonces al menos un modelo mental válido. AES-NI es probablemente uno de los ejemplos más extremos: ¡una instrucción CISC por ronda AES!
un CVn
57

sourceo el equivalente pero estándar de puntos. no ejecutar la secuencia de comandos, pero lee los comandos del archivo de comandos, a continuación, ejecutarlos, línea por línea, en el entorno de shell actual.

No hay nada en contra del uso del bit de ejecución, porque el shell sólo tiene que leer permiso para leer el contenido del archivo.

El bit de ejecución solo es necesario cuando ejecuta el script. Aquí el shell fork()procesará de nuevo y luego usará la execve()función para crear una nueva imagen de proceso a partir del script, que debe ser un archivo ejecutable normal.

Cuonglm
fuente
55
@alexis: No estoy segura de seguirlo. Si lo hace bash < script, obtendrá esencialmente el mismo resultado que source script. ¿Qué protección proporciona una comprobación de bit de ejecución?
Compilador conspicuo
27
@alexis, no es tarea de un intérprete verificar los permisos de los scripts que interpreta. Nada hace esto, ni Python, ni Ruby, ni la máquina virtual Java, ni otros shells aleatorios. El bit de ejecución controla si la execvfamilia OS * de syscalls se puede usar con un ejecutable, no si un intérprete lo ejecutará. ¿Por qué confundir a las personas (y romper la capacidad de evaluar el código transmitido desde fuentes que no son archivos) al romper las convenciones?
Charles Duffy
14
@alexis, ... además, tiene un significado valioso tener una biblioteca de shell que no es ejecutable: dice "Soy una biblioteca, no un comando: busca mi fuente, no me ejecutes". De lo contrario, necesitaría escribir código para detectar y remediar el mal uso del código que solo se debe obtener, pero al no tener el permiso + x, puede asegurarse de que los usuarios no puedan (no) usar indebidamente una biblioteca de esa manera.
Charles Duffy
66
@alexis "fue una decisión de los autores" Solo en el sentido de que cualquier otro fragmento posible de código que no se haya incluido fue una decisión. El shell abre el archivo para leerlo, para leer los comandos que contiene. No esperaría que verificara el permiso de lectura, solo intenta abrir el archivo y falla si no puede. Del mismo modo, no verifica el permiso de escritura o ejecución, ya que esas verificaciones no son relevantes para leer el archivo.
Randy Orrison
2
@alexis Esto se usa en la práctica, sin embargo, por ejemplo, con el virtualenv de python, el script para activarlo debe tener su origen y no ejecutarse, por lo bin/activateque no tiene un bit ejecutable. Los scripts pueden ser totalmente bibliotecas u otras cosas por el estilo. Supongo que tener .sh también podría ser una señal, pero tener un mínimo de pistolas es bueno: es bueno que no sea posible correr ./bin/activatepor accidente en lugar de. bin/activate
daboross
20

El bit ejecutable (a diferencia del resto) en archivos no setuid y no setguid no es un gran mecanismo de seguridad. Cualquier cosa que pueda leer, puede ejecutarse indirectamente, y Linux le permitirá leer indirectamente cualquier cosa que pueda ejecutar pero no leer directamente (eso debería ser suficiente para perforar un agujero en el concepto de no-set (g) uid x-bit siendo un medida de seguridad).

Es más conveniente: deje que el sistema lo ejecute directamente si el bit está configurado, de lo contrario, debo hacerlo indirectamente ( bash the_script;o algo de piratería para obtener la imagen de memoria de un ejecutable sin permiso de lectura ).

Puede configurarlo para mayor comodidad si tiene la intención de utilizar tanto en la fuente como ejecutar su insurcable.

Aparentemente, sin embargo, muchos implementadores de bibliotecas compartidas comparten su pensamiento y, en consecuencia, muchos sistemas requieren que las bibliotecas compartidas, que son esencialmente el equivalente nativo de los insurcables de shell, se marquen como ejecutables para ser utilizables. Consulte ¿Por qué las bibliotecas compartidas son ejecutables? .

PSkocik
fuente
3
@ Motte001 Pueden ejecutar esos archivos adjuntos si realmente lo desean. Es una conveniencia.
PSkocik
2
El bit ejecutable no es por seguridad: si tengo un archivo, soy libre de chmodhacerlo y lo hago ejecutable. Es para clasificar datos de programas, por lo que la pregunta del OP es razonable.
alexis
1
@ Motte001 Esa es más una característica de seguridad de la aplicación de correo / navegador de archivos GUI: podría fácilmente decidir que hacer doble clic en cualquier archivo cuyo nombre termine ".sh" se ejecute en un terminal (estilo Windows), o viceversa al hacer clic en cualquier archivo en el directorio de "archivos adjuntos descargados", se invocará una aplicación de vista previa integrada de espacio aislado. El xbit es solo un lugar adicional donde puede leer / escribir una pista de qué hacer.
IMSoP
3
Una razón por la que necesitamos el bit ejecutable es para ejecutar programas setuid, particularmente aquellos propiedad de root. Si bien ciertamente puede ejecutarlos por su cuenta, necesita el sistema operativo para ejecutarlos para que tengan los permisos necesarios. En algunos otros casos, es realmente una conveniencia.
Waleed Khan
2
"Linux te permitirá leer todo lo que puedas ejecutar a través de / proc" - Bueno, mi núcleo Debian de stock no: /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

¡Buena pregunta! Unix usa el bit ejecutable para distinguir entre programas y datos. El sistema operativo no requiere el bit de ejecución, ya que un script de origen no se pasa al sistema operativo para su ejecución como un nuevo proceso. Pero el shell trata un script de origen como un programa y buscará $PATHel archivo que desea obtener. Por lo tanto, el propio shell podría haber requerido permiso de ejecución para los archivos de origen; Pero no fue así.

La pregunta debe haber surgido hace mucho tiempo. El diseño de la carcasa Bourne fue el resultado de "una larga secuencia de modificación, diálogo, discusión" entre los habitantes de los Laboratorios Bell, y SR Bourne y otros discutieron muchas decisiones de diseño a lo largo de los años. Desafortunadamente, mi mirada rápida no encontró ninguna discusión sobre la función fuente (en mi defensa, es difícil buscarla en Google). Lo que sí encontré es que el "." El comando Bourne no aparece en esta introducción temprana al shell, pero está presente en la versión más madura de la Versión 7 .

En ausencia de autoridad, aquí está mi propia interpretación:

  1. El .comando, alias source, es en efecto inclusión textual (como #includeen el preprocesador C) en el código fuente del script de ejecución o sesión interactiva. Como tal, el archivo incluido posiblemente no esté "ejecutado".

  2. La filosofía de Unix siempre ha sido dar a los programadores suficiente cuerda para ahorcarse. Demasiadas restricciones de mano y arbitrarias solo se interponen en el camino. Recientemente, algunas distribuciones se rm -r /negaron a hacer lo que usted le pide. (Este comando le indica rmque elimine todo de su computadora. ¡ No lo intente como root! O, mejor aún, no lo haga en absoluto). Entonces, quizás Bourne al. solo decidí que cuando intentas obtener un archivo, se supone que sabes lo que estás haciendo. Eso también evita el trabajo innecesario, y los ciclos importaban mucho en ese entonces.

alexis
fuente
Pero definitivamente no se debe hacer lo que dijo @cat.
TOOGAM
Me sorprende que no se haya marcado y eliminado @TOOGAM, pero puse el / s.
gato
Allí, no sé lo que @cat había escrito, pero hice la prueba a prueba de niños aún más. (Pero vamos, ya era bastante claro por el contexto.)
alexis
4

En lo que respecta al sistema operativo, un archivo que contiene script de shell es solo información. Si pasa el nombre de dicho archivo de datos al sourcecomando o lo pasa en la línea de comando a una invocación del shell bash, todo lo que ve el sistema operativo es una cadena que coincide con el nombre de un archivo que contiene datos.

¿Cómo sería relevante el bit de ejecución en ese caso?

Stephen M. Webb
fuente
3

La distinción es importante porque puede tener un archivo de comandos de shell que no es útil como ejecutable, pero solo es útil cuando se obtiene. Para este archivo, puede desactivar el bit de ejecución y luego nunca se accederá a él, a menos que sea explícitamente en un comando fuente. La razón de tal cosa es tener efectos secundarios en el shell desde el que se ejecuta. Para un ejemplo específico, tengo un script llamado fix_path que mira y modifica la ruta.

MAPA
fuente
1

Por si alguien está interesado en más estudios y / o aclaraciones: en un shell casi compatible con POSIX implementado hace un tiempo, el funcionamiento interno de las funciones 'exec_program ()' y 'builtin_source ()' es muy ejemplar. En esas funciones, ve exactamente cuál es la diferencia entre ellas:

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

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

Básicamente, el abastecimiento puede verse como el shell que redirige temporalmente su descriptor de archivo interno desde donde analiza el script de shell (el terminal en modo interactivo). por lo que es muy similar a otras redirecciones como <input_file.txty, >>append_to_something.listy estas solo necesitan abrir y cerrar archivos.

entonces la ejecución es manejada por la execve()llamada del sistema para la cual el bit de ejecución es obligatorio.

Recuerdo haber visto algunos sistemas que permiten la ejecución de archivos binarios ELF / a.out, pero mediante la ejecución de "/lib/ld-dynamic-linker.so" y con el programa binario (sin bit de ejecución) como primer argumento. Creo que eso estaba en algunas máquinas DEC Alpha o VAX (¿Podría haber sido SCO Unix?)

rsenn
fuente
-2

Otro punto de vista:

La secuencia de comandos de origen consiste básicamente en funciones integradas de shell y llamadas a programas. Los componentes integrados de shell ( sourceentre ellos) son partes del shell y el shell debe ser ejecutable en primer lugar. Cada programa llamado (que es ELF, otro script con shebang, lo que sea) debe tener un bit de ejecución establecido, de lo contrario no se ejecutará.

Por lo tanto, no está en contra del uso de un bit de ejecución, porque nada sin el bit de ejecución se ejecutará. La validación no se produce para el script de origen en su conjunto; se realiza para cada parte por separado, pero lo es.

Kamil Maciorowski
fuente
Las compilaciones de shell no se obtienen de un script de shell. Por lo general, son parte del ejecutable del shell. En ksh93, zsh y un par de otros shells pueden ser extensiones de shell.
fpmurphy
@ fpmurphy1 ¿No es esta mi frase "los componentes integrados del caparazón (...) son partes del caparazón"?
Kamil Maciorowski