LSMを使用してLinuxカーネルの主要なメカニズムに埋め込む機能

ソフトウェアシステムへの埋め込みは、追加の(サードパーティの)ソフトウェア要素を導入するプロセスとして理解され、一方では機能が維持され、他方では機能が拡張または変更されるように実装されます。



Linuxカーネルに組み込む方法の中で、Linuxベースのセキュリティ裁量モデル(DAC)の拡張を目的としたさまざまなセキュリティモデルを統合するように設計されたLinux Security Modulesフレームワーク(以下、LSM)の使用に基づく方法に注目する価値があります。



LSMのLinuxカーネル実装について



後で説明するLSM実装の機能により、このサブシステムを使用してLinuxカーネルの主要なメカニズムに組み込むことができます。 同時に、LSMの主な目的は、現在のLinuxセキュリティモデルを拡張するための統一されたインターフェイスをユーザーに提供することであるため、これに関連する制限があることに留意する必要があります。



LSMサブシステムのアーキテクチャは単純です。 技術的には、OSのカーネルにインストールされたフックのセットで、さまざまな操作中にシステムが呼び出すカスタムハンドラーを実装するためのインターフェイスを提供します。 基本的なセキュリティモデル記述子構造はsecurity_operations



構造であり、その説明はinclude / linux / security.hファイルにあります。



 struct security_operations { char name[SECURITY_NAME_MAX + 1]; ... int inode_permission(struct inode * inode, int mask); ... }
      
      







ご覧のように、セキュリティモデルは次のパラメーターによって記述されます:名前とハンドラー関数へのポインターのセット。その本質は、実装されたセキュリティモデルの主要な機能です-資格、ロールベース、または他のモデルです。 ここでの主なことは、オブジェクトへのサブジェクトのアクセスの実現を担当するカーネルコードのセクションの実行中に制御をインターセプトする機能です。



システムの重要な場所に配置されているLSMフックは、現在のセキュリティモデルの1つまたは別の機能を特別に設計された呼び出しであり、変更する可能性は残されています。 技術的には、これは追加のエンティティ(現在アクティブなsecurity_ops セキュリティモデルへのポインタ)を導入することで実装されます。



 static struct security_o perations * security_ops; static struct security_o perations default_security_ops = { . name = "default", };
      
      







システム起動時のこのポインターの初期値は、 default_security_ops構造体で記述され、システムのデフォルト動作の実装を含む標準セキュリティモデルに対応しています。 同時に、ハンドラー関数の入力は最小限です。 ただし、システムの操作中に別のモデルをアクティブにする必要が生じた場合は、このポインターの内容を単純に置き換えることでこれを実行できます。 以下は、対応するフックで security_ops



を使用する例です。



 int security_inode_permission(struct inode *inode, int mask) { if (unlikely(IS_PRIVATE(inode))) return 0; return security_ops->inode_permission(inode, mask); }
      
      







ご覧のとおり、 inode_permission



ハンドラーinode_permission



は、上記に対応するsecurity_ops



ポインターの値を使用して間接的にinode_permission



ます。 良い例は、ファイルシステムオブジェクトへの現在のプロセス(サブジェクト)のアクセスを制御するカーネル仮想ファイルシステム(VFS)の重要な機能であるinode_permissionの例を使用したアクセス制御実装の図です。



 int inode_permission(struct inode *inode, int mask) { int retval; retval = sb_permission(inode->i_sb, inode, mask); if (retval) return retval; return __inode_permission(inode, mask); } int __inode_permission(struct inode *inode, int mask) { int retval; if (unlikely(mask & MAY_WRITE)) { /* * Nobody gets write access to an immutable file. */ if (IS_IMMUTABLE(inode)) return -EACCES; } retval = do_inode_permission(inode, mask); if (retval) return retval; retval = devcgroup_inode_permission(inode, mask); if (retval) return retval; return security_inode_permission(inode, mask); }
      
      







security_inode_permission



LSMフックは機能を終了し、ファイルシステムのinode



構造で表されるファイル(オブジェクト)へのプロセス(サブジェクト)アクセスをチェックする段階の最後の行です。 したがって、以前のすべてのチェックが正常に完了した場合にのみ機能します。これは、セキュリティモジュールが基本システムモデルを置き換えず、それを補完および拡張するだけのLSMコンセプトに対応します。



LSMの簡単な説明を要約すると、さまざまな期間におけるこのフレームワークの開発のダイナミクスを反映した興味深い統計を検討する価値があります。 これらの目的のために特に収集された情報は、カーネルバージョンに応じたLSMフックの総数を反映するグラフに表示されます。







まず、その数は注目に値します。 実際、バージョンv2.6.11



のカーネルに134個のフックがある場合、バージョン3.8のカーネルには既に189個のフックがあり、これはLSMのカーネルへの統合の深さの指標です。 第二に、フックの数の変化のダイナミクスは、バージョンv2.6.34



より前のLSMの急速な発展と、サブシステムの成熟度、相対的な完全性、およびアーキテクチャの安定性の指標である安定化を示します。



LSMを使用した埋め込みについて



基礎となるLinuxカーネルセキュリティモデルを拡張する手段として、LSMはその不可欠なコンポーネントではありません。 カーネル内のこのフレームワークの存在は、 CONFIG_SECURITY



構成変数の状態によって決定され、コンパイル段階で設定されます。 実際には、これは、ターゲットシステムのコアをLSMなしで構築できることを意味します。 それにもかかわらず、他の場合では、この機能を埋め込みに使用することは非常に実用的であるように見えますが、LSM自体のアーキテクチャに主に関連する独自の特性があります。 実際、埋め込み機能は、カーネルLSMフックでのカバレッジの完全性によって決まります。 言い換えれば、 それが提供されている場所でのみ統合することが可能です。



アーキテクチャに従って、LSMメカニズムの機能を保証するLinuxカーネルの主要なオブジェクトはsecurity_ops



ポインターです。これは、現在の(アクティブな)LSMモデルの記述子構造のアドレスを格納するカーネルメモリセルです。 さらに、モデルへのアクセスに関連するすべての操作はこのポインターを介して実行されるため、埋め込みにはこの値を制御できることが必要かつ十分であると主張できます。



カーネルモジュールによるLSMモデルの実装について言えば、LinuxカーネルへのLSMに課せられた制限の段階的導入に関連する機能があることに注意する価値があります。 したがって、セキュリティ上の理由から、システムの動作中に現在のセキュリティモデルを変更できるように、インターフェイスはカーネルから順次除外されました。







これらの機能は、埋め込み用のLSMの範囲を制限するように設計されていますが、実際には重要ではなく、それらを回避する方法があります。これについては後で説明します。



埋め込みの実用的な側面



カーネルv2.6.34



、システムの動作中に現在のセキュリティモデルを変更する定期的な機能v2.6.34



ないことに注意してください。 ただし、現在のモデルポインターsecurity_ops



アドレスを決定する方法が見つかった場合、この状況は克服できます。 簡単で信頼できる方法の1つは、カーネルコード、つまりこのポインターが使用される関数の1つを逆アセンブルすることです。 実際、 gdb



デバッガーを使用して64ビットx86システム用に取得したエクスポートされたsecurity_sb_copy_dataフック関数のリストを例として検討する価値があります。



 int security_sb_copy_data(char *orig, char *copy) { return security_ops->sb_copy_data(orig, copy); } EXPORT_SYMBOL(security_sb_copy_data);
      
      





 # gdb vmlinux /proc/kcore (gdb) x/7i security_sb_copy_data 0xffffffff811f61b0: push %rbp 0xffffffff811f61b1: mov %rsp,%rbp 0xffffffff811f61b4: data32 data32 data32 xchg %ax,%ax 0xffffffff811f61b9: mov 0x881690(%rip),%rax # 0xffffffff81a77850 0xffffffff811f61c0: callq *0x98(%rax) 0xffffffff811f61c6: pop %rbp 0xffffffff811f61c7: retq (gdb) x/s* 0xffffffff81a77850 0xffffffff81850fa0: "default"
      
      







ご覧のとおり、ディスパッチ命令callq *0x98(%rax)



は、 %rax



格納されたポインターを基準にしてオフセット0x98



sb_copy_data



ハンドラーsb_copy_data



を呼び出します。 つまり、 %rax



にはsecurity_ops



も含まれていsecurity_ops



。 したがって、このポインタを取得するには、この関数の先頭からmov 0x881690(%rip),%rax



を見つけるまでコマンドをスキャンし、目的の値を%rax



に読み込んでオペランドの簡単な分析を実行する必要があります。 、必要なポインターを取得します。 以下は、これをudis86アセンブラーを使用してx86で実行できるようにするコードです。



 static void * get_lsm_entry(void) { /* this one is exported */ return (void *)&security_sb_copy_data; } static struct security_operations ** get_lsm_sop(void) { ud_t ud; void * entry = get_lsm_entry(), * result = NULL; ud_initialize(&ud, BITS_PER_LONG, UD_VENDOR_ANY, entry, 128); while (ud_disassemble(&ud) && ud.mnemonic != UD_Iret) { if (ud.mnemonic == UD_Imov && \ ud.operand[0].type == UD_OP_REG && ud.operand[1].type == UD_OP_MEM) { #ifdef CONFIG_X86_64 result = entry + ud_insn_off(&ud) + ud_insn_len(&ud); #endif result = result + ud.operand[1].lval.sdword; break; } } return result; }
      
      







したがって、 security_ops



ポインターへのポインターを受け取ったら、このポインターの値を置き換えるだけで、アクティブなセキュリティモデルを独自の実装に置き換えることができます。 例として、 inode_permission



関数を置き換えるfakelsmプロジェクトのコードをinode_permission



ます。



おわりに



結論として、次のことを言ってください。 カーネルの外部でLSMを使用する際に順次導入される制限にもかかわらず、このフレームワークは興味深いものです。 カーネルに埋め込む絶好の機会を提供します。 ただし、その機能は、LSMフックでのカーネルコードのカバレッジの程度によって制限されることを理解する必要があります。 また、最新のカーネルは非常に多数ありますが、これらの制限は明らかです。



さらに、LSMアーキテクチャの概念自体は、単純で効率的であるため、安全ではないことが重要です。 実際、拡張カーネルセキュリティモデル全体は、単一のポインタsecurity_ops



基づいています!



読むために



  1. Linuxセキュリティモジュール
  2. udis86のドキュメント
  3. GrsecurityおよびRSBAC LSM批判



All Articles