「C++の応用 - PolKit」の版間の差分

提供: MochiuWiki : SUSE, EC, PCB

編集の要約なし
 
(同じ利用者による、間の4版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
PolKit(以前は、PolicyKitとして知られていた)は、非特権ユーザセッションと特権システムコンテキストの間のネゴシエータとして機能するアプリケーションフレームワークである。<br>
Polkit (formerly PolicyKit) は、Linuxシステムで特権的な操作を行う際の認可 (authorization) を決定するフレームワークである。<br>
sudoのようにプロセス全体をrootで実行するのではなく、細かいアクション単位で許可を判定できる。<br>
<br>
<br>
PolKitは、ユーザ、グループ、名前により特定のアクションを制限する機能を持つ。<br>
Polkitは2008年頃にDavid Zeuthenによって開発され、D-Busベースの認可フレームワークとして成熟した。<br>
ユーザセッションのプロセスが、システムコンテキストでアクションを実行するごとに、PolKitはクエリされる。
中核となるAuthorityは <code>org.freedesktop.PolicyKit1</code> というD-Busサービスとして動作し、アプリケーションからSubjectとAction IDを受け取って判定を行う。<br>
所謂、ポリシーで指定された設定に基づいて、"yes"、"no"、"needs authentication"である可能性がある。<br>
<br>
<br>
<code>sudo</code>のような古典的な権限承認プログラムとは異なり、PolKitはセッション全体に対してroot権限を与えるのではなく、各アクションに対してのみ権限を与える。<br>
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のインストール ==
== 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>
==== ソースコードからインストール ====
==== ソースコードからインストール ====
多くのLinuxディストリビューションには、初期状態でPolKitがインストールされている。<br>
まず、Polkitのビルドに必要な依存関係のライブラリをインストールする。<br>
もし、別途インストールする必要がある場合、ソースコードからPolKitをインストールする。<br>
<br>
<br>
PolKitのビルドに必要なライブラリをインストールする。<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-dpc
  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/ PolKitの公式Webサイト]または[https://gitlab.freedesktop.org/polkit/polkit/-/tags GitLab]にアクセスして、ソースコードをダウンロードする。<br>
[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>コマンドを実行して、PolKitのソースコードをダウンロードする。<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>
Polkitのビルドおよびインストールを行う。<br>
設定可能なオプションの一覧は、<code>meson configure</code>コマンドで取得することができる。<br>
指定可能なオプションは、<code>meson configure</code> コマンドで確認できる。<br>
<br>
  # RHEL
  # RHEL
  meson setup build --prefix=<PolKitのインストールディレクトリ> -Dos_type=redhat -Dexamples=true -Dman=true -Dgtk_doc=true
  meson setup build \
      --prefix=<Polkitのインストールディレクトリ> \
      -Dos_type=redhat \
      -Dexamples=true \
      -Dman=true       \
      -Dgtk_doc=true
   
   
  # SUSE
  # SUSE
  meson setup build --prefix=<PolKitのインストールディレクトリ> -Dos_type=suse -Dexamples=true -Dman=true -Dgtk_doc=true
  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>
<br>
 
== CMakeファイルの設定 ==
== CMakeファイルの設定 ==
==== Pkg-Configを使用しない場合 ====
==== Pkg-Configを使用する場合 ====
<syntaxhighlight lang="cmake">
以下の例は、<code>pkg_check_modules</code> コマンドを使用して依存関係を解決する例である。<br>
# CMakeLists.txtファイル
# 実行ファイルの場合
add_executable(<プロジェクト名>
  # ...略
)
# ライブラリの場合
add_library(<プロジェクト名>
    # ...略
)
## インクルードファイル
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
    # ...略
)
</syntaxhighlight>
<br>
<br>
==== Pkg-Configを使用する場合 ====
  <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(<プロジェクト名>
  # ...略
)
# ライブラリの場合
add_library(<プロジェクト名>
     # ...略
     # ...略
  )
  )
124行目: 199行目:
     ${GOBJECT2_CFLAGS_OTHER}
     ${GOBJECT2_CFLAGS_OTHER}
     ${GIO2_CFLAGS_OTHER}
     ${GIO2_CFLAGS_OTHER}
)
target_link_libraries(<プロジェクト名>
    ${GLIB2_LIBRARIES}
    ${POLKITGOBJECT2_LIBRARIES}
    ${GOBJECT2_LIBRARIES}
    ${GIO2_LIBRARIES}
  )
  )
  </syntaxhighlight>
  </syntaxhighlight>
<br><br>
<br>
 
==== Pkg-Configを使用しない場合 ====
== QMakeファイルの設定 ==
以下の例は、Pkg-Configを使用せずに直接パスを指定する例である。<br>
  <syntaxhighlight lang="make">
<br>
  # Pkg-Configを使用する場合
  <syntaxhighlight lang="cmake">
CONFIG += link_pkgconfig
  # CMakeLists.txtファイル
PKGCONFIG += \
    polkit-gobject-1
   
   
  LIBS += \
  add_executable(<プロジェクト名>
     -lglib-2.0 \
     # ...
    -lpolkit-gobject-1      \
)
    -lgobject-2.0          \
    -lgio-2.0
   
   
  # Pkg-Configを使用しない場合
  ## インクルードファイル
  LIBS += \
  include_directories(
     -lglib-2.0 \
    # ...略
    -lpolkit-gobject-1      \
     /usr/lib64/glib-2.0/include
     -lgobject-2.0           \
     /usr/include/glib-2.0
     -lgio-2.0
     /usr/include/polkit-1
    # ...
)
   
   
  INCLUDEPATH += \
  ## ライブラリファイル
     /usr/lib64/glib-2.0/include \
target_link_libraries(<プロジェクト名>
     /usr/include/glib-2.0 \
    # ...略
     /usr/include/polkit-1
     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);
164行目: 359行目:
  int main()
  int main()
  {
  {
    // Action ID for PolKit
     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);
   
   
    // 権威への参照を同期的に取得 (応答を受け取るまで呼び出し元のスレッドはブロックされる)
    // 非同期にする場合は、polkit_authority_get_async関数を使用する
     PolkitAuthority *authority = polkit_authority_get_sync(nullptr, nullptr);
     PolkitAuthority *authority = polkit_authority_get_sync(nullptr, nullptr);
   
   
     // 多くのクライアントはD-Busを介してメカニズムと通信するため、PolkitSystemBusNameを使用する
     // 親プロセスのPIDを使用する例
    // しかし、この例では、呼び出しプロセスのプロセスIDを使用する
    // 親プロセスが終了した場合、init(1)が認可されているかどうかをチェックしないように注意する (常に認可されている)
    // 親プロセスのPIDを取得
     auto parent_pid = getppid();
     auto parent_pid = getppid();
     if (parent_pid == 1) {
     if (parent_pid == 1) {
185行目: 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());
   
   
    // PolKit認証画面でタイムアウトを使用する場合 (以下の例では、10[sec])
     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(
    polkit_authority_check_authorization(authority, subject, action_id, nullptr,
      authority,
                                        POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
      subject,
                                        nullptr, (GAsyncReadyCallback)check_authorization, loop);
      action_id,
      nullptr,
    // 認証画面の表示 (タイムアウト設定あり)
      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
    // polkit_authority_check_authorization(authority, subject, action_id, nullptr,
      cancellable,
    //                                      POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
      (GAsyncReadyCallback)check_authorization,
    //                                      cancellable, (GAsyncReadyCallback)check_authorization, loop);
      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);
214行目: 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);
     }
     }
227行目: 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);
      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 FALSE;
     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は、下表に示す要素で構成される。

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) に対するデフォルトポリシー
    yesnoauth_selfauth_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を利用する主な方法を比較する。

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を使用する場合、GCancellableGMainLoop の組み合わせにより、応答待ち中でも他のイベント処理を継続できる。


D-Busを直接使用する方法

D-Busインターフェースの概要

PolkitのAuthorityは、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} の辞書を渡す。

  • 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の pidstart-time が正しくない。

  • 対策
    • 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ファイルで事前に許可するか、別途認証方法を用意する。


コンパイル時に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権限で動作する必要があるが、呼び出し側は通常ユーザでよい。



関連情報