事前の予想
自分のデスクトップにLinuxを使いはじめてから15年くらいです。
昔CD-R焼けなくて対応したときの記憶をたどると、たしかこんなかんじだったんじゃないかなと:
- /dev/cdrom あたりはrootユーザ、cdromグループになっている
- 使えるユーザはcdromグループに入っているから使える
確認と謎
自分のマシン(debian testingでinitはsystemdにしてます)でチェックするとこんなかんじ
dragon:~$ ls -l /dev/cdrom
lrwxrwxrwx 1 root root 3 May 2 03:07 /dev/cdrom -> sr0
dragon:~$ ls -l /dev/sr0
brw-rw----+ 1 root cdrom 11, 0 May 2 03:07 /dev/sr0
dragon:~$ idああcdromグループに入ってるわ…… ということでちょっと安心しましたが、長期間使っている環境なので油断できません。Fedoraマシンでみると
uid=1000(kankun) gid=1000(kankun) groups=1000(kankun),24(cdrom),25(floppy),29(audio),30(dip),44(video),
46(plugdev),105(scanner),109(bluetooth),111(netdev)
[kankun@snake ~]$ id uid=1000(kankun) gid=1000(kankun) groups=1000(kankun),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023cdromグループは別にあるもののユーザはcdromグループに入っていません。RHEL5,6,7betaもご同様。
しかしいずれの環境でも、普段からCD読み書きできています。気になります。
「ファイルシステムとしてmountするのはudisksあたりが頑張ってくれてるはずだから、直接デバイス読んでみよう」 less -f /dev/sr0 すると CD/DVDの内容が読めました。
brw-rw----+ 1 root cdrom 11, 0 May 2 03:07 /dev/sr0
という状態のデバイスをrootでもcdromグループでもない一般ユーザがアクセスできるのです。
一瞬頭に「???」が浮かびましたが「brw-rw----+」の最後に+があります。確認するとユーザにアクセスを許可するACLが設定されていました。
dragon:~$ getfacl /dev/sr0
getfacl: Removing leading '/' from absolute path names
# file: dev/sr0
# owner: root
# group: cdrom
user::rw-
user:kankun:rw-
group::rw-
mask::rw-
other::---
誰がこのACLを設定しているの?(タイトルでネタバレ)
# ここからの調査は最初RHEL6でやったんですが、より実装がためになるのでFedora20で調べなおして再現してます。なのでちょいフィクションです。このACLを設定したプログラムに心あたりがないわけです。自分で明示的に指定したこともありません。自分のマシンで自分の知らない権限設定がされているとかいやですね。しらべましょう。誰がこんな設定してるんでしょうか。ちょっと予想します。
- 今のログインユーザだから(pam?)
- インストーラで作成したアカウントだから何か特別に設定が書かれている(どこぞの設定ファイルがudevのルールから参照されてる?)
- GNOMEデスクトップユーザむけのなにかが動いている(udisksとか??)
やはりACLが設定されています。GNOMEは排除できました。
pamをうたがって別のvirtual terminal(VT)でrootでログインします。ACLの状態がかわりました。これでインストーラも排除できました。
getfacl: Removing leading '/' from absolute path names今ログインしてるユーザ全員に許可をだしているわけではなさそうです。一般ユーザのアカウントへの許可は消えてしまいました。最後にログインしたユーザーかな??
# file: dev/sr0
# owner: root
# group: cdrom
user::rw-
group::rw-
mask::rw-
other::---
VTきりかえてkankunアカウントでもういっぺん確認します。
getfacl: Removing leading '/' from absolute path namesやばい…… VT切り替えしかしてないのにACLが書き変わってる……。pamだけじゃこの動きはできません。予想が全滅してポルナレフごっことかやりたくなります。やっても話が進まないのでぐっと堪えます。
# file: dev/sr0
# owner: root
# group: cdrom
user::rw-
user:kankun:rw-
group::rw-
mask::rw-
other::---
ls -l /dev して同じように+がついているものをみると kvm, rfkill, video0 でも同様のACLが設定されていました。今デスクトップを目の前でつかっているアカウントがデスクトップ用途で使いやすいようにACLを変更する、という意図でやっていそうです。
こういうのたしかsystemdに含まれているlogindってひとが、ConsoleKitから引きついでました。
コンソール切り替えの情報とか監視してないかなー と、lsof -n |grep ttyとかしてみます。
systemd-l 683 root 5r REG 0,15 4096 4386 /sys/devices/virtual/tty/tty0/activeあった!
systemd-logindによるACL設定
logindが何やってるか知りませんが、こいつかこいつ経由で情報もらった誰かがACLいじっているはず。logindはsystemdのリポジトリにはいってます。
systemdのリポジトリでaclをgrepします。。。./src/login/logind-acl.c ってのがいました。
logind-acl.hで定義されている関数は、 devnode_acl、devnode_acl_all で、デバイスファイルからold_uidとして指定されたuidへの許可ACLを削除して、new_uidとして指定されたuidへの許可ACLを設定します。
devnode_acl_all関数の中で以下のディレクトリを探索してACL設定する対象となるデバイスを決定していました。udevによるタグで絞り込まれています。
dir = opendir("/run/udev/static_node-tags/uaccess");
このタグづけ対象デバイスは、logindのソースと同じディレクトリにある70-uaccess.rulesの中で定義されていて、たとえばCD/DVDについては以下のようなルールがありました。
# optical drivesこれでさっきのディレクトリに登場しそうです。
SUBSYSTEM=="block", ENV{ID_CDROM}=="1", TAG+="uaccess"
SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="4|5", TAG+="uaccess"
$ ls /run/udev/tags/uaccessしてました。b11:0 です。ブロックデバイスのmajor 11, minor 0ですね。
b11:0 c10:232 c116:1 c116:10 c116:11 c116:12 c116:2 c116:3 c116:33 c116:4 c116:5 c116:6 c116:7 c116:8 c116:9 c189:257 c189:258 c189:259 c189:260 c189:385 c21:2 c226:0 c81:0
systemd-logindがACL設定をuaccessタグがついているデバイスにおこなっていそうなことはわかりましたが、どういう契機で行われるかという疑問が残っています。もうひといきです。
1. ログイン時のACL設定
systemd-logindのソースと同じところにpam-module.cといういかにもあやしいのがいます。これをビルドするとpam_systemd.so というモジュールができてセッション作成時に systemd-logindへセッション作成依頼をdbus経由で投げつけます。systemd-logindがこの依頼を受けてセッション作成し、必要に応じてACL設定の変更をおこなっています。
2. VTの状態によるACL設定
systemd-logind内での関数呼び出し元をおいかけると、以下のようになっていて、VTの状態を反映したACL設定をおこなっていました。VT切り替え監視そのものもsystemd-logindが見ていてmanager_dispatch_console() がハンドラになっています。manager_dispatch_console()
-> seat_read_active_vt()
-> seat_active_vt_changed()
-> seat_set_active()
-> seat_apply_acls()
-> devnode_acl_all()
-> devnode_acl()
3. udevdによるACL設定
さらにlogindのソースとおなじディレクトリに追いてある73-seat-late.rules.in をみるとTAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"という記載があって、uaccessタグがついているデバイスがつながるとudev組み込みのuaccessという処理が走るように設定されています。これは systemdのsrc/udev/udev-builtin-uaccess.cで定義されていて、既にseatとユーザがひもづいている状態でデバイスを接続した時に、そのデバイスについてdevnode_acl()関数を呼びだしてACL設定をおこなうものでした。
まとめると以下のようになっていて、ログインしたとき、画面切り替えした時、既にログイン済みでデバイスをUSBなどで追加したときにちゃんとACLが設定できるようになっていました。
はじめの疑問にもどる
そして結局一般ユーザにCD, DVDを焼けないように設定したいという当初の質問がどうなったかというと……/lib/udev/rules.d/70-uaccess.rules を/etc/udev/rules.d にコピーしてCD用のエントリを消すと、uaccessタグが付与されなくなるのでまずは一般ユーザからCD, DVD焼けなくなる。 でも読む分にはudisks2がユーザ権限でmountしてくれるのでこれでOK。
この方法の問題点としては、70-uaccess.rules に将来の変更で対象となるデバイスが追加された場合にうまく反映されない点があるので手放しでおすすめはしづらいところ。アップデート時には注意が必要です。
systemd以前の世代だと類似のしくみがhal+ConsoleKitだったりudev+ConsoleKitで別途実装されているのでこの方法とちょっとかわります。「一般的にこのコマンド叩けばOK」という美しい方法はないのでした。なやましい……。
0 件のコメント:
コメントを投稿