Compare commits
62 Commits
RELEASE.0.
...
RELEASE.0.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2207b99afb | ||
![]() |
1d7522bc06 | ||
![]() |
a4e61c265b | ||
![]() |
502a48afa1 | ||
![]() |
efc3841fd2 | ||
![]() |
f9d65b0984 | ||
![]() |
edcdf43264 | ||
![]() |
722470994a | ||
![]() |
14f00247a8 | ||
![]() |
78048973c5 | ||
![]() |
e15f5d97a0 | ||
![]() |
01de39e585 | ||
![]() |
6cc50263e2 | ||
![]() |
beca3742a2 | ||
![]() |
d6cfa7c6b9 | ||
![]() |
ae17fbcd2c | ||
![]() |
0c53895bc0 | ||
![]() |
dc446eff08 | ||
![]() |
aa6e9c6eca | ||
![]() |
2e097c7760 | ||
![]() |
77bd3c1215 | ||
![]() |
0e5eb4d9c5 | ||
![]() |
8ee79a5118 | ||
![]() |
0ad861d74c | ||
![]() |
b6d13ebe8a | ||
![]() |
06e1136ea0 | ||
![]() |
2a6c49a73a | ||
![]() |
da653774aa | ||
![]() |
9203a62789 | ||
![]() |
6de7ed8a7b | ||
![]() |
8937980044 | ||
![]() |
cc7703092f | ||
![]() |
7502920c7f | ||
![]() |
ff6f2dbe52 | ||
![]() |
e5ee4a5be3 | ||
![]() |
30bb7db0d1 | ||
![]() |
8d90bbfc28 | ||
![]() |
e9b0710b4b | ||
![]() |
379e510c87 | ||
![]() |
ebfbd22e59 | ||
![]() |
9a0ba35457 | ||
![]() |
023c54d95d | ||
![]() |
ba2f21eb85 | ||
![]() |
3ea661a574 | ||
![]() |
30755999c5 | ||
![]() |
8e0e6d81dd | ||
![]() |
cba673de6f | ||
![]() |
11b27e52c3 | ||
![]() |
f289bcdd54 | ||
![]() |
66e7462f01 | ||
![]() |
ed88c32368 | ||
![]() |
83b95eb13e | ||
![]() |
ba420fc7bf | ||
![]() |
ae9ad1ef6f | ||
![]() |
d3f854c21b | ||
![]() |
5d9c0d50f3 | ||
![]() |
35a3e7a6c0 | ||
![]() |
15b8489046 | ||
![]() |
dce388e9c5 | ||
![]() |
0138e36352 | ||
![]() |
d6039f39e4 | ||
![]() |
0e0ed2aff4 |
@@ -18,9 +18,10 @@ all:
|
||||
(cd $$dir && $(MAKE) all) \
|
||||
|| case "$(MFLAGS)" in *k*) fail=yes;; *) exit 1;; esac; \
|
||||
done && test -z "$$fail"
|
||||
sample:
|
||||
$(CC) -o ssh2_sample.o ssh2_sample.c $(CFLAGS)
|
||||
$(CC) -o ssh2_sample ssh2_sample.o $(LIBS)
|
||||
install:
|
||||
install: all
|
||||
$(top_srcdir)/mkinstalldirs $(DESTDIR)$(incldir)
|
||||
$(top_srcdir)/mkinstalldirs $(DESTDIR)$(libdir)
|
||||
@for dir in ${subdirs}; do \
|
||||
@@ -29,6 +30,7 @@ install:
|
||||
done && test -z "$$fail"
|
||||
$(INSTALL) -m 644 include/libssh2.h $(DESTDIR)$(incldir)/
|
||||
$(INSTALL) -m 644 include/libssh2_sftp.h $(DESTDIR)$(incldir)/
|
||||
$(INSTALL) -m 644 include/libssh2_publickey.h $(DESTDIR)$(incldir)/
|
||||
clean:
|
||||
@for dir in ${subdirs}; do \
|
||||
(cd $$dir && $(MAKE) clean) \
|
||||
@@ -46,7 +48,7 @@ dist:
|
||||
$(DISTLIB)/mkinstalldirs $(DISTLIB)/install-sh $(DISTLIB)/config.sub $(DISTLIB)/config.guess \
|
||||
$(DISTLIB)/src/*.c $(DISTLIB)/src/Makefile.in \
|
||||
$(DISTLIB)/include/libssh2.h $(DISTLIB)/include/libssh2_priv.h $(DISTLIB)/include/libssh2_sftp.h \
|
||||
$(DISTLIB)/include/libssh2_config.h.in \
|
||||
$(DISTLIB)/include/libssh2_publickey.h $(DISTLIB)/include/libssh2_config.h.in \
|
||||
$(DISTLIB)/win32/config.mk $(DISTLIB)/win32/libssh2_config.h $(DISTLIB)/win32/rules.mk \
|
||||
$(DISTLIB)/win32/libssh2.dsp $(DISTLIB)/win32/libssh2.dsw $(DISTLIB)/win32/ssh2_sample.dsp
|
||||
rm -f $(DISTLIB)
|
||||
@@ -59,6 +61,7 @@ dist_nmake:
|
||||
$(DISTLIB)/NMakefile $(DISTLIB)/ssh2_sample.c $(DISTLIB)/src/*.c \
|
||||
$(DISTLIB)/LICENSE $(DISTLIB)/README $(DISTLIB)/TODO $(DISTLIB)/INSTALL \
|
||||
$(DISTLIB)/include/libssh2.h $(DISTLIB)/include/libssh2_priv.h $(DISTLIB)/include/libssh2_sftp.h \
|
||||
$(DISTLIB)/include/libssh2_publickey.h \
|
||||
$(DISTLIB)/win32/config.mk $(DISTLIB)/win32/libssh2_config.h $(DISTLIB)/win32/rules.mk
|
||||
rm -f $(DISTLIB)
|
||||
|
||||
|
82
README
82
README
@@ -1,6 +1,88 @@
|
||||
libssh2 - SSH2 library
|
||||
======================
|
||||
|
||||
Version 0.13
|
||||
------------
|
||||
|
||||
Fixed channel not being marked closed when CHANNEL_CLOSE package cannot be sent. (David Robins)
|
||||
|
||||
Fixed payload packet allocation bug when invalid packet length received. (David Robins)
|
||||
|
||||
Fixed `make install' target for MacOSX.
|
||||
|
||||
Add terminating NULL character to readlink()/realpath() results.
|
||||
|
||||
BugFix#1436593: Apply build options for HPUX targets.
|
||||
|
||||
Version 0.12
|
||||
------------
|
||||
|
||||
Added support for publickey subsytem (not the same as publickey auth).
|
||||
|
||||
Fix x11_req. Multiple packet_len issues and error handling logic.
|
||||
(Thanks Simon Hart)
|
||||
|
||||
Fix generation of 'e' portion of Diffie-Hellman keyset.
|
||||
Use appropriate order for BN_rand() rather than fixed group1-specific value.
|
||||
|
||||
Re-fixed libssh2_sftp_rename_ex()
|
||||
Transport had right packet_len, but sftp layer still had extra 4 bytes.
|
||||
|
||||
Fixed build with newer OpenSSL headers.
|
||||
|
||||
Added extern "C" declarations to libssh2_sftp.h for C++ compatability.
|
||||
|
||||
Version 0.11
|
||||
------------
|
||||
|
||||
Added libssh2_chnnale_get_exit_status() -- Mikhail
|
||||
|
||||
Added libssh2_channel_wait_closed() -- Mikhail
|
||||
|
||||
Added libssh2_userauth_keyboard_interactive_ex() -- Mikhail
|
||||
|
||||
Added libssh2_channel_receive_window_adjust() to be able to increase the size of the receive window.
|
||||
|
||||
Added queueing for small window_adjust packets to avoid unnecessary packet conversation.
|
||||
|
||||
Fixed libssh2_sftp_rename_ex() to only send flags parameter if version >= 5 negotiated
|
||||
(not currently possible, but will be and might as well keep the API consistent).
|
||||
|
||||
Version 0.10
|
||||
------------
|
||||
|
||||
Added developer debugging hooks. See --enable-debug-* options to ./configure
|
||||
|
||||
Ignore extended data in the SFTP layer. With no other mechanism to deal with that data it'd just fill up and get stuck.
|
||||
|
||||
(Re)Fixed channel_write() to provide an opportunity for window space to become available again.
|
||||
|
||||
(Re)Fixed SFTP INIT to send the correct SFTP packet length.
|
||||
|
||||
Fixed segfault when client and host can't agree on a hostkey/crypt/mac/comp method. (Thanks puudeli)
|
||||
|
||||
Fixed major issue with sftp packet buffering mechanism. Using wrong blocking semantics. (No puudeli, YOU the man)
|
||||
|
||||
Reduced busy-looping of libssh2_sftp_packet_requirev.
|
||||
|
||||
Version 0.9
|
||||
-----------
|
||||
|
||||
Changed blocking_read to only block as much as necessary and not an arbitrary length of time. (Thanks Felix)
|
||||
|
||||
Fixed SFTP INIT/VERSION to exclude request_id and send correct maximum version number.
|
||||
|
||||
Fixed SFTP to be properly BC with version 1 and 2 servers.
|
||||
|
||||
Fixed libssh2_poll() to recognized closed sessions/channels.
|
||||
|
||||
Fixed libssh2_channel_write_ex() to fully block when set to blocking mode. Return actual bytes written as well. (Thanks deadem)
|
||||
|
||||
Added tests for -lm and -lsocket and add them when necessary.
|
||||
|
||||
Added libssh2_channel_window_read_ex() and libssh2_channel_window_write_ex()
|
||||
for examining the ssh transport windowing states.
|
||||
|
||||
Version 0.8
|
||||
-----------
|
||||
|
||||
|
62
configure.in
62
configure.in
@@ -1,8 +1,11 @@
|
||||
# AC_PREREQ(2.57)
|
||||
AC_INIT(libssh2,0.8,sarag@libssh2.org)
|
||||
AC_INIT(libssh2,0.13,sarag@libssh2.org)
|
||||
AC_CONFIG_SRCDIR([src])
|
||||
AC_CONFIG_HEADER([include/libssh2_config.h])
|
||||
|
||||
# Default to the same as CC
|
||||
LDCC="\$(CC)"
|
||||
|
||||
# Check for the OS.
|
||||
AC_CANONICAL_HOST
|
||||
case "$host" in
|
||||
@@ -16,13 +19,28 @@ case "$host" in
|
||||
SHLIB_LDFLAGS="-dynamiclib -flat_namespace"
|
||||
CFLAGS="$CFLAGS -DLIBSSH2_DARWIN"
|
||||
;;
|
||||
*hpux*)
|
||||
SHLIB_SUFFIX_NAME="sl"
|
||||
SHLIB_LDFLAGS="-b +vnocompatwarnings -L/lib/pa20_64"
|
||||
LDCC="ld"
|
||||
;;
|
||||
*)
|
||||
SHLIB_SUFFIX_NAME="so"
|
||||
SHLIB_LDFLAGS="-shared"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_CHECK_LIB(socket, socket, [
|
||||
SHLIB_LDFLAGS="$SHLIB_LDFLAGS -lsocket"
|
||||
LIBS="$LIBS -lsocket"
|
||||
])
|
||||
AC_CHECK_LIB(m, ceil, [ SHLIB_LDFLAGS="$SHLIB_LDFLAGS -lm" ])
|
||||
|
||||
|
||||
AC_SUBST(SHLIB_SUFFIX_NAME)
|
||||
AC_SUBST(SHLIB_LDFLAGS)
|
||||
AC_SUBST(LDCC)
|
||||
AC_SUBST(LIBS)
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
@@ -160,6 +178,48 @@ if test "$GEX_NEW" != "no"; then
|
||||
AC_DEFINE(LIBSSH2_DH_GEX_NEW, 1, [Enable newer diffie-hellman-group-exchange-sha1 syntax])
|
||||
fi
|
||||
|
||||
#
|
||||
# Optional debugging -- Meant for developer maintenance only
|
||||
# When enabled, the relevant debugging information will be written on stderr
|
||||
#
|
||||
AC_ARG_ENABLE(debug-transport,
|
||||
AC_HELP_STRING([--enable-debug-transport],[Output transport layer debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-kex,
|
||||
AC_HELP_STRING([--enable-debug-kex],[Output Key Exchange debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-userauth,
|
||||
AC_HELP_STRING([--enable-debug-userauth],[Output userauth debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_USERAUTH, 1, [Output userauth layer debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-channel,
|
||||
AC_HELP_STRING([--enable-debug-connection],[Output connection layer debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_CONNECTION, 1, [Output connection layer debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-scp,
|
||||
AC_HELP_STRING([--enable-debug-scp],[Output scp subsystem debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_SCP, 1, [Output scp subsystem debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-sftp,
|
||||
AC_HELP_STRING([--enable-debug-sftp],[Output sftp subsystem debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_SFTP, 1, [Output sftp subsystem debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-publickey,
|
||||
AC_HELP_STRING([--enable-debug-publickey],[Output publickey subsystem debugging info to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_PUBLICKEY, 1, [Output publickey subsystem debugging info to stderr])])
|
||||
AC_ARG_ENABLE(debug-errors,
|
||||
AC_HELP_STRING([--enable-debug-errors],[Output failure events to stderr]),
|
||||
[AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])])
|
||||
AC_ARG_ENABLE(debug-all,
|
||||
AC_HELP_STRING([--enable-debug-all],[Output debugging info for all layers to stderr]),
|
||||
[
|
||||
AC_DEFINE(LIBSSH2_DEBUG_TRANSPORT, 1, [Output transport layer debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_KEX, 1, [Output Key Exchange debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_USERAUTH, 1, [Output userauth layer debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_CONNECTION, 1, [Output connection layer debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_SCP, 1, [Output scp subsystem debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_SFTP, 1, [Output sftp subsystem debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_PUBLICKEY, 1, [Output publickey subsystem debugging info to stderr])
|
||||
AC_DEFINE(LIBSSH2_DEBUG_ERRORS, 1, [Output failure events to stderr])
|
||||
])
|
||||
|
||||
|
||||
# Checks for header files.
|
||||
# AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h sys/uio.h sys/select.h])
|
||||
|
@@ -38,6 +38,10 @@
|
||||
#ifndef LIBSSH2_H
|
||||
#define LIBSSH2_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -67,8 +71,8 @@ typedef unsigned long long libssh2_uint64_t;
|
||||
typedef long long libssh2_int64_t;
|
||||
#endif
|
||||
|
||||
#define LIBSSH2_VERSION "0.8"
|
||||
#define LIBSSH2_APINO 200503221619
|
||||
#define LIBSSH2_VERSION "0.13"
|
||||
#define LIBSSH2_APINO 200507211326
|
||||
|
||||
/* Part of every banner, user specified or not */
|
||||
#define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION
|
||||
@@ -107,6 +111,22 @@ typedef long long libssh2_int64_t;
|
||||
#define LIBSSH2_REALLOC_FUNC(name) void *name(void *ptr, size_t count, void **abstract)
|
||||
#define LIBSSH2_FREE_FUNC(name) void name(void *ptr, void **abstract)
|
||||
|
||||
typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT
|
||||
{
|
||||
char* text;
|
||||
unsigned int length;
|
||||
unsigned char echo;
|
||||
} LIBSSH2_USERAUTH_KBDINT_PROMPT;
|
||||
|
||||
typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE
|
||||
{
|
||||
char* text;
|
||||
unsigned int length;
|
||||
} LIBSSH2_USERAUTH_KBDINT_RESPONSE;
|
||||
|
||||
/* 'keyboard-interactive' authentication callback */
|
||||
#define LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC(name_) void name_(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)
|
||||
|
||||
/* Callbacks for special SSH packets */
|
||||
#define LIBSSH2_IGNORE_FUNC(name) void name(LIBSSH2_SESSION *session, const char *message, int message_len, void **abstract)
|
||||
#define LIBSSH2_DEBUG_FUNC(name) void name(LIBSSH2_SESSION *session, int always_display, const char *message, int message_len, const char *language, int language_len,void **abstract)
|
||||
@@ -163,15 +183,18 @@ typedef struct _LIBSSH2_POLLFD {
|
||||
|
||||
/* Note: Win32 Doesn't actually have a poll() implementation, so some of these values are faked with select() data */
|
||||
/* Poll FD events/revents -- Match sys/poll.h where possible */
|
||||
#define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or connection available -- All */
|
||||
#define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to be read -- Socket only */
|
||||
#define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to be read -- Channel only */
|
||||
#define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- Socket/Channel */
|
||||
#define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or connection available -- All */
|
||||
#define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to be read -- Socket only */
|
||||
#define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to be read -- Channel only */
|
||||
#define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- Socket/Channel */
|
||||
/* revents only */
|
||||
#define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */
|
||||
#define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket/Channel */
|
||||
#define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket Only */
|
||||
#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- Socket/Win32 */
|
||||
#define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */
|
||||
#define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket */
|
||||
#define LIBSSH2_POLLFD_SESSION_CLOSED 0x0010 /* Session Disconnect */
|
||||
#define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket Only */
|
||||
#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- Socket/Win32 */
|
||||
#define LIBSSH2_POLLFD_CHANNEL_CLOSED 0x0080 /* Channel Disconnect */
|
||||
#define LIBSSH2_POLLFD_LISTENER_CLOSED 0x0080 /* Listener Disconnect */
|
||||
|
||||
/* Hash Types */
|
||||
#define LIBSSH2_HOSTKEY_HASH_MD5 1
|
||||
@@ -230,6 +253,7 @@ typedef struct _LIBSSH2_POLLFD {
|
||||
#define LIBSSH2_ERROR_METHOD_NOT_SUPPORTED -33
|
||||
#define LIBSSH2_ERROR_INVAL -34
|
||||
#define LIBSSH2_ERROR_INVALID_POLL_TYPE -35
|
||||
#define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL -36
|
||||
|
||||
/* Session API */
|
||||
LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)), LIBSSH2_FREE_FUNC((*my_free)), LIBSSH2_REALLOC_FUNC((*my_realloc)), void *abstract);
|
||||
@@ -237,45 +261,57 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_all
|
||||
LIBSSH2_API void **libssh2_session_abstract(LIBSSH2_SESSION *session);
|
||||
|
||||
LIBSSH2_API void *libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbtype, void *callback);
|
||||
LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, char *banner);
|
||||
LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner);
|
||||
|
||||
LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket);
|
||||
LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, char *description, char *lang);
|
||||
LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang);
|
||||
#define libssh2_session_disconnect(session, description) libssh2_session_disconnect_ex((session), SSH_DISCONNECT_BY_APPLICATION, (description), "")
|
||||
LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session);
|
||||
|
||||
LIBSSH2_API char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type);
|
||||
LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type);
|
||||
|
||||
LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, char *prefs);
|
||||
LIBSSH2_API char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type);
|
||||
LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, const char *prefs);
|
||||
LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type);
|
||||
LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf);
|
||||
|
||||
LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int value);
|
||||
|
||||
/* Userauth API */
|
||||
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, char *username, int username_len);
|
||||
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, int username_len);
|
||||
LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session);
|
||||
LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *username, int username_len, char *password, int password_len, LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)));
|
||||
LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, int username_len, const char *password, int password_len, LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)));
|
||||
#define libssh2_userauth_password(session, username, password) libssh2_userauth_password_ex((session), (username), strlen(username), (password), strlen(password), NULL)
|
||||
|
||||
LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, char *username, int username_len,
|
||||
char *publickey, char *privatekey,
|
||||
char *passphrase);
|
||||
LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len,
|
||||
const char *publickey, const char *privatekey,
|
||||
const char *passphrase);
|
||||
#define libssh2_userauth_publickey_fromfile(session, username, publickey, privatekey, passphrase) \
|
||||
libssh2_userauth_publickey_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase))
|
||||
LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, char *username, int username_len,
|
||||
char *publickey, char *privatekey,
|
||||
char *passphrase,
|
||||
char *hostname, int hostname_len,
|
||||
char *local_username, int local_username_len);
|
||||
LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len,
|
||||
const char *publickey, const char *privatekey,
|
||||
const char *passphrase,
|
||||
const char *hostname, int hostname_len,
|
||||
const char *local_username, int local_username_len);
|
||||
#define libssh2_userauth_hostbased_fromfile(session, username, publickey, privatekey, passphrase, hostname) \
|
||||
libssh2_userauth_hostbased_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase), (hostname), strlen(hostname), (username), strlen(username))
|
||||
|
||||
/*
|
||||
* response_callback is provided with filled by library prompts array,
|
||||
* but client must allocate and fill individual responses. Responses
|
||||
* array is already allocated. Responses data will be freed by libssh2
|
||||
* after callback return, but before subsequent callback invokation.
|
||||
*/
|
||||
LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION* session, const char *username, int username_len,
|
||||
LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)));
|
||||
#define libssh2_userauth_keyboard_interactive(session, username, response_callback) \
|
||||
libssh2_userauth_keyboard_interactive_ex((session), (username), strlen(username), (response_callback))
|
||||
|
||||
LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeout);
|
||||
|
||||
/* Channel API */
|
||||
#define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536
|
||||
#define LIBSSH2_CHANNEL_PACKET_DEFAULT 16384
|
||||
#define LIBSSH2_CHANNEL_MINADJUST 1024
|
||||
|
||||
/* Extended Data Handling */
|
||||
#define LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL 0
|
||||
@@ -284,11 +320,11 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
|
||||
|
||||
#define SSH_EXTENDED_DATA_STDERR 1
|
||||
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, char *channel_type, int channel_type_len, int window_size, int packet_size, char *message, int message_len);
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, int channel_type_len, int window_size, int packet_size, const char *message, int message_len);
|
||||
#define libssh2_channel_open_session(session) libssh2_channel_open_ex((session), "session", sizeof("session") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0)
|
||||
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, char *host, int port, char *shost, int sport);
|
||||
#define libssh2_channel_direct_tcpip(session, host, port) libssh2_channel_direct_tcpip_ex((session), (host), (port), "127.0.0.1", 22)
|
||||
#define libssh2_channel_direct_tcpip(session, host, port) libssh2_channel_direct_tcpip_ex((session), (host), (port), "127.0.0.1", 22)
|
||||
|
||||
LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize);
|
||||
#define libssh2_channel_forward_listen(session, port) libssh2_channel_forward_listen_ex((session), NULL, (port), NULL, 16)
|
||||
@@ -306,7 +342,7 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
|
||||
LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, char *auth_proto, char *auth_cookie, int screen_number);
|
||||
#define libssh2_channel_x11_req(channel, screen_number) libssh2_channel_x11_req_ex((channel), 0, NULL, NULL, (screen_number))
|
||||
|
||||
LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *request, int request_len, char *message, int message_len);
|
||||
LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, int request_len, const char *message, int message_len);
|
||||
#define libssh2_channel_shell(channel) libssh2_channel_process_startup((channel), "shell", sizeof("shell") - 1, NULL, 0)
|
||||
#define libssh2_channel_exec(channel, command) libssh2_channel_process_startup((channel), "exec", sizeof("exec") - 1, (command), strlen(command))
|
||||
#define libssh2_channel_subsystem(channel, subsystem) libssh2_channel_process_startup((channel), "subsystem", sizeof("subsystem") - 1, (subsystem), strlen(subsystem))
|
||||
@@ -315,15 +351,25 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
#define libssh2_channel_read(channel, buf, buflen) libssh2_channel_read_ex((channel), 0, (buf), (buflen))
|
||||
#define libssh2_channel_read_stderr(channel, buf, buflen) libssh2_channel_read_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
|
||||
|
||||
LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended);
|
||||
|
||||
LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial);
|
||||
#define libssh2_channel_window_read(channel) libssh2_channel_window_read_ex((channel), NULL, NULL)
|
||||
|
||||
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force);
|
||||
|
||||
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen);
|
||||
#define libssh2_channel_write(channel, buf, buflen) libssh2_channel_write_ex((channel), 0, (buf), (buflen))
|
||||
#define libssh2_channel_write_stderr(channel, buf, buflen) libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen))
|
||||
|
||||
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial);
|
||||
#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL)
|
||||
|
||||
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
|
||||
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode);
|
||||
/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1
|
||||
* Future uses should use libssh2_channel_handle_extended_data() directly
|
||||
* if LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE is passed, extended data will be read (FIFO) from the standard data channel
|
||||
* if LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE is passed, extended data will be read (FIFO) from the standard data channel
|
||||
*/
|
||||
/* DEPRECATED */
|
||||
#define libssh2_channel_ignore_extended_data(channel, ignore) libssh2_channel_handle_extended_data((channel), (ignore) ? LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE : LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL )
|
||||
@@ -333,16 +379,22 @@ LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
|
||||
LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid);
|
||||
#define libssh2_channel_flush(channel) libssh2_channel_flush_ex((channel), 0)
|
||||
#define libssh2_channel_flush_stderr(channel) libssh2_channel_flush_ex((channel), SSH_EXTENDED_DATA_STDERR)
|
||||
LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel);
|
||||
|
||||
LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel);
|
||||
LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel);
|
||||
LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel);
|
||||
LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel);
|
||||
LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel);
|
||||
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *path, struct stat *sb);
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char *path, int mode, size_t size, long mtime, long atime);
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb);
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime);
|
||||
#define libssh2_scp_send(session, path, mode, size) libssh2_scp_send_ex((session), (path), (mode), (size), 0, 0)
|
||||
|
||||
LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **dest, int *dest_len, char *src, int src_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LIBSSH2_H */
|
||||
|
@@ -54,6 +54,30 @@
|
||||
/* Enable "none" cipher -- NOT RECOMMENDED */
|
||||
#undef LIBSSH2_CRYPT_NONE
|
||||
|
||||
/* Output connection layer debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_CONNECTION
|
||||
|
||||
/* Output failure events to stderr */
|
||||
#undef LIBSSH2_DEBUG_ERRORS
|
||||
|
||||
/* Output Key Exchange debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_KEX
|
||||
|
||||
/* Output publickey subsystem debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_PUBLICKEY
|
||||
|
||||
/* Output scp subsystem debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_SCP
|
||||
|
||||
/* Output sftp subsystem debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_SFTP
|
||||
|
||||
/* Output transport layer debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_TRANSPORT
|
||||
|
||||
/* Output userauth layer debugging info to stderr */
|
||||
#undef LIBSSH2_DEBUG_USERAUTH
|
||||
|
||||
/* Enable newer diffie-hellman-group-exchange-sha1 syntax */
|
||||
#undef LIBSSH2_DH_GEX_NEW
|
||||
|
||||
|
@@ -46,9 +46,19 @@
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include <openssl/evp.h>
|
||||
#ifndef OPENSSL_NO_SHA
|
||||
#include <openssl/sha.h>
|
||||
#endif
|
||||
#ifndef OPENSSL_NO_MD5
|
||||
#include <openssl/md5.h>
|
||||
#endif
|
||||
|
||||
#ifdef __hpux
|
||||
# define inline
|
||||
#endif
|
||||
|
||||
#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract)
|
||||
#define LIBSSH2_REALLOC(session, ptr, count) session->realloc((ptr), (count), &(session)->abstract)
|
||||
#define LIBSSH2_REALLOC(session, ptr, count) ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : session->alloc((count), &(session)->abstract))
|
||||
#define LIBSSH2_FREE(session, ptr) session->free((ptr), &(session)->abstract)
|
||||
|
||||
#define LIBSSH2_IGNORE(session, data, datalen) session->ssh_msg_ignore((session), (data), (datalen), &(session)->abstract)
|
||||
@@ -112,7 +122,11 @@ struct _LIBSSH2_CHANNEL {
|
||||
|
||||
int blocking;
|
||||
|
||||
/* channel's program exit status */
|
||||
int exit_status;
|
||||
|
||||
libssh2_channel_data local, remote;
|
||||
unsigned long adjust_queue; /* Amount of bytes to be refunded to receive window (but not yet sent) */
|
||||
|
||||
LIBSSH2_SESSION *session;
|
||||
|
||||
@@ -263,7 +277,7 @@ struct _LIBSSH2_HOSTKEY_METHOD {
|
||||
unsigned long hash_len;
|
||||
|
||||
int (*init)(LIBSSH2_SESSION *session, unsigned char *hostkey_data, unsigned long hostkey_data_len, void **abstract);
|
||||
int (*initPEM)(LIBSSH2_SESSION *session, unsigned char *privkeyfile, unsigned char *passphrase, void **abstract);
|
||||
int (*initPEM)(LIBSSH2_SESSION *session, unsigned const char *privkeyfile, unsigned const char *passphrase, void **abstract);
|
||||
int (*sig_verify)(LIBSSH2_SESSION *session, const unsigned char *sig, unsigned long sig_len, const unsigned char *m, unsigned long m_len, void **abstract);
|
||||
int (*sign)(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, const unsigned char *data, unsigned long data_len, void **abstract);
|
||||
int (*signv)(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len, unsigned long veccount, const struct iovec datavec[], void **abstract);
|
||||
@@ -317,6 +331,38 @@ struct _LIBSSH2_MAC_METHOD {
|
||||
int (*dtor)(LIBSSH2_SESSION *session, void **abstract);
|
||||
};
|
||||
|
||||
#if defined(LIBSSH2_DEBUG_TRANSPORT) || defined(LIBSSH2_DEBUG_KEX) || defined(LIBSSH2_DEBUG_USERAUTH) || defined(LIBSSH2_DEBUG_CONNECTION) || defined(LIBSSH2_DEBUG_SCP) || defined(LIBSSH2_DEBUG_SFTP) || defined(LIBSSH2_DEBUG_ERRORS)
|
||||
#define LIBSSH2_DEBUG_ENABLED
|
||||
|
||||
/* Internal debugging contexts -- Used with --enable-debug-* */
|
||||
#define LIBSSH2_DBG_TRANS 1
|
||||
#define LIBSSH2_DBG_KEX 2
|
||||
#define LIBSSH2_DBG_AUTH 3
|
||||
#define LIBSSH2_DBG_CONN 4
|
||||
#define LIBSSH2_DBG_SCP 5
|
||||
#define LIBSSH2_DBG_SFTP 6
|
||||
#define LIBSSH2_DBG_ERROR 7
|
||||
#define LIBSSH2_DBG_PUBLICKEY 8
|
||||
|
||||
void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, ...);
|
||||
|
||||
#endif /* LIBSSH2_DEBUG_ENABLED */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_ERRORS
|
||||
#define libssh2_error(session, errcode, errmsg, should_free) \
|
||||
{ \
|
||||
if (session->err_msg && session->err_should_free) { \
|
||||
LIBSSH2_FREE(session, session->err_msg); \
|
||||
} \
|
||||
session->err_msg = errmsg; \
|
||||
session->err_msglen = strlen(errmsg); \
|
||||
session->err_should_free = should_free; \
|
||||
session->err_code = errcode; \
|
||||
_libssh2_debug(session, LIBSSH2_DBG_ERROR, "%d - %s", session->err_code, session->err_msg); \
|
||||
}
|
||||
|
||||
#else /* ! LIBSSH2_DEBUG_ERRORS */
|
||||
|
||||
#define libssh2_error(session, errcode, errmsg, should_free) \
|
||||
{ \
|
||||
if (session->err_msg && session->err_should_free) { \
|
||||
@@ -328,6 +374,9 @@ struct _LIBSSH2_MAC_METHOD {
|
||||
session->err_code = errcode; \
|
||||
}
|
||||
|
||||
#endif /* LIBSSH2_DEBUG_ENABLED */
|
||||
|
||||
|
||||
#define LIBSSH2_SOCKET_UNKNOWN 1
|
||||
#define LIBSSH2_SOCKET_CONNECTED 0
|
||||
#define LIBSSH2_SOCKET_DISCONNECTED -1
|
||||
@@ -372,6 +421,9 @@ struct _LIBSSH2_MAC_METHOD {
|
||||
#define SSH_MSG_USERAUTH_PK_OK 60
|
||||
/* "password" method */
|
||||
#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60
|
||||
/* "keyboard-interactive" method */
|
||||
#define SSH_MSG_USERAUTH_INFO_REQUEST 60
|
||||
#define SSH_MSG_USERAUTH_INFO_RESPONSE 61
|
||||
|
||||
/* Channels */
|
||||
#define SSH_MSG_GLOBAL_REQUEST 80
|
||||
@@ -401,9 +453,15 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int block);
|
||||
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
|
||||
#define libssh2_packet_ask(session, packet_type, data, data_len, poll_socket) \
|
||||
libssh2_packet_ask_ex((session), (packet_type), (data), (data_len), 0, NULL, 0, (poll_socket))
|
||||
int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket);
|
||||
#define libssh2_packet_askv(session, packet_types, data, data_len, poll_socket) \
|
||||
libssh2_packet_askv_ex((session), (packet_types), (data), (data_len), 0, NULL, 0, (poll_socket))
|
||||
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
||||
#define libssh2_packet_require(session, packet_type, data, data_len) \
|
||||
libssh2_packet_require_ex((session), (packet_type), (data), (data_len), 0, NULL, 0)
|
||||
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
||||
#define libssh2_packet_requirev(session, packet_types, data, data_len) \
|
||||
libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0)
|
||||
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
|
||||
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
|
||||
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);
|
||||
|
101
include/libssh2_publickey.h
Normal file
101
include/libssh2_publickey.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/* Copyright (c) 2004-2005, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Note: This include file is only needed for using the
|
||||
* publickey SUBSYSTEM which is not the same as publickey
|
||||
* authentication. For authentication you only need libssh2.h
|
||||
*
|
||||
* For more information on the publickey subsystem,
|
||||
* refer to IETF draft: secsh-publickey
|
||||
*/
|
||||
|
||||
#ifndef LIBSSH2_PUBLICKEY_H
|
||||
#define LIBSSH2_PUBLICKEY_H 1
|
||||
|
||||
typedef struct _LIBSSH2_PUBLICKEY LIBSSH2_PUBLICKEY;
|
||||
|
||||
typedef struct _libssh2_publickey_attribute {
|
||||
char *name;
|
||||
unsigned long name_len;
|
||||
char *value;
|
||||
unsigned long value_len;
|
||||
char mandatory;
|
||||
} libssh2_publickey_attribute;
|
||||
|
||||
typedef struct _libssh2_publickey_list {
|
||||
unsigned char *packet; /* For freeing */
|
||||
|
||||
unsigned char *name;
|
||||
unsigned long name_len;
|
||||
unsigned char *blob;
|
||||
unsigned long blob_len;
|
||||
unsigned long num_attrs;
|
||||
libssh2_publickey_attribute *attrs; /* free me */
|
||||
} libssh2_publickey_list;
|
||||
|
||||
/* Generally use the first macro here, but if both name and value are string literals, you can use _fast() to take advantage of preprocessing */
|
||||
#define libssh2_publickey_attribute(name, value, mandatory) { (name), strlen(name), (value), strlen(value), (mandatory) },
|
||||
#define libssh2_publickey_attribute_fast(name, value, mandatory) { (name), sizeof(name) - 1, (value), sizeof(value) - 1, (mandatory) },
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Publickey Subsystem */
|
||||
LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session);
|
||||
|
||||
LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len,
|
||||
const unsigned char *blob, unsigned long blob_len, char overwrite,
|
||||
unsigned long num_attrs, libssh2_publickey_attribute attrs[]);
|
||||
#define libssh2_publickey_add(pkey, name, blob, blob_len, overwrite, num_attrs, attrs) \
|
||||
libssh2_publickey_add_ex((pkey), (name), strlen(name), (blob), (blob_len), (overwrite), (num_attrs), (attrs))
|
||||
|
||||
LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len,
|
||||
const unsigned char *blob, unsigned long blob_len);
|
||||
#define libssh2_publickey_remove(pkey, name, blob, blob_len) \
|
||||
libssh2_publickey_remove_ex((pkey), (name), strlen(name), (blob), (blob_len))
|
||||
|
||||
LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list);
|
||||
LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_publickey_list *pkey_list);
|
||||
|
||||
LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* ndef: LIBSSH2_PUBLICKEY_H */
|
@@ -38,6 +38,10 @@
|
||||
#ifndef LIBSSH2_SFTP_H
|
||||
#define LIBSSH2_SFTP_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Note: Version 6 was documented at the time of writing
|
||||
* However it was marked as "DO NOT IMPLEMENT" due to pending changes
|
||||
*
|
||||
@@ -187,4 +191,8 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, in
|
||||
#define libssh2_sftp_readlink(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_READLINK)
|
||||
#define libssh2_sftp_realpath(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_REALPATH)
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* LIBSSH2_SFTP_H */
|
||||
|
@@ -1,4 +1,4 @@
|
||||
OBJECTS = channel.o comp.o crypt.o hostkey.o kex.o mac.o misc.o packet.o scp.o session.o sftp.o userauth.o
|
||||
OBJECTS = channel.o comp.o crypt.o hostkey.o kex.o mac.o misc.o packet.o publickey.o scp.o session.o sftp.o userauth.o
|
||||
|
||||
top_srcdir = @top_srcdir@
|
||||
prefix = @prefix@
|
||||
@@ -7,8 +7,10 @@ libdir = @exec_prefix@/lib
|
||||
incldir = @prefix@/include
|
||||
|
||||
CC = @CC@
|
||||
LDCC = @LDCC@
|
||||
CFLAGS = -c @CFLAGS@ -Wall -g -I../include/ -fPIC
|
||||
LDFLAGS = @LDFLAGS@
|
||||
SHLIB_LDFLAGS = @SHLIB_LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
INSTALL = @INSTALL@
|
||||
|
||||
@@ -36,6 +38,9 @@ misc.o: misc.c
|
||||
packet.o: packet.c
|
||||
$(CC) -o packet.o packet.c $(CFLAGS) $(LIBS)
|
||||
|
||||
publickey.o: publickey.c
|
||||
$(CC) -o publickey.o publickey.c $(CFLAGS) $(LIBS)
|
||||
|
||||
scp.o: scp.c
|
||||
$(CC) -o scp.o scp.c $(CFLAGS) $(LIBS)
|
||||
|
||||
@@ -51,7 +56,7 @@ userauth.o: userauth.c
|
||||
all: libssh2.@SHLIB_SUFFIX_NAME@
|
||||
|
||||
libssh2.@SHLIB_SUFFIX_NAME@: $(OBJECTS)
|
||||
$(CC) -o libssh2.@SHLIB_SUFFIX_NAME@ $(SHLIB_LDFLAGS) $(OBJECTS) $(LIBS) $(LDFLAGS) @SHLIB_LDFLAGS@
|
||||
$(LDCC) -o libssh2.@SHLIB_SUFFIX_NAME@ $(SHLIB_LDFLAGS) $(OBJECTS) $(LIBS) $(LDFLAGS)
|
||||
libssh2.a: $(OBJECTS)
|
||||
rm -f libssh2.a
|
||||
ar q libssh2.a $(OBJECTS)
|
||||
|
641
src/channel.c
641
src/channel.c
@@ -7,17 +7,17 @@
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
@@ -64,7 +64,9 @@ unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session)
|
||||
* Gets picked up by the new one.... Pretty unlikely all told...
|
||||
*/
|
||||
session->next_channel = id + 1;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocated new channel ID#%lu", id);
|
||||
#endif
|
||||
return id;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -97,24 +99,27 @@ LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long
|
||||
} \
|
||||
(channel)->next = NULL; \
|
||||
(session)->channels.tail = (channel); \
|
||||
(channel)->session = (session); \
|
||||
(channel)->session = (session); \
|
||||
}
|
||||
|
||||
/* {{{ libssh2_channel_open_session
|
||||
* Establish a generic session channel
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, char *channel_type, int channel_type_len, int window_size, int packet_size,
|
||||
char *message, int message_len)
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, int channel_type_len, int window_size, int packet_size,
|
||||
const char *message, int message_len)
|
||||
{
|
||||
unsigned char reply_codes[3] = { SSH_MSG_CHANNEL_OPEN_CONFIRMATION, SSH_MSG_CHANNEL_OPEN_FAILURE, 0 };
|
||||
LIBSSH2_CHANNEL *channel = NULL;
|
||||
unsigned long local_channel = libssh2_channel_nextid(session);
|
||||
unsigned char *s, *packet = NULL;
|
||||
unsigned long packet_len = channel_type_len + message_len + 17; /* packet_type(1) + channel_type_len(4) + sender_channel(4) +
|
||||
unsigned long packet_len = channel_type_len + message_len + 17; /* packet_type(1) + channel_type_len(4) + sender_channel(4) +
|
||||
window_size(4) + packet_size(4) */
|
||||
unsigned char *data = NULL;
|
||||
unsigned long data_len;
|
||||
int polls = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Opening Channel - win %d pack %d", window_size, packet_size);
|
||||
#endif
|
||||
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
|
||||
if (!channel) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0);
|
||||
@@ -161,35 +166,30 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, c
|
||||
goto channel_error;
|
||||
}
|
||||
|
||||
while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &data, &data_len, 1, packet + 5 + channel_type_len, 4, 1) == 0) {
|
||||
/* YAY! You like me! */
|
||||
break;
|
||||
}
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_OPEN_FAILURE, &data, &data_len, 1, packet + 5 + channel_type_len, 4, 0) == 0) {
|
||||
/* But! Dear! I thought we had something! */
|
||||
|
||||
/* TODO: provide reason code and description */
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0);
|
||||
goto channel_error;
|
||||
}
|
||||
usleep(LIBSSH2_SOCKET_POLL_UDELAY);
|
||||
if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
|
||||
/* Give up waiting */
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timed out waiting for response", 0);
|
||||
goto channel_error;
|
||||
}
|
||||
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, packet + 5 + channel_type_len, 4)) {
|
||||
goto channel_error;
|
||||
}
|
||||
|
||||
channel->remote.id = libssh2_ntohu32(data + 5);
|
||||
channel->local.window_size = libssh2_ntohu32(data + 9);
|
||||
channel->local.window_size_initial = libssh2_ntohu32(data + 9);
|
||||
channel->local.packet_size = libssh2_ntohu32(data + 13);
|
||||
if (data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
|
||||
channel->remote.id = libssh2_ntohu32(data + 5);
|
||||
channel->local.window_size = libssh2_ntohu32(data + 9);
|
||||
channel->local.window_size_initial = libssh2_ntohu32(data + 9);
|
||||
channel->local.packet_size = libssh2_ntohu32(data + 13);
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection Established - ID: %lu/%lu win: %lu/%lu pack: %lu/%lu",
|
||||
channel->local.id, channel->remote.id,
|
||||
channel->local.window_size, channel->remote.window_size,
|
||||
channel->local.packet_size, channel->remote.packet_size);
|
||||
#endif
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return channel;
|
||||
}
|
||||
|
||||
return channel;
|
||||
if (data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Channel open failure", 0);
|
||||
}
|
||||
|
||||
channel_error:
|
||||
|
||||
@@ -240,6 +240,10 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
|
||||
unsigned long host_len = strlen(host), shost_len = strlen(shost);
|
||||
unsigned long message_len = host_len + shost_len + 16; /* host_len(4) + port(4) + shost_len(4) + sport(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting direct-tcpip session to from %s:%d to %s:%d", shost, sport, host, port);
|
||||
#endif
|
||||
|
||||
s = message = LIBSSH2_ALLOC(session, message_len);
|
||||
if (!message) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0);
|
||||
@@ -265,11 +269,16 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *se
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, char *host, int port, int *bound_port, int queue_maxsize)
|
||||
{
|
||||
unsigned char *packet, *s;
|
||||
unsigned char *packet, *s, *data, reply_codes[3] = { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 };
|
||||
unsigned long data_len;
|
||||
unsigned long host_len = (host ? strlen(host) : (sizeof("0.0.0.0") - 1));
|
||||
unsigned long packet_len = host_len + (sizeof("tcpip-forward") - 1) + 14;
|
||||
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting tcpip-forward session for %s:%d", host, port);
|
||||
#endif
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
|
||||
@@ -292,59 +301,61 @@ LIBSSH2_API LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_REQUEST_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
LIBSSH2_LISTENER *listener;
|
||||
|
||||
listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER));
|
||||
if (!listener) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return NULL;
|
||||
}
|
||||
memset(listener, 0, sizeof(LIBSSH2_LISTENER));
|
||||
listener->session = session;
|
||||
listener->host = LIBSSH2_ALLOC(session, host_len + 1);
|
||||
if (!listener->host) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
|
||||
LIBSSH2_FREE(session, listener);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(listener->host, host ? host : "0.0.0.0", host_len);
|
||||
listener->host[host_len] = 0;
|
||||
if (data_len >= 5 && !port) {
|
||||
listener->port = libssh2_ntohu32(data + 1);
|
||||
} else {
|
||||
listener->port = port;
|
||||
}
|
||||
|
||||
listener->queue_size = 0;
|
||||
listener->queue_maxsize = queue_maxsize;
|
||||
|
||||
listener->next = session->listeners;
|
||||
listener->prev = NULL;
|
||||
if (session->listeners) {
|
||||
session->listeners->prev = listener;
|
||||
}
|
||||
session->listeners = listener;
|
||||
|
||||
if (bound_port) {
|
||||
*bound_port = listener->port;
|
||||
}
|
||||
if (data[0] == SSH_MSG_REQUEST_SUCCESS) {
|
||||
LIBSSH2_LISTENER *listener;
|
||||
|
||||
listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER));
|
||||
if (!listener) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return listener;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_REQUEST_FAILURE, &data, &data_len, 0) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0);
|
||||
return NULL;
|
||||
}
|
||||
memset(listener, 0, sizeof(LIBSSH2_LISTENER));
|
||||
listener->session = session;
|
||||
listener->host = LIBSSH2_ALLOC(session, host_len + 1);
|
||||
if (!listener->host) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for listener queue", 0);
|
||||
LIBSSH2_FREE(session, listener);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(listener->host, host ? host : "0.0.0.0", host_len);
|
||||
listener->host[host_len] = 0;
|
||||
if (data_len >= 5 && !port) {
|
||||
listener->port = libssh2_ntohu32(data + 1);
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Dynamic tcpip-forward port allocated: %d", listener->port);
|
||||
#endif
|
||||
} else {
|
||||
listener->port = port;
|
||||
}
|
||||
|
||||
listener->queue_size = 0;
|
||||
listener->queue_maxsize = queue_maxsize;
|
||||
|
||||
listener->next = session->listeners;
|
||||
listener->prev = NULL;
|
||||
if (session->listeners) {
|
||||
session->listeners->prev = listener;
|
||||
}
|
||||
session->listeners = listener;
|
||||
|
||||
if (bound_port) {
|
||||
*bound_port = listener->port;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
return listener;
|
||||
}
|
||||
|
||||
if (data[0] == SSH_MSG_REQUEST_FAILURE) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, "Unable to complete request for forward-listen", 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -364,6 +375,10 @@ LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener)
|
||||
unsigned long packet_len = host_len + 14 + sizeof("cancel-tcpip-forward") - 1;
|
||||
/* packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + port(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Cancelling tcpip-forward session for %s:%d", listener->host, listener->port);
|
||||
#endif
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
|
||||
@@ -451,10 +466,15 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *li
|
||||
LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len)
|
||||
{
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *s, *packet;
|
||||
unsigned long packet_len = varname_len + value_len + 21; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" +
|
||||
unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
|
||||
unsigned long data_len;
|
||||
unsigned long packet_len = varname_len + value_len + 21; /* packet_type(1) + channel_id(4) + request_len(4) + request(3)"env" +
|
||||
want_reply(1) + varname_len(4) + value_len(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Setting remote environment variable: %s=%s on channel %lu/%lu", varname, value, channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
|
||||
@@ -481,26 +501,18 @@ LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varnam
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
unsigned char local_channel[4];
|
||||
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 0) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-setenv", 0);
|
||||
return -1;
|
||||
}
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Never reached, just giving the compiler something to not complain about */
|
||||
if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-setenv", 0);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -514,10 +526,15 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
|
||||
int width_px, int height_px)
|
||||
{
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *s, *packet;
|
||||
unsigned long packet_len = term_len + modes_len + 41; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) +
|
||||
unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
|
||||
unsigned long data_len;
|
||||
unsigned long packet_len = term_len + modes_len + 41; /* packet_type(1) + channel(4) + pty_req_len(4) + "pty_req"(7) + want_reply(1) +
|
||||
term_len(4) + width(4) + height(4) + width_px(4) + height_px(4) + modes_len(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocating tty on channel %lu/%lu", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0);
|
||||
@@ -553,26 +570,18 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
unsigned char local_channel[4];
|
||||
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel request-pty", 0);
|
||||
return -1;
|
||||
}
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Never reached, just giving the compiler something to not complain about */
|
||||
if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel request-pty", 0);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -585,11 +594,19 @@ LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *t
|
||||
LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, char *auth_proto, char *auth_cookie, int screen_number)
|
||||
{
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *s, *packet;
|
||||
unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
|
||||
unsigned long data_len;
|
||||
unsigned long proto_len = auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1);
|
||||
unsigned long cookie_len = auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN;
|
||||
unsigned long packet_len = proto_len + cookie_len + 41; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) +
|
||||
single_cnx(4) + proto_len(4) + cookie_len(4) + screen_num(4) */
|
||||
unsigned long packet_len = proto_len + cookie_len + 30; /* packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + want_reply(1) +
|
||||
single_cnx(1) + proto_len(4) + cookie_len(4) + screen_num(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Requesting x11-req for channel %lu/%lu: single=%d proto=%s cookie=%s screen=%d",
|
||||
channel->local.id, channel->remote.id, single_connection,
|
||||
auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1",
|
||||
auth_cookie ? auth_cookie : "<random>", screen_number);
|
||||
#endif
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
@@ -609,7 +626,7 @@ LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_
|
||||
memcpy(s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", proto_len);
|
||||
s += proto_len;
|
||||
|
||||
libssh2_htonu32(s, cookie_len);
|
||||
libssh2_htonu32(s, cookie_len); s += 4;
|
||||
if (auth_cookie) {
|
||||
memcpy(s, auth_cookie, cookie_len);
|
||||
} else {
|
||||
@@ -626,32 +643,25 @@ LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_
|
||||
libssh2_htonu32(s, screen_number); s += 4;
|
||||
|
||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send pty-request packet", 0);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send x11-req packet", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
unsigned char local_channel[4];
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel x11-req", 0);
|
||||
return -1;
|
||||
}
|
||||
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Never reached, just giving the compiler something to not complain about */
|
||||
if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel x11-req", 0);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -659,16 +669,21 @@ LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_
|
||||
/* {{{ libssh2_channel_process_startup
|
||||
* Primitive for libssh2_channel_(shell|exec|subsystem)
|
||||
*/
|
||||
LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *request, int request_len, char *message, int message_len)
|
||||
LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, int request_len, const char *message, int message_len)
|
||||
{
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *s, *packet;
|
||||
unsigned char *s, *packet, *data, reply_codes[3] = { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }, local_channel[4];
|
||||
unsigned long data_len;
|
||||
unsigned long packet_len = request_len + 10; /* packet_type(1) + channel(4) + request_len(4) + want_reply(1) */
|
||||
|
||||
if (message) {
|
||||
packet_len += message_len + 4;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "starting request(%s) on channel %lu/%lu, message=%s", request, channel->local.id, channel->remote.id, message);
|
||||
#endif
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0);
|
||||
@@ -694,26 +709,18 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
unsigned char local_channel[4];
|
||||
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_SUCCESS, &data, &data_len, 1, local_channel, 4, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_FAILURE, &data, &data_len, 1, local_channel, 4, 0) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-process-startup", 0);
|
||||
return -1;
|
||||
}
|
||||
libssh2_htonu32(local_channel, channel->local.id);
|
||||
if (libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, 1, local_channel, 4)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Never reached, just giving the compiler something to not complain about */
|
||||
if (data[0] == SSH_MSG_CHANNEL_SUCCESS) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, "Unable to complete request for channel-process-startup", 0);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -723,6 +730,9 @@ LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *
|
||||
*/
|
||||
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking)
|
||||
{
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting blocking mode on channel %lu/%lu to %d", channel->local.id, channel->remote.id, blocking);
|
||||
#endif
|
||||
channel->blocking = blocking;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -743,13 +753,19 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
|
||||
if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
||||
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
|
||||
/* It's our channel at least */
|
||||
unsigned long packet_stream_id = (packet_type == SSH_MSG_CHANNEL_DATA) ? 0 : libssh2_ntohu32(packet->data + 5);
|
||||
if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) ||
|
||||
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid = libssh2_ntohu32(packet->data + 5)))) ||
|
||||
((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) || (streamid == packet_stream_id))) ||
|
||||
((packet_type == SSH_MSG_CHANNEL_DATA) && (streamid == 0))) {
|
||||
int bytes_to_flush = packet->data_len - packet->data_head;
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Flushing %d bytes of data from stream %lu on channel %lu/%lu", bytes_to_flush,
|
||||
packet_stream_id, channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
/* It's one of the streams we wanted to flush */
|
||||
refund_bytes += packet->data_len - 13;
|
||||
flush_bytes += packet->data_len - packet->data_head;
|
||||
flush_bytes += bytes_to_flush;
|
||||
|
||||
LIBSSH2_FREE(channel->session, packet->data);
|
||||
if (packet->prev) {
|
||||
@@ -768,26 +784,70 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
|
||||
packet = next;
|
||||
}
|
||||
|
||||
if (refund_bytes && channel->remote.window_size_initial) {
|
||||
unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */
|
||||
|
||||
/* Adjust the window based on the block we just freed */
|
||||
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
|
||||
libssh2_htonu32(adjust + 1, channel->remote.id);
|
||||
libssh2_htonu32(adjust + 5, refund_bytes);
|
||||
|
||||
if (libssh2_packet_write(channel->session, adjust, 9)) {
|
||||
libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet", 0);
|
||||
return -1;
|
||||
} else {
|
||||
channel->remote.window_size += refund_bytes;
|
||||
}
|
||||
if (refund_bytes) {
|
||||
libssh2_channel_receive_window_adjust(channel, refund_bytes, 0);
|
||||
}
|
||||
|
||||
return flush_bytes;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_get_exit_status
|
||||
* Return the channel's program exit status
|
||||
*/
|
||||
LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel)
|
||||
{
|
||||
return channel->exit_status;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_receive_window_adjust
|
||||
* Adjust the receive window for a channel by adjustment bytes
|
||||
* If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and force is 0
|
||||
* The adjustment amount will be queued for a later packet
|
||||
*
|
||||
* Returns the new size of the receive window (as understood by remote end)
|
||||
*/
|
||||
LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force)
|
||||
{
|
||||
unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */
|
||||
|
||||
if (!force && (adjustment + channel->adjust_queue < LIBSSH2_CHANNEL_MINADJUST)) {
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Queing %lu bytes for receive window adjustment for channel %lu/%lu", adjustment, channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
channel->adjust_queue += adjustment;
|
||||
return channel->remote.window_size;
|
||||
}
|
||||
|
||||
if (!adjustment && !channel->adjust_queue) {
|
||||
return channel->remote.window_size;
|
||||
}
|
||||
|
||||
adjustment += channel->adjust_queue;
|
||||
channel->adjust_queue = 0;
|
||||
|
||||
|
||||
/* Adjust the window based on the block we just freed */
|
||||
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
|
||||
libssh2_htonu32(adjust + 1, channel->remote.id);
|
||||
libssh2_htonu32(adjust + 5, adjustment);
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Adjusting window %lu bytes for data flushed from channel %lu/%lu", adjustment, channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
if (libssh2_packet_write(channel->session, adjust, 9)) {
|
||||
libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet, deferring", 0);
|
||||
channel->adjust_queue = adjustment;
|
||||
} else {
|
||||
channel->remote.window_size += adjustment;
|
||||
}
|
||||
|
||||
return channel->remote.window_size;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_handle_extended_data
|
||||
* How should extended data look to the calling app?
|
||||
* Keep it in separate channels[_read() _read_stdder()]? (NORMAL)
|
||||
@@ -796,6 +856,9 @@ LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid)
|
||||
*/
|
||||
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode)
|
||||
{
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(channel->session, LIBSSH2_DBG_CONN, "Setting channel %lu/%lu handle_extended_data mode to %d", channel->local.id, channel->remote.id, ignore_mode);
|
||||
#endif
|
||||
channel->remote.extended_data_ignore_mode = ignore_mode;
|
||||
|
||||
if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) {
|
||||
@@ -812,6 +875,9 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
int bytes_read = 0, blocking_read = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Attempting to read %d bytes from channel %lu/%lu stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
do {
|
||||
LIBSSH2_PACKET *packet;
|
||||
|
||||
@@ -838,13 +904,14 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
unlink_packet = 1;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Reading %d of buffered data from %lu/%lu/%d", want, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
memcpy(buf + bytes_read, packet->data + packet->data_head, want);
|
||||
packet->data_head += want;
|
||||
bytes_read += want;
|
||||
|
||||
if (unlink_packet) {
|
||||
unsigned char adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */
|
||||
|
||||
if (packet->prev) {
|
||||
packet->prev->next = packet->next;
|
||||
} else {
|
||||
@@ -857,18 +924,10 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
}
|
||||
LIBSSH2_FREE(session, packet->data);
|
||||
|
||||
/* Adjust the window based on the block we just freed */
|
||||
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
|
||||
libssh2_htonu32(adjust + 1, channel->remote.id);
|
||||
libssh2_htonu32(adjust + 5, packet->data_len - (stream_id ? 13 : 9));
|
||||
|
||||
if (libssh2_packet_write(session, adjust, 9)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet", 0);
|
||||
} else {
|
||||
/* Don't forget to acknowledge the adjust on this end */
|
||||
channel->remote.window_size += (packet->data_len - (stream_id ? 13 : 9));
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Unlinking empty packet buffer from channel %lu/%lu", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
libssh2_channel_receive_window_adjust(channel, packet->data_len - (stream_id ? 13 : 9), 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
}
|
||||
}
|
||||
@@ -891,9 +950,12 @@ LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id,
|
||||
LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen)
|
||||
{
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *packet, *s;
|
||||
unsigned long packet_len;
|
||||
unsigned char *packet;
|
||||
unsigned long packet_len, bufwrote = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Writing %d bytes on channel %lu/%lu, stream #%d", (int)buflen, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
if (channel->local.close) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0);
|
||||
return -1;
|
||||
@@ -903,48 +965,78 @@ LIBSSH2_API int libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_EOF_SENT, "EOF has already been sight, data might be ignored", 0);
|
||||
}
|
||||
|
||||
if (channel->blocking && (channel->local.window_size <= 0)) {
|
||||
/* twiddle our thumbs until there's window space available */
|
||||
if (libssh2_packet_read(session, 1) < 0) {
|
||||
/* Error occured, disconnect? */
|
||||
return -1;
|
||||
}
|
||||
if (!channel->blocking && (channel->local.window_size <= 0)) {
|
||||
/* Can't write anything */
|
||||
return 0;
|
||||
}
|
||||
|
||||
packet_len = buflen + (stream_id ? 13 : 9); /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocte space for data transmission packet", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA;
|
||||
libssh2_htonu32(s, channel->remote.id); s += 4;
|
||||
if (stream_id) {
|
||||
libssh2_htonu32(s, stream_id); s += 4;
|
||||
}
|
||||
while (buflen > 0) {
|
||||
size_t bufwrite = buflen;
|
||||
unsigned char *s = packet;
|
||||
|
||||
/* Don't exceed the remote end's limits */
|
||||
/* REMEMBER local means local as the SOURCE of the data */
|
||||
if (buflen > channel->local.window_size) {
|
||||
buflen = channel->local.window_size;
|
||||
}
|
||||
if (buflen > channel->local.packet_size) {
|
||||
buflen = channel->local.packet_size;
|
||||
}
|
||||
libssh2_htonu32(s, buflen); s += 4;
|
||||
memcpy(s, buf, buflen); s += buflen;
|
||||
*(s++) = stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA;
|
||||
libssh2_htonu32(s, channel->remote.id); s += 4;
|
||||
if (stream_id) {
|
||||
libssh2_htonu32(s, stream_id); s += 4;
|
||||
}
|
||||
|
||||
if (libssh2_packet_write(session, packet, s - packet)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
|
||||
return -1;
|
||||
/* twiddle our thumbs until there's window space available */
|
||||
while (channel->local.window_size <= 0) {
|
||||
/* Don't worry -- This is never hit unless it's a blocking channel anyway */
|
||||
if (libssh2_packet_read(session, 1) < 0) {
|
||||
/* Error occured, disconnect? */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't exceed the remote end's limits */
|
||||
/* REMEMBER local means local as the SOURCE of the data */
|
||||
if (bufwrite > channel->local.window_size) {
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte window_size on %lu/%lu/%d", channel->local.window_size, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
bufwrite = channel->local.window_size;
|
||||
}
|
||||
if (bufwrite > channel->local.packet_size) {
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Splitting write block due to %lu byte packet_size on %lu/%lu/%d", channel->local.packet_size, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
bufwrite = channel->local.packet_size;
|
||||
}
|
||||
libssh2_htonu32(s, bufwrite); s += 4;
|
||||
memcpy(s, buf, bufwrite); s += bufwrite;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending %d bytes on channel %lu/%lu, stream_id=%d", (int)bufwrite, channel->local.id, channel->remote.id, stream_id);
|
||||
#endif
|
||||
if (libssh2_packet_write(session, packet, s - packet)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
/* Shrink local window size */
|
||||
channel->local.window_size -= bufwrite;
|
||||
|
||||
/* Adjust buf for next iteration */
|
||||
buflen -= bufwrite;
|
||||
buf += bufwrite;
|
||||
bufwrote += bufwrite;
|
||||
|
||||
if (!channel->blocking) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Shrink local window size */
|
||||
channel->local.window_size -= buflen;
|
||||
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
return buflen;
|
||||
return bufwrote;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -956,6 +1048,9 @@ LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel)
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char packet[5]; /* packet_type(1) + channelno(4) */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending EOF on channel %lu/%lu",channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
packet[0] = SSH_MSG_CHANNEL_EOF;
|
||||
libssh2_htonu32(packet + 1, channel->remote.id);
|
||||
if (libssh2_packet_write(session, packet, 5)) {
|
||||
@@ -977,7 +1072,7 @@ LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel)
|
||||
LIBSSH2_PACKET *packet = session->packets.head;
|
||||
|
||||
while (packet) {
|
||||
if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) || (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
||||
if (((packet->data[0] == SSH_MSG_CHANNEL_DATA) || (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
||||
(channel->local.id == libssh2_ntohu32(packet->data + 1))) {
|
||||
/* There's data waiting to be read yet, mask the EOF status */
|
||||
return 0;
|
||||
@@ -1002,9 +1097,14 @@ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Closing channel %lu/%lu", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
if (channel->close_cb) {
|
||||
LIBSSH2_CHANNEL_CLOSE(session, channel);
|
||||
}
|
||||
channel->local.close = 1;
|
||||
|
||||
packet[0] = SSH_MSG_CHANNEL_CLOSE;
|
||||
libssh2_htonu32(packet + 1, channel->remote.id);
|
||||
@@ -1012,7 +1112,6 @@ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel)
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send close-channel request", 0);
|
||||
return -1;
|
||||
}
|
||||
channel->local.close = 1;
|
||||
|
||||
/* TODO: Wait up to a timeout value for a CHANNEL_CLOSE to come back, to avoid the problem alluded to in channel_nextid */
|
||||
|
||||
@@ -1020,6 +1119,35 @@ LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_wait_closed
|
||||
* Awaiting channel close after EOF
|
||||
*/
|
||||
LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel)
|
||||
{
|
||||
LIBSSH2_SESSION* session = channel->session;
|
||||
|
||||
if (!libssh2_channel_eof(channel)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_INVAL, "libssh2_channel_wait_closed() invoked when channel is not in EOF state", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Awaiting close of channel %lu/%lu", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
/* while channel is not closed, read more
|
||||
* packets from the network.
|
||||
* Either or channel will be closed
|
||||
* or network timeout will occur
|
||||
*/
|
||||
while (!channel->remote.close && libssh2_packet_read(session, 1) > 0)
|
||||
;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* {{{ libssh2_channel_free
|
||||
* Make sure a channel is closed, then remove the channel from the session and free its resource(s)
|
||||
*/
|
||||
@@ -1029,13 +1157,16 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
|
||||
unsigned char channel_id[4], *data;
|
||||
unsigned long data_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Freeing channel %lu/%lu resources", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
/* Allow channel freeing even when the socket has lost its connection */
|
||||
if (!channel->local.close && (session->socket_state == LIBSSH2_SOCKET_CONNECTED) &&
|
||||
libssh2_channel_close(channel)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* channel->remote.close *might* not be set yet, Well...
|
||||
/* channel->remote.close *might* not be set yet, Well...
|
||||
* We've sent the close packet, what more do you want?
|
||||
* Just let packet_add ignore it when it finally arrives
|
||||
*/
|
||||
@@ -1069,3 +1200,53 @@ LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_window_read_ex
|
||||
* Check the status of the read window
|
||||
* Returns the number of bytes which the remote end may send without overflowing the window limit
|
||||
* read_avail (if passed) will be populated with the number of bytes actually available to be read
|
||||
* window_size_initial (if passed) will be populated with the window_size_initial as defined by the channel_open request
|
||||
*/
|
||||
LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial)
|
||||
{
|
||||
if (window_size_initial) {
|
||||
*window_size_initial = channel->remote.window_size_initial;
|
||||
}
|
||||
|
||||
if (read_avail) {
|
||||
unsigned long bytes_queued = 0;
|
||||
LIBSSH2_PACKET *packet = channel->session->packets.head;
|
||||
|
||||
while (packet) {
|
||||
unsigned char packet_type = packet->data[0];
|
||||
|
||||
if (((packet_type == SSH_MSG_CHANNEL_DATA) || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) &&
|
||||
(libssh2_ntohu32(packet->data + 1) == channel->local.id)) {
|
||||
bytes_queued += packet->data_len - packet->data_head;
|
||||
}
|
||||
|
||||
packet = packet->next;
|
||||
}
|
||||
|
||||
*read_avail = bytes_queued;
|
||||
}
|
||||
|
||||
return channel->remote.window_size;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_channel_window_write_ex
|
||||
* Check the status of the write window
|
||||
* Returns the number of bytes which may be safely writen on the channel without blocking
|
||||
* window_size_initial (if passed) will be populated with the size of the initial window as defined by the channel_open request
|
||||
*/
|
||||
LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial)
|
||||
{
|
||||
if (window_size_initial) {
|
||||
/* For locally initiated channels this is very often 0, so it's not *that* useful as information goes */
|
||||
*window_size_initial = channel->local.window_size_initial;
|
||||
}
|
||||
|
||||
return channel->local.window_size;
|
||||
}
|
||||
/* }}} */
|
||||
|
@@ -109,7 +109,7 @@ static int libssh2_hostkey_method_ssh_rsadsa_passphrase_cb(char *buf, int size,
|
||||
/* {{{ libssh2_hostkey_method_ssh_rsa_initPEM
|
||||
* Load a Private Key from a PEM file
|
||||
*/
|
||||
static int libssh2_hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION *session, unsigned char *privkeyfile, unsigned char *passphrase, void **abstract)
|
||||
static int libssh2_hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION *session, unsigned const char *privkeyfile, unsigned const char *passphrase, void **abstract)
|
||||
{
|
||||
RSA *rsactx;
|
||||
FILE *fp;
|
||||
@@ -131,7 +131,7 @@ static int libssh2_hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION *session, unsi
|
||||
*/
|
||||
OpenSSL_add_all_ciphers();
|
||||
}
|
||||
rsactx = PEM_read_RSAPrivateKey(fp, NULL, (void*)libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, passphrase);
|
||||
rsactx = PEM_read_RSAPrivateKey(fp, NULL, (void*)libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, (void*)passphrase);
|
||||
if (!rsactx) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
@@ -323,7 +323,7 @@ static int libssh2_hostkey_method_ssh_dss_init(LIBSSH2_SESSION *session, unsigne
|
||||
/* {{{ libssh2_hostkey_method_ssh_dss_initPEM
|
||||
* Load a Private Key from a PEM file
|
||||
*/
|
||||
static int libssh2_hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION *session, unsigned char *privkeyfile, unsigned char *passphrase, void **abstract)
|
||||
static int libssh2_hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION *session, unsigned const char *privkeyfile, unsigned const char *passphrase, void **abstract)
|
||||
{
|
||||
DSA *dsactx;
|
||||
FILE *fp;
|
||||
@@ -345,7 +345,7 @@ static int libssh2_hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION *session, unsi
|
||||
*/
|
||||
OpenSSL_add_all_ciphers();
|
||||
}
|
||||
dsactx = PEM_read_DSAPrivateKey(fp, NULL, (void*)libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, passphrase);
|
||||
dsactx = PEM_read_DSAPrivateKey(fp, NULL, (void*)libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, (void*)passphrase);
|
||||
if (!dsactx) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
@@ -523,7 +523,7 @@ LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void)
|
||||
* Length of buffer is determined by hash type
|
||||
* i.e. MD5 == 16, SHA1 == 20
|
||||
*/
|
||||
LIBSSH2_API char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type)
|
||||
LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type)
|
||||
{
|
||||
switch (hash_type) {
|
||||
#ifndef OPENSSL_NO_MD5
|
||||
|
123
src/kex.c
123
src/kex.c
@@ -68,7 +68,7 @@
|
||||
/* {{{ libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange
|
||||
* Diffie Hellman Key Exchange, Group Agnostic
|
||||
*/
|
||||
static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, BIGNUM *g, BIGNUM *p,
|
||||
static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION *session, BIGNUM *g, BIGNUM *p, int group_order,
|
||||
unsigned char packet_type_init, unsigned char packet_type_reply,
|
||||
unsigned char *midhash, unsigned long midhash_len)
|
||||
{
|
||||
@@ -85,7 +85,7 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
SHA_CTX exchange_hash;
|
||||
|
||||
/* Generate x and e */
|
||||
BN_rand(x, 128, 0, -1);
|
||||
BN_rand(x, group_order, 0, -1);
|
||||
BN_mod_exp(e, g, x, p, ctx);
|
||||
|
||||
/* Send KEX init */
|
||||
@@ -109,6 +109,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
BN_bn2bin(e, e_packet + 6);
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", (int)packet_type_init);
|
||||
#endif
|
||||
if (libssh2_packet_write(session, e_packet, e_packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEX init message", 0);
|
||||
ret = -11;
|
||||
@@ -143,7 +146,18 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
MD5_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len);
|
||||
MD5_Final(session->server_hostkey_md5, &fingerprint_ctx);
|
||||
}
|
||||
#endif
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
{
|
||||
char fingerprint[50], *fprint = fingerprint;
|
||||
int i;
|
||||
for(i = 0; i < 16; i++, fprint += 3) {
|
||||
snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]);
|
||||
}
|
||||
*(--fprint) = '\0';
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's MD5 Fingerprint: %s", fingerprint);
|
||||
}
|
||||
#endif /* LIBSSH2_DEBUG_KEX */
|
||||
#endif /* ! OPENSSL_NO_MD5 */
|
||||
|
||||
#ifndef OPENSSL_NO_SHA
|
||||
{
|
||||
@@ -153,7 +167,18 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
SHA1_Update(&fingerprint_ctx, session->server_hostkey, session->server_hostkey_len);
|
||||
SHA1_Final(session->server_hostkey_sha1, &fingerprint_ctx);
|
||||
}
|
||||
#endif
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
{
|
||||
char fingerprint[64], *fprint = fingerprint;
|
||||
int i;
|
||||
for(i = 0; i < 20; i++, fprint += 3) {
|
||||
snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]);
|
||||
}
|
||||
*(--fprint) = '\0';
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server's SHA1 Fingerprint: %s", fingerprint);
|
||||
}
|
||||
#endif /* LIBSSH2_DEBUG_KEX */
|
||||
#endif /* ! OPENSSL_NO_SHA */
|
||||
|
||||
if (session->hostkey->init(session, session->server_hostkey, session->server_hostkey_len, &session->server_hostkey_abstract)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, "Unable to initialize hostkey importer", 0);
|
||||
@@ -161,7 +186,6 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
|
||||
f_value_len = libssh2_ntohu32(s); s += 4;
|
||||
f_value = s; s += f_value_len;
|
||||
BN_bin2bn(f_value, f_value_len, f);
|
||||
@@ -250,6 +274,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message");
|
||||
#endif
|
||||
c = SSH_MSG_NEWKEYS;
|
||||
if (libssh2_packet_write(session, &c, 1)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send NEWKEYS message", 0);
|
||||
@@ -264,6 +291,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
}
|
||||
/* The first key exchange has been performed, switch to active crypt/comp/mac mode */
|
||||
session->state |= LIBSSH2_STATE_NEWKEYS;
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message");
|
||||
#endif
|
||||
|
||||
/* This will actually end up being just packet_type(1) for this packet type anyway */
|
||||
LIBSSH2_FREE(session, tmp);
|
||||
@@ -276,6 +306,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
}
|
||||
memcpy(session->session_id, h_sig_comp, SHA_DIGEST_LENGTH);
|
||||
session->session_id_len = SHA_DIGEST_LENGTH;
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "session_id calculated");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Calculate IV/Secret/Key for each direction */
|
||||
@@ -327,6 +360,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
LIBSSH2_FREE(session, secret);
|
||||
}
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server IV and Key calculated");
|
||||
#endif
|
||||
|
||||
if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
|
||||
if (session->remote.crypt_abstract) {
|
||||
@@ -376,6 +412,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
LIBSSH2_FREE(session, secret);
|
||||
}
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client IV and Key calculated");
|
||||
#endif
|
||||
|
||||
if (session->local.mac->dtor) {
|
||||
session->local.mac->dtor(session, &session->local.mac_abstract);
|
||||
@@ -393,6 +432,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
LIBSSH2_FREE(session, key);
|
||||
}
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Client to Server HMAC Key calculated");
|
||||
#endif
|
||||
|
||||
if (session->remote.mac->dtor) {
|
||||
session->remote.mac->dtor(session, &session->remote.mac_abstract);
|
||||
@@ -410,6 +452,9 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
LIBSSH2_FREE(session, key);
|
||||
}
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Server to Client HMAC Key calculated");
|
||||
#endif
|
||||
|
||||
clean_exit:
|
||||
BN_clear_free(x);
|
||||
@@ -470,7 +515,10 @@ static int libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SE
|
||||
BN_set_word(g, 2);
|
||||
BN_bin2bn(p_value, 128, p);
|
||||
|
||||
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0);
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group1 Key Exchange");
|
||||
#endif
|
||||
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, 128, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0);
|
||||
|
||||
BN_clear_free(p);
|
||||
BN_clear_free(g);
|
||||
@@ -526,7 +574,10 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S
|
||||
BN_set_word(g, 2);
|
||||
BN_bin2bn(p_value, 256, p);
|
||||
|
||||
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0);
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group14 Key Exchange");
|
||||
#endif
|
||||
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, 256, SSH_MSG_KEXDH_INIT, SSH_MSG_KEXDH_REPLY, NULL, 0);
|
||||
|
||||
BN_clear_free(p);
|
||||
BN_clear_free(g);
|
||||
@@ -542,7 +593,7 @@ static int libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_S
|
||||
static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LIBSSH2_SESSION *session)
|
||||
{
|
||||
unsigned char request[13], *s, *data;
|
||||
unsigned long data_len, len, request_len;
|
||||
unsigned long data_len, p_len, g_len, request_len;
|
||||
BIGNUM *p = BN_new();
|
||||
BIGNUM *g = BN_new();
|
||||
int ret;
|
||||
@@ -554,10 +605,16 @@ static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LI
|
||||
libssh2_htonu32(request + 5, LIBSSH2_DH_GEX_OPTGROUP);
|
||||
libssh2_htonu32(request + 9, LIBSSH2_DH_GEX_MAXGROUP);
|
||||
request_len = 13;
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (New Method)");
|
||||
#endif
|
||||
#else
|
||||
request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD;
|
||||
libssh2_htonu32(request + 1, LIBSSH2_DH_GEX_OPTGROUP);
|
||||
request_len = 5;
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Initiating Diffie-Hellman Group-Exchange (Old Method)");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (libssh2_packet_write(session, request, request_len)) {
|
||||
@@ -573,13 +630,13 @@ static int libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange(LI
|
||||
}
|
||||
|
||||
s = data + 1;
|
||||
len = libssh2_ntohu32(s); s += 4;
|
||||
BN_bin2bn(s, len, p); s += len;
|
||||
p_len = libssh2_ntohu32(s); s += 4;
|
||||
BN_bin2bn(s, p_len, p); s += p_len;
|
||||
|
||||
len = libssh2_ntohu32(s); s += 4;
|
||||
BN_bin2bn(s, len, g); s += len;
|
||||
g_len = libssh2_ntohu32(s); s += 4;
|
||||
BN_bin2bn(s, g_len, g); s += g_len;
|
||||
|
||||
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, data + 1, data_len - 1);
|
||||
ret = libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, g, p, p_len, SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, data + 1, data_len - 1);
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
@@ -746,6 +803,23 @@ static int libssh2_kexinit(LIBSSH2_SESSION *session)
|
||||
*(s++) = 0;
|
||||
*(s++) = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
{
|
||||
/* Funnily enough, they'll all "appear" to be '\0' terminated */
|
||||
char *p = data + 21; /* type(1) + cookie(16) + len(4) */
|
||||
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); p += kex_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); p += hostkey_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); p += crypt_cs_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); p += crypt_sc_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); p += mac_cs_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); p += mac_sc_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); p += comp_cs_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); p += comp_sc_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); p += lang_cs_len + 4;
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); p += lang_sc_len + 4;
|
||||
}
|
||||
#endif /* LIBSSH2_DEBUG_KEX */
|
||||
if (libssh2_packet_write(session, data, data_len)) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send KEXINIT packet to remote host", 0);
|
||||
@@ -854,7 +928,7 @@ static int libssh2_kex_agree_hostkey(LIBSSH2_SESSION *session, unsigned long kex
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((*hostkeyp)->name) {
|
||||
while (hostkeyp && (*hostkeyp)->name) {
|
||||
s = libssh2_kex_agree_instr(hostkey, hostkey_len, (*hostkeyp)->name, strlen((*hostkeyp)->name));
|
||||
if (s) {
|
||||
/* So far so good, but does it suit our purposes? (Encrypting vs Signing) */
|
||||
@@ -962,7 +1036,7 @@ static int libssh2_kex_agree_crypt(LIBSSH2_SESSION *session, libssh2_endpoint_da
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((*cryptp)->name) {
|
||||
while (*cryptp && (*cryptp)->name) {
|
||||
s = libssh2_kex_agree_instr(crypt, crypt_len, (*cryptp)->name, strlen((*cryptp)->name));
|
||||
if (s) {
|
||||
endpoint->crypt = *cryptp;
|
||||
@@ -1007,7 +1081,7 @@ static int libssh2_kex_agree_mac(LIBSSH2_SESSION *session, libssh2_endpoint_data
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((*macp)->name) {
|
||||
while (*macp && (*macp)->name) {
|
||||
s = libssh2_kex_agree_instr(mac, mac_len, (*macp)->name, strlen((*macp)->name));
|
||||
if (s) {
|
||||
endpoint->mac = *macp;
|
||||
@@ -1052,7 +1126,7 @@ static int libssh2_kex_agree_comp(LIBSSH2_SESSION *session, libssh2_endpoint_dat
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((*compp)->name) {
|
||||
while (*compp && (*compp)->name) {
|
||||
s = libssh2_kex_agree_instr(comp, comp_len, (*compp)->name, strlen((*compp)->name));
|
||||
if (s) {
|
||||
endpoint->comp = *compp;
|
||||
@@ -1120,6 +1194,19 @@ static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *da
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on KEX method: %s", session->kex->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on HOSTKEY method: %s", session->hostkey->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_CS method: %s", session->local.crypt->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_SC method: %s", session->remote.crypt->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_CS method: %s", session->local.mac->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_SC method: %s", session->remote.mac->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_CS method: %s", session->local.comp->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_SC method: %s", session->remote.comp->name);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_CS method:"); /* None yet */
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_SC method:"); /* None yet */
|
||||
#endif
|
||||
|
||||
/* Initialize compression layer */
|
||||
if (session->local.comp && session->local.comp->init &&
|
||||
session->local.comp->init(session, 1, &session->local.comp_abstract)) {
|
||||
@@ -1200,7 +1287,7 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
|
||||
/* {{{ libssh2_session_method_pref
|
||||
* Set preferred method
|
||||
*/
|
||||
LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, char *prefs)
|
||||
LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, const char *prefs)
|
||||
{
|
||||
char **prefvar, *s, *newprefs;
|
||||
int prefs_len = strlen(prefs);
|
||||
|
35
src/misc.c
35
src/misc.c
@@ -169,3 +169,38 @@ LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **data, int
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_ENABLED
|
||||
/* {{{ _libssh2_debug
|
||||
* Internal debug logging facility
|
||||
* Just writes to stderr until a good reason comes up to support anything else
|
||||
*/
|
||||
void _libssh2_debug(LIBSSH2_SESSION *session, int context, const char *format, ...)
|
||||
{
|
||||
char buffer[1536];
|
||||
int len;
|
||||
va_list vargs;
|
||||
char *contexts[9] = { "Unknown",
|
||||
"Transport",
|
||||
"Key Exhange",
|
||||
"Userauth",
|
||||
"Connection",
|
||||
"scp",
|
||||
"SFTP Subsystem",
|
||||
"Failure Event",
|
||||
"Publickey Subsystem",
|
||||
};
|
||||
|
||||
if (context < 1 || context > 8) {
|
||||
context = 0;
|
||||
}
|
||||
|
||||
len = snprintf(buffer, 1535, "[libssh2] %s: ", contexts[context]);
|
||||
|
||||
va_start(vargs, format);
|
||||
len += vsnprintf(buffer + len, 1535 - len, format, vargs);
|
||||
buffer[len] = '\n';
|
||||
va_end(vargs);
|
||||
write(2, buffer, len + 1);
|
||||
}
|
||||
/* }}} */
|
||||
#endif
|
||||
|
263
src/packet.c
263
src/packet.c
@@ -49,6 +49,19 @@
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POLL
|
||||
# include <sys/poll.h>
|
||||
#else
|
||||
# ifdef HAVE_SELECT
|
||||
# ifdef HAVE_SYS_SELECT_H
|
||||
# include <sys/select.h>
|
||||
# else
|
||||
# include <sys/time.h>
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* {{{ libssh2_packet_queue_listener
|
||||
* Queue a connection request for a listener
|
||||
*/
|
||||
@@ -78,6 +91,9 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
shost = s; s += shost_len;
|
||||
sport = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Remote received connection from %s:%ld to %s:%ld", shost, sport, host, port);
|
||||
#endif
|
||||
while (l) {
|
||||
if ((l->port == port) &&
|
||||
(strlen(l->host) == host_len) &&
|
||||
@@ -89,6 +105,9 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
(l->queue_maxsize <= l->queue_size)) {
|
||||
/* Queue is full */
|
||||
failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Listener queue full, ignoring");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -121,6 +140,13 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
channel->local.window_size = initial_window_size;
|
||||
channel->local.packet_size = packet_size;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Connection queued: channel %lu/%lu win %lu/%lu packet %lu/%lu",
|
||||
channel->local.id, channel->remote.id,
|
||||
channel->local.window_size, channel->remote.window_size,
|
||||
channel->local.packet_size, channel->remote.packet_size);
|
||||
#endif
|
||||
|
||||
p = packet;
|
||||
*(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
|
||||
libssh2_htonu32(p, channel->remote.id); p += 4;
|
||||
@@ -130,7 +156,7 @@ inline int libssh2_packet_queue_listener(LIBSSH2_SESSION *session, unsigned char
|
||||
|
||||
if (libssh2_packet_write(session, packet, 17)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0);
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Link the channel into the end of the queue list */
|
||||
@@ -195,6 +221,9 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
|
||||
shost = s; s += shost_len;
|
||||
sport = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection Received from %s:%ld on channel %lu", shost, sport, sender_channel);
|
||||
#endif
|
||||
if (session->x11) {
|
||||
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
|
||||
if (!channel) {
|
||||
@@ -225,6 +254,12 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
|
||||
channel->local.window_size = initial_window_size;
|
||||
channel->local.packet_size = packet_size;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "X11 Connection established: channel %lu/%lu win %lu/%lu packet %lu/%lu",
|
||||
channel->local.id, channel->remote.id,
|
||||
channel->local.window_size, channel->remote.window_size,
|
||||
channel->local.packet_size, channel->remote.packet_size);
|
||||
#endif
|
||||
p = packet;
|
||||
*(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION;
|
||||
libssh2_htonu32(p, channel->remote.id); p += 4;
|
||||
@@ -234,7 +269,7 @@ inline int libssh2_packet_x11_open(LIBSSH2_SESSION *session, unsigned char *data
|
||||
|
||||
if (libssh2_packet_write(session, packet, 17)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel open confirmation", 0);
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Link the channel into the session */
|
||||
@@ -281,6 +316,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
LIBSSH2_PACKET *packet;
|
||||
unsigned long data_head = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Packet type %d received, length=%d", (int)data[0], (int)datalen);
|
||||
#endif
|
||||
if (macstate == LIBSSH2_MAC_INVALID) {
|
||||
if (session->macerror) {
|
||||
if (LIBSSH2_MACERROR(session, data, datalen) == 0) {
|
||||
@@ -329,6 +367,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
if (session->ssh_msg_disconnect) {
|
||||
LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len);
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnect(%d): %s(%s)", reason, message, language);
|
||||
#endif
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
|
||||
return -1;
|
||||
@@ -369,6 +410,10 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
if (session->ssh_msg_debug) {
|
||||
LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len);
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
/* _libssh2_debug will actually truncate this for us so that it's not an inordinate about of data */
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Debug Packet: %s", message);
|
||||
#endif
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
@@ -385,22 +430,27 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
{
|
||||
unsigned long stream_id = 0;
|
||||
|
||||
if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) {
|
||||
stream_id = libssh2_ntohu32(data + 5);
|
||||
}
|
||||
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "%d bytes received for channel %lu/%lu stream #%lu", (int)(datalen - data_head), channel->local.id, channel->remote.id, stream_id);
|
||||
}
|
||||
#endif
|
||||
if ((channel->remote.extended_data_ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) {
|
||||
/* Pretend we didn't receive this */
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
if (channel->remote.window_size_initial) {
|
||||
/* Adjust the window based on the block we just freed */
|
||||
unsigned char adjust[9];
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Ignoring extended data and refunding %d bytes", (int)(datalen - 13));
|
||||
#endif
|
||||
/* Adjust the window based on the block we just freed */
|
||||
libssh2_channel_receive_window_adjust(channel, datalen - 13, 0);
|
||||
|
||||
adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST;
|
||||
libssh2_htonu32(adjust + 1, channel->remote.id);
|
||||
libssh2_htonu32(adjust + 5, datalen - 13);
|
||||
|
||||
if (libssh2_packet_write(channel->session, adjust, 9)) {
|
||||
libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send transfer-window adjustment packet", 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -410,7 +460,7 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "Packet contains more data than we offered to receive, truncating", 0);
|
||||
datalen = channel->remote.packet_size + data_head;
|
||||
}
|
||||
if (channel->remote.window_size_initial && (channel->remote.window_size <= 0)) {
|
||||
if (channel->remote.window_size <= 0) {
|
||||
/* Spec says we MAY ignore bytes sent beyond window_size */
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "The current receive window is full, data ignored", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
@@ -419,7 +469,7 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
/* Reset EOF status */
|
||||
channel->remote.eof = 0;
|
||||
|
||||
if (channel->remote.window_size_initial && ((datalen - data_head) > channel->remote.window_size)) {
|
||||
if ((datalen - data_head) > channel->remote.window_size) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, "Remote sent more data than current window allows, truncating", 0);
|
||||
datalen = channel->remote.window_size + data_head;
|
||||
} else {
|
||||
@@ -438,12 +488,35 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "EOF received for channel %lu/%lu", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
channel->remote.eof = 1;
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case SSH_MSG_CHANNEL_REQUEST:
|
||||
{
|
||||
if (libssh2_ntohu32(data+5) == sizeof("exit-status") - 1
|
||||
&& !memcmp("exit-status", data + 9, sizeof("exit-status") - 1)) {
|
||||
|
||||
/* we've got "exit-status" packet. Set the session value */
|
||||
LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data+1));
|
||||
|
||||
if (channel) {
|
||||
channel->exit_status = libssh2_ntohu32(data + 9 + sizeof("exit-status"));
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Exit status %lu received for channel %lu/%lu", channel->exit_status, channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SSH_MSG_CHANNEL_CLOSE:
|
||||
{
|
||||
LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));
|
||||
@@ -453,6 +526,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Close received for channel %lu/%lu", channel->local.id, channel->remote.id);
|
||||
#endif
|
||||
|
||||
channel->remote.close = 1;
|
||||
/* TODO: Add a callback for this */
|
||||
@@ -487,6 +563,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
if (channel && bytestoadd) {
|
||||
channel->local.window_size += bytestoadd;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_CONNECTION
|
||||
_libssh2_debug(session, LIBSSH2_DBG_CONN, "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", channel->local.id, channel->remote.id, bytestoadd, channel->local.window_size);
|
||||
#endif
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
@@ -519,6 +598,9 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
* Well, it's already in the brigade,
|
||||
* let's just call back into ourselves
|
||||
*/
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys");
|
||||
#endif
|
||||
libssh2_kex_exchange(session, 1);
|
||||
/* If there was a key reexchange failure, let's just hope we didn't send NEWKEYS yet, otherwise remote will drop us like a rock */
|
||||
}
|
||||
@@ -533,7 +615,22 @@ static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, siz
|
||||
static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count)
|
||||
{
|
||||
size_t bytes_read = 0;
|
||||
#if !defined(HAVE_POLL) && !defined(HAVE_SELECT)
|
||||
int polls = 0;
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
fcntl(session->socket_fd, F_SETFL, 0);
|
||||
#else
|
||||
{
|
||||
u_long block = FALSE;
|
||||
ioctlsocket(session->socket_fd, FIONBIO, &block);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes", (int)count);
|
||||
#endif
|
||||
|
||||
while (bytes_read < count) {
|
||||
int ret;
|
||||
@@ -542,18 +639,42 @@ static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, s
|
||||
if (ret < 0) {
|
||||
#ifdef WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEWOULDBLOCK: errno = EAGAIN;
|
||||
case WSAEWOULDBLOCK: errno = EAGAIN; break;
|
||||
case WSAENOTCONN:
|
||||
case WSAENOTSOCK:
|
||||
case WSAECONNABORTED: errno = EBADF;
|
||||
case WSAEINTR: errno = EINTR;
|
||||
case WSAECONNABORTED: errno = EBADF; break;
|
||||
case WSAEINTR: errno = EINTR; break;
|
||||
}
|
||||
#endif
|
||||
if (errno == EAGAIN) {
|
||||
#ifdef HAVE_POLL
|
||||
struct pollfd read_socket;
|
||||
|
||||
read_socket.fd = session->socket_fd;
|
||||
read_socket.events = POLLIN;
|
||||
|
||||
if (poll(&read_socket, 1, 30000) <= 0) {
|
||||
return -1;
|
||||
}
|
||||
#elif defined(HAVE_SELECT)
|
||||
fd_set read_socket;
|
||||
struct timeval timeout;
|
||||
|
||||
FD_ZERO(&read_socket);
|
||||
FD_SET(session->socket_fd, &read_socket);
|
||||
|
||||
timeout.tv_sec = 30;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
if (select(session->socket_fd + 1, &read_socket, NULL, NULL, &timeout) <= 0) {
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
|
||||
return -1;
|
||||
}
|
||||
usleep(LIBSSH2_SOCKET_POLL_UDELAY);
|
||||
#endif /* POLL/SELECT/SLEEP */
|
||||
continue;
|
||||
}
|
||||
if (errno == EINTR) {
|
||||
@@ -569,6 +690,10 @@ static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, s
|
||||
bytes_read += ret;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking read: %d bytes actually read", (int)bytes_read);
|
||||
#endif
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -595,6 +720,10 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
|
||||
ioctlsocket(session->socket_fd, FIONBIO, &non_block);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Checking for packet: will%s block", should_block ? "" : " not");
|
||||
#endif
|
||||
if (session->state & LIBSSH2_STATE_NEWKEYS) {
|
||||
/* Temporary Buffer
|
||||
* The largest blocksize (currently) is 32, the largest MAC (currently) is 20
|
||||
@@ -637,6 +766,9 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
|
||||
|
||||
packet_len = libssh2_ntohu32(block);
|
||||
padding_len = block[4];
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing packet %lu bytes long (with %lu bytes padding)", packet_len, padding_len);
|
||||
#endif
|
||||
memcpy(tmp, block, 5); /* Use this for MAC later */
|
||||
|
||||
payload_len = packet_len - 1; /* padding_len(1) */
|
||||
@@ -700,6 +832,9 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
|
||||
LIBSSH2_FREE(session, payload);
|
||||
return -1;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Payload decompressed: %lu bytes(compressed) to %lu bytes(uncompressed)", data_len, payload_len);
|
||||
#endif
|
||||
if (free_payload) {
|
||||
LIBSSH2_FREE(session, payload);
|
||||
payload = data;
|
||||
@@ -752,9 +887,16 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
|
||||
}
|
||||
packet_length = libssh2_ntohu32(buf);
|
||||
padding_length = buf[4];
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Processing plaintext packet %lu bytes long (with %lu bytes padding)", packet_length, padding_length);
|
||||
#endif
|
||||
|
||||
payload_len = packet_length - padding_length - 1; /* padding_length(1) */
|
||||
payload = LIBSSH2_ALLOC(session, payload_len);
|
||||
if (!payload) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of plaintext data", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_blocking_read(session, payload, payload_len) < payload_len) {
|
||||
return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
|
||||
@@ -782,7 +924,7 @@ int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
|
||||
/* {{{ libssh2_packet_ask
|
||||
* Scan the brigade for a matching packet type, optionally poll the socket for a packet first
|
||||
*/
|
||||
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len,
|
||||
int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len,
|
||||
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket)
|
||||
{
|
||||
LIBSSH2_PACKET *packet = session->packets.head;
|
||||
@@ -792,10 +934,13 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, u
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int)packet_type);
|
||||
#endif
|
||||
while (packet) {
|
||||
if (packet->data[0] == packet_type &&
|
||||
(packet->data_len >= (match_ofs + match_len)) &&
|
||||
(!match_buf || (strncmp(packet->data + match_ofs, match_buf, match_len) == 0))) {
|
||||
(!match_buf || (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) {
|
||||
*data = packet->data;
|
||||
*data_len = packet->data_len;
|
||||
|
||||
@@ -821,9 +966,27 @@ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, u
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_askv
|
||||
* Scan for any of a list of packet types in the brigade, optionally poll the socket for a packet first
|
||||
*/
|
||||
int libssh2_packet_askv_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len,
|
||||
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket)
|
||||
{
|
||||
int i, packet_types_len = strlen(packet_types);
|
||||
|
||||
for(i = 0; i < packet_types_len; i++) {
|
||||
if (0 == libssh2_packet_ask_ex(session, packet_types[i], data, data_len, match_ofs, match_buf, match_len, i ? 0 : poll_socket)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_require
|
||||
* Loops libssh2_packet_read() until the packet requested is available
|
||||
* SSH_DISCONNECT will cause a bailout though
|
||||
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
|
||||
*/
|
||||
int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len,
|
||||
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len)
|
||||
@@ -833,6 +996,9 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet of type %d becomes available", (int)packet_type);
|
||||
#endif
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret = libssh2_packet_read(session, 1);
|
||||
if (ret < 0) {
|
||||
@@ -851,6 +1017,39 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_requirev
|
||||
* Loops libssh2_packet_read() until one of a list of packet types requested is available
|
||||
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
|
||||
* packet_types is a null terminated list of packet_type numbers
|
||||
*/
|
||||
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len,
|
||||
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len)
|
||||
{
|
||||
if (libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) {
|
||||
/* One of the packets listed was available in the packet brigade */
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) {
|
||||
int ret = libssh2_packet_read(session, 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strchr(packet_types, ret)) {
|
||||
/* Be lazy, let packet_ask pull it out of the brigade */
|
||||
return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Only reached if the socket died */
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_write
|
||||
* Send a packet, encrypting it and adding a MAC code if necessary
|
||||
* Returns 0 on success, non-zero on failure
|
||||
@@ -863,14 +1062,26 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned
|
||||
unsigned long padding_length;
|
||||
int free_data = 0;
|
||||
unsigned char buf[246]; /* 6 byte header plus max padding size(240) */
|
||||
int i;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
{
|
||||
/* Show a hint of what's being sent */
|
||||
char excerpt[32];
|
||||
int ex_len = 0, db_ofs = 0;
|
||||
|
||||
for (; ex_len < 24 && db_ofs < data_len; ex_len += 3, db_ofs++) snprintf(excerpt + ex_len, 4, "%02X ", data[db_ofs]);
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet type %d, length=%lu, %s", (int)data[0], data_len, excerpt);
|
||||
}
|
||||
#endif
|
||||
if ((session->state & LIBSSH2_STATE_NEWKEYS) &&
|
||||
strcmp(session->local.comp->name, "none")) {
|
||||
|
||||
if (session->local.comp->comp(session, 1, &data, &data_len, LIBSSH2_PACKET_MAXCOMP, &free_data, data, data_len, &session->local.comp_abstract)) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Compressed payload to %lu bytes", data_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
@@ -892,11 +1103,9 @@ int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned
|
||||
packet_length += padding_length;
|
||||
libssh2_htonu32(buf, packet_length);
|
||||
buf[4] = padding_length;
|
||||
|
||||
for (i = 0; i < padding_length; i++) {
|
||||
/* Make random */
|
||||
buf[5 + i] = '\0';
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending packet with total length %lu (%lu bytes padding)", packet_length, padding_length);
|
||||
#endif
|
||||
|
||||
if (session->state & LIBSSH2_STATE_NEWKEYS) {
|
||||
/* Encryption is in effect */
|
||||
|
728
src/publickey.c
Normal file
728
src/publickey.c
Normal file
@@ -0,0 +1,728 @@
|
||||
/* Copyright (c) 2004-2005, Sara Golemon <sarag@libssh2.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "libssh2_priv.h"
|
||||
#include "libssh2_publickey.h"
|
||||
|
||||
struct _LIBSSH2_PUBLICKEY {
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
unsigned long version;
|
||||
};
|
||||
|
||||
#define LIBSSH2_PUBLICKEY_VERSION 2
|
||||
|
||||
/* Numericised response codes -- Not IETF standard, just a local representation */
|
||||
#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0
|
||||
#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1
|
||||
#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2
|
||||
|
||||
typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST {
|
||||
int code;
|
||||
char *name;
|
||||
int name_len;
|
||||
} LIBSSH2_PUBLICKEY_CODE_LIST;
|
||||
|
||||
static LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_response_codes[] = {
|
||||
{ LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey", sizeof("publickey") - 1 },
|
||||
{ 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/* PUBLICKEY status codes -- IETF defined */
|
||||
#define LIBSSH2_PUBLICKEY_SUCCESS 0
|
||||
#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1
|
||||
#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2
|
||||
#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3
|
||||
#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4
|
||||
#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5
|
||||
#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6
|
||||
#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7
|
||||
#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8
|
||||
|
||||
#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8
|
||||
|
||||
static LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_status_codes[] = {
|
||||
{ LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied", sizeof("access denied") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded", sizeof("storage exceeded") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported", sizeof("version not supported") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found", sizeof("key not found") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported", sizeof("key not supported") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present", sizeof("key already present") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure", sizeof("general failure") - 1 },
|
||||
{ LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported", sizeof("request not supported") - 1 },
|
||||
{ 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/* {{{ libssh2_publickey_status_error
|
||||
* Format an error message from a status code
|
||||
*/
|
||||
#define LIBSSH2_PUBLICKEY_STATUS_TEXT_START "Publickey Subsystem Error: \""
|
||||
#define LIBSSH2_PUBLICKEY_STATUS_TEXT_MID "\" Server Resports: \""
|
||||
#define LIBSSH2_PUBLICKEY_STATUS_TEXT_END "\""
|
||||
static void libssh2_publickey_status_error(LIBSSH2_PUBLICKEY *pkey, LIBSSH2_SESSION *session, int status, unsigned char *message, int message_len)
|
||||
{
|
||||
char *status_text;
|
||||
int status_text_len;
|
||||
char *m, *s;
|
||||
int m_len;
|
||||
|
||||
/* GENERAL_FAILURE got remapped between version 1 and 2 */
|
||||
if (status == 6 && pkey && pkey->version == 1) {
|
||||
status = 7;
|
||||
}
|
||||
|
||||
if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) {
|
||||
status_text = "unknown";
|
||||
status_text_len = sizeof("unknown") - 1;
|
||||
} else {
|
||||
status_text = libssh2_publickey_status_codes[status].name;
|
||||
status_text_len = libssh2_publickey_status_codes[status].name_len;
|
||||
}
|
||||
|
||||
m_len = (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1) + status_text_len +
|
||||
(sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1) + message_len +
|
||||
(sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1);
|
||||
m = LIBSSH2_ALLOC(session, m_len + 1);
|
||||
if (!m) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for status message", 0);
|
||||
return;
|
||||
}
|
||||
s = m;
|
||||
memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_START, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1);
|
||||
s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1;
|
||||
memcpy(s, status_text, status_text_len); s += status_text_len;
|
||||
memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_MID, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1);
|
||||
s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1;
|
||||
memcpy(s, message, message_len); s += message_len;
|
||||
memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_END, sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1);
|
||||
s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, m, 1);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_packet_receive
|
||||
* Read a packet from the subsystem
|
||||
*/
|
||||
static int libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY *pkey, unsigned char **data, unsigned long *data_len)
|
||||
{
|
||||
LIBSSH2_CHANNEL *channel = pkey->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char buffer[4];
|
||||
unsigned long packet_len;
|
||||
unsigned char *packet;
|
||||
|
||||
if (libssh2_channel_read(channel, buffer, 4) != 4) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid response from publickey subsystem", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
packet_len = libssh2_ntohu32(buffer);
|
||||
packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate publickey response buffer", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_channel_read(channel, packet, packet_len) != packet_len) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for publickey subsystem response packet", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*data = packet;
|
||||
*data_len = packet_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_response_id
|
||||
* Translate a string response name to a numeric code
|
||||
* Data will be incremented by 4 + response_len on success only
|
||||
*/
|
||||
static int libssh2_publickey_response_id(unsigned char **pdata, int data_len)
|
||||
{
|
||||
unsigned long response_len;
|
||||
unsigned char *data = *pdata;
|
||||
LIBSSH2_PUBLICKEY_CODE_LIST *codes = libssh2_publickey_response_codes;
|
||||
|
||||
if (data_len < 4) {
|
||||
/* Malformed response */
|
||||
return -1;
|
||||
}
|
||||
response_len = libssh2_ntohu32(data); data += 4; data_len -= 4;
|
||||
if (data_len < response_len) {
|
||||
/* Malformed response */
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (codes->name) {
|
||||
if (codes->name_len == response_len &&
|
||||
strncmp(codes->name, data, response_len) == 0) {
|
||||
*pdata = data + response_len;
|
||||
return codes->code;
|
||||
}
|
||||
codes++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_response_success
|
||||
* Generic helper routine to wait for success response and nothing else
|
||||
*/
|
||||
static int libssh2_publickey_response_success(LIBSSH2_PUBLICKEY *pkey)
|
||||
{
|
||||
LIBSSH2_SESSION *session = pkey->channel->session;
|
||||
unsigned char *data, *s;
|
||||
unsigned long data_len, response;
|
||||
|
||||
while (1) {
|
||||
if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = data;
|
||||
if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (response) {
|
||||
case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
|
||||
/* Error, or processing complete */
|
||||
{
|
||||
unsigned long status, descr_len, lang_len;
|
||||
unsigned char *descr, *lang;
|
||||
|
||||
status = libssh2_ntohu32(s); s += 4;
|
||||
descr_len = libssh2_ntohu32(s); s += 4;
|
||||
descr = s; s += descr_len;
|
||||
lang_len = libssh2_ntohu32(s); s += 4;
|
||||
lang = s; s += lang_len;
|
||||
|
||||
if (s > data + data_len) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (status == LIBSSH2_PUBLICKEY_SUCCESS) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
libssh2_publickey_status_error(pkey, session, status, descr, descr_len);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
/* Unknown/Unexpected */
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
/* never reached, but include `return` to silence compiler warnings */
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* *****************
|
||||
* Publickey API *
|
||||
***************** */
|
||||
|
||||
/* {{{ libssh2_publickey_init
|
||||
* Startup the publickey subsystem
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session)
|
||||
{
|
||||
LIBSSH2_PUBLICKEY *pkey = NULL;
|
||||
LIBSSH2_CHANNEL *channel = NULL;
|
||||
unsigned char buffer[19];
|
||||
/* packet_len(4) +
|
||||
version_len(4) +
|
||||
"version"(7) +
|
||||
version_num(4) */
|
||||
unsigned char *s, *data = NULL;
|
||||
unsigned long data_len;
|
||||
int response;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Initializing publickey subsystem");
|
||||
#endif
|
||||
|
||||
channel = libssh2_channel_open_session(session);
|
||||
if (!channel) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
if (libssh2_channel_subsystem(channel, "publickey")) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to request publickey subsystem", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
libssh2_channel_set_blocking(channel, 1);
|
||||
libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
|
||||
|
||||
pkey = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY));
|
||||
if (!pkey) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new publickey structure", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
pkey->channel = channel;
|
||||
pkey->version = 0;
|
||||
|
||||
s = buffer;
|
||||
libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); s += 4;
|
||||
libssh2_htonu32(s, sizeof("version") - 1); s += 4;
|
||||
memcpy(s, "version", sizeof("version") - 1); s += sizeof("version") - 1;
|
||||
libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); s += 4;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey version packet advertising version %d support", (int)LIBSSH2_PUBLICKEY_VERSION);
|
||||
#endif
|
||||
if ((s - buffer) != libssh2_channel_write(channel, buffer, (s - buffer))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey version packet", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
s = data;
|
||||
if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
switch (response) {
|
||||
case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
|
||||
/* Error */
|
||||
{
|
||||
unsigned long status, descr_len, lang_len;
|
||||
unsigned char *descr, *lang;
|
||||
|
||||
status = libssh2_ntohu32(s); s += 4;
|
||||
descr_len = libssh2_ntohu32(s); s += 4;
|
||||
descr = s; s += descr_len;
|
||||
lang_len = libssh2_ntohu32(s); s += 4;
|
||||
lang = s; s += lang_len;
|
||||
|
||||
if (s > data + data_len) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
libssh2_publickey_status_error(NULL, session, status, descr, descr_len);
|
||||
goto err_exit;
|
||||
}
|
||||
case LIBSSH2_PUBLICKEY_RESPONSE_VERSION:
|
||||
/* What we want */
|
||||
pkey->version = libssh2_ntohu32(s);
|
||||
if (pkey->version > LIBSSH2_PUBLICKEY_VERSION) {
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Truncating remote publickey version from %lu", pkey->version);
|
||||
#endif
|
||||
pkey->version = LIBSSH2_PUBLICKEY_VERSION;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Enabling publickey subsystem version %lu", pkey->version);
|
||||
#endif
|
||||
LIBSSH2_FREE(session, data);
|
||||
return pkey;
|
||||
default:
|
||||
/* Unknown/Unexpected */
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Never reached except by direct goto */
|
||||
err_exit:
|
||||
if (channel) {
|
||||
libssh2_channel_close(channel);
|
||||
}
|
||||
if (pkey) {
|
||||
LIBSSH2_FREE(session, pkey);
|
||||
}
|
||||
if (data) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_add_ex
|
||||
* Add a new public key entry
|
||||
*/
|
||||
LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len,
|
||||
const unsigned char *blob, unsigned long blob_len, char overwrite,
|
||||
unsigned long num_attrs, libssh2_publickey_attribute attrs[])
|
||||
{
|
||||
LIBSSH2_CHANNEL *channel = pkey->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *packet = NULL, *s;
|
||||
unsigned long i, packet_len = 19 + name_len + blob_len;
|
||||
unsigned char *comment = NULL;
|
||||
unsigned long comment_len = 0;
|
||||
/* packet_len(4) +
|
||||
add_len(4) +
|
||||
"add"(3) +
|
||||
name_len(4) +
|
||||
{name}
|
||||
blob_len(4) +
|
||||
{blob} */
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s pubickey", name);
|
||||
#endif
|
||||
|
||||
if (pkey->version == 1) {
|
||||
for(i = 0; i < num_attrs; i++) {
|
||||
/* Search for a comment attribute */
|
||||
if (attrs[i].name_len == (sizeof("comment") - 1) &&
|
||||
strncmp(attrs[i].name, "comment", sizeof("comment") - 1) == 0) {
|
||||
comment = attrs[i].value;
|
||||
comment_len = attrs[i].value_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
packet_len += 4 + comment_len;
|
||||
} else {
|
||||
packet_len += 5; /* overwrite(1) + attribute_count(4) */
|
||||
for(i = 0; i < num_attrs; i++) {
|
||||
packet_len += 9 + attrs[i].name_len + attrs[i].value_len;
|
||||
/* name_len(4) + value_len(4) + mandatory(1) */
|
||||
}
|
||||
}
|
||||
|
||||
packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"add\" packet", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = packet;
|
||||
libssh2_htonu32(s, packet_len - 4); s += 4;
|
||||
libssh2_htonu32(s, sizeof("add") - 1); s += 4;
|
||||
memcpy(s, "add", sizeof("add") - 1); s += sizeof("add") - 1;
|
||||
if (pkey->version == 1) {
|
||||
libssh2_htonu32(s, comment_len); s += 4;
|
||||
if (comment) {
|
||||
memcpy(s, comment, comment_len); s += comment_len;
|
||||
}
|
||||
|
||||
libssh2_htonu32(s, name_len); s += 4;
|
||||
memcpy(s, name, name_len); s += name_len;
|
||||
libssh2_htonu32(s, blob_len); s += 4;
|
||||
memcpy(s, blob, blob_len); s += blob_len;
|
||||
} else {
|
||||
/* Version == 2 */
|
||||
|
||||
libssh2_htonu32(s, name_len); s += 4;
|
||||
memcpy(s, name, name_len); s += name_len;
|
||||
libssh2_htonu32(s, blob_len); s += 4;
|
||||
memcpy(s, blob, blob_len); s += blob_len;
|
||||
*(s++) = overwrite ? 0xFF : 0;
|
||||
libssh2_htonu32(s, num_attrs); s += 4;
|
||||
for(i = 0; i < num_attrs; i++) {
|
||||
libssh2_htonu32(s, attrs[i].name_len); s += 4;
|
||||
memcpy(s, attrs[i].name, attrs[i].name_len); s += attrs[i].name_len;
|
||||
libssh2_htonu32(s, attrs[i].value_len); s += 4;
|
||||
memcpy(s, attrs[i].value, attrs[i].value_len); s += attrs[i].value_len;
|
||||
*(s++) = attrs[i].mandatory ? 0xFF : 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld", name, blob_len, num_attrs);
|
||||
#endif
|
||||
if ((s - packet) != libssh2_channel_write(channel, packet, (s - packet))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey add packet", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
packet = NULL;
|
||||
|
||||
return libssh2_publickey_response_success(pkey);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_remove_ex
|
||||
* Remove an existing publickey so that authentication can no longer be performed using it
|
||||
*/
|
||||
LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len,
|
||||
const unsigned char *blob, unsigned long blob_len)
|
||||
{
|
||||
LIBSSH2_CHANNEL *channel = pkey->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char *s, *packet = NULL;
|
||||
unsigned long packet_len = 22 + name_len + blob_len;
|
||||
/* packet_len(4) +
|
||||
remove_len(4) +
|
||||
"remove"(6) +
|
||||
name_len(4) +
|
||||
{name}
|
||||
blob_len(4) +
|
||||
{blob} */
|
||||
|
||||
packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey \"remove\" packet", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = packet;
|
||||
libssh2_htonu32(s, packet_len - 4); s += 4;
|
||||
libssh2_htonu32(s, sizeof("remove") - 1); s += 4;
|
||||
memcpy(s, "remove", sizeof("remove") - 1); s += sizeof("remove") - 1;
|
||||
libssh2_htonu32(s, name_len); s += 4;
|
||||
memcpy(s, name, name_len); s += name_len;
|
||||
libssh2_htonu32(s, blob_len); s += 4;
|
||||
memcpy(s, blob, blob_len); s += blob_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"remove\" packet: type=%s blob_len=%ld", name, blob_len);
|
||||
#endif
|
||||
if ((s - packet) != libssh2_channel_write(channel, packet, (s - packet))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey remove packet", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
packet = NULL;
|
||||
|
||||
return libssh2_publickey_response_success(pkey);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_list_fetch
|
||||
* Fetch a list of supported public key from a server
|
||||
*/
|
||||
LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list)
|
||||
{
|
||||
LIBSSH2_CHANNEL *channel = pkey->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
libssh2_publickey_list *list = NULL;
|
||||
unsigned char *s, buffer[12], *data = NULL;
|
||||
unsigned long buffer_len = 12, keys = 0, max_keys = 0, data_len, i, response;
|
||||
/* packet_len(4) +
|
||||
list_len(4) +
|
||||
"list"(4) */
|
||||
|
||||
s = buffer;
|
||||
libssh2_htonu32(s, buffer_len - 4); s += 4;
|
||||
libssh2_htonu32(s, sizeof("list") - 1); s += 4;
|
||||
memcpy(s, "list", sizeof("list") - 1); s += sizeof("list") - 1;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_PUBLICKEY
|
||||
_libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Sending publickey \"list\" packet");
|
||||
#endif
|
||||
if ((s - buffer) != libssh2_channel_write(channel, buffer, (s - buffer))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send publickey list packet", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (libssh2_publickey_packet_receive(pkey, &data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from publickey subsystem", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
s = data;
|
||||
if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Invalid publickey subsystem response code", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
switch (response) {
|
||||
case LIBSSH2_PUBLICKEY_RESPONSE_STATUS:
|
||||
/* Error, or processing complete */
|
||||
{
|
||||
unsigned long status, descr_len, lang_len;
|
||||
unsigned char *descr, *lang;
|
||||
|
||||
status = libssh2_ntohu32(s); s += 4;
|
||||
descr_len = libssh2_ntohu32(s); s += 4;
|
||||
descr = s; s += descr_len;
|
||||
lang_len = libssh2_ntohu32(s); s += 4;
|
||||
lang = s; s += lang_len;
|
||||
|
||||
if (s > data + data_len) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Malformed publickey subsystem packet", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (status == LIBSSH2_PUBLICKEY_SUCCESS) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
*pkey_list = list;
|
||||
*num_keys = keys;
|
||||
return 0;
|
||||
}
|
||||
|
||||
libssh2_publickey_status_error(pkey, session, status, descr, descr_len);
|
||||
goto err_exit;
|
||||
}
|
||||
case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY:
|
||||
/* What we want */
|
||||
if (keys >= max_keys) {
|
||||
/* Grow the key list if necessary */
|
||||
max_keys += 8;
|
||||
list = LIBSSH2_REALLOC(session, list, (max_keys + 1) * sizeof(libssh2_publickey_list));
|
||||
if (!list) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey list", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
}
|
||||
if (pkey->version == 1) {
|
||||
unsigned long comment_len;
|
||||
|
||||
comment_len = libssh2_ntohu32(s); s += 4;
|
||||
if (comment_len) {
|
||||
list[keys].num_attrs = 1;
|
||||
list[keys].attrs = LIBSSH2_ALLOC(session, sizeof(libssh2_publickey_attribute));
|
||||
if (!list[keys].attrs) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
list[keys].attrs[0].name = "comment";
|
||||
list[keys].attrs[0].name_len = sizeof("comment") - 1;
|
||||
list[keys].attrs[0].value = s;
|
||||
list[keys].attrs[0].value_len = comment_len;
|
||||
list[keys].attrs[0].mandatory = 0;
|
||||
|
||||
s += comment_len;
|
||||
} else {
|
||||
list[keys].num_attrs = 0;
|
||||
list[keys].attrs = NULL;
|
||||
}
|
||||
list[keys].name_len = libssh2_ntohu32(s); s += 4;
|
||||
list[keys].name = s; s += list[keys].name_len;
|
||||
list[keys].blob_len = libssh2_ntohu32(s); s += 4;
|
||||
list[keys].blob = s; s += list[keys].blob_len;
|
||||
} else {
|
||||
/* Version == 2 */
|
||||
list[keys].name_len = libssh2_ntohu32(s); s += 4;
|
||||
list[keys].name = s; s += list[keys].name_len;
|
||||
list[keys].blob_len = libssh2_ntohu32(s); s += 4;
|
||||
list[keys].blob = s; s += list[keys].blob_len;
|
||||
list[keys].num_attrs = libssh2_ntohu32(s); s += 4;
|
||||
if (list[keys].num_attrs) {
|
||||
list[keys].attrs = LIBSSH2_ALLOC(session, list[keys].num_attrs * sizeof(libssh2_publickey_attribute));
|
||||
if (!list[keys].attrs) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for publickey attributes", 0);
|
||||
goto err_exit;
|
||||
}
|
||||
for(i = 0; i < list[keys].num_attrs; i++) {
|
||||
list[keys].attrs[i].name_len = libssh2_ntohu32(s); s += 4;
|
||||
list[keys].attrs[i].name = s; s += list[keys].attrs[i].name_len;
|
||||
list[keys].attrs[i].value_len = libssh2_ntohu32(s); s += 4;
|
||||
list[keys].attrs[i].value = s; s += list[keys].attrs[i].value_len;
|
||||
list[keys].attrs[i].mandatory = 0; /* actually an ignored value */
|
||||
}
|
||||
} else {
|
||||
list[keys].attrs = NULL;
|
||||
}
|
||||
}
|
||||
list[keys].packet = data; /* To be FREEd in libssh2_publickey_list_free() */
|
||||
keys++;
|
||||
|
||||
list[keys].packet = NULL; /* Terminate the list */
|
||||
data = NULL;
|
||||
break;
|
||||
default:
|
||||
/* Unknown/Unexpected */
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, "Unexpected publickey subsystem response, ignoring", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Only reached via explicit goto */
|
||||
err_exit:
|
||||
if (data) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
}
|
||||
if (list) {
|
||||
libssh2_publickey_list_free(pkey, list);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_list_free
|
||||
* Free a previously fetched list of public keys
|
||||
*/
|
||||
LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_publickey_list *pkey_list)
|
||||
{
|
||||
LIBSSH2_SESSION *session = pkey->channel->session;
|
||||
libssh2_publickey_list *p = pkey_list;
|
||||
|
||||
while (p->packet) {
|
||||
if (p->attrs) {
|
||||
LIBSSH2_FREE(session, p->attrs);
|
||||
}
|
||||
LIBSSH2_FREE(session, p->packet);
|
||||
p++;
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, pkey_list);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_publickey_shutdown
|
||||
* Shutdown the publickey subsystem
|
||||
*/
|
||||
LIBSSH2_API void libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey)
|
||||
{
|
||||
LIBSSH2_SESSION *session = pkey->channel->session;
|
||||
|
||||
libssh2_channel_free(pkey->channel);
|
||||
LIBSSH2_FREE(session, pkey);
|
||||
}
|
||||
/* }}} */
|
28
src/scp.c
28
src/scp.c
@@ -44,7 +44,7 @@
|
||||
/* {{{ libssh2_scp_recv
|
||||
* Open a channel and request a remote file via SCP
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *path, struct stat *sb)
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb)
|
||||
{
|
||||
int path_len = strlen(path);
|
||||
unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
|
||||
@@ -70,6 +70,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
|
||||
}
|
||||
command[command_len - 1] = '\0';
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP receive");
|
||||
#endif
|
||||
/* Allocate a channel */
|
||||
if ((channel = libssh2_channel_open_session(session)) == NULL) {
|
||||
LIBSSH2_FREE(session, command);
|
||||
@@ -86,6 +89,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
|
||||
}
|
||||
LIBSSH2_FREE(session, command);
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup");
|
||||
#endif
|
||||
/* SCP ACK */
|
||||
response[0] = '\0';
|
||||
if (libssh2_channel_write(channel, response, 1) != 1) {
|
||||
@@ -197,6 +203,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
|
||||
libssh2_channel_free(channel);
|
||||
return NULL;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "mtime = %ld, atime = %ld", mtime, atime);
|
||||
#endif
|
||||
|
||||
/* We *should* check that atime.usec is valid, but why let that stop use? */
|
||||
break;
|
||||
@@ -295,6 +304,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
|
||||
libssh2_channel_free(channel);
|
||||
return NULL;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "mod = 0%lo size = %ld", mode, size);
|
||||
#endif
|
||||
|
||||
/* We *should* check that basename is valid, but why let that stop us? */
|
||||
break;
|
||||
@@ -318,11 +330,12 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, char *pa
|
||||
/* {{{ libssh2_scp_send_ex
|
||||
* Send a file using SCP
|
||||
*/
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char *path, int mode, size_t size, long mtime, long atime)
|
||||
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime)
|
||||
{
|
||||
int path_len = strlen(path);
|
||||
unsigned char *command, *base, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
|
||||
unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
|
||||
unsigned long response_len, command_len = path_len + sizeof("scp -t ");
|
||||
unsigned const char *base;
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
|
||||
if (mtime || atime) {
|
||||
@@ -344,6 +357,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char
|
||||
}
|
||||
command[command_len - 1] = '\0';
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Opening channel for SCP send");
|
||||
#endif
|
||||
/* Allocate a channel */
|
||||
if ((channel = libssh2_channel_open_session(session)) == NULL) {
|
||||
/* previous call set libssh2_session_last_error(), pass it through */
|
||||
@@ -372,6 +388,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char
|
||||
/* Send mtime and atime to be used for file */
|
||||
if (mtime || atime) {
|
||||
response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, atime);
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response);
|
||||
#endif
|
||||
if (libssh2_channel_write(channel, response, response_len) != response_len) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send time data for SCP file", 0);
|
||||
libssh2_channel_free(channel);
|
||||
@@ -394,6 +413,9 @@ LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, char
|
||||
}
|
||||
|
||||
response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long)size, base);
|
||||
#ifdef LIBSSH2_DEBUG_SCP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", response);
|
||||
#endif
|
||||
if (libssh2_channel_write(channel, response, response_len) != response_len) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send core file data for SCP file", 0);
|
||||
libssh2_channel_free(channel);
|
||||
|
@@ -128,6 +128,9 @@ static int libssh2_banner_receive(LIBSSH2_SESSION *session)
|
||||
session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
|
||||
memcpy(session->remote.banner, banner, banner_len);
|
||||
session->remote.banner[banner_len] = '\0';
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s", session->remote.banner);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -145,6 +148,22 @@ static int libssh2_banner_send(LIBSSH2_SESSION *session)
|
||||
banner_len = strlen(session->local.banner);
|
||||
banner = session->local.banner;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
{
|
||||
/* Hack and slash to avoid sending CRLF in debug output */
|
||||
char banner_dup[256];
|
||||
|
||||
if (banner_len < 256) {
|
||||
memcpy(banner_dup, banner, banner_len - 2);
|
||||
banner_dup[banner_len - 2] = '\0';
|
||||
} else {
|
||||
memcpy(banner_dup, banner, 255);
|
||||
banner[255] = '\0';
|
||||
}
|
||||
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", banner_dup);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (send(session->socket_fd, banner, banner_len, LIBSSH2_SOCKET_SEND_FLAGS(session)) == banner_len) ? 0 : 1;
|
||||
}
|
||||
@@ -153,7 +172,7 @@ static int libssh2_banner_send(LIBSSH2_SESSION *session)
|
||||
/* {{{ libssh2_banner_set
|
||||
* Set the local banner
|
||||
*/
|
||||
LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, char *banner)
|
||||
LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner)
|
||||
{
|
||||
int banner_len = banner ? strlen(banner) : 0;
|
||||
|
||||
@@ -173,6 +192,10 @@ LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, char *banner)
|
||||
}
|
||||
|
||||
memcpy(session->local.banner, banner, banner_len);
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
session->local.banner[banner_len] = '\0';
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", session->local.banner);
|
||||
#endif
|
||||
session->local.banner[banner_len++] = '\r';
|
||||
session->local.banner[banner_len++] = '\n';
|
||||
session->local.banner[banner_len++] = '\0';
|
||||
@@ -208,6 +231,9 @@ LIBSSH2_API LIBSSH2_SESSION *libssh2_session_init_ex(
|
||||
session->free = local_free;
|
||||
session->realloc = local_realloc;
|
||||
session->abstract = abstract;
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "New session resource allocated");
|
||||
#endif
|
||||
|
||||
return session;
|
||||
}
|
||||
@@ -248,6 +274,9 @@ LIBSSH2_API void* libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbt
|
||||
return oldcb;
|
||||
break;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting Callback %d", cbtype);
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -266,6 +295,9 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
||||
unsigned char service[sizeof("ssh-userauth") + 5 - 1];
|
||||
unsigned long service_length;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "session_startup for socket %d", socket);
|
||||
#endif
|
||||
if (socket <= 0) {
|
||||
/* Did we forget something? */
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "No socket provided", 0);
|
||||
@@ -291,6 +323,9 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
||||
return LIBSSH2_ERROR_KEX_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Requesting userauth service");
|
||||
#endif
|
||||
/* Request the userauth service */
|
||||
service[0] = SSH_MSG_SERVICE_REQUEST;
|
||||
libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1);
|
||||
@@ -323,6 +358,9 @@ LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
|
||||
*/
|
||||
LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
|
||||
{
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Freeing session resource", session->remote.banner);
|
||||
#endif
|
||||
while (session->channels.head) {
|
||||
LIBSSH2_CHANNEL *tmp = session->channels.head;
|
||||
|
||||
@@ -459,11 +497,14 @@ LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
|
||||
|
||||
/* {{{ libssh2_session_disconnect_ex
|
||||
*/
|
||||
LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, char *description, char *lang)
|
||||
LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang)
|
||||
{
|
||||
unsigned char *s, *data;
|
||||
unsigned long data_len, descr_len = 0, lang_len = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Disconnecting: reason=%d, desc=%s, lang=%s", reason, description, lang);
|
||||
#endif
|
||||
if (description) {
|
||||
descr_len = strlen(description);
|
||||
}
|
||||
@@ -506,7 +547,7 @@ LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reas
|
||||
* NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string regardless of actual negotiation
|
||||
* Strings should NOT be freed
|
||||
*/
|
||||
LIBSSH2_API char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type)
|
||||
LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type)
|
||||
{
|
||||
/* All methods have char *name as their first element */
|
||||
LIBSSH2_KEX_METHOD *method = NULL;
|
||||
@@ -641,7 +682,7 @@ LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int val
|
||||
* Returns 0 if no data is waiting on channel,
|
||||
* non-0 if data is available
|
||||
*/
|
||||
static int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended)
|
||||
LIBSSH2_API int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended)
|
||||
{
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
LIBSSH2_PACKET *packet = session->packets.head;
|
||||
@@ -653,7 +694,7 @@ static int libssh2_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended)
|
||||
return 1;
|
||||
}
|
||||
packet = packet->next;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -784,12 +825,21 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
|
||||
((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { /* Not yet known to be ready for write */
|
||||
fds[i].revents |= libssh2_poll_channel_write(fds[i].fd.channel) ? LIBSSH2_POLLFD_POLLOUT : 0;
|
||||
}
|
||||
if (fds[i].fd.channel->remote.close || fds[i].fd.channel->local.close) {
|
||||
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED;
|
||||
}
|
||||
if (fds[i].fd.channel->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
||||
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||
}
|
||||
break;
|
||||
case LIBSSH2_POLLFD_LISTENER:
|
||||
case LIBSSH2_POLLFD_LISTENER:
|
||||
if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */
|
||||
((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */
|
||||
fds[i].revents |= libssh2_poll_listener_queued(fds[i].fd.listener) ? LIBSSH2_POLLFD_POLLIN : 0;
|
||||
}
|
||||
if (fds[i].fd.listener->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
||||
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -838,6 +888,9 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
|
||||
/* Spin session until no data available */
|
||||
while (libssh2_packet_read(fds[i].fd.channel->session, 0) > 0);
|
||||
}
|
||||
if (sockets[i].revents & POLLHUP) {
|
||||
fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||
}
|
||||
sockets[i].revents = 0;
|
||||
break;
|
||||
case LIBSSH2_POLLFD_LISTENER:
|
||||
@@ -845,6 +898,9 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeou
|
||||
/* Spin session until no data available */
|
||||
while (libssh2_packet_read(fds[i].fd.listener->session, 0) > 0);
|
||||
}
|
||||
if (sockets[i].revents & POLLHUP) {
|
||||
fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED;
|
||||
}
|
||||
sockets[i].revents = 0;
|
||||
break;
|
||||
}
|
||||
|
176
src/sftp.c
176
src/sftp.c
@@ -89,6 +89,11 @@ struct _LIBSSH2_SFTP {
|
||||
#define LIBSSH2_SFTP_HANDLE_FILE 0
|
||||
#define LIBSSH2_SFTP_HANDLE_DIR 1
|
||||
|
||||
/* S_IFREG */
|
||||
#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000
|
||||
/* S_IFDIR */
|
||||
#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000
|
||||
|
||||
struct _LIBSSH2_SFTP_HANDLE {
|
||||
LIBSSH2_SFTP *sftp;
|
||||
LIBSSH2_SFTP_HANDLE *prev, *next;
|
||||
@@ -118,6 +123,9 @@ static int libssh2_sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data, unsi
|
||||
LIBSSH2_SESSION *session = sftp->channel->session;
|
||||
LIBSSH2_PACKET *packet;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Received packet %d", (int)data[0]);
|
||||
#endif
|
||||
packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate datablock for SFTP packet", 0);
|
||||
@@ -151,8 +159,11 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned char buffer[4]; /* To store the packet length */
|
||||
unsigned char *packet;
|
||||
unsigned long packet_len;
|
||||
unsigned long packet_len, packet_received;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet: %s block", should_block ? "will" : "willnot");
|
||||
#endif
|
||||
if (should_block) {
|
||||
libssh2_channel_set_blocking(channel, 1);
|
||||
if (4 != libssh2_channel_read(channel, buffer, 4)) {
|
||||
@@ -172,6 +183,9 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
|
||||
}
|
||||
}
|
||||
packet_len = libssh2_ntohu32(buffer);
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Data begin - Packet Length: %lu", packet_len);
|
||||
#endif
|
||||
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, "SFTP packet too large", 0);
|
||||
return -1;
|
||||
@@ -183,10 +197,16 @@ static int libssh2_sftp_packet_read(LIBSSH2_SFTP *sftp, int should_block)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (packet_len != libssh2_channel_read(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for SFTP packet", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
packet_received = 0;
|
||||
while (packet_len > packet_received) {
|
||||
long bytes_received = libssh2_channel_read(channel, packet + packet_received, packet_len - packet_received);
|
||||
|
||||
if (bytes_received < 0) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Receive error waiting for SFTP packet", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
packet_received += bytes_received;
|
||||
}
|
||||
|
||||
if (libssh2_sftp_packet_add(sftp, packet, packet_len)) {
|
||||
@@ -206,7 +226,11 @@ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type
|
||||
LIBSSH2_SESSION *session = sftp->channel->session;
|
||||
LIBSSH2_PACKET *packet = sftp->packets.head;
|
||||
unsigned char match_buf[5];
|
||||
int match_len = 5;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Asking for %d packet", (int)packet_type);
|
||||
#endif
|
||||
if (poll_channel) {
|
||||
if (libssh2_sftp_packet_read(sftp, 0) < 0) {
|
||||
return -1;
|
||||
@@ -214,10 +238,15 @@ static int libssh2_sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type
|
||||
}
|
||||
|
||||
match_buf[0] = packet_type;
|
||||
libssh2_htonu32(match_buf + 1, request_id);
|
||||
if (packet_type == SSH_FXP_VERSION) {
|
||||
/* Special consideration when matching VERSION packet */
|
||||
match_len = 1;
|
||||
} else {
|
||||
libssh2_htonu32(match_buf + 1, request_id);
|
||||
}
|
||||
|
||||
while (packet) {
|
||||
if (strncmp(packet->data, match_buf, 5) == 0) {
|
||||
if (strncmp(packet->data, match_buf, match_len) == 0) {
|
||||
*data = packet->data;
|
||||
*data_len = packet->data_len;
|
||||
|
||||
@@ -250,6 +279,9 @@ static int libssh2_sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_
|
||||
{
|
||||
LIBSSH2_SESSION *session = sftp->channel->session;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", (int)packet_type);
|
||||
#endif
|
||||
if (libssh2_sftp_packet_ask(sftp, packet_type, request_id, data, data_len, 0) == 0) {
|
||||
/* A packet was available in the packet brigade */
|
||||
return 0;
|
||||
@@ -280,12 +312,21 @@ static int libssh2_sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_respon
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Flush */
|
||||
while (libssh2_sftp_packet_read(sftp, 0) > 0);
|
||||
|
||||
while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret;
|
||||
for(i = 0; i < num_valid_responses; i++) {
|
||||
if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, !i) == 0) {
|
||||
if (libssh2_sftp_packet_ask(sftp, valid_responses[i], request_id, data, data_len, 0) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ret = libssh2_sftp_packet_read(sftp, 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -408,9 +449,12 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
{
|
||||
LIBSSH2_SFTP *sftp;
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
unsigned char *data, *s, buffer[13]; /* sftp_header(9) + version_id(4) */
|
||||
unsigned long data_len, request_id;
|
||||
unsigned char *data, *s, buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */
|
||||
unsigned long data_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Initializing SFTP subsystem");
|
||||
#endif
|
||||
channel = libssh2_channel_open_session(session);
|
||||
if (!channel) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, "Unable to startup channel", 0);
|
||||
@@ -424,6 +468,8 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
|
||||
libssh2_channel_set_blocking(channel, 1);
|
||||
|
||||
libssh2_channel_handle_extended_data(channel, LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
|
||||
|
||||
sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP));
|
||||
if (!sftp) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a new SFTP structure", 0);
|
||||
@@ -432,21 +478,23 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
}
|
||||
memset(sftp, 0, sizeof(LIBSSH2_SFTP));
|
||||
sftp->channel = channel;
|
||||
sftp->request_id = 0;
|
||||
|
||||
request_id = sftp->request_id++;
|
||||
libssh2_htonu32(buffer, 4 + 5);
|
||||
libssh2_htonu32(buffer, 5);
|
||||
buffer[4] = SSH_FXP_INIT;
|
||||
libssh2_htonu32(buffer + 5, request_id);
|
||||
libssh2_htonu32(buffer + 9, 6);
|
||||
libssh2_htonu32(buffer + 5, LIBSSH2_SFTP_VERSION);
|
||||
|
||||
if (13 != libssh2_channel_write(channel, buffer, 13)) {
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending FXP_INIT packet advertising version %d support", (int)LIBSSH2_SFTP_VERSION);
|
||||
#endif
|
||||
if (9 != libssh2_channel_write(channel, buffer, 9)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send SSH_FXP_INIT", 0);
|
||||
libssh2_channel_free(channel);
|
||||
LIBSSH2_FREE(session, sftp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, request_id, &data, &data_len)) {
|
||||
if (libssh2_sftp_packet_require(sftp, SSH_FXP_VERSION, 0, &data, &data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, "Timeout waiting for response from SFTP subsystem", 0);
|
||||
libssh2_channel_free(channel);
|
||||
LIBSSH2_FREE(session, sftp);
|
||||
@@ -462,8 +510,14 @@ LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
s = data + 1;
|
||||
sftp->version = libssh2_ntohu32(s); s += 4;
|
||||
if (sftp->version > LIBSSH2_SFTP_VERSION) {
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Truncating remote SFTP version from %lu", sftp->version);
|
||||
#endif
|
||||
sftp->version = LIBSSH2_SFTP_VERSION;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Enabling SFTP version %lu compatability", sftp->version);
|
||||
#endif
|
||||
while (s < (data + data_len)) {
|
||||
char *extension_name, *extension_data;
|
||||
unsigned long extname_len, extdata_len;
|
||||
@@ -515,10 +569,11 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0);
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_OPEN or FXP_OPENDIR packet", 0);
|
||||
return NULL;
|
||||
}
|
||||
attrs.permissions = mode;
|
||||
/* Filetype in SFTP 3 and earlier */
|
||||
attrs.permissions = mode | ((open_type == LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
|
||||
|
||||
libssh2_htonu32(s, packet_len - 4); s += 4;
|
||||
*(s++) = (open_type == LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
|
||||
@@ -531,8 +586,11 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
||||
s += libssh2_sftp_attr2bin(s, &attrs);
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", (open_type == LIBSSH2_SFTP_OPENFILE) ? "file" : "directory");
|
||||
#endif
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_OPEN or FXP_OPENDIR command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return NULL;
|
||||
}
|
||||
@@ -583,6 +641,9 @@ LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, char *
|
||||
|
||||
fp->u.file.offset = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Open command successful");
|
||||
#endif
|
||||
return fp;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -601,6 +662,9 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
unsigned char read_responses[2] = { SSH_FXP_DATA, SSH_FXP_STATUS };
|
||||
size_t bytes_read = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading %lu bytes from SFTP handle", (unsigned long)buffer_maxlen);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0);
|
||||
@@ -639,6 +703,9 @@ LIBSSH2_API size_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
if (bytes_read > (data_len - 9)) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu bytes returned", (unsigned long)bytes_read);
|
||||
#endif
|
||||
memcpy(buffer, data + 9, bytes_read);
|
||||
handle->u.file.offset += bytes_read;
|
||||
LIBSSH2_FREE(session, data);
|
||||
@@ -705,6 +772,9 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
libssh2_htonu32(s, handle->handle_len); s += 4;
|
||||
memcpy(s, handle->handle, handle->handle_len); s += handle->handle_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Reading entries from directory handle");
|
||||
#endif
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_READ command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
@@ -732,6 +802,9 @@ LIBSSH2_API int libssh2_sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
||||
}
|
||||
|
||||
num_names = libssh2_ntohu32(data + 5);
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", num_names);
|
||||
#endif
|
||||
if (num_names <= 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return (num_names == 0) ? 0 : -1;
|
||||
@@ -776,6 +849,9 @@ LIBSSH2_API size_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *b
|
||||
unsigned long packet_len = handle->handle_len + count + 25; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + offset(8) + count(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", (unsigned long)count);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_WRITE packet", 0);
|
||||
@@ -832,6 +908,9 @@ LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char fstat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS };
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", setstat ? "set-stat" : "stat");
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FSTAT/FSETSTAT packet", 0);
|
||||
@@ -912,6 +991,9 @@ LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
|
||||
unsigned long packet_len = handle->handle_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle");
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_CLOSE packet", 0);
|
||||
@@ -980,6 +1062,9 @@ LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, char *filename, int f
|
||||
unsigned long packet_len = filename_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_REMOVE packet", 0);
|
||||
@@ -1028,10 +1113,19 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filenam
|
||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||
LIBSSH2_SESSION *session = channel->session;
|
||||
unsigned long data_len, retcode = -1, request_id;
|
||||
unsigned long packet_len = source_filename_len + dest_filename_len + 21; /* packet_len(4) + packet_type(1) + request_id(4) +
|
||||
source_filename_len(4) + dest_filename_len(4) + flags(4) */
|
||||
unsigned long packet_len = source_filename_len + dest_filename_len + 17 + (sftp->version >= 5 ? 4 : 0);
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) +
|
||||
source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
if (sftp->version < 2) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support RENAME", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Renaming %s to %s", source_filename, dest_filename);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_RENAME packet", 0);
|
||||
@@ -1046,10 +1140,13 @@ LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, char *source_filenam
|
||||
memcpy(s, source_filename, source_filename_len); s += source_filename_len;
|
||||
libssh2_htonu32(s, dest_filename_len); s += 4;
|
||||
memcpy(s, dest_filename, dest_filename_len); s += dest_filename_len;
|
||||
libssh2_htonu32(s, flags); s += 4;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0);
|
||||
if (sftp->version >= 5) {
|
||||
libssh2_htonu32(s, flags); s += 4;
|
||||
}
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, s - packet)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_RENAME command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
@@ -1100,12 +1197,16 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_l
|
||||
/* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Creating directory %s with mode 0%lo", path, mode);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0);
|
||||
return -1;
|
||||
}
|
||||
attrs.permissions = mode;
|
||||
/* Filetype in SFTP 3 and earlier */
|
||||
attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR;
|
||||
|
||||
libssh2_htonu32(s, packet_len - 4); s += 4;
|
||||
*(s++) = SSH_FXP_MKDIR;
|
||||
@@ -1116,7 +1217,7 @@ LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_l
|
||||
s += libssh2_sftp_attr2bin(s, &attrs);
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
@@ -1151,6 +1252,9 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_l
|
||||
unsigned long packet_len = path_len + 13; /* packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
||||
unsigned char *packet, *s, *data;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", path);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0);
|
||||
@@ -1165,7 +1269,7 @@ LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, char *path, int path_l
|
||||
memcpy(s, path, path_len); s += path_len;
|
||||
|
||||
if (packet_len != libssh2_channel_write(channel, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_REMOVE command", 0);
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send FXP_MKDIR command", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
return -1;
|
||||
}
|
||||
@@ -1202,6 +1306,9 @@ LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, char *path, int path_le
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char stat_responses[2] = { SSH_FXP_ATTRS, SSH_FXP_STATUS };
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : (stat_type == LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path);
|
||||
#endif
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for FXP_MKDIR packet", 0);
|
||||
@@ -1275,12 +1382,22 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, in
|
||||
unsigned char *packet, *s, *data;
|
||||
unsigned char link_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS };
|
||||
|
||||
if ((sftp->version < 3) &&
|
||||
(link_type != LIBSSH2_SFTP_REALPATH)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "Server does not support SYMLINK or READLINK", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
||||
if (!packet) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for SYMLINK/READLINK/REALPATH packet", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_SFTP
|
||||
_libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s on %s", (link_type == LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading",
|
||||
(link_type == LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path);
|
||||
#endif
|
||||
libssh2_htonu32(s, packet_len - 4); s += 4;
|
||||
switch (link_type) {
|
||||
case LIBSSH2_SFTP_REALPATH:
|
||||
@@ -1335,10 +1452,11 @@ LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, in
|
||||
}
|
||||
|
||||
link_len = libssh2_ntohu32(data + 9);
|
||||
if (link_len > target_len) {
|
||||
link_len = target_len;
|
||||
if (link_len >= target_len) {
|
||||
link_len = target_len - 1;
|
||||
}
|
||||
memcpy(target, data + 13, link_len);
|
||||
target[link_len] = 0;
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
return link_len;
|
||||
|
546
src/userauth.c
546
src/userauth.c
@@ -49,8 +49,9 @@
|
||||
* Not a common configuration for any SSH server though
|
||||
* username should be NULL, or a null terminated string
|
||||
*/
|
||||
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, char *username, int username_len)
|
||||
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, int username_len)
|
||||
{
|
||||
unsigned char reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
|
||||
unsigned long data_len = username_len + 31; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" +
|
||||
method_len(4) + method(4)"none" */
|
||||
unsigned long methods_len;
|
||||
@@ -81,25 +82,23 @@ LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, char *username
|
||||
}
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
while (1) {
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
/* Wow, who'dve thought... */
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return NULL;
|
||||
}
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
|
||||
/* What we *actually* wanted to happen */
|
||||
break;
|
||||
}
|
||||
/* TODO: Timeout? */
|
||||
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
|
||||
/* Wow, who'dve thought... */
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
methods_len = libssh2_ntohu32(data + 1);
|
||||
memcpy(data, data + 5, methods_len);
|
||||
data[methods_len] = '\0';
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", data);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -117,11 +116,11 @@ LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session)
|
||||
/* {{{ libssh2_userauth_password
|
||||
* Plain ol' login
|
||||
*/
|
||||
LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *username, int username_len,
|
||||
char *password, int password_len,
|
||||
LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, int username_len,
|
||||
const char *password, int password_len,
|
||||
LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
|
||||
{
|
||||
unsigned char *data, *s;
|
||||
unsigned char *data, *s, reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 };
|
||||
unsigned long data_len = username_len + password_len + 40; /* packet_type(1) + username_len(4) + service_len(4) + service(14)"ssh-connection" +
|
||||
method_len(4) + method(8)"password" + chgpwdbool(1) + password_len(4) */
|
||||
|
||||
@@ -146,6 +145,9 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *use
|
||||
libssh2_htonu32(s, password_len); s += 4;
|
||||
memcpy(s, password, password_len); s += password_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting to login using password authentication");
|
||||
#endif
|
||||
if (libssh2_packet_write(session, data, data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password request", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
@@ -153,71 +155,78 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *use
|
||||
}
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
while (1) {
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, &data, &data_len, 0) == 0) {
|
||||
char *newpw = NULL;
|
||||
int newpw_len = 0;
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
if (passwd_change_cb) {
|
||||
passwd_change_cb(session, &newpw, &newpw_len, &session->abstract);
|
||||
if (!newpw) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0);
|
||||
return -1;
|
||||
}
|
||||
data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */
|
||||
s = data = LIBSSH2_ALLOC(session, data_len);
|
||||
if (!data) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(s++) = SSH_MSG_USERAUTH_REQUEST;
|
||||
libssh2_htonu32(s, username_len); s += 4;
|
||||
memcpy(s, username, username_len); s += username_len;
|
||||
|
||||
libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4;
|
||||
memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1;
|
||||
|
||||
libssh2_htonu32(s, sizeof("password") - 1); s += 4;
|
||||
memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1;
|
||||
|
||||
*s = 0xFF; s++;
|
||||
|
||||
libssh2_htonu32(s, password_len); s += 4;
|
||||
memcpy(s, password, password_len); s += password_len;
|
||||
|
||||
libssh2_htonu32(s, newpw_len); s += 4;
|
||||
memcpy(s, newpw, newpw_len); s += newpw_len;
|
||||
|
||||
if (libssh2_packet_write(session, data, data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, newpw);
|
||||
/* TODO: Reset timeout? */
|
||||
} else {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* TODO: Timeout? */
|
||||
password_response:
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password authentication successful");
|
||||
#endif
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data[0] == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) {
|
||||
char *newpw = NULL;
|
||||
int newpw_len = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Password change required");
|
||||
#endif
|
||||
LIBSSH2_FREE(session, data);
|
||||
if (passwd_change_cb) {
|
||||
passwd_change_cb(session, &newpw, &newpw_len, &session->abstract);
|
||||
if (!newpw) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password expired, and callback failed", 0);
|
||||
return -1;
|
||||
}
|
||||
data_len = username_len + password_len + 44 + newpw_len; /* basic data_len + newpw_len(4) */
|
||||
s = data = LIBSSH2_ALLOC(session, data_len);
|
||||
if (!data) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password-change request", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(s++) = SSH_MSG_USERAUTH_REQUEST;
|
||||
libssh2_htonu32(s, username_len); s += 4;
|
||||
memcpy(s, username, username_len); s += username_len;
|
||||
|
||||
libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4;
|
||||
memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1;
|
||||
|
||||
libssh2_htonu32(s, sizeof("password") - 1); s += 4;
|
||||
memcpy(s, "password", sizeof("password") - 1); s += sizeof("password") - 1;
|
||||
|
||||
*s = 0xFF; s++;
|
||||
|
||||
libssh2_htonu32(s, password_len); s += 4;
|
||||
memcpy(s, password, password_len); s += password_len;
|
||||
|
||||
libssh2_htonu32(s, newpw_len); s += 4;
|
||||
memcpy(s, newpw, newpw_len); s += newpw_len;
|
||||
|
||||
if (libssh2_packet_write(session, data, data_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-password-change request", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, newpw);
|
||||
|
||||
/* Ugliest use of goto ever. Blame it on the askN => requirev migration. */
|
||||
goto password_response;
|
||||
} else {
|
||||
libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, "Password Expired, and no callback specified", 0);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* FAILURE */
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -226,12 +235,15 @@ LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, char *use
|
||||
*/
|
||||
static int libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char **method, unsigned long *method_len,
|
||||
unsigned char **pubkeydata, unsigned long *pubkeydata_len,
|
||||
char *pubkeyfile)
|
||||
const char *pubkeyfile)
|
||||
{
|
||||
FILE *fd;
|
||||
char *pubkey = NULL, c, *sp1, *sp2, *tmp;
|
||||
int pubkey_len = 0, tmp_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading public key file: %s", pubkeyfile);
|
||||
#endif
|
||||
/* Read Public Key */
|
||||
fd = fopen(pubkeyfile, "r");
|
||||
if (!fd) {
|
||||
@@ -298,15 +310,18 @@ static int libssh2_file_read_publickey(LIBSSH2_SESSION *session, unsigned char *
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_file_read_publickey
|
||||
/* {{{ libssh2_file_read_privatekey
|
||||
* Read a PEM encoded private key from an id_??? style file
|
||||
*/
|
||||
static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, LIBSSH2_HOSTKEY_METHOD **hostkey_method, void **hostkey_abstract,
|
||||
char *method, int method_len,
|
||||
char *privkeyfile, char *passphrase)
|
||||
const char *method, int method_len,
|
||||
const char *privkeyfile, const char *passphrase)
|
||||
{
|
||||
LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = libssh2_hostkey_methods();
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading private key file: %s", privkeyfile);
|
||||
#endif
|
||||
*hostkey_method = NULL;
|
||||
*hostkey_abstract = NULL;
|
||||
while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
|
||||
@@ -334,18 +349,18 @@ static int libssh2_file_read_privatekey(LIBSSH2_SESSION *session, LIBSSH2_HOSTKE
|
||||
/* {{{ libssh2_userauth_hostbased_fromfile_ex
|
||||
* Authenticate using a keypair found in the named files
|
||||
*/
|
||||
LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, char *username, int username_len,
|
||||
char *publickey, char *privatekey,
|
||||
char *passphrase,
|
||||
char *hostname, int hostname_len,
|
||||
char *local_username, int local_username_len)
|
||||
LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len,
|
||||
const char *publickey, const char *privatekey,
|
||||
const char *passphrase,
|
||||
const char *hostname, int hostname_len,
|
||||
const char *local_username, int local_username_len)
|
||||
{
|
||||
LIBSSH2_HOSTKEY_METHOD *privkeyobj;
|
||||
void *abstract;
|
||||
unsigned char buf[5];
|
||||
struct iovec datavec[4];
|
||||
unsigned char *method, *pubkeydata, *packet, *s, *sig;
|
||||
unsigned long method_len, pubkeydata_len, packet_len, sig_len;
|
||||
unsigned char *method, *pubkeydata, *packet, *s, *sig, *data, reply_codes[3] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 };
|
||||
unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len;
|
||||
|
||||
if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) {
|
||||
return -1;
|
||||
@@ -431,6 +446,9 @@ LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
memcpy(s, sig, sig_len); s += sig_len;
|
||||
LIBSSH2_FREE(session, sig);
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting hostbased authentication");
|
||||
#endif
|
||||
if (libssh2_packet_write(session, packet, s - packet)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-hostbased request", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
@@ -438,43 +456,41 @@ LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
/* We are us and we've proved it. */
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
|
||||
/* This public key is not allowed for this user on this server */
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
|
||||
return -1;
|
||||
}
|
||||
/* TODO: Timeout? */
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Hostbased authentication successful");
|
||||
#endif
|
||||
/* We are us and we've proved it. */
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This public key is not allowed for this user on this server */
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_userauth_publickey_fromfile_ex
|
||||
* Authenticate using a keypair found in the named files
|
||||
*/
|
||||
LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, char *username, int username_len,
|
||||
char *publickey, char *privatekey,
|
||||
char *passphrase)
|
||||
LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session, const char *username, int username_len,
|
||||
const char *publickey, const char *privatekey,
|
||||
const char *passphrase)
|
||||
{
|
||||
LIBSSH2_HOSTKEY_METHOD *privkeyobj;
|
||||
void *abstract;
|
||||
unsigned char buf[5];
|
||||
struct iovec datavec[4];
|
||||
unsigned char *method, *pubkeydata, *packet, *s, *b, *sig;
|
||||
unsigned long method_len, pubkeydata_len, packet_len, sig_len;
|
||||
unsigned char *method, *pubkeydata, *packet, *s, *b, *sig, *data;
|
||||
unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_PK_OK, 0 };
|
||||
unsigned long method_len, pubkeydata_len, packet_len, sig_len, data_len;
|
||||
|
||||
if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) {
|
||||
return -1;
|
||||
@@ -507,6 +523,9 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
libssh2_htonu32(s, pubkeydata_len); s += 4;
|
||||
memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication");
|
||||
#endif
|
||||
if (libssh2_packet_write(session, packet, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
@@ -515,45 +534,38 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
/* God help any SSH server that allows an UNVERIFIED public key to validate the user */
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, method);
|
||||
LIBSSH2_FREE(session, pubkeydata);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
|
||||
/* This public key is not allowed for this user on this server */
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, method);
|
||||
LIBSSH2_FREE(session, pubkeydata);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_PK_OK, &data, &data_len, 0) == 0) {
|
||||
/* Semi-Success! */
|
||||
if ((libssh2_ntohu32(data + 1) != method_len) ||
|
||||
strncmp(data + 5, method, method_len) ||
|
||||
(libssh2_ntohu32(data + 5 + method_len) != pubkeydata_len) ||
|
||||
strncmp(data + 5 + method_len + 4, pubkeydata, pubkeydata_len)) {
|
||||
/* Unlikely but possible, the server has responded to a different userauth public key request */
|
||||
LIBSSH2_FREE(session, data);
|
||||
continue;
|
||||
}
|
||||
LIBSSH2_FREE(session, data);
|
||||
break;
|
||||
}
|
||||
/* TODO: Timeout? */
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, method);
|
||||
LIBSSH2_FREE(session, pubkeydata);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Pubkey authentication prematurely successful");
|
||||
#endif
|
||||
/* God help any SSH server that allows an UNVERIFIED public key to validate the user */
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, method);
|
||||
LIBSSH2_FREE(session, pubkeydata);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data[0] == SSH_MSG_USERAUTH_FAILURE) {
|
||||
/* This public key is not allowed for this user on this server */
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
LIBSSH2_FREE(session, method);
|
||||
LIBSSH2_FREE(session, pubkeydata);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, "Username/PublicKey combination invalid", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Semi-Success! */
|
||||
LIBSSH2_FREE(session, data);
|
||||
LIBSSH2_FREE(session, pubkeydata);
|
||||
|
||||
if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) {
|
||||
@@ -607,6 +619,9 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
memcpy(s, sig, sig_len); s += sig_len;
|
||||
LIBSSH2_FREE(session, sig);
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting publickey authentication -- phase 2");
|
||||
#endif
|
||||
if (libssh2_packet_write(session, packet, s - packet)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-publickey request", 0);
|
||||
LIBSSH2_FREE(session, packet);
|
||||
@@ -614,26 +629,227 @@ LIBSSH2_API int libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION *session,
|
||||
}
|
||||
LIBSSH2_FREE(session, packet);
|
||||
|
||||
while (1) {
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
/* PK_OK is no longer valid */
|
||||
reply_codes[2] = 0;
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_SUCCESS, &data, &data_len, 1) == 0) {
|
||||
/* We are us and we've proved it. */
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Publickey authentication successful");
|
||||
#endif
|
||||
/* We are us and we've proved it. */
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This public key is not allowed for this user on this server */
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_userauth_keyboard_interactive
|
||||
* Authenticate using a challenge-response authentication
|
||||
*/
|
||||
LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION *session, const char *username, int username_len,
|
||||
LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback)))
|
||||
{
|
||||
unsigned char *s, *data; /* packet */
|
||||
unsigned long packet_len;
|
||||
|
||||
packet_len = 1 /* byte SSH_MSG_USERAUTH_REQUEST */
|
||||
+ 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */
|
||||
+ 4 + 14 /* string service name (US-ASCII) */
|
||||
+ 4 + 20 /* string "keyboard-interactive" (US-ASCII) */
|
||||
+ 4 + 0 /* string language tag (as defined in [RFC-3066]) */
|
||||
+ 4 + 0 /* string submethods (ISO-10646 UTF-8) */
|
||||
;
|
||||
|
||||
if (!(data = s = LIBSSH2_ALLOC(session, packet_len))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive authentication", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*s++ = SSH_MSG_USERAUTH_REQUEST;
|
||||
|
||||
/* user name */
|
||||
libssh2_htonu32(s, username_len); s += 4;
|
||||
memcpy(s, username, username_len); s += username_len;
|
||||
|
||||
/* service name */
|
||||
libssh2_htonu32(s, sizeof("ssh-connection") - 1); s += 4;
|
||||
memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); s += sizeof("ssh-connection") - 1;
|
||||
|
||||
/* "keyboard-interactive" */
|
||||
libssh2_htonu32(s, sizeof("keyboard-interactive") - 1); s += 4;
|
||||
memcpy(s, "keyboard-interactive", sizeof("keyboard-interactive") - 1); s += sizeof("keyboard-interactive") - 1;
|
||||
|
||||
/* language tag */
|
||||
libssh2_htonu32(s, 0); s += 4;
|
||||
|
||||
/* submethods */
|
||||
libssh2_htonu32(s, 0); s += 4;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Attempting keyboard-interactive authentication");
|
||||
#endif
|
||||
if (libssh2_packet_write(session, data, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send keyboard-interactive request", 0);
|
||||
LIBSSH2_FREE(session, data);
|
||||
return -1;
|
||||
}
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
for (;;) {
|
||||
unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 };
|
||||
unsigned int auth_name_len;
|
||||
char* auth_name = NULL;
|
||||
unsigned auth_instruction_len;
|
||||
char* auth_instruction = NULL;
|
||||
unsigned int language_tag_len;
|
||||
unsigned long data_len;
|
||||
unsigned int num_prompts = 0;
|
||||
unsigned int i;
|
||||
int auth_failure = 1;
|
||||
LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts = NULL;
|
||||
LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses = NULL;
|
||||
|
||||
if (libssh2_packet_requirev(session, reply_codes, &data, &data_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data[0] == SSH_MSG_USERAUTH_SUCCESS) {
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive authentication successful");
|
||||
#endif
|
||||
LIBSSH2_FREE(session, data);
|
||||
session->state |= LIBSSH2_STATE_AUTHENTICATED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (libssh2_packet_ask(session, SSH_MSG_USERAUTH_FAILURE, &data, &data_len, 0) == 0) {
|
||||
/* This public key is not allowed for this user on this server */
|
||||
if (data[0] == SSH_MSG_USERAUTH_FAILURE) {
|
||||
LIBSSH2_FREE(session, data);
|
||||
libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, "Invalid signature for supplied public key, or bad username/public key combination", 0);
|
||||
return -1;
|
||||
}
|
||||
/* TODO: Timeout? */
|
||||
|
||||
/* server requested PAM-like conversation */
|
||||
|
||||
s = data + 1;
|
||||
|
||||
/* string name (ISO-10646 UTF-8) */
|
||||
auth_name_len = libssh2_ntohu32(s); s += 4;
|
||||
if (!(auth_name = LIBSSH2_ALLOC(session, auth_name_len))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'name' request field", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(auth_name, s, auth_name_len); s += auth_name_len;
|
||||
|
||||
/* string instruction (ISO-10646 UTF-8) */
|
||||
auth_instruction_len = libssh2_ntohu32(s); s += 4;
|
||||
if (!(auth_instruction = LIBSSH2_ALLOC(session, auth_instruction_len))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive 'instruction' request field", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(auth_instruction, s, auth_instruction_len); s += auth_instruction_len;
|
||||
|
||||
/* string language tag (as defined in [RFC-3066]) */
|
||||
language_tag_len = libssh2_ntohu32(s); s += 4;
|
||||
/* ignoring this field as deprecated */ s += language_tag_len;
|
||||
|
||||
/* int num-prompts */
|
||||
num_prompts = libssh2_ntohu32(s); s += 4;
|
||||
|
||||
prompts = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts);
|
||||
if (!prompts) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompts array", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
memset(prompts, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * num_prompts);
|
||||
|
||||
responses = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts);
|
||||
if (!responses) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive responses array", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
memset(responses, 0, sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * num_prompts);
|
||||
|
||||
for(i = 0; i != num_prompts; ++i) {
|
||||
/* string prompt[1] (ISO-10646 UTF-8) */
|
||||
prompts[i].length = libssh2_ntohu32(s); s += 4;
|
||||
if (!(prompts[i].text = LIBSSH2_ALLOC(session, prompts[i].length))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive prompt message", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(prompts[i].text, s, prompts[i].length); s += prompts[i].length;
|
||||
|
||||
/* boolean echo[1] */
|
||||
prompts[i].echo = *s++;
|
||||
}
|
||||
|
||||
response_callback(auth_name, auth_name_len, auth_instruction, auth_instruction_len, num_prompts, prompts, responses, &session->abstract);
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_USERAUTH
|
||||
_libssh2_debug(session, LIBSSH2_DBG_AUTH, "Keyboard-interactive response callback function invoked");
|
||||
#endif
|
||||
|
||||
packet_len = 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */
|
||||
+ 4 /* int num-responses */
|
||||
;
|
||||
|
||||
for (i = 0; i != num_prompts; ++i) {
|
||||
packet_len += 4 + responses[i].length; /* string response[1] (ISO-10646 UTF-8) */
|
||||
}
|
||||
|
||||
if (!(data = s = LIBSSH2_ALLOC(session, packet_len))) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for keyboard-interactive response packet", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*s = SSH_MSG_USERAUTH_INFO_RESPONSE; s++;
|
||||
libssh2_htonu32(s, num_prompts); s += 4;
|
||||
|
||||
for (i = 0; i != num_prompts; ++i) {
|
||||
libssh2_htonu32(s, responses[i].length); s += 4;
|
||||
memcpy(s, responses[i].text, responses[i].length); s += responses[i].length;
|
||||
}
|
||||
|
||||
if (libssh2_packet_write(session, data, packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-keyboard-interactive request", 0);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
auth_failure = 0;
|
||||
|
||||
cleanup:
|
||||
/* It's safe to clean all the data here, because unallocated pointers
|
||||
* are filled by zeroes
|
||||
*/
|
||||
|
||||
LIBSSH2_FREE(session, data);
|
||||
|
||||
if (prompts) {
|
||||
for (i = 0; i != num_prompts; ++i) {
|
||||
LIBSSH2_FREE(session, prompts[i].text);
|
||||
}
|
||||
}
|
||||
|
||||
if (responses) {
|
||||
for (i = 0; i != num_prompts; ++i) {
|
||||
LIBSSH2_FREE(session, responses[i].text);
|
||||
}
|
||||
}
|
||||
|
||||
LIBSSH2_FREE(session, prompts);
|
||||
LIBSSH2_FREE(session, responses);
|
||||
|
||||
if (auth_failure) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
@@ -17,7 +17,7 @@
|
||||
int main(int argc, char *argv[]) {
|
||||
int sock, i, auth_pw = 1;
|
||||
struct sockaddr_in sin;
|
||||
char *fingerprint;
|
||||
const char *fingerprint;
|
||||
LIBSSH2_SESSION *session;
|
||||
LIBSSH2_CHANNEL *channel;
|
||||
#ifdef WIN32
|
||||
|
@@ -1,19 +0,0 @@
|
||||
LIBS = -L../src -lssh2
|
||||
CFLAGS = -c -I../include
|
||||
CC = gcc -Wall -g
|
||||
OBJS = main.o util.o methods.o auth.o forward.o
|
||||
|
||||
all: libssh2-test
|
||||
|
||||
libssh2-test: $(OBJS)
|
||||
$(CC) -o libssh2-test $(OBJS) $(LIBS)
|
||||
|
||||
|
||||
main.o: main.c libssh2-test.h
|
||||
util.o: util.c libssh2-test.h
|
||||
methods.o: methods.c libssh2-test.h
|
||||
auth.o: auth.c libssh2-test.h
|
||||
forward.o: forward.c libssh2-test.h
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) libssh2-test
|
58
test/README
58
test/README
@@ -1,58 +0,0 @@
|
||||
This directory contains some code meant to test libssh2's protocol compliance
|
||||
against other implementations, as well as providing a body of example code.
|
||||
The following tests are done:
|
||||
|
||||
* methods.c:
|
||||
|
||||
Tries a connection + authentication with every possible key exchange,
|
||||
hostkey, encryption, mac and compression method (see
|
||||
libssh2_session_method_pref()).
|
||||
|
||||
The first connection is done with methods that are required to be supported
|
||||
by server implementations according to the standard: key exchange method
|
||||
"diffie-hellman-group1-sha1", hostkey method "ssh-dss", encryption method
|
||||
"3des-cbc", mac method "hmac-sha1" and no compression.
|
||||
|
||||
Then every method is tried in turn, with all the other methods set to these
|
||||
"known good" methods. Hence, the test will report exactly which method of
|
||||
which method type failed to initialize a connection. A total of 26 tests
|
||||
is performed.
|
||||
|
||||
|
||||
* auth.c:
|
||||
|
||||
This test tries to authenticate with username+pasword, and public/private
|
||||
key + passphrase. In addition, the hostkey is retrieved in both MD5 and SHA1
|
||||
formats, and sanity-checked.
|
||||
|
||||
|
||||
* forward.c:
|
||||
|
||||
This test tries to make the remote host listen on a port, then requests a
|
||||
tunnel through the remote host to that port, accepting the connection
|
||||
locally. A block of data is then sent through the tunnel, and this data is
|
||||
then compared to the original when received.
|
||||
|
||||
This is tried four times: one each for every combination of the remote
|
||||
host's bind address provided or not, and bind port provided or not.
|
||||
|
||||
|
||||
|
||||
If you want to add a test, you will need to do the following:
|
||||
|
||||
* create a new source file, and add it to the Makefile
|
||||
* call your main function (runtest_yourtest) from main() in main.c
|
||||
* at the top of your source file, put:
|
||||
|
||||
#include <libssh2.h>
|
||||
#include "libssh2-test.h"
|
||||
|
||||
extern struct authdefs auth;
|
||||
|
||||
* call init_test("description of your test", number_of_steps)
|
||||
* use new_socket() to get a connected socket, and log_line() to log
|
||||
errors if your test fails.
|
||||
* before every step in your test, call increase_progress()
|
||||
* after every step, if it was successful, call step_successful()
|
||||
* after your test, print a "\n".
|
||||
|
215
test/auth.c
215
test/auth.c
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* auth.c -- test authentication methods
|
||||
*
|
||||
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libssh2.h>
|
||||
#include "libssh2-test.h"
|
||||
|
||||
extern struct authdefs auth;
|
||||
|
||||
|
||||
static int auth_publickey(LIBSSH2_SESSION *session)
|
||||
{
|
||||
|
||||
if(libssh2_userauth_publickey_fromfile(session, auth.username,
|
||||
auth.pubkey, auth.privkey, auth.passphrase))
|
||||
{
|
||||
log_line(ERROR, "Public key authentication failed\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(!libssh2_userauth_authenticated(session))
|
||||
{
|
||||
log_line(ERROR, "Public key authentication succeeded, but authentication not set\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int auth_password(LIBSSH2_SESSION *session)
|
||||
{
|
||||
|
||||
if(libssh2_userauth_password(session, auth.username, auth.password))
|
||||
{
|
||||
log_line(ERROR, "Password authentication failed\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(!libssh2_userauth_authenticated(session))
|
||||
{
|
||||
log_line(ERROR, "Password authentication succeeded, but authentication not set\n");
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static void all_auth(void)
|
||||
{
|
||||
LIBSSH2_SESSION *session;
|
||||
int sock, size, res, sum, i;
|
||||
unsigned char *hash;
|
||||
char *errmsg, *_authlist, *authlist, *authmethod, *sep;
|
||||
|
||||
authlist = NULL;
|
||||
authmethod = "";
|
||||
while(authmethod)
|
||||
{
|
||||
sock = new_socket();
|
||||
if(sock == -1)
|
||||
{
|
||||
log_line(ERROR, "new_socket() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
session = libssh2_session_init();
|
||||
|
||||
res = libssh2_session_startup(session, sock);
|
||||
if(res)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "session_startup() failed: %s\n", errmsg);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!authlist)
|
||||
{
|
||||
_authlist = libssh2_userauth_list(session, auth.username, strlen(auth.username));
|
||||
if(_authlist == NULL)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "userauth_list() failed: %s\n", errmsg);
|
||||
libssh2_session_disconnect(session, "All done.");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
authlist = strdup(_authlist);
|
||||
authmethod = authlist;
|
||||
|
||||
/* only need to check hostkey hashes once... might as well do that here */
|
||||
increase_progress();
|
||||
hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
|
||||
if(hash)
|
||||
{
|
||||
sum = 0;
|
||||
for(i = 0; i < 16; i++)
|
||||
sum += hash[i];
|
||||
if(sum > 0)
|
||||
step_successful();
|
||||
else
|
||||
log_line(ERROR, "MD5 hostkey hash invalid\n");
|
||||
}
|
||||
else
|
||||
log_line(ERROR, "MD5 hostkey hash failed\n");
|
||||
|
||||
increase_progress();
|
||||
hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
||||
if(hash)
|
||||
{
|
||||
sum = 0;
|
||||
for(i = 0; i < 20; i++)
|
||||
sum += hash[i];
|
||||
if(sum > 0)
|
||||
step_successful();
|
||||
else
|
||||
log_line(ERROR, "SHA1 hostkey hash invalid\n");
|
||||
}
|
||||
else
|
||||
log_line(ERROR, "SHA1 hostkey hash failed\n");
|
||||
|
||||
}
|
||||
|
||||
if( (sep = strchr(authmethod, ',')) )
|
||||
*sep++ = '\0';
|
||||
|
||||
if(!strcasecmp(authmethod, "publickey"))
|
||||
{
|
||||
increase_progress();
|
||||
if(auth_publickey(session))
|
||||
step_successful();
|
||||
}
|
||||
else if(!strcasecmp(authmethod, "password"))
|
||||
{
|
||||
increase_progress();
|
||||
if(auth_password(session))
|
||||
step_successful();
|
||||
}
|
||||
else if(!strcasecmp(authmethod, "keyboard-interactive"))
|
||||
{
|
||||
/* no idea how to test this */
|
||||
}
|
||||
else
|
||||
{
|
||||
log_line(DEBUG, "Unknown authentication method %s\n", authmethod);
|
||||
}
|
||||
|
||||
authmethod = sep;
|
||||
|
||||
libssh2_session_disconnect(session, "All done.");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
}
|
||||
|
||||
free(authlist);
|
||||
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void runtest_auth(void)
|
||||
{
|
||||
|
||||
init_test("authentication", 4);
|
||||
|
||||
all_auth();
|
||||
|
||||
}
|
||||
|
272
test/forward.c
272
test/forward.c
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* forward.c -- ssh2 port forwarding test
|
||||
*
|
||||
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
#include <libssh2.h>
|
||||
#include "libssh2-test.h"
|
||||
|
||||
struct authdefs auth;
|
||||
extern struct addrinfo *cur_ai;
|
||||
|
||||
#define LISTEN_PORT 22617
|
||||
#define TESTBUF_SIZE 10
|
||||
|
||||
|
||||
static int loopback(LIBSSH2_SESSION *session, int hostbind, int portbind)
|
||||
{
|
||||
LIBSSH2_CHANNEL *inbound, *outbound;
|
||||
LIBSSH2_LISTENER *listener;
|
||||
int listen_port, size, i, res;
|
||||
char paramstr[64], ipstr[128], *errmsg, *host, *sendbuf, *recvbuf;
|
||||
|
||||
snprintf(paramstr, 64, "(%shost bind, %sport bind)", hostbind ? "" : "no ", portbind ? "" : "no ");
|
||||
|
||||
host = NULL;
|
||||
if(hostbind)
|
||||
{
|
||||
if(getnameinfo(cur_ai->ai_addr, cur_ai->ai_addrlen, ipstr, sizeof(ipstr), NULL, 0, NI_NUMERICHOST))
|
||||
{
|
||||
log_line(ERROR, "getnameinfo() failed\n");
|
||||
return(0);
|
||||
}
|
||||
host = ipstr;
|
||||
}
|
||||
|
||||
listen_port = 0;
|
||||
if(portbind)
|
||||
listen_port = LISTEN_PORT;
|
||||
|
||||
listener = libssh2_channel_forward_listen_ex(session, host, listen_port, &listen_port, 2);
|
||||
if(!listener)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "Listen failed %s: %s\n", paramstr, errmsg);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
outbound = libssh2_channel_direct_tcpip(session, auth.hostname, listen_port);
|
||||
if(!outbound)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "Outbound channel setup failed %s: %s\n", paramstr, errmsg);
|
||||
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
inbound = libssh2_channel_forward_accept(listener);
|
||||
if(!inbound)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "Forwarding channel accept failed %s: %s\n", paramstr, errmsg);
|
||||
|
||||
libssh2_channel_free(outbound);
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
sendbuf = malloc(TESTBUF_SIZE);
|
||||
if(!sendbuf)
|
||||
{
|
||||
log_line(ERROR, "sendbuf malloc failed\n");
|
||||
|
||||
libssh2_channel_free(inbound);
|
||||
libssh2_channel_free(outbound);
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
for(i = 0; i < TESTBUF_SIZE; i++)
|
||||
sendbuf[i] = (char) random;
|
||||
|
||||
res = libssh2_channel_write(outbound, sendbuf, TESTBUF_SIZE);
|
||||
if(res != TESTBUF_SIZE)
|
||||
{
|
||||
if(res == -1)
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
else
|
||||
errmsg = NULL;
|
||||
log_line(ERROR, "Unable to send %d bytes across tunnel %s%s%s\n",
|
||||
TESTBUF_SIZE, paramstr, errmsg ? ": " : "", errmsg ? errmsg : "");
|
||||
|
||||
free(sendbuf);
|
||||
libssh2_channel_free(inbound);
|
||||
libssh2_channel_free(outbound);
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
recvbuf = malloc(TESTBUF_SIZE);
|
||||
if(!recvbuf)
|
||||
{
|
||||
log_line(ERROR, "recvbuf malloc failed\n");
|
||||
|
||||
free(sendbuf);
|
||||
libssh2_channel_free(inbound);
|
||||
libssh2_channel_free(outbound);
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
res = libssh2_channel_read(inbound, recvbuf, TESTBUF_SIZE);
|
||||
if(res != TESTBUF_SIZE)
|
||||
{
|
||||
if(res == -1)
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
else
|
||||
errmsg = NULL;
|
||||
log_line(ERROR, "Unable to receive %d bytes across tunnel %s%s%s\n",
|
||||
TESTBUF_SIZE, paramstr, errmsg ? ": " : "", errmsg ? errmsg : "");
|
||||
|
||||
free(sendbuf);
|
||||
free(recvbuf);
|
||||
libssh2_channel_free(inbound);
|
||||
libssh2_channel_free(outbound);
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
res = 1;
|
||||
for(i = 0; i < TESTBUF_SIZE; i++)
|
||||
{
|
||||
if(recvbuf[i] != sendbuf[i])
|
||||
{
|
||||
log_line(ERROR, "Received data did not match sent data %s\n", paramstr);
|
||||
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(sendbuf);
|
||||
free(recvbuf);
|
||||
libssh2_channel_free(inbound);
|
||||
libssh2_channel_free(outbound);
|
||||
libssh2_channel_forward_cancel(listener);
|
||||
|
||||
return(res);
|
||||
}
|
||||
|
||||
|
||||
static void all_forward(void)
|
||||
{
|
||||
LIBSSH2_SESSION *session;
|
||||
int sock, res, size;
|
||||
char *errmsg;
|
||||
|
||||
sock = new_socket();
|
||||
if(sock == -1)
|
||||
{
|
||||
log_line(ERROR, "Unable to open a socket\n");
|
||||
return;
|
||||
}
|
||||
|
||||
session = libssh2_session_init();
|
||||
|
||||
res = libssh2_session_startup(session, sock);
|
||||
if(res)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "Session startup failed: %s\n", errmsg);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
if(libssh2_userauth_password(session, auth.username, auth.password))
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "Authentication failed%s%s\n", errmsg[0] ? ": " : "", errmsg);
|
||||
libssh2_session_disconnect(session, "All done.");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
increase_progress();
|
||||
if(loopback(session, 1, 1))
|
||||
step_successful();
|
||||
|
||||
increase_progress();
|
||||
if(loopback(session, 1, 0))
|
||||
step_successful();
|
||||
|
||||
increase_progress();
|
||||
if(loopback(session, 0, 1))
|
||||
step_successful();
|
||||
|
||||
increase_progress();
|
||||
if(loopback(session, 0, 0))
|
||||
step_successful();
|
||||
|
||||
libssh2_session_disconnect(session, "All done.");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void runtest_forward(void)
|
||||
{
|
||||
|
||||
init_test("TCP port forwarding/tunneling", 4);
|
||||
|
||||
all_forward();
|
||||
|
||||
}
|
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* prototest.h
|
||||
*
|
||||
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LIBSSH2_TEST_H
|
||||
#define LIBSSH2_TEST_H 1
|
||||
|
||||
#define MAX_LOGLINE_LEN 256
|
||||
|
||||
|
||||
void log_line(int priority, char *format, ...);
|
||||
void init_test(char *msg, int num_items);
|
||||
void increase_progress(void);
|
||||
void step_successful(void);
|
||||
void output_testresults(void);
|
||||
int new_socket(void);
|
||||
void runtest_methods(void);
|
||||
void runtest_auth(void);
|
||||
void runtest_forward(void);
|
||||
|
||||
|
||||
|
||||
struct authdefs {
|
||||
char *hostname;
|
||||
char *port;
|
||||
char *username;
|
||||
char *password;
|
||||
char *privkey;
|
||||
char *pubkey;
|
||||
char *passphrase;
|
||||
};
|
||||
|
||||
struct methodlist {
|
||||
int method_type;
|
||||
char *description;
|
||||
char **list;
|
||||
int cursor;
|
||||
int done;
|
||||
};
|
||||
|
||||
struct logentry {
|
||||
int priority;
|
||||
char *logline;
|
||||
struct logentry *next;
|
||||
};
|
||||
|
||||
struct logresults {
|
||||
char *description;
|
||||
int num_steps;
|
||||
int success_steps;
|
||||
int progress;
|
||||
struct logentry *log;
|
||||
struct logresults *next;
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
ERROR,
|
||||
WARNING,
|
||||
NORMAL,
|
||||
DEBUG
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
174
test/main.c
174
test/main.c
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* main.c -- ssh2 protocol compliance tester for libssh2
|
||||
*
|
||||
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
#include <libssh2.h>
|
||||
#include "libssh2-test.h"
|
||||
|
||||
struct authdefs auth;
|
||||
char *progress_message;
|
||||
int progress, progress_max;
|
||||
struct addrinfo *hostai = NULL, *cur_ai = NULL;
|
||||
|
||||
extern struct logresults *testlogs;
|
||||
extern struct logentry *logfile;
|
||||
|
||||
|
||||
|
||||
void cleanup(void)
|
||||
{
|
||||
struct logresults *test, *nexttest;
|
||||
struct logentry *logfile, *nextlog;
|
||||
|
||||
if(hostai)
|
||||
freeaddrinfo(hostai);
|
||||
|
||||
test = testlogs;
|
||||
while(test)
|
||||
{
|
||||
logfile = test->log;
|
||||
while(logfile)
|
||||
{
|
||||
nextlog = logfile->next;
|
||||
free(logfile->logline);
|
||||
free(logfile);
|
||||
|
||||
logfile = nextlog;
|
||||
}
|
||||
nexttest = test->next;
|
||||
free(test);
|
||||
|
||||
test = nexttest;
|
||||
}
|
||||
|
||||
free(auth.hostname);
|
||||
free(auth.port);
|
||||
free(auth.username);
|
||||
free(auth.password);
|
||||
free(auth.privkey);
|
||||
free(auth.pubkey);
|
||||
free(auth.passphrase);
|
||||
|
||||
}
|
||||
|
||||
|
||||
char *get_interactive(char *prompt, int size, char *default_value)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if( !(str = malloc(size)) )
|
||||
{
|
||||
log_line(ERROR, "unable to malloc %d bytes for %s\n", size, prompt);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
printf("%s [%s]: ", prompt, default_value);
|
||||
fgets(str, size, stdin);
|
||||
if(str[strlen(str)-1] == '\n')
|
||||
str[strlen(str)-1] = 0;
|
||||
if(!str[0])
|
||||
strncpy(str, default_value, size);
|
||||
|
||||
return(str);
|
||||
}
|
||||
|
||||
|
||||
char *resolve_tilde(char *path)
|
||||
{
|
||||
wordexp_t we;
|
||||
|
||||
if( (wordexp(path, &we, 0)) == 0 && we.we_wordc == 1)
|
||||
{
|
||||
free(path);
|
||||
path = strdup(we.we_wordv[0]);
|
||||
wordfree(&we);
|
||||
}
|
||||
|
||||
return(path);
|
||||
}
|
||||
|
||||
|
||||
void get_auth(void)
|
||||
{
|
||||
|
||||
auth.hostname = get_interactive("hostname", 64, "localhost");
|
||||
auth.port = get_interactive("port", 6, "22");
|
||||
// auth.username = get_interactive("username", 20, getenv("USER"));
|
||||
// auth.password = get_interactive("password", 20, "");
|
||||
auth.username = get_interactive("username", 20, "bert2");
|
||||
auth.password = get_interactive("password", 20, "blinko");
|
||||
|
||||
auth.privkey = resolve_tilde(get_interactive("private key filename", 128, "~/.ssh/id_dsa"));
|
||||
auth.pubkey = resolve_tilde(get_interactive("public key filename", 128, "~/.ssh/id_dsa.pub"));
|
||||
auth.passphrase = get_interactive("passphrase", 256, "");
|
||||
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
get_auth();
|
||||
if(!strlen(auth.username) || !strlen(auth.password))
|
||||
{
|
||||
printf("Not enough authentication info to continue.\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
runtest_methods();
|
||||
runtest_auth();
|
||||
runtest_forward();
|
||||
|
||||
output_testresults();
|
||||
|
||||
cleanup();
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
214
test/methods.c
214
test/methods.c
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* methods.c -- test all available key exchange, hostkey, encryption, mac
|
||||
* and compression methods
|
||||
*
|
||||
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libssh2.h>
|
||||
#include "libssh2-test.h"
|
||||
|
||||
extern struct authdefs auth;
|
||||
|
||||
|
||||
static char *kex_methods[] = {
|
||||
"diffie-hellman-group1-sha1",
|
||||
"diffie-hellman-group14-sha1",
|
||||
"diffie-hellman-group-exchange-sha1",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *hostkey_methods[] = {
|
||||
"ssh-dss",
|
||||
"ssh-rsa",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *crypt_methods[] = {
|
||||
"3des-cbc",
|
||||
"aes256-cbc",
|
||||
"aes192-cbc",
|
||||
"aes128-cbc",
|
||||
"blowfish-cbc",
|
||||
"arcfour",
|
||||
"cast128-cbc",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *mac_methods[] = {
|
||||
"hmac-sha1",
|
||||
"hmac-sha1-96",
|
||||
"hmac-md5",
|
||||
"hmac-md5-96",
|
||||
"hmac-ripemd160",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char *compression_methods[] = {
|
||||
"none",
|
||||
"zlib",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static struct methodlist methods[] = {
|
||||
{ LIBSSH2_METHOD_KEX, "kex", kex_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_HOSTKEY, "hostkey", hostkey_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_CRYPT_CS, "crypt (cs)", crypt_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_CRYPT_SC, "crypt (sc)", crypt_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_MAC_CS, "MAC (cs)", mac_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_MAC_SC, "MAC (sc)", mac_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_COMP_CS, "compression (cs)", compression_methods, 0, 0 },
|
||||
{ LIBSSH2_METHOD_COMP_SC, "compression (sc)", compression_methods, 0, 0 },
|
||||
{ 0, NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
static void dump_methods(LIBSSH2_SESSION *session)
|
||||
{
|
||||
|
||||
printf(" Key exchange methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_KEX));
|
||||
printf(" Hostkey methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY));
|
||||
printf(" Crypt C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS));
|
||||
printf(" Crypt S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC));
|
||||
printf(" MAC C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS));
|
||||
printf(" MAC S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC));
|
||||
printf("Compression C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS));
|
||||
printf("Compression S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC));
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
static void cycle_methods(void)
|
||||
{
|
||||
LIBSSH2_SESSION *session;
|
||||
int sock, size, res, method_type, method, i, methods_set;
|
||||
char *errmsg;
|
||||
|
||||
method_type = 0;
|
||||
method = 0;
|
||||
while(methods[method_type].description)
|
||||
{
|
||||
while(methods[method_type].list[method])
|
||||
{
|
||||
increase_progress();
|
||||
sock = new_socket();
|
||||
if(sock == -1)
|
||||
{
|
||||
log_line(ERROR, "new_socket() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
session = libssh2_session_init();
|
||||
|
||||
methods_set = 1;
|
||||
for(i = 0; methods[i].description; i++)
|
||||
{
|
||||
res = libssh2_session_method_pref(session, methods[i].method_type,
|
||||
methods[i].list[ i == method_type ? method : 0 ]);
|
||||
if(res != 0)
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "%s method set to '%s' failed: %s\n",
|
||||
methods[i].description,
|
||||
methods[i].list[ i == method_type ? method : 0 ], errmsg);
|
||||
|
||||
methods_set = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(methods_set)
|
||||
{
|
||||
res = libssh2_session_startup(session, sock);
|
||||
if(res == 0)
|
||||
{
|
||||
if(libssh2_userauth_password(session, auth.username, auth.password))
|
||||
{
|
||||
log_line(ERROR, "Authentication failed\n");
|
||||
}
|
||||
else
|
||||
step_successful();
|
||||
}
|
||||
else
|
||||
{
|
||||
libssh2_session_last_error(session, &errmsg, &size, 0);
|
||||
log_line(ERROR, "Session startup for %s method %s failed: %s\n",
|
||||
methods[method_type].description, methods[method_type].list[method], errmsg);
|
||||
}
|
||||
}
|
||||
|
||||
libssh2_session_disconnect(session, "All done.");
|
||||
libssh2_session_free(session);
|
||||
close(sock);
|
||||
|
||||
method++;
|
||||
}
|
||||
method_type++;
|
||||
method = 1;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void runtest_methods(void)
|
||||
{
|
||||
int i, j, num_steps;
|
||||
|
||||
num_steps = 0;
|
||||
for(i = 0; methods[i].description; i++)
|
||||
{
|
||||
for(j = 0; methods[i].list[j]; j++)
|
||||
;
|
||||
num_steps += j - 1;
|
||||
}
|
||||
num_steps++;
|
||||
|
||||
init_test("kex/hostkey/crypt/mac/compression methods", num_steps);
|
||||
|
||||
cycle_methods();
|
||||
|
||||
}
|
||||
|
222
test/util.c
222
test/util.c
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* util.c
|
||||
*
|
||||
* Copyright (C) 2005 Bert Vermeulen <bert@biot.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms,
|
||||
* with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
*
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* Neither the name of the copyright holder nor the names
|
||||
* of any other contributors may be used to endorse or
|
||||
* promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libssh2.h>
|
||||
#include "libssh2-test.h"
|
||||
|
||||
|
||||
extern struct addrinfo *hostai, *cur_ai;
|
||||
struct logresults *testlogs = NULL, *cur_testlog;
|
||||
struct logentry *logfile = NULL, *cur_logentry;
|
||||
|
||||
extern struct authdefs auth;
|
||||
extern char *progress_message;
|
||||
extern int progress, progress_max;
|
||||
|
||||
|
||||
struct loglevel {
|
||||
int priority;
|
||||
char *descr;
|
||||
} loglevels[] = {
|
||||
{ ERROR, "ERROR" },
|
||||
{ WARNING, "WARNING" },
|
||||
{ NORMAL, "NORMAL" },
|
||||
{ DEBUG, "DEBUG" }
|
||||
};
|
||||
|
||||
|
||||
|
||||
void log_line(int priority, char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
struct logentry *entry;
|
||||
char line[MAX_LOGLINE_LEN];
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(line, MAX_LOGLINE_LEN, format, args);
|
||||
va_end(args);
|
||||
|
||||
entry = malloc(sizeof(struct logentry));
|
||||
entry->priority = priority;
|
||||
entry->logline = malloc(strlen(line)+1);
|
||||
strcpy(entry->logline, line);
|
||||
entry->next = NULL;
|
||||
|
||||
if(!cur_testlog->log)
|
||||
cur_testlog->log = entry;
|
||||
else
|
||||
cur_logentry->next = entry;
|
||||
|
||||
cur_logentry = entry;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void init_test(char *msg, int num_items)
|
||||
{
|
||||
struct logresults *newtest;
|
||||
|
||||
newtest = malloc(sizeof(struct logresults));
|
||||
newtest->description = msg;
|
||||
newtest->num_steps = num_items;
|
||||
newtest->success_steps = 0;
|
||||
newtest->progress = 0;
|
||||
newtest->log = NULL;
|
||||
newtest->next = NULL;
|
||||
|
||||
if(!testlogs)
|
||||
testlogs = newtest;
|
||||
else
|
||||
cur_testlog->next = newtest;
|
||||
|
||||
cur_testlog = newtest;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void increase_progress(void)
|
||||
{
|
||||
|
||||
cur_testlog->progress++;
|
||||
|
||||
printf("Testing %s... %3d/%d\r", cur_testlog->description, cur_testlog->progress, cur_testlog->num_steps);
|
||||
fflush(stdout);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void step_successful(void)
|
||||
{
|
||||
|
||||
cur_testlog->success_steps++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void output_testresults(void)
|
||||
{
|
||||
struct logresults *test;
|
||||
struct logentry *logfile;
|
||||
int total_steps, total_success;
|
||||
|
||||
printf("\nTest results\n============\n");
|
||||
|
||||
total_steps = 0;
|
||||
total_success = 0;
|
||||
test = testlogs;
|
||||
while(test)
|
||||
{
|
||||
total_steps += test->num_steps;
|
||||
total_success += test->success_steps;
|
||||
printf("Test: %s (%d/%d)\n", test->description, test->success_steps, test->num_steps);
|
||||
logfile = test->log;
|
||||
while(logfile)
|
||||
{
|
||||
printf(" %s", logfile->logline);
|
||||
logfile = logfile->next;
|
||||
}
|
||||
|
||||
test = test->next;
|
||||
}
|
||||
|
||||
printf("%d/%d steps successful\n", total_success, total_steps);
|
||||
|
||||
}
|
||||
|
||||
|
||||
int new_socket(void)
|
||||
{
|
||||
int sock, res;
|
||||
struct addrinfo hints, *ai;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if(!hostai)
|
||||
{
|
||||
res = getaddrinfo(auth.hostname, auth.port, &hints, &hostai);
|
||||
if(res)
|
||||
{
|
||||
printf("unable to resolve %s: %s\n", auth.hostname, gai_strerror(res));
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
|
||||
sock = 0;
|
||||
ai = hostai;
|
||||
while(ai)
|
||||
{
|
||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if(sock > 0)
|
||||
{
|
||||
res = connect(sock, ai->ai_addr, ai->ai_addrlen);
|
||||
if(res == 0)
|
||||
break;
|
||||
|
||||
close(sock);
|
||||
sock = 0;
|
||||
}
|
||||
ai = ai->ai_next;
|
||||
}
|
||||
|
||||
if(!sock)
|
||||
{
|
||||
printf("unable to connect: %s\n", strerror(errno));
|
||||
close(sock);
|
||||
sock = -1;
|
||||
}
|
||||
|
||||
cur_ai = ai;
|
||||
|
||||
return(sock);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user