Skip to content

Commit

Permalink
Add POSIX setgid/setuid/sticky bit support (#59)
Browse files Browse the repository at this point in the history
* posix: Do not refute listings including setuid/setgid/sticky bits

As written, the regex that matches on POSIX directory listings misses
some cases that can appear, especially on more complex systems.  These
are cases where the set-user-ID on execution, set-group-ID on execution,
and saved-text (aka setuid, setgid, and "sticky" bits respectively) are
set, typically by a system administrator.

These are quite consistently represented by 's' and 't' in place of the
eXecute bit, and the uppercase alternatives ('S' and 'T') if the eXecute
bit is not set, but the corresponding setuid/setgid/sticky bit is set.

Given that the extant regex does not attempt to validate the ordering
(i.e., xwrxwrxwr is entirely valid), I chose to simply extend the set of
valid values that are accepted in the string of 9 characters following
the initial -/l/d part.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* posix: Correct pex parsing logic to handle uppercase S/T bits

Lowercase s and lowercase t indicate the presence of a setuid/setgid or
sticky bit (respectively), when the execute bit is also present. Their
uppercase counterparts indicate the presence of the bit but not the
execute bit.

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

* posix: Minor cleanup / typo correction

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>

---------

Signed-off-by: Kristofer Rye <kristofer.rye@gmail.com>
  • Loading branch information
rye authored Oct 16, 2023
1 parent 9b45545 commit 1e9cf06
Showing 1 changed file with 93 additions and 4 deletions.
97 changes: 93 additions & 4 deletions suppaftp/src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use thiserror::Error;

/// POSIX system regex to parse list output
static POSIX_LS_RE: Lazy<Regex> = lazy_regex!(
r#"^([\-ld])([\-rwxs]{9})\s+(\d+)\s+(.+)\s+(.+)\s+(\d+)\s+(.+\s+\d{1,2}\s+(?:\d{1,2}:\d{1,2}|\d{4}))\s+(.+)$"#
r#"^([\-ld])([\-rwxsStT]{9})\s+(\d+)\s+(.+)\s+(.+)\s+(\d+)\s+(.+\s+\d{1,2}\s+(?:\d{1,2}:\d{1,2}|\d{4}))\s+(.+)$"#
);
/// DOS system regex to parse list output
static DOS_LS_RE: Lazy<Regex> =
Expand Down Expand Up @@ -194,7 +194,7 @@ impl File {
match POSIX_LS_RE.captures(line) {
// String matches regex
Some(metadata) => {
trace!("Parsed POXIS line {}", line);
trace!("Parsed POSIX line {}", line);
// NOTE: metadata fmt: (regex, file_type, permissions, link_count, uid, gid, filesize, mtime, filename)
// Expected 7 + 1 (8) values: + 1 cause regex is repeated at 0
if metadata.len() < 8 {
Expand All @@ -214,7 +214,7 @@ impl File {
let mut count: u8 = 0;
for (i, c) in metadata.get(2).unwrap().as_str()[range].chars().enumerate() {
match c {
'-' => {}
'-' | 'S' | 'T' => {}
_ => {
count += match i {
0 => 4,
Expand Down Expand Up @@ -414,10 +414,11 @@ impl TryFrom<&str> for File {
type Error = ParseError;

fn try_from(line: &str) -> Result<Self, Self::Error> {
// First try to parse the line in POSIX format (vast majority case).
match Self::from_posix_line(line) {
Ok(entry) => Ok(entry),
// If POSIX parsing fails, try with DOS parser.
Err(_) => match Self::from_dos_line(line) {
// If POSIX parsing fails, try with DOS parser
Ok(entry) => Ok(entry),
Err(err) => Err(err),
},
Expand Down Expand Up @@ -586,6 +587,94 @@ mod test {
.unwrap(),
Duration::from_secs(1541376000)
);
// Setuid bit
let file: File =
File::from_str("drws------ 2 u-redacted g-redacted 3864 Feb 17 2023 sas")
.ok()
.unwrap();
assert_eq!(file.is_directory(), true);
assert_eq!(file.can_read(PosixPexQuery::Owner), true);
assert_eq!(file.can_write(PosixPexQuery::Owner), true);
assert_eq!(file.can_execute(PosixPexQuery::Owner), true);
assert_eq!(file.can_read(PosixPexQuery::Group), false);
assert_eq!(file.can_write(PosixPexQuery::Group), false);
assert_eq!(file.can_execute(PosixPexQuery::Group), false);
assert_eq!(file.can_read(PosixPexQuery::Others), false);
assert_eq!(file.can_write(PosixPexQuery::Others), false);
assert_eq!(file.can_execute(PosixPexQuery::Others), false);
let file: File =
File::from_str("drwS------ 2 u-redacted g-redacted 3864 Feb 17 2023 sas")
.ok()
.unwrap();
assert_eq!(file.is_directory(), true);
assert_eq!(file.can_read(PosixPexQuery::Owner), true);
assert_eq!(file.can_write(PosixPexQuery::Owner), true);
assert_eq!(file.can_execute(PosixPexQuery::Owner), false);
assert_eq!(file.can_read(PosixPexQuery::Group), false);
assert_eq!(file.can_write(PosixPexQuery::Group), false);
assert_eq!(file.can_execute(PosixPexQuery::Group), false);
assert_eq!(file.can_read(PosixPexQuery::Others), false);
assert_eq!(file.can_write(PosixPexQuery::Others), false);
assert_eq!(file.can_execute(PosixPexQuery::Others), false);
// Setgid bit
let file: File =
File::from_str("drwx--s--- 2 u-redacted g-redacted 3864 Feb 17 2023 sas")
.ok()
.unwrap();
assert_eq!(file.is_directory(), true);
assert_eq!(file.can_read(PosixPexQuery::Owner), true);
assert_eq!(file.can_write(PosixPexQuery::Owner), true);
assert_eq!(file.can_execute(PosixPexQuery::Owner), true);
assert_eq!(file.can_read(PosixPexQuery::Group), false);
assert_eq!(file.can_write(PosixPexQuery::Group), false);
assert_eq!(file.can_execute(PosixPexQuery::Group), true);
assert_eq!(file.can_read(PosixPexQuery::Others), false);
assert_eq!(file.can_write(PosixPexQuery::Others), false);
assert_eq!(file.can_execute(PosixPexQuery::Others), false);
let file: File =
File::from_str("drwx--S--- 2 u-redacted g-redacted 3864 Feb 17 2023 sas")
.ok()
.unwrap();
assert_eq!(file.is_directory(), true);
assert_eq!(file.can_read(PosixPexQuery::Owner), true);
assert_eq!(file.can_write(PosixPexQuery::Owner), true);
assert_eq!(file.can_execute(PosixPexQuery::Owner), true);
assert_eq!(file.can_read(PosixPexQuery::Group), false);
assert_eq!(file.can_write(PosixPexQuery::Group), false);
assert_eq!(file.can_execute(PosixPexQuery::Group), false);
assert_eq!(file.can_read(PosixPexQuery::Others), false);
assert_eq!(file.can_write(PosixPexQuery::Others), false);
assert_eq!(file.can_execute(PosixPexQuery::Others), false);
// Sticky bit
let file: File =
File::from_str("drwx-----t 2 u-redacted g-redacted 3864 Feb 17 2023 sas")
.ok()
.unwrap();
assert_eq!(file.is_directory(), true);
assert_eq!(file.can_read(PosixPexQuery::Owner), true);
assert_eq!(file.can_write(PosixPexQuery::Owner), true);
assert_eq!(file.can_execute(PosixPexQuery::Owner), true);
assert_eq!(file.can_read(PosixPexQuery::Group), false);
assert_eq!(file.can_write(PosixPexQuery::Group), false);
assert_eq!(file.can_execute(PosixPexQuery::Group), false);
assert_eq!(file.can_read(PosixPexQuery::Others), false);
assert_eq!(file.can_write(PosixPexQuery::Others), false);
assert_eq!(file.can_execute(PosixPexQuery::Others), true);
let file: File =
File::from_str("drwx--S--T 2 u-redacted g-redacted 3864 Feb 17 2023 sas")
.ok()
.unwrap();
assert_eq!(file.is_directory(), true);
assert_eq!(file.can_read(PosixPexQuery::Owner), true);
assert_eq!(file.can_write(PosixPexQuery::Owner), true);
assert_eq!(file.can_execute(PosixPexQuery::Owner), true);
assert_eq!(file.can_read(PosixPexQuery::Group), false);
assert_eq!(file.can_write(PosixPexQuery::Group), false);
assert_eq!(file.can_execute(PosixPexQuery::Group), false);
assert_eq!(file.can_read(PosixPexQuery::Others), false);
assert_eq!(file.can_write(PosixPexQuery::Others), false);
assert_eq!(file.can_execute(PosixPexQuery::Others), false);

// Error
assert_eq!(
File::from_posix_line("drwxrwxr-x 1 0 9 Nov 5 2018 docs")
Expand Down

0 comments on commit 1e9cf06

Please sign in to comment.