この記事はRustその2 Advent Calendar 2019の14日目の記事です。
本記事ではRustでLinuxのケーパビリティを操作する方法を紹介します。
ケーパビリティとは
ケーパビリティはLinuxのスーパーユーザー(rootなど)がもっている権限をいくつかのグループに分割したものです。Linuxではsetuid
などで特権が必要な操作に対してスーパーユーザーと同様の権限を与える事も可能ですが、それでは操作に必要な権限だけでなくスーパーユーザーがもつ権限を全て与える事になるので、必要以上の権限を与える事になってしまいます。
そういった時にケーパビリティを使えば全ての権限を与えるのではなく、操作に必要な権限のみを渡す事ができます。
詳細を知りたい方はcapabilities(7)などで確認してもらえればと思います。
環境
- OS: Ubuntu 16.04(kernel 4.4.0)
- Rust: 1.38.0
Rustでケーパビリティの操作
今回、Rustでケーパビリティを操作するに当たってcaps-rsというcrate
を使います。あとcapabilityの操作には関係しませんが、Linuxの操作をするにあたって便利なnix-rustというcrate
も同じく使います。
[dependencies]
caps = "0.3.3"
nix = "0.16.0"
前提として以下に記載する操作は基本的に一般ユーザーで行います。
ケーパビリティの確認
まず、自分自身のケーパビリティを確認してみます。
extern crate caps;
use caps::CapSet;
fn main() {
let cur = caps::read(None, CapSet::Permitted).unwrap();
println!("-> Current permitted caps: {:?}.", cur);
}
これは自分自身のPermitted
と呼ばれるケーパビリティセットの内容を表示します。
基本的にはケーパビリティは表示されないはずです(もちろん環境によります)sudo
などを使用してスーパーユーザーの権限で実行すると全てのケーパビリティが表示されます。
// 一般ユーザーで実行
$ cargo run
-> Current permitted caps: {}.
// スーパーユーザーの権限で実行(ケーパビリティが多く表示されるので一部のみ記載)
$ sudo cargo run
-> Current permitted caps: {CAP_LINUX_IMMUTABLE, CAP_SETGID, CAP_NET_BROADCAST, CAP_SYS_NICE, ...}.
ケーパビリティセットはPermitted
以外にもありますが同様の方法で確認する事ができます。
ケーパビリティの削除
caps-rs
を使えば簡単に特定のケーパビリティを削除する事ができます。
ここではファイルの所有者とグループを変更するchown
を例に取っていきたいと思います。
chown
ではCAP_CHOWN
というケーパビリティが必要になります。
まず最初はケーパビリティを削除せずにRustからchown
を実行してみます。
extern crate caps;
extern crate nix;
use caps::{CapSet, Capability};
use nix::unistd::{chown, Gid, Uid};
fn main() {
let cur = caps::read(None, CapSet::Effective).unwrap();
println!("-> Current effective caps: {:?}.", cur);
// スーパーユーザーに所有者とグループを変更
chown("test.txt", Some(Uid::from_raw(0)), Some(Gid::from_raw(0))).unwrap();
}
// test.txt作成
$ touch test.txt
$ ls -al
drwxrwxr-x 5 vagrant vagrant 4096 Dec 14 06:48 ./
drwxrwxr-x 6 vagrant vagrant 4096 Dec 14 01:40 ../
-rw-rw-r-- 1 vagrant vagrant 5667 Dec 14 03:33 Cargo.lock
-rw-rw-r-- 1 vagrant vagrant 252 Dec 14 03:33 Cargo.toml
drwxrwxr-x 6 vagrant vagrant 4096 Dec 14 01:40 .git/
-rw-rw-r-- 1 vagrant vagrant 19 Dec 14 01:40 .gitignore
drwxrwxr-x 2 vagrant vagrant 4096 Dec 14 07:13 src/
drwxrwxr-x 3 vagrant vagrant 4096 Dec 14 01:46 target/
-rw-rw-r-- 1 vagrant vagrant 0 Dec 14 06:48 test.txt
// 一般ユーザーで実行(エラー)
$ cargo run
-> Current permitted caps: {}.
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Sys(EPERM)', src/libcore/result.rs:1084:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
// スーパーユーザーの権限で実行
$ sudo cargo run
-> Current permitted caps: {CAP_LINUX_IMMUTABLE, CAP_SETGID, CAP_NET_BROADCAST, CAP_SYS_NICE, ...}.
$ ls -al
drwxrwxr-x 5 vagrant vagrant 4096 Dec 14 06:48 ./
drwxrwxr-x 6 vagrant vagrant 4096 Dec 14 01:40 ../
-rw-rw-r-- 1 vagrant vagrant 5667 Dec 14 03:33 Cargo.lock
-rw-rw-r-- 1 vagrant vagrant 252 Dec 14 03:33 Cargo.toml
drwxrwxr-x 6 vagrant vagrant 4096 Dec 14 01:40 .git/
-rw-rw-r-- 1 vagrant vagrant 19 Dec 14 01:40 .gitignore
drwxrwxr-x 2 vagrant vagrant 4096 Dec 14 07:13 src/
drwxrwxr-x 3 vagrant vagrant 4096 Dec 14 01:46 target/
-rw-rw-r-- 1 root root 0 Dec 14 06:48 test.txt
test.txt
を配置してsudo cargo run
で実行するとtext.txt
の所有者とグループがスーパーユーザー(root)に変わります。
(cargo run
で一般ユーザーの権限で実行するとCAP_CHOWN
のケーパービリティを持っていないので権限がなくエラーとなります)
次にCAP_CHOWN
の権限を削除した上でchown
を実行させてみます。
extern crate caps;
extern crate nix;
use caps::{CapSet, Capability};
use nix::unistd::{chown, Gid, Uid};
fn main() {
let cur = caps::read(None, CapSet::Effective).unwrap();
println!("-> Current effective caps: {:?}.", cur);
// CAP_CHOWNをEffectiveケーパビリティセットから削除
caps::drop(None, CapSet::Effective, Capability::CAP_CHOWN).unwrap();
let cur = caps::read(None, CapSet::Effective).unwrap();
println!("-> Current effective caps: {:?}.", cur);
// スーパーユーザーに所有者とグループを変更
chown("test.txt", Some(Uid::from_raw(0)), Some(Gid::from_raw(0))).unwrap();
}
// test.txtを作り直し
$ sudo rm test.txt
$ touch test.txt
$ ls -al
drwxrwxr-x 5 vagrant vagrant 4096 Dec 14 07:20 ./
drwxrwxr-x 6 vagrant vagrant 4096 Dec 14 01:40 ../
-rw-rw-r-- 1 vagrant vagrant 5667 Dec 14 03:33 Cargo.lock
-rw-rw-r-- 1 vagrant vagrant 252 Dec 14 03:33 Cargo.toml
drwxrwxr-x 6 vagrant vagrant 4096 Dec 14 01:40 .git/
-rw-rw-r-- 1 vagrant vagrant 19 Dec 14 01:40 .gitignore
drwxrwxr-x 2 vagrant vagrant 4096 Dec 14 07:19 src/
drwxrwxr-x 3 vagrant vagrant 4096 Dec 14 01:46 target/
-rw-rw-r-- 1 vagrant vagrant 0 Dec 14 07:20 test.txt
// 一般ユーザーで実行(エラー)
$ cargo run
-> Current effective caps: {}.
-> Current effective caps: {}.
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Sys(ENOENT)', src/libcore/result.rs:1084:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
// スーパーユーザーの権限で実行(エラー)
$ sudo cargo run
-> Current effective caps: {CAP_LINUX_IMMUTABLE, CAP_SETGID, CAP_NET_BROADCAST, CAP_SYS_NICE, ...}.
-> Current effective caps: {CAP_LINUX_IMMUTABLE, CAP_SETGID, CAP_NET_BROADCAST, CAP_SYS_NICE, ...}.
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Sys(ENOENT)', src/libcore/result.rs:1084:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
chown
が実行できなくなった事によりCAP_CHOWN
が削除できていることが確認できました。スーパーユーザーでもケーパビリティが削除されればこのように権限がなく実行に失敗します。
まとめ
caps-rs
を使用すればRustでケーパビリティの確認・削除といった操作を簡単に実行する事ができます。上記以外にもケーパビリティをセットする・特定のケーパビリティが存在するか確認する、といった事も可能ですので詳しく知りたい方はドキュメントを確認してもらえればと思います。