ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Endpoint Security Appのビルド
System ExtensionsはmacOSの信頼性とセキュリティを向上させます。Kernel Authorization KPIの最新リプレースメントについて知り、Endpoint Securityフレームワークによって優れたセキュリティ製品を作るためのヒントをご覧ください。
リソース
関連ビデオ
WWDC22
WWDC20
WWDC19
-
ダウンロード
こんにちは WWDCへようこそ “Endpoint Security Appのビルド” 私の名前はマシュー セキュリティー工学が専門で アーキテクチャーチームのメンバーです 今日はEndpoint Security Frameworkについて 話そうと思います 最初に導入されたのは 昨年のmacOS Catalinaのケースでした その目的はサポートされていない Macカーネルフレームワークである― Kauth KPIとOpenBSMオーディットトレールを 置き換えるためでした
カーネル拡張 通称KEXTSの 開発は容易ではなく デバッグはさらに困難で 極めつけにKEXTSの保守は 悪夢そのものでした カーネルインタフェースの変更が 頻繁に行われたからです
その上 カーネルは小さなバグでも しばしば動作不良を起こしてしまうので システム全体のセキュリティと安定性の 低下を引き起こしていました
Endpoint Security Frameworkを使用すれば カーネル拡張を 開発する必要はなくなり 製品が目指す本当の目標に 焦点を絞ることができます
Endpoint Security 通称ESを使用する製品は 通常のアプリケーションから 豊富なイベントストリームを利用できます 現在は約100のイベントタイプをサポートし その数は今も増え続けています イベントタイプには2つのカテゴリーがあります NOTIFYイベントはオペレーションの実行を ユーザーに通知します 一方 AUTHイベントを利用することで ユーザーは― オペレーションの継続を許可するか否かを 制御することができます では Endpoint Securityサブシステムが どのように機能するかと 製品がイベントストリームを 最適利用する方法 およびユーザーが利用可能な 拡張機能について説明しましょう
ESアプリケーションはスタンドアローンの 製品としても配置できますが Endpoint Securityをベースにした システム拡張として提供する方が より多くの恩恵を受けられる と確信しています システム拡張機能も同じく昨年に macOS Catalinaに導入されています サポートされている拡張のタイプは VPNやコンテンツフィルタなどの ネットワーク拡張 ハードウェア制御用のDriverKit エンドポイント検出やレスポンスに特化した Endpoint Securityが含まれています システム拡張機能についての 詳細に関しては昨年のWWDC 2019より “システム拡張とDriverKit”を 参照してください まず最初に拡張がインストールされると システム整合性保護が 偶発事故や悪意ある攻撃から 拡張とアセットを保護します
加えて使用しているデーモンに対し システムデーモンとほぼ同等の かなり高レベルの保護を提供できます つまりルートユーザーでさえ 起動されたジョブを アンロードできなくります
さらにEndpoint Securityには システム始動時などに サードパーティーアプリケーションが 実行可能となる前に イベントストリームを セットアップして実行する能力のような システム拡張がある場合にのみ 製品が使用できる機能がいくつかあります 私は最初に例を挙げて説明するのが好みですので Endpoint Securityを開始するために 必要なキーコンポーネントに 少し目を向けてみましょう まず注目するのは このフレームワークが Cのライブラリとして提供される点ですが それは皆さんが目にする ほかの多くのAppleフレームワークとは 少し異なっています Cを使用すると メモリーや性能に関するいくつかの特性を かなり制御できるという利点があります さらに私たちの目標は 既存の製品がすぐに採用可能な ライブラリーを提供する ことでもありました Cを使用することで このライブラリーは SwiftやObjective-C Rustなどの 様々な言語から呼び出すことができます
この例では最初に ES new client APIを使用して 新しいイベントストリームを 初期化することから始めます この関数はESクライアントTハンドルを 出力パラメータとして戻しますが 重要なのは イベントハンドラーブロックも 定義しているという点です ここで渡されたブロックは 処理の準備が整ったイベントが ある時に起動されます
この例で示したブロックは 単に イベントタイプをプリントするだけのものです 次にES subscribe APIを使用して イベントストリーム用の サブスクリプションをセットアップします この例でアレイには単一のイベント NOTIFY_EXECイベントがあります
この例でもES delete client APIが 使用されており サブスクリプションのセットアップで 何か不具合が起きると このAPIを使用して 以前にES new clientを呼び出して 取得したリソースをクリーンアップします
最後にdispatch mainを呼び出します これによりプログラムは 実行を継続して 上で示したイベントハンドラーブロックを通して イベントを処理します
Endpoint Securityがどのように機能するかを わかりやすく説明するため ハイレベルのアーキテクチャーデザインに 目を向けてみましょう プロセスがES new client APIを使用して Endpoint Securityサブシステムに接続する時 チャネルがセットアップされ それを通して メッセージは処理用の待ち行列に入れられます この図には2つの異なるESベースの アプリケーションが示されています 1つはES new clientの多重呼び出しを使用して 2つのイベントストリームをセットアップし 2番目のものには単一のチャネルがあります 私たちは普段これらのチャネルを ESクライアントと呼んでいます それぞれのESクライアントは 受信するイベントを制御するための 専用のサブスクリプションセットを 持つことができます
カーネルでイベントがトリガされ 私たちのクライアントがそのイベントに サブスクライブしているなら Endpoint Securityは このイベントをインターセプトして 分析を行うESクライアントに 有用な情報を付加します ここで収集される情報について もう少し詳しく説明します 収集されたデータは メッセージエンベロープにラップされます そのメッセージはすべての 適切なESクライアントに送信され そこで処理するイベントハンドラーブロックの メッセージがエンキューされます メッセージのエンキューは すべてのクライアントで同時に行われます 以前 イベントはクライアントに 順次配送されていたので Endpoint Securityになじみのある方には こうした動作には若干の違和感が 感じられることでしょう イベントにはレスポンスが必要なものがあり それについて簡単に説明します こうしたケースではレスポンスを受信するまで オリジナルのオペレーションは カーネルで停止状態となります 通常の通知イベントや AUTHイベントへのレスポンスを受信した場合 オペレーションは直ちにカーネルで ブロック解除され 続行可能となります
この図で留意すべき最後の点は AUTHイベントのために配送する必要がある メッセージの数を削減するために Endpoint Securityが できるだけ多くメッセージを キャッシュしようと試みることです キャッシュに関しては 後でさらに詳しく説明します
メッセージエンベロープは イベントハンドラーブロックを通して ESクライアントに配送される すべてのESイベントのラッパーである ESメッセージT構造を 参照しているということです 各メッセージには3つの主要なカテゴリーの 情報が含まれています 1つ目はメッセージメタデータです AUTHかNOTIFYのどちらでもイベントタイプや メッセージ生成時間などが含まれ さらに互換性を保つために使用されます メッセージバージョン番号が付けられます これについては後で説明します
次に すべてのメッセージには 最初にイベントをトリガさせるプロセスに関する 情報が含まれます このプロセス情報には すべてのステータス情報や コードサイニング情報 プロセスIDおよびユーザID情報や 実行可能ファイルに関する情報が含まれます 最後に各メッセージには 発生したイベント固有の情報が含まれます たとえば SIGNALイベントでは シグナル生成するプロセスと シグナル番号に関する情報が含まれます 実行イベントでは 実行対象のファイルや実行可能な引数 などの情報が含まれ― OPENイベントではオープン対象の ファイルとオープンフラグに関する― 情報が含まれます
Time-of-check to time-of-useの 問題は非常に重要で 独自のES製品を開発する場合には 十分に注意する必要があります あまり詳しくない方のために 簡単に説明すると これはプログラムが確認した時点では 成立していたある条件が 使用する時点ではもう成立しなくなっている ことで発生するタイプのバグを表します 典型的な例としては 最初にファイルの存在をチェックし 次にそのファイルをオープンする ツーステップのプロセスが挙げられます プログラムが存在を確認した後 それをオープンする前に システム上の誰かが そのファイルを削除してしまい そのプログラムがオープンの失敗を 適正に処理できないと 予期しない動作が起きることになります ESメッセージで提供される情報は 特定時点でのスナップショットを反映しています システムは継続して 複数のスレッドを同時に処理しており アプリケーションがメッセージを 調べるチャンスが訪れる前に それらのスレッドがシステムのステータスを 変化させる何かを行う可能性があります いくつかのケースでは そのために ユーザーが自分で情報を照会した時点の内容と メッセージの一部の内容が異なる という結果が生じます
製品はこうしたTOCTOUのような 問題に十分配慮する必要があります これはカーネル拡張ベースの製品が 現在直面している問題と 何ら変わりはありません デモに進む前に留意すべき 最後のポイントは Endpoint Securityには ESイベントストリームを正しく確立するために プログラムが達成しなければならない いくつかのランタイム要件があることです
第一にアプリケーションはEndpoint Securityの 適正な実行権を持っていなければなりません これは制限付きの実行権であり ユーザーはこのリンクを通して プロビジョニングプロファイルを リクエストできます
アプリケーションがシステム拡張として バンドルされる場合は 内包されるアプリバンドルが 拡張をインストールするために 追加の実行権が必要となります これに関する情報は システム拡張文書に記載されています 第二にシステム拡張には インストール実行のユーザー承認が必要です ある拡張がシステム拡張APIを使用して インストールプロセスを開始したなら ユーザーは“Security and Privacy Preferences” ダイアログのGeneralペインで そのインストールを 承認しなければなりません
続いてユーザーのプライバシーを向上するため アプリケーションは“Security and Privacy Preferences”ダイアログの Privacyペインで Full Disk Accessを許可するという形で ユーザーの同意を得る 必要があります
システム拡張として展開する場合 ユーザーがより簡単にそれらの許可を 行えるようにするため インストールの際に このダイアログに 拡張をあらかじめ配置します
製品が管理デバイス上に 展開されている場合は 分散を支援するために 2つのMDMペイロードが利用できます 第一はユーザの承認なしで 自動的にインストールが許可される― 拡張を定義するペイロードです 第二は プライバシープリファレンスペイロードで 自動的にFull Disk Accessが 許可されます さて それでは 最初のデモを始めましょう このデモでは 通知メッセージを確認しながら 既存のES拡張を拡張します
ここで表示されている NOTIFYデモのmain関数を見れば すぐに そのコードが前のいくつかの スライドで目にした例の中のコードに かなり類似していることに 気づくと思います まずES new clientを使用して 新しいクライアントイベントストリームを セットアップすることからスタートします ハンドラーブロックが ハンドルイベント関数を呼び出すのを すぐに目にすることができます 次にサブスクリプションをセットアップして ES subscribeを呼び出します 今 ここではNOTIFY_EXECと NOTIFY_FORKのイベントタイプが発生しています 続いてサブスクリプションを開始したなら dispatch mainを呼び出して プログラムがこれらのイベントの 実行と処理を継続できるようにします ではハンドルイベントへ進んでみましょう ここではメッセージイベントタイプで switchステートメントを使用しており この例のケースではそれぞれのイベントが画面に メッセージを残すだけであることがわかります この拡張は既にインストールされ システム上で承認され 実行中なので どんな様子になっているかを ちょっと見てみましょう ここでは最初に ログストリームをセットアップし さらに関連する述語関数を用意して 今まさに行っている実行に 意識を集中できるようにします 次はpsユーティリティを 使用する例を取り上げます ここではpsを実行する時に 2つのメッセージが届くのが確認できます 最初に目にするのはZシェルが 新しい子プロセスを分岐させ さらにその分岐したプロセスから 新しくexecutable/bin/psを 実行しようとしていることです それでは このデモを拡張して すべてのプロセスライフサイクル情報を 取得するためのEXITイベントを 追加してみましょう サブスクリプションをリストアップするため イベントアレイに― ES_event_type_notify_exitを追加します
先ほどのハンドルイベント関数に この新しいケースを追加する必要があります
さらに先に進めて breakステートメントを追加しますが 後でそれを行うのを忘れるといけないので 今 そのことを記録に留めておきます さて ここでOSのログ機能と書式指定 文字列用の一般的に用いられる― コードをいくつかセットアップし 先に進んで以前にやったように pidと共にinstigatingプロセス情報を 追加します
さて次にEXITイベントにはexitステータスに 関連するイベント固有情報が いくつか含まれていますので 同じようにそれを取得します
できました ここで この書式指定文字列用の データを入力し 最初にinstigatingプロセスを 探す必要がありますが それはメッセージ構造のプロセスメンバーを 見ながら進めることができます
続いて 実行可能ファイルを参照し 最後にパスを取得します
次に そのプロセスと他のいくつかの プロセスのためのpidが必要です Endpoint Securityはその情報に関する auditトークンを提供します その中にユーザIDやプロセスIDなどの 情報が含まれています それらの抽出にはlib BSM関数 audit_token_to_pidを使用できます ここでもう一度 プロセスフィールドを参照して auditトークンを取得します
最後にEXITイベントでフリーの イベント固有情報― exit statusコードを取得します それを行うためにメッセージの イベントメンバーを参照します そこからEXITイベントを取得し 最終的には そのステータスコードの statフィールドを取得します
では次は この拡張を構築して それをインストールする必要があります このシステム上ではシステム拡張開発者モードを すでにセットアップ済みなので テスト用の拡張より迅速に 展開することができます
もう一つ留意すべき点は システム拡張では/Applications内の containingアプリバンドルが 拡張をインストールできるようにする― 必要があるということです なので今構築したcontainingアプリを /Applicationsにドラッグします さらに そのアプリを実行することで 最終的に拡張がインストールされます この時点ではアップグレードだけを 実行していますが 新しい拡張はシステム上で 実行され 稼働中です 再度 psユーティリティを 例として取り上げます 見てのとおり 3番目のexitメッセージを取得しました その中にはプロセスが 正常に終了したことを示す― ゼロのステータスコードが含まれています ちょっと確かめてみるために 無効なフラグをpsに渡してみると ステータスコードはプロセスで問題が 発生した可能性を示す256に なっていることが確認できます
ここまで通知イベントが 実行される様子を見てきたので 今度はAUTHイベントについて話しましょう Endpoint Securityの重要な特徴の一つが システム上のオペレーションを 認可する能力です AUTHイベントは同期型で すなわちESクライアントの メッセージをエンキューすると そのオペレーションはカーネルで 停止状態となり レスポンスを受信するか 時間切れに なるまでは再開できないことを意味します
受信したそれぞれのAUTHメッセージには メッセージ構造の中に個別の deadlineフィールドが含まれています ESクライアントは各メッセージについて この値をチェックして 実行する作業を期限切れになる前に 完了させる必要があります 期限切れになる前にクライアントが レスポンスできないと アプリケーションは終了します
アプリケーションがシステム拡張の場合 起動されたジョブは 自動的にリスタートされます 特に重要なのは 個々のメッセージにはそれぞれ― 固有の有効期限があり 各メッセージのレスポンス時間が 同じであるという保証は どこにもないということです これらの値は時間とともに 変化する可能性があります
有効期限を過ぎるとレスポンスとして implicit ALLOWが適用されますが 結果はキャッシュされないため 以降のオペレーションは 再評価されます
AUTHイベントへのレスポンスに 利用可能なAPIは2つあります ほとんどのイベントタイプで必要となるのは es_respond_auth_result APIを使用する― シンプルなALLOWまたはDENYレスポンスです
2番目のレスポンスAPIである es_respond_flags_resultは イベントの許可または否定のオプションに 幅がある場合に使用されます 例えばオープンを許可するファイルを 読み書き可能ではなく 読み取り専用属性のものに制限するなどの ケースが挙げられます
各イベントタイプに どのAPIを使用するかについては ドキュメンテーションを 参照していただく必要がありますが 今のところflags result APIを使用するのは AUTH OPENイベントだけです
システム上の複数のクライアントが 同じAUTHイベントにサブスクライブする場合 最も限定的なレスポンスを適用して すべてのクライアントからの レスポンスがひとつに結合されるため あるイベントに対して4つのクライアントが ALLOWでレスポンスしたけれども 1つがDENYを返した場合 すべての結果は オペレーションの拒否となります
同様にflagsレスポンスの場合 すべてのクライアントがセットした フラグのサブセットだけが容認されます trivial deadlockを引き起こす可能性があるため ESは introspective AUTHイベントを 送信しないことに留意してください イベントの許可は暗黙的に行われます ただしプロセスが開始したイベントについては NOTIFYメッセージが送信されます
ここで注意しなければならないのは flags result APIを使用する場合 ESクライアントプロセスは 個別のイベントについて リクエストされたフラグだけでなく それが許可したすべてのフラグをレスポンスする 必要があることに注意してください これはキャッシングにも関係してくるのですが 後ほど詳しく取り上げます mutingを使用するとESクライアントは アプリケーションの関心対象外の プロセスからのメッセージを 受信しないようにすることができます これを行う主な方法は2つあります
第一はes_mute_processを使用する方法で ESアプリケーションはプロセスを固有に 識別するauditトークンを提供します 一般にクライアントは前回のメッセージから auditトークンを取得します Endpoint Securityサブシステムは プロセスの終了を自動的に追跡し クライアントのmuting設定から それらのプロセスを削除します クライアントが 以前にmutingしたプロセスからの メッセージの受信再開を望まない限り 対応するes_unmute_process APIを 手動で使用する必要はありません
またESはpath literalによる mutingもサポートします このパスは es_mute_path_literalと パスプレフィックスes_mute_path_prefixを 使用する完全パスです 名前が示すように このメカニズムは 指示されたパスと一致する― instigatingプロセスからの メッセージを受信しないようにします ESクライアントが示す最後の2つのAPIを 使用する場合は十分に注意する必要があります なぜなら高速ルックアップオペレーションで 使用するデータ構造に 大量のパスを追加すると 性能に悪影響を及ぼす 可能性があるためです イベントをmutingする方法としては process auditトークンでmutingを行う方が オーバーヘッドをあまり増やさないので 一般に優れています
AUTHイベントの多くは ESクライアントからのレスポンスを 結合した結果をキャッシュし それをすべてのESクライアント間で共有する 単一のグローバルキャッシュに保存できます
Endpoint Securityが採用したキャッシュ戦略は ベストエフォート方式でエントリーは いつでも期限切れになる可能性があります Endpoint Securityは 複数のオペレーションを追跡し ファイルへの書き込みやサイズ変更 削除などの処理が行われた場合には 該当するキャッシュされたエントリーを 自動的に無効化します
レスポンスがキャッシュされるかは 保証の限りではありませんが ESクライアントがresponse API内で キャッシュフラグをfalseに設定すると たとえ別のクライアントがレスポンスの キャッシュを要求したとしても ESはキャッシュを 行わないようになります ESクライアントが EXECイベントのキャッシュを 拒否しようとする理由はいくつかあり 実行可能ファイルが実行可能ファイルの 引数やinterpretersなどの環境変数に かなり依存している場合に インタープリターが実行する― スクリプトを検査したい場合 などが挙げられます
ESアプリケーションはes_clear_cache APIを 使用してキャッシュ全体を消去できますが 個別のキャッシュエントリーの 無効化はサポートしていません 新しいクライアントが接続されたり 既存のクライアントが切断された時にも ESが自動的にキャッシュを 消去することに留意してください このような場合 残りのクライアントが 以前の結合されたキャッシュ結果とは 異なる結果をレスポンスするなど 様々なシナリオが考えられます
いくつか前のスライドでも 重要な注意を促したと思いますが es_respond_flags_result APIを使用する場合は 常に十分に注意する必要があります
初めは理解しにくいかもしれませんが レスポンスにはESクライアントがそれまでに オペレーションに対して許可したすべての フラグを含まなければなりません その理由は flagsレスポンスのキャッシュは AUTHレスポンスと同じように機能するからです
将来 イベントが発生した時には 最初にキャッシュが参照されます キャッシュされたエントリーが存在していれば そのエントリーのフラグ 新しいオペレーションについて 要求されたフラグを比較し AUTHメッセージを生成することなく その結果が適用されます ここで読み取り専用フラグが セットされているファイルをオープンしようとし オープン対象のファイルがアプリケーションの 関心対象外であるケースについて考えます この場合 そのアプリケーションは プロセスに対してファイル操作に 必要な許可を与えようとします ESクライアントプロセスが flags結果のレスポンスを戻す際には プロセスはこの特定イベントについて 要求されなかったものも含め WRITEフラグを含むすべての適切なフラグを セットする必要があります ここで仮にWRITEフラグをセット しなかったならどうなるでしょうか そのプロセスが後で同じファイルを 書き込みのためにオープンした時 キャッシュされた結果が まだ存在していたなら たとえそれが製品の意図に 反するものであろうと そのオペレーションは AUTHメッセージを生成することなく 自動的に拒否されます これまでに見てきた AUTHとNOTIFYの2つのイベントについて 簡単に比較してみましょう
NOTIFYイベントは常に非同期です クライアントへのメッセージは エンキューされますが オペレーションは直ちに続行され ESクライアントがメッセージを 参照する機会を得る前に 完了してしまう可能性があります
AUTHイベントは 前に述べたように同期型であるため レスポンスを受信するまで オペレーションは停止します
配布に関してはNOTIFYメッセージは 常に配布されます 一方 AUTHメッセージは キャッシュされた結果が 存在しない場合にのみ配布され イベントを開始した同じプロセスには メッセージは配布されません ただし開始したプロセスが mutingされている場合は AUTHとNOTIFYの どちらのメッセージも配布されません
最後に この2つのメッセージ構造にも 若干の相違があります NOTIFYメッセージにはEndpoint Securityが 適用した結果が含まれます すなわち対応するAUTHメッセージが もたらしたALLOWまたはDENYの結果です AUTHバリアントにサブスクライブされている ESクライアントがない場合 またはイベントにAUTHバリアントが 存在していない場合 結果は暗黙で許可されます AUTHメッセージについては結果情報は まだ明確には存在していません ただしメッセージには 有効期限の値が存在しており さらにESクライアントプロセスからの レスポンスも必要です では別のデモに進みましょう 今度はAUTHイベントタイプに注目し それらのメッセージを処理してレスポンスする クライアントの記述方法について見てみましょう
改めてもう一度 main関数に注目します それが前のデモでセットアップしたものに よく似ていることに気づかれたと思います 一点だけわずかに違っているのは 非同期のディスパッチキューで いくつかの作業を 非同期で行おうとしていることです では最初に そのキューをセットアップします ただし この新しいクライアントイベント ストリームは後でES new clientを 使用して初期化するつもりです そしてハンドラーブロックで再度 ハンドルイベント関数を呼び出す予定です
このデモではAUTH_EXECと AUTH_OPENのイベントタイプに注目します そしてサブスクライブの後で もう一度dispatch_mainを呼び出して プログラムがこれらのイベントの 実行と処理を継続できるようにします
ではハンドルイベント関数を 見てみましょう ここでもう一度 個別の各イベントタイプに 適切なハンドル関数を 呼び出すための switchステートメントに注目します まず EXECからスタートします
ここで注目すべきは ESがAUTH結果に すべてのEXECSを許可する ES AUTH結果でレスポンスしていることです しかしsigning IDと一致する新しいEXECを 拒否するためにすべきことがまだ残っています 一般のほとんどの製品は CDハッシュでのマッチングなどのような より制限されたポリシーを 求めていると考えられます しかし デモンストレーションのため 何をブロックしようとしているかを もう少し明確にしましょう では どのようにすればいいでしょうか 1つのやり方として 実行しようとする 新しいプロセスで文字列を簡単に比較し 次いで ブロックしようとする対象の signing IDを比較するという方法があります
ここまではイベント固有のデータに 注目してきたので 今度はevent unionに目を向けます ここから先はターゲットプロセスと signing_idデータの取得に注目します
そのデータをsigning_id_to_blockと 比較してみましょう それらが等しい場合は それを拒否しようと思います 拒否を行うには es_respond_auth_result APIを使用します ここでクライアントにメッセージの 引数を渡す必要があります 次に結果 すなわち単純なAUTH_RESULT_DENYを 定義する必要があります
さらに これがキャッシュされるようにします そして これを完成させるため ここでelseステートメントに 前回の既存のレスポンスを配置します これで新しい実行可能ファイルが マッチングを行って ブロック対象のsigning IDと 一致したものについては“拒否”結果で レスポンスし それ以外は許可します
今度はオープンケースを見てみましょう
handle_openでは あまり見かけないかもしれませんが まだ全く話していないけれども 導入する必要がある コンセプトがいくつかあります ESクライアントには イベントハンドラーブロックではできるだけ 作業をさせないようにしてください 多数の入出力を伴ったり CPUに大きな負担をかけるような タスクを行わせないようにするという意味です ここでの目標は イベントハンドラーブロックが できる限り迅速に動作できるようにして メッセージをデキューして戻す作業を 継続できるようにすることです それによりメッセージキューの サイズが小さく保たれ メッセージがドロップするのを 防ぐことができます
この関数では es_copy_message APIを使用して メッセージをコピーしますが それについては後で詳しく説明します
メッセージをコピーした後 handle_open_worker関数を 非同期で呼び出し 処理が完了したら次に進んで コピーされたメッセージを解放します ではworker関数の役割とは何でしょう? それに関連する 3つのケースについて説明します 第一のケースは EICARファイルのテストです EICARファイルは簡単に説明すると 悪意あるコードを― 実際にシステムに導入することなく 製品が通常使用するアンチウイルスを オペレーション環境でテストするための テスト用ファイルです この機能はデモの目的に関しては あまり重要ではありません ファイルのコンテンツを オープンして検査する必要があるため ドライバのサイズが大きくなっており それが非同期で動作させる理由です
これをEICARファイルと仮定して es_respond_flags_result APIを 使用してみましょう ご覧のように マスクにはゼロが定義されています 従って原則として すべてのビットがクリアされ そのファイルに対するすべての オープンオペレーションは拒否されます
さて まだ私たちには やるべきことが残されています
こちらのifステートメントでは オープンする対象のファイルの オープンイベント固有データを検査して それをここでは/usr/local/binと 定義されている― 読み取り専用の プレフィックスと比較しています 従って これらのディレクトリのファイルを 誰かが操作するのを防ぐ必要があります
そのためにes_respond_flags_result APIを 再度使用します さらにクライアントメッセージの 引数も渡します 次はフラグを定義する必要があります ここで拒否しようとしている 唯一のオペレーションは 書き込みオペレーションです すべてのビットがセットされるように マスクを定義し 次にbitwiseオペレーションを何回か使用して writeフラグをクリアします
ここで皆さんはEndpoint Securityが オープンイベントにフラグを提供していると 気付くかもしれません それはflagsのカーネルバージョンで オープンシステムコールで 見かけたことがあるかもしれません これについてはオープンイベントの説明書に 記載されていますので ご覧になってください
最後に これが キャッシュされるようにします そして最後のケースについては ここにすでに述べていますが 他のすべてのオープンオペレーションを セットするため 一括してセットする方法により すべてのビットをセットして すべてのオープンオペレーションが 正常に続行できるようにします
では ここまでの内容を要約します
このプログラムは3つの機能を 実行するよう意図されています 一番目はテキスト編集アプリケーションによる オープンを拒否することです まだ拡張はインストールしていないので すべてが機能していることが確認できます そのためテキスト編集も 正常にオープンできます もう一つ意図したのは/usr/local/bin内の ファイルへの書き込みを拒否することです 例を示しましょう high.shという名前のスクリプトが セットアップされています これは画面上に“high”と 表示するだけのものです
そして その動作を確認するために それを実行します それはまさに予期したとおりの 動作を示しています 最後にホームディレクトリには EICARファイルがあります ここでそれをプリントすると 標準のEICARテストファイルの 定義内容を参照できます
では先に進めて 拡張を構築しましょう
再度 システム拡張を インストールする必要があるので /Applicationsにドラッグします
containingアプリバンドルを起動して その新しい拡張をインストールすると AUTHデモが実行され 動作中です 最初にテキスト編集アプリケーションを オープンしようと試みます アイコンに動きはみられるものの 何も起動しません
次に/usr/local/bin内のファイルを 変更してみようと思います ここではhighスクリプトのコンテンツを 代わりに“Bye”と表示するように 変更しようと試みます しかし もう書き込みは許可されていません ただし 読み取り専用の目的で オープンすることは まだ許可されており スクリプトの実行も可能です
最後にEICARファイルは それに対する すべてのオペレーションを拒否するのが目的です そのためファイルの読み取りも もはや許可されません ESクライアントに書き込んで AUTHイベントを処理することを理解したら さらに高度なトピックを取り上げましょう メッセージ構造には ESベースのアプリケーションが リリースの異なるオペレーティング システム間に展開された場合に 互換性を維持するための バージョンフィールドが含まれています
時とともに様々なEndpoint Security構造に 新しいフィールドを追加できるようになり ESクライアントに追加の情報を 提供できるようになります バージョン番号は単精度整数の値で あるOSリリースのすべてのメッセージは 同じバージョン番号を共有します 互換性に何らかの影響を及ぼす可能性が あるような変更がOSリリースに加えられた場合 バージョン番号は増分されます より新しいフィールドを使用するクライアントは フィールドが利用可能であることを確かめるため 最初にメッセージのバージョンを 確認する必要があります その確認を行わなければ 未定義の動作を引き起こす可能性があります ES構造へのdocsヘッダーには 細心の注意を払うようにしてください どのバージョンに新しいフィールドが 追加されたかが示されています
簡単な例として aclフィールドで createイベントを見てみましょう このフィールドは初期のリリースには 存在していませんでした それが追加された時 メッセージバージョンは2に引き上げられました ここでは handle_notify_create関数を 使用します それにより NOTIFY_CREATEイベントが起動します それがaclメンバーにアクセスするのを 以前に目にしています この関数は最初に メッセージバージョンが 2以上であることを確認します
いわゆるearly boot機能は Endpoint Securityの強力なメカニズムです この機能は システム拡張であるESベースの― 製品でのみ利用可能で スタンドアロンのESアプリケーションでは 利用できないということです この機能で拡張を有効にするには その拡張の input/outputリストに NSEndpoint SecurityEarlyBootキーを設定します システムの起動時にearly boot クライアントを登録している場合でも システムは通常どおりに起動します ただし すべてのearly boot拡張が 準備完了となるまで 追加のサードパーティアプリケーションを 実行することはできません 準備完了を伝えるために 拡張は 少なくとも1回 es_subscribe APIを 呼び出す必要があります
すべてのearly bootクライアントが 最初のサブスクリプションを完了すると サードパーティアプリケーションの 実行がようやく許可されます これは 1回es_subscribeを呼び出し 必要なすべてのイベントを― サブスクライブする必要が あることを意味しています イベントを複数回の呼び出しに 分割した場合 追加のサブスクリプションが 実行される前に サードパーティアプリケーションの 実行が開始され クライアントが イベントを見逃がす可能性があります さらに 留意すべき重要な点として Endpoint Securityサブシステムが 有効期限を強制適用するため すべてのearly bootクライアントは それまでに準備完了を伝えなければなりません 有効期限に達すると すべてのサードパーティアプリは 自動的に実行継続を許可されます イベントの見逃がしを防ぐため サブスクリプションを行う前の 拡張の初期化ステップの実行が 長くなりすぎないよう十分に注意してください
有効期限の値をAPIにしようとは 考えていませんし 値は時間の経過とともに変化しますが その値はエンドユーザーに 多大な影響を及ぼすことなく 製品が起動して準備を完了するのに 十分な長さであると考えています
すでにEndpoint Securityフレームワークで 作業した経験のある方は おそらく気付かれたと思いますが ネットワーキングオペレーションに 関連するイベントは提供されません その方がNetworkExtension フレームワークでカバーしやすいため そのようにしています
UNIXドメインソケットに関しては例外があり ESはそれらにイベントを提供します
また 単一の結合されたシステム拡張から Endpoint Securityと NetworkExtensionフレームワークを 両方使用できることも 知っておく必要があります これらの製品では システム拡張APIを使用して 単一拡張タイプの 拡張の場合と同じように フローをインストールする必要があります さらに拡張のinfo.plist内の 固有のキーを ネットワーク拡張とEndpoint Security拡張の どちらかと結合しておくと システムはそれらの値を適切に 適用することに配慮するため安全です
ESはシステム内で発生した順に メッセージをクライアントに送信します 例えば一般的なシナリオで クライアントがFORKとEXECイベントに サブスクライブしている場合 新しいプロセスが発生すると クライアントのハンドラーブロックは 常に対応するexecより先に forkイベントを受信します また メッセージの順序は個別のESクライアントの サブスクリプションにも適用されます アプリケーションが複数の ESクライアントを作成し それらにサブスクリプションを分割する場合 イベントの順序は各ESクライアントの サブスクリプションにのみ 関連した並びになります 必要であれば メッセージstructに含まれる メッセージ生成時間を利用して グローバルな順序で 再構築することもできます
メッセージは 一度に1つずつ ESクライアントに配布され ESクライアントのイベントハンドラー ブロックが前回のメッセージ処理から 戻った時にのみ メッセージは配布されます ハンドラーブロックが受信した メッセージ構造には有効期間があり そのメッセージのハンドラーブロックが 起動されている期間と同じ 長さの間だけ有効となります 一度 ブロックから戻ったら メッセージへの アクセスを継続すべきではありません 継続した場合 結果として未定義の 動作が生じる可能性があります メッセージの有効期間を 延長したい場合 デモにおいてもそれを行う必要があるので es_copy_message APIを使用して 期間を延長します これにより 対応するes_free_message APIが 呼び出されるまで有効なハンドルが メッセージに戻されます
これが意味する重要な点は AUTHイベントは必ずしも ハンドラーブロックが戻る前に 応答を受け取る必要がないということです さらに AUTHイベントも 必ずしも ESクライアントに提示されたのと 同じ順序でレスポンスを受け取る 必要がありません これはアプリケーションが― 前のメッセージに関する 決定を行うために将来のメッセージを 最初に検査する場合 重要な影響を 及ぼす可能性があります このケースでは ESアプリケーションは 最初にメッセージをコピーし ハンドラーブロックから戻ったなら 追加のメッセージの検査を開始し その後でコピーされたメッセージに 適切なレスポンスで応答します 最後に 不要になったメッセージの 解放を忘れないようにしてください 非同期のディスパッチキューを 使用する場合などのように 何らかの非同期メッセージ 処理が必要な場合は そのアプリケーションに適切な サービス品質クラスでディスパッチ キューを初期化してください このような場合 AUTHイベントに サブスクライブするか否か どの程度のイベントボリュームが予測されるか あるいは CPUやシステムリソースを どれくらい使用するかといった 様々な要素が深く関わってきます 次に 優れたEndpoint Securityベースの製品を 開発する上で役立つ― 様々なヒントを紹介しようと思います
メッセージ構造内の is_es_clientブールメンバーという フィールドにお気付きでしょうか ESアプリケーションは自身の アクションを認可できません それらのintrospectiveイベントは 自動的に許可されるからです ただし システム上に複数の ESクライアントが存在し ESクライアントが 他のESクライアントのアクションを 認可する能力を持っている場合があります 開始プロセスにEndpoint Security クライアント実行権が含まれているなら AUTHとNOTIFYの両方のメッセージで is_es_clientフィールドは “true”にセットされます
クライアントのアクションに 不適切な干渉が生じたり フィードバックループを生成してしまうような アクションが実行されるのを防ぐために ESクライアントは このフィールドを検査して 適切な対応を行う必要があります
パスmutingは― クライアントにメッセージが殺到するのを 防ぐ理想的な方法です さらに 処理または認可する必要がある メッセージの数を減らすことで システム全体の性能を より高い状態に保つのにも役立ちます 例えば クライアントが― Mac OSのindexingファイルの Spotlightサービスに関与していない場合 適切なindexingプロセスをmutingすることで いくつかのイベントタイプで受信する メッセージの数を大幅に削減できます
キャッシュの恩恵を受ける アプリケーションは キャッシュは性能目的のみとし 決してポリシーに使用しないよう 留意する必要があります
理由は 前にも述べたように キャッシュエントリーは いつでも期限切れになる可能性があるからです 例えば― アプリケーションが あるプロセスによる ファイルのオープンを拒否することを決定し そのレスポンスをキャッシュ可能にしたなら プロセスをmutingしてはいけません キャッシュエントリーが後で無効化または 削除されたならどうなるか考えてみましょう ESクライアントがプロセスを mutingしていたなら プロセスが― ファイルの再オープンを試みても AUTHイベントは生成されないので オープンオペレーションは 自動的に許可されてしまいます
次のヒントは AUTHイベントに サブスクライブしているESアプリケーションを デバッグする際は 注意を要するということです レスポンスの有効期限を無効化したり 延長する方法はありません ブレークポイントに達している時に AUTHイベントがエンキューされた場合でも 有効期限が切れる前に レスポンスを行う必要があり それが実行されなければ 通常のルールに則ってプロセスは終了します 最後に メッセージキューが満杯なら Endpoint Securityはメッセージを クライアントに送信することなく ドロップできます クライアントは メッセージ構造の― sequence numberフィールドを検査することにより 同様のケースが発生したか確認できます このシーケンス番号は クライアントごと イベントタイプごとに付けられるため それぞれにおいて欠落がいくつ生じたか 正確に判別できます ドロップ率が比較的高い クライアントは muting 非同期処理や 複数のESクライアント間での サブスクリプション分割などの より高度な― Endpoint Security API利用パターンの 恩恵を活用すべきでしょう
今年は auditサブシステムの使用を 推奨しない旨が発表されています これは 主にオーディットトレールファイルに 書き込まれるイベントや “/var/audit”ディレクトリで 通常 確認できるもの あるいはライブイベントストリームを 要望するアプリケーションのために Auditpipe疑似デバイスに 送信されるイベントなどの auditイベント関連の 機能がそれに該当します 一方 この声明は auditトークンや auditセッションのような システムのコアパートであり続けている 機能には適用されません auditイベントに依存する製品は Endpoint Securityフレームワークを 使用する方向に移行すべきです macOS Big Surについては 何か目新しいことがあるでしょうか
まず最初に EXECイベント用の新しい イベント固有APIがいくつかあります 現在 ESは新規の実行プロセスを 始めるためのファイルディスクリプターと ファイルディスクリプタータイプ のリストを提供しています また 固有の識別子 特にPipeファイル ディスクリプター用の識別子も提供しています それにより ユーザーは複数のプロセスが Pipeプロセス間通信機構を通して 通信を行う方法を 探し出すことができます ただし 性能限界のため すべてのファイル ディスクリプターを提供できるとは限りません 重点を置いているのは“standard in” “standard out”と “standard err”ですが ほかにもいくつかの ディスクリプターを提供するとともに まだ列挙していない追加の ディスクリプターがあれば それらをクライアントに公開します
性能に関しては Endpoint Securityフレームワーク のearly adopterを検討しており macOS Big Surでも 数々の事柄を用意しています メモリーアロケーションを削減し イベントスループットを向上させるために データ構造の多くを書き直しました さらに 無効化のボトルネックを排除し 総合的なメモリー性能を 改善するために キャッシュの調整を行いました これらの機能拡張を1つに結合することで さらなるシステム性能の向上と ドロップの削減を達成しました これにより 皆さんの Endpoint Security製品にも より優れた性能特性が もたらされると考えています
macOS Big Surにも 新しい イベントタイプがいくつか追加されています いくつかは以前からあったNOTIFYイベントに 対応した追加のAUTHバリアントですが いくつかは新しいタイプです 2件ほど紹介します 第一に 幅広い層から要望があった トレースイベントが追加されました これはプロセスがデバッグ中であることを クライアントに通知します
第二に 心躍るような CS_INVALIDATEDイベントが導入されます 皆さんの多くがすでにご存じのように signedプロセスが実行されると カーネルは 常にそれが ページインしているコードが 個別のページハッシュのバイナリーと 一致するかを検証します 不一致が見つかると プロセスは CS_VALIDビットをクリアするために そのcode signingフラグを更新します これまでは ESクライアントが 将来のメッセージを待機し 開始プロセスのcode signing フラグを検査して 無効になっていないかを 判別する必要がありました しかし このイベントを使用すれば― クライアントは即座に通知を 取得することができます ひとつ注意すべき点は バイナリーが使用する ランタイム機能が強化されていることです 例えば CS_KILL code signing フラグがセットされている場合 ハッシュの不一致が見つかったなら システムはそれを自動的に終了させます
さて あとは何について話しましょうか まず最初に 皆さんにお見せした トップリンクを使用して 限定的なEndpoint Security entitlement実行権の 要求プロセスを開始できます 次に Apple Developer Webサイトで 公式のEndpoint Securityと System Extension API文書の レビューを始めることができます SDKのEndpoint Securityヘッダー文書は 入念にお読みいただくことをお勧めします ウェブサイトには書かれていない コメントや詳細が追加されており よくある質問への回答になるとともに すばらしい製品を構築する助けになると思います 最後に 今日実施したデモの大半をベースにした 利用可能なサンプルコードを作成します 皆さんがEndpoint Securityフレームワークを 動作させ始める際に役立つことでしょう ご清聴いただきありがとうございました 皆さんが構築したものを目にする機会を 楽しみにしています そして WWDC2020をお楽しみいただけますように
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。