Bene, puoi sempre fare:
#! /bin/bash -
{ shopt -s expand_aliases;SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";};alias skip=":||:<<'SWITCH_TO_USER $_u'"
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
${_u+:} alias skip=:;} 2>/dev/null
skip
echo test
a=foo
set a b
SWITCH_TO_USER root
echo "$a and $1 as $(id -un)"
set -x
foo() { echo "bar as $(id -un)"; }
SWITCH_TO_USER rag
foo
set +x
SWITCH_TO_USER root again
echo "hi again from $(id -un)"
(ʘ‿ʘ)
Ciò è iniziato inizialmente come uno scherzo poiché implementa ciò che è richiesto, sebbene probabilmente non esattamente come previsto, e non è praticamente utile. Ma mentre si è evoluto in qualcosa che funziona in una certa misura e coinvolge alcuni bei hack, ecco una piccola spiegazione:
Come ha detto Miroslav , se lasciamo da parte le funzionalità in stile Linux (che non aiuterebbero comunque qui), l'unico modo per un processo senza privilegi di cambiare uid è eseguendo un eseguibile setuid.
Tuttavia, una volta ottenuto il privilegio di superutente (eseguendo un eseguibile setuid il cui proprietario è root, ad esempio), è possibile cambiare l'ID utente effettivo avanti e indietro tra l'ID utente originale, 0 e qualsiasi altro id a meno che non si rinunci all'ID utente set salvato ( come cose come sudo
o in su
genere fanno).
Per esempio:
$ sudo cp /usr/bin/env .
$ sudo chmod 4755 ./env
Ora ho un env
comando che mi permette di eseguire qualsiasi comando con un ID utente efficace e un ID utente impostato salvato pari a 0 (il mio ID utente reale è ancora 1000):
$ ./env id -u
0
$ ./env id -ru
1000
$ ./env -u PATH =perl -e '$>=1; system("id -u"); $>=0;$>=2; system("id -u");
$>=0; $>=$<=3; system("id -ru; id -u"); $>=0;$<=$>=4; system("id -ru; id -u")'
1
2
3
3
4
4
perl
ha wrapper per setuid
/ seteuid
(quelli $>
e $<
variabili).
Anche zsh:
$ sudo zsh -c 'EUID=1; id -u; EUID=0; EUID=2; id -u'
1
2
Sebbene sopra quei id
comandi vengano chiamati con un vero ID utente e salvato un set userid di 0 (anche se se avessi usato il mio ./env
invece di sudo
quello sarebbe stato solo il set salvato userid, mentre il vero ID utente sarebbe rimasto 1000), il che significa che se fossero comandi non attendibili, potrebbero comunque arrecare danno, quindi ti consigliamo di scriverlo come:
$ sudo zsh -c 'UID=1 id -u; UID=2 id -u'
(che è impostato su tutti gli uid (set effettivo, reale e salvato) solo per l'esecuzione di quei comandi.
bash
non ha alcun modo per modificare gli ID utente. Quindi, anche se avessi un eseguibile setuid con cui chiamare il tuo bash
script, questo non sarebbe d'aiuto.
Con bash
, ti resta con l'esecuzione di un eseguibile setuid ogni volta che vuoi cambiare uid.
L'idea nello script sopra è su una chiamata a SWITCH_TO_USER, per eseguire una nuova istanza bash per eseguire il resto dello script.
SWITCH_TO_USER someuser
è più o meno una funzione che esegue nuovamente lo script come un altro utente (usando sudo
) ma salta l'inizio dello script fino a quando SWITCH_TO_USER someuser
.
Dove diventa difficile è che vogliamo mantenere lo stato della bash corrente dopo aver avviato la nuova bash come utente diverso.
Analizziamolo:
{ shopt -s expand_aliases;
Avremo bisogno di alias. Uno dei trucchi di questo script è saltare la parte dello script fino a quando SWITCH_TO_USER someuser
, con qualcosa del tipo:
:||: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER
Tale modulo è simile a quello #if 0
utilizzato in C, ovvero un modo per commentare completamente un codice.
:
è una no-op che ritorna vera. Quindi : || :
, il secondo :
non viene mai eseguito. Tuttavia, viene analizzato. E << 'xxx'
c'è una forma di documento qui dove (perché xxx
è citato), non viene fatta alcuna espansione o interpretazione.
Avremmo potuto fare:
: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER
Ma ciò avrebbe significato che il documento qui avrebbe dovuto essere scritto e trasmesso come standard :
. :||:
evita quello.
Ora, dove diventa confuso è che usiamo il fatto che bash
espande gli alias molto presto nel suo processo di analisi. Essere skip
alias della :||: << 'SWITCH_TO_USER someuther'
parte del costrutto per commentare .
Andiamo avanti:
SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";}
Ecco la definizione della funzione SWITCH_TO_USER . Vedremo di seguito che SWITCH_TO_USER alla fine sarà un alias racchiuso in quella funzione.
Tale funzione svolge la maggior parte della riesecuzione dello script. Alla fine vediamo che si ri-esegue (nello stesso processo a causa di exec
) bash
con la _x
variabile nel suo ambiente (usiamo env
qui perché di sudo
solito igienizza il suo ambiente e non consente il passaggio di ambiti arbitrari attraverso l'altro). Ciò bash
valuta il contenuto di quella $_x
variabile come codice bash e genera lo script stesso.
_x
è definito in precedenza come:
_x="$(declare;alias;shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a'
Tutti i declare
, alias
, shopt -p
set +o
uscita costituiscono un dump dello stato interno del guscio. Cioè, scaricano la definizione di tutte le variabili, funzioni, alias e opzioni come codice shell pronto per essere valutato. Inoltre, aggiungiamo l'impostazione dei parametri posizionali ( $1
, $2
...) in base al valore $_a
dell'array (vedi sotto) e alcuni clean up in modo che l'enorme $_x
variabile non rimanga nell'ambiente per il resto della sceneggiatura.
Noterai che la prima parte fino a set +x
è racchiusa in un gruppo di comandi il cui stderr viene reindirizzato a /dev/null
( {...} 2> /dev/null
). Questo perché, se ad un certo punto dello script set -x
(o set -o xtrace
) viene eseguito, non vogliamo che quel preambolo generi tracce perché vogliamo renderlo il meno invasivo possibile. Quindi eseguiamo un set +x
(dopo esserci assicurati di scaricare xtrace
preventivamente le opzioni (incluse )) in cui le tracce vengono inviate a / dev / null.
Lo eval "$_X"
stderr viene anche reindirizzato su / dev / null per ragioni simili, ma anche per evitare errori di scrittura nel tentativo di variabili speciali di sola lettura.
Andiamo avanti con la sceneggiatura:
alias skip=":||:<<'SWITCH_TO_USER $_u'"
Questo è il nostro trucco descritto sopra. L'invocazione iniziale dello script verrà annullata (vedi sotto).
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
Ora il wrapper alias attorno a SWITCH_TO_USER. Il motivo principale è riuscire a passare i parametri posizionali ( $1
, $2
...) al nuovo bash
che interpreterà il resto dello script. Non abbiamo potuto farlo nella SWITCH_TO_USER
funzione perché all'interno della funzione, "$@"
sono gli argomenti alle funzioni, non quelli degli script. Il reindirizzamento stderr su / dev / null è di nuovo per nascondere xtraces e eval
deve aggirare un bug bash
. Quindi chiamiamo la SWITCH_TO_USER
funzione .
${_u+:} alias skip=:
Quella parte annulla l' skip
alias (lo sostituisce con il :
comando no-op) a meno che non $_u
sia impostata la variabile.
skip
Questo è il nostro skip
alias. Alla prima invocazione, sarà solo :
(la no-op). Su subsequence re-invocazioni, sarà qualcosa di simile a: :||: << 'SWITCH_TO_USER root'
.
echo test
a=foo
set a b
SWITCH_TO_USER root
Quindi qui, ad esempio, a quel punto, riprendiamo lo script come root
utente e lo script ripristinerà lo stato salvato, salterà su quella SWITCH_TO_USER root
linea e proseguirà.
Ciò significa che deve essere scritto esattamente come stat, con SWITCH_TO_USER
all'inizio della riga e con esattamente uno spazio tra gli argomenti.
La maggior parte dello stato, stdin, stdout e stderr saranno conservati, ma non gli altri descrittori di file perché in sudo
genere li chiudono a meno che non siano esplicitamente configurati per non. Quindi per esempio:
exec 3> some-file
SWITCH_TO_USER bob
echo test >&3
in genere non funzionerà.
Nota anche che se lo fai:
SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root
Funziona solo se hai il diritto di sudo
as alice
e alice
ha il diritto di sudo
as bob
e bob
as root
.
Quindi, in pratica, questo non è davvero utile. Usare su
invece di sudo
(o una sudo
configurazione in cui sudo
autentica l'utente target anziché il chiamante) potrebbe avere un po 'più senso, ma ciò significherebbe comunque che dovresti conoscere le password di tutti quei tipi.