linux-keyutils-0.2.3/.cargo_vcs_info.json0000644000000001360000000000100140440ustar { "git": { "sha1": "231c325149a914fc5cea67578db867bdd1efc377" }, "path_in_vcs": "" }linux-keyutils-0.2.3/.github/workflows/checks.yml000064400000000000000000000061061046102023000202170ustar 00000000000000name: Checks on: push: branches: [ "main" ] pull_request: branches: [ "main" ] env: CARGO_TERM_COLOR: always jobs: # Taken from rust-lang/libz-sys CI/CD example. # # This job downloads and stores `cross` as an artifact, so that it can be # redownloaded across all of the jobs. install-cross: runs-on: ubuntu-latest steps: - uses: XAMPPRocky/get-github-release@v1 id: cross with: owner: rust-embedded repo: cross matches: ${{ matrix.platform }} token: ${{ secrets.GITHUB_TOKEN }} - uses: actions/upload-artifact@v3 with: name: cross-${{ matrix.platform }} path: ${{ steps.cross.outputs.install_path }} strategy: matrix: platform: [linux-musl] # Build and run unit tests build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Build run: cargo build --verbose - name: Run tests run: cargo test --verbose # Ensure clippy and formatting pass clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: clippy - name: Run Clippy run: cargo clippy --verbose -- --deny "warnings" - name: Run RustFmt run: cargo fmt -- --check # Cross compile for multiple architectures. # # Cannot run the unit tests until we can specify a seccomp profile for cross' # docker images. Since the default seccomp profile blocks keyctl calls. cross-linux: runs-on: ubuntu-latest needs: install-cross steps: - uses: actions/checkout@v3 with: submodules: 'recursive' - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - name: Download Cross uses: actions/download-artifact@v3 with: name: cross-linux-musl path: /tmp - run: rustup toolchain install ${{ matrix.channel }} - run: chmod +x /tmp/cross - name: Build run: /tmp/cross build --all-features --target ${{ matrix.target }} strategy: fail-fast: false matrix: channel: [stable] target: - mips64-unknown-linux-muslabi64 - mips-unknown-linux-musl - aarch64-unknown-linux-musl - arm-unknown-linux-musleabihf - i686-unknown-linux-musl - x86_64-unknown-linux-musl - x86_64-unknown-linux-gnu # Run miri (commented out since miri doesn't support the keyctl # syscalls) #miri: # name: "miri" # runs-on: ubuntu-latest # steps: # - uses: actions/checkout@v3 # - uses: actions-rs/toolchain@v1 # with: # profile: minimal # toolchain: nightly # override: true # components: miri # - name: Setup Miri # run: cargo +nightly miri setup # - name: Test with Miri # run: cargo +nightly miri test linux-keyutils-0.2.3/.github/workflows/coverage.yml000064400000000000000000000016061046102023000205520ustar 00000000000000name: Coverage # Controls when the action will run. Triggers the workflow on push or pull request # events but only for the master branch on: push: branches: [ main ] pull_request: branches: [ main ] # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: tarpaulin: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - name: Run cargo-tarpaulin uses: actions-rs/tarpaulin@v0.1 with: version: '0.21.0' args: "--lib" - name: Upload to codecov.io uses: codecov/codecov-action@v2 with: file: ./cobertura.xml linux-keyutils-0.2.3/.gitignore000064400000000000000000000000371046102023000146240ustar 00000000000000/target /Cargo.lock *html *xml linux-keyutils-0.2.3/.gitmodules000064400000000000000000000002011046102023000150020ustar 00000000000000[submodule "vendor/keyutils"] path = vendor/keyutils url = git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/keyutils.git linux-keyutils-0.2.3/Cargo.lock0000644000000110540000000000100120200ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "clap" version = "3.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" dependencies = [ "bitflags", "clap_derive", "clap_lex", "indexmap", "once_cell", "textwrap", ] [[package]] name = "clap_derive" version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] name = "indexmap" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", ] [[package]] name = "libc" version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "linux-keyutils" version = "0.2.3" dependencies = [ "bitflags", "clap", "libc", "zeroize", ] [[package]] name = "once_cell" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "os_str_bytes" version = "6.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "unicode-ident" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "zeroize" version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" linux-keyutils-0.2.3/Cargo.toml0000644000000025530000000000100120470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "linux-keyutils" version = "0.2.3" authors = ["landhb "] description = """ Rust interface to the Linux key-management facility. Provides a safe interface around the raw system calls allowing user-space programs to perform key manipulation. """ homepage = "https://github.com/landhb/linux-keyutils" readme = "README.md" keywords = [ "keyutils", "keyctl", "linux", "keyring", "secure-storage", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/landhb/linux-keyutils" [[example]] name = "keyctl" required-features = ["std"] [dependencies.bitflags] version = "1.3" [dependencies.libc] version = "0.2.132" default-features = false [dev-dependencies.clap] version = "3.2.22" features = [ "std", "derive", ] default-features = false [dev-dependencies.zeroize] version = "1.5.7" [features] default = [] std = [] linux-keyutils-0.2.3/Cargo.toml.orig000064400000000000000000000015141046102023000155240ustar 00000000000000[package] name = "linux-keyutils" version = "0.2.3" edition = "2021" authors = ["landhb "] description = """ Rust interface to the Linux key-management facility. Provides a safe interface around the raw system calls allowing user-space programs to perform key manipulation. """ homepage = "https://github.com/landhb/linux-keyutils" repository = "https://github.com/landhb/linux-keyutils" keywords = ["keyutils", "keyctl", "linux","keyring", "secure-storage"] readme = "README.md" license = "Apache-2.0 OR MIT" [features] default = [] std = [] [[example]] name = "keyctl" required-features = ["std"] [dependencies] libc = {version = "0.2.132", default-features = false} bitflags = "1.3" [dev-dependencies] zeroize = "1.5.7" clap = {version = "3.2.22", default-features = false, features = ["std", "derive"]} linux-keyutils-0.2.3/README.md000064400000000000000000000044131046102023000141150ustar 00000000000000# linux-keyutils [![cargo-badge-lib][]][cargo-lib] [![docs-badge-lib][]][docs-lib] [![license-badge][]][license] [![rust-version-badge][]][rust-version] [![build][]][build-url] [![codecov][]][codecov-url] Rust interface to the Linux key-management facility. Provides a safe interface around the raw system calls allowing user-space programs to perform key manipulation. ## Basic Usage To use `linux-keyutils`, first add this to your `Cargo.toml`: ```toml [dependencies] linux-keyutils = "0.2" ``` For more information please view the full [documentation](https://docs.rs/linux-keyutils). There is also a small example program in the [examples directory](examples/keyctl.rs). ## Features * `#![no_std]` by default. * For std programs `KeyError` implements `std::error::Error` when the `std` feature of this crate enabled. * Small footprint, the library only relies on the `libc` and `bitflags` crates. ## License Licensed under either of the following at your discretion: * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions. [//]: # (badges) [license-badge]: https://img.shields.io/badge/license-MIT/Apache--2.0-lightgray.svg?style=flat-square [license]: #license [rust-version-badge]: https://img.shields.io/badge/rust-latest%20stable-blue.svg?style=flat-square [rust-version]: #rust-version-policy [cargo-badge-lib]: https://img.shields.io/crates/v/linux-keyutils.svg?style=flat-square&label=linux-keyutils [cargo-lib]: https://crates.io/crates/linux-keyutils [docs-badge-lib]: https://img.shields.io/docsrs/linux-keyutils/latest?style=flat-square [docs-lib]: https://docs.rs/linux-keyutils [codecov]: https://img.shields.io/codecov/c/github/landhb/linux-keyutils?style=flat-square [codecov-url]: https://codecov.io/gh/landhb/linux-keyutils [build]: https://img.shields.io/github/actions/workflow/status/landhb/linux-keyutils/checks.yml?branch=main&style=flat-square [build-url]: https://github.com/landhb/linux-keyutils/actions?query=workflow%3Achecks linux-keyutils-0.2.3/examples/keyctl.rs000064400000000000000000000062461046102023000163230ustar 00000000000000//! Example CLI application that allows you to interact //! with the Linux kernel keyring from user space. //! //! Demo code for the linux_keyutils crate. use clap::Parser; use linux_keyutils::{KeyPermissionsBuilder, Permission}; use linux_keyutils::{KeyRing, KeyRingIdentifier}; use std::error::Error; use zeroize::Zeroizing; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { #[clap(subcommand)] subcommand: Command, } #[derive(clap::Subcommand, Debug, PartialEq)] enum Command { /// Create a new key Create { #[clap(short, long)] description: String, #[clap(short, long)] secret: String, }, /// Read the secret from a key Read { #[clap(short, long)] description: String, }, /// Change ownership of a key Chown { #[clap(short, long)] description: String, #[clap(short, long)] uid: Option, #[clap(short, long)] gid: Option, }, /// Change permissions of a key Chmod { #[clap(short, long)] description: String, }, /// Invalidate a key Invalidate { #[clap(short, long)] description: String, }, } fn main() -> Result<(), Box> { let args = Args::parse(); // Obtain the default User keyring for the current UID/user // See [KeyRingIdentifier] and `man 2 keyctl` for more information on default // keyrings for processes. let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false)?; _ = match args.subcommand { // Add a new key to the keyring Command::Create { description, secret, } => { let key = ring.add_key(&description, &secret)?; println!("Created key with ID {:?}", key.get_id()); } // Search for an existing key by description and read the secret // data from the keyring Command::Read { description } => { let key = ring.search(&description)?; let mut buf = Zeroizing::new([0u8; 2048]); let len = key.read(&mut buf)?; println!("Secret {:?}", std::str::from_utf8(&buf[..len])?); } // Search for an existing key by description and attempt to // change ownership of the key Command::Chown { description, uid, gid, } => { let key = ring.search(&description)?; key.chown(uid, gid)?; } // Search for an existing key by description and attempt to // change permissions of the key Command::Chmod { description } => { let key = ring.search(&description)?; let perms = KeyPermissionsBuilder::builder() .user(Permission::ALL) .build(); key.set_perms(perms)?; } // Search for an existing key by description and attempt to // invalidate they key Command::Invalidate { description } => { let key = ring.search(&description)?; key.invalidate()?; println!("Removed key with ID {:?}", key.get_id()); } }; Ok(()) } linux-keyutils-0.2.3/src/errors.rs000064400000000000000000000046321046102023000153120ustar 00000000000000use core::fmt::Debug; use core::fmt::Display; use core::fmt::Formatter; use core::fmt::Result; #[cfg(feature = "std")] use std::error::Error; /// Error type for this library, optionally implements `std::error::Error`. #[allow(dead_code)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum KeyError { /// The keyring wasn't available for modification by the user. AccessDenied, /// The key quota for this user would be exceeded by creating /// this key or linking it to the keyring. QuotaExceeded, /// One or more of type, description, and payload points outside /// process's accessible address space. BadAddress, /// Provided bad arguments InvalidArguments, /// The keyring has expired. KeyExpired, /// The keyring has been revoked. KeyRevoked, /// The attempt to generate a new key was rejected. KeyRejected, /// The keyring doesn't exist. KeyringDoesNotExist, /// They key does not exist KeyDoesNotExist, /// Insufficient memory to create a key. OutOfMemory, /// Invalid Description InvalidDescription, /// An invalid identifier was returned InvalidIdentifier, /// Operation not supported OperationNotSupported, /// Write to destination failed WriteError, /// Unknown - catch all, return this instead of panicing Unknown(i32), } impl Display for KeyError { #[inline(always)] fn fmt(&self, f: &mut Formatter) -> Result { ::fmt(self, f) } } #[cfg(feature = "std")] impl Error for KeyError {} impl KeyError { /// Obtain the KeyError derived from checking errno pub fn from_errno() -> KeyError { match unsafe { *libc::__errno_location() } { // Create Errors libc::EACCES => KeyError::AccessDenied, libc::EDQUOT => KeyError::QuotaExceeded, libc::EFAULT => KeyError::BadAddress, libc::EINVAL => KeyError::InvalidArguments, libc::EKEYEXPIRED => KeyError::KeyExpired, libc::EKEYREVOKED => KeyError::KeyRevoked, libc::EKEYREJECTED => KeyError::KeyRejected, libc::ENOMEM => KeyError::OutOfMemory, libc::ENOKEY => KeyError::KeyDoesNotExist, libc::ENOTSUP => KeyError::OperationNotSupported, // Unknown, provide error code for debugging x => KeyError::Unknown(x), } } } linux-keyutils-0.2.3/src/ffi/functions.rs000064400000000000000000000054471046102023000165570ustar 00000000000000//! Raw System Call Wrappers //! use super::types::{KeyCtlOperation, KeySerialId, KeyType}; use crate::KeyError; use alloc::ffi::CString; use core::ffi::CStr; /// add_key() creates or updates a key of the given type and description, instantiates /// it with the payload of length plen, attaches it to the nominated keyring, and /// returns the key's serial number. /// /// The key may be rejected if the provided data is in the wrong format or it is invalid /// in some other way. If the destination keyring already contains a key that matches the /// specified type and description, then, if the key type supports it, that key will be /// updated rather than a new key being created; if not, a new key (with a different ID) /// will be created and it will displace the link to the extant key from the keyring. /// /// The destination keyring serial number may be that of a valid keyring for which the /// caller has write permission. Alternatively, it may be one of the following special /// keyring IDs: pub(crate) fn add_key( ktype: KeyType, keyring: libc::c_ulong, description: &str, payload: Option<&[u8]>, ) -> Result { // Perform conversion into a c string let description = CString::new(description).or(Err(KeyError::InvalidDescription))?; // When creating keyrings the payload will be NULL let (payload, plen) = match payload { Some(p) => (p.as_ptr(), p.len()), None => (core::ptr::null(), 0), }; // Perform the actual system call let res = unsafe { libc::syscall( libc::SYS_add_key, Into::<&'static CStr>::into(ktype).as_ptr(), description.as_ptr(), payload, plen as libc::size_t, keyring as u32, ) }; // Return the underlying error if res < 0 { return Err(KeyError::from_errno()); } // Otherwise return the ID Ok(KeySerialId::new( res.try_into().or(Err(KeyError::InvalidIdentifier))?, )) } /// keyctl() allows user-space programs to perform key manipulation. /// /// The operation performed by keyctl() is determined by the value of the operation argument. /// Each of these operations is wrapped by the KeyCtl interface (provided by the this crate) /// into individual functions (noted below) to permit the compiler to check types. pub(crate) fn keyctl_impl( operation: KeyCtlOperation, arg2: libc::c_ulong, arg3: libc::c_ulong, arg4: libc::c_ulong, arg5: libc::c_ulong, ) -> Result { // Perform the actual system call let res = unsafe { libc::syscall(libc::SYS_keyctl, operation as u32, arg2, arg3, arg4, arg5) }; // Return the underlying error if res < 0 { return Err(KeyError::from_errno()); } // Otherwise return the result Ok(res) } linux-keyutils-0.2.3/src/ffi/mod.rs000064400000000000000000000012461046102023000153170ustar 00000000000000#[allow(dead_code)] mod functions; mod types; macro_rules! keyctl { ( $op:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr ) => { $crate::ffi::keyctl_impl($op, $a2, $a3, $a4, $a5) }; ( $op:expr, $a2:expr, $a3:expr, $a4:expr) => { $crate::ffi::keyctl_impl($op, $a2, $a3, $a4, 0) }; ( $op:expr, $a2:expr, $a3:expr ) => { $crate::ffi::keyctl_impl($op, $a2, $a3, 0, 0) }; ( $op:expr, $a2:expr ) => { $crate::ffi::keyctl_impl($op, $a2, 0, 0, 0) }; } #[allow(unused_imports)] pub use types::*; #[allow(unused_imports)] pub(crate) use functions::{add_key, keyctl_impl}; // Export the macro for use pub(crate) use keyctl; linux-keyutils-0.2.3/src/ffi/types.rs000064400000000000000000000134521046102023000157060ustar 00000000000000//! Definitions ported from the C keyutils library //! use crate::utils::CStr; use crate::KeyError; /// Primary kernel identifier for a key or keyring. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(transparent)] pub struct KeySerialId(pub i32); /// Pre-defined key types the kernel understands. See `man 7 keyrings`. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum KeyType { /// Keyrings are special key types that may contain links to sequences of other /// keys of any type. KeyRing, /// This is a general purpose key type whose payload may be read and updated by /// user-space applications. The key is kept entirely within kernel memory. /// The payload for keys of this type is a blob of arbitrary data of up to 32,767 bytes. User, /// This key type is essentially the same as "user", but it does not permit the key /// to read. This is suitable for storing payloads that you do not want to be /// readable from user space. Logon, /// This key type is similar to "user", but may hold a payload of up to 1 MiB. /// If the key payload is large enough, then it may be stored encrypted in /// tmpfs (which can be swapped out) rather than kernel memory. BigKey, } /// Special identifiers for default keyrings. See `man 7 keyrings`. #[allow(dead_code)] pub enum KeyRingIdentifier { /// Key ID for thread-specific keyring Thread = -1, /// Key ID for process-specific keyring Process = -2, /// Key ID for session-specific keyring Session = -3, /// Key ID for UID-specific keyring User = -4, /// Key ID for UID-session keyring UserSession = -5, /// Key ID for GID-specific keyring Group = -6, /// Key ID for assumed request_key auth key ReqKeyAuthKey = -7, } #[allow(dead_code)] pub enum DefaultKeyring { NoChange = -1, Default = 0, Thread = 1, Process = 2, Session = 3, User = 4, UserSession = 5, Group = 6, } #[allow(dead_code)] #[repr(u32)] pub enum KeyCtlOperation { /// Ask for a keyring's ID GetKeyRingId = 0, /// Join or start named session keyring JoinSessionKeyRing = 1, /// Update a key Update = 2, /// Revoke a key Revoke = 3, /// Set ownership of a key Chown = 4, /// Set permissions of a key SetPerm = 5, /// Describe a key Describe = 6, /// Clear contents of a keyring Clear = 7, /// Link a key into a keyring Link = 8, /// Unlink a key from a keyring Unlink = 9, /// Search for a key in a keyring Search = 10, /// Read a key or keyring's contents Read = 11, /// Instantiate a partially constructed key Instantiate = 12, /// Negate a partially constructed key Negate = 13, /// Set default request-key keyring SetRequestKeyKeyring = 14, /// Set timeout on a key SetTimeout = 15, /// Assume authority to instantiate key AssumeAuthority = 16, /// Get key security label GetSecurityLabel = 17, /// Set my session keyring on my parent process SessionToParent = 18, /// Reject a partially constructed key Reject = 19, /// Instantiate a partially constructed key InstantiageIov = 20, /// Invalidate a key Invalidate = 21, /// Get a user's persistent keyring GetPersistent = 22, /// Compute Diffie-Hellman values DiffieHellmanCompute = 23, /// Query public key parameters PubkeyQuery = 24, /// Encrypt a blob using a public key PubkeyEncrypt = 25, /// Decrypt a blob using a public key PubkeyDecrypt = 26, /// Create a public key signature PubkeySign = 27, /// Verify a public key signature PubkeyVerify = 28, /// Restrict keys allowed to link to a keyring RestrictKeyring = 29, /// Move keys between keyrings Move = 30, /// Find capabilities of keyrings subsystem Capabilities = 31, /// Watch a key or ring of keys for changes WatchKey = 32, } impl KeySerialId { /// Construct from a raw i32 pub fn new(raw: i32) -> Self { Self(raw) } /// Allow conversion into the raw i32 for FFI pub fn as_raw_id(&self) -> i32 { self.0 } } /// Perform the conversion here so that invalid KeyType strings cannot be used. /// Using Rust's type system to ensure only valid strings are provided to the syscall. impl From for &'static CStr { fn from(t: KeyType) -> &'static CStr { unsafe { match t { KeyType::KeyRing => CStr::from_bytes_with_nul_unchecked(b"keyring\0"), KeyType::User => CStr::from_bytes_with_nul_unchecked(b"user\0"), KeyType::Logon => CStr::from_bytes_with_nul_unchecked(b"logon\0"), KeyType::BigKey => CStr::from_bytes_with_nul_unchecked(b"big_key\0"), } } } } /// Perform the conversion here so that invalid KeyType strings cannot be used. /// Using Rust's type system to ensure only valid strings are provided to the syscall. impl TryFrom<&str> for KeyType { type Error = KeyError; fn try_from(s: &str) -> Result { let val = match s { "keyring" => KeyType::KeyRing, "user" => KeyType::User, "logon" => KeyType::Logon, "big_key" => KeyType::BigKey, _ => return Err(KeyError::InvalidIdentifier), }; Ok(val) } } /// Allow easy conversion from i32 to KeySerialId impl From for i32 { fn from(id: KeySerialId) -> i32 { id.0 } } /// Direct conversion impl From for KeySerialId { fn from(n: i32) -> Self { Self(n) } } /// Allow easy conversion from u64 to KeySerialId impl TryFrom for KeySerialId { type Error = KeyError; fn try_from(n: i64) -> Result { Ok(Self(n.try_into().or(Err(KeyError::InvalidIdentifier))?)) } } linux-keyutils-0.2.3/src/key.rs000064400000000000000000000254751046102023000145760ustar 00000000000000use crate::ffi::{self, KeyCtlOperation, KeySerialId}; use crate::utils::Vec; use crate::{KeyError, KeyPermissions, Metadata}; use core::fmt; /// A key corresponding to a specific real ID. /// /// Generally you will either create or obtain a Key via the [KeyRing](crate::KeyRing) /// interface. Since keys must be linked with a keyring to be valid. /// /// For example: /// /// ``` /// use linux_keyutils::{Key, KeyRing, KeyRingIdentifier, KeyError}; /// use zeroize::Zeroize; /// /// // Name of my program's key /// const KEYNAME: &'static str = "my-process-key"; /// /// // Locate the key in the process keyring and update the secret /// fn update_secret + Zeroize>(data: &T) -> Result<(), KeyError> { /// // Get the current process keyring /// let ring = KeyRing::from_special_id(KeyRingIdentifier::Process, false)?; /// /// // Locate the key we previously created /// let key = ring.search(KEYNAME)?; /// /// // Change the data it contains /// key.update(data)?; /// Ok(()) /// } /// ``` #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Key(KeySerialId); impl fmt::Display for Key { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let info = self.metadata().map_err(|_| fmt::Error::default())?; write!(f, "Key({:?})", info) } } impl Key { /// Initialize a new [Key] object from the provided ID pub fn from_id(id: KeySerialId) -> Self { Self(id) } /// Obtain a copy of the ID of this key pub fn get_id(&self) -> KeySerialId { self.0 } /// Obtain information describing the attributes of this key. /// /// The key must grant the caller view permission. pub fn metadata(&self) -> Result { Metadata::from_id(self.0) } /// Read the payload data of a key into a provided mutable slice. /// /// The returned usize is the number of bytes read into the slice. /// /// The key must either grant the caller read permission, or grant /// the caller search permission when searched for from the process /// keyrings (i.e., the key is possessed). pub fn read>(&self, buffer: &mut T) -> Result { // TODO: alternate key types? Currenlty we don't support KeyType::BigKey let len = ffi::keyctl!( KeyCtlOperation::Read, self.0.as_raw_id() as libc::c_ulong, buffer.as_mut().as_mut_ptr() as _, buffer.as_mut().len() as _ )? as usize; Ok(len) } /// Read the payload data of a key, returning a newly allocated vector. /// /// The key must either grant the caller read permission, or grant /// the caller search permission when searched for from the process /// keyrings (i.e., the key is possessed). pub fn read_to_vec(&self) -> Result, KeyError> { // Ensure we have enough room to write up to the maximum for a UserKey let mut buffer = Vec::with_capacity(65536); // Obtain the key let len = ffi::keyctl!( KeyCtlOperation::Read, self.0.as_raw_id() as libc::c_ulong, buffer.as_mut_ptr() as _, buffer.capacity() as _ )? as usize; // Update length unsafe { buffer.set_len(len); } Ok(buffer) } /// Update a key's data payload. /// /// The caller must have write permission on the key specified and the key /// type must support updating. /// /// A negatively instantiated key (see the description of [Key::reject]) /// can be positively instantiated with this operation. pub fn update>(&self, update: &T) -> Result<(), KeyError> { _ = ffi::keyctl!( KeyCtlOperation::Update, self.0.as_raw_id() as libc::c_ulong, update.as_ref().as_ptr() as _, update.as_ref().len() as _ )?; Ok(()) } /// Change the permissions of the key with the ID provided /// /// If the caller doesn't have the CAP_SYS_ADMIN capability, it can change /// permissions only only for the keys it owns. (More precisely: the caller's /// filesystem UID must match the UID of the key.) pub fn set_perms(&self, perm: KeyPermissions) -> Result<(), KeyError> { _ = ffi::keyctl!( KeyCtlOperation::SetPerm, self.0.as_raw_id() as libc::c_ulong, perm.bits() as _ )?; Ok(()) } /// Change the ownership (user and group ID) of a key. /// /// For the UID to be changed, or for the GID to be changed to a group /// the caller is not a member of, the caller must have the CAP_SYS_ADMIN /// capability (see capabilities(7)). /// /// If the UID is to be changed, the new user must have sufficient quota /// to accept the key. The quota deduction will be removed from the old /// user to the new user should the UID be changed. pub fn chown(&self, uid: Option, gid: Option) -> Result<(), KeyError> { let uid_opt = uid.unwrap_or(u32::MAX); let gid_opt = gid.unwrap_or(u32::MAX); _ = ffi::keyctl!( KeyCtlOperation::Chown, self.0.as_raw_id() as libc::c_ulong, uid_opt as _, gid_opt as _ )?; Ok(()) } /// Set a timeout on a key. /// /// Specifying the timeout value as 0 clears any existing timeout on the key. /// /// The `/proc/keys` file displays the remaining time until each key will expire. /// (This is the only method of discovering the timeout on a key.) /// /// The caller must either have the setattr permission on the key or hold an /// instantiation authorization token for the key. /// /// The key and any links to the key will be automatically garbage collected /// after the timeout expires. Subsequent attempts to access the key will /// then fail with the error EKEYEXPIRED. /// /// This operation cannot be used to set timeouts on revoked, expired, or /// negatively instantiated keys. pub fn set_timeout(&self, seconds: usize) -> Result<(), KeyError> { _ = ffi::keyctl!( KeyCtlOperation::SetTimeout, self.0.as_raw_id() as libc::c_ulong, seconds as _ )?; Ok(()) } /// Revoke this key. Similar to [Key::reject] just without the timeout. /// /// The key is scheduled for garbage collection; it will no longer be findable, /// and will be unavailable for further operations. Further attempts to use the /// key will fail with the error `EKEYREVOKED`. /// /// The caller must have write or setattr permission on the key. pub fn revoke(&self) -> Result<(), KeyError> { _ = ffi::keyctl!(KeyCtlOperation::Revoke, self.0.as_raw_id() as libc::c_ulong)?; Ok(()) } /// Mark a key as negatively instantiated and set an expiration timer on the key. /// /// This will prevent others from retrieving the key in further searches. And they /// will receive a `EKEYREJECTED` error when performing the search. /// /// Similar to [Key::revoke] but with a timeout. pub fn reject(&self, seconds: usize) -> Result<(), KeyError> { _ = ffi::keyctl!( KeyCtlOperation::Reject, self.0.as_raw_id() as libc::c_ulong, seconds as _, libc::EKEYREJECTED as _ )?; Ok(()) } /// Mark a key as invalid. /// /// To invalidate a key, the caller must have search permission on the /// key. /// /// This operation marks the key as invalid and schedules immediate /// garbage collection. The garbage collector removes the invali‐ /// dated key from all keyrings and deletes the key when its refer‐ /// ence count reaches zero. After this operation, the key will be /// ignored by all searches, even if it is not yet deleted. /// /// Keys that are marked invalid become invisible to normal key oper‐ /// ations immediately, though they are still visible in `/proc/keys` /// (marked with an 'i' flag) until they are actually removed. pub fn invalidate(&self) -> Result<(), KeyError> { ffi::keyctl!( KeyCtlOperation::Invalidate, self.0.as_raw_id() as libc::c_ulong )?; Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::{KeyRing, KeyRingIdentifier, KeyType, Permission}; use zeroize::Zeroizing; #[test] fn test_from_raw_id() { let raw: i32 = 0x12345; let _key = Key::from_id(raw.into()); } #[test] fn test_metadata() { let secret = "Test Data"; // Obtain the default User keyring let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap(); // Create the key let key = ring.add_key("my-info-key", secret).unwrap(); // Obtain and verify the info let info = key.metadata().unwrap(); assert_eq!(info.get_type(), KeyType::User); assert_eq!(info.get_uid(), unsafe { libc::geteuid() }); assert_eq!(info.get_gid(), unsafe { libc::getegid() }); assert_eq!(info.get_perms().bits(), 0x3F010000); assert_eq!(info.get_description(), "my-info-key"); // Cleanup key.invalidate().unwrap() } #[test] fn test_read_into_vec() { let secret = "Test Data"; // Obtain the default User keyring let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap(); // Create the key let key = ring.add_key("vec-read-key", secret).unwrap(); // Verify the payload let payload = key.read_to_vec().unwrap(); assert_eq!(secret.as_bytes(), &payload); key.invalidate().unwrap(); } #[test] fn test_user_keyring_add_key() { let secret = "Test Data"; // Obtain the default User keyring let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap(); // Create the key let key = ring.add_key("my-super-secret-test-key", secret).unwrap(); // A buffer that is ensured to be zeroed when // out of scope let mut buf = Zeroizing::new([0u8; 4096]); // Allow P/U/G full permissions let mut perms = KeyPermissions::new(); perms.set_posessor_perms(Permission::ALL); perms.set_user_perms(Permission::ALL); perms.set_group_perms(Permission::ALL); // Set the permissions key.set_perms(perms).unwrap(); // Read the secret and verify it matches let len = key.read(&mut buf).unwrap(); assert_eq!(secret.as_bytes(), &buf[..len]); // Update it key.update(&"wow".as_bytes()).unwrap(); // Verify it matches the new content let len = key.read(&mut buf).unwrap(); assert_eq!("wow".as_bytes(), &buf[..len]); key.invalidate().unwrap() } } linux-keyutils-0.2.3/src/keyring.rs000064400000000000000000000270441046102023000154500ustar 00000000000000use crate::ffi::{self, KeyCtlOperation}; use crate::utils::{CStr, CString, Vec}; use crate::{Key, KeyError, KeyRingIdentifier, KeySerialId, KeyType, LinkNode, Links, Metadata}; use core::convert::TryInto; /// Interface to perform keyring operations. Used to locate, create, /// search, add, and link/unlink keys to & from keyrings. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct KeyRing { id: KeySerialId, } impl KeyRing { /// Initialize a new [Key] object from the provided ID pub(crate) fn from_id(id: KeySerialId) -> Self { Self { id } } /// Obtain a KeyRing from its special identifier. /// /// If the create argument is true, then this method will attempt /// to create the keyring. Otherwise it will only succeed if the /// keyring already exists and is valid. /// /// Internally this uses KEYCTL_GET_KEYRING_ID to resolve a keyrings /// real ID from the special identifier. pub fn from_special_id(id: KeyRingIdentifier, create: bool) -> Result { let id: KeySerialId = ffi::keyctl!( KeyCtlOperation::GetKeyRingId, id as libc::c_ulong, u32::from(create).into() )? .try_into() .or(Err(KeyError::InvalidIdentifier))?; Ok(Self { id }) } /// Get the persistent keyring (persistent-keyring(7)) of the current user /// and link it to a specified keyring. /// /// If the call is successful, a link to the persistent keyring is added to the /// keyring specified in the `link_with` argument. /// /// The caller must have write permission on the keyring. /// /// The persistent keyring will be created by the kernel if it does not yet exist. /// /// Each time the [KeyRing::get_persistent] operation is performed, the persistent /// keyring will have its expiration timeout reset to the value in: /// /// `/proc/sys/kernel/keys/persistent_keyring_expiry` /// /// Should the timeout be reached, the persistent keyring will be removed and /// everything it pins can then be garbage collected. /// /// Persistent keyrings were added to Linux in kernel version 3.13. pub fn get_persistent(link_with: KeyRingIdentifier) -> Result { let id: KeySerialId = ffi::keyctl!( KeyCtlOperation::GetPersistent, u32::MAX as _, link_with as libc::c_ulong )? .try_into() .or(Err(KeyError::InvalidIdentifier))?; Ok(Self { id }) } /// Obtain information describing the attributes of this keyring. /// /// The keyring must grant the caller view permission. pub fn metadata(&self) -> Result { Metadata::from_id(self.id) } /// Creates or updates a key of the given type and description, instantiates /// it with the payload of length plen, attaches it to the User keyring. /// /// If the destination keyring already contains a key that matches /// the specified type and description, then, if the key type supports /// it, that key will be updated rather than a new key being created; /// if not, a new key (with a different ID) will be created and it will /// displace the link to the extant key from the keyring. pub fn add_key + ?Sized, S: AsRef<[u8]> + ?Sized>( &self, description: &D, secret: &S, ) -> Result { let id = ffi::add_key( KeyType::User, self.id.as_raw_id() as libc::c_ulong, description.as_ref(), Some(secret.as_ref()), )?; Ok(Key::from_id(id)) } /// Search for a key in the keyring tree, starting with this keyring as the head, /// returning its ID. /// /// The search is performed breadth-first and recursively. /// /// The source keyring must grant search permission to the caller. When /// performing the recursive search, only keyrings that grant the caller search /// permission will be searched. Only keys with for which the caller has /// search permission can be found. /// /// If the key is found, its ID is returned as the function result. pub fn search + ?Sized>(&self, description: &D) -> Result { // The provided description must be properly null terminated for the kernel let description = CString::new(description.as_ref()).or(Err(KeyError::InvalidDescription))?; // Perform the raw syscall and validate that the result is a valid ID let id: KeySerialId = ffi::keyctl!( KeyCtlOperation::Search, self.id.as_raw_id() as libc::c_ulong, Into::<&'static CStr>::into(KeyType::User).as_ptr() as _, description.as_ptr() as _, 0 )? .try_into() .or(Err(KeyError::InvalidIdentifier))?; // Construct a key object from the ID Ok(Key::from_id(id)) } /// Obtain a list of the keys/keyrings linked to this keyring. /// /// This method allocates, but you can provide a maximum number of entries /// to read. Each returned entry is 4 bytes. /// /// The keyring must either grant the caller read permission, or grant /// the caller search permission. pub fn get_links(&self, max: usize) -> Result { // Allocate the requested capacity let mut buffer = Vec::::with_capacity(max); // Perform the read let len = ffi::keyctl!( KeyCtlOperation::Read, self.id.as_raw_id() as libc::c_ulong, buffer.as_mut_ptr() as _, buffer.capacity() as _ )? as usize; // Set the size of the results unsafe { buffer.set_len(len / core::mem::size_of::()); } // Remap the results to complete keys Ok(buffer .iter() .filter_map(|&id| LinkNode::from_id(id).ok()) .collect()) } /// Create a link from this keyring to a key. /// /// If a key with the same type and description is already linked in the keyring, /// then that key is displaced from the keyring. /// /// Before creating the link, the kernel checks the nesting of the keyrings /// and returns appropriate errors if the link would produce a cycle or if the /// nesting of keyrings would be too deep (The limit on the nesting of keyrings is /// determined by the kernel constant KEYRING_SEARCH_MAX_DEPTH, defined with the /// value 6, and is necessary to prevent overflows on the kernel stack when /// recursively searching keyrings). /// /// The caller must have link permission on the key being added and write /// permission on the keyring. pub fn link_key(&self, key: Key) -> Result<(), KeyError> { _ = ffi::keyctl!( KeyCtlOperation::Link, key.get_id().as_raw_id() as _, self.id.as_raw_id() as libc::c_ulong )?; Ok(()) } /// Unlink a key from this keyring. /// /// If the key is not currently linked into the keyring, an error results. If the /// last link to a key is removed, then that key will be scheduled for destruction. /// /// The caller must have write permission on the keyring from which the key is being /// removed. pub fn unlink_key(&self, key: Key) -> Result<(), KeyError> { _ = ffi::keyctl!( KeyCtlOperation::Unlink, key.get_id().as_raw_id() as _, self.id.as_raw_id() as libc::c_ulong )?; Ok(()) } /// Clear the contents of (i.e., unlink all keys from) this keyring. /// /// The caller must have write permission on the keyring. pub fn clear(&self) -> Result<(), KeyError> { _ = ffi::keyctl!(KeyCtlOperation::Clear, self.id.as_raw_id() as libc::c_ulong)?; Ok(()) } } #[cfg(test)] mod test { use super::*; use crate::{KeyPermissionsBuilder, Permission}; #[test] fn test_from_special_id() { // Test that a keyring that normally doesn't exist by default is // created when called. let ring = KeyRing::from_special_id(KeyRingIdentifier::Thread, true).unwrap(); assert!(ring.id.as_raw_id() > 0); // Test that a keyring that should already exist is returned let ring = KeyRing::from_special_id(KeyRingIdentifier::User, false).unwrap(); assert!(ring.id.as_raw_id() > 0); } #[test] fn test_get_persistent() { // Test that a keyring that should already exist is returned let user_ring = KeyRing::from_special_id(KeyRingIdentifier::User, false).unwrap(); assert!(user_ring.id.as_raw_id() > 0); let user_perm_ring = KeyRing::get_persistent(KeyRingIdentifier::User).unwrap(); assert_ne!(user_ring, user_perm_ring); } #[test] fn test_metadata() { // Test that a keyring that normally doesn't exist by default is // created when called. let ring = KeyRing::from_special_id(KeyRingIdentifier::Thread, true).unwrap(); assert!(ring.id.as_raw_id() > 0); // Obtain and verify the info let info = ring.metadata().unwrap(); assert_eq!(info.get_type(), KeyType::KeyRing); assert_eq!(info.get_uid(), unsafe { libc::geteuid() }); assert_eq!(info.get_gid(), unsafe { libc::getegid() }); assert_eq!(info.get_description(), "_tid"); } #[test] fn test_search_existing_key() { // Test that a keyring that normally doesn't exist by default is // created when called. let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap(); let key = ring.add_key("test_search", b"data").unwrap(); // Ensure we have search permission on the key let perms = KeyPermissionsBuilder::builder() .posessor(Permission::ALL) .user(Permission::ALL) .build(); // Enforce perms key.set_perms(perms).unwrap(); // Search should succeed let result = ring.search("test_search").unwrap(); // Assert that the ID is the same assert_eq!(key.get_id(), result.get_id()); // Invalidate the key key.invalidate().unwrap(); } #[test] fn test_search_non_existing_key() { // Test that a keyring that normally doesn't exist by default is // created when called. let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap(); // Search should succeed let result = ring.search("test_search_no_exist"); // Assert that the ID is the same assert!(result.is_err()); assert_eq!(result.unwrap_err(), KeyError::KeyDoesNotExist); } #[test] fn test_get_linked_items() { // Test that a keyring that should already exist is returned let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false).unwrap(); assert!(ring.id.as_raw_id() > 0); // Add the key let key = ring.add_key("test_read_key", b"test").unwrap(); // Obtain a list of the linked keys let items = ring.get_links(200).unwrap(); // Assert that the key is in the ring assert!(items.len() > 0); assert!(items.contains(&key)); // Use the alternate reference to the key let key_ref = items.get(&key).unwrap().as_key().unwrap(); // Invalidate the key key_ref.invalidate().unwrap(); // Assert that the key is no longer on the ring let items = ring.get_links(200).unwrap(); assert!(!items.contains(&key)); } } linux-keyutils-0.2.3/src/lib.rs000064400000000000000000000060701046102023000145420ustar 00000000000000//! Rust interface to the Linux key-management facility. //! Provides a safe interface around the raw system calls allowing //! user-space programs to perform key manipulation. //! //! Example usage: //! //! ``` //! use linux_keyutils::{Key, KeyRing, KeyError, KeyRingIdentifier}; //! use linux_keyutils::{KeyPermissionsBuilder, Permission}; //! //! fn main() -> Result<(), KeyError> { //! // Obtain the default session keyring for the current process //! // See [KeyRingIdentifier] and `man 2 keyctl` for more information on default //! // keyrings for processes. //! let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false)?; //! //! // Insert a new key //! let key = ring.add_key("my-new-key", b"secret")?; //! //! // Utiltiies to create proper permissions //! let perms = KeyPermissionsBuilder::builder() //! .posessor(Permission::ALL) //! .user(Permission::ALL) //! .group(Permission::VIEW | Permission::READ) //! .build(); //! //! // Perform manipulations on the key such as setting permissions //! key.set_perms(perms)?; //! //! // Or setting a timeout for how long the key should exist //! key.set_timeout(300)?; //! //! // Or invalidating (removing) the key //! key.invalidate()?; //! Ok(()) //! } //! ``` //! //! To look for an existing key you can use the [KeyRing::search] method. Usage: //! //! ``` //! use linux_keyutils::{Key, KeyRing, KeyError, KeyRingIdentifier}; //! use linux_keyutils::{KeyPermissionsBuilder, Permission}; //! //! fn get_key(description: &str) -> Result { //! // Obtain the default session keyring for the current process //! // See `KeyRingIdentifier` and `man 7 keyrings` for more information on default //! // keyrings for processes and users. //! let ring = KeyRing::from_special_id(KeyRingIdentifier::Session, false)?; //! //! // Lookup an existing key //! let key = ring.search(description)?; //! Ok(key) //! } //! ``` #![cfg_attr(not(feature = "std"), no_std)] #![deny(warnings)] // CString requires alloc however extern crate alloc; // Use the std-lib when available #[cfg(feature = "std")] mod utils { pub use std::ffi::{CStr, CString}; pub use std::string::String; pub use std::vec::Vec; } // #![no_std] CStr/CString support stabilized in Rust 1.64.0 #[cfg(not(feature = "std"))] mod utils { pub use alloc::ffi::CString; pub use alloc::string::String; pub use alloc::vec::Vec; pub use core::ffi::CStr; } // Internal FFI for raw syscalls mod ffi; // Export certain FFI types pub use ffi::{KeyRingIdentifier, KeySerialId, KeyType}; // Expose error types mod errors; pub use errors::KeyError; // Primary keyring interface mod keyring; pub use keyring::KeyRing; // Primary key interface mod key; pub use key::Key; // Information about nodes (either keys or keyrings) mod metadata; pub use metadata::Metadata; // Nodes in a ring/tree mod links; pub use links::{LinkNode, Links}; // Expose KeyPermissions API mod permissions; pub use permissions::{KeyPermissions, KeyPermissionsBuilder, Permission}; linux-keyutils-0.2.3/src/links.rs000064400000000000000000000067711046102023000151240ustar 00000000000000//! Helper types for iterating over keyring entries //! use crate::utils::Vec; use crate::{Key, KeyError, KeyRing, KeySerialId, KeyType, Metadata}; use core::cmp::PartialEq; use core::ops::Deref; /// An item/node linked to a ring. Both keys and other keyrings /// can be linked to a particular keyring. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum LinkNode { KeyRing(KeyRing), Key(Key), } /// A collection of LinkNodes, returned from [KeyRing::get_links] /// /// For example: /// /// ``` /// use linux_keyutils::{Key, KeyRing, KeyRingIdentifier, KeyError}; /// /// // Test if a particular Key is linked to the user session KeyRing /// fn is_linked_to_user_session(key: &Key) -> Result { /// // Get the keyring /// let ring = KeyRing::from_special_id(KeyRingIdentifier::UserSession, false)?; /// /// // Locate all the links /// let links = ring.get_links(200)?; /// /// // Determine if the key is linked to the ring /// Ok(links.contains(key)) /// } /// ``` #[derive(Debug, Clone, PartialEq, Eq)] pub struct Links(Vec); impl PartialEq for LinkNode { fn eq(&self, other: &Key) -> bool { matches!(self, LinkNode::Key(x) if x == other) } } impl PartialEq for &LinkNode { fn eq(&self, other: &Key) -> bool { matches!(self, LinkNode::Key(x) if x == other) } } impl PartialEq for LinkNode { fn eq(&self, other: &KeyRing) -> bool { matches!(self, LinkNode::KeyRing(x) if x == other) } } impl PartialEq for &LinkNode { fn eq(&self, other: &KeyRing) -> bool { matches!(self, LinkNode::KeyRing(x) if x == other) } } impl LinkNode { /// Internal method to construct a LinkNode from a raw ID pub(crate) fn from_id(id: KeySerialId) -> Result { let metadata = Metadata::from_id(id)?; let node = match metadata.get_type() { KeyType::KeyRing => Self::KeyRing(KeyRing::from_id(id)), KeyType::User => Self::Key(Key::from_id(id)), _ => return Err(KeyError::OperationNotSupported), }; Ok(node) } /// Attempt to convert this LinkNode to a Key /// /// Returns the key if the entry is a Key, None otherwise. pub fn as_key(&self) -> Option { match self { Self::Key(inner) => Some(*inner), _ => None, } } /// Attempt to convert this LinkNode to a KeyRing /// /// Returns the ring if the entry is a KeyRing, None otherwise. pub fn as_ring(&self) -> Option { match self { Self::KeyRing(inner) => Some(*inner), _ => None, } } } impl Deref for Links { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl FromIterator for Links { fn from_iter(iter: T) -> Self where T: IntoIterator, { Self(iter.into_iter().collect()) } } impl Links { /// Internal constructor to abstract the list of objects pub fn new(inner: Vec) -> Self { Self(inner) } /// Obtain the entry with the provided ID/Key/Keyring pub fn get(&self, entry: &T) -> Option<&LinkNode> where LinkNode: PartialEq, { self.0.iter().find(|v| *v == entry) } /// Check if the list contains the provided ID/Key/Keyring pub fn contains(&self, entry: &T) -> bool where LinkNode: PartialEq, { self.0.iter().any(|v| v == entry) } } linux-keyutils-0.2.3/src/metadata.rs000064400000000000000000000065201046102023000155540ustar 00000000000000use crate::ffi::{self, KeyCtlOperation, KeySerialId}; use crate::utils::{CStr, String}; use crate::{KeyError, KeyPermissions, KeyType}; use alloc::string::ToString; use core::str::{self, FromStr}; /// Information about the given node/entry. /// Returned by [Key::metadata](crate::Key::metadata) /// or [KeyRing::metadata](crate::KeyRing::metadata) #[derive(Debug, Clone)] pub struct Metadata { ktype: KeyType, uid: u32, gid: u32, perm: KeyPermissions, description: String, } impl FromStr for Metadata { type Err = KeyError; /// The returned string contains the following information about /// the key: /// /// `type;uid;gid;perm;description` /// /// And is then parsed into a valid [Metadata] struct. /// /// In the above, type and description are strings, uid and gid are /// decimal strings, and perm is a hexadecimal permissions mask. fn from_str(s: &str) -> Result { // Begin parsing let mut iter = s.split(';'); // Parse type into KeyType let ktype: KeyType = iter .next() .and_then(|v| v.try_into().ok()) .ok_or(KeyError::InvalidDescription)?; // Parse the UID let uid = iter .next() .and_then(|v| v.parse().ok()) .ok_or(KeyError::InvalidDescription)?; // Parse the GID let gid = iter .next() .and_then(|v| v.parse().ok()) .ok_or(KeyError::InvalidDescription)?; // Parse the permissions let perms: u32 = iter .next() .and_then(|v| u32::from_str_radix(v, 16).ok()) .ok_or(KeyError::InvalidDescription)?; // Copy the actual description let description = iter.next().ok_or(KeyError::InvalidDescription)?.to_string(); // Create the description Ok(Self { ktype, uid, gid, perm: KeyPermissions::from_u32(perms), description, }) } } impl Metadata { /// Internal method to derive information from an /// arbitrary node based on ID alone. pub(crate) fn from_id(id: KeySerialId) -> Result { let mut result = alloc::vec![0u8; 512]; // Obtain the description from the kernel let len = ffi::keyctl!( KeyCtlOperation::Describe, id.as_raw_id() as libc::c_ulong, result.as_mut_ptr() as _, result.len() as _ )? as usize; // Construct the CStr first to remove the null terminator let cs = CStr::from_bytes_with_nul(&result[..len]).or(Err(KeyError::InvalidDescription))?; // Construct the string from the resulting data ensuring utf8 compat let s = cs.to_str().or(Err(KeyError::InvalidDescription))?; Self::from_str(s) } /// The type of this entry pub fn get_type(&self) -> KeyType { self.ktype } /// The owning UID of this entry pub fn get_uid(&self) -> u32 { self.uid } /// The owning GID of this entry pub fn get_gid(&self) -> u32 { self.gid } /// The current permissions of this entry pub fn get_perms(&self) -> KeyPermissions { self.perm } /// The description for this entry pub fn get_description(&self) -> &str { &self.description } } linux-keyutils-0.2.3/src/permissions.rs000064400000000000000000000152431046102023000163510ustar 00000000000000//! Create a more rust-like permissions construct, ported from the unix //! permissions defined in keyutils.h use bitflags::bitflags; /// Construct key permissions for use with [Key::set_perms](crate::Key::set_perms) /// or returned by [Metadata::get_perms](crate::Metadata::get_perms). /// /// Usage: /// /// ``` /// use linux_keyutils::{Permission, KeyPermissions}; /// /// let mut perms = KeyPermissions::new(); /// perms.set_user_perms(Permission::ALL); /// perms.set_group_perms(Permission::VIEW); /// ``` #[derive(Debug, Copy, Clone)] pub struct KeyPermissions(u32); /// Construct key permissions with the builder pattern. /// /// Usage: /// /// ``` /// use linux_keyutils::{Permission, KeyPermissionsBuilder}; /// /// let perms = KeyPermissionsBuilder::builder() /// .user(Permission::ALL) /// .group(Permission::VIEW) /// .build(); /// ``` #[derive(Debug, Copy, Clone)] pub struct KeyPermissionsBuilder(KeyPermissions); bitflags! { /// Pre-defined bit-flags to construct permissions easily. #[repr(transparent)] pub struct Permission: u8 { /// Allows viewing a key's attributes const VIEW = 0x1; /// Allows reading a key's payload / viewing a keyring const READ = 0x2; /// Allows writing/updating a key's payload / adding a link to keyring const WRITE = 0x4; /// Allows finding a key in search / searching a keyring const SEARCH = 0x8; /// Allows creating a link to a key/keyring const LINK = 0x10; /// Allows setting attributes for a key const SETATTR = 0x20; /// Allows all actions const ALL = 0x3f; } } impl Default for KeyPermissions { fn default() -> Self { Self::new() } } impl KeyPermissions { /// Create a new KeyPermissions object, defaults to empty permissions pub fn new() -> Self { Self(0) } /// Construct the permissions manually pub fn from_u32(raw: u32) -> Self { Self(raw) } /// Obtain the u32 bits for this set pub fn bits(&self) -> u32 { self.0 } /// Set the permissions available to the key's possessor pub fn set_posessor_perms(&mut self, perm: Permission) { self.0 &= !(0xFF << 24); self.0 += (perm.bits() as u32) << 24; } /// Set the permissions available to the key's owning user (UID) pub fn set_user_perms(&mut self, perm: Permission) { self.0 &= !(0xFF << 16); self.0 += (perm.bits() as u32) << 16; } /// Set the permissions available to the key's owning group (GID) pub fn set_group_perms(&mut self, perm: Permission) { self.0 &= !(0xFF << 8); self.0 += (perm.bits() as u32) << 8; } /// Set the permissions available to any 3rd party pub fn set_world_perms(&mut self, perm: Permission) { self.0 &= !0xFF; self.0 += perm.bits() as u32; } } impl KeyPermissionsBuilder { /// Start a KeyPermissionsBuilder pub fn builder() -> Self { Self(KeyPermissions::default()) } /// Set the permissions available to the key's possessor pub fn posessor(mut self, perm: Permission) -> Self { self.0.set_posessor_perms(perm); self } /// Set the permissions available to the key's owning user (UID) pub fn user(mut self, perm: Permission) -> Self { self.0.set_user_perms(perm); self } /// Set the permissions available to the key's owning group (GID) pub fn group(mut self, perm: Permission) -> Self { self.0.set_group_perms(perm); self } /// Set the permissions available to any 3rd party pub fn world(mut self, perm: Permission) -> Self { self.0.set_world_perms(perm); self } /// Finish the build and obtain the created KeyPermissions pub fn build(self) -> KeyPermissions { self.0 } } #[test] fn test_posessor_perms() { let mut perm = KeyPermissions::default(); // Initial perm.set_posessor_perms(Permission::ALL); assert_eq!(perm.0, 0x3f000000); // Update perm.set_posessor_perms(Permission::SEARCH); assert_eq!(perm.0, 0x08000000); // Combination perm.set_posessor_perms(Permission::SEARCH | Permission::VIEW); assert_eq!(perm.0, 0x09000000); // Combination two perm.set_posessor_perms( Permission::SETATTR | Permission::VIEW | Permission::READ | Permission::WRITE, ); assert_eq!(perm.0, 0x27000000); } #[test] fn test_user_perms() { let mut perm = KeyPermissions::default(); // Initial perm.set_user_perms(Permission::ALL); assert_eq!(perm.0, 0x003f0000); // Update perm.set_user_perms(Permission::SEARCH); assert_eq!(perm.0, 0x00080000); // Combination perm.set_user_perms(Permission::SEARCH | Permission::VIEW); assert_eq!(perm.0, 0x00090000); // Combination2 perm.set_user_perms( Permission::SETATTR | Permission::VIEW | Permission::READ | Permission::WRITE, ); assert_eq!(perm.0, 0x00270000); } #[test] fn test_group_perms() { let mut perm = KeyPermissions::default(); // Initial perm.set_group_perms(Permission::ALL); assert_eq!(perm.0, 0x00003f00); // Update perm.set_group_perms(Permission::SEARCH); assert_eq!(perm.0, 0x00000800); // Combination perm.set_group_perms(Permission::SEARCH | Permission::VIEW); assert_eq!(perm.0, 0x00000900); // Combination2 perm.set_group_perms( Permission::SETATTR | Permission::VIEW | Permission::READ | Permission::WRITE, ); assert_eq!(perm.0, 0x00002700); } #[test] fn test_world_perms() { let mut perm = KeyPermissions::default(); // Initial perm.set_world_perms(Permission::ALL); assert_eq!(perm.0, 0x0000003f); // Update perm.set_world_perms(Permission::SEARCH); assert_eq!(perm.0, 0x00000008); // Combination perm.set_world_perms(Permission::SEARCH | Permission::VIEW); assert_eq!(perm.0, 0x00000009); // Combination2 perm.set_world_perms( Permission::SETATTR | Permission::VIEW | Permission::READ | Permission::WRITE, ); assert_eq!(perm.0, 0x00000027); } #[test] fn test_combined_perms() { let mut perm = KeyPermissions::default(); // Posessor perm.set_posessor_perms(Permission::ALL); assert_eq!(perm.0, 0x3f000000); // User perm.set_user_perms(Permission::VIEW | Permission::READ | Permission::WRITE); assert_eq!(perm.0, 0x3f070000); // Group perm.set_group_perms(Permission::SEARCH | Permission::VIEW); assert_eq!(perm.0, 0x3f070900); // World perm.set_world_perms( Permission::SETATTR | Permission::VIEW | Permission::READ | Permission::WRITE, ); assert_eq!(perm.0, 0x3f070927); } linux-keyutils-0.2.3/tarpaulin.toml000064400000000000000000000001001046102023000155170ustar 00000000000000[coverage] run-types = ["Lib"] [report] out = ["Html", "Xml"]