Warum können wir ohne sudo keine Liste von Befehlen als unterschiedliche Benutzer ausführen?

8

Diese Frage wurde in anderen Foren anders gestellt. Aber es gab keine anständige Erklärung, warum Sie das Folgende in Bash nicht tun können.

#!/bin/bash
command1
SWITCH_USER_TO rag
command2
command3

Normalerweise ist der vorgeschlagene Weg

#!/bin/bash
command1
sudo -u rag command2
sudo -u rag command3

Aber warum ist es nicht möglich bash, irgendwann während der Ausführung des Bash-Skripts zu einem anderen Benutzer zu wechseln und den Rest der Befehle als anderer Benutzer auszuführen?

Lappen
quelle
Diese Frage wurde auch auf Superuser gefragt, bei superuser.com/questions/93385/... , und diese Antwort superuser.com/a/573882/76251 ein HIER DOC -ähnlichen Methode für Benutzer innerhalb eines Skripts ändern.
Tim Kennedy

Antworten:

4

Die Informationen sind bereits in meiner anderen Antwort enthalten , aber sie sind dort etwas vergraben. Also dachte ich, ich würde es hier hinzufügen.

bashEs ist nicht vorgesehen, Benutzer zu wechseln, dies ist jedoch der zshFall.

In zshändern Sie Benutzer, indem Sie diesen Variablen Werte zuweisen:

  • $EUID: Ändern Sie die effektive Benutzer-ID (und sonst nichts). Normalerweise können Sie Ihre EUID zwischen Ihrer tatsächlichen Benutzer-ID und der gespeicherten Set-Benutzer-ID ändern (wenn sie von einer ausführbaren Setuid-Datei aufgerufen wird) oder zu einer beliebigen ändern, wenn Ihre EUID 0 ist.
  • $UID: Ändern Sie die effektive Benutzer-ID, die tatsächliche Benutzer-ID und die gespeicherte Benutzer-ID auf den neuen Wert. Wenn dieser neue Wert nicht 0 ist, gibt es kein Zurück mehr. Da alle 3 auf denselben Wert eingestellt wurden, kann er nicht mehr geändert werden.
  • $EGIDund $GID: dasselbe, aber für Gruppen-IDs.
  • $USERNAME. Das ist wie mit sudooder su. Es setzt Ihre euid, ruid, ssuid auf die uid dieses Benutzers. Außerdem werden die Gruppen egid, rgid und ssgid sowie die zusätzlichen Gruppen basierend auf den in der Benutzerdatenbank definierten Gruppenmitgliedschaften festgelegt. Wie für $UID, es sei denn, Sie setzen $USERNAMEauf root, es gibt kein Zurück, aber wie für $UID, können Sie den Benutzer nur für eine Subshell ändern.

Wenn Sie diese Skripte als "root" ausführen:

#! /bin/zsh -
UID=0 # make sure all our uids are 0

id -u # run a command as root

EUID=1000

id -u # run a command as uid 1000 (but the real user id is still 0
      # so that command would be able to change its euid to that.
      # As for the gids, we only have those we had initially, so 
      # if started as "sudo the-script", only the groups root is a
      # member of.

EUID=0 # we're allowed to do that because our ruid is 0. We need to do
       # that because as a non-priviledged user, we can't set our euid
       # to anything else.

EUID=1001 # now we can change our euid since we're superuser again.

id -u # same as above

Um den Benutzer wie in sudooder zu ändern su, können wir dies nur mit Unterschalen tun, andernfalls können wir es nur einmal tun:

#! /bin/zsh -

id -u # run as root

(
  USERNAME=rag
  # that's a subshell running as "rag"

  id # see all relevant group memberships are applied
)
# now back to the parent shell process running as root

(
  USERNAME=stephane
  # another subshell this time running as "stephane"

  id
)
Stéphane Chazelas
quelle
8

Kernel Angebote man 2 setuidund Freunde.

Jetzt funktioniert es beim Aufruf des Prozesses. Noch wichtiger ist, dass Sie Ihre Privilegien nicht erhöhen können. Deshalb suund sudoSETUID - Bit gesetzt haben , so dass sie immer mit höchsten Privilegien ausführen ( root) und entsprechend gewünschten Benutzer löschen.

Diese Kombination bedeutet, dass Sie die Shell-UID nicht ändern können, indem Sie ein anderes Programm ausführen, um dies zu tun (deshalb können Sie nicht erwarten, sudodass ein anderes Programm dies tut), und Sie können es (als Shell) nicht selbst tun, es sei denn, Sie sind bereit, als root oder als derselbe Benutzer zu arbeiten, zu dem Sie wechseln möchten, was keinen Sinn ergibt. Darüber hinaus gibt es keinen Weg zurück, sobald Sie Berechtigungen fallen lassen.

Miroslav Koškár
quelle
6

Nun, Sie könnten immer tun:

#! /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)"

(ʘ‿ʘ)

Das begann zunächst als Witz, da es implementiert, was angefordert wird, obwohl es wahrscheinlich nicht genau wie erwartet ist, und praktisch nicht nützlich ist. Aber da es sich zu etwas entwickelt hat, das bis zu einem gewissen Grad funktioniert und ein paar nette Hacks beinhaltet, hier eine kleine Erklärung:

Wie Miroslav sagte , besteht die einzige Möglichkeit für einen nicht privilegierten Prozess, die UID zu ändern, darin, eine ausführbare Setuid-Datei auszuführen , wenn wir die Linux- Funktionen außer Acht lassen (was auch hier sowieso nicht wirklich hilfreich wäre).

Sobald Sie jedoch das Superuser-Privileg erhalten haben (indem Sie beispielsweise eine ausführbare Setuid-Datei ausführen, deren Eigentümer root ist), können Sie die effektive Benutzer-ID zwischen Ihrer ursprünglichen Benutzer-ID 0 und einer anderen ID hin und her wechseln, es sei denn, Sie geben Ihre gespeicherte Set-Benutzer-ID auf ( wie Dinge wie sudooder in der suRegel tun).

Zum Beispiel:

$ sudo cp /usr/bin/env .
$ sudo chmod 4755 ./env

Jetzt habe ich einen envBefehl, mit dem ich jeden Befehl mit einer effektiven Benutzer-ID und einer gespeicherten Benutzer-ID von 0 ausführen kann (meine tatsächliche Benutzer-ID ist immer noch 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

perlhat Wrapper für setuid/ seteuid(diese $>und $<Variablen).

Zsh auch:

$ sudo zsh -c 'EUID=1; id -u; EUID=0; EUID=2; id -u'
1
2

Obwohl oben diese idBefehle mit einer echten Benutzer-ID und einer gespeicherten Set-Benutzer-ID von 0 aufgerufen werden (obwohl, wenn ich meine ./envstattdessen verwendet sudohätte, nur die gespeicherte Set-Benutzer-ID gewesen wäre, während die echte Benutzer-ID 1000 geblieben wäre), was bedeutet, dass Wenn es sich um nicht vertrauenswürdige Befehle handelt, können sie dennoch Schaden anrichten. Schreiben Sie sie stattdessen wie folgt:

$ sudo zsh -c 'UID=1 id -u; UID=2 id -u'

(das heißt, alle UIDs (effektive, reale und gespeicherte Menge) nur für die Ausführung dieser Befehle.

bashhat keine Möglichkeit, die Benutzer-IDs zu ändern. Selbst wenn Sie eine ausführbare setuid-Datei hätten, mit der Sie Ihr bashSkript aufrufen könnten , würde dies nicht helfen.

Mit bashbleibt Ihnen die Möglichkeit, jedes Mal, wenn Sie die UID ändern möchten, eine ausführbare Setuid-Datei auszuführen.

Die Idee im obigen Skript besteht darin, SWITCH_TO_USER aufzurufen, um eine neue Bash-Instanz auszuführen und den Rest des Skripts auszuführen.

SWITCH_TO_USER someuserist mehr oder weniger eine Funktion, die das Skript erneut als anderer Benutzer ausführt (mit sudo), aber den Start des Skripts bis überspringt SWITCH_TO_USER someuser.

Schwierig wird es, dass wir den Status der aktuellen Bash beibehalten möchten, nachdem wir die neue Bash als anderer Benutzer gestartet haben.

Lassen Sie es uns zusammenfassen:

{ shopt -s expand_aliases;

Wir brauchen Aliase. Einer der Tricks in diesem Skript besteht darin, den Teil des Skripts bis zum zu überspringen SWITCH_TO_USER someuser, mit etwas wie:

:||: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

Dieses Formular ähnelt dem #if 0in C verwendeten, mit dem Sie Code vollständig auskommentieren können.

:ist ein No-Op, das true zurückgibt. So in : || :dem zweiten :wird nie ausgeführt. Es wird jedoch analysiert. Und das << 'xxx'ist eine Form des Here-Dokuments, bei dem (weil xxxzitiert wird) keine Erweiterung oder Interpretation vorgenommen wird.

Wir hätten tun können:

: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

Aber das hätte bedeutet, dass das Here-Dokument geschrieben und als Standard übergeben werden müsste :. :||:vermeidet das.

Nun, wo es hacky wird, ist, dass wir die Tatsache verwenden, dass bashAliase sehr früh in seinem Parsing-Prozess erweitert werden. Zu haben , skipist ein Alias für den :||: << 'SWITCH_TO_USER someuther'Teil des Kommentierung-out - Konstrukts.

Lass uns weitermachen:

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";}

Hier ist die Definition der Funktion SWITCH_TO_USER . Wir werden unten sehen, dass SWITCH_TO_USER irgendwann ein Alias ​​sein wird, der um diese Funktion gewickelt ist.

Diese Funktion führt den Großteil der erneuten Ausführung des Skripts aus. Am Ende sehen wir, dass es (im selben Prozess wegen exec) bashmit der _xVariablen in seiner Umgebung erneut ausgeführt wird (wir verwenden es envhier, weil es sudonormalerweise seine Umgebung bereinigt und es nicht erlaubt, beliebige env vars über zu übergeben). Das bashwertet den Inhalt dieses $_xVariable als Bash - Code und Quellen Skript selbst.

_x ist früher definiert als:

_x="$(declare;alias;shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a'

Alle die declare, alias, shopt -p set +oein Abbild des inneren Zustands der Schale bilden ausgegeben. Das heißt, sie speichern die Definition aller Variablen, Funktionen, Aliase und Optionen als Shell-Code, der ausgewertet werden kann. Darüber hinaus fügen wir die Einstellung der Positionsparameter ( $1, $2...) basierend auf dem Wert des $_aArrays hinzu (siehe unten) und einige Bereinigungen, damit die große $_xVariable für die verbleibenden nicht in der Umgebung bleibt des Skripts.

Sie werden feststellen, dass der erste Teil bis zu set +xin eine Befehlsgruppe eingeschlossen ist, deren stderr zu /dev/null( {...} 2> /dev/null) umgeleitet wird . Das liegt daran, dass wir , wenn das Skript set -x(oder set -o xtrace) irgendwann ausgeführt wird, nicht möchten, dass diese Präambel Spuren generiert, da wir sie so wenig aufdringlich wie möglich gestalten möchten. Wir führen also eine aus set +x(nachdem wir zuvor sichergestellt haben, dass die Optionen (einschließlich xtrace) Einstellungen gespeichert werden), in denen die Traces an / dev / null gesendet werden.

Der eval "$_X"stderr wird aus ähnlichen Gründen auch nach / dev / null umgeleitet, aber auch, um die Fehler beim Schreiben in spezielle schreibgeschützte Variablen zu vermeiden.

Fahren wir mit dem Skript fort:

alias skip=":||:<<'SWITCH_TO_USER $_u'"

Das ist unser oben beschriebener Trick. Beim ersten Skriptaufruf wird er abgebrochen (siehe unten).

alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"

Nun der Alias-Wrapper um SWITCH_TO_USER. Der Hauptgrund besteht darin, die Positionsparameter ( $1, $2...) an die neuen übergeben zu können bash, die den Rest des Skripts interpretieren. Wir konnten es in der SWITCH_TO_USER Funktion nicht tun, da sich innerhalb der Funktion "$@"die Argumente für die Funktionen befinden, nicht die der Skripte. Die stderr-Umleitung nach / dev / null dient erneut zum Ausblenden von xtraces und zum Umgehen evaleines Fehlers bash. Dann rufen wir die SWITCH_TO_USER Funktion auf .

${_u+:} alias skip=:

Dieser Teil bricht den skipAlias ​​ab (ersetzt ihn durch den :Befehl no-op), sofern die $_uVariable nicht festgelegt ist.

skip

Das ist unser skipPseudonym. Beim ersten Aufruf wird es nur sein :(das No-Op). Bei erneuten Aufrufen von Subsequenzen wird es so etwas wie : :||: << 'SWITCH_TO_USER root'.

echo test
a=foo
set a b

SWITCH_TO_USER root

Als Beispiel rufen wir an diesem Punkt das Skript als rootBenutzer erneut auf, und das Skript stellt den gespeicherten Status wieder her, springt zu dieser SWITCH_TO_USER rootZeile und fährt fort.

Das bedeutet, dass es genau wie stat geschrieben werden muss, mit SWITCH_TO_USERam Anfang der Zeile und mit genau einem Leerzeichen zwischen den Argumenten.

Der größte Teil von state, stdin, stdout und stderr bleibt erhalten, nicht jedoch die anderen Dateideskriptoren, da sie sudonormalerweise geschlossen werden, sofern dies nicht ausdrücklich so konfiguriert ist. Also zum Beispiel:

exec 3> some-file
SWITCH_TO_USER bob
echo test >&3

wird normalerweise nicht funktionieren.

Beachten Sie außerdem Folgendes:

SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root

Das funktioniert nur, wenn Sie das Recht auf sudoas aliceund alicedas Recht auf sudoas bobund bobas haben root.

In der Praxis ist das also nicht wirklich nützlich. Die Verwendung von suanstelle von sudo(oder einer sudoKonfiguration, bei sudoder der Zielbenutzer anstelle des Anrufers authentifiziert wird) ist möglicherweise etwas sinnvoller, aber das würde immer noch bedeuten, dass Sie die Kennwörter all dieser Typen kennen müssen.

Stéphane Chazelas
quelle
Können Sie bitte erklären, was Ihr Skript tut?
Lappen
@rag, du hast danach gefragt ...
Stéphane Chazelas
1
Ist das dein Eintrag für die ioshcc? Sie sollten in nur einer Zeile tho eingegeben haben.
ott--
-2

Mit dem Befehl "sudo -u ram sh" können Sie zum Benutzer "ram" wechseln, indem Sie den Befehl "sh" oder shell ausführen. Mit dem Befehl "exit" kehren Sie zum ursprünglichen Benutzer zurück.

user45185
quelle