Welche Shell wird Sudo verwenden, um ein Shell-Skript ohne die Shebang-Zeile auszuführen?

7

Meine Umgebung ist Ubuntu 12.04 LTS und die sudoVersion ist 1.8.3p1.

Zuerst logge ich mich als normaler Benutzer ein:

$ whoami
fin

$ cat /etc/passwd | grep -i "root\|fin"
root:x:0:0:root:/root:/bin/bash
fin:x:1000:1000:This is a normal user:/home/fin:/bin/bash

$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Mar 30  2012 /bin/sh -> dash

$ ls -l /bin/bash
-rwxr-xr-x 1 root root 920788 Apr  3  2012 /bin/bash

$ echo $SHELL
/bin/bash

$ ps | grep "$$" | awk '{ print $4 }'
bash

$ ls -l ./test.sh
-rwxr-xr-x 1 fin fin 37 Sep 27 16:46 test.sh

$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'

$ ./test.sh
bash

$ sudo ./test.sh
sh

Ich nehme an, die letzte Ausgabe sollte auch sein, bashweil /etc/passwdzeigt, dass root verwendet bash, fehlen mir irgendwelche Punkte sudo?

Layka
quelle

Antworten:

12

Es verwendet _PATH_BSHELLwie execvp()unter Linux wie /bin/shin definiert /usr/include/paths.h. Das sollte das gleiche sein wie bei der Ausführung mit envoder find -execzum Beispiel.

Es sollte auf keinen Fall die Login-Shell des Benutzers verwenden. Die Tatsache, die Sie bashoben sehen, ist, dass es bash(die Shell, in die Sie diese Befehlszeile eingeben) versucht, sie auszuführen, und wenn sie einen ENOEXECFehlercode erhält execve, beschließt sie, sie stattdessen mit sich selbst zu interpretieren (im shKompatibilitätsmodus).

Stéphane Chazelas
quelle
Vielen Dank für Ihre Antwort. Es hängt also von der sudo-Implementierung ab, zu entscheiden, ob sie verwendet shoder analysiert werden soll, /etc/passwdwenn ein ENOEXECFehler auftritt .
Layka
Nein, sudodie Anmeldeshell des Benutzers wird nur verwendet, wenn Sie ihm mitteilen, dass Sie ihm eine Befehlszeile übergeben möchten, die von einer Shell (mit -s) interpretiert werden soll, im Gegensatz zu einem auszuführenden Befehl. Wenn ein Befehl ausgeführt werden soll, verhält er sich so execvp, als ob das System Befehle ausführen soll.
Stéphane Chazelas
@ Stephen Chazelas: Ich denke, es bedeutet nicht, dass das System einen Befehl ausführt. sudoRichten Sie die Ausführungsumgebung immer vor dem Aufruf von ein execve(). Wenn Sie nicht bestehen -s, wird sudo verwenden _PATH_BSHELL.
Cuonglm
@Gnouc, Ja, sudo verwendet _PATH_BSHELL wie das System (in execvp (), env, find, vi die meisten Shells ...), es implementiert einfach eine eigene Version von execvp(). Es kann auch _PATH_BSHELL mit -s verwenden, wenn die Ausführung $SHELLENOEXEC zurückgibt. Weitere Einzelheiten finden Sie in meinem Kommentar zu Ihrer Antwort.
Stéphane Chazelas
5

Da Sie die -sOption nicht verwenden , sudowird sie auch verwendet _PATH_BSHELL(was in /usr/include/paths.hUbuntu 12.04 LTS definiert ist), um die $SHELLAusführung festzulegen . Blick in den sudoQuellcode:

/* Stash user's shell for use with the -s flag; don't pass to plugin. */
    if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
    ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_BSHELL;
    }

Wenn Sie die -sOption verwenden, sudoverwenden Sie Ihre $SHELLanstelle von _PATH_BSHELL:

$ cat ./test.sh
ps | grep "$$" | awk '{ print $4 }'

$ ./test.sh
bash

$ sudo -s ./test.sh
bash
cuonglm
quelle
1
Beachten Sie, dass -sdies nicht bedeutet, dass das Skript von interpretiert wird $SHELL, sondern nur, dass $SHELLder Befehl (vorausgesetzt, Sie wurden zur Verwendung als Zielbenutzer autorisiert ) als gestartet wird $SHELL -c ./test.sh. Shells mögen bash, yashoder ksh93können die sie Bang lose Skripte mit einer Kopie von sich selbst, andere nicht und nennen würde das System ist zu interpretieren , shstatt.
Stéphane Chazelas
Mit anderen Worten, es ist nicht $ SHELL anstelle von _PATH_BSHELL , sondern exec($SHELL, "-c", "./test.sh")bedingungslos anstelle von if (exec("./test.sh") == ENOEXEC) exec(_PATH_BSHELL, "./test.sh")(Pseudocode).
Stéphane Chazelas
4

Der Kernel kann nur ausführbare Binärbilder ausführen. Wie werden Skripte ausgeführt? Immerhin kann ich my_script_without_shebangan einer Shell-Eingabeaufforderung tippen und bekomme keine ENOEXECFehlermeldung. Die Skriptausführung erfolgt nicht durch den Kernel, sondern durch die Shell. Der Exec-Code in der Shell sieht normalerweise so aus:

/* try to run the program */
execl(program, basename(program), (char *)0);

/* the exec failed -- maybe it is a shell script without shebang? */
if (errno == ENOEXEC)
    execl ("/bin/sh", "sh", "-c", program, (char *)0);

Sie können dies überprüfen, indem Sie ein Dummy-Shell-Skript ohne Shebang verfolgen:

cat > /tmp/foo.sh <<EOF
echo
EOF

chmod u+x /tmp/foo.sh

strace /tmp/foo.sh 2>&1 | grep exec
execve("/tmp/foo.sh", ["/tmp/foo.sh"], [/* 28 vars */]) = -1 ENOEXEC (Exec format error)

Die Ausführung erfolgt also wie von Stephane beschrieben - die Standard-Shell wird verwendet (im obigen Code-Snippet ist sie fest codiert). Diese nette UNIX-FAQ kann mehr beantworten.

dsmsk80
quelle