diff --git a/service/src/cli/rootset.rs b/service/src/cli/rootset.rs index fc32b9607..1826c0c7e 100644 --- a/service/src/cli/rootset.rs +++ b/service/src/cli/rootset.rs @@ -26,17 +26,13 @@ pub fn cmd(_: Flags, cmd_args: &ArgMatches) -> i32 { if path.is_some() && secret_arg.is_some() { let path = path.unwrap(); let secret_arg = secret_arg.unwrap(); - let secret = crate::utils::parse_cli_identity(secret_arg, true); + let secret = crate::utils::parse_cli_identity_secret(secret_arg); let json_data = read_limit(path, DEFAULT_FILE_IO_READ_LIMIT); if secret.is_err() { eprintln!("ERROR: unable to parse '{}' or read as a file.", secret_arg); return exitcode::ERR_IOERR; } let secret = secret.unwrap(); - if !secret.secret.is_some() { - eprintln!("ERROR: identity does not include secret key, which is required for signing."); - return exitcode::ERR_IOERR; - } if json_data.is_err() { eprintln!("ERROR: unable to read '{}'.", path); return exitcode::ERR_IOERR; @@ -102,7 +98,7 @@ pub fn cmd(_: Flags, cmd_args: &ArgMatches) -> i32 { eprintln!("ERROR: root set JSON parsing failed: {}", root_set.err().unwrap().to_string()); return exitcode::ERR_IOERR; } - let _ = std::io::stdout().write_all(root_set.unwrap().to_bytes().as_slice()); + let _ = std::io::stdout().write_all(root_set.unwrap().to_buffer::<16384>().unwrap().as_ref()); } else { eprintln!("ERROR: 'rootset marshal' requires a path to a root set in JSON format."); return exitcode::ERR_IOERR; diff --git a/service/src/main.rs b/service/src/main.rs index 5927891cb..472fd4696 100644 --- a/service/src/main.rs +++ b/service/src/main.rs @@ -207,28 +207,34 @@ fn main() { if let Ok(_tokio_runtime) = zerotier_utils::tokio::runtime::Builder::new_multi_thread().enable_all().build() { let test_inner = Arc::new(DummyInnerLayer); let datadir = open_datadir(&flags); - let svc = VL1Service::new(datadir, test_inner, zerotier_vl1_service::VL1Settings::default()); - if svc.is_ok() { - let svc = svc.unwrap(); - svc.node().init_default_roots(); - - // Wait for kill signal on Unix-like platforms. - #[cfg(unix)] - { - let term = Arc::new(AtomicBool::new(false)); - let _ = signal_hook::flag::register(libc::SIGINT, term.clone()); - let _ = signal_hook::flag::register(libc::SIGTERM, term.clone()); - let _ = signal_hook::flag::register(libc::SIGQUIT, term.clone()); - while !term.load(Ordering::Relaxed) { - std::thread::sleep(Duration::from_secs(1)); - } - } - - println!("Terminate signal received, shutting down..."); - exitcode::OK - } else { - eprintln!("FATAL: error launching service: {}", svc.err().unwrap().to_string()); + let id = datadir.read_identity(true, true); + if let Err(e) = id { + eprintln!("FATAL: error generator or writing identity: {}", e.to_string()); exitcode::ERR_IOERR + } else { + let svc = VL1Service::new(id.unwrap(), test_inner, zerotier_vl1_service::VL1Settings::default()); + if svc.is_ok() { + let svc = svc.unwrap(); + svc.node.init_default_roots(); + + // Wait for kill signal on Unix-like platforms. + #[cfg(unix)] + { + let term = Arc::new(AtomicBool::new(false)); + let _ = signal_hook::flag::register(libc::SIGINT, term.clone()); + let _ = signal_hook::flag::register(libc::SIGTERM, term.clone()); + let _ = signal_hook::flag::register(libc::SIGQUIT, term.clone()); + while !term.load(Ordering::Relaxed) { + std::thread::sleep(Duration::from_secs(1)); + } + } + + println!("Terminate signal received, shutting down..."); + exitcode::OK + } else { + eprintln!("FATAL: error launching service: {}", svc.err().unwrap().to_string()); + exitcode::ERR_IOERR + } } } else { eprintln!("FATAL: error launching service: can't start async runtime"); diff --git a/service/src/utils.rs b/service/src/utils.rs index 546ffb1d7..ba0f3edb2 100644 --- a/service/src/utils.rs +++ b/service/src/utils.rs @@ -3,7 +3,7 @@ use std::path::Path; use std::str::FromStr; -use zerotier_network_hypervisor::vl1::identity::Identity; +use zerotier_network_hypervisor::vl1::identity::{Identity, IdentitySecret}; use zerotier_utils::io::read_limit; /// Returns true if the string starts with [yY1tT] or false for [nN0fF]. @@ -36,7 +36,6 @@ pub fn is_valid_port(v: &str) -> Result<(), String> { Err(format!("invalid TCP/IP port number: {}", v)) } -/// Read an identity as either a literal or from a file. pub fn parse_cli_identity(input: &str, validate: bool) -> Result { let parse_func = |s: &str| { Identity::from_str(s).map_or_else( @@ -64,6 +63,20 @@ pub fn parse_cli_identity(input: &str, validate: bool) -> Result Result { + let parse_func = |s: &str| IdentitySecret::from_str(s).map_err(|e| format!("invalid identity: {}", e.to_string())); + + let input_p = Path::new(input); + if input_p.is_file() { + read_limit(input_p, 16384).map_or_else( + |e| Err(e.to_string()), + |v| String::from_utf8(v).map_or_else(|e| Err(e.to_string()), |s| parse_func(s.as_str())), + ) + } else { + parse_func(input) + } +} + //#[cfg(unix)] //pub fn c_strerror() -> String { // unsafe { std::ffi::CStr::from_ptr(libc::strerror(*libc::__error()).cast()).to_string_lossy().to_string() } diff --git a/vl1-service/src/datadir.rs b/vl1-service/src/datadir.rs index 52f4a5e4d..630430c8c 100644 --- a/vl1-service/src/datadir.rs +++ b/vl1-service/src/datadir.rs @@ -1,12 +1,15 @@ // (c) 2020-2022 ZeroTier, Inc. -- currently proprietary pending actual release and licensing. See LICENSE.md. +use std::io::ErrorKind; use std::path::{Path, PathBuf}; +use std::str::FromStr; use std::sync::{Arc, Mutex, RwLock}; use serde::de::DeserializeOwned; use serde::Serialize; use zerotier_crypto::random::next_u32_secure; +use zerotier_network_hypervisor::vl1::identity::{Identity, IdentitySecret}; use zerotier_utils::io::{fs_restrict_permissions, read_limit, DEFAULT_FILE_IO_READ_LIMIT}; use zerotier_utils::json::to_json_pretty; @@ -56,6 +59,31 @@ impl std::io::Result { + let identity_path = self.base_path.join(IDENTITY_SECRET_FILENAME); + match read_limit(&identity_path, 4096) { + Ok(id_bytes) => { + return IdentitySecret::from_str(String::from_utf8_lossy(id_bytes.as_slice()).as_ref()) + .map_err(|_| std::io::Error::new(ErrorKind::InvalidData, "invalid identity")); + } + Err(e) => match e.kind() { + ErrorKind::NotFound => { + if auto_generate { + let id = Identity::generate(generate_x25519_only); + let ids = id.to_string(); + std::fs::write(&identity_path, ids.as_bytes())?; + std::fs::write(self.base_path.join(IDENTITY_PUBLIC_FILENAME), id.public.to_string().as_bytes())?; + return Ok(id); + } else { + return Err(e); + } + } + _ => return Err(e), + }, + } + } + /// Get authorization token for local API, creating and saving if it does not exist. pub fn authtoken(&self) -> std::io::Result { let authtoken = self.authtoken.lock().unwrap().clone();