@@ -72,7 +72,9 @@ use aes::cipher::block_padding::UnpadError;
7272use aes:: cipher:: inout:: PadError ;
7373use byteorder:: { BigEndian , WriteBytesExt } ;
7474use data_encoding:: BASE64_MIME ;
75+ use hmac:: { Hmac , Mac } ;
7576use log:: debug;
77+ use sha1:: Sha1 ;
7678use thiserror:: Error ;
7779
7880pub mod encoding;
@@ -365,8 +367,7 @@ pub fn check_known_hosts_path<P: AsRef<Path>>(
365367 let key = s. next ( ) ;
366368 if let ( Some ( h) , Some ( k) ) = ( hosts, key) {
367369 debug ! ( "{:?} {:?}" , h, k) ;
368- let host_matches = h. split ( ',' ) . any ( |x| x == host_port) ;
369- if host_matches {
370+ if match_hostname ( & host_port, h) {
370371 if & parse_public_key_base64 ( k) ? == pubkey {
371372 return Ok ( true ) ;
372373 } else {
@@ -381,6 +382,28 @@ pub fn check_known_hosts_path<P: AsRef<Path>>(
381382 Ok ( false )
382383}
383384
385+ fn match_hostname ( host : & str , pattern : & str ) -> bool {
386+ for entry in pattern. split ( ',' ) {
387+ if entry. starts_with ( "|1|" ) {
388+ let mut parts = entry. split ( '|' ) . skip ( 2 ) ;
389+ let Some ( Ok ( salt) ) = parts. next ( ) . map ( |p| BASE64_MIME . decode ( p. as_bytes ( ) ) ) else {
390+ continue ;
391+ } ;
392+ let Some ( Ok ( hash) ) = parts. next ( ) . map ( |p| BASE64_MIME . decode ( p. as_bytes ( ) ) ) else {
393+ continue ;
394+ } ;
395+ if let Ok ( hmac) = Hmac :: < Sha1 > :: new_from_slice ( & salt) {
396+ if hmac. chain_update ( host) . verify_slice ( & hash) . is_ok ( ) {
397+ return true ;
398+ }
399+ }
400+ } else if host == entry {
401+ return true ;
402+ }
403+ }
404+ false
405+ }
406+
384407/// Record a host's public key into the user's known_hosts file.
385408#[ cfg( target_os = "windows" ) ]
386409pub fn learn_known_hosts ( host : & str , port : u16 , pubkey : & key:: PublicKey ) -> Result < ( ) , Error > {
@@ -526,7 +549,10 @@ QR+u0AypRPmzHnOPAAAAEXJvb3RAMTQwOTExNTQ5NDBkAQ==
526549 let path = dir. path ( ) . join ( "known_hosts" ) ;
527550 {
528551 let mut f = File :: create ( & path) . unwrap ( ) ;
529- f. write ( b"[localhost]:13265 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJdD7y3aLq454yWBdwLWbieU1ebz9/cu7/QEXn9OIeZJ\n #pijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G2sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\n pijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G1sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\n " ) . unwrap ( ) ;
552+ f. write_all ( b"[localhost]:13265 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJdD7y3aLq454yWBdwLWbieU1ebz9/cu7/QEXn9OIeZJ\n " ) . unwrap ( ) ;
553+ f. write_all ( b"#pijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G2sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\n " ) . unwrap ( ) ;
554+ f. write_all ( b"pijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G1sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\n " ) . unwrap ( ) ;
555+ f. write_all ( b"|1|O33ESRMWPVkMYIwJ1Uw+n877jTo=|nuuC5vEqXlEZ/8BXQR7m619W6Ak= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILIG2T/B0l0gaqj3puu510tu9N1OkQ4znY3LYuEm5zCF\n " ) . unwrap ( ) ;
530556 }
531557
532558 // Valid key, non-standard port.
@@ -538,6 +564,15 @@ QR+u0AypRPmzHnOPAAAAEXJvb3RAMTQwOTExNTQ5NDBkAQ==
538564 . unwrap ( ) ;
539565 assert ! ( check_known_hosts_path( host, port, & hostkey, & path) . unwrap( ) ) ;
540566
567+ // Valid key, hashed.
568+ let host = "example.com" ;
569+ let port = 22 ;
570+ let hostkey = parse_public_key_base64 (
571+ "AAAAC3NzaC1lZDI1NTE5AAAAILIG2T/B0l0gaqj3puu510tu9N1OkQ4znY3LYuEm5zCF" ,
572+ )
573+ . unwrap ( ) ;
574+ assert ! ( check_known_hosts_path( host, port, & hostkey, & path) . unwrap( ) ) ;
575+
541576 // Valid key, several hosts, port 22
542577 let host = "pijul.org" ;
543578 let port = 22 ;
0 commit comments