Skip to main content

ssh_packet/
userauth.rs

1//! Messages involved in the SSH's **authentication** (`SSH-USERAUTH`) part of the protocol,
2//! as defined in the [RFC 4252](https://datatracker.ietf.org/doc/html/rfc4252) and [RFC 4256](https://datatracker.ietf.org/doc/html/rfc4256).
3
4use binrw::binrw;
5
6use super::{Packet, arch};
7
8impl Packet for Request<'_> {}
9impl Packet for Failure<'_> {}
10impl Packet for Success {}
11impl Packet for Banner<'_> {}
12impl Packet for PkOk<'_> {}
13impl Packet for PasswdChangereq<'_> {}
14impl Packet for InfoRequest<'_> {}
15impl Packet for InfoResponse {}
16
17/// The `SSH_MSG_USERAUTH_REQUEST` message.
18///
19/// see <https://datatracker.ietf.org/doc/html/rfc4252#section-5>.
20#[binrw]
21#[derive(Debug, Clone)]
22#[brw(big, magic = 50_u8)]
23pub struct Request<'b> {
24    /// Username for the auth request.
25    pub username: arch::Utf8<'b>,
26
27    /// Service name to query.
28    pub service_name: arch::Ascii<'b>,
29
30    #[bw(calc = method.as_ascii())]
31    auth_method: arch::Ascii<'b>,
32
33    /// Authentication method used.
34    #[br(args(auth_method))]
35    pub method: Method<'b>,
36}
37
38/// The authentication method in the `SSH_MSG_USERAUTH_REQUEST` message.
39#[binrw]
40#[derive(Debug, Clone)]
41#[br(import(method: arch::Ascii<'_>))]
42pub enum Method<'b> {
43    /// Authenticate using the `none` method,
44    /// as defined in [RFC4252 section 5.2](https://datatracker.ietf.org/doc/html/rfc4252#section-5.2).
45    #[br(pre_assert(method == Method::NONE))]
46    None,
47
48    /// Authenticate using the `publickey` method,
49    /// as defined in [RFC4252 section 7](https://datatracker.ietf.org/doc/html/rfc4252#section-7).
50    #[br(pre_assert(method == Method::PUBLICKEY))]
51    Publickey {
52        #[bw(calc = arch::Bool::from(signature.is_some()))]
53        signed: arch::Bool,
54
55        /// Public key algorithm's name.
56        algorithm: arch::Bytes<'b>,
57        /// Public key blob.
58        blob: arch::Bytes<'b>,
59
60        /// The optional signature of the authentication packet,
61        /// signed with the according private key.
62        #[br(if(*signed))]
63        signature: Option<arch::Bytes<'b>>,
64    },
65
66    /// Authenticate using the `password` method,
67    /// as defined in [RFC4252 section 8](https://datatracker.ietf.org/doc/html/rfc4252#section-8).
68    #[br(pre_assert(method == Method::PASSWORD))]
69    Password {
70        #[bw(calc = arch::Bool::from(new.is_some()))]
71        change: arch::Bool,
72
73        /// Plaintext password.
74        password: arch::Utf8<'b>,
75
76        /// In the case of a the receival of a [`PasswdChangereq`],
77        /// the new password to be set in place of the old one.
78        #[br(if(*change))]
79        new: Option<arch::Utf8<'b>>,
80    },
81
82    /// Authenticate using the `hostbased` method,
83    /// as defined in [RFC4252 section 9](https://datatracker.ietf.org/doc/html/rfc4252#section-9).
84    #[br(pre_assert(method == Method::HOSTBASED))]
85    Hostbased {
86        /// Public key algorithm for the host key.
87        algorithm: arch::Bytes<'b>,
88
89        /// Public host key and certificates for client host.
90        host_key: arch::Bytes<'b>,
91
92        /// Client host name expressed as the FQDN.
93        client_fqdn: arch::Ascii<'b>,
94
95        /// User name on the client host.
96        username: arch::Utf8<'b>,
97
98        /// The signature of the authentication packet.
99        signature: arch::Bytes<'b>,
100    },
101
102    /// Authenticate using the `keyboard-interactive` method,
103    /// as defined in [RFC4256 section 3.1](https://datatracker.ietf.org/doc/html/rfc4256#section-3.1).
104    #[br(pre_assert(method == Method::KEYBOARD_INTERACTIVE))]
105    KeyboardInteractive {
106        /// Language tag.
107        language: arch::Ascii<'b>,
108
109        /// A hint for the prefered interactive submethod.
110        submethods: arch::Utf8<'b>,
111    },
112}
113
114impl Method<'_> {
115    /// The SSH `none` authentication method.
116    pub const NONE: arch::Ascii<'static> = arch::ascii!("none");
117
118    /// The SSH `publickey` authentication method.
119    pub const PUBLICKEY: arch::Ascii<'static> = arch::ascii!("publickey");
120
121    /// The SSH `password` authentication method.
122    pub const PASSWORD: arch::Ascii<'static> = arch::ascii!("password");
123
124    /// The SSH `hostbased` authentication method.
125    pub const HOSTBASED: arch::Ascii<'static> = arch::ascii!("hostbased");
126
127    /// The SSH `keyboard-interactive` authentication method.
128    pub const KEYBOARD_INTERACTIVE: arch::Ascii<'static> = arch::ascii!("keyboard-interactive");
129
130    /// Get the [`Method`]'s SSH identifier.
131    pub fn as_ascii(&self) -> arch::Ascii<'static> {
132        match self {
133            Self::None { .. } => Self::NONE,
134            Self::Publickey { .. } => Self::PUBLICKEY,
135            Self::Password { .. } => Self::PASSWORD,
136            Self::Hostbased { .. } => Self::HOSTBASED,
137            Self::KeyboardInteractive { .. } => Self::KEYBOARD_INTERACTIVE,
138        }
139    }
140}
141
142/// The `SSH_MSG_USERAUTH_FAILURE` message.
143///
144/// see <https://datatracker.ietf.org/doc/html/rfc4252#section-5.1>.
145#[binrw]
146#[derive(Debug, Default, Clone)]
147#[brw(big, magic = 51_u8)]
148pub struct Failure<'b> {
149    /// Authentications that can continue.
150    pub continue_with: arch::NameList<'b>,
151
152    /// Partial success.
153    pub partial_success: arch::Bool,
154}
155
156/// The `SSH_MSG_USERAUTH_SUCCESS` message.
157///
158/// see <https://datatracker.ietf.org/doc/html/rfc4252#section-5.1>.
159#[binrw]
160#[derive(Debug, Default, Clone)]
161#[brw(big, magic = 52_u8)]
162pub struct Success;
163
164/// The `SSH_MSG_USERAUTH_BANNER` message.
165///
166/// see <https://datatracker.ietf.org/doc/html/rfc4252#section-5.4>.
167#[binrw]
168#[derive(Debug, Default, Clone)]
169#[brw(big, magic = 53_u8)]
170pub struct Banner<'b> {
171    /// The auth banner message.
172    pub message: arch::Utf8<'b>,
173
174    /// Language tag.
175    pub language: arch::Ascii<'b>,
176}
177
178/// The `SSH_MSG_USERAUTH_PK_OK` message.
179///
180/// see <https://datatracker.ietf.org/doc/html/rfc4252#section-7>.
181#[binrw]
182#[derive(Debug, Clone)]
183#[brw(big, magic = 60_u8)]
184pub struct PkOk<'b> {
185    /// Public key algorithm name from the request.
186    pub algorithm: arch::Bytes<'b>,
187
188    /// Public key blob from the request.
189    pub blob: arch::Bytes<'b>,
190}
191
192/// The `SSH_MSG_USERAUTH_PASSWD_CHANGEREQ` message.
193///
194/// see <https://datatracker.ietf.org/doc/html/rfc4252#section-8>.
195#[binrw]
196#[derive(Debug, Default, Clone)]
197#[brw(big, magic = 60_u8)]
198pub struct PasswdChangereq<'b> {
199    /// Password change prompt.
200    pub prompt: arch::Utf8<'b>,
201
202    /// Language tag (deprecated).
203    pub language: arch::Ascii<'b>,
204}
205
206/// The `SSH_MSG_USERAUTH_INFO_REQUEST` message.
207///
208/// see <https://datatracker.ietf.org/doc/html/rfc4256#section-3.2>.
209#[binrw]
210#[derive(Debug, Clone)]
211#[brw(big, magic = 60_u8)]
212pub struct InfoRequest<'b> {
213    /// Name of the challenge.
214    pub name: arch::Utf8<'b>,
215
216    /// Instructions for the challenge.
217    pub instruction: arch::Utf8<'b>,
218
219    /// Language tag (deprecated).
220    pub language: arch::Ascii<'b>,
221
222    #[bw(calc = prompts.len() as u32)]
223    num_prompts: u32,
224
225    /// The challenge's prompts.
226    #[br(count = num_prompts)]
227    pub prompts: Vec<InfoRequestPrompt<'static>>,
228}
229
230/// A prompt in the `SSH_MSG_USERAUTH_INFO_REQUEST` message.
231#[binrw]
232#[derive(Debug, Clone)]
233#[brw(big)]
234pub struct InfoRequestPrompt<'b> {
235    /// Challenge prompt text.
236    pub prompt: arch::Utf8<'b>,
237
238    /// Whether the client should echo back typed characters.
239    pub echo: arch::Bool,
240}
241
242/// The `SSH_MSG_USERAUTH_INFO_RESPONSE` message.
243///
244/// see <https://datatracker.ietf.org/doc/html/rfc4256#section-3.4>.
245#[binrw]
246#[derive(Debug, Clone)]
247#[brw(big, magic = 61_u8)]
248pub struct InfoResponse {
249    #[bw(calc = responses.len() as u32)]
250    num_responses: u32,
251
252    /// Responses to the provided challenge.
253    #[br(count = num_responses)]
254    pub responses: Vec<arch::Utf8<'static>>,
255}