You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

438 lines
15 KiB

3 years ago
  1. use actix_web::client::{Client, ClientRequest};
  2. use actix_web::{http, web, HttpRequest, HttpResponse};
  3. use askama::Template;
  4. use chrono::Utc;
  5. use regex::Regex;
  6. use std::time::Duration;
  7. use url::Url;
  8. use csrf::{AesGcmCsrfProtection, CsrfProtection};
  9. use crate::config::get_csrf_key;
  10. use crate::account::*;
  11. use crate::config::PAYLOAD_LIMIT;
  12. use crate::config::PROXY_TIMEOUT;
  13. use crate::database::methods::InsertableForm;
  14. use crate::database::structs::Form;
  15. use crate::debug;
  16. use crate::errors::{crash, TrainCrash};
  17. use crate::sniff::*;
  18. use crate::templates::*;
  19. use crate::DbPool;
  20. use crate::CONFIG;
  21. pub async fn forward(
  22. req: HttpRequest,
  23. body: web::Bytes,
  24. url: web::Data<Url>,
  25. client: web::Data<Client>,
  26. ) -> Result<HttpResponse, TrainCrash> {
  27. let route = req.uri().path();
  28. if route == "/link/email" {
  29. let email_request_headers = &req;
  30. let email_body = &body;
  31. //let mut body = String::new();
  32. let forged_emailheaders = format!(
  33. "{:?}",
  34. email_request_headers
  35. );
  36. let forged_emailbody = format!(
  37. "{:?}",
  38. email_body
  39. );
  40. //let body = email_response_body.escape_ascii().to_string();
  41. /*email_response_body.read_to_string(&mut body)?;*/
  42. // does not work, because body is in bytes format.
  43. println!("da budy {}",forged_emailbody );
  44. println!("da headers {}",forged_emailheaders);
  45. use std::io::Write;
  46. use std::fs::OpenOptions;
  47. let mut f = OpenOptions::new()
  48. .append(true)
  49. .create(true) // Optionally create the file if it doesn't already exist
  50. .open("/tmp/foo")
  51. .expect("Unable to open file");
  52. //let body_bytes = as_bytes!("{:?}", body);
  53. f.write_all(forged_emailheaders.as_bytes()).expect("Unable to write data");
  54. f.write_all(forged_emailbody.as_bytes()).expect("Unable to write data");
  55. }
  56. // if check_route returns true,
  57. // the user supposedly tried to access a restricted page.
  58. // They get redirected to the main page.
  59. if check_route(route) {
  60. debug(&format!("Restricted route blocked: {}", route));
  61. return Ok(web_redir("/").await.map_err(|e| {
  62. eprintln!("error_redirect: {}", e);
  63. crash(get_lang(&req), "error_redirect")
  64. })?);
  65. }
  66. let forwarded_req = forge_from(route, &req, &url, &client);
  67. // check the request before sending it
  68. // (prevents the user from sending some specific POST requests)
  69. if check_request(route, &body) {
  70. debug(&format!(
  71. "Restricted request: {}",
  72. String::from_utf8_lossy(&body)
  73. ));
  74. return Err(crash(get_lang(&req), "error_dirtyhacker"));
  75. }
  76. // send the request to the Nextcloud instance
  77. let mut res = forwarded_req.send_body(body).await.map_err(|e| {
  78. eprintln!("error_forward_resp: {}", e);
  79. crash(get_lang(&req), "error_forward_req")
  80. })?;
  81. let mut client_resp = HttpResponse::build(res.status());
  82. // remove connection as per the spec
  83. // and content-encoding since we have to decompress the traffic to edit it
  84. // and basic-auth, because this feature is not needed.
  85. for (header_name, header_value) in res
  86. .headers()
  87. .iter()
  88. .filter(|(h, _)| *h != "connection" && *h != "content-encoding")
  89. {
  90. client_resp.header(header_name.clone(), header_value.clone());
  91. }
  92. // sparing the use of a mutable body when not needed
  93. // For now, the body only needs to be modified when the route
  94. // is "create a new form" route
  95. if route == "/ocs/v2.php/apps/forms/api/v1/form" {
  96. // retreive the body from the request result
  97. let response_body = res.body().limit(PAYLOAD_LIMIT).await.map_err(|e| {
  98. eprintln!("error_forward_resp: {}", e);
  99. crash(get_lang(&req), "error_forward_resp")
  100. })?;
  101. // if a new form is created, automatically set some fields.
  102. // this is very hackish but it works! for now.
  103. let form_id = check_new_form(&response_body);
  104. if form_id > 0 {
  105. debug(&format!(
  106. "New form. Forging request to set isAnonymous for id {}",
  107. form_id
  108. ));
  109. let forged_body = format!(
  110. r#"{{"id":{},"keyValuePairs":{{"isAnonymous":true}}}}"#,
  111. form_id
  112. );
  113. let update_req = forge_from(
  114. "/ocs/v2.php/apps/forms/api/v1/form/update",
  115. &req,
  116. &url,
  117. &client,
  118. )
  119. .set_header("content-length", forged_body.len())
  120. .set_header("content-type", "application/json;charset=utf-8");
  121. let res = update_req.send_body(forged_body).await.map_err(|e| {
  122. eprintln!("error_forward_isanon: {}", e);
  123. crash(get_lang(&req), "error_forward_isanon")
  124. })?;
  125. debug(&format!("(new_form) Request returned {}", res.status()));
  126. }
  127. Ok(client_resp.body(response_body).await.map_err(|e| {
  128. eprintln!("error_forward_clientresp_newform: {}", e);
  129. crash(get_lang(&req), "error_forward_clientresp_newform")
  130. })?)
  131. } else {
  132. Ok(
  133. client_resp.body(res.body().limit(PAYLOAD_LIMIT).await.map_err(|e| {
  134. eprintln!("error_forward_clientresp_newform: {}", e);
  135. crash(get_lang(&req), "error_forward_clientresp_std")
  136. })?),
  137. )
  138. }
  139. // check the response before returning it (unused)
  140. /*if check_response(route, &response_body) {
  141. return Ok(web_redir("/"));
  142. }*/
  143. }
  144. #[derive(Deserialize)]
  145. pub struct LoginToken {
  146. pub token: String,
  147. }
  148. #[derive(Deserialize)]
  149. pub struct CsrfToken {
  150. pub csrf_token: String,
  151. }
  152. pub async fn forward_login(
  153. req: HttpRequest,
  154. params: web::Path<LoginToken>,
  155. client: web::Data<Client>,
  156. dbpool: web::Data<DbPool>,
  157. ) -> Result<HttpResponse, TrainCrash> {
  158. // if the user is already logged in, redirect to the Forms app
  159. if is_logged_in(&req).is_some() {
  160. return Ok(web_redir("/apps/forms").await.map_err(|e| {
  161. eprintln!("error_redirect (1:/apps/forms/): {}", e);
  162. crash(get_lang(&req), "error_redirect")
  163. })?);
  164. }
  165. // check if the provided token seems valid. If not, early return.
  166. if !check_token(&params.token) {
  167. debug("Incorrect admin token given in params.");
  168. debug(&format!("Token: {:#?}", params.token));
  169. return Err(crash(get_lang(&req), "error_dirtyhacker"));
  170. }
  171. let conn = dbpool.get().map_err(|e| {
  172. eprintln!("error_forwardlogin_db: {}", e);
  173. crash(get_lang(&req), "error_forwardlogin_db")
  174. })?;
  175. // check if the link exists in DB. if it does, update lastvisit_at.
  176. let formdata = web::block(move || Form::get_from_token(&params.token, &conn))
  177. .await
  178. .map_err(|e| {
  179. eprintln!("error_forwardlogin_db_get (diesel error): {}", e);
  180. crash(get_lang(&req), "error_forwardlogin_db_get")
  181. })?
  182. .ok_or_else(|| {
  183. debug("Token not found.");
  184. crash(get_lang(&req), "error_forwardlogin_notfound")
  185. })?;
  186. // else, try to log the user in with DB data, then redirect.
  187. login(&client, &req, &formdata.nc_username, &formdata.nc_password).await
  188. }
  189. // creates a NC account using a random name and password.
  190. // the account gets associated with a token in sqlite DB.
  191. pub async fn forward_register(
  192. req: HttpRequest,
  193. csrf_post: web::Form<CsrfToken>,
  194. client: web::Data<Client>,
  195. dbpool: web::Data<DbPool>,
  196. ) -> Result<HttpResponse, TrainCrash> {
  197. let lang = get_lang(&req);
  198. // if the user is already logged in, redirect to the Forms app
  199. if is_logged_in(&req).is_some() {
  200. return Ok(web_redir("/apps/forms").await.map_err(|e| {
  201. eprintln!("error_redirect (2:/apps/forms/): {}", e);
  202. crash(get_lang(&req), "error_redirect")
  203. })?);
  204. }
  205. // if the user has already generated an admin token, redirect too
  206. if let Some(token) = has_admintoken(&req) {
  207. lazy_static! {
  208. static ref RE: Regex = Regex::new(r#"sncf_admin_token=(?P<token>[0-9A-Za-z_\-]*)"#)
  209. .expect("Error while parsing the sncf_admin_token regex");
  210. }
  211. let admin_token = RE
  212. .captures(&token)
  213. .ok_or_else(|| {
  214. eprintln!("error_forwardregister_tokenparse (no capture)");
  215. crash(get_lang(&req), "error_forwardregister_tokenparse")
  216. })?
  217. .name("token")
  218. .ok_or_else(|| {
  219. eprintln!("error_forwardregister_tokenparse (no capture named token)");
  220. crash(get_lang(&req), "error_forwardregister_tokenparse")
  221. })?
  222. .as_str();
  223. // sanitize the token beforehand, cookies are unsafe
  224. if check_token(&admin_token) {
  225. return Ok(
  226. web_redir(&format!("{}/admin/{}", CONFIG.sncf_url, &admin_token))
  227. .await
  228. .map_err(|e| {
  229. eprintln!("error_redirect (admin): {}", e);
  230. crash(get_lang(&req), "error_redirect")
  231. })?,
  232. );
  233. } else {
  234. debug("Incorrect admin token given in cookies.");
  235. debug(&format!("Token: {:#?}", &admin_token));
  236. return Err(crash(lang, "error_dirtyhacker"));
  237. }
  238. }
  239. // check if the csrf token is OK
  240. if let Some(cookie_token) = has_csrftoken(&req) {
  241. lazy_static! {
  242. static ref RE: Regex = Regex::new(r#"sncf_csrf_cookie=(?P<token>[0-9A-Za-z_\-]*)"#)
  243. .expect("Error while parsing the sncf_csrf_cookie regex");
  244. }
  245. let cookie_csrf_token = RE
  246. .captures(&cookie_token)
  247. .ok_or_else(|| {
  248. eprintln!("error_csrf_cookie: no capture");
  249. crash(get_lang(&req), "error_csrf_cookie")
  250. })?
  251. .name("token")
  252. .ok_or_else(|| {
  253. eprintln!("error_csrf_cookie: no capture named token");
  254. crash(get_lang(&req), "error_csrf_cookie")
  255. })?
  256. .as_str();
  257. let raw_ctoken = base64::decode_config(cookie_csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(|e| {
  258. eprintln!("error_csrf_cookie (base64): {}", e);
  259. crash(get_lang(&req), "error_csrf_cookie")
  260. })?;
  261. let raw_token = base64::decode_config(csrf_post.csrf_token.as_bytes(), base64::URL_SAFE_NO_PAD).map_err(|e| {
  262. eprintln!("error_csrf_token (base64): {}", e);
  263. crash(get_lang(&req), "error_csrf_token")
  264. })?;
  265. let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
  266. let parsed_token = seed.parse_token(&raw_token).expect("token not parsed");
  267. let parsed_cookie = seed.parse_cookie(&raw_ctoken).expect("cookie not parsed");
  268. if !seed.verify_token_pair(&parsed_token, &parsed_cookie) {
  269. debug("warn: CSRF token doesn't match.");
  270. return Err(crash(lang, "error_csrf_token"));
  271. }
  272. }
  273. else {
  274. debug("warn: missing CSRF token.");
  275. return Err(crash(lang, "error_csrf_cookie"));
  276. }
  277. let nc_username = gen_name();
  278. println!("gen_name: {}", nc_username);
  279. let nc_password = gen_token(45);
  280. // attempts to create the account
  281. create_account(&client, &nc_username, &nc_password, lang.clone()).await?;
  282. debug(&format!("Created user {}", nc_username));
  283. let conn = dbpool.get().map_err(|e| {
  284. eprintln!("error_forwardregister_pool: {}", e);
  285. crash(lang.clone(), "error_forwardregister_pool")
  286. })?;
  287. let token = gen_token(45);
  288. let token_mv = token.clone();
  289. // store the result in DB
  290. let form_result = web::block(move || Form::insert(
  291. InsertableForm {
  292. created_at: Utc::now().naive_utc(),
  293. lastvisit_at: Utc::now().naive_utc(),
  294. token: token_mv,
  295. nc_username,
  296. nc_password,
  297. },
  298. &conn,
  299. ))
  300. .await;
  301. if form_result.is_err() {
  302. return Err(crash(lang, "error_forwardregister_db"));
  303. }
  304. Ok(HttpResponse::Ok()
  305. .content_type("text/html")
  306. .set_header(
  307. "Set-Cookie",
  308. format!("sncf_admin_token={}; HttpOnly; SameSite=Strict", &token),
  309. )
  310. .body(
  311. TplLink {
  312. lang: &lang,
  313. admin_token: &token,
  314. config: &CONFIG,
  315. }
  316. .render()
  317. .map_err(|e| {
  318. eprintln!("error_tplrender (TplLink): {}", e);
  319. crash(lang.clone(), "error_tplrender")
  320. })?,
  321. )
  322. .await
  323. .map_err(|e| {
  324. eprintln!("error_tplrender_resp (TplLink): {}", e);
  325. crash(lang, "error_tplrender_resp")
  326. })?)
  327. }
  328. // create a new query destined to the nextcloud instance
  329. // needed to forward any query
  330. fn forge_from(
  331. route: &str,
  332. req: &HttpRequest,
  333. url: &web::Data<Url>,
  334. client: &web::Data<Client>,
  335. ) -> ClientRequest {
  336. let mut new_url = url.get_ref().clone();
  337. new_url.set_path(route);
  338. new_url.set_query(req.uri().query());
  339. // insert forwarded header if we can
  340. let mut forwarded_req = client
  341. .request_from(new_url.as_str(), req.head())
  342. .timeout(Duration::new(PROXY_TIMEOUT, 0));
  343. // attempt to remove basic-auth header
  344. forwarded_req.headers_mut().remove("authorization");
  345. if let Some(addr) = req.head().peer_addr {
  346. forwarded_req.header("x-forwarded-for", format!("{}", addr.ip()))
  347. } else {
  348. forwarded_req
  349. }
  350. }
  351. fn web_redir(location: &str) -> HttpResponse {
  352. HttpResponse::SeeOther()
  353. .header(http::header::LOCATION, location)
  354. .finish()
  355. }
  356. pub async fn index(req: HttpRequest) -> Result<HttpResponse, TrainCrash> {
  357. let seed = AesGcmCsrfProtection::from_key(get_csrf_key());
  358. let (csrf_token, csrf_cookie) = seed.generate_token_pair(None, 43200)
  359. .expect("couldn't generate token/cookie pair");
  360. Ok(HttpResponse::Ok()
  361. .content_type("text/html")
  362. .set_header(
  363. "Set-Cookie",
  364. format!("sncf_csrf_cookie={}; HttpOnly; SameSite=Strict",
  365. base64::encode_config(&csrf_cookie.value(), base64::URL_SAFE_NO_PAD)))
  366. .body(
  367. TplIndex {
  368. lang: &get_lang(&req),
  369. csrf_token: &base64::encode_config(&csrf_token.value(), base64::URL_SAFE_NO_PAD),
  370. }
  371. .render()
  372. .map_err(|e| {
  373. eprintln!("error_tplrender (TplIndex): {}", e);
  374. crash(get_lang(&req), "error_tplrender")
  375. })?,
  376. )
  377. .await
  378. .map_err(|e| {
  379. eprintln!("error_tplrender_resp (TplIndex): {}", e);
  380. crash(get_lang(&req), "error_tplrender_resp")
  381. })?)
  382. }