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)] #![allow(mismatched_lifetime_syntaxes)] #![cfg_attr(
49 not(all(feature = "full", feature = "experimental")),
50 allow(unused, unreachable_pub)
51)]
52
53#[macro_use] mod time_store;
55
56mod internal_prelude;
57
58mod anon_level;
59pub mod config;
60mod err;
61mod helpers;
62mod ipt_establish;
63mod ipt_lid;
64mod ipt_mgr;
65mod ipt_set;
66mod keys;
67mod pow;
68mod publish;
69mod rend_handshake;
70mod replay;
71mod req;
72pub mod status;
73mod timeout_track;
74
75#[doc(hidden)]
85pub mod timeout_track_for_doctests_unstable_no_semver_guarantees {
86 pub use crate::timeout_track::*;
87}
88#[doc(hidden)]
89pub mod time_store_for_doctests_unstable_no_semver_guarantees {
90 pub use crate::time_store::*;
91}
92
93use std::pin::Pin;
94
95use internal_prelude::*;
96
97pub use anon_level::Anonymity;
100pub use config::OnionServiceConfig;
101pub use err::{ClientError, EstablishSessionError, FatalError, IntroRequestError, StartupError};
102pub use ipt_mgr::IptError;
103pub use keys::{
104 BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
105 HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
106};
107use pow::{NewPowManager, PowManager};
108pub use publish::UploadError as DescUploadError;
109pub use req::{RendRequest, StreamRequest};
110pub use tor_hscrypto::pk::HsId;
111pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
112
113pub use helpers::handle_rend_requests;
114
115pub(crate) type LinkSpecs = Vec<tor_linkspec::EncodedLinkSpec>;
119
120pub(crate) type NtorPublicKey = curve25519::PublicKey;
125
126#[must_use = "a hidden service object will terminate the service when dropped"]
133pub struct RunningOnionService {
134 inner: Mutex<SvcInner>,
136 nickname: HsNickname,
138 keymgr: Arc<KeyMgr>,
140}
141
142struct SvcInner {
144 config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
146
147 _shutdown_tx: postage::broadcast::Sender<void::Void>,
149
150 status_tx: StatusSender,
153
154 #[allow(clippy::type_complexity)]
156 unlaunched: Option<(
157 Pin<Box<dyn Stream<Item = RendRequest> + Send + Sync>>,
158 Box<dyn Launchable + Send + Sync>,
159 )>,
160}
161
162struct ForLaunch<R: Runtime> {
164 publisher: Publisher<R, publish::Real<R>>,
170
171 ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
177
178 ipt_mgr_view: IptsManagerView,
182
183 pow_manager: Arc<PowManager<R>>,
185}
186
187trait Launchable: Send + Sync {
190 fn launch(self: Box<Self>) -> Result<(), StartupError>;
192}
193
194impl<R: Runtime> Launchable for ForLaunch<R> {
195 fn launch(self: Box<Self>) -> Result<(), StartupError> {
196 self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
197 self.publisher.launch()?;
198 self.pow_manager.launch()?;
199
200 Ok(())
201 }
202}
203
204#[derive(PartialEq)]
208#[must_use]
209pub(crate) enum ShutdownStatus {
210 Continue,
212 Terminate,
214}
215
216impl From<oneshot::Canceled> for ShutdownStatus {
217 fn from(_: oneshot::Canceled) -> ShutdownStatus {
218 ShutdownStatus::Terminate
219 }
220}
221
222#[derive(Builder)]
231#[builder(build_fn(private, name = "build_unvalidated", error = "FatalError"))]
232pub struct OnionService {
233 config: OnionServiceConfig,
235 keymgr: Arc<KeyMgr>,
237 state_dir: StateDirectory,
239}
240
241impl OnionService {
242 pub fn builder() -> OnionServiceBuilder {
244 OnionServiceBuilder::default()
245 }
246
247 pub fn launch<R>(
257 self,
258 runtime: R,
259 netdir_provider: Arc<dyn NetDirProvider>,
260 circ_pool: Arc<HsCircPool<R>>,
261 path_resolver: Arc<tor_config_path::CfgPathResolver>,
262 ) -> Result<(Arc<RunningOnionService>, impl Stream<Item = RendRequest>), StartupError>
263 where
264 R: Runtime,
265 {
266 let OnionService {
267 config,
268 keymgr,
269 state_dir,
270 } = self;
271
272 let nickname = config.nickname.clone();
273
274 let offline_hsid = false;
278
279 let selector = KeystoreSelector::Primary;
281 maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
282
283 if config.restricted_discovery.enabled {
284 info!(
285 nickname=%nickname,
286 "Launching onion service in restricted discovery mode"
287 );
288 } else {
289 info!(
290 nickname=%nickname,
291 "Launching onion service"
292 );
293 }
294
295 let state_handle = state_dir
296 .acquire_instance(&config.nickname)
297 .map_err(StartupError::StateDirectoryInaccessible)?;
298
299 let iptpub_storage_handle = state_handle
302 .storage_handle("iptpub")
303 .map_err(StartupError::StateDirectoryInaccessible)?;
304
305 let pow_manager_storage_handle = state_handle
306 .storage_handle("pow_manager")
307 .map_err(StartupError::StateDirectoryInaccessible)?;
308 let pow_nonce_dir = state_handle
309 .raw_subdir("pow_nonces")
310 .map_err(StartupError::StateDirectoryInaccessible)?;
311 let NewPowManager {
312 pow_manager,
313 rend_req_tx,
314 rend_req_rx,
315 publisher_update_rx,
316 } = PowManager::new(
317 runtime.clone(),
318 nickname.clone(),
319 pow_nonce_dir,
320 keymgr.clone(),
321 pow_manager_storage_handle,
322 )?;
323
324 let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
325 let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
326
327 let (ipt_mgr_view, publisher_view) =
328 crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
329
330 let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
331
332 let ipt_mgr = IptManager::new(
333 runtime.clone(),
334 netdir_provider.clone(),
335 nickname.clone(),
336 config_rx.clone(),
337 rend_req_tx,
338 shutdown_rx.clone(),
339 &state_handle,
340 crate::ipt_mgr::Real {
341 circ_pool: circ_pool.clone(),
342 },
343 keymgr.clone(),
344 status_tx.clone().into(),
345 )?;
346
347 let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
348 runtime,
349 nickname.clone(),
350 netdir_provider,
351 circ_pool,
352 publisher_view,
353 config_rx,
354 status_tx.clone().into(),
355 Arc::clone(&keymgr),
356 path_resolver,
357 pow_manager.clone(),
358 publisher_update_rx,
359 );
360
361 let svc = Arc::new(RunningOnionService {
362 nickname,
363 keymgr,
364 inner: Mutex::new(SvcInner {
365 config_tx,
366 _shutdown_tx: shutdown_tx,
367 status_tx,
368 unlaunched: Some((
369 rend_req_rx,
370 Box::new(ForLaunch {
371 publisher,
372 ipt_mgr,
373 ipt_mgr_view,
374 pow_manager,
375 }),
376 )),
377 }),
378 });
379
380 let stream = svc.launch()?;
381 Ok((svc, stream))
382 }
383
384 pub fn onion_address(&self) -> Option<HsId> {
392 onion_address(&self.keymgr, &self.config.nickname)
393 }
394
395 #[deprecated = "Use the new onion_address method instead"]
399 pub fn onion_name(&self) -> Option<HsId> {
400 self.onion_address()
401 }
402
403 pub fn generate_identity_key(&self, selector: KeystoreSelector) -> Result<HsId, StartupError> {
422 let offline_hsid = false;
426
427 maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
428 }
429}
430
431impl OnionServiceBuilder {
432 pub fn build(&self) -> Result<OnionService, StartupError> {
434 let svc = self.build_unvalidated()?;
435 Ok(svc)
436 }
437}
438
439impl RunningOnionService {
440 pub fn reconfigure(
446 &self,
447 new_config: OnionServiceConfig,
448 how: Reconfigure,
449 ) -> Result<(), ReconfigureError> {
450 let mut inner = self.inner.lock().expect("lock poisoned");
451 inner.config_tx.try_maybe_send(|cur_config| {
452 let new_config = cur_config.for_transition_to(new_config, how)?;
453 Ok(match how {
454 tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
456 _ => Arc::new(new_config),
458 })
459 })
460
461 }
465
466 pub fn status(&self) -> OnionServiceStatus {
475 self.inner.lock().expect("poisoned lock").status_tx.get()
476 }
477
478 pub fn status_events(&self) -> OnionServiceStatusStream {
481 self.inner
482 .lock()
483 .expect("poisoned lock")
484 .status_tx
485 .subscribe()
486 }
487
488 fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest>, StartupError> {
494 let (rend_req_rx, launch) = {
495 let mut inner = self.inner.lock().expect("poisoned lock");
496 inner
497 .unlaunched
498 .take()
499 .ok_or(StartupError::AlreadyLaunched)?
500 };
501
502 match launch.launch() {
503 Ok(()) => {}
504 Err(e) => {
505 return Err(e);
506 }
507 }
508
509 Ok(rend_req_rx)
515 }
516
517 pub fn onion_address(&self) -> Option<HsId> {
537 onion_address(&self.keymgr, &self.nickname)
538 }
539
540 #[deprecated = "Use the new onion_address method instead"]
544 pub fn onion_name(&self) -> Option<HsId> {
545 self.onion_address()
546 }
547}
548
549fn maybe_generate_hsid(
553 keymgr: &Arc<KeyMgr>,
554 nickname: &HsNickname,
555 offline_hsid: bool,
556 selector: KeystoreSelector,
557) -> Result<HsId, StartupError> {
558 if offline_hsid {
559 unimplemented!("offline hsid mode");
560 }
561
562 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
563
564 let kp = keymgr
565 .get::<HsIdKey>(&hsid_spec)
566 .map_err(|cause| StartupError::Keystore {
567 action: "read",
568 cause,
569 })?;
570
571 let mut rng = tor_llcrypto::rng::CautiousRng;
572 let (hsid, generated) = match kp {
573 Some(kp) => (kp.id(), false),
574 None => {
575 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
579 let kp = keymgr
580 .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false )
581 .map_err(|cause| StartupError::Keystore {
582 action: "generate",
583 cause,
584 })?;
585
586 (HsIdKey::from(&kp).id(), true)
587 }
588 };
589
590 if generated {
591 info!(
592 "Generated a new identity for service {nickname}: {}",
593 hsid.display_redacted()
594 );
595 } else {
596 info!(
599 "Using existing identity for service {nickname}: {}",
600 hsid.display_redacted()
601 );
602 }
603
604 Ok(hsid)
605}
606
607fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
619 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
620
621 keymgr
622 .get::<HsIdKey>(&hsid_spec)
623 .ok()?
624 .map(|hsid| hsid.id())
625}
626
627pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
630 use tor_protover::named::*;
631 [
634 HSINTRO_V3,
636 HSINTRO_RATELIM,
637 HSREND_V3,
638 HSDIR_V3,
639 ]
640 .into_iter()
641 .collect()
642}
643
644#[cfg(test)]
645pub(crate) mod test {
646 #![allow(clippy::bool_assert_comparison)]
648 #![allow(clippy::clone_on_copy)]
649 #![allow(clippy::dbg_macro)]
650 #![allow(clippy::mixed_attributes_style)]
651 #![allow(clippy::print_stderr)]
652 #![allow(clippy::print_stdout)]
653 #![allow(clippy::single_char_pattern)]
654 #![allow(clippy::unwrap_used)]
655 #![allow(clippy::unchecked_duration_subtraction)]
656 #![allow(clippy::useless_vec)]
657 #![allow(clippy::needless_pass_by_value)]
658 use super::*;
660
661 use std::fmt::Display;
662 use std::path::Path;
663
664 use fs_mistrust::Mistrust;
665 use test_temp_dir::{test_temp_dir, TestTempDir, TestTempDirGuard};
666
667 use tor_basic_utils::test_rng::testing_rng;
668 use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
669 use tor_llcrypto::pk::ed25519;
670 use tor_persist::state_dir::InstanceStateHandle;
671
672 use crate::config::OnionServiceConfigBuilder;
673 use crate::ipt_set::IptSetStorageHandle;
674 use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
675
676 const TEST_SVC_NICKNAME: &str = "test-svc";
678
679 #[test]
680 fn protocols() {
681 let pr = supported_hsservice_protocols();
682 let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
683 assert_eq!(pr, expected);
684 }
685
686 pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
688 temp_dir.subdir_used_by("keystore", |keystore_dir| {
689 let keystore = ArtiNativeKeystore::from_path_and_mistrust(
690 keystore_dir,
691 &Mistrust::new_dangerously_trust_everyone(),
692 )
693 .unwrap();
694
695 Arc::new(
696 KeyMgrBuilder::default()
697 .primary_store(Box::new(keystore))
698 .build()
699 .unwrap(),
700 )
701 })
702 }
703
704 #[allow(clippy::let_and_return)] pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
706 let nick = HsNickname::new(nick.to_string()).unwrap();
707 let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
708 let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
709 let instance = state_dir.acquire_instance(&nick).unwrap();
710 instance
711 }
712
713 pub(crate) fn create_storage_handles(
714 dir: &Path,
715 ) -> (
716 tor_persist::state_dir::InstanceStateHandle,
717 IptSetStorageHandle,
718 ) {
719 let nick = HsNickname::try_from("allium".to_owned()).unwrap();
720 create_storage_handles_from_state_dir(dir, &nick)
721 }
722
723 pub(crate) fn create_storage_handles_from_state_dir(
724 state_dir: &Path,
725 nick: &HsNickname,
726 ) -> (
727 tor_persist::state_dir::InstanceStateHandle,
728 IptSetStorageHandle,
729 ) {
730 let instance = mk_state_instance(state_dir, nick);
731 let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
732 (instance, iptpub_state_handle)
733 }
734
735 macro_rules! maybe_generate_hsid {
736 ($keymgr:expr, $offline_hsid:expr) => {{
737 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
738 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
739 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
740
741 assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
742 assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
743
744 maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
745 }};
746 }
747
748 fn create_hsid() -> (HsIdKeypair, HsIdKey) {
750 let mut rng = testing_rng();
751 let keypair = ed25519::Keypair::generate(&mut rng);
752
753 let id_pub = HsIdKey::from(keypair.verifying_key());
754 let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
755
756 (id_keypair, id_pub)
757 }
758
759 #[test]
760 fn generate_hsid() {
761 let temp_dir = test_temp_dir!();
762 let keymgr = create_keymgr(&temp_dir);
763
764 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
765 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
766
767 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
768 maybe_generate_hsid!(keymgr, false );
769 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
770 }
771
772 #[test]
773 fn hsid_keypair_already_exists() {
774 let temp_dir = test_temp_dir!();
775 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
776 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
777 let keymgr = create_keymgr(&temp_dir);
778
779 let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
781 let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
782 let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
783
784 keymgr
785 .insert(
786 existing_hsid_keypair,
787 &hsid_spec,
788 KeystoreSelector::Primary,
789 true,
790 )
791 .unwrap();
792
793 maybe_generate_hsid(
794 &keymgr,
795 &nickname,
796 false, Default::default(),
798 )
799 .unwrap();
800
801 let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
802 let pk: HsIdKey = (&keypair).into();
803
804 assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
805 }
806
807 #[test]
808 #[ignore] fn generate_hsid_offline_hsid() {
810 let temp_dir = test_temp_dir!();
811 let keymgr = create_keymgr(&temp_dir);
812
813 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
814 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
815 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
816
817 maybe_generate_hsid!(keymgr, true );
818
819 assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
820 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
821 }
822
823 #[test]
824 #[ignore] fn generate_hsid_corrupt_keystore() {
826 let temp_dir = test_temp_dir!();
827 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
828 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
829 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
830
831 let keymgr = create_keymgr(&temp_dir);
832
833 let (hsid_keypair, _hsid_public) = create_hsid();
834 let (_hsid_keypair, hsid_public) = create_hsid();
835
836 keymgr
837 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
838 .unwrap();
839
840 keymgr
842 .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
843 .unwrap();
844
845 assert!(maybe_generate_hsid(
846 &keymgr,
847 &nickname,
848 false, Default::default()
850 )
851 .is_err());
852 }
853
854 #[test]
855 fn onion_address() {
856 let temp_dir = test_temp_dir!();
857 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
858 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
859 let keymgr = create_keymgr(&temp_dir);
860
861 let (hsid_keypair, hsid_public) = create_hsid();
862
863 keymgr
865 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
866 .unwrap();
867
868 let config = OnionServiceConfigBuilder::default()
869 .nickname(nickname)
870 .build()
871 .unwrap();
872
873 let state_dir = StateDirectory::new(
874 temp_dir.as_path_untracked(),
875 &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
876 )
877 .unwrap();
878
879 let service = OnionService::builder()
880 .config(config)
881 .keymgr(Arc::clone(&*keymgr))
882 .state_dir(state_dir)
883 .build()
884 .unwrap();
885
886 let hsid = HsId::from(hsid_public);
887 assert_eq!(service.onion_address().unwrap(), hsid);
888
889 drop(temp_dir); }
891}