From 872197c55805161e1ed6bbbd13b169b3bc809fd5 Mon Sep 17 00:00:00 2001 From: Jan Wolff Date: Sun, 17 May 2020 12:54:10 +0200 Subject: [PATCH] mime type handling and setuid/setgid support --- Cargo.lock | 20 ++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 17 ++++++++++++++++- src/mime/mod.rs | 20 ++++++++++++++++++++ src/server/handler.rs | 7 ++++++- src/server/mod.rs | 38 +++++++++++++++++++++++++++++++++++--- 6 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 src/mime/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 19bbbb0..65bd816 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,19 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "nix" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + [[package]] name = "openssl" version = "0.10.29" @@ -111,6 +124,7 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" name = "sheldond" version = "0.1.0" dependencies = [ + "nix", "openssl", "url", ] @@ -155,3 +169,9 @@ name = "vcpkg" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index ab1b0c2..d61783c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2018" [dependencies] openssl = { version = "0.10" } url = { version = "2.1" } +nix = { version = "0.17" } diff --git a/src/main.rs b/src/main.rs index a381e28..aa572bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod server; +mod mime; use std::env; fn help() { @@ -12,12 +13,16 @@ fn help() { println!(" -l, --listen\t\tadd a listening address (you can define multiple)"); println!(" -d, --default-host\tdefault hostname to listen for"); println!(" -g, --gem-root\tpath to the gemini root, aka the folder to serve files from"); + println!(" --user\tuser to drop to after opening TLS socket"); + println!(" --group\tgroup to drop to after opening TLS socket"); } fn parse_args() -> Option { let mut has_addr = false; let mut has_host = false; let mut has_root = false; + let mut has_user = false; + let mut has_group = false; let mut config = server::ServerConfig::new(); let mut args = env::args(); @@ -42,12 +47,22 @@ fn parse_args() -> Option { config.set_gem_root(gem_root); has_root = true; } + if arg == "--user" { + let user = args.next().unwrap(); + config.set_user(user); + has_user = true; + } + if arg == "--group" { + let group = args.next().unwrap(); + config.set_group(group); + has_group = true; + } } None => break, } } - if !has_addr || !has_host || !has_root { + if !has_addr || !has_host || !has_root || !has_user || !has_group { return None; } diff --git a/src/mime/mod.rs b/src/mime/mod.rs new file mode 100644 index 0000000..4727f7e --- /dev/null +++ b/src/mime/mod.rs @@ -0,0 +1,20 @@ +use std::ffi::OsStr; + +pub fn default_mime_type() -> &'static str { + "application/octet-stream" +} + +pub fn get_mime_type(extension: &OsStr) -> &'static str { + let ext_str = match extension.to_str() { + Some(ext_str) => ext_str, + None => { + return default_mime_type(); + }, + }; + + match ext_str { + "gmi" => "text/gemini", + "txt" => "text/plain", + _ => default_mime_type(), + } +} diff --git a/src/server/handler.rs b/src/server/handler.rs index d27d292..ec9b523 100644 --- a/src/server/handler.rs +++ b/src/server/handler.rs @@ -1,5 +1,6 @@ use crate::server::response; use crate::server::ServerConfig; +use crate::mime; use openssl::ssl::SslStream; use std::fs::File; use std::io::{copy, BufReader, BufWriter}; @@ -77,7 +78,11 @@ fn handle_response(config: &ServerConfig, url: Url, mut stream: &mut SslStream mime::get_mime_type(ext), + None => mime::default_mime_type(), + }; + let header = response::Header::new(response::Status::Success, mime_type); send_header(&mut stream, &header); let mut buf_file = BufReader::new(file); diff --git a/src/server/mod.rs b/src/server/mod.rs index 696a3a5..f00ab35 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use std::thread; use std::vec::Vec; use url::Url; +use nix::unistd; pub mod handler; pub mod response; @@ -14,6 +15,8 @@ pub struct ServerConfig { default_host: Url, gem_root: PathBuf, addrs: Vec, + user: unistd::Uid, + group: unistd::Gid, } impl ServerConfig { @@ -22,6 +25,8 @@ impl ServerConfig { default_host: Url::parse("gemini://localhost").unwrap(), gem_root: PathBuf::from(""), addrs: Vec::new(), + user: unistd::getuid(), + group: unistd::getgid(), } } @@ -45,17 +50,29 @@ impl ServerConfig { Err(e) => panic!(e), }); } + + pub fn set_user(&mut self, uname: String) { + self.user = match unistd::User::from_name(&uname) { + Ok(user) => user.unwrap().uid, + Err(e) => panic!(e), + }; + } + + pub fn set_group(&mut self, gname: String) { + self.group = match unistd::Group::from_name(&gname) { + Ok(group) => group.unwrap().gid, + Err(e) => panic!(e), + }; + } } pub struct Server { - acceptor: std::sync::Arc, config: ServerConfig, } impl Server { pub fn new(config: &ServerConfig) -> Server { Server { - acceptor: Server::build_acceptor(), config: config.clone(), } } @@ -71,12 +88,27 @@ impl Server { } pub fn serve(&self) { + let acceptor = Server::build_acceptor(); let listener = TcpListener::bind(&self.config.addrs[..]).unwrap(); + if self.config.user.is_root() { + panic!("refusing to run as root"); + } + + match unistd::setgid(self.config.group) { + Ok(_) => {}, + Err(e) => {panic!(e);}, + }; + + match unistd::setuid(self.config.user) { + Ok(_) => {}, + Err(e) => {panic!(e);}, + }; + for stream in listener.incoming() { match stream { Ok(stream) => { - let acceptor = self.acceptor.clone(); + let acceptor = acceptor.clone(); let config = self.config.clone(); thread::spawn(move || { let stream = acceptor.accept(stream).unwrap();