/src/suricata8/rust/src/krb/detect.rs
Line | Count | Source |
1 | | /* Copyright (C) 2018 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | // written by Pierre Chifflier <chifflier@wzdftpd.net> |
19 | | |
20 | | use crate::krb::krb5::{test_weak_encryption, KRB5Transaction}; |
21 | | use suricata_sys::sys::DetectEngineThreadCtx; |
22 | | |
23 | | use kerberos_parser::krb5::EncryptionType; |
24 | | |
25 | | use nom7::branch::alt; |
26 | | use nom7::bytes::complete::{is_a, tag, take_while, take_while1}; |
27 | | use nom7::character::complete::char; |
28 | | use nom7::combinator::{all_consuming, map_res, opt}; |
29 | | use nom7::multi::many1; |
30 | | use nom7::IResult; |
31 | | |
32 | | use std::ffi::CStr; |
33 | | use std::os::raw::c_void; |
34 | | |
35 | | #[no_mangle] |
36 | 0 | pub unsafe extern "C" fn SCKrb5TxGetMsgType(tx: &KRB5Transaction, ptr: *mut u32) { |
37 | 0 | *ptr = tx.msg_type.0; |
38 | 0 | } |
39 | | |
40 | | /// Get error code, if present in transaction |
41 | | /// Return 0 if error code was filled, else 1 |
42 | | #[no_mangle] |
43 | 0 | pub unsafe extern "C" fn SCKrb5TxGetErrorCode(tx: &KRB5Transaction, ptr: *mut i32) -> u32 { |
44 | 0 | match tx.error_code { |
45 | 0 | Some(ref e) => { |
46 | 0 | *ptr = e.0; |
47 | 0 | 0 |
48 | | } |
49 | 0 | None => 1, |
50 | | } |
51 | 0 | } |
52 | | |
53 | | #[no_mangle] |
54 | 0 | pub unsafe extern "C" fn SCKrb5TxGetCname( |
55 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flags: u8, i: u32, buffer: *mut *const u8, |
56 | 0 | buffer_len: *mut u32, |
57 | 0 | ) -> bool { |
58 | 0 | let tx = cast_pointer!(tx, KRB5Transaction); |
59 | 0 | if let Some(ref s) = tx.cname { |
60 | 0 | if (i as usize) < s.name_string.len() { |
61 | 0 | let value = &s.name_string[i as usize]; |
62 | 0 | *buffer = value.as_ptr(); |
63 | 0 | *buffer_len = value.len() as u32; |
64 | 0 | return true; |
65 | 0 | } |
66 | 0 | } |
67 | 0 | false |
68 | 0 | } |
69 | | |
70 | | #[no_mangle] |
71 | 0 | pub unsafe extern "C" fn SCKrb5TxGetSname( |
72 | 0 | _de: *mut DetectEngineThreadCtx, tx: *const c_void, _flags: u8, i: u32, buffer: *mut *const u8, |
73 | 0 | buffer_len: *mut u32, |
74 | 0 | ) -> bool { |
75 | 0 | let tx = cast_pointer!(tx, KRB5Transaction); |
76 | 0 | if let Some(ref s) = tx.sname { |
77 | 0 | if (i as usize) < s.name_string.len() { |
78 | 0 | let value = &s.name_string[i as usize]; |
79 | 0 | *buffer = value.as_ptr(); |
80 | 0 | *buffer_len = value.len() as u32; |
81 | 0 | return true; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | false |
85 | 0 | } |
86 | | |
87 | | const KRB_TICKET_FASTARRAY_SIZE: usize = 256; |
88 | | |
89 | | #[derive(Debug)] |
90 | | pub struct DetectKrb5TicketEncryptionList { |
91 | | positive: [bool; KRB_TICKET_FASTARRAY_SIZE], |
92 | | negative: [bool; KRB_TICKET_FASTARRAY_SIZE], |
93 | | other: Vec<EncryptionType>, |
94 | | } |
95 | | |
96 | | impl Default for DetectKrb5TicketEncryptionList { |
97 | 0 | fn default() -> Self { |
98 | 0 | Self::new() |
99 | 0 | } |
100 | | } |
101 | | |
102 | | impl DetectKrb5TicketEncryptionList { |
103 | 850 | pub fn new() -> Self { |
104 | 850 | Self { |
105 | 850 | positive: [false; KRB_TICKET_FASTARRAY_SIZE], |
106 | 850 | negative: [false; KRB_TICKET_FASTARRAY_SIZE], |
107 | 850 | other: Vec::new(), |
108 | 850 | } |
109 | 850 | } |
110 | | } |
111 | | |
112 | | // Suppress large enum variant lint as the LIST is very large compared |
113 | | // to the boolean variant. |
114 | | #[derive(Debug)] |
115 | | #[allow(clippy::large_enum_variant)] |
116 | | pub enum DetectKrb5TicketEncryptionData { |
117 | | WEAK(bool), |
118 | | LIST(DetectKrb5TicketEncryptionList), |
119 | | } |
120 | | |
121 | 872 | pub fn detect_parse_encryption_weak(i: &str) -> IResult<&str, DetectKrb5TicketEncryptionData> { |
122 | 872 | let (i, neg) = opt(char('!'))(i)?; |
123 | 872 | let (i, _) = tag("weak")(i)?; |
124 | 22 | let value = neg.is_none(); |
125 | 22 | return Ok((i, DetectKrb5TicketEncryptionData::WEAK(value))); |
126 | 872 | } |
127 | | |
128 | | trait MyFromStr { |
129 | | fn from_str(s: &str) -> Result<Self, String> |
130 | | where |
131 | | Self: Sized; |
132 | | } |
133 | | |
134 | | impl MyFromStr for EncryptionType { |
135 | 13.1k | fn from_str(s: &str) -> Result<Self, String> { |
136 | 13.1k | let su_slice: &str = s; |
137 | 13.1k | match su_slice { |
138 | 13.1k | "des-cbc-crc" => Ok(EncryptionType::DES_CBC_CRC), |
139 | 13.1k | "des-cbc-md4" => Ok(EncryptionType::DES_CBC_MD4), |
140 | 13.1k | "des-cbc-md5" => Ok(EncryptionType::DES_CBC_MD5), |
141 | 13.1k | "des3-cbc-md5" => Ok(EncryptionType::DES3_CBC_MD5), |
142 | 13.1k | "des3-cbc-sha1" => Ok(EncryptionType::DES3_CBC_SHA1), |
143 | 13.1k | "dsaWithSHA1-CmsOID" => Ok(EncryptionType::DSAWITHSHA1_CMSOID), |
144 | 13.1k | "md5WithRSAEncryption-CmsOID" => Ok(EncryptionType::MD5WITHRSAENCRYPTION_CMSOID), |
145 | 13.1k | "sha1WithRSAEncryption-CmsOID" => Ok(EncryptionType::SHA1WITHRSAENCRYPTION_CMSOID), |
146 | 13.1k | "rc2CBC-EnvOID" => Ok(EncryptionType::RC2CBC_ENVOID), |
147 | 13.1k | "rsaEncryption-EnvOID" => Ok(EncryptionType::RSAENCRYPTION_ENVOID), |
148 | 13.1k | "rsaES-OAEP-ENV-OID" => Ok(EncryptionType::RSAES_OAEP_ENV_OID), |
149 | 13.1k | "des-ede3-cbc-Env-OID" => Ok(EncryptionType::DES_EDE3_CBC_ENV_OID), |
150 | 13.1k | "des3-cbc-sha1-kd" => Ok(EncryptionType::DES3_CBC_SHA1_KD), |
151 | 13.1k | "aes128-cts-hmac-sha1-96" => Ok(EncryptionType::AES128_CTS_HMAC_SHA1_96), |
152 | 13.1k | "aes256-cts-hmac-sha1-96" => Ok(EncryptionType::AES256_CTS_HMAC_SHA1_96), |
153 | 13.1k | "aes128-cts-hmac-sha256-128" => Ok(EncryptionType::AES128_CTS_HMAC_SHA256_128), |
154 | 13.1k | "aes256-cts-hmac-sha384-192" => Ok(EncryptionType::AES256_CTS_HMAC_SHA384_192), |
155 | 13.1k | "rc4-hmac" => Ok(EncryptionType::RC4_HMAC), |
156 | 13.1k | "rc4-hmac-exp" => Ok(EncryptionType::RC4_HMAC_EXP), |
157 | 13.1k | "camellia128-cts-cmac" => Ok(EncryptionType::CAMELLIA128_CTS_CMAC), |
158 | 13.1k | "camellia256-cts-cmac" => Ok(EncryptionType::CAMELLIA256_CTS_CMAC), |
159 | 13.1k | "subkey-keymaterial" => Ok(EncryptionType::SUBKEY_KEYMATERIAL), |
160 | 13.1k | "rc4-md4" => Ok(EncryptionType::RC4_MD4), |
161 | 13.0k | "rc4-plain2" => Ok(EncryptionType::RC4_PLAIN2), |
162 | 13.0k | "rc4-lm" => Ok(EncryptionType::RC4_LM), |
163 | 12.8k | "rc4-sha" => Ok(EncryptionType::RC4_SHA), |
164 | 12.8k | "des-plain" => Ok(EncryptionType::DES_PLAIN), |
165 | 12.8k | "rc4-hmac-OLD" => Ok(EncryptionType::RC4_HMAC_OLD), |
166 | 12.8k | "rc4-plain-OLD" => Ok(EncryptionType::RC4_PLAIN_OLD), |
167 | 12.8k | "rc4-hmac-OLD-exp" => Ok(EncryptionType::RC4_HMAC_OLD_EXP), |
168 | 12.8k | "rc4-plain-OLD-exp" => Ok(EncryptionType::RC4_PLAIN_OLD_EXP), |
169 | 12.8k | "rc4-plain" => Ok(EncryptionType::RC4_PLAIN), |
170 | 12.8k | "rc4-plain-exp" => Ok(EncryptionType::RC4_PLAIN_EXP), |
171 | | _ => { |
172 | 12.8k | if let Ok(num) = s.parse::<i32>() { |
173 | 12.1k | return Ok(EncryptionType(num)); |
174 | | } else { |
175 | 624 | return Err(format!("'{}' is not a valid value for EncryptionType", s)); |
176 | | } |
177 | | } |
178 | | } |
179 | 13.1k | } |
180 | | } |
181 | | |
182 | 98.4k | pub fn is_alphanumeric_or_dash(chr: char) -> bool { |
183 | 98.4k | return chr.is_alphanumeric() || chr == '-'; |
184 | 98.4k | } |
185 | | |
186 | 13.4k | pub fn detect_parse_encryption_item(i: &str) -> IResult<&str, EncryptionType> { |
187 | 13.4k | let (i, _) = opt(is_a(" "))(i)?; |
188 | 13.4k | let (i, e) = map_res(take_while1(is_alphanumeric_or_dash), |s: &str| { |
189 | 13.1k | EncryptionType::from_str(s) |
190 | 13.4k | })(i)?; |
191 | 12.5k | let (i, _) = opt(is_a(" "))(i)?; |
192 | 12.5k | let (i, _) = opt(char(','))(i)?; |
193 | 12.5k | return Ok((i, e)); |
194 | 13.4k | } |
195 | | |
196 | 850 | pub fn detect_parse_encryption_list(i: &str) -> IResult<&str, DetectKrb5TicketEncryptionData> { |
197 | 850 | let mut l = DetectKrb5TicketEncryptionList::new(); |
198 | 850 | let (i, v) = many1(detect_parse_encryption_item)(i)?; |
199 | 12.5k | for &val in v.iter() { |
200 | 12.5k | let vali = val.0; |
201 | | // KRB_TICKET_FASTARRAY_SIZE is a constant typed usize but which fits in a i32 |
202 | 12.5k | if vali < 0 && vali > -(KRB_TICKET_FASTARRAY_SIZE as i32) { |
203 | 1.25k | l.negative[(-vali) as usize] = true; |
204 | 11.2k | } else if vali >= 0 && (vali as usize) < KRB_TICKET_FASTARRAY_SIZE { |
205 | 1.49k | l.positive[vali as usize] = true; |
206 | 9.80k | } else { |
207 | 9.80k | l.other.push(val); |
208 | 9.80k | } |
209 | | } |
210 | 341 | return Ok((i, DetectKrb5TicketEncryptionData::LIST(l))); |
211 | 850 | } |
212 | | |
213 | 872 | pub fn detect_parse_encryption(i: &str) -> IResult<&str, DetectKrb5TicketEncryptionData> { |
214 | 872 | let (i, _) = opt(is_a(" "))(i)?; |
215 | 872 | let (i, parsed) = alt((detect_parse_encryption_weak, detect_parse_encryption_list))(i)?; |
216 | 517 | let (i, _) = all_consuming(take_while(|c| c == ' '))(i)?; |
217 | 216 | return Ok((i, parsed)); |
218 | 872 | } |
219 | | |
220 | | #[no_mangle] |
221 | 872 | pub unsafe extern "C" fn SCKrb5DetectEncryptionParse( |
222 | 872 | ustr: *const std::os::raw::c_char, |
223 | 872 | ) -> *mut DetectKrb5TicketEncryptionData { |
224 | 872 | let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe |
225 | 872 | if let Ok(s) = ft_name.to_str() { |
226 | 872 | if let Ok((_, ctx)) = detect_parse_encryption(s) { |
227 | 216 | let boxed = Box::new(ctx); |
228 | 216 | return Box::into_raw(boxed) as *mut _; |
229 | 656 | } |
230 | 0 | } |
231 | 656 | return std::ptr::null_mut(); |
232 | 872 | } |
233 | | |
234 | | #[no_mangle] |
235 | 0 | pub unsafe extern "C" fn SCKrb5DetectEncryptionMatch( |
236 | 0 | tx: &KRB5Transaction, ctx: &DetectKrb5TicketEncryptionData, |
237 | 0 | ) -> std::os::raw::c_int { |
238 | 0 | if let Some(x) = tx.ticket_etype { |
239 | 0 | match ctx { |
240 | 0 | DetectKrb5TicketEncryptionData::WEAK(w) => { |
241 | 0 | if (test_weak_encryption(x) && *w) || (!test_weak_encryption(x) && !*w) { |
242 | 0 | return 1; |
243 | 0 | } |
244 | | } |
245 | 0 | DetectKrb5TicketEncryptionData::LIST(l) => { |
246 | 0 | let vali = x.0; |
247 | 0 | if vali < 0 && ((-vali) as usize) < KRB_TICKET_FASTARRAY_SIZE { |
248 | 0 | if l.negative[(-vali) as usize] { |
249 | 0 | return 1; |
250 | 0 | } |
251 | 0 | } else if vali >= 0 && (vali as usize) < KRB_TICKET_FASTARRAY_SIZE { |
252 | 0 | if l.positive[vali as usize] { |
253 | 0 | return 1; |
254 | 0 | } |
255 | | } else { |
256 | 0 | for &val in l.other.iter() { |
257 | 0 | if x == val { |
258 | 0 | return 1; |
259 | 0 | } |
260 | | } |
261 | | } |
262 | | } |
263 | | } |
264 | 0 | } |
265 | 0 | return 0; |
266 | 0 | } |
267 | | |
268 | | #[no_mangle] |
269 | 216 | pub unsafe extern "C" fn SCKrb5DetectEncryptionFree(ctx: &mut DetectKrb5TicketEncryptionData) { |
270 | | // Just unbox... |
271 | 216 | std::mem::drop(Box::from_raw(ctx)); |
272 | 216 | } |
273 | | |
274 | | #[cfg(test)] |
275 | | mod tests { |
276 | | |
277 | | use super::*; |
278 | | |
279 | | #[test] |
280 | | fn test_detect_parse_encryption() { |
281 | | match detect_parse_encryption(" weak ") { |
282 | | Ok((rem, ctx)) => { |
283 | | match ctx { |
284 | | DetectKrb5TicketEncryptionData::WEAK(w) => { |
285 | | assert!(w); |
286 | | } |
287 | | _ => { |
288 | | panic!("Result should have been weak."); |
289 | | } |
290 | | } |
291 | | // And we should have no bytes left. |
292 | | assert_eq!(rem.len(), 0); |
293 | | } |
294 | | _ => { |
295 | | panic!("Result should have been ok."); |
296 | | } |
297 | | } |
298 | | match detect_parse_encryption("!weak") { |
299 | | Ok((rem, ctx)) => { |
300 | | match ctx { |
301 | | DetectKrb5TicketEncryptionData::WEAK(w) => { |
302 | | assert!(!w); |
303 | | } |
304 | | _ => { |
305 | | panic!("Result should have been weak."); |
306 | | } |
307 | | } |
308 | | // And we should have no bytes left. |
309 | | assert_eq!(rem.len(), 0); |
310 | | } |
311 | | _ => { |
312 | | panic!("Result should have been ok."); |
313 | | } |
314 | | } |
315 | | match detect_parse_encryption(" des-cbc-crc , -128,2 257") { |
316 | | Ok((rem, ctx)) => { |
317 | | match ctx { |
318 | | DetectKrb5TicketEncryptionData::LIST(l) => { |
319 | | assert!(l.positive[EncryptionType::DES_CBC_CRC.0 as usize]); |
320 | | assert!(l.negative[128]); |
321 | | assert!(l.positive[2]); |
322 | | assert_eq!(l.other.len(), 1); |
323 | | assert_eq!(l.other[0], EncryptionType(257)); |
324 | | } |
325 | | _ => { |
326 | | panic!("Result should have been list."); |
327 | | } |
328 | | } |
329 | | // And we should have no bytes left. |
330 | | assert_eq!(rem.len(), 0); |
331 | | } |
332 | | _ => { |
333 | | panic!("Result should have been ok."); |
334 | | } |
335 | | } |
336 | | let ctx = detect_parse_encryption("-2147483648").unwrap().1; |
337 | | match ctx { |
338 | | DetectKrb5TicketEncryptionData::LIST(l) => { |
339 | | assert_eq!(l.other.len(), 1); |
340 | | assert_eq!(l.other[0], EncryptionType(i32::MIN)); |
341 | | } |
342 | | _ => { |
343 | | panic!("Result should have been list."); |
344 | | } |
345 | | } |
346 | | } |
347 | | } |