aboutsummaryrefslogtreecommitdiff
path: root/crates/arti-rpcserver/src/connection/auth.rs
blob: bcbd60458d0167e049d67dfb129dfc84f76cf989 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! RPC commands and related functionality for authentication.
//!
//! In Arti's RPC system, authentication is a kind of method that can be invoked
//! on the special "connection" object, which gives you an RPC _session_ as a
//! result.  The RPC session is the root for all other capabilities.

use std::sync::Arc;

use super::Connection;
use derive_deftly::Deftly;
use tor_rpcbase as rpc;
use tor_rpcbase::templates::*;

/*
    TODO RPC: This is disabled because the design isn't really useful.
    If we're going to provide something here, it should probably
    contain a list of protocol elements/aspects, and it should be designed
    to enable compatibility, with a clear view of what applications are
    supposed to do about it.

/// Declare the get_rpc_protocol method.
mod get_rpc_protocol {
    use super::Connection;
    use std::sync::Arc;
    use tor_rpcbase as rpc;

    /// Method to inquire about the RPC protocol.
    #[derive(Debug, serde::Deserialize)]
    struct GetRpcProtocol {}

    /// Reply to the [`GetRpcProtocol`] method
    #[derive(Debug, serde::Serialize)]
    struct GetProtocolReply {
        /// The version of the RPC protocol that this server speaks.
        // TODO RPC: Should this be a list?
        version: RpcProtocolId,
    }

    /// Identifier for a version of this RPC meta-protocol.
    #[derive(Debug, Copy, Clone, serde::Serialize)]
    enum RpcProtocolId {
        /// Alpha version of the protocol.  Things might break between here and the
        /// stable protocol.
        ///
        /// TODO RPC: CHange this to v0.
        #[serde(rename = "alpha")]
        Alpha,
    }
    rpc::decl_method! {"auth:get_rpc_protocol" => GetRpcProtocol}
    impl rpc::Method for GetRpcProtocol {
        type Output = GetProtocolReply;
        type Update = rpc::NoUpdates;
    }

    /// Describe which version of the RPC protocol our connection implements.
    async fn conn_get_rpc_protocol(
        _conn: Arc<Connection>,
        _method: Box<GetRpcProtocol>,
        _ctx: Box<dyn rpc::Context>,
    ) -> Result<GetProtocolReply, rpc::RpcError> {
        Ok(GetProtocolReply {
            version: RpcProtocolId::Alpha,
        })
    }
    rpc::static_rpc_invoke_fn! {
        conn_get_rpc_protocol(Connection, GetRpcProtocol);
    }
}
*/

/// Information about how an RPC session has been authenticated.
///
/// Currently, this isn't actually used for anything, since there's only one way
/// to authenticate a connection.  It exists so that later we can pass
/// information to the session-creator function.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct RpcAuthentication {}

/// The authentication scheme as enumerated in the spec.
///
/// Conceptually, an authentication scheme answers the question "How can the
/// Arti process know you have permissions to use or administer it?"
///
/// TODO RPC: The only supported one for now is "inherent:unix_path"
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
enum AuthenticationScheme {
    /// Inherent authority based on the ability to access an AF_UNIX address.
    #[serde(rename = "inherent:unix_path")]
    InherentUnixPath,
}

/// Method to ask which authentication methods are supported.
#[derive(Debug, serde::Deserialize, Deftly)]
#[derive_deftly(DynMethod)]
#[deftly(rpc(method_name = "auth:query"))]
struct AuthQuery {}

/// A list of supported authentication schemes and their parameters.
#[derive(Debug, serde::Serialize)]
struct SupportedAuth {
    /// A list of the supported authentication schemes.
    ///
    /// TODO RPC: Actually, this should be able to contain strings _or_ maps,
    /// where the maps are additional information about the parameters needed
    /// for a particular scheme.  But I think that's a change we can make later
    /// once we have a scheme that takes parameters.
    ///
    /// TODO RPC: Should we indicate which schemes get you additional privileges?
    schemes: Vec<AuthenticationScheme>,
}

impl rpc::Method for AuthQuery {
    type Output = SupportedAuth;
    type Update = rpc::NoUpdates;
}
/// Implement `auth:AuthQuery` on a connection.
async fn conn_authquery(
    _conn: Arc<Connection>,
    _query: Box<AuthQuery>,
    _ctx: Box<dyn rpc::Context>,
) -> Result<SupportedAuth, rpc::RpcError> {
    // Right now, every connection supports the same scheme.
    Ok(SupportedAuth {
        schemes: vec![AuthenticationScheme::InherentUnixPath],
    })
}
rpc::static_rpc_invoke_fn! {
    conn_authquery;
}

/// Method to implement basic authentication.  Right now only "I connected to
/// you so I must have permission!" is supported.
#[derive(Debug, serde::Deserialize, Deftly)]
#[derive_deftly(DynMethod)]
#[deftly(rpc(method_name = "auth:authenticate"))]
struct Authenticate {
    /// The authentication scheme as enumerated in the spec.
    ///
    /// TODO RPC: The only supported one for now is "inherent:unix_path"
    scheme: AuthenticationScheme,
}

/// A reply from the `Authenticate` method.
#[derive(Debug, serde::Serialize)]
struct AuthenticateReply {
    /// An owned reference to a `Session` object.
    session: rpc::ObjectId,
}

impl rpc::Method for Authenticate {
    type Output = AuthenticateReply;
    type Update = rpc::NoUpdates;
}

/// An error during authentication.
#[derive(Debug, Clone, thiserror::Error, serde::Serialize)]
enum AuthenticationFailure {}

impl tor_error::HasKind for AuthenticationFailure {
    fn kind(&self) -> tor_error::ErrorKind {
        // TODO RPC not right.
        tor_error::ErrorKind::LocalProtocolViolation
    }
}

/// Invoke the "authenticate" method on a connection.
///
/// TODO RPC: This behavior is wrong; we'll need to fix it to be all
/// capabilities-like.
async fn authenticate_connection(
    unauth: Arc<Connection>,
    method: Box<Authenticate>,
    ctx: Box<dyn rpc::Context>,
) -> Result<AuthenticateReply, rpc::RpcError> {
    match method.scheme {
        // For now, we only support AF_UNIX connections, and we assume that if
        // you have permission to open such a connection to us, you have
        // permission to use Arti. We will refine this later on!
        AuthenticationScheme::InherentUnixPath => {}
    }

    let auth = RpcAuthentication {};
    let session = {
        let mgr = unauth.mgr()?;
        mgr.create_session(&auth)
    };
    let session = ctx.register_owned(session);
    Ok(AuthenticateReply { session })
}
rpc::static_rpc_invoke_fn! {
    authenticate_connection;
}