From c7f67be0100967d041450285a30b2bec6cbfad64 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 2 Jul 2026 09:07:05 +0200 Subject: [PATCH] vssh: keyfile use cleanups - make the libssh backend do the same fallback for private and public key filename as libssh2 already did and is documented behavior. This now uses a common function. - set the file names already in setup_connection if public key auth is requested, so that the connection reuse functions can use the information when checking for existing connections to reuse - rename the oddly named struct fields 'rsa' to 'priv_key' and 'rsa_pub' to 'pub_key' to better reflect their purposes Fixes #22243 Closes #22244 --- lib/url.c | 4 +- lib/vssh/libssh.c | 22 ++++------ lib/vssh/libssh2.c | 101 +++++---------------------------------------- lib/vssh/ssh.h | 4 +- lib/vssh/vssh.c | 84 +++++++++++++++++++++++++++++++++++++ lib/vssh/vssh.h | 4 ++ 6 files changed, 112 insertions(+), 107 deletions(-) diff --git a/lib/url.c b/lib/url.c index a7e7b4d06d..164aef797a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -681,8 +681,8 @@ static bool ssh_config_matches(struct connectdata *one, sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); - return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && - Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); + return sshc1 && sshc2 && Curl_safecmp(sshc1->priv_key, sshc2->priv_key) && + Curl_safecmp(sshc1->pub_key, sshc2->pub_key); } #endif diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 93be7ca21f..ea21e19289 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -832,8 +832,7 @@ static int myssh_in_AUTHLIST(struct Curl_easy *data, /* For public key auth we need either the private key or CURLSSH_AUTH_AGENT. */ if((sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) && - (data->set.str[STRING_SSH_PRIVATE_KEY] || - (data->set.ssh_auth_types & CURLSSH_AUTH_AGENT))) { + (sshc->priv_key || (data->set.ssh_auth_types & CURLSSH_AUTH_AGENT))) { myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); infof(data, "Authentication using SSH public key file"); } @@ -863,7 +862,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, /* Two choices, (1) private key was given on CMD, * (2) use the "default" keys. */ - if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + if(sshc->priv_key) { if(sshc->pubkey && !data->set.ssl.primary.key_passwd) { rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey); if(rc == SSH_AUTH_AGAIN) @@ -875,13 +874,11 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, } } - rc = ssh_pki_import_privkey_file(data-> - set.str[STRING_SSH_PRIVATE_KEY], + rc = ssh_pki_import_privkey_file(sshc->priv_key, data->set.ssl.primary.key_passwd, NULL, NULL, &sshc->privkey); if(rc != SSH_OK) { - failf(data, "Could not load private key file %s", - data->set.str[STRING_SSH_PRIVATE_KEY]); + failf(data, "Could not load private key file %s", sshc->priv_key); rc = myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); return rc; } @@ -1916,8 +1913,8 @@ static void sshc_cleanup(struct ssh_conn *sshc) sshc->pubkey = NULL; } - curlx_safefree(sshc->rsa_pub); - curlx_safefree(sshc->rsa); + curlx_safefree(sshc->pub_key); + curlx_safefree(sshc->priv_key); curlx_safefree(sshc->quote_path1); curlx_safefree(sshc->quote_path2); curlx_dyn_free(&sshc->readdir_buf); @@ -2547,7 +2544,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor)) return CURLE_OUT_OF_MEMORY; - return CURLE_OK; + return Curl_ssh_setup_pkey(data, sshc); } static Curl_recv scp_recv, sftp_recv; @@ -2655,9 +2652,8 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) sshc->privkey = NULL; sshc->pubkey = NULL; - if(data->set.str[STRING_SSH_PUBLIC_KEY]) { - rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], - &sshc->pubkey); + if(sshc->pub_key) { + rc = ssh_pki_import_pubkey_file(sshc->pub_key, &sshc->pubkey); if(rc != SSH_OK) { failf(data, "Could not load public key file"); return CURLE_FAILED_INIT; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index a3d0adf3b2..5f13533033 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -1094,8 +1094,7 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, return CURLE_OK; } -static CURLcode ssh_state_pkey_init(struct Curl_easy *data, - struct ssh_conn *sshc) +static void ssh_state_pkey_init(struct Curl_easy *data, struct ssh_conn *sshc) { /* * Check the supported auth types in the order I feel is most secure @@ -1105,88 +1104,13 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && strstr(sshc->authlist, "publickey")) { - bool out_of_memory = FALSE; - - sshc->rsa_pub = sshc->rsa = NULL; - - if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - sshc->rsa = curlx_strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); - if(!sshc->rsa) - out_of_memory = TRUE; - } - else { - /* To ponder about: should really the lib be messing about with the - HOME environment variable etc? */ - char *home = curl_getenv("HOME"); - curlx_struct_stat sbuf; - - /* If no private key file is specified, try some common paths. */ - if(home) { - /* Try ~/.ssh first. */ - sshc->rsa = curl_maprintf("%s/.ssh/id_rsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(curlx_stat(sshc->rsa, &sbuf)) { - curlx_free(sshc->rsa); - sshc->rsa = curl_maprintf("%s/.ssh/id_dsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(curlx_stat(sshc->rsa, &sbuf)) { - curlx_safefree(sshc->rsa); - } - } - curlx_free(home); - } - if(!out_of_memory && !sshc->rsa) { - /* Nothing found; try the current dir. */ - sshc->rsa = curlx_strdup("id_rsa"); - if(sshc->rsa && curlx_stat(sshc->rsa, &sbuf)) { - curlx_free(sshc->rsa); - sshc->rsa = curlx_strdup("id_dsa"); - if(sshc->rsa && curlx_stat(sshc->rsa, &sbuf)) { - curlx_free(sshc->rsa); - /* Out of guesses. Set to the empty string to avoid - * surprising info messages. */ - sshc->rsa = curlx_strdup(""); - } - } - } - } - - /* - * Unless the user explicitly specifies a public key file, let - * libssh2 extract the public key from the private key file. - * This is done by passing sshc->rsa_pub = NULL. - */ - if(!out_of_memory && data->set.str[STRING_SSH_PUBLIC_KEY] && - /* treat empty string the same way as NULL */ - data->set.str[STRING_SSH_PUBLIC_KEY][0]) { - sshc->rsa_pub = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); - if(!sshc->rsa_pub) - out_of_memory = TRUE; - } - - if(out_of_memory || !sshc->rsa) { - curlx_safefree(sshc->rsa); - curlx_safefree(sshc->rsa_pub); - myssh_to(data, sshc, SSH_SESSION_FREE); - return CURLE_OUT_OF_MEMORY; - } - - sshc->passphrase = data->set.ssl.primary.key_passwd; - if(!sshc->passphrase) - sshc->passphrase = ""; - - if(sshc->rsa_pub) - infof(data, "SSH: trying public key file '%s'", sshc->rsa_pub); - infof(data, "SSH: trying private key file '%s'", sshc->rsa); - + if(sshc->pub_key) + infof(data, "SSH: trying public key file '%s'", sshc->pub_key); + infof(data, "SSH: trying private key file '%s'", sshc->priv_key); myssh_to(data, sshc, SSH_AUTH_PKEY); } - else { + else myssh_to(data, sshc, SSH_AUTH_PASS_INIT); - } - return CURLE_OK; } static CURLcode sftp_quote_stat(struct Curl_easy *data, @@ -1559,14 +1483,11 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, user, curlx_uztoui(strlen(user)), - sshc->rsa_pub, - sshc->rsa, sshc->passphrase); + sshc->pub_key, + sshc->priv_key, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - curlx_safefree(sshc->rsa_pub); - curlx_safefree(sshc->rsa); - if(rc == 0) { sshc->authed = TRUE; infof(data, "SSH: authenticated via publickey"); @@ -2626,8 +2547,8 @@ static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, DEBUGASSERT(!sshc->kh); DEBUGASSERT(!sshc->ssh_agent); - curlx_safefree(sshc->rsa_pub); - curlx_safefree(sshc->rsa); + curlx_safefree(sshc->pub_key); + curlx_safefree(sshc->priv_key); curlx_safefree(sshc->quote_path1); curlx_safefree(sshc->quote_path2); curlx_safefree(sshc->homedir); @@ -2968,7 +2889,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_AUTH_PKEY_INIT: - result = ssh_state_pkey_init(data, sshc); + ssh_state_pkey_init(data, sshc); break; case SSH_AUTH_PKEY: @@ -3343,7 +3264,7 @@ static CURLcode ssh_setup_connection(struct Curl_easy *data, if(Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor)) return CURLE_OUT_OF_MEMORY; - return CURLE_OK; + return Curl_ssh_setup_pkey(data, sshc); } static Curl_recv scp_recv, sftp_recv; diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index de76c8a253..a299c3c476 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -144,8 +144,8 @@ struct ssh_conn { /* common */ const char *passphrase; /* pass-phrase to use */ - char *rsa_pub; /* strdup'ed public key file */ - char *rsa; /* strdup'ed private key file */ + char *pub_key; /* strdup'ed public key file */ + char *priv_key; /* strdup'ed private key file */ sshstate state; /* always use ssh.c:state() to change state! */ sshstate nextstate; /* the state to goto after stopping */ struct curl_slist *quote_item; /* for the quote option */ diff --git a/lib/vssh/vssh.c b/lib/vssh/vssh.c index 9c82d0a884..ac02171829 100644 --- a/lib/vssh/vssh.c +++ b/lib/vssh/vssh.c @@ -32,6 +32,7 @@ #include "escape.h" #include "select.h" /* for Curl_pollset_change() */ #include "url.h" /* for Curl_conn_meta_get() */ +#include "curlx/fopen.h" #ifdef CURLVERBOSE const char *Curl_ssh_statename(sshstate state) @@ -361,4 +362,87 @@ CURLcode Curl_ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps) return CURLE_OK; } +CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc) +{ + char *home = NULL; + if(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) { + sshc->pub_key = sshc->priv_key = NULL; + + if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + sshc->priv_key = curlx_strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + if(!sshc->priv_key) + goto fail; + } + else { + /* To ponder about: should really the lib be messing about with the HOME + environment variable etc? */ + curlx_struct_stat sbuf; + home = curl_getenv("HOME"); + + /* If no private key file is specified, try some common paths. */ + if(home) { + /* Try ~/.ssh first. */ + sshc->priv_key = curl_maprintf("%s/.ssh/id_rsa", home); + if(!sshc->priv_key) + goto fail; + else if(curlx_stat(sshc->priv_key, &sbuf)) { + curlx_free(sshc->priv_key); + sshc->priv_key = curl_maprintf("%s/.ssh/id_dsa", home); + if(!sshc->priv_key) + goto fail; + else if(curlx_stat(sshc->priv_key, &sbuf)) { + curlx_safefree(sshc->priv_key); + } + } + curlx_safefree(home); + } + if(!sshc->priv_key) { + /* Nothing found; try the current dir. */ + sshc->priv_key = curlx_strdup("id_rsa"); + if(sshc->priv_key && curlx_stat(sshc->priv_key, &sbuf)) { + curlx_free(sshc->priv_key); + sshc->priv_key = curlx_strdup("id_dsa"); + if(sshc->priv_key && curlx_stat(sshc->priv_key, &sbuf)) { + curlx_free(sshc->priv_key); + /* Out of guesses. Set to the empty string to avoid + * surprising info messages. */ + sshc->priv_key = curlx_strdup(""); + } + } + } + } + + /* + * Unless the user explicitly specifies a public key file, let the SSH + * library extract the public key from the private key file. This is done + * by passing sshc->pub_key = NULL. + */ + if(data->set.str[STRING_SSH_PUBLIC_KEY] && + /* treat empty string the same way as NULL */ + data->set.str[STRING_SSH_PUBLIC_KEY][0]) { + sshc->pub_key = curlx_strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + if(!sshc->pub_key) + goto fail; + } + + sshc->passphrase = data->set.ssl.primary.key_passwd; + if(!sshc->passphrase) + sshc->passphrase = ""; + + if(sshc->pub_key) + infof(data, "SSH: public key file '%s'", sshc->pub_key); + if(sshc->priv_key) + infof(data, "SSH: private key file '%s'", sshc->priv_key); + else + infof(data, "SSH: public key auth without private key set!"); + } + return CURLE_OK; + +fail: + curlx_safefree(home); + curlx_safefree(sshc->priv_key); + curlx_safefree(sshc->pub_key); + return CURLE_OUT_OF_MEMORY; +} + #endif /* USE_SSH */ diff --git a/lib/vssh/vssh.h b/lib/vssh/vssh.h index 89fe41e0ae..4c505c453a 100644 --- a/lib/vssh/vssh.h +++ b/lib/vssh/vssh.h @@ -29,6 +29,8 @@ #include "urldata.h" +struct ssh_conn; + CURLcode Curl_getworkingpath(struct Curl_easy *data, const char *homedir, char **path); @@ -40,5 +42,7 @@ CURLcode Curl_ssh_range(struct Curl_easy *data, curl_off_t *startp, curl_off_t *sizep); CURLcode Curl_ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps); +CURLcode Curl_ssh_setup_pkey(struct Curl_easy *data, struct ssh_conn *sshc); + #endif /* USE_SSH */ #endif /* HEADER_CURL_VSSH_VSSH_H */