mime type handling and setuid/setgid support

This commit is contained in:
Jan Wolff 2020-05-17 12:54:10 +02:00
parent 4a9c6c11ac
commit 872197c558
6 changed files with 98 additions and 5 deletions

20
Cargo.lock generated
View file

@ -68,6 +68,19 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 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]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.29" version = "0.10.29"
@ -111,6 +124,7 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
name = "sheldond" name = "sheldond"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"nix",
"openssl", "openssl",
"url", "url",
] ]
@ -155,3 +169,9 @@ name = "vcpkg"
version = "0.2.8" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

View file

@ -7,4 +7,5 @@ edition = "2018"
[dependencies] [dependencies]
openssl = { version = "0.10" } openssl = { version = "0.10" }
url = { version = "2.1" } url = { version = "2.1" }
nix = { version = "0.17" }

View file

@ -1,4 +1,5 @@
mod server; mod server;
mod mime;
use std::env; use std::env;
fn help() { fn help() {
@ -12,12 +13,16 @@ fn help() {
println!(" -l, --listen\t\tadd a listening address (you can define multiple)"); println!(" -l, --listen\t\tadd a listening address (you can define multiple)");
println!(" -d, --default-host\tdefault hostname to listen for"); 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!(" -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<server::ServerConfig> { fn parse_args() -> Option<server::ServerConfig> {
let mut has_addr = false; let mut has_addr = false;
let mut has_host = false; let mut has_host = false;
let mut has_root = false; let mut has_root = false;
let mut has_user = false;
let mut has_group = false;
let mut config = server::ServerConfig::new(); let mut config = server::ServerConfig::new();
let mut args = env::args(); let mut args = env::args();
@ -42,12 +47,22 @@ fn parse_args() -> Option<server::ServerConfig> {
config.set_gem_root(gem_root); config.set_gem_root(gem_root);
has_root = true; 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, None => break,
} }
} }
if !has_addr || !has_host || !has_root { if !has_addr || !has_host || !has_root || !has_user || !has_group {
return None; return None;
} }

20
src/mime/mod.rs Normal file
View file

@ -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(),
}
}

View file

@ -1,5 +1,6 @@
use crate::server::response; use crate::server::response;
use crate::server::ServerConfig; use crate::server::ServerConfig;
use crate::mime;
use openssl::ssl::SslStream; use openssl::ssl::SslStream;
use std::fs::File; use std::fs::File;
use std::io::{copy, BufReader, BufWriter}; use std::io::{copy, BufReader, BufWriter};
@ -77,7 +78,11 @@ fn handle_response(config: &ServerConfig, url: Url, mut stream: &mut SslStream<T
} }
}; };
let header = response::Header::new(response::Status::Success, "text/gemini"); let mime_type = match path.extension() {
Some(ext) => 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); send_header(&mut stream, &header);
let mut buf_file = BufReader::new(file); let mut buf_file = BufReader::new(file);

View file

@ -5,6 +5,7 @@ use std::sync::Arc;
use std::thread; use std::thread;
use std::vec::Vec; use std::vec::Vec;
use url::Url; use url::Url;
use nix::unistd;
pub mod handler; pub mod handler;
pub mod response; pub mod response;
@ -14,6 +15,8 @@ pub struct ServerConfig {
default_host: Url, default_host: Url,
gem_root: PathBuf, gem_root: PathBuf,
addrs: Vec<SocketAddr>, addrs: Vec<SocketAddr>,
user: unistd::Uid,
group: unistd::Gid,
} }
impl ServerConfig { impl ServerConfig {
@ -22,6 +25,8 @@ impl ServerConfig {
default_host: Url::parse("gemini://localhost").unwrap(), default_host: Url::parse("gemini://localhost").unwrap(),
gem_root: PathBuf::from(""), gem_root: PathBuf::from(""),
addrs: Vec::new(), addrs: Vec::new(),
user: unistd::getuid(),
group: unistd::getgid(),
} }
} }
@ -45,17 +50,29 @@ impl ServerConfig {
Err(e) => panic!(e), 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 { pub struct Server {
acceptor: std::sync::Arc<SslAcceptor>,
config: ServerConfig, config: ServerConfig,
} }
impl Server { impl Server {
pub fn new(config: &ServerConfig) -> Server { pub fn new(config: &ServerConfig) -> Server {
Server { Server {
acceptor: Server::build_acceptor(),
config: config.clone(), config: config.clone(),
} }
} }
@ -71,12 +88,27 @@ impl Server {
} }
pub fn serve(&self) { pub fn serve(&self) {
let acceptor = Server::build_acceptor();
let listener = TcpListener::bind(&self.config.addrs[..]).unwrap(); 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() { for stream in listener.incoming() {
match stream { match stream {
Ok(stream) => { Ok(stream) => {
let acceptor = self.acceptor.clone(); let acceptor = acceptor.clone();
let config = self.config.clone(); let config = self.config.clone();
thread::spawn(move || { thread::spawn(move || {
let stream = acceptor.accept(stream).unwrap(); let stream = acceptor.accept(stream).unwrap();