11use std:: {
22 env,
3+ fmt,
34 io:: { self , IsTerminal } ,
4- path:: PathBuf ,
5+ path:: { Path , PathBuf } ,
56 sync:: Arc ,
67 time:: { Duration , Instant } ,
78} ;
@@ -12,15 +13,52 @@ use russh::{
1213 keys:: { decode_secret_key, PrivateKeyWithHashAlg } ,
1314} ;
1415
16+ #[ derive( Debug ) ]
17+ pub enum AuthError {
18+ ReadIdentityFile ( String ) ,
19+ DecodeSecretKey ( String ) ,
20+ RsaHash ( String ) ,
21+ PublicKeyTimeout ( f64 ) ,
22+ PublicKeyFailed ( String ) ,
23+ PublicKeyRejected ,
24+ PasswordTimeout ( f64 ) ,
25+ PasswordFailed ( String ) ,
26+ PasswordRejected ,
27+ AllMethodsFailed ,
28+ }
29+
30+ impl fmt:: Display for AuthError {
31+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
32+ match self {
33+ Self :: ReadIdentityFile ( msg) => write ! ( f, "Failed to read identity file: {msg}" ) ,
34+ Self :: DecodeSecretKey ( msg) => write ! ( f, "Failed to decode secret key: {msg}" ) ,
35+ Self :: RsaHash ( msg) => write ! ( f, "Failed to get RSA hash algorithm: {msg}" ) ,
36+ Self :: PublicKeyTimeout ( timeout) => {
37+ write ! ( f, "Public key authentication timed out after {timeout} seconds" )
38+ }
39+ Self :: PublicKeyFailed ( msg) => write ! ( f, "Public key authentication failed: {msg}" ) ,
40+ Self :: PublicKeyRejected => write ! ( f, "Public key authentication returned false" ) ,
41+ Self :: PasswordTimeout ( timeout) => {
42+ write ! ( f, "Password authentication timed out after {timeout} seconds" )
43+ }
44+ Self :: PasswordFailed ( msg) => write ! ( f, "Password authentication failed: {msg}" ) ,
45+ Self :: PasswordRejected => write ! ( f, "Password authentication returned false" ) ,
46+ Self :: AllMethodsFailed => write ! ( f, "All authentication methods failed" ) ,
47+ }
48+ }
49+ }
50+
51+ impl std:: error:: Error for AuthError { }
52+
1553async fn authenticate_publickey < H : client:: Handler > (
1654 session : & mut client:: Handle < H > ,
1755 user : & str ,
18- identity : & PathBuf ,
56+ identity : & Path ,
1957 password : Option < & str > ,
2058 timeout : f64 ,
21- ) -> Result < ( ) , String > {
59+ ) -> Result < ( ) , AuthError > {
2260 let key_content = std:: fs:: read_to_string ( identity)
23- . map_err ( |e| format ! ( "Failed to read identity file: {e}" ) ) ?;
61+ . map_err ( |e| AuthError :: ReadIdentityFile ( e . to_string ( ) ) ) ?;
2462
2563 // Try to decode the key with the provided password first
2664 let mut key_result = decode_secret_key ( & key_content, password) ;
@@ -35,25 +73,25 @@ async fn authenticate_publickey<H: client::Handler>(
3573 }
3674 }
3775
38- let key = key_result. map_err ( |e| format ! ( "Failed to decode secret key: {e}" ) ) ?;
76+ let key = key_result. map_err ( |e| AuthError :: DecodeSecretKey ( e . to_string ( ) ) ) ?;
3977
4078 // Get the best supported RSA hash algorithm for the connection
4179 let rsa_hash = session
4280 . best_supported_rsa_hash ( )
4381 . await
44- . map_err ( |e| format ! ( "Failed to get RSA hash algorithm: {e}" ) ) ?
82+ . map_err ( |e| AuthError :: RsaHash ( e . to_string ( ) ) ) ?
4583 . flatten ( ) ;
4684
4785 let timeout_result = tokio:: time:: timeout (
4886 Duration :: from_secs_f64 ( timeout) ,
4987 session. authenticate_publickey ( user, PrivateKeyWithHashAlg :: new ( Arc :: new ( key) , rsa_hash) ) ,
5088 )
5189 . await
52- . map_err ( |_| format ! ( "Public key authentication timed out after { timeout} seconds" ) ) ?;
90+ . map_err ( |_| AuthError :: PublicKeyTimeout ( timeout) ) ?;
5391 let auth_result =
54- timeout_result. map_err ( |e| format ! ( "Public key authentication failed: {e}" ) ) ?;
92+ timeout_result. map_err ( |e| AuthError :: PublicKeyFailed ( e . to_string ( ) ) ) ?;
5593 if !auth_result. success ( ) {
56- return Err ( "Public key authentication returned false" . to_string ( ) ) ;
94+ return Err ( AuthError :: PublicKeyRejected ) ;
5795 }
5896
5997 info ! ( "Public key authentication succeeded" ) ;
@@ -85,16 +123,16 @@ async fn authenticate_password<H: client::Handler>(
85123 user : & str ,
86124 password : & str ,
87125 timeout : f64 ,
88- ) -> Result < ( ) , String > {
126+ ) -> Result < ( ) , AuthError > {
89127 let timeout_result = tokio:: time:: timeout (
90128 Duration :: from_secs_f64 ( timeout) ,
91129 session. authenticate_password ( user, password) ,
92130 )
93131 . await
94- . map_err ( |_| format ! ( "Password authentication timed out after { timeout} seconds" ) ) ?;
95- let auth_result = timeout_result. map_err ( |e| format ! ( "Password authentication failed: {e}" ) ) ?;
132+ . map_err ( |_| AuthError :: PasswordTimeout ( timeout) ) ?;
133+ let auth_result = timeout_result. map_err ( |e| AuthError :: PasswordFailed ( e . to_string ( ) ) ) ?;
96134 if !auth_result. success ( ) {
97- return Err ( "Password authentication returned false" . to_string ( ) ) ;
135+ return Err ( AuthError :: PasswordRejected ) ;
98136 }
99137
100138 info ! ( "Password authentication succeeded" ) ;
@@ -106,9 +144,9 @@ pub async fn authenticate_all<H: client::Handler>(
106144 user : & str ,
107145 host : & str ,
108146 password : Option < & str > ,
109- identity : Option < & PathBuf > ,
147+ identity : Option < & Path > ,
110148 timeout : f64 ,
111- ) -> Result < Duration , & ' static str > {
149+ ) -> Result < Duration , AuthError > {
112150 let start = Instant :: now ( ) ;
113151
114152 // Try public key authentication if identity file is provided
@@ -165,5 +203,5 @@ pub async fn authenticate_all<H: client::Handler>(
165203 }
166204
167205 // Fails if all authentication methods fail
168- Err ( "All authentication methods failed" )
206+ Err ( AuthError :: AllMethodsFailed )
169207}
0 commit comments