まあ、あなたはいつでも行うことができます:
#! /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)"
(ʘ‿ʘ)
それは最初は冗談として始まりましたが、おそらく期待どおりではないかもしれませんが、実際には有用ではありません。しかし、ある程度機能するようになり、いくつかの素晴らしいハックが含まれるようになったので、ここで少し説明します。
以下のようミロスラフが言った、我々はさておきLinuxのスタイルのままにしておくと、機能のuidを変更するには、特権のないプロセスのために(実際にはどちらか、とにかく、ここで役に立たない)、唯一の方法はsetuid実行可能ファイルを実行することです。
ただし、スーパーユーザー権限を取得すると(たとえば、所有者がrootであるsetuid実行可能ファイルを実行して)、保存されたセットユーザーIDを放棄しない限り、有効なユーザーIDを元のユーザーID 0と他のIDの間で切り替えることができます(のようなもの、sudo
またはsu
通常そうするもの)。
例えば:
$ sudo cp /usr/bin/env .
$ sudo chmod 4755 ./env
これenv
で、有効なユーザーIDと0の保存されたセットユーザーID(実際のユーザーIDはまだ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
setuid
/ seteuid
(それら$>
と$<
変数)へのラッパーがあります。
zshも同様です。
$ sudo zsh -c 'EUID=1; id -u; EUID=0; EUID=2; id -u'
1
2
上記では、これらのid
コマンドは実際のユーザーIDと0の保存セットユーザーIDを使用して呼び出されます(ただし、./env
代わりに自分を使用した場合、保存ユーザーID sudo
のみで、実際のユーザーIDは1000のままでした)。つまり、信頼されていないコマンドであったとしても、いくつかのダメージを与える可能性があるため、代わりに次のように記述します。
$ sudo zsh -c 'UID=1 id -u; UID=2 id -u'
(つまり、これらのコマンドを実行するためだけにすべてのuid(有効、実、保存されたセット)が設定されます。
bash
ユーザーIDを変更するような方法はありません。したがって、bash
スクリプトを呼び出すためのsetuid実行可能ファイルがあったとしても、それは役に立ちません。
ではbash
、uidを変更するたびにsetuid実行可能ファイルを実行する必要があります。
上記のスクリプトのアイデアは、SWITCH_TO_USERを呼び出し、新しいbashインスタンスを実行して残りのスクリプトを実行することです。
SWITCH_TO_USER someuser
多かれ少なかれ、別のユーザーとして(を使用してsudo
)スクリプトを再度実行する関数ですが、スクリプトの開始はまでスキップしSWITCH_TO_USER someuser
ます。
トリッキーになるのは、別のユーザーとして新しいbashを開始した後も、現在のbashの状態を保持したい場合です。
分解してみましょう:
{ shopt -s expand_aliases;
エイリアスが必要です。このスクリプトのトリックの1つは、SWITCH_TO_USER someuser
次のようなものを使用して、スクリプトの一部をまでスキップすることです。
:||: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER
この形式は#if 0
Cで使用されているものに似ています。これは、一部のコードを完全にコメント化する方法です。
:
trueを返す何もしないことです。したがって、: || :
では、2番目:
は実行されません。ただし、解析されます。そして、これ<< 'xxx'
は(xxx
引用されているため)ヒアドキュメントの形式であり、拡張も解釈も行われません。
私たちはできたでしょう:
: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER
しかし、それはヒアドキュメントがstdinとしてに書かれて渡されなければならなかったことを意味するでしょう:
。:||:
それを避けます。
ここでハックになるのはbash
、解析プロセスの非常に早い段階でエイリアスを展開するという事実を使用していることです。持っているskip
の別名であること:||: << 'SWITCH_TO_USER someuther'
の一部コメントアウト構築物。
続けましょう:
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";}
SWITCH_TO_USER 関数の定義は次のとおりです。SWITCH_TO_USERが最終的にその関数をラップするエイリアスになることを以下に示します。
この関数は、スクリプトの再実行の大部分を行います。最後に、環境内の変数を使用して(のため同じプロセスでexec
)再実行することがわかります(通常は環境をサニタイズし、任意のenv変数を渡せないため、ここで使用します)。これにより、その変数の内容がbashコードとして評価され、スクリプト自体が提供されます。bash
_x
env
sudo
bash
$_x
_x
以前は次のように定義されています。
_x="$(declare;alias;shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a'
すべてがdeclare
、alias
、shopt -p
set +o
出力はシェルの内部状態のダンプを構成しています。つまり、すべての変数、関数、エイリアス、オプションの定義を、評価可能なシェルコードとしてダンプします。その上で、配列の値に基づいて位置パラメーター($1
、$2
...)の設定を追加し$_a
(以下を参照)、いくつかのクリーンアップを実行して、巨大な$_x
変数が残りの環境にとどまらないようにしますスクリプトの。
までの最初の部分set +x
は、stderrが/dev/null
({...} 2> /dev/null
)にリダイレクトされるコマンドグループにラップされていることがわかります。これは、スクリプトset -x
(またはset -o xtrace
)のある時点で実行される場合、そのプリアンブルがトレースを生成しないようにするためです。したがって、トレースを/ dev / nullに送信するset +x
(xtrace
事前にオプション(を含む)設定をダンプすることを確認した後)を実行します。
eval "$_X"
標準エラー出力にも同様の理由では/ dev / nullにリダイレクトされるだけでなく、特別な読み取り専用の変数への試みを書くことについてエラーを回避します。
スクリプトを続けましょう。
alias skip=":||:<<'SWITCH_TO_USER $_u'"
それが上記のトリックです。最初のスクリプト呼び出しでは、キャンセルされます(以下を参照)。
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
SWITCH_TO_USERのエイリアスラッパーになりました。主な理由は、残りのスクリプトを解釈するnewに位置パラメータ($1
、$2
...)を渡すことができるようにするためですbash
。SWITCH_TO_USER
関数内"$@"
ではスクリプトの引数ではなく、関数の引数であるため、関数でそれを行うことはできませんでした。/ dev / nullへのstderrリダイレクトは、xtraceを非表示にすることであり、これeval
はのバグを回避することですbash
。次に、SWITCH_TO_USER
関数を呼び出します。
${_u+:} alias skip=:
変数が設定されていない限り、その部分はskip
エイリアスをキャンセルします(:
no-opコマンドで置き換え$_u
ます)。
skip
それがskip
エイリアスです。最初の呼び出しでは、それは:
(ノーオペレーション)になります。後続の再呼び出しでは、次のようになります:||: << 'SWITCH_TO_USER root'
。
echo test
a=foo
set a b
SWITCH_TO_USER root
したがって、ここでは例として、その時点で、root
ユーザーとしてスクリプトを再度呼び出し、スクリプトは保存された状態を復元し、そのSWITCH_TO_USER root
行までスキップして続行します。
つまり、statとまったく同じようSWITCH_TO_USER
に、行の先頭に、引数の間に1つのスペースを入れて記述する必要があります。
状態、stdin、stdout、およびstderrのほとんどは保持されますが、sudo
明示的に構成しない限り通常は閉じられるため、他のファイル記述子は保持されません。たとえば:
exec 3> some-file
SWITCH_TO_USER bob
echo test >&3
通常は機能しません。
また、次のことに注意してください。
SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root
それはあなたがする権利があれば働くsudo
ようにalice
とalice
する権利を有するsudo
などbob
、およびbob
などroot
。
したがって、実際には、それは実際には役に立ちません。のsu
代わりにsudo
(または呼び出し元の代わりにターゲットユーザーを認証するsudo
構成sudo
)を使用することは少し意味を成すかもしれませんが、それでも、それらすべての人のパスワードを知る必要があることを意味します。