M Cargo.lock => Cargo.lock +29 -1
@@ 7,6 7,15 @@ name = "agena-cgi"
version = "0.1.0"
[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 186,12 195,14 @@ dependencies = [
[[package]]
name = "gemserv"
-version = "0.6.5"
+version = "0.6.6"
dependencies = [
"futures-util",
+ "lazy_static",
"log",
"mime",
"new_mime_guess",
+ "regex",
"rustls-pemfile",
"serde",
"serde_derive",
@@ 504,6 515,23 @@ dependencies = [
]
[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
M Cargo.toml => Cargo.toml +3 -1
@@ 1,6 1,6 @@
[package]
name = "gemserv"
-version = "0.6.5"
+version = "0.6.6"
authors = ["int 80h <int@80h.dev>"]
edition = "2018"
description = "A gemini server"
@@ 27,6 27,8 @@ log = "0.4.14"
simple_logger = "1.16"
sha2 = "0.9.8"
x509-parser = "0.12"
+regex = "1.5.4"
+lazy_static = "1.4.0"
[dependencies.tokio-rustls]
version = "0.23.2"
M README => README +4 -0
@@ 78,6 78,10 @@ TLS variables
## Changelog
+### [0.6.6] - 20220217
+
+Bug fix: File path is checked to make sure it's in the root directory or in ~/public_gemini
+
### [0.6.5] - 20220209
Bug fix: Another traversal bug.
M src/con_handler.rs => src/con_handler.rs +33 -0
@@ 3,6 3,7 @@ use std::path::{Path, PathBuf};
use tokio::fs::{self, File};
use tokio::io::{self, AsyncBufReadExt, AsyncWrite, BufReader};
use url::Url;
+use regex::Regex;
#[cfg(any(feature = "cgi", feature = "scgi"))]
use crate::cgi;
@@ 15,6 16,7 @@ use crate::util;
type Result<T = ()> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
+
fn get_mime(path: &Path) -> String {
let mut mime = "text/gemini".to_string();
if path.is_dir() {
@@ 248,6 250,37 @@ pub async fn handle_connection(mut con: conn::Connection, url: url::Url) -> Resu
}
}
+ match path.canonicalize() {
+ Ok(p) => {
+ if !p.starts_with(&con.srv.server.dir) {
+ if con.srv.server.usrdir.unwrap_or(false) {
+ #[cfg(target_os = "macos")]
+ lazy_static! {
+ static ref RE: Regex = Regex::new(r"^/Users/([^/]*)/public_gemini(.*)").unwrap();
+ }
+ #[cfg(not(target_os = "macos"))]
+ lazy_static! {
+ static ref RE: Regex = Regex::new(r"^/home/([^/]*)/public_gemini(.*)").unwrap();
+ }
+ if !RE.is_match(p.to_str().unwrap()) {
+ logger::logger(con.peer_addr, Status::NotFound, url.as_str());
+ con.send_status(Status::NotFound, None).await?;
+ return Ok(());
+ }
+ } else {
+ logger::logger(con.peer_addr, Status::NotFound, url.as_str());
+ con.send_status(Status::NotFound, None).await?;
+ return Ok(());
+ }
+ }
+ },
+ Err(_) => {
+ logger::logger(con.peer_addr, Status::NotFound, url.as_str());
+ con.send_status(Status::NotFound, None).await?;
+ return Ok(());
+ },
+ }
+
if !path.exists() {
// See if it's a subpath of a CGI script before returning NotFound
#[cfg(feature = "cgi")]
M src/main.rs => src/main.rs +2 -0
@@ 1,5 1,7 @@
#[macro_use]
extern crate serde_derive;
+#[macro_use]
+extern crate lazy_static;
#[cfg(any(feature = "cgi", feature = "scgi"))]
mod cgi;