34

非特権ユーザーとして実行されるが、ある時点で(新しい仮想デバイスを作成するために)root特権を必要とするソフトウェア(C ++、Linux / Mac OSX用)を作成しています。

このプログラムをrootとして実行することはオプションではなく(主にセキュリティの問題のため)、「実際の」ユーザーのID(uid)を知る必要があります。

「sudo」コマンドの動作(ユーザーパスワードを要求する)を模倣して、一時的にroot権限を取得し、特定のタスクを実行する方法はありますか?もしそうなら、どの関数を使用しますか?

ご助力ありがとうございます !

4

7 に答える 7

20

毎回root権限が必要な場合は、プログラムをrootとして起動し、setuidsetgidを使用して(サブプロセスで)ドロップするのが最善の方法です。これは、制限されたポート80にバインドする必要がある場合にapacheが行うことです。

ルート権限の取得がルールではなく例外であり、プログラムがインタラクティブに実行される場合、別の方法は、プログラムadd_interfaceを記述して実行することです。

sudo add_interface args

sudoに認証を処理させます。sudoの代わりに、gksu、gksudo、kdesu、kdesudoなどのグラフィカルフロントエンドを使用することもできます。安全なパスワード入力を自分で実装しようとはしません。それはトリッキーな問題になる可能性があり、おそらくギャップのあるセキュリティホールと機能の問題を残すでしょう(指紋リーダーをサポートしていますか?)。

もう1つの代替手段は、以前はPolicyKitと呼ばれていたpolkitです。

于 2010-03-20T16:21:42.770 に答える
15

元の回答

実行可能ファイル自体のsetuidスイッチを検討することもできます。ウィキペディアには、との違いを非常に効果的に示す記事があります。前者はあなたが「エミュレート」している人を見つけるためのものであり、後者はあなたが「誰である」かを知るためのものです。たとえば、sudoプロセスは0(root)を返し、getuidはユーザーのIDを返す必要がありますが、そのサブプロセスは実際にはrootとして実行されます(これはで確認できます)。geteuid()getuid()sudo id -u -r

プログラムで簡単にrootアクセスを取得する方法はないと思います。結局のところ、最小特権の原則を適用すると、なぜそうする必要があるのでしょうか。一般的な方法は、昇格された特権でコードの限られた部分のみを実行することです。多くのデーモンなども、必要なほとんどの特権を持つ独自のユーザーとして実行するために、最新のシステムの下にセットアップされています。ルート権限が本当に必要になるのは、非常に特定の操作(マウントなど)の場合のみです。

2013年の更新

私の元の答えは正しいですが(2013年の自己は2010年の自己よりもうまくいくかもしれませんが)、rootアクセスを必要とするアプリケーションを設計している場合は、どのような種類のrootアクセスが必要かを正確に検討し、POSIX機能 の使用(マニュアルページ)。これらは、L4etalで実装されている機能ベースのセキュリティとは異なります。POSIX機能を使用すると、アプリケーションにrootの権限のサブセットを付与できます。たとえばCAP_SYS_MODULE、カーネルモジュールを挿入することはできますが、他のルートパワーは与えられません。これはディストリビューションで使用されています。たとえば、Fedoraには、無差別なルートアクセスでsetuidバイナリを完全に削除する機能があります。

プログラマーとして、あなたのコードは明らかに完璧なので、これは重要です!しかし、あなたが依存しているライブラリー(あなたがそれらを書いただけならため息をつく!)はそれらに脆弱性を持っているかもしれません。機能を使用すると、このエクスプロイトの使用を制限し、セキュリティ関連の監視から自分自身と会社を救うことができます。これにより、誰もが幸せになります。

于 2010-03-20T16:25:18.560 に答える
9

ルート権限を取得することはできません。ルート権限から始めて、必要に応じて権限を減らす必要があります。これを行う通常の方法は、「setuid」ビットを設定してプログラムをインストールすることです。これにより、ファイル所有者の有効なユーザーIDを使用してプログラムが実行されます。で実行するls -lsudo、次のようにインストールされていることがわかります。

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo

プログラムがroot権限で実行されているときに、setuid(2)システムコールを呼び出して、有効なユーザーIDを非特権ユーザーに変更できます。setuidビットをオンにしてプログラムをrootとしてインストールし、すぐに特権を減らしてから、必要に応じて特権を復元できると思います(ただし、これを試したことはありません)。それを復元することができます)。

より良い解決策は、rootとして実行する必要のあるプログラムの一部を分割し、setuidビットをオンにしてインストールすることです。もちろん、マスタープログラムの外部で呼び出すことができないように、合理的な予防策を講じる必要があります。

于 2010-03-20T16:26:53.860 に答える
4

通常、これはバイナリのsuid-rootを作成することによって行われます。

プログラムに対する攻撃を困難にするためにこれを管理する1つの方法は、次のようにrootとして実行されるコードを最小化することです。

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を使用します。

特権コードは、実行する必要のあるすべての操作のセキュリティを検証する必要がありますが、このコードは非特権コードに比べて小さいため、かなり簡単です。

于 2010-03-20T16:29:44.430 に答える
2

これらのAPIを確認することをお勧めします。

setuid, seteuid, setgid, setegid, ...

これらはLinuxシステムのヘッダーで定義されてい<unistd.h>ます(MACについてはよくわかりませんが、同様のヘッダーが必要です)。

私が見ることができる1つの問題は、プロセスがそのユーザー/グループIDを変更するのに十分な特権を持っている必要があるということです。そうしないと、上記の関数を呼び出すと、にerrorno設定されたエラーが発生しEPERMます。

rootプログラムをユーザーとして実行し、最初に有効なユーザーID(を使用してseteuid)を恵まれないユーザーに変更することをお勧めします。次に、権限を昇格する必要があるときはいつでも、パスワードの入力を求めてseteuidから、もう一度使用してユーザーに戻りrootます。

于 2010-03-20T16:26:54.970 に答える
2

OS Xでは、この機能を使用できますAuthorizationExecuteWithPrivilegesAuthorization 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;
}
于 2016-02-10T13:26:05.507 に答える
1

コマンドを起動して、バックグラウンドシェルを介して仮想デバイス(sudoを含む)を作成してみてください。独自のダイアログでユーザーのパスワードを要求し、sudoが要求したときにそれをシェルにパイプします。gksuを使用するような他のソリューションもありますが、それらがすべてのマシンで利用できるとは限りません。

プログラム全体をrootとして実行するのではなく、rootを必要とするプログラムのごく一部のみを実行します。そのために別のプロセスを作成する必要があります。sudoが役立つ場合があります。

于 2010-03-20T16:16:18.847 に答える