119 lines
3.0 KiB
Rust
119 lines
3.0 KiB
Rust
use std::fs::{metadata, set_permissions};
|
|||
|
|
use std::io::{Error, ErrorKind, Read, Write};
|
||
|
|
use std::net::Shutdown;
|
||
|
|
use std::os::unix::fs::PermissionsExt;
|
||
|
|
use std::os::unix::net::{UnixListener, UnixStream};
|
||
|
|
use std::path::PathBuf;
|
||
|
|
use std::thread;
|
||
|
|
|
||
|
|
extern crate yaml_rust;
|
||
|
|
|
||
|
|
use yaml_rust::{YamlEmitter, YamlLoader};
|
||
|
|
|
||
|
|
const RESPONSE_NO: [u8; 4] = [0x0, 0x2, b'N', b'O'];
|
||
|
|
const RESPONSE_OK: [u8; 4] = [0x0, 0x2, b'O', b'K'];
|
||
|
|
|
||
|
|
struct SaslSock {
|
||
|
|
name: PathBuf,
|
||
|
|
listener: UnixListener,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl SaslSock {
|
||
|
|
pub fn new(name: &str) -> Result<SaslSock, Error> {
|
||
|
|
let path = PathBuf::from(&name);
|
||
|
|
if path.exists() {
|
||
|
|
std::fs::remove_file(&path)?;
|
||
|
|
}
|
||
|
|
|
||
|
|
let listener = UnixListener::bind(&name)?;
|
||
|
|
|
||
|
|
let mut perms = metadata(&path)?.permissions();
|
||
|
|
perms.set_mode(0o0777);
|
||
|
|
set_permissions(&path, perms)?;
|
||
|
|
|
||
|
|
Ok(SaslSock {
|
||
|
|
name: PathBuf::from(&name),
|
||
|
|
listener,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Drop for SaslSock {
|
||
|
|
fn drop(&mut self) {
|
||
|
|
let _result = std::fs::remove_file(&self.name);
|
||
|
|
println!("Shutting down");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// The saslauthd protocol transmits strings as
|
||
|
|
/// 16-bit unsigned network-byte-order lengths followed
|
||
|
|
/// by the string itself.
|
||
|
|
///
|
||
|
|
fn read_string(stream: &mut UnixStream) -> Result<String, Error> {
|
||
|
|
let mut length_buffer = [0u8, 0u8];
|
||
|
|
|
||
|
|
stream.read_exact(&mut length_buffer)?;
|
||
|
|
let length = u16::from_be_bytes(length_buffer);
|
||
|
|
|
||
|
|
let mut string_buffer = vec![0u8; length as usize];
|
||
|
|
|
||
|
|
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(s: &str, stream: &mut UnixStream) -> 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];
|
||
|
|
|
||
|
|
stream.write_all(&length_buffer)?;
|
||
|
|
stream.write_all(s.as_bytes())?;
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn check_auth(mut stream: UnixStream) -> Result<(), Error> {
|
||
|
|
let userid = read_string(&mut stream)?;
|
||
|
|
let password = read_string(&mut stream)?;
|
||
|
|
let service = read_string(&mut stream)?;
|
||
|
|
let _realm = read_string(&mut stream)?;
|
||
|
|
|
||
|
|
stream.write_all(&RESPONSE_NO)?;
|
||
|
|
stream.shutdown(Shutdown::Both)?;
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn handle_client(stream: UnixStream) {
|
||
|
|
if let Err(error) = check_auth(stream) {
|
||
|
|
eprint!("{}", error);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn main() -> std::io::Result<()> {
|
||
|
|
let sock = SaslSock::new("/tmp/saslauthd.sock")?;
|
||
|
|
|
||
|
|
// accept connections and process them, spawning a new thread for each one
|
||
|
|
for stream in sock.listener.incoming() {
|
||
|
|
match stream {
|
||
|
|
Ok(stream) => {
|
||
|
|
/* connection succeeded */
|
||
|
|
thread::spawn(|| handle_client(stream));
|
||
|
|
}
|
||
|
|
Err(_err) => {
|
||
|
|
/* connection failed */
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|