編集の要約なし |
|||
| (同じ利用者による、間の2版が非表示) | |||
| 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> | 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> | <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) | ||
| 97行目: | 176行目: | ||
pkg_check_modules(GIO2 REQUIRED gio-2.0) | pkg_check_modules(GIO2 REQUIRED gio-2.0) | ||
add_executable(<プロジェクト名> | add_executable(<プロジェクト名> | ||
# ...略 | # ...略 | ||
) | ) | ||
| 127行目: | 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); | ||
| 171行目: | 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) { | ||
| 192行目: | 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); | ||
| 221行目: | 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); | ||
} | } | ||
| 234行目: | 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++]] | ||