use std::env;
use std::fs;
use std::path::Path;
use crate::error::{Error, Result};
use crate::pkcs11::*;
use crate::storage;
use crate::storage::StorageDBInfo;
use serde::de;
use serde::{Deserialize, Serialize};
use toml;
macro_rules! trace_config {
($err:expr, $val:expr) => {
#[cfg(feature = "log")]
{
use log::error;
error!("{}:{}: {} {}", file!(), line!(), $err, $val,);
}
};
}
#[cfg(test)]
use crate::pkcs11::vendor::KRR_SLOT_CONFIG;
#[cfg(not(test))]
const DEFAULT_CONF_DIR: &str = {
match option_env!("CONFDIR") {
Some(p) => p,
None => "/usr/local/etc",
}
};
#[cfg(test)]
const DEFAULT_CONF_DIR: &str = "test";
pub const DEFAULT_CONF_NAME: &str = "token.conf";
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum ObjectsDedup {
TrustOnly,
TrustAndCertificates,
All,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Slot {
pub slot: u32,
pub description: Option<String>,
pub manufacturer: Option<String>,
pub dbtype: Option<String>,
pub dbargs: Option<String>,
pub mechanisms: Option<Vec<String>>,
#[serde(default)]
pub objects_dedup: Option<ObjectsDedup>,
#[cfg(feature = "fips")]
#[serde(default)]
pub fips_behavior: FipsBehavior,
}
impl Slot {
pub fn new() -> Slot {
Slot {
slot: u32::MAX,
description: None,
manufacturer: None,
dbtype: None,
dbargs: None,
mechanisms: None,
objects_dedup: None,
#[cfg(feature = "fips")]
fips_behavior: FipsBehavior::default(),
}
}
#[cfg(test)]
pub fn with_db(dbtype: &str, dbargs: Option<String>) -> Slot {
Slot {
slot: u32::MAX,
description: None,
manufacturer: None,
dbtype: Some(dbtype.to_string()),
dbargs: dbargs,
mechanisms: None,
objects_dedup: None,
#[cfg(feature = "fips")]
fips_behavior: FipsBehavior {
keys_always_sensitive: if dbtype == "nssdb" {
true
} else {
false
},
},
}
}
pub fn mech_list(&self) -> Result<(Option<Vec<CK_ULONG>>, bool)> {
let list = match &self.mechanisms {
Some(l) => l,
None => return Ok((None, false)),
};
let mut vec = Vec::<CK_ULONG>::with_capacity(list.len());
let mut deny = false;
let mut idx = 0;
while idx < list.len() {
if idx == 0 {
if list[0] == "DENY" {
trace_config!("mechanism list type is", "deny");
deny = true;
idx += 1;
continue;
} else {
trace_config!("mechanism list type is", "allow");
}
}
let mech = match name_to_mech(&list[idx]) {
Ok(m) => m,
Err(e) => {
trace_config!("invalid mechanism name:", &list[idx]);
return Err(e);
}
};
vec.push(mech);
idx += 1;
}
Ok((Some(vec), deny))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "encoding")]
pub enum EcPointEncoding {
Bytes,
Der,
}
impl Default for EcPointEncoding {
fn default() -> Self {
EcPointEncoding::Bytes
}
}
#[cfg(feature = "fips")]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FipsBehavior {
pub keys_always_sensitive: bool,
}
#[cfg(feature = "fips")]
impl Default for FipsBehavior {
fn default() -> Self {
FipsBehavior {
keys_always_sensitive: false,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
#[serde(default)]
pub ec_point_encoding: EcPointEncoding,
pub slots: Vec<Slot>,
}
fn config_error<E: de::Error + 'static>(error: E) -> Error {
Error::ck_rv_from_error(CKR_TOKEN_NOT_RECOGNIZED, error)
}
impl Config {
pub fn new() -> Config {
Config {
ec_point_encoding: EcPointEncoding::default(),
slots: Vec::new(),
}
}
#[cfg(test)]
pub fn add_slot(&mut self, slot: Slot) -> Result<()> {
for s in &self.slots {
if slot.slot == u32::MAX || slot.slot == s.slot {
return Err(KRR_SLOT_CONFIG)?;
}
}
self.slots.push(slot);
Ok(())
}
fn find_conf() -> Result<String> {
match env::var("KRYOPTIC_CONF") {
Ok(var) => return Ok(var),
Err(_) => (),
}
let datafile = match env::var("XDG_CONFIG_HOME") {
Ok(xdg) => format!("{}/kryoptic/{}", xdg, DEFAULT_CONF_NAME),
Err(_) => match env::var("HOME") {
Ok(home) => {
format!("{}/.config/kryoptic/{}", home, DEFAULT_CONF_NAME)
}
Err(_) => format!(
"{}/kryoptic/{}",
DEFAULT_CONF_DIR, DEFAULT_CONF_NAME
),
},
};
if Path::new(&datafile).is_file() {
Ok(datafile)
} else {
Err(CKR_ARGUMENTS_BAD)?
}
}
fn from_file(filename: &str) -> Result<Config> {
let config_str = fs::read_to_string(filename)?;
let conf: Config = toml::from_str(&config_str).map_err(config_error)?;
Ok(conf)
}
fn from_legacy_conf_string(name: &str) -> Result<Config> {
let mut conf = Self::new();
#[cfg(feature = "sqlitedb")]
if name.ends_with(".sql") {
let mut slot = Slot::new();
slot.dbtype = Some(storage::sqlite::DBINFO.dbtype().to_string());
slot.dbargs = Some(name.to_string());
let _ = conf.slots.push(slot);
}
Ok(conf)
}
fn fix_slot_numbers(&mut self) {
let mut slotnum: u32 = 0;
let mut missing = false;
for slot in &self.slots {
if slot.slot != u32::MAX {
if slotnum <= slot.slot {
slotnum = slot.slot + 1;
}
} else {
missing = true;
}
}
if missing {
for slot in &mut self.slots {
if slot.slot == u32::MAX {
slot.slot = slotnum;
slotnum += 1;
}
}
}
}
pub fn default_config() -> Result<Config> {
let filename = Self::find_conf()?;
match Self::from_file(&filename) {
Ok(conf) => Ok(conf),
Err(e) => {
match Self::from_legacy_conf_string(&filename) {
Ok(mut conf) => {
conf.fix_slot_numbers();
Ok(conf)
}
Err(_) => return Err(e),
}
}
}
}
pub fn load_env_vars_overrides(&mut self) {
match env::var("KRYOPTIC_EC_POINT_ENCODING") {
Ok(var) => {
self.ec_point_encoding = match var.as_str() {
"DER" => EcPointEncoding::Der,
"BYTES" => EcPointEncoding::Bytes,
_ =>
{
self.ec_point_encoding
}
}
}
Err(_) => (),
}
}
#[cfg(feature = "nssdb")]
fn from_nss_init_args(args: &str) -> Result<Config> {
let mut conf = Self::new();
let mut slot = Slot::new();
slot.dbtype = Some(storage::nssdb::DBINFO.dbtype().to_string());
slot.dbargs = Some(args.to_string());
slot.objects_dedup = Some(ObjectsDedup::All);
#[cfg(feature = "fips")]
{
slot.fips_behavior = FipsBehavior {
keys_always_sensitive: true,
};
}
conf.slots.push(slot);
Ok(conf)
}
fn conf_from_args(&self, args: &str) -> Result<Config> {
if args.starts_with("kryoptic_conf=") {
let comps: Vec<&str> = args.splitn(2, '=').collect();
return Self::from_file(comps[1]);
}
#[cfg(feature = "nssdb")]
if args.contains("configDir=") {
return Self::from_nss_init_args(args);
}
Self::from_legacy_conf_string(args)
}
pub fn from_init_args(&mut self, args: &str) -> Result<()> {
let conf = self.conf_from_args(args)?;
for mut slot in conf.slots {
let mut found = false;
for s in &self.slots {
if slot.slot == u32::MAX {
if s.dbtype.as_deref() == slot.dbtype.as_deref()
&& s.dbargs.as_deref() == slot.dbargs.as_deref()
{
found = true;
slot.slot = s.slot;
}
} else {
if slot.slot != s.slot {
if s.dbtype.as_deref() == slot.dbtype.as_deref()
&& s.dbargs.as_deref() == slot.dbargs.as_deref()
{
return Err(CKR_ARGUMENTS_BAD)?;
}
} else {
if s.dbtype.as_deref() != slot.dbtype.as_deref() {
return Err(CKR_ARGUMENTS_BAD)?;
}
if s.dbargs.as_deref() != slot.dbargs.as_deref() {
return Err(CKR_ARGUMENTS_BAD)?;
}
found = true;
}
}
}
if !found {
self.slots.push(slot);
}
}
self.fix_slot_numbers();
Ok(())
}
}