Commit fa7d04fe authored by Jeremy Lin's avatar Jeremy Lin Committed by Daniel Stenberg
Browse files

ssh: improve key file search

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.
parent b1c4c39c
Loading
Loading
Loading
Loading
+18 −8
Original line number Original line Diff line number Diff line
@@ -41,12 +41,19 @@ SIMPLE USAGE


  Get a file from an SSH server using SFTP:
  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 \
        curl -u username: --key ~/.ssh/id_rsa \
            scp://shell.example.com/~/personal.txt
             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:
  Get the main page from an IPv6 web server:


@@ -91,10 +98,13 @@ USING PASSWORDS


 SFTP / SCP
 SFTP / SCP


   This is similar to FTP, but you can specify a private key to use instead of
   This is similar to FTP, but you can use the --key option to specify a
   a password. Note that the private key may itself be protected by a password
   private key to use instead of a password. Note that the private key may
   that is unrelated to the login password of the remote system.  If you
   itself be protected by a password that is unrelated to the login password
   provide a private key file you must also provide a public key file.
   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
 HTTP


+7 −1
Original line number Original line Diff line number Diff line
@@ -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.
unspecified, the option defaults to 60 seconds.
.IP "--key <key>"
.IP "--key <key>"
(SSL/SSH) Private key file name. Allows you to provide your private key in this
(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.
If this option is used several times, the last one will be used.
.IP "--key-type <type>"
.IP "--key-type <type>"
@@ -1283,6 +1284,11 @@ protocol instead of the default HTTP 1.1.
separate file.
separate file.


If this option is used several times, the last one will be used.
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"
.IP "-q"
If used as the first parameter on the command line, the \fIcurlrc\fP config
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
file will not be read and used. See the \fI-K, --config\fP for details on the
+48 −27
Original line number Original line Diff line number Diff line
@@ -786,7 +786,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
      if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
      if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
         (strstr(sshc->authlist, "publickey") != NULL)) {
         (strstr(sshc->authlist, "publickey") != NULL)) {
        char *home = NULL;
        char *home = NULL;
        bool rsa_pub_empty_but_ok = FALSE;
        bool out_of_memory = FALSE;


        sshc->rsa_pub = sshc->rsa = NULL;
        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 environment variable etc? */
        home = curl_getenv("HOME");
        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])
        if(data->set.str[STRING_SSH_PRIVATE_KEY])
          sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
          sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]);
        else if(home)
        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);
              sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
        else
              if(!sshc->rsa)
          /* as a final resort, try current dir! */
                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");
              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("");
              }
            }
          }
        }

        /*
         * 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(home);
          Curl_safefree(sshc->rsa);
          Curl_safefree(sshc->rsa_pub);
          Curl_safefree(sshc->rsa_pub);
          state(conn, SSH_SESSION_FREE);
          state(conn, SSH_SESSION_FREE);
          sshc->actualcode = CURLE_OUT_OF_MEMORY;
          sshc->actualcode = CURLE_OUT_OF_MEMORY;
@@ -834,8 +855,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)


        Curl_safefree(home);
        Curl_safefree(home);


        infof(data, "Using ssh public key file %s\n", sshc->rsa_pub);
        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 private key file '%s'\n", sshc->rsa);


        state(conn, SSH_AUTH_PKEY);
        state(conn, SSH_AUTH_PKEY);
      }
      }