最近、openSUSE Leap 15.6から16.0に乗り換え作業をしていたら、nmhのincコマンドが
"inc: error zero'ing /var/mail/user1: Permission denied, continuing..."
というエラーを吐いて、ファイルをゼロクリアできないことに気付きました。色々と原因の可能性をつぶしていった結果、表題のような問題だと判明しました。丸一日付き合ってくれたCopilotくんに感謝していますが、色々とヒントをくれたものの、ズバリの回答は出せませんでした。
原因究明のヒントになった記事がこちらです。
ざっくり言うと、
# sysctl fs.protected_regular=0
にすると従来通り動作することが分かりました。
症状
以下のような条件で症状が出ました。
- /var/mail/user1 は owner=user1, group=user1grp で、group R/W permissionが付いている。すなわち -rw-rw---- (0660) の状態。
- incコマンドを実行するユーザは user2 で、user1grp がgroupsに含まれている。
(コマンドラインからは問題なく cp /dev/null /var/mail/user1 とかできる)
当初、様々なものを疑いましたが、どれも当たらず_('、3」∠)_
- incコマンドのバグ?
- メールスプールをNFSマウントしているので、NFSの問題か?
(NFSv4からNFSv3に変更しても変わらず) - ロック機能の問題?
- メールスプールのパーミッション、拡張パーミッションの問題?
(ファイルを /tmp に移してみてもダメ) - umaskがおかしい?
(umask 000 でもダメ) - AppArmorがブロックしている?
原因究明のためのツール
問題を絞り込むために、半日以上Copilotくんに相手をしてもらい、もらったサンプルコードを少し手直ししたのがこちら。
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void){
int fd = open("/tmp/user1", O_WRONLY|O_CREAT|O_NONBLOCK, 0666);
if(fd==-1){ perror("open"); return 1; }
close(fd);
puts("open ok"); return 0;
}
実行すると open: Permission denied になります。
次のコマンドにより、EACCES になっていることが分かりました。
(ユーザ user2 として実行)
$ strace -f -s 200 -e trace=process,openat,open,stat,access -o /tmp/open-c-full.strace ./open_test$ sed -n '1,200p' /tmp/open-c-full.strace | egrep 'setgroups|setgid|setuid|setreuid|setresuid|openat|open|access|stat' | tail -n 80
出力はこんな感じ ↓
5242 execve("./open_test", ["./open_test"], 0x7ffc4ea90ed8 /* 66 vars */) = 0
5242 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
5242 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
5242 openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
5242 openat(AT_FDCWD, "/var/tmp/user1", O_WRONLY|O_CREAT|O_NONBLOCK, 0666) = -1 EACCES (Permission denied)
openのところで O_CREAT を外すと、open ok になります。つまり、上書きはグループパーミッションで正常にできるということです。
解決にむけて
ここまで相当の時間を費やしたのですが、ファイルサーバとして既に動いていたので、AppArmorを止めてみるところまでは試しませんでした。
翌朝、前述した記事を見つけて試してみたところ、見事に open ok になりました。
セキュリティ強化のために Linux に導入された fs.protected_regular が悪さをしていたようです。
しかし、これをオフにしたくはないので、別の方法がないかどうかさらに調査を進めます。
/tmp や /var/mail にスティッキービットが立っているのが気になって、Copilotくんに「影響ある?」と聞いてみたところ、きっぱりと否定されてしまいました。
しかし、どうしても気になったので、別のディレクトリを 0777 で掘って、そこで試してみたところ、open ok になるじゃあありませんか!chmod 1777すると、失敗します。
再度Copilotくんに聞いてみたところ、このようなお答えでした ↓
What to do (recommendations)
Don’t remove the sticky bit on a multi-user shared directory like /tmp — that would be insecure.
Preferred secure fixes while keeping fs.protected_regular=1 and keeping the sticky bit:
Make the file owned by the writer process: sudo chown <writer-user>:<writer-group> /var/mail/user1 This is the simplest correct fix if that ownership change is acceptable.
Avoid O_CREAT/O_TRUNC as a non-owner — write to a temp file and atomically rename into place: tmp=$(mktemp /var/mail/user1.tmp.XXXXXX)
はい、要するに /var/mail の中で group permission でゼロクリアするのは無理そうということです。
まとめ
こんな条件で問題が発生していたことが分かりました。
- 最近のLinuxで、fs.protected_regular が有効になっている。
- ディレクトリにsticky bitが立っている。
- プログラム内で既存ファイルをopenするところに、O_CREAT が含まれている。(おそらくO_TRUNCも同様)
- openしようとするファイルのownerは、プログラムを実行するownerと異なるが、ファイルにgroup r/w permissionが付いていて、実行者はそのグループに属している (他のプログラムでは正常に読み書きできることがある)。
原因は分かったものの、完全な解決には至りませんでした。
だ、だれかー_(゚。3」∠)_












