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