diff --git a/.cirrus.yml b/.cirrus.yml index adcb9e6..97b0574 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -19,13 +19,12 @@ rustfmt_task: linux_task: matrix: - container: - image: rust:1.36.0 + image: rust:1.38.0 - container: image: rust:latest - allow_failures: true container: image: rustlang/rust:nightly - keyutils_script: apt-get update && apt-get install libkeyutils-dev lockfile_script: cargo generate-lockfile cargo_cache: folder: $CARGO_HOME/registry @@ -50,7 +49,6 @@ coverage_task: image: rust:latest environment: CODECOV_TOKEN: ENCRYPTED[1e221ef78a37c960613ff80db7141f3158e3218031934395466f4720f450b7acfd74e587819435ce9be0b13fa1b68f1b] - keyutils_script: apt-get update && apt-get install libkeyutils-dev tarpaulin_cache: folder: .ci populate_script: .ci/tarpaulin.sh diff --git a/Cargo.toml b/Cargo.toml index 96b1d68..f5765ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,6 @@ edition = "2018" [workspace] members = ["keyutils-raw"] -[features] -static = ["keyutils-raw/static"] - [dev-dependencies] lazy_static = "1" regex = "1" @@ -26,7 +23,9 @@ semver = "*" [dependencies] bitflags = "1.0.4" errno = "0.2" +itertools = "0.9" keyutils-raw = { path = "keyutils-raw" } log = "0.4.4" +uninit = "0.3" -libc = "0.2" +libc = "0.2.68" diff --git a/keyutils-raw/Cargo.toml b/keyutils-raw/Cargo.toml index f2172b1..8c8f9bc 100644 --- a/keyutils-raw/Cargo.toml +++ b/keyutils-raw/Cargo.toml @@ -7,11 +7,11 @@ description = "Raw bindings to Linux keyring syscalls" repository = "https://github.com/mathstuf/rust-keyutils.git" homepage = "https://github.com/mathstuf/rust-keyutils" keywords = ["keyutils"] -links = "keyutils" edition = "2018" -[features] -static = [] - [dependencies] +log = "0.4.4" + +errno = "0.2" libc = "0.2" +uninit = "0.3" diff --git a/keyutils-raw/build.rs b/keyutils-raw/build.rs deleted file mode 100644 index 74aac32..0000000 --- a/keyutils-raw/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2017, Ben Boeckel -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of this project nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -fn main() { - if cfg!(feature = "static") { - println!("cargo:rustc-link-lib=static=keyutils"); - } else { - println!("cargo:rustc-link-lib=keyutils"); - } -} diff --git a/keyutils-raw/src/functions.rs b/keyutils-raw/src/functions.rs index cd8bf07..f061bd6 100644 --- a/keyutils-raw/src/functions.rs +++ b/keyutils-raw/src/functions.rs @@ -24,124 +24,357 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{KeyPermissions, KeyringSerial, TimeoutSeconds}; - -// Remove when rust-lang/rust#60300 is in stable -#[allow(improper_ctypes)] - -#[rustfmt::skip] -extern "C" { - pub fn add_key( - type_: *const libc::c_char, - description: *const libc::c_char, - payload: *const libc::c_void, - plen: libc::size_t, - keyring: KeyringSerial) - -> KeyringSerial; - pub fn request_key( - type_: *const libc::c_char, - description: *const libc::c_char, - callout_info: *const libc::c_char, - keyring: Option<KeyringSerial>) - -> KeyringSerial; - - pub fn keyctl_get_keyring_ID( - id: KeyringSerial, - create: libc::c_int) - -> KeyringSerial; - pub fn keyctl_join_session_keyring( - name: *const libc::c_char) - -> KeyringSerial; - pub fn keyctl_update( - id: KeyringSerial, - payload: *const libc::c_void, - plen: libc::size_t) - -> libc::c_long; - pub fn keyctl_revoke( - id: KeyringSerial) - -> libc::c_long; - pub fn keyctl_chown( - id: KeyringSerial, - uid: libc::uid_t, - gid: libc::gid_t) - -> libc::c_long; - pub fn keyctl_setperm( - id: KeyringSerial, - perm: KeyPermissions) - -> libc::c_long; - pub fn keyctl_describe( - id: KeyringSerial, - buffer: *mut libc::c_char, - buflen: libc::size_t) - -> libc::c_long; - pub fn keyctl_clear( - ringid: KeyringSerial) - -> libc::c_long; - pub fn keyctl_link( - id: KeyringSerial, - ringid: KeyringSerial) - -> libc::c_long; - pub fn keyctl_unlink( - id: KeyringSerial, - ringid: KeyringSerial) - -> libc::c_long; - pub fn keyctl_search( - ringid: KeyringSerial, - type_: *const libc::c_char, - description: *const libc::c_char, - destringid: Option<KeyringSerial>) - -> libc::c_long; - pub fn keyctl_read( - id: KeyringSerial, - buffer: *mut libc::c_char, - buflen: libc::size_t) - -> libc::c_long; - pub fn keyctl_instantiate( - id: KeyringSerial, - payload: *const libc::c_void, - plen: libc::size_t, - ringid: Option<KeyringSerial>) - -> libc::c_long; - pub fn keyctl_negate( - id: KeyringSerial, - timeout: TimeoutSeconds, - ringid: Option<KeyringSerial>) - -> libc::c_long; - pub fn keyctl_set_reqkey_keyring( - reqkey_defl: libc::c_int) - -> libc::c_long; - pub fn keyctl_set_timeout( - key: KeyringSerial, - timeout: TimeoutSeconds) - -> libc::c_long; - pub fn keyctl_assume_authority( - key: Option<KeyringSerial>) - -> libc::c_long; - pub fn keyctl_get_security( - key: KeyringSerial, - buffer: *mut libc::c_char, - buflen: libc::size_t) - -> libc::c_long; - //pub fn keyctl_session_to_parent() - // -> libc::c_long; - pub fn keyctl_reject( - id: KeyringSerial, - timeout: TimeoutSeconds, - error: libc::c_uint, - ringid: Option<KeyringSerial>) - -> libc::c_long; - pub fn keyctl_invalidate( - id: KeyringSerial) - -> libc::c_long; - pub fn keyctl_get_persistent( - uid: libc::uid_t, - id: KeyringSerial) - -> libc::c_long; - pub fn keyctl_dh_compute( - private: KeyringSerial, - prime: KeyringSerial, - base: KeyringSerial, - buffer: *mut libc::c_char, - buflen: libc::size_t) - -> libc::c_long; +use std::convert::TryInto; +use std::ffi::CString; +use std::ptr; + +use log::error; +use uninit::out_ref::Out; + +use crate::{DefaultKeyring, KeyPermissions, KeyringSerial, TimeoutSeconds}; + +/// Reexport of `Errno` as `Error`. +type Error = errno::Errno; +/// Simpler `Result` type with the error already set. +type Result<T> = std::result::Result<T, Error>; + +fn check_syscall(res: libc::c_long) -> Result<libc::c_long> { + if res == -1 { + Err(errno::errno()) + } else { + Ok(res) + } +} + +static THE_KERNEL_LIED: &str = concat!( + "It appears as though the kernel made a 64-bit key ID. Please report a bug.\n\n", + env!("CARGO_PKG_REPOSITORY"), +); +static ZERO_KEY_ID_FOUND: &str = concat!( + "It appears as though a key ID of zero was found. This is novel and should not happen. Please \ + report a bug.\n\n", + env!("CARGO_PKG_REPOSITORY"), +); +static BUFFER_OVERFLOW: &str = concat!( + "The kernel returned a size that could not be represented as a `usize`. This should not be \ + possible. Please report a bug.\n\n", + env!("CARGO_PKG_REPOSITORY"), +); + +fn cstring(s: &str) -> CString { + CString::new(s.as_bytes()).unwrap() +} + +fn opt_cstring(opt: Option<&str>) -> Option<CString> { + opt.map(cstring) +} + +fn opt_cstring_ptr(opt: &Option<CString>) -> *const libc::c_char { + opt.as_ref().map_or(ptr::null(), |cs| cs.as_ptr()) +} + +fn opt_key_serial(opt: Option<KeyringSerial>) -> i32 { + opt.map(KeyringSerial::get).unwrap_or(0) +} + +fn keyring_serial(res: libc::c_long) -> KeyringSerial { + KeyringSerial::new(res.try_into().expect(THE_KERNEL_LIED)).expect(ZERO_KEY_ID_FOUND) +} + +fn default_keyring(res: libc::c_long) -> Result<DefaultKeyring> { + res.try_into().map_err(|err: crate::UnknownDefault| { + error!( + concat!( + "The kernel has returned an unexpected default keyring ID: {}. Please report a \ + bug.\n\n", + env!("CARGO_PKG_REPOSITORY"), + ), + err.0, + ); + errno::Errno(libc::EINVAL) + }) +} + +fn size(res: libc::c_long) -> usize { + res.try_into().expect(BUFFER_OVERFLOW) +} + +fn ignore(res: libc::c_long) { + assert_eq!(res, 0); +} + +macro_rules! syscall { + ( $( $arg:expr, )* ) => { + check_syscall(libc::syscall($( $arg, )*)) + }; +} + +macro_rules! keyctl { + ( $( $arg:expr, )* ) => { + syscall!(libc::SYS_keyctl, $( $arg, )*) + }; +} + +pub fn add_key( + type_: &str, + description: &str, + payload: &[u8], + keyring: KeyringSerial, +) -> Result<KeyringSerial> { + let type_cstr = cstring(type_); + let desc_cstr = cstring(description); + unsafe { + syscall!( + libc::SYS_add_key, + type_cstr.as_ptr(), + desc_cstr.as_ptr(), + payload.as_ptr() as *const libc::c_void, + payload.len(), + keyring.get(), + ) + } + .map(keyring_serial) +} + +pub fn request_key( + type_: &str, + description: &str, + callout_info: Option<&str>, + keyring: Option<KeyringSerial>, +) -> Result<KeyringSerial> { + let type_cstr = cstring(type_); + let desc_cstr = cstring(description); + let callout_cstr = opt_cstring(callout_info); + let callout_ptr = opt_cstring_ptr(&callout_cstr); + + unsafe { + syscall!( + libc::SYS_request_key, + type_cstr.as_ptr(), + desc_cstr.as_ptr(), + callout_ptr, + opt_key_serial(keyring), + ) + } + .map(keyring_serial) +} + +pub fn keyctl_get_keyring_id(id: KeyringSerial, create: bool) -> Result<KeyringSerial> { + unsafe { + keyctl!( + libc::KEYCTL_GET_KEYRING_ID, + id.get(), + if create { 1 } else { 0 }, + ) + } + .map(keyring_serial) +} + +pub fn keyctl_join_session_keyring(name: Option<&str>) -> Result<KeyringSerial> { + let name_cstr = opt_cstring(name); + let name_ptr = opt_cstring_ptr(&name_cstr); + + unsafe { keyctl!(libc::KEYCTL_JOIN_SESSION_KEYRING, name_ptr,) }.map(keyring_serial) +} + +pub fn keyctl_update(id: KeyringSerial, payload: &[u8]) -> Result<()> { + unsafe { + keyctl!( + libc::KEYCTL_UPDATE, + id.get(), + payload.as_ptr() as *const libc::c_void, + payload.len(), + ) + } + .map(ignore) +} + +pub fn keyctl_revoke(id: KeyringSerial) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_REVOKE, id.get(),) }.map(ignore) +} + +pub fn keyctl_chown( + id: KeyringSerial, + uid: Option<libc::uid_t>, + gid: Option<libc::gid_t>, +) -> Result<()> { + unsafe { + keyctl!( + libc::KEYCTL_CHOWN, + id.get(), + uid.unwrap_or(!0), + gid.unwrap_or(!0), + ) + } + .map(ignore) +} + +pub fn keyctl_setperm(id: KeyringSerial, perm: KeyPermissions) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_SETPERM, id.get(), perm,) }.map(ignore) +} + +pub fn keyctl_describe(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> { + unsafe { + let capacity = buffer.as_mut().map_or(0, |b| b.len()); + keyctl!( + libc::KEYCTL_DESCRIBE, + id.get(), + buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()), + capacity, + ) + } + .map(size) +} + +pub fn keyctl_clear(id: KeyringSerial) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_CLEAR, id.get(),) }.map(ignore) +} + +pub fn keyctl_link(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_LINK, id.get(), ringid.get(),) }.map(ignore) +} + +pub fn keyctl_unlink(id: KeyringSerial, ringid: KeyringSerial) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_UNLINK, id.get(), ringid.get(),) }.map(ignore) +} + +pub fn keyctl_search( + ringid: KeyringSerial, + type_: &str, + description: &str, + destringid: Option<KeyringSerial>, +) -> Result<KeyringSerial> { + let type_cstr = cstring(type_); + let desc_cstr = cstring(description); + unsafe { + keyctl!( + libc::KEYCTL_SEARCH, + ringid.get(), + type_cstr.as_ptr(), + desc_cstr.as_ptr(), + opt_key_serial(destringid), + ) + } + .map(keyring_serial) +} + +pub fn keyctl_read(id: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> { + unsafe { + let capacity = buffer.as_mut().map_or(0, |b| b.len()); + keyctl!( + libc::KEYCTL_READ, + id.get(), + buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()), + capacity, + ) + } + .map(size) +} + +pub fn keyctl_instantiate( + id: KeyringSerial, + payload: &[u8], + ringid: Option<KeyringSerial>, +) -> Result<()> { + unsafe { + keyctl!( + libc::KEYCTL_INSTANTIATE, + id.get(), + payload.as_ptr() as *const libc::c_void, + payload.len(), + opt_key_serial(ringid), + ) + } + .map(ignore) +} + +pub fn keyctl_negate( + id: KeyringSerial, + timeout: TimeoutSeconds, + ringid: Option<KeyringSerial>, +) -> Result<()> { + unsafe { + keyctl!( + libc::KEYCTL_NEGATE, + id.get(), + timeout, + opt_key_serial(ringid), + ) + } + .map(ignore) +} + +pub fn keyctl_set_reqkey_keyring(reqkey_defl: DefaultKeyring) -> Result<DefaultKeyring> { + unsafe { keyctl!(libc::KEYCTL_SET_REQKEY_KEYRING, reqkey_defl,) }.and_then(default_keyring) +} + +pub fn keyctl_set_timeout(key: KeyringSerial, timeout: TimeoutSeconds) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_SET_TIMEOUT, key.get(), timeout,) }.map(ignore) +} + +pub fn keyctl_assume_authority(key: Option<KeyringSerial>) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_ASSUME_AUTHORITY, opt_key_serial(key),) }.map(ignore) +} + +pub fn keyctl_get_security(key: KeyringSerial, mut buffer: Option<Out<[u8]>>) -> Result<usize> { + unsafe { + let capacity = buffer.as_mut().map_or(0, |b| b.len()); + keyctl!( + libc::KEYCTL_GET_SECURITY, + key.get(), + buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()), + capacity, + ) + } + .map(size) +} + +pub fn keyctl_reject( + id: KeyringSerial, + timeout: TimeoutSeconds, + error: errno::Errno, + ringid: Option<KeyringSerial>, +) -> Result<()> { + unsafe { + keyctl!( + libc::KEYCTL_REJECT, + id.get(), + timeout, + error, + opt_key_serial(ringid), + ) + } + .map(ignore) +} + +pub fn keyctl_invalidate(id: KeyringSerial) -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_INVALIDATE, id.get(),) }.map(ignore) +} + +pub fn keyctl_get_persistent(uid: libc::uid_t, id: KeyringSerial) -> Result<KeyringSerial> { + unsafe { keyctl!(libc::KEYCTL_GET_PERSISTENT, uid, id.get(),) }.map(keyring_serial) +} + +pub fn keyctl_session_to_parent() -> Result<()> { + unsafe { keyctl!(libc::KEYCTL_SESSION_TO_PARENT,) }.map(ignore) +} + +pub fn keyctl_dh_compute( + private: KeyringSerial, + prime: KeyringSerial, + base: KeyringSerial, + mut buffer: Option<Out<[u8]>>, +) -> Result<usize> { + unsafe { + let capacity = buffer.as_mut().map_or(0, |b| b.len()); + keyctl!( + libc::KEYCTL_DH_COMPUTE, + private.get(), + prime.get(), + base.get(), + buffer.as_mut().map_or(ptr::null(), |b| b.as_mut_ptr()), + capacity, + ) + } + .map(size) } diff --git a/keyutils-raw/src/types.rs b/keyutils-raw/src/types.rs index bda3222..f55608c 100644 --- a/keyutils-raw/src/types.rs +++ b/keyutils-raw/src/types.rs @@ -72,7 +72,7 @@ pub enum DefaultKeyring { } #[derive(Debug, PartialEq, Eq)] -pub struct UnknownDefault(libc::c_long); +pub struct UnknownDefault(pub libc::c_long); impl TryFrom<libc::c_long> for DefaultKeyring { type Error = UnknownDefault; diff --git a/src/api.rs b/src/api.rs index 314f5e4..0136c4d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -26,15 +26,14 @@ use std::borrow::Borrow; use std::convert::TryInto; -use std::ffi::CString; use std::mem; -use std::ptr; use std::result; use std::str; use std::time::Duration; use keyutils_raw::*; use log::error; +use uninit::extension_traits::VecCapacity; use crate::constants::{Permission, SpecialKeyring}; use crate::keytype::*; @@ -45,39 +44,35 @@ pub type Error = errno::Errno; /// Simpler `Result` type with the error already set. pub type Result<T> = result::Result<T, Error>; -fn check_call(res: libc::c_long) -> Result<()> { - match res { - -1 => Err(errno::errno()), - _ => Ok(()), - } -} - -fn check_call_key(res: KeyringSerial) -> Result<Key> { - check_call(res.get().into())?; - Ok(Key::new_impl(res)) -} - -fn check_call_keyring(res: KeyringSerial) -> Result<Keyring> { - check_call(res.get().into())?; - Ok(Keyring::new_impl(res)) -} - -fn into_serial(res: libc::c_long) -> KeyringSerial { - KeyringSerial::new(res as i32).unwrap() -} - /// Request a key from the kernel. fn request_impl<K: KeyType>( description: &str, info: Option<&str>, id: Option<KeyringSerial>, -) -> KeyringSerial { - let type_cstr = CString::new(K::name()).unwrap(); - let desc_cstr = CString::new(description).unwrap(); - let info_cstr = info.map(|i| CString::new(i).unwrap()); +) -> Result<KeyringSerial> { + request_key(K::name(), description, info, id) +} - let info_ptr = info_cstr.map_or(ptr::null(), |cs| cs.as_ptr()); - unsafe { request_key(type_cstr.as_ptr(), desc_cstr.as_ptr(), info_ptr, id) } +fn read_impl(id: KeyringSerial) -> Result<Vec<u8>> { + // Get the size of the description. + let mut sz = keyctl_read(id, None)?; + // Allocate this description. + let mut buffer = vec![0; sz]; + loop { + let write_buffer = buffer.get_backing_buffer(); + // Fetch the description. + sz = keyctl_read(id, Some(write_buffer))?; + + // If we got everything, exit. + if sz <= buffer.capacity() { + break; + } + + // Resize for the additional capacity we need. + buffer.resize(sz, 0); + } + buffer.truncate(sz); + Ok(buffer) } /// Representation of a kernel keyring. @@ -120,9 +115,7 @@ impl Keyring { /// If the kernel returns a keyring value which the library does not understand, the conversion /// from the return value into a `DefaultKeyring` will panic. pub fn set_default(keyring: DefaultKeyring) -> Result<DefaultKeyring> { - let ret = unsafe { keyctl_set_reqkey_keyring(keyring as libc::c_int) }; - check_call(ret)?; - Ok(ret.try_into().unwrap()) + keyctl_set_reqkey_keyring(keyring) } /// Requests a keyring with the given description by searching the thread, process, and session @@ -140,15 +133,16 @@ impl Keyring { I: Into<Option<&'s str>>, T: Into<Option<TargetKeyring<'a>>>, { - check_call_keyring(request_impl::<keytypes::Keyring>( + request_impl::<keytypes::Keyring>( description.as_ref(), info.into().as_ref().copied(), target.into().map(TargetKeyring::serial), - )) + ) + .map(Self::new_impl) } fn get_keyring(id: SpecialKeyring, create: bool) -> Result<Keyring> { - check_call_keyring(unsafe { keyctl_get_keyring_ID(id.serial(), create.into()) }) + keyctl_get_keyring_id(id.serial(), create).map(Self::new_impl) } /// Attach to a special keyring. Fails if the keyring does not already exist. @@ -163,7 +157,7 @@ impl Keyring { /// Create a new anonymous keyring and set it as the session keyring. pub fn join_anonymous_session() -> Result<Self> { - check_call_keyring(unsafe { keyctl_join_session_keyring(ptr::null()) }) + keyctl_join_session_keyring(None).map(Self::new_impl) } /// Attached to a named session keyring. @@ -174,15 +168,14 @@ impl Keyring { where N: AsRef<str>, { - let name_cstr = CString::new(name.as_ref()).unwrap(); - check_call_keyring(unsafe { keyctl_join_session_keyring(name_cstr.as_ptr()) }) + keyctl_join_session_keyring(Some(name.as_ref())).map(Self::new_impl) } /// Clears the contents of the keyring. /// /// Requires `write` permission on the keyring. pub fn clear(&mut self) -> Result<()> { - check_call(unsafe { keyctl_clear(self.id) }) + keyctl_clear(self.id) } /// Adds a link to `key` to the keyring. @@ -190,14 +183,14 @@ impl Keyring { /// Any link to an existing key with the same description is removed. Requires `write` /// permission on the keyring and `link` permission on the key. pub fn link_key(&mut self, key: &Key) -> Result<()> { - check_call(unsafe { keyctl_link(key.id, self.id) }) + keyctl_link(key.id, self.id) } /// Removes the link to `key` from the keyring. /// /// Requires `write` permission on the keyring. pub fn unlink_key(&mut self, key: &Key) -> Result<()> { - check_call(unsafe { keyctl_unlink(key.id, self.id) }) + keyctl_unlink(key.id, self.id) } /// Adds a link to `keyring` to the keyring. @@ -205,30 +198,30 @@ impl Keyring { /// Any link to an existing keyring with the same description is removed. Requires `write` /// permission on the current keyring and `link` permission on the linked keyring. pub fn link_keyring(&mut self, keyring: &Keyring) -> Result<()> { - check_call(unsafe { keyctl_link(keyring.id, self.id) }) + keyctl_link(keyring.id, self.id) } /// Removes the link to `keyring` from the keyring. /// /// Requires `write` permission on the keyring. pub fn unlink_keyring(&mut self, keyring: &Keyring) -> Result<()> { - check_call(unsafe { keyctl_unlink(keyring.id, self.id) }) + keyctl_unlink(keyring.id, self.id) } - fn search_impl<K>(&self, description: &str, destination: Option<&mut Keyring>) -> KeyringSerial + fn search_impl<K>( + &self, + description: &str, + destination: Option<&mut Keyring>, + ) -> Result<KeyringSerial> where K: KeyType, { - let type_cstr = CString::new(K::name()).unwrap(); - let desc_cstr = CString::new(description).unwrap(); - into_serial(unsafe { - keyctl_search( - self.id, - type_cstr.as_ptr(), - desc_cstr.as_ptr(), - destination.map(|dest| dest.id), - ) - }) + keyctl_search( + self.id, + K::name(), + description, + destination.map(|dest| dest.id), + ) } /// Recursively search the keyring for a key with the matching description. @@ -242,9 +235,8 @@ impl Keyring { D: Borrow<K::Description>, DK: Into<Option<&'a mut Keyring>>, { - check_call_key( - self.search_impl::<K>(&description.borrow().description(), destination.into()), - ) + self.search_impl::<K>(&description.borrow().description(), destination.into()) + .map(Key::new_impl) } /// Recursively search the keyring for a keyring with the matching description. @@ -258,10 +250,11 @@ impl Keyring { D: Borrow<<keytypes::Keyring as KeyType>::Description>, DK: Into<Option<&'a mut Keyring>>, { - check_call_keyring(self.search_impl::<keytypes::Keyring>( + self.search_impl::<keytypes::Keyring>( &description.borrow().description(), destination.into(), - )) + ) + .map(Self::new_impl) } /// Return all immediate children of the keyring. @@ -280,22 +273,32 @@ impl Keyring { return Err(errno::Errno(libc::ENOTDIR)); } - let sz = unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }; - check_call(sz)?; - let mut buffer = Vec::with_capacity((sz as usize) / mem::size_of::<KeyringSerial>()); - let actual_sz = unsafe { - keyctl_read( - self.id, - buffer.as_mut_ptr() as *mut libc::c_char, - sz as usize, - ) + let buffer = read_impl(self.id)?; + let keyring_children = { + let chunk_size = mem::size_of::<KeyringSerial>(); + let chunks = buffer.chunks(chunk_size); + chunks.map(|chunk| { + let bytes = chunk.try_into().map_err(|err| { + error!( + "A keyring did not have the right number of bytes for a child key or \ + keyring ID: {}", + err, + ); + errno::Errno(libc::EINVAL) + })?; + let id = i32::from_ne_bytes(bytes); + let serial = KeyringSerial::new(id).ok_or_else(|| { + error!("A keyring had a child key or keyring ID of 0"); + errno::Errno(libc::EINVAL) + })?; + Ok(Key::new_impl(serial)) + }) }; - check_call(actual_sz)?; - unsafe { buffer.set_len((actual_sz as usize) / mem::size_of::<KeyringSerial>()) }; let mut keys = Vec::new(); let mut keyrings = Vec::new(); - for key in buffer.into_iter().map(Key::new_impl) { + for key in keyring_children { + let key = key?; match key.description() { Ok(description) => { if description.type_ == keytypes::Keyring::name() { @@ -318,7 +321,7 @@ impl Keyring { /// /// If one does not exist, it will be created. Requires `write` permission on the keyring. pub fn attach_persistent(&mut self) -> Result<Self> { - check_call_keyring(into_serial(unsafe { keyctl_get_persistent(!0, self.id) })) + keyctl_get_persistent(!0, self.id).map(Self::new_impl) } /// Adds a key of a specific type to the keyring. @@ -331,7 +334,8 @@ impl Keyring { D: Borrow<K::Description>, P: Borrow<K::Payload>, { - check_call_key(self.add_key_impl::<K>(description.borrow(), payload.borrow())) + self.add_key_impl::<K>(description.borrow(), payload.borrow()) + .map(Key::new_impl) } /// Monomorphization of adding a key. @@ -339,22 +343,16 @@ impl Keyring { &mut self, description: &K::Description, payload: &K::Payload, - ) -> KeyringSerial + ) -> Result<KeyringSerial> where K: KeyType, { - let type_cstr = CString::new(K::name()).unwrap(); - let desc_cstr = CString::new(description.description().as_bytes()).unwrap(); - let payload = payload.payload(); - unsafe { - add_key( - type_cstr.as_ptr(), - desc_cstr.as_ptr(), - payload.as_ptr() as *const libc::c_void, - payload.len(), - self.id, - ) - } + add_key( + K::name(), + &description.description(), + &payload.payload(), + self.id, + ) } /// Adds a keyring to the current keyring. @@ -365,14 +363,15 @@ impl Keyring { where D: Borrow<<keytypes::Keyring as KeyType>::Description>, { - check_call_keyring(self.add_key_impl::<keytypes::Keyring>(description.borrow(), &())) + self.add_key_impl::<keytypes::Keyring>(description.borrow(), &()) + .map(Self::new_impl) } /// Revokes the keyring. /// /// Requires `write` permission on the keyring. pub fn revoke(self) -> Result<()> { - check_call(unsafe { keyctl_revoke(self.id) }) + keyctl_revoke(self.id) } /// Change the user which owns the keyring. @@ -380,7 +379,7 @@ impl Keyring { /// Requires the `setattr` permission on the keyring and the SysAdmin capability to change it /// to anything other than the current user. pub fn chown(&mut self, uid: libc::uid_t) -> Result<()> { - check_call(unsafe { keyctl_chown(self.id, uid, !0) }) + keyctl_chown(self.id, Some(uid), None) } /// Change the group which owns the keyring. @@ -388,7 +387,7 @@ impl Keyring { /// Requires the `setattr` permission on the keyring and the SysAdmin capability to change it /// to anything other than a group of which the current user is a member. pub fn chgrp(&mut self, gid: libc::gid_t) -> Result<()> { - check_call(unsafe { keyctl_chown(self.id, !0, gid) }) + keyctl_chown(self.id, None, Some(gid)) } /// Set the permissions on the keyring. @@ -396,27 +395,35 @@ impl Keyring { /// Requires the `setattr` permission on the keyring and the SysAdmin capability if the current /// user does not own the keyring. pub fn set_permissions(&mut self, perms: Permission) -> Result<()> { - check_call(unsafe { keyctl_setperm(self.id, perms.bits()) }) + keyctl_setperm(self.id, perms.bits()) } #[cfg(test)] pub(crate) fn set_permissions_raw(&mut self, perms: KeyPermissions) -> Result<()> { - check_call(unsafe { keyctl_setperm(self.id, perms) }) + keyctl_setperm(self.id, perms) } fn description_raw(&self) -> Result<String> { - let sz = unsafe { keyctl_describe(self.id, ptr::null_mut(), 0) }; - check_call(sz)?; - let mut buffer = Vec::with_capacity(sz as usize); - let actual_sz = unsafe { - keyctl_describe( - self.id, - buffer.as_mut_ptr() as *mut libc::c_char, - sz as usize, - ) - }; - check_call(actual_sz)?; - unsafe { buffer.set_len((actual_sz - 1) as usize) }; + // Get the size of the description. + let mut sz = keyctl_describe(self.id, None)?; + // Allocate this description. + let mut buffer = vec![0; sz]; + loop { + let write_buffer = buffer.get_backing_buffer(); + // Fetch the description. + sz = keyctl_describe(self.id, Some(write_buffer))?; + + // If we got everything, exit. + if sz <= buffer.capacity() { + break; + } + + // Resize for the additional capacity we need. + buffer.resize(sz, 0); + } + // Remove 1 from the size for the trailing NUL the kernel adds. + buffer.truncate(sz.saturating_sub(1)); + // The kernel guarantees that we get ASCII data from this. let str_slice = str::from_utf8(&buffer[..]).unwrap(); Ok(str_slice.to_owned()) } @@ -436,24 +443,32 @@ impl Keyring { /// Any partial seconds are ignored. A timeout of 0 means "no expiration". Requires the /// `setattr` permission on the keyring. pub fn set_timeout(&mut self, timeout: Duration) -> Result<()> { - check_call(unsafe { keyctl_set_timeout(self.id, timeout.as_secs() as TimeoutSeconds) }) + keyctl_set_timeout(self.id, timeout.as_secs() as TimeoutSeconds) } /// The security context of the keyring. Depends on the security manager loaded into the kernel /// (e.g., SELinux or AppArmor). pub fn security(&self) -> Result<String> { - let sz = unsafe { keyctl_get_security(self.id, ptr::null_mut(), 0) }; - check_call(sz)?; - let mut buffer = Vec::with_capacity(sz as usize); - let actual_sz = unsafe { - keyctl_get_security( - self.id, - buffer.as_mut_ptr() as *mut libc::c_char, - sz as usize, - ) - }; - check_call(actual_sz)?; - unsafe { buffer.set_len(actual_sz as usize) }; + // Get the size of the description. + let mut sz = keyctl_get_security(self.id, None)?; + // Allocate this description. + let mut buffer = vec![0; sz]; + loop { + let write_buffer = buffer.get_backing_buffer(); + // Fetch the description. + sz = keyctl_get_security(self.id, Some(write_buffer))?; + + // If we got everything, exit. + if sz <= buffer.capacity() { + break; + } + + // Resize for the additional capacity we need. + buffer.resize(sz, 0); + } + // Remove 1 from the size for the trailing NUL the kernel adds. + buffer.truncate(sz.saturating_sub(1)); + // The kernel guarantees that we get ASCII data from this. let str_slice = str::from_utf8(&buffer[..]).unwrap(); Ok(str_slice.to_owned()) } @@ -461,7 +476,7 @@ impl Keyring { /// Invalidates the keyring and schedules it for removal. Requires the `search` permission on /// the keyring. pub fn invalidate(self) -> Result<()> { - check_call(unsafe { keyctl_invalidate(self.id) }) + keyctl_invalidate(self.id) } } @@ -510,11 +525,12 @@ impl Key { I: Into<Option<&'s str>>, T: Into<Option<TargetKeyring<'a>>>, { - check_call_key(request_impl::<K>( + request_impl::<K>( &description.borrow().description(), info.into().as_ref().copied(), target.into().map(TargetKeyring::serial), - )) + ) + .map(Self::new_impl) } /// Update the payload in the key. @@ -522,10 +538,7 @@ impl Key { where D: AsRef<[u8]>, { - let data = data.as_ref(); - check_call(unsafe { - keyctl_update(self.id, data.as_ptr() as *const libc::c_void, data.len()) - }) + keyctl_update(self.id, data.as_ref()) } /// Revokes the key. Requires `write` permission on the key. @@ -573,19 +586,7 @@ impl Key { /// Read the payload of the key. Requires `read` permissions on the key. pub fn read(&self) -> Result<Vec<u8>> { - let sz = unsafe { keyctl_read(self.id, ptr::null_mut(), 0) }; - check_call(sz)?; - let mut buffer = Vec::with_capacity(sz as usize); - let actual_sz = unsafe { - keyctl_read( - self.id, - buffer.as_mut_ptr() as *mut libc::c_char, - sz as usize, - ) - }; - check_call(actual_sz)?; - unsafe { buffer.set_len(actual_sz as usize) }; - Ok(buffer) + read_impl(self.id) } /// Set an expiration timer on the keyring to `timeout`. @@ -620,34 +621,30 @@ impl Key { /// /// See `KeyManager::request_key_auth_key`. pub fn manage(&mut self) -> Result<KeyManager> { - check_call(unsafe { keyctl_assume_authority(Some(self.id)) })?; + keyctl_assume_authority(Some(self.id))?; Ok(KeyManager::new(Key::new_impl(self.id))) } /// Compute a Diffie-Hellman prime for use as a shared secret or public key. pub fn compute_dh(private: &Key, prime: &Key, base: &Key) -> Result<Vec<u8>> { - let sz = unsafe { - keyctl_dh_compute( - private.id, - prime.id, - base.id, - ptr::null_mut() as *mut libc::c_char, - 0, - ) - }; - check_call(sz)?; - let mut buffer = Vec::with_capacity(sz as usize); - let actual_sz = unsafe { - keyctl_dh_compute( - private.id, - prime.id, - base.id, - buffer.as_mut_ptr() as *mut libc::c_char, - sz as usize, - ) - }; - check_call(actual_sz)?; - unsafe { buffer.set_len(actual_sz as usize) }; + // Get the size of the description. + let mut sz = keyctl_dh_compute(private.id, prime.id, base.id, None)?; + // Allocate this description. + let mut buffer = vec![0; sz]; + loop { + let write_buffer = buffer.get_backing_buffer(); + // Fetch the description. + sz = keyctl_dh_compute(private.id, prime.id, base.id, Some(write_buffer))?; + + // If we got everything, exit. + if sz <= buffer.capacity() { + break; + } + + // Resize for the additional capacity we need. + buffer.resize(sz, 0); + } + buffer.truncate(sz); Ok(buffer) } } @@ -768,14 +765,14 @@ impl KeyManager { /// /// This key must be present in an available keyring before `Key::manage` may be called. pub fn request_key_auth_key(create: bool) -> Result<Key> { - check_call_key(unsafe { keyctl_get_keyring_ID(KEY_SPEC_REQKEY_AUTH_KEY, create.into()) }) + keyctl_get_keyring_id(KEY_SPEC_REQKEY_AUTH_KEY, create).map(Key::new_impl) } /// Drop authority for the current thread. /// /// This invalidates pub fn drop_authority() -> Result<()> { - check_call(unsafe { keyctl_assume_authority(None) }) + keyctl_assume_authority(None) } /// Instantiate the key with the given payload. @@ -784,15 +781,11 @@ impl KeyManager { T: Into<Option<TargetKeyring<'a>>>, P: AsRef<[u8]>, { - let payload = payload.as_ref(); - check_call(unsafe { - keyctl_instantiate( - self.key.id, - payload.as_ptr() as *const libc::c_void, - payload.len(), - keyring.into().map(TargetKeyring::serial), - ) - }) + keyctl_instantiate( + self.key.id, + payload.as_ref(), + keyring.into().map(TargetKeyring::serial), + ) } /// Reject the key with the given `error`. @@ -805,15 +798,12 @@ impl KeyManager { where T: Into<Option<TargetKeyring<'a>>>, { - let errno::Errno(errval) = error; - check_call(unsafe { - keyctl_reject( - self.key.id, - timeout.as_secs() as TimeoutSeconds, - errval as u32, - keyring.into().map(TargetKeyring::serial), - ) - }) + keyctl_reject( + self.key.id, + timeout.as_secs() as TimeoutSeconds, + error, + keyring.into().map(TargetKeyring::serial), + ) } /// Reject the key with `ENOKEY`. @@ -826,12 +816,10 @@ impl KeyManager { where T: Into<Option<TargetKeyring<'a>>>, { - check_call(unsafe { - keyctl_negate( - self.key.id, - timeout.as_secs() as TimeoutSeconds, - keyring.into().map(TargetKeyring::serial), - ) - }) + keyctl_negate( + self.key.id, + timeout.as_secs() as TimeoutSeconds, + keyring.into().map(TargetKeyring::serial), + ) } }