非特権ユーザーとして実行されますが、ある時点で(新しい仮想デバイスを作成するために)root特権を必要とするソフトウェア(C++、Linux/Mac OSX)を書いています。
このプログラムをrootとして実行することはオプションではなく(主にセキュリティの問題のため)、「実際の」ユーザーのID(uid)を知る必要があります。
「Sudo」コマンドの動作を模倣して(ユーザーパスワードを要求)、一時的にroot権限を取得して特定のタスクを実行する方法はありますか?もしそうなら、どの機能を使用しますか?
手伝ってくれてありがとうございます !
元の答え
実行可能ファイル自体のsetuidスイッチを検討する場合があります。ウィキペディアには article があり、geteuid()
とgetuid()
の違いをかなり効果的に示しています。前者は「エミュレートしている人を見つけるためのものです。 「そして後者はあなたが誰であるか」です。たとえば、seuプロセスはgeteuidが0(root)を返し、getuidがユーザーのIDを返す必要がありますが、そのサブプロセスは本当にrootとして実行されます(Sudo id -u -r
でこれを確認できます)。
プログラムでルートアクセスを簡単に取得する方法はないと思います。結局、最小特権の原則を適用すると、なぜ必要なのでしょうか。一般的な方法は、昇格された特権でコードの限られた部分のみを実行することです。多くのデーモンなども、必要なほとんどの特権を持つ独自のユーザーとして実行するように、最新のシステムでセットアップされています。 root権限が本当に必要なのは、非常に特殊な操作(マウントなど)の場合だけです。
2013年の更新
私の元の答えはそのままです(私の2013年の自己は2010年のそれよりもうまくいくかもしれませんが)ルートアクセスを必要とするアプリケーションを設計している場合、必要なルートアクセスの種類を正確に検討し、 POSIX Capabilities(man page) の使用。これらは、L4などで実装されている capability-based security とは異なります。 POSIX機能により、アプリケーションにrootの権限のサブセットを付与できます。たとえば、CAP_SYS_MODULE
を使用すると、カーネルモジュールを挿入できますが、他のルート権限は付与されません。これはディストリビューションで使用されています。 Fedoraには、setuidバイナリを完全に削除する機能があります 無差別のrootアクセス。
プログラマーとしてあなたのコードは明らかに完璧であるため、これは重要です!しかし、あなたが依存しているライブラリ(もしあなたがそれらを書いただけなら!)には脆弱性があるかもしれません。機能を使用して、このエクスプロイトの使用を制限し、セキュリティ関連の精査から自分自身と会社を救うことができます。これは誰もが幸せになります。
毎回root権限が必要な場合は、プログラムをrootとして起動し、それらを(サブプロセスで) setuid および setgid で削除することをお勧めします。これは、Apacheが制限されたポート80にバインドする必要がある場合に行うことです。
ルールではなくルート権限の取得が例外であり、プログラムがインタラクティブに実行される場合、別の方法はプログラムadd_interfaceを記述して実行することです
Sudo add_interface args
sudoに認証を処理させます。 Sudoの代わりに、gksu、gksudo、kdesu、kdesudoなどのグラフィカルフロントエンドを使用することもできます。安全なパスワード入力を自分で実装しようとはしません。これはトリッキーな問題になる可能性があり、おそらくセキュリティホールや機能上の問題が残されます(指紋リーダーをサポートしていますか?)。
別の代替手段は、以前はPolicyKitと呼ばれていた polkit です。
Root権限を取得することはできません。root権限から始めて、必要に応じて権限を減らす必要があります。これを行う通常の方法は、「setuid」ビットを設定してプログラムをインストールすることです。これにより、ファイル所有者の有効なユーザーIDでプログラムが実行されます。 Sudo
で_ls -l
_を実行すると、そのようにインストールされていることがわかります。
_-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/Sudo
_
プログラムがroot特権で実行されている間、setuid(2)
システムコールを呼び出して、有効なユーザーIDを特権のないユーザーに変更できます。 (これを試したことはありませんが)setuidビットをオンにしてrootとしてプログラムをインストールし、すぐに特権を減らし、必要に応じて特権を復元できると信じています(ただし、一度特権を下げると、それを復元することができます)。
より良い解決策は、rootとして実行する必要があるプログラムの一部を取り出し、setuidビットをオンにしてインストールすることです。もちろん、マスタープログラムの外部からは呼び出せないように、適切な予防策を講じる必要があります。
通常、これはバイナリをsuid-rootにすることで行われます。
プログラムに対する攻撃が困難になるようにこれを管理する1つの方法は、ルートとして実行されるコードを次のように最小化することです。
int privileged_server(int argc, char **argv);
int unprivileged_client(int argc, char **argv, int comlink);
int main(int argc, char **argv) {
int sockets[2];
pid_t child;
socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */
child = fork();
if (child < 0) {
perror("fork");
exit(3);
} elseif (child == 0) {
close(sockets[0]);
dup2(sockets[1], 0);
close(sockets[1]);
dup2(0, 1);
dup2(0, 2); /* or not */
_exit(privileged_server(argc, argv));
} else {
close(sockets[1]);
int rtn;
setuid(getuid());
rtn = unprivileged_client(argc, argv, sockets[0]);
wait(child);
return rtn;
}
}
非特権コードは、fd comlink(接続されたソケット)を介して特権コードと通信します。対応する特権コードは、comlinkの終わりとしてstdin/stdoutを使用します。
特権コードは、実行する必要があるすべての操作のセキュリティを検証する必要がありますが、このコードは非特権コードに比べて小さいため、これはかなり簡単です。
OS Xでは、 AuthorizationExecuteWithPrivileges
関数を使用できます。 Authorization Services Tasks のページには、この(および関連する)関数についての詳細な説明があります。
管理者権限でプログラムを実行するためのC++コードを次に示します。
static bool execute(const std::string &program, const std::vector<std::string> &arguments)
{
AuthorizationRef ref;
if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) {
return false;
}
AuthorizationItem item = {
kAuthorizationRightExecute, 0, 0, 0
};
AuthorizationRights rights = { 1, &item };
const AuthorizationFlags flags = kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagPreAuthorize
| kAuthorizationFlagExtendRights;
if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) {
AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
return false;
}
std::vector<char*> args;
for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
args.Push_back(it->c_str());
}
args.Push_back(0);
OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0);
AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
return status == errAuthorizationSuccess;
}
次のAPIをご覧ください。
setuid, seteuid, setgid, setegid, ...
Linuxシステムではヘッダー<unistd.h>
で定義されています(MACについてはあまり知りませんが、同様のヘッダーもそこにあるはずです)。
私が見ることができる1つの問題は、プロセスがそのユーザー/グループIDを変更するための十分な特権を持っている必要があることです。そうでない場合、上記の関数を呼び出すと、errorno
がEPERM
に設定されたエラーが発生します。
root
ユーザーとしてプログラムを実行し、有効なユーザーIDを(seteuid
を使用して)最初から権限のないユーザーに変更することをお勧めします。次に、権限を昇格する必要がある場合は常に、パスワードの入力を求め、次にseteuid
を使用してroot
ユーザーに戻ります。
コマンドを起動して、バックグラウンドシェルから仮想デバイス(Sudoを含む)を作成してみてください。独自のダイアログでユーザーのパスワードを要求し、Sudoが要求したときにそれをシェルにパイプします。 gksuの使用のような他のソリューションがありますが、それらはすべてのマシンで利用できるとは限りません。
プログラム全体をルートとして実行するのではなく、ルートを必要とするプログラムのごく一部のみを実行します。そのために別のプロセスを生成する必要があります。Sudoが役立つ場合があります。