NetBSD-10における所有者とグループを考慮したハードリンク作成制限機能

[NetBSD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/netbsd) 8日目の記事です。今日はNetBSD-10で追加されたハードリンクの作成制限機能を紹介しようと思います。

## ハードリンクの作成制限機能

NetBSD-10の[リリースノート](https://www.netbsd.org/releases/formal-10/NetBSD-10.0.html)を読んでいると、[secmodel_extensions(9)](https://man.netbsd.org/NetBSD-10.0/secmodel_extensions.9)に関連する以下のMIB(Management Information Base)エントリが[sysctl(8)](https://man.netbsd.org/NetBSD-10.0/sysctl.8)に追加されていました。

> Hardlink restrictions
>     Prevent hardlinks to files that the user does not own or has group access to.
>  　
>     To enable user ownership checks, set the sysctl(7) variable
>     security.models.extensions.hardlink_check_uid to a non-zero value.
>　
>     To enable group membership checks, set the sysctl(7) variable
>     security.models.extensions.hardlink_check_gid to a non-zero value.

ざっくりと翻訳すると以下のようになります。

> ハードリンクの制限
>     ユーザーが所有していない、またはグループアクセス権を持っていないファイルへのハードリンクを防止する。
>　
>     ユーザの所有権チェックを有効にするには、sysctl(7)変数のsecurity.models.extensions.hardlink_check_uidをゼロ以外の値に設定します。
>　
>     グループメンバーシップチェックを有効にするには、sysctl(7)変数のsecurity.models.extensions.hardlink_check_gidをゼロ以外の値に設定する。

どうやらハードリンクの作成において、所有者とグループをチェックする設定となっているようです。

## さっそく試してみる

### ユーザとグループの作成

事前準備として、確認用のユーザとグループを作成します。とりあえず `taro` と `hanako` というユーザを作成し、それぞれを `tarogroup` と `hanakogroup` に所属させます。

```sh
# # ユーザtaro,hanakoを作成する。
# useradd -g users -G wheel -m -s /usr/pkg/bin/bash taro
# useradd -g users -G wheel -m -s /usr/pkg/bin/bash hanako
#
# # それぞれのユーザにパスワードを設定する。
# passwd taro
# passwd hanako
#
# # グループ作成する。
# groupadd -g 2000 tarogroup
# groupadd -g 2001 hanakogroup
#
# # グループを確認する。
# egrep '^taro|^hanako' /etc/group
tarogroup:*:2000:taro
hanakogroup:*:2001:hanako
```

それぞれ以下のようなユーザとグループ設定になりました。

```sh
# # 所属しているグループを確認する。
# id taro
uid=1000(taro) gid=100(users) groups=100(users),0(wheel),2000(tarogroup)
# id hanako
uid=1001(hanako) gid=100(users) groups=100(users),0(wheel),2001(hanakogroup)
```

ハードリンク用のファイルを作成し、これで準備は完了です。

```sh
# sudo -i -u taro touch /tmp/taro_file
# sudo -i -u hanako touch /tmp/hanako_file
#
# ls -la /tmp
total 6
drwxrwxrwt   2 root    wheel   96 Dec 10 21:01 ./
drwxr-xr-x  21 root    wheel  512 Dec 10 20:50 ../
-rw-r--r--   1 hanako  wheel    0 Dec 10 21:01 hanako_file
-rw-r--r--   1 taro    wheel    0 Dec 10 21:01 taro_file
```

### ファイル所有者以外のハードリンク作成を制限する

まずはファイル所有者以外のハードリンク作成制限を試してみます。 `security.models.extensions.hardlink_check_*` のデフォルト値は以下のようになっており、ハードリンク作成は制限なく行えます。

```sh
# sysctl -a | grep hardlink_check
security.models.extensions.hardlink_check_uid = 0
security.models.extensions.hardlink_check_gid = 0
```

ユーザ `taro` が `hanako` の所有するファイルに対してハードリンクを作成できます。

```sh
# sudo -i -u taro ln /tmp/hanako_file /tmp/taro_file_hardlink02
# ls -l /tmp/taro_file_hardlink
-rw-r--r--  4 hanako  hanakogroup  0 Dec 10 21:01 /tmp/taro_file_hardlink
```

ここで `security.models.extensions.hardlink_check_uid` の値を変更し、ハードリンク作成時の所有者チェックを有効にしてみます。

```sh
# sysctl security.models.extensions.hardlink_check_uid
security.models.extensions.hardlink_check_uid = 0
#
# sysctl -w security.models.extensions.hardlink_check_uid=1
security.models.extensions.hardlink_check_uid: 0 -> 1
#
# sysctl -a | grep hardlink_check
security.models.extensions.hardlink_check_uid = 1
security.models.extensions.hardlink_check_gid = 0
```

ファイル所有者が `hanako` のファイルに対し、ユーザ `taro` がハードリンクを作成しようとするとエラーとして扱われるようになりました。

```sh
# sudo -i -u taro ln /tmp/hanako_file /tmp/taro_file_hardlink02
ln: /tmp/taro_file_hardlink02: Permission denied
```

### グループメンバ以外のハードリンク作成を制限する

次にグループメンバ以外のハードリンク作成制限を試してみます。ファイルの所有者とグループをそれぞれ `taro:tarogroup` と `hanako:hanakogroup` に設定します。

```sh
# cat /etc/group | egrep '^taro|^hanako'
tarogroup:*:2000:taro
hanakogroup:*:2001:hanako

# chown taro:tarogroup /tmp/taro_file
# chown hanako:hanakogroup /tmp/hanako_file

# # ファイルのグループを確認する。
# ls -l /tmp/ | grep file$
-rw-r--r--  3 hanako  hanakogroup  0 Dec 10 21:01 hanako_file
-rw-r--r--  1 taro    tarogroup    0 Dec 10 21:01 taro_file
```

`security.models.extensions.hardlink_check_*` がデフォルト値の状態でハードリンクを作成してみます。 `hanakogroup` のファイルに対し、ユーザ `taro` によるハードリンク作成が行えます。

```sh
# sysctl -a | grep hardlink_check
security.models.extensions.hardlink_check_uid = 0
security.models.extensions.hardlink_check_gid = 0
#
# sudo -i -u taro ln /tmp/hanako_file /tmp/taro_file_hardlink03
# ls -l /tmp/taro_file_hardlink03
-rw-r--r--  4 hanako  hanakogroup  0 Dec 10 21:01 /tmp/taro_file_hardlink03
```

ここで `security.models.extensions.hardlink_check_gid` の値を変更し、ハードリンク作成時のグループチェックを有効化します。

```sh
# sysctl -w security.models.extensions.hardlink_check_gid=1
security.models.extensions.hardlink_check_gid: 0 -> 1
#
# sysctl -a | grep hardlink_check
security.models.extensions.hardlink_check_uid = 0
security.models.extensions.hardlink_check_gid = 1
```

設定変更により、所属していないグループのファイルに対するハードリンク作成がエラーとして扱われるようになりました。

```sh
# sudo -i -u taro ln /tmp/hanako_file /tmp/taro_file_hardlink04
ln: /tmp/taro_file_hardlink04: Permission denied
```

## まとめ

NetBSD-10でついかされたハードリンク作成制限機能を試してみました。ファイルへのリンクはセキュリティ上の問題を発生させることがあり、OSレベルで制限をかけることでよりセキュアな構成にできそうです。

## 参考URL

 * [POS01-C. ファイルを操作するときにはリンクかどうかを確認する](https://www.jpcert.or.jp/sc-rules/c-pos01-c.html)(2020/06/16)