「C++の応用 - PolKit」の版間の差分
編集の要約なし |
|||
| (同じ利用者による、間の3版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
Polkit (formerly PolicyKit) は、Linuxシステムで特権的な操作を行う際の認可 (authorization) を決定するフレームワークである。<br> | |||
sudoのようにプロセス全体をrootで実行するのではなく、細かいアクション単位で許可を判定できる。<br> | |||
<br> | <br> | ||
Polkitは2008年頃にDavid Zeuthenによって開発され、D-Busベースの認可フレームワークとして成熟した。<br> | |||
中核となるAuthorityは <code>org.freedesktop.PolicyKit1</code> というD-Busサービスとして動作し、アプリケーションからSubjectとAction IDを受け取って判定を行う。<br> | |||
<br> | <br> | ||
<code>sudo</code> | Action IDは <u>/usr/share/polkit-1/actions/</u> ディレクトリの <code>.policy</code> ファイル (XML形式) で定義され、認可ルールは <u>/etc/polkit-1/rules.d/</u> 等のJavaScriptファイルで記述される。<br> | ||
これにより、管理者はアプリケーションやユーザ/groupごとに詳細なポリシーを設定できる。<br> | |||
<br> | |||
C++からPolkitを利用するには、<code>libpolkit-gobject-1</code> をラップする方法と、D-Busインターフェースを直接呼び出す方法がある。<br> | |||
<br> | |||
C++と外部ライブラリのみで開発する場合は、GLib / GObjectのリソース管理やD-Busメッセージのパースを適切に行う必要がある。<br> | |||
<br> | |||
Polkitを適切に使用することで、GUIやCLIアプリケーションから最小限の特権で安全にシステム操作を実行できる。<br> | |||
例えば、システム設定の変更やデバイスのマウント等、root権限が必要な操作をユーザに確認しながら行うことが可能である。<br> | |||
<br><br> | |||
== Polkitの基本 == | |||
==== 主要な構成要素 ==== | |||
Polkitは、下表に示す要素で構成される。<br> | |||
<br> | |||
<center> | |||
{| class="wikitable" | |||
|+ Polkitの主要な構成要素 | |||
|- | |||
! 要素 !! 説明 | |||
|- | |||
| Authority || D-Bus上の <code>org.freedesktop.PolicyKit1</code> サービス<br>認可判定の最終的な権限を持つ。 | |||
|- | |||
| Subject || 認可を求めるエンティティ<br>通常は <code>("unix-process", {"pid": uint32, "start-time": uint64})</code> 形式のD-Bus構造体 | |||
|- | |||
| Action || <code>.policy</code> XMLファイルで定義された操作<br>Action ID (例: <code>org.freedesktop.policykit.exec</code>) で識別される。 | |||
|- | |||
| Authentication Agent || ユーザにパスワード等を要求するUIプログラム<br>GUIやTUIで動作する。 | |||
|- | |||
| Rule files || JavaScriptで記述された <code>.rules</code> ファイル<br>管理者がポリシーをカスタマイズするために使用する。 | |||
|} | |||
</center> | |||
<br> | |||
Subjectには、プロセス形式の <code>unix-process</code> の他、セッション形式の <code>unix-session</code> も存在する。<br> | |||
<br> | |||
C++アプリケーションから自プロセスの認可を確認する場合、<code>unix-process</code> Subjectを使用するのが一般的である。<br> | |||
<br> | |||
Authentication Agentの実装については、<code>PolkitAgentListener</code> のサブクラス化が必要であるが、本ページではアプリケーション側の認可チェックを中心に解説する。<br> | |||
<br> | |||
==== アクションとルールファイル ==== | |||
Actionは、<u>/usr/share/polkit-1/actions</u> 内の <code>.policy</code> ファイルで定義される。<br> | |||
<br> | |||
各アクションには、以下の属性が含まれる。<br> | |||
<br> | |||
* action id | |||
*: 一意の識別子 | |||
*: 例: <u>org.example.myapp.configure</u> | |||
*: <br> | |||
* description / message | |||
*: ユーザに表示される説明文 | |||
*: <br> | |||
* defaults | |||
*: 各認可結果 (allow_any, allow_inactive, allow_active) に対するデフォルトポリシー | |||
*: <code>yes</code>、<code>no</code>、<code>auth_self</code>、<code>auth_admin</code> 等が指定できる。 | |||
*: <br> | |||
* vendor / vendor_url | |||
*: アクションの提供者情報 | |||
<br> | |||
Ruleファイルは、<u>/etc/polkit-1/rules.d/</u> と <u>/usr/share/polkit-1/rules.d/</u> に配置される。<br> | |||
ファイル名は <code><数字>-name.rules</code> の形式で、数字の小さいものから順に評価される。<br> | |||
<br> | |||
JavaScript形式のRuleファイルの例を以下に示す。<br> | |||
<br> | |||
<syntaxhighlight lang="javascript"> | |||
polkit.addRule(function(action, subject) { | |||
if (action.id == "org.example.myapp.configure" && subject.isInGroup("wheel")) { | |||
return polkit.Result.YES; | |||
} | |||
}); | |||
</syntaxhighlight> | |||
<br> | |||
この例では、<code>wheel</code> グループに属するユーザが <code>org.example.myapp.configure</code> アクションを実行できるようにしている。<br> | |||
<br> | |||
Ruleファイルを編集した後は、<code>polkit</code> デーモンが自動的に再読み込みを行うが、即座に反映させたい場合は <code>sudo systemctl restart polkit</code> コマンドを実行する。<br> | |||
<br><br> | |||
== C++からPolkitを利用する方法の比較 == | |||
C++からPolkitを利用する主な方法を比較する。<br> | |||
<br> | |||
<center> | |||
{| class="wikitable" | |||
|+ Polkit利用方法の比較 | |||
|- | |||
! 方法 !! 依存関係 !! 実装難易度 !! 用途 | |||
|- | |||
| libpolkit-gobject-1 || GLib / GObject、D-Busシステムバス || 低 || 一般的なアプリケーションの認可チェック | |||
|- | |||
| D-Bus直接呼び出し (libdbus等) || D-Busライブラリ、システムバス || 中 || 軽量実装や特殊なD-Bus制御が必要な場合 | |||
|- | |||
| sd-bus (systemd) || libsystemd || 中 || systemdベースの環境で直接使用 | |||
|} | |||
</center> | |||
<br> | |||
libpolkit-gobject-1は、GObjectのリファレンスカウントとGErrorを適切に扱う必要がある。<br> | |||
<br> | |||
D-Bus直接呼び出しは、Subject構造体や戻り値のパースを自前で行う必要がある。<br> | |||
<br><br> | <br><br> | ||
== | == Polkitのインストール == | ||
==== パッケージ管理システムからインストール ==== | ==== パッケージ管理システムからインストール ==== | ||
多くのLinuxディストリビューションには、初期状態でPolkitがインストールされている。<br> | |||
<br> | |||
C++から開発する場合は、追加で開発用パッケージが必要となる。<br> | |||
<br> | |||
# RHEL | # RHEL | ||
sudo dnf install polkit-devel | sudo dnf install polkit-devel glib2-devel pkg-config | ||
# SUSE | # SUSE | ||
sudo zypper install polkit-devel | sudo zypper install polkit-devel glib2-devel pkg-config | ||
<br> | <br> | ||
==== ソースコードからインストール ==== | ==== ソースコードからインストール ==== | ||
まず、Polkitのビルドに必要な依存関係のライブラリをインストールする。<br> | |||
<br> | <br> | ||
# RHEL | |||
sudo dnf install meson dbus-devel expat-devel systemd-devel glib2-devel \ | |||
gtk3-devel gobject-introspection-devel pam-devel \ | |||
duktape-devel gtk-doc mozjs115-devel | |||
# SUSE | # SUSE | ||
sudo zypper install meson dbus-1-devel libexpat-devel systemd-devel glib2-devel gtk3-devel gobject-introspection-devel mozjs78-devel pam-devel duktape-devel gtk- | sudo zypper install meson dbus-1-devel libexpat-devel systemd-devel \ | ||
glib2-devel gtk3-devel gobject-introspection-devel \ | |||
mozjs78-devel pam-devel duktape-devel gtk-doc | |||
<br> | <br> | ||
[https://www.freedesktop.org/software/polkit/releases/ | [https://www.freedesktop.org/software/polkit/releases/ Polkit公式Webサイト] または [https://gitlab.freedesktop.org/polkit/polkit/-/tags GitLab]からソースコードをダウンロードする。<br> | ||
ダウンロードしたファイルを解凍する。<br> | ダウンロードしたファイルを解凍する。<br> | ||
<br> | |||
tar xf polkit-<バージョン>.tar.gz | tar xf polkit-<バージョン>.tar.gz | ||
cd polkit-<バージョン> | cd polkit-<バージョン> | ||
<br> | <br> | ||
または、<code>git clone</code> コマンドを実行して、ソースコードをダウンロードする。<br> | |||
<br> | |||
git clone https://gitlab.freedesktop.org/polkit/polkit.git | git clone https://gitlab.freedesktop.org/polkit/polkit.git | ||
cd polkit | cd polkit | ||
<br> | <br> | ||
Polkitのビルドおよびインストールを行う。<br> | |||
指定可能なオプションは、<code>meson configure</code> コマンドで確認できる。<br> | |||
<br> | |||
# RHEL | # RHEL | ||
meson setup build --prefix=< | meson setup build \ | ||
--prefix=<Polkitのインストールディレクトリ> \ | |||
-Dos_type=redhat \ | |||
-Dexamples=true \ | |||
-Dman=true \ | |||
-Dgtk_doc=true | |||
# SUSE | # SUSE | ||
meson setup build --prefix=< | meson setup build \ | ||
--prefix=<Polkitのインストールディレクトリ> \ | |||
-Dos_type=suse \ | |||
-Dexamples=true \ | |||
-Dman=true \ | |||
-Dgtk_doc=true | |||
meson compile -C build | meson compile -C build | ||
meson install -C build | meson install -C build | ||
<br> | <br> | ||
== CMakeファイルの設定 == | == CMakeファイルの設定 == | ||
==== Pkg- | ==== Pkg-Configを使用する場合 ==== | ||
以下の例は、<code>pkg_check_modules</code> コマンドを使用して依存関係を解決する例である。<br> | |||
<br> | <br> | ||
<syntaxhighlight lang="cmake"> | <syntaxhighlight lang="cmake"> | ||
# CMakeLists.txtファイル | # CMakeLists.txtファイル | ||
find_package(PkgConfig REQUIRED) | find_package(PkgConfig REQUIRED) | ||
pkg_check_modules(GLIB2 REQUIRED glib-2.0) | pkg_check_modules(GLIB2 REQUIRED glib-2.0) | ||
pkg_check_modules(POLKITGOBJECT2 REQUIRED polkit-gobject-1) | pkg_check_modules(POLKITGOBJECT2 REQUIRED polkit-gobject-1) | ||
| 95行目: | 176行目: | ||
pkg_check_modules(GIO2 REQUIRED gio-2.0) | pkg_check_modules(GIO2 REQUIRED gio-2.0) | ||
add_executable(<プロジェクト名> | add_executable(<プロジェクト名> | ||
# ...略 | # ...略 | ||
) | ) | ||
| 125行目: | 200行目: | ||
${GIO2_CFLAGS_OTHER} | ${GIO2_CFLAGS_OTHER} | ||
) | ) | ||
target_link_libraries(<プロジェクト名> | |||
${GLIB2_LIBRARIES} | |||
${POLKITGOBJECT2_LIBRARIES} | |||
${GOBJECT2_LIBRARIES} | |||
${GIO2_LIBRARIES} | |||
) | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
==== Pkg-Configを使用しない場合 ==== | ==== Pkg-Configを使用しない場合 ==== | ||
<syntaxhighlight lang=" | 以下の例は、Pkg-Configを使用せずに直接パスを指定する例である。<br> | ||
<br> | |||
<syntaxhighlight lang="cmake"> | |||
# CMakeLists.txtファイル | |||
add_executable(<プロジェクト名> | |||
/usr/lib64/glib-2.0/include | # ...略 | ||
/usr/include/glib-2.0 | ) | ||
## インクルードファイル | |||
include_directories( | |||
# ...略 | |||
/usr/lib64/glib-2.0/include | |||
/usr/include/glib-2.0 | |||
/usr/include/polkit-1 | /usr/include/polkit-1 | ||
# ...略 | |||
) | |||
## ライブラリファイル | |||
target_link_libraries(<プロジェクト名> | |||
# ...略 | |||
glib-2.0 | |||
polkit-gobject-1 | |||
gobject-2.0 | |||
gio-2.0 | |||
# ...略 | |||
) | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== | == libpolkit-gobject-1を使用する方法 == | ||
==== libpolkit-gobject-1とは ==== | |||
libpolkit-gobject-1は、Polkitの機能をGLib / GObjectベースのC APIで提供するライブラリである。<br> | |||
<br> | |||
主な関数として、Authorityを取得する <code>polkit_authority_get_sync()</code> と、認可を確認する <code>polkit_authority_check_authorization_sync()</code> が存在する。<br> | |||
また、Subjectを作成する <code>polkit_unix_process_new_for_owner()</code> 等の関数も提供する。<br> | |||
<br> | |||
<u>C++から使用する場合、<code>g_object_unref()</code> によるリソース解放や <code>g_error_free()</code> によるGErrorの解放を徹底する。</u><br> | |||
<br> | |||
また、RAIIラッパーを用意することで、GObjectのリファレンスカウントを安全に管理できる。<br> | |||
<br> | |||
==== 開発パッケージのインストール ==== | |||
# RHEL | |||
sudo dnf install pkg-config polkit-devel glib2-devel | |||
# SUSE | |||
sudo zypper install pkg-config polkit-devel glib2-devel | |||
<br> | |||
==== 認可チェックの実装例 ==== | |||
以下の例では、libpolkit-gobject-1を使用して、Action ID <u>org.example.myapp.configure</u> の認可を確認している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#include <iostream> | |||
#include <memory> | |||
#include <unistd.h> | |||
#include <polkit/polkit.h> | #include <polkit/polkit.h> | ||
// GErrorをRAIIで管理するヘルパー | |||
struct GErrorDeleter { | |||
void operator()(GError* e) const { if (e) g_error_free(e); } | |||
}; | |||
using GErrorPtr = std::unique_ptr<GError, GErrorDeleter>; | |||
// GObjectをRAIIで管理するヘルパー | |||
struct GObjectDeleter { | |||
void operator()(gpointer o) const { if (o) g_object_unref(o); } | |||
}; | |||
template<typename T> | |||
using GObjectPtr = std::unique_ptr<T, GObjectDeleter>; | |||
int main() | |||
{ | |||
GError* raw_error = nullptr; | |||
PolkitAuthority* authority = polkit_authority_get_sync(nullptr, &raw_error); | |||
GErrorPtr error(raw_error); | |||
if (!authority) { | |||
std::cerr << "Failed to get polkit authority: " | |||
<< (error ? error->message : "unknown") << "\n"; | |||
return 1; | |||
} | |||
GObjectPtr<PolkitAuthority> auth_ptr(authority); | |||
// Subjectを作成 (自プロセス) | |||
pid_t pid = getpid(); | |||
guint64 start_time = 0; // 実際には /proc/self/stat から取得する | |||
PolkitSubject* subject = polkit_unix_process_new_for_owner(pid, start_time, getuid()); | |||
if (!subject) { | |||
std::cerr << "Failed to create subject\n"; | |||
return 1; | |||
} | |||
GObjectPtr<PolkitSubject> subject_ptr(subject); | |||
// 認可チェック | |||
PolkitAuthorizationResult* result = polkit_authority_check_authorization_sync( | |||
authority, | |||
subject, | |||
"org.example.myapp.configure", | |||
nullptr, // details | |||
POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, | |||
nullptr, // cancellable | |||
&raw_error); | |||
error.reset(raw_error); | |||
if (!result) { | |||
std::cerr << "Authorization check failed: " | |||
<< (error ? error->message : "unknown") << "\n"; | |||
return 1; | |||
} | |||
GObjectPtr<PolkitAuthorizationResult> result_ptr(result); | |||
if (polkit_authorization_result_get_is_authorized(result)) { | |||
std::cout << "Authorized\n"; | |||
} | |||
else if (polkit_authorization_result_get_is_challenge(result)) { | |||
std::cout << "Authentication required\n"; | |||
} | |||
else { | |||
std::cout << "Not authorized\n"; | |||
} | |||
return 0; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
コンパイル時は、<code>pkg-config</code> を使用して、PolkitとGLibのフラグを取得する。<br> | |||
<br> | |||
g++ -std=c++17 polkit_check.cpp -o polkit_check $(pkg-config --cflags --libs polkit-gobject-1) | |||
<br> | |||
<u>※注意</u><br> | |||
<u><code>start_time</code> において、実際には <u>/proc/self/stat</u> ファイルから取得する必要がある。</u><br> | |||
<u><code>polkit_unix_process_new_for_owner()</code> に0を渡すと、Authority側でstart-timeの検証ができない場合がある。</u><br> | |||
<br> | |||
<u>認証エージェントを起動させる場合は、flagsに <code>POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION</code> を指定する。</u><br> | |||
<br> | |||
==== 非同期認可チェックの例 ==== | |||
以下の例では、<code>GMainLoop</code> と非同期APIを使用して認可チェックを行っている。<br> | |||
<br> | |||
タイムアウト処理も含めて、GUIや長時間実行されるアプリケーションで使用する場合に適している。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <unistd.h> | |||
#include <polkit/polkit.h> | |||
void check_authorization(PolkitAuthority *authority, GAsyncResult *res, gpointer user_data); | void check_authorization(PolkitAuthority *authority, GAsyncResult *res, gpointer user_data); | ||
gboolean do_cancel(GCancellable *cancellable); | gboolean do_cancel(GCancellable *cancellable); | ||
| 169行目: | 359行目: | ||
int main() | int main() | ||
{ | { | ||
const gchar *action_id = "org.example.policykit.do"; | const gchar *action_id = "org.example.policykit.do"; | ||
GMainLoop *loop = g_main_loop_new(nullptr, FALSE); | GMainLoop *loop = g_main_loop_new(nullptr, FALSE); | ||
PolkitAuthority *authority = polkit_authority_get_sync(nullptr, nullptr); | PolkitAuthority *authority = polkit_authority_get_sync(nullptr, nullptr); | ||
// | // 親プロセスのPIDを使用する例 | ||
auto parent_pid = getppid(); | auto parent_pid = getppid(); | ||
if (parent_pid == 1) { | if (parent_pid == 1) { | ||
| 190行目: | 371行目: | ||
} | } | ||
PolkitSubject *subject = polkit_unix_process_new_for_owner(parent_pid, 0, getuid()); | PolkitSubject *subject = polkit_unix_process_new_for_owner(parent_pid, 0, getuid()); | ||
GCancellable *cancellable = g_cancellable_new(); | GCancellable *cancellable = g_cancellable_new(); | ||
g_timeout_add(1000 * 10, (GSourceFunc)do_cancel, cancellable); | g_timeout_add(1000 * 10, (GSourceFunc)do_cancel, cancellable); | ||
polkit_authority_check_authorization( | |||
authority, | |||
subject, | |||
action_id, | |||
nullptr, | |||
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, | |||
cancellable, | |||
(GAsyncReadyCallback)check_authorization, | |||
loop); | |||
g_main_loop_run(loop); | g_main_loop_run(loop); | ||
g_object_unref(authority); | g_object_unref(authority); | ||
g_object_unref(subject); | g_object_unref(subject); | ||
| 219行目: | 396行目: | ||
} | } | ||
void check_authorization(PolkitAuthority *authority, GAsyncResult *res, gpointer user_data) | void check_authorization(PolkitAuthority *authority, GAsyncResult *res, gpointer user_data) | ||
{ | { | ||
GError *error = nullptr; | GError *error = nullptr; | ||
PolkitAuthorizationResult *result = polkit_authority_check_authorization_finish(authority, res, &error); | PolkitAuthorizationResult *result = polkit_authority_check_authorization_finish(authority, res, &error); | ||
if (error != nullptr) { | if (error != nullptr) { | ||
g_print("Error checking authorization: %s\n", error->message); | |||
g_print ("Error checking authorization: %s\n", error->message); | |||
g_error_free(error); | g_error_free(error); | ||
} | } | ||
| 232行目: | 408行目: | ||
const gchar *result_str; | const gchar *result_str; | ||
if (polkit_authorization_result_get_is_authorized(result)) { | if (polkit_authorization_result_get_is_authorized(result)) { | ||
result_str = "authorized"; | result_str = "authorized"; | ||
} | } | ||
else if (polkit_authorization_result_get_is_challenge(result)) { | else if (polkit_authorization_result_get_is_challenge(result)) { | ||
result_str = "challenge"; | result_str = "challenge"; | ||
} | } | ||
else { | else { | ||
result_str = "not authorized"; | result_str = "not authorized"; | ||
} | |||
g_print("Authorization result: %s\n", result_str); | |||
} | } | ||
if (result) g_object_unref(result); | |||
auto loop = static_cast<GMainLoop*>(user_data); | auto loop = static_cast<GMainLoop*>(user_data); | ||
g_main_loop_quit(loop); | g_main_loop_quit(loop); | ||
} | } | ||
gboolean do_cancel(GCancellable *cancellable) | gboolean do_cancel(GCancellable *cancellable) | ||
{ | { | ||
g_print("Timer has expired; cancelling authorization check\n"); | g_print("Timer has expired; cancelling authorization check\n"); | ||
g_cancellable_cancel(cancellable); | g_cancellable_cancel(cancellable); | ||
return FALSE; | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
非同期APIを使用する場合、<code>GCancellable</code> と <code>GMainLoop</code> の組み合わせにより、応答待ち中でも他のイベント処理を継続できる。<br> | |||
<br><br> | |||
== D-Busを直接使用する方法 == | |||
==== D-Busインターフェースの概要 ==== | |||
PolkitのAuthorityは、D-Busシステムバス上で以下に示すサービスとして公開されている。<br> | |||
<br> | |||
<center> | |||
{| class="wikitable" | |||
|+ Polkit AuthorityのD-Busインターフェース | |||
|- | |||
! 項目 !! 内容 | |||
|- | |||
| Well-known name || <u>org.freedesktop.PolicyKit1</u> | |||
|- | |||
| Object path || <u>/org/freedesktop/PolicyKit1/Authority</u> | |||
|- | |||
| Interface || <u>org.freedesktop.PolicyKit1.Authority</u> | |||
|- | |||
| Method || <u>CheckAuthorization</u> | |||
|- | |||
| Bus type || システムバス | |||
|} | |||
</center> | |||
<br> | |||
<code>CheckAuthorization()</code> の引数は以下の通りである。<br> | |||
<br> | |||
* subject | |||
*: D-Bus構造体 <code>(sv)</code> | |||
*: sに <code>"unix-process"</code>、vに <code>{"pid": uint32, "start-time": uint64}</code> の辞書を渡す。 | |||
*: <br> | |||
* action_id | |||
*: 確認対象のアクションID (string型) | |||
*: <br> | |||
* details | |||
*: 追加情報を表す <code>a{ss}</code> 形式の辞書 | |||
*: <br> | |||
* flags | |||
*: uint32型 | |||
*: <u>0</u> は問い合わせのみ | |||
*: <u>1</u> (<code>AllowUserInteraction</code>) は認証エージェントの起動を許可する。 | |||
*: <br> | |||
* cancellation_id | |||
*: キャンセル用の文字列 | |||
*: 空文字列を指定可能 | |||
<br> | |||
戻り値は <code>(bba{ss})</code> 形式の構造体であり、is_authorized (bool)、is_challenge (bool)、details (dict) の順に格納される。<br> | |||
<br> | |||
==== 認可チェックの実装例 (sd-busを使用) ==== | |||
以下の例では、libsystemdのsd-busを使用して、Authorityを直接呼び出している。<br> | |||
<br> | |||
以下の例では、エラーハンドリングは簡潔化しているため、実運用ではより詳細なチェックが必要である。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <iostream> | |||
#include <cstring> | |||
#include <systemd/sd-bus.h> | |||
#include <unistd.h> | |||
int main() | |||
{ | |||
sd_bus* bus = nullptr; | |||
int r = sd_bus_default_system(&bus); | |||
if (r < 0) { | |||
std::cerr << "Failed to connect to system bus: " | |||
<< strerror(-r) << "\n"; | |||
return 1; | |||
} | |||
sd_bus_error error = SD_BUS_ERROR_NULL; | |||
sd_bus_message* reply = nullptr; | |||
r = sd_bus_call_method( | |||
bus, | |||
"org.freedesktop.PolicyKit1", | |||
"/org/freedesktop/PolicyKit1/Authority", | |||
"org.freedesktop.PolicyKit1.Authority", | |||
"CheckAuthorization", | |||
&error, | |||
&reply, | |||
"(sv)sa{ss}us", | |||
"unix-process", | |||
"a{sv}", 2, | |||
"pid", "u", static_cast<uint32_t>(getpid()), | |||
"start-time", "t", static_cast<uint64_t>(0), | |||
"org.example.myapp.configure", | |||
0, // details: empty a{ss} | |||
0u, // flags | |||
""); // cancellation_id | |||
if (r < 0) { | |||
std::cerr << "CheckAuthorization failed: " | |||
<< error.message << "\n"; | |||
sd_bus_error_free(&error); | |||
sd_bus_unref(bus); | |||
return 1; | |||
} | |||
int authorized = 0; | |||
int challenge = 0; | |||
r = sd_bus_message_read(reply, "(bba{ss})", &authorized, &challenge, nullptr); | |||
if (r < 0) { | |||
std::cerr << "Failed to parse reply\n"; | |||
} | |||
else { | |||
std::cout << "Authorized: " << authorized | |||
<< ", Challenge: " << challenge << "\n"; | |||
} | |||
sd_bus_message_unref(reply); | |||
sd_bus_unref(bus); | |||
return | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | |||
コンパイル時は、<code>pkg-config</code> コマンドを使用してlibsystemdのフラグを取得する。<br> | |||
<br> | |||
g++ -std=c++17 polkit_dbus_check.cpp -o polkit_dbus_check $(pkg-config --cflags --libs libsystemd) | |||
<br> | |||
D-Bus直接呼び出しでは、Subject構造体の作成や戻り値のパースを正確に行う必要がある。<br> | |||
<br> | |||
start-timeに0を渡す場合、Polkitはプロセスのstart-timeを検証できないため、本番環境では <u>/proc/self/stat</u> ファイルから取得すること。<br> | |||
<br><br> | <br><br> | ||
== トラブルシューティング == | |||
==== 認可が常に拒否される ==== | |||
* 原因 | |||
** Action IDが <u>/usr/share/polkit-1/actions</u> ディレクトリ内の <code>.policy</code> ファイルで定義されていない。 | |||
** Ruleファイルで明示的に拒否 (return <code>polkit.Result.NO</code>) されている。 | |||
** Subjectの <code>pid</code> や <code>start-time</code> が正しくない。 | |||
*: <br> | |||
* 対策 | |||
** <code>pkaction --verbose</code> でAction IDが登録されているか確認する。 | |||
** Ruleファイルの優先順位 (ファイル名の数字) を確認する。 | |||
** Subjectのstart-timeを <u>/proc/self/stat</u> から正しく取得する。 | |||
<br> | |||
==== 認証ダイアログが表示されない ==== | |||
* 原因 | |||
** <code>CheckAuthorization</code> のflagsに <code>AllowUserInteraction</code> (1) が指定されていない。 | |||
** セッションに認証エージェントが登録されていない。 | |||
** SSHやSystemdサービス等の非対話的環境で実行されている。 | |||
*: <br> | |||
* 対策 | |||
** flagsに <code>POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION</code> または <code>1u</code> を指定する。 | |||
** グラフィカルセッションやPolkitエージェントが動作している環境でテストする。 | |||
** 非対話的環境では、Ruleファイルで事前に許可するか、別途認証方法を用意する。 | |||
<br> | |||
==== コンパイル時にGObjectのリンクエラーが発生する ==== | |||
* 原因 | |||
** <code>pkg-config --libs polkit-gobject-1</code> の出力に含まれるGLib/GObjectのライブラリがリンクされていない。 | |||
** ヘッダファイルのincludeパスが通っていない。 | |||
*: <br> | |||
* 対策 | |||
** 必ず <code>$(pkg-config --cflags --libs polkit-gobject-1)</code> をコンパイルコマンドに含める。 | |||
** 必要に応じて <code>glib-2.0</code> のpkg-configも追加する。 | |||
** CMakeを使用する場合は <code>pkg_check_modules(POLKIT polkit-gobject-1)</code> を使用する。 | |||
<br> | |||
==== D-Bus呼び出しで "Permission denied" となる ==== | |||
* 原因 | |||
** システムバスへの接続権限がない。 | |||
** 必要なD-Busポリシーが設定されていない。 | |||
*: <br> | |||
* 対策 | |||
** プログラムが通常ユーザ権限で実行されていることを確認する。 | |||
** D-Bus設定ファイル (<u>/usr/share/dbus-1/system.d/</u> ディレクトリ等) でサービスへのアクセス権限を確認する。 | |||
** Polkit自身はroot権限で動作する必要があるが、呼び出し側は通常ユーザでよい。 | |||
<br><br> | |||
== 関連情報 == | |||
* [https://www.freedesktop.org/software/polkit/docs/latest/ Polkit公式ドキュメント] | |||
* [https://gitlab.freedesktop.org/polkit/polkit Polkit GitLab] | |||
* [https://www.freedesktop.org/wiki/Software/polkit/ Polkit Wiki] | |||
* [https://www.freedesktop.org/software/systemd/man/latest/sd-bus.html sd-bus documentation] | |||
* [[Qtの基礎 - 管理者権限]] | |||
<br><br> | |||
{{#seo: | |||
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki | |||
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Polkit,PolicyKit,Authorization,D-Bus,電気回路,電子回路,基板,プリント基板 | |||
|description={{PAGENAME}} - C++からPolkit (PolicyKit) の認可を確認する方法。libpolkit-gobject-1のラップとsd-busによるD-Bus直接呼び出し、非同期認可チェック、CMake設定、ビルド、トラブルシューティングを紹介する | This page is {{PAGENAME}} in our wiki about checking Polkit authorization from pure C++ without Qt using libpolkit-gobject-1 and sd-bus | |||
|image=/resources/assets/MochiuLogo_Single_Blue.png | |||
}} | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:C++]] | [[カテゴリ:C++]] | ||
2026年6月13日 (土) 15:43時点における最新版
概要
Polkit (formerly PolicyKit) は、Linuxシステムで特権的な操作を行う際の認可 (authorization) を決定するフレームワークである。
sudoのようにプロセス全体をrootで実行するのではなく、細かいアクション単位で許可を判定できる。
Polkitは2008年頃にDavid Zeuthenによって開発され、D-Busベースの認可フレームワークとして成熟した。
中核となるAuthorityは org.freedesktop.PolicyKit1 というD-Busサービスとして動作し、アプリケーションからSubjectとAction IDを受け取って判定を行う。
Action IDは /usr/share/polkit-1/actions/ ディレクトリの .policy ファイル (XML形式) で定義され、認可ルールは /etc/polkit-1/rules.d/ 等のJavaScriptファイルで記述される。
これにより、管理者はアプリケーションやユーザ/groupごとに詳細なポリシーを設定できる。
C++からPolkitを利用するには、libpolkit-gobject-1 をラップする方法と、D-Busインターフェースを直接呼び出す方法がある。
C++と外部ライブラリのみで開発する場合は、GLib / GObjectのリソース管理やD-Busメッセージのパースを適切に行う必要がある。
Polkitを適切に使用することで、GUIやCLIアプリケーションから最小限の特権で安全にシステム操作を実行できる。
例えば、システム設定の変更やデバイスのマウント等、root権限が必要な操作をユーザに確認しながら行うことが可能である。
Polkitの基本
主要な構成要素
Polkitは、下表に示す要素で構成される。
| 要素 | 説明 |
|---|---|
| Authority | D-Bus上の org.freedesktop.PolicyKit1 サービス認可判定の最終的な権限を持つ。 |
| Subject | 認可を求めるエンティティ 通常は ("unix-process", {"pid": uint32, "start-time": uint64}) 形式のD-Bus構造体
|
| Action | .policy XMLファイルで定義された操作Action ID (例: org.freedesktop.policykit.exec) で識別される。
|
| Authentication Agent | ユーザにパスワード等を要求するUIプログラム GUIやTUIで動作する。 |
| Rule files | JavaScriptで記述された .rules ファイル管理者がポリシーをカスタマイズするために使用する。 |
Subjectには、プロセス形式の unix-process の他、セッション形式の unix-session も存在する。
C++アプリケーションから自プロセスの認可を確認する場合、unix-process Subjectを使用するのが一般的である。
Authentication Agentの実装については、PolkitAgentListener のサブクラス化が必要であるが、本ページではアプリケーション側の認可チェックを中心に解説する。
アクションとルールファイル
Actionは、/usr/share/polkit-1/actions 内の .policy ファイルで定義される。
各アクションには、以下の属性が含まれる。
- action id
- 一意の識別子
- 例: org.example.myapp.configure
- description / message
- ユーザに表示される説明文
- defaults
- 各認可結果 (allow_any, allow_inactive, allow_active) に対するデフォルトポリシー
yes、no、auth_self、auth_admin等が指定できる。
- vendor / vendor_url
- アクションの提供者情報
Ruleファイルは、/etc/polkit-1/rules.d/ と /usr/share/polkit-1/rules.d/ に配置される。
ファイル名は <数字>-name.rules の形式で、数字の小さいものから順に評価される。
JavaScript形式のRuleファイルの例を以下に示す。
polkit.addRule(function(action, subject) {
if (action.id == "org.example.myapp.configure" && subject.isInGroup("wheel")) {
return polkit.Result.YES;
}
});
この例では、wheel グループに属するユーザが org.example.myapp.configure アクションを実行できるようにしている。
Ruleファイルを編集した後は、polkit デーモンが自動的に再読み込みを行うが、即座に反映させたい場合は sudo systemctl restart polkit コマンドを実行する。
C++からPolkitを利用する方法の比較
C++からPolkitを利用する主な方法を比較する。
| 方法 | 依存関係 | 実装難易度 | 用途 |
|---|---|---|---|
| libpolkit-gobject-1 | GLib / GObject、D-Busシステムバス | 低 | 一般的なアプリケーションの認可チェック |
| D-Bus直接呼び出し (libdbus等) | D-Busライブラリ、システムバス | 中 | 軽量実装や特殊なD-Bus制御が必要な場合 |
| sd-bus (systemd) | libsystemd | 中 | systemdベースの環境で直接使用 |
libpolkit-gobject-1は、GObjectのリファレンスカウントとGErrorを適切に扱う必要がある。
D-Bus直接呼び出しは、Subject構造体や戻り値のパースを自前で行う必要がある。
Polkitのインストール
パッケージ管理システムからインストール
多くのLinuxディストリビューションには、初期状態でPolkitがインストールされている。
C++から開発する場合は、追加で開発用パッケージが必要となる。
# RHEL sudo dnf install polkit-devel glib2-devel pkg-config # SUSE sudo zypper install polkit-devel glib2-devel pkg-config
ソースコードからインストール
まず、Polkitのビルドに必要な依存関係のライブラリをインストールする。
# RHEL
sudo dnf install meson dbus-devel expat-devel systemd-devel glib2-devel \
gtk3-devel gobject-introspection-devel pam-devel \
duktape-devel gtk-doc mozjs115-devel
# SUSE
sudo zypper install meson dbus-1-devel libexpat-devel systemd-devel \
glib2-devel gtk3-devel gobject-introspection-devel \
mozjs78-devel pam-devel duktape-devel gtk-doc
Polkit公式Webサイト または GitLabからソースコードをダウンロードする。
ダウンロードしたファイルを解凍する。
tar xf polkit-<バージョン>.tar.gz cd polkit-<バージョン>
または、git clone コマンドを実行して、ソースコードをダウンロードする。
git clone https://gitlab.freedesktop.org/polkit/polkit.git cd polkit
Polkitのビルドおよびインストールを行う。
指定可能なオプションは、meson configure コマンドで確認できる。
# RHEL
meson setup build \
--prefix=<Polkitのインストールディレクトリ> \
-Dos_type=redhat \
-Dexamples=true \
-Dman=true \
-Dgtk_doc=true
# SUSE
meson setup build \
--prefix=<Polkitのインストールディレクトリ> \
-Dos_type=suse \
-Dexamples=true \
-Dman=true \
-Dgtk_doc=true
meson compile -C build
meson install -C build
CMakeファイルの設定
Pkg-Configを使用する場合
以下の例は、pkg_check_modules コマンドを使用して依存関係を解決する例である。
# CMakeLists.txtファイル
find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB2 REQUIRED glib-2.0)
pkg_check_modules(POLKITGOBJECT2 REQUIRED polkit-gobject-1)
pkg_check_modules(GOBJECT2 REQUIRED gobject-2.0)
pkg_check_modules(GIO2 REQUIRED gio-2.0)
add_executable(<プロジェクト名>
# ...略
)
include_directories(
${GLIB2_INCLUDE_DIRS}
${POLKITGOBJECT2_INCLUDE_DIRS}
${GOBJECT2_INCLUDE_DIRS}
${GIO2_INCLUDE_DIRS}
)
link_directories(
${GLIB2_LIBRARY_DIRS}
${POLKITGOBJECT2_LIBRARY_DIRS}
${GOBJECT2_LIBRARY_DIRS}
${GIO2_LIBRARY_DIRS}
)
add_definitions(
${GLIB2_CFLAGS_OTHER}
${POLKITGOBJECT2_CFLAGS_OTHER}
${GOBJECT2_CFLAGS_OTHER}
${GIO2_CFLAGS_OTHER}
)
target_link_libraries(<プロジェクト名>
${GLIB2_LIBRARIES}
${POLKITGOBJECT2_LIBRARIES}
${GOBJECT2_LIBRARIES}
${GIO2_LIBRARIES}
)
Pkg-Configを使用しない場合
以下の例は、Pkg-Configを使用せずに直接パスを指定する例である。
# CMakeLists.txtファイル
add_executable(<プロジェクト名>
# ...略
)
## インクルードファイル
include_directories(
# ...略
/usr/lib64/glib-2.0/include
/usr/include/glib-2.0
/usr/include/polkit-1
# ...略
)
## ライブラリファイル
target_link_libraries(<プロジェクト名>
# ...略
glib-2.0
polkit-gobject-1
gobject-2.0
gio-2.0
# ...略
)
libpolkit-gobject-1を使用する方法
libpolkit-gobject-1とは
libpolkit-gobject-1は、Polkitの機能をGLib / GObjectベースのC APIで提供するライブラリである。
主な関数として、Authorityを取得する polkit_authority_get_sync() と、認可を確認する polkit_authority_check_authorization_sync() が存在する。
また、Subjectを作成する polkit_unix_process_new_for_owner() 等の関数も提供する。
C++から使用する場合、g_object_unref() によるリソース解放や g_error_free() によるGErrorの解放を徹底する。
また、RAIIラッパーを用意することで、GObjectのリファレンスカウントを安全に管理できる。
開発パッケージのインストール
# RHEL sudo dnf install pkg-config polkit-devel glib2-devel # SUSE sudo zypper install pkg-config polkit-devel glib2-devel
認可チェックの実装例
以下の例では、libpolkit-gobject-1を使用して、Action ID org.example.myapp.configure の認可を確認している。
#include <iostream>
#include <memory>
#include <unistd.h>
#include <polkit/polkit.h>
// GErrorをRAIIで管理するヘルパー
struct GErrorDeleter {
void operator()(GError* e) const { if (e) g_error_free(e); }
};
using GErrorPtr = std::unique_ptr<GError, GErrorDeleter>;
// GObjectをRAIIで管理するヘルパー
struct GObjectDeleter {
void operator()(gpointer o) const { if (o) g_object_unref(o); }
};
template<typename T>
using GObjectPtr = std::unique_ptr<T, GObjectDeleter>;
int main()
{
GError* raw_error = nullptr;
PolkitAuthority* authority = polkit_authority_get_sync(nullptr, &raw_error);
GErrorPtr error(raw_error);
if (!authority) {
std::cerr << "Failed to get polkit authority: "
<< (error ? error->message : "unknown") << "\n";
return 1;
}
GObjectPtr<PolkitAuthority> auth_ptr(authority);
// Subjectを作成 (自プロセス)
pid_t pid = getpid();
guint64 start_time = 0; // 実際には /proc/self/stat から取得する
PolkitSubject* subject = polkit_unix_process_new_for_owner(pid, start_time, getuid());
if (!subject) {
std::cerr << "Failed to create subject\n";
return 1;
}
GObjectPtr<PolkitSubject> subject_ptr(subject);
// 認可チェック
PolkitAuthorizationResult* result = polkit_authority_check_authorization_sync(
authority,
subject,
"org.example.myapp.configure",
nullptr, // details
POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
nullptr, // cancellable
&raw_error);
error.reset(raw_error);
if (!result) {
std::cerr << "Authorization check failed: "
<< (error ? error->message : "unknown") << "\n";
return 1;
}
GObjectPtr<PolkitAuthorizationResult> result_ptr(result);
if (polkit_authorization_result_get_is_authorized(result)) {
std::cout << "Authorized\n";
}
else if (polkit_authorization_result_get_is_challenge(result)) {
std::cout << "Authentication required\n";
}
else {
std::cout << "Not authorized\n";
}
return 0;
}
コンパイル時は、pkg-config を使用して、PolkitとGLibのフラグを取得する。
g++ -std=c++17 polkit_check.cpp -o polkit_check $(pkg-config --cflags --libs polkit-gobject-1)
※注意
start_time において、実際には /proc/self/stat ファイルから取得する必要がある。
polkit_unix_process_new_for_owner() に0を渡すと、Authority側でstart-timeの検証ができない場合がある。
認証エージェントを起動させる場合は、flagsに POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION を指定する。
非同期認可チェックの例
以下の例では、GMainLoop と非同期APIを使用して認可チェックを行っている。
タイムアウト処理も含めて、GUIや長時間実行されるアプリケーションで使用する場合に適している。
#include <unistd.h>
#include <polkit/polkit.h>
void check_authorization(PolkitAuthority *authority, GAsyncResult *res, gpointer user_data);
gboolean do_cancel(GCancellable *cancellable);
int main()
{
const gchar *action_id = "org.example.policykit.do";
GMainLoop *loop = g_main_loop_new(nullptr, FALSE);
PolkitAuthority *authority = polkit_authority_get_sync(nullptr, nullptr);
// 親プロセスのPIDを使用する例
auto parent_pid = getppid();
if (parent_pid == 1) {
g_printerr("Parent process was reaped by init(1)\n");
return -1;
}
PolkitSubject *subject = polkit_unix_process_new_for_owner(parent_pid, 0, getuid());
GCancellable *cancellable = g_cancellable_new();
g_timeout_add(1000 * 10, (GSourceFunc)do_cancel, cancellable);
polkit_authority_check_authorization(
authority,
subject,
action_id,
nullptr,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
cancellable,
(GAsyncReadyCallback)check_authorization,
loop);
g_main_loop_run(loop);
g_object_unref(authority);
g_object_unref(subject);
g_object_unref(cancellable);
g_main_loop_unref(loop);
return 0;
}
void check_authorization(PolkitAuthority *authority, GAsyncResult *res, gpointer user_data)
{
GError *error = nullptr;
PolkitAuthorizationResult *result = polkit_authority_check_authorization_finish(authority, res, &error);
if (error != nullptr) {
g_print("Error checking authorization: %s\n", error->message);
g_error_free(error);
}
else {
const gchar *result_str;
if (polkit_authorization_result_get_is_authorized(result)) {
result_str = "authorized";
}
else if (polkit_authorization_result_get_is_challenge(result)) {
result_str = "challenge";
}
else {
result_str = "not authorized";
}
g_print("Authorization result: %s\n", result_str);
}
if (result) g_object_unref(result);
auto loop = static_cast<GMainLoop*>(user_data);
g_main_loop_quit(loop);
}
gboolean do_cancel(GCancellable *cancellable)
{
g_print("Timer has expired; cancelling authorization check\n");
g_cancellable_cancel(cancellable);
return FALSE;
}
非同期APIを使用する場合、GCancellable と GMainLoop の組み合わせにより、応答待ち中でも他のイベント処理を継続できる。
D-Busを直接使用する方法
D-Busインターフェースの概要
PolkitのAuthorityは、D-Busシステムバス上で以下に示すサービスとして公開されている。
| 項目 | 内容 |
|---|---|
| Well-known name | org.freedesktop.PolicyKit1 |
| Object path | /org/freedesktop/PolicyKit1/Authority |
| Interface | org.freedesktop.PolicyKit1.Authority |
| Method | CheckAuthorization |
| Bus type | システムバス |
CheckAuthorization() の引数は以下の通りである。
- subject
- D-Bus構造体
(sv) - sに
"unix-process"、vに{"pid": uint32, "start-time": uint64}の辞書を渡す。
- D-Bus構造体
- action_id
- 確認対象のアクションID (string型)
- details
- 追加情報を表す
a{ss}形式の辞書
- 追加情報を表す
- flags
- uint32型
- 0 は問い合わせのみ
- 1 (
AllowUserInteraction) は認証エージェントの起動を許可する。
- cancellation_id
- キャンセル用の文字列
- 空文字列を指定可能
戻り値は (bba{ss}) 形式の構造体であり、is_authorized (bool)、is_challenge (bool)、details (dict) の順に格納される。
認可チェックの実装例 (sd-busを使用)
以下の例では、libsystemdのsd-busを使用して、Authorityを直接呼び出している。
以下の例では、エラーハンドリングは簡潔化しているため、実運用ではより詳細なチェックが必要である。
#include <iostream>
#include <cstring>
#include <systemd/sd-bus.h>
#include <unistd.h>
int main()
{
sd_bus* bus = nullptr;
int r = sd_bus_default_system(&bus);
if (r < 0) {
std::cerr << "Failed to connect to system bus: "
<< strerror(-r) << "\n";
return 1;
}
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message* reply = nullptr;
r = sd_bus_call_method(
bus,
"org.freedesktop.PolicyKit1",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.PolicyKit1.Authority",
"CheckAuthorization",
&error,
&reply,
"(sv)sa{ss}us",
"unix-process",
"a{sv}", 2,
"pid", "u", static_cast<uint32_t>(getpid()),
"start-time", "t", static_cast<uint64_t>(0),
"org.example.myapp.configure",
0, // details: empty a{ss}
0u, // flags
""); // cancellation_id
if (r < 0) {
std::cerr << "CheckAuthorization failed: "
<< error.message << "\n";
sd_bus_error_free(&error);
sd_bus_unref(bus);
return 1;
}
int authorized = 0;
int challenge = 0;
r = sd_bus_message_read(reply, "(bba{ss})", &authorized, &challenge, nullptr);
if (r < 0) {
std::cerr << "Failed to parse reply\n";
}
else {
std::cout << "Authorized: " << authorized
<< ", Challenge: " << challenge << "\n";
}
sd_bus_message_unref(reply);
sd_bus_unref(bus);
return 0;
}
コンパイル時は、pkg-config コマンドを使用してlibsystemdのフラグを取得する。
g++ -std=c++17 polkit_dbus_check.cpp -o polkit_dbus_check $(pkg-config --cflags --libs libsystemd)
D-Bus直接呼び出しでは、Subject構造体の作成や戻り値のパースを正確に行う必要がある。
start-timeに0を渡す場合、Polkitはプロセスのstart-timeを検証できないため、本番環境では /proc/self/stat ファイルから取得すること。
トラブルシューティング
認可が常に拒否される
- 原因
- Action IDが /usr/share/polkit-1/actions ディレクトリ内の
.policyファイルで定義されていない。 - Ruleファイルで明示的に拒否 (return
polkit.Result.NO) されている。 - Subjectの
pidやstart-timeが正しくない。
- Action IDが /usr/share/polkit-1/actions ディレクトリ内の
- 対策
pkaction --verboseでAction IDが登録されているか確認する。- Ruleファイルの優先順位 (ファイル名の数字) を確認する。
- Subjectのstart-timeを /proc/self/stat から正しく取得する。
認証ダイアログが表示されない
- 原因
CheckAuthorizationのflagsにAllowUserInteraction(1) が指定されていない。- セッションに認証エージェントが登録されていない。
- SSHやSystemdサービス等の非対話的環境で実行されている。
- 対策
- flagsに
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTIONまたは1uを指定する。 - グラフィカルセッションやPolkitエージェントが動作している環境でテストする。
- 非対話的環境では、Ruleファイルで事前に許可するか、別途認証方法を用意する。
- flagsに
コンパイル時にGObjectのリンクエラーが発生する
- 原因
pkg-config --libs polkit-gobject-1の出力に含まれるGLib/GObjectのライブラリがリンクされていない。- ヘッダファイルのincludeパスが通っていない。
- 対策
- 必ず
$(pkg-config --cflags --libs polkit-gobject-1)をコンパイルコマンドに含める。 - 必要に応じて
glib-2.0のpkg-configも追加する。 - CMakeを使用する場合は
pkg_check_modules(POLKIT polkit-gobject-1)を使用する。
- 必ず
D-Bus呼び出しで "Permission denied" となる
- 原因
- システムバスへの接続権限がない。
- 必要なD-Busポリシーが設定されていない。
- 対策
- プログラムが通常ユーザ権限で実行されていることを確認する。
- D-Bus設定ファイル (/usr/share/dbus-1/system.d/ ディレクトリ等) でサービスへのアクセス権限を確認する。
- Polkit自身はroot権限で動作する必要があるが、呼び出し側は通常ユーザでよい。
関連情報