1use 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#[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 Des3CbcRaw = ENCTYPE_DES3_CBC_RAW as krb5_enctype,
24 Des3CbcSha1 = ENCTYPE_DES3_CBC_SHA1 as krb5_enctype,
26 ArcfourHmac = ENCTYPE_ARCFOUR_HMAC as krb5_enctype,
28 ArcfourHmacExp = ENCTYPE_ARCFOUR_HMAC_EXP as krb5_enctype,
30 Aes128CtsHmacSha196 = ENCTYPE_AES128_CTS_HMAC_SHA1_96 as krb5_enctype,
32 Aes256CtsHmacSha196 = ENCTYPE_AES256_CTS_HMAC_SHA1_96 as krb5_enctype,
34 Camellia128CtsCmac = ENCTYPE_CAMELLIA128_CTS_CMAC as krb5_enctype,
36 Camellia256CtsCmac = ENCTYPE_CAMELLIA256_CTS_CMAC as krb5_enctype,
38 Aes128CtsHmacSha256128 = ENCTYPE_AES128_CTS_HMAC_SHA256_128 as krb5_enctype,
40 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#[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 Normal = KRB5_KDB_SALTTYPE_NORMAL as krb5_int32,
107 NoRealm = KRB5_KDB_SALTTYPE_NOREALM as krb5_int32,
109 OnlyRealm = KRB5_KDB_SALTTYPE_ONLYREALM as krb5_int32,
111 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#[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 pub enctype: EncryptionType,
197 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#[derive(Clone, Debug, PartialEq, Eq)]
222#[allow(clippy::exhaustive_structs)]
223#[cfg_attr(feature = "python", pyclass(get_all, set_all))]
224pub struct KeySalts {
225 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}