kadmin/
keysalt.rs

1//! Kerberos keysalt lists
2use std::{
3    collections::HashSet,
4    ffi::{CStr, CString, c_char},
5    str::FromStr,
6};
7
8use kadmin_sys::*;
9#[cfg(feature = "python")]
10use pyo3::prelude::*;
11use strum::FromRepr;
12
13use crate::error::{Error, Result};
14
15/// Kerberos encryption type
16// In MIT krb5: src/lib/crypto/krb/etypes.c
17#[derive(Copy, Clone, Debug, FromRepr, PartialEq, Eq, Hash)]
18#[allow(clippy::exhaustive_enums)]
19#[repr(i32)]
20#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
21pub enum EncryptionType {
22    /// Triple DES cbc mode raw (weak, deprecated)
23    Des3CbcRaw = ENCTYPE_DES3_CBC_RAW as krb5_enctype,
24    /// Triple DES cbc mode with HMAC/sha1 (deprecated)
25    Des3CbcSha1 = ENCTYPE_DES3_CBC_SHA1 as krb5_enctype,
26    /// ArcFour with HMAC/md5 (deprecated)
27    ArcfourHmac = ENCTYPE_ARCFOUR_HMAC as krb5_enctype,
28    /// Exportable ArcFour with HMAC/md5 (weak, deprecated)
29    ArcfourHmacExp = ENCTYPE_ARCFOUR_HMAC_EXP as krb5_enctype,
30    /// AES-128 CTS mode with 96-bit SHA-1 HMAC
31    Aes128CtsHmacSha196 = ENCTYPE_AES128_CTS_HMAC_SHA1_96 as krb5_enctype,
32    /// AES-256 CTS mode with 96-bit SHA-1 HMAC
33    Aes256CtsHmacSha196 = ENCTYPE_AES256_CTS_HMAC_SHA1_96 as krb5_enctype,
34    /// Camellia-128 CTS mode with CMAC
35    Camellia128CtsCmac = ENCTYPE_CAMELLIA128_CTS_CMAC as krb5_enctype,
36    /// Camellia-256 CTS mode with CMAC
37    Camellia256CtsCmac = ENCTYPE_CAMELLIA256_CTS_CMAC as krb5_enctype,
38    /// AES-128 CTS mode with 128-bit SHA-256 HMAC
39    Aes128CtsHmacSha256128 = ENCTYPE_AES128_CTS_HMAC_SHA256_128 as krb5_enctype,
40    /// AES-256 CTS mode with 192-bit SHA-384 HMAC
41    Aes256CtsHmacSha384192 = ENCTYPE_AES256_CTS_HMAC_SHA384_192 as krb5_enctype,
42}
43
44impl From<EncryptionType> for krb5_enctype {
45    fn from(enctype: EncryptionType) -> Self {
46        enctype as Self
47    }
48}
49
50impl TryFrom<krb5_enctype> for EncryptionType {
51    type Error = Error;
52
53    fn try_from(enctype: krb5_enctype) -> Result<Self> {
54        Self::from_repr(enctype).ok_or(Error::EncryptionTypeConversion)
55    }
56}
57
58impl FromStr for EncryptionType {
59    type Err = Error;
60
61    fn from_str(s: &str) -> Result<Self> {
62        let s = CString::new(s)?;
63        let mut enctype = -1;
64        let code = unsafe { krb5_string_to_enctype(s.as_ptr().cast_mut(), &mut enctype) };
65        if code != KRB5_OK {
66            Err(Error::EncryptionTypeConversion)
67        } else {
68            enctype.try_into()
69        }
70    }
71}
72
73impl TryFrom<&str> for EncryptionType {
74    type Error = Error;
75
76    fn try_from(s: &str) -> Result<Self> {
77        Self::from_str(s)
78    }
79}
80
81impl TryFrom<EncryptionType> for String {
82    type Error = Error;
83
84    fn try_from(enctype: EncryptionType) -> Result<Self> {
85        let buffer = [0; 100];
86        let code = unsafe {
87            let mut b: [c_char; 100] = std::mem::transmute(buffer);
88            krb5_enctype_to_string(enctype.into(), b.as_mut_ptr(), 100)
89        };
90        if code != KRB5_OK {
91            return Err(Error::EncryptionTypeConversion);
92        }
93        let s = CStr::from_bytes_until_nul(&buffer).map_err(|_| Error::EncryptionTypeConversion)?;
94        Ok(s.to_owned().into_string()?)
95    }
96}
97
98/// Kerberos salt type
99// In MIT krb5: src/lib/krb5/krb/str_conv.c
100#[derive(Copy, Clone, Debug, FromRepr, PartialEq, Eq, Hash)]
101#[allow(clippy::exhaustive_enums)]
102#[repr(i32)]
103#[cfg_attr(feature = "python", pyclass(eq, eq_int))]
104pub enum SaltType {
105    /// Default for Kerberos Version 5
106    Normal = KRB5_KDB_SALTTYPE_NORMAL as krb5_int32,
107    /// Same as the default, without using realm information
108    NoRealm = KRB5_KDB_SALTTYPE_NOREALM as krb5_int32,
109    /// Uses only realm information as the salt
110    OnlyRealm = KRB5_KDB_SALTTYPE_ONLYREALM as krb5_int32,
111    /// Generate a random salt
112    Special = KRB5_KDB_SALTTYPE_SPECIAL as krb5_int32,
113}
114
115impl Default for SaltType {
116    fn default() -> Self {
117        Self::Normal
118    }
119}
120
121impl From<SaltType> for krb5_int32 {
122    fn from(salttype: SaltType) -> Self {
123        salttype as Self
124    }
125}
126
127impl TryFrom<krb5_int32> for SaltType {
128    type Error = Error;
129
130    fn try_from(salttype: krb5_int32) -> Result<Self> {
131        Self::from_repr(salttype).ok_or(Error::SaltTypeConversion)
132    }
133}
134
135impl FromStr for SaltType {
136    type Err = Error;
137
138    fn from_str(s: &str) -> Result<Self> {
139        if s.is_empty() {
140            return Ok(SaltType::Normal);
141        }
142        let s = CString::new(s)?;
143        let mut salttype = 0;
144        let code = unsafe { krb5_string_to_salttype(s.as_ptr().cast_mut(), &mut salttype) };
145        if code != KRB5_OK {
146            Err(Error::SaltTypeConversion)
147        } else {
148            salttype.try_into()
149        }
150    }
151}
152
153impl TryFrom<&str> for SaltType {
154    type Error = Error;
155
156    fn try_from(s: &str) -> Result<Self> {
157        Self::from_str(s)
158    }
159}
160
161impl TryFrom<Option<&str>> for SaltType {
162    type Error = Error;
163
164    fn try_from(s: Option<&str>) -> Result<Self> {
165        if let Some(s) = s {
166            s.try_into()
167        } else {
168            Ok(SaltType::Normal)
169        }
170    }
171}
172
173impl TryFrom<SaltType> for String {
174    type Error = Error;
175
176    fn try_from(salttype: SaltType) -> Result<Self> {
177        let buffer = [0; 100];
178        let code = unsafe {
179            let mut b: [c_char; 100] = std::mem::transmute(buffer);
180            krb5_enctype_to_string(salttype.into(), b.as_mut_ptr(), 100)
181        };
182        if code != KRB5_OK {
183            return Err(Error::SaltTypeConversion);
184        }
185        let s = CStr::from_bytes_until_nul(&buffer).map_err(|_| Error::SaltTypeConversion)?;
186        Ok(s.to_owned().into_string()?)
187    }
188}
189
190/// Kerberos keysalt
191#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
192#[allow(clippy::exhaustive_structs)]
193#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
194pub struct KeySalt {
195    /// Encryption type
196    pub enctype: EncryptionType,
197    /// Salt type
198    pub salttype: SaltType,
199}
200
201impl TryFrom<KeySalt> for String {
202    type Error = Error;
203
204    fn try_from(ks: KeySalt) -> Result<Self> {
205        let enctype: String = ks.enctype.try_into()?;
206        let salttype: String = ks.salttype.try_into()?;
207        Ok(enctype + ":" + &salttype)
208    }
209}
210
211impl From<KeySalt> for krb5_key_salt_tuple {
212    fn from(ks: KeySalt) -> Self {
213        Self {
214            ks_enctype: ks.enctype.into(),
215            ks_salttype: ks.salttype.into(),
216        }
217    }
218}
219
220/// Kerberos keysalt list
221#[derive(Clone, Debug, PartialEq, Eq)]
222#[allow(clippy::exhaustive_structs)]
223#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
224pub struct KeySalts {
225    /// Keysalt list
226    pub keysalts: HashSet<KeySalt>,
227}
228
229impl TryFrom<&KeySalts> for String {
230    type Error = Error;
231
232    fn try_from(ksl: &KeySalts) -> Result<Self> {
233        Ok(ksl
234            .keysalts
235            .iter()
236            .map(|ks| (*ks).try_into())
237            .collect::<Result<Vec<String>>>()?
238            .join(","))
239    }
240}
241
242impl KeySalts {
243    pub(crate) fn from_str(s: &str) -> Result<Self> {
244        let mut keysalts = HashSet::new();
245        for ks in s.split([',', ' ', '\t']) {
246            let (enctype, salttype) = if let Some((enctype, salttype)) = ks.split_once(":") {
247                (enctype.try_into()?, salttype.try_into()?)
248            } else {
249                (ks.try_into()?, Default::default())
250            };
251            keysalts.insert(KeySalt { enctype, salttype });
252        }
253
254        Ok(Self { keysalts })
255    }
256
257    pub(crate) fn to_cstring(&self) -> Result<CString> {
258        let s: String = self.try_into()?;
259        Ok(CString::new(s)?)
260    }
261
262    pub(crate) fn to_raw(&self) -> Vec<krb5_key_salt_tuple> {
263        self.keysalts.iter().map(|ks| (*ks).into()).collect()
264    }
265}