1
//! Request objects used to implement onion services.
2
//!
3
//! These requests are yielded on a stream, and the calling code needs to decide
4
//! whether to permit or reject them.
5

            
6
use crate::internal_prelude::*;
7

            
8
use tor_cell::relaycell::msg::{Connected, End, Introduce2};
9
use tor_hscrypto::Subcredential;
10
use tor_keymgr::ArtiPath;
11
use tor_proto::stream::{IncomingStream, IncomingStreamRequest};
12

            
13
/// Request to complete an introduction/rendezvous handshake.
14
///
15
/// A request of this kind indicates that a client has asked permission to
16
/// connect to an onion service through an introduction point.  The caller needs
17
/// to decide whether or not to complete the handshake.
18
///
19
/// Protocol details: More specifically, we create one of these whenever we get a well-formed
20
/// `INTRODUCE2` message.  Based on this, the caller decides whether to send a
21
/// `RENDEZVOUS1` message.
22
#[derive(Educe)]
23
#[educe(Debug)]
24
pub struct RendRequest {
25
    /// The introduction point that sent this request.
26
    ipt_lid: IptLocalId,
27

            
28
    /// The message as received from the remote introduction point.
29
    raw: Introduce2,
30

            
31
    /// Reference to the keys we'll need to decrypt and handshake with this request.
32
    #[educe(Debug(ignore))]
33
    context: Arc<RendRequestContext>,
34

            
35
    /// The introduce2 message that we've decrypted and processed.
36
    ///
37
    /// We do not compute this immediately upon receiving the Introduce2 cell,
38
    /// since there is a bit of cryptography involved and we don't want to add
39
    /// any extra latency to the message handler.
40
    ///
41
    /// TODO: This also contains `raw`, which is maybe not so great; it would be
42
    /// neat to implement more efficiently.
43
    expanded: once_cell::unsync::OnceCell<rend_handshake::IntroRequest>,
44
}
45

            
46
/// A request from a client to open a new stream to an onion service.
47
///
48
/// We can only receive these _after_ we have already permitted the client to
49
/// connect via a [`RendRequest`].
50
///
51
/// Protocol details: More specifically, we create one of these whenever we get a well-formed
52
/// `BEGIN` message.  Based on this, the caller decides whether to send a
53
/// `CONNECTED` message.
54
#[derive(Debug)]
55
pub struct StreamRequest {
56
    /// The object that will be used to send data to and from the client.
57
    stream: IncomingStream,
58

            
59
    /// The circuit that made this request.
60
    on_circuit: Arc<ClientCirc>,
61
}
62

            
63
/// Keys and objects needed to answer a RendRequest.
64
pub(crate) struct RendRequestContext {
65
    /// The nickname of the service receiving the request.
66
    pub(crate) nickname: HsNickname,
67

            
68
    /// The key manager, used for looking up subcredentials.
69
    pub(crate) keymgr: Arc<KeyMgr>,
70

            
71
    /// Key we'll use to decrypt the rendezvous request.
72
    pub(crate) kp_hss_ntor: Arc<HsSvcNtorKeypair>,
73

            
74
    /// We use this key to identify our session with this introduction point,
75
    /// and prevent replays across sessions.
76
    pub(crate) kp_hs_ipt_sid: HsIntroPtSessionIdKey,
77

            
78
    /// Configuration for a filter for this service.
79
    pub(crate) filter: rend_handshake::RequestFilter,
80

            
81
    /// Provider we'll use to find a directory so that we can build a rendezvous
82
    /// circuit.
83
    pub(crate) netdir_provider: Arc<dyn tor_netdir::NetDirProvider>,
84

            
85
    /// Circuit pool we'll use to build a rendezvous circuit.
86
    pub(crate) circ_pool: Arc<dyn RendCircConnector + Send + Sync>,
87
}
88

            
89
impl RendRequestContext {
90
    /// Obtain the all current `Subcredential`s of `nickname`
91
    /// from the `K_hs_blind_id` read from the keystore.
92
    pub(crate) fn compute_subcredentials(&self) -> Result<Vec<Subcredential>, FatalError> {
93
        let hsid_key_spec = HsIdPublicKeySpecifier::new(self.nickname.clone());
94

            
95
        let hsid = self
96
            .keymgr
97
            .get::<HsIdKey>(&hsid_key_spec)?
98
            .ok_or_else(|| FatalError::MissingHsIdKeypair(self.nickname.clone()))?;
99

            
100
        let pattern = BlindIdKeypairSpecifierPattern {
101
            nickname: Some(self.nickname.clone()),
102
            period: None,
103
        }
104
        .arti_pattern()?;
105

            
106
        let blind_id_kps: Vec<(HsBlindIdKeypair, TimePeriod)> = self
107
            .keymgr
108
            .list_matching(&pattern)?
109
            .iter()
110
            .map(|entry| -> Result<Option<_>, FatalError> {
111
                let path = entry
112
                    .key_path()
113
                    .arti()
114
                    .ok_or_else(|| internal!("CTorPath matched arti pattern?!"))?;
115
                let matches = path
116
                    .matches(&pattern)
117
                    .ok_or_else(|| internal!("path matched but no longer does?!"))?;
118
                let period = Self::parse_time_period(path, &matches)?;
119
                // Try to retrieve the key.
120
                self.keymgr
121
                    .get_entry::<HsBlindIdKeypair>(entry)
122
                    .map_err(FatalError::Keystore)
123
                    // If the key is not found, it means it has been garbage collected between the time
124
                    // we queried the keymgr for the list of keys matching the pattern and now.
125
                    // This is OK, because we only need the "current" keys
126
                    .map(|maybe_key| maybe_key.map(|key| (key, period)))
127
            })
128
            .flatten_ok()
129
            .collect::<Result<Vec<_>, FatalError>>()?;
130

            
131
        Ok(blind_id_kps
132
            .iter()
133
            .map(|(blind_id_key, period)| hsid.compute_subcredential(&blind_id_key.into(), *period))
134
            .collect())
135
    }
136

            
137
    /// Try to parse the `captures` of `path` as a [`TimePeriod`].
138
    fn parse_time_period(
139
        path: &ArtiPath,
140
        captures: &[ArtiPathRange],
141
    ) -> Result<TimePeriod, tor_keymgr::Error> {
142
        use tor_keymgr::{KeyPathError, KeystoreCorruptionError as KCE};
143

            
144
        let [denotator] = captures else {
145
            return Err(internal!(
146
                "invalid number of denotator captures: expected 1, found {}",
147
                captures.len()
148
            )
149
            .into());
150
        };
151

            
152
        let Some(denotator) = path.substring(denotator) else {
153
            return Err(internal!("captured substring out of range?!").into());
154
        };
155

            
156
        let slug = Slug::new(denotator.to_string()).map_err(|e| {
157
            KCE::KeyPath(KeyPathError::InvalidArtiPath {
158
                path: path.clone(),
159
                error: e.into(),
160
            })
161
        })?;
162
        let tp = TimePeriod::from_slug(&slug).map_err(|error| {
163
            KCE::KeyPath(KeyPathError::InvalidKeyPathComponentValue {
164
                key: "time_period".to_owned(),
165
                path: path.clone(),
166
                value: slug,
167
                error,
168
            })
169
        })?;
170

            
171
        Ok(tp)
172
    }
173
}
174

            
175
impl RendRequest {
176
    /// Construct a new RendRequest from its parts.
177
    pub(crate) fn new(
178
        ipt_lid: IptLocalId,
179
        msg: Introduce2,
180
        context: Arc<RendRequestContext>,
181
    ) -> Self {
182
        Self {
183
            ipt_lid,
184
            raw: msg,
185
            context,
186
            expanded: Default::default(),
187
        }
188
    }
189

            
190
    /// Try to return a reference to the intro_request, creating it if it did
191
    /// not previously exist.
192
    fn intro_request(
193
        &self,
194
    ) -> Result<&rend_handshake::IntroRequest, rend_handshake::IntroRequestError> {
195
        self.expanded.get_or_try_init(|| {
196
            rend_handshake::IntroRequest::decrypt_from_introduce2(self.raw.clone(), &self.context)
197
        })
198
    }
199

            
200
    /// Mark this request as accepted, and try to connect to the client's
201
    /// provided rendezvous point.
202
    pub async fn accept(
203
        mut self,
204
    ) -> Result<impl Stream<Item = StreamRequest> + Unpin, ClientError> {
205
        // Make sure the request is there.
206
        self.intro_request().map_err(ClientError::BadIntroduce)?;
207
        // Take ownership of the request.
208
        let intro_request = self
209
            .expanded
210
            .take()
211
            .expect("intro_request succeeded but did not fill 'expanded'.");
212
        let rend_handshake::OpenSession {
213
            stream_requests,
214
            circuit,
215
        } = intro_request
216
            .establish_session(
217
                self.context.filter.clone(),
218
                self.context.circ_pool.clone(),
219
                self.context.netdir_provider.clone(),
220
            )
221
            .await
222
            .map_err(ClientError::EstablishSession)?;
223

            
224
        // Note that we move circuit (which is an Arc<ClientCirc>) into this
225
        // closure, which lives for as long as the stream of StreamRequest, and
226
        // for as long as each individual StreamRequest.  This is how we keep
227
        // the rendezvous circuit alive, and ensure that it gets closed when
228
        // the Stream we return is dropped.
229
        Ok(stream_requests.map(move |stream| StreamRequest {
230
            stream,
231
            on_circuit: circuit.clone(),
232
        }))
233
    }
234

            
235
    /// Reject this request.  (The client will receive no notification.)
236
    pub async fn reject(self) -> Result<(), Bug> {
237
        // nothing to do.
238
        Ok(())
239
    }
240

            
241
    // TODO: also add various accessors, as needed.
242
}
243

            
244
impl StreamRequest {
245
    /// Return the message that was used to request this stream.
246
    ///
247
    /// NOTE: for consistency with other onion service implementations, you
248
    /// should typically only accept `BEGIN` messages, and only check the port
249
    /// in those messages. If you behave differently, your implementation will
250
    /// be distinguishable.
251
    pub fn request(&self) -> &IncomingStreamRequest {
252
        self.stream.request()
253
    }
254

            
255
    /// Accept this request and send the client a `CONNECTED` message.
256
    pub async fn accept(self, connected_message: Connected) -> Result<DataStream, ClientError> {
257
        self.stream
258
            .accept_data(connected_message)
259
            .await
260
            .map_err(ClientError::AcceptStream)
261
    }
262

            
263
    /// Reject this request, and send the client an `END` message.
264
    ///
265
    /// NOTE: If you need to be consistent with other onion service
266
    /// implementations, you should typically only send back `End` messages with
267
    /// the `DONE` reason. If you send back any other rejection, your
268
    /// implementation will be distinguishable.
269
    pub async fn reject(self, end_message: End) -> Result<(), ClientError> {
270
        self.stream
271
            .reject(end_message)
272
            .await
273
            .map_err(ClientError::RejectStream)
274
    }
275

            
276
    /// Reject this request and close the rendezvous circuit entirely,
277
    /// along with all other streams attached to the circuit.
278
    pub fn shutdown_circuit(self) -> Result<(), Bug> {
279
        self.on_circuit.terminate();
280
        Ok(())
281
    }
282

            
283
    // TODO various accessors, including for circuit.
284
}