feat: verify signatures for incoming requests
This commit is contained in:
		
							parent
							
								
									f89e3668fd
								
							
						
					
					
						commit
						1f84013b2a
					
				
					 2 changed files with 149 additions and 9 deletions
				
			
		|  | @ -4,11 +4,12 @@ use ruma::{ | ||||||
|     identifiers::{DeviceId, UserId}, |     identifiers::{DeviceId, UserId}, | ||||||
|     Outgoing, |     Outgoing, | ||||||
| }; | }; | ||||||
|  | use std::collections::BTreeMap; | ||||||
| use std::ops::Deref; | use std::ops::Deref; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "conduit_bin")] | #[cfg(feature = "conduit_bin")] | ||||||
| use { | use { | ||||||
|     crate::utils, |     crate::{server_server, utils}, | ||||||
|     log::{debug, warn}, |     log::{debug, warn}, | ||||||
|     rocket::{ |     rocket::{ | ||||||
|         data::{ |         data::{ | ||||||
|  | @ -21,7 +22,11 @@ use { | ||||||
|         tokio::io::AsyncReadExt, |         tokio::io::AsyncReadExt, | ||||||
|         Request, State, |         Request, State, | ||||||
|     }, |     }, | ||||||
|     ruma::api::{AuthScheme, IncomingRequest}, |     ruma::{ | ||||||
|  |         api::{AuthScheme, IncomingRequest}, | ||||||
|  |         signatures::CanonicalJsonValue, | ||||||
|  |         ServerName, | ||||||
|  |     }, | ||||||
|     std::convert::TryFrom, |     std::convert::TryFrom, | ||||||
|     std::io::Cursor, |     std::io::Cursor, | ||||||
| }; | }; | ||||||
|  | @ -72,6 +77,11 @@ where | ||||||
|                 .map(|s| s[7..].to_owned()) // Split off "Bearer "
 |                 .map(|s| s[7..].to_owned()) // Split off "Bearer "
 | ||||||
|                 .or_else(|| request.get_query_value("access_token").and_then(|r| r.ok())); |                 .or_else(|| request.get_query_value("access_token").and_then(|r| r.ok())); | ||||||
| 
 | 
 | ||||||
|  |             let limit = db.globals.max_request_size(); | ||||||
|  |             let mut handle = data.open(ByteUnit::Byte(limit.into())); | ||||||
|  |             let mut body = Vec::new(); | ||||||
|  |             handle.read_to_end(&mut body).await.unwrap(); | ||||||
|  | 
 | ||||||
|             let (sender_user, sender_device, from_appservice) = if let Some((_id, registration)) = |             let (sender_user, sender_device, from_appservice) = if let Some((_id, registration)) = | ||||||
|                 db.appservice |                 db.appservice | ||||||
|                     .iter_all() |                     .iter_all() | ||||||
|  | @ -129,7 +139,139 @@ where | ||||||
|                             return Failure((Status::raw(582), ())); |                             return Failure((Status::raw(582), ())); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     AuthScheme::ServerSignatures => (None, None, false), |                     AuthScheme::ServerSignatures => { | ||||||
|  |                         // Get origin from header
 | ||||||
|  |                         let x_matrix = match request | ||||||
|  |                             .headers() | ||||||
|  |                             .get_one("Authorization") | ||||||
|  |                             .map(|s| { | ||||||
|  |                                 s[9..] | ||||||
|  |                                     .split_terminator(',').map(|field| {let mut splits = field.splitn(2, '='); (splits.next(), splits.next().map(|s| s.trim_matches('"')))}).collect::<BTreeMap<_, _>>() | ||||||
|  |                             }) // Split off "X-Matrix " and parse the rest
 | ||||||
|  |                         { | ||||||
|  |                             Some(t) => t, | ||||||
|  |                             None => { | ||||||
|  |                                 warn!("No Authorization header"); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         let origin_str = match x_matrix.get(&Some("origin")) { | ||||||
|  |                             Some(Some(o)) => *o, | ||||||
|  |                             _ => { | ||||||
|  |                                 warn!("Invalid X-Matrix header origin field: {:?}", x_matrix); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         let origin = match Box::<ServerName>::try_from(origin_str) { | ||||||
|  |                             Ok(s) => s, | ||||||
|  |                             _ => { | ||||||
|  |                                 warn!( | ||||||
|  |                                     "Invalid server name in X-Matrix header origin field: {:?}", | ||||||
|  |                                     x_matrix | ||||||
|  |                                 ); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         let key = match x_matrix.get(&Some("key")) { | ||||||
|  |                             Some(Some(k)) => *k, | ||||||
|  |                             _ => { | ||||||
|  |                                 warn!("Invalid X-Matrix header key field: {:?}", x_matrix); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         let sig = match x_matrix.get(&Some("sig")) { | ||||||
|  |                             Some(Some(s)) => *s, | ||||||
|  |                             _ => { | ||||||
|  |                                 warn!("Invalid X-Matrix header sig field: {:?}", x_matrix); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         let json_body = serde_json::from_slice::<CanonicalJsonValue>(&body); | ||||||
|  | 
 | ||||||
|  |                         let mut request_map = BTreeMap::<String, CanonicalJsonValue>::new(); | ||||||
|  | 
 | ||||||
|  |                         if let Ok(json_body) = json_body { | ||||||
|  |                             request_map.insert("content".to_owned(), json_body); | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         request_map.insert( | ||||||
|  |                             "method".to_owned(), | ||||||
|  |                             CanonicalJsonValue::String(request.method().to_string()), | ||||||
|  |                         ); | ||||||
|  |                         request_map.insert( | ||||||
|  |                             "uri".to_owned(), | ||||||
|  |                             CanonicalJsonValue::String(request.uri().to_string()), | ||||||
|  |                         ); | ||||||
|  |                         request_map.insert( | ||||||
|  |                             "origin".to_owned(), | ||||||
|  |                             CanonicalJsonValue::String(origin.as_str().to_owned()), | ||||||
|  |                         ); | ||||||
|  |                         request_map.insert( | ||||||
|  |                             "destination".to_owned(), | ||||||
|  |                             CanonicalJsonValue::String( | ||||||
|  |                                 db.globals.server_name().as_str().to_owned(), | ||||||
|  |                             ), | ||||||
|  |                         ); | ||||||
|  | 
 | ||||||
|  |                         let mut origin_signatures = BTreeMap::new(); | ||||||
|  |                         origin_signatures | ||||||
|  |                             .insert(key.to_owned(), CanonicalJsonValue::String(sig.to_owned())); | ||||||
|  | 
 | ||||||
|  |                         let mut signatures = BTreeMap::new(); | ||||||
|  |                         signatures.insert( | ||||||
|  |                             origin.as_str().to_owned(), | ||||||
|  |                             CanonicalJsonValue::Object(origin_signatures), | ||||||
|  |                         ); | ||||||
|  | 
 | ||||||
|  |                         request_map.insert( | ||||||
|  |                             "signatures".to_owned(), | ||||||
|  |                             CanonicalJsonValue::Object(signatures), | ||||||
|  |                         ); | ||||||
|  | 
 | ||||||
|  |                         let keys = match server_server::fetch_signing_keys( | ||||||
|  |                             &db, | ||||||
|  |                             &origin, | ||||||
|  |                             vec![&key.to_owned()], | ||||||
|  |                         ) | ||||||
|  |                         .await | ||||||
|  |                         { | ||||||
|  |                             Ok(b) => b, | ||||||
|  |                             Err(e) => { | ||||||
|  |                                 warn!("Failed to fetch signing keys: {}", e); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         }; | ||||||
|  | 
 | ||||||
|  |                         let mut pub_key_map = BTreeMap::new(); | ||||||
|  |                         pub_key_map.insert(origin.as_str().to_owned(), keys); | ||||||
|  | 
 | ||||||
|  |                         match ruma::signatures::verify_json(&pub_key_map, &request_map) { | ||||||
|  |                             Ok(()) => (None, None, false), | ||||||
|  |                             Err(e) => { | ||||||
|  |                                 warn!("Failed to verify json request: {}: {:?} {:?}", e, pub_key_map, request_map); | ||||||
|  | 
 | ||||||
|  |                                 // Forbidden
 | ||||||
|  |                                 return Failure((Status::raw(580), ())); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|                     AuthScheme::None => (None, None, false), |                     AuthScheme::None => (None, None, false), | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|  | @ -141,11 +283,6 @@ where | ||||||
|                 http_request = http_request.header(header.name.as_str(), &*header.value); |                 http_request = http_request.header(header.name.as_str(), &*header.value); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             let limit = db.globals.max_request_size(); |  | ||||||
|             let mut handle = data.open(ByteUnit::Byte(limit.into())); |  | ||||||
|             let mut body = Vec::new(); |  | ||||||
|             handle.read_to_end(&mut body).await.unwrap(); |  | ||||||
| 
 |  | ||||||
|             let http_request = http_request.body(&*body).unwrap(); |             let http_request = http_request.body(&*body).unwrap(); | ||||||
|             debug!("{:?}", http_request); |             debug!("{:?}", http_request); | ||||||
|             match <T::Incoming as IncomingRequest>::try_from_http_request(http_request) { |             match <T::Incoming as IncomingRequest>::try_from_http_request(http_request) { | ||||||
|  |  | ||||||
|  | @ -2106,7 +2106,10 @@ pub fn get_room_information_route<'a>( | ||||||
|     let room_id = db |     let room_id = db | ||||||
|         .rooms |         .rooms | ||||||
|         .id_from_alias(&body.room_alias)? |         .id_from_alias(&body.room_alias)? | ||||||
|         .ok_or(Error::BadRequest(ErrorKind::NotFound, "Room alias not found."))?; |         .ok_or(Error::BadRequest( | ||||||
|  |             ErrorKind::NotFound, | ||||||
|  |             "Room alias not found.", | ||||||
|  |         ))?; | ||||||
| 
 | 
 | ||||||
|     Ok(get_room_information::v1::Response { |     Ok(get_room_information::v1::Response { | ||||||
|         room_id, |         room_id, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue