Perché la fonte di Bash non ha bisogno del bit di esecuzione?


57

Con Bash sourceè possibile eseguire uno script senza un set di bit di esecuzione. Questo è un comportamento documentato e atteso, ma non è contro l'uso di un bit di esecuzione?

Lo so, questo sourcenon crea una subshell.


2
Il fatto che chmodpuò permetterti di impostare permessi (incluso `x) con un numero ottale fornisce qualche indizio su da quale epoca provenga. Non sarei sorpreso se fosse iniziato come un indicatore rapido e sporco "questo è un file binario che puoi eseguire", dai giorni prima dell'invenzione di lei-bang, ma non ho prove a
riguardo

9
Inoltre, quando entra in gioco SUID, diventano più importanti diversi livelli di protezione per diverse categorie di utenti. Vai avanti e leggi il mio programma SUID se vuoi. Ma se lo esegui semplicemente leggendolo, i poteri SUID non stanno arrivando con esso
infissi il

@infixed Il caricatore di programmi Linux non guarderà nemmeno lo shebang a meno che non sia impostato il bit di esecuzione. (Per suonare un po 'il mio corno: vedi qui .)
Kyle Strand,

Puoi anche avere + xr, ma è un po 'strano perché di solito è possibile leggere il binario iniettando il codice nel processo in esecuzione
Niklas B.

@KyleStrand di "se lo esegui semplicemente leggendolo, i poteri dei SUID non lo accompagnano" Stavo immaginando qualcosa del generecp /sbin/suidexecutable /tmp/mycopy; /tmp/mycopy
infisso il

Risposte:


36

Bash è un interprete; accetta input e fa quello che vuole. Non è necessario prestare attenzione al bit eseguibile. In effetti, Bash è portatile e può essere eseguito su sistemi operativi e filesystem che non hanno alcun concetto di bit eseguibile.

Ciò che interessa del bit eseguibile è il kernel del sistema operativo. Quando il kernel Linux esegue exec, ad esempio, controlla che il filesystem non sia montato con noexecun'opzione, controlla il bit eseguibile del file di programma e applica tutti i requisiti imposti dai moduli di sicurezza (come SELinux o AppArmor).

Si noti che il bit eseguibile è un tipo di controllo piuttosto discrezionale. Su un sistema Linux x86-64, ad esempio, è possibile ignorare la verifica del bit eseguibile da parte del kernel invocando esplicitamente /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2come interprete :

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

Questo è in qualche modo analogo all'acquisizione del codice sorgente di Bash in Bash, tranne che ld.soè l'interprete, e il codice che esegue è il codice macchina in formato ELF.


3
Usare il caricatore come "interprete" di "bytecode" che sembra essere un vero binario eseguibile ..... fantastico. Grazie per questo.
Kyle Strand,

@KyleStrand potrebbero esserci diversi livelli di indiretta (la differenza tra il programma memorizzato e il processo caricato e come è mappata la memoria, vari supervisori, VM, ecc.) Ma in linea di principio il codice macchina può essere eseguito direttamente dalla CPU (nessun microcodice, ecc.) e quindi il codice macchina non viene interpretato: l'hardware lo comprende così com'è - è la lingua madre - non sono necessari interpreti.
jfs,

2
@JFSebastian Sì, sappiamo che è un codice nativo, quindi "interprete" è tra virgolette. Il compito di ld.soè fare il collegamento dinamico.
200_successo

@JFSebastian Cosa ha detto 200_success. Inoltre, questo è ciò che intendevo per "binario eseguibile reale", e ho anche inserito "bytecode" tra virgolette (dal momento che AFAIK "bytecode" non viene in genere utilizzato per fare riferimento al codice binario eseguibile nativamente compilato). Inoltre, un bel nome utente.
Kyle Strand,

@JFSebastian Credo che le moderne CPU x86 siano in realtà RISC internamente, con le istruzioni CISC vecchio stile impostate essenzialmente per eseguire l'esecuzione di microcodici delle corrispondenti istruzioni RISC, dove un'istruzione CISC può causare l'esecuzione di molte istruzioni RISC. Quindi, in un certo senso, chiamare "interpretazione" ciò che la CPU fa con il codice macchina RISC è, se non completamente corretto da un punto di vista tecnico, almeno un modello mentale valido. AES-NI è probabilmente uno degli esempi più estremi: un'istruzione CISC per round AES!
un CVn il

57

sourceo l'equivalente, ma di serie dot. non eseguire lo script, ma leggere i comandi dal file di script, per poi eseguire, riga per riga, in ambiente di shell corrente.

Non c'è niente contro l'uso di bit di esecuzione, perché il guscio solo bisogno leggere il permesso di leggere il contenuto dei file.

Il bit di esecuzione è richiesto solo quando si esegue lo script. Qui la shell eseguirà un fork()nuovo processo, quindi utilizzerà la execve()funzione per creare una nuova immagine di processo dallo script, che deve essere un normale file eseguibile.


5
@alexis: Non sono sicuro di seguirlo davvero. Se lo fai bash < script, ottieni essenzialmente lo stesso risultato di source script. Quale protezione fornisce un controllo bit di esecuzione?
Conspicuous Compiler,

27
@alexis, non è compito di un interprete verificare le autorizzazioni degli script che interpreta. Niente fa questo: né Python, né Ruby, né la macchina virtuale Java, né altre shell casuali. Il bit di esecuzione controlla se la execvfamiglia di syscall del sistema operativo * può essere utilizzata con un eseguibile, non se verrà eseguita da un interprete. Perché confondere le persone (e interrompere la capacità di valutare il codice trasmesso da fonti non di file) rompendo le convenzioni?
Charles Duffy,

14
@alexis, ... inoltre, ha un significato prezioso avere una libreria shell che non è eseguibile: dice "Sono una libreria, non un comando - procurami, non eseguirmi". Altrimenti dovresti scrivere codice per rilevare e rimediare all'uso improprio del codice destinato a essere solo di provenienza, ma non avendo l'autorizzazione + x puoi assicurarti che gli utenti non possano (non) abusare di una libreria in questo modo.
Charles Duffy,

6
@alexis "è stata una decisione degli autori" Solo nel senso che ogni altra possibile parte di codice che non era inclusa era una decisione. La shell apre il file per la lettura, per leggere i comandi da esso. Non mi aspetto che controlli l'autorizzazione di lettura, prova solo ad aprire il file e fallisce se non può. Allo stesso modo non verifica l'autorizzazione alla scrittura o all'esecuzione, poiché tali controlli non sono rilevanti per la lettura del file.
Randy Orrison,

2
@alexis Questo è usato in pratica sebbene, ad esempio con virtualenv di python, lo script per attivarlo debba essere di provenienza e non eseguito, e quindi bin/activatenon ha un bit eseguibile. Gli script possono essere totalmente librerie o altre cose del genere. Immagino che avere .sh potrebbe anche essere un segnale, ma avere un minimo di armi da fuoco è buono: è bello che non sia possibile correre ./bin/activateper caso invece di. bin/activate
daboross il

20

Il bit eseguibile (a differenza del resto) sui file nonsetuid e nonsetguid non è molto un meccanismo di sicurezza. Qualunque cosa tu possa leggere, puoi eseguire indirettamente, e Linux ti consentirà di leggere indirettamente tutto ciò che puoi eseguire ma non leggere direttamente (ciò dovrebbe essere sufficiente per creare un buco nel concetto di non-set (g) uid x-bit essendo un misure di sicurezza).

È più una questione di convenienza: lascia che il sistema lo esegua direttamente per me se il bit è impostato, altrimenti devo farlo indirettamente ( bash the_script;o qualche hacking per ottenere l'immagine di memoria di un eseguibile senza permesso di lettura ).

Puoi impostarlo per comodità se intendi sia in-source che eseguire l'insourcable.

Apparentemente, tuttavia, molti implementatori di librerie condivise condividono il tuo pensiero e, di conseguenza, molti sistemi richiedono che le librerie condivise, che sono essenzialmente l'equivalente nativo degli insourcables della shell, siano contrassegnate come eseguibili per essere utilizzabili. Vedi Perché sono eseguibili le librerie condivise? .


3
@ Motte001 Possono eseguire quegli allegati se vogliono davvero. È una comodità.
PSkocik,

2
Il bit eseguibile non è per sicurezza: se possiedo un file, sono libero chmode lo eseguo. Serve per ordinare i dati dai programmi, quindi la domanda del PO è ragionevole.
alexis,

1
@ Motte001 Questa è più una caratteristica di sicurezza del browser di file GUI / applicazione di posta - potrebbe facilmente decidere che il doppio clic su qualsiasi file il cui nome termina ".sh" venga eseguito in un terminale (stile Windows), o al contrario quel doppio- facendo clic su qualsiasi file nella directory "allegati scaricati" verrà richiamata un'app di anteprima sandbox integrata. Il xbit è solo un posto in più in cui può leggere / scrivere un suggerimento su cosa fare.
IMSoP,

3
Uno dei motivi per cui abbiamo bisogno del bit eseguibile è eseguire programmi setuid, in particolare quelli di proprietà di root. Sebbene sia possibile eseguirli da soli, è necessario il sistema operativo per eseguirli in modo che dispongano delle autorizzazioni necessarie. In alcuni altri casi è davvero solo una comodità.
Waleed Khan,

2
"Linux ti permetterà di leggere tutto ciò che puoi eseguire tramite / proc" - Beh, il mio kernel Debian non lo fa: /tmp$ cp /bin/cat ./cat ; chmod a-rw ./cat ; ./cat & cp /proc/$!/exe /tmp/cat2->cp: cannot stat ‘/proc/16260/exe’: Permission denied
ilkkachu il

9

Questa è una bella domanda! Unix utilizza il bit eseguibile per distinguere tra programmi e dati. Il sistema operativo non richiede il bit di esecuzione, poiché uno script di origine non viene passato al sistema operativo per l'esecuzione come nuovo processo. Ma la shell tratta uno script di provenienza come un programma e cercherà $PATHil file che si desidera ottenere. Quindi, la shell stessa avrebbe potuto richiedere l'autorizzazione di esecuzione per i file di origine; ma non lo fece.

La domanda deve essere stata posta molto tempo fa. Il design della shell Bourne è stato il risultato di "una lunga sequenza di modifiche, dialoghi, discussioni" tra gli abitanti di Bell Labs e molte decisioni di progettazione sono state discusse da SR Bourne e altri nel corso degli anni. Sfortunatamente, il mio rapido sguardo non ha trovato alcuna discussione sulla funzione di origine (a mia difesa, è difficile cercarlo su Google). Quello che ho trovato è che il "." Il comando non appare in questa prima introduzione alla shell dello stesso Bourne, ma è presente nella versione 7 più matura .

Assente autorità, ecco la mia interpretazione:

  1. Il .comando, aka source, è in effetti inclusione testuale (come #includenel preprocessore C) nel codice sorgente dello script in esecuzione o della sessione interattiva. Pertanto, il file incluso non è probabilmente "eseguito".

  2. La filosofia Unix è sempre stata quella di dare ai programmatori abbastanza corda per impiccarsi. Troppe restrizioni arbitrarie e di stretta di mano si frappongono. Solo di recente alcune distribuzioni hanno rm -r /rifiutato di fare ciò che chiedi. (Questo comando dice rmdi cancellare tutto sul tuo computer. Non provarlo come root! O meglio ancora, per niente.) Quindi, forse Bourne ad al. ho appena deciso che quando si tenta di creare un file, si presuppone che si sappia cosa si sta facendo. Ciò evita anche il lavoro non necessario e i cicli contavano molto allora.


Ma si dovrebbe sicuramente non fare quello che @cat detto.
TOOGAM,

Sono sorpreso che non sia stato contrassegnato e cancellato @TOOGAM ma ho inserito il / i!
gatto

Lì, non so cosa avesse scritto @cat, ma ho reso la risposta ancora più sicura. (Ma dai, era già abbastanza chiaro dal contesto.)
alexis,

4

Per quanto riguarda il sistema operativo, un file contenente script di shell sono solo dati. Se si passa il nome di tale file di dati al sourcecomando o lo si passa dalla riga di comando a un richiamo della shell bash, tutto il sistema operativo vede una stringa che coincide con il nome di un file contenente dati.

Come sarebbe il bit di esecuzione rilevante in quel caso?


3

La distinzione è importante perché potresti avere un file di comandi shell che non è utile come eseguibile, ma utile solo quando è di origine. Per questo file è possibile disattivare il bit di esecuzione e quindi non sarà mai possibile accedervi se non esplicitamente in un comando di origine. La ragione di una cosa del genere è avere effetti collaterali sulla shell da cui è stata lanciata. Per un esempio specifico, ho uno script chiamato fix_path che guarda e modifica il percorso.


1

Nel caso in cui qualcuno fosse interessato a ulteriori studi e / o chiarimenti: in una shell quasi conforme a POSIX implementata qualche tempo fa, i meccanismi interni delle funzioni 'exec_program ()' e 'builtin_source ()' sono molto esemplari. In quelle funzioni vedi esattamente qual è la differenza tra loro:

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

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

sostanzialmente il sourcing può essere visto come la shell che reindirizza temporaneamente il suo descrittore di file interno da dove analizza lo script della shell (il terminale in modalità interattiva). quindi è molto simile ad altri reindirizzamenti come <input_file.txte >>append_to_something.liste questi devono solo aprire e chiudere i file.

pertanto l'esecuzione è gestita dalla execve()chiamata di sistema per cui il bit di esecuzione è obbligatorio.

Ricordo di aver visto alcuni sistemi che consentono l'esecuzione di file binari ELF / a.out, ma tramite l'esecuzione di "/lib/ld-dynamic-linker.so" e con il programma binario (senza bit di esecuzione) come primo argomento. Credo che fosse su alcune macchine DEC Alpha o VAX (avrebbe potuto essere SCO Unix?)


-2

Un altro punto di vista:

Lo script di origine è essenzialmente composto da shell incorporate e chiamate di programma. I builtin della shell ( sourcetra cui) sono parti della shell e la shell deve essere eseguibile in primo luogo. Ogni programma chiamato (che sia ELF, un altro script con shebang, qualunque cosa) deve avere un bit di esecuzione impostato, altrimenti non verrà eseguito.

Quindi non è contro l'uso di un bit di esecuzione, perché non verrà eseguito nulla senza bit di esecuzione. La convalida non si verifica per l'intero script di provenienza; viene eseguito per ogni parte separatamente, ma lo è.


I buildin della shell non provengono da uno script della shell. In genere fanno parte dell'eseguibile della shell. In ksh93, zsh e un paio di altre shell possono essere estensioni della shell.
fpmurphy,

@ fpmurphy1 Non è questa la mia frase "builtins della shell (...) sono parti della shell"?
Kamil Maciorowski il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.