Mengapa kita tidak bisa menjalankan daftar perintah sebagai pengguna yang berbeda tanpa sudo?

8

Pertanyaan ini ditanyakan dengan cara berbeda di forum lain. Tetapi belum ada penjelasan yang layak mengapa Anda tidak dapat melakukan hal di bawah ini di bash.

#!/bin/bash
command1
SWITCH_USER_TO rag
command2
command3

Biasanya, cara yang disarankan adalah

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

tetapi mengapa tidak mungkin bashuntuk mengubah ke pengguna yang berbeda di beberapa titik selama eksekusi skrip bash dan menjalankan sisa perintah sebagai pengguna yang berbeda?

lap
sumber
Pertanyaan ini juga ditanyakan pada Superuser, di superuser.com/questions/93385/… , dan jawaban ini superuser.com/a/573882/76251 menyediakan metode seperti SINI DOC untuk mengubah pengguna dalam skrip.
Tim Kennedy

Jawaban:

4

Informasi ini sudah ada di jawaban saya yang lain , tetapi agak terkubur di sana. Jadi saya pikir, saya akan menambahkannya di sini.

bashtidak memiliki ketentuan untuk mengubah pengguna, tetapi zshtidak.

Di zsh, Anda mengubah pengguna dengan menetapkan nilai ke variabel-variabel itu:

  • $EUID: ubah id pengguna yang efektif (dan tidak ada yang lain). Biasanya, Anda dapat mengubah euid Anda antara userid asli Anda dan set id pengguna yang disimpan (jika dipanggil dari setuid yang dapat dieksekusi) atau mengubah apa pun jika euid Anda adalah 0.
  • $UID: mengubah id pengguna yang efektif, id pengguna nyata dan id pengguna set yang disimpan ke nilai baru. Kecuali jika nilai baru adalah 0, tidak akan kembali, karena ketiga ketiganya telah disetel ke nilai yang sama, tidak ada cara untuk mengubahnya ke hal lain.
  • $EGIDdan $GID: hal yang sama tetapi untuk id grup.
  • $USERNAME. Itu seperti menggunakan sudoatau su. Ini mengatur cairan Anda, ruid, ssuid ke uid pengguna itu. Ini juga menetapkan kelompok egid, rgid dan ssgid dan tambahan berdasarkan keanggotaan grup sebagaimana didefinisikan dalam database pengguna. Seperti untuk $UID, kecuali jika Anda mengatur $USERNAMEke root, tidak ada kembali, tetapi seperti untuk $UID, Anda dapat mengubah pengguna hanya untuk subkulit.

Jika Anda menjalankan skrip ini sebagai "root":

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

Sekarang, untuk mengubah pengguna seperti pada sudoatau su, kita hanya bisa melakukannya dengan menggunakan subshell, kalau tidak kita hanya bisa melakukannya sekali:

#! /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
sumber
8

Penawaran kernel man 2 setuiddan teman-teman.

Sekarang, ini berfungsi pada proses panggilan. Lebih penting lagi, Anda tidak dapat meningkatkan hak istimewa Anda. Itu sebabnya sudan sudotetapkan SETUID agar mereka selalu berjalan dengan hak istimewa tertinggi ( root) dan turunkan ke pengguna yang diinginkan.

Kombinasi itu berarti bahwa Anda tidak dapat mengubah UID shell dengan menjalankan beberapa program lain untuk melakukan itu (itu sebabnya Anda tidak dapat mengharapkan sudoatau program lain untuk melakukannya) dan Anda tidak dapat (sebagai shell) melakukannya sendiri kecuali Anda bersedia dijalankan sebagai root atau pengguna yang sama yang ingin Anda alihkan yang tidak masuk akal. Terlebih lagi begitu Anda melepaskan hak istimewa, tidak ada jalan kembali.

Miroslav Koškár
sumber
6

Nah, Anda selalu bisa melakukan:

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

(ʘ‿ʘ)

Itu pertama kali dimulai sebagai lelucon karena mengimplementasikan apa yang diminta meskipun mungkin tidak persis seperti yang diharapkan, dan secara praktis tidak berguna. Tapi ketika berevolusi menjadi sesuatu yang bekerja sampai batas tertentu dan melibatkan beberapa peretasan yang bagus, berikut adalah sedikit penjelasan:

Seperti yang dikatakan Miroslav , jika kita mengesampingkan kemampuan gaya Linux (yang toh tidak akan membantu di sini juga), satu-satunya cara untuk proses tidak berubah untuk mengubah uid adalah dengan mengeksekusi setuid executable.

Setelah Anda mendapatkan hak istimewa pengguna super (dengan mengeksekusi setuid executable yang pemiliknya root misalnya), Anda dapat mengganti id pengguna yang efektif bolak-balik antara id pengguna asli Anda, 0 dan id lainnya kecuali jika Anda melepaskan id pengguna set yang disimpan ( suka hal-hal seperti sudoatau subiasanya dilakukan).

Contohnya:

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

Sekarang saya punya envperintah yang memungkinkan saya untuk menjalankan perintah dengan id pengguna yang efektif dan set id pengguna yang disimpan 0 ( id pengguna asli saya masih 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

perlmemiliki pembungkus ke setuid/ seteuid(yang $>dan $<variabel).

Begitu juga zsh:

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

Meskipun di atas idperintah - perintah itu disebut dengan id pengguna nyata dan set userid disimpan dari 0 (meskipun jika saya telah menggunakan saya ./envbukan sudoitu akan hanya set userid yang disimpan, sedangkan id pengguna sebenarnya akan tetap 1000), yang berarti bahwa jika itu adalah perintah yang tidak dipercaya, mereka masih bisa melakukan kerusakan, jadi Anda ingin menulisnya seperti:

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

(Yaitu mengatur semua uids (set efektif, nyata dan disimpan) hanya untuk pelaksanaan perintah-perintah itu.

bashtidak memiliki cara untuk mengubah id pengguna. Jadi, bahkan jika Anda memiliki setuid yang dapat dieksekusi untuk memanggil bashskrip Anda , itu tidak akan membantu.

Dengan bash, Anda harus menjalankan setuid yang dapat dieksekusi setiap kali Anda ingin mengubah uid.

Gagasan dalam skrip di atas adalah panggilan ke SWITCH_TO_USER, untuk mengeksekusi contoh bash baru untuk mengeksekusi sisa skrip.

SWITCH_TO_USER someuserlebih atau kurang fungsi yang mengeksekusi skrip lagi sebagai pengguna yang berbeda (menggunakan sudo) tetapi melewatkan awal skrip sampai SWITCH_TO_USER someuser.

Yang menjadi rumit adalah kami ingin menjaga status bash saat ini setelah memulai bash baru sebagai pengguna yang berbeda.

Mari kita jabarkan:

{ shopt -s expand_aliases;

Kami membutuhkan alias. Salah satu trik dalam skrip ini adalah untuk melewatkan bagian skrip hingga SWITCH_TO_USER someuser, dengan sesuatu seperti:

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

Bentuk itu mirip dengan yang #if 0digunakan dalam C, yaitu cara untuk melengkapi komentar beberapa kode.

:adalah no-op yang mengembalikan true. Jadi : || :, yang kedua :tidak pernah dieksekusi. Namun, ini diuraikan. Dan itu << 'xxx'adalah bentuk di sini-dokumen di mana (karena xxxdikutip), tidak ada perluasan atau interpretasi dilakukan.

Kita bisa melakukan:

: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

Tetapi itu berarti bahwa dokumen di sini harus ditulis dan diteruskan sebagai standar :. :||:hindari itu.

Sekarang, di mana peretasannya adalah kita menggunakan fakta yang bashmemperluas alias lebih awal dalam proses penguraiannya. Menjadi skipalias ke :||: << 'SWITCH_TO_USER someuther'bagian konstruksi komentar .

Mari kita lanjutkan:

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

Inilah definisi fungsi SWITCH_TO_USER . Kita akan melihat di bawah bahwa SWITCH_TO_USER pada akhirnya akan menjadi alias yang membungkus fungsi itu.

Fungsi itu melakukan sebagian besar mengeksekusi kembali skrip. Pada akhirnya kita melihat bahwa itu dieksekusi kembali (dalam proses yang sama karena exec) bashdengan _xvariabel di lingkungan itu (kita gunakan di envsini karena sudobiasanya membersihkan lingkungannya dan tidak memungkinkan melewati sewenang-wenang dan melalui). Itu bashmengevaluasi konten $_xvariabel itu sebagai kode bash dan sumber skrip itu sendiri.

_x didefinisikan sebelumnya sebagai:

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

Semua declare, alias, shopt -p set +okeluaran membuat dump dari keadaan internal shell. Artinya, mereka membuang definisi semua variabel, fungsi, alias dan opsi sebagai kode shell yang siap dievaluasi. Selain itu, kami menambahkan pengaturan parameter posisi ( $1, $2...) berdasarkan nilai $_aarray (lihat di bawah), dan beberapa membersihkan sehingga $_xvariabel besar tidak tinggal di lingkungan selama sisa naskah.

Anda akan melihat bahwa bagian pertama hingga set +xdibungkus dalam grup perintah yang stderrnya diarahkan ke /dev/null( {...} 2> /dev/null). Itu karena, jika pada beberapa titik dalam skrip set -x(atau set -o xtrace) dijalankan, kami tidak ingin pembukaan itu menghasilkan jejak karena kami ingin membuatnya sesedikit mungkin mengganggu. Jadi kami menjalankan set +x(setelah memastikan untuk membuang opsi (termasuk xtrace) pengaturan sebelumnya) di mana jejak dikirim ke / dev / null.

The eval "$_X"stderr juga diarahkan ke / dev / null untuk alasan yang sama tetapi juga untuk menghindari kesalahan tentang menulis upaya untuk variabel read-only khusus.

Mari kita lanjutkan dengan skrip:

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

Itu trik kami yang dijelaskan di atas. Pada permintaan skrip awal, itu akan dibatalkan (lihat di bawah).

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

Sekarang pembungkus alias di sekitar SWITCH_TO_USER. Alasan utamanya adalah untuk dapat meneruskan parameter posisi ( $1, $2...) ke yang baru bashyang akan menafsirkan sisa skrip. Kami tidak dapat melakukannya dalam SWITCH_TO_USER fungsi karena di dalam fungsi, "$@"ada argumen untuk fungsi, bukan dari skrip. Pengalihan stderr ke / dev / null lagi untuk menyembunyikan xtraces, dan evaluntuk mengatasi bug di bash. Lalu kita memanggil SWITCH_TO_USER fungsi .

${_u+:} alias skip=:

Bagian itu membatalkan skipalias (menggantinya dengan perintah :no-op) kecuali $_uvariabelnya disetel.

skip

Itu skipalias kami . Pada doa pertama, itu hanya akan menjadi :(no-op). Pada subsequence re-doa, itu akan menjadi sesuatu seperti: :||: << 'SWITCH_TO_USER root'.

echo test
a=foo
set a b

SWITCH_TO_USER root

Jadi di sini, sebagai contoh, pada saat itu, kami menginstal kembali skrip sebagai rootpengguna, dan skrip akan memulihkan keadaan yang disimpan, dan melompat ke SWITCH_TO_USER rootbaris itu dan melanjutkan.

Artinya adalah harus ditulis persis seperti stat, dengan SWITCH_TO_USERdi awal baris dan dengan tepat satu spasi di antara argumen.

Sebagian besar status, stdin, stdout, dan stderr akan dipertahankan, tetapi bukan deskriptor file lainnya karena sudobiasanya ditutup kecuali dikonfigurasikan secara eksplisit untuk tidak melakukannya. Jadi misalnya:

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

biasanya tidak akan berfungsi.

Perhatikan juga bahwa jika Anda melakukannya:

SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root

Yang hanya bekerja jika Anda memiliki hak untuk sudosebagai alicedan alicememiliki hak untuk sudosebagai bob, dan bobsebagai root.

Jadi, dalam praktiknya, itu tidak terlalu berguna. Menggunakan sualih-alih sudo(atau sudokonfigurasi tempat sudomengotentikasi pengguna target alih-alih penelepon) mungkin lebih masuk akal, tetapi itu tetap berarti Anda harus mengetahui kata sandi semua orang itu.

Stéphane Chazelas
sumber
dapatkah Anda jelaskan apa yang sedang dilakukan skrip Anda?
kain
@rag, Anda memintanya ...
Stéphane Chazelas
1
Apakah itu entri Anda untuk ioshcc? Anda seharusnya memasukkan hanya satu baris.
ott--
-2

Perintah "sudo -u ram sh" dapat digunakan untuk mengubah ke pengguna "ram", dengan menjalankan perintah "sh" atau shell. Perintah "keluar" akan membawa Anda kembali ke pengguna asli.

pengguna45185
sumber