rustbus/
auth.rs

1//! Deals with authentication to the other side. You probably do not need this.
2
3use nix::unistd::getuid;
4use std::io::{Read, Write};
5use std::os::unix::net::UnixStream;
6
7fn write_message(msg: &str, stream: &mut UnixStream) -> std::io::Result<()> {
8    let mut buf = Vec::new();
9    buf.extend(msg.bytes());
10    buf.push(b'\r');
11    buf.push(b'\n');
12    stream.write_all(&buf)?;
13    Ok(())
14}
15
16fn has_line_ending(buf: &[u8]) -> bool {
17    for idx in 1..buf.len() {
18        if buf[idx - 1] == b'\r' && buf[idx] == b'\n' {
19            return true;
20        }
21    }
22    false
23}
24
25fn find_line_ending(buf: &[u8]) -> Option<usize> {
26    for idx in 1..buf.len() {
27        if buf[idx - 1] == b'\r' && buf[idx] == b'\n' {
28            return Some(idx - 1);
29        }
30    }
31    None
32}
33
34fn read_message(stream: &mut UnixStream, buf: &mut Vec<u8>) -> std::io::Result<String> {
35    let mut tmpbuf = [0u8; 512];
36    while !has_line_ending(buf) {
37        let bytes = stream.read(&mut tmpbuf[..])?;
38        buf.extend(&tmpbuf[..bytes])
39    }
40    let idx = find_line_ending(buf).unwrap();
41    let line = buf.drain(0..idx).collect::<Vec<_>>();
42    Ok(String::from_utf8(line).unwrap())
43}
44
45fn get_uid_as_hex() -> String {
46    let uid = getuid();
47    let mut tmp = uid.as_raw();
48    let mut numbers = Vec::new();
49    if tmp == 0 {
50        return "30".to_owned();
51    }
52    while tmp > 0 {
53        numbers.push(tmp % 10);
54        tmp /= 10;
55    }
56    let mut hex = String::new();
57    for idx in 0..numbers.len() {
58        hex.push_str(match numbers[numbers.len() - 1 - idx] {
59            0 => "30",
60            1 => "31",
61            2 => "32",
62            3 => "33",
63            4 => "34",
64            5 => "35",
65            6 => "36",
66            7 => "37",
67            8 => "38",
68            9 => "39",
69            _ => unreachable!(),
70        })
71    }
72
73    hex
74}
75
76pub enum AuthResult {
77    Ok,
78    Rejected,
79}
80
81pub fn do_auth(stream: &mut UnixStream) -> std::io::Result<AuthResult> {
82    // send a null byte as the first thing
83    stream.write_all(&[0])?;
84    write_message(&format!("AUTH EXTERNAL {}", get_uid_as_hex()), stream)?;
85
86    let mut read_buf = Vec::new();
87    let msg = read_message(stream, &mut read_buf)?;
88    if msg.starts_with("OK") {
89        Ok(AuthResult::Ok)
90    } else {
91        Ok(AuthResult::Rejected)
92    }
93}
94
95pub fn negotiate_unix_fds(stream: &mut UnixStream) -> std::io::Result<AuthResult> {
96    write_message("NEGOTIATE_UNIX_FD", stream)?;
97
98    let mut read_buf = Vec::new();
99    let msg = read_message(stream, &mut read_buf)?;
100    if msg.starts_with("AGREE_UNIX_FD") {
101        Ok(AuthResult::Ok)
102    } else {
103        Ok(AuthResult::Rejected)
104    }
105}
106
107pub fn send_begin(stream: &mut UnixStream) -> std::io::Result<()> {
108    write_message("BEGIN", stream)?;
109    Ok(())
110}