use crate::{error, tls::Tls, util::*};
use std::{
collections::HashMap,
convert::TryInto,
ffi::{CStr, CString},
io,
marker::{Send, Sync},
os::unix::io::RawFd,
path::{Path, PathBuf},
};
#[derive(Debug)]
pub struct Config(pub(crate) *mut libtls_sys::tls_config);
#[deprecated(since = "1.1.1", note = "Please use `Config` instead of `TlsConfig`")]
pub type TlsConfig = Config;
impl Config {
pub fn new() -> io::Result<Self> {
let config = unsafe { libtls_sys::tls_config_new() };
if config.is_null() {
Err(io::Error::last_os_error())
} else {
Ok(Config(config))
}
}
pub unsafe fn from_sys(config: *mut libtls_sys::tls_config) -> Self {
if config.is_null() {
panic!(io::Error::last_os_error())
}
Config(config)
}
pub fn add_keypair_file<P: AsRef<Path>>(
&mut self,
cert_file: P,
key_file: P,
) -> error::Result<()> {
call_file2(
self,
(cert_file, "cert file"),
(key_file, "key file"),
libtls_sys::tls_config_add_keypair_file,
)
}
pub fn add_keypair_mem(&mut self, cert: &[u8], key: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_add_keypair_mem(
self.0,
cert.as_ptr(),
cert.len().try_into()?,
key.as_ptr(),
key.len().try_into()?,
)
})
}
pub fn add_keypair_ocsp_file<P: AsRef<Path>>(
&mut self,
cert_file: P,
key_file: P,
ocsp_staple_file: P,
) -> error::Result<()> {
call_file3(
self,
(cert_file, "cert file"),
(key_file, "key file"),
(ocsp_staple_file, "ocsp staple file"),
libtls_sys::tls_config_add_keypair_ocsp_file,
)
}
pub fn add_keypair_ocsp_mem(
&mut self,
cert: &[u8],
key: &[u8],
ocsp_staple: &[u8],
) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_add_keypair_ocsp_mem(
self.0,
cert.as_ptr(),
cert.len().try_into()?,
key.as_ptr(),
key.len().try_into()?,
ocsp_staple.as_ptr(),
ocsp_staple.len().try_into()?,
)
})
}
pub fn set_alpn(&mut self, alpn: &str) -> error::Result<()> {
call_string1(self, alpn, libtls_sys::tls_config_set_alpn)
}
pub fn set_ca_file<P: AsRef<Path>>(&mut self, ca_file: P) -> error::Result<()> {
call_file1(
self,
(ca_file, "ca file"),
libtls_sys::tls_config_set_ca_file,
)
}
pub fn set_ca_path<P: AsRef<Path>>(&mut self, ca_path: P) -> error::Result<()> {
call_file1(
self,
(ca_path, "ca path"),
libtls_sys::tls_config_set_ca_path,
)
}
pub fn set_ca_mem(&mut self, ca: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_ca_mem(self.0, ca.as_ptr(), ca.len().try_into()?)
})
}
pub fn tls_config_set_ca_mem(&mut self, ca: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_ca_mem(self.0, ca.as_ptr(), ca.len().try_into()?)
})
}
pub fn set_cert_file<P: AsRef<Path>>(&mut self, cert_file: P) -> error::Result<()> {
call_file1(
self,
(cert_file, "cert file"),
libtls_sys::tls_config_set_cert_file,
)
}
pub fn set_cert_mem(&mut self, cert: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_cert_mem(self.0, cert.as_ptr(), cert.len().try_into()?)
})
}
pub fn set_ciphers(&mut self, ciphers: &str) -> error::Result<()> {
call_string1(self, ciphers, libtls_sys::tls_config_set_ciphers)
}
pub fn set_crl_file<P: AsRef<Path>>(&mut self, crl_file: P) -> error::Result<()> {
call_file1(
self,
(crl_file, "crl file"),
libtls_sys::tls_config_set_crl_file,
)
}
pub fn set_crl_mem(&mut self, crl: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_crl_mem(self.0, crl.as_ptr(), crl.len().try_into()?)
})
}
pub fn set_dheparams(&mut self, dheparams: &str) -> error::Result<()> {
call_string1(self, dheparams, libtls_sys::tls_config_set_dheparams)
}
#[deprecated(
since = "2.6.1-LibreSSL",
note = "Replaced by [set_ecdhecurves](#method.set_ecdhecurves)."
)]
pub fn set_ecdhecurve(&mut self, ecdhecurve: &str) -> error::Result<()> {
call_string1(self, ecdhecurve, libtls_sys::tls_config_set_ecdhecurve)
}
pub fn set_ecdhecurves(&mut self, ecdhecurves: &str) -> error::Result<()> {
call_string1(self, ecdhecurves, libtls_sys::tls_config_set_ecdhecurves)
}
pub fn set_key_file<P: AsRef<Path>>(&mut self, key_file: P) -> error::Result<()> {
call_file1(
self,
(key_file, "key file"),
libtls_sys::tls_config_set_key_file,
)
}
pub fn set_key_mem(&mut self, key: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_key_mem(self.0, key.as_ptr(), key.len().try_into()?)
})
}
pub fn set_keypair_file<P: AsRef<Path>>(
&mut self,
cert_file: P,
key_file: P,
) -> error::Result<()> {
call_file2(
self,
(cert_file, "cert file"),
(key_file, "key file"),
libtls_sys::tls_config_set_keypair_file,
)
}
pub fn set_keypair_mem(&mut self, cert: &[u8], key: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_keypair_mem(
self.0,
cert.as_ptr(),
cert.len().try_into()?,
key.as_ptr(),
key.len().try_into()?,
)
})
}
pub fn set_keypair_ocsp_file<P: AsRef<Path>>(
&mut self,
cert_file: P,
key_file: P,
ocsp_staple_file: P,
) -> error::Result<()> {
call_file3(
self,
(cert_file, "cert file"),
(key_file, "key file"),
(ocsp_staple_file, "ocsp staple file"),
libtls_sys::tls_config_set_keypair_ocsp_file,
)
}
pub fn set_keypair_ocsp_mem(
&mut self,
cert: &[u8],
key: &[u8],
ocsp_staple: &[u8],
) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_keypair_ocsp_mem(
self.0,
cert.as_ptr(),
cert.len().try_into()?,
key.as_ptr(),
key.len().try_into()?,
ocsp_staple.as_ptr(),
ocsp_staple.len().try_into()?,
)
})
}
pub fn set_ocsp_staple_mem(&mut self, ocsp_staple: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_ocsp_staple_mem(
self.0,
ocsp_staple.as_ptr(),
ocsp_staple.len().try_into()?,
)
})
}
pub fn set_ocsp_staple_file<P: AsRef<Path>>(
&mut self,
ocsp_staple_file: P,
) -> error::Result<()> {
call_file1(
self,
(ocsp_staple_file, "ocsp staple file"),
libtls_sys::tls_config_set_ocsp_staple_file,
)
}
pub fn set_protocols(&mut self, protocols: u32) -> error::Result<()> {
call_arg1(self, protocols, libtls_sys::tls_config_set_protocols)
}
pub fn set_session_fd(&mut self, session_fd: RawFd) -> error::Result<()> {
call_arg1(self, session_fd, libtls_sys::tls_config_set_session_fd)
}
pub fn set_verify_depth(&mut self, verify_depth: usize) -> error::Result<()> {
call_arg1(
self,
verify_depth as i32,
libtls_sys::tls_config_set_verify_depth,
)
}
pub fn prefer_ciphers_client(&mut self) {
unsafe { libtls_sys::tls_config_prefer_ciphers_client(self.0) }
}
pub fn prefer_ciphers_server(&mut self) {
unsafe { libtls_sys::tls_config_prefer_ciphers_server(self.0) }
}
pub fn insecure_noverifycert(&mut self) {
unsafe { libtls_sys::tls_config_insecure_noverifycert(self.0) }
}
pub fn insecure_noverifyname(&mut self) {
unsafe { libtls_sys::tls_config_insecure_noverifyname(self.0) }
}
pub fn insecure_noverifytime(&mut self) {
unsafe { libtls_sys::tls_config_insecure_noverifytime(self.0) }
}
pub fn verify(&mut self) {
unsafe { libtls_sys::tls_config_verify(self.0) }
}
pub fn ocsp_require_stapling(&mut self) {
unsafe { libtls_sys::tls_config_ocsp_require_stapling(self.0) }
}
pub fn verify_client(&mut self) {
unsafe { libtls_sys::tls_config_verify_client(self.0) }
}
pub fn verify_client_optional(&mut self) {
unsafe { libtls_sys::tls_config_verify_client_optional(self.0) }
}
pub fn clear_keys(&mut self) {
unsafe { libtls_sys::tls_config_clear_keys(self.0) }
}
pub fn set_session_id(&mut self, session_id: &[u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_set_session_id(
self.0,
session_id.as_ptr(),
session_id.len().try_into()?,
)
})
}
pub fn set_session_lifetime(&mut self, lifetime: usize) -> error::Result<()> {
call_arg1(
self,
lifetime as i32,
libtls_sys::tls_config_set_session_lifetime,
)
}
pub fn add_ticket_key(&mut self, keyrev: u32, key: &mut [u8]) -> error::Result<()> {
cvt(self, unsafe {
libtls_sys::tls_config_add_ticket_key(
self.0,
keyrev,
key.as_mut_ptr(),
key.len().try_into()?,
)
})
}
}
impl error::LastError for Config {
fn last_error(&self) -> error::Result<String> {
unsafe { cvt_no_error(libtls_sys::tls_config_error(self.0)) }
}
fn to_error<T>(errstr: String) -> error::Result<T> {
Err(error::Error::ConfigError(errstr))
}
}
impl Drop for Config {
fn drop(&mut self) {
unsafe { libtls_sys::tls_config_free(self.0) };
}
}
unsafe impl Send for Config {}
unsafe impl Sync for Config {}
pub fn default_ca_cert_file() -> PathBuf {
unsafe { CStr::from_ptr(libtls_sys::tls_default_ca_cert_file()) }
.to_string_lossy()
.into_owned()
.into()
}
pub fn parse_protocols(protostr: &str) -> error::Result<u32> {
let c_protostr = CString::new(protostr)?;
let mut protocols: u32 = 0;
let retval =
unsafe { libtls_sys::tls_config_parse_protocols(&mut protocols, c_protostr.as_ptr()) };
if retval == -1 {
Err(io::Error::new(io::ErrorKind::Other, "Invalid protocols string").into())
} else {
Ok(protocols)
}
}
pub fn load_file<P: AsRef<Path>>(file: P, password: Option<&str>) -> error::Result<Vec<u8>> {
let mut size = 0;
let s_file = cvt_option(
file.as_ref().to_str(),
io::Error::new(io::ErrorKind::InvalidInput, "file"),
)?;
unsafe {
let c_file = CString::new(s_file)?;
let data = match password {
Some(password) => {
let c_password = CString::new(password)?;
let raw = c_password.into_raw();
let data = libtls_sys::tls_load_file(c_file.as_ptr(), &mut size, raw);
let _ = CString::from_raw(raw);
data
}
None => libtls_sys::tls_load_file(c_file.as_ptr(), &mut size, std::ptr::null_mut()),
};
if data.is_null() {
Err(io::Error::last_os_error().into())
} else {
let len = size.try_into()?;
Ok(Vec::from_raw_parts(data, len, len))
}
}
}
pub fn unload_file(mut data: Vec<u8>) {
let ptr = data.as_mut_ptr();
let len = data.len() as u64;
std::mem::forget(data);
unsafe { libtls_sys::tls_unload_file(ptr, len) }
}
#[derive(Debug, Clone)]
enum KeyData {
File(PathBuf),
Path(PathBuf),
KeyPairFiles(PathBuf, PathBuf, Option<PathBuf>),
KeyPairMem(Vec<u8>, Vec<u8>, Option<Vec<u8>>),
Mem(Vec<u8>),
}
#[derive(Default, Debug, Clone)]
pub struct Builder {
alpn: Option<String>,
ca: Option<KeyData>,
ciphers: Option<String>,
crl: Option<KeyData>,
dheparams: Option<String>,
ecdhecurves: Option<String>,
keypairs: Vec<KeyData>,
noverifycert: bool,
noverifyname: bool,
noverifytime: bool,
protocols: Option<u32>,
session_fd: Option<RawFd>,
session_id: Option<Vec<u8>>,
session_lifetime: Option<usize>,
ticket_key: HashMap<u32, Vec<u8>>,
verify: bool,
verify_client: bool,
verify_client_optional: bool,
verify_depth: Option<usize>,
}
#[deprecated(
since = "1.1.1",
note = "Please use `Builder` instead of `TlsConfigBuilder`"
)]
pub type TlsConfigBuilder = Builder;
impl Builder {
pub fn new() -> Self {
Default::default()
}
pub fn build(&self) -> error::Result<Config> {
let mut config = Config::new()?;
self.build_keypairs(&mut config)?;
if let Some(ref alpn) = self.alpn {
config.set_alpn(alpn)?;
}
if let Some(ref ca) = self.ca {
match ca {
KeyData::Mem(mem) => config.set_ca_mem(mem)?,
KeyData::File(file) => config.set_ca_file(file)?,
KeyData::Path(path) => config.set_ca_path(path)?,
_ => return Err(error::Error::NoError),
};
} else if !default_ca_cert_file().exists() {
config.set_ca_path("/etc/ssl/certs")?;
};
if let Some(ref ciphers) = self.ciphers {
config.set_ciphers(ciphers)?;
}
if let Some(ref crl) = self.crl {
match crl {
KeyData::Mem(mem) => config.set_ca_mem(mem)?,
KeyData::File(file) => config.set_ca_file(file)?,
_ => return Err(error::Error::NoError),
};
}
if let Some(ref dheparams) = self.dheparams {
config.set_dheparams(dheparams)?;
}
if let Some(ref ecdhecurves) = self.ecdhecurves {
config.set_ecdhecurves(ecdhecurves)?;
}
if let Some(protocols) = self.protocols {
config.set_protocols(protocols)?;
}
if let Some(session_fd) = self.session_fd {
config.set_session_fd(session_fd)?;
}
if let Some(ref session_id) = self.session_id {
config.set_session_id(session_id)?;
}
if let Some(session_lifetime) = self.session_lifetime {
config.set_session_lifetime(session_lifetime)?;
}
for (keyrev, key) in self.ticket_key.iter() {
config.add_ticket_key(*keyrev, key.clone().as_mut_slice())?;
}
if self.noverifycert {
config.insecure_noverifycert();
}
if self.noverifyname {
config.insecure_noverifyname();
}
if self.noverifytime {
config.insecure_noverifytime();
}
if let Some(verify_depth) = self.verify_depth {
config.set_verify_depth(verify_depth)?;
}
if self.verify_client_optional {
config.verify_client_optional();
}
if self.verify_client {
config.verify_client();
}
if self.verify {
config.verify();
}
Ok(config)
}
fn build_keypairs(&self, config: &mut Config) -> error::Result<()> {
for (i, kp) in self.keypairs.iter().enumerate() {
match kp {
KeyData::KeyPairMem(cert, key, ocsp) => {
if let Some(ocsp) = ocsp {
if i == 0 {
config.set_keypair_ocsp_mem(cert, key, ocsp)?;
}
config.add_keypair_ocsp_mem(cert, key, ocsp)?;
} else {
if i == 0 {
config.set_keypair_mem(cert, key)?;
}
config.add_keypair_mem(cert, key)?;
}
}
KeyData::KeyPairFiles(cert, key, ocsp) => {
if let Some(ocsp) = ocsp {
if i == 0 {
config.set_keypair_ocsp_file(cert, key, ocsp)?;
}
config.add_keypair_ocsp_file(cert, key, ocsp)?;
} else {
if i == 0 {
config.set_keypair_file(cert, key)?;
}
config.add_keypair_file(cert, key)?;
}
}
_ => return Err(error::Error::NoError),
};
}
Ok(())
}
pub fn client(&self) -> error::Result<Tls> {
let mut client = Tls::client()?;
client.configure(&self.build()?)?;
Ok(client)
}
pub fn server(&self) -> error::Result<Tls> {
let mut server = Tls::server()?;
server.configure(&self.build()?)?;
Ok(server)
}
pub fn alpn(&'_ mut self, alpn: &str) -> &'_ mut Self {
self.alpn = Some(alpn.to_owned());
self
}
pub fn ca_file<P: AsRef<Path>>(&'_ mut self, path: P) -> &'_ mut Self {
self.ca = Some(KeyData::File(path.as_ref().to_owned()));
self
}
pub fn ca_path<P: AsRef<Path>>(&'_ mut self, path: P) -> &'_ mut Self {
self.ca = Some(KeyData::Path(path.as_ref().to_owned()));
self
}
pub fn ca_mem(&'_ mut self, mem: &[u8]) -> &'_ mut Self {
self.ca = Some(KeyData::Mem(mem.to_vec()));
self
}
pub fn ciphers(&'_ mut self, ciphers: &str) -> &'_ mut Self {
self.ciphers = Some(ciphers.to_owned());
self
}
pub fn crl_file<P: AsRef<Path>>(&'_ mut self, path: P) -> &'_ mut Self {
self.crl = Some(KeyData::File(path.as_ref().to_owned()));
self
}
pub fn crl_mem(&'_ mut self, mem: &[u8]) -> &'_ mut Self {
self.crl = Some(KeyData::Mem(mem.to_vec()));
self
}
pub fn dheparams(&'_ mut self, dheparams: &str) -> &'_ mut Self {
self.dheparams = Some(dheparams.to_owned());
self
}
pub fn ecdhecurves(&'_ mut self, ecdhecurves: &str) -> &'_ mut Self {
self.ecdhecurves = Some(ecdhecurves.to_owned());
self
}
pub fn keypair_file<P: AsRef<Path>>(
&'_ mut self,
cert: P,
key: P,
ocsp_staple: Option<P>,
) -> &'_ mut Self {
let certdata = cert.as_ref().to_owned();
let keydata = key.as_ref().to_owned();
let ocspdata = match ocsp_staple {
Some(path) => Some(path.as_ref().to_owned()),
None => None,
};
self.keypairs
.push(KeyData::KeyPairFiles(certdata, keydata, ocspdata));
self
}
pub fn keypair_mem(
&'_ mut self,
cert: &[u8],
key: &[u8],
ocsp_staple: Option<&[u8]>,
) -> &'_ mut Self {
let certdata = cert.to_vec();
let keydata = key.to_vec();
let ocspdata = match ocsp_staple {
Some(mem) => Some(mem.to_vec()),
None => None,
};
self.keypairs
.push(KeyData::KeyPairMem(certdata, keydata, ocspdata));
self
}
pub fn noverifycert(&'_ mut self) -> &'_ mut Self {
self.noverifycert = true;
self
}
pub fn noverifyname(&'_ mut self) -> &'_ mut Self {
self.noverifyname = true;
self
}
pub fn noverifytime(&'_ mut self) -> &'_ mut Self {
self.noverifytime = true;
self
}
pub fn protocols(&'_ mut self, protocols: u32) -> &'_ mut Self {
self.protocols = Some(protocols);
self
}
pub fn session_fd(&'_ mut self, fd: RawFd) -> &'_ mut Self {
self.session_fd = Some(fd);
self
}
pub fn session_id(&'_ mut self, id: &[u8]) -> &'_ mut Self {
self.session_id = Some(id.to_vec());
self
}
pub fn session_lifetime(&'_ mut self, lifetime: usize) -> &'_ mut Self {
self.session_lifetime = Some(lifetime);
self
}
pub fn ticket_key(&'_ mut self, keyrev: u32, key: &[u8]) -> &'_ mut Self {
self.ticket_key.insert(keyrev, key.to_vec());
self
}
pub fn verify(&'_ mut self) -> &'_ mut Self {
self.verify = true;
self
}
pub fn verify_client(&'_ mut self) -> &'_ mut Self {
self.verify_client = true;
self
}
pub fn verify_client_optional(&'_ mut self) -> &'_ mut Self {
self.verify_client_optional = true;
self
}
pub fn verify_depth(&'_ mut self, depth: usize) -> &'_ mut Self {
self.verify_depth = Some(depth);
self
}
}