diff --git a/.ci-scripts/make-edition.sh b/.ci-scripts/make-edition.sh index f595e5e..57c857a 100755 --- a/.ci-scripts/make-edition.sh +++ b/.ci-scripts/make-edition.sh @@ -3,8 +3,8 @@ export EDITION=$1 export BA="--build-arg EDITION=$1" -podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/dev-container:latest" --target=dev-container . -podman push "$CI_REGISTRY_IMAGE/$EDITION/dev-container:latest" +# podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/dev-container:latest" --target=dev-container . +# podman push "$CI_REGISTRY_IMAGE/$EDITION/dev-container:latest" podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/node-holder:$CI_COMMIT_SHORT_SHA" --target=node-holder . podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/proxy-secure:$CI_COMMIT_SHORT_SHA" --target=proxy-secure . podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/proxy-insecure:$CI_COMMIT_SHORT_SHA" --target=proxy-insecure . diff --git a/Cargo.lock b/Cargo.lock index 53c829e..b7ebf9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -672,6 +672,7 @@ version = "0.1.1" dependencies = [ "anyhow", "bytemuck", + "cfg-if", "chrono", "dotenv", "hex", diff --git a/out/backend_server_secure b/out/backend_server_secure new file mode 100755 index 0000000..be14059 Binary files /dev/null and b/out/backend_server_secure differ diff --git a/proxy/src/insecure.rs b/proxy/src/insecure.rs index 4a32adf..1451a64 100644 --- a/proxy/src/insecure.rs +++ b/proxy/src/insecure.rs @@ -1,5 +1,3 @@ -use std::process::abort; - use proxy::edge_node_dc_callback; use proxy_common::{ProxyStartupParam, setup_edge_node_connection}; use rnex_core::common::setup; diff --git a/prudpv0/src/crypto/friends_insecure.rs b/prudpv0/src/crypto/friends_insecure.rs index 9fbfe95..1f05681 100644 --- a/prudpv0/src/crypto/friends_insecure.rs +++ b/prudpv0/src/crypto/friends_insecure.rs @@ -65,11 +65,14 @@ impl Crypto for Insecure { packet_data: &[u8], self_signat: [u8; 4], remote_signat: [u8; 4], - ) -> Self::Instance { - InsecureInstance { - pair: EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY)), - self_signat, - remote_signat, - } + ) -> Option<(Self::Instance, Vec)> { + Some(( + InsecureInstance { + pair: EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY)), + self_signat, + remote_signat, + }, + vec![], + )) } } diff --git a/prudpv0/src/crypto/friends_secure.rs b/prudpv0/src/crypto/friends_secure.rs index 206efec..acfa3bc 100644 --- a/prudpv0/src/crypto/friends_secure.rs +++ b/prudpv0/src/crypto/friends_secure.rs @@ -1,10 +1,18 @@ use hmac::Mac; -use rc4::{Rc4, StreamCipher}; -use rnex_core::prudp::{ - encryption::EncryptionPair, - types_flags::{TypesFlags, types::DATA}, +use md5::{Digest, Md5}; +use rc4::{KeyInit, Rc4, StreamCipher}; +use rnex_core::{ + executables::common::SECURE_SERVER_ACCOUNT, + nex::account::Account, + prudp::{ + encryption::EncryptionPair, + ticket::read_secure_connection_data, + types_flags::{TypesFlags, types::DATA}, + }, + rmc::structures::RmcSerialize, }; -use typenum::U32; +use std::io::Write; +use typenum::U16; use crate::crypto::{ Crypto, CryptoInstance, @@ -13,7 +21,7 @@ use crate::crypto::{ }; pub struct SecureInstance { - pair: EncryptionPair>, + pair: EncryptionPair>, uid: u32, self_signat: [u8; 4], remote_signat: [u8; 4], @@ -34,7 +42,9 @@ impl CryptoInstance for SecureInstance { if data.len() == 0 { [0x78, 0x56, 0x34, 0x12] } else { - let mut hmac = ::new_from_slice(ACCESS_KEY.as_bytes()) + let mut hash = Md5::new(); + hash.write(ACCESS_KEY.as_bytes()).unwrap(); + let mut hmac = ::new_from_slice(&hash.finalize().as_slice()) .expect("unable to create hmac md5"); hmac.update(data); hmac.finalize().into_bytes()[0..4].try_into().unwrap() @@ -45,12 +55,12 @@ impl CryptoInstance for SecureInstance { } } -pub struct Secure(); +pub struct Secure(&'static Account); impl Crypto for Secure { type Instance = SecureInstance; fn new() -> Self { - Self() + Self(&SECURE_SERVER_ACCOUNT) } fn calculate_checksum(&self, data: &[u8]) -> u8 { common_checksum(ACCESS_KEY, data) @@ -60,7 +70,27 @@ impl Crypto for Secure { data: &[u8], self_signat: [u8; 4], remote_signat: [u8; 4], - ) -> Self::Instance { - todo!() + ) -> Option<(Self::Instance, Vec)> { + let (session_key, pid, check_value) = read_secure_connection_data(data, &self.0)?; + + let check_value_response = check_value + 1; + + let data = bytemuck::bytes_of(&check_value_response); + + let mut response = Vec::new(); + + data.serialize(&mut response).ok()?; + + Some(( + SecureInstance { + pair: EncryptionPair::init_both(|| { + Rc4::new_from_slice(&session_key).expect("unable to initialize rc4 stream") + }), + self_signat, + remote_signat, + uid: pid, + }, + response, + )) } } diff --git a/prudpv0/src/crypto/mod.rs b/prudpv0/src/crypto/mod.rs index 5fd0d3f..f4806f6 100644 --- a/prudpv0/src/crypto/mod.rs +++ b/prudpv0/src/crypto/mod.rs @@ -19,7 +19,7 @@ pub trait Crypto: Send + Sync + 'static { data: &[u8], self_signat: [u8; 4], remote_signat: [u8; 4], - ) -> Self::Instance; + ) -> Option<(Self::Instance, Vec)>; } cfg_if! { diff --git a/prudpv0/src/packet.rs b/prudpv0/src/packet.rs index aa68194..e94ab46 100644 --- a/prudpv0/src/packet.rs +++ b/prudpv0/src/packet.rs @@ -1,7 +1,7 @@ use std::mem::transmute; use bytemuck::{Pod, Zeroable, try_from_bytes, try_from_bytes_mut}; -use log::{error, info}; +use log::{error, info, warn}; use rnex_core::prudp::{ types_flags::{ self, TypesFlags, @@ -146,6 +146,15 @@ impl> PRUDPV0Packet { let Some(checksum) = self.checksum() else { return false; }; + + if checksum != crypto.calculate_checksum(data) { + warn!( + "checksum doesnt match expected checksum: {} != {}", + checksum, + crypto.calculate_checksum(data) + ) + } + checksum == crypto.calculate_checksum(data) } @@ -230,7 +239,7 @@ pub fn new_connect_packet( ) -> Vec { let type_flags = TypesFlags::default().types(CONNECT).flags(flags); - let vec = vec![0; precalc_size(type_flags, 0)]; + let vec = vec![0; precalc_size(type_flags, data.len())]; let mut packet = PRUDPV0Packet::new(vec); let header = packet.header_mut().expect("packet malformed in creation"); @@ -245,6 +254,12 @@ pub fn new_connect_packet( *packet .connection_signature_mut() .expect("packet malformed in creation") = remote_signat; + + packet + .payload_mut() + .expect("packet malformed in creation") + .copy_from_slice(data); + if let Some(size) = packet.size_mut() { size.copy_from_slice(&(data.len() as u16).to_le_bytes()); } @@ -281,6 +296,8 @@ pub fn new_data_packet( .expect("packet malformed in creation") .copy_from_slice(data); + crypto_instance.encrypt_outgoing(packet.payload_mut().expect("packet malformed in creation")); + if let Some(size) = packet.size_mut() { size.copy_from_slice(&(data.len() as u16).to_le_bytes()); } diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index 55b508e..aa603f7 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -3,7 +3,7 @@ use std::{ hash::Hash, net::{Ipv4Addr, SocketAddr, SocketAddrV4}, sync::{ - Arc, LazyLock, + Arc, LazyLock, Weak, atomic::{AtomicBool, AtomicU32}, }, thread::sleep, @@ -123,13 +123,14 @@ impl Server { let packet = Arc::new(packet); let packet_ref = Arc::downgrade(&packet); - let conn = Arc::downgrade(&conn); - let this = Arc::downgrade(&self); inner.unacknowledged_packets.insert(seq, packet); drop(inner); + let conn = Arc::downgrade(&conn); + let this = Arc::downgrade(&self); + spawn(async move { for n in 0..5 { let Some(data) = packet_ref.upgrade() else { @@ -153,10 +154,11 @@ impl Server { } async fn connection_thread( self: Arc, - conn: Arc>, + conn: Weak>, mut recv: SplittableBufferConnection, ) { while let Some(data) = recv.recv().await { + let Some(conn) = conn.upgrade() else { break }; if &data[..] == &[0, 0, 0, 0, 0] { info!("got keepalive"); continue; @@ -168,8 +170,8 @@ impl Server { async fn timeout_thread(self: Arc, conn: Arc>) { loop { sleep(Duration::from_secs(3)); - info!("running another loop"); let mut inner = conn.inner.lock().await; + if (Instant::now() - inner.last_action).as_secs() > 5 { warn!("connection exceeded silence limit, sending ping"); let packet = new_ping_packet( @@ -187,11 +189,11 @@ impl Server { .await; } - if (Instant::now() - conn.inner.lock().await.last_action).as_secs() > 15 { + if (Instant::now() - inner.last_action).as_secs() > 15 { warn!("client timed out..."); let packet = new_disconnect_packet( - 0, + NEED_ACK, self.param.virtual_port, conn.addr.virtual_port, 0, @@ -209,9 +211,12 @@ impl Server { self.socket .send_to(&packet, conn.addr.regular_socket_addr) .await; + drop(inner); + let mut conns = self.connections.write().await; conns.remove(&conn.addr); drop(conns); + break; } drop(inner); @@ -247,7 +252,10 @@ impl Server { remote_signat[3], ]; - let ci = self.crypto.instantiate(data, self_signat, remote_signat); + let Some((ci, data)) = self.crypto.instantiate(&data, self_signat, remote_signat) else { + warn!("unable to instantiate crypto instance"); + return; + }; let pid = ci.get_user_id(); let buf_conn = new_backend_connection(&self.param, addr, pid).await; @@ -281,7 +289,7 @@ impl Server { spawn({ let this = self.clone(); - let conn = conn.clone(); + let conn = Arc::downgrade(&conn); this.connection_thread(conn, buf_conn) }); spawn({ @@ -297,7 +305,7 @@ impl Server { self_signat, remote_signat, packet.header().unwrap().session_id, - &[], + &data, &self.crypto, ); @@ -314,10 +322,7 @@ impl Server { return; }; - let rd = self.connections.read().await; - let res = rd.get(&addr).cloned(); - drop(rd); - let Some(res) = res else { + let Some(res) = self.get_connection(addr).await else { warn!("data packet on inactive connection from: {:?}", addr); return; }; @@ -335,7 +340,6 @@ impl Server { &self.crypto, ); self.socket.send_to(&ack, addr.regular_socket_addr).await; - conn.last_action = Instant::now(); conn.packet_queue.insert( packet.header().unwrap().sequence_id, (Instant::now(), packet), @@ -356,6 +360,7 @@ impl Server { res.target.send(payload.to_owned()).await; conn.client_packet_counter += 1; } + info!("finished handeling packets, dropping inner connection"); drop(conn); } @@ -378,7 +383,6 @@ impl Server { &self.crypto, ); drop(inner); - drop(conn); self.socket.send_to(&packet, addr.regular_socket_addr).await; } @@ -405,7 +409,6 @@ impl Server { &self.crypto, ); drop(inner); - drop(conn); self.socket.send_to(&packet, addr.regular_socket_addr).await; self.socket.send_to(&packet, addr.regular_socket_addr).await; @@ -429,17 +432,19 @@ impl Server { return; }; + info!("len: {}", packet.0.len()); + let addr = PRUDPSockAddr::new(addr, header.source); - if header.type_flags.get_flags() & ACK != 0 { - info!("got ack(acks are ignored for now)"); - return; - } if let Some(conn) = self.get_connection(addr).await { let mut inner = conn.inner.lock().await; inner.last_action = Instant::now(); drop(inner); }; + if header.type_flags.get_flags() & ACK != 0 { + info!("got ack(acks are ignored for now)"); + return; + } println!("{:?}", header); match header.type_flags.get_types() { SYN => { @@ -464,20 +469,18 @@ impl Server { } pub async fn run_task(self: Arc) { loop { - let mut vec: Vec = vec![]; - let addr = match self.socket.recv_buf_from(&mut vec).await { + let mut vec: Vec = vec![0u8; 65507]; + let (len, addr) = match self.socket.recv_from(&mut vec).await { Err(e) => { error!("unable to recv: {}", e); break; } - Ok(v) => { - assert_eq!(vec.len(), v.0); - v.1 - } + Ok(v) => v, }; let this = self.clone(); tokio::spawn(async move { let mut data = vec; + data.resize(len, 0); let packet = PRUDPV0Packet::new(data); let SocketAddr::V4(addr) = addr else { diff --git a/prudpv1/src/prudp/secure.rs b/prudpv1/src/prudp/secure.rs index 604ca58..9bd441b 100644 --- a/prudpv1/src/prudp/secure.rs +++ b/prudpv1/src/prudp/secure.rs @@ -8,73 +8,12 @@ use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; use rnex_core::kerberos::{TicketInternalData, derive_key}; use rnex_core::nex::account::Account; use rnex_core::prudp::encryption::EncryptionPair; +use rnex_core::prudp::ticket::read_secure_connection_data; use rnex_core::rmc::structures::RmcSerialize; use std::io::Cursor; use typenum::U5; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; -pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)> { - let mut cursor = Cursor::new(data); - - let mut ticket_data: Vec = Vec::deserialize(&mut cursor).ok()?; - let mut request_data: Vec = Vec::deserialize(&mut cursor).ok()?; - - let ticket_data_size = ticket_data.len(); - - let ticket_data = &mut ticket_data[0..ticket_data_size - 0x10]; - - let server_key = derive_key(act.pid, &act.kerbros_password[..]); - - let mut rc4: StreamCipherCoreWrapper> = - Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream"); - - rc4.apply_keystream(ticket_data); - - let ticket_data: &TicketInternalData = match bytemuck::try_from_bytes(ticket_data) { - Ok(v) => v, - Err(e) => { - error!("unable to read internal ticket data: {}", e); - return None; - } - }; - - // todo: add ticket expiration - - let TicketInternalData { - session_key, - pid: ticket_source_pid, - issued_time, - } = *ticket_data; - - // todo: add checking if tickets are signed with a valid md5-hmac - let request_data_length = request_data.len(); - let request_data = &mut request_data[0..request_data_length - 0x10]; - - let mut rc4: StreamCipherCoreWrapper> = - Rc4::new_from_slice(&session_key).expect("unable to init rc4 keystream"); - - rc4.apply_keystream(request_data); - - let mut reqest_data_cursor = Cursor::new(request_data); - - let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; - - if pid != ticket_source_pid { - let ticket_created_on = issued_time.to_regular_time(); - - error!( - "someone tried to spoof their pid, ticket was created on: {}", - ticket_created_on.to_rfc2822() - ); - return None; - } - - let _cid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; - let response_check: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; - - Some((session_key, pid, response_check)) -} - type Rc4U32 = StreamCipherCoreWrapper>; pub fn generate_secure_encryption_pairs( diff --git a/rnex-core/src/executables/backend_server_insecure.rs b/rnex-core/src/executables/backend_server_insecure.rs index efb08db..b7ce979 100644 --- a/rnex-core/src/executables/backend_server_insecure.rs +++ b/rnex-core/src/executables/backend_server_insecure.rs @@ -1,8 +1,6 @@ -use cfg_if::cfg_if; use once_cell::sync::Lazy; use rnex_core::common::setup; use rnex_core::executables::common::{SECURE_SERVER_ACCOUNT, new_simple_backend}; -use rnex_core::executables::regular_backend; use rnex_core::nex::auth_handler::AuthHandler; use rnex_core::reggie::EdgeNodeHolderConnectOption::DontRegister; use rnex_core::reggie::RemoteEdgeNodeHolder; @@ -25,11 +23,25 @@ pub static FORWARD_EDGE_NODE_HOLDER: Lazy = Lazy::new(|| { async fn main() { setup(); - cfg_if! { - if #[cfg(features = "friends")]{ + let conn = TcpStream::connect(&*FORWARD_EDGE_NODE_HOLDER) + .await + .unwrap(); - } else { - regular_backend::start_regular_backend().await - } - } + let conn: SplittableBufferConnection = conn.into(); + + conn.send(DontRegister.to_data().unwrap()).await; + + let conn = new_rmc_gateway_connection(conn, |r| { + Arc::new(OnlyRemote::::new(r)) + }); + + new_simple_backend(move |_, _| { + let controller = conn.clone(); + Arc::new(AuthHandler { + destination_server_acct: &SECURE_SERVER_ACCOUNT, + build_name: env!("AUTH_REPORT_VERSION"), + control_server: controller, + }) + }) + .await; }