【ITニュース解説】My Google Summer of Code Journey: Part 3
2025年09月21日に「Dev.to」が公開したITニュース「My Google Summer of Code Journey: Part 3」について初心者にもわかりやすく解説しています。
ITニュース概要
Google Summer of CodeでFreeBSDのセキュリティモジュールmac_do(4)を改善。特定のプログラムだけが権限を変更できたのを、jailごとに許可する実行ファイルを柔軟に設定可能にした。従来のsetuid等システムコールにも対応させ、細かなユーザー権限制御ができるmdo(1)ツールも強化し、システムのセキュリティと柔軟性を高めた。
ITニュース解説
この解説は、FreeBSDという高性能で安全なオペレーティングシステムにおいて、プロセスの権限管理をより柔軟かつセキュアにするための二つの主要なプロジェクト、すなわちカーネルセキュリティモジュールであるmac_do(4)の改良と、その補助ツールであるmdo(1)の改良について述べる。システムエンジニアにとって、システムがどのように動作し、どのようにセキュリティが保たれているかを理解することは非常に重要である。特に、プログラムが持つ権限を安全に変更することは、システムの安定性とセキュリティに直結する課題である。
まず、mac_do(4)の改善点から説明する。mac_do(4)は、特定のプログラムがユーザーIDやグループIDといった資格情報を安全に変更できるようにするカーネルモジュールである。これは、よく知られたsudoやdoasのようなプログラムが抱えるセキュリティ上の課題を根本的に解決することを目指して開発されたものである。このプロジェクトには二つの大きな目標があった。
一つ目の目標は、mac_do(4)が権限移行を許可するプログラムのリストを、FreeBSDの仮想環境である「jail」ごとに設定できるようにすることだった。以前のmac_do(4)は、/usr/bin/mdoという特定のプログラムしか権限移行を許可しない硬直的な仕組みだった。これでは、他のアプリケーションがこの安全な権限移行機能を利用できず、柔軟性に欠けていた。この問題を解決するため、まずシステムの設定を保持するデータ構造を改良した。元々、権限移行ルールを管理するstruct rulesという構造体があったが、これに加えて許可される実行ファイルのパスリストも格納できるstruct confという新しい構造体を作成し、両方を一元的に管理するようにした。
この変更に伴い、カーネル内のメモリ管理も大きな課題となった。設定データが安全に確保され、不要になった際に適切に解放されるよう、参照カウントという仕組みをstruct confのレベルで統一して管理する必要があった。また、jailが親のjailから設定を継承する際には、単にポインタをコピーする「シャローコピー」では問題が生じる。ポインタが指し示す実データ自体も新しく複製する「ディープコピー」を行うことで、各jailが独立した設定を持てるようにした。これは、一つの設定変更が他のjailに意図せず影響を与えることを防ぐために不可欠な処理である。さらに、カーネルは複数の処理が同時に動作する環境であるため、データの整合性を保ちながらデッドロック(複数の処理がお互いのリソースを待ち続けて停止する状態)を防ぐため、「prison locks」という排他制御の仕組みを慎重に適用した。システム管理者やjailの作成者がこれらの設定を柔軟に変更できるように、sysctlというFreeBSDのシステムパラメータ設定インターフェースを拡張し、新しい実行パスリストを設定できるようにした。最終的に、プログラムが権限移行を要求した際に、その実行パスが設定リストに含まれているかを動的にチェックするcheck_proc()関数も改良され、これによりmac_do(4)は仮想環境ごとに異なるプログラムに権限移行を許可できる、非常に柔軟なシステムへと進化した。
二つ目の目標は、mac_do(4)がsetuid(2)やsetgid(2)といった、より一般的なPOSIXシステムコールからの権限変更要求も承認できるようにすることだった。これまではsetcred(2)という特定のシステムコールからのみ権限移行を扱っていたが、多くのアプリケーションは伝統的なシステムコールを利用している。そこで、これら広く使われているシステムコールからの権限変更要求も傍受し、承認できるようにmac_do(4)を拡張した。具体的には、setuid(2)であれば目標とするUID、setgroups(2)であれば新しいグループの配列といった、各システムコールが扱う特定のデータを安全にやり取りするための専用データ構造を作成した。これらのデータ構造は共通のヘッダを持ち、プログラムの実行スレッドごとに管理された。権限付与のロジックを担うmac_do_priv_grant()関数も拡張され、PRIV_CRED_SETUIDなどの新しい権限タイプに対応できるようになった。さらに、各システムコールに対して、「enter」「check」「exit」という3つのセキュリティフックを実装した。enterフックは権限移行が開始される際にスレッド固有のデータを準備し、checkフックはシステムコールが要求する具体的な値を記録して権限チェックの準備を整え、exitフックは処理完了後にデータをクリーンアップする役割を果たす。setreuid(2)のように、複数のユーザーID引数を取り、-1が「変更なし」を意味する複雑なシステムコールに対しても、適切なロジックを実装し、実効的なターゲットを正確に判断できるようにした。また、setgroups(2)のようにグループの配列全体を扱うシステムコールには、その配列を直接評価する特別なルール付与関数を実装した。これらの変更をカーネルのMAC(Mandatory Access Control)フレームワークに統合するため、新しいポリシーエントリポイントを追加し、全てのフックが適切に呼び出されるようにした。この改良により、mac_do(4)はより広範囲の権限変更要求を安全に監視・制御できるようになり、システム全体のセキュリティ強化に大きく貢献した。
次に、mac_do(4)の補助ツールであるmdo(1)の改善点について説明する。mdo(1)はユーザーがコマンドラインからmac_do(4)に権限移行を要求するためのプログラムである。
一つ目の目標は、mdo(1)が単にユーザーを切り替えるだけでなく、プライマリグループやセカンダリグループリストの変更、ユーザーIDやグループIDの明示的な指定といった、より詳細な資格情報制御を可能にすることだった。以前のmdo(1)は基本的なユーザー切り替え機能しか持っていなかったため、システム管理者が権限を細かく調整したい場合に不便だった。この問題を解決するため、まずユーザー名やグループ名を数値IDに正確に変換する関数、そして動的なグループIDの配列を効率的に管理(メモリの再確保、グループの追加、削除、重複除去など)するユーティリティ関数を開発した。新しいmdo(1)は、グループの設定方法として、正確なグループリストの指定、+や-を使った既存リストの修正、そしてユーザーデータベースや現在のプロセスからのスマートなデフォルト継承という三つの強力な方法をサポートするようになった。特にグループリストの修正機能は、ユーザーが非常に柔軟にグループメンバーシップを変更できるようになった点である。これらの複雑なオプションの組み合わせに対応するため、プログラムのメインロジックでは、オプション間の非互換性をチェックしたり、特定の状況下で必須となるパラメータの存在を確認したりするなど、詳細な条件判断とエラー処理を実装した。これにより、mdo(1)は非常に強力で柔軟な権限制御ツールへと生まれ変わった。
二つ目の目標は、ユーザーが設定しようとしている権限移行が、mac_do(4)のルールとしてどのように表現されるべきかを--print-ruleオプションで確認できるようにすることだった。この機能により、ユーザーは実際に権限移行を実行する前に、その要求がどのようなルール(例: uid=1000,gid=1000,+gid=1001,1002)に相当するのかを確認できるようになった。これは、mac_do(4)のルールを設定する際のデバッグや理解を大いに助ける機能である。
結論として、これらのプロジェクトはFreeBSDというオペレーティングシステムのカーネルセキュリティとユーザーランドツールの両面から、プロセスの権限管理を飛躍的に向上させた。カーネルレベルでの開発は、メモリの効率的な利用、並行処理の安全性確保、システムコールとの複雑な連携など、非常に高度で挑戦的な課題を伴う。この経験は、システム開発の奥深さと、それがシステムの安定性、セキュリティ、そしてパフォーマンスに直接寄与する重要性を教えてくれるものであった。