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 { 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 { 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 { // 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); } } }