Skip to content

Commit

Permalink
ssh: improve key file search
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jjlin authored and bagder committed Oct 3, 2014
1 parent b1c4c39 commit fa7d04f
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 36 deletions.
26 changes: 18 additions & 8 deletions docs/MANUAL
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down
8 changes: 7 additions & 1 deletion docs/curl.1
Original file line number Diff line number Diff line change
Expand Up @@ -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>"
Expand Down Expand Up @@ -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
Expand Down
75 changes: 48 additions & 27 deletions lib/ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,42 +786,63 @@ 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;

/* To ponder about: should really the lib be messing about with the
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) {

This comment has been minimized.

Copy link
@gvanem

gvanem Oct 14, 2014

Contributor

R_OK is not present in Visual-C's header <io.h>. Hence this patch would be appropriate:

@@ -94,6 +94,9 @@
 #ifdef WIN32
 #  undef  PATH_MAX
 #  define PATH_MAX MAX_PATH
+#  ifndef R_OK
+#    define R_OK 4
+#  endif
 #endif

 #ifndef PATH_MAX

Works here.

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;
Expand All @@ -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);
}
Expand Down

0 comments on commit fa7d04f

Please sign in to comment.