1
0
mirror of https://github.com/moparisthebest/curl synced 2025-03-11 07:39:50 -04:00

James Housley brought support for SCP transfers

This commit is contained in:
Daniel Stenberg 2006-11-02 21:56:40 +00:00
parent 7f1870da5f
commit 2147284cad
14 changed files with 768 additions and 11 deletions

View File

@ -6,6 +6,10 @@
Changelog Changelog
Daniel (2 November 2006)
- James Housley brought support for SCP transfers, based on the libssh2 library
for the actual network protocol stuff.
Version 7.16.0 (30 October 2006) Version 7.16.0 (30 October 2006)
Daniel (25 October 2006) Daniel (25 October 2006)

View File

@ -11,7 +11,7 @@ Curl and libcurl 7.16.1
This release includes the following changes: This release includes the following changes:
o o Support for SCP added
This release includes the following bugfixes: This release includes the following bugfixes:
@ -28,6 +28,6 @@ New curl mirrors:
This release would not have looked like this without help, code, reports and This release would not have looked like this without help, code, reports and
advice from friends like these: advice from friends like these:
James Housley
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@ -78,6 +78,7 @@ AC_SUBST(PKGADD_VENDOR)
dnl dnl
dnl initialize all the info variables dnl initialize all the info variables
curl_ssl_msg="no (--with-ssl / --with-gnutls)" curl_ssl_msg="no (--with-ssl / --with-gnutls)"
curl_ssh_msg="no (--with-libssh2)"
curl_zlib_msg="no (--with-zlib)" curl_zlib_msg="no (--with-zlib)"
curl_krb4_msg="no (--with-krb4*)" curl_krb4_msg="no (--with-krb4*)"
curl_gss_msg="no (--with-gssapi)" curl_gss_msg="no (--with-gssapi)"
@ -1043,6 +1044,72 @@ if test X"$OPT_SSL" != Xno; then
fi fi
dnl **********************************************************************
dnl Check for the presence of LIBSSH2 libraries and headers
dnl **********************************************************************
dnl Default to compiler & linker defaults for LIBSSH2 files & libraries.
OPT_LIBSSH2=off
AC_ARG_WITH(libssh2,dnl
AC_HELP_STRING([--with-libssh2=PATH],[Where to look for libssh2, PATH points to the LIBSSH2 installation (default: /usr/local/lib); when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
AC_HELP_STRING([--without-libssh2], [disable LIBSSH2]),
OPT_LIBSSH2=$withval)
if test X"$OPT_LIBSSH2" != Xno; then
dnl backup the pre-libssh2 variables
CLEANLDFLAGS="$LDFLAGS"
CLEANCPPFLAGS="$CPPFLAGS"
CLEANLIBS="$LIBS"
case "$OPT_LIBSSH2" in
yes)
dnl --with-libssh2 (without path) used
PREFIX_LIBSSH2=/usr/local/lib
LIB_LIBSSH2="$PREFIX_LIBSSH2/lib$libsuff"
;;
off)
dnl no --with-libssh2 option given, just check default places
PREFIX_LIBSSH2=
;;
*)
dnl check the given --with-libssh2 spot
PREFIX_LIBSSH2=$OPT_LIBSSH2
LIB_LIBSSH2="$PREFIX_LIBSSH2/lib$libsuff"
LDFLAGS="$LDFLAGS -L$LIB_LIBSSH2"
CPPFLAGS="$CPPFLAGS -I$PREFIX_LIBSSH2/include/libssh2 -I$PREFIX_LIBSSH2/include"
;;
esac
if test X"$HAVECRYPTO" = X"yes"; then
dnl This is only reasonable to do if crypto actually is there: check for
dnl LIBSSH2 libs NOTE: it is important to do this AFTER the crypto lib
AC_CHECK_LIB(ssh2, libssh2_channel_open_ex)
AC_CHECK_HEADERS(libssh2.h,
curl_ssh_msg="enabled (libSSH2)"
LIBSSH2_ENABLED=1
AC_DEFINE(USE_LIBSSH2, 1, [if libSSH2 is in use]))
if test X"OPT_LIBSSH2" != Xoff &&
test "$LIBSSH2_ENABLED" != "1"; then
AC_MSG_ERROR([libSSH2 libs and/or directories were not found where specified!])
fi
fi
if test "$LIBSSH2_ENABLED" = "1"; then
if test -n "$LIB_LIBSSH2"; then
dnl when the libssh2 shared libs were found in a path that the run-time
dnl linker doesn't search through, we need to add it to LD_LIBRARY_PATH
dnl to prevent further configure tests to fail due to this
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$LIB_LIBSSH2"
export LD_LIBRARY_PATH
AC_MSG_NOTICE([Added $LIB_LIBSSH2 to LD_LIBRARY_PATH])
fi
fi
fi
dnl ********************************************************************** dnl **********************************************************************
dnl Check for the random seed preferences dnl Check for the random seed preferences
dnl ********************************************************************** dnl **********************************************************************
@ -2076,6 +2143,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
Install prefix: ${prefix} Install prefix: ${prefix}
Compiler: ${CC} Compiler: ${CC}
SSL support: ${curl_ssl_msg} SSL support: ${curl_ssl_msg}
SSH support: ${curl_ssh_msg}
zlib support: ${curl_zlib_msg} zlib support: ${curl_zlib_msg}
krb4 support: ${curl_krb4_msg} krb4 support: ${curl_krb4_msg}
GSSAPI support: ${curl_gss_msg} GSSAPI support: ${curl_gss_msg}

View File

@ -21,7 +21,7 @@
.\" * $Id$ .\" * $Id$
.\" ************************************************************************** .\" **************************************************************************
.\" .\"
.TH curl_version_info 3 "19 Apr 2006" "libcurl 7.15.4" "libcurl Manual" .TH curl_version_info 3 "2 Nov 2006" "libcurl 7.16.1" "libcurl Manual"
.SH NAME .SH NAME
curl_version_info - returns run-time libcurl version info curl_version_info - returns run-time libcurl version info
.SH SYNOPSIS .SH SYNOPSIS
@ -66,6 +66,11 @@ typedef struct {
/* when 'age' is 2 or higher, the member below also exists: */ /* when 'age' is 2 or higher, the member below also exists: */
const char *libidn; /* human readable string */ const char *libidn; /* human readable string */
/* when 'age' is 3 or higher, the members below also exist: */
int iconv_ver_num; /* '_libiconv_version' if iconv support enabled */
const char *libssh_version; /* human readable string */
} curl_version_info_data; } curl_version_info_data;
.fi .fi

View File

@ -392,6 +392,11 @@ typedef enum {
CURLOPT_CONV_FROM_UTF8_FUNCTION */ CURLOPT_CONV_FROM_UTF8_FUNCTION */
CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing
or wrong format */ or wrong format */
CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */
CURLE_SSH, /* 79 - error from the SSH layer, somewhat
generic so the error message will be of
interest when this has happened */
CURL_LAST /* never use! */ CURL_LAST /* never use! */
} CURLcode; } CURLcode;
@ -427,6 +432,14 @@ typedef enum {
#define CURLAUTH_ANY ~0 /* all types set */ #define CURLAUTH_ANY ~0 /* all types set */
#define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC) #define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC)
#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */
#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */
#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */
#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */
#define CURLSSH_AUTH_HOST (1<<2) /* host key files */
#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */
#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY
#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
the obsolete stuff removed! */ the obsolete stuff removed! */
/* this was the error code 50 in 7.7.3 and a few earlier versions, this /* this was the error code 50 in 7.7.3 and a few earlier versions, this
@ -1029,6 +1042,13 @@ typedef enum {
enabled (== 1) */ enabled (== 1) */
CINIT(SSL_SESSIONID_CACHE, LONG, 150), CINIT(SSL_SESSIONID_CACHE, LONG, 150),
/* allowed SSH authentication methods */
CINIT(SSH_AUTH_TYPES, LONG, 151),
/* Used by scp/sftp to do public/private key authentication */
CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152),
CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;
@ -1506,6 +1526,7 @@ typedef enum {
CURLVERSION_FIRST, CURLVERSION_FIRST,
CURLVERSION_SECOND, CURLVERSION_SECOND,
CURLVERSION_THIRD, CURLVERSION_THIRD,
CURLVERSION_FOURTH,
CURLVERSION_LAST /* never actually use this */ CURLVERSION_LAST /* never actually use this */
} CURLversion; } CURLversion;
@ -1514,7 +1535,7 @@ typedef enum {
meant to be a built-in version number for what kind of struct the caller meant to be a built-in version number for what kind of struct the caller
expects. If the struct ever changes, we redefine the NOW to another enum expects. If the struct ever changes, we redefine the NOW to another enum
from above. */ from above. */
#define CURLVERSION_NOW CURLVERSION_THIRD #define CURLVERSION_NOW CURLVERSION_FOURTH
typedef struct { typedef struct {
CURLversion age; /* age of the returned struct */ CURLversion age; /* age of the returned struct */
@ -1535,8 +1556,13 @@ typedef struct {
/* This field was added in CURLVERSION_THIRD */ /* This field was added in CURLVERSION_THIRD */
const char *libidn; const char *libidn;
/* These field were added in CURLVERSION_FOURTH */
/* Same as '_libiconv_version' if built with HAVE_ICONV */ /* Same as '_libiconv_version' if built with HAVE_ICONV */
int iconv_ver_num; int iconv_ver_num;
const char *libssh_version; /* human readable string */
} curl_version_info_data; } curl_version_info_data;
#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ #define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */

View File

@ -8,7 +8,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
content_encoding.c share.c http_digest.c md5.c http_negotiate.c \ content_encoding.c share.c http_digest.c md5.c http_negotiate.c \
http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \ http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \
hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \ hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \
select.c gtls.c sslgen.c tftp.c splay.c strdup.c socks.c select.c gtls.c sslgen.c tftp.c splay.c strdup.c socks.c ssh.c
HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \
@ -18,6 +18,6 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \
share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \ share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \
inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \ inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \
setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \ setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \
gtls.h tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h gtls.h tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h

View File

@ -715,6 +715,9 @@ void curl_easy_reset(CURL *curl)
/* This is our prefered CA cert bundle since install time */ /* This is our prefered CA cert bundle since install time */
data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE; data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE;
#endif #endif
data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
type */
} }
#ifdef CURL_DOES_CONVERSIONS #ifdef CURL_DOES_CONVERSIONS

View File

@ -45,6 +45,7 @@
#include "sendf.h" #include "sendf.h"
#include "connect.h" /* for the Curl_sockerrno() proto */ #include "connect.h" /* for the Curl_sockerrno() proto */
#include "sslgen.h" #include "sslgen.h"
#include "ssh.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */ #define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -326,9 +327,15 @@ CURLcode Curl_write(struct connectdata *conn,
CURLcode retcode; CURLcode retcode;
int num = (sockfd == conn->sock[SECONDARYSOCKET]); int num = (sockfd == conn->sock[SECONDARYSOCKET]);
if (conn->ssl[num].use) if (conn->ssl[num].use) {
/* only TRUE if SSL enabled */ /* only TRUE if SSL enabled */
bytes_written = Curl_ssl_send(conn, num, mem, len); bytes_written = Curl_ssl_send(conn, num, mem, len);
}
#ifdef USE_LIBSSH2
else if (conn->protocol & PROT_SCP) {
bytes_written = Curl_scp_send(conn, num, mem, len);
}
#endif /* !USE_LIBSSH2 */
else { else {
if(conn->sec_complete) if(conn->sec_complete)
/* only TRUE if krb4 enabled */ /* only TRUE if krb4 enabled */
@ -499,9 +506,15 @@ int Curl_read(struct connectdata *conn, /* connection data */
if(conn->ssl[num].use) { if(conn->ssl[num].use) {
nread = Curl_ssl_recv(conn, num, conn->master_buffer, bytesfromsocket); nread = Curl_ssl_recv(conn, num, conn->master_buffer, bytesfromsocket);
if(nread == -1) if(nread == -1) {
return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */ return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */
}
} }
#ifdef USE_LIBSSH2
else if (conn->protocol & PROT_SCP) {
nread = Curl_scp_recv(conn, num, conn->master_buffer, bytesfromsocket);
}
#endif /* !USE_LIBSSH2 */
else { else {
if(conn->sec_complete) if(conn->sec_complete)
nread = Curl_sec_read(conn, sockfd, conn->master_buffer, nread = Curl_sec_read(conn, sockfd, conn->master_buffer,

501
lib/ssh.c Normal file
View File

@ -0,0 +1,501 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#define CURL_LIBSSH2_DEBUG
#include "setup.h"
#ifdef USE_LIBSSH2
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <libssh2.h>
#include <libssh2_sftp.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#else /* probably some kind of unix */
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_UTSNAME_H
#include <sys/utsname.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef VMS
#include <in.h>
#include <inet.h>
#endif
#endif
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
#undef in_addr_t
#define in_addr_t unsigned long
#endif
#include <curl/curl.h>
#include "urldata.h"
#include "sendf.h"
#include "easyif.h" /* for Curl_convert_... prototypes */
#include "if2ip.h"
#include "hostip.h"
#include "progress.h"
#include "transfer.h"
#include "escape.h"
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "ssh.h"
#include "url.h"
#include "speedcheck.h"
#include "getinfo.h"
#include "strtoofft.h"
#include "strequal.h"
#include "sslgen.h"
#include "connect.h"
#include "strerror.h"
#include "memory.h"
#include "inet_ntop.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "multiif.h"
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
#include "inet_ntoa_r.h"
#endif
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
#define DIRSEP '\\'
#else
#define DIRSEP '/'
#endif
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* The last #include file should be: */
#ifdef CURLDEBUG
#include "memdebug.h"
#endif
static LIBSSH2_ALLOC_FUNC(libssh2_malloc);
static LIBSSH2_REALLOC_FUNC(libssh2_realloc);
static LIBSSH2_FREE_FUNC(libssh2_free);
struct auth_
{
const char * user;
const char * pw;
} auth;
static void
kbd_callback(const char *name, int name_len, const char *instruction,
int instruction_len, int num_prompts,
const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
void **abstract)
{
#ifdef CURL_LIBSSH2_DEBUG
fprintf(stderr, "name=%s\n", name);
fprintf(stderr, "name_len=%d\n", name_len);
fprintf(stderr, "instruction=%s\n", instruction);
fprintf(stderr, "instruction_len=%d\n", instruction_len);
fprintf(stderr, "num_prompts=%d\n", num_prompts);
#endif /* CURL_LIBSSH2_DEBUG */
if (num_prompts == 1) {
responses[0].text = strdup(auth.pw);
responses[0].length = strlen(auth.pw);
}
(void)prompts;
(void)abstract;
return;
} /* kbd_callback */
static CURLcode libssh2_error_to_CURLE(struct connectdata *conn)
{
int errorcode;
struct SCPPROTO *scp = conn->data->reqdata.proto.scp;
/* Get the libssh2 error code and string */
errorcode = libssh2_session_last_error(scp->scpSession, &scp->errorstr, NULL,
0);
if (errorcode == LIBSSH2_FX_OK)
return CURLE_OK;
infof(conn->data, "libssh2 error %d, '%s'\n", errorcode, scp->errorstr);
/* TODO: map some of the libssh2 errors to the more appropriate CURLcode
error code, and possibly add a few new SSH-related one. We must however
not return or even depend on libssh2 errors in the public libcurl API */
return CURLE_SSH;
}
static LIBSSH2_ALLOC_FUNC(libssh2_malloc)
{
return malloc(count);
(void)abstract;
}
static LIBSSH2_REALLOC_FUNC(libssh2_realloc)
{
return realloc(ptr, count);
(void)abstract;
}
static LIBSSH2_FREE_FUNC(libssh2_free)
{
free(ptr);
(void)abstract;
}
static CURLcode scp_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct SCPPROTO *scp;
if (data->reqdata.proto.scp)
return CURLE_OK;
scp = (struct SCPPROTO *)calloc(sizeof(struct SCPPROTO), 1);
if (!scp)
return CURLE_OUT_OF_MEMORY;
data->reqdata.proto.scp = scp;
/* get some initial data into the scp struct */
scp->bytecountp = &data->reqdata.keep.bytecount;
/* no need to duplicate them, this connectdata struct won't change */
scp->user = conn->user;
scp->passwd = conn->passwd;
scp->errorstr = NULL;
return CURLE_OK;
}
/*
* Curl_scp_connect() gets called from Curl_protocol_connect() to allow us to
* do protocol-specific actions at connect-time.
*/
CURLcode Curl_scp_connect(struct connectdata *conn, bool *done)
{
int i;
struct SCPPROTO *scp;
const char *fingerprint;
const char *authlist;
char *home;
char rsa_pub[PATH_MAX];
char rsa[PATH_MAX];
curl_socket_t sock;
char *real_path;
char *working_path;
bool authed = FALSE;
CURLcode result;
struct SessionHandle *data = conn->data;
result = scp_init(conn);
if (result)
return result;
rsa_pub[0] = rsa[0] = '\0';
scp = data->reqdata.proto.scp;
working_path = curl_easy_unescape(data, data->reqdata.path, 0, NULL);
if (!working_path)
return CURLE_OUT_OF_MEMORY;
real_path = (char *)malloc(strlen(working_path)+1);
if (real_path == NULL) {
Curl_safefree(working_path);
return CURLE_OUT_OF_MEMORY;
}
/* Check for /~/ , indicating realative to the users home directory */
if (working_path[1] == '~')
/* It is referenced to the home directory, so strip the leading '/' */
memcpy(real_path, working_path+1, 1+strlen(working_path)-1);
else
memcpy(real_path, working_path, 1+strlen(working_path));
Curl_safefree(working_path);
scp->path = real_path;
#ifdef CURL_LIBSSH2_DEBUG
if (scp->user) {
infof(data, "User: %s\n", scp->user);
}
if (scp->passwd) {
infof(data, "Password: %s\n", scp->passwd);
}
#endif /* CURL_LIBSSH2_DEBUG */
sock = conn->sock[FIRSTSOCKET];
scp->scpSession = libssh2_session_init_ex(libssh2_malloc, libssh2_free,
libssh2_realloc, NULL);
if (scp->scpSession == NULL) {
failf(data, "Failure initialising ssh session\n");
return CURLE_FAILED_INIT;
}
#ifdef CURL_LIBSSH2_DEBUG
infof(data, "Socket: %d\n", sock);
#endif /* CURL_LIBSSH2_DEBUG */
if (libssh2_session_startup(scp->scpSession, sock)) {
failf(data, "Failure establishing ssh session\n");
return CURLE_FAILED_INIT;
}
/*
* Before we authenticate we should check the hostkey's fingerprint against
* our known hosts. How that is handled (reading from file, whatever) is
* up to us. As for know not much is implemented, besides showing how to
* get the fingerprint.
*/
fingerprint = libssh2_hostkey_hash(scp->scpSession,
LIBSSH2_HOSTKEY_HASH_MD5);
#ifdef CURL_LIBSSH2_DEBUG
/* The fingerprint points to static storage (!), don't free() it. */
for (i = 0; i < 16; i++) {
infof(data, "%02X ", (unsigned char) fingerprint[i]);
}
infof(data, "\n");
#endif /* CURL_LIBSSH2_DEBUG */
/* TBD - methods to check the host keys need to be done */
/*
* Figure out authentication methods
* NB: As soon as we have provided a username to an openssh server we must
* never change it later. Thus, always specify the correct username here,
* even though the libssh2 docs kind of indicate that it should be possible
* to get a 'generic' list (not user-specific) of authentication methods,
* presumably with a blank username. That won't work in my experience.
* So always specify it here.
*/
authlist = libssh2_userauth_list(scp->scpSession, scp->user,
strlen(scp->user));
/*
* Check the supported auth types in the order I feel is most secure with the
* requested type of authentication
*/
if ((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) &&
(strstr(authlist, "publickey") != NULL)) {
/* To ponder about: should really the lib be messing about with the HOME
environment variable etc? */
home = curl_getenv("HOME");
if (data->set.ssh_public_key)
snprintf(rsa_pub, sizeof(rsa_pub), "%s", data->set.ssh_public_key);
else if(home)
snprintf(rsa_pub, sizeof(rsa_pub), "%s/.ssh/id_dsa.pub", home);
if(data->set.ssh_private_key)
snprintf(rsa, sizeof(rsa), "%s", data->set.ssh_private_key);
else if(home) {
snprintf(rsa, sizeof(rsa), "%s/.ssh/id_dsa", home);
}
curl_free(home);
if (rsa_pub[0]) {
/* The function below checks if the files exists, no need to stat() here.
*/
if (libssh2_userauth_publickey_fromfile(scp->scpSession, scp->user,
rsa_pub, rsa, "") == 0) {
authed = TRUE;
}
}
}
if (!authed &&
(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
(strstr(authlist, "password") != NULL)) {
if (libssh2_userauth_password(scp->scpSession, scp->user, scp->passwd)
== 0) {
authed = TRUE;
}
}
if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
(strstr(authlist, "hostbased") != NULL)) {
}
if (!authed && (data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
&& (strstr(authlist, "keyboard-interactive") != NULL)) {
/* Authentication failed. Continue with keyboard-interactive now. */
auth.user = scp->user;
auth.pw = scp->passwd;
if (libssh2_userauth_keyboard_interactive_ex(scp->scpSession, scp->user,
strlen(scp->user),
&kbd_callback) == 0) {
authed = TRUE;
}
}
if (!authed) {
failf(data, "Authentication failure\n");
return CURLE_FAILED_INIT;
}
/*
* At this point we have an authenticated ssh session.
*/
conn->sockfd = sock;
conn->writesockfd = CURL_SOCKET_BAD;
*done = TRUE;
return CURLE_OK;
}
CURLcode Curl_scp_do(struct connectdata *conn, bool *done)
{
struct stat sb;
struct SCPPROTO *scp = conn->data->reqdata.proto.scp;
CURLcode res = CURLE_OK;
*done = TRUE; /* unconditionally */
if (conn->data->set.upload) {
/*
* NOTE!!! libssh2 requires that the destination path is a full path
* that includes the destination file and name OR ends in a "/" .
* If this is not done the destination file will be named the
* same name as the last directory in the path.
*/
scp->scpChannel = libssh2_scp_send_ex(scp->scpSession, scp->path,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
conn->data->set.infilesize, 0, 0);
if (scp->scpChannel == NULL) {
return CURLE_FAILED_INIT;
}
conn->writesockfd = conn->sockfd;
conn->sockfd = CURL_SOCKET_BAD;
}
else {
/*
* We must check the remote file, if it is a directory I have no idea
* what I will do until the scp "-r" option is supported
*/
memset(&sb, 0, sizeof(struct stat));
if ((scp->scpChannel = libssh2_scp_recv(scp->scpSession, scp->path, &sb))
== NULL) {
if ((sb.st_mode == 0) && (sb.st_atime == 0) && (sb.st_mtime == 0) &&
(sb.st_size == 0)) {
/* Since sb is still empty, it is likely the file was not found */
return CURLE_REMOTE_FILE_NOT_FOUND;
}
return libssh2_error_to_CURLE(conn);
}
conn->data->reqdata.size = sb.st_size;
conn->data->reqdata.maxdownload = sb.st_size;
}
return res;
}
CURLcode Curl_scp_done(struct connectdata *conn, CURLcode status)
{
struct SCPPROTO *scp = conn->data->reqdata.proto.scp;
Curl_safefree(scp->freepath);
scp->freepath = NULL;
if (scp->scpChannel) {
if (libssh2_channel_close(scp->scpChannel) < 0) {
failf(conn->data, "Failed to stop libssh2 channel subsystem\n");
}
}
if (scp->scpSession) {
libssh2_session_disconnect(scp->scpSession, "Shutdown");
libssh2_session_free(scp->scpSession);
}
free(conn->data->reqdata.proto.scp);
conn->data->reqdata.proto.scp = NULL;
Curl_pgrsDone(conn);
(void)status; /* unused */
return CURLE_OK;
}
/* return number of received (decrypted) bytes */
int Curl_scp_send(struct connectdata *conn, int sockindex,
void *mem, size_t len)
{
ssize_t nwrite;
nwrite = libssh2_channel_write(conn->data->reqdata.proto.scp->scpChannel,
mem, len);
(void)sockindex;
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
int Curl_scp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
ssize_t nread;
nread = libssh2_channel_read(conn->data->reqdata.proto.scp->scpChannel,
mem, len);
(void)sockindex;
return nread;
}
#endif /* USE_LIBSSH2 */

40
lib/ssh.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef __SFTP_H
#define __SFTP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#ifdef USE_LIBSSH2
CURLcode Curl_scp_connect(struct connectdata *conn, bool *done);
CURLcode Curl_scp_do(struct connectdata *conn, bool *done);
CURLcode Curl_scp_done(struct connectdata *conn, CURLcode);
int Curl_scp_send(struct connectdata *conn, int sockindex,
void *mem, size_t len);
int Curl_scp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len);
#endif
#endif /* USE_LIBSSH2 */

View File

@ -277,6 +277,12 @@ curl_easy_strerror(CURLcode error)
case CURLE_CONV_REQD: case CURLE_CONV_REQD:
return "caller must register CURLOPT_CONV_ callback options"; return "caller must register CURLOPT_CONV_ callback options";
case CURLE_REMOTE_FILE_NOT_FOUND:
return "Remote file not found";
case CURLE_SSH:
return "Error in the SSH layer";
/* error codes not used by current libcurl */ /* error codes not used by current libcurl */
case CURLE_URL_MALFORMAT_USER: case CURLE_URL_MALFORMAT_USER:
case CURLE_FTP_USER_PASSWORD_INCORRECT: case CURLE_FTP_USER_PASSWORD_INCORRECT:

View File

@ -128,6 +128,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#include "http.h" #include "http.h"
#include "file.h" #include "file.h"
#include "ldap.h" #include "ldap.h"
#include "ssh.h"
#include "url.h" #include "url.h"
#include "connect.h" #include "connect.h"
#include "inet_ntop.h" #include "inet_ntop.h"
@ -165,7 +166,7 @@ static void signalPipeClose(struct curl_llist *pipe);
#define MAX_PIPELINE_LENGTH 5 #define MAX_PIPELINE_LENGTH 5
/* /*
* We use this ZERO_NULL to avoid picky compiler warnings, * We use this ZERO_NULL to avoid picky compiler warnings,
* when assigning a NULL pointer to a function pointer var. * when assigning a NULL pointer to a function pointer var.
*/ */
@ -546,6 +547,9 @@ CURLcode Curl_open(struct SessionHandle **curl)
the first call to curl_easy_perform() or when the handle is added to a the first call to curl_easy_perform() or when the handle is added to a
multi stack. */ multi stack. */
data->set.ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth
type */
/* most recent connection is not yet defined */ /* most recent connection is not yet defined */
data->state.lastconnect = -1; data->state.lastconnect = -1;
@ -1673,6 +1677,24 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
data->set.ssl.sessionid = (bool)(0 != va_arg(param, long)); data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
break; break;
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
break;
case CURLOPT_SSH_PUBLIC_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa.pub file
*/
data->set.ssh_public_key = va_arg(param, char *);
break;
case CURLOPT_SSH_PRIVATE_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa file
*/
data->set.ssh_private_key = va_arg(param, char *);
break;
default: default:
/* unknown tag and its companion, just ignore: */ /* unknown tag and its companion, just ignore: */
result = CURLE_FAILED_INIT; /* correct this */ result = CURLE_FAILED_INIT; /* correct this */
@ -3023,7 +3045,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_connecting = Curl_https_connecting; conn->curl_connecting = Curl_https_connecting;
conn->curl_proto_getsock = Curl_https_getsock; conn->curl_proto_getsock = Curl_https_getsock;
#else /* USE_SS */ #else /* USE_SSL */
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
" was built with SSL disabled, https: not supported!"); " was built with SSL disabled, https: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL; return CURLE_UNSUPPORTED_PROTOCOL;
@ -3213,6 +3235,21 @@ static CURLcode CreateConnection(struct SessionHandle *data,
#else #else
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
" was built with TFTP disabled!"); " was built with TFTP disabled!");
#endif
}
else if (strequal(conn->protostr, "SCP")) {
#ifdef USE_LIBSSH2
conn->port = PORT_SSH;
conn->remote_port = PORT_SSH;
conn->protocol = PROT_SCP;
conn->curl_connect = Curl_scp_connect; /* ssh_connect? */
conn->curl_do = Curl_scp_do;
conn->curl_done = Curl_scp_done;
conn->curl_do_more = (Curl_do_more_func)NULL;
#else
failf(data, LIBCURL_NAME
" was built without LIBSSH2, scp: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif #endif
} }
else { else {
@ -3381,7 +3418,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
user[0] =0; /* to make everything well-defined */ user[0] =0; /* to make everything well-defined */
passwd[0]=0; passwd[0]=0;
if (conn->protocol & (PROT_FTP|PROT_HTTP)) { if (conn->protocol & (PROT_FTP|PROT_HTTP|PROT_SCP)) {
/* This is a FTP or HTTP URL, we will now try to extract the possible /* This is a FTP or HTTP URL, we will now try to extract the possible
* user+password pair in a string like: * user+password pair in a string like:
* ftp://user:password@ftp.my.site:8021/README */ * ftp://user:password@ftp.my.site:8021/README */

View File

@ -35,6 +35,7 @@
#define PORT_DICT 2628 #define PORT_DICT 2628
#define PORT_LDAP 389 #define PORT_LDAP 389
#define PORT_TFTP 69 #define PORT_TFTP 69
#define PORT_SSH 22
#define DICT_MATCH "/MATCH:" #define DICT_MATCH "/MATCH:"
#define DICT_MATCH2 "/M:" #define DICT_MATCH2 "/M:"
@ -109,6 +110,11 @@
# endif # endif
#endif #endif
#ifdef HAVE_LIBSSH2_H
#include <libssh2.h>
#include <libssh2_sftp.h>
#endif /* HAVE_LIBSSH2_H */
/* Download buffer size, keep it fairly big for speed reasons */ /* Download buffer size, keep it fairly big for speed reasons */
#undef BUFSIZE #undef BUFSIZE
#define BUFSIZE CURL_MAX_WRITE_SIZE #define BUFSIZE CURL_MAX_WRITE_SIZE
@ -392,6 +398,22 @@ struct ftp_conn {
ftpstate state; /* always use ftp.c:state() to change state! */ ftpstate state; /* always use ftp.c:state() to change state! */
}; };
struct SCPPROTO {
curl_off_t *bytecountp;
char *user;
char *passwd;
char *path; /* the path we operate on */
char *freepath; /* pointer to the allocated block we must
free, this might differ from the 'path'
pointer */
char *errorstr;
#ifdef USE_LIBSSH2
LIBSSH2_SESSION *scpSession; /* Secure Shell session */
LIBSSH2_CHANNEL *scpChannel; /* SCP channel handle */
#endif /* USE_LIBSSH2 */
};
/**************************************************************************** /****************************************************************************
* FILE unique setup * FILE unique setup
***************************************************************************/ ***************************************************************************/
@ -647,6 +669,7 @@ struct HandleData {
struct FILEPROTO *file; struct FILEPROTO *file;
void *telnet; /* private for telnet.c-eyes only */ void *telnet; /* private for telnet.c-eyes only */
void *generic; void *generic;
struct SCPPROTO *scp;
} proto; } proto;
}; };
@ -681,6 +704,7 @@ struct connectdata {
#define PROT_FTPS (1<<9) #define PROT_FTPS (1<<9)
#define PROT_SSL (1<<10) /* protocol requires SSL */ #define PROT_SSL (1<<10) /* protocol requires SSL */
#define PROT_TFTP (1<<11) #define PROT_TFTP (1<<11)
#define PROT_SCP (1<<12)
/* 'dns_entry' is the particular host we use. This points to an entry in the /* 'dns_entry' is the particular host we use. This points to an entry in the
DNS cache and it will not get pruned while locked. It gets unlocked in DNS cache and it will not get pruned while locked. It gets unlocked in
@ -1250,6 +1274,11 @@ struct UserDefined {
bool ftp_skip_ip; /* skip the IP address the FTP server passes on to bool ftp_skip_ip; /* skip the IP address the FTP server passes on to
us */ us */
bool connect_only; /* make connection, let application use the socket */ bool connect_only; /* make connection, let application use the socket */
long ssh_auth_types; /* allowed SSH auth types */
char *ssh_public_key; /* the path to the public key file for
authentication */
char *ssh_private_key; /* the path to the private key file for
authentication */
}; };
struct Names { struct Names {

View File

@ -45,6 +45,11 @@
#include <iconv.h> #include <iconv.h>
#endif #endif
#ifdef USE_LIBSSH2
#include <libssh2.h>
#endif
char *curl_version(void) char *curl_version(void)
{ {
static char version[200]; static char version[200];
@ -88,6 +93,11 @@ char *curl_version(void)
left -= len; left -= len;
ptr += len; ptr += len;
#endif #endif
#ifdef USE_LIBSSH2
len = snprintf(ptr, left, " libssh2/%s", LIBSSH2_VERSION);
left -= len;
ptr += len;
#endif
return version; return version;
} }
@ -125,6 +135,11 @@ static const char * const protocols[] = {
"ftps", "ftps",
#endif #endif
#endif #endif
#ifdef USE_LIBSSH2
"scp",
#endif
NULL NULL
}; };
@ -179,10 +194,15 @@ static curl_version_info_data version_info = {
0, /* c-ares version numerical */ 0, /* c-ares version numerical */
NULL, /* libidn version */ NULL, /* libidn version */
0, /* iconv version */ 0, /* iconv version */
NULL, /* ssh lib version */
}; };
curl_version_info_data *curl_version_info(CURLversion stamp) curl_version_info_data *curl_version_info(CURLversion stamp)
{ {
#ifdef USE_LIBSSH2
static char ssh_buffer[80];
#endif
#ifdef USE_SSL #ifdef USE_SSL
static char ssl_buffer[80]; static char ssl_buffer[80];
Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
@ -217,6 +237,11 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
#endif /* _LIBICONV_VERSION */ #endif /* _LIBICONV_VERSION */
#endif #endif
#ifdef USE_LIBSSH2
snprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
version_info.libssh_version = ssh_buffer;
#endif
(void)stamp; /* avoid compiler warnings, we don't use this */ (void)stamp; /* avoid compiler warnings, we don't use this */
return &version_info; return &version_info;