]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
ssh: improve key file search
authorJeremy Lin <jjlin@cs.stanford.edu>
Tue, 16 Sep 2014 04:16:46 +0000 (21:16 -0700)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 3 Oct 2014 14:20:54 +0000 (16:20 +0200)
For private keys, use the first match from: user-specified key file
(if provided), ~/.ssh/id_rsa, ~/.ssh/id_dsa, ./id_rsa, ./id_dsa

Note that the previous code only looked for id_dsa files. id_rsa is
now generally preferred, as it supports larger key sizes.

For public keys, use the user-specified key file, if provided.
Otherwise, try to extract the public key from the private key file.
This means that passing --pubkey is typically no longer required,
and makes the key-handling behavior more like OpenSSH.

docs/MANUAL
docs/curl.1
lib/ssh.c

index 06b3abee5e52db7c5c2a1b832a7b71dfedcd6199..18fecf6c5f5cefa37a14aa88cba9f08d6def8eac 100644 (file)
@@ -41,12 +41,19 @@ SIMPLE USAGE
 
   Get a file from an SSH server using SFTP:
 
-        curl -u username sftp://shell.example.com/etc/issue
+        curl -u username sftp://example.com/etc/issue
 
-  Get a file from an SSH server using SCP using a private key to authenticate:
+  Get a file from an SSH server using SCP using a private key
+  (not password-protected) to authenticate:
 
-        curl -u username: --key ~/.ssh/id_dsa --pubkey ~/.ssh/id_dsa.pub \
-            scp://shell.example.com/~/personal.txt
+        curl -u username: --key ~/.ssh/id_rsa \
+             scp://example.com/~/file.txt
+
+  Get a file from an SSH server using SCP using a private key
+  (password-protected) to authenticate:
+
+        curl -u username: --key ~/.ssh/id_rsa --pass private_key_password \
+             scp://example.com/~/file.txt
 
   Get the main page from an IPv6 web server:
 
@@ -91,10 +98,13 @@ USING PASSWORDS
 
  SFTP / SCP
 
-   This is similar to FTP, but you can specify a private key to use instead of
-   a password. Note that the private key may itself be protected by a password
-   that is unrelated to the login password of the remote system.  If you
-   provide a private key file you must also provide a public key file.
+   This is similar to FTP, but you can use the --key option to specify a
+   private key to use instead of a password. Note that the private key may
+   itself be protected by a password that is unrelated to the login password
+   of the remote system; this password is specified using the --pass option.
+   Typically, curl will automatically extract the public key from the private
+   key file, but in cases where curl does not have the proper library support,
+   a matching public key file must be specified using the --pubkey option.
 
  HTTP
 
index e52a7e4dde40df366744a7510458e2fd96e5d87e..4d97227af313647d366e19b16dac6df935500fe1 100644 (file)
@@ -825,7 +825,8 @@ If this option is used several times, the last one will be used. If
 unspecified, the option defaults to 60 seconds.
 .IP "--key <key>"
 (SSL/SSH) Private key file name. Allows you to provide your private key in this
-separate file.
+separate file. For SSH, if not specified, curl tries the following candidates
+in order: '~/.ssh/id_rsa', '~/.ssh/id_dsa', './id_rsa', './id_dsa'.
 
 If this option is used several times, the last one will be used.
 .IP "--key-type <type>"
@@ -1283,6 +1284,11 @@ protocol instead of the default HTTP 1.1.
 separate file.
 
 If this option is used several times, the last one will be used.
+
+(As of 7.39.0, curl attempts to automatically extract the public key from the
+private key file, so passing this option is generally not required. Note that
+this public key extraction requires libcurl to be linked against a copy of
+libssh2 1.2.8 or higher that is itself linked against OpenSSL.)
 .IP "-q"
 If used as the first parameter on the command line, the \fIcurlrc\fP config
 file will not be read and used. See the \fI-K, --config\fP for details on the
index 887e10f21ca9c9d9e46bbd656c5847bc76328c5d..76389a0363971126b3a5d0b589be24a5c7f18f50 100644 (file)
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -786,7 +786,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
       if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
          (strstr(sshc->authlist, "publickey") != NULL)) {
         char *home = NULL;
-        bool rsa_pub_empty_but_ok = FALSE;
+        bool out_of_memory = FALSE;
 
         sshc->rsa_pub = sshc->rsa = NULL;
 
@@ -794,34 +794,55 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
            HOME environment variable etc? */
         home = curl_getenv("HOME");
 
-        if(data->set.str[STRING_SSH_PUBLIC_KEY] &&
-           !*data->set.str[STRING_SSH_PUBLIC_KEY])
-           rsa_pub_empty_but_ok = true;
-        else if(data->set.str[STRING_SSH_PUBLIC_KEY])
-          sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]);
-        else if(home)
-          sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home);
-        else
-          /* as a final resort, try current dir! */
-          sshc->rsa_pub = strdup("id_dsa.pub");
-
-        if(!rsa_pub_empty_but_ok && (sshc->rsa_pub == NULL)) {
-          Curl_safefree(home);
-          state(conn, SSH_SESSION_FREE);
-          sshc->actualcode = CURLE_OUT_OF_MEMORY;
-          break;
+        if(data->set.str[STRING_SSH_PRIVATE_KEY])
+          sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]);
+        else {
+          /* If no private key file is specified, try some common paths. */
+          if(home) {
+            /* Try ~/.ssh first. */
+            sshc->rsa = aprintf("%s/.ssh/id_rsa", home);
+            if(!sshc->rsa)
+              out_of_memory = TRUE;
+            else if(access(sshc->rsa, R_OK) != 0) {
+              Curl_safefree(sshc->rsa);
+              sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
+              if(!sshc->rsa)
+                out_of_memory = TRUE;
+              else if(access(sshc->rsa, R_OK) != 0) {
+                Curl_safefree(sshc->rsa);
+              }
+            }
+          }
+          if(!out_of_memory && !sshc->rsa) {
+            /* Nothing found; try the current dir. */
+            sshc->rsa = strdup("id_rsa");
+            if(sshc->rsa && access(sshc->rsa, R_OK) != 0) {
+              Curl_safefree(sshc->rsa);
+              sshc->rsa = strdup("id_dsa");
+              if(sshc->rsa && access(sshc->rsa, R_OK) != 0) {
+                Curl_safefree(sshc->rsa);
+                /* Out of guesses. Set to the empty string to avoid
+                 * surprising info messages. */
+                sshc->rsa = strdup("");
+              }
+            }
+          }
         }
 
-        if(data->set.str[STRING_SSH_PRIVATE_KEY])
-          sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
-        else if(home)
-          sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
-        else
-          /* as a final resort, try current dir! */
-          sshc->rsa = strdup("id_dsa");
+        /*
+         * Unless the user explicitly specifies a public key file, let
+         * libssh2 extract the public key from the private key file.
+         * This is done by simply passing sshc->rsa_pub = NULL.
+         */
+        if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
+          sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]);
+          if(!sshc->rsa_pub)
+            out_of_memory = TRUE;
+        }
 
-        if(sshc->rsa == NULL) {
+        if(out_of_memory || sshc->rsa == NULL) {
           Curl_safefree(home);
+          Curl_safefree(sshc->rsa);
           Curl_safefree(sshc->rsa_pub);
           state(conn, SSH_SESSION_FREE);
           sshc->actualcode = CURLE_OUT_OF_MEMORY;
@@ -834,8 +855,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
 
         Curl_safefree(home);
 
-        infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
-        infof(data, "Using ssh private key file %s\n", sshc->rsa);
+        infof(data, "Using SSH public key file '%s'\n", sshc->rsa_pub);
+        infof(data, "Using SSH private key file '%s'\n", sshc->rsa);
 
         state(conn, SSH_AUTH_PKEY);
       }