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 Diff line number Diff line
@@ -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

+7 −1
Original line number 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.
.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
+48 −27
Original line number 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) &&
         (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 = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]);
        else if(home)
          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);
        else
          /* as a final resort, try current dir! */
              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("");
              }
            }
          }
        }

        /*
         * 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);
      }