Linuxセキュリティモジュールの作成

Linux Security Modules(LSM)は、Linuxのさまざまなセキュリティモデルのサポートを追加するフレームワークです。 Linuxバージョン2.6以降、LSMはカーネルの一部です。 現在、SELinux、AppArmor、Tomoyo、およびSmackセキュリティモジュールは、公式コアに「常駐」しています。

モジュールは、「ネイティブ」のLinuxセキュリティモデルである随意アクセス制御(DAC)と並行して動作します。 LSMチェックは、DACによって承認されたアクションに対して呼び出されます。

LSMメカニズムを適用するには多くの方法があります。 ほとんどの場合、これは必須アクセス制御の追加です(たとえば、SELinuxの場合)。 さらに、独自のセキュリティモデルを考え出し、モジュールとして実装し、フレームワークを使用して簡単に実装できます。 たとえば、特別なUSBデバイスがある場合にシステム内のアクションに権限を与えるモジュールの実装を検討してください。

図を見て、LSMフックがどのように機能するかを理解してみましょう(例としてオープンシステムコールを使用)。



複雑なことは何もありません。 LSMの主なタスクは、カーネルモジュールへのアクセスを制御するメカニズムをセキュリティモジュールに提供することです(オブジェクトにアクセスする直前にフックがカーネルコードに挿入されます)。 カーネルが内部オブジェクトにアクセスする前に、LSMが提供する検証機能が呼び出されます。

言い換えれば、LSMにより、モジュールは「サブジェクトSはOBJコアの内部オブジェクトに対してOPアクションを実行できますか?」という質問に答えることができます。
これはとてもクールです。 これは、ほとんどのポインターをフックするsys_call_tableではありません。

ドラフトセキュリティモジュールを記述することから始めるのが妥当です。 彼は非常に謙虚で、すべての点でDACに同意します。 必要なソースは、カーネルソースのセキュリティディレクトリにあります。

ソースを掘る

/ linux / security.hをインクルードします(カーネルバージョン2.6.39.4のソースコードがあります)。 ここで最も重要なことは、強力なsecurity_ops構造です。
以下にその一部を示します。

struct security_operations { char name[SECURITY_NAME_MAX + 1]; int (*ptrace_access_check) (struct task_struct *child, unsigned int mode); int (*ptrace_traceme) (struct task_struct *parent); int (*capget) (struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted); … }; 


これは、セキュリティモジュールがチェックを実行するために使用できる、事前定義され文書化されたコールバック関数のリストです。 デフォルトでは、これらの関数はほとんどの場合、0を返し、それによりすべてのアクションが許可されます。 しかし、POSIXセキュリティモジュールを使用する人もいます。 これらはCommon Capabilities関数であり、ファイルsecurity / commoncap.cにあります。

この場合、include / linux / security.cからの次の関数が重要です。

  /** * register_security –     . * @ops:    security_options,   * . * * This function allows a security module to register itself with the * kernel security subsystem. Some rudimentary checking is done on the @ops * value passed to this function. You'll need to check first if your LSM * is allowed to register its @ops by calling security_module_enable(@ops). * *       ,   .  *   0. */ int __init register_security(struct security_operations *ops) { if (verify(ops)) { printk(KERN_DEBUG "%s could not verify " "security_operations structure.\n", __func__); return -EINVAL; } if (security_ops != &default_security_ops) return -EAGAIN; security_ops = ops; return 0; } 


空白を書く

配布キットBackTrack 5 R1(カーネルバージョン2.6.39.4)を手元に持っています。 SELinux(ディレクトリ/ security / selinux /)などの既製のセキュリティモジュールを見てください。 その主なメカニズムは、hooks.cファイルに記述されています。 このファイルに基づいて、新しいセキュリティモジュールのスケルトンを作成しました(少し後から、何か興味深いものを作成します)。

巨大なsecurity_ops構造体を関数へのポインタで埋めます。 これを行うには、すべての関数がselinuxをモジュールの名前(この例ではPtLSM)に置き換えるだけで十分です。 すべての関数の本体を編集します。voidemptyを返し、intは0を返す必要があります。結果は何もしないLSMであり、ネイティブの防御メカニズムが許可するすべてを許可します。 (モジュールのソースコード: pastebin.com/Cst0VVQh )。

少し悲しい後退。 バージョン2.6.24以降、セキュリティ上の理由から、カーネルはセキュリティモジュールをロード可能なカーネルモジュール(Linux Kernel Module、LKM)として記述するために必要な文字のエクスポートを停止しました。 たとえば、モジュールとそのフックを登録できるregister_security関数は、エクスポートから削除されました。 したがって、カーネルをモジュールにアセンブルします。

PtLSMモジュールの名前でディレクトリを作成します:/usr/src/linux-2.6.39.4/security/ptlsm/。
モジュールをビルドするには、次の手順を実行します。

1. Makefileを作成します。

obj-m:= ptlsm.o

2. Kconfigファイルを作成します。

構成SECURITY_PTLSM
bool「ポジティブプロテクション」
デフォルトn
助けて
このモジュールは、前向きな方法では何もしません。

この質問に答える方法がわからない場合は、Nと答えてください。


3. / security / Makefileおよび/ security / Kconfigを編集します。これにより、全世界が新しいモジュールを認識します。 他のモジュールと同様に、行を追加します。

PtLSMが追加された私のファイル:
1)Makefile-pastebin.com/k7amsnQK
2)Kconfig-pastebin.com/YDsPBGAz

次に、カーネルソースのあるディレクトリでmenuconfigを作成し、[セキュリティオプション]項目で[PtLSM]を選択します。



次に、make、modules_install、make installを実行します。 モジュールはカーネルに配置され、dmesgユーティリティを使用して、ログに書き込まれた内容を確認できます。

超クールなモジュールを書く

それでは、モジュールを超クールにしましょう! 指定されたベンダーIDと製品IDのUSBデバイスが接続されていない場合、モジュールはコンピューター上で何もしないようにします(私の例では、これらはGalaxy S IIの電話IDです)。



プロセスにファイルを作成する機能があるかどうかを確認するptlsm_inode_create関数の本体を変更しました。 関数が「より高い権限のデバイス」を見つけた場合、実行する許可を与えます。 他のアクションでも同様のチェックを実行できます。

 static int ptlsm_inode_create(struct inode *dir, struct dentry *dentry, int mask) { if (find_usb_device() != 0) { printk(KERN_ALERT "You shall not pass!\n"); return -EACCES; } else { printk(KERN_ALERT "Found supreme USB device\n"); } return 0; } 


さて、find_usb_device関数を書くといいでしょう。 彼女はシステム内のすべてのUSBデバイスを分析し、目的のIDを持つUSBデバイスを探します。 USBデバイス上のデータは、ルートがルートハブデバイスと呼ばれるツリーの形式で保存されます。 すべてのルートのリストは、usb_bus_listバスリストにあります。

 static int find_usb_device(void) { struct list_head* buslist; struct usb_bus* bus; int retval = -ENODEV; mutex_lock(&usb_bus_list_lock); for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { bus = container_of(buslist, struct usb_bus, bus_list); retval = match_device(bus->root_hub); if (retval == 0) { break; } } mutex_unlock(&usb_bus_list_lock); return retval; } 


最後に、ベンダーIDと製品IDをチェックするmatch_device関数。

 static int match_device(struct usb_device* dev) { int retval = -ENODEV; int child; if ((dev->descriptor.idVendor == vendor_id) && (dev->descriptor.idProduct == product_id)) { return 0; } for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { retval = match_device(dev->children[child]); if (retval == 0) { return retval; } } } return retval; } 


USBを使用するには、いくつかのヘッダーを接続します。

 #include <linux/usb.h> #include <linux/usb/hcd.h> 


モジュールを挿入する手順を繰り返し、コンピューターを使用するためにクールな電話を購入します。

Source: https://habr.com/ru/post/J144014/


All Articles