]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
vssh: keyfile use cleanups
authorDaniel Stenberg <daniel@haxx.se>
Thu, 2 Jul 2026 07:07:05 +0000 (09:07 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 2 Jul 2026 10:37:20 +0000 (12:37 +0200)
- 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
lib/vssh/libssh.c
lib/vssh/libssh2.c
lib/vssh/ssh.h
lib/vssh/vssh.c
lib/vssh/vssh.h

index a7e7b4d06d44b588bc075ac7dc3a80f3ee2616e1..164aef797a2b234408b0a0c52aa31b4d4ff254e3 100644 (file)
--- 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
 
index 93be7ca21f4a9a5538b71f7db97aab8e589f73ac..ea21e19289cfc39fdfc2969d5ac114d839e8b3d3 100644 (file)
@@ -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;
index a3d0adf3b2840ed67e0819bcdf564b8485b0e2b1..5f1353303359b50bd8c435fcd81ec64a8af7ef83 100644 (file)
@@ -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;
index de76c8a253d0816b16f638163dfae2adb8935858..a299c3c4764cf21bbd2e3a17706cd99d04bce528 100644 (file)
@@ -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 */
index 9c82d0a88465d75f6219b6df03179f40717b9550..ac021718290788476150ec458f22f7ff52a00846 100644 (file)
@@ -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 */
index 89fe41e0ae6e2fc7666e6dce9f0d7903d1ced9dc..4c505c453a3ed541ba09c67347abbec6fbbf68ef 100644 (file)
@@ -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 */