@@ -467,56 +467,54 @@ static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session
467467 return error ;
468468}
469469
470- static const char * hostkey_type_to_string (int type )
470+ static void add_hostkey_pref_if_avail (
471+ LIBSSH2_KNOWNHOSTS * known_hosts ,
472+ const char * hostname ,
473+ int port ,
474+ git_str * prefs ,
475+ int type ,
476+ const char * type_name )
471477{
472- switch (type ) {
473- case LIBSSH2_KNOWNHOST_KEY_SSHRSA :
474- return "ssh-rsa" ;
475- case LIBSSH2_KNOWNHOST_KEY_SSHDSS :
476- return "ssh-dss" ;
477- #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
478- case LIBSSH2_KNOWNHOST_KEY_ECDSA_256 :
479- return "ecdsa-sha2-nistp256" ;
480- case LIBSSH2_KNOWNHOST_KEY_ECDSA_384 :
481- return "ecdsa-sha2-nistp384" ;
482- case LIBSSH2_KNOWNHOST_KEY_ECDSA_521 :
483- return "ecdsa-sha2-nistp521" ;
484- #endif
485- #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
486- case LIBSSH2_KNOWNHOST_KEY_ED25519 :
487- return "ssh-ed25519" ;
488- #endif
489- }
478+ struct libssh2_knownhost * host = NULL ;
479+ const char key = '\0' ;
480+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | type ;
481+ int error ;
490482
491- return NULL ;
483+ error = libssh2_knownhost_checkp (known_hosts , hostname , port , & key , 1 , mask , & host );
484+ if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH ) {
485+ if (git_str_len (prefs ) > 0 ) {
486+ git_str_putc (prefs , ',' );
487+ }
488+ git_str_puts (prefs , type_name );
489+ }
492490}
493491
494492/*
495493 * We figure out what kind of key we want to ask the remote for by trying to
496494 * look it up with a nonsense key and using that mismatch to figure out what key
497495 * we do have stored for the host.
498496 *
499- * Returns the string to pass to libssh2_session_method_pref or NULL if we were
500- * unable to find anything or an error happened.
497+ * Populates prefs with the string to pass to libssh2_session_method_pref.
501498 */
502- static const char * find_hostkey_preference (LIBSSH2_KNOWNHOSTS * known_hosts , const char * hostname , int port )
499+ static void find_hostkey_preference (
500+ LIBSSH2_KNOWNHOSTS * known_hosts ,
501+ const char * hostname ,
502+ int port ,
503+ git_str * prefs )
503504{
504- struct libssh2_knownhost * host = NULL ;
505- /* Specify no key type so we don't filter on that */
506- int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW ;
507- const char key = '\0' ;
508- int error ;
509-
510505 /*
511- * In case of mismatch, we can find the type of key from known_hosts in
512- * the returned host's information as it means that an entry was found
513- * but our nonsense key obviously didn't match.
506+ * The order here is important as it indicates the priority of what will
507+ * be preferred.
514508 */
515- error = libssh2_knownhost_checkp (known_hosts , hostname , port , & key , 1 , type , & host );
516- if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH )
517- return hostkey_type_to_string (host -> typemask & LIBSSH2_KNOWNHOST_KEY_MASK );
518-
519- return NULL ;
509+ #ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
510+ add_hostkey_pref_if_avail (known_hosts , hostname , port , prefs , LIBSSH2_KNOWNHOST_KEY_ED25519 , "ssh-ed25519" );
511+ #endif
512+ #ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
513+ add_hostkey_pref_if_avail (known_hosts , hostname , port , prefs , LIBSSH2_KNOWNHOST_KEY_ECDSA_256 , "ecdsa-sha2-nistp256" );
514+ add_hostkey_pref_if_avail (known_hosts , hostname , port , prefs , LIBSSH2_KNOWNHOST_KEY_ECDSA_384 , "ecdsa-sha2-nistp384" );
515+ add_hostkey_pref_if_avail (known_hosts , hostname , port , prefs , LIBSSH2_KNOWNHOST_KEY_ECDSA_521 , "ecdsa-sha2-nistp521" );
516+ #endif
517+ add_hostkey_pref_if_avail (known_hosts , hostname , port , prefs , LIBSSH2_KNOWNHOST_KEY_SSHRSA , "ssh-rsa" );
520518}
521519
522520static int _git_ssh_session_create (
@@ -526,11 +524,11 @@ static int _git_ssh_session_create(
526524 int port ,
527525 git_stream * io )
528526{
529- int rc = 0 ;
527+ git_socket_stream * socket = GIT_CONTAINER_OF ( io , git_socket_stream , parent ) ;
530528 LIBSSH2_SESSION * s ;
531529 LIBSSH2_KNOWNHOSTS * known_hosts ;
532- git_socket_stream * socket = GIT_CONTAINER_OF ( io , git_socket_stream , parent ) ;
533- const char * keytype = NULL ;
530+ git_str prefs = GIT_STR_INIT ;
531+ int rc = 0 ;
534532
535533 GIT_ASSERT_ARG (session );
536534 GIT_ASSERT_ARG (hosts );
@@ -547,16 +545,17 @@ static int _git_ssh_session_create(
547545 return -1 ;
548546 }
549547
550- if ((keytype = find_hostkey_preference (known_hosts , hostname , port )) != NULL ) {
548+ find_hostkey_preference (known_hosts , hostname , port , & prefs );
549+ if (git_str_len (& prefs ) > 0 ) {
551550 do {
552- rc = libssh2_session_method_pref (s , LIBSSH2_METHOD_HOSTKEY , keytype );
551+ rc = libssh2_session_method_pref (s , LIBSSH2_METHOD_HOSTKEY , git_str_cstr ( & prefs ) );
553552 } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc );
554553 if (rc != LIBSSH2_ERROR_NONE ) {
555554 ssh_error (s , "failed to set hostkey preference" );
556555 goto on_error ;
557556 }
558557 }
559-
558+ git_str_dispose ( & prefs );
560559
561560 do {
562561 rc = libssh2_session_handshake (s , socket -> s );
0 commit comments