1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_duration_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![cfg_attr(
48 not(all(feature = "full", feature = "experimental")),
49 allow(unused, unreachable_pub)
50)]
51
52#[macro_use] mod time_store;
54
55mod internal_prelude;
56
57mod anon_level;
58pub mod config;
59mod err;
60mod helpers;
61mod ipt_establish;
62mod ipt_lid;
63mod ipt_mgr;
64mod ipt_set;
65mod keys;
66mod publish;
67mod rend_handshake;
68mod replay;
69mod req;
70pub mod status;
71mod timeout_track;
72
73#[doc(hidden)]
83pub mod timeout_track_for_doctests_unstable_no_semver_guarantees {
84 pub use crate::timeout_track::*;
85}
86#[doc(hidden)]
87pub mod time_store_for_doctests_unstable_no_semver_guarantees {
88 pub use crate::time_store::*;
89}
90
91use internal_prelude::*;
92
93pub use anon_level::Anonymity;
96pub use config::OnionServiceConfig;
97pub use err::{ClientError, EstablishSessionError, FatalError, IntroRequestError, StartupError};
98pub use ipt_mgr::IptError;
99pub use keys::{
100 BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
101 HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
102};
103pub use publish::UploadError as DescUploadError;
104pub use req::{RendRequest, StreamRequest};
105pub use tor_hscrypto::pk::HsId;
106pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
107
108pub use helpers::handle_rend_requests;
109
110pub(crate) type LinkSpecs = Vec<tor_linkspec::EncodedLinkSpec>;
114
115pub(crate) type NtorPublicKey = curve25519::PublicKey;
120
121#[must_use = "a hidden service object will terminate the service when dropped"]
128pub struct RunningOnionService {
129 inner: Mutex<SvcInner>,
131 nickname: HsNickname,
133 keymgr: Arc<KeyMgr>,
135}
136
137struct SvcInner {
139 config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
141
142 _shutdown_tx: postage::broadcast::Sender<void::Void>,
144
145 status_tx: StatusSender,
148
149 unlaunched: Option<(
151 mpsc::Receiver<RendRequest>,
152 Box<dyn Launchable + Send + Sync>,
153 )>,
154}
155
156struct ForLaunch<R: Runtime> {
158 publisher: Publisher<R, publish::Real<R>>,
164
165 ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
171
172 ipt_mgr_view: IptsManagerView,
176}
177
178trait Launchable: Send + Sync {
181 fn launch(self: Box<Self>) -> Result<(), StartupError>;
183}
184
185impl<R: Runtime> Launchable for ForLaunch<R> {
186 fn launch(self: Box<Self>) -> Result<(), StartupError> {
187 self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
188 self.publisher.launch()?;
189
190 Ok(())
191 }
192}
193
194#[derive(PartialEq)]
198#[must_use]
199pub(crate) enum ShutdownStatus {
200 Continue,
202 Terminate,
204}
205
206impl From<oneshot::Canceled> for ShutdownStatus {
207 fn from(_: oneshot::Canceled) -> ShutdownStatus {
208 ShutdownStatus::Terminate
209 }
210}
211
212#[derive(Builder)]
221#[builder(build_fn(private, name = "build_unvalidated", error = "FatalError"))]
222pub struct OnionService {
223 config: OnionServiceConfig,
225 keymgr: Arc<KeyMgr>,
227 state_dir: StateDirectory,
229}
230
231impl OnionService {
232 pub fn builder() -> OnionServiceBuilder {
234 OnionServiceBuilder::default()
235 }
236
237 pub fn launch<R>(
247 self,
248 runtime: R,
249 netdir_provider: Arc<dyn NetDirProvider>,
250 circ_pool: Arc<HsCircPool<R>>,
251 path_resolver: Arc<tor_config_path::CfgPathResolver>,
252 ) -> Result<(Arc<RunningOnionService>, impl Stream<Item = RendRequest>), StartupError>
253 where
254 R: Runtime,
255 {
256 let OnionService {
257 config,
258 keymgr,
259 state_dir,
260 } = self;
261
262 let nickname = config.nickname.clone();
263
264 let offline_hsid = false;
268
269 let selector = KeystoreSelector::Primary;
271 maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
272
273 if config.restricted_discovery.enabled {
274 info!(
275 nickname=%nickname,
276 "Launching onion service in restricted discovery mode"
277 );
278 } else {
279 info!(
280 nickname=%nickname,
281 "Launching onion service"
282 );
283 }
284
285 let state_handle = state_dir
286 .acquire_instance(&config.nickname)
287 .map_err(StartupError::StateDirectoryInaccessible)?;
288
289 let iptpub_storage_handle = state_handle
292 .storage_handle("iptpub")
293 .map_err(StartupError::StateDirectoryInaccessible)?;
294
295 let (rend_req_tx, rend_req_rx) = mpsc_channel_no_memquota(32);
298
299 let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
300 let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
301
302 let (ipt_mgr_view, publisher_view) =
303 crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
304
305 let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
306
307 let ipt_mgr = IptManager::new(
308 runtime.clone(),
309 netdir_provider.clone(),
310 nickname.clone(),
311 config_rx.clone(),
312 rend_req_tx,
313 shutdown_rx.clone(),
314 &state_handle,
315 crate::ipt_mgr::Real {
316 circ_pool: circ_pool.clone(),
317 },
318 keymgr.clone(),
319 status_tx.clone().into(),
320 )?;
321
322 let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
323 runtime,
324 nickname.clone(),
325 netdir_provider,
326 circ_pool,
327 publisher_view,
328 config_rx,
329 status_tx.clone().into(),
330 Arc::clone(&keymgr),
331 path_resolver,
332 );
333
334 let svc = Arc::new(RunningOnionService {
335 nickname,
336 keymgr,
337 inner: Mutex::new(SvcInner {
338 config_tx,
339 _shutdown_tx: shutdown_tx,
340 status_tx,
341 unlaunched: Some((
342 rend_req_rx,
343 Box::new(ForLaunch {
344 publisher,
345 ipt_mgr,
346 ipt_mgr_view,
347 }),
348 )),
349 }),
350 });
351
352 let stream = svc.launch()?;
353 Ok((svc, stream))
354 }
355
356 pub fn onion_address(&self) -> Option<HsId> {
364 onion_address(&self.keymgr, &self.config.nickname)
365 }
366
367 #[deprecated = "Use the new onion_address method instead"]
371 pub fn onion_name(&self) -> Option<HsId> {
372 self.onion_address()
373 }
374
375 pub fn generate_identity_key(&self, selector: KeystoreSelector) -> Result<HsId, StartupError> {
394 let offline_hsid = false;
398
399 maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
400 }
401}
402
403impl OnionServiceBuilder {
404 pub fn build(&self) -> Result<OnionService, StartupError> {
406 let svc = self.build_unvalidated()?;
407 Ok(svc)
408 }
409}
410
411impl RunningOnionService {
412 pub fn reconfigure(
418 &self,
419 new_config: OnionServiceConfig,
420 how: Reconfigure,
421 ) -> Result<(), ReconfigureError> {
422 let mut inner = self.inner.lock().expect("lock poisoned");
423 inner.config_tx.try_maybe_send(|cur_config| {
424 let new_config = cur_config.for_transition_to(new_config, how)?;
425 Ok(match how {
426 tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
428 _ => Arc::new(new_config),
430 })
431 })
432
433 }
437
438 pub fn status(&self) -> OnionServiceStatus {
447 self.inner.lock().expect("poisoned lock").status_tx.get()
448 }
449
450 pub fn status_events(&self) -> OnionServiceStatusStream {
453 self.inner
454 .lock()
455 .expect("poisoned lock")
456 .status_tx
457 .subscribe()
458 }
459
460 fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest>, StartupError> {
466 let (rend_req_rx, launch) = {
467 let mut inner = self.inner.lock().expect("poisoned lock");
468 inner
469 .unlaunched
470 .take()
471 .ok_or(StartupError::AlreadyLaunched)?
472 };
473
474 match launch.launch() {
475 Ok(()) => {}
476 Err(e) => {
477 return Err(e);
478 }
479 }
480
481 Ok(rend_req_rx)
487 }
488
489 pub fn onion_address(&self) -> Option<HsId> {
509 onion_address(&self.keymgr, &self.nickname)
510 }
511
512 #[deprecated = "Use the new onion_address method instead"]
516 pub fn onion_name(&self) -> Option<HsId> {
517 self.onion_address()
518 }
519}
520
521fn maybe_generate_hsid(
525 keymgr: &Arc<KeyMgr>,
526 nickname: &HsNickname,
527 offline_hsid: bool,
528 selector: KeystoreSelector,
529) -> Result<HsId, StartupError> {
530 if offline_hsid {
531 unimplemented!("offline hsid mode");
532 }
533
534 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
535
536 let kp = keymgr
537 .get::<HsIdKey>(&hsid_spec)
538 .map_err(|cause| StartupError::Keystore {
539 action: "read",
540 cause,
541 })?;
542
543 let mut rng = tor_llcrypto::rng::CautiousRng;
544 let (hsid, generated) = match kp {
545 Some(kp) => (kp.id(), false),
546 None => {
547 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
551 let kp = keymgr
552 .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false )
553 .map_err(|cause| StartupError::Keystore {
554 action: "generate",
555 cause,
556 })?;
557
558 (HsIdKey::from(&kp).id(), true)
559 }
560 };
561
562 if generated {
563 info!(
564 "Generated a new identity for service {nickname}: {}",
565 sensitive(hsid)
566 );
567 } else {
568 info!(
571 "Using existing identity for service {nickname}: {}",
572 sensitive(hsid)
573 );
574 }
575
576 Ok(hsid)
577}
578
579fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
591 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
592
593 keymgr
594 .get::<HsIdKey>(&hsid_spec)
595 .ok()?
596 .map(|hsid| hsid.id())
597}
598
599pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
602 use tor_protover::named::*;
603 [
606 HSINTRO_V3,
608 HSINTRO_RATELIM,
609 HSREND_V3,
610 HSDIR_V3,
611 ]
612 .into_iter()
613 .collect()
614}
615
616#[cfg(test)]
617pub(crate) mod test {
618 #![allow(clippy::bool_assert_comparison)]
620 #![allow(clippy::clone_on_copy)]
621 #![allow(clippy::dbg_macro)]
622 #![allow(clippy::mixed_attributes_style)]
623 #![allow(clippy::print_stderr)]
624 #![allow(clippy::print_stdout)]
625 #![allow(clippy::single_char_pattern)]
626 #![allow(clippy::unwrap_used)]
627 #![allow(clippy::unchecked_duration_subtraction)]
628 #![allow(clippy::useless_vec)]
629 #![allow(clippy::needless_pass_by_value)]
630 use super::*;
632
633 use std::fmt::Display;
634 use std::path::Path;
635
636 use fs_mistrust::Mistrust;
637 use test_temp_dir::{test_temp_dir, TestTempDir, TestTempDirGuard};
638
639 use tor_basic_utils::test_rng::testing_rng;
640 use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
641 use tor_llcrypto::pk::ed25519;
642 use tor_persist::state_dir::InstanceStateHandle;
643
644 use crate::config::OnionServiceConfigBuilder;
645 use crate::ipt_set::IptSetStorageHandle;
646 use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
647
648 const TEST_SVC_NICKNAME: &str = "test-svc";
650
651 #[test]
652 fn protocols() {
653 let pr = supported_hsservice_protocols();
654 let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
655 assert_eq!(pr, expected);
656 }
657
658 pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
660 temp_dir.subdir_used_by("keystore", |keystore_dir| {
661 let keystore = ArtiNativeKeystore::from_path_and_mistrust(
662 keystore_dir,
663 &Mistrust::new_dangerously_trust_everyone(),
664 )
665 .unwrap();
666
667 Arc::new(
668 KeyMgrBuilder::default()
669 .primary_store(Box::new(keystore))
670 .build()
671 .unwrap(),
672 )
673 })
674 }
675
676 #[allow(clippy::let_and_return)] pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
678 let nick = HsNickname::new(nick.to_string()).unwrap();
679 let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
680 let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
681 let instance = state_dir.acquire_instance(&nick).unwrap();
682 instance
683 }
684
685 pub(crate) fn create_storage_handles(
686 dir: &Path,
687 ) -> (
688 tor_persist::state_dir::InstanceStateHandle,
689 IptSetStorageHandle,
690 ) {
691 let nick = HsNickname::try_from("allium".to_owned()).unwrap();
692 create_storage_handles_from_state_dir(dir, &nick)
693 }
694
695 pub(crate) fn create_storage_handles_from_state_dir(
696 state_dir: &Path,
697 nick: &HsNickname,
698 ) -> (
699 tor_persist::state_dir::InstanceStateHandle,
700 IptSetStorageHandle,
701 ) {
702 let instance = mk_state_instance(state_dir, nick);
703 let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
704 (instance, iptpub_state_handle)
705 }
706
707 macro_rules! maybe_generate_hsid {
708 ($keymgr:expr, $offline_hsid:expr) => {{
709 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
710 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
711 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
712
713 assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
714 assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
715
716 maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
717 }};
718 }
719
720 fn create_hsid() -> (HsIdKeypair, HsIdKey) {
722 let mut rng = testing_rng();
723 let keypair = ed25519::Keypair::generate(&mut rng);
724
725 let id_pub = HsIdKey::from(keypair.verifying_key());
726 let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
727
728 (id_keypair, id_pub)
729 }
730
731 #[test]
732 fn generate_hsid() {
733 let temp_dir = test_temp_dir!();
734 let keymgr = create_keymgr(&temp_dir);
735
736 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
737 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
738
739 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
740 maybe_generate_hsid!(keymgr, false );
741 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
742 }
743
744 #[test]
745 fn hsid_keypair_already_exists() {
746 let temp_dir = test_temp_dir!();
747 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
748 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
749 let keymgr = create_keymgr(&temp_dir);
750
751 let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
753 let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
754 let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
755
756 keymgr
757 .insert(
758 existing_hsid_keypair,
759 &hsid_spec,
760 KeystoreSelector::Primary,
761 true,
762 )
763 .unwrap();
764
765 maybe_generate_hsid(
766 &keymgr,
767 &nickname,
768 false, Default::default(),
770 )
771 .unwrap();
772
773 let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
774 let pk: HsIdKey = (&keypair).into();
775
776 assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
777 }
778
779 #[test]
780 #[ignore] fn generate_hsid_offline_hsid() {
782 let temp_dir = test_temp_dir!();
783 let keymgr = create_keymgr(&temp_dir);
784
785 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
786 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
787 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
788
789 maybe_generate_hsid!(keymgr, true );
790
791 assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
792 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
793 }
794
795 #[test]
796 #[ignore] fn generate_hsid_corrupt_keystore() {
798 let temp_dir = test_temp_dir!();
799 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
800 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
801 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
802
803 let keymgr = create_keymgr(&temp_dir);
804
805 let (hsid_keypair, _hsid_public) = create_hsid();
806 let (_hsid_keypair, hsid_public) = create_hsid();
807
808 keymgr
809 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
810 .unwrap();
811
812 keymgr
814 .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
815 .unwrap();
816
817 assert!(maybe_generate_hsid(
818 &keymgr,
819 &nickname,
820 false, Default::default()
822 )
823 .is_err());
824 }
825
826 #[test]
827 fn onion_address() {
828 let temp_dir = test_temp_dir!();
829 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
830 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
831 let keymgr = create_keymgr(&temp_dir);
832
833 let (hsid_keypair, hsid_public) = create_hsid();
834
835 keymgr
837 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
838 .unwrap();
839
840 let config = OnionServiceConfigBuilder::default()
841 .nickname(nickname)
842 .build()
843 .unwrap();
844
845 let state_dir = StateDirectory::new(
846 temp_dir.as_path_untracked(),
847 &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
848 )
849 .unwrap();
850
851 let service = OnionService::builder()
852 .config(config)
853 .keymgr(Arc::clone(&*keymgr))
854 .state_dir(state_dir)
855 .build()
856 .unwrap();
857
858 let hsid = HsId::from(hsid_public);
859 assert_eq!(service.onion_address().unwrap(), hsid);
860
861 drop(temp_dir); }
863}