├── .github ├── dependabot.yml └── workflows │ └── rust.yml ├── .gitignore ├── COPYRIGHT ├── Cargo.toml ├── LICENSE-APACHE-2.0 ├── LICENSE-MIT ├── README.md ├── examples ├── all_caps.rs ├── clear_permitted.rs ├── legacy.rs ├── manipulate_sys_nice.rs └── parse.rs ├── src ├── ambient.rs ├── base.rs ├── bounding.rs ├── errors.rs ├── lib.rs ├── nr.rs ├── runtime.rs └── securebits.rs └── tests ├── ambient.rs ├── bounding.rs ├── effective.rs ├── runtime.rs └── securebits.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Rust 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | # Pinned toolchain for linting 12 | ACTION_LINTS_TOOLCHAIN: 1.85.0 13 | 14 | jobs: 15 | tests-stable: 16 | name: "Tests, stable toolchain" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v2 21 | - name: Install toolchain 22 | uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: "stable" 25 | default: true 26 | - name: cargo build --all-features 27 | run: cargo build --all-features 28 | - name: cargo test --all-features 29 | run: cargo test --all-features 30 | tests-release-stable: 31 | name: "Tests (release), stable toolchain" 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v2 36 | - name: Install toolchain 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | toolchain: "stable" 40 | default: true 41 | - name: cargo build (release) 42 | run: cargo build --release 43 | - name: cargo test (release) 44 | run: cargo test --release 45 | tests-release-msrv: 46 | name: "Tests (release), minimum supported toolchain" 47 | runs-on: ubuntu-latest 48 | steps: 49 | - name: Checkout repository 50 | uses: actions/checkout@v2 51 | - name: Extract MSRV 52 | run: echo "ACTION_MSRV_TOOLCHAIN=$(grep 'rust-version' Cargo.toml | cut -d '"' -f2)" >> $GITHUB_ENV 53 | - name: Install toolchain 54 | uses: actions-rs/toolchain@v1 55 | with: 56 | toolchain: ${{ env['ACTION_MSRV_TOOLCHAIN'] }} 57 | default: true 58 | - name: cargo build (release) 59 | run: cargo build --release 60 | - name: cargo test (release) 61 | run: cargo test --release 62 | linting: 63 | name: "Lints, pinned toolchain" 64 | runs-on: ubuntu-latest 65 | steps: 66 | - name: Checkout repository 67 | uses: actions/checkout@v2 68 | - name: Install toolchain 69 | uses: actions-rs/toolchain@v1 70 | with: 71 | toolchain: ${{ env['ACTION_LINTS_TOOLCHAIN'] }} 72 | default: true 73 | components: rustfmt, clippy 74 | - name: cargo clippy (warnings) 75 | run: cargo clippy -- -D warnings 76 | - name: cargo fmt (check) 77 | run: cargo fmt -- --check -l 78 | tests-other-channels: 79 | name: "Tests, unstable toolchain" 80 | runs-on: ubuntu-latest 81 | continue-on-error: true 82 | strategy: 83 | matrix: 84 | channel: 85 | - "beta" 86 | - "nightly" 87 | steps: 88 | - name: Checkout repository 89 | uses: actions/checkout@v2 90 | - name: Install toolchain 91 | uses: actions-rs/toolchain@v1 92 | with: 93 | toolchain: ${{ matrix.channel }} 94 | default: true 95 | - name: cargo build 96 | run: cargo build 97 | - name: cargo test 98 | run: cargo test 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: caps 3 | Source: https://www.github.com/lucab/caps-rs 4 | 5 | Files: * 6 | Copyright: 2017-2019, Project contributors 7 | License: MIT or Apache-2.0 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "caps" 3 | version = "0.5.5" 4 | edition = "2018" 5 | rust-version = "1.63" 6 | authors = ["Luca Bruno "] 7 | license = "MIT/Apache-2.0" 8 | repository = "https://github.com/lucab/caps-rs" 9 | documentation = "https://docs.rs/caps" 10 | description = "A pure-Rust library to work with Linux capabilities" 11 | keywords = ["Linux", "capabilities", "POSIX", "getcap", "setcap"] 12 | exclude = [ 13 | ".gitignore", 14 | ".travis.yml", 15 | ] 16 | 17 | [dependencies] 18 | libc = "^0.2" 19 | thiserror = "^1.0" 20 | serde = { version = "^1.0", features = ["derive"], optional = true} 21 | 22 | [features] 23 | serde_support = ["serde"] 24 | 25 | [dev-dependencies] 26 | serde_json = "^1.0" 27 | 28 | [package.metadata.release] 29 | publish = false 30 | push = false 31 | post-release-commit-message = "cargo: development version bump" 32 | pre-release-commit-message = "cargo: caps release {{version}}" 33 | sign-commit = true 34 | sign-tag = true 35 | tag-message = "caps {{version}}" 36 | -------------------------------------------------------------------------------- /LICENSE-APACHE-2.0: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining 2 | a copy of this software and associated documentation files (the 3 | "Software"), to deal in the Software without restriction, including 4 | without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to 6 | permit persons to whom the Software is furnished to do so, subject to 7 | the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be 10 | included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 18 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # caps 2 | 3 | [![crates.io](https://img.shields.io/crates/v/caps.svg)](https://crates.io/crates/caps) 4 | [![Documentation](https://docs.rs/caps/badge.svg)](https://docs.rs/caps) 5 | 6 | A pure-Rust library to work with Linux capabilities. 7 | 8 | `caps` provides support for manipulating capabilities available in modern Linux 9 | kernels. It supports traditional POSIX sets (Effective, Inheritable, Permitted) 10 | as well as Linux-specific Ambient and Bounding capabilities sets. 11 | 12 | `caps` provides a simple and idiomatic interface to handle capabilities on Linux. 13 | See `capabilities(7)` for more details. 14 | 15 | ## Motivations 16 | 17 | This library tries to achieve the following goals: 18 | * fully support modern kernels, including recent capabilities and sets 19 | * provide an idiomatic interface 20 | * be usable in static targets, without requiring an external C library 21 | 22 | ## Example 23 | 24 | ```rust 25 | type ExResult = Result>; 26 | 27 | fn manipulate_caps() -> ExResult<()> { 28 | use caps::{Capability, CapSet}; 29 | 30 | // Retrieve permitted set. 31 | let cur = caps::read(None, CapSet::Permitted)?; 32 | println!("Current permitted caps: {:?}.", cur); 33 | 34 | // Retrieve effective set. 35 | let cur = caps::read(None, CapSet::Effective)?; 36 | println!("Current effective caps: {:?}.", cur); 37 | 38 | // Check if CAP_CHOWN is in permitted set. 39 | let perm_chown = caps::has_cap(None, CapSet::Permitted, Capability::CAP_CHOWN)?; 40 | if !perm_chown { 41 | return Err("Try running this as root!".into()); 42 | } 43 | 44 | // Clear all effective caps. 45 | caps::clear(None, CapSet::Effective)?; 46 | println!("Cleared effective caps."); 47 | let cur = caps::read(None, CapSet::Effective)?; 48 | println!("Current effective caps: {:?}.", cur); 49 | 50 | // Since `CAP_CHOWN` is still in permitted, it can be raised again. 51 | caps::raise(None, CapSet::Effective, Capability::CAP_CHOWN)?; 52 | println!("Raised CAP_CHOWN in effective set."); 53 | let cur = caps::read(None, CapSet::Effective)?; 54 | println!("Current effective caps: {:?}.", cur); 55 | 56 | Ok(()) 57 | } 58 | ``` 59 | 60 | Some more examples are available under [examples](examples). 61 | 62 | ## License 63 | 64 | Licensed under either of 65 | 66 | * MIT license - 67 | * Apache License, Version 2.0 - 68 | 69 | at your option. 70 | -------------------------------------------------------------------------------- /examples/all_caps.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | A simple example showing how to introspect all available capabilities. 3 | 4 | This differentiates between: 5 | * library set: the static set hardcoded in the library at build-time. 6 | * runtime set: the dynamic set available in the running kernel. 7 | 8 | The runtime set can be introspected in two different ways: via procfs 9 | (faster but not always possible) or via probing (slower). Where both 10 | mechanisms are available, they produce the exact same result. 11 | !*/ 12 | 13 | type ExResult = Result>; 14 | 15 | fn main() -> ExResult<()> { 16 | let library_set = caps::all(); 17 | let runtime_set = caps::runtime::procfs_all_supported(None).unwrap_or_else(|e| { 18 | eprintln!("procfs introspection failed: {}", e); 19 | caps::runtime::thread_all_supported() 20 | }); 21 | 22 | println!("Capabilities tally:"); 23 | println!(" -> known by this library: {}.", library_set.len()); 24 | println!(" -> known by the current kernel: {}.\n", runtime_set.len()); 25 | 26 | println!("Library vs kernel differences:"); 27 | println!( 28 | " -> newer caps only in the library set: {:?}.", 29 | library_set.difference(&runtime_set) 30 | ); 31 | println!( 32 | " -> newer caps only in the current kernel set: {:?}.", 33 | runtime_set.difference(&library_set) 34 | ); 35 | 36 | Ok(()) 37 | } 38 | -------------------------------------------------------------------------------- /examples/clear_permitted.rs: -------------------------------------------------------------------------------- 1 | //! A simple example showing how to manipulate capabilities. 2 | //! 3 | //! It clears Permitted set to show its interaction 4 | //! with Effective set. 5 | //! 6 | //! This is an example ONLY: do NOT panic/unwrap/assert 7 | //! in production code! 8 | 9 | type ExResult = Result>; 10 | 11 | fn main() -> ExResult<()> { 12 | use caps::{CapSet, Capability}; 13 | 14 | // Check if `CAP_CHOWN` was originally available. 15 | let cur = caps::read(None, CapSet::Permitted)?; 16 | println!("-> Current permitted caps: {:?}.", cur); 17 | let cur = caps::read(None, CapSet::Effective)?; 18 | println!("-> Current effective caps: {:?}.", cur); 19 | let perm_chown = caps::has_cap(None, CapSet::Permitted, Capability::CAP_CHOWN); 20 | assert!(perm_chown.is_ok()); 21 | if !perm_chown? { 22 | return Err( 23 | "Try running this again as root/sudo or with CAP_CHOWN file capability!".into(), 24 | ); 25 | } 26 | 27 | // Clear all effective caps. 28 | let r = caps::clear(None, CapSet::Effective); 29 | assert!(r.is_ok()); 30 | println!("Cleared effective caps."); 31 | let cur = caps::read(None, CapSet::Effective)?; 32 | println!("-> Current effective caps: {:?}.", cur); 33 | 34 | // Since `CAP_CHOWN` is still in permitted, it can be raised again. 35 | let r = caps::raise(None, CapSet::Effective, Capability::CAP_CHOWN); 36 | assert!(r.is_ok()); 37 | println!("Raised CAP_CHOWN in effective set."); 38 | let cur = caps::read(None, CapSet::Effective)?; 39 | println!("-> Current effective caps: {:?}.", cur); 40 | 41 | // Clearing Permitted also impacts effective. 42 | let r = caps::clear(None, CapSet::Permitted); 43 | assert!(r.is_ok()); 44 | println!("Cleared permitted caps."); 45 | let cur = caps::read(None, CapSet::Permitted)?; 46 | println!("-> Current permitted caps: {:?}.", cur); 47 | let cur = caps::read(None, CapSet::Effective)?; 48 | println!("-> Current effective caps: {:?}.", cur); 49 | 50 | // Trying to raise `CAP_CHOWN` now fails. 51 | let r = caps::raise(None, CapSet::Effective, Capability::CAP_CHOWN); 52 | assert!(r.is_err()); 53 | println!("Tried to raise CAP_CHOWN but failed."); 54 | let cur = caps::read(None, CapSet::Effective)?; 55 | println!("-> Current effective caps: {:?}.", cur); 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /examples/legacy.rs: -------------------------------------------------------------------------------- 1 | use caps::runtime; 2 | 3 | fn main() { 4 | let amb_set = runtime::ambient_set_supported().is_ok(); 5 | println!("Ambient set supported: {}", amb_set); 6 | 7 | let all = caps::all(); 8 | let supported = runtime::thread_all_supported(); 9 | let missing = all.difference(&supported); 10 | println!("Unsupported new capabilities: {:?}", missing); 11 | } 12 | -------------------------------------------------------------------------------- /examples/manipulate_sys_nice.rs: -------------------------------------------------------------------------------- 1 | //! A simple example showing how to manipulate capabilities. 2 | //! 3 | //! It drops and raises `CAP_SYS_NICE` to show its interaction 4 | //! with `getpriority(2)`. 5 | //! 6 | //! This is an example ONLY: do NOT panic/unwrap/assert 7 | //! in production code! 8 | 9 | type ExResult = Result>; 10 | 11 | fn main() -> ExResult<()> { 12 | use caps::{CapSet, Capability}; 13 | 14 | // Any process can lower its own priority. 15 | println!("-> Current process priority is {}.", proc_nice()); 16 | let r = renice(19); 17 | assert_eq!(r, 0); 18 | println!("Lowered priority to +19."); 19 | println!("-> Current process priority is {}.", proc_nice()); 20 | 21 | // Without `CAP_SYS_NICE` increasing priority is not possible. 22 | let r = caps::drop(None, CapSet::Effective, Capability::CAP_SYS_NICE); 23 | assert!(r.is_ok()); 24 | println!("Dropped CAP_SYS_NICE."); 25 | let has_sys_nice = caps::has_cap(None, CapSet::Effective, Capability::CAP_SYS_NICE); 26 | assert!(has_sys_nice.is_ok()); 27 | assert_eq!(has_sys_nice.unwrap_or(true), false); 28 | let r = renice(-20); 29 | assert_eq!(r, -1); 30 | println!("Unprivileged, unable to raise priority to -20."); 31 | 32 | // If `CAP_SYS_NICE` is still in permitted set, it can be raised again. 33 | let perm_sys_nice = caps::has_cap(None, CapSet::Permitted, Capability::CAP_SYS_NICE); 34 | assert!(perm_sys_nice.is_ok()); 35 | if !perm_sys_nice? { 36 | return Err( 37 | "Try running this again as root/sudo or with CAP_SYS_NICE file capability!".into(), 38 | ); 39 | } 40 | let r = caps::raise(None, CapSet::Effective, Capability::CAP_SYS_NICE); 41 | assert!(r.is_ok()); 42 | println!("Raised CAP_SYS_NICE."); 43 | 44 | // With CAP_SYS_NICE, priority can be raised further. 45 | let r = renice(-20); 46 | assert_eq!(r, 0); 47 | println!("Privileged, raised priority to -20."); 48 | println!("-> Current process priority is {}.", proc_nice()); 49 | 50 | Ok(()) 51 | } 52 | 53 | #[cfg(target_env = "musl")] 54 | const PRIO_PROCESS: i32 = libc::PRIO_PROCESS; 55 | #[cfg(not(target_env = "musl"))] 56 | const PRIO_PROCESS: u32 = libc::PRIO_PROCESS as u32; 57 | 58 | fn renice(prio: libc::c_int) -> libc::c_int { 59 | // This is not proper logic, as it does not record errno value on error. 60 | unsafe { libc::setpriority(PRIO_PROCESS, 0, prio) } 61 | } 62 | 63 | fn proc_nice() -> libc::c_int { 64 | // This is not proper logic, as it does not special-case -1 nor record errno. 65 | let r = unsafe { libc::getpriority(PRIO_PROCESS as u32, 0) }; 66 | if r == -1 { 67 | panic!("getpriority failed."); 68 | } 69 | r 70 | } 71 | -------------------------------------------------------------------------------- /examples/parse.rs: -------------------------------------------------------------------------------- 1 | use caps::Capability; 2 | use std::str::FromStr; 3 | 4 | fn main() { 5 | let input = std::env::args().nth(1).expect("missing argument"); 6 | match Capability::from_str(&input.to_uppercase()) { 7 | Ok(p) => println!( 8 | "Parsed: {} -> index={}, bitmask={}", 9 | p, 10 | p.index(), 11 | p.bitmask() 12 | ), 13 | Err(e) => println!("{}", e), 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/ambient.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of Ambient set. 2 | 3 | use crate::errors::CapsError; 4 | use crate::nr; 5 | use crate::runtime; 6 | use crate::{Capability, CapsHashSet}; 7 | use std::io::Error; 8 | 9 | pub fn clear() -> Result<(), CapsError> { 10 | let ret = unsafe { libc::prctl(nr::PR_CAP_AMBIENT, nr::PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) }; 11 | match ret { 12 | 0 => Ok(()), 13 | _ => Err(format!( 14 | "PR_CAP_AMBIENT_CLEAR_ALL failure: {}", 15 | Error::last_os_error() 16 | ) 17 | .into()), 18 | } 19 | } 20 | 21 | pub fn drop(cap: Capability) -> Result<(), CapsError> { 22 | let ret = unsafe { 23 | libc::prctl( 24 | nr::PR_CAP_AMBIENT, 25 | nr::PR_CAP_AMBIENT_LOWER, 26 | libc::c_uint::from(cap.index()), 27 | 0, 28 | 0, 29 | ) 30 | }; 31 | match ret { 32 | 0 => Ok(()), 33 | _ => Err(format!("PR_CAP_AMBIENT_LOWER failure: {}", Error::last_os_error()).into()), 34 | } 35 | } 36 | 37 | pub fn has_cap(cap: Capability) -> Result { 38 | let ret = unsafe { 39 | libc::prctl( 40 | nr::PR_CAP_AMBIENT, 41 | nr::PR_CAP_AMBIENT_IS_SET, 42 | libc::c_uint::from(cap.index()), 43 | 0, 44 | 0, 45 | ) 46 | }; 47 | match ret { 48 | 0 => Ok(false), 49 | 1 => Ok(true), 50 | _ => Err(format!("PR_CAP_AMBIENT_IS_SET failure: {}", Error::last_os_error()).into()), 51 | } 52 | } 53 | 54 | pub fn raise(cap: Capability) -> Result<(), CapsError> { 55 | let ret = unsafe { 56 | libc::prctl( 57 | nr::PR_CAP_AMBIENT, 58 | nr::PR_CAP_AMBIENT_RAISE, 59 | libc::c_uint::from(cap.index()), 60 | 0, 61 | 0, 62 | ) 63 | }; 64 | match ret { 65 | 0 => Ok(()), 66 | _ => Err(format!("PR_CAP_AMBIENT_RAISE failure: {}", Error::last_os_error()).into()), 67 | } 68 | } 69 | 70 | pub fn read() -> Result { 71 | let mut res = super::CapsHashSet::new(); 72 | for c in runtime::thread_all_supported() { 73 | if has_cap(c)? { 74 | res.insert(c); 75 | } 76 | } 77 | Ok(res) 78 | } 79 | 80 | pub fn set(value: &super::CapsHashSet) -> Result<(), CapsError> { 81 | for c in runtime::thread_all_supported() { 82 | if value.contains(&c) { 83 | raise(c)?; 84 | } else { 85 | drop(c)?; 86 | }; 87 | } 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /src/base.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::CapsError; 2 | use crate::nr; 3 | use crate::{CapSet, Capability, CapsHashSet}; 4 | use std::io::Error; 5 | 6 | #[allow(clippy::unreadable_literal)] 7 | const CAPS_V3: u32 = 0x20080522; 8 | 9 | fn capget(hdr: &mut CapUserHeader, data: &mut CapUserData) -> Result<(), CapsError> { 10 | let r = unsafe { libc::syscall(nr::CAPGET, hdr, data) }; 11 | match r { 12 | 0 => Ok(()), 13 | _ => Err(format!("capget failure: {}", Error::last_os_error()).into()), 14 | } 15 | } 16 | 17 | fn capset(hdr: &mut CapUserHeader, data: &CapUserData) -> Result<(), CapsError> { 18 | let r = unsafe { libc::syscall(nr::CAPSET, hdr, data) }; 19 | match r { 20 | 0 => Ok(()), 21 | _ => Err(format!("capset failure: {}", Error::last_os_error()).into()), 22 | } 23 | } 24 | 25 | pub fn has_cap(tid: i32, cset: CapSet, cap: Capability) -> Result { 26 | let mut hdr = CapUserHeader { 27 | version: CAPS_V3, 28 | pid: tid, 29 | }; 30 | let mut data: CapUserData = Default::default(); 31 | capget(&mut hdr, &mut data)?; 32 | let caps: u64 = match cset { 33 | CapSet::Effective => (u64::from(data.effective_s1) << 32) + u64::from(data.effective_s0), 34 | CapSet::Inheritable => { 35 | (u64::from(data.inheritable_s1) << 32) + u64::from(data.inheritable_s0) 36 | } 37 | CapSet::Permitted => (u64::from(data.permitted_s1) << 32) + u64::from(data.permitted_s0), 38 | CapSet::Bounding | CapSet::Ambient => return Err("not a base set".into()), 39 | }; 40 | let has_cap = (caps & cap.bitmask()) != 0; 41 | Ok(has_cap) 42 | } 43 | 44 | pub fn clear(tid: i32, cset: CapSet) -> Result<(), CapsError> { 45 | let mut hdr = CapUserHeader { 46 | version: CAPS_V3, 47 | pid: tid, 48 | }; 49 | let mut data: CapUserData = Default::default(); 50 | capget(&mut hdr, &mut data)?; 51 | match cset { 52 | CapSet::Effective => { 53 | data.effective_s0 = 0; 54 | data.effective_s1 = 0; 55 | } 56 | CapSet::Inheritable => { 57 | data.inheritable_s0 = 0; 58 | data.inheritable_s1 = 0; 59 | } 60 | CapSet::Permitted => { 61 | data.effective_s0 = 0; 62 | data.effective_s1 = 0; 63 | data.permitted_s0 = 0; 64 | data.permitted_s1 = 0; 65 | } 66 | CapSet::Bounding | CapSet::Ambient => return Err("not a base set".into()), 67 | } 68 | capset(&mut hdr, &data) 69 | } 70 | 71 | pub fn read(tid: i32, cset: CapSet) -> Result { 72 | let mut hdr = CapUserHeader { 73 | version: CAPS_V3, 74 | pid: tid, 75 | }; 76 | let mut data: CapUserData = Default::default(); 77 | capget(&mut hdr, &mut data)?; 78 | let caps: u64 = match cset { 79 | CapSet::Effective => (u64::from(data.effective_s1) << 32) + u64::from(data.effective_s0), 80 | CapSet::Inheritable => { 81 | (u64::from(data.inheritable_s1) << 32) + u64::from(data.inheritable_s0) 82 | } 83 | CapSet::Permitted => (u64::from(data.permitted_s1) << 32) + u64::from(data.permitted_s0), 84 | CapSet::Bounding | CapSet::Ambient => return Err("not a base set".into()), 85 | }; 86 | let mut res = CapsHashSet::new(); 87 | for c in super::all() { 88 | if (caps & c.bitmask()) != 0 { 89 | res.insert(c); 90 | } 91 | } 92 | Ok(res) 93 | } 94 | 95 | pub fn set(tid: i32, cset: CapSet, value: &CapsHashSet) -> Result<(), CapsError> { 96 | let mut hdr = CapUserHeader { 97 | version: CAPS_V3, 98 | pid: tid, 99 | }; 100 | let mut data: CapUserData = Default::default(); 101 | capget(&mut hdr, &mut data)?; 102 | { 103 | let (s1, s0) = match cset { 104 | CapSet::Effective => (&mut data.effective_s1, &mut data.effective_s0), 105 | CapSet::Inheritable => (&mut data.inheritable_s1, &mut data.inheritable_s0), 106 | CapSet::Permitted => (&mut data.permitted_s1, &mut data.permitted_s0), 107 | CapSet::Bounding | CapSet::Ambient => return Err("not a base set".into()), 108 | }; 109 | *s1 = 0; 110 | *s0 = 0; 111 | for c in value { 112 | match c.index() { 113 | 0..=31 => { 114 | *s0 |= c.bitmask() as u32; 115 | } 116 | 32..=63 => { 117 | *s1 |= (c.bitmask() >> 32) as u32; 118 | } 119 | _ => return Err(format!("overlarge capability index {}", c.index()).into()), 120 | } 121 | } 122 | } 123 | capset(&mut hdr, &data)?; 124 | Ok(()) 125 | } 126 | 127 | pub fn drop(tid: i32, cset: CapSet, cap: Capability) -> Result<(), CapsError> { 128 | let mut caps = read(tid, cset)?; 129 | if caps.remove(&cap) { 130 | set(tid, cset, &caps)?; 131 | }; 132 | Ok(()) 133 | } 134 | 135 | pub fn raise(tid: i32, cset: CapSet, cap: Capability) -> Result<(), CapsError> { 136 | let mut caps = read(tid, cset)?; 137 | if caps.insert(cap) { 138 | set(tid, cset, &caps)?; 139 | }; 140 | Ok(()) 141 | } 142 | 143 | #[derive(Debug)] 144 | #[repr(C)] 145 | struct CapUserHeader { 146 | // Linux capabilities version (runtime kernel support) 147 | version: u32, 148 | // Process ID (thread) 149 | pid: i32, 150 | } 151 | 152 | #[derive(Debug, Default, Clone)] 153 | #[repr(C)] 154 | struct CapUserData { 155 | effective_s0: u32, 156 | permitted_s0: u32, 157 | inheritable_s0: u32, 158 | effective_s1: u32, 159 | permitted_s1: u32, 160 | inheritable_s1: u32, 161 | } 162 | -------------------------------------------------------------------------------- /src/bounding.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::CapsError; 2 | use crate::nr; 3 | use crate::runtime; 4 | use crate::Capability; 5 | use std::io::Error; 6 | 7 | pub fn clear() -> Result<(), CapsError> { 8 | for c in super::all() { 9 | if has_cap(c)? { 10 | drop(c)?; 11 | } 12 | } 13 | Ok(()) 14 | } 15 | 16 | pub fn drop(cap: Capability) -> Result<(), CapsError> { 17 | let ret = unsafe { libc::prctl(nr::PR_CAPBSET_DROP, libc::c_uint::from(cap.index()), 0, 0) }; 18 | match ret { 19 | 0 => Ok(()), 20 | _ => Err(CapsError::from(format!( 21 | "PR_CAPBSET_DROP failure: {}", 22 | Error::last_os_error() 23 | ))), 24 | } 25 | } 26 | 27 | pub fn has_cap(cap: Capability) -> Result { 28 | let ret = unsafe { libc::prctl(nr::PR_CAPBSET_READ, libc::c_uint::from(cap.index()), 0, 0) }; 29 | match ret { 30 | 0 => Ok(false), 31 | 1 => Ok(true), 32 | _ => Err(CapsError::from(format!( 33 | "PR_CAPBSET_READ failure: {}", 34 | Error::last_os_error() 35 | ))), 36 | } 37 | } 38 | 39 | pub fn read() -> Result { 40 | let mut res = super::CapsHashSet::new(); 41 | for c in runtime::thread_all_supported() { 42 | if has_cap(c)? { 43 | res.insert(c); 44 | } 45 | } 46 | Ok(res) 47 | } 48 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | //! Error handling. 2 | 3 | use thiserror::Error; 4 | 5 | /// Library errors. 6 | #[derive(Error, Debug)] 7 | #[error("caps error: {0}")] 8 | pub struct CapsError(pub(crate) String); 9 | 10 | impl From<&str> for CapsError { 11 | fn from(arg: &str) -> Self { 12 | Self(arg.to_string()) 13 | } 14 | } 15 | 16 | impl From for CapsError { 17 | fn from(arg: String) -> Self { 18 | Self(arg) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | A pure-Rust library to work with Linux capabilities. 3 | 4 | It provides support for manipulating capabilities available on modern Linux 5 | kernels. It supports traditional POSIX sets (Effective, Inheritable, Permitted) 6 | as well as Linux-specific Ambient and Bounding capabilities sets. 7 | 8 | ```rust 9 | type ExResult = Result>; 10 | 11 | fn manipulate_caps() -> ExResult<()> { 12 | use caps::{Capability, CapSet}; 13 | 14 | if caps::has_cap(None, CapSet::Permitted, Capability::CAP_SYS_NICE)? { 15 | caps::drop(None, CapSet::Effective, Capability::CAP_SYS_NICE)?; 16 | let effective = caps::read(None, CapSet::Effective)?; 17 | assert_eq!(effective.contains(&Capability::CAP_SYS_NICE), false); 18 | 19 | caps::clear(None, CapSet::Effective)?; 20 | let cleared = caps::read(None, CapSet::Effective)?; 21 | assert_eq!(cleared.is_empty(), true); 22 | }; 23 | 24 | Ok(()) 25 | } 26 | ``` 27 | */ 28 | 29 | pub mod errors; 30 | pub mod runtime; 31 | pub mod securebits; 32 | 33 | // Implementation of Bounding set. 34 | mod ambient; 35 | // Implementation of POSIX sets. 36 | mod base; 37 | // Implementation of Bounding set. 38 | mod bounding; 39 | // All kernel-related constants. 40 | mod nr; 41 | 42 | use crate::errors::CapsError; 43 | use std::iter::FromIterator; 44 | 45 | /// Linux capabilities sets. 46 | /// 47 | /// All capabilities sets supported by Linux, including standard 48 | /// POSIX and custom ones. See `capabilities(7)`. 49 | #[derive(Debug, Clone, Copy)] 50 | pub enum CapSet { 51 | /// Ambient capabilities set (from Linux 4.3). 52 | Ambient, 53 | /// Bounding capabilities set (from Linux 2.6.25) 54 | Bounding, 55 | /// Effective capabilities set (from POSIX) 56 | Effective, 57 | /// Inheritable capabilities set (from POSIX) 58 | Inheritable, 59 | /// Permitted capabilities set (from POSIX) 60 | Permitted, 61 | } 62 | 63 | /// Linux capabilities. 64 | /// 65 | /// All capabilities supported by Linux, including standard 66 | /// POSIX and custom ones. See `capabilities(7)`. 67 | #[allow(clippy::manual_non_exhaustive)] 68 | #[allow(non_camel_case_types)] 69 | #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] 70 | #[repr(u8)] 71 | #[cfg_attr( 72 | feature = "serde_support", 73 | derive(serde::Serialize, serde::Deserialize) 74 | )] 75 | pub enum Capability { 76 | /// `CAP_CHOWN` (from POSIX) 77 | CAP_CHOWN = nr::CAP_CHOWN, 78 | /// `CAP_DAC_OVERRIDE` (from POSIX) 79 | CAP_DAC_OVERRIDE = nr::CAP_DAC_OVERRIDE, 80 | /// `CAP_DAC_READ_SEARCH` (from POSIX) 81 | CAP_DAC_READ_SEARCH = nr::CAP_DAC_READ_SEARCH, 82 | /// `CAP_FOWNER` (from POSIX) 83 | CAP_FOWNER = nr::CAP_FOWNER, 84 | /// `CAP_FSETID` (from POSIX) 85 | CAP_FSETID = nr::CAP_FSETID, 86 | /// `CAP_KILL` (from POSIX) 87 | CAP_KILL = nr::CAP_KILL, 88 | /// `CAP_SETGID` (from POSIX) 89 | CAP_SETGID = nr::CAP_SETGID, 90 | /// `CAP_SETUID` (from POSIX) 91 | CAP_SETUID = nr::CAP_SETUID, 92 | /// `CAP_SETPCAP` (from Linux) 93 | CAP_SETPCAP = nr::CAP_SETPCAP, 94 | CAP_LINUX_IMMUTABLE = nr::CAP_LINUX_IMMUTABLE, 95 | CAP_NET_BIND_SERVICE = nr::CAP_NET_BIND_SERVICE, 96 | CAP_NET_BROADCAST = nr::CAP_NET_BROADCAST, 97 | CAP_NET_ADMIN = nr::CAP_NET_ADMIN, 98 | CAP_NET_RAW = nr::CAP_NET_RAW, 99 | CAP_IPC_LOCK = nr::CAP_IPC_LOCK, 100 | CAP_IPC_OWNER = nr::CAP_IPC_OWNER, 101 | /// `CAP_SYS_MODULE` (from Linux) 102 | CAP_SYS_MODULE = nr::CAP_SYS_MODULE, 103 | /// `CAP_SYS_RAWIO` (from Linux) 104 | CAP_SYS_RAWIO = nr::CAP_SYS_RAWIO, 105 | /// `CAP_SYS_CHROOT` (from Linux) 106 | CAP_SYS_CHROOT = nr::CAP_SYS_CHROOT, 107 | /// `CAP_SYS_PTRACE` (from Linux) 108 | CAP_SYS_PTRACE = nr::CAP_SYS_PTRACE, 109 | /// `CAP_SYS_PACCT` (from Linux) 110 | CAP_SYS_PACCT = nr::CAP_SYS_PACCT, 111 | /// `CAP_SYS_ADMIN` (from Linux) 112 | CAP_SYS_ADMIN = nr::CAP_SYS_ADMIN, 113 | /// `CAP_SYS_BOOT` (from Linux) 114 | CAP_SYS_BOOT = nr::CAP_SYS_BOOT, 115 | /// `CAP_SYS_NICE` (from Linux) 116 | CAP_SYS_NICE = nr::CAP_SYS_NICE, 117 | /// `CAP_SYS_RESOURCE` (from Linux) 118 | CAP_SYS_RESOURCE = nr::CAP_SYS_RESOURCE, 119 | /// `CAP_SYS_TIME` (from Linux) 120 | CAP_SYS_TIME = nr::CAP_SYS_TIME, 121 | /// `CAP_SYS_TTY_CONFIG` (from Linux) 122 | CAP_SYS_TTY_CONFIG = nr::CAP_SYS_TTY_CONFIG, 123 | /// `CAP_SYS_MKNOD` (from Linux, >= 2.4) 124 | CAP_MKNOD = nr::CAP_MKNOD, 125 | /// `CAP_LEASE` (from Linux, >= 2.4) 126 | CAP_LEASE = nr::CAP_LEASE, 127 | CAP_AUDIT_WRITE = nr::CAP_AUDIT_WRITE, 128 | /// `CAP_AUDIT_CONTROL` (from Linux, >= 2.6.11) 129 | CAP_AUDIT_CONTROL = nr::CAP_AUDIT_CONTROL, 130 | CAP_SETFCAP = nr::CAP_SETFCAP, 131 | CAP_MAC_OVERRIDE = nr::CAP_MAC_OVERRIDE, 132 | CAP_MAC_ADMIN = nr::CAP_MAC_ADMIN, 133 | /// `CAP_SYSLOG` (from Linux, >= 2.6.37) 134 | CAP_SYSLOG = nr::CAP_SYSLOG, 135 | /// `CAP_WAKE_ALARM` (from Linux, >= 3.0) 136 | CAP_WAKE_ALARM = nr::CAP_WAKE_ALARM, 137 | CAP_BLOCK_SUSPEND = nr::CAP_BLOCK_SUSPEND, 138 | /// `CAP_AUDIT_READ` (from Linux, >= 3.16). 139 | CAP_AUDIT_READ = nr::CAP_AUDIT_READ, 140 | /// `CAP_PERFMON` (from Linux, >= 5.8). 141 | CAP_PERFMON = nr::CAP_PERFMON, 142 | /// `CAP_BPF` (from Linux, >= 5.8). 143 | CAP_BPF = nr::CAP_BPF, 144 | /// `CAP_CHECKPOINT_RESTORE` (from Linux, >= 5.9). 145 | CAP_CHECKPOINT_RESTORE = nr::CAP_CHECKPOINT_RESTORE, 146 | #[doc(hidden)] 147 | __Nonexhaustive, 148 | } 149 | 150 | impl std::fmt::Display for Capability { 151 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 152 | let name = match *self { 153 | Capability::CAP_CHOWN => "CAP_CHOWN", 154 | Capability::CAP_DAC_OVERRIDE => "CAP_DAC_OVERRIDE", 155 | Capability::CAP_DAC_READ_SEARCH => "CAP_DAC_READ_SEARCH", 156 | Capability::CAP_FOWNER => "CAP_FOWNER", 157 | Capability::CAP_FSETID => "CAP_FSETID", 158 | Capability::CAP_KILL => "CAP_KILL", 159 | Capability::CAP_SETGID => "CAP_SETGID", 160 | Capability::CAP_SETUID => "CAP_SETUID", 161 | Capability::CAP_SETPCAP => "CAP_SETPCAP", 162 | Capability::CAP_LINUX_IMMUTABLE => "CAP_LINUX_IMMUTABLE", 163 | Capability::CAP_NET_BIND_SERVICE => "CAP_NET_BIND_SERVICE", 164 | Capability::CAP_NET_BROADCAST => "CAP_NET_BROADCAST", 165 | Capability::CAP_NET_ADMIN => "CAP_NET_ADMIN", 166 | Capability::CAP_NET_RAW => "CAP_NET_RAW", 167 | Capability::CAP_IPC_LOCK => "CAP_IPC_LOCK", 168 | Capability::CAP_IPC_OWNER => "CAP_IPC_OWNER", 169 | Capability::CAP_SYS_MODULE => "CAP_SYS_MODULE", 170 | Capability::CAP_SYS_RAWIO => "CAP_SYS_RAWIO", 171 | Capability::CAP_SYS_CHROOT => "CAP_SYS_CHROOT", 172 | Capability::CAP_SYS_PTRACE => "CAP_SYS_PTRACE", 173 | Capability::CAP_SYS_PACCT => "CAP_SYS_PACCT", 174 | Capability::CAP_SYS_ADMIN => "CAP_SYS_ADMIN", 175 | Capability::CAP_SYS_BOOT => "CAP_SYS_BOOT", 176 | Capability::CAP_SYS_NICE => "CAP_SYS_NICE", 177 | Capability::CAP_SYS_RESOURCE => "CAP_SYS_RESOURCE", 178 | Capability::CAP_SYS_TIME => "CAP_SYS_TIME", 179 | Capability::CAP_SYS_TTY_CONFIG => "CAP_SYS_TTY_CONFIG", 180 | Capability::CAP_MKNOD => "CAP_MKNOD", 181 | Capability::CAP_LEASE => "CAP_LEASE", 182 | Capability::CAP_AUDIT_WRITE => "CAP_AUDIT_WRITE", 183 | Capability::CAP_AUDIT_CONTROL => "CAP_AUDIT_CONTROL", 184 | Capability::CAP_SETFCAP => "CAP_SETFCAP", 185 | Capability::CAP_MAC_OVERRIDE => "CAP_MAC_OVERRIDE", 186 | Capability::CAP_MAC_ADMIN => "CAP_MAC_ADMIN", 187 | Capability::CAP_SYSLOG => "CAP_SYSLOG", 188 | Capability::CAP_WAKE_ALARM => "CAP_WAKE_ALARM", 189 | Capability::CAP_BLOCK_SUSPEND => "CAP_BLOCK_SUSPEND", 190 | Capability::CAP_AUDIT_READ => "CAP_AUDIT_READ", 191 | Capability::CAP_PERFMON => "CAP_PERFMON", 192 | Capability::CAP_BPF => "CAP_BPF", 193 | Capability::CAP_CHECKPOINT_RESTORE => "CAP_CHECKPOINT_RESTORE", 194 | Capability::__Nonexhaustive => unreachable!("invalid capability"), 195 | }; 196 | write!(f, "{}", name) 197 | } 198 | } 199 | 200 | impl std::str::FromStr for Capability { 201 | type Err = CapsError; 202 | 203 | fn from_str(s: &str) -> std::result::Result { 204 | match s { 205 | "CAP_CHOWN" => Ok(Capability::CAP_CHOWN), 206 | "CAP_DAC_OVERRIDE" => Ok(Capability::CAP_DAC_OVERRIDE), 207 | "CAP_DAC_READ_SEARCH" => Ok(Capability::CAP_DAC_READ_SEARCH), 208 | "CAP_FOWNER" => Ok(Capability::CAP_FOWNER), 209 | "CAP_FSETID" => Ok(Capability::CAP_FSETID), 210 | "CAP_KILL" => Ok(Capability::CAP_KILL), 211 | "CAP_SETGID" => Ok(Capability::CAP_SETGID), 212 | "CAP_SETUID" => Ok(Capability::CAP_SETUID), 213 | "CAP_SETPCAP" => Ok(Capability::CAP_SETPCAP), 214 | "CAP_LINUX_IMMUTABLE" => Ok(Capability::CAP_LINUX_IMMUTABLE), 215 | "CAP_NET_BIND_SERVICE" => Ok(Capability::CAP_NET_BIND_SERVICE), 216 | "CAP_NET_BROADCAST" => Ok(Capability::CAP_NET_BROADCAST), 217 | "CAP_NET_ADMIN" => Ok(Capability::CAP_NET_ADMIN), 218 | "CAP_NET_RAW" => Ok(Capability::CAP_NET_RAW), 219 | "CAP_IPC_LOCK" => Ok(Capability::CAP_IPC_LOCK), 220 | "CAP_IPC_OWNER" => Ok(Capability::CAP_IPC_OWNER), 221 | "CAP_SYS_MODULE" => Ok(Capability::CAP_SYS_MODULE), 222 | "CAP_SYS_RAWIO" => Ok(Capability::CAP_SYS_RAWIO), 223 | "CAP_SYS_CHROOT" => Ok(Capability::CAP_SYS_CHROOT), 224 | "CAP_SYS_PTRACE" => Ok(Capability::CAP_SYS_PTRACE), 225 | "CAP_SYS_PACCT" => Ok(Capability::CAP_SYS_PACCT), 226 | "CAP_SYS_ADMIN" => Ok(Capability::CAP_SYS_ADMIN), 227 | "CAP_SYS_BOOT" => Ok(Capability::CAP_SYS_BOOT), 228 | "CAP_SYS_NICE" => Ok(Capability::CAP_SYS_NICE), 229 | "CAP_SYS_RESOURCE" => Ok(Capability::CAP_SYS_RESOURCE), 230 | "CAP_SYS_TIME" => Ok(Capability::CAP_SYS_TIME), 231 | "CAP_SYS_TTY_CONFIG" => Ok(Capability::CAP_SYS_TTY_CONFIG), 232 | "CAP_MKNOD" => Ok(Capability::CAP_MKNOD), 233 | "CAP_LEASE" => Ok(Capability::CAP_LEASE), 234 | "CAP_AUDIT_WRITE" => Ok(Capability::CAP_AUDIT_WRITE), 235 | "CAP_AUDIT_CONTROL" => Ok(Capability::CAP_AUDIT_CONTROL), 236 | "CAP_SETFCAP" => Ok(Capability::CAP_SETFCAP), 237 | "CAP_MAC_OVERRIDE" => Ok(Capability::CAP_MAC_OVERRIDE), 238 | "CAP_MAC_ADMIN" => Ok(Capability::CAP_MAC_ADMIN), 239 | "CAP_SYSLOG" => Ok(Capability::CAP_SYSLOG), 240 | "CAP_WAKE_ALARM" => Ok(Capability::CAP_WAKE_ALARM), 241 | "CAP_BLOCK_SUSPEND" => Ok(Capability::CAP_BLOCK_SUSPEND), 242 | "CAP_AUDIT_READ" => Ok(Capability::CAP_AUDIT_READ), 243 | "CAP_PERFMON" => Ok(Capability::CAP_PERFMON), 244 | "CAP_BPF" => Ok(Capability::CAP_BPF), 245 | "CAP_CHECKPOINT_RESTORE" => Ok(Capability::CAP_CHECKPOINT_RESTORE), 246 | _ => Err(format!("invalid capability: {}", s).into()), 247 | } 248 | } 249 | } 250 | 251 | impl Capability { 252 | /// Returns the bitmask corresponding to this capability value. 253 | #[allow(clippy::trivially_copy_pass_by_ref)] 254 | pub fn bitmask(&self) -> u64 { 255 | 1u64 << (*self as u8) 256 | } 257 | 258 | /// Returns the index of this capability, i.e. its kernel-defined value. 259 | #[allow(clippy::trivially_copy_pass_by_ref)] 260 | pub fn index(&self) -> u8 { 261 | *self as u8 262 | } 263 | } 264 | 265 | /// An `HashSet` specialized on `Capability`. 266 | pub type CapsHashSet = std::collections::HashSet; 267 | 268 | /// Check if a thread contains a capability in a set. 269 | /// 270 | /// Check if set `cset` for thread `tid` contains capability `cap`. 271 | /// If `tid` is `None`, this operates on current thread (tid=0). 272 | /// It cannot check Ambient or Bounding capabilities of other processes. 273 | pub fn has_cap(tid: Option, cset: CapSet, cap: Capability) -> Result { 274 | let t = tid.unwrap_or(0); 275 | match cset { 276 | CapSet::Ambient if t == 0 => ambient::has_cap(cap), 277 | CapSet::Bounding if t == 0 => bounding::has_cap(cap), 278 | CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::has_cap(t, cset, cap), 279 | _ => Err("operation not supported".into()), 280 | } 281 | } 282 | 283 | /// Return all capabilities in a set for a thread. 284 | /// 285 | /// Return current content of set `cset` for thread `tid`. 286 | /// If `tid` is `None`, this operates on current thread (tid=0). 287 | /// It cannot read Ambient or Bounding capabilities of other processes. 288 | pub fn read(tid: Option, cset: CapSet) -> Result { 289 | let t = tid.unwrap_or(0); 290 | match cset { 291 | CapSet::Ambient if t == 0 => ambient::read(), 292 | CapSet::Bounding if t == 0 => bounding::read(), 293 | CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::read(t, cset), 294 | _ => Err("operation not supported".into()), 295 | } 296 | } 297 | 298 | /// Set a capability set for a thread to a new value. 299 | /// 300 | /// All and only capabilities in `value` will be set for set `cset` for thread `tid`. 301 | /// If `tid` is `None`, this operates on current thread (tid=0). 302 | /// It cannot manipulate Ambient set of other processes. 303 | /// Capabilities cannot be set in Bounding set. 304 | pub fn set(tid: Option, cset: CapSet, value: &CapsHashSet) -> Result<(), CapsError> { 305 | let t = tid.unwrap_or(0); 306 | match cset { 307 | CapSet::Ambient if t == 0 => ambient::set(value), 308 | CapSet::Effective | CapSet::Inheritable | CapSet::Permitted => base::set(t, cset, value), 309 | _ => Err("operation not supported".into()), 310 | } 311 | } 312 | 313 | /// Clear all capabilities in a set for a thread. 314 | /// 315 | /// All capabilities will be cleared from set `cset` for thread `tid`. 316 | /// If `tid` is `None`, this operates on current thread (tid=0). 317 | /// It cannot manipulate Ambient or Bounding set of other processes. 318 | pub fn clear(tid: Option, cset: CapSet) -> Result<(), CapsError> { 319 | let t = tid.unwrap_or(0); 320 | match cset { 321 | CapSet::Ambient if t == 0 => ambient::clear(), 322 | CapSet::Bounding if t == 0 => bounding::clear(), 323 | CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::clear(t, cset), 324 | _ => Err("operation not supported".into()), 325 | } 326 | } 327 | 328 | /// Raise a single capability in a set for a thread. 329 | /// 330 | /// Capabilities `cap` will be raised from set `cset` of thread `tid`. 331 | /// If `tid` is `None`, this operates on current thread (tid=0). 332 | /// It cannot manipulate Ambient set of other processes. 333 | /// Capabilities cannot be raised in Bounding set. 334 | pub fn raise(tid: Option, cset: CapSet, cap: Capability) -> Result<(), CapsError> { 335 | let t = tid.unwrap_or(0); 336 | match cset { 337 | CapSet::Ambient if t == 0 => ambient::raise(cap), 338 | CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::raise(t, cset, cap), 339 | _ => Err("operation not supported".into()), 340 | } 341 | } 342 | 343 | /// Drop a single capability from a set for a thread. 344 | /// 345 | /// Capabilities `cap` will be dropped from set `cset` of thread `tid`. 346 | /// If `tid` is `None`, this operates on current thread (tid=0). 347 | /// It cannot manipulate Ambient and Bounding sets of other processes. 348 | pub fn drop(tid: Option, cset: CapSet, cap: Capability) -> Result<(), CapsError> { 349 | let t = tid.unwrap_or(0); 350 | match cset { 351 | CapSet::Ambient if t == 0 => ambient::drop(cap), 352 | CapSet::Bounding if t == 0 => bounding::drop(cap), 353 | CapSet::Effective | CapSet::Permitted | CapSet::Inheritable => base::drop(t, cset, cap), 354 | _ => Err("operation not supported".into()), 355 | } 356 | } 357 | 358 | /// Return the set of all capabilities supported by this library. 359 | pub fn all() -> CapsHashSet { 360 | let slice = vec![ 361 | Capability::CAP_CHOWN, 362 | Capability::CAP_DAC_OVERRIDE, 363 | Capability::CAP_DAC_READ_SEARCH, 364 | Capability::CAP_FOWNER, 365 | Capability::CAP_FSETID, 366 | Capability::CAP_KILL, 367 | Capability::CAP_SETGID, 368 | Capability::CAP_SETUID, 369 | Capability::CAP_SETPCAP, 370 | Capability::CAP_LINUX_IMMUTABLE, 371 | Capability::CAP_NET_BIND_SERVICE, 372 | Capability::CAP_NET_BROADCAST, 373 | Capability::CAP_NET_ADMIN, 374 | Capability::CAP_NET_RAW, 375 | Capability::CAP_IPC_LOCK, 376 | Capability::CAP_IPC_OWNER, 377 | Capability::CAP_SYS_MODULE, 378 | Capability::CAP_SYS_RAWIO, 379 | Capability::CAP_SYS_CHROOT, 380 | Capability::CAP_SYS_PTRACE, 381 | Capability::CAP_SYS_PACCT, 382 | Capability::CAP_SYS_ADMIN, 383 | Capability::CAP_SYS_BOOT, 384 | Capability::CAP_SYS_NICE, 385 | Capability::CAP_SYS_RESOURCE, 386 | Capability::CAP_SYS_TIME, 387 | Capability::CAP_SYS_TTY_CONFIG, 388 | Capability::CAP_MKNOD, 389 | Capability::CAP_LEASE, 390 | Capability::CAP_AUDIT_WRITE, 391 | Capability::CAP_AUDIT_CONTROL, 392 | Capability::CAP_SETFCAP, 393 | Capability::CAP_MAC_OVERRIDE, 394 | Capability::CAP_MAC_ADMIN, 395 | Capability::CAP_SYSLOG, 396 | Capability::CAP_WAKE_ALARM, 397 | Capability::CAP_BLOCK_SUSPEND, 398 | Capability::CAP_AUDIT_READ, 399 | Capability::CAP_PERFMON, 400 | Capability::CAP_BPF, 401 | Capability::CAP_CHECKPOINT_RESTORE, 402 | ]; 403 | CapsHashSet::from_iter(slice) 404 | } 405 | 406 | /// Convert an informal capability name into a canonical form. 407 | /// 408 | /// This converts the input string to uppercase and ensures that it starts with 409 | /// `CAP_`, prepending it if necessary. It performs no validity checks so the 410 | /// output may not represent an actual capability. To check if it is, pass it 411 | /// to [`from_str`]. 412 | /// 413 | /// [`from_str`]: enum.Capability.html#method.from_str 414 | pub fn to_canonical(name: &str) -> String { 415 | let uppername = name.to_uppercase(); 416 | if uppername.starts_with("CAP_") { 417 | uppername 418 | } else { 419 | ["CAP_", &uppername].concat() 420 | } 421 | } 422 | 423 | #[cfg(test)] 424 | mod tests { 425 | use super::*; 426 | use std::str::FromStr; 427 | 428 | #[test] 429 | fn test_all_roundtrip() { 430 | let all = all(); 431 | assert!(all.len() > 0); 432 | for c in all { 433 | let name = c.to_string(); 434 | let parsed: Capability = name.parse().unwrap(); 435 | assert_eq!(c, parsed); 436 | } 437 | } 438 | 439 | #[test] 440 | fn test_parse_invalid() { 441 | let p1 = Capability::from_str("CAP_FOO"); 442 | let p1_err = p1.unwrap_err(); 443 | assert!(p1_err.to_string().contains("invalid")); 444 | assert!(format!("{}", p1_err).contains("CAP_FOO")); 445 | let p2: Result = "CAP_BAR".parse(); 446 | assert!(p2.is_err()); 447 | } 448 | 449 | #[test] 450 | fn test_to_canonical() { 451 | let p1 = "foo"; 452 | assert!(Capability::from_str(&to_canonical(p1)).is_err()); 453 | let p2 = "sys_admin"; 454 | assert!(Capability::from_str(&to_canonical(p2)).is_ok()); 455 | let p3 = "CAP_SYS_CHROOT"; 456 | assert!(Capability::from_str(&to_canonical(p3)).is_ok()); 457 | } 458 | 459 | #[test] 460 | #[cfg(feature = "serde_support")] 461 | fn test_serde() { 462 | let input = "CAP_CHOWN"; 463 | // Serialization 464 | { 465 | let p1 = Capability::from_str(input).unwrap(); 466 | let ser = serde_json::to_value(&p1).unwrap(); 467 | let json_str = ser.as_str().unwrap(); 468 | assert_eq!(json_str, input); 469 | let deser: Capability = serde_json::from_value(ser).unwrap(); 470 | assert_eq!(deser, p1); 471 | } 472 | // Deserialization 473 | { 474 | let json_input = format!(r#""{}""#, input); 475 | let deser: Capability = serde_json::from_str(&json_input).unwrap(); 476 | let ser = serde_json::to_value(&deser).unwrap(); 477 | let json_str = ser.as_str().unwrap(); 478 | assert_eq!(json_str, input); 479 | } 480 | } 481 | } 482 | -------------------------------------------------------------------------------- /src/nr.rs: -------------------------------------------------------------------------------- 1 | /* from */ 2 | 3 | pub const CAP_CHOWN: u8 = 0; 4 | pub const CAP_DAC_OVERRIDE: u8 = 1; 5 | pub const CAP_DAC_READ_SEARCH: u8 = 2; 6 | pub const CAP_FOWNER: u8 = 3; 7 | pub const CAP_FSETID: u8 = 4; 8 | pub const CAP_KILL: u8 = 5; 9 | pub const CAP_SETGID: u8 = 6; 10 | pub const CAP_SETUID: u8 = 7; 11 | pub const CAP_SETPCAP: u8 = 8; 12 | pub const CAP_LINUX_IMMUTABLE: u8 = 9; 13 | pub const CAP_NET_BIND_SERVICE: u8 = 10; 14 | pub const CAP_NET_BROADCAST: u8 = 11; 15 | pub const CAP_NET_ADMIN: u8 = 12; 16 | pub const CAP_NET_RAW: u8 = 13; 17 | pub const CAP_IPC_LOCK: u8 = 14; 18 | pub const CAP_IPC_OWNER: u8 = 15; 19 | pub const CAP_SYS_MODULE: u8 = 16; 20 | pub const CAP_SYS_RAWIO: u8 = 17; 21 | pub const CAP_SYS_CHROOT: u8 = 18; 22 | pub const CAP_SYS_PTRACE: u8 = 19; 23 | pub const CAP_SYS_PACCT: u8 = 20; 24 | pub const CAP_SYS_ADMIN: u8 = 21; 25 | pub const CAP_SYS_BOOT: u8 = 22; 26 | pub const CAP_SYS_NICE: u8 = 23; 27 | pub const CAP_SYS_RESOURCE: u8 = 24; 28 | pub const CAP_SYS_TIME: u8 = 25; 29 | pub const CAP_SYS_TTY_CONFIG: u8 = 26; 30 | pub const CAP_MKNOD: u8 = 27; 31 | pub const CAP_LEASE: u8 = 28; 32 | pub const CAP_AUDIT_WRITE: u8 = 29; 33 | pub const CAP_AUDIT_CONTROL: u8 = 30; 34 | pub const CAP_SETFCAP: u8 = 31; 35 | pub const CAP_MAC_OVERRIDE: u8 = 32; 36 | pub const CAP_MAC_ADMIN: u8 = 33; 37 | pub const CAP_SYSLOG: u8 = 34; 38 | pub const CAP_WAKE_ALARM: u8 = 35; 39 | pub const CAP_BLOCK_SUSPEND: u8 = 36; 40 | pub const CAP_AUDIT_READ: u8 = 37; 41 | pub const CAP_PERFMON: u8 = 38; 42 | pub const CAP_BPF: u8 = 39; 43 | pub const CAP_CHECKPOINT_RESTORE: u8 = 40; 44 | 45 | /* from */ 46 | 47 | pub const PR_GET_KEEPCAPS: i32 = 7; 48 | pub const PR_SET_KEEPCAPS: i32 = 8; 49 | pub const PR_CAPBSET_READ: i32 = 23; 50 | pub const PR_CAPBSET_DROP: i32 = 24; 51 | pub const PR_CAP_AMBIENT: i32 = 47; 52 | pub const PR_CAP_AMBIENT_IS_SET: i32 = 1; 53 | pub const PR_CAP_AMBIENT_RAISE: i32 = 2; 54 | pub const PR_CAP_AMBIENT_LOWER: i32 = 3; 55 | pub const PR_CAP_AMBIENT_CLEAR_ALL: i32 = 4; 56 | 57 | /* from */ 58 | 59 | #[cfg(target_arch = "x86")] 60 | pub const CAPGET: i32 = 184; 61 | #[cfg(target_arch = "x86")] 62 | pub const CAPSET: i32 = 185; 63 | 64 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 65 | pub const CAPGET: i64 = 125; 66 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] 67 | pub const CAPSET: i64 = 126; 68 | 69 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] 70 | pub const CAPGET: i32 = 0x40000000 + 125; 71 | #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] 72 | pub const CAPSET: i32 = 0x40000000 + 126; 73 | 74 | #[cfg(target_arch = "aarch64")] 75 | pub const CAPGET: i64 = 90; 76 | #[cfg(target_arch = "aarch64")] 77 | pub const CAPSET: i64 = 91; 78 | 79 | #[cfg(target_arch = "powerpc")] 80 | pub const CAPGET: i32 = 183; 81 | #[cfg(target_arch = "powerpc")] 82 | pub const CAPSET: i32 = 184; 83 | 84 | #[cfg(target_arch = "powerpc64")] 85 | pub const CAPGET: i64 = 183; 86 | #[cfg(target_arch = "powerpc64")] 87 | pub const CAPSET: i64 = 184; 88 | 89 | #[cfg(target_arch = "mips")] 90 | pub const CAPGET: i32 = 4204; 91 | #[cfg(target_arch = "mips")] 92 | pub const CAPSET: i32 = 4205; 93 | 94 | #[cfg(target_arch = "mips64")] 95 | pub const CAPGET: i64 = 5123; 96 | #[cfg(target_arch = "mips64")] 97 | pub const CAPSET: i64 = 5124; 98 | 99 | #[cfg(target_arch = "arm")] 100 | pub const CAPGET: i32 = 184; 101 | #[cfg(target_arch = "arm")] 102 | pub const CAPSET: i32 = 185; 103 | 104 | #[cfg(target_arch = "s390x")] 105 | pub const CAPGET: i64 = 184; 106 | #[cfg(target_arch = "s390x")] 107 | pub const CAPSET: i64 = 185; 108 | 109 | #[cfg(target_arch = "sparc")] 110 | pub const CAPGET: i64 = 21; 111 | #[cfg(target_arch = "sparc")] 112 | pub const CAPSET: i64 = 22; 113 | 114 | #[cfg(target_arch = "sparc64")] 115 | pub const CAPGET: i64 = 21; 116 | #[cfg(target_arch = "sparc64")] 117 | pub const CAPSET: i64 = 22; 118 | 119 | #[cfg(target_arch = "riscv64")] 120 | pub const CAPGET: i64 = 90; 121 | #[cfg(target_arch = "riscv64")] 122 | pub const CAPSET: i64 = 91; 123 | 124 | #[cfg(target_arch = "loongarch64")] 125 | pub const CAPGET: i64 = 90; 126 | #[cfg(target_arch = "loongarch64")] 127 | pub const CAPSET: i64 = 91; 128 | -------------------------------------------------------------------------------- /src/runtime.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Detect kernel features at runtime. 3 | 4 | This module exposes methods to perform detection of kernel 5 | features at runtime. This allows applications to auto-detect 6 | whether recent options are implemented by the currently 7 | running kernel. 8 | 9 | ## Example 10 | 11 | ```rust 12 | let ambient = caps::runtime::ambient_set_supported().is_ok(); 13 | println!("Supported ambient set: {}", ambient); 14 | 15 | let all = caps::runtime::procfs_all_supported(None) 16 | .unwrap_or_else(|_| caps::runtime::thread_all_supported()); 17 | println!("Supported capabilities: {}", all.len()); 18 | ``` 19 | */ 20 | 21 | use super::{ambient, CapSet, Capability, CapsHashSet}; 22 | use crate::errors::CapsError; 23 | use std::io::Read; 24 | use std::path::{Path, PathBuf}; 25 | 26 | /// Check whether the running kernel supports the ambient set. 27 | /// 28 | /// Ambient set was introduced in Linux kernel 4.3. On recent kernels 29 | /// where the ambient set is supported, this will return `Ok`. 30 | /// On a legacy kernel, an `Err` is returned instead. 31 | pub fn ambient_set_supported() -> Result<(), CapsError> { 32 | ambient::has_cap(Capability::CAP_CHOWN)?; 33 | Ok(()) 34 | } 35 | 36 | /// Return the set of all capabilities supported by the running kernel. 37 | /// 38 | /// This requires a mounted `procfs` and a kernel version >= 3.2. By default, 39 | /// it uses `/proc/` as the procfs mountpoint. 40 | pub fn procfs_all_supported(proc_mountpoint: Option) -> Result { 41 | /// See `man 2 capabilities`. 42 | const LAST_CAP_FILEPATH: &str = "./sys/kernel/cap_last_cap"; 43 | let last_cap_path = proc_mountpoint 44 | .unwrap_or_else(|| PathBuf::from("/proc/")) 45 | .join(Path::new(LAST_CAP_FILEPATH)); 46 | 47 | let max_cap: u8 = { 48 | let mut buf = String::with_capacity(4); 49 | std::fs::File::open(last_cap_path.clone()) 50 | .and_then(|mut file| file.read_to_string(&mut buf)) 51 | .map_err(|e| format!("failed to read '{}': {}", last_cap_path.display(), e))?; 52 | buf.trim_end() 53 | .parse() 54 | .map_err(|e| format!("failed to parse '{}': {}", last_cap_path.display(), e))? 55 | }; 56 | 57 | let mut supported = super::all(); 58 | for c in super::all() { 59 | if c.index() > max_cap { 60 | supported.remove(&c); 61 | } 62 | } 63 | Ok(supported) 64 | } 65 | 66 | /// Return the set of all capabilities supported on the current thread. 67 | /// 68 | /// This does not require a mounted `procfs`, and it works with any 69 | /// kernel version >= 2.6.25. 70 | /// It internally uses `prctl(2)` and `PR_CAPBSET_READ`; if those are 71 | /// unavailable, this will result in an empty set. 72 | pub fn thread_all_supported() -> CapsHashSet { 73 | let mut supported = super::all(); 74 | for c in super::all() { 75 | if super::has_cap(None, CapSet::Bounding, c).is_err() { 76 | supported.remove(&c); 77 | } 78 | } 79 | supported 80 | } 81 | -------------------------------------------------------------------------------- /src/securebits.rs: -------------------------------------------------------------------------------- 1 | //! Manipulate securebits flags. 2 | //! 3 | //! This module exposes methods to get and set per-thread securebits 4 | //! flags, which can be used to disable special handling of capabilities 5 | //! for UID 0 (root). 6 | 7 | use crate::errors::CapsError; 8 | use crate::nr; 9 | use std::io::Error; 10 | 11 | /// Return whether the current thread's "keep capabilities" flag is set. 12 | pub fn has_keepcaps() -> Result { 13 | let ret = unsafe { libc::prctl(nr::PR_GET_KEEPCAPS, 0, 0, 0) }; 14 | match ret { 15 | 0 => Ok(false), 16 | 1 => Ok(true), 17 | _ => Err(CapsError::from(format!( 18 | "PR_GET_KEEPCAPS failure: {}", 19 | Error::last_os_error() 20 | ))), 21 | } 22 | } 23 | 24 | /// Set the value of the current thread's "keep capabilities" flag. 25 | pub fn set_keepcaps(keep_caps: bool) -> Result<(), CapsError> { 26 | let flag = if keep_caps { 1 } else { 0 }; 27 | let ret = unsafe { libc::prctl(nr::PR_SET_KEEPCAPS, flag, 0, 0) }; 28 | match ret { 29 | 0 => Ok(()), 30 | _ => Err(CapsError::from(format!( 31 | "PR_SET_KEEPCAPS failure: {}", 32 | Error::last_os_error() 33 | ))), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/ambient.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_ambient_has_cap() { 3 | caps::has_cap(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).unwrap(); 4 | } 5 | 6 | #[test] 7 | fn test_ambient_read() { 8 | caps::read(None, caps::CapSet::Ambient).unwrap(); 9 | } 10 | 11 | #[test] 12 | fn test_ambient_clear() { 13 | caps::clear(None, caps::CapSet::Ambient).unwrap(); 14 | let empty = caps::read(None, caps::CapSet::Ambient).unwrap(); 15 | assert_eq!(empty.len(), 0); 16 | } 17 | 18 | #[test] 19 | fn test_ambient_drop() { 20 | caps::drop(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).unwrap(); 21 | let no_cap = caps::has_cap(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).unwrap(); 22 | assert_eq!(no_cap, false); 23 | } 24 | 25 | #[test] 26 | fn test_ambient_drop_other() { 27 | assert!(caps::drop(Some(1), caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).is_err()); 28 | } 29 | 30 | #[test] 31 | fn test_ambient_raise() { 32 | let r = caps::raise(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN); 33 | let perm = caps::has_cap(None, caps::CapSet::Permitted, caps::Capability::CAP_CHOWN).unwrap(); 34 | let inhe = caps::has_cap(None, caps::CapSet::Inheritable, caps::Capability::CAP_CHOWN).unwrap(); 35 | match (perm, inhe) { 36 | (false, _) => assert!(r.is_err()), 37 | (true, false) => { 38 | caps::raise(None, caps::CapSet::Inheritable, caps::Capability::CAP_CHOWN).unwrap(); 39 | caps::raise(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).unwrap(); 40 | } 41 | (true, true) => r.unwrap(), 42 | }; 43 | } 44 | 45 | #[test] 46 | fn test_ambient_set() { 47 | let mut v = caps::CapsHashSet::new(); 48 | caps::set(None, caps::CapSet::Ambient, &v).unwrap(); 49 | let empty = caps::read(None, caps::CapSet::Ambient).unwrap(); 50 | assert_eq!(empty.len(), 0); 51 | v.insert(caps::Capability::CAP_CHOWN); 52 | caps::drop(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).unwrap(); 53 | assert!(caps::set(None, caps::CapSet::Ambient, &v).is_err()); 54 | } 55 | -------------------------------------------------------------------------------- /tests/bounding.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_bounding_has_cap() { 3 | caps::has_cap( 4 | None, 5 | caps::CapSet::Bounding, 6 | caps::Capability::CAP_SYS_CHROOT, 7 | ) 8 | .unwrap(); 9 | } 10 | 11 | #[test] 12 | fn test_bounding_read() { 13 | caps::read(None, caps::CapSet::Bounding).unwrap(); 14 | } 15 | 16 | #[test] 17 | fn test_bounding_clear() { 18 | let ret = caps::clear(None, caps::CapSet::Bounding); 19 | if caps::has_cap(None, caps::CapSet::Effective, caps::Capability::CAP_SETPCAP).unwrap() { 20 | ret.unwrap(); 21 | let empty = caps::read(None, caps::CapSet::Bounding).unwrap(); 22 | assert_eq!(empty.len(), 0); 23 | } else { 24 | assert!(ret.is_err()); 25 | }; 26 | } 27 | 28 | #[test] 29 | fn test_bounding_drop() { 30 | let ret = caps::drop( 31 | None, 32 | caps::CapSet::Bounding, 33 | caps::Capability::CAP_SYS_CHROOT, 34 | ); 35 | if caps::has_cap(None, caps::CapSet::Effective, caps::Capability::CAP_SETPCAP).unwrap() { 36 | ret.unwrap(); 37 | let set = caps::read(None, caps::CapSet::Bounding).unwrap(); 38 | assert!(!set.contains(&caps::Capability::CAP_SYS_CHROOT)); 39 | } else { 40 | assert!(ret.is_err()); 41 | } 42 | } 43 | 44 | #[test] 45 | fn test_bounding_drop_other() { 46 | assert!(caps::drop(Some(1), caps::CapSet::Bounding, caps::Capability::CAP_CHOWN).is_err()); 47 | } 48 | 49 | #[test] 50 | fn test_bounding_raise() { 51 | assert!(caps::raise(None, caps::CapSet::Bounding, caps::Capability::CAP_CHOWN).is_err()); 52 | } 53 | 54 | #[test] 55 | fn test_bounding_set() { 56 | let v = caps::CapsHashSet::new(); 57 | assert!(caps::set(None, caps::CapSet::Bounding, &v).is_err()); 58 | } 59 | -------------------------------------------------------------------------------- /tests/effective.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_effective_has_cap() { 3 | caps::has_cap(None, caps::CapSet::Effective, caps::Capability::CAP_CHOWN).unwrap(); 4 | } 5 | 6 | #[test] 7 | fn test_effective_read() { 8 | caps::read(None, caps::CapSet::Effective).unwrap(); 9 | } 10 | 11 | #[test] 12 | fn test_effective_clear() { 13 | caps::clear(None, caps::CapSet::Effective).unwrap(); 14 | let empty = caps::read(None, caps::CapSet::Effective).unwrap(); 15 | assert_eq!(empty.len(), 0); 16 | } 17 | 18 | #[test] 19 | fn test_effective_drop() { 20 | caps::drop(None, caps::CapSet::Effective, caps::Capability::CAP_CHOWN).unwrap(); 21 | let no_eff = caps::has_cap(None, caps::CapSet::Effective, caps::Capability::CAP_CHOWN).unwrap(); 22 | assert_eq!(no_eff, false); 23 | } 24 | 25 | #[test] 26 | fn test_effective_raise() { 27 | let perm = caps::has_cap(None, caps::CapSet::Permitted, caps::Capability::CAP_CHOWN).unwrap(); 28 | caps::drop(None, caps::CapSet::Effective, caps::Capability::CAP_CHOWN).unwrap(); 29 | let r = caps::raise(None, caps::CapSet::Effective, caps::Capability::CAP_CHOWN); 30 | if perm { 31 | r.unwrap(); 32 | } else { 33 | assert!(r.is_err()); 34 | } 35 | } 36 | 37 | #[test] 38 | fn test_effective_set() { 39 | let mut v = caps::CapsHashSet::new(); 40 | caps::set(None, caps::CapSet::Effective, &v).unwrap(); 41 | let empty = caps::read(None, caps::CapSet::Effective).unwrap(); 42 | assert_eq!(empty.len(), 0); 43 | v.insert(caps::Capability::CAP_CHOWN); 44 | caps::drop(None, caps::CapSet::Ambient, caps::Capability::CAP_CHOWN).unwrap(); 45 | assert!(caps::set(None, caps::CapSet::Ambient, &v).is_err()); 46 | } 47 | -------------------------------------------------------------------------------- /tests/runtime.rs: -------------------------------------------------------------------------------- 1 | use caps::runtime; 2 | 3 | #[test] 4 | fn test_ambient_supported() { 5 | runtime::ambient_set_supported().unwrap(); 6 | } 7 | 8 | #[test] 9 | fn test_thread_all_supported() { 10 | assert!(runtime::thread_all_supported().len() > 0); 11 | assert!(runtime::thread_all_supported().len() <= caps::all().len()); 12 | } 13 | 14 | #[test] 15 | fn test_procfs_all_supported() { 16 | use std::path::PathBuf; 17 | 18 | let p1 = runtime::procfs_all_supported(None).unwrap(); 19 | let p2 = runtime::procfs_all_supported(Some(PathBuf::from("/proc"))).unwrap(); 20 | let thread = runtime::thread_all_supported(); 21 | let all = caps::all(); 22 | 23 | assert!(thread.len() > 0); 24 | assert!(thread.len() <= all.len()); 25 | assert_eq!( 26 | p1, 27 | p2, 28 | "{:?}", 29 | p1.symmetric_difference(&p2).collect::>() 30 | ); 31 | assert_eq!( 32 | p1, 33 | thread, 34 | "{:?}", 35 | p1.symmetric_difference(&thread).collect::>() 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /tests/securebits.rs: -------------------------------------------------------------------------------- 1 | use caps::securebits; 2 | 3 | #[test] 4 | fn test_keepcaps() { 5 | // Test a roundtrip on SET_KEEPCAPS. 6 | let f0 = securebits::has_keepcaps().unwrap(); 7 | securebits::set_keepcaps(!f0).unwrap(); 8 | let f1 = securebits::has_keepcaps().unwrap(); 9 | assert_eq!(f0, !f1); 10 | securebits::set_keepcaps(!f1).unwrap(); 11 | let f2 = securebits::has_keepcaps().unwrap(); 12 | assert_eq!(f0, f2); 13 | } 14 | --------------------------------------------------------------------------------