1//! Authentication for RpcConn.
23use serde::{Deserialize, Serialize};
4use tor_rpc_connect::auth::cookie::{Cookie, CookieAuthMac, CookieAuthNonce};
56use crate::msgs::{request::Request, ObjectId};
78use super::{ConnectError, RpcConn};
910/// Arguments to an `auth:authenticate` request.
11#[derive(Serialize, Debug)]
12struct AuthParams<'a> {
13/// The authentication scheme we are using.
14scheme: &'a str,
15}
16/// Response to an `auth:authenticate` or `auth:cookie_continue` request.
17#[derive(Deserialize, Debug)]
18struct AuthenticatedReply {
19/// A session object that we use to access the rest of Arti's functionality.
20session: ObjectId,
21}
2223/// Arguments to an `auth:cookie_begin` request.
24#[derive(Serialize, Debug)]
25struct CookieBeginParams {
26/// Client-selected nonce; used while the server is proving knowledge of the cookie.
27client_nonce: CookieAuthNonce,
28}
2930#[derive(Deserialize, Debug)]
31struct CookieBeginReply {
32/// Temporary ID to use while authenticating.
33cookie_auth: ObjectId,
34/// Address that the server thinks it's listening on.
35server_addr: String,
36/// MAC returned by the server to prove knowledge of the cookie.
37server_mac: CookieAuthMac,
38/// Server-selected nonce to use while we prove knowledge of the cookie.
39server_nonce: CookieAuthNonce,
40}
4142/// Arguments to an `auth:cookie_begin` request.
43#[derive(Serialize, Debug)]
44struct CookieContinueParams {
45/// Make to prove our knowledge of the cookie.
46client_mac: CookieAuthMac,
47}
4849impl RpcConn {
50/// Try to negotiate "inherent" authentication, using the provided scheme name.
51 ///
52 /// (Inherent authentication is available whenever the client proves that they
53 /// are authorized through being able to connect to Arti at all. Examples
54 /// include connecting to a unix domain socket, and an in-process Arti implementation.)
55pub(crate) fn authenticate_inherent(
56&self,
57 scheme_name: &str,
58 ) -> Result<ObjectId, ConnectError> {
59let r: Request<AuthParams> = Request::new(
60 ObjectId::connection_id(),
61"auth:authenticate",
62 AuthParams {
63 scheme: scheme_name,
64 },
65 );
66let authenticated: AuthenticatedReply = self
67.execute_internal(&r.encode()?)?
68.map_err(ConnectError::AuthenticationFailed)?;
6970Ok(authenticated.session)
71 }
7273/// Try to negotiate "cookie" authentication, using the provided cookie and server address.
74pub(crate) fn authenticate_cookie(
75&self,
76 cookie: &Cookie,
77 server_addr: &str,
78 ) -> Result<ObjectId, ConnectError> {
79// This protocol is documented in `rpc-cookie-sketch.md`.
80let client_nonce = CookieAuthNonce::new(&mut rand::rng());
8182let cookie_begin: Request<CookieBeginParams> = Request::new(
83 ObjectId::connection_id(),
84"auth:cookie_begin",
85 CookieBeginParams {
86 client_nonce: client_nonce.clone(),
87 },
88 );
89let reply: CookieBeginReply = self
90.execute_internal(&cookie_begin.encode()?)?
91.map_err(ConnectError::AuthenticationFailed)?;
9293if server_addr != reply.server_addr {
94return Err(ConnectError::ServerAddressMismatch {
95 ours: server_addr.into(),
96 theirs: reply.server_addr,
97 });
98 }
99100let expected_server_mac =
101 cookie.server_mac(&client_nonce, &reply.server_nonce, server_addr);
102if reply.server_mac != expected_server_mac {
103return Err(ConnectError::CookieMismatch);
104 }
105106let client_mac = cookie.client_mac(&client_nonce, &reply.server_nonce, server_addr);
107let cookie_auth_obj = reply.cookie_auth.clone();
108let cookie_continue = Request::new(
109 cookie_auth_obj.clone(),
110"auth:cookie_continue",
111 CookieContinueParams { client_mac },
112 );
113let authenticated: AuthenticatedReply = self
114.execute_internal(&cookie_continue.encode()?)?
115.map_err(ConnectError::AuthenticationFailed)?;
116117// Drop the cookie_auth_obj: we don't need it now that we have authenticated.
118self.release_obj(cookie_auth_obj)?;
119120Ok(authenticated.session)
121 }
122}