101 lines
2.8 KiB
Rust
101 lines
2.8 KiB
Rust
use std::fs::File;
|
|
use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write};
|
|
use std::net::Shutdown;
|
|
use std::os::unix::net::UnixStream;
|
|
|
|
use crate::options::OPTIONS;
|
|
|
|
const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O'];
|
|
const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K'];
|
|
|
|
pub struct Server {
|
|
stream: UnixStream,
|
|
}
|
|
|
|
impl Server {
|
|
pub fn new(stream: UnixStream) -> Result<Server, Error> {
|
|
Ok(Server { stream })
|
|
}
|
|
|
|
/// The saslauthd protocol transmits strings as
|
|
/// 16-bit unsigned network-byte-order lengths followed
|
|
/// by the string itself.
|
|
///
|
|
fn read_string(&mut self) -> Result<String, Error> {
|
|
let mut length_buffer = [0u8, 0u8];
|
|
|
|
self.stream.read_exact(&mut length_buffer)?;
|
|
let length = u16::from_be_bytes(length_buffer);
|
|
|
|
let mut string_buffer = vec![0u8; length as usize];
|
|
|
|
self.stream.read_exact(&mut string_buffer)?;
|
|
|
|
match String::from_utf8(string_buffer) {
|
|
Ok(str) => Ok(str),
|
|
Err(_err) => Err(Error::new(ErrorKind::Other, "Invalid UTF-8 sent")),
|
|
}
|
|
}
|
|
|
|
fn write_string(&mut self, s: &str) -> Result<(), Error> {
|
|
let length = s.len();
|
|
|
|
if length > 0xffff {
|
|
return Err(Error::new(ErrorKind::Other, "String too long to write"));
|
|
}
|
|
|
|
let length_buffer = [(length >> 8) as u8, (length & 0xff) as u8];
|
|
|
|
self.stream.write_all(&length_buffer)?;
|
|
self.stream.write_all(s.as_bytes())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn check_auth(&mut self, userid: &str, password: &str) -> Result<bool, Error> {
|
|
// Don't allow empty userids or passwords
|
|
//
|
|
if userid.is_empty() || password.is_empty() {
|
|
return Ok(false);
|
|
}
|
|
|
|
let mut password_file = OPTIONS.passwords.clone();
|
|
password_file.push(&userid);
|
|
|
|
let file = File::open(password_file)?;
|
|
|
|
for line in BufReader::new(file).lines().flatten() {
|
|
if !line.starts_with('#') && line.trim() == password {
|
|
return Ok(true);
|
|
}
|
|
}
|
|
|
|
Ok(false)
|
|
}
|
|
|
|
fn communicate(&mut self) -> Result<(), Error> {
|
|
let userid = self.read_string()?;
|
|
let password = self.read_string()?;
|
|
let _service = self.read_string()?;
|
|
let _realm = self.read_string()?;
|
|
|
|
match self.check_auth(&userid, &password) {
|
|
Ok(true) => self.stream.write_all(&RESPONSE_OK)?,
|
|
Ok(false) => self.stream.write_all(&RESPONSE_NO)?,
|
|
Err(e) => {
|
|
self.stream.write_all(&RESPONSE_NO)?;
|
|
self.stream.shutdown(Shutdown::Both)?;
|
|
return Err(e);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn handle_client(&mut self) {
|
|
if let Err(error) = self.communicate() {
|
|
eprint!("{}", error);
|
|
}
|
|
}
|
|
}
|