UID、GID、capabilitycapability従来rootへまとめて与えられていた権限を、操作の種類ごとに分けた単位。などをcredentialcredentialカーネルがプロセスの身元と権限を確認するために保持する情報。として保持します。
要求を許可する場所と、カーネル自身を守る仕組み
アクセス制御とカーネル防御
Linuxは、誰が何へアクセスするのかを操作ごとに確認します。アプリが侵害された後に到達できる処理を減らし、カーネル内の脆弱性を悪用しにくくする防御も重ねます。
プロセスの要求から、カーネル内部の実行までを分けて見る
UID・GIDとファイルの権限から始まる
プロセスがファイルを開いたり、別のプロセスへシグナルシグナルカーネルや別のプロセスが、プロセスへイベントの発生を非同期に知らせる仕組み。を送ったりするとき、カーネルは要求したプロセスの身元と権限を調べます。LinuxはUID、GID、補助グループ、capability、LSMLSMカーネル内の操作へ、アクセス制御を呼び出すhookを用意する共通の枠組み。が使うセキュリティ属性などをプロセスに保持します。これらをまとめたカーネル内の情報をcredentialcredentialカーネルがプロセスの身元と権限を確認するために保持する情報。と呼びます。
ファイル側には所有者のUIDとGID、所有者・group・その他に対するread・write・executeのmode、必要に応じてACLがあります。プロセスとファイルの情報を照合する基本のアクセス制御をDACDAC所有者が設定できる権限をもとに、対象へのアクセスを確認する仕組み。と呼びます。
credential
/srv/report.txt
rootの強い権限を操作の種類ごとに分ける
従来のUnixでは、effective UID 0のプロセスを特権プロセスとして扱いました。Linuxのcapabilitycapability従来rootへまとめて与えられていた権限を、操作の種類ごとに分けた単位。は、その権限を独立した単位へ分けます。
CAP_NET_BIND_SERVICE1024未満のportへbindするCAP_SYS_TIMEシステム時刻を変更するCAP_SYS_PTRACE別のプロセスをtraceするCAP_BPF特権が必要なBPF操作を行う
seccomp、DAC、LSMは別の場所を確認する
アプリは、ファイル、通信、プロセスなどをカーネルに操作してもらうため、システムコールを使います。プロセスが使えるシステムコールと引数をfilterで絞る仕組みをseccompseccompプロセスが呼び出せるシステムコールと引数をfilterで制限する仕組み。と呼びます。
通常のファイル権限に加えて、システム全体やアプリ固有の方針も確認できます。カーネル内の操作箇所へ、アクセス制御を呼び出す共通のhookを用意する枠組みがLSMです。
openat()でファイルを指定する
プロセスは、ディレクトリを示すファイルディスクリプタ、パス、読み書きの指定をカーネルへ渡します。
LSMのhookでは、有効になっているsecurity moduleがそれぞれの方針を確認します。現在のLinuxでは複数のLSMを重ねて使う構成もあります。
SELinux
プロセスとファイルなどへsecurity contextを付け、typeなどと操作の組み合わせをpolicyで許可します。
AppArmor
実行するプログラムへprofileを対応づけ、path、network、capabilityなどの操作を記述します。
Landlock
一般ユーザーのプロセスもrulesetを作り、自分と子孫がアクセスできる対象を狭められます。
seccompとLandlockは異なる対象を制限する
seccomp
seccompseccompプロセスが呼び出せるシステムコールと引数をfilterで制限する仕組み。は、システムコール番号と値として渡された引数をfilterへ通します。許可、errnoの返却、プロセス終了、trace、ユーザー空間への通知などの結果を返せます。
socket()mount()ioctl()bpf()Landlock
LandlockLandlock一般ユーザーのプロセスも、自分と子孫のアクセス権を狭められるLSM。は、ファイル階層、network port、signal、UNIX domain socketなど、カーネルが管理する対象へのアクセスをrulesetで狭めます。
/usr/**TCP :443signal scopeUNIX socketno_new_privsno_new_privsexecve後も、現在より強い権限を得ないとカーネルへ約束する属性。を設定すると、その後のexecve()でset-user-IDやfile capabilityから新しい権限を得ないことをカーネルへ約束します。一般ユーザーがseccomp filterやLandlockを適用するときの前提として使われます。
対象の追加と、実運用に必要な機能の整備が続く
初期のLandlockはファイル階層を制限しました。その後、TCP、デバイスのioctl、signal、UNIX domain socket、UDPへ対象が広がり、拒否理由のaudit log、同じプロセスに属する全スレッドへの一括適用、ログ抑制も加わりました。アプリは実行中のカーネルからABI versionABIコンパイル済みのプログラムとOSやlibraryが、binary levelでやり取りするための約束。を取得し、そのカーネルが扱える権限だけでrulesetを作ります。
ファイル
実行、読み書き、作成、削除をディレクトリツリーごとに制限
移動とlink
ディレクトリをまたぐrenameとlinkを個別に許可
truncate
ファイルの長さを変更する操作を制限
TCP
bindとconnectをport番号で制限
デバイスのioctl
character deviceとblock deviceのioctlを制限
signalとIPC
signalとabstract UNIX socketの範囲を分ける
audit
拒否した操作と理由をaudit logへ記録
全スレッド
同じプロセスに属する全スレッドへ同時にrulesetを適用
UNIX socket
pathname UNIX socketへの接続を制限
UDPとログ抑制
UDPのbindと送信先、audit logを抑える対象を指定
横へスクロールしてABI 10まで確認できます。
攻撃の入口、情報漏えい、メモリ破壊、制御の乗っ取りを分けて防ぐ
カーネルへ到達できる入力を減らしても、使う機能のコードには脆弱性があり得ます。Linuxのself-protectionは、バグを悪用して権限を奪うまでに必要な条件を増やし、途中で検出または阻止する仕組みを重ねます。
脆弱な処理へ入力を届ける
seccomp、権限確認、未使用機能の無効化
配置や内部データを知る
KASLR、kernel pointerの公開制限、初期化
境界外へ書き込み、状態を壊す
stack canary、FORTIFY、read-only data、strict RWX
命令の流れを乗っ取る
KCFI、kernel IBT、SMEP・SMAP、PAN
KASLR
起動ごとにカーネルの配置を変えます。配置が漏れれば効果が下がるため、情報漏えい対策と組み合わせます。
KPTI
Meltdownへの対策として、ユーザー空間を実行するときに見えるカーネル側のページテーブルを最小限へ分けます。
カーネルはIBT、ユーザー空間はshadow stack
現在の64-bit x86 Linuxでは、カーネルの間接分岐をIBTで検査します。shadow stackはユーザー空間のreturn addressを保護する機能として提供されています。
stack canary
関数のstack上で、local variableとreturn addressの間に検査用の値を置きます。関数から戻る前に値が変わっていないかを確認します。
FORTIFY_SOURCE
compilerがbufferの大きさを判断できる箇所で、文字列やmemoryのcopyが範囲を越えないかを検査します。
strict RWX
kernel codeを実行可能かつ書き込み不可にし、書き換えられるdataを実行不可にします。read-only dataへの書き込みも防ぎます。
KCFI
function pointerを使う間接呼び出しで、呼び出し先のfunction typeが期待した型と合うかを検査します。
kernel IBT
x86 CPUが、間接的なcallやjumpの行き先として許された位置かを確認します。
SMEP・SMAP / PAN
kernelがuser spaceのmemoryを意図せず実行したり、読み書きしたりする経路をCPUの機能で制限します。
利用できる防御はCPUアーキテクチャ、コンパイラ、カーネル設定によって変わります。KASLRだけ、stack canaryだけで完結するものではなく、異なる段階を扱う機能を組み合わせます。
kernel moduleの署名を読み込み時に確認する
kernel moduleはカーネル空間で動くコードです。module signingは、moduleへ暗号学的な署名を付け、読み込み時にカーネルが検証します。CONFIG_MODULE_SIG_FORCEまたはmodule.sig_enforce=1を使う構成では、信頼する公開鍵で検証できないmoduleを拒否します。