一覧OS-10-3 · Linuxの利用者と権限

ACLとLinuxケーパビリティ

個別利用者のACLをmaskで評価し、特定の特権だけを持つプロセスの許可範囲を確かめます。

説明

通常の所有者・グループ・その他だけでは、特定の利用者だけへ別の権限を与えにくい場合があります。POSIX ACLは名前付き利用者やグループの項目を追加し、maskがその一部の有効権限の上限になります。capabilityはLinuxの特権操作を種類ごとに分けた仕組みで、ファイルACLとは役割が異なります。

aya向けにrwを指定したACLから、maskによって実際にはrだけになる状態を計算します。maskや所有者を変えて判定を比べた後、CAP_NET_BIND_SERVICEだけを有効にしたプロセスについて、許される操作を確認します。

1名前付き利用者とmask

r=4、w=2、x=1です。所有者は2001、ayaはUID1000とし、aya向け項目はrw=6、maskはr=4にします。

acl = {'owner_uid':2001,'owner':6,'users':{1000:6},'group':4,'mask':4,'other':0}ACLの各項目; print(acl)設定内容

ayaの項目だけを見るとrwですが、名前付き利用者にはmaskも適用されます。所有者の項目とotherはこのmaskの上限対象ではありません。

2設定したrwと有効なr

ayaの指定ビットとmaskをANDします。

effective = acl['users'][1000] & acl['mask']名前付き項目とmaskの交差; print(effective,bool(effective&4),bool(effective&2))有効ビット・読取り・書込み

4・True・Falseです。getfaclでは名前付き利用者にrwと表示されても、effective:r--のように実際の上限が併記される場合があります。設定された項目と有効権限を両方確認します。

3maskを広げると影響する項目

maskをrw=6へ広げます。aya向け項目と所有グループ項目の有効値をそれぞれ計算します。

acl['mask'] = 6上限をrwへ; print(acl['users'][1000]&acl['mask'],acl['group']&acl['mask'])利用者とグループの有効値

ayaは6、グループは4です。maskは上限なので、元のグループ項目にないwを追加しません。一方で、別の名前付き項目が以前からwを持っていれば、その項目も新たに有効になる可能性があります。maskの変更は一人だけに作用する設定ではありません。

4所有者に一致する場合の先行判定

ayaが所有者になった場合を計算します。所有者の項目はr=4、名前付きayaはrw=6として、所有者一致を先に判定します。

acl['owner_uid'] = 1000; acl['owner'] = 4所有者とその権限; uid = 1000判定する主体; selected = acl['owner'] if uid==acl['owner_uid'] else acl['users'][uid]&acl['mask']所有者を先に判定; print(selected)選んだ権限

4が選ばれます。所有者へ一致した場合はowner項目で判断し、名前付き利用者のより広い権限へ切り替えません。この式は所有者または名前付き利用者に一致する今回の条件に限定した判定です。

5capabilityの許可集合と有効集合

次はプロセスの特権です。permittedは有効にできる上限、effectiveは権限検査で実際に使う集合です。CAP_NET_BIND_SERVICEをpermittedだけへ入れます。

permitted = {'CAP_NET_BIND_SERVICE'}有効にできる集合; active = set()現在の有効集合は空; print('CAP_NET_BIND_SERVICE' in permitted,'CAP_NET_BIND_SERVICE' in active)保持と有効状態

True・Falseです。保持して有効化できることと、現在の権限検査で使われることは別です。inheritable・ambient・boundingなどの集合もあり、exec前後の引継ぎには別の規則が適用されます。

6一つの特権を有効にする

permittedにある特権をeffectiveへ加えます。CAP_NET_BIND_SERVICEは保護された低位ポートへのbindに関わります。ここではその境界を1024とするネットワーク名前空間です。

active.add('CAP_NET_BIND_SERVICE')保持している特権を有効化; port = 80保護されたポート; allowed = port>=1024 or 'CAP_NET_BIND_SERVICE' in activeこの特権の要否; print(allowed)必要条件を満たすか; print('CAP_DAC_OVERRIDE' in active)ファイル権限を迂回する特権は別

ポート80への必要な特権は満たしますが、CAP_DAC_OVERRIDEはFalseです。低位ポート用の権限から、任意のファイルの読書き権限までは得られません。実際のbindはアドレス使用中など別の条件でも失敗するため、Trueは全処理の成功保証ではありません。

7上限で削られた特権とexecの失敗

実行ファイルが付与しようとする二つの特権を、bounding setで制限する例です。引継ぎ集合とambientは空、非rootでのexec、ファイルのeffectiveフラグありという条件を使います。

file_permitted = {'CAP_NET_BIND_SERVICE','CAP_CHOWN'}ファイル側の付与候補; bounding = {'CAP_NET_BIND_SERVICE'}上限集合; after_permitted = file_permitted & bounding上限との交差; after_effective = set(after_permitted)effectiveフラグがある条件; print(sorted(after_permitted),sorted(after_effective))計算上の集合; missing = file_permitted-after_permitted要求から欠落した特権; print('EPERM' if missing else 'exec permitted')effectiveフラグ付き実行ファイルの検査

計算上の集合にはCAP_NET_BIND_SERVICEだけが残ります。この条件ではファイルのeffectiveフラグが立っており、要求したCAP_CHOWNが欠落するため、capability-dumbバイナリ向けの安全性検査でexecはEPERMになります。付与予定の集合の計算と、実行が許されるかの検査を両方確認します。

8ACLと特権のどこで許可が出るか

ファイルの通常のアクセス権と、対象操作を迂回する特権があるかを別々の列にします。ここではACLが拒否する通常ファイルの読取りを例にします。

acl_read = FalseACLによる読取り許可なし; override = 'CAP_DAC_OVERRIDE' in active対象操作の迂回権限; print(acl_read,override,acl_read or override)個別の条件とこの範囲の結論

False・False・Falseです。ネットワーク用の特権を有効にしても、このファイルを読める根拠にはなりません。さらにLinux Security Moduleなど他の制御もあるため、一つの許可要因を確認しただけで全アクセス制御の結果を断定しないようにします。

まとめ

名前付きACL項目の設定値はmaskで制限され、所有者には先行する判定があります。

capabilityは種類ごとの特権で、permittedとeffectiveも別の集合です。

ACL・capability・exec時の引継ぎ・追加のアクセス制御を、それぞれの適用条件で確認します。

操作履歴と解説を振り返れます。

Python 3

1
acl = {'owner_uid':2001,'owner':6,'users':{1000:6},'group':4,'mask':4,'other':0}; print(acl)
{'owner_uid': 2001, 'owner': 6, 'users': {1000: 6}, 'group': 4, 'mask': 4, 'other': 0}
2
effective = acl['users'][1000] & acl['mask']; print(effective,bool(effective&4),bool(effective&2))
4 True False
3
acl['mask'] = 6; print(acl['users'][1000]&acl['mask'],acl['group']&acl['mask'])
6 4
4
acl['owner_uid'] = 1000; acl['owner'] = 4; uid = 1000; selected = acl['owner'] if uid==acl['owner_uid'] else acl['users'][uid]&acl['mask']; print(selected)
4
5
permitted = {'CAP_NET_BIND_SERVICE'}; active = set(); print('CAP_NET_BIND_SERVICE' in permitted,'CAP_NET_BIND_SERVICE' in active)
True False
6
active.add('CAP_NET_BIND_SERVICE'); port = 80; allowed = port>=1024 or 'CAP_NET_BIND_SERVICE' in active; print(allowed); print('CAP_DAC_OVERRIDE' in active)
True
False
7
file_permitted = {'CAP_NET_BIND_SERVICE','CAP_CHOWN'}; bounding = {'CAP_NET_BIND_SERVICE'}; after_permitted = file_permitted & bounding; after_effective = set(after_permitted); print(sorted(after_permitted),sorted(after_effective)); missing = file_permitted-after_permitted; print('EPERM' if missing else 'exec permitted')
['CAP_NET_BIND_SERVICE'] ['CAP_NET_BIND_SERVICE']
EPERM
8
acl_read = False; override = 'CAP_DAC_OVERRIDE' in active; print(acl_read,override,acl_read or override)
False False False

演習完了