@@ -126,6 +126,16 @@ impl MethodSet {
126126 ] )
127127 }
128128
129+ pub ( crate ) fn server_supported ( ) -> Self {
130+ Self ( vec ! [
131+ MethodKind :: None ,
132+ MethodKind :: Password ,
133+ MethodKind :: PublicKey ,
134+ MethodKind :: HostBased ,
135+ MethodKind :: KeyboardInteractive ,
136+ ] )
137+ }
138+
129139 pub fn remove ( & mut self , method : MethodKind ) {
130140 self . 0 . retain ( |x| * x != method) ;
131141 }
@@ -168,27 +178,71 @@ pub trait Signer: Sized {
168178 ) -> impl Future < Output = Result < Vec < u8 > , Self :: Error > > + Send ;
169179}
170180
181+ /// One step of a GSSAPI security context exchange, as produced by a
182+ /// [`GssapiAuthenticator`].
171183#[ derive( Debug , Clone , PartialEq , Eq ) ]
172184pub enum GssapiStep {
185+ /// The context is not established yet; send `token` to the server and
186+ /// wait for its next token.
173187 Continue {
174188 token : Vec < u8 > ,
175189 } ,
190+ /// The context is established. `token` is the final output token, if any.
191+ /// `mic` is the MIC computed over the `mic_data` passed to
192+ /// [`GssapiAuthenticator::gssapi_step`]. Implementations MUST produce a
193+ /// MIC whenever the established context supports integrity protection
194+ /// (RFC 4462, Section 3.5); `None` falls back to
195+ /// `SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE`.
176196 Complete {
177197 token : Option < Vec < u8 > > ,
178198 mic : Option < Vec < u8 > > ,
179199 } ,
180200}
181201
202+ /// A GSS-API error reported by the server during `gssapi-with-mic`
203+ /// authentication. Informational: the server follows up with an
204+ /// authentication failure.
205+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
206+ pub enum GssapiError {
207+ /// `SSH_MSG_USERAUTH_GSSAPI_ERROR` (RFC 4462, Section 3.8).
208+ Status {
209+ major_status : u32 ,
210+ minor_status : u32 ,
211+ message : String ,
212+ } ,
213+ /// `SSH_MSG_USERAUTH_GSSAPI_ERRTOK` (RFC 4462, Section 3.10). May be
214+ /// passed to `GSS_Init_sec_context()` to obtain mechanism-specific
215+ /// error details.
216+ ErrorToken ( Vec < u8 > ) ,
217+ }
218+
182219#[ cfg_attr( feature = "async-trait" , async_trait:: async_trait) ]
183220pub trait GssapiAuthenticator : Sized {
184221 type Error : From < crate :: SendError > ;
185222
223+ /// Advance the GSSAPI security context.
224+ ///
225+ /// `selected_mechanism` is `Some` on the first step and carries the
226+ /// DER-encoded OID of the mechanism the server selected; implementations
227+ /// must verify it is one of the mechanisms they offered (RFC 4462,
228+ /// Section 3.3). It is `None` on subsequent steps.
229+ ///
230+ /// `input_token` is the token received from the server, if any.
231+ /// `mic_data` is the data to compute the final MIC over once the context
232+ /// is established.
186233 fn gssapi_step (
187234 & mut self ,
188- selected_mechanism : Vec < u8 > ,
235+ selected_mechanism : Option < Vec < u8 > > ,
189236 input_token : Option < Vec < u8 > > ,
190237 mic_data : Vec < u8 > ,
191238 ) -> impl Future < Output = Result < GssapiStep , Self :: Error > > + Send ;
239+
240+ /// Called when the server reports a GSS-API error; the server follows up
241+ /// with an authentication failure. The default implementation ignores
242+ /// the error.
243+ fn gssapi_error ( & mut self , _error : GssapiError ) -> impl Future < Output = ( ) > + Send {
244+ async { }
245+ }
192246}
193247
194248#[ derive( Debug , Error ) ]
@@ -304,33 +358,22 @@ impl AuthRequest {
304358 }
305359
306360 pub ( crate ) fn new ( method : & Method ) -> Self {
307- match method {
308- Method :: KeyboardInteractive { submethods } => Self {
309- initial_methods : MethodSet :: all ( ) ,
310- methods : MethodSet :: all ( ) ,
311- partial_success : false ,
312- current : Some ( CurrentRequest :: KeyboardInteractive {
361+ let current = match method {
362+ Method :: KeyboardInteractive { submethods } => {
363+ Some ( CurrentRequest :: KeyboardInteractive {
313364 submethods : submethods. to_string ( ) ,
314- } ) ,
315- principal : None ,
316- rejection_count : 0 ,
317- } ,
318- Method :: GssapiWithMic { .. } => Self {
319- initial_methods : MethodSet :: all ( ) ,
320- methods : MethodSet :: all ( ) ,
321- partial_success : false ,
322- current : Some ( CurrentRequest :: GssapiWithMic ) ,
323- principal : None ,
324- rejection_count : 0 ,
325- } ,
326- _ => Self {
327- initial_methods : MethodSet :: all ( ) ,
328- methods : MethodSet :: all ( ) ,
329- partial_success : false ,
330- current : None ,
331- principal : None ,
332- rejection_count : 0 ,
333- } ,
365+ } )
366+ }
367+ Method :: GssapiWithMic { .. } => Some ( CurrentRequest :: GssapiWithMic ) ,
368+ _ => None ,
369+ } ;
370+ Self {
371+ initial_methods : MethodSet :: all ( ) ,
372+ methods : MethodSet :: all ( ) ,
373+ partial_success : false ,
374+ current,
375+ principal : None ,
376+ rejection_count : 0 ,
334377 }
335378 }
336379
0 commit comments