2019-02-02 20:42:42 -05:00
|
|
|
use std::path::{Path,PathBuf};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::io::{self,Read,Write};
|
|
|
|
use std::fs;
|
|
|
|
use std::ffi::CString;
|
|
|
|
use std::os::raw::c_char;
|
|
|
|
|
2020-06-19 10:48:39 -04:00
|
|
|
use libc::{self,c_long,c_ulong, c_int};
|
2019-02-02 20:42:42 -05:00
|
|
|
|
|
|
|
use hex;
|
|
|
|
use sodiumoxide::randombytes::randombytes_into;
|
|
|
|
use sodiumoxide::crypto::{
|
|
|
|
sign::{
|
|
|
|
self, SEEDBYTES,
|
|
|
|
},
|
|
|
|
pwhash::{
|
|
|
|
self,SALTBYTES, Salt,
|
|
|
|
},
|
|
|
|
secretbox::{
|
|
|
|
self, NONCEBYTES, Nonce,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-07-02 09:34:09 -04:00
|
|
|
use crate::{Result, Error, KeyPair};
|
2019-02-02 20:42:42 -05:00
|
|
|
|
|
|
|
#[derive(Serialize,Deserialize,Debug)]
|
|
|
|
pub struct KeyRing {
|
|
|
|
keypairs: HashMap<String, String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KeyRing {
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn create_new() -> Self {
|
|
|
|
let seed = Self::new_random_seed();
|
2019-02-02 20:42:42 -05:00
|
|
|
let mut keypairs = HashMap::new();
|
|
|
|
keypairs.insert("realmfs-user".to_string(), hex::encode(&seed.0));
|
|
|
|
KeyRing { keypairs }
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn load<P: AsRef<Path>>(path: P, passphrase: &str) -> Result<Self> {
|
2019-02-02 20:42:42 -05:00
|
|
|
let mut sbox = SecretBox::new(path.as_ref());
|
2020-07-02 09:34:09 -04:00
|
|
|
sbox.read().map_err(context!("error reading keyring file"))?;
|
2019-02-02 20:42:42 -05:00
|
|
|
let mut bytes = sbox.open(passphrase)?;
|
2020-07-02 09:34:09 -04:00
|
|
|
let keyring = toml::from_slice::<KeyRing>(&bytes)
|
|
|
|
.map_err(context!("failed to parse keyring file {:?}", path.as_ref()))?;
|
2019-02-02 20:42:42 -05:00
|
|
|
bytes.iter_mut().for_each(|b| *b = 0);
|
|
|
|
Ok(keyring)
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn load_with_cryptsetup_passphrase<P: AsRef<Path>>(path: P) -> Result<Self> {
|
|
|
|
let passphrase = Self::get_cryptsetup_passphrase()?;
|
|
|
|
Self::load(path, &passphrase)
|
2019-02-02 20:42:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_cryptsetup_passphrase() -> Result<String> {
|
2019-04-03 16:05:09 -04:00
|
|
|
let key = Self::get_key("cryptsetup")?;
|
2019-02-02 20:42:42 -05:00
|
|
|
info!("Got key {}", key.0);
|
|
|
|
let buf = key.read()?;
|
|
|
|
match buf.split(|b| *b == 0).map(|bs| String::from_utf8_lossy(bs).to_string()).last() {
|
|
|
|
Some(s) => Ok(s),
|
|
|
|
None => Ok(String::new()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 15:14:41 -04:00
|
|
|
fn get_key(name: &str) -> Result<KernelKey> {
|
|
|
|
if let Ok(key) = KernelKey::user_keyring().search(name) {
|
|
|
|
debug!("Found {} key in user keyring: (keyid: {:08x})", name, key.0);
|
|
|
|
if let Err(e) = key.read() {
|
|
|
|
info!("err tho on read: {}", e);
|
|
|
|
} else {
|
|
|
|
return Ok(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Ok(key) = KernelKey::request_key("user", name) {
|
|
|
|
info!("Found {} key with request_key", name);
|
|
|
|
return Ok(key);
|
|
|
|
}
|
2020-07-02 09:34:09 -04:00
|
|
|
bail!("kernel key '{}' not found", name)
|
2019-04-02 15:14:41 -04:00
|
|
|
}
|
|
|
|
|
2019-02-02 20:42:42 -05:00
|
|
|
pub fn add_keys_to_kernel(&self) -> Result<()> {
|
|
|
|
for (k,v) in self.keypairs.iter() {
|
|
|
|
info!("Adding {} to kernel keystore", k.as_str());
|
2020-07-02 09:34:09 -04:00
|
|
|
let bytes = hex::decode(v).map_err(|_| format_err!("failed to hex decode ({})", v))?;
|
2019-04-02 15:14:41 -04:00
|
|
|
let key = KernelKey::add_key("user", k.as_str(), &bytes, KEY_SPEC_USER_KEYRING)?;
|
2019-04-03 16:05:09 -04:00
|
|
|
key.set_perm(0x3f03_0000)?;
|
2019-02-02 20:42:42 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-04-02 15:14:41 -04:00
|
|
|
pub fn get_kernel_keypair(name: &str) -> Result<KeyPair> {
|
2019-04-03 16:05:09 -04:00
|
|
|
let key = Self::get_key(name)?;
|
2019-04-02 15:14:41 -04:00
|
|
|
let data = key.read()?;
|
|
|
|
KeyPair::from_bytes(&data)
|
|
|
|
}
|
|
|
|
|
2019-02-02 20:42:42 -05:00
|
|
|
pub fn write<P: AsRef<Path>>(&self, path: P, passphrase: &str) -> Result<()> {
|
|
|
|
let salt = pwhash::gen_salt();
|
|
|
|
let nonce = secretbox::gen_nonce();
|
|
|
|
let key = SecretBox::passphrase_to_key(passphrase, &salt)?;
|
2020-07-02 09:34:09 -04:00
|
|
|
let bytes = toml::to_vec(self)
|
|
|
|
.map_err(context!("failed to serialize keyring"))?;
|
2019-02-02 20:42:42 -05:00
|
|
|
let ciphertext = secretbox::seal(&bytes, &nonce, &key);
|
|
|
|
|
2020-07-02 09:34:09 -04:00
|
|
|
Self::write_keyring(path.as_ref(), &salt.0, &nonce.0, &ciphertext)
|
|
|
|
.map_err(context!("error writing keyring file {:?}", path.as_ref()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_keyring(path: &Path, salt: &[u8], nonce: &[u8], ciphertext: &[u8]) -> io::Result<()> {
|
|
|
|
let mut file = fs::File::create(path)?;
|
|
|
|
file.write_all(&salt)?;
|
|
|
|
file.write_all(&nonce)?;
|
2019-02-02 20:42:42 -05:00
|
|
|
file.write_all(&ciphertext)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_random_seed() -> sign::Seed {
|
|
|
|
let mut seedbuf = [0; SEEDBYTES];
|
|
|
|
randombytes_into(&mut seedbuf);
|
|
|
|
sign::Seed(seedbuf)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for KeyRing {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
for (_,v) in self.keypairs.drain() {
|
|
|
|
v.into_bytes().iter_mut().for_each(|b| *b = 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct SecretBox {
|
|
|
|
path: PathBuf,
|
|
|
|
salt: Salt,
|
|
|
|
nonce: Nonce,
|
|
|
|
data: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SecretBox {
|
2019-04-03 16:05:09 -04:00
|
|
|
fn new(path: &Path) -> Self {
|
2019-02-02 20:42:42 -05:00
|
|
|
SecretBox {
|
|
|
|
path: path.to_path_buf(),
|
|
|
|
salt: Salt([0; SALTBYTES]),
|
|
|
|
nonce: Nonce([0; NONCEBYTES]),
|
|
|
|
data: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read(&mut self) -> Result<()> {
|
|
|
|
if !self.data.is_empty() {
|
|
|
|
self.data.clear();
|
|
|
|
}
|
2020-07-02 09:34:09 -04:00
|
|
|
|
|
|
|
self.read_keyring_file()
|
|
|
|
.map_err(context!("error reading keyring file {:?}", self.path))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_keyring_file(&mut self) -> io::Result<()> {
|
2019-02-02 20:42:42 -05:00
|
|
|
let mut file = fs::File::open(&self.path)?;
|
|
|
|
file.read_exact(&mut self.salt.0)?;
|
|
|
|
file.read_exact(&mut self.nonce.0)?;
|
|
|
|
file.read_to_end(&mut self.data)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn open(&self, passphrase: &str) -> Result<Vec<u8>> {
|
2019-04-03 16:05:09 -04:00
|
|
|
let key = Self::passphrase_to_key(passphrase, &self.salt)?;
|
2019-02-02 20:42:42 -05:00
|
|
|
let result = secretbox::open(&self.data, &self.nonce, &key)
|
2020-07-02 09:34:09 -04:00
|
|
|
.map_err(|_| format_err!("failed to decrypt {:?}", self.path))?;
|
2019-02-02 20:42:42 -05:00
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn passphrase_to_key(passphrase: &str, salt: &Salt) -> Result<secretbox::Key> {
|
|
|
|
let mut keybuf = [0; secretbox::KEYBYTES];
|
|
|
|
pwhash::derive_key(&mut keybuf, passphrase.as_bytes(), salt, pwhash::OPSLIMIT_INTERACTIVE, pwhash::MEMLIMIT_INTERACTIVE)
|
2020-07-02 09:34:09 -04:00
|
|
|
.map_err(|_| format_err!("failed to derive key"))?;
|
2019-02-02 20:42:42 -05:00
|
|
|
Ok(secretbox::Key(keybuf))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-04-02 15:14:41 -04:00
|
|
|
const KEYCTL_GET_KEYRING_ID : c_int = 0; // ask for a keyring's ID
|
|
|
|
const KEYCTL_SETPERM : c_int = 5; // set perms on a key
|
|
|
|
const KEYCTL_DESCRIBE : c_int = 6; // describe a key
|
|
|
|
const KEYCTL_SEARCH : c_int = 10; // search for a key in a keyring
|
2019-02-02 20:42:42 -05:00
|
|
|
const KEYCTL_READ : c_int = 11; // read a key or keyring's contents
|
2019-04-02 15:14:41 -04:00
|
|
|
|
2019-02-02 20:42:42 -05:00
|
|
|
const KEY_SPEC_USER_KEYRING : c_int = -4; // - key ID for UID-specific keyring
|
|
|
|
|
|
|
|
|
2020-06-19 10:48:39 -04:00
|
|
|
pub struct KernelKey(i32);
|
2019-02-02 20:42:42 -05:00
|
|
|
|
|
|
|
impl KernelKey {
|
2019-04-02 15:14:41 -04:00
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn user_keyring() -> Self {
|
2019-04-02 15:14:41 -04:00
|
|
|
KernelKey(KEY_SPEC_USER_KEYRING)
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn request_key(key_type: &str, description: &str) -> Result<Self> {
|
2019-02-02 20:42:42 -05:00
|
|
|
let key_type = CString::new(key_type).unwrap();
|
|
|
|
let description = CString::new(description).unwrap();
|
|
|
|
let serial = _request_key(key_type.as_ptr(), description.as_ptr())?;
|
|
|
|
Ok(KernelKey(serial as i32))
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn add_key(key_type: &str, description: &str, payload: &[u8], ring_id: c_int) -> Result<Self> {
|
2019-02-02 20:42:42 -05:00
|
|
|
let key_type = CString::new(key_type).unwrap();
|
|
|
|
let description = CString::new(description).unwrap();
|
|
|
|
let serial = _add_key(key_type.as_ptr(), description.as_ptr(), payload.as_ptr(), payload.len(), ring_id)?;
|
|
|
|
Ok(KernelKey(serial as i32))
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn get_keyring_id(&self, create: bool) -> Result<Self> {
|
2019-04-02 15:14:41 -04:00
|
|
|
let serial = keyctl2(KEYCTL_GET_KEYRING_ID, self.id(), create as u64)?;
|
|
|
|
Ok(KernelKey(serial as i32))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_perm(&self, perm: u32) -> Result<()> {
|
2019-04-03 16:05:09 -04:00
|
|
|
keyctl2(KEYCTL_SETPERM, self.id(), u64::from(perm))?;
|
2019-04-02 15:14:41 -04:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn describe(&self) -> Result<String> {
|
|
|
|
let mut size = 0;
|
|
|
|
loop {
|
|
|
|
size = match self.buffer_request(KEYCTL_DESCRIBE, size) {
|
2020-07-02 09:34:09 -04:00
|
|
|
BufferResult::Err(err) => bail!("error calling KEYCTL_DESCRIBE on key: {}", err),
|
2019-04-02 15:14:41 -04:00
|
|
|
BufferResult::Ok(vec) => return Ok(String::from_utf8(vec).expect("KEYCTL_DESCRIBE returned bad utf8")),
|
|
|
|
BufferResult::TooSmall(sz) => sz,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 16:05:09 -04:00
|
|
|
pub fn search(&self, description: &str) -> Result<Self> {
|
2019-04-02 15:14:41 -04:00
|
|
|
let key_type = CString::new("user").unwrap();
|
|
|
|
let description = CString::new(description).unwrap();
|
|
|
|
|
|
|
|
let serial = keyctl4(KEYCTL_SEARCH, self.id(), key_type.as_ptr() as u64, description.as_ptr() as u64, 0)?;
|
|
|
|
Ok(KernelKey(serial as i32))
|
|
|
|
}
|
|
|
|
|
2019-02-02 20:42:42 -05:00
|
|
|
pub fn read(&self) -> Result<Vec<u8>> {
|
|
|
|
let mut size = 0;
|
|
|
|
loop {
|
|
|
|
size = match self.buffer_request(KEYCTL_READ, size) {
|
2020-07-02 09:34:09 -04:00
|
|
|
BufferResult::Err(err) => bail!("Error reading key: {}", err),
|
2019-02-02 20:42:42 -05:00
|
|
|
BufferResult::Ok(buffer) => return Ok(buffer),
|
|
|
|
BufferResult::TooSmall(sz) => sz + 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn buffer_request(&self, command: c_int, size: usize) -> BufferResult {
|
|
|
|
if size == 0 {
|
|
|
|
return match keyctl1(command, self.id()) {
|
|
|
|
Err(err) => BufferResult::Err(err),
|
2020-07-02 09:34:09 -04:00
|
|
|
Ok(n) if n < 0 => BufferResult::Err(format_err!("keyctl returned bad size").into()),
|
2019-02-02 20:42:42 -05:00
|
|
|
Ok(n) => BufferResult::TooSmall(n as usize),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
let mut buffer = vec![0u8; size];
|
|
|
|
match keyctl3(command, self.id(), buffer.as_ptr() as u64, buffer.len() as u64) {
|
|
|
|
Err(err) => BufferResult::Err(err),
|
2020-07-02 09:34:09 -04:00
|
|
|
Ok(n) if n < 0 => BufferResult::Err(format_err!("keyctrl returned bad size {}", n).into()),
|
2019-02-02 20:42:42 -05:00
|
|
|
Ok(sz) if size >= (sz as usize) => {
|
|
|
|
let sz = sz as usize;
|
|
|
|
if size > sz {
|
|
|
|
buffer.truncate(sz)
|
|
|
|
}
|
|
|
|
BufferResult::Ok(buffer)
|
|
|
|
},
|
|
|
|
Ok(n) => BufferResult::TooSmall(n as usize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn id(&self) -> c_ulong {
|
|
|
|
self.0 as c_ulong
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
enum BufferResult {
|
|
|
|
Ok(Vec<u8>),
|
|
|
|
Err(Error),
|
|
|
|
TooSmall(usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn keyctl1(command: c_int, arg2: c_ulong) -> Result<c_long> {
|
2019-04-02 15:14:41 -04:00
|
|
|
sys_keyctl(command, arg2, 0, 0, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn keyctl2(command: c_int, arg2: c_ulong, arg3: c_ulong) -> Result<c_long> {
|
|
|
|
sys_keyctl(command, arg2, arg3, 0, 0)
|
2019-02-02 20:42:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn keyctl3(command: c_int, arg2: c_ulong, arg3: c_ulong, arg4: c_ulong) -> Result<c_long> {
|
2019-04-02 15:14:41 -04:00
|
|
|
sys_keyctl(command, arg2, arg3, arg4, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn keyctl4(command: c_int, arg2: c_ulong, arg3: c_ulong, arg4: c_ulong, arg5: c_ulong) -> Result<c_long> {
|
|
|
|
sys_keyctl(command, arg2, arg3, arg4, arg5)
|
2019-02-02 20:42:42 -05:00
|
|
|
}
|
|
|
|
|
2019-04-02 15:14:41 -04:00
|
|
|
fn sys_keyctl(command: c_int, arg2: c_ulong, arg3: c_ulong, arg4: c_ulong, arg5: c_ulong) -> Result<c_long> {
|
2019-02-02 20:42:42 -05:00
|
|
|
unsafe {
|
|
|
|
let r = libc::syscall(libc::SYS_keyctl, command, arg2, arg3, arg4, arg5);
|
|
|
|
if r == -1 {
|
2020-07-02 09:34:09 -04:00
|
|
|
bail!("error calling sys_keyctl(): {}", io::Error::last_os_error());
|
2019-02-02 20:42:42 -05:00
|
|
|
} else {
|
|
|
|
Ok(r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _request_key(key_type: *const c_char, description: *const c_char) -> Result<c_long> {
|
|
|
|
unsafe {
|
|
|
|
let r = libc::syscall(libc::SYS_request_key, key_type, description, 0, 0);
|
|
|
|
if r == -1 {
|
2020-07-02 09:34:09 -04:00
|
|
|
bail!("error calling sys_request_key(): {}", io::Error::last_os_error());
|
2019-02-02 20:42:42 -05:00
|
|
|
} else {
|
|
|
|
Ok(r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _add_key(key_type: *const c_char, description: *const c_char, payload: *const u8, plen: usize, ring_id: c_int) -> Result<c_long> {
|
|
|
|
unsafe {
|
|
|
|
let r = libc::syscall(libc::SYS_add_key, key_type, description, payload, plen, ring_id);
|
|
|
|
if r == -1 {
|
2020-07-02 09:34:09 -04:00
|
|
|
bail!("error calling sys_add_key(): {}", io::Error::last_os_error());
|
2019-02-02 20:42:42 -05:00
|
|
|
} else {
|
|
|
|
Ok(r)
|
|
|
|
}
|
|
|
|
}
|
2019-04-02 15:14:41 -04:00
|
|
|
}
|