8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

RustでLinuxのケーパビリティを操作する

Posted at

この記事は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も同じく使います。

Cargo.toml
[dependencies]
caps = "0.3.3"
nix = "0.16.0"

前提として以下に記載する操作は基本的に一般ユーザーで行います。

ケーパビリティの確認

まず、自分自身のケーパビリティを確認してみます。

main.rs
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を実行してみます。

main.rs
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を実行させてみます。

main.rs
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でケーパビリティの確認・削除といった操作を簡単に実行する事ができます。上記以外にもケーパビリティをセットする・特定のケーパビリティが存在するか確認する、といった事も可能ですので詳しく知りたい方はドキュメントを確認してもらえればと思います。

参考

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?