6666use std:: borrow:: Cow ;
6767use std:: fs:: { File , OpenOptions } ;
6868use std:: io:: { BufRead , BufReader , Read , Seek , SeekFrom , Write } ;
69- use std:: path:: Path ;
69+ use std:: path:: { Path , PathBuf } ;
7070
7171use aes:: cipher:: block_padding:: UnpadError ;
7272use aes:: cipher:: inout:: PadError ;
@@ -293,6 +293,11 @@ fn is_base64_char(c: char) -> bool {
293293 || c == '='
294294}
295295
296+ /// Record a host's public key into the user's known_hosts file.
297+ pub fn learn_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < ( ) , Error > {
298+ learn_known_hosts_path ( host, port, pubkey, known_hosts_path ( ) ?)
299+ }
300+
296301/// Record a host's public key into a nonstandard location.
297302pub fn learn_known_hosts_path < P : AsRef < Path > > (
298303 host : & str ,
@@ -333,17 +338,21 @@ pub fn learn_known_hosts_path<P: AsRef<Path>>(
333338 Ok ( ( ) )
334339}
335340
336- /// Check that a server key matches the one recorded in file `path`.
337- pub fn check_known_hosts_path < P : AsRef < Path > > (
341+ /// Get the server key that matches the one recorded in the user's known_hosts file.
342+ pub fn known_host_key ( host : & str , port : u16 ) -> Result < Option < ( usize , key:: PublicKey ) > , Error > {
343+ known_host_key_path ( host, port, known_hosts_path ( ) ?)
344+ }
345+
346+ /// Get the server key that matches the one recorded in `path`.
347+ pub fn known_host_key_path < P : AsRef < Path > > (
338348 host : & str ,
339349 port : u16 ,
340- pubkey : & key:: PublicKey ,
341350 path : P ,
342- ) -> Result < bool , Error > {
351+ ) -> Result < Option < ( usize , key :: PublicKey ) > , Error > {
343352 let mut f = if let Ok ( f) = File :: open ( path) {
344353 BufReader :: new ( f)
345354 } else {
346- return Ok ( false ) ;
355+ return Ok ( None ) ;
347356 } ;
348357 let mut buffer = String :: new ( ) ;
349358
@@ -368,18 +377,14 @@ pub fn check_known_hosts_path<P: AsRef<Path>>(
368377 if let ( Some ( h) , Some ( k) ) = ( hosts, key) {
369378 debug ! ( "{:?} {:?}" , h, k) ;
370379 if match_hostname ( & host_port, h) {
371- if & parse_public_key_base64 ( k) ? == pubkey {
372- return Ok ( true ) ;
373- } else {
374- return Err ( Error :: KeyChanged { line } ) ;
375- }
380+ return Ok ( Some ( ( line, parse_public_key_base64 ( k) ?) ) ) ;
376381 }
377382 }
378383 }
379384 buffer. clear ( ) ;
380385 line += 1 ;
381386 }
382- Ok ( false )
387+ Ok ( None )
383388}
384389
385390fn match_hostname ( host : & str , pattern : & str ) -> bool {
@@ -404,49 +409,42 @@ fn match_hostname(host: &str, pattern: &str) -> bool {
404409 false
405410}
406411
407- /// Record a host's public key into the user's known_hosts file.
408- #[ cfg( target_os = "windows" ) ]
409- pub fn learn_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < ( ) , Error > {
410- if let Some ( mut known_host_file) = dirs:: home_dir ( ) {
411- known_host_file. push ( "ssh" ) ;
412- known_host_file. push ( "known_hosts" ) ;
413- learn_known_hosts_path ( host, port, pubkey, & known_host_file)
414- } else {
415- Err ( Error :: NoHomeDir )
416- }
412+ /// Check whether the host is known, from its standard location.
413+ pub fn check_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < bool , Error > {
414+ check_known_hosts_path ( host, port, pubkey, known_hosts_path ( ) ?)
417415}
418416
419- /// Record a host's public key into the user's known_hosts file.
420- #[ cfg( not( target_os = "windows" ) ) ]
421- pub fn learn_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < ( ) , Error > {
422- if let Some ( mut known_host_file) = dirs:: home_dir ( ) {
423- known_host_file. push ( ".ssh" ) ;
424- known_host_file. push ( "known_hosts" ) ;
425- learn_known_hosts_path ( host, port, pubkey, & known_host_file)
417+ /// Check that a server key matches the one recorded in file `path`.
418+ pub fn check_known_hosts_path < P : AsRef < Path > > (
419+ host : & str ,
420+ port : u16 ,
421+ pubkey : & key:: PublicKey ,
422+ path : P ,
423+ ) -> Result < bool , Error > {
424+ if let Some ( ( line, recorded) ) = known_host_key_path ( host, port, path) ? {
425+ if recorded == * pubkey {
426+ Ok ( true )
427+ } else {
428+ Err ( Error :: KeyChanged { line } )
429+ }
426430 } else {
427- Err ( Error :: NoHomeDir )
431+ Ok ( false )
428432 }
429433}
430434
431- /// Check whether the host is known, from its standard location.
432435#[ cfg( target_os = "windows" ) ]
433- pub fn check_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < bool , Error > {
434- if let Some ( mut known_host_file) = dirs:: home_dir ( ) {
435- known_host_file. push ( "ssh" ) ;
436- known_host_file. push ( "known_hosts" ) ;
437- check_known_hosts_path ( host, port, pubkey, & known_host_file)
436+ fn known_hosts_path ( ) -> Result < PathBuf , Error > {
437+ if let Some ( home_dir) = dirs:: home_dir ( ) {
438+ Ok ( home_dir. join ( "ssh" ) . join ( "known_hosts" ) )
438439 } else {
439440 Err ( Error :: NoHomeDir )
440441 }
441442}
442443
443- /// Check whether the host is known, from its standard location.
444444#[ cfg( not( target_os = "windows" ) ) ]
445- pub fn check_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < bool , Error > {
446- if let Some ( mut known_host_file) = dirs:: home_dir ( ) {
447- known_host_file. push ( ".ssh" ) ;
448- known_host_file. push ( "known_hosts" ) ;
449- check_known_hosts_path ( host, port, pubkey, & known_host_file)
445+ fn known_hosts_path ( ) -> Result < PathBuf , Error > {
446+ if let Some ( home_dir) = dirs:: home_dir ( ) {
447+ Ok ( home_dir. join ( ".ssh" ) . join ( "known_hosts" ) )
450448 } else {
451449 Err ( Error :: NoHomeDir )
452450 }
0 commit comments