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_circmgr::ServiceOnionServiceDataTunnel;
10
use tor_hscrypto::Subcredential;
11
use tor_keymgr::ArtiPath;
12
use tor_proto::client::stream::{IncomingStream, IncomingStreamRequest};
13

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

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

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

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

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

            
62
    /// The circuit that made this request.
63
    on_tunnel: Arc<ServiceOnionServiceDataTunnel>,
64
}
65

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

            
71
    /// The key manager, used for looking up subcredentials.
72
    pub(crate) keymgr: Arc<KeyMgr>,
73

            
74
    /// Key we'll use to decrypt the rendezvous request.
75
    pub(crate) kp_hss_ntor: Arc<HsSvcNtorKeypair>,
76

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

            
81
    /// Configuration for a filter for this service.
82
    pub(crate) filter: rend_handshake::RequestFilter,
83

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

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

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

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

            
103
        let pattern = BlindIdKeypairSpecifierPattern {
104
            nickname: Some(self.nickname.clone()),
105
            period: None,
106
        }
107
        .arti_pattern()?;
108

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

            
134
        Ok(blind_id_kps
135
            .iter()
136
            .map(|(blind_id_key, period)| hsid.compute_subcredential(&blind_id_key.into(), *period))
137
            .collect())
138
    }
139

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

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

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

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

            
174
        Ok(tp)
175
    }
176
}
177

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

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

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

            
227
        let tunnel = Arc::new(tunnel);
228

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

            
240
    /// Reject this request.  (The client will receive no notification.)
241
    pub async fn reject(self) -> Result<(), Bug> {
242
        // nothing to do.
243
        Ok(())
244
    }
245

            
246
    // TODO: also add various accessors, as needed.
247
}
248

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

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

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

            
281
    /// Reject this request and close the rendezvous circuit entirely,
282
    /// along with all other streams attached to the circuit.
283
    pub fn shutdown_circuit(self) -> Result<(), Bug> {
284
        self.on_tunnel.terminate();
285
        Ok(())
286
    }
287

            
288
    // TODO various accessors, including for circuit.
289
}