Initial revision

This commit is contained in:
Sara Golemon 2004-12-07 21:17:20 +00:00
commit 7a5ffc8cee
23 changed files with 6431 additions and 0 deletions

37
LICENSE Normal file
View File

@ -0,0 +1,37 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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.
*/

48
Makefile.in Normal file
View File

@ -0,0 +1,48 @@
subdirs = src/
top_srcdir = @top_srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
libdir = @exec_prefix@/lib
incldir = @prefix@/include
distdir = @top_srcdir@/dist
CC = @CC@
CFLAGS = -c @CFLAGS@ -Iinclude/ -Wall -g
LIBS = -lssh2 -Lsrc/
INSTALL = @INSTALL@
VERSION=0.1-dev
DISTLIB=libssh2-$(VERSION)
all:
@for dir in ${subdirs}; do \
(cd $$dir && $(MAKE) all) \
|| case "$(MFLAGS)" in *k*) fail=yes;; *) exit 1;; esac; \
done && test -z "$$fail"
$(CC) -o ssh2_sample.o ssh2_sample.c $(CFLAGS)
$(CC) -o ssh2_sample ssh2_sample.o $(LIBS)
install:
$(top_srcdir)/mkinstalldirs $(incldir)
$(top_srcdir)/mkinstalldirs $(libdir)
@for dir in ${subdirs}; do \
(cd $$dir && $(MAKE) install) \
|| case "$(MFLAGS)" in *k*) fail=yes;; *) exit 1;; esac; \
done && test -z "$$fail"
$(INSTALL) -m 644 include/libssh2.h $(incldir)/
clean:
@for dir in ${subdirs}; do \
(cd $$dir && $(MAKE) clean) \
|| case "$(MFLAGS)" in *k*) fail=yes;; *) exit 1;; esac; \
done && test -z "$$fail"
rm -f ssh2_sample.o ssh2_sample
dist:
autoheader
autoconf
rm -f $(DISTLIB)
ln -s . $(DISTLIB)
tar -zcf $(DISTLIB).tar.gz \
$(DISTLIB)/configure.in $(DISTLIB)/configure $(DISTLIB)/Makefile.in $(DISTLIB)/ssh2_sample.c \
$(DISTLIB)/LICENSE $(DISTLIB)/README $(DISTLIB)/TODO \
$(DISTLIB)/mkinstalldirs $(DISTLIB)/install-sh \
$(DISTLIB)/src/*.c $(DISTLIB)/src/Makefile.in \
$(DISTLIB)/include/libssh2.h $(DISTLIB)/include/libssh2_priv.h $(DISTLIB)/include/libssh2_config.h.in
rm -f $(DISTLIB)

14
README Normal file
View File

@ -0,0 +1,14 @@
libssh2 - SSH2 library
======================
Version 0.1-dev
---------------
Initial Release:
KEX methods: diffie-hellman-group14-sha1, diffie-hellman-group-exchange-sha1, diffie-hellman-group1-sha1
Hostkey methods: ssh-rsa, ssh-dss
Cipher methods: aes256-cbc, rijndael-cbc@lysator.liu.se, aes192-cbc, aes128-cbc, blowfish-cbc, arcfour, cast128-cbc, 3des-cbc, none*
Compression methods: zlib, none
MAC methods: hmac-sha1, hmac-sha1-96, hmac-ripemd160, hmac-ripemd160@openssh.com none*
*Cipher/MAC "none" is disabled by default for security purposes,
Use --enable-crypt-none and/or --enable-mac-none with ./configure to enable

4
TODO Normal file
View File

@ -0,0 +1,4 @@
* More Crypt Methods
* hmac-md5, hmac-md5-96
* SFTP support
* Review callbacks

150
configure.in Normal file
View File

@ -0,0 +1,150 @@
# AC_PREREQ(2.57)
AC_INIT(libssh2, 0.1 , pollita@php.net)
AC_CONFIG_SRCDIR([src])
AC_CONFIG_HEADER([include/libssh2_config.h])
SHLIB_SUFFIX_NAME="so"
SHLIB_LDFLAGS="-shared"
AC_SUBST(SHLIB_SUFFIX_NAME)
AC_SUBST(SHLIB_LDFLAGS)
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_RANLIB
AC_C_BIGENDIAN
if test -z "$PKG_CONFIG"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
fi
#
# Look for OpenSSL
#
AC_ARG_WITH(openssl,
AC_HELP_STRING([--with-openssl=DIR],[Look for OpenSSL in PATH]),
[LIBSSH2_OPENSSL_DIR=$withval],[LIBSSH2_OPENSSL_DIR=yes])
if test "$LIBSSH2_OPENSSL_DIR" = "no" || test "$LIBSSH2_OPENSSL_DIR" = "yes"; then
unset LIBSSH2_OPENSSL_DIR
fi
found_openssl=no
unset OPENSSL_INCDIR
unset OPENSSL_LIBDIR
AC_MSG_CHECKING([for OpenSSL])
# Explicit path given, use it rather than pkg-config
if test ! -z "$LIBSSH2_OPENSSL_DIR"; then
found_openssl=yes
OPENSSL_LIBDIR=$LIBSSH2_OPENSSL_DIR/lib
OPENSSL_INCDIR=$LIBSSH2_OPENSSL_DIR/include
AC_MSG_RESULT([Using explicit path $LIBSSH2_OPENSSL_DIR])
fi
# If pkg-config is found try using it
if test "$found_openssl" = "no" && test -x "$PKG_CONFIG" && $PKG_CONFIG --exists openssl; then
found_openssl=yes
OPENSSL_LIBDIR=`$PKG_CONFIG --libs openssl`
OPENSSL_INCDIR=`$PKG_CONFIG --variable=includedir openssl`
AC_MSG_RESULT([Using paths from pkg-config])
fi
# Elsewise, search for OpenSSL wherever it might be
if test "$found_openssl" = "no"; then
OPENSSL_SEARCH_PATH="/usr/local/ssl /usr/local /usr /usr/local/openssl"
for i in $OPENSSL_SEARCH_PATH; do
if test -r $i/include/openssl/evp.h; then
OPENSSL_INCDIR=$i/include
fi
if test -r $i/include/openssl/hmac.h; then
OPENSSL_INCDIR=$i/include
fi
if test -r $i/lib/libcrypto.a -o -r $i/lib/libcrypto.$SHLIB_SUFFIX_NAME; then
OPENSSL_LIBDIR=$i/lib
fi
test -n "$OPENSSL_INCDIR" && test -n "$OPENSSL_LIBDIR" && break
done
if test -z "$OPENSSL_INCDIR"; then
AC_MSG_ERROR([Cannot find OpenSSL's <evp.h> or <hmac.h>])
fi
if test -z "$OPENSSL_LIBDIR"; then
AC_MSG_ERROR([Cannot find OpenSSL's libcrypto])
fi
AC_MSG_RESULT([$OPENSSL_INCDIR $OPENSSL_LIBDIR])
fi
#
# Confirm required OpenSSL libs
#
if test ! -r $OPENSSL_INCDIR/openssl/bn.h || test ! -r $OPENSSL_INCDIR/openssl/evp.h || \
test ! -r $OPENSSL_INCDIR/openssl/hmac.h || test ! -r $OPENSSL_INCDIR/openssl/pem.h || \
test ! -r $OPENSSL_INCDIR/openssl/sha.h; then
AC_MSG_ERROR([Missing one or more of <openssl/bn.h>, <openssl/evp.h>, <openssl/hmac.h>, <openssl/pem.h>, <openssl/sha.h>])
fi
CFLAGS="$CFLAGS -I$OPENSSL_INCDIR"
LDFLAGS="$LDFLAGS -L$OPENSSL_LIBDIR -lcrypto"
#
# zlib
#
AC_ARG_WITH(libz,
AC_HELP_STRING([--with-libz=PATH],[Look for libz in PATH]),
[LIBSSH2_LIBZ_DIR=$withval],[LIBSSH2_LIBZ_DIR="/usr/local /usr /usr/local/libz /usr/libz /usr/local/zlib /usr/zlib"])
if test "$LIBSSH2_LIBZ_DIR" = "no" || test "$LIBSSH2_LIBZ_DIR" = "yes"; then
unset LIBSSH2_LIBZ_DIR
fi
unset LIBZ_INCDIR
unset LIBZ_LIBDIR
AC_MSG_CHECKING([for libz])
for i in $LIBSSH2_LIBZ_DIR; do
if test -r $i/include/zlib.h; then
LIBZ_INCDIR=$i/include
fi
if test -r $i/lib/libz.a -o -r $i/lib/libz.$SHLIB_SUFFIX_NAME; then
LIBZ_LIBDIR=$i/lib
fi
test -n "$LIBZ_INCDIR" && test -n "$LIBZ_LIBDIR" && break
done
if test -n "$LIBZ_INCDIR" && test -n "$LIBZ_LIBDIR"; then
AC_MSG_RESULT([Found in $LIBZ_INCDIR $LIBZ_LIBDIR])
CFLAGS="$CFLAGS -I$LIBZ_INCDIR"
LDFLAGS="$LDFLAGS -L$LIBZ_LIBDIR -lz"
AC_DEFINE(LIBSSH2_HAVE_ZLIB, 1, [Compile in zlib support])
else
AC_MSG_RESULT([Cannot find libz's <zlib.h>])
fi
#
# Optional Settings
#
AC_ARG_ENABLE(crypt-none,
AC_HELP_STRING([--enable-crypt-none],[Permit "none" cipher -- NOT RECOMMENDED]),
[AC_DEFINE(LIBSSH2_CRYPT_NONE, 1, [Enable "none" cipher -- NOT RECOMMENDED])])
AC_ARG_ENABLE(mac-none,
AC_HELP_STRING([--enable-mac-none],[Permit "none" MAC -- NOT RECOMMENDED]),
[AC_DEFINE(LIBSSH2_MAC_NONE, 1, [Enable "none" MAC -- NOT RECOMMENDED])])
# Checks for header files.
# AC_HEADER_STDC
AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_CONFIG_FILES([Makefile
src/Makefile])
AC_OUTPUT

217
include/libssh2.h Normal file
View File

@ -0,0 +1,217 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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_H
#define LIBSSH2_H 1
#include <stddef.h>
#include <string.h>
#include <sys/stat.h>
#define LIBSSH2_VERSION "0.1dev"
/* Part of every banner, user specified or not */
#define LIBSSH2_SSH_BANNER "SSH-2.0-libssh2_" LIBSSH2_VERSION
/* We *could* add a comment here if we so chose */
#define LIBSSH2_SSH_DEFAULT_BANNER LIBSSH2_SSH_BANNER
#define LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF LIBSSH2_SSH_DEFAULT_BANNER "\r\n"
/* Enable the "new" version of diffie-hellman-group-exchange-sha1 */
#define LIBSSH2_DH_GEX_NEW
/* Default generate and safe prime sizes for diffie-hellman-group-exchange-sha1 */
#define LIBSSH2_DH_GEX_MINGROUP 1024
#define LIBSSH2_DH_GEX_OPTGROUP 1536
#define LIBSSH2_DH_GEX_MAXGROUP 2048
/* Defaults for pty requests */
#define LIBSSH2_TERM_WIDTH 80
#define LIBSSH2_TERM_HEIGHT 24
#define LIBSSH2_TERM_WIDTH_PX 0
#define LIBSSH2_TERM_HEIGHT_PX 0
/* 1/4 second */
#define LIBSSH2_SOCKET_POLL_UDELAY 250000
/* 0.25 * 120 == 30 seconds */
#define LIBSSH2_SOCKET_POLL_MAXLOOPS 120
/* Maximum size to allow a payload to compress to, plays it safe by falling short of spec limits */
#define LIBSSH2_PACKET_MAXCOMP 32000
/* Maximum size to allow a payload to deccompress to, plays it safe by allowing more than spec requires */
#define LIBSSH2_PACKET_MAXDECOMP 40000
/* Malloc callbacks */
#define LIBSSH2_ALLOC_FUNC(name) void *name(size_t count, void **abstract)
#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)
/* 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)
#define LIBSSH2_DISCONNECT_FUNC(name) void name(LIBSSH2_SESSION *session, int reason, const char *message, int message_len, const char *language, int language_len, void **abstract)
#define LIBSSH2_PASSWD_CHANGEREQ_FUNC(name) void name(LIBSSH2_SESSION *session, char **newpw, int *newpw_len, void **abstract)
#define LIBSSH2_MACERROR_FUNC(name) int name(LIBSSH2_SESSION *session, const char *packet, int packet_len, void **abstract)
typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION;
typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL;
#ifdef WIN_32
#define LIBSSH2_API __declspec(dllexport)
#else
#define LIBSSH2_API
#endif
#define LIBSSH2_HOSTKEY_HASH_MD5 1
#define LIBSSH2_HOSTKEY_HASH_SHA1 2
/* Disconnect Codes (defined by SSH protocol) */
#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
#define SSH_DISCONNECT_PROTOCOL_ERROR 2
#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3
#define SSH_DISCONNECT_RESERVED 4
#define SSH_DISCONNECT_MAC_ERROR 5
#define SSH_DISCONNECT_COMPRESSION_ERROR 6
#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7
#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
#define SSH_DISCONNECT_CONNECTION_LOST 10
#define SSH_DISCONNECT_BY_APPLICATION 11
#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12
#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13
#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
#define SSH_DISCONNECT_ILLEGAL_USER_NAME 15
/* Error Codes (defined by libssh2) */
#define LIBSSH2_ERROR_SOCKET_NONE -1
#define LIBSSH2_ERROR_BANNER_NONE -2
#define LIBSSH2_ERROR_BANNER_SEND -3
#define LIBSSH2_ERROR_INVALID_MAC -4
#define LIBSSH2_ERROR_KEX_FAILURE -5
#define LIBSSH2_ERROR_ALLOC -6
#define LIBSSH2_ERROR_SOCKET_SEND -7
#define LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE -8
#define LIBSSH2_ERROR_TIMEOUT -9
#define LIBSSH2_ERROR_HOSTKEY_INIT -10
#define LIBSSH2_ERROR_HOSTKEY_SIGN -11
#define LIBSSH2_ERROR_DECRYPT -12
#define LIBSSH2_ERROR_SOCKET_DISCONNECT -13
#define LIBSSH2_ERROR_PROTO -14
#define LIBSSH2_ERROR_PASSWORD_EXPIRED -15
#define LIBSSH2_ERROR_FILE -16
#define LIBSSH2_ERROR_METHOD_NONE -17
#define LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED -18
#define LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED -19
#define LIBSSH2_ERROR_CHANNEL_OUTOFORDER -20
#define LIBSSH2_ERROR_CHANNEL_FAILURE -21
#define LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED -22
#define LIBSSH2_ERROR_CHANNEL_UNKNOWN -23
#define LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED -24
#define LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED -25
#define LIBSSH2_ERROR_CHANNEL_CLOSED -26
#define LIBSSH2_ERROR_CHANNEL_EOF_SENT -27
#define LIBSSH2_ERROR_SCP_PROTOCOL -28
#define LIBSSH2_ERROR_ZLIB -29
#define LIBSSH2_ERROR_SOCKET_TIMEOUT -30
/* 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);
#define libssh2_session_init() libssh2_session_init_ex(NULL, NULL, NULL, NULL)
LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket);
LIBSSH2_API void libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, char *description, 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);
/* Userauth API */
LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, 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)));
#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);
#define libssh2_userauth_publickey_fromfile(session, username, publickey, privatekey, passphrase) \
libssh2_userauth_publickey_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase))
/* Channel API */
#define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536
#define LIBSSH2_CHANNEL_PACKET_DEFAULT 16384
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);
#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)
LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, char *varname, int varname_len, char *value, int value_len);
#define libssh2_channel_setenv(channel, varname, value) libssh2_channel_setenv_ex((channel), (varname), strlen(varname), (value), strlen(value))
LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *term, int term_len, char *modes, int modes_len, int width, int height, int width_px, int height_px);
#define libssh2_channel_request_pty(channel, term) libssh2_channel_request_pty_ex((channel), (term), strlen(term), NULL, 0, LIBSSH2_TERM_WIDTH, LIBSSH2_TERM_HEIGHT, LIBSSH2_TERM_WIDTH_PX, LIBSSH2_TERM_HEIGHT_PX)
LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, char *request, int request_len, 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))
#define SSH_EXTENDED_DATA_STDERR 1
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen);
#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_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 void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking);
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_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);
#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);
#endif /* LIBSSH2_H */

View File

@ -0,0 +1,71 @@
/* include/libssh2_config.h.in. Generated from configure.in by autoheader. */
/* Define to 1 if you have the <errno.h> header file. */
#undef HAVE_ERRNO_H
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Enable "none" cipher -- NOT RECOMMENDED */
#undef LIBSSH2_CRYPT_NONE
/* Compile in zlib support */
#undef LIBSSH2_HAVE_ZLIB
/* Enable "none" MAC -- NOT RECOMMENDED */
#undef LIBSSH2_MAC_NONE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
#undef WORDS_BIGENDIAN
/* Define to empty if `const' does not conform to ANSI C. */
#undef const

379
include/libssh2_priv.h Normal file
View File

@ -0,0 +1,379 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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_PRIV_H
#define LIBSSH2_PRIV_H 1
/* Definitions shared with the public */
#include "libssh2_config.h"
#include "libssh2.h"
#include <sys/socket.h>
#include <openssl/evp.h>
#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract)
#define LIBSSH2_REALLOC(session, ptr, count) session->realloc((ptr), (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)
#define LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len) \
session->ssh_msg_disconnect((session), (always_display), (message), (message_len), (language), (language_len), &(session)->abstract)
#define LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len) \
session->ssh_msg_disconnect((session), (reason), (message), (message_len), (language), (language_len), &(session)->abstract)
#define LIBSSH2_MACERROR(session, data, datalen) session->macerror((session), (data), (datalen), (session)->abstract)
typedef struct _LIBSSH2_KEX_METHOD LIBSSH2_KEX_METHOD;
typedef struct _LIBSSH2_HOSTKEY_METHOD LIBSSH2_HOSTKEY_METHOD;
typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
typedef struct _LIBSSH2_CRYPT_METHOD LIBSSH2_CRYPT_METHOD;
typedef struct _LIBSSH2_COMP_METHOD LIBSSH2_COMP_METHOD;
typedef struct _LIBSSH2_PACKET LIBSSH2_PACKET;
typedef struct _LIBSSH2_PACKET_BRIGADE LIBSSH2_PACKET_BRIGADE;
typedef struct _LIBSSH2_CHANNEL_BRIGADE LIBSSH2_CHANNEL_BRIGADE;
struct _LIBSSH2_PACKET {
unsigned char type;
/* Unencrypted Payload (no type byte, no padding, just the facts ma'am) */
unsigned char *data;
unsigned long data_len;
/* Where to start reading data from,
* used for channel data that's been partially consumed */
unsigned long data_head;
/* Can the message be confirmed? */
int mac;
LIBSSH2_PACKET_BRIGADE *brigade;
LIBSSH2_PACKET *next, *prev;
};
struct _LIBSSH2_PACKET_BRIGADE {
LIBSSH2_PACKET *head, *tail;
};
typedef struct _libssh2_channel_data {
/* Identifier */
unsigned long id;
/* Limits and restrictions */
unsigned long window_size_initial, window_size, packet_size;
/* Set to 1 when CHANNEL_CLOSE / CHANNEL_EOF sent/received */
int close, eof;
} libssh2_channel_data;
struct _LIBSSH2_CHANNEL {
unsigned char *channel_type;
unsigned channel_type_len;
int blocking;
libssh2_channel_data local, remote;
LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *next, *prev;
};
struct _LIBSSH2_CHANNEL_BRIGADE {
LIBSSH2_CHANNEL *head, *tail;
};
typedef struct _libssh2_endpoint_data {
unsigned char *banner;
unsigned char *kexinit;
unsigned long kexinit_len;
LIBSSH2_CRYPT_METHOD *crypt;
void *crypt_abstract;
LIBSSH2_MAC_METHOD *mac;
unsigned long seqno;
void *mac_abstract;
LIBSSH2_COMP_METHOD *comp;
void *comp_abstract;
/* Method Preferences -- NULL yields "load order" */
LIBSSH2_CRYPT_METHOD **crypt_prefs;
LIBSSH2_MAC_METHOD **mac_prefs;
LIBSSH2_COMP_METHOD **comp_prefs;
} libssh2_endpoint_data;
struct _LIBSSH2_SESSION {
/* Memory management callbacks */
void *abstract;
LIBSSH2_ALLOC_FUNC((*alloc));
LIBSSH2_REALLOC_FUNC((*realloc));
LIBSSH2_FREE_FUNC((*free));
/* Other callbacks */
LIBSSH2_IGNORE_FUNC((*ssh_msg_ignore));
LIBSSH2_DEBUG_FUNC((*ssh_msg_debug));
LIBSSH2_DISCONNECT_FUNC((*ssh_msg_disconnect));
LIBSSH2_MACERROR_FUNC((*macerror));
/* Method preferences -- NULL yields "load order" */
LIBSSH2_KEX_METHOD **kex_prefs;
LIBSSH2_HOSTKEY_METHOD **hostkey_prefs;
int exchanging_keys;
int newkeys;
int authenticated;
/* Agreed Key Exchange Method */
LIBSSH2_KEX_METHOD *kex;
unsigned char *session_id;
unsigned long session_id_len;
/* Server's public key */
LIBSSH2_HOSTKEY_METHOD *hostkey;
void *server_hostkey_abstract;
/* Either set with libssh2_session_hostkey() (for server mode)
* Or read from server in (eg) KEXDH_INIT (for client mode)
*/
unsigned char *server_hostkey;
unsigned long server_hostkey_len;
#ifndef OPENSSL_NO_MD5
unsigned char server_hostkey_md5[MD5_DIGEST_LENGTH];
#endif /* ! OPENSSL_NO_MD5 */
#ifndef OPENSSL_NO_SHA
unsigned char server_hostkey_sha1[SHA_DIGEST_LENGTH];
#endif
/* (remote as source of data -- packet_read ) */
libssh2_endpoint_data remote;
/* (local as source of data -- packet_write ) */
libssh2_endpoint_data local;
/* Inbound Data buffer -- Sometimes the packet that comes in isn't the packet we're ready for */
LIBSSH2_PACKET_BRIGADE packets;
/* Active connection channels */
LIBSSH2_CHANNEL_BRIGADE channels;
unsigned long next_channel;
/* Actual I/O socket */
int socket_fd;
int socket_block;
int socket_state;
/* Error tracking */
char *err_msg;
unsigned long err_msglen;
int err_should_free;
int err_code;
};
/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional methods via .so/.dll */
struct _LIBSSH2_KEX_METHOD {
char *name;
/* integrity key length */
unsigned long key_len;
/* Key exchange, populates session->* and returns 0 on success, non-0 on error */
int (*exchange_keys)(LIBSSH2_SESSION *session);
long flags;
};
struct _LIBSSH2_HOSTKEY_METHOD {
char *name;
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 (*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);
int (*encrypt)(LIBSSH2_SESSION *session, unsigned char **dst, unsigned long *dst_len, const unsigned char *src, unsigned long src_len, void **abstract);
int (*dtor)(LIBSSH2_SESSION *session, void **abstract);
};
/* When FLAG_EVP is set, crypt contains a pointer to an EVP_CIPHER generator and init and dtor are ignored
* Yes, I know it's a hack.
*/
#define LIBSSH2_CRYPT_METHOD_FLAG_EVP 0x0001
struct _LIBSSH2_CRYPT_METHOD {
char *name;
int blocksize;
/* iv and key sizes (-1 for variable length) */
int iv_len;
int secret_len;
long flags;
int (*init)(LIBSSH2_SESSION *session, unsigned char *iv, int *free_iv, unsigned char *secret, int *free_secret, int encrypt, void **abstract);
int (*crypt)(LIBSSH2_SESSION *session, unsigned char *block, void **abstract);
int (*dtor)(LIBSSH2_SESSION *session, void **abstract);
};
struct _LIBSSH2_COMP_METHOD {
char *name;
int (*init)(LIBSSH2_SESSION *session, int compress, void **abstract);
int (*comp)(LIBSSH2_SESSION *session, int compress, unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest,
const unsigned char *src, unsigned long src_len, void **abstract);
int (*dtor)(LIBSSH2_SESSION *session, int compress, void **abstract);
};
struct _LIBSSH2_MAC_METHOD {
char *name;
/* The length of a given MAC packet */
int mac_len;
/* Message Authentication Code Hashing algo */
int (*init)(LIBSSH2_SESSION *session, unsigned char *key, int *free_key, void **abstract);
int (*hash)(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno, const unsigned char *packet, unsigned long packet_len, const unsigned char *addtl, unsigned long addtl_len, void **abstract);
int (*dtor)(LIBSSH2_SESSION *session, void **abstract);
};
#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; \
}
#define LIBSSH2_SOCKET_UNKNOWN 1
#define LIBSSH2_SOCKET_CONNECTED 0
#define LIBSSH2_SOCKET_DISCONNECTED -1
/* Initial packet state, prior to MAC check */
#define LIBSSH2_MAC_UNCONFIRMED 1
/* When MAC type is "none" (proto initiation phase) all packets are deemed "confirmed" */
#define LIBSSH2_MAC_CONFIRMED 0
/* Something very bad is going on */
#define LIBSSH2_MAC_INVALID -1
/* SSH Packet Types -- Defined by internet draft */
/* Transport Layer */
#define SSH_MSG_DISCONNECT 1
#define SSH_MSG_IGNORE 2
#define SSH_MSG_UNIMPLEMENTED 3
#define SSH_MSG_DEBUG 4
#define SSH_MSG_SERVICE_REQUEST 5
#define SSH_MSG_SERVICE_ACCEPT 6
#define SSH_MSG_KEXINIT 20
#define SSH_MSG_NEWKEYS 21
/* diffie-hellman-group1-sha1 */
#define SSH_MSG_KEXDH_INIT 30
#define SSH_MSG_KEXDH_REPLY 31
/* diffie-hellman-group-exchange-sha1 */
#define SSH_MSG_KEX_DH_GEX_REQUEST_OLD 30
#define SSH_MSG_KEX_DH_GEX_REQUEST 34
#define SSH_MSG_KEX_DH_GEX_GROUP 31
#define SSH_MSG_KEX_DH_GEX_INIT 32
#define SSH_MSG_KEX_DH_GEX_REPLY 33
/* User Authentication */
#define SSH_MSG_USERAUTH_REQUEST 50
#define SSH_MSG_USERAUTH_FAILURE 51
#define SSH_MSG_USERAUTH_SUCCESS 52
#define SSH_MSG_USERAUTH_BANNER 53
/* "public key" method */
#define SSH_MSG_USERAUTH_PK_OK 60
/* "password" method */
#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 60
/* Channels */
#define SSH_MSG_GLOBAL_REQUEST 80
#define SSH_MSG_REQUEST_SUCCESS 81
#define SSH_MSG_REQUEST_FAILURE 82
#define SSH_MSG_CHANNEL_OPEN 90
#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION 91
#define SSH_MSG_CHANNEL_OPEN_FAILURE 92
#define SSH_MSG_CHANNEL_WINDOW_ADJUST 93
#define SSH_MSG_CHANNEL_DATA 94
#define SSH_MSG_CHANNEL_EXTENDED_DATA 95
#define SSH_MSG_CHANNEL_EOF 96
#define SSH_MSG_CHANNEL_CLOSE 97
#define SSH_MSG_CHANNEL_REQUEST 98
#define SSH_MSG_CHANNEL_SUCCESS 99
#define SSH_MSG_CHANNEL_FAILURE 100
void libssh2_session_shutdown(LIBSSH2_SESSION *session);
unsigned long libssh2_ntohu32(const unsigned char *buf);
void libssh2_htonu32(unsigned char *buf, unsigned long val);
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_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_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id);
/* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */
LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void);
LIBSSH2_COMP_METHOD **libssh2_comp_methods(void);
LIBSSH2_MAC_METHOD **libssh2_mac_methods(void);
/* Language API doesn't exist yet. Just act like we've agreed on a language */
#define libssh2_kex_agree_lang(session, endpoint, str, str_len) 0
#endif /* LIBSSH2_H */

251
install-sh Executable file
View File

@ -0,0 +1,251 @@
#!/bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch. It can only install one file at a time, a restriction
# shared with many OS's install programs.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
chmodcmd=""
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

40
mkinstalldirs Executable file
View File

@ -0,0 +1,40 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Public domain
# $Id: mkinstalldirs,v 1.1 2004/12/07 21:17:20 sarag Exp $
errstatus=0
for file
do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d
do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp"
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# mkinstalldirs ends here

60
src/Makefile.in Normal file
View File

@ -0,0 +1,60 @@
OBJECTS = channel.o comp.o crypt.o hostkey.o kex.o mac.o misc.o packet.o scp.o session.o userauth.o
top_srcdir = @top_srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
libdir = @exec_prefix@/lib
incldir = @prefix@/include
CC = @CC@
CFLAGS = -c @CFLAGS@ -Wall -g -I../include/ -fPIC
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
INSTALL = @INSTALL@
channel.o: channel.c
$(CC) -o channel.o channel.c $(CFLAGS) $(LIBS)
comp.o: comp.c
$(CC) -o comp.o comp.c $(CFLAGS) $(LIBS)
crypt.o: crypt.c
$(CC) -o crypt.o crypt.c $(CFLAGS) $(LIBS)
hostkey.o: hostkey.c
$(CC) -o hostkey.o hostkey.c $(CFLAGS) $(LIBS)
kex.o: kex.c
$(CC) -o kex.o kex.c $(CFLAGS) $(LIBS)
mac.o: mac.c
$(CC) -o mac.o mac.c $(CFLAGS) $(LIBS)
misc.o: misc.c
$(CC) -o misc.o misc.c $(CFLAGS) $(LIBS)
packet.o: packet.c
$(CC) -o packet.o packet.c $(CFLAGS) $(LIBS)
scp.o: scp.c
$(CC) -o scp.o scp.c $(CFLAGS) $(LIBS)
session.o: session.c
$(CC) -o session.o session.c $(CFLAGS) $(LIBS)
userauth.o: userauth.c
$(CC) -o userauth.o userauth.c $(CFLAGS) $(LIBS)
all: libssh2.@SHLIB_SUFFIX_NAME@
libssh2.@SHLIB_SUFFIX_NAME@: $(OBJECTS)
$(CC) -o libssh2.@SHLIB_SUFFIX_NAME@ $(SHLIB_LDFLAGS) $(OBJECTS) $(LIBS) $(LDFLAGS) @SHLIB_LDFLAGS@
libssh2.a: $(OBJECTS)
rm -f libssh2.a
ar q libssh2.a $(OBJECTS)
@RANLIB@ libssh2.a
install: all
$(INSTALL) libssh2.@SHLIB_SUFFIX_NAME@ $(libdir)
clean:
rm -f *~ libssh2.a libssh2.@SHLIB_SUFFIX_NAME@ *.o

682
src/channel.c Normal file
View File

@ -0,0 +1,682 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <unistd.h>
/* {{{ libssh2_channel_nextid
* Determine the next channel ID we can use at our end
*/
static unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session)
{
unsigned long id = session->next_channel;
LIBSSH2_CHANNEL *channel;
channel = session->channels.head;
while (channel) {
if (channel->local.id > id) {
id = channel->local.id;
}
channel = channel->next;
}
/* This is a shortcut to avoid waiting for close packets on channels we've forgotten about,
* This *could* be a problem if we request and close 4 billion or so channels in too rapid succession
* for the remote end to respond, but the worst case scenario is that some data meant for another channel
* Gets picked up by the new one.... Pretty unlikely all told...
*/
session->next_channel = id + 1;
return id;
}
/* }}} */
/* {{{ libssh2_channel_locate
* Locate a channel pointer by number
*/
LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION *session, unsigned long channel_id)
{
LIBSSH2_CHANNEL *channel = session->channels.head;
while (channel) {
if (channel->local.id == channel_id) {
return channel;
}
channel = channel->next;
}
return NULL;
}
/* }}} */
#define libssh2_channel_add(session, channel) \
{ \
if ((session)->channels.tail) { \
(session)->channels.tail->next = (channel); \
(channel)->prev = (session)->channels.tail; \
} else { \
(session)->channels.head = (channel); \
(channel)->prev = NULL; \
} \
(channel)->next = NULL; \
(session)->channels.tail = (channel); \
(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_CHANNEL *channel;
unsigned long local_channel = libssh2_channel_nextid(session);
unsigned char *s, *packet;
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;
unsigned long data_len;
int polls = 0;
s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate temporary space for packet", 0);
return NULL;
}
*(s++) = SSH_MSG_CHANNEL_OPEN;
libssh2_htonu32(s, channel_type_len); s += 4;
memcpy(s, channel_type, channel_type_len); s += channel_type_len;
libssh2_htonu32(s, local_channel); s += 4;
libssh2_htonu32(s, window_size); s += 4;
libssh2_htonu32(s, packet_size); s += 4;
if (message && message_len) {
memcpy(s, message, message_len); s += message_len;
}
if (libssh2_packet_write(session, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-open request", 0);
LIBSSH2_FREE(session, packet);
return NULL;
}
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);
LIBSSH2_FREE(session, data);
LIBSSH2_FREE(session, packet);
return NULL;
}
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);
LIBSSH2_FREE(session, packet);
return NULL;
}
}
LIBSSH2_FREE(session, packet);
channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL));
if (!channel) {
/* Play nice and close that channel that we're not going to use after all */
data[3] = SSH_MSG_CHANNEL_CLOSE;
libssh2_packet_write(session, data + 3, 5);
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate space for channel data", 0);
LIBSSH2_FREE(session, data);
return NULL;
}
memset(channel, 0, sizeof(LIBSSH2_CHANNEL));
channel->channel_type_len = channel_type_len;
channel->channel_type = LIBSSH2_ALLOC(session, channel_type_len);
if (!channel->channel_type) {
/* Play nice and close that channel that we're not going to use after all */
data[4] = SSH_MSG_CHANNEL_CLOSE;
libssh2_packet_write(session, data + 4, 5);
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating memory for channel type name", 0);
LIBSSH2_FREE(session, channel);
return NULL;
}
memcpy(channel->channel_type, channel_type, channel_type_len);
/* REMEMBER: local as in locally sourced */
channel->local.id = local_channel;
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);
channel->remote.id = libssh2_ntohu32(data + 5);
channel->remote.window_size = window_size;
channel->remote.window_size_initial = window_size;
channel->remote.packet_size = packet_size;
LIBSSH2_FREE(session, data);
libssh2_channel_add(session, channel);
return channel;
}
/* }}} */
/* {{{ libssh2_channel_direct_tcpip_ex
* Tunnel TCP/IP connect through the SSH session to direct host/port
*/
LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_direct_tcpip_ex(LIBSSH2_SESSION *session, char *host, int port, char *shost, int sport)
{
unsigned char *message, *s;
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) */
s = message = LIBSSH2_ALLOC(session, message_len);
if (!message) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for direct-tcpip connection", 0);
return NULL;
}
libssh2_htonu32(s, host_len); s += 4;
memcpy(s, host, host_len); s += host_len;
libssh2_htonu32(s, port); s += 4;
libssh2_htonu32(s, shost_len); s += 4;
memcpy(s, shost, shost_len); s += shost_len;
libssh2_htonu32(s, sport); s += 4;
return libssh2_channel_open_ex(session, "direct-tcpip", sizeof("direct-tcpip") - 1, LIBSSH2_CHANNEL_WINDOW_DEFAULT, LIBSSH2_CHANNEL_PACKET_DEFAULT, message, message_len);
}
/* }}} */
/* {{{ libssh2_channel_setenv_ex
* Set an environment variable prior to requesting a shell/program/subsystem
*/
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" +
want_reply(1) + varname_len(4) + value_len(4) */
s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memeory for setenv packet", 0);
return -1;
}
*(s++) = SSH_MSG_CHANNEL_REQUEST;
libssh2_htonu32(s, channel->remote.id); s += 4;
libssh2_htonu32(s, sizeof("env") - 1); s += 4;
memcpy(s, "env", sizeof("env") - 1); s += sizeof("env") - 1;
*(s++) = 0xFF;
libssh2_htonu32(s, varname_len); s += 4;
memcpy(s, varname, varname_len); s += varname_len;
libssh2_htonu32(s, value_len); s += 4;
memcpy(s, value, value_len); s += value_len;
if (libssh2_packet_write(session, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel-request packet for setenv request", 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);
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;
}
}
/* Never reached, just giving the compiler something to not complain about */
return -1;
}
/* }}} */
/* {{{ libssh2_channel_request_pty_ex
* Duh... Request a PTY
*/
LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel, char *term, int term_len,
char *modes, int modes_len,
int width, int height,
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) +
term_len(4) + width(4) + height(4) + width_px(4) + height_px(4) + modes_len(4) */
s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for pty-request", 0);
return -1;
}
*(s++) = SSH_MSG_CHANNEL_REQUEST;
libssh2_htonu32(s, channel->remote.id); s += 4;
libssh2_htonu32(s, sizeof("pty-req") - 1); s += 4;
memcpy(s, "pty-req", sizeof("pty-req") - 1); s += sizeof("pty-req") - 1;
*(s++) = 0xFF;
libssh2_htonu32(s, term_len); s += 4;
if (term) {
memcpy(s, term, term_len); s += term_len;
}
libssh2_htonu32(s, width); s += 4;
libssh2_htonu32(s, height); s += 4;
libssh2_htonu32(s, width_px); s += 4;
libssh2_htonu32(s, height_px); s += 4;
libssh2_htonu32(s, modes_len); s += 4;
if (modes) {
memcpy(s, modes, modes_len); s += modes_len;
}
if (libssh2_packet_write(session, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send pty-request packet", 0);
LIBSSH2_FREE(session, packet);
return -1;
}
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;
}
}
/* Never reached, just giving the compiler something to not complain about */
return -1;
}
/* }}} */
/* {{{ 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_SESSION *session = channel->session;
unsigned char *s, *packet;
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;
}
s = packet = LIBSSH2_ALLOC(session, packet_len);
if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for channel-process request", 0);
return -1;
}
*(s++) = SSH_MSG_CHANNEL_REQUEST;
libssh2_htonu32(s, channel->remote.id); s += 4;
libssh2_htonu32(s, request_len); s += 4;
memcpy(s, request, request_len); s += request_len;
*(s++) = 0xFF;
if (message) {
libssh2_htonu32(s, message_len); s += 4;
memcpy(s, message, message_len); s += message_len;
}
if (libssh2_packet_write(session, packet, packet_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel request", 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);
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;
}
}
/* Never reached, just giving the compiler something to not complain about */
return -1;
}
/* }}} */
/* {{{ libssh2_channel_set_blocking
* Set a channel's blocking mode on or off, similar to a socket's fcntl(fd, F_SETFL, O_NONBLOCK); type command
*/
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking)
{
channel->blocking = blocking;
}
/* }}} */
/* {{{ libssh2_channel_read_ex
* Read data from a channel
*/
LIBSSH2_API int libssh2_channel_read_ex(LIBSSH2_CHANNEL *channel, int stream_id, char *buf, size_t buflen)
{
LIBSSH2_SESSION *session = channel->session;
int bytes_read = 0, blocking_read = 0;
do {
LIBSSH2_PACKET *packet = session->packets.head;
/* Process any waiting packets */
while (libssh2_packet_read(session, blocking_read) > 0) blocking_read = 0;
while (packet && (bytes_read < buflen)) {
/* In case packet gets destroyed during this iteration */
LIBSSH2_PACKET *next = packet->next;
if ((stream_id && (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1))) ||
(!stream_id && (packet->data[0] == SSH_MSG_CHANNEL_DATA) && (channel->local.id == libssh2_ntohu32(packet->data + 1)))) {
int want = buflen - bytes_read;
int unlink_packet = 0;
if (want >= (packet->data_len - packet->data_head)) {
want = packet->data_len - packet->data_head;
unlink_packet = 1;
}
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 {
session->packets.head = packet->next;
}
if (packet->next) {
packet->next->prev = packet->prev;
} else {
session->packets.tail = packet->prev;
}
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);
}
LIBSSH2_FREE(session, packet);
}
}
packet = next;
}
blocking_read = 1;
} while (channel->blocking && (bytes_read == 0) && !channel->remote.close);
if (channel->blocking && (bytes_read == 0)) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "Remote end has closed this channel", 0);
}
return bytes_read;
}
/* }}} */
/* {{{ libssh2_channel_write_ex
* Send data to a channel
*/
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;
if (channel->local.close) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, "We've already closed this channel", 0);
return -1;
}
if (channel->local.eof) {
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_initial && (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 0;
}
}
packet_len = buflen + (stream_id ? 13 : 9); /* packet_type(1) + channelno(4) [ + streamid(4) ] + buflen(4) */
s = 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;
}
/* Don't exceed the remote end's limits */
/* REMEMBER local means local as the SOURCE of the data */
if (channel->local.window_size_initial && (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;
if (libssh2_packet_write(session, packet, s - packet)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send channel data", 0);
return -1;
}
/* Shrink local window size */
channel->local.window_size -= buflen;
LIBSSH2_FREE(session, packet);
return buflen;
}
/* }}} */
/* {{{ libssh2_channel_send_eof
* Send EOF on channel
*/
LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel)
{
LIBSSH2_SESSION *session = channel->session;
unsigned char packet[5]; /* packet_type(1) + channelno(4) */
packet[0] = SSH_MSG_CHANNEL_EOF;
libssh2_htonu32(packet + 1, channel->remote.id);
if (libssh2_packet_write(session, packet, 5)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send EOF on channel", 0);
return -1;
}
channel->local.eof = 1;
return 0;
}
/* }}} */
/* {{{ libssh2_channel_eof
* Read channel's eof status
*/
LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel)
{
LIBSSH2_SESSION *session = channel->session;
LIBSSH2_PACKET *packet = session->packets.head;
while (packet) {
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;
}
packet = packet->next;
}
return channel->remote.eof;
}
/* }}} */
/* {{{ libssh2_channel_close
* Close a channel
*/
LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel)
{
LIBSSH2_SESSION *session = channel->session;
unsigned char packet[5];
if (channel->local.close) {
/* Already closed, act like we sent another close, even though we didn't... shhhhhh */
return 0;
}
packet[0] = SSH_MSG_CHANNEL_CLOSE;
libssh2_htonu32(packet + 1, channel->remote.id);
if (libssh2_packet_write(session, packet, 5)) {
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 */
return 0;
}
/* }}} */
/* {{{ libssh2_channel_free
* Make sure a channel is closed, then remove the channel from the session and free its resource(s)
*/
LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel)
{
LIBSSH2_SESSION *session = channel->session;
unsigned char channel_id[4], *data;
unsigned long data_len;
/* 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...
* We've sent the close packet, what more do you want?
* Just let packet_add ignore it when it finally arrives
*/
/* Clear out packets meant for this channel */
libssh2_htonu32(channel_id, channel->local.id);
while ((libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0) ||
(libssh2_packet_ask_ex(session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, channel_id, 4, 1) >= 0)) {
LIBSSH2_FREE(session, data);
}
/* Unlink from channel brigade */
if (channel->prev) {
channel->prev->next = channel->next;
} else {
session->channels.head = channel->next;
}
if (channel->next) {
channel->next->prev = channel->prev;
} else {
session->channels.tail = channel->prev;
}
LIBSSH2_FREE(session, channel);
return 0;
}
/* }}} */

249
src/comp.c Normal file
View File

@ -0,0 +1,249 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <zlib.h>
/* ********
* none *
******** */
/* {{{ libssh2_comp_method_none_comp
* Minimalist compression: Absolutely none
*/
static int libssh2_comp_method_none_comp(LIBSSH2_SESSION *session, int compress,
unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest,
const unsigned char *src, unsigned long src_len, void **abstract)
{
*dest = (unsigned char *)src;
*dest_len = src_len;
*free_dest = 0;
return 0;
}
/* }}} */
static LIBSSH2_COMP_METHOD libssh2_comp_method_none = {
"none",
NULL,
libssh2_comp_method_none_comp,
NULL
};
#ifdef LIBSSH2_HAVE_ZLIB
/* ********
* zlib *
******** */
/* {{{ Memory management wrappers
* Yes, I realize we're doing a callback to a callback,
* Deal...
*/
static voidpf libssh2_comp_method_zlib_alloc(voidpf opaque, uInt items, uInt size)
{
LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)opaque;
return (voidpf)LIBSSH2_ALLOC(session, items * size);
}
static void libssh2_comp_method_zlib_free(voidpf opaque, voidpf address)
{
LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)opaque;
LIBSSH2_FREE(session, address);
}
/* }}} */
/* {{{ libssh2_comp_method_zlib_init
* All your bandwidth are belong to us (so save some)
*/
static int libssh2_comp_method_zlib_init(LIBSSH2_SESSION *session, int compress, void **abstract)
{
z_stream *strm;
int status;
strm = LIBSSH2_ALLOC(session, sizeof(z_stream));
if (!strm) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for zlib compression/decompression", 0);
return -1;
}
memset(strm, 0, sizeof(z_stream));
strm->opaque = (voidpf)session;
strm->zalloc = (alloc_func)libssh2_comp_method_zlib_alloc;
strm->zfree = (free_func)libssh2_comp_method_zlib_free;
if (compress) {
/* deflate */
status = deflateInit(strm, Z_DEFAULT_COMPRESSION);
} else {
/* inflate */
status = inflateInit(strm);
}
if (status != Z_OK) {
LIBSSH2_FREE(session, strm);
return -1;
}
*abstract = strm;
return 0;
}
/* }}} */
/* {{{ libssh2_comp_method_zlib_comp
* zlib, a compression standard for all occasions
*/
static int libssh2_comp_method_zlib_comp(LIBSSH2_SESSION *session, int compress,
unsigned char **dest, unsigned long *dest_len, unsigned long payload_limit, int *free_dest,
const unsigned char *src, unsigned long src_len, void **abstract)
{
z_stream *strm = *abstract;
/* A short-term alloc of a full data chunk is better than a series of reallocs */
char *out;
int out_maxlen = compress ? src_len : (2 * src_len);
int limiter = 0;
/* In practice they never come smaller than this */
if (out_maxlen < 21) {
out_maxlen = 21;
}
if (out_maxlen > payload_limit) {
out_maxlen = payload_limit;
}
strm->next_in = (char *)src;
strm->avail_in = src_len;
out = strm->next_out = LIBSSH2_ALLOC(session, out_maxlen);
strm->avail_out = out_maxlen;
if (!strm->next_out) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate compression/decompression buffer", 0);
return -1;
}
while (strm->avail_in) {
int status;
if (compress) {
status = deflate(strm, Z_PARTIAL_FLUSH);
} else {
status = inflate(strm, Z_PARTIAL_FLUSH);
}
if (status != Z_OK) {
libssh2_error(session, LIBSSH2_ERROR_ZLIB, "compress/decompression failure", 0);
LIBSSH2_FREE(session, strm->next_out);
return -1;
}
if (strm->avail_in) {
unsigned long out_ofs = out_maxlen - strm->avail_out;
out_maxlen += compress ? strm->avail_in : (2 * strm->avail_in);
if ((out_maxlen > payload_limit) && !compress && limiter++) {
libssh2_error(session, LIBSSH2_ERROR_ZLIB, "Excessive growth in decompression phase", 0);
LIBSSH2_FREE(session, strm->next_out);
return -1;
}
out = LIBSSH2_REALLOC(session, out, out_maxlen);
if (!out) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to expand compress/decompression buffer", 0);
return -1;
}
strm->next_out = out + out_ofs;
strm->avail_out += compress ? strm->avail_in : (2 * strm->avail_in);
}
}
*dest = out;
*dest_len = out_maxlen - strm->avail_out;
*free_dest = 1;
return 0;
}
/* }}} */
/* {{{ libssh2_comp_method_zlib_dtor
* All done, no more compression for you
*/
static int libssh2_comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compress, void **abstract)
{
z_stream *strm = *abstract;
if (strm) {
if (compress) {
/* deflate */
deflateEnd(strm);
} else {
/* inflate */
inflateEnd(strm);
}
LIBSSH2_FREE(session, strm);
}
*abstract = NULL;
return 0;
}
/* }}} */
static LIBSSH2_COMP_METHOD libssh2_comp_method_zlib = {
"zlib",
libssh2_comp_method_zlib_init,
libssh2_comp_method_zlib_comp,
libssh2_comp_method_zlib_dtor,
};
#endif /* LIBSSH2_HAVE_ZLIB */
/* ***********************
* Compression Methods *
*********************** */
static LIBSSH2_COMP_METHOD *_libssh2_comp_methods[] = {
#ifdef LIBSSH2_HAVE_ZLIB
&libssh2_comp_method_zlib,
#endif /* LIBSSH2_HAVE_ZLIB */
&libssh2_comp_method_none,
NULL
};
LIBSSH2_COMP_METHOD **libssh2_comp_methods(void) {
return _libssh2_comp_methods;
}

189
src/crypt.c Normal file
View File

@ -0,0 +1,189 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <openssl/evp.h>
#ifdef LIBSSH2_CRYPT_NONE
/* {{{ libssh2_crypt_none_crypt
* Minimalist cipher: VERY secure *wink*
*/
static int libssh2_crypt_none_crypt(LIBSSH2_SESSION *session, unsigned char *buf, void **abstract)
{
/* Do nothing to the data! */
return 0;
}
/* }}} */
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_none = {
"none",
8, /* blocksize (SSH2 defines minimum blocksize as 8) */
0, /* iv_len */
0, /* secret_len */
0, /* flags */
NULL,
libssh2_crypt_none_crypt,
NULL
};
#endif
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = {
"3des-cbc",
8, /* blocksize */
8, /* initial value length */
24, /* secret length */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_des_ede3_cbc,
NULL,
};
#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES)
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = {
"aes128-cbc",
16, /* blocksize */
16, /* initial value length */
16, /* secret length -- 16*8 == 128bit */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_aes_128_cbc,
NULL,
};
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = {
"aes192-cbc",
16, /* blocksize */
16, /* initial value length */
24, /* secret length -- 24*8 == 192bit */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_aes_192_cbc,
NULL,
};
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = {
"aes256-cbc",
16, /* blocksize */
16, /* initial value length */
32, /* secret length -- 32*8 == 256bit */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_aes_256_cbc,
NULL,
};
/* rijndael-cbc@lysator.liu.se == aes256-cbc */
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_rijndael_cbc_lysator_liu_se = {
"rijndael-cbc@lysator.liu.se",
16, /* blocksize */
16, /* initial value length */
32, /* secret length -- 32*8 == 256bit */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_aes_256_cbc,
NULL,
};
#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES)*/
#ifndef OPENSSL_NO_BLOWFISH
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = {
"blowfish-cbc",
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_bf_cbc,
NULL,
};
#endif /* ! OPENSSL_NO_BLOWFISH */
#ifndef OPENSSL_NO_CAST
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = {
"cast128-cbc",
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_cast5_cbc,
NULL,
};
#endif /* ! OPENSSL_NO_CAST */
#ifndef OPENSSL_NO_RC4
static LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = {
"arcfour",
8, /* blocksize */
8, /* initial value length */
16, /* secret length */
LIBSSH2_CRYPT_METHOD_FLAG_EVP,
NULL,
(void*)EVP_rc4,
NULL,
};
#endif /* ! OPENSSL_NO_RC4 */
static LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = {
#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES)
&libssh2_crypt_method_aes256_cbc,
&libssh2_crypt_method_rijndael_cbc_lysator_liu_se, /* == aes256-cbc */
&libssh2_crypt_method_aes192_cbc,
&libssh2_crypt_method_aes128_cbc,
#endif /* OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES) */
#ifndef OPENSSL_NO_BLOWFISH
&libssh2_crypt_method_blowfish_cbc,
#endif /* ! OPENSSL_NO_BLOWFISH */
#ifndef OPENSSL_NO_RC4
&libssh2_crypt_method_arcfour,
#endif /* ! OPENSSL_NO_RC4 */
#ifndef OPENSSL_NO_CAST
&libssh2_crypt_method_cast128_cbc,
#endif /* ! OPENSSL_NO_CAST */
#ifndef OPENSSL_NO_DES
&libssh2_crypt_method_3des_cbc,
#endif /* ! OPENSSL_NO_DES */
#ifdef LIBSSH2_CRYPT_NONE
&libssh2_crypt_method_none,
#endif
NULL
};
/* Expose to kex.c */
LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void) {
return _libssh2_crypt_methods;
}

509
src/hostkey.c Normal file
View File

@ -0,0 +1,509 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <openssl/bn.h>
#include <openssl/pem.h>
#ifndef OPENSSL_NO_RSA
/* ***********
* ssh-rsa *
*********** */
static int libssh2_hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION *session, void **abstract);
/* {{{ libssh2_hostkey_method_ssh_rsa_init
* Initialize the server hostkey working area with e/n pair
*/
static int libssh2_hostkey_method_ssh_rsa_init(LIBSSH2_SESSION *session, unsigned char *hostkey_data, unsigned long hostkey_data_len, void **abstract)
{
RSA *rsactx;
unsigned char *s, *e, *n;
unsigned long len, e_len, n_len;
if (*abstract) {
libssh2_hostkey_method_ssh_rsa_dtor(session, abstract);
*abstract = NULL;
}
s = hostkey_data;
len = libssh2_ntohu32(s); s += 4;
if (len != 7 || strncmp(s, "ssh-rsa", 7) != 0) {
return -1;
} s += 7;
e_len = libssh2_ntohu32(s); s += 4;
e = s; s += e_len;
n_len = libssh2_ntohu32(s); s += 4;
n = s; s += n_len;
rsactx = RSA_new();
rsactx->e = BN_new();
BN_bin2bn(e, e_len, rsactx->e);
rsactx->n = BN_new();
BN_bin2bn(n, n_len, rsactx->n);
*abstract = rsactx;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_rsa_passphrase_cb
* TODO: Optionally call a passphrase callback specified by the calling program
*/
static int libssh2_hostkey_method_ssh_rsadsa_passphrase_cb(char *buf, int size, int rwflag, char *passphrase){
int passphrase_len = strlen(passphrase);
if (passphrase_len > (size - 1)) {
passphrase_len = size - 1;
}
memcpy(buf, passphrase, passphrase_len);
buf[passphrase_len] = '\0';
return passphrase_len;
}
/* }}} */
/* {{{ 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)
{
RSA *rsactx;
FILE *fp;
if (*abstract) {
libssh2_hostkey_method_ssh_rsa_dtor(session, abstract);
*abstract = NULL;
}
fp = fopen(privkeyfile, "r");
if (!fp) {
return -1;
}
rsactx = PEM_read_RSAPrivateKey(fp, NULL, (void*)libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, passphrase);
if (!rsactx) {
fclose(fp);
return -1;
}
fclose(fp);
*abstract = rsactx;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_rsa_sign
* Verify signature created by remote
*/
static int libssh2_hostkey_method_ssh_rsa_sig_verify(LIBSSH2_SESSION *session, const unsigned char *sig, unsigned long sig_len,
const unsigned char *m, unsigned long m_len, void **abstract)
{
RSA *rsactx = (RSA*)(*abstract);
unsigned char hash[SHA_DIGEST_LENGTH];
int ret;
/* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */
sig += 15; sig_len -= 15;
SHA1(m, m_len, hash);
ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, (char *)sig, sig_len, rsactx);
return (ret == 1) ? 0 : -1;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_rsa_sign
* Sign data to send to remote
*/
static int libssh2_hostkey_method_ssh_rsa_sign(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len,
const unsigned char *buf, unsigned long buf_len, void **abstract)
{
RSA *rsactx = (RSA*)(*abstract);
int ret;
unsigned char hash[SHA_DIGEST_LENGTH];
SHA_CTX ctx;
char *sig;
int sig_len;
sig_len = RSA_size(rsactx);
sig = LIBSSH2_ALLOC(session, sig_len);
if (!sig) {
return -1;
}
SHA1_Init(&ctx);
SHA1_Update(&ctx, buf, buf_len);
SHA1_Final(hash, &ctx);
ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sig, &sig_len, rsactx);
if (!ret) {
LIBSSH2_FREE(session, sig);
return -1;
}
*signature = sig;
*signature_len = sig_len;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_rsa_signv
* Construct a signature from an array of vectors
*/
static int libssh2_hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len,
unsigned long veccount, const struct iovec datavec[], void **abstract)
{
RSA *rsactx = (RSA*)(*abstract);
int ret, i;
unsigned char hash[SHA_DIGEST_LENGTH];
SHA_CTX ctx;
char *sig;
int sig_len;
sig_len = RSA_size(rsactx);
sig = LIBSSH2_ALLOC(session, sig_len);
if (!sig) {
return -1;
}
SHA1_Init(&ctx);
for(i = 0; i < veccount; i++) {
SHA1_Update(&ctx, datavec[i].iov_base, datavec[i].iov_len);
}
SHA1_Final(hash, &ctx);
ret = RSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sig, &sig_len, rsactx);
if (!ret) {
LIBSSH2_FREE(session, sig);
return -1;
}
*signature = sig;
*signature_len = sig_len;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_rsa_dtor
* Shutdown the hostkey
*/
static int libssh2_hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION *session, void **abstract)
{
RSA *rsactx = (RSA*)(*abstract);
RSA_free(rsactx);
*abstract = NULL;
return 0;
}
/* }}} */
static LIBSSH2_HOSTKEY_METHOD libssh2_hostkey_method_ssh_rsa = {
"ssh-rsa",
MD5_DIGEST_LENGTH,
libssh2_hostkey_method_ssh_rsa_init,
libssh2_hostkey_method_ssh_rsa_initPEM,
libssh2_hostkey_method_ssh_rsa_sig_verify,
libssh2_hostkey_method_ssh_rsa_sign,
libssh2_hostkey_method_ssh_rsa_signv,
NULL, /* encrypt */
libssh2_hostkey_method_ssh_rsa_dtor,
};
#endif /* ! OPENSSL_NO_RSA */
#ifndef OPENSSL_NO_DSA
/* ***********
* ssh-dss *
*********** */
static int libssh2_hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION *session, void **abstract);
/* {{{ libssh2_hostkey_method_ssh_dss_init
* Initialize the server hostkey working area with p/q/g/y set
*/
static int libssh2_hostkey_method_ssh_dss_init(LIBSSH2_SESSION *session, unsigned char *hostkey_data, unsigned long hostkey_data_len, void **abstract)
{
DSA *dsactx;
unsigned char *p, *q, *g, *y, *s;
unsigned long p_len, q_len, g_len, y_len, len;
if (*abstract) {
libssh2_hostkey_method_ssh_dss_dtor(session, abstract);
*abstract = NULL;
}
s = hostkey_data;
len = libssh2_ntohu32(s); s += 4;
if (len != 7 || strncmp(s, "ssh-dss", 7) != 0) {
return -1;
} s += 7;
p_len = libssh2_ntohu32(s); s += 4;
p = s; s += p_len;
q_len = libssh2_ntohu32(s); s += 4;
q = s; s += q_len;
g_len = libssh2_ntohu32(s); s += 4;
g = s; s += g_len;
y_len = libssh2_ntohu32(s); s += 4;
y = s; s += y_len;
dsactx = DSA_new();
dsactx->p = BN_new();
BN_bin2bn(p, p_len, dsactx->p);
dsactx->q = BN_new();
BN_bin2bn(q, q_len, dsactx->q);
dsactx->g = BN_new();
BN_bin2bn(g, g_len, dsactx->g);
dsactx->pub_key = BN_new();
BN_bin2bn(y, y_len, dsactx->pub_key);
*abstract = dsactx;
return 0;
}
/* }}} */
/* {{{ 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)
{
DSA *dsactx;
FILE *fp;
if (*abstract) {
libssh2_hostkey_method_ssh_dss_dtor(session, abstract);
*abstract = NULL;
}
fp = fopen(privkeyfile, "r");
if (!fp) {
return -1;
}
dsactx = PEM_read_DSAPrivateKey(fp, NULL, (void*)libssh2_hostkey_method_ssh_rsadsa_passphrase_cb, passphrase);
if (!dsactx) {
fclose(fp);
return -1;
}
fclose(fp);
*abstract = dsactx;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_dss_sign
* Verify signature created by remote
*/
static int libssh2_hostkey_method_ssh_dss_sig_verify(LIBSSH2_SESSION *session, const unsigned char *sig, unsigned long sig_len,
const unsigned char *m, unsigned long m_len, void **abstract)
{
DSA *dsactx = (DSA*)(*abstract);
unsigned char hash[SHA_DIGEST_LENGTH];
DSA_SIG dsasig;
int ret;
/* Skip past keyname_len(4) + keyname(7){"ssh-dss"} + signature_len(4) */
sig += 15; sig_len -= 15;
if (sig_len != 40) {
libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid DSS signature length", 0);
return -1;
}
dsasig.r = BN_new();
BN_bin2bn(sig, 20, dsasig.r);
dsasig.s = BN_new();
BN_bin2bn(sig + 20, 20, dsasig.s);
SHA1(m, m_len, hash);
ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, &dsasig, dsactx);
return (ret == 1) ? 0 : -1;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_dss_sign
* Sign data to send to remote
*/
static int libssh2_hostkey_method_ssh_dss_sign(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len,
const unsigned char *buf, unsigned long buf_len, void **abstract)
{
DSA *dsactx = (DSA*)(*abstract);
int ret;
unsigned char hash[SHA_DIGEST_LENGTH];
SHA_CTX ctx;
char *sig;
int sig_len;
sig_len = DSA_size(dsactx);
sig = LIBSSH2_ALLOC(session, sig_len);
if (!sig) {
return -1;
}
SHA1_Init(&ctx);
SHA1_Update(&ctx, buf, buf_len);
SHA1_Final(hash, &ctx);
ret = DSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sig, &sig_len, dsactx);
if (!ret) {
LIBSSH2_FREE(session, sig);
return -1;
}
*signature = sig;
*signature_len = sig_len;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_dss_signv
* Construct a signature from an array of vectors
*/
static int libssh2_hostkey_method_ssh_dss_signv(LIBSSH2_SESSION *session, unsigned char **signature, unsigned long *signature_len,
unsigned long veccount, const struct iovec datavec[], void **abstract)
{
DSA *dsactx = (DSA*)(*abstract);
int ret, i;
unsigned char hash[SHA_DIGEST_LENGTH];
SHA_CTX ctx;
char *sig;
int sig_len;
sig_len = DSA_size(dsactx);
sig = LIBSSH2_ALLOC(session, sig_len);
if (!sig) {
return -1;
}
SHA1_Init(&ctx);
for(i = 0; i < veccount; i++) {
SHA1_Update(&ctx, datavec[i].iov_base, datavec[i].iov_len);
}
SHA1_Final(hash, &ctx);
ret = DSA_sign(NID_sha1, hash, SHA_DIGEST_LENGTH, sig, &sig_len, dsactx);
if (!ret) {
LIBSSH2_FREE(session, sig);
return -1;
}
*signature = sig;
*signature_len = sig_len;
return 0;
}
/* }}} */
/* {{{ libssh2_hostkey_method_ssh_dss_dtor
* Shutdown the hostkey method
*/
static int libssh2_hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION *session, void **abstract)
{
DSA *dsactx = (DSA*)(*abstract);
DSA_free(dsactx);
*abstract = NULL;
return 0;
}
/* }}} */
static LIBSSH2_HOSTKEY_METHOD libssh2_hostkey_method_ssh_dss = {
"ssh-dss",
MD5_DIGEST_LENGTH,
libssh2_hostkey_method_ssh_dss_init,
libssh2_hostkey_method_ssh_dss_initPEM,
libssh2_hostkey_method_ssh_dss_sig_verify,
libssh2_hostkey_method_ssh_dss_sign,
libssh2_hostkey_method_ssh_dss_signv,
NULL, /* encrypt */
libssh2_hostkey_method_ssh_dss_dtor,
};
#endif /* ! OPENSSL_NO_DSA */
static LIBSSH2_HOSTKEY_METHOD *_libssh2_hostkey_methods[] = {
#ifndef OPENSSL_NO_RSA
&libssh2_hostkey_method_ssh_rsa,
#endif /* ! OPENSSL_NO_RSA */
#ifndef OPENSSL_NO_DSA
&libssh2_hostkey_method_ssh_dss,
#endif /* ! OPENSSL_NO_DSA */
NULL
};
LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void)
{
return _libssh2_hostkey_methods;
}
/* {{{ libssh2_hostkey_hash
* Returns NULL terminated hash signature
*/
LIBSSH2_API char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type)
{
switch (hash_type) {
#ifndef OPENSSL_NO_MD5
case LIBSSH2_HOSTKEY_HASH_MD5:
return session->server_hostkey_md5;
break;
#endif /* ! OPENSSL_NO_MD5 */
#ifndef OPENSSL_NO_SHA
case LIBSSH2_HOSTKEY_HASH_SHA1:
return session->server_hostkey_sha1;
break;
#endif /* ! OPENSSL_NO_SHA */
default:
return NULL;
}
}
/* }}} */

1056
src/kex.c Normal file

File diff suppressed because it is too large Load Diff

268
src/mac.c Normal file
View File

@ -0,0 +1,268 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <openssl/hmac.h>
#ifdef LIBSSH2_MAC_NONE
/* {{{ libssh2_mac_none_MAC
* Minimalist MAC: No MAC
*/
static int libssh2_mac_none_MAC(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno,
const unsigned char *packet, unsigned long packet_len,
const unsigned char *addtl, unsigned long addtl_len, void **abstract)
{
return 0;
}
/* }}} */
static LIBSSH2_MAC_METHOD libssh2_mac_method_none = {
"none",
0,
NULL,
libssh2_mac_none_MAC,
NULL
};
#endif /* LIBSSH2_MAC_NONE */
/* {{{ libssh2_mac_method_common_init
* Initialize simple mac methods
*/
static int libssh2_mac_method_common_init(LIBSSH2_SESSION *session, unsigned char *key, int *free_key, void **abstract)
{
*abstract = key;
*free_key = 0;
return 0;
}
/* }}} */
/* {{{ libssh2_mac_method_common_dtor
* Cleanup simple mac methods
*/
static int libssh2_mac_method_common_dtor(LIBSSH2_SESSION *session, void **abstract)
{
if (*abstract) {
LIBSSH2_FREE(session, *abstract);
}
*abstract = NULL;
return 0;
}
/* }}} */
/* {{{ libssh2_mac_method_hmac_sha1_hash
* Calculate hash using full sha1 value
*/
static int libssh2_mac_method_hmac_sha1_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno,
const unsigned char *packet, unsigned long packet_len,
const unsigned char *addtl, unsigned long addtl_len, void **abstract)
{
HMAC_CTX ctx;
unsigned char seqno_buf[4];
libssh2_htonu32(seqno_buf, seqno);
HMAC_Init(&ctx, *abstract, session->kex->key_len, EVP_sha1());
HMAC_Update(&ctx, seqno_buf, 4);
HMAC_Update(&ctx, packet, packet_len);
if (addtl && addtl_len) {
HMAC_Update(&ctx, addtl, addtl_len);
}
HMAC_Final(&ctx, buf, NULL);
HMAC_cleanup(&ctx);
return 0;
}
/* }}} */
static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_sha1 = {
"hmac-sha1",
SHA_DIGEST_LENGTH,
libssh2_mac_method_common_init,
libssh2_mac_method_hmac_sha1_hash,
libssh2_mac_method_common_dtor,
};
/* {{{ libssh2_mac_method_hmac_sha1_96_hash
* Calculate hash using first 96 bits of sha1 value
*/
static int libssh2_mac_method_hmac_sha1_96_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno,
const unsigned char *packet, unsigned long packet_len,
const unsigned char *addtl, unsigned long addtl_len, void **abstract)
{
char temp[SHA_DIGEST_LENGTH];
libssh2_mac_method_hmac_sha1_hash(session, temp, seqno, packet, packet_len, addtl, addtl_len, abstract);
memcpy(buf, temp, 96 / 8);
return 0;
}
/* }}} */
static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_sha1_96 = {
"hmac-sha1-96",
96 / 8,
libssh2_mac_method_common_init,
libssh2_mac_method_hmac_sha1_96_hash,
libssh2_mac_method_common_dtor,
};
#ifdef WHY_DOESNT_MD5_WORK
/* {{{ libssh2_mac_method_hmac_md5_hash
* Calculate hash using full md5 value
*/
static int libssh2_mac_method_hmac_md5_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno,
const unsigned char *packet, unsigned long packet_len,
const unsigned char *addtl, unsigned long addtl_len, void **abstract)
{
HMAC_CTX ctx;
unsigned char seqno_buf[4];
libssh2_htonu32(seqno_buf, seqno);
HMAC_Init(&ctx, *abstract, session->kex->key_len, EVP_md5());
HMAC_Update(&ctx, seqno_buf, 4);
HMAC_Update(&ctx, packet, packet_len);
if (addtl && addtl_len) {
HMAC_Update(&ctx, addtl, addtl_len);
}
HMAC_Final(&ctx, buf, NULL);
HMAC_cleanup(&ctx);
return 0;
}
/* }}} */
static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_md5 = {
"hmac-md5",
MD5_DIGEST_LENGTH,
libssh2_mac_method_common_init,
libssh2_mac_method_hmac_md5_hash,
libssh2_mac_method_common_dtor,
};
/* {{{ libssh2_mac_method_hmac_md5_96_hash
* Calculate hash using first 96 bits of md5 value
*/
static int libssh2_mac_method_hmac_md5_96_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned seqno,
const unsigned char *packet, unsigned packet_len,
const unsigned char *addtl, unsigned long addtl_len, void **abstract)
{
char temp[MD5_DIGEST_LENGTH];
libssh2_mac_method_hmac_md5_hash(session, temp, seqno, packet, packet_len, addtl, addtl_len, abstract);
memcpy(buf, temp, 96 / 8);
return 0;
}
/* }}} */
static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_md5_96 = {
"hmac-md5-96",
96 / 8,
libssh2_mac_method_common_init,
libssh2_mac_method_hmac_md5_96_hash,
libssh2_mac_method_common_dtor,
};
#endif /* WHY_DOESNT_MD5_WORK */
#ifndef OPENSSL_NO_RIPEMD
/* {{{ libssh2_mac_method_hmac_ripemd160_hash
* Calculate hash using ripemd160 value
*/
static int libssh2_mac_method_hmac_ripemd160_hash(LIBSSH2_SESSION *session, unsigned char *buf, unsigned long seqno,
const unsigned char *packet, unsigned long packet_len,
const unsigned char *addtl, unsigned long addtl_len, void **abstract)
{
HMAC_CTX ctx;
unsigned char seqno_buf[4];
libssh2_htonu32(seqno_buf, seqno);
HMAC_Init(&ctx, *abstract, session->kex->key_len, EVP_ripemd160());
HMAC_Update(&ctx, seqno_buf, 4);
HMAC_Update(&ctx, packet, packet_len);
if (addtl && addtl_len) {
HMAC_Update(&ctx, addtl, addtl_len);
}
HMAC_Final(&ctx, buf, NULL);
HMAC_cleanup(&ctx);
return 0;
}
/* }}} */
static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_ripemd160 = {
"hmac-ripemd160",
160 / 8,
libssh2_mac_method_common_init,
libssh2_mac_method_hmac_ripemd160_hash,
libssh2_mac_method_common_dtor,
};
static LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_ripemd160_openssh_com = {
"hmac-ripemd160@openssh.com",
160 / 8,
libssh2_mac_method_common_init,
libssh2_mac_method_hmac_ripemd160_hash,
libssh2_mac_method_common_dtor,
};
#endif /* ! OPENSSL_NO_RIPEMD */
static LIBSSH2_MAC_METHOD *_libssh2_mac_methods[] = {
&libssh2_mac_method_hmac_sha1,
&libssh2_mac_method_hmac_sha1_96,
#ifdef WHY_DOESNT_MD5_WORK
&libssh2_mac_method_hmac_md5,
&libssh2_mac_method_hmac_md5_96,
#endif /* WHY_DOESNT_MD5_WORK */
#ifndef OPENSSL_NO_RIPEMD
&libssh2_mac_method_hmac_ripemd160,
&libssh2_mac_method_hmac_ripemd160_openssh_com,
#endif /* ! OPENSSL_NO_RIPEMD */
#ifdef LIBSSH2_MAC_NONE
&libssh2_mac_method_none,
#endif /* LIBSSH2_MAC_NONE */
NULL
};
LIBSSH2_MAC_METHOD **libssh2_mac_methods(void) {
return _libssh2_mac_methods;
}

138
src/misc.c Normal file
View File

@ -0,0 +1,138 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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"
/* {{{ libssh2_ntohu32
*/
unsigned long libssh2_ntohu32(const unsigned char *buf)
{
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
/* }}} */
/* {{{ libssh2_htonu32
*/
void libssh2_htonu32(unsigned char *buf, unsigned long value)
{
buf[0] = (value >> 24) & 0xFF;
buf[1] = (value >> 16) & 0xFF;
buf[2] = (value >> 8) & 0xFF;
buf[3] = value & 0xFF;
}
/* }}} */
/* Base64 Conversion */
/* {{{ */
static const char libssh2_base64_table[] =
{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '\0'
};
static const char libssh2_base64_pad = '=';
static const short libssh2_base64_reverse_table[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
/* }}} */
/* {{{ libssh2_base64_decode
* Decode a base64 chunk and store it into a newly alloc'd buffer
*/
LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **data, int *datalen,
char *src, int src_len)
{
unsigned char *s, *d;
short v;
int i = 0, len = 0;
*data = d = LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1);
if (!d) {
return -1;
}
for(s = src; ((char*)s) < (src + src_len); s++) {
if ((v = libssh2_base64_reverse_table[*s]) < 0) continue;
switch (i % 4) {
case 0:
d[len] = v << 2;
break;
case 1:
d[len++] |= v >> 4;
d[len] = v << 4;
break;
case 2:
d[len++] |= v >> 2;
d[len] = v << 6;
break;
case 3:
d[len++] |= v;
break;
}
i++;
}
if ((i % 4) == 1) {
/* Invalid -- We have a byte which belongs exclusively to a partial octet */
LIBSSH2_FREE(session, *data);
return -1;
}
*datalen = len;
return 0;
}
/* }}} */

665
src/packet.c Normal file
View File

@ -0,0 +1,665 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <openssl/evp.h>
/* {{{ libssh2_packet_new
* Create a new packet and attach it to the brigade
*/
static int libssh2_packet_add(LIBSSH2_SESSION *session, unsigned char *data, size_t datalen, int macstate)
{
LIBSSH2_PACKET *packet;
unsigned long data_head = 0;
if (macstate == LIBSSH2_MAC_INVALID) {
if (session->macerror) {
if (LIBSSH2_MACERROR(session, data, datalen) == 0) {
/* Calling app has given the OK, Process it anyway */
macstate = LIBSSH2_MAC_CONFIRMED;
} else {
libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0);
if (session->ssh_msg_disconnect) {
LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0);
}
return -1;
}
} else {
libssh2_error(session, LIBSSH2_ERROR_INVALID_MAC, "Invalid Message Authentication Code received", 0);
if (session->ssh_msg_disconnect) {
LIBSSH2_DISCONNECT(session, SSH_DISCONNECT_MAC_ERROR, "Invalid MAC received", sizeof("Invalid MAC received") - 1, "", 0);
}
return -1;
}
}
/* A couple exceptions to the packet adding rule: */
switch (data[0]) {
case SSH_MSG_DISCONNECT:
{
char *message, *language;
int reason, message_len, language_len;
reason = libssh2_ntohu32(data + 1);
message_len = libssh2_ntohu32(data + 5);
message = data + 9; /* packet_type(1) + reason(4) + message_len(4) */
language_len = libssh2_ntohu32(data + 9 + message_len);
/* This is where we hack on the data a little,
* Use the MSB of language_len to to a terminating NULL (In all liklihood it is already)
* Shift the language tag back a byte (In all likelihood it's zero length anyway
* Store a NULL in the last byte of the packet to terminate the language string
* With the lengths passed this isn't *REALLY* necessary, but it's "kind"
*/
message[message_len] = '\0';
language = data + 9 + message_len + 3;
if (language_len) {
memcpy(language, language + 1, language_len);
}
language[language_len] = '\0';
if (session->ssh_msg_disconnect) {
LIBSSH2_DISCONNECT(session, reason, message, message_len, language, language_len);
}
LIBSSH2_FREE(session, data);
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
return -1;
}
break;
case SSH_MSG_IGNORE:
/* As with disconnect, back it up one and add a trailing NULL */
memcpy(data + 4, data + 5, datalen - 5);
data[datalen] = '\0';
if (session->ssh_msg_ignore) {
LIBSSH2_IGNORE(session, data + 4, datalen - 5);
}
LIBSSH2_FREE(session, data);
return 0;
break;
case SSH_MSG_DEBUG:
{
int always_display = data[0];
char *message, *language;
int message_len, language_len;
message_len = libssh2_ntohu32(data + 2);
message = data + 6; /* packet_type(1) + display(1) + message_len(4) */
language_len = libssh2_ntohu32(data + 6 + message_len);
/* This is where we hack on the data a little,
* Use the MSB of language_len to to a terminating NULL (In all liklihood it is already)
* Shift the language tag back a byte (In all likelihood it's zero length anyway
* Store a NULL in the last byte of the packet to terminate the language string
* With the lengths passed this isn't *REALLY* necessary, but it's "kind"
*/
message[message_len] = '\0';
language = data + 6 + message_len + 3;
if (language_len) {
memcpy(language, language + 1, language_len);
}
language[language_len] = '\0';
if (session->ssh_msg_debug) {
LIBSSH2_DEBUG(session, always_display, message, message_len, language, language_len);
}
LIBSSH2_FREE(session, data);
return 0;
}
break;
case SSH_MSG_CHANNEL_EXTENDED_DATA:
data_head += 4; /* streamid(4) */
case SSH_MSG_CHANNEL_DATA:
data_head += 9; /* packet_type(1) + channelno(4) + datalen(4) */
{
LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));
if (!channel) {
libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, "Packet received for unknown channel, ignoring", 0);
LIBSSH2_FREE(session, data);
return 0;
}
/* REMEMBER! remote means remote as source of data, NOT remote window! */
if (channel->remote.packet_size < (datalen - data_head)) {
/* Spec says we MAY ignore bytes sent beyond packet_size */
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)) {
/* 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);
return 0;
}
/* Reset EOF status */
channel->remote.eof = 0;
if (channel->remote.window_size_initial && ((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 {
/* Now that we've received it, shrink our window */
channel->remote.window_size -= datalen - data_head;
}
}
break;
case SSH_MSG_CHANNEL_EOF:
{
LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));
if (!channel) {
/* We may have freed already, just quietly ignore this... */
LIBSSH2_FREE(session, data);
return 0;
}
channel->remote.eof = 1;
LIBSSH2_FREE(session, data);
return 0;
}
break;
case SSH_MSG_CHANNEL_CLOSE:
{
LIBSSH2_CHANNEL *channel = libssh2_channel_locate(session, libssh2_ntohu32(data + 1));
if (!channel) {
/* We may have freed already, just quietly ignore this... */
LIBSSH2_FREE(session, data);
return 0;
}
channel->remote.close = 1;
/* TODO: Add a callback for this */
LIBSSH2_FREE(session, data);
return 0;
}
break;
}
packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET));
memset(packet, 0, sizeof(LIBSSH2_PACKET));
packet->data = data;
packet->data_len = datalen;
packet->data_head = data_head;
packet->mac = macstate;
packet->brigade = &session->packets;
packet->next = NULL;
if (session->packets.tail) {
packet->prev = session->packets.tail;
packet->prev->next = packet;
session->packets.tail = packet;
} else {
session->packets.head = packet;
session->packets.tail = packet;
packet->prev = NULL;
}
if (data[0] == SSH_MSG_KEXINIT && !session->exchanging_keys) {
/* Remote wants new keys
* Well, it's already in the brigade,
* let's just call back into ourselves
*/
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 */
}
return 0;
}
/* }}} */
/* {{{ libssh2_blocking_read
* Force a blocking read, regardless of socket settings
*/
static int libssh2_blocking_read(LIBSSH2_SESSION *session, unsigned char *buf, size_t count)
{
size_t bytes_read = 0;
int polls = 0;
while (bytes_read < count) {
int ret;
ret = read(session->socket_fd, buf + bytes_read, count - bytes_read);
if (ret < 0) {
if (errno == EAGAIN) {
if (polls++ > LIBSSH2_SOCKET_POLL_MAXLOOPS) {
return -1;
}
usleep(LIBSSH2_SOCKET_POLL_UDELAY);
continue;
}
if (errno == EINTR) {
continue;
}
if ((errno == EBADF) || (errno == EIO)) {
session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
}
return -1;
}
if (ret == 0) continue;
bytes_read += ret;
}
return bytes_read;
}
/* }}} */
/* {{{ libssh2_packet_read
* Collect a packet into the input brigade
* block only controls whether or not to wait for a packet to start,
* Once a packet starts, libssh2 will block until it is complete
* Returns packet type added to input brigade (0 if nothing added), or -1 on failure
*/
int libssh2_packet_read(LIBSSH2_SESSION *session, int should_block)
{
int packet_type = -1;
if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
return 0;
}
fcntl(session->socket_fd, F_SETFL, O_NONBLOCK);
if (session->newkeys) {
unsigned char *block, *payload, *s, tmp[6];
long read_len;
unsigned long blocksize = session->remote.crypt->blocksize;
unsigned long packet_len, payload_len;
int padding_len;
int macstate;
int free_payload = 1;
/* Safely ignored in CUSTOM cipher mode */
EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->remote.crypt_abstract;
/* Temporary Buffer */
block = LIBSSH2_ALLOC(session, 2 * (blocksize > session->remote.mac->mac_len ? blocksize : session->remote.mac->mac_len));
/* Note: If we add any cipher with a blocksize less than 6 we'll need to get more creative with this
* For now, all blocksize sizes are 8+
*/
if (should_block) {
read_len = libssh2_blocking_read(session, block, blocksize);
} else {
read_len = read(session->socket_fd, block, 1);
if (read_len <= 0) {
LIBSSH2_FREE(session, block);
return 0;
}
read_len += libssh2_blocking_read(session, block + read_len, blocksize - read_len);
}
if (read_len < blocksize) {
LIBSSH2_FREE(session, block);
return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
}
if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
EVP_Cipher(ctx, block + blocksize, block, blocksize);
memcpy(block, block + blocksize, blocksize);
} else {
if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) {
libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0);
LIBSSH2_FREE(session, block);
return -1;
}
}
packet_len = libssh2_ntohu32(block);
padding_len = block[4];
memcpy(tmp, block, 5); /* Use this for MAC later */
payload_len = packet_len - 1; /* padding_len(1) */
s = payload = LIBSSH2_ALLOC(session, payload_len);
memcpy(s, block + 5, blocksize - 5);
s += blocksize - 5;
while ((s - payload) < payload_len) {
read_len = libssh2_blocking_read(session, block, blocksize);
if (read_len < blocksize) {
LIBSSH2_FREE(session, block);
LIBSSH2_FREE(session, payload);
return -1;
}
if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
EVP_Cipher(ctx, block + blocksize, block, blocksize);
memcpy(s, block + blocksize, blocksize);
} else {
if (session->remote.crypt->crypt(session, block, &session->remote.crypt_abstract)) {
libssh2_error(session, LIBSSH2_ERROR_DECRYPT, "Error decrypting packet preamble", 0);
LIBSSH2_FREE(session, block);
LIBSSH2_FREE(session, payload);
return -1;
}
memcpy(s, block, blocksize);
}
s += blocksize;
}
read_len = libssh2_blocking_read(session, block, session->remote.mac->mac_len);
if (read_len < session->remote.mac->mac_len) {
LIBSSH2_FREE(session, block);
LIBSSH2_FREE(session, payload);
return -1;
}
/* Calculate MAC hash */
session->remote.mac->hash(session, block + session->remote.mac->mac_len, session->remote.seqno, tmp, 5, payload, payload_len, &session->remote.mac_abstract);
macstate = (strncmp(block, block + session->remote.mac->mac_len, session->remote.mac->mac_len) == 0) ? LIBSSH2_MAC_CONFIRMED : LIBSSH2_MAC_INVALID;
/* SMG */
if (macstate == LIBSSH2_MAC_INVALID) libssh2_error(session, -255, "EEEK an error!", 0);
session->remote.seqno++;
LIBSSH2_FREE(session, block);
/* Ignore padding */
payload_len -= padding_len;
if (session->remote.comp &&
strcmp(session->remote.comp->name, "none")) {
/* Decompress */
unsigned char *data;
unsigned long data_len;
if (session->remote.comp->comp(session, 0, &data, &data_len, LIBSSH2_PACKET_MAXDECOMP, &free_payload, payload, payload_len, &session->remote.comp_abstract)) {
LIBSSH2_FREE(session, payload);
return -1;
}
if (free_payload) {
LIBSSH2_FREE(session, payload);
payload = data;
payload_len = data_len;
} else {
if (data == payload) {
/* It's not to be freed, because the compression layer reused payload,
* So let's do the same!
*/
payload_len = data_len;
} else {
/* No comp_method actually lets this happen, but let's prepare for the future */
LIBSSH2_FREE(session, payload);
/* We need a freeable struct otherwise the brigade won't know what to do with it */
payload = LIBSSH2_ALLOC(session, data_len);
if (!payload) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for copy of uncompressed data", 0);
LIBSSH2_FREE(session, block);
return -1;
}
memcpy(payload, data, data_len);
payload_len = data_len;
}
}
}
libssh2_packet_add(session, payload, payload_len, macstate);
packet_type = payload[0];
} else { /* No cipher active */
unsigned char *payload;
unsigned char buf[24];
unsigned long buf_len, payload_len;
unsigned long packet_length;
unsigned long padding_length;
if (should_block) {
buf_len = libssh2_blocking_read(session, buf, 5);
} else {
buf_len = read(session->socket_fd, buf, 1);
if (buf_len <= 0) {
return 0;
}
buf_len += libssh2_blocking_read(session, buf, 5 - buf_len);
}
if (buf_len < 5) {
/* Something bad happened */
return -1;
}
packet_length = libssh2_ntohu32(buf);
padding_length = buf[4];
payload_len = packet_length - padding_length - 1; /* padding_length(1) */
payload = LIBSSH2_ALLOC(session, payload_len);
if (libssh2_blocking_read(session, payload, payload_len) < payload_len) {
return (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) ? 0 : -1;
}
while (padding_length) {
/* Flush padding */
padding_length -= libssh2_blocking_read(session, buf, padding_length);
}
/* MACs don't exist in non-encrypted mode */
libssh2_packet_add(session, payload, payload_len, LIBSSH2_MAC_CONFIRMED);
session->remote.seqno++;
packet_type = payload[0];
}
return packet_type;
}
/* }}} */
/* {{{ 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,
unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket)
{
LIBSSH2_PACKET *packet = session->packets.head;
if (poll_socket) {
if (libssh2_packet_read(session, 0) < 0) {
return -1;
}
}
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))) {
*data = packet->data;
*data_len = packet->data_len;
if (packet->prev) {
packet->prev->next = packet->next;
} else {
session->packets.head = packet->next;
}
if (packet->next) {
packet->next->prev = packet->prev;
} else {
session->packets.tail = packet->prev;
}
LIBSSH2_FREE(session, packet);
return 0;
}
packet = packet->next;
}
return -1;
}
/* }}} */
/* {{{ libssh2_packet_require
* Loops libssh2_packet_read() until the packet requested is available
* SSH_DISCONNECT will cause a bailout though
*/
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)
{
if (libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0) == 0) {
/* A packet was available in the packet brigade */
return 0;
}
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
int ret = libssh2_packet_read(session, 1);
if (ret < 0) {
return -1;
}
if (ret == 0) continue;
if (packet_type == ret) {
/* Be lazy, let packet_ask pull it out of the brigade */
return libssh2_packet_ask_ex(session, packet_type, 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
*/
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len)
{
unsigned long packet_length = data_len + 1;
unsigned long block_size = (session->newkeys) ? session->local.crypt->blocksize : 8;
/* At this point packet_length doesn't include the packet_len field itself */
unsigned long padding_length;
int free_data = 0;
unsigned char buf[246]; /* 6 byte header plus max padding size(240) */
int i;
if (session->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;
}
}
fcntl(session->socket_fd, F_SETFL, 0);
packet_length = data_len + 1; /* padding_length(1) -- MAC doesn't count -- Padding to be added soon */
padding_length = block_size - ((packet_length + 4) % block_size);
if (padding_length < 4) {
padding_length += block_size;
}
/* TODO: Maybe add 1 or 2 times block_size to padding_length randomly -- shake things up a bit... */
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';
}
if (session->newkeys) {
/* Encryption is in effect */
unsigned char *encbuf, *s;
int ret;
/* Safely ignored in CUSTOM cipher mode */
EVP_CIPHER_CTX *ctx = (EVP_CIPHER_CTX *)session->local.crypt_abstract;
/* include packet_length(4) itself and room for the hash at the end */
encbuf = LIBSSH2_ALLOC(session, 4 + packet_length + session->local.mac->mac_len);
if (!encbuf) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate encryption buffer", 0);
if (free_data) {
LIBSSH2_FREE(session, data);
}
return -1;
}
/* Copy packet to encoding buffer */
memcpy(encbuf, buf, 5);
memcpy(encbuf + 5, data, data_len);
memcpy(encbuf + 5 + data_len, buf + 5, padding_length);
if (free_data) {
LIBSSH2_FREE(session, data);
}
/* Calculate MAC hash */
session->local.mac->hash(session, encbuf + 4 + packet_length , session->local.seqno, encbuf, 4 + packet_length, NULL, 0, &session->local.mac_abstract);
/* Encrypt data */
for(s = encbuf; (s - encbuf) < (4 + packet_length) ; s += session->local.crypt->blocksize) {
if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
EVP_Cipher(ctx, buf, s, session->local.crypt->blocksize);
memcpy(s, buf, session->local.crypt->blocksize);
} else {
session->local.crypt->crypt(session, s, &session->local.crypt_abstract);
}
}
session->local.seqno++;
/* Send It */
ret = ((4 + packet_length + session->local.mac->mac_len) == write(session->socket_fd, encbuf, 4 + packet_length + session->local.mac->mac_len)) ? 0 : -1;
/* Cleanup environment */
LIBSSH2_FREE(session, encbuf);
return ret;
} else { /* LIBSSH2_ENDPOINT_CRYPT_NONE */
/* Simplified write for non-encrypted mode */
struct iovec data_vector[3];
/* Using vectors means we don't have to alloc a new buffer -- a byte saved is a byte earned
* No MAC during unencrypted phase
*/
data_vector[0].iov_base = buf;
data_vector[0].iov_len = 5;
data_vector[1].iov_base = (char*)data;
data_vector[1].iov_len = data_len;
data_vector[2].iov_base = buf + 5;
data_vector[2].iov_len = padding_length;
session->local.seqno++;
/* Ignore this, it can't actually happen :) */
if (free_data) {
LIBSSH2_FREE(session, data);
}
return ((packet_length + 4) == writev(session->socket_fd, data_vector, 3)) ? 0 : 1;
}
}
/* }}} */

408
src/scp.c Normal file
View File

@ -0,0 +1,408 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define LIBSSH2_SCP_RESPONSE_BUFLEN 256
/* {{{ 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)
{
int path_len = strlen(path);
unsigned char *command, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
unsigned long command_len = path_len + sizeof("scp -f "), response_len;
LIBSSH2_CHANNEL *channel;
long mode = 0, size = 0, mtime = 0, atime = 0;
if (sb) {
command_len++;
}
command = LIBSSH2_ALLOC(session, command_len);
if (!command) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0);
return NULL;
}
if (sb) {
memcpy(command, "scp -pf ", sizeof("scp -pf ") - 1);
memcpy(command + sizeof("scp -pf ") - 1, path, path_len);
} else {
memcpy(command, "scp -f ", sizeof("scp -f ") - 1);
memcpy(command + sizeof("scp -f ") - 1, path, path_len);
}
command[command_len - 1] = '\0';
/* Allocate a channel */
if ((channel = libssh2_channel_open_session(session)) == NULL) {
LIBSSH2_FREE(session, command);
return NULL;
}
/* Request SCP for the desired file */
if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, command_len)) {
LIBSSH2_FREE(session, command);
libssh2_channel_free(channel);
return NULL;
}
LIBSSH2_FREE(session, command);
/* SCP ACK */
response[0] = '\0';
if (libssh2_channel_write(channel, response, 1) != 1) {
libssh2_channel_free(channel);
return NULL;
}
/* Parse SCP response */
response_len = 0;
while (sb && (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN)) {
unsigned char *s, *p;
if (libssh2_channel_read(channel, response + response_len, 1) <= 0) {
/* Timeout, give up */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
libssh2_channel_free(channel);
return NULL;
}
response_len++;
if (response[0] != 'T') {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response, missing Time data", 0);
libssh2_channel_free(channel);
return NULL;
}
if ((response_len > 1) &&
((response[response_len-1] < '0') || (response[response_len-1] > '9')) &&
(response[response_len-1] != ' ') &&
(response[response_len-1] != '\r') &&
(response[response_len-1] != '\n')) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0);
libssh2_channel_free(channel);
return NULL;
}
if ((response_len < 9) || (response[response_len-1] != '\n')) {
if (response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) {
/* You had your chance */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0);
libssh2_channel_free(channel);
return NULL;
}
/* Way too short to be an SCP response, or not done yet, short circuit */
continue;
}
/* We're guaranteed not to go under response_len == 0 by the logic above */
while ((response[response_len-1] == '\r') || (response[response_len-1] == '\n')) response_len--;
response[response_len] = '\0';
if (response_len < 8) {
/* EOL came too soon */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0);
libssh2_channel_free(channel);
return NULL;
}
s = response + 1;
p = strchr(s, ' ');
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime", 0);
libssh2_channel_free(channel);
return NULL;
}
*(p++) = '\0';
/* Make sure we don't get fooled by leftover values */
errno = 0;
mtime = strtol(s, NULL, 10);
if (errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mtime", 0);
libssh2_channel_free(channel);
return NULL;
}
s = strchr(p, ' ');
if (!s || ((s - p) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mtime.usec", 0);
libssh2_channel_free(channel);
return NULL;
}
/* Ignore mtime.usec */
s++;
p = strchr(s, ' ');
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0);
libssh2_channel_free(channel);
return NULL;
}
*(p++) = '\0';
/* Make sure we don't get fooled by leftover values */
errno = 0;
atime = strtol(s, NULL, 10);
if (errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid atime", 0);
libssh2_channel_free(channel);
return NULL;
}
/* SCP ACK */
response[0] = '\0';
if (libssh2_channel_write(channel, response, 1) != 1) {
libssh2_channel_free(channel);
return NULL;
}
/* We *should* check that atime.usec is valid, but why let that stop use? */
break;
}
response_len = 0;
while (response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
char *s, *p, *e = NULL;
if (libssh2_channel_read(channel, response + response_len, 1) <= 0) {
/* Timeout, give up */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Timed out waiting for SCP response", 0);
libssh2_channel_free(channel);
return NULL;
}
response_len++;
if (response[0] != 'C') {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server", 0);
libssh2_channel_free(channel);
return NULL;
}
if ((response_len > 1) &&
(response[response_len-1] != '\r') &&
(response[response_len-1] != '\n') &&
((response[response_len-1] < 32) || (response[response_len-1] > 126))) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid data in SCP response", 0);
libssh2_channel_free(channel);
return NULL;
}
if ((response_len < 7) || (response[response_len-1] != '\n')) {
if (response_len == LIBSSH2_SCP_RESPONSE_BUFLEN) {
/* You had your chance */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Unterminated response from SCP server", 0);
libssh2_channel_free(channel);
return NULL;
}
/* Way too short to be an SCP response, or not done yet, short circuit */
continue;
}
/* We're guaranteed not to go under response_len == 0 by the logic above */
while ((response[response_len-1] == '\r') || (response[response_len-1] == '\n')) response_len--;
response[response_len] = '\0';
if (response_len < 6) {
/* EOL came too soon */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short", 0);
libssh2_channel_free(channel);
return NULL;
}
s = response + 1;
p = strchr(s, ' ');
if (!p || ((p - s) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, malformed mode", 0);
libssh2_channel_free(channel);
return NULL;
}
*(p++) = '\0';
/* Make sure we don't get fooled by leftover values */
errno = 0;
mode = strtol(s, &e, 8);
if ((e && *e) || errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid mode", 0);
libssh2_channel_free(channel);
return NULL;
}
s = strchr(p, ' ');
if (!s || ((s - p) <= 0)) {
/* No spaces or space in the wrong spot */
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, too short or malformed", 0);
libssh2_channel_free(channel);
return NULL;
}
*(s++) = '\0';
/* Make sure we don't get fooled by leftover values */
errno = 0;
size = strtol(p, &e, 10);
if ((e && *e) || errno) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid response from SCP server, invalid size", 0);
libssh2_channel_free(channel);
return NULL;
}
/* SCP ACK */
response[0] = '\0';
if (libssh2_channel_write(channel, response, 1) != 1) {
libssh2_channel_free(channel);
return NULL;
}
/* We *should* check that basename is valid, but why let that stop us? */
break;
}
if (sb) {
memset(sb, 0, sizeof(struct stat));
sb->st_mtime = mtime;
sb->st_atime = atime;
sb->st_size = size;
sb->st_mode = mode;
}
/* Let the data BEGIN! */
return channel;
}
/* }}} */
/* {{{ 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)
{
int path_len = strlen(path);
unsigned char *command, *base, response[LIBSSH2_SCP_RESPONSE_BUFLEN];
unsigned long response_len, command_len = path_len + sizeof("scp -t ");
LIBSSH2_CHANNEL *channel;
if (mtime || atime) {
command_len++;
}
command = LIBSSH2_ALLOC(session, command_len);
if (!command) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate a command buffer for scp session", 0);
return NULL;
}
if (mtime || atime) {
memcpy(command, "scp -pt ", sizeof("scp -pt ") - 1);
memcpy(command + sizeof("scp -pt ") - 1, path, path_len);
} else {
memcpy(command, "scp -t ", sizeof("scp -t ") - 1);
memcpy(command + sizeof("scp -t ") - 1, path, path_len);
}
command[command_len - 1] = '\0';
/* Allocate a channel */
if ((channel = libssh2_channel_open_session(session)) == NULL) {
LIBSSH2_FREE(session, command);
return NULL;
}
/* Request SCP for the desired file */
if (libssh2_channel_process_startup(channel, "exec", sizeof("exec") - 1, command, command_len)) {
LIBSSH2_FREE(session, command);
libssh2_channel_free(channel);
return NULL;
}
LIBSSH2_FREE(session, command);
/* Wait for ACK */
if ((libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
libssh2_channel_free(channel);
return NULL;
}
/* 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);
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);
return NULL;
}
/* Wait for ACK */
if ((libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
libssh2_channel_free(channel);
return NULL;
}
}
/* Send mode, size, and basename */
base = strrchr(path, '/');
if (base) {
base++;
} else {
base = path;
}
response_len = snprintf(response, LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, (unsigned long)size, base);
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);
return NULL;
}
/* Wait for ACK */
if ((libssh2_channel_read(channel, response, 1) <= 0) || (response[0] != 0)) {
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, "Invalid ACK response from remote", 0);
libssh2_channel_free(channel);
return NULL;
}
/* Ready to send file */
return channel;
}
/* }}} */

372
src/session.c Normal file
View File

@ -0,0 +1,372 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <errno.h>
#include <unistd.h>
#include <stdlib.h>
/* {{{ libssh2_default_alloc
*/
static LIBSSH2_ALLOC_FUNC(libssh2_default_alloc)
{
return malloc(count);
}
/* }}} */
/* {{{ libssh2_default_free
*/
static LIBSSH2_FREE_FUNC(libssh2_default_free)
{
free(ptr);
}
/* }}} */
/* {{{ libssh2_default_realloc
*/
static LIBSSH2_REALLOC_FUNC(libssh2_default_realloc)
{
return realloc(ptr, count);
}
/* }}} */
/* {{{ libssh2_banner_receive
* Wait for a hello from the remote host
* Allocate a buffer and store the banner in session->remote.banner
* Returns: 0 on success, 1 on failure
*/
static int libssh2_banner_receive(LIBSSH2_SESSION *session)
{
char banner[256];
int banner_len = 0;
while ((banner_len < sizeof(banner)) &&
((banner_len == 0) || (banner[banner_len-1] != '\n'))) {
char c = '\0';
int ret;
ret = read(session->socket_fd, &c, 1);
if ((ret < 0) && (ret != EAGAIN)) {
/* Some kinda error, but don't break for non-blocking issues */
return 1;
}
if (ret <= 0) continue;
if (c == '\0') {
/* NULLs are not allowed in SSH banners */
return 1;
}
banner[banner_len++] = c;
}
while (banner_len &&
((banner[banner_len-1] == '\n') || (banner[banner_len-1] == '\r'))) {
banner_len--;
}
if (!banner_len) return 1;
session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1);
memcpy(session->remote.banner, banner, banner_len);
session->remote.banner[banner_len] = '\0';
return 0;
}
/* }}} */
/* {{{ libssh2_banner_send
* Send the default banner, or the one set via libssh2_setopt_string
*/
static int libssh2_banner_send(LIBSSH2_SESSION *session)
{
char *banner = LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF;
int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1;
if (session->local.banner) {
/* setopt_string will have given us our \r\n characters */
banner_len = strlen(session->local.banner);
banner = session->local.banner;
}
return (write(session->socket_fd, banner, banner_len) == banner_len) ? 0 : 1;
}
/* }}} */
/* {{{ proto libssh2_session_init
* Allocate and initialize a libssh2 session structure
* Allows for malloc callbacks in case the calling program has its own memory manager
* It's allowable (but unadvisable) to define some but not all of the malloc callbacks
* An additional pointer value may be optionally passed to be sent to the callbacks (so they know who's asking)
*/
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)
{
LIBSSH2_ALLOC_FUNC((*local_alloc)) = libssh2_default_alloc;
LIBSSH2_FREE_FUNC((*local_free)) = libssh2_default_free;
LIBSSH2_REALLOC_FUNC((*local_realloc)) = libssh2_default_realloc;
LIBSSH2_SESSION *session;
if (my_alloc) local_alloc = my_alloc;
if (my_free) local_free = my_free;
if (my_realloc) local_realloc = my_realloc;
session = local_alloc(sizeof(LIBSSH2_SESSION), abstract);
memset(session, 0, sizeof(LIBSSH2_SESSION));
session->alloc = local_alloc;
session->free = local_free;
session->realloc = local_realloc;
session->abstract = abstract;
return session;
}
/* }}} */
/* {{{ proto libssh2_session_startup
* session: LIBSSH2_SESSION struct allocated and owned by the calling program
* Returns: 0 on success, or non-zero on failure
* Any memory allocated by libssh2 will use alloc/realloc/free callbacks in session
* socket *must* be populated with an opened socket
*/
LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int socket)
{
unsigned char *data;
unsigned long data_len;
unsigned char service[sizeof("ssh-userauth") + 5 - 1];
unsigned long service_length;
if (socket <= 0) {
/* Did we forget something? */
libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, "No socket provided", 0);
return LIBSSH2_ERROR_SOCKET_NONE;
}
session->socket_fd = socket;
/* TODO: Liveness check */
if (libssh2_banner_receive(session)) {
/* Unable to receive banner from remote */
libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, "Timeout waiting for banner", 0);
return LIBSSH2_ERROR_BANNER_NONE;
}
if (libssh2_banner_send(session)) {
/* Unable to send banner? */
libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND, "Error sending banner to remote host", 0);
return LIBSSH2_ERROR_BANNER_SEND;
}
if (libssh2_kex_exchange(session, 0)) {
libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, "Unable to exchange encryption keys", 0);
return LIBSSH2_ERROR_KEX_FAILURE;
}
/* Request the userauth service */
service[0] = SSH_MSG_SERVICE_REQUEST;
libssh2_htonu32(service + 1, sizeof("ssh-userauth") - 1);
memcpy(service + 5, "ssh-userauth", sizeof("ssh-userauth") - 1);
if (libssh2_packet_write(session, service, sizeof("ssh-userauth") + 5 - 1)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to ask for ssh-userauth service", 0);
return LIBSSH2_ERROR_SOCKET_SEND;
}
if (libssh2_packet_require(session, SSH_MSG_SERVICE_ACCEPT, &data, &data_len)) {
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
}
service_length = libssh2_ntohu32(data + 1);
if ((service_length != (sizeof("ssh-userauth") - 1)) ||
strncmp("ssh-userauth", data + 5, service_length)) {
LIBSSH2_FREE(session, data);
libssh2_error(session, LIBSSH2_ERROR_PROTO, "Invalid response received from server", 0);
return LIBSSH2_ERROR_PROTO;
}
LIBSSH2_FREE(session, data);
return 0;
}
/* }}} */
/* {{{ proto libssh2_session_free
* Frees the memory allocated to the session
* Also closes and frees any channels attached to this session
*/
LIBSSH2_API void libssh2_session_free(LIBSSH2_SESSION *session)
{
while (session->channels.head) {
LIBSSH2_CHANNEL *tmp = session->channels.head;
libssh2_channel_free(session->channels.head);
if (tmp == session->channels.head) {
/* channel_free couldn't do it's job, perform a messy cleanup */
tmp = session->channels.head;
/* unlink */
session->channels.head = tmp->next;
/* free */
LIBSSH2_FREE(session, tmp);
/* reverse linking isn't important here, we're killing the structure */
}
}
if (session->newkeys) {
/* hostkey */
if (session->hostkey && session->hostkey->dtor) {
session->hostkey->dtor(session, &session->server_hostkey_abstract);
}
/* Client to Server */
/* crypt */
if (session->local.crypt) {
if (session->local.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
if (session->local.crypt_abstract) {
LIBSSH2_FREE(session, session->local.crypt_abstract);
session->local.crypt_abstract = NULL;
}
} else if (session->local.crypt->dtor) {
session->local.crypt->dtor(session, &session->local.crypt_abstract);
}
}
/* comp */
if (session->local.comp && session->local.comp->dtor) {
session->local.comp->dtor(session, 1, &session->local.comp_abstract);
}
/* mac */
if (session->local.mac && session->local.mac->dtor) {
session->local.mac->dtor(session, &session->local.mac_abstract);
}
/* Server to Client */
/* crypt */
if (session->remote.crypt) {
if (session->remote.crypt->flags & LIBSSH2_CRYPT_METHOD_FLAG_EVP) {
if (session->remote.crypt_abstract) {
LIBSSH2_FREE(session, session->remote.crypt_abstract);
session->remote.crypt_abstract = NULL;
}
} else if (session->remote.crypt->dtor) {
session->remote.crypt->dtor(session, &session->remote.crypt_abstract);
}
}
/* comp */
if (session->remote.comp && session->remote.comp->dtor) {
session->remote.comp->dtor(session, 0, &session->remote.comp_abstract);
}
/* mac */
if (session->remote.mac && session->remote.mac->dtor) {
session->remote.mac->dtor(session, &session->remote.mac_abstract);
}
/* session_id */
if (session->session_id) {
LIBSSH2_FREE(session, session->session_id);
}
}
if (session->remote.banner) {
LIBSSH2_FREE(session, session->remote.banner);
}
if (session->local.banner) {
LIBSSH2_FREE(session, session->local.banner);
}
/* Cleanup any remaining packets */
while (session->packets.head) {
LIBSSH2_PACKET *tmp = session->packets.head;
/* unlink */
session->packets.head = tmp->next;
/* free */
LIBSSH2_FREE(session, tmp->data);
LIBSSH2_FREE(session, tmp);
}
if (session->local.banner) {
LIBSSH2_FREE(session, session->local.banner);
}
LIBSSH2_FREE(session, session);
}
/* }}} */
/* {{{ libssh2_session_disconnect_ex
*/
LIBSSH2_API void libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, char *description, char *lang)
{
unsigned char *data;
unsigned long data_len, descr_len = 0, lang_len = 0;
if (description) {
descr_len = strlen(description);
}
if (lang) {
lang_len = strlen(lang);
}
data_len = descr_len + lang_len + 13; /* packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */
data = LIBSSH2_ALLOC(session, data_len);
if (data) {
unsigned char *s = data;
*(s++) = SSH_MSG_DISCONNECT;
libssh2_htonu32(s, reason); s += 4;
libssh2_htonu32(s, descr_len); s += 4;
if (description) {
memcpy(s, description, descr_len);
s += descr_len;
}
libssh2_htonu32(s, lang_len); s += 4;
if (lang) {
memcpy(s, lang, lang_len);
s += lang_len;
}
libssh2_packet_write(session, data, data_len);
LIBSSH2_FREE(session, data);
}
}
/* }}} */

503
src/userauth.c Normal file
View File

@ -0,0 +1,503 @@
/* Copyright (c) 2004, Sara Golemon <sarag@users.sourceforge.net>
* 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 <stdio.h>
/* {{{ proto libssh2_userauth_list
* List authentication methods
* Will yield successful login if "none" happens to be allowable for this user
* 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)
{
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;
unsigned char *data, *s;
s = data = LIBSSH2_ALLOC(session, data_len);
if (!data) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth_list", 0);
return NULL;
}
*(s++) = SSH_MSG_USERAUTH_REQUEST;
libssh2_htonu32(s, username_len); s += 4;
if (username) {
memcpy(s, username, username_len); s += username_len;
}
libssh2_htonu32(s, 14); s += 4;
memcpy(s, "ssh-connection", 14); s += 14;
libssh2_htonu32(s, 4); s += 4;
memcpy(s, "none", 4); s += 4;
if (libssh2_packet_write(session, data, data_len)) {
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, "Unable to send userauth-none request", 0);
LIBSSH2_FREE(session, data);
return NULL;
}
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->authenticated = 1;
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? */
}
methods_len = libssh2_ntohu32(data + 1);
memcpy(data, data + 5, methods_len);
data[methods_len] = '\0';
return data;
}
/* }}} */
/* {{{ libssh2_userauth_authenticated
* 0 if not yet authenticated
* non-zero is already authenticated
*/
LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session)
{
return session->authenticated;
}
/* }}} */
/* {{{ 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_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb)))
{
unsigned char *data, *s;
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) */
s = data = LIBSSH2_ALLOC(session, data_len);
if (!data) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for userauth-password 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 = '\0'; s++;
libssh2_htonu32(s, password_len); s += 4;
memcpy(s, password, password_len); s += password_len;
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);
return -1;
}
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->authenticated = 1;
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? */
}
return 0;
}
/* }}} */
/* {{{ libssh2_file_read_publickey
* Read a public key from an id_???.pub style file
*/
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)
{
FILE *fd;
char *pubkey = NULL, c, *sp1, *sp2, *tmp;
int pubkey_len = 0, tmp_len;
/* Read Public Key */
fd = fopen(pubkeyfile, "r");
if (!fd) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to open public key file", 0);
return -1;
}
while (!feof(fd) && (c = fgetc(fd)) != '\r' && c != '\n') pubkey_len++;
rewind(fd);
if (pubkey_len <= 1) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid data in public key file", 0);
fclose(fd);
return -1;
}
pubkey = LIBSSH2_ALLOC(session, pubkey_len);
if (!pubkey) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Unable to allocate memory for public key data", 0);
fclose(fd);
return -1;
}
if (fread(pubkey, 1, pubkey_len, fd) != pubkey_len) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to read public key from file", 0);
LIBSSH2_FREE(session, pubkey);
fclose(fd);
return -1;
}
fclose(fd);
while (pubkey_len && (pubkey[pubkey_len-1] == '\r' || pubkey[pubkey_len-1] == '\n')) pubkey_len--;
if (!pubkey_len) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Missing public key data", 0);
LIBSSH2_FREE(session, pubkey);
return -1;
}
if ((sp1 = memchr(pubkey, ' ', pubkey_len)) == NULL) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid public key data", 0);
LIBSSH2_FREE(session, pubkey);
return -1;
}
/* Wasting some bytes here (okay, more than some),
* but since it's likely to be freed soon anyway,
* we'll just avoid the extra free/alloc and call it a wash */
*method = pubkey;
*method_len = sp1 - pubkey;
sp1++;
if ((sp2 = memchr(sp1, ' ', pubkey_len - *method_len)) == NULL) {
/* Assume that the id string is missing, but that it's okay */
sp2 = pubkey + pubkey_len;
}
if (libssh2_base64_decode(session, &tmp, &tmp_len, sp1, sp2 - sp1)) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Invalid key data, not base64 encoded", 0);
LIBSSH2_FREE(session, pubkey);
return -1;
}
*pubkeydata = tmp;
*pubkeydata_len = tmp_len;
return 0;
}
/* }}} */
/* {{{ libssh2_file_read_publickey
* 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)
{
LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = session->hostkey_prefs ? session->hostkey_prefs : libssh2_hostkey_methods();
*hostkey_method = NULL;
*hostkey_abstract = NULL;
while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) {
if ((*hostkey_methods_avail)->initPEM &&
strncmp((*hostkey_methods_avail)->name, method, method_len) == 0) {
*hostkey_method = *hostkey_methods_avail;
break;
}
hostkey_methods_avail++;
}
if (!*hostkey_method) {
libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, "No handler for specified private key", 0);
return -1;
}
if ((*hostkey_method)->initPEM(session, privkeyfile, passphrase, hostkey_abstract)) {
libssh2_error(session, LIBSSH2_ERROR_FILE, "Unable to initialize private key from file", 0);
return -1;
}
return 0;
}
/* }}} */
/* {{{ 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_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;
if (libssh2_file_read_publickey(session, &method, &method_len, &pubkeydata, &pubkeydata_len, publickey)) {
return -1;
}
packet_len = username_len + method_len + pubkeydata_len + 45; /* packet_type(1) + username_len(4) + servicename_len(4) +
service_name(14)"ssh-connection" + authmethod_len(4) +
authmethod(9)"publickey" + sig_included(1)'\0' +
algmethod_len(4) + publickey_len(4) */
/* Preallocate space for an overall length, method name again,
* and the signature, which won't be any larger than the size of the publickeydata itself */
s = packet = LIBSSH2_ALLOC(session, packet_len + 4 + (4 + method_len) + (4 + pubkeydata_len));
*(s++) = SSH_MSG_USERAUTH_REQUEST;
libssh2_htonu32(s, username_len); s += 4;
memcpy(s, username, username_len); s += username_len;
libssh2_htonu32(s, 14); s += 4;
memcpy(s, "ssh-connection", 14); s += 14;
libssh2_htonu32(s, 9); s += 4;
memcpy(s, "publickey", 9); s += 9;
b = s;
*(s++) = 0; /* Not sending signature with *this* packet */
libssh2_htonu32(s, method_len); s += 4;
memcpy(s, method, method_len); s += method_len;
libssh2_htonu32(s, pubkeydata_len); s += 4;
memcpy(s, pubkeydata, pubkeydata_len); s += pubkeydata_len;
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);
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, pubkeydata);
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->authenticated = 1;
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? */
}
LIBSSH2_FREE(session, pubkeydata);
if (libssh2_file_read_privatekey(session, &privkeyobj, &abstract, method, method_len, privatekey, passphrase)) {
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, packet);
return -1;
}
*b = 0xFF;
libssh2_htonu32(buf, session->session_id_len);
datavec[0].iov_base = buf;
datavec[0].iov_len = 4;
datavec[1].iov_base = session->session_id;
datavec[1].iov_len = session->session_id_len;
datavec[2].iov_base = packet;
datavec[2].iov_len = packet_len;
if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) {
LIBSSH2_FREE(session, method);
LIBSSH2_FREE(session, packet);
if (privkeyobj->dtor) {
privkeyobj->dtor(session, &abstract);
}
return -1;
}
if (privkeyobj->dtor) {
privkeyobj->dtor(session, &abstract);
}
if (sig_len > pubkeydata_len) {
/* Should *NEVER* happen, but...well.. better safe than sorry */
packet = LIBSSH2_REALLOC(session, packet, packet_len + 4 + (4 + method_len) + (4 + sig_len)); /* PK sigblob */
if (!packet) {
libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Failed allocating additional space for userauth-publickey packet", 0);
LIBSSH2_FREE(session, method);
return -1;
}
}
s = packet + packet_len;
libssh2_htonu32(s, 4 + method_len + 4 + sig_len); s += 4;
libssh2_htonu32(s, method_len); s += 4;
memcpy(s, method, method_len); s += method_len;
LIBSSH2_FREE(session, method);
libssh2_htonu32(s, sig_len); s += 4;
memcpy(s, sig, sig_len); s += sig_len;
LIBSSH2_FREE(session, sig);
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);
return -1;
}
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->authenticated = 1;
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? */
}
return 0;
}
/* }}} */

121
ssh2_sample.c Normal file
View File

@ -0,0 +1,121 @@
#include "libssh2.h"
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
int sock, i, auth_pw = 1;
struct sockaddr_in sin;
char *fingerprint;
LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel;
/* Ultra basic "connect to port 22 on localhost"
* Your code is responsible for creating the socket establishing the connection
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
fcntl(sock, F_SETFL, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = htonl(0x7F000001);
connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in));
/* Create a session instance and start it up
* This will trade welcome banners, exchange keys, and setup crypto, compression, and MAC layers
*/
session = libssh2_session_init();
if (libssh2_session_startup(session, sock)) {
fprintf(stderr, "Failure establishing SSH session\n");
return -1;
}
/* At this point we havn't authenticated,
* The first thing to do is check the hostkey's fingerprint against our known hosts
* Your app may have it hard coded, may go to a file, may present it to the user, that's your call
*/
fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
printf("Fingerprint: ");
for(i = 0; i < 16; i++) {
printf("%02X ", (unsigned char)fingerprint[i]);
}
printf("\n");
if (auth_pw) {
/* We could authenticate via password */
if (libssh2_userauth_password(session, "username", "password")) {
printf("Authentication by password failed.\n");
goto shutdown;
}
} else {
/* Or by public key */
if (libssh2_userauth_publickey_fromfile(session, "username", "/home/username/.ssh/id_rsa.pub", "/home/username/.ssh/id_rsa", "pasphrase")) {
printf("\tAuthentication by public key failed\n");
goto shutdown;
}
}
/* Request a shell */
if (!(channel = libssh2_channel_open_session(session))) {
fprintf(stderr, "Unable to open a session\n");
goto shutdown;
}
/* Some environment variables may be set,
* It's up to the server which ones it'll allow though
*/
libssh2_channel_setenv(channel, "FOO", "bar");
/* Request a terminal with 'vanilla' terminal emulation
* See /etc/termcap for more options
*/
if (libssh2_channel_request_pty(channel, "vanilla")) {
fprintf(stderr, "Failed requesting pty\n");
goto skip_shell;
}
/* Open a SHELL on that pty */
if (libssh2_channel_shell(channel)) {
fprintf(stderr, "Unable to request shell on allocated pty\n");
goto shutdown;
}
/* At this point the shell can be interacted with using
* libssh2_channel_read()
* libssh2_channel_read_stderr()
* libssh2_channel_write()
* libssh2_channel_write_stderr()
*
* Blocking mode may be (en|dis)abled with: libssh2_channel_set_blocking()
* If the server send EOF, libssh2_channel_eof() will return non-0
* To send EOF to the server use: libssh2_channel_send_eof()
* A channel can be closed with: libssh2_channel_close()
* A channel can be freed with: libssh2_channel_free()
*/
skip_shell:
if (channel) {
libssh2_channel_free(channel);
channel = NULL;
}
/* Other channel types are supported via:
* libssh2_scp_send()
* libssh2_scp_recv()
* libssh2_channel_direct_tcpip()
*/
shutdown:
libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
libssh2_session_free(session);
sleep(1);
close(sock);
return 0;
}