summaryrefslogtreecommitdiffstats
path: root/rust/src/rdp/util.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/rdp/util.rs')
-rw-r--r--rust/src/rdp/util.rs175
1 files changed, 175 insertions, 0 deletions
diff --git a/rust/src/rdp/util.rs b/rust/src/rdp/util.rs
new file mode 100644
index 0000000..a4228f2
--- /dev/null
+++ b/rust/src/rdp/util.rs
@@ -0,0 +1,175 @@
+/* Copyright (C) 2019 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// Author: Zach Kelly <zach.kelly@lmco.com>
+
+use crate::rdp::error::RdpError;
+use byteorder::ReadBytesExt;
+use memchr::memchr;
+use nom7::{Err, IResult, Needed};
+use std::io::Cursor;
+use widestring::U16CString;
+
+/// converts a raw u8 slice of little-endian wide chars into a String
+pub fn le_slice_to_string(input: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
+ let mut vec = Vec::new();
+ let mut cursor = Cursor::new(input);
+ while let Ok(x) = cursor.read_u16::<byteorder::LittleEndian>() {
+ if x == 0 {
+ break;
+ }
+ vec.push(x);
+ }
+ match U16CString::new(vec) {
+ Ok(x) => match x.to_string() {
+ Ok(x) => Ok(x),
+ Err(e) => Err(e.into()),
+ },
+ Err(e) => Err(e.into()),
+ }
+}
+
+/// converts a raw u8 slice of null-padded utf7 chars into a String, dropping the nulls
+pub fn utf7_slice_to_string(input: &[u8]) -> Result<String, Box<dyn std::error::Error>> {
+ let s = match memchr(b'\0', input) {
+ Some(end) => &input[..end],
+ None => input,
+ };
+ match std::str::from_utf8(s) {
+ Ok(s) => Ok(String::from(s)),
+ Err(e) => Err(e.into()),
+ }
+}
+
+/// parses a PER length determinant, to determine the length of the data following
+/// x.691-spec: section 10.9
+pub fn parse_per_length_determinant(input: &[u8]) -> IResult<&[u8], u32, RdpError> {
+ if input.is_empty() {
+ // need a single byte to begin length determination
+ Err(Err::Incomplete(Needed::new(1)))
+ } else {
+ let bit7 = input[0] >> 7;
+ match bit7 {
+ 0b0 => {
+ // byte starts with 0b0. Length stored in the lower 7 bits of the current byte
+ let length = input[0] as u32 & 0x7f;
+ Ok((&input[1..], length))
+ }
+ _ => {
+ let bit6 = input[0] >> 6 & 0x1;
+ match bit6 {
+ 0b0 => {
+ // byte starts with 0b10. Length stored in the remaining 6 bits and the next byte
+ if input.len() < 2 {
+ Err(Err::Incomplete(Needed::new(2)))
+ } else {
+ let length = ((input[0] as u32 & 0x3f) << 8) | input[1] as u32;
+ Ok((&input[2..], length))
+ }
+ }
+ _ => {
+ // byte starts with 0b11. Without an example to confirm 16K+ lengths are properly
+ // handled, leaving this branch unimplemented
+ Err(Err::Error(RdpError::UnimplementedLengthDeterminant))
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::rdp::error::RdpError;
+ use nom7::Needed;
+
+ #[test]
+ fn test_le_string_abc() {
+ let abc = &[0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00];
+ assert_eq!(String::from("ABC"), le_slice_to_string(abc).unwrap());
+ }
+
+ #[test]
+ fn test_le_string_empty() {
+ let empty = &[];
+ assert_eq!(String::from(""), le_slice_to_string(empty).unwrap());
+ }
+
+ #[test]
+ fn test_le_string_invalid() {
+ let not_utf16le = &[0x00, 0xd8, 0x01, 0x00];
+ assert!(le_slice_to_string(not_utf16le).is_err());
+ }
+
+ #[test]
+ fn test_utf7_string_abc() {
+ let abc = &[0x41, 0x42, 0x43, 0x00, 0x00];
+ assert_eq!(String::from("ABC"), utf7_slice_to_string(abc).unwrap());
+ }
+
+ #[test]
+ fn test_utf7_string_empty() {
+ let empty = &[];
+ assert_eq!(String::from(""), utf7_slice_to_string(empty).unwrap());
+ }
+
+ #[test]
+ fn test_utf7_string_invalid() {
+ let not_utf7 = &[0x80];
+ assert!(utf7_slice_to_string(not_utf7).is_err());
+ }
+
+ #[test]
+ fn test_length_single_length() {
+ let bytes = &[0x28];
+ assert_eq!(Ok((&[][..], 0x28)), parse_per_length_determinant(bytes));
+ }
+
+ #[test]
+ fn test_length_double_length() {
+ let bytes = &[0x81, 0x28];
+ assert_eq!(Ok((&[][..], 0x128)), parse_per_length_determinant(bytes));
+ }
+
+ #[test]
+ fn test_length_single_length_incomplete() {
+ let bytes = &[];
+ assert_eq!(
+ Err(Err::Incomplete(Needed::new(1))),
+ parse_per_length_determinant(bytes)
+ )
+ }
+
+ #[test]
+ fn test_length_16k_unimplemented() {
+ let bytes = &[0xc0];
+ assert_eq!(
+ Err(Err::Error(RdpError::UnimplementedLengthDeterminant)),
+ parse_per_length_determinant(bytes)
+ )
+ }
+
+ #[test]
+ fn test_length_double_length_incomplete() {
+ let bytes = &[0x81];
+ assert_eq!(
+ Err(Err::Incomplete(Needed::new(2))),
+ parse_per_length_determinant(bytes)
+ )
+ }
+}