Index: config.w32 =================================================================== --- config.w32 (.../tags/RELEASE_0_11_0) +++ config.w32 (.../trunk) @@ -0,0 +1,37 @@ +// $Id$ +// vim:ft=javascript + +ARG_WITH("ssh2", "SSH2 support", "no"); + +if (PHP_SSH2 != "no") { + if ((((PHP_ZLIB=="no") && (CHECK_LIB("zlib_a.lib", "ssh2", PHP_SSH2) || CHECK_LIB("zlib.lib", "ssh2", PHP_SSH2))) || + (PHP_ZLIB_SHARED && CHECK_LIB("zlib.lib", "ssh2", PHP_SSH2)) || (PHP_ZLIB == "yes" && (!PHP_ZLIB_SHARED))) && + CHECK_LIB("libeay32.lib", "ssh2", PHP_SSH2) && + CHECK_LIB("ssleay32.lib", "ssh2", PHP_SSH2) && + CHECK_LIB("ws2_32.lib", "ssh2", PHP_SSH2)) { + + // Use bundled lib if none installed, even if it is outdated. + if (!(CHECK_LIB("libssh2_a.lib;libssh2.lib", "ssh2", PHP_SSH2) && + CHECK_HEADER_ADD_INCLUDE("libssh2.h", "CFLAGS_SSH2", PHP_PHP_BUILD + "\\include\\libssh2"))) { + FSO.CopyFile(configure_module_dirname + "\\libssh2\\src\\libssh2_config.h.in.w32", + configure_module_dirname + "\\libssh2\\src\\libssh2_config.h"); + + ADD_FLAG('CFLAGS_SSH2', '/DLIBSSH2_WIN32=1 /DLIBSSH2_API= /I ' + + configure_module_dirname + '/libssh2/include'); + } + + AC_DEFINE('HAVE_SSH2LIB', 1); + AC_DEFINE('PHP_SSH2_REMOTE_FORWARDING', 1); + AC_DEFINE('PHP_SSH2_HOSTBASED_AUTH', 1); + AC_DEFINE('PHP_SSH2_POLL', 1); + AC_DEFINE('PHP_SSH2_PUBLICKEY_SUBSYSTEM', 1); + + EXTENSION("ssh2", "ssh2.c ssh2_fopen_wrappers.c ssh2_sftp.c"); + + ADD_SOURCES(configure_module_dirname + "/libssh2/src", "channel.c comp.c crypt.c \ + hostkey.c kex.c mac.c misc.c openssl.c packet.c pem.c publickey.c scp.c \ + session.c sftp.c transport.c userauth.c", "ssh2"); + } else { + WARNING("ssh2 not enabled: libraries or headers not found"); + } +} Property changes on: config.w32 ___________________________________________________________________ Added: svn:eol-style + native Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.11 Index: ssh2.c =================================================================== --- ssh2.c (.../tags/RELEASE_0_11_0) +++ ssh2.c (.../trunk) @@ -47,14 +47,9 @@ int le_ssh2_pkey_subsys; #endif -#ifdef ZEND_ENGINE_2 -static - ZEND_BEGIN_ARG_INFO(php_ssh2_first_arg_force_ref, 0) - ZEND_ARG_PASS_INFO(1) - ZEND_END_ARG_INFO() -#else -static unsigned char php_ssh2_first_arg_force_ref[] = { 1, BYREF_FORCE }; -#endif +ZEND_BEGIN_ARG_INFO(php_ssh2_first_arg_force_ref, 0) + ZEND_ARG_PASS_INFO(1) +ZEND_END_ARG_INFO() /* ************* * Callbacks * @@ -248,7 +243,7 @@ /* {{{ php_ssh2_set_callback * Try to set a method if it's passed in with the hash table */ -static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data) +static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data TSRMLS_DC) { zval **handler, *copyval; void *internal_handler; @@ -257,7 +252,7 @@ return 0; } - if (!handler || !*handler || !zend_is_callable(*handler, 0, NULL)) { + if (!handler || !*handler || !zend_is_callable(*handler, 0, NULL ZEND_IS_CALLABLE_TSRMLS_CC)) { return -1; } @@ -358,7 +353,7 @@ if (!session) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to initialize SSH2 session"); efree(data); - close(socket); + closesocket(socket); return NULL; } libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER " PHP"); @@ -411,19 +406,19 @@ if (callbacks) { /* ignore debug disconnect macerror */ - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data)) { + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting IGNORE callback"); } - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data)) { + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DEBUG callback"); } - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data)) { + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting MACERROR callback"); } - if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data)) { + if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed setting DISCONNECT callback"); } } @@ -434,7 +429,7 @@ last_error = libssh2_session_last_error(session, &error_msg, NULL, 0); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error starting up SSH connection(%d): %s", last_error, error_msg); - close(socket); + closesocket(socket); libssh2_session_free(session); efree(data); return NULL; @@ -1148,7 +1143,7 @@ zval_ptr_dtor(&(*data)->disconnect_cb); } - close((*data)->socket); + closesocket((*data)->socket); efree(*data); *data = NULL; @@ -1274,7 +1269,7 @@ /* {{{ ssh2_functions[] */ -function_entry ssh2_functions[] = { +zend_function_entry ssh2_functions[] = { PHP_FE(ssh2_connect, NULL) PHP_FE(ssh2_methods_negotiated, NULL) PHP_FE(ssh2_fingerprint, NULL) Property changes on: ssh2.c ___________________________________________________________________ Modified: cvs2svn:cvs-rev - 1.22 + 1.25 Index: libssh2/include/libssh2_publickey.h =================================================================== --- libssh2/include/libssh2_publickey.h (.../tags/RELEASE_0_11_0) +++ libssh2/include/libssh2_publickey.h (.../trunk) @@ -0,0 +1,101 @@ +/* Copyright (c) 2004-2006, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* Note: This include file is only needed for using the + * publickey SUBSYSTEM which is not the same as publickey + * authentication. For authentication you only need libssh2.h + * + * For more information on the publickey subsystem, + * refer to IETF draft: secsh-publickey + */ + +#ifndef LIBSSH2_PUBLICKEY_H +#define LIBSSH2_PUBLICKEY_H 1 + +typedef struct _LIBSSH2_PUBLICKEY LIBSSH2_PUBLICKEY; + +typedef struct _libssh2_publickey_attribute { + const char *name; + unsigned long name_len; + const char *value; + unsigned long value_len; + char mandatory; +} libssh2_publickey_attribute; + +typedef struct _libssh2_publickey_list { + unsigned char *packet; /* For freeing */ + + const unsigned char *name; + unsigned long name_len; + const unsigned char *blob; + unsigned long blob_len; + unsigned long num_attrs; + libssh2_publickey_attribute *attrs; /* free me */ +} libssh2_publickey_list; + +/* Generally use the first macro here, but if both name and value are string literals, you can use _fast() to take advantage of preprocessing */ +#define libssh2_publickey_attribute(name, value, mandatory) { (name), strlen(name), (value), strlen(value), (mandatory) }, +#define libssh2_publickey_attribute_fast(name, value, mandatory) { (name), sizeof(name) - 1, (value), sizeof(value) - 1, (mandatory) }, + +#ifdef __cplusplus +extern "C" { +#endif + +/* Publickey Subsystem */ +LIBSSH2_API LIBSSH2_PUBLICKEY *libssh2_publickey_init(LIBSSH2_SESSION *session); + +LIBSSH2_API int libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len, char overwrite, + unsigned long num_attrs, const libssh2_publickey_attribute attrs[]); +#define libssh2_publickey_add(pkey, name, blob, blob_len, overwrite, num_attrs, attrs) \ + libssh2_publickey_add_ex((pkey), (name), strlen(name), (blob), (blob_len), (overwrite), (num_attrs), (attrs)) + +LIBSSH2_API int libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY *pkey, const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len); +#define libssh2_publickey_remove(pkey, name, blob, blob_len) \ + libssh2_publickey_remove_ex((pkey), (name), strlen(name), (blob), (blob_len)) + +LIBSSH2_API int libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY *pkey, unsigned long *num_keys, libssh2_publickey_list **pkey_list); +LIBSSH2_API void libssh2_publickey_list_free(LIBSSH2_PUBLICKEY *pkey, libssh2_publickey_list *pkey_list); + +LIBSSH2_API int libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY *pkey); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ndef: LIBSSH2_PUBLICKEY_H */ Property changes on: libssh2/include/libssh2_publickey.h ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/include/libssh2_sftp.h =================================================================== --- libssh2/include/libssh2_sftp.h (.../tags/RELEASE_0_11_0) +++ libssh2/include/libssh2_sftp.h (.../trunk) @@ -0,0 +1,251 @@ +/* Copyright (c) 2004-2008, Sara Golemon + * 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_SFTP_H +#define LIBSSH2_SFTP_H 1 + +#ifndef WIN32 +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Note: Version 6 was documented at the time of writing + * However it was marked as "DO NOT IMPLEMENT" due to pending changes + * + * Let's start with Version 3 (The version found in OpenSSH) and go from there + */ +#define LIBSSH2_SFTP_VERSION 3 +#define LIBSSH2_SFTP_PACKET_MAXLEN 40000 + +typedef struct _LIBSSH2_SFTP LIBSSH2_SFTP; +typedef struct _LIBSSH2_SFTP_HANDLE LIBSSH2_SFTP_HANDLE; +typedef struct _LIBSSH2_SFTP_ATTRIBUTES LIBSSH2_SFTP_ATTRIBUTES; + +/* Flags for open_ex() */ +#define LIBSSH2_SFTP_OPENFILE 0 +#define LIBSSH2_SFTP_OPENDIR 1 + +/* Flags for rename_ex() */ +#define LIBSSH2_SFTP_RENAME_OVERWRITE 0x00000001 +#define LIBSSH2_SFTP_RENAME_ATOMIC 0x00000002 +#define LIBSSH2_SFTP_RENAME_NATIVE 0x00000004 + +/* Flags for stat_ex() */ +#define LIBSSH2_SFTP_STAT 0 +#define LIBSSH2_SFTP_LSTAT 1 +#define LIBSSH2_SFTP_SETSTAT 2 + +/* Flags for symlink_ex() */ +#define LIBSSH2_SFTP_SYMLINK 0 +#define LIBSSH2_SFTP_READLINK 1 +#define LIBSSH2_SFTP_REALPATH 2 + +/* SFTP attribute flag bits */ +#define LIBSSH2_SFTP_ATTR_SIZE 0x00000001 +#define LIBSSH2_SFTP_ATTR_UIDGID 0x00000002 +#define LIBSSH2_SFTP_ATTR_PERMISSIONS 0x00000004 +#define LIBSSH2_SFTP_ATTR_ACMODTIME 0x00000008 +#define LIBSSH2_SFTP_ATTR_EXTENDED 0x80000000 + +struct _LIBSSH2_SFTP_ATTRIBUTES { + /* If flags & ATTR_* bit is set, then the value in this struct will be meaningful + * Otherwise it should be ignored + */ + unsigned long flags; + + libssh2_uint64_t filesize; + unsigned long uid, gid; + unsigned long permissions; + unsigned long atime, mtime; +}; + +/* SFTP filetypes */ +#define LIBSSH2_SFTP_TYPE_REGULAR 1 +#define LIBSSH2_SFTP_TYPE_DIRECTORY 2 +#define LIBSSH2_SFTP_TYPE_SYMLINK 3 +#define LIBSSH2_SFTP_TYPE_SPECIAL 4 +#define LIBSSH2_SFTP_TYPE_UNKNOWN 5 +#define LIBSSH2_SFTP_TYPE_SOCKET 6 +#define LIBSSH2_SFTP_TYPE_CHAR_DEVICE 7 +#define LIBSSH2_SFTP_TYPE_BLOCK_DEVICE 8 +#define LIBSSH2_SFTP_TYPE_FIFO 9 + +/* + * Reproduce the POSIX file modes here for systems that are not + * POSIX compliant. + * + * These is used in "permissions" of "struct _LIBSSH2_SFTP_ATTRIBUTES" + */ +/* File type */ +#define LIBSSH2_SFTP_S_IFMT 0170000 /* type of file mask */ +#define LIBSSH2_SFTP_S_IFIFO 0010000 /* named pipe (fifo) */ +#define LIBSSH2_SFTP_S_IFCHR 0020000 /* character special */ +#define LIBSSH2_SFTP_S_IFDIR 0040000 /* directory */ +#define LIBSSH2_SFTP_S_IFBLK 0060000 /* block special */ +#define LIBSSH2_SFTP_S_IFREG 0100000 /* regular */ +#define LIBSSH2_SFTP_S_IFLNK 0120000 /* symbolic link */ +#define LIBSSH2_SFTP_S_IFSOCK 0140000 /* socket */ + +/* File mode */ +/* Read, write, execute/search by owner */ +#define LIBSSH2_SFTP_S_IRWXU 0000700 /* RWX mask for owner */ +#define LIBSSH2_SFTP_S_IRUSR 0000400 /* R for owner */ +#define LIBSSH2_SFTP_S_IWUSR 0000200 /* W for owner */ +#define LIBSSH2_SFTP_S_IXUSR 0000100 /* X for owner */ +/* Read, write, execute/search by group */ +#define LIBSSH2_SFTP_S_IRWXG 0000070 /* RWX mask for group */ +#define LIBSSH2_SFTP_S_IRGRP 0000040 /* R for group */ +#define LIBSSH2_SFTP_S_IWGRP 0000020 /* W for group */ +#define LIBSSH2_SFTP_S_IXGRP 0000010 /* X for group */ +/* Read, write, execute/search by others */ +#define LIBSSH2_SFTP_S_IRWXO 0000007 /* RWX mask for other */ +#define LIBSSH2_SFTP_S_IROTH 0000004 /* R for other */ +#define LIBSSH2_SFTP_S_IWOTH 0000002 /* W for other */ +#define LIBSSH2_SFTP_S_IXOTH 0000001 /* X for other */ + +/* SFTP File Transfer Flags -- (e.g. flags parameter to sftp_open()) + * Danger will robinson... APPEND doesn't have any effect on OpenSSH servers */ +#define LIBSSH2_FXF_READ 0x00000001 +#define LIBSSH2_FXF_WRITE 0x00000002 +#define LIBSSH2_FXF_APPEND 0x00000004 +#define LIBSSH2_FXF_CREAT 0x00000008 +#define LIBSSH2_FXF_TRUNC 0x00000010 +#define LIBSSH2_FXF_EXCL 0x00000020 + +/* SFTP Status Codes (returned by libssh2_sftp_last_error() ) */ +#define LIBSSH2_FX_OK 0 +#define LIBSSH2_FX_EOF 1 +#define LIBSSH2_FX_NO_SUCH_FILE 2 +#define LIBSSH2_FX_PERMISSION_DENIED 3 +#define LIBSSH2_FX_FAILURE 4 +#define LIBSSH2_FX_BAD_MESSAGE 5 +#define LIBSSH2_FX_NO_CONNECTION 6 +#define LIBSSH2_FX_CONNECTION_LOST 7 +#define LIBSSH2_FX_OP_UNSUPPORTED 8 +#define LIBSSH2_FX_INVALID_HANDLE 9 +#define LIBSSH2_FX_NO_SUCH_PATH 10 +#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11 +#define LIBSSH2_FX_WRITE_PROTECT 12 +#define LIBSSH2_FX_NO_MEDIA 13 +#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14 +#define LIBSSH2_FX_QUOTA_EXCEEDED 15 +#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 /* Initial mis-spelling */ +#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16 +#define LIBSSH2_FX_LOCK_CONFlICT 17 /* Initial mis-spelling */ +#define LIBSSH2_FX_LOCK_CONFLICT 17 +#define LIBSSH2_FX_DIR_NOT_EMPTY 18 +#define LIBSSH2_FX_NOT_A_DIRECTORY 19 +#define LIBSSH2_FX_INVALID_FILENAME 20 +#define LIBSSH2_FX_LINK_LOOP 21 + +/* Returned by any function that would block during a read/write opperation */ +#define LIBSSH2SFTP_EAGAIN LIBSSH2_ERROR_EAGAIN + +/* SFTP API */ +LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session); +LIBSSH2_API int libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp); +LIBSSH2_API unsigned long libssh2_sftp_last_error(LIBSSH2_SFTP *sftp); + +/* File / Directory Ops */ +LIBSSH2_API LIBSSH2_SFTP_HANDLE *libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename, unsigned int filename_len, + unsigned long flags, long mode, int open_type); +#define libssh2_sftp_open(sftp, filename, flags, mode) \ + libssh2_sftp_open_ex((sftp), (filename), strlen(filename), (flags), (mode), LIBSSH2_SFTP_OPENFILE) +#define libssh2_sftp_opendir(sftp, path) \ + libssh2_sftp_open_ex((sftp), (path), strlen(path), 0, 0, LIBSSH2_SFTP_OPENDIR) + +LIBSSH2_API ssize_t libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen); + +LIBSSH2_API int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, + char *longentry, size_t longentry_maxlen, + LIBSSH2_SFTP_ATTRIBUTES *attrs); +#define libssh2_sftp_readdir(handle, buffer, buffer_maxlen, attrs) \ + libssh2_sftp_readdir_ex((handle), (buffer), (buffer_maxlen), NULL, 0, (attrs)) + +LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count); + +LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle); +#define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle) +#define libssh2_sftp_closedir(handle) libssh2_sftp_close_handle(handle) + +LIBSSH2_API void libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset); +LIBSSH2_API void libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset); +#define libssh2_sftp_rewind(handle) libssh2_sftp_seek64((handle), 0) + +LIBSSH2_API size_t libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle); +LIBSSH2_API libssh2_uint64_t libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle); + +LIBSSH2_API int libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat); +#define libssh2_sftp_fstat(handle, attrs) libssh2_sftp_fstat_ex((handle), (attrs), 0) +#define libssh2_sftp_fsetstat(handle, attrs) libssh2_sftp_fstat_ex((handle), (attrs), 1) + + + +/* Miscellaneous Ops */ +LIBSSH2_API int libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename, unsigned int srouce_filename_len, + const char *dest_filename, unsigned int dest_filename_len, + long flags); +#define libssh2_sftp_rename(sftp, sourcefile, destfile) libssh2_sftp_rename_ex((sftp), (sourcefile), strlen(sourcefile), (destfile), strlen(destfile), \ + LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE) + +LIBSSH2_API int libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename, unsigned int filename_len); +#define libssh2_sftp_unlink(sftp, filename) libssh2_sftp_unlink_ex((sftp), (filename), strlen(filename)) + +LIBSSH2_API int libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, long mode); +#define libssh2_sftp_mkdir(sftp, path, mode) libssh2_sftp_mkdir_ex((sftp), (path), strlen(path), (mode)) + +LIBSSH2_API int libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len); +#define libssh2_sftp_rmdir(sftp, path) libssh2_sftp_rmdir_ex((sftp), (path), strlen(path)) + +LIBSSH2_API int libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES *attrs); +#define libssh2_sftp_stat(sftp, path, attrs) libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_STAT, (attrs)) +#define libssh2_sftp_lstat(sftp, path, attrs) libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_LSTAT, (attrs)) +#define libssh2_sftp_setstat(sftp, path, attrs) libssh2_sftp_stat_ex((sftp), (path), strlen(path), LIBSSH2_SFTP_SETSTAT, (attrs)) + +LIBSSH2_API int libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, char *target, unsigned int target_len, int link_type); +#define libssh2_sftp_symlink(sftp, orig, linkpath) libssh2_sftp_symlink_ex((sftp), (orig), strlen(orig), (linkpath), strlen(linkpath), LIBSSH2_SFTP_SYMLINK) +#define libssh2_sftp_readlink(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_READLINK) +#define libssh2_sftp_realpath(sftp, path, target, maxlen) libssh2_sftp_symlink_ex((sftp), (path), strlen(path), (target), (maxlen), LIBSSH2_SFTP_REALPATH) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBSSH2_SFTP_H */ Property changes on: libssh2/include/libssh2_sftp.h ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/include/libssh2.h =================================================================== --- libssh2/include/libssh2.h (.../tags/RELEASE_0_11_0) +++ libssh2/include/libssh2.h (.../trunk) @@ -0,0 +1,489 @@ +/* Copyright (c) 2004-2008, Sara Golemon + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +/* Allow alternate API prefix from CFLAGS or calling app */ +#ifndef LIBSSH2_API +# ifdef LIBSSH2_WIN32 +# ifdef LIBSSH2_LIBRARY +# define LIBSSH2_API __declspec(dllexport) +# else +# define LIBSSH2_API __declspec(dllimport) +# endif /* LIBSSH2_LIBRARY */ +# else /* !LIBSSH2_WIN32 */ +# define LIBSSH2_API +# endif /* LIBSSH2_WIN32 */ +#endif /* LIBSSH2_API */ + +#if defined(LIBSSH2_DARWIN) || (defined(LIBSSH2_WIN32) && !defined(_MSC_VER) && !defined(__MINGW32__)) +# include +#endif + +#if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) +# include +typedef unsigned int uint32_t; +#endif + +#if defined(LIBSSH2_WIN32) && defined(_MSC_VER) && (_MSC_VER <= 1400) +typedef unsigned __int64 libssh2_uint64_t; +typedef __int64 libssh2_int64_t; +typedef unsigned int uint32_t; +#ifndef _SSIZE_T_DEFINED +typedef int ssize_t; +#define _SSIZE_T_DEFINED +#endif +#else +typedef unsigned long long libssh2_uint64_t; +typedef long long libssh2_int64_t; +#endif + +/* We use underscore instead of dash when appending CVS in dev versions just + to make the BANNER define (used by src/session.c) be a valid SSH + banner. Release versions have no appended strings and may of coruse not + have dashes either. */ +#define LIBSSH2_VERSION "1.0" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBSSH2_VERSION_MAJOR 1 +#define LIBSSH2_VERSION_MINOR 0 +#define LIBSSH2_VERSION_PATCH + +/* This is the numeric version of the libssh2 version number, meant for easier + parsing and comparions by programs. The LIBSSH2_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. +*/ +#define LIBSSH2_VERSION_NUM 0x010000 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in CVS, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date should follow this template: + * + * "Mon Feb 12 11:35:33 UTC 2007" + */ +#define LIBSSH2_TIMESTAMP "Fri Dec 26 21:35:07 UTC 2008" + +/* 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" + +/* 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 + +/* Maximum size for an inbound compressed payload, plays it safe by overshooting spec limits */ +#define LIBSSH2_PACKET_MAXPAYLOAD 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) + +typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT +{ + char* text; + unsigned int length; + unsigned char echo; +} LIBSSH2_USERAUTH_KBDINT_PROMPT; + +typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE +{ + char* text; + unsigned int length; +} LIBSSH2_USERAUTH_KBDINT_RESPONSE; + +/* 'keyboard-interactive' authentication callback */ +#define LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC(name_) void name_(const char* name, int name_len, const char* instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract) + +/* Callbacks for special SSH packets */ +#define LIBSSH2_IGNORE_FUNC(name) void name(LIBSSH2_SESSION *session, const char *message, int message_len, void **abstract) +#define LIBSSH2_DEBUG_FUNC(name) void name(LIBSSH2_SESSION *session, int always_display, const char *message, int message_len, const char *language, int language_len,void **abstract) +#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) +#define LIBSSH2_X11_OPEN_FUNC(name) void name(LIBSSH2_SESSION *session, LIBSSH2_CHANNEL *channel, const char *shost, int sport, void **abstract) + +#define LIBSSH2_CHANNEL_CLOSE_FUNC(name) void name(LIBSSH2_SESSION *session, void **session_abstract, LIBSSH2_CHANNEL *channel, void **channel_abstract) + +/* libssh2_session_callback_set() constants */ +#define LIBSSH2_CALLBACK_IGNORE 0 +#define LIBSSH2_CALLBACK_DEBUG 1 +#define LIBSSH2_CALLBACK_DISCONNECT 2 +#define LIBSSH2_CALLBACK_MACERROR 3 +#define LIBSSH2_CALLBACK_X11 4 + +/* libssh2_session_method_pref() constants */ +#define LIBSSH2_METHOD_KEX 0 +#define LIBSSH2_METHOD_HOSTKEY 1 +#define LIBSSH2_METHOD_CRYPT_CS 2 +#define LIBSSH2_METHOD_CRYPT_SC 3 +#define LIBSSH2_METHOD_MAC_CS 4 +#define LIBSSH2_METHOD_MAC_SC 5 +#define LIBSSH2_METHOD_COMP_CS 6 +#define LIBSSH2_METHOD_COMP_SC 7 +#define LIBSSH2_METHOD_LANG_CS 8 +#define LIBSSH2_METHOD_LANG_SC 9 + +/* session.flags bits */ +#define LIBSSH2_FLAG_SIGPIPE 0x00000001 + +typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION; +typedef struct _LIBSSH2_CHANNEL LIBSSH2_CHANNEL; +typedef struct _LIBSSH2_LISTENER LIBSSH2_LISTENER; + +typedef struct _LIBSSH2_POLLFD { + unsigned char type; /* LIBSSH2_POLLFD_* below */ + + union { + int socket; /* File descriptors -- examined with system select() call */ + LIBSSH2_CHANNEL *channel; /* Examined by checking internal state */ + LIBSSH2_LISTENER *listener; /* Read polls only -- are inbound connections waiting to be accepted? */ + } fd; + + unsigned long events; /* Requested Events */ + unsigned long revents; /* Returned Events */ +} LIBSSH2_POLLFD; + +/* Poll FD Descriptor Types */ +#define LIBSSH2_POLLFD_SOCKET 1 +#define LIBSSH2_POLLFD_CHANNEL 2 +#define LIBSSH2_POLLFD_LISTENER 3 + +/* Note: Win32 Doesn't actually have a poll() implementation, so some of these values are faked with select() data */ +/* Poll FD events/revents -- Match sys/poll.h where possible */ +#define LIBSSH2_POLLFD_POLLIN 0x0001 /* Data available to be read or connection available -- All */ +#define LIBSSH2_POLLFD_POLLPRI 0x0002 /* Priority data available to be read -- Socket only */ +#define LIBSSH2_POLLFD_POLLEXT 0x0002 /* Extended data available to be read -- Channel only */ +#define LIBSSH2_POLLFD_POLLOUT 0x0004 /* Can may be written -- Socket/Channel */ +/* revents only */ +#define LIBSSH2_POLLFD_POLLERR 0x0008 /* Error Condition -- Socket */ +#define LIBSSH2_POLLFD_POLLHUP 0x0010 /* HangUp/EOF -- Socket */ +#define LIBSSH2_POLLFD_SESSION_CLOSED 0x0010 /* Session Disconnect */ +#define LIBSSH2_POLLFD_POLLNVAL 0x0020 /* Invalid request -- Socket Only */ +#define LIBSSH2_POLLFD_POLLEX 0x0040 /* Exception Condition -- Socket/Win32 */ +#define LIBSSH2_POLLFD_CHANNEL_CLOSED 0x0080 /* Channel Disconnect */ +#define LIBSSH2_POLLFD_LISTENER_CLOSED 0x0080 /* Listener Disconnect */ + +#define HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION +/* Block Direction Types */ +#define LIBSSH2_SESSION_BLOCK_INBOUND 0x0001 +#define LIBSSH2_SESSION_BLOCK_OUTBOUND 0x0002 + +/* Hash Types */ +#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_NONE 0 +#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 +#define LIBSSH2_ERROR_SFTP_PROTOCOL -31 +#define LIBSSH2_ERROR_REQUEST_DENIED -32 +#define LIBSSH2_ERROR_METHOD_NOT_SUPPORTED -33 +#define LIBSSH2_ERROR_INVAL -34 +#define LIBSSH2_ERROR_INVALID_POLL_TYPE -35 +#define LIBSSH2_ERROR_PUBLICKEY_PROTOCOL -36 +#define LIBSSH2_ERROR_EAGAIN -37 + +/* 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 void **libssh2_session_abstract(LIBSSH2_SESSION *session); + +LIBSSH2_API void *libssh2_session_callback_set(LIBSSH2_SESSION *session, int cbtype, void *callback); +LIBSSH2_API int libssh2_banner_set(LIBSSH2_SESSION *session, const char *banner); + +LIBSSH2_API int libssh2_session_startup(LIBSSH2_SESSION *session, int sock); +LIBSSH2_API int libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang); +#define libssh2_session_disconnect(session, description) libssh2_session_disconnect_ex((session), SSH_DISCONNECT_BY_APPLICATION, (description), "") +LIBSSH2_API int libssh2_session_free(LIBSSH2_SESSION *session); + +LIBSSH2_API const char *libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type); + +LIBSSH2_API int libssh2_session_method_pref(LIBSSH2_SESSION *session, int method_type, const char *prefs); +LIBSSH2_API const char *libssh2_session_methods(LIBSSH2_SESSION *session, int method_type); +LIBSSH2_API int libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf); +LIBSSH2_API int libssh2_session_last_errno(LIBSSH2_SESSION *session); +LIBSSH2_API int libssh2_session_block_directions(LIBSSH2_SESSION *session); + +LIBSSH2_API int libssh2_session_flag(LIBSSH2_SESSION *session, int flag, int value); + +/* Userauth API */ +LIBSSH2_API char *libssh2_userauth_list(LIBSSH2_SESSION *session, const char *username, unsigned int username_len); +LIBSSH2_API int libssh2_userauth_authenticated(LIBSSH2_SESSION *session); +LIBSSH2_API int libssh2_userauth_password_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, const char *password, unsigned 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, const char *username, unsigned int username_len, + const char *publickey, const char *privatekey, + const char *passphrase); +#define libssh2_userauth_publickey_fromfile(session, username, publickey, privatekey, passphrase) \ + libssh2_userauth_publickey_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase)) +LIBSSH2_API int libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION *session, const char *username, unsigned int username_len, + const char *publickey, const char *privatekey, + const char *passphrase, + const char *hostname, unsigned int hostname_len, + const char *local_username, unsigned int local_username_len); +#define libssh2_userauth_hostbased_fromfile(session, username, publickey, privatekey, passphrase, hostname) \ + libssh2_userauth_hostbased_fromfile_ex((session), (username), strlen(username), (publickey), (privatekey), (passphrase), (hostname), strlen(hostname), (username), strlen(username)) + +/* + * response_callback is provided with filled by library prompts array, + * but client must allocate and fill individual responses. Responses + * array is already allocated. Responses data will be freed by libssh2 + * after callback return, but before subsequent callback invokation. + */ +LIBSSH2_API int libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION* session, const char *username, unsigned int username_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))); +#define libssh2_userauth_keyboard_interactive(session, username, response_callback) \ + libssh2_userauth_keyboard_interactive_ex((session), (username), strlen(username), (response_callback)) + +LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds, long timeout); + +/* Channel API */ +#define LIBSSH2_CHANNEL_WINDOW_DEFAULT 65536 +#define LIBSSH2_CHANNEL_PACKET_DEFAULT 16384 +#define LIBSSH2_CHANNEL_MINADJUST 1024 + +/* Extended Data Handling */ +#define LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL 0 +#define LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE 1 +#define LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE 2 + +#define SSH_EXTENDED_DATA_STDERR 1 + +/* Returned by any function that would block during a read/write opperation */ +#define LIBSSH2CHANNEL_EAGAIN LIBSSH2_ERROR_EAGAIN + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_open_ex(LIBSSH2_SESSION *session, const char *channel_type, unsigned int channel_type_len, unsigned int window_size, unsigned int packet_size, const char *message, unsigned 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, const char *host, int port, const 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 LIBSSH2_LISTENER *libssh2_channel_forward_listen_ex(LIBSSH2_SESSION *session, const char *host, int port, int *bound_port, int queue_maxsize); +#define libssh2_channel_forward_listen(session, port) libssh2_channel_forward_listen_ex((session), NULL, (port), NULL, 16) + +LIBSSH2_API int libssh2_channel_forward_cancel(LIBSSH2_LISTENER *listener); + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_channel_forward_accept(LIBSSH2_LISTENER *listener); + +LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel, const char *varname, unsigned int varname_len, const char *value, unsigned 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, const char *term, unsigned int term_len, const char *modes, unsigned 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_request_pty_size_ex(LIBSSH2_CHANNEL * channel, int width, int height, int width_px, int height_px); +#define libssh2_channel_request_pty_size(channel, width, height) libssh2_channel_request_pty_size_ex( (channel), (width), (height), 0, 0) + +LIBSSH2_API int libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL *channel, int single_connection, const char *auth_proto, const char *auth_cookie, int screen_number); +#define libssh2_channel_x11_req(channel, screen_number) libssh2_channel_x11_req_ex((channel), 0, NULL, NULL, (screen_number)) + +LIBSSH2_API int libssh2_channel_process_startup(LIBSSH2_CHANNEL *channel, const char *request, unsigned int request_len, const char *message, unsigned 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)) + +LIBSSH2_API ssize_t 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_poll_channel_read(LIBSSH2_CHANNEL *channel, int extended); + +LIBSSH2_API unsigned long libssh2_channel_window_read_ex(LIBSSH2_CHANNEL *channel, unsigned long *read_avail, unsigned long *window_size_initial); +#define libssh2_channel_window_read(channel) \ + libssh2_channel_window_read_ex((channel), NULL, NULL) + +LIBSSH2_API unsigned long libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL *channel, unsigned long adjustment, unsigned char force); + +LIBSSH2_API ssize_t libssh2_channel_write_ex(LIBSSH2_CHANNEL *channel, int stream_id, const char *buf, size_t buflen); + +#define libssh2_channel_write(channel, buf, buflen) \ + libssh2_channel_write_ex((channel), 0, (buf), (buflen)) +#define libssh2_channel_write_stderr(channel, buf, buflen) \ + libssh2_channel_write_ex((channel), SSH_EXTENDED_DATA_STDERR, (buf), (buflen)) + +LIBSSH2_API unsigned long libssh2_channel_window_write_ex(LIBSSH2_CHANNEL *channel, unsigned long *window_size_initial); +#define libssh2_channel_window_write(channel) libssh2_channel_window_write_ex((channel), NULL) + +LIBSSH2_API void libssh2_session_set_blocking(LIBSSH2_SESSION* session, int blocking); +LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION* session); + +LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, int blocking); + +LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, int ignore_mode); +LIBSSH2_API int libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL *channel, int ignore_mode); +/* libssh2_channel_ignore_extended_data() is defined below for BC with version 0.1 + * Future uses should use libssh2_channel_handle_extended_data() directly + * if LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE is passed, extended data will be read (FIFO) from the standard data channel + */ +/* DEPRECATED */ +#define libssh2_channel_ignore_extended_data(channel, ignore) libssh2_channel_handle_extended_data((channel), (ignore) ? LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE : LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL ) + +#define LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA -1 +#define LIBSSH2_CHANNEL_FLUSH_ALL -2 +LIBSSH2_API int libssh2_channel_flush_ex(LIBSSH2_CHANNEL *channel, int streamid); +#define libssh2_channel_flush(channel) libssh2_channel_flush_ex((channel), 0) +#define libssh2_channel_flush_stderr(channel) libssh2_channel_flush_ex((channel), SSH_EXTENDED_DATA_STDERR) +LIBSSH2_API int libssh2_channel_get_exit_status(LIBSSH2_CHANNEL* channel); + +LIBSSH2_API int libssh2_channel_send_eof(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_eof(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_wait_eof(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_close(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_wait_closed(LIBSSH2_CHANNEL *channel); +LIBSSH2_API int libssh2_channel_free(LIBSSH2_CHANNEL *channel); + +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_recv(LIBSSH2_SESSION *session, const char *path, struct stat *sb); +LIBSSH2_API LIBSSH2_CHANNEL *libssh2_scp_send_ex(LIBSSH2_SESSION *session, const char *path, int mode, size_t size, long mtime, long atime); +#define libssh2_scp_send(session, path, mode, size) libssh2_scp_send_ex((session), (path), (mode), (size), 0, 0) + +LIBSSH2_API int libssh2_base64_decode(LIBSSH2_SESSION *session, char **dest, unsigned int *dest_len, const char *src, unsigned int src_len); + +/* NOTE NOTE NOTE + libssh2_trace() has no function in builds that aren't built with debug + enabled + */ +LIBSSH2_API int libssh2_trace(LIBSSH2_SESSION *session, int bitmask); +#define LIBSSH2_TRACE_TRANS (1<<1) +#define LIBSSH2_TRACE_KEX (1<<2) +#define LIBSSH2_TRACE_AUTH (1<<3) +#define LIBSSH2_TRACE_CONN (1<<4) +#define LIBSSH2_TRACE_SCP (1<<5) +#define LIBSSH2_TRACE_SFTP (1<<6) +#define LIBSSH2_TRACE_ERROR (1<<7) +#define LIBSSH2_TRACE_PUBLICKEY (1<<8) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBSSH2_H */ Property changes on: libssh2/include/libssh2.h ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.3 Added: svn:eol-style + native Index: libssh2/src/comp.c =================================================================== --- libssh2/src/comp.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/comp.c (.../trunk) @@ -0,0 +1,340 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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" +#ifdef LIBSSH2_HAVE_ZLIB +# include +#endif + +/* ******** + * 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) +{ + (void) session; + (void) compress; + (void) payload_limit; + (void) abstract; + *dest = (unsigned char *) src; + *dest_len = src_len; + + *free_dest = 0; + + return 0; +} + +/* }}} */ + +static const 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 + 4) : (2 * src_len); + int limiter = 0; + + /* In practice they never come smaller than this */ + if (out_maxlen < 25) { + out_maxlen = 25; + } + + if (out_maxlen > (int) payload_limit) { + out_maxlen = payload_limit; + } + + strm->next_in = (unsigned char *) src; + strm->avail_in = src_len; + strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen); + out = (char *) strm->next_out; + 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, out); + return -1; + } + if (strm->avail_in) { + unsigned long out_ofs = out_maxlen - strm->avail_out; + char *newout; + + out_maxlen += + compress ? (strm->avail_in + 4) : (2 * strm->avail_in); + + if ((out_maxlen > (int) payload_limit) && !compress && limiter++) { + libssh2_error(session, LIBSSH2_ERROR_ZLIB, + "Excessive growth in decompression phase", 0); + LIBSSH2_FREE(session, out); + return -1; + } + + newout = LIBSSH2_REALLOC(session, out, out_maxlen); + if (!newout) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to expand compress/decompression buffer", + 0); + LIBSSH2_FREE(session, out); + return -1; + } + out = newout; + strm->next_out = (unsigned char *) out + out_ofs; + strm->avail_out += + compress ? (strm->avail_in + 4) : (2 * strm->avail_in); + } else + while (!strm->avail_out) { + /* Done with input, might be a byte or two in internal buffer during compress + * Or potentially many bytes if it's a decompress + */ + int grow_size = compress ? 8 : 1024; + char *newout; + + if (out_maxlen >= (int) payload_limit) { + libssh2_error(session, LIBSSH2_ERROR_ZLIB, + "Excessive growth in decompression phase", + 0); + LIBSSH2_FREE(session, out); + return -1; + } + + if (grow_size > (int) (payload_limit - out_maxlen)) { + grow_size = payload_limit - out_maxlen; + } + + out_maxlen += grow_size; + strm->avail_out = grow_size; + + newout = LIBSSH2_REALLOC(session, out, out_maxlen); + if (!newout) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to expand final compress/decompress buffer", + 0); + LIBSSH2_FREE(session, out); + return -1; + } + out = newout; + strm->next_out = (unsigned char *) out + out_maxlen - + grow_size; + + 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, out); + return -1; + } + } + } + + *dest = (unsigned char *) 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 const 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 const LIBSSH2_COMP_METHOD *_libssh2_comp_methods[] = { + &libssh2_comp_method_none, +#ifdef LIBSSH2_HAVE_ZLIB + &libssh2_comp_method_zlib, +#endif /* LIBSSH2_HAVE_ZLIB */ + NULL +}; + +const LIBSSH2_COMP_METHOD ** +libssh2_comp_methods(void) +{ + return _libssh2_comp_methods; +} Property changes on: libssh2/src/comp.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/libgcrypt.c =================================================================== --- libssh2/src/libgcrypt.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/libgcrypt.c (.../trunk) @@ -0,0 +1,560 @@ +/* Copyright (C) 2006, 2007, The Written Word, Inc. + * Copyright (C) 2008, Simon Josefsson + * 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 + +int +_libssh2_rsa_new(libssh2_rsa_ctx ** rsa, + const unsigned char *edata, + unsigned long elen, + const unsigned char *ndata, + unsigned long nlen, + const unsigned char *ddata, + unsigned long dlen, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *e1data, + unsigned long e1len, + const unsigned char *e2data, + unsigned long e2len, + const unsigned char *coeffdata, unsigned long coefflen) +{ + int rc; + (void) e1data; + (void) e1len; + (void) e2data; + (void) e2len; + + if (ddata) { + rc = gcry_sexp_build + (rsa, NULL, + "(private-key(rsa(n%b)(e%b)(d%b)(q%b)(p%b)(u%b)))", + nlen, ndata, elen, edata, dlen, ddata, plen, pdata, + qlen, qdata, coefflen, coeffdata); + } else { + rc = gcry_sexp_build(rsa, NULL, "(public-key(rsa(n%b)(e%b)))", + nlen, ndata, elen, edata); + } + if (rc) { + *rsa = NULL; + return -1; + } + + return 0; +} + +int +_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa, + const unsigned char *sig, + unsigned long sig_len, + const unsigned char *m, unsigned long m_len) +{ + unsigned char hash[SHA_DIGEST_LENGTH]; + gcry_sexp_t s_sig, s_hash; + int rc = -1; + + libssh2_sha1(m, m_len, hash); + + rc = gcry_sexp_build(&s_hash, NULL, + "(data (flags pkcs1) (hash sha1 %b))", + SHA_DIGEST_LENGTH, hash); + if (rc != 0) { + return -1; + } + + rc = gcry_sexp_build(&s_sig, NULL, "(sig-val(rsa(s %b)))", sig_len, sig); + if (rc != 0) { + gcry_sexp_release(s_hash); + return -1; + } + + rc = gcry_pk_verify(s_sig, s_hash, rsa); + gcry_sexp_release(s_sig); + gcry_sexp_release(s_hash); + + return (rc == 0) ? 0 : -1; +} + +int +_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, + const unsigned char *p, + unsigned long p_len, + const unsigned char *q, + unsigned long q_len, + const unsigned char *g, + unsigned long g_len, + const unsigned char *y, + unsigned long y_len, + const unsigned char *x, unsigned long x_len) +{ + int rc; + + if (x_len) { + rc = gcry_sexp_build + (dsactx, NULL, + "(private-key(dsa(p%b)(q%b)(g%b)(y%b)(x%b)))", + p_len, p, q_len, q, g_len, g, y_len, y, x_len, x); + } else { + rc = gcry_sexp_build(dsactx, NULL, + "(public-key(dsa(p%b)(q%b)(g%b)(y%b)))", + p_len, p, q_len, q, g_len, g, y_len, y); + } + + if (rc) { + *dsactx = NULL; + return -1; + } + + return 0; +} + +int +_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase) +{ + unsigned char *data, *save_data; + unsigned int datalen; + int ret; + unsigned char *n, *e, *d, *p, *q, *e1, *e2, *coeff; + unsigned int nlen, elen, dlen, plen, qlen, e1len, e2len, coefflen; + + (void) passphrase; + + ret = _libssh2_pem_parse(session, + "-----BEGIN RSA PRIVATE KEY-----", + "-----END RSA PRIVATE KEY-----", + fp, &data, &datalen); + if (ret) { + return -1; + } + + save_data = data; + + if (_libssh2_pem_decode_sequence(&data, &datalen)) { + ret = -1; + goto fail; + } +/* First read Version field (should be 0). */ + ret = _libssh2_pem_decode_integer(&data, &datalen, &n, &nlen); + if (ret != 0 || (nlen != 1 && *n != '\0')) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &n, &nlen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &e, &elen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &d, &dlen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &q, &qlen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &e1, &e1len); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &e2, &e2len); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &coeff, &coefflen); + if (ret != 0) { + ret = -1; + goto fail; + } + + if (_libssh2_rsa_new(rsa, e, elen, n, nlen, d, dlen, p, plen, + q, qlen, e1, e1len, e2, e2len, coeff, coefflen)) { + ret = -1; + goto fail; + } + + ret = 0; + + fail: + LIBSSH2_FREE(session, save_data); + return ret; +} + +int +_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase) +{ + unsigned char *data, *save_data; + unsigned int datalen; + int ret; + unsigned char *p, *q, *g, *y, *x; + unsigned int plen, qlen, glen, ylen, xlen; + + (void) passphrase; + + ret = _libssh2_pem_parse(session, + "-----BEGIN DSA PRIVATE KEY-----", + "-----END DSA PRIVATE KEY-----", + fp, &data, &datalen); + if (ret) { + return -1; + } + + save_data = data; + + if (_libssh2_pem_decode_sequence(&data, &datalen)) { + ret = -1; + goto fail; + } + +/* First read Version field (should be 0). */ + ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen); + if (ret != 0 || (plen != 1 && *p != '\0')) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &p, &plen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &q, &qlen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &g, &glen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &y, &ylen); + if (ret != 0) { + ret = -1; + goto fail; + } + + ret = _libssh2_pem_decode_integer(&data, &datalen, &x, &xlen); + if (ret != 0) { + ret = -1; + goto fail; + } + + if (datalen != 0) { + ret = -1; + goto fail; + } + + if (_libssh2_dsa_new(dsa, p, plen, q, qlen, g, glen, y, ylen, x, xlen)) { + ret = -1; + goto fail; + } + + ret = 0; + + fail: + LIBSSH2_FREE(session, save_data); + return ret; +} + +int +_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, + libssh2_dsa_ctx * rsactx, + const unsigned char *hash, + unsigned long hash_len, + unsigned char **signature, unsigned long *signature_len) +{ + gcry_sexp_t sig_sexp; + gcry_sexp_t data; + int rc; + const char *tmp; + size_t size; + + if (hash_len != SHA_DIGEST_LENGTH) { + return -1; + } + + if (gcry_sexp_build(&data, NULL, + "(data (flags pkcs1) (hash sha1 %b))", + hash_len, hash)) { + return -1; + } + + rc = gcry_pk_sign(&sig_sexp, data, rsactx); + + gcry_sexp_release(data); + + if (rc != 0) { + return -1; + } + + data = gcry_sexp_find_token(sig_sexp, "s", 0); + if (!data) { + return -1; + } + + tmp = gcry_sexp_nth_data(data, 1, &size); + if (!tmp) { + return -1; + } + + if (tmp[0] == '\0') { + tmp++; + size--; + } + + *signature = LIBSSH2_ALLOC(session, size); + memcpy(*signature, tmp, size); + *signature_len = size; + + return rc; +} + +int +_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, + const unsigned char *hash, + unsigned long hash_len, unsigned char *sig) +{ + unsigned char zhash[SHA_DIGEST_LENGTH + 1]; + gcry_sexp_t sig_sexp; + gcry_sexp_t data; + int ret; + const char *tmp; + size_t size; + + if (hash_len != SHA_DIGEST_LENGTH) { + return -1; + } + + memcpy(zhash + 1, hash, hash_len); + zhash[0] = 0; + + if (gcry_sexp_build(&data, NULL, "(data (value %b))", hash_len + 1, zhash)) { + return -1; + } + + ret = gcry_pk_sign(&sig_sexp, data, dsactx); + + gcry_sexp_release(data); + + if (ret != 0) { + return -1; + } + +/* Extract R. */ + + data = gcry_sexp_find_token(sig_sexp, "r", 0); + if (!data) { + ret = -1; + goto out; + } + + tmp = gcry_sexp_nth_data(data, 1, &size); + if (!tmp) { + ret = -1; + goto out; + } + + if (tmp[0] == '\0') { + tmp++; + size--; + } + + if (size != 20) { + ret = -1; + goto out; + } + + memcpy(sig, tmp, 20); + + gcry_sexp_release(data); + +/* Extract S. */ + + data = gcry_sexp_find_token(sig_sexp, "s", 0); + if (!data) { + ret = -1; + goto out; + } + + tmp = gcry_sexp_nth_data(data, 1, &size); + if (!tmp) { + ret = -1; + goto out; + } + + if (tmp[0] == '\0') { + tmp++; + size--; + } + + if (size != 20) { + ret = -1; + goto out; + } + + memcpy(sig + 20, tmp, 20); + + ret = 0; + out: + if (sig_sexp) { + gcry_sexp_release(sig_sexp); + } + if (data) { + gcry_sexp_release(data); + } + return ret; +} + +int +_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, + const unsigned char *sig, + const unsigned char *m, unsigned long m_len) +{ + unsigned char hash[SHA_DIGEST_LENGTH + 1]; + gcry_sexp_t s_sig, s_hash; + int rc = -1; + + libssh2_sha1(m, m_len, hash + 1); + hash[0] = 0; + + if (gcry_sexp_build(&s_hash, NULL, "(data(flags raw)(value %b))", + SHA_DIGEST_LENGTH + 1, hash)) { + return -1; + } + + if (gcry_sexp_build(&s_sig, NULL, "(sig-val(dsa(r %b)(s %b)))", + 20, sig, 20, sig + 20)) { + gcry_sexp_release(s_hash); + return -1; + } + + rc = gcry_pk_verify(s_sig, s_hash, dsactx); + gcry_sexp_release(s_sig); + gcry_sexp_release(s_hash); + + return (rc == 0) ? 0 : -1; +} + +int +_libssh2_cipher_init(_libssh2_cipher_ctx * h, + _libssh2_cipher_type(algo), + unsigned char *iv, unsigned char *secret, int encrypt) +{ + int mode = 0, ret; + int keylen = gcry_cipher_get_algo_keylen(algo); + + (void) encrypt; + + if (algo != GCRY_CIPHER_ARCFOUR) { + mode = GCRY_CIPHER_MODE_CBC; + } + + ret = gcry_cipher_open(h, algo, mode, 0); + if (ret) { + return -1; + } + + ret = gcry_cipher_setkey(*h, secret, keylen); + if (ret) { + gcry_cipher_close(*h); + return -1; + } + + if (algo != GCRY_CIPHER_ARCFOUR) { + int blklen = gcry_cipher_get_algo_blklen(algo); + ret = gcry_cipher_setiv(*h, iv, blklen); + if (ret) { + gcry_cipher_close(*h); + return -1; + } + } + + return 0; +} + +int +_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, + _libssh2_cipher_type(algo), + int encrypt, unsigned char *block) +{ + size_t blklen = gcry_cipher_get_algo_blklen(algo); + int ret; + if (blklen == 1) { +/* Hack for arcfour. */ + blklen = 8; + } + + if (encrypt) { + ret = gcry_cipher_encrypt(*ctx, block, blklen, block, blklen); + } else { + ret = gcry_cipher_decrypt(*ctx, block, blklen, block, blklen); + } + return ret; +} Property changes on: libssh2/src/libgcrypt.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/userauth.c =================================================================== --- libssh2/src/userauth.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/userauth.c (.../trunk) @@ -0,0 +1,1473 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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 +#include + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + + +/* {{{ 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, const char *username, + unsigned int username_len) +{ + static const unsigned char reply_codes[3] = + { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; + /* 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 *s; + int rc; + + if (session->userauth_list_state == libssh2_NB_state_idle) { + /* Zero the whole thing out */ + memset(&session->userauth_list_packet_requirev_state, 0, + sizeof(session->userauth_list_packet_requirev_state)); + + session->userauth_list_data_len = username_len + 31; + + s = session->userauth_list_data = + LIBSSH2_ALLOC(session, session->userauth_list_data_len); + if (!session->userauth_list_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; + + session->userauth_list_state = libssh2_NB_state_created; + } + + if (session->userauth_list_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_list_data, + session->userauth_list_data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block requesting userauth list", 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-none request", 0); + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + session->userauth_list_state = libssh2_NB_state_idle; + return NULL; + } + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + + session->userauth_list_state = libssh2_NB_state_sent; + } + + if (session->userauth_list_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->userauth_list_data, + &session->userauth_list_data_len, 0, + NULL, 0, + &session-> + userauth_list_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block requesting userauth list", 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_NONE, "No error", 0); + session->userauth_list_state = libssh2_NB_state_idle; + return NULL; + } + + if (session->userauth_list_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + /* Wow, who'dve thought... */ + libssh2_error(session, LIBSSH2_ERROR_NONE, "No error", 0); + LIBSSH2_FREE(session, session->userauth_list_data); + session->userauth_list_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_list_state = libssh2_NB_state_idle; + return NULL; + } + + methods_len = libssh2_ntohu32(session->userauth_list_data + 1); + + /* Do note that the memory areas overlap! */ + memmove(session->userauth_list_data, session->userauth_list_data + 5, + methods_len); + session->userauth_list_data[methods_len] = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Permitted auth methods: %s", + session->userauth_list_data); + } + + session->userauth_list_state = libssh2_NB_state_idle; + return (char *) session->userauth_list_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->state & LIBSSH2_STATE_AUTHENTICATED; +} + +/* }}} */ + +/* {{{ libssh2_userauth_password + * Plain ol' login + */ +LIBSSH2_API int +libssh2_userauth_password_ex(LIBSSH2_SESSION * session, const char *username, + unsigned int username_len, const char *password, + unsigned int password_len, + LIBSSH2_PASSWD_CHANGEREQ_FUNC((*passwd_change_cb))) +{ + unsigned char *s; + static const unsigned char reply_codes[4] = + { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, + SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 0 + }; + int rc; + + if (session->userauth_pswd_state == libssh2_NB_state_idle) { + /* Zero the whole thing out */ + memset(&session->userauth_pswd_packet_requirev_state, 0, + sizeof(session->userauth_pswd_packet_requirev_state)); + + /* + * 40 = acket_type(1) + username_len(4) + service_len(4) + + * service(14)"ssh-connection" + method_len(4) + method(8)"password" + + * chgpwdbool(1) + password_len(4) */ + session->userauth_pswd_data_len = username_len + password_len + 40; + + session->userauth_pswd_data0 = ~SSH_MSG_USERAUTH_PASSWD_CHANGEREQ; + + s = session->userauth_pswd_data = + LIBSSH2_ALLOC(session, session->userauth_pswd_data_len); + if (!session->userauth_pswd_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; + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Attempting to login using password authentication"); + + session->userauth_pswd_state = libssh2_NB_state_created; + } + + if (session->userauth_pswd_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_pswd_data, + session->userauth_pswd_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-password request", 0); + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + session->userauth_pswd_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + + session->userauth_pswd_state = libssh2_NB_state_sent; + } + + password_response: + + if ((session->userauth_pswd_state == libssh2_NB_state_sent) + || (session->userauth_pswd_state == libssh2_NB_state_sent1) + || (session->userauth_pswd_state == libssh2_NB_state_sent2)) { + if (session->userauth_pswd_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->userauth_pswd_data, + &session->userauth_pswd_data_len, + 0, NULL, 0, + &session-> + userauth_pswd_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + session->userauth_pswd_state = libssh2_NB_state_idle; + return -1; + } + + if (session->userauth_pswd_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Password authentication successful"); + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_pswd_state = libssh2_NB_state_idle; + return 0; + } + + session->userauth_pswd_newpw = NULL; + session->userauth_pswd_newpw_len = 0; + + session->userauth_pswd_state = libssh2_NB_state_sent1; + } + + if ((session->userauth_pswd_data[0] == + SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) + || (session->userauth_pswd_data0 == + SSH_MSG_USERAUTH_PASSWD_CHANGEREQ)) { + session->userauth_pswd_data0 = SSH_MSG_USERAUTH_PASSWD_CHANGEREQ; + + if ((session->userauth_pswd_state == libssh2_NB_state_sent1) || + (session->userauth_pswd_state == libssh2_NB_state_sent2)) { + if (session->userauth_pswd_state == libssh2_NB_state_sent1) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Password change required"); + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + } + if (passwd_change_cb) { + if (session->userauth_pswd_state == libssh2_NB_state_sent1) { + passwd_change_cb(session, + &session->userauth_pswd_newpw, + &session->userauth_pswd_newpw_len, + &session->abstract); + if (!session->userauth_pswd_newpw) { + libssh2_error(session, + LIBSSH2_ERROR_PASSWORD_EXPIRED, + "Password expired, and callback failed", + 0); + return -1; + } + + /* basic data_len + newpw_len(4) */ + session->userauth_pswd_data_len = + username_len + password_len + 44 + + session->userauth_pswd_newpw_len; + + s = session->userauth_pswd_data = + LIBSSH2_ALLOC(session, + session->userauth_pswd_data_len); + if (!session->userauth_pswd_data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for userauth-password-change request", + 0); + LIBSSH2_FREE(session, + session->userauth_pswd_newpw); + session->userauth_pswd_newpw = NULL; + 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 = 0x01; + s++; + + libssh2_htonu32(s, password_len); + s += 4; + memcpy(s, password, password_len); + s += password_len; + + libssh2_htonu32(s, session->userauth_pswd_newpw_len); + s += 4; + memcpy(s, session->userauth_pswd_newpw, + session->userauth_pswd_newpw_len); + s += session->userauth_pswd_newpw_len; + + session->userauth_pswd_state = libssh2_NB_state_sent2; + } + + if (session->userauth_pswd_state == libssh2_NB_state_sent2) { + rc = libssh2_packet_write(session, + session->userauth_pswd_data, + session-> + userauth_pswd_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-password-change request", + 0); + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + LIBSSH2_FREE(session, + session->userauth_pswd_newpw); + session->userauth_pswd_newpw = NULL; + return -1; + } + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + LIBSSH2_FREE(session, session->userauth_pswd_newpw); + session->userauth_pswd_newpw = NULL; + + /* + * Ugliest use of goto ever. Blame it on the + * askN => requirev migration. + */ + session->userauth_pswd_state = libssh2_NB_state_sent; + goto password_response; + } + } + } else { + libssh2_error(session, LIBSSH2_ERROR_PASSWORD_EXPIRED, + "Password Expired, and no callback specified", + 0); + session->userauth_pswd_state = libssh2_NB_state_idle; + return -1; + } + } + } + + /* FAILURE */ + LIBSSH2_FREE(session, session->userauth_pswd_data); + session->userauth_pswd_data = NULL; + session->userauth_pswd_state = libssh2_NB_state_idle; + return -1; +} + +/* }}} */ + +/* {{{ 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, + const char *pubkeyfile) +{ + FILE *fd; + char c; + unsigned char *pubkey = NULL, *sp1, *sp2, *tmp; + size_t pubkey_len = 0; + unsigned int tmp_len; + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading public key file: %s", + pubkeyfile); + /* 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++; + if (feof(fd)) { + /* the last character was EOF */ + 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); + /* + * Remove trailing whitespace + */ + while (pubkey_len && isspace(pubkey[pubkey_len - 1])) + 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, (char **) &tmp, &tmp_len, (char *) 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_privatekey + * Read a PEM encoded private key from an id_??? style file + */ +static int +libssh2_file_read_privatekey(LIBSSH2_SESSION * session, + const LIBSSH2_HOSTKEY_METHOD ** hostkey_method, + void **hostkey_abstract, + const unsigned char *method, int method_len, + const char *privkeyfile, const char *passphrase) +{ + const LIBSSH2_HOSTKEY_METHOD **hostkey_methods_avail = + libssh2_hostkey_methods(); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, "Loading private key file: %s", + privkeyfile); + *hostkey_method = NULL; + *hostkey_abstract = NULL; + while (*hostkey_methods_avail && (*hostkey_methods_avail)->name) { + if ((*hostkey_methods_avail)->initPEM + && strncmp((*hostkey_methods_avail)->name, (const char *) 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, (unsigned char *) passphrase, + hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_FILE, + "Unable to initialize private key from file", 0); + return -1; + } + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_userauth_hostbased_fromfile_ex + * Authenticate using a keypair found in the named files + */ +LIBSSH2_API int +libssh2_userauth_hostbased_fromfile_ex(LIBSSH2_SESSION * session, + const char *username, + unsigned int username_len, + const char *publickey, + const char *privatekey, + const char *passphrase, + const char *hostname, + unsigned int hostname_len, + const char *local_username, + unsigned int local_username_len) +{ + static const unsigned char reply_codes[3] = + { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, 0 }; + int rc; + + if (session->userauth_host_state == libssh2_NB_state_idle) { + const LIBSSH2_HOSTKEY_METHOD *privkeyobj; + unsigned char *pubkeydata, *sig; + unsigned long pubkeydata_len; + unsigned long sig_len; + void *abstract; + unsigned char buf[5]; + struct iovec datavec[4]; + + /* Zero the whole thing out */ + memset(&session->userauth_host_packet_requirev_state, 0, + sizeof(session->userauth_host_packet_requirev_state)); + + if (libssh2_file_read_publickey + (session, &session->userauth_host_method, + &session->userauth_host_method_len, &pubkeydata, &pubkeydata_len, + publickey)) { + return -1; + } + + /* + * 48 = packet_type(1) + username_len(4) + servicename_len(4) + + * service_name(14)"ssh-connection" + authmethod_len(4) + + * authmethod(9)"hostbased" + method_len(4) + pubkeydata_len(4) + + * local_username_len(4) + */ + session->userauth_host_packet_len = + username_len + session->userauth_host_method_len + hostname_len + + local_username_len + pubkeydata_len + 48; + + /* + * 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 + */ + session->userauth_host_s = session->userauth_host_packet = + LIBSSH2_ALLOC(session, + session->userauth_host_packet_len + 4 + (4 + + session-> + userauth_host_method_len) + + (4 + pubkeydata_len)); + if (!session->userauth_host_packet) { + LIBSSH2_FREE(session, session->userauth_host_method); + session->userauth_host_method = NULL; + return -1; + } + + *(session->userauth_host_s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(session->userauth_host_s, username_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, username, username_len); + session->userauth_host_s += username_len; + + libssh2_htonu32(session->userauth_host_s, 14); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, "ssh-connection", 14); + session->userauth_host_s += 14; + + libssh2_htonu32(session->userauth_host_s, 9); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, "hostbased", 9); + session->userauth_host_s += 9; + + libssh2_htonu32(session->userauth_host_s, + session->userauth_host_method_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, session->userauth_host_method, + session->userauth_host_method_len); + session->userauth_host_s += session->userauth_host_method_len; + + libssh2_htonu32(session->userauth_host_s, pubkeydata_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, pubkeydata, pubkeydata_len); + session->userauth_host_s += pubkeydata_len; + + libssh2_htonu32(session->userauth_host_s, hostname_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, hostname, hostname_len); + session->userauth_host_s += hostname_len; + + libssh2_htonu32(session->userauth_host_s, local_username_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, local_username, local_username_len); + session->userauth_host_s += local_username_len; + + if (libssh2_file_read_privatekey + (session, &privkeyobj, &abstract, session->userauth_host_method, + session->userauth_host_method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, session->userauth_host_method); + session->userauth_host_method = NULL; + LIBSSH2_FREE(session, session->userauth_host_packet); + session->userauth_host_packet = NULL; + return -1; + } + + 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 = session->userauth_host_packet; + datavec[2].iov_len = session->userauth_host_packet_len; + + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, session->userauth_host_method); + session->userauth_host_method = NULL; + LIBSSH2_FREE(session, session->userauth_host_packet); + session->userauth_host_packet = NULL; + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + return -1; + } + + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + + if (sig_len > pubkeydata_len) { + unsigned char *newpacket; + /* Should *NEVER* happen, but...well.. better safe than sorry */ + newpacket = LIBSSH2_REALLOC(session, session->userauth_host_packet, session->userauth_host_packet_len + 4 + (4 + session->userauth_host_method_len) + (4 + sig_len)); /* PK sigblob */ + if (!newpacket) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Failed allocating additional space for userauth-hostbased packet", + 0); + LIBSSH2_FREE(session, sig); + LIBSSH2_FREE(session, session->userauth_host_packet); + session->userauth_host_packet = NULL; + LIBSSH2_FREE(session, session->userauth_host_method); + session->userauth_host_method = NULL; + return -1; + } + session->userauth_host_packet = newpacket; + } + + session->userauth_host_s = + session->userauth_host_packet + session->userauth_host_packet_len; + + libssh2_htonu32(session->userauth_host_s, + 4 + session->userauth_host_method_len + 4 + sig_len); + session->userauth_host_s += 4; + + libssh2_htonu32(session->userauth_host_s, + session->userauth_host_method_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, session->userauth_host_method, + session->userauth_host_method_len); + session->userauth_host_s += session->userauth_host_method_len; + LIBSSH2_FREE(session, session->userauth_host_method); + session->userauth_host_method = NULL; + + libssh2_htonu32(session->userauth_host_s, sig_len); + session->userauth_host_s += 4; + memcpy(session->userauth_host_s, sig, sig_len); + session->userauth_host_s += sig_len; + LIBSSH2_FREE(session, sig); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Attempting hostbased authentication"); + + session->userauth_host_state = libssh2_NB_state_created; + } + + if (session->userauth_host_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_host_packet, + session->userauth_host_s - + session->userauth_host_packet); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-hostbased request", 0); + LIBSSH2_FREE(session, session->userauth_host_packet); + session->userauth_host_packet = NULL; + session->userauth_host_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, session->userauth_host_packet); + session->userauth_host_packet = NULL; + + session->userauth_host_state = libssh2_NB_state_sent; + } + + if (session->userauth_host_state == libssh2_NB_state_sent) { + unsigned long data_len; + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->userauth_host_data, + &data_len, 0, NULL, 0, + &session-> + userauth_host_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + session->userauth_host_state = libssh2_NB_state_idle; + return -1; + } + + if (session->userauth_host_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Hostbased authentication successful"); + /* We are us and we've proved it. */ + LIBSSH2_FREE(session, session->userauth_host_data); + session->userauth_host_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_host_state = libssh2_NB_state_idle; + return 0; + } + } + + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, session->userauth_host_data); + session->userauth_host_data = NULL; + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, + "Invalid signature for supplied public key, or bad username/public key combination", + 0); + session->userauth_host_state = libssh2_NB_state_idle; + return -1; +} + +/* }}} */ + +/* {{{ libssh2_userauth_publickey_fromfile_ex + * Authenticate using a keypair found in the named files + */ +LIBSSH2_API int +libssh2_userauth_publickey_fromfile_ex(LIBSSH2_SESSION * session, + const char *username, + unsigned int username_len, + const char *publickey, + const char *privatekey, + const char *passphrase) +{ + unsigned long pubkeydata_len = 0; + unsigned char reply_codes[4] = + { SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, + SSH_MSG_USERAUTH_PK_OK, 0 + }; + int rc; + + if (session->userauth_pblc_state == libssh2_NB_state_idle) { + unsigned char *pubkeydata; + + /* Zero the whole thing out */ + memset(&session->userauth_pblc_packet_requirev_state, 0, + sizeof(session->userauth_pblc_packet_requirev_state)); + + if (libssh2_file_read_publickey + (session, &session->userauth_pblc_method, + &session->userauth_pblc_method_len, &pubkeydata, &pubkeydata_len, + publickey)) { + return -1; + } + + /* + * 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) + */ + session->userauth_pblc_packet_len = + username_len + session->userauth_pblc_method_len + pubkeydata_len + + 45; + + /* + * 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 + */ + session->userauth_pblc_s = session->userauth_pblc_packet = + LIBSSH2_ALLOC(session, + session->userauth_pblc_packet_len + 4 + (4 + + session-> + userauth_pblc_method_len) + + (4 + pubkeydata_len)); + if (!session->userauth_pblc_packet) { + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + LIBSSH2_FREE(session, pubkeydata); + return -1; + } + + *(session->userauth_pblc_s++) = SSH_MSG_USERAUTH_REQUEST; + libssh2_htonu32(session->userauth_pblc_s, username_len); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, username, username_len); + session->userauth_pblc_s += username_len; + + libssh2_htonu32(session->userauth_pblc_s, 14); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, "ssh-connection", 14); + session->userauth_pblc_s += 14; + + libssh2_htonu32(session->userauth_pblc_s, 9); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, "publickey", 9); + session->userauth_pblc_s += 9; + + session->userauth_pblc_b = session->userauth_pblc_s; + /* Not sending signature with *this* packet */ + *(session->userauth_pblc_s++) = 0; + + libssh2_htonu32(session->userauth_pblc_s, + session->userauth_pblc_method_len); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, session->userauth_pblc_method, + session->userauth_pblc_method_len); + session->userauth_pblc_s += session->userauth_pblc_method_len; + + libssh2_htonu32(session->userauth_pblc_s, pubkeydata_len); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, pubkeydata, pubkeydata_len); + session->userauth_pblc_s += pubkeydata_len; + LIBSSH2_FREE(session, pubkeydata); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Attempting publickey authentication"); + + session->userauth_pblc_state = libssh2_NB_state_created; + } + + if (session->userauth_pblc_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_pblc_packet, + session->userauth_pblc_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-publickey request", 0); + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + + session->userauth_pblc_state = libssh2_NB_state_sent; + } + + if (session->userauth_pblc_state == libssh2_NB_state_sent) { + const LIBSSH2_HOSTKEY_METHOD *privkeyobj; + void *abstract; + unsigned char buf[5]; + struct iovec datavec[4]; + unsigned char *sig; + unsigned long sig_len; + + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->userauth_pblc_data, + &session->userauth_pblc_data_len, 0, + NULL, 0, + &session-> + userauth_pblc_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + + if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Pubkey authentication prematurely successful"); + /* + * God help any SSH server that allows an UNVERIFIED + * public key to validate the user + */ + LIBSSH2_FREE(session, session->userauth_pblc_data); + session->userauth_pblc_data = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_pblc_state = libssh2_NB_state_idle; + return 0; + } + + if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_FAILURE) { + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, session->userauth_pblc_data); + session->userauth_pblc_data = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED, + "Username/PublicKey combination invalid", 0); + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + + /* Semi-Success! */ + LIBSSH2_FREE(session, session->userauth_pblc_data); + session->userauth_pblc_data = NULL; + + if (libssh2_file_read_privatekey + (session, &privkeyobj, &abstract, session->userauth_pblc_method, + session->userauth_pblc_method_len, privatekey, passphrase)) { + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + + *session->userauth_pblc_b = 0x01; + + 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 = session->userauth_pblc_packet; + datavec[2].iov_len = session->userauth_pblc_packet_len; + + if (privkeyobj->signv(session, &sig, &sig_len, 3, datavec, &abstract)) { + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + + if (privkeyobj->dtor) { + privkeyobj->dtor(session, &abstract); + } + + /* + * If this function was restarted, pubkeydata_len might still be 0 + * which will cause an unnecessary but harmless realloc here. + */ + if (sig_len > pubkeydata_len) { + unsigned char *newpacket; + /* Should *NEVER* happen, but...well.. better safe than sorry */ + newpacket = LIBSSH2_REALLOC(session, session->userauth_pblc_packet, session->userauth_pblc_packet_len + 4 + (4 + session->userauth_pblc_method_len) + (4 + sig_len)); /* PK sigblob */ + if (!newpacket) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Failed allocating additional space for userauth-publickey packet", + 0); + LIBSSH2_FREE(session, sig); + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + session->userauth_pblc_packet = newpacket; + } + + session->userauth_pblc_s = + session->userauth_pblc_packet + session->userauth_pblc_packet_len; + session->userauth_pblc_b = NULL; + + libssh2_htonu32(session->userauth_pblc_s, + 4 + session->userauth_pblc_method_len + 4 + sig_len); + session->userauth_pblc_s += 4; + + libssh2_htonu32(session->userauth_pblc_s, + session->userauth_pblc_method_len); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, session->userauth_pblc_method, + session->userauth_pblc_method_len); + session->userauth_pblc_s += session->userauth_pblc_method_len; + LIBSSH2_FREE(session, session->userauth_pblc_method); + session->userauth_pblc_method = NULL; + + libssh2_htonu32(session->userauth_pblc_s, sig_len); + session->userauth_pblc_s += 4; + memcpy(session->userauth_pblc_s, sig, sig_len); + session->userauth_pblc_s += sig_len; + LIBSSH2_FREE(session, sig); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Attempting publickey authentication -- phase 2"); + + session->userauth_pblc_state = libssh2_NB_state_sent1; + } + + if (session->userauth_pblc_state == libssh2_NB_state_sent1) { + rc = libssh2_packet_write(session, session->userauth_pblc_packet, + session->userauth_pblc_s - + session->userauth_pblc_packet); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-publickey request", 0); + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, session->userauth_pblc_packet); + session->userauth_pblc_packet = NULL; + + session->userauth_pblc_state = libssh2_NB_state_sent2; + } + + /* PK_OK is no longer valid */ + reply_codes[2] = 0; + + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->userauth_pblc_data, + &session->userauth_pblc_data_len, 0, NULL, + 0, + &session-> + userauth_pblc_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; + } + + if (session->userauth_pblc_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Publickey authentication successful"); + /* We are us and we've proved it. */ + LIBSSH2_FREE(session, session->userauth_pblc_data); + session->userauth_pblc_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_pblc_state = libssh2_NB_state_idle; + return 0; + } + + /* This public key is not allowed for this user on this server */ + LIBSSH2_FREE(session, session->userauth_pblc_data); + session->userauth_pblc_data = NULL; + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED, + "Invalid signature for supplied public key, or bad username/public key combination", + 0); + session->userauth_pblc_state = libssh2_NB_state_idle; + return -1; +} + +/* }}} */ + +/* {{{ libssh2_userauth_keyboard_interactive + * Authenticate using a challenge-response authentication + */ +LIBSSH2_API int +libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION * session, + const char *username, + unsigned int username_len, + LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*response_callback))) +{ + unsigned char *s; + int rc; + + static const unsigned char reply_codes[4] = { SSH_MSG_USERAUTH_SUCCESS, + SSH_MSG_USERAUTH_FAILURE, SSH_MSG_USERAUTH_INFO_REQUEST, 0 + }; + unsigned int language_tag_len; + unsigned int i; + + if (session->userauth_kybd_state == libssh2_NB_state_idle) { + session->userauth_kybd_auth_name = NULL; + session->userauth_kybd_auth_instruction = NULL; + session->userauth_kybd_num_prompts = 0; + session->userauth_kybd_auth_failure = 1; + session->userauth_kybd_prompts = NULL; + session->userauth_kybd_responses = NULL; + + /* Zero the whole thing out */ + memset(&session->userauth_kybd_packet_requirev_state, 0, + sizeof(session->userauth_kybd_packet_requirev_state)); + + session->userauth_kybd_packet_len = 1 /* byte SSH_MSG_USERAUTH_REQUEST */ + + 4 + username_len /* string user name (ISO-10646 UTF-8, as defined in [RFC-3629]) */ + + 4 + 14 /* string service name (US-ASCII) */ + + 4 + 20 /* string "keyboard-interactive" (US-ASCII) */ + + 4 + 0 /* string language tag (as defined in [RFC-3066]) */ + + 4 + 0 /* string submethods (ISO-10646 UTF-8) */ + ; + + session->userauth_kybd_data = s = + LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len); + if (!s) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive authentication", + 0); + return -1; + } + + *s++ = SSH_MSG_USERAUTH_REQUEST; + + /* user name */ + libssh2_htonu32(s, username_len); + s += 4; + memcpy(s, username, username_len); + s += username_len; + + /* service name */ + libssh2_htonu32(s, sizeof("ssh-connection") - 1); + s += 4; + memcpy(s, "ssh-connection", sizeof("ssh-connection") - 1); + s += sizeof("ssh-connection") - 1; + + /* "keyboard-interactive" */ + libssh2_htonu32(s, sizeof("keyboard-interactive") - 1); + s += 4; + memcpy(s, "keyboard-interactive", sizeof("keyboard-interactive") - 1); + s += sizeof("keyboard-interactive") - 1; + + /* language tag */ + libssh2_htonu32(s, 0); + s += 4; + + /* submethods */ + libssh2_htonu32(s, 0); + s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Attempting keyboard-interactive authentication"); + + session->userauth_kybd_state = libssh2_NB_state_created; + } + + if (session->userauth_kybd_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->userauth_kybd_data, + session->userauth_kybd_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send keyboard-interactive request", 0); + LIBSSH2_FREE(session, session->userauth_kybd_data); + session->userauth_kybd_data = NULL; + session->userauth_kybd_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, session->userauth_kybd_data); + session->userauth_kybd_data = NULL; + + session->userauth_kybd_state = libssh2_NB_state_sent; + } + + for(;;) { + if (session->userauth_kybd_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->userauth_kybd_data, + &session->userauth_kybd_data_len, + 0, NULL, 0, + &session-> + userauth_kybd_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + session->userauth_kybd_state = libssh2_NB_state_idle; + return -1; + } + + if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_SUCCESS) { + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Keyboard-interactive authentication successful"); + LIBSSH2_FREE(session, session->userauth_kybd_data); + session->userauth_kybd_data = NULL; + session->state |= LIBSSH2_STATE_AUTHENTICATED; + session->userauth_kybd_state = libssh2_NB_state_idle; + return 0; + } + + if (session->userauth_kybd_data[0] == SSH_MSG_USERAUTH_FAILURE) { + LIBSSH2_FREE(session, session->userauth_kybd_data); + session->userauth_kybd_data = NULL; + session->userauth_kybd_state = libssh2_NB_state_idle; + return -1; + } + + /* server requested PAM-like conversation */ + + s = session->userauth_kybd_data + 1; + + /* string name (ISO-10646 UTF-8) */ + session->userauth_kybd_auth_name_len = libssh2_ntohu32(s); + s += 4; + session->userauth_kybd_auth_name = + LIBSSH2_ALLOC(session, session->userauth_kybd_auth_name_len); + if (!session->userauth_kybd_auth_name) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive 'name' request field", + 0); + goto cleanup; + } + memcpy(session->userauth_kybd_auth_name, s, + session->userauth_kybd_auth_name_len); + s += session->userauth_kybd_auth_name_len; + + /* string instruction (ISO-10646 UTF-8) */ + session->userauth_kybd_auth_instruction_len = libssh2_ntohu32(s); + s += 4; + session->userauth_kybd_auth_instruction = + LIBSSH2_ALLOC(session, + session->userauth_kybd_auth_instruction_len); + if (!session->userauth_kybd_auth_instruction) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive 'instruction' request field", + 0); + goto cleanup; + } + memcpy(session->userauth_kybd_auth_instruction, s, + session->userauth_kybd_auth_instruction_len); + s += session->userauth_kybd_auth_instruction_len; + + /* string language tag (as defined in [RFC-3066]) */ + language_tag_len = libssh2_ntohu32(s); + s += 4; + /* ignoring this field as deprecated */ + s += language_tag_len; + + /* int num-prompts */ + session->userauth_kybd_num_prompts = libssh2_ntohu32(s); + s += 4; + + session->userauth_kybd_prompts = + LIBSSH2_ALLOC(session, + sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * + session->userauth_kybd_num_prompts); + if (!session->userauth_kybd_prompts) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive prompts array", + 0); + goto cleanup; + } + memset(session->userauth_kybd_prompts, 0, + sizeof(LIBSSH2_USERAUTH_KBDINT_PROMPT) * + session->userauth_kybd_num_prompts); + + session->userauth_kybd_responses = + LIBSSH2_ALLOC(session, + sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * + session->userauth_kybd_num_prompts); + if (!session->userauth_kybd_responses) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive responses array", + 0); + goto cleanup; + } + memset(session->userauth_kybd_responses, 0, + sizeof(LIBSSH2_USERAUTH_KBDINT_RESPONSE) * + session->userauth_kybd_num_prompts); + + for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { + /* string prompt[1] (ISO-10646 UTF-8) */ + session->userauth_kybd_prompts[i].length = libssh2_ntohu32(s); + s += 4; + session->userauth_kybd_prompts[i].text = + LIBSSH2_ALLOC(session, + session->userauth_kybd_prompts[i].length); + if (!session->userauth_kybd_prompts[i].text) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive prompt message", + 0); + goto cleanup; + } + memcpy(session->userauth_kybd_prompts[i].text, s, + session->userauth_kybd_prompts[i].length); + s += session->userauth_kybd_prompts[i].length; + + /* boolean echo[1] */ + session->userauth_kybd_prompts[i].echo = *s++; + } + + response_callback(session->userauth_kybd_auth_name, + session->userauth_kybd_auth_name_len, + session->userauth_kybd_auth_instruction, + session->userauth_kybd_auth_instruction_len, + session->userauth_kybd_num_prompts, + session->userauth_kybd_prompts, + session->userauth_kybd_responses, + &session->abstract); + + _libssh2_debug(session, LIBSSH2_DBG_AUTH, + "Keyboard-interactive response callback function invoked"); + + session->userauth_kybd_packet_len = 1 /* byte SSH_MSG_USERAUTH_INFO_RESPONSE */ + + 4 /* int num-responses */ + ; + + for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { + /* string response[1] (ISO-10646 UTF-8) */ + session->userauth_kybd_packet_len += + 4 + session->userauth_kybd_responses[i].length; + } + + session->userauth_kybd_data = s = + LIBSSH2_ALLOC(session, session->userauth_kybd_packet_len); + if (!s) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for keyboard-interactive response packet", + 0); + goto cleanup; + } + + *s = SSH_MSG_USERAUTH_INFO_RESPONSE; + s++; + libssh2_htonu32(s, session->userauth_kybd_num_prompts); + s += 4; + + for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { + libssh2_htonu32(s, session->userauth_kybd_responses[i].length); + s += 4; + memcpy(s, session->userauth_kybd_responses[i].text, + session->userauth_kybd_responses[i].length); + s += session->userauth_kybd_responses[i].length; + } + + session->userauth_kybd_state = libssh2_NB_state_sent1; + } + + if (session->userauth_kybd_state == libssh2_NB_state_sent1) { + rc = libssh2_packet_write(session, session->userauth_kybd_data, + session->userauth_kybd_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send userauth-keyboard-interactive request", + 0); + goto cleanup; + } + + session->userauth_kybd_auth_failure = 0; + } + + cleanup: + /* + * It's safe to clean all the data here, because unallocated pointers + * are filled by zeroes + */ + + LIBSSH2_FREE(session, session->userauth_kybd_data); + session->userauth_kybd_data = NULL; + + if (session->userauth_kybd_prompts) { + for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { + LIBSSH2_FREE(session, session->userauth_kybd_prompts[i].text); + session->userauth_kybd_prompts[i].text = NULL; + } + } + + if (session->userauth_kybd_responses) { + for(i = 0; i != session->userauth_kybd_num_prompts; ++i) { + LIBSSH2_FREE(session, + session->userauth_kybd_responses[i].text); + session->userauth_kybd_responses[i].text = NULL; + } + } + + LIBSSH2_FREE(session, session->userauth_kybd_prompts); + session->userauth_kybd_prompts = NULL; + LIBSSH2_FREE(session, session->userauth_kybd_responses); + session->userauth_kybd_responses = NULL; + + if (session->userauth_kybd_auth_failure) { + session->userauth_kybd_state = libssh2_NB_state_idle; + return -1; + } + + session->userauth_kybd_state = libssh2_NB_state_sent; + } +} + +/* }}} */ Property changes on: libssh2/src/userauth.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/mac.c =================================================================== --- libssh2/src/mac.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/mac.c (.../trunk) @@ -0,0 +1,311 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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" + +#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, + 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; + (void) session; + + 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) +{ + libssh2_hmac_ctx ctx; + unsigned char seqno_buf[4]; + (void) session; + + libssh2_htonu32(seqno_buf, seqno); + + libssh2_hmac_sha1_init(&ctx, *abstract, 20); + libssh2_hmac_update(ctx, seqno_buf, 4); + libssh2_hmac_update(ctx, packet, packet_len); + if (addtl && addtl_len) { + libssh2_hmac_update(ctx, addtl, addtl_len); + } + libssh2_hmac_final(ctx, buf); + libssh2_hmac_cleanup(&ctx); + + return 0; +} + +/* }}} */ + +static const LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_sha1 = { + "hmac-sha1", + 20, + 20, + 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) +{ + unsigned char temp[SHA_DIGEST_LENGTH]; + + libssh2_mac_method_hmac_sha1_hash(session, temp, seqno, packet, packet_len, + addtl, addtl_len, abstract); + memcpy(buf, (char *) temp, 96 / 8); + + return 0; +} + +/* }}} */ + +static const LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_sha1_96 = { + "hmac-sha1-96", + 12, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_sha1_96_hash, + libssh2_mac_method_common_dtor, +}; + +/* {{{ 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) +{ + libssh2_hmac_ctx ctx; + unsigned char seqno_buf[4]; + (void) session; + + libssh2_htonu32(seqno_buf, seqno); + + libssh2_hmac_md5_init(&ctx, *abstract, 16); + libssh2_hmac_update(ctx, seqno_buf, 4); + libssh2_hmac_update(ctx, packet, packet_len); + if (addtl && addtl_len) { + libssh2_hmac_update(ctx, addtl, addtl_len); + } + libssh2_hmac_final(ctx, buf); + libssh2_hmac_cleanup(&ctx); + + return 0; +} + +/* }}} */ + +static const LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_md5 = { + "hmac-md5", + 16, + 16, + 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 long seqno, + const unsigned char *packet, + unsigned long packet_len, + const unsigned char *addtl, + unsigned long addtl_len, void **abstract) +{ + unsigned char temp[MD5_DIGEST_LENGTH]; + + libssh2_mac_method_hmac_md5_hash(session, temp, seqno, packet, packet_len, + addtl, addtl_len, abstract); + memcpy(buf, (char *) temp, 96 / 8); + + return 0; +} + +/* }}} */ + +static const LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_md5_96 = { + "hmac-md5-96", + 12, + 16, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_md5_96_hash, + libssh2_mac_method_common_dtor, +}; + +#if LIBSSH2_HMAC_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) +{ + libssh2_hmac_ctx ctx; + unsigned char seqno_buf[4]; + (void) session; + + libssh2_htonu32(seqno_buf, seqno); + + libssh2_hmac_ripemd160_init(&ctx, *abstract, 20); + libssh2_hmac_update(ctx, seqno_buf, 4); + libssh2_hmac_update(ctx, packet, packet_len); + if (addtl && addtl_len) { + libssh2_hmac_update(ctx, addtl, addtl_len); + } + libssh2_hmac_final(ctx, buf); + libssh2_hmac_cleanup(&ctx); + + return 0; +} + +/* }}} */ + +static const LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_ripemd160 = { + "hmac-ripemd160", + 20, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_ripemd160_hash, + libssh2_mac_method_common_dtor, +}; + +static const LIBSSH2_MAC_METHOD libssh2_mac_method_hmac_ripemd160_openssh_com = { + "hmac-ripemd160@openssh.com", + 20, + 20, + libssh2_mac_method_common_init, + libssh2_mac_method_hmac_ripemd160_hash, + libssh2_mac_method_common_dtor, +}; +#endif /* LIBSSH2_HMAC_RIPEMD */ + +static const LIBSSH2_MAC_METHOD *_libssh2_mac_methods[] = { + &libssh2_mac_method_hmac_sha1, + &libssh2_mac_method_hmac_sha1_96, + &libssh2_mac_method_hmac_md5, + &libssh2_mac_method_hmac_md5_96, +#if LIBSSH2_HMAC_RIPEMD + &libssh2_mac_method_hmac_ripemd160, + &libssh2_mac_method_hmac_ripemd160_openssh_com, +#endif /* LIBSSH2_HMAC_RIPEMD */ +#ifdef LIBSSH2_MAC_NONE + &libssh2_mac_method_none, +#endif /* LIBSSH2_MAC_NONE */ + NULL +}; + +const LIBSSH2_MAC_METHOD ** +libssh2_mac_methods(void) +{ + return _libssh2_mac_methods; +} Property changes on: libssh2/src/mac.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/crypt.c =================================================================== --- libssh2/src/crypt.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/crypt.c (.../trunk) @@ -0,0 +1,256 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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" + +#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 const 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 /* LIBSSH2_CRYPT_NONE */ + +struct crypt_ctx +{ + int encrypt; + _libssh2_cipher_type(algo); + _libssh2_cipher_ctx h; +}; + +static int +_libssh2_init(LIBSSH2_SESSION * session, + const LIBSSH2_CRYPT_METHOD * method, + unsigned char *iv, int *free_iv, + unsigned char *secret, int *free_secret, + int encrypt, void **abstract) +{ + struct crypt_ctx *ctx = LIBSSH2_ALLOC(session, + sizeof(struct crypt_ctx)); + if (!ctx) { + return -1; + } + ctx->encrypt = encrypt; + ctx->algo = method->algo; + if (_libssh2_cipher_init(&ctx->h, ctx->algo, iv, secret, encrypt)) { + LIBSSH2_FREE(session, ctx); + return -1; + } + *abstract = ctx; + *free_iv = 1; + *free_secret = 1; + return 0; +} + +static int +_libssh2_encrypt(LIBSSH2_SESSION * session, unsigned char *block, + void **abstract) +{ + struct crypt_ctx *cctx = *(struct crypt_ctx **) abstract; + (void) session; + return _libssh2_cipher_crypt(&cctx->h, cctx->algo, cctx->encrypt, block); +} + +static int +_libssh2_dtor(LIBSSH2_SESSION * session, void **abstract) +{ + struct crypt_ctx **cctx = (struct crypt_ctx **) abstract; + if (cctx && *cctx) { + _libssh2_cipher_dtor(&(*cctx)->h); + LIBSSH2_FREE(session, *cctx); + *abstract = NULL; + } + return 0; +} + +#if LIBSSH2_AES +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = { + "aes128-cbc", + 16, /* blocksize */ + 16, /* initial value length */ + 16, /* secret length -- 16*8 == 128bit */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_aes128 +}; + +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = { + "aes192-cbc", + 16, /* blocksize */ + 16, /* initial value length */ + 24, /* secret length -- 24*8 == 192bit */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_aes192 +}; + +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = { + "aes256-cbc", + 16, /* blocksize */ + 16, /* initial value length */ + 32, /* secret length -- 32*8 == 256bit */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_aes256 +}; + +/* rijndael-cbc@lysator.liu.se == aes256-cbc */ +static const 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 */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_aes256 +}; +#endif /* LIBSSH2_AES */ + +#if LIBSSH2_BLOWFISH +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = { + "blowfish-cbc", + 8, /* blocksize */ + 8, /* initial value length */ + 16, /* secret length */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_blowfish +}; +#endif /* LIBSSH2_BLOWFISH */ + +#if LIBSSH2_RC4 +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = { + "arcfour", + 8, /* blocksize */ + 8, /* initial value length */ + 16, /* secret length */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_arcfour +}; +#endif /* LIBSSH2_RC4 */ + +#if LIBSSH2_CAST +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = { + "cast128-cbc", + 8, /* blocksize */ + 8, /* initial value length */ + 16, /* secret length */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_cast5 +}; +#endif /* LIBSSH2_CAST */ + +#if LIBSSH2_3DES +static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = { + "3des-cbc", + 8, /* blocksize */ + 8, /* initial value length */ + 24, /* secret length */ + 0, /* flags */ + &_libssh2_init, + &_libssh2_encrypt, + &_libssh2_dtor, + _libssh2_cipher_3des +}; +#endif + +static const LIBSSH2_CRYPT_METHOD *_libssh2_crypt_methods[] = { +#if LIBSSH2_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 /* LIBSSH2_AES */ +#if LIBSSH2_BLOWFISH + &libssh2_crypt_method_blowfish_cbc, +#endif /* LIBSSH2_BLOWFISH */ +#if LIBSSH2_RC4 + &libssh2_crypt_method_arcfour, +#endif /* LIBSSH2_RC4 */ +#if LIBSSH2_CAST + &libssh2_crypt_method_cast128_cbc, +#endif /* LIBSSH2_CAST */ +#if LIBSSH2_3DES + &libssh2_crypt_method_3des_cbc, +#endif /* LIBSSH2_DES */ +#ifdef LIBSSH2_CRYPT_NONE + &libssh2_crypt_method_none, +#endif + NULL +}; + +/* Expose to kex.c */ +const LIBSSH2_CRYPT_METHOD ** +libssh2_crypt_methods(void) +{ + return _libssh2_crypt_methods; +} Property changes on: libssh2/src/crypt.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/libgcrypt.h =================================================================== --- libssh2/src/libgcrypt.h (.../tags/RELEASE_0_11_0) +++ libssh2/src/libgcrypt.h (.../trunk) @@ -0,0 +1,187 @@ +/* Copyright (C) 2006, 2007, The Written Word, Inc. + * Copyright (C) 2008, Simon Josefsson + * 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 + +#define LIBSSH2_MD5 1 + +#define LIBSSH2_HMAC_RIPEMD 1 + +#define LIBSSH2_AES 1 +#define LIBSSH2_BLOWFISH 1 +#define LIBSSH2_RC4 1 +#define LIBSSH2_CAST 1 +#define LIBSSH2_3DES 1 + +#define LIBSSH2_RSA 1 +#define LIBSSH2_DSA 1 + +#define MD5_DIGEST_LENGTH 16 +#define SHA_DIGEST_LENGTH 20 + +#define libssh2_random(buf, len) \ + (gcry_randomize ((buf), (len), GCRY_STRONG_RANDOM), 1) + +#define libssh2_sha1_ctx gcry_md_hd_t +#define libssh2_sha1_init(ctx) gcry_md_open (ctx, GCRY_MD_SHA1, 0); +#define libssh2_sha1_update(ctx, data, len) gcry_md_write (ctx, data, len) +#define libssh2_sha1_final(ctx, out) \ + memcpy (out, gcry_md_read (ctx, 0), 20), gcry_md_close (ctx) +#define libssh2_sha1(message, len, out) \ + gcry_md_hash_buffer (GCRY_MD_SHA1, out, message, len) + +#define libssh2_md5_ctx gcry_md_hd_t +#define libssh2_md5_init(ctx) gcry_md_open (ctx, GCRY_MD_MD5, 0); +#define libssh2_md5_update(ctx, data, len) gcry_md_write (ctx, data, len) +#define libssh2_md5_final(ctx, out) \ + memcpy (out, gcry_md_read (ctx, 0), 20), gcry_md_close (ctx) +#define libssh2_md5(message, len, out) \ + gcry_md_hash_buffer (GCRY_MD_MD5, out, message, len) + +#define libssh2_hmac_ctx gcry_md_hd_t +#define libssh2_hmac_sha1_init(ctx, key, keylen) \ + gcry_md_open (ctx, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC), \ + gcry_md_setkey (*ctx, key, keylen) +#define libssh2_hmac_md5_init(ctx, key, keylen) \ + gcry_md_open (ctx, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC), \ + gcry_md_setkey (*ctx, key, keylen) +#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \ + gcry_md_open (ctx, GCRY_MD_RMD160, GCRY_MD_FLAG_HMAC), \ + gcry_md_setkey (*ctx, key, keylen) +#define libssh2_hmac_update(ctx, data, datalen) \ + gcry_md_write (ctx, data, datalen) +#define libssh2_hmac_final(ctx, data) \ + memcpy (data, gcry_md_read (ctx, 0), \ + gcry_md_get_algo_dlen (gcry_md_get_algo (ctx))) +#define libssh2_hmac_cleanup(ctx) gcry_md_close (*ctx); + +#define libssh2_crypto_init() gcry_control (GCRYCTL_DISABLE_SECMEM) + +#define libssh2_rsa_ctx struct gcry_sexp + +int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, + const unsigned char *edata, + unsigned long elen, + const unsigned char *ndata, + unsigned long nlen, + const unsigned char *ddata, + unsigned long dlen, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *e1data, + unsigned long e1len, + const unsigned char *e2data, + unsigned long e2len, + const unsigned char *coeffdata, unsigned long coefflen); +int _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase); +int _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa, + const unsigned char *sig, + unsigned long sig_len, + const unsigned char *m, unsigned long m_len); +int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, + libssh2_rsa_ctx * rsactx, + const unsigned char *hash, + unsigned long hash_len, + unsigned char **signature, + unsigned long *signature_len); + +#define _libssh2_rsa_free(rsactx) gcry_sexp_release (rsactx) + +#define libssh2_dsa_ctx struct gcry_sexp + +int _libssh2_dsa_new(libssh2_dsa_ctx ** dsa, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *gdata, + unsigned long glen, + const unsigned char *ydata, + unsigned long ylen, + const unsigned char *x, unsigned long x_len); +int _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase); +int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsa, + const unsigned char *sig, + const unsigned char *m, unsigned long m_len); +int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, + const unsigned char *hash, + unsigned long hash_len, unsigned char *sig); + +#define _libssh2_dsa_free(dsactx) gcry_sexp_release (dsactx) + +#define _libssh2_cipher_type(name) int name +#define _libssh2_cipher_ctx gcry_cipher_hd_t + +#define _libssh2_cipher_aes256 GCRY_CIPHER_AES256 +#define _libssh2_cipher_aes192 GCRY_CIPHER_AES192 +#define _libssh2_cipher_aes128 GCRY_CIPHER_AES128 +#define _libssh2_cipher_blowfish GCRY_CIPHER_BLOWFISH +#define _libssh2_cipher_arcfour GCRY_CIPHER_ARCFOUR +#define _libssh2_cipher_cast5 GCRY_CIPHER_CAST5 +#define _libssh2_cipher_3des GCRY_CIPHER_3DES + +int _libssh2_cipher_init(_libssh2_cipher_ctx * h, + _libssh2_cipher_type(algo), + unsigned char *iv, + unsigned char *secret, int encrypt); + +int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, + _libssh2_cipher_type(algo), + int encrypt, unsigned char *block); + +#define _libssh2_cipher_dtor(ctx) gcry_cipher_close(*(ctx)) + +#define _libssh2_bn struct gcry_mpi +#define _libssh2_bn_ctx int +#define _libssh2_bn_ctx_new() 0 +#define _libssh2_bn_ctx_free(bnctx) ((void)0) +#define _libssh2_bn_init() gcry_mpi_new(0) +#define _libssh2_bn_rand(bn, bits, top, bottom) gcry_mpi_randomize (bn, bits, GCRY_WEAK_RANDOM) +#define _libssh2_bn_mod_exp(r, a, p, m, ctx) gcry_mpi_powm (r, a, p, m) +#define _libssh2_bn_set_word(bn, val) gcry_mpi_set_ui(bn, val) +#define _libssh2_bn_from_bin(bn, len, val) gcry_mpi_scan(&((bn)), GCRYMPI_FMT_USG, val, len, NULL) +#define _libssh2_bn_to_bin(bn, val) gcry_mpi_print (GCRYMPI_FMT_USG, val, _libssh2_bn_bytes(bn), NULL, bn) +#define _libssh2_bn_bytes(bn) (gcry_mpi_get_nbits (bn) / 8 + ((gcry_mpi_get_nbits (bn) % 8 == 0) ? 0 : 1)) +#define _libssh2_bn_bits(bn) gcry_mpi_get_nbits (bn) +#define _libssh2_bn_free(bn) gcry_mpi_release(bn) Property changes on: libssh2/src/libgcrypt.h ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/packet.c =================================================================== --- libssh2/src/packet.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/packet.c (.../trunk) @@ -0,0 +1,1270 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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 +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include +#endif + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#include + +/* {{{ libssh2_packet_queue_listener + * Queue a connection request for a listener + */ +static inline int +libssh2_packet_queue_listener(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long datalen, + packet_queue_listener_state_t * listen_state) +{ + /* + * Look for a matching listener + */ + unsigned char *s = data + (sizeof("forwarded-tcpip") - 1) + 5; + /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + unsigned long packet_len = 17 + (sizeof(FwdNotReq) - 1); + unsigned char *p; + LIBSSH2_LISTENER *listen = session->listeners; + char failure_code = 1; /* SSH_OPEN_ADMINISTRATIVELY_PROHIBITED */ + int rc; + + (void) datalen; + + if (listen_state->state == libssh2_NB_state_idle) { + listen_state->sender_channel = libssh2_ntohu32(s); + s += 4; + + listen_state->initial_window_size = libssh2_ntohu32(s); + s += 4; + listen_state->packet_size = libssh2_ntohu32(s); + s += 4; + + listen_state->host_len = libssh2_ntohu32(s); + s += 4; + listen_state->host = s; + s += listen_state->host_len; + listen_state->port = libssh2_ntohu32(s); + s += 4; + + listen_state->shost_len = libssh2_ntohu32(s); + s += 4; + listen_state->shost = s; + s += listen_state->shost_len; + listen_state->sport = libssh2_ntohu32(s); + s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Remote received connection from %s:%ld to %s:%ld", + listen_state->shost, listen_state->sport, + listen_state->host, listen_state->port); + + listen_state->state = libssh2_NB_state_allocated; + } + + if (listen_state->state != libssh2_NB_state_sent) { + while (listen) { + if ((listen->port == (int) listen_state->port) && + (strlen(listen->host) == listen_state->host_len) && + (memcmp + (listen->host, listen_state->host, + listen_state->host_len) == 0)) { + /* This is our listener */ + LIBSSH2_CHANNEL *channel, *last_queued = listen->queue; + + last_queued = listen->queue; + if (listen_state->state == libssh2_NB_state_allocated) { + if (listen->queue_maxsize && + (listen->queue_maxsize <= listen->queue_size)) { + /* Queue is full */ + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Listener queue full, ignoring"); + listen_state->state = libssh2_NB_state_sent; + break; + } + + channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a channel for new connection", + 0); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + listen_state->state = libssh2_NB_state_sent; + break; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + + channel->session = session; + channel->channel_type_len = sizeof("forwarded-tcpip") - 1; + channel->channel_type = LIBSSH2_ALLOC(session, + channel-> + channel_type_len + + 1); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a channel for new connection", + 0); + LIBSSH2_FREE(session, channel); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + listen_state->state = libssh2_NB_state_sent; + break; + } + memcpy(channel->channel_type, "forwarded-tcpip", + channel->channel_type_len + 1); + + channel->remote.id = listen_state->sender_channel; + channel->remote.window_size_initial = + LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = + LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = + LIBSSH2_CHANNEL_PACKET_DEFAULT; + + channel->local.id = libssh2_channel_nextid(session); + channel->local.window_size_initial = + listen_state->initial_window_size; + channel->local.window_size = + listen_state->initial_window_size; + channel->local.packet_size = listen_state->packet_size; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Connection queued: channel %lu/%lu win %lu/%lu packet %lu/%lu", + channel->local.id, channel->remote.id, + channel->local.window_size, + channel->remote.window_size, + channel->local.packet_size, + channel->remote.packet_size); + + p = listen_state->packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + libssh2_htonu32(p, channel->remote.id); + p += 4; + libssh2_htonu32(p, channel->local.id); + p += 4; + libssh2_htonu32(p, channel->remote.window_size_initial); + p += 4; + libssh2_htonu32(p, channel->remote.packet_size); + p += 4; + + listen_state->state = libssh2_NB_state_created; + } + + if (listen_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, listen_state->packet, + 17); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel open confirmation", + 0); + listen_state->state = libssh2_NB_state_idle; + return -1; + } + + /* Link the channel into the end of the queue list */ + + if (!last_queued) { + listen->queue = channel; + listen_state->state = libssh2_NB_state_idle; + return 0; + } + + while (last_queued->next) { + last_queued = last_queued->next; + } + + last_queued->next = channel; + channel->prev = last_queued; + + listen->queue_size++; + + listen_state->state = libssh2_NB_state_idle; + return 0; + } + } + + listen = listen->next; + } + + listen_state->state = libssh2_NB_state_sent; + } + + /* We're not listening to you */ + { + p = listen_state->packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + libssh2_htonu32(p, listen_state->sender_channel); + p += 4; + libssh2_htonu32(p, failure_code); + p += 4; + libssh2_htonu32(p, sizeof(FwdNotReq) - 1); + p += 4; + memcpy(s, FwdNotReq, sizeof(FwdNotReq) - 1); + p += sizeof(FwdNotReq) - 1; + libssh2_htonu32(p, 0); + + rc = libssh2_packet_write(session, listen_state->packet, packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send open failure", 0); + listen_state->state = libssh2_NB_state_idle; + return -1; + } + listen_state->state = libssh2_NB_state_idle; + return 0; + } +} + +/* }}} */ + +/* {{{ libssh2_packet_x11_open + * Accept a forwarded X11 connection + */ +static inline int +libssh2_packet_x11_open(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long datalen, + packet_x11_open_state_t * x11open_state) +{ + int failure_code = 2; /* SSH_OPEN_CONNECT_FAILED */ + unsigned char *s = data + (sizeof("x11") - 1) + 5; + /* 17 = packet_type(1) + channel(4) + reason(4) + descr(4) + lang(4) */ + unsigned long packet_len = 17 + (sizeof(X11FwdUnAvil) - 1); + unsigned char *p; + LIBSSH2_CHANNEL *channel; + int rc; + + (void) datalen; + + if (x11open_state->state == libssh2_NB_state_idle) { + x11open_state->sender_channel = libssh2_ntohu32(s); + s += 4; + x11open_state->initial_window_size = libssh2_ntohu32(s); + s += 4; + x11open_state->packet_size = libssh2_ntohu32(s); + s += 4; + x11open_state->shost_len = libssh2_ntohu32(s); + s += 4; + x11open_state->shost = s; + s += x11open_state->shost_len; + x11open_state->sport = libssh2_ntohu32(s); + s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "X11 Connection Received from %s:%ld on channel %lu", + x11open_state->shost, x11open_state->sport, + x11open_state->sender_channel); + + x11open_state->state = libssh2_NB_state_allocated; + } + + if (session->x11) { + if (x11open_state->state == libssh2_NB_state_allocated) { + channel = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a channel for new connection", + 0); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + goto x11_exit; + } + memset(channel, 0, sizeof(LIBSSH2_CHANNEL)); + + channel->session = session; + channel->channel_type_len = sizeof("x11") - 1; + channel->channel_type = LIBSSH2_ALLOC(session, + channel->channel_type_len + + 1); + if (!channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a channel for new connection", + 0); + LIBSSH2_FREE(session, channel); + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + goto x11_exit; + } + memcpy(channel->channel_type, "x11", + channel->channel_type_len + 1); + + channel->remote.id = x11open_state->sender_channel; + channel->remote.window_size_initial = + LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.window_size = LIBSSH2_CHANNEL_WINDOW_DEFAULT; + channel->remote.packet_size = LIBSSH2_CHANNEL_PACKET_DEFAULT; + + channel->local.id = libssh2_channel_nextid(session); + channel->local.window_size_initial = + x11open_state->initial_window_size; + channel->local.window_size = x11open_state->initial_window_size; + channel->local.packet_size = x11open_state->packet_size; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "X11 Connection established: channel %lu/%lu win %lu/%lu packet %lu/%lu", + channel->local.id, channel->remote.id, + channel->local.window_size, + channel->remote.window_size, + channel->local.packet_size, + channel->remote.packet_size); + p = x11open_state->packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_CONFIRMATION; + libssh2_htonu32(p, channel->remote.id); + p += 4; + libssh2_htonu32(p, channel->local.id); + p += 4; + libssh2_htonu32(p, channel->remote.window_size_initial); + p += 4; + libssh2_htonu32(p, channel->remote.packet_size); + p += 4; + + x11open_state->state = libssh2_NB_state_created; + } + + if (x11open_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, x11open_state->packet, 17); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel open confirmation", 0); + x11open_state->state = libssh2_NB_state_idle; + return -1; + } + + /* Link the channel into the session */ + 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; + + /* + * Pass control to the callback, they may turn right around and + * free the channel, or actually use it + */ + LIBSSH2_X11_OPEN(channel, (char *) x11open_state->shost, + x11open_state->sport); + + x11open_state->state = libssh2_NB_state_idle; + return 0; + } + } else { + failure_code = 4; /* SSH_OPEN_RESOURCE_SHORTAGE */ + } + + x11_exit: + p = x11open_state->packet; + *(p++) = SSH_MSG_CHANNEL_OPEN_FAILURE; + libssh2_htonu32(p, x11open_state->sender_channel); + p += 4; + libssh2_htonu32(p, failure_code); + p += 4; + libssh2_htonu32(p, sizeof(X11FwdUnAvil) - 1); + p += 4; + memcpy(s, X11FwdUnAvil, sizeof(X11FwdUnAvil) - 1); + p += sizeof(X11FwdUnAvil) - 1; + libssh2_htonu32(p, 0); + + rc = libssh2_packet_write(session, x11open_state->packet, packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send open failure", 0); + x11open_state->state = libssh2_NB_state_idle; + return -1; + } + x11open_state->state = libssh2_NB_state_idle; + return 0; +} + +/* }}} */ + +/* {{{ libssh2_packet_new + * Create a new packet and attach it to the brigade + */ +int +libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, + size_t datalen, int macstate) +{ + int rc; + + if (session->packAdd_state == libssh2_NB_state_idle) { + session->packAdd_data_head = 0; + + /* Zero the whole thing out */ + memset(&session->packAdd_key_state, 0, + sizeof(session->packAdd_key_state)); + + /* Zero the whole thing out */ + memset(&session->packAdd_Qlstn_state, 0, + sizeof(session->packAdd_Qlstn_state)); + + /* Zero the whole thing out */ + memset(&session->packAdd_x11open_state, 0, + sizeof(session->packAdd_x11open_state)); + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Packet type %d received, length=%d", + (int) data[0], (int) datalen); + if (macstate == LIBSSH2_MAC_INVALID) { + if (session->macerror) { + if (LIBSSH2_MACERROR(session, (char *) 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); + } + LIBSSH2_FREE(session, data); + 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); + } + LIBSSH2_FREE(session, data); + return -1; + } + } + + session->packAdd_state = libssh2_NB_state_allocated; + } + + /* + * =============================== NOTE =============================== + * I know this is very ugly and not a really good use of "goto", but + * this case statement would be even uglier to do it any other way + */ + if (session->packAdd_state == libssh2_NB_state_jump1) { + goto libssh2_packet_add_jump_point1; + } else if (session->packAdd_state == libssh2_NB_state_jump2) { + goto libssh2_packet_add_jump_point2; + } else if (session->packAdd_state == libssh2_NB_state_jump3) { + goto libssh2_packet_add_jump_point3; + } + + if (session->packAdd_state == libssh2_NB_state_allocated) { + /* 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); + /* 9 = packet_type(1) + reason(4) + message_len(4) */ + message = (char *) data + 9; + 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 = (char *) 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_debug(session, LIBSSH2_DBG_TRANS, + "Disconnect(%d): %s(%s)", reason, + message, language); + LIBSSH2_FREE(session, data); + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + session->packAdd_state = libssh2_NB_state_idle; + 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, (char *) data + 4, datalen - 5); + } + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + 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); + /* 6 = packet_type(1) + display(1) + message_len(4) */ + message = (char *) data + 6; + 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 = (char *) 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_debug will actually truncate this for us so + * that it's not an inordinate about of data + */ + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Debug Packet: %s", message); + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + break; + + case SSH_MSG_CHANNEL_EXTENDED_DATA: + /* streamid(4) */ + session->packAdd_data_head += 4; + case SSH_MSG_CHANNEL_DATA: + /* packet_type(1) + channelno(4) + datalen(4) */ + session->packAdd_data_head += 9; + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32 + (data + 1)); + + if (!session->packAdd_channel) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_UNKNOWN, + "Packet received for unknown channel, ignoring", + 0); + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } +#ifdef LIBSSH2DEBUG + { + unsigned long stream_id = 0; + + if (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) { + stream_id = libssh2_ntohu32(data + 5); + } + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "%d bytes received for channel %lu/%lu stream #%lu", + (int) (datalen - + session->packAdd_data_head), + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id, + stream_id); + } +#endif + if ((session->packAdd_channel->remote. + extended_data_ignore_mode == + LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) + && (data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA)) { + /* Pretend we didn't receive this */ + LIBSSH2_FREE(session, data); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Ignoring extended data and refunding %d bytes", + (int) (datalen - 13)); + /* Adjust the window based on the block we just freed */ + libssh2_packet_add_jump_point1: + session->packAdd_state = libssh2_NB_state_jump1; + rc = libssh2_channel_receive_window_adjust(session-> + packAdd_channel, + datalen - 13, + 0); + if (rc == PACKET_EAGAIN) { + session->socket_block_directions = + LIBSSH2_SESSION_BLOCK_OUTBOUND; + return PACKET_EAGAIN; + } + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + + /* + * REMEMBER! remote means remote as source of data, + * NOT remote window! + */ + if (session->packAdd_channel->remote.packet_size < + (datalen - session->packAdd_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 = + session->packAdd_channel->remote.packet_size + + session->packAdd_data_head; + } + if (session->packAdd_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); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + /* Reset EOF status */ + session->packAdd_channel->remote.eof = 0; + + if ((datalen - session->packAdd_data_head) > + session->packAdd_channel->remote.window_size) { + libssh2_error(session, + LIBSSH2_ERROR_CHANNEL_WINDOW_EXCEEDED, + "Remote sent more data than current window allows, truncating", + 0); + datalen = + session->packAdd_channel->remote.window_size + + session->packAdd_data_head; + } else { + /* Now that we've received it, shrink our window */ + session->packAdd_channel->remote.window_size -= + datalen - session->packAdd_data_head; + } + } + break; + + case SSH_MSG_CHANNEL_EOF: + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32 + (data + 1)); + + if (!session->packAdd_channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + + _libssh2_debug(session, + LIBSSH2_DBG_CONN, + "EOF received for channel %lu/%lu", + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id); + session->packAdd_channel->remote.eof = 1; + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + break; + + case SSH_MSG_CHANNEL_REQUEST: + { + if (libssh2_ntohu32(data + 5) == sizeof("exit-status") - 1 + && !memcmp("exit-status", data + 9, + sizeof("exit-status") - 1)) { + + /* we've got "exit-status" packet. Set the session value */ + session->packAdd_channel = + libssh2_channel_locate(session, + libssh2_ntohu32(data + 1)); + + if (session->packAdd_channel) { + session->packAdd_channel->exit_status = + libssh2_ntohu32(data + 9 + sizeof("exit-status")); + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Exit status %lu received for channel %lu/%lu", + session->packAdd_channel->exit_status, + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id); + } + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + } + break; + + case SSH_MSG_CHANNEL_CLOSE: + { + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32 + (data + 1)); + + if (!session->packAdd_channel) { + /* We may have freed already, just quietly ignore this... */ + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Close received for channel %lu/%lu", + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id); + + session->packAdd_channel->remote.close = 1; + session->packAdd_channel->remote.eof = 1; + /* TODO: Add a callback for this */ + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + break; + + case SSH_MSG_CHANNEL_OPEN: + if ((datalen >= (sizeof("forwarded-tcpip") + 4)) && + ((sizeof("forwarded-tcpip") - 1) == libssh2_ntohu32(data + 1)) + && + (memcmp + (data + 5, "forwarded-tcpip", + sizeof("forwarded-tcpip") - 1) == 0)) { + + libssh2_packet_add_jump_point2: + session->packAdd_state = libssh2_NB_state_jump2; + rc = libssh2_packet_queue_listener(session, data, datalen, + &session-> + packAdd_Qlstn_state); + if (rc == PACKET_EAGAIN) { + session->socket_block_directions = + LIBSSH2_SESSION_BLOCK_OUTBOUND; + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return rc; + } + if ((datalen >= (sizeof("x11") + 4)) && + ((sizeof("x11") - 1) == libssh2_ntohu32(data + 1)) && + (memcmp(data + 5, "x11", sizeof("x11") - 1) == 0)) { + + libssh2_packet_add_jump_point3: + session->packAdd_state = libssh2_NB_state_jump3; + rc = libssh2_packet_x11_open(session, data, datalen, + &session->packAdd_x11open_state); + if (rc == PACKET_EAGAIN) { + session->socket_block_directions = + LIBSSH2_SESSION_BLOCK_OUTBOUND; + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return rc; + } + break; + + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + { + unsigned long bytestoadd = libssh2_ntohu32(data + 5); + session->packAdd_channel = libssh2_channel_locate(session, + libssh2_ntohu32 + (data + 1)); + + if (session->packAdd_channel && bytestoadd) { + session->packAdd_channel->local.window_size += bytestoadd; + } + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Window adjust received for channel %lu/%lu, adding %lu bytes, new window_size=%lu", + session->packAdd_channel->local.id, + session->packAdd_channel->remote.id, + bytestoadd, + session->packAdd_channel->local.window_size); + + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return 0; + } + break; + } + + session->packAdd_state = libssh2_NB_state_sent; + } + + if (session->packAdd_state == libssh2_NB_state_sent) { + session->packAdd_packet = + LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + if (!session->packAdd_packet) { + _libssh2_debug(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for LIBSSH2_PACKET"); + LIBSSH2_FREE(session, data); + session->packAdd_state = libssh2_NB_state_idle; + return -1; + } + memset(session->packAdd_packet, 0, sizeof(LIBSSH2_PACKET)); + + session->packAdd_packet->data = data; + session->packAdd_packet->data_len = datalen; + session->packAdd_packet->data_head = session->packAdd_data_head; + session->packAdd_packet->mac = macstate; + session->packAdd_packet->brigade = &session->packets; + session->packAdd_packet->next = NULL; + + if (session->packets.tail) { + session->packAdd_packet->prev = session->packets.tail; + session->packAdd_packet->prev->next = session->packAdd_packet; + session->packets.tail = session->packAdd_packet; + } else { + session->packets.head = session->packAdd_packet; + session->packets.tail = session->packAdd_packet; + session->packAdd_packet->prev = NULL; + } + + session->packAdd_state = libssh2_NB_state_sent1; + } + + if ((data[0] == SSH_MSG_KEXINIT && + !(session->state & LIBSSH2_STATE_EXCHANGING_KEYS)) || + (session->packAdd_state == libssh2_NB_state_sent2)) { + if (session->packAdd_state == libssh2_NB_state_sent1) { + /* + * Remote wants new keys + * Well, it's already in the brigade, + * let's just call back into ourselves + */ + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Renegotiating Keys"); + + session->packAdd_state = libssh2_NB_state_sent2; + } + + /* + * The KEXINIT message has been added to the queue. + * The packAdd and readPack states need to be reset + * because libssh2_kex_exchange (eventually) calls upon + * libssh2_packet_read to read the rest of the key exchange + * conversation. + */ + session->readPack_state = libssh2_NB_state_idle; + session->packet.total_num = 0; + session->packAdd_state = libssh2_NB_state_idle; + session->fullpacket_state = libssh2_NB_state_idle; + + /* + * Also, don't use packAdd_key_state for key re-exchange, + * as it will be wiped out in the middle of the exchange. + * How about re-using the startup_key_state? + */ + memset(&session->startup_key_state, 0, sizeof(key_exchange_state_t)); + + /* + * 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 + */ + rc = libssh2_kex_exchange(session, 1, &session->startup_key_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + + session->packAdd_state = libssh2_NB_state_idle; + return 0; +} + +/* }}} */ + +/* {{{ 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) { + /* + * XXX CHECK *** + * When "poll_socket" is "1" libhss2_packet_read() can return + * PACKET_EAGAIN. I am not sure what should happen, but internally + * there is only one location that might do so, libssh2_packet_askv_ex() + */ + libssh2pack_t rc = libssh2_packet_read(session); + if ((rc < 0) && !packet) { + return rc; + } + } + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Looking for packet of type: %d", (int) packet_type); + + while (packet) { + if (packet->data[0] == packet_type + && (packet->data_len >= (match_ofs + match_len)) && (!match_buf + || + (memcmp + (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_askv + * Scan for any of a list of packet types in the brigade, optionally poll the + * socket for a packet first + */ +int +libssh2_packet_askv_ex(LIBSSH2_SESSION * session, + const unsigned char *packet_types, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len, int poll_socket) +{ + int i, packet_types_len = strlen((char *) packet_types); + + for(i = 0; i < packet_types_len; i++) { + /* + * XXX CHECK XXX + * When "poll_socket" is "1" libssh2_packet_ask_ex() could + * return PACKET_EAGAIN. Not sure the correct action, I + * think it is right as is. + */ + if (0 == libssh2_packet_ask_ex(session, packet_types[i], data, + data_len, match_ofs, match_buf, + match_len, i ? 0 : poll_socket)) { + return 0; + } + } + + return -1; +} + +/* }}} */ + +/* {{{ waitsocket + * Returns + * negative on error + * >0 on incoming data + * 0 on timeout + * + * FIXME: convert to use poll on systems that have it. + */ +int +libssh2_waitsocket(LIBSSH2_SESSION * session, long seconds) +{ + struct timeval timeout; + int rc; + fd_set fd; + + timeout.tv_sec = seconds; + timeout.tv_usec = 0; + + FD_ZERO(&fd); + + FD_SET(session->socket_fd, &fd); + + rc = select(session->socket_fd + 1, &fd, NULL, NULL, &timeout); + + return rc; +} + +/* {{{ libssh2_packet_require + * Loops libssh2_packet_read() until the packet requested is available + * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout + * + * Returns negative on error + * Returns 0 when it has taken care of the requested packet. + */ +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, + packet_require_state_t * state) +{ + if (state->start == 0) { + 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; + } + + state->start = time(NULL); + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "May block until packet of type %d becomes available", + (int) packet_type); + } + + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + libssh2pack_t ret = libssh2_packet_read(session); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if ((ret == 0) && (!session->socket_block)) { + /* If we are in non-blocking and there is no data, return that */ + return PACKET_EAGAIN; + } else if (ret < 0) { + state->start = 0; + /* an error which is not just because of blocking */ + return ret; + } else if (ret == packet_type) { + /* Be lazy, let packet_ask pull it out of the brigade */ + ret = + libssh2_packet_ask_ex(session, packet_type, data, data_len, + match_ofs, match_buf, match_len, 0); + state->start = 0; + return ret; + } else if (ret == 0) { + /* nothing available, wait until data arrives or we time out */ + long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); + + if ((left <= 0) || (libssh2_waitsocket(session, left) <= 0)) { + state->start = 0; + return PACKET_TIMEOUT; + } + } + } + + /* Only reached if the socket died */ + return -1; +} + +/* }}} */ + +/* {{{ libssh2_packet_burn + * Loops libssh2_packet_read() until any packet is available and promptly + * discards it + * Used during KEX exchange to discard badly guessed KEX_INIT packets + */ +int +libssh2_packet_burn(LIBSSH2_SESSION * session, + libssh2_nonblocking_states * state) +{ + unsigned char *data; + unsigned long data_len; + unsigned char all_packets[255]; + int i; + int ret; + + if (*state == libssh2_NB_state_idle) { + for(i = 1; i < 256; i++) { + all_packets[i - 1] = i; + } + + if (libssh2_packet_askv_ex + (session, all_packets, &data, &data_len, 0, NULL, 0, 0) == 0) { + i = data[0]; + /* A packet was available in the packet brigade, burn it */ + LIBSSH2_FREE(session, data); + return i; + } + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Blocking until packet becomes available to burn"); + *state = libssh2_NB_state_created; + } + + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + if ((ret = libssh2_packet_read(session)) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (ret < 0) { + *state = libssh2_NB_state_idle; + return ret; + } else if (ret == 0) { + /* FIXME: this might busyloop */ + continue; + } + + /* Be lazy, let packet_ask pull it out of the brigade */ + if (0 == + libssh2_packet_ask_ex(session, ret, &data, &data_len, 0, NULL, 0, + 0)) { + /* Smoke 'em if you got 'em */ + LIBSSH2_FREE(session, data); + *state = libssh2_NB_state_idle; + return ret; + } + } + + /* Only reached if the socket died */ + return -1; +} + +/* }}} */ + +/* + * {{{ libssh2_packet_requirev + * + * Loops libssh2_packet_read() until one of a list of packet types requested is + * available + * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout + * packet_types is a null terminated list of packet_type numbers + */ + +int +libssh2_packet_requirev_ex(LIBSSH2_SESSION * session, + const unsigned char *packet_types, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len, + packet_requirev_state_t * state) +{ + if (libssh2_packet_askv_ex + (session, packet_types, data, data_len, match_ofs, match_buf, + match_len, 0) == 0) { + /* One of the packets listed was available in the packet + brigade */ + state->start = 0; + return 0; + } + + if (state->start == 0) { + state->start = time(NULL); + } + + while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { + int ret = libssh2_packet_read(session); + if ((ret < 0) && (ret != PACKET_EAGAIN)) { + state->start = 0; + return ret; + } + if (ret <= 0) { + long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); + + if ((left <= 0) || (libssh2_waitsocket(session, left) <= 0)) { + state->start = 0; + return PACKET_TIMEOUT; + } else if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + + if (strchr((char *) packet_types, ret)) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return libssh2_packet_askv_ex(session, packet_types, data, + data_len, match_ofs, match_buf, + match_len, 0); + } + } + + /* Only reached if the socket died */ + state->start = 0; + return -1; +} + +/* }}} */ Property changes on: libssh2/src/packet.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/channel.c =================================================================== --- libssh2/src/channel.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/channel.c (.../trunk) @@ -0,0 +1,2260 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * Copyright (c) 2008 by Daniel Stenberg + * + * 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" +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_INTTYPES_H +#include +#endif + + +/* {{{ libssh2_channel_nextid + * Determine the next channel ID we can use at our end + */ +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; + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Allocated new channel ID#%lu", + id); + 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 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_ex + * Establish a generic session channel + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_open_ex(LIBSSH2_SESSION * session, const char *channel_type, + unsigned int channel_type_len, + unsigned int window_size, unsigned int packet_size, + const char *message, unsigned int message_len) +{ + static const unsigned char reply_codes[3] = { + SSH_MSG_CHANNEL_OPEN_CONFIRMATION, + SSH_MSG_CHANNEL_OPEN_FAILURE, + 0 + }; + unsigned char *s; + int rc; + + if (session->open_state == libssh2_NB_state_idle) { + session->open_channel = NULL; + session->open_packet = NULL; + session->open_data = NULL; + /* 17 = packet_type(1) + channel_type_len(4) + sender_channel(4) + + * window_size(4) + packet_size(4) */ + session->open_packet_len = channel_type_len + message_len + 17; + session->open_local_channel = libssh2_channel_nextid(session); + + /* Zero the whole thing out */ + memset(&session->open_packet_requirev_state, 0, + sizeof(session->open_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Opening Channel - win %d pack %d", window_size, + packet_size); + session->open_channel = + LIBSSH2_ALLOC(session, sizeof(LIBSSH2_CHANNEL)); + if (!session->open_channel) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate space for channel data", 0); + return NULL; + } + memset(session->open_channel, 0, sizeof(LIBSSH2_CHANNEL)); + + session->open_channel->channel_type_len = channel_type_len; + session->open_channel->channel_type = + LIBSSH2_ALLOC(session, channel_type_len); + if (!session->open_channel->channel_type) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Failed allocating memory for channel type name", 0); + LIBSSH2_FREE(session, session->open_channel); + session->open_channel = NULL; + return NULL; + } + memcpy(session->open_channel->channel_type, channel_type, + channel_type_len); + + /* REMEMBER: local as in locally sourced */ + session->open_channel->local.id = session->open_local_channel; + session->open_channel->remote.window_size = window_size; + session->open_channel->remote.window_size_initial = window_size; + session->open_channel->remote.packet_size = packet_size; + + CHANNEL_ADD(session, session->open_channel); + + s = session->open_packet = + LIBSSH2_ALLOC(session, session->open_packet_len); + if (!session->open_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate temporary space for packet", 0); + goto channel_error; + } + *(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, session->open_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; + } + + session->open_state = libssh2_NB_state_created; + } + + if (session->open_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->open_packet, + session->open_packet_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending channel-open request", 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel-open request", 0); + goto channel_error; + } + + session->open_state = libssh2_NB_state_sent; + } + + if (session->open_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, + &session->open_data, + &session->open_data_len, 1, + session->open_packet + 5 + + channel_type_len, 4, + &session->open_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block", 0); + return NULL; + } else if (rc) { + goto channel_error; + } + + if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_CONFIRMATION) { + session->open_channel->remote.id = + libssh2_ntohu32(session->open_data + 5); + session->open_channel->local.window_size = + libssh2_ntohu32(session->open_data + 9); + session->open_channel->local.window_size_initial = + libssh2_ntohu32(session->open_data + 9); + session->open_channel->local.packet_size = + libssh2_ntohu32(session->open_data + 13); + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Connection Established - ID: %lu/%lu win: %lu/%lu" + " pack: %lu/%lu", + session->open_channel->local.id, + session->open_channel->remote.id, + session->open_channel->local.window_size, + session->open_channel->remote.window_size, + session->open_channel->local.packet_size, + session->open_channel->remote.packet_size); + LIBSSH2_FREE(session, session->open_packet); + session->open_packet = NULL; + LIBSSH2_FREE(session, session->open_data); + session->open_data = NULL; + + session->open_state = libssh2_NB_state_idle; + return session->open_channel; + } + + if (session->open_data[0] == SSH_MSG_CHANNEL_OPEN_FAILURE) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, + "Channel open failure", 0); + } + } + + channel_error: + + if (session->open_data) { + LIBSSH2_FREE(session, session->open_data); + session->open_data = NULL; + } + if (session->open_packet) { + LIBSSH2_FREE(session, session->open_packet); + session->open_packet = NULL; + } + if (session->open_channel) { + unsigned char channel_id[4]; + LIBSSH2_FREE(session, session->open_channel->channel_type); + + if (session->open_channel->next) { + session->open_channel->next->prev = session->open_channel->prev; + } + if (session->open_channel->prev) { + session->open_channel->prev->next = session->open_channel->next; + } + if (session->channels.head == session->open_channel) { + session->channels.head = session->open_channel->next; + } + if (session->channels.tail == session->open_channel) { + session->channels.tail = session->open_channel->prev; + } + + /* Clear out packets meant for this channel */ + libssh2_htonu32(channel_id, session->open_channel->local.id); + while ((libssh2_packet_ask_ex + (session, SSH_MSG_CHANNEL_DATA, &session->open_data, + &session->open_data_len, 1, channel_id, 4, 0) >= 0) + || + (libssh2_packet_ask_ex + (session, SSH_MSG_CHANNEL_EXTENDED_DATA, &session->open_data, + &session->open_data_len, 1, channel_id, 4, 0) >= 0)) { + LIBSSH2_FREE(session, session->open_data); + session->open_data = NULL; + } + + /* Free any state variables still holding data */ + if (session->open_channel->write_packet) { + LIBSSH2_FREE(session, session->open_channel->write_packet); + session->open_channel->write_packet = NULL; + } + + LIBSSH2_FREE(session, session->open_channel); + session->open_channel = NULL; + } + + session->open_state = libssh2_NB_state_idle; + return NULL; +} + +/* }}} */ + +/* {{{ 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, const char *host, + int port, const char *shost, int sport) +{ + LIBSSH2_CHANNEL *channel; + unsigned char *s; + + if (session->direct_state == libssh2_NB_state_idle) { + session->direct_host_len = strlen(host); + session->direct_shost_len = strlen(shost); + /* host_len(4) + port(4) + shost_len(4) + sport(4) */ + session->direct_message_len = + session->direct_host_len + session->direct_shost_len + 16; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Requesting direct-tcpip session to from %s:%d to %s:%d", + shost, sport, host, port); + + s = session->direct_message = + LIBSSH2_ALLOC(session, session->direct_message_len); + if (!session->direct_message) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for direct-tcpip connection", + 0); + return NULL; + } + libssh2_htonu32(s, session->direct_host_len); + s += 4; + memcpy(s, host, session->direct_host_len); + s += session->direct_host_len; + libssh2_htonu32(s, port); + s += 4; + + libssh2_htonu32(s, session->direct_shost_len); + s += 4; + memcpy(s, shost, session->direct_shost_len); + s += session->direct_shost_len; + libssh2_htonu32(s, sport); + s += 4; + + session->direct_state = libssh2_NB_state_created; + } + + channel = + libssh2_channel_open_ex(session, "direct-tcpip", + sizeof("direct-tcpip") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, + (char *) session->direct_message, + session->direct_message_len); + if (!channel) { + if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + /* The error code is still set to LIBSSH2_ERROR_EAGAIN */ + return NULL; + } else { + LIBSSH2_FREE(session, session->direct_message); + session->direct_message = NULL; + return NULL; + } + } + + LIBSSH2_FREE(session, session->direct_message); + session->direct_message = NULL; + + return channel; +} + +/* }}} */ + +/* {{{ libssh2_channel_forward_listen_ex + * Bind a port on the remote host and listen for connections + */ +LIBSSH2_API LIBSSH2_LISTENER * +libssh2_channel_forward_listen_ex(LIBSSH2_SESSION * session, const char *host, + int port, int *bound_port, int queue_maxsize) +{ + unsigned char *s, *data; + static const unsigned char reply_codes[3] = + { SSH_MSG_REQUEST_SUCCESS, SSH_MSG_REQUEST_FAILURE, 0 }; + unsigned long data_len; + int rc; + + if (session->fwdLstn_state == libssh2_NB_state_idle) { + session->fwdLstn_host_len = + (host ? strlen(host) : (sizeof("0.0.0.0") - 1)); + /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + + port(4) */ + session->fwdLstn_packet_len = + session->fwdLstn_host_len + (sizeof("tcpip-forward") - 1) + 14; + + /* Zero the whole thing out */ + memset(&session->fwdLstn_packet_requirev_state, 0, + sizeof(session->fwdLstn_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Requesting tcpip-forward session for %s:%d", host, + port); + + s = session->fwdLstn_packet = + LIBSSH2_ALLOC(session, session->fwdLstn_packet_len); + if (!session->fwdLstn_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memeory for setenv packet", 0); + return NULL; + } + + *(s++) = SSH_MSG_GLOBAL_REQUEST; + libssh2_htonu32(s, sizeof("tcpip-forward") - 1); + s += 4; + memcpy(s, "tcpip-forward", sizeof("tcpip-forward") - 1); + s += sizeof("tcpip-forward") - 1; + *(s++) = 0x01; /* want_reply */ + + libssh2_htonu32(s, session->fwdLstn_host_len); + s += 4; + memcpy(s, host ? host : "0.0.0.0", session->fwdLstn_host_len); + s += session->fwdLstn_host_len; + libssh2_htonu32(s, port); + s += 4; + + session->fwdLstn_state = libssh2_NB_state_created; + } + + if (session->fwdLstn_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, session->fwdLstn_packet, + session->fwdLstn_packet_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending global-request packet for " + "forward listen request", + 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send global-request packet for forward " + "listen request", + 0); + LIBSSH2_FREE(session, session->fwdLstn_packet); + session->fwdLstn_packet = NULL; + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + LIBSSH2_FREE(session, session->fwdLstn_packet); + session->fwdLstn_packet = NULL; + + session->fwdLstn_state = libssh2_NB_state_sent; + } + + if (session->fwdLstn_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, + 0, NULL, 0, + &session-> + fwdLstn_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, "Would block", 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_PROTO, "Unknown", 0); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + + if (data[0] == SSH_MSG_REQUEST_SUCCESS) { + LIBSSH2_LISTENER *listener; + + listener = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_LISTENER)); + if (!listener) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for listener queue", + 0); + LIBSSH2_FREE(session, data); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + memset(listener, 0, sizeof(LIBSSH2_LISTENER)); + listener->session = session; + listener->host = + LIBSSH2_ALLOC(session, session->fwdLstn_host_len + 1); + if (!listener->host) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for listener queue", + 0); + LIBSSH2_FREE(session, listener); + LIBSSH2_FREE(session, data); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + memcpy(listener->host, host ? host : "0.0.0.0", + session->fwdLstn_host_len); + listener->host[session->fwdLstn_host_len] = 0; + if (data_len >= 5 && !port) { + listener->port = libssh2_ntohu32(data + 1); + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Dynamic tcpip-forward port allocated: %d", + listener->port); + } else { + listener->port = port; + } + + listener->queue_size = 0; + listener->queue_maxsize = queue_maxsize; + + listener->next = session->listeners; + listener->prev = NULL; + if (session->listeners) { + session->listeners->prev = listener; + } + session->listeners = listener; + + if (bound_port) { + *bound_port = listener->port; + } + + LIBSSH2_FREE(session, data); + session->fwdLstn_state = libssh2_NB_state_idle; + return listener; + } + + if (data[0] == SSH_MSG_REQUEST_FAILURE) { + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_REQUEST_DENIED, + "Unable to complete request for forward-listen", 0); + session->fwdLstn_state = libssh2_NB_state_idle; + return NULL; + } + } + + session->fwdLstn_state = libssh2_NB_state_idle; + + return NULL; +} + +/* }}} */ + +/* {{{ libssh2_channel_forward_cancel + * Stop listening on a remote port and free the listener + * Toss out any pending (un-accept()ed) connections + * + * Return 0 on success, PACKET_EAGAIN if would block, -1 on error + */ +LIBSSH2_API int +libssh2_channel_forward_cancel(LIBSSH2_LISTENER * listener) +{ + LIBSSH2_SESSION *session = listener->session; + LIBSSH2_CHANNEL *queued = listener->queue; + unsigned char *packet, *s; + unsigned long host_len = strlen(listener->host); + /* 14 = packet_type(1) + request_len(4) + want_replay(1) + host_len(4) + + port(4) */ + unsigned long packet_len = + host_len + 14 + sizeof("cancel-tcpip-forward") - 1; + int rc; + + if (listener->chanFwdCncl_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Cancelling tcpip-forward session for %s:%d", + listener->host, listener->port); + + 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_GLOBAL_REQUEST; + libssh2_htonu32(s, sizeof("cancel-tcpip-forward") - 1); + s += 4; + memcpy(s, "cancel-tcpip-forward", sizeof("cancel-tcpip-forward") - 1); + s += sizeof("cancel-tcpip-forward") - 1; + *(s++) = 0x00; /* want_reply */ + + libssh2_htonu32(s, host_len); + s += 4; + memcpy(s, listener->host, host_len); + s += host_len; + libssh2_htonu32(s, listener->port); + s += 4; + + listener->chanFwdCncl_state = libssh2_NB_state_created; + } else { + packet = listener->chanFwdCncl_data; + } + + if (listener->chanFwdCncl_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, packet, packet_len); + if (rc == PACKET_EAGAIN) { + listener->chanFwdCncl_data = packet; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send global-request packet for forward " + "listen request", + 0); + LIBSSH2_FREE(session, packet); + listener->chanFwdCncl_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, packet); + + listener->chanFwdCncl_state = libssh2_NB_state_sent; + } + + while (queued) { + LIBSSH2_CHANNEL *next = queued->next; + + rc = libssh2_channel_free(queued); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + queued = next; + } + LIBSSH2_FREE(session, listener->host); + + if (listener->next) { + listener->next->prev = listener->prev; + } + if (listener->prev) { + listener->prev->next = listener->next; + } else { + session->listeners = listener->next; + } + + LIBSSH2_FREE(session, listener); + + listener->chanFwdCncl_state = libssh2_NB_state_idle; + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_channel_forward_accept + * Accept a connection + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_channel_forward_accept(LIBSSH2_LISTENER * listener) +{ + libssh2pack_t rc; + + do { + rc = libssh2_packet_read(listener->session); + if (rc == PACKET_EAGAIN) { + libssh2_error(listener->session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for packet", 0); + return NULL; + } + } while (rc > 0); + + if (listener->queue) { + LIBSSH2_SESSION *session = listener->session; + LIBSSH2_CHANNEL *channel; + + channel = listener->queue; + + listener->queue = listener->queue->next; + if (listener->queue) { + listener->queue->prev = NULL; + } + + channel->prev = NULL; + channel->next = session->channels.head; + session->channels.head = channel; + + if (channel->next) { + channel->next->prev = channel; + } else { + session->channels.tail = channel; + } + listener->queue_size--; + + return channel; + } + + return NULL; +} + +/* }}} */ + +/* {{{ 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, const char *varname, + unsigned int varname_len, const char *value, + unsigned int value_len) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *data; + static const unsigned char reply_codes[3] = + { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; + unsigned long data_len; + int rc; + + if (channel->setenv_state == libssh2_NB_state_idle) { + /* 21 = packet_type(1) + channel_id(4) + request_len(4) + + * request(3)"env" + want_reply(1) + varname_len(4) + value_len(4) */ + channel->setenv_packet_len = varname_len + value_len + 21; + + /* Zero the whole thing out */ + memset(&channel->setenv_packet_requirev_state, 0, + sizeof(channel->setenv_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Setting remote environment variable: %s=%s on " + "channel %lu/%lu", + varname, value, channel->local.id, channel->remote.id); + + s = channel->setenv_packet = + LIBSSH2_ALLOC(session, channel->setenv_packet_len); + if (!channel->setenv_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++) = 0x01; + + 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; + + channel->setenv_state = libssh2_NB_state_created; + } + + if (channel->setenv_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->setenv_packet, + channel->setenv_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel-request packet for " + "setenv request", + 0); + LIBSSH2_FREE(session, channel->setenv_packet); + channel->setenv_packet = NULL; + channel->setenv_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->setenv_packet); + channel->setenv_packet = NULL; + + libssh2_htonu32(channel->setenv_local_channel, channel->local.id); + + channel->setenv_state = libssh2_NB_state_sent; + } + + if (channel->setenv_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, + 1, channel->setenv_local_channel, 4, + &channel-> + setenv_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + channel->setenv_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->setenv_state = libssh2_NB_state_idle; + return 0; + } + + LIBSSH2_FREE(session, data); + } + + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, + "Unable to complete request for channel-setenv", 0); + channel->setenv_state = libssh2_NB_state_idle; + return -1; +} + +/* }}} */ + +/* {{{ libssh2_channel_request_pty_ex + * Duh... Request a PTY + */ +LIBSSH2_API int +libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL * channel, const char *term, + unsigned int term_len, const char *modes, + unsigned int modes_len, int width, int height, + int width_px, int height_px) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *data; + static const unsigned char reply_codes[3] = + { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; + unsigned long data_len; + int rc; + + if (channel->reqPTY_state == libssh2_NB_state_idle) { + /* 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) */ + channel->reqPTY_packet_len = term_len + modes_len + 41; + + /* Zero the whole thing out */ + memset(&channel->reqPTY_packet_requirev_state, 0, + sizeof(channel->reqPTY_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Allocating tty on channel %lu/%lu", channel->local.id, + channel->remote.id); + + s = channel->reqPTY_packet = + LIBSSH2_ALLOC(session, channel->reqPTY_packet_len); + if (!channel->reqPTY_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++) = 0x01; + + 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; + } + + channel->reqPTY_state = libssh2_NB_state_created; + } + + if (channel->reqPTY_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->reqPTY_packet, + channel->reqPTY_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send pty-request packet", 0); + LIBSSH2_FREE(session, channel->reqPTY_packet); + channel->reqPTY_packet = NULL; + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->reqPTY_packet); + channel->reqPTY_packet = NULL; + + libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id); + + channel->reqPTY_state = libssh2_NB_state_sent; + } + + if (channel->reqPTY_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, + 1, channel->reqPTY_local_channel, 4, + &channel-> + reqPTY_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->reqPTY_state = libssh2_NB_state_idle; + return 0; + } + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, + "Unable to complete request for channel request-pty", 0); + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; +} + +/* }}} */ + +LIBSSH2_API int +libssh2_channel_request_pty_size_ex(LIBSSH2_CHANNEL * channel, int width, + int height, int width_px, int height_px) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s; + int rc; + + if (channel->reqPTY_state == libssh2_NB_state_idle) { + channel->reqPTY_packet_len = 39; + + /* Zero the whole thing out */ + memset(&channel->reqPTY_packet_requirev_state, 0, + sizeof(channel->reqPTY_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "changing tty size on channel %lu/%lu", + channel->local.id, + channel->remote.id); + + s = channel->reqPTY_packet = + LIBSSH2_ALLOC(session, channel->reqPTY_packet_len); + + if (!channel->reqPTY_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("window-change") - 1); + s += 4; + memcpy(s, "window-change", sizeof("window-change") - 1); + s += sizeof("window-change") - 1; + + *(s++) = 0x00; /* Don't reply */ + 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; + + channel->reqPTY_state = libssh2_NB_state_created; + } + + if (channel->reqPTY_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->reqPTY_packet, + channel->reqPTY_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send window-change packet", 0); + LIBSSH2_FREE(session, channel->reqPTY_packet); + channel->reqPTY_packet = NULL; + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->reqPTY_packet); + channel->reqPTY_packet = NULL; + libssh2_htonu32(channel->reqPTY_local_channel, channel->local.id); + channel->reqPTY_state = libssh2_NB_state_sent; + + return 0; + } + + channel->reqPTY_state = libssh2_NB_state_idle; + return -1; +} + +/* Keep this an even number */ +#define LIBSSH2_X11_RANDOM_COOKIE_LEN 32 + +/* {{{ libssh2_channel_x11_req_ex + * Request X11 forwarding + */ +LIBSSH2_API int +libssh2_channel_x11_req_ex(LIBSSH2_CHANNEL * channel, int single_connection, + const char *auth_proto, const char *auth_cookie, + int screen_number) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *data; + static const unsigned char reply_codes[3] = + { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; + unsigned long data_len; + unsigned long proto_len = + auth_proto ? strlen(auth_proto) : (sizeof("MIT-MAGIC-COOKIE-1") - 1); + unsigned long cookie_len = + auth_cookie ? strlen(auth_cookie) : LIBSSH2_X11_RANDOM_COOKIE_LEN; + int rc; + + if (channel->reqX11_state == libssh2_NB_state_idle) { + /* 30 = packet_type(1) + channel(4) + x11_req_len(4) + "x11-req"(7) + + * want_reply(1) + single_cnx(1) + proto_len(4) + cookie_len(4) + + * screen_num(4) */ + channel->reqX11_packet_len = proto_len + cookie_len + 30; + + /* Zero the whole thing out */ + memset(&channel->reqX11_packet_requirev_state, 0, + sizeof(channel->reqX11_packet_requirev_state)); + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Requesting x11-req for channel %lu/%lu: single=%d " + "proto=%s cookie=%s screen=%d", + channel->local.id, channel->remote.id, + single_connection, + auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", + auth_cookie ? auth_cookie : "", screen_number); + + s = channel->reqX11_packet = + LIBSSH2_ALLOC(session, channel->reqX11_packet_len); + if (!channel->reqX11_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("x11-req") - 1); + s += 4; + memcpy(s, "x11-req", sizeof("x11-req") - 1); + s += sizeof("x11-req") - 1; + + *(s++) = 0x01; /* want_reply */ + *(s++) = single_connection ? 0x01 : 0x00; + + libssh2_htonu32(s, proto_len); + s += 4; + memcpy(s, auth_proto ? auth_proto : "MIT-MAGIC-COOKIE-1", proto_len); + s += proto_len; + + libssh2_htonu32(s, cookie_len); + s += 4; + if (auth_cookie) { + memcpy(s, auth_cookie, cookie_len); + } else { + int i; + unsigned char buffer[LIBSSH2_X11_RANDOM_COOKIE_LEN / 2]; + + libssh2_random(buffer, LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); + for(i = 0; i < (LIBSSH2_X11_RANDOM_COOKIE_LEN / 2); i++) { + snprintf((char *) s + (i * 2), 2, "%02X", buffer[i]); + } + } + s += cookie_len; + + libssh2_htonu32(s, screen_number); + s += 4; + + channel->reqX11_state = libssh2_NB_state_created; + } + + if (channel->reqX11_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->reqX11_packet, + channel->reqX11_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send x11-req packet", 0); + LIBSSH2_FREE(session, channel->reqX11_packet); + channel->reqX11_packet = NULL; + channel->reqX11_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->reqX11_packet); + channel->reqX11_packet = NULL; + + libssh2_htonu32(channel->reqX11_local_channel, channel->local.id); + + channel->reqX11_state = libssh2_NB_state_sent; + } + + if (channel->reqX11_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, + 1, channel->reqX11_local_channel, 4, + &channel-> + reqX11_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + channel->reqX11_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->reqX11_state = libssh2_NB_state_idle; + return 0; + } + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, + "Unable to complete request for channel x11-req", 0); + return -1; +} + +/* }}} */ + +/* {{{ libssh2_channel_process_startup + * Primitive for libssh2_channel_(shell|exec|subsystem) + */ +LIBSSH2_API int +libssh2_channel_process_startup(LIBSSH2_CHANNEL * channel, const char *request, + unsigned int request_len, const char *message, + unsigned int message_len) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char *s, *data; + static const unsigned char reply_codes[3] = + { SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, 0 }; + unsigned long data_len; + libssh2pack_t rc; + + if (channel->process_state == libssh2_NB_state_idle) { + /* 10 = packet_type(1) + channel(4) + request_len(4) + want_reply(1) */ + channel->process_packet_len = request_len + 10; + + /* Zero the whole thing out */ + memset(&channel->process_packet_requirev_state, 0, + sizeof(channel->process_packet_requirev_state)); + + if (message) { + channel->process_packet_len += message_len + 4; + } + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "starting request(%s) on channel %lu/%lu, message=%s", + request, channel->local.id, channel->remote.id, + message); + s = channel->process_packet = + LIBSSH2_ALLOC(session, channel->process_packet_len); + if (!channel->process_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++) = 0x01; + + if (message) { + libssh2_htonu32(s, message_len); + s += 4; + memcpy(s, message, message_len); + s += message_len; + } + + channel->process_state = libssh2_NB_state_created; + } + + if (channel->process_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->process_packet, + channel->process_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel request", 0); + LIBSSH2_FREE(session, channel->process_packet); + channel->process_packet = NULL; + channel->process_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, channel->process_packet); + channel->process_packet = NULL; + + libssh2_htonu32(channel->process_local_channel, channel->local.id); + + channel->process_state = libssh2_NB_state_sent; + } + + if (channel->process_state == libssh2_NB_state_sent) { + rc = libssh2_packet_requirev_ex(session, reply_codes, &data, &data_len, + 1, channel->process_local_channel, 4, + &channel-> + process_packet_requirev_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + channel->process_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_MSG_CHANNEL_SUCCESS) { + LIBSSH2_FREE(session, data); + channel->process_state = libssh2_NB_state_idle; + return 0; + } + } + + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_REQUEST_DENIED, + "Unable to complete request for channel-process-startup", 0); + channel->process_state = libssh2_NB_state_idle; + 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) +{ + (void) _libssh2_session_set_blocking(channel->session, blocking); +} + +/* }}} */ + +/* {{{ libssh2_channel_flush_ex + * Flush data from one (or all) stream + * Returns number of bytes flushed, or -1 on failure + */ +LIBSSH2_API int +libssh2_channel_flush_ex(LIBSSH2_CHANNEL * channel, int streamid) +{ + LIBSSH2_PACKET *packet = channel->session->packets.head; + + if (channel->flush_state == libssh2_NB_state_idle) { + channel->flush_refund_bytes = 0; + channel->flush_flush_bytes = 0; + + while (packet) { + LIBSSH2_PACKET *next = packet->next; + unsigned char packet_type = packet->data[0]; + + if (((packet_type == SSH_MSG_CHANNEL_DATA) + || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) + && (libssh2_ntohu32(packet->data + 1) == channel->local.id)) { + /* It's our channel at least */ + long packet_stream_id = + (packet_type == + SSH_MSG_CHANNEL_DATA) ? 0 : libssh2_ntohu32(packet->data + + 5); + if ((streamid == LIBSSH2_CHANNEL_FLUSH_ALL) + || ((packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA) + && ((streamid == LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) + || (streamid == packet_stream_id))) + || ((packet_type == SSH_MSG_CHANNEL_DATA) + && (streamid == 0))) { + int bytes_to_flush = packet->data_len - packet->data_head; + + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, + "Flushing %d bytes of data from stream " + "%lu on channel %lu/%lu", + bytes_to_flush, packet_stream_id, + channel->local.id, channel->remote.id); + + /* It's one of the streams we wanted to flush */ + channel->flush_refund_bytes += packet->data_len - 13; + channel->flush_flush_bytes += bytes_to_flush; + + LIBSSH2_FREE(channel->session, packet->data); + if (packet->prev) { + packet->prev->next = packet->next; + } else { + channel->session->packets.head = packet->next; + } + if (packet->next) { + packet->next->prev = packet->prev; + } else { + channel->session->packets.tail = packet->prev; + } + LIBSSH2_FREE(channel->session, packet); + } + } + packet = next; + } + + channel->flush_state = libssh2_NB_state_created; + } + + if (channel->flush_refund_bytes) { + int rc; + + rc = libssh2_channel_receive_window_adjust(channel, + channel->flush_refund_bytes, + 0); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + + channel->flush_state = libssh2_NB_state_idle; + + return channel->flush_flush_bytes; +} + +/* }}} */ + +/* {{{ libssh2_channel_get_exit_status + * Return the channel's program exit status + */ +LIBSSH2_API int +libssh2_channel_get_exit_status(LIBSSH2_CHANNEL * channel) +{ + return channel->exit_status; +} + +/* }}} */ + +/* {{{ libssh2_channel_receive_window_adjust + * Adjust the receive window for a channel by adjustment bytes + * If the amount to be adjusted is less than LIBSSH2_CHANNEL_MINADJUST and + * force is 0 the adjustment amount will be queued for a later packet + * + * Returns the new size of the receive window (as understood by remote end) + */ +LIBSSH2_API unsigned long +libssh2_channel_receive_window_adjust(LIBSSH2_CHANNEL * channel, + unsigned long adjustment, + unsigned char force) +{ + int rc; + + if (channel->adjust_state == libssh2_NB_state_idle) { + if (!force + && (adjustment + channel->adjust_queue < + LIBSSH2_CHANNEL_MINADJUST)) { + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, + "Queueing %lu bytes for receive window adjustment " + "for channel %lu/%lu", + adjustment, channel->local.id, channel->remote.id); + channel->adjust_queue += adjustment; + return channel->remote.window_size; + } + + if (!adjustment && !channel->adjust_queue) { + return channel->remote.window_size; + } + + adjustment += channel->adjust_queue; + channel->adjust_queue = 0; + + + /* Adjust the window based on the block we just freed */ + channel->adjust_adjust[0] = SSH_MSG_CHANNEL_WINDOW_ADJUST; + libssh2_htonu32(channel->adjust_adjust + 1, channel->remote.id); + libssh2_htonu32(channel->adjust_adjust + 5, adjustment); + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, + "Adjusting window %lu bytes for data flushed from " + "channel %lu/%lu", + adjustment, channel->local.id, channel->remote.id); + + channel->adjust_state = libssh2_NB_state_created; + } + + rc = libssh2_packet_write(channel->session, channel->adjust_adjust, 9); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(channel->session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send transfer-window adjustment packet, " + "deferring", + 0); + channel->adjust_queue = adjustment; + channel->adjust_state = libssh2_NB_state_idle; + } else { + channel->adjust_state = libssh2_NB_state_idle; + channel->remote.window_size += adjustment; + } + + return channel->remote.window_size; +} + +/* }}} */ + +/* {{{ libssh2_channel_handle_extended_data + * + * How should extended data look to the calling app? Keep it in separate + * channels[_read() _read_stdder()]? (NORMAL) Merge the extended data to the + * standard data? [everything via _read()]? (MERGE) Ignore it entirely [toss + * out packets as they come in]? (IGNORE) + */ +LIBSSH2_API void +libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL * channel, + int ignore_mode) +{ + while (libssh2_channel_handle_extended_data2(channel, ignore_mode) == + PACKET_EAGAIN); +} + +LIBSSH2_API int +libssh2_channel_handle_extended_data2(LIBSSH2_CHANNEL * channel, + int ignore_mode) +{ + if (channel->extData2_state == libssh2_NB_state_idle) { + _libssh2_debug(channel->session, LIBSSH2_DBG_CONN, + "Setting channel %lu/%lu handle_extended_data mode to %d", + channel->local.id, channel->remote.id, ignore_mode); + channel->remote.extended_data_ignore_mode = ignore_mode; + + channel->extData2_state = libssh2_NB_state_created; + } + + if (channel->extData2_state == libssh2_NB_state_idle) { + if (ignore_mode == LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE) { + if (libssh2_channel_flush_ex + (channel, + LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + } + + channel->extData2_state = libssh2_NB_state_idle; + return 0; +} + +/* }}} */ + +/* + * {{{ libssh2_channel_read_ex + * Read data from a channel blocking or non-blocking depending on set state + * + * When this is done non-blocking, it is important to not return 0 until the + * currently read channel is complete. If we read stuff from the wire but it + * was no payload data to fill in the buffer with, we MUST make sure to return + * PACKET_EAGAIN. + */ +LIBSSH2_API ssize_t +libssh2_channel_read_ex(LIBSSH2_CHANNEL * channel, int stream_id, char *buf, + size_t buflen) +{ + LIBSSH2_SESSION *session = channel->session; + libssh2pack_t rc = 0; + + if (channel->read_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Attempting to read %d bytes from channel %lu/%lu stream #%d", + (int) buflen, channel->local.id, channel->remote.id, + stream_id); + + /* process all incoming packets */ + do { + if (libssh2_waitsocket(session, 0) > 0) { + rc = libssh2_packet_read(session); + } else { + /* Set for PACKET_EAGAIN so we continue */ + rc = PACKET_EAGAIN; + } + } while (rc > 0); + + if ((rc < 0) && (rc != PACKET_EAGAIN)) { + return rc; + } + channel->read_bytes_read = 0; + + channel->read_packet = session->packets.head; + channel->read_state = libssh2_NB_state_created; + } + + /* + * =============================== NOTE =============================== + * I know this is very ugly and not a really good use of "goto", but + * this case statement would be even uglier to do it any other way + */ + if (channel->read_state == libssh2_NB_state_jump1) { + goto channel_read_ex_point1; + } + + rc = 0; + channel->read_block = 0; + + do { + if (channel->read_block) { + /* in the second lap and onwards, do this... + * If we haven't yet filled our buffer, try to read more + * data. */ + if ( channel->read_bytes_read < (int) buflen) { + rc = libssh2_packet_read(session); + + /* If we didn't find any more data to read */ + if (rc < 0) { + if ( channel->read_bytes_read > 0){ + break; /* finish processing and return */ + } + + /* no packets available, no data read. */ + channel->read_state = libssh2_NB_state_idle; + return rc; + } + /* We read more data, restart our processing at the beginning + * of our packet list. */ + channel->read_packet = session->packets.head; + } + else { /* The read buffer is full, finish processing and return */ + break; + } + } + + while (channel->read_packet + && (channel->read_bytes_read < (int) buflen)) { + /* In case packet gets destroyed during this iteration */ + channel->read_next = channel->read_packet->next; + + channel->read_local_id = + libssh2_ntohu32(channel->read_packet->data + 1); + + /* + * Either we asked for a specific extended data stream + * (and data was available), + * or the standard stream (and data was available), + * or the standard stream with extended_data_merge + * enabled and data was available + */ + if ((stream_id + && (channel->read_packet->data[0] == + SSH_MSG_CHANNEL_EXTENDED_DATA) + && (channel->local.id == channel->read_local_id) + && (stream_id == + (int) libssh2_ntohu32(channel->read_packet->data + 5))) + || (!stream_id + && (channel->read_packet->data[0] == SSH_MSG_CHANNEL_DATA) + && (channel->local.id == channel->read_local_id)) + || (!stream_id + && (channel->read_packet->data[0] == + SSH_MSG_CHANNEL_EXTENDED_DATA) + && (channel->local.id == channel->read_local_id) + && (channel->remote.extended_data_ignore_mode == + LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) { + + channel->read_want = buflen - channel->read_bytes_read; + channel->read_unlink_packet = 0; + + if (channel->read_want >= + (int) (channel->read_packet->data_len - + channel->read_packet->data_head)) { + channel->read_want = + channel->read_packet->data_len - + channel->read_packet->data_head; + channel->read_unlink_packet = 1; + } + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Reading %d of buffered data from %lu/%lu/%d", + channel->read_want, channel->local.id, + channel->remote.id, stream_id); + memcpy(buf + channel->read_bytes_read, + channel->read_packet->data + + channel->read_packet->data_head, channel->read_want); + channel->read_packet->data_head += channel->read_want; + channel->read_bytes_read += channel->read_want; + + if (channel->read_unlink_packet) { + if (channel->read_packet->prev) { + channel->read_packet->prev->next = + channel->read_packet->next; + } else { + session->packets.head = channel->read_packet->next; + } + if (channel->read_packet->next) { + channel->read_packet->next->prev = + channel->read_packet->prev; + } else { + session->packets.tail = channel->read_packet->prev; + } + LIBSSH2_FREE(session, channel->read_packet->data); + + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Unlinking empty packet buffer from " + "channel %lu/%lu", + channel->local.id, channel->remote.id); + channel_read_ex_point1: + channel->read_state = libssh2_NB_state_jump1; + rc = libssh2_channel_receive_window_adjust(channel, + channel-> + read_packet-> + data_len - + (stream_id ? 13 + : 9), 0); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + channel->read_state = libssh2_NB_state_created; + LIBSSH2_FREE(session, channel->read_packet); + channel->read_packet = NULL; + } + } + channel->read_packet = channel->read_next; + } + channel->read_block = 1; + } while ((channel->read_bytes_read == 0) && !channel->remote.close); + + channel->read_state = libssh2_NB_state_idle; + if (channel->read_bytes_read == 0) { + if (channel->session->socket_block) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_CLOSED, + "Remote end has closed this channel", 0); + } else { + /* + * when non-blocking, we must return PACKET_EAGAIN if we haven't + * completed reading the channel + */ + if (!libssh2_channel_eof(channel)) { + return PACKET_EAGAIN; + } + } + } + + channel->read_state = libssh2_NB_state_idle; + return channel->read_bytes_read; +} + +/* }}} */ + +/* + * {{{ libssh2_channel_packet_data_len + * Return the size of the data block of the current packet, or 0 if there + * isn't a packet. + */ +unsigned long +libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, int stream_id) +{ + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_PACKET *read_packet; + uint32_t read_local_id; + + if ((read_packet = session->packets.head) == NULL) { + return 0; + } + + while (read_packet) { + read_local_id = libssh2_ntohu32(read_packet->data + 1); + + /* + * Either we asked for a specific extended data stream + * (and data was available), + * or the standard stream (and data was available), + * or the standard stream with extended_data_merge + * enabled and data was available + */ + if ((stream_id + && (read_packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA) + && (channel->local.id == read_local_id) + && (stream_id == (int) libssh2_ntohu32(read_packet->data + 5))) + || (!stream_id && (read_packet->data[0] == SSH_MSG_CHANNEL_DATA) + && (channel->local.id == read_local_id)) || + (!stream_id + && (read_packet-> + data[0] == + SSH_MSG_CHANNEL_EXTENDED_DATA) + && (channel-> + local.id == + read_local_id) + && (channel-> + remote. + extended_data_ignore_mode + == + LIBSSH2_CHANNEL_EXTENDED_DATA_MERGE))) + { + return (read_packet->data_len - read_packet->data_head); + } + read_packet = read_packet->next; + } + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_channel_write_ex + * Send data to a channel + */ +LIBSSH2_API ssize_t +libssh2_channel_write_ex(LIBSSH2_CHANNEL * channel, int stream_id, + const char *buf, size_t buflen) +{ + LIBSSH2_SESSION *session = channel->session; + libssh2pack_t rc; + + if (channel->write_state == libssh2_NB_state_idle) { + channel->write_bufwrote = 0; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Writing %d bytes on channel %lu/%lu, stream #%d", + (int) buflen, channel->local.id, channel->remote.id, + stream_id); + + 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); + } + + /* [13] 9 = packet_type(1) + channelno(4) [ + streamid(4) ] + + buflen(4) */ + channel->write_packet_len = buflen + (stream_id ? 13 : 9); + channel->write_packet = + LIBSSH2_ALLOC(session, channel->write_packet_len); + if (!channel->write_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocte space for data transmission packet", + 0); + return -1; + } + + channel->write_state = libssh2_NB_state_allocated; + } + + while (buflen > 0) { + if (channel->write_state == libssh2_NB_state_allocated) { + channel->write_bufwrite = buflen; + channel->write_s = channel->write_packet; + + *(channel->write_s++) = + stream_id ? SSH_MSG_CHANNEL_EXTENDED_DATA : + SSH_MSG_CHANNEL_DATA; + libssh2_htonu32(channel->write_s, channel->remote.id); + channel->write_s += 4; + if (stream_id) { + libssh2_htonu32(channel->write_s, stream_id); + channel->write_s += 4; + } + + /* twiddle our thumbs until there's window space available */ + while (channel->local.window_size <= 0) { + /* Don't worry -- This is never hit unless it's a + blocking channel anyway */ + rc = libssh2_packet_read(session); + + if (rc < 0) { + /* Error or EAGAIN occurred, disconnect? */ + if (rc != PACKET_EAGAIN) { + channel->write_state = libssh2_NB_state_idle; + } + return rc; + } + + if ((rc == 0) && (session->socket_block == 0)) { + /* + * if rc == 0 and in non-blocking, then fake EAGAIN + * to prevent busyloops until data arriaves on the network + * which seemed like a very bad idea + */ + return PACKET_EAGAIN; + } + } + + /* Don't exceed the remote end's limits */ + /* REMEMBER local means local as the SOURCE of the data */ + if (channel->write_bufwrite > channel->local.window_size) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Splitting write block due to %lu byte " + "window_size on %lu/%lu/%d", + channel->local.window_size, channel->local.id, + channel->remote.id, stream_id); + channel->write_bufwrite = channel->local.window_size; + } + if (channel->write_bufwrite > channel->local.packet_size) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Splitting write block due to %lu byte " + "packet_size on %lu/%lu/%d", + channel->local.packet_size, channel->local.id, + channel->remote.id, stream_id); + channel->write_bufwrite = channel->local.packet_size; + } + libssh2_htonu32(channel->write_s, channel->write_bufwrite); + channel->write_s += 4; + memcpy(channel->write_s, buf, channel->write_bufwrite); + channel->write_s += channel->write_bufwrite; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Sending %d bytes on channel %lu/%lu, stream_id=%d", + (int) channel->write_bufwrite, channel->local.id, + channel->remote.id, stream_id); + + channel->write_state = libssh2_NB_state_created; + } + + if (channel->write_state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, channel->write_packet, + channel->write_s - + channel->write_packet); + if (rc == PACKET_EAGAIN) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "libssh2_packet_write returned EAGAIN"); + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send channel data", 0); + LIBSSH2_FREE(session, channel->write_packet); + channel->write_packet = NULL; + channel->write_state = libssh2_NB_state_idle; + return -1; + } + /* Shrink local window size */ + channel->local.window_size -= channel->write_bufwrite; + + /* Adjust buf for next iteration */ + buflen -= channel->write_bufwrite; + buf += channel->write_bufwrite; + channel->write_bufwrote += channel->write_bufwrite; + + channel->write_state = libssh2_NB_state_allocated; + + /* + * Not sure this is still wanted + if (!channel->session->socket_block) { + break; + } + */ + } + } + + LIBSSH2_FREE(session, channel->write_packet); + channel->write_packet = NULL; + + channel->write_state = libssh2_NB_state_idle; + + return channel->write_bufwrote; +} + +/* }}} */ + +/* {{{ 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) */ + int rc; + + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Sending EOF on channel %lu/%lu", + channel->local.id, channel->remote.id); + packet[0] = SSH_MSG_CHANNEL_EOF; + libssh2_htonu32(packet + 1, channel->remote.id); + rc = libssh2_packet_write(session, packet, 5); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + 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_wait_eof +* Awaiting channel EOF +*/ +LIBSSH2_API int +libssh2_channel_wait_eof(LIBSSH2_CHANNEL * channel) +{ + LIBSSH2_SESSION *session = channel->session; + int rc; + + if (channel->wait_eof_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Awaiting close of channel %lu/%lu", channel->local.id, + channel->remote.id); + + channel->wait_eof_state = libssh2_NB_state_created; + } + + /* + * While channel is not eof, read more packets from the network. + * Either the EOF will be set or network timeout will occur. + */ + do { + if (channel->remote.eof) { + break; + } + rc = libssh2_packet_read(session); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc < 0) { + channel->wait_eof_state = libssh2_NB_state_idle; + return -1; + } + } while (1); + + channel->wait_eof_state = libssh2_NB_state_idle; + + return 0; +} + +/* }}} */ + + +/* {{{ libssh2_channel_close + * Close a channel + */ +LIBSSH2_API int +libssh2_channel_close(LIBSSH2_CHANNEL * channel) +{ + LIBSSH2_SESSION *session = channel->session; + int rc = 0; + int retcode; + + if (channel->local.close) { + /* Already closed, act like we sent another close, + * even though we didn't... shhhhhh */ + channel->close_state = libssh2_NB_state_idle; + return 0; + } + + if (channel->close_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, "Closing channel %lu/%lu", + channel->local.id, channel->remote.id); + + if (channel->close_cb) { + LIBSSH2_CHANNEL_CLOSE(session, channel); + } + channel->local.close = 1; + + channel->close_packet[0] = SSH_MSG_CHANNEL_CLOSE; + libssh2_htonu32(channel->close_packet + 1, channel->remote.id); + + channel->close_state = libssh2_NB_state_created; + } + + if (channel->close_state == libssh2_NB_state_created) { + retcode = libssh2_packet_write(session, channel->close_packet, 5); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (retcode) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send close-channel request", 0); + channel->close_state = libssh2_NB_state_idle; + return -1; + } + + channel->close_state = libssh2_NB_state_sent; + } + + if (channel->close_state == libssh2_NB_state_sent) { + /* We must wait for the remote SSH_MSG_CHANNEL_CLOSE message */ + if (!channel->remote.close) { + libssh2pack_t ret; + + do { + ret = libssh2_packet_read(session); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (ret < 0) { + rc = -1; + } + } while ((ret != SSH_MSG_CHANNEL_CLOSE) && (rc == 0)); + } + } + + channel->close_state = libssh2_NB_state_idle; + + return rc; +} + +/* }}} */ + +/* {{{ libssh2_channel_wait_closed + * Awaiting channel close after EOF + */ +LIBSSH2_API int +libssh2_channel_wait_closed(LIBSSH2_CHANNEL * channel) +{ + LIBSSH2_SESSION *session = channel->session; + int rc; + + if (!libssh2_channel_eof(channel)) { + libssh2_error(session, LIBSSH2_ERROR_INVAL, + "libssh2_channel_wait_closed() invoked when channel is " + "not in EOF state", + 0); + return -1; + } + + if (channel->wait_closed_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Awaiting close of channel %lu/%lu", channel->local.id, + channel->remote.id); + + channel->wait_closed_state = libssh2_NB_state_created; + } + + /* + * While channel is not closed, read more packets from the network. + * Either the channel will be closed or network timeout will occur. + */ + do { + if (!channel->remote.close) { + break; + } + rc = libssh2_packet_read(session); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc <= 0) { + break; + } + } while (1); + + channel->wait_closed_state = libssh2_NB_state_idle; + + return 0; +} + +/* }}} */ + + +/* {{{ libssh2_channel_free + * Make sure a channel is closed, then remove the channel from the session + * and free its resource(s) + * + * Returns 0 on success, -1 on failure + */ +LIBSSH2_API int +libssh2_channel_free(LIBSSH2_CHANNEL * channel) +{ + LIBSSH2_SESSION *session = channel->session; + unsigned char channel_id[4]; + unsigned char *data; + unsigned long data_len; + int rc; + + if (channel->free_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Freeing channel %lu/%lu resources", channel->local.id, + channel->remote.id); + + channel->free_state = libssh2_NB_state_created; + } + + /* Allow channel freeing even when the socket has lost its connection */ + if (!channel->local.close + && (session->socket_state == LIBSSH2_SOCKET_CONNECTED)) { + while ((rc = libssh2_channel_close(channel)) == PACKET_EAGAIN); + if (rc) { + channel->free_state = libssh2_NB_state_idle; + return -1; + } + } + + channel->free_state = libssh2_NB_state_idle; + + /* + * 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, + 0) >= 0) + || + (libssh2_packet_ask_ex + (session, SSH_MSG_CHANNEL_EXTENDED_DATA, &data, &data_len, 1, + channel_id, 4, 0) >= 0)) { + LIBSSH2_FREE(session, data); + } + + /* free "channel_type" */ + if (channel->channel_type) { + LIBSSH2_FREE(session, channel->channel_type); + } + + /* 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; + } + + /* + * Make sure all memory used in the state variables are free + */ + if (channel->setenv_packet) { + LIBSSH2_FREE(session, channel->setenv_packet); + } + if (channel->reqPTY_packet) { + LIBSSH2_FREE(session, channel->reqPTY_packet); + } + if (channel->reqX11_packet) { + LIBSSH2_FREE(session, channel->reqX11_packet); + } + if (channel->process_packet) { + LIBSSH2_FREE(session, channel->process_packet); + } + if (channel->write_packet) { + LIBSSH2_FREE(session, channel->write_packet); + } + + LIBSSH2_FREE(session, channel); + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_channel_window_read_ex + * + * Check the status of the read window. Returns the number of bytes which the + * remote end may send without overflowing the window limit read_avail (if + * passed) will be populated with the number of bytes actually available to be + * read window_size_initial (if passed) will be populated with the + * window_size_initial as defined by the channel_open request + */ +LIBSSH2_API unsigned long +libssh2_channel_window_read_ex(LIBSSH2_CHANNEL * channel, + unsigned long *read_avail, + unsigned long *window_size_initial) +{ + if (window_size_initial) { + *window_size_initial = channel->remote.window_size_initial; + } + + if (read_avail) { + unsigned long bytes_queued = 0; + LIBSSH2_PACKET *packet = channel->session->packets.head; + + while (packet) { + unsigned char packet_type = packet->data[0]; + + if (((packet_type == SSH_MSG_CHANNEL_DATA) + || (packet_type == SSH_MSG_CHANNEL_EXTENDED_DATA)) + && (libssh2_ntohu32(packet->data + 1) == channel->local.id)) { + bytes_queued += packet->data_len - packet->data_head; + } + + packet = packet->next; + } + + *read_avail = bytes_queued; + } + + return channel->remote.window_size; +} + +/* }}} */ + +/* {{{ libssh2_channel_window_write_ex + * + * Check the status of the write window Returns the number of bytes which may + * be safely writen on the channel without blocking window_size_initial (if + * passed) will be populated with the size of the initial window as defined by + * the channel_open request + */ +LIBSSH2_API unsigned long +libssh2_channel_window_write_ex(LIBSSH2_CHANNEL * channel, + unsigned long *window_size_initial) +{ + if (window_size_initial) { + /* For locally initiated channels this is very often 0, so it's not + * *that* useful as information goes */ + *window_size_initial = channel->local.window_size_initial; + } + + return channel->local.window_size; +} + +/* }}} */ Property changes on: libssh2/src/channel.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/libssh2_priv.h =================================================================== --- libssh2/src/libssh2_priv.h (.../tags/RELEASE_0_11_0) +++ libssh2/src/libssh2_priv.h (.../trunk) @@ -0,0 +1,1211 @@ +/* Copyright (c) 2004-2008, Sara Golemon + * 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 + +#define LIBSSH2_LIBRARY +#include "libssh2_config.h" + +#ifdef HAVE_WINDOWS_H +#include +#endif + +#ifdef HAVE_WS2TCPIP_H +#include +#endif + +#include +#include + +/* The following CPP block should really only be in session.c and + packet.c. However, AIX have #define's for 'events' and 'revents' + and we are using those names in libssh2.h, so we need to include + the AIX headers first, to make sure all code is compiled with + consistent names of these fields. While arguable the best would to + change libssh2.h to use other names, that would break backwards + compatibility. For more information, see: + http://www.mail-archive.com/libssh2-devel%40lists.sourceforge.net/msg00003.html + http://www.mail-archive.com/libssh2-devel%40lists.sourceforge.net/msg00224.html +*/ +#ifdef HAVE_POLL +# include +#else +# if defined(HAVE_SELECT) && !defined(WIN32) +# ifdef HAVE_SYS_SELECT_H +# include +# else +# include +# include +# endif +# endif +#endif + +#include "libssh2.h" +#include "libssh2_publickey.h" +#include "libssh2_sftp.h" + +/* Provide iovec / writev on WIN32 platform. */ +#ifdef WIN32 + +/* same as WSABUF */ +struct iovec { + u_long iov_len; + char *iov_base; +}; + +#define inline __inline + +static inline int writev(int sock, struct iovec *iov, int nvecs) +{ + DWORD ret; + if (WSASend(sock, (LPWSABUF)iov, nvecs, &ret, 0, NULL, NULL) == 0) { + return ret; + } + return -1; +} + +#endif /* WIN32 */ + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif + +#ifdef LIBSSH2_LIBGCRYPT +#include "libgcrypt.h" +#else +#include "openssl.h" +#endif + +#ifdef HAVE_WINSOCK2_H + +#include +#include +#include + +#ifdef _MSC_VER +/* "inline" keyword is valid only with C++ engine! */ +#define inline __inline +#endif + +/* not really usleep, but safe for the way we use it in this lib */ +static inline int usleep(int udelay) +{ + Sleep(udelay / 1000); + return 0; +} + +#endif + +/* RFC4253 section 6.1 Maximum Packet Length says: + * + * "All implementations MUST be able to process packets with + * uncompressed payload length of 32768 bytes or less and + * total packet size of 35000 bytes or less (including length, + * padding length, payload, padding, and MAC.)." + */ +#define MAX_SSH_PACKET_LEN 35000 + +#define LIBSSH2_ALLOC(session, count) session->alloc((count), &(session)->abstract) +#define LIBSSH2_REALLOC(session, ptr, count) ((ptr) ? session->realloc((ptr), (count), &(session)->abstract) : session->alloc((count), &(session)->abstract)) +#define LIBSSH2_FREE(session, ptr) session->free((ptr), &(session)->abstract) + +#define LIBSSH2_IGNORE(session, data, datalen) session->ssh_msg_ignore((session), (data), (datalen), &(session)->abstract) +#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) +#define LIBSSH2_X11_OPEN(channel, shost, sport) channel->session->x11(((channel)->session), (channel), (shost), (sport), (&(channel)->session->abstract)) + +#define LIBSSH2_CHANNEL_CLOSE(session, channel) channel->close_cb((session), &(session)->abstract, (channel), &(channel)->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; + +typedef int libssh2pack_t; + +typedef enum +{ + libssh2_NB_state_idle = 0, + libssh2_NB_state_allocated, + libssh2_NB_state_created, + libssh2_NB_state_sent, + libssh2_NB_state_sent1, + libssh2_NB_state_sent2, + libssh2_NB_state_sent3, + libssh2_NB_state_sent4, + libssh2_NB_state_sent5, + libssh2_NB_state_sent6, + libssh2_NB_state_sent7, + libssh2_NB_state_jump1, + libssh2_NB_state_jump2, + libssh2_NB_state_jump3 +} libssh2_nonblocking_states; + +typedef struct packet_require_state_t +{ + libssh2_nonblocking_states state; + time_t start; +} packet_require_state_t; + +typedef struct packet_requirev_state_t +{ + time_t start; +} packet_requirev_state_t; + +typedef struct kmdhgGPsha1kex_state_t +{ + libssh2_nonblocking_states state; + unsigned char *e_packet; + unsigned char *s_packet; + unsigned char *tmp; + unsigned char h_sig_comp[SHA_DIGEST_LENGTH]; + unsigned char c; + unsigned long e_packet_len; + unsigned long s_packet_len; + unsigned long tmp_len; + _libssh2_bn_ctx *ctx; + _libssh2_bn *x; + _libssh2_bn *e; + _libssh2_bn *f; + _libssh2_bn *k; + unsigned char *s; + unsigned char *f_value; + unsigned char *k_value; + unsigned char *h_sig; + unsigned long f_value_len; + unsigned long k_value_len; + unsigned long h_sig_len; + libssh2_sha1_ctx exchange_hash; + packet_require_state_t req_state; + libssh2_nonblocking_states burn_state; +} kmdhgGPsha1kex_state_t; + +typedef struct key_exchange_state_low_t +{ + libssh2_nonblocking_states state; + packet_require_state_t req_state; + kmdhgGPsha1kex_state_t exchange_state; + _libssh2_bn *p; /* SSH2 defined value (p_value) */ + _libssh2_bn *g; /* SSH2 defined value (2) */ + unsigned char request[13]; + unsigned char *data; + unsigned long request_len; + unsigned long data_len; +} key_exchange_state_low_t; + +typedef struct key_exchange_state_t +{ + libssh2_nonblocking_states state; + packet_require_state_t req_state; + key_exchange_state_low_t key_state_low; + unsigned char *data; + unsigned long data_len; + unsigned char *oldlocal; + unsigned long oldlocal_len; +} key_exchange_state_t; + +#define FwdNotReq "Forward not requested" + +typedef struct packet_queue_listener_state_t +{ + libssh2_nonblocking_states state; + unsigned char packet[17 + (sizeof(FwdNotReq) - 1)]; + unsigned char *host; + unsigned char *shost; + uint32_t sender_channel; + uint32_t initial_window_size; + uint32_t packet_size; + uint32_t port; + uint32_t sport; + uint32_t host_len; + uint32_t shost_len; +} packet_queue_listener_state_t; + +#define X11FwdUnAvil "X11 Forward Unavailable" + +typedef struct packet_x11_open_state_t +{ + libssh2_nonblocking_states state; + unsigned char packet[17 + (sizeof(X11FwdUnAvil) - 1)]; + unsigned char *shost; + uint32_t sender_channel; + uint32_t initial_window_size; + uint32_t packet_size; + uint32_t sport; + uint32_t shost_len; +} packet_x11_open_state_t; + +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 */ + char close, eof, extended_data_ignore_mode; +} libssh2_channel_data; + +struct _LIBSSH2_CHANNEL +{ + unsigned char *channel_type; + unsigned channel_type_len; + + /* channel's program exit status */ + int exit_status; + + libssh2_channel_data local, remote; + /* Amount of bytes to be refunded to receive window (but not yet sent) */ + unsigned long adjust_queue; + + LIBSSH2_SESSION *session; + + LIBSSH2_CHANNEL *next, *prev; + + void *abstract; + LIBSSH2_CHANNEL_CLOSE_FUNC((*close_cb)); + + /* State variables used in libssh2_channel_setenv_ex() */ + libssh2_nonblocking_states setenv_state; + unsigned char *setenv_packet; + unsigned long setenv_packet_len; + unsigned char setenv_local_channel[4]; + packet_requirev_state_t setenv_packet_requirev_state; + + /* State variables used in libssh2_channel_request_pty_ex() */ + libssh2_nonblocking_states reqPTY_state; + unsigned char *reqPTY_packet; + unsigned long reqPTY_packet_len; + unsigned char reqPTY_local_channel[4]; + packet_requirev_state_t reqPTY_packet_requirev_state; + + /* State variables used in libssh2_channel_x11_req_ex() */ + libssh2_nonblocking_states reqX11_state; + unsigned char *reqX11_packet; + unsigned long reqX11_packet_len; + unsigned char reqX11_local_channel[4]; + packet_requirev_state_t reqX11_packet_requirev_state; + + /* State variables used in libssh2_channel_process_startup() */ + libssh2_nonblocking_states process_state; + unsigned char *process_packet; + unsigned long process_packet_len; + unsigned char process_local_channel[4]; + packet_requirev_state_t process_packet_requirev_state; + + /* State variables used in libssh2_channel_flush_ex() */ + libssh2_nonblocking_states flush_state; + unsigned long flush_refund_bytes; + unsigned long flush_flush_bytes; + + /* State variables used in libssh2_channel_receive_window_adjust() */ + libssh2_nonblocking_states adjust_state; + unsigned char adjust_adjust[9]; /* packet_type(1) + channel(4) + adjustment(4) */ + + /* State variables used in libssh2_channel_read_ex() */ + libssh2_nonblocking_states read_state; + LIBSSH2_PACKET *read_packet; + LIBSSH2_PACKET *read_next; + int read_block; + int read_bytes_read; + uint32_t read_local_id; + int read_want; + int read_unlink_packet; + + /* State variables used in libssh2_channel_write_ex() */ + libssh2_nonblocking_states write_state; + unsigned char *write_packet; + unsigned char *write_s; + unsigned long write_packet_len; + unsigned long write_bufwrote; + size_t write_bufwrite; + + /* State variables used in libssh2_channel_close() */ + libssh2_nonblocking_states close_state; + unsigned char close_packet[5]; + + /* State variables used in libssh2_channel_wait_closedeof() */ + libssh2_nonblocking_states wait_eof_state; + + /* State variables used in libssh2_channel_wait_closed() */ + libssh2_nonblocking_states wait_closed_state; + + /* State variables used in libssh2_channel_free() */ + libssh2_nonblocking_states free_state; + + /* State variables used in libssh2_channel_handle_extended_data2() */ + libssh2_nonblocking_states extData2_state; +}; + +struct _LIBSSH2_CHANNEL_BRIGADE +{ + LIBSSH2_CHANNEL *head, *tail; +}; + +struct _LIBSSH2_LISTENER +{ + LIBSSH2_SESSION *session; + + char *host; + int port; + + LIBSSH2_CHANNEL *queue; + int queue_size; + int queue_maxsize; + + LIBSSH2_LISTENER *prev, *next; + + /* State variables used in libssh2_channel_forward_cancel() */ + libssh2_nonblocking_states chanFwdCncl_state; + unsigned char *chanFwdCncl_data; + size_t chanFwdCncl_data_len; +}; + +typedef struct _libssh2_endpoint_data +{ + unsigned char *banner; + + unsigned char *kexinit; + unsigned long kexinit_len; + + const LIBSSH2_CRYPT_METHOD *crypt; + void *crypt_abstract; + + const LIBSSH2_MAC_METHOD *mac; + unsigned long seqno; + void *mac_abstract; + + const LIBSSH2_COMP_METHOD *comp; + void *comp_abstract; + + /* Method Preferences -- NULL yields "load order" */ + char *crypt_prefs; + char *mac_prefs; + char *comp_prefs; + char *lang_prefs; +} libssh2_endpoint_data; + +#define PACKETBUFSIZE 4096 + +struct transportpacket +{ + /* ------------- for incoming data --------------- */ + unsigned char buf[PACKETBUFSIZE]; + unsigned char init[5]; /* first 5 bytes of the incoming data stream, + still encrypted */ + int writeidx; /* at what array index we do the next write into + the buffer */ + int readidx; /* at what array index we do the next read from + the buffer */ + int packet_length; /* the most recent packet_length as read from the + network data */ + int padding_length; /* the most recent padding_length as read from the + network data */ + int data_num; /* How much of the total package that has been read + so far. */ + int total_num; /* How much a total package is supposed to be, in + number of bytes. A full package is + packet_length + padding_length + 4 + + mac_length. */ + unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC() + area to which we write decrypted data */ + unsigned char *wptr; /* write pointer into the payload to where we + are currently writing decrypted data */ + + /* ------------- for outgoing data --------------- */ + unsigned char *outbuf; /* pointer to a LIBSSH2_ALLOC() area for the + outgoing data */ + int ototal_num; /* size of outbuf in number of bytes */ + unsigned char *odata; /* original pointer to the data we stored in + outbuf */ + unsigned long olen; /* original size of the data we stored in + outbuf */ + unsigned long osent; /* number of bytes already sent */ +}; + +struct _LIBSSH2_PUBLICKEY +{ + LIBSSH2_CHANNEL *channel; + unsigned long version; + + /* State variables used in libssh2_publickey_packet_receive() */ + libssh2_nonblocking_states receive_state; + unsigned char *receive_packet; + unsigned long receive_packet_len; + + /* State variables used in libssh2_publickey_add_ex() */ + libssh2_nonblocking_states add_state; + unsigned char *add_packet; + unsigned char *add_s; + + /* State variables used in libssh2_publickey_remove_ex() */ + libssh2_nonblocking_states remove_state; + unsigned char *remove_packet; + unsigned char *remove_s; + + /* State variables used in libssh2_publickey_list_fetch() */ + libssh2_nonblocking_states listFetch_state; + unsigned char *listFetch_s; + unsigned char listFetch_buffer[12]; + unsigned char *listFetch_data; + unsigned long listFetch_data_len; +}; + +#define SFTP_HANDLE_MAXLEN 256 /* according to spec! */ + +struct _LIBSSH2_SFTP_HANDLE +{ + LIBSSH2_SFTP *sftp; + LIBSSH2_SFTP_HANDLE *prev, *next; + + /* This is a pre-allocated buffer used for sending SFTP requests as the + whole thing might not get sent in one go. This buffer is used for read, + write, close and MUST thus be big enough to suit all these. */ + unsigned char request_packet[SFTP_HANDLE_MAXLEN + 25]; + + char handle[SFTP_HANDLE_MAXLEN]; + int handle_len; + + char handle_type; + + union _libssh2_sftp_handle_data + { + struct _libssh2_sftp_handle_file_data + { + libssh2_uint64_t offset; + } file; + struct _libssh2_sftp_handle_dir_data + { + unsigned long names_left; + void *names_packet; + char *next_name; + } dir; + } u; + + /* State variables used in libssh2_sftp_close_handle() */ + libssh2_nonblocking_states close_state; + unsigned long close_request_id; + unsigned char *close_packet; +}; + +struct _LIBSSH2_SFTP +{ + LIBSSH2_CHANNEL *channel; + + unsigned long request_id, version; + + LIBSSH2_PACKET_BRIGADE packets; + + LIBSSH2_SFTP_HANDLE *handles; + + unsigned long last_errno; + + /* Holder for partial packet, use in libssh2_sftp_packet_read() */ + unsigned char *partial_packet; /* The data */ + unsigned long partial_len; /* Desired number of bytes */ + unsigned long partial_received; /* Bytes received so far */ + + /* Time that libssh2_sftp_packet_requirev() started reading */ + time_t requirev_start; + + /* State variables used in libssh2_sftp_open_ex() */ + libssh2_nonblocking_states open_state; + unsigned char *open_packet; + ssize_t open_packet_len; + unsigned long open_request_id; + + /* State variables used in libssh2_sftp_read() */ + libssh2_nonblocking_states read_state; + unsigned char *read_packet; + unsigned long read_request_id; + size_t read_total_read; + + /* State variables used in libssh2_sftp_readdir() */ + libssh2_nonblocking_states readdir_state; + unsigned char *readdir_packet; + unsigned long readdir_request_id; + + /* State variables used in libssh2_sftp_write() */ + libssh2_nonblocking_states write_state; + unsigned char *write_packet; + unsigned long write_request_id; + + /* State variables used in libssh2_sftp_fstat_ex() */ + libssh2_nonblocking_states fstat_state; + unsigned char *fstat_packet; + unsigned long fstat_request_id; + + /* State variables used in libssh2_sftp_unlink_ex() */ + libssh2_nonblocking_states unlink_state; + unsigned char *unlink_packet; + unsigned long unlink_request_id; + + /* State variables used in libssh2_sftp_rename_ex() */ + libssh2_nonblocking_states rename_state; + unsigned char *rename_packet; + unsigned char *rename_s; + unsigned long rename_request_id; + + /* State variables used in libssh2_sftp_mkdir() */ + libssh2_nonblocking_states mkdir_state; + unsigned char *mkdir_packet; + unsigned long mkdir_request_id; + + /* State variables used in libssh2_sftp_rmdir() */ + libssh2_nonblocking_states rmdir_state; + unsigned char *rmdir_packet; + unsigned long rmdir_request_id; + + /* State variables used in libssh2_sftp_stat() */ + libssh2_nonblocking_states stat_state; + unsigned char *stat_packet; + unsigned long stat_request_id; + + /* State variables used in libssh2_sftp_symlink() */ + libssh2_nonblocking_states symlink_state; + unsigned char *symlink_packet; + unsigned long symlink_request_id; +}; + +#define LIBSSH2_SCP_RESPONSE_BUFLEN 256 + +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)); + LIBSSH2_X11_OPEN_FUNC((*x11)); + + /* Method preferences -- NULL yields "load order" */ + char *kex_prefs; + char *hostkey_prefs; + + int state; + int flags; + + /* Agreed Key Exchange Method */ + const LIBSSH2_KEX_METHOD *kex; + int burn_optimistic_kexinit:1; + + unsigned char *session_id; + unsigned long session_id_len; + + /* Server's public key */ + const 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; +#if LIBSSH2_MD5 + unsigned char server_hostkey_md5[MD5_DIGEST_LENGTH]; +#endif /* ! LIBSSH2_MD5 */ + unsigned char server_hostkey_sha1[SHA_DIGEST_LENGTH]; + + /* (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; + + LIBSSH2_LISTENER *listeners; + + /* Actual I/O socket */ + int socket_fd; + int socket_block; + int socket_state; + int socket_block_directions; + + /* Error tracking */ + char *err_msg; + unsigned long err_msglen; + int err_should_free; + int err_code; + + /* struct members for packet-level reading */ + struct transportpacket packet; +#ifdef LIBSSH2DEBUG + int showmask; /* what debug/trace messages to display */ +#endif + + /* State variables used in libssh2_banner_send() */ + libssh2_nonblocking_states banner_TxRx_state; + char banner_TxRx_banner[256]; + ssize_t banner_TxRx_total_send; + + /* State variables used in libssh2_kexinit() */ + libssh2_nonblocking_states kexinit_state; + unsigned char *kexinit_data; + size_t kexinit_data_len; + + /* State variables used in libssh2_session_startup() */ + libssh2_nonblocking_states startup_state; + unsigned char *startup_data; + unsigned long startup_data_len; + unsigned char startup_service[sizeof("ssh-userauth") + 5 - 1]; + unsigned long startup_service_length; + packet_require_state_t startup_req_state; + key_exchange_state_t startup_key_state; + + /* State variables used in libssh2_session_free() */ + libssh2_nonblocking_states free_state; + + /* State variables used in libssh2_session_disconnect_ex() */ + libssh2_nonblocking_states disconnect_state; + unsigned char *disconnect_data; + unsigned long disconnect_data_len; + + /* State variables used in libssh2_packet_read() */ + libssh2_nonblocking_states readPack_state; + int readPack_encrypted; + + /* State variables used in libssh2_userauth_list() */ + libssh2_nonblocking_states userauth_list_state; + unsigned char *userauth_list_data; + unsigned long userauth_list_data_len; + packet_requirev_state_t userauth_list_packet_requirev_state; + + /* State variables used in libssh2_userauth_password_ex() */ + libssh2_nonblocking_states userauth_pswd_state; + unsigned char *userauth_pswd_data; + unsigned char userauth_pswd_data0; + unsigned long userauth_pswd_data_len; + char *userauth_pswd_newpw; + int userauth_pswd_newpw_len; + packet_requirev_state_t userauth_pswd_packet_requirev_state; + + /* State variables used in libssh2_userauth_hostbased_fromfile_ex() */ + libssh2_nonblocking_states userauth_host_state; + unsigned char *userauth_host_data; + unsigned long userauth_host_data_len; + unsigned char *userauth_host_packet; + unsigned long userauth_host_packet_len; + unsigned char *userauth_host_method; + unsigned long userauth_host_method_len; + unsigned char *userauth_host_s; + packet_requirev_state_t userauth_host_packet_requirev_state; + + /* State variables used in libssh2_userauth_publickey_fromfile_ex() */ + libssh2_nonblocking_states userauth_pblc_state; + unsigned char *userauth_pblc_data; + unsigned long userauth_pblc_data_len; + unsigned char *userauth_pblc_packet; + unsigned long userauth_pblc_packet_len; + unsigned char *userauth_pblc_method; + unsigned long userauth_pblc_method_len; + unsigned char *userauth_pblc_s; + unsigned char *userauth_pblc_b; + packet_requirev_state_t userauth_pblc_packet_requirev_state; + + /* State variables used in llibssh2_userauth_keyboard_interactive_ex() */ + libssh2_nonblocking_states userauth_kybd_state; + unsigned char *userauth_kybd_data; + unsigned long userauth_kybd_data_len; + unsigned char *userauth_kybd_packet; + unsigned long userauth_kybd_packet_len; + unsigned int userauth_kybd_auth_name_len; + char *userauth_kybd_auth_name; + unsigned userauth_kybd_auth_instruction_len; + char *userauth_kybd_auth_instruction; + unsigned int userauth_kybd_num_prompts; + int userauth_kybd_auth_failure; + LIBSSH2_USERAUTH_KBDINT_PROMPT *userauth_kybd_prompts; + LIBSSH2_USERAUTH_KBDINT_RESPONSE *userauth_kybd_responses; + packet_requirev_state_t userauth_kybd_packet_requirev_state; + + /* State variables used in libssh2_channel_open_ex() */ + libssh2_nonblocking_states open_state; + packet_requirev_state_t open_packet_requirev_state; + LIBSSH2_CHANNEL *open_channel; + unsigned char *open_packet; + unsigned long open_packet_len; + unsigned char *open_data; + unsigned long open_data_len; + unsigned long open_local_channel; + + /* State variables used in libssh2_channel_direct_tcpip_ex() */ + libssh2_nonblocking_states direct_state; + unsigned char *direct_message; + unsigned long direct_host_len; + unsigned long direct_shost_len; + unsigned long direct_message_len; + + /* State variables used in libssh2_channel_forward_listen_ex() */ + libssh2_nonblocking_states fwdLstn_state; + unsigned char *fwdLstn_packet; + unsigned long fwdLstn_host_len; + unsigned long fwdLstn_packet_len; + packet_requirev_state_t fwdLstn_packet_requirev_state; + + /* State variables used in libssh2_publickey_init() */ + libssh2_nonblocking_states pkeyInit_state; + LIBSSH2_PUBLICKEY *pkeyInit_pkey; + LIBSSH2_CHANNEL *pkeyInit_channel; + unsigned char *pkeyInit_data; + unsigned long pkeyInit_data_len; + + /* State variables used in libssh2_packet_add() */ + libssh2_nonblocking_states packAdd_state; + LIBSSH2_PACKET *packAdd_packet; + LIBSSH2_CHANNEL *packAdd_channel; + unsigned long packAdd_data_head; + key_exchange_state_t packAdd_key_state; + packet_queue_listener_state_t packAdd_Qlstn_state; + packet_x11_open_state_t packAdd_x11open_state; + + /* State variables used in fullpacket() */ + libssh2_nonblocking_states fullpacket_state; + int fullpacket_macstate; + int fullpacket_payload_len; + libssh2pack_t fullpacket_packet_type; + + /* State variables used in libssh2_sftp_init() */ + libssh2_nonblocking_states sftpInit_state; + LIBSSH2_SFTP *sftpInit_sftp; + LIBSSH2_CHANNEL *sftpInit_channel; + unsigned char sftpInit_buffer[9]; /* sftp_header(5){excludes request_id} + version_id(4) */ + + /* State variables used in libssh2_scp_recv() */ + libssh2_nonblocking_states scpRecv_state; + unsigned char *scpRecv_command; + unsigned long scpRecv_command_len; + unsigned char scpRecv_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; + unsigned long scpRecv_response_len; + long scpRecv_mode; +#if defined(HAVE_LONGLONG) && defined(strtoll) + /* we have the type and we can parse such numbers */ + long long scpRecv_size; +#define scpsize_strtol strtoll +#else + long scpRecv_size; +#define scpsize_strtol strtol +#endif + long scpRecv_mtime; + long scpRecv_atime; + char *scpRecv_err_msg; + long scpRecv_err_len; + LIBSSH2_CHANNEL *scpRecv_channel; + + /* State variables used in libssh2_scp_send_ex() */ + libssh2_nonblocking_states scpSend_state; + unsigned char *scpSend_command; + unsigned long scpSend_command_len; + unsigned char scpSend_response[LIBSSH2_SCP_RESPONSE_BUFLEN]; + unsigned long scpSend_response_len; + char *scpSend_err_msg; + long scpSend_err_len; + LIBSSH2_CHANNEL *scpSend_channel; +}; + +/* session.state bits */ +#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001 +#define LIBSSH2_STATE_NEWKEYS 0x00000002 +#define LIBSSH2_STATE_AUTHENTICATED 0x00000004 +#define LIBSSH2_STATE_KEX_ACTIVE 0x00000008 + +/* session.flag helpers */ +#ifdef MSG_NOSIGNAL +#define LIBSSH2_SOCKET_SEND_FLAGS(session) (((session)->flags & LIBSSH2_FLAG_SIGPIPE) ? 0 : MSG_NOSIGNAL) +#define LIBSSH2_SOCKET_RECV_FLAGS(session) (((session)->flags & LIBSSH2_FLAG_SIGPIPE) ? 0 : MSG_NOSIGNAL) +#else +/* If MSG_NOSIGNAL isn't defined we're SOL on blocking SIGPIPE */ +#define LIBSSH2_SOCKET_SEND_FLAGS(session) 0 +#define LIBSSH2_SOCKET_RECV_FLAGS(session) 0 +#endif + +/* libssh2 extensible ssh api, ultimately I'd like to allow loading additional methods via .so/.dll */ + +struct _LIBSSH2_KEX_METHOD +{ + const char *name; + + /* Key exchange, populates session->* and returns 0 on success, non-0 on error */ + int (*exchange_keys) (LIBSSH2_SESSION * session, + key_exchange_state_low_t * key_state); + + long flags; +}; + +struct _LIBSSH2_HOSTKEY_METHOD +{ + const char *name; + unsigned long hash_len; + + int (*init) (LIBSSH2_SESSION * session, const unsigned char *hostkey_data, + unsigned long hostkey_data_len, void **abstract); + int (*initPEM) (LIBSSH2_SESSION * session, const char *privkeyfile, + unsigned const char *passphrase, void **abstract); + int (*sig_verify) (LIBSSH2_SESSION * session, const unsigned char *sig, + unsigned long sig_len, const unsigned char *m, + unsigned long m_len, void **abstract); + int (*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); +}; + +struct _LIBSSH2_CRYPT_METHOD +{ + const 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, + const LIBSSH2_CRYPT_METHOD * method, 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); + + _libssh2_cipher_type(algo); +}; + +struct _LIBSSH2_COMP_METHOD +{ + const 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 +{ + const char *name; + + /* The length of a given MAC packet */ + int mac_len; + + /* integrity key length */ + int key_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_DBG_TRANS 1 +#define LIBSSH2_DBG_KEX 2 +#define LIBSSH2_DBG_AUTH 3 +#define LIBSSH2_DBG_CONN 4 +#define LIBSSH2_DBG_SCP 5 +#define LIBSSH2_DBG_SFTP 6 +#define LIBSSH2_DBG_ERROR 7 +#define LIBSSH2_DBG_PUBLICKEY 8 +#ifdef LIBSSH2DEBUG +void _libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, + ...); +#else +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +/* C99 style */ +#define _libssh2_debug(x,y,z, __VA_ARGS__) do {} while (0) +#elif defined(__GNUC__) +/* GNU style */ +#define _libssh2_debug(x,y,z,...) do {} while (0) +#else +/* no gcc and not C99, do static and hopefully inline */ +static inline void +_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...) +{ +} +#endif +#endif + +#ifdef LIBSSH2DEBUG +#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 = (char *)errmsg; \ + session->err_msglen = strlen(errmsg); \ + session->err_should_free = should_free; \ + session->err_code = errcode; \ + _libssh2_debug(session, LIBSSH2_DBG_ERROR, "%d - %s", session->err_code, session->err_msg); \ +} + +#else /* ! LIBSSH2DEBUG */ + +#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 = (char *)errmsg; \ + session->err_msglen = strlen(errmsg); \ + session->err_should_free = should_free; \ + session->err_code = errcode; \ +} + +#endif /* ! LIBSSH2DEBUG */ + + +#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 +/* "keyboard-interactive" method */ +#define SSH_MSG_USERAUTH_INFO_REQUEST 60 +#define SSH_MSG_USERAUTH_INFO_RESPONSE 61 + +/* 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); +libssh2_uint64_t libssh2_ntohu64(const unsigned char *buf); +void libssh2_htonu32(unsigned char *buf, unsigned long val); +void libssh2_htonu64(unsigned char *buf, libssh2_uint64_t val); + +#define LIBSSH2_READ_TIMEOUT 60 /* generic timeout in seconds used when + waiting for more data to arrive */ +int libssh2_waitsocket(LIBSSH2_SESSION * session, long seconds); + + +/* CAUTION: some of these error codes are returned in the public API and is + there known with other #defined names from the public header file. They + should not be changed. */ + +#define PACKET_TIMEOUT -7 +#define PACKET_BADUSE -6 +#define PACKET_COMPRESS -5 +#define PACKET_TOOBIG -4 +#define PACKET_ENOMEM -3 +#define PACKET_EAGAIN LIBSSH2_ERROR_EAGAIN +#define PACKET_FAIL -1 +#define PACKET_NONE 0 + +libssh2pack_t libssh2_packet_read(LIBSSH2_SESSION * session); + +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); + +int libssh2_packet_askv_ex(LIBSSH2_SESSION * session, + const unsigned char *packet_types, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len, int poll_socket); +int 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, + packet_require_state_t * state); +int libssh2_packet_requirev_ex(LIBSSH2_SESSION * session, + const unsigned char *packet_types, + unsigned char **data, unsigned long *data_len, + unsigned long match_ofs, + const unsigned char *match_buf, + unsigned long match_len, + packet_requirev_state_t * state); +int libssh2_packet_burn(LIBSSH2_SESSION * session, + libssh2_nonblocking_states * state); +int libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long data_len); +int libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data, + size_t datalen, int macstate); +int libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange, + key_exchange_state_t * state); +unsigned long libssh2_channel_nextid(LIBSSH2_SESSION * session); +LIBSSH2_CHANNEL *libssh2_channel_locate(LIBSSH2_SESSION * session, + unsigned long channel_id); +unsigned long libssh2_channel_packet_data_len(LIBSSH2_CHANNEL * channel, + int stream_id); + +/* this is the lib-internal set blocking function */ +int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking); + +/* Let crypt.c/hostkey.c/comp.c/mac.c expose their method structs */ +const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void); +const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void); +const LIBSSH2_COMP_METHOD **libssh2_comp_methods(void); +const 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 + +/* pem.c */ +int _libssh2_pem_parse(LIBSSH2_SESSION * session, + const char *headerbegin, + const char *headerend, + FILE * fp, unsigned char **data, unsigned int *datalen); +int _libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen); +int _libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen, + unsigned char **i, unsigned int *ilen); + +#endif /* LIBSSH2_H */ Property changes on: libssh2/src/libssh2_priv.h ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/sftp.c =================================================================== --- libssh2/src/sftp.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/sftp.c (.../trunk) @@ -0,0 +1,2358 @@ +/* Copyright (c) 2004-2008, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include "libssh2_sftp.h" + +/* Note: Version 6 was documented at the time of writing + * However it was marked as "DO NOT IMPLEMENT" due to pending changes + * + * This release of libssh2 implements Version 5 with automatic downgrade + * based on server's declaration + */ + +/* SFTP packet types */ +#define SSH_FXP_INIT 1 +#define SSH_FXP_VERSION 2 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 +#define SSH_FXP_READLINK 19 +#define SSH_FXP_SYMLINK 20 +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 +#define SSH_FXP_EXTENDED 200 +#define SSH_FXP_EXTENDED_REPLY 201 + +#define LIBSSH2_SFTP_HANDLE_FILE 0 +#define LIBSSH2_SFTP_HANDLE_DIR 1 + +/* S_IFREG */ +#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000 +/* S_IFDIR */ +#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000 + +/* {{{ sftp_packet_add + * Add a packet to the SFTP packet brigade + */ +static int +sftp_packet_add(LIBSSH2_SFTP * sftp, unsigned char *data, + unsigned long data_len) +{ + LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_PACKET *packet; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Received packet %d (len %d)", + (int) data[0], data_len); + packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PACKET)); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate datablock for SFTP packet", 0); + return -1; + } + memset(packet, 0, sizeof(LIBSSH2_PACKET)); + + packet->data = data; + packet->data_len = data_len; + packet->data_head = 5; + packet->brigade = &sftp->packets; + packet->next = NULL; + packet->prev = sftp->packets.tail; + if (packet->prev) { + packet->prev->next = packet; + } else { + sftp->packets.head = packet; + } + sftp->packets.tail = packet; + + return 0; +} + +/* }}} */ + +/* {{{ sftp_packet_read + * Frame an SFTP packet off the channel + */ +static int +sftp_packet_read(LIBSSH2_SFTP * sftp) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char buffer[4]; /* To store the packet length */ + unsigned char *packet; + unsigned long packet_len, packet_received; + ssize_t bytes_received; + int rc; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Waiting for packet"); + + + /* If there was a previous partial, start using it */ + if (sftp->partial_packet) { + + packet = sftp->partial_packet; + packet_len = sftp->partial_len; + packet_received = sftp->partial_received; + sftp->partial_packet = NULL; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "partial read cont, len: %lu", packet_len); + } + else { + rc = libssh2_channel_read_ex(channel, 0, (char *) buffer, 4); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (4 != rc) { + /* TODO: this is stupid since we can in fact get 1-3 bytes in a + legitimate working case as well if the connection happens to be + super slow or something */ + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for FXP packet", 0); + return -1; + } + + packet_len = libssh2_ntohu32(buffer); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Data begin - Packet Length: %lu", packet_len); + if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED, + "SFTP packet too large", 0); + return -1; + } + + packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate SFTP packet", 0); + return -1; + } + + packet_received = 0; + } + + /* Read as much of the packet as we can */ + while (packet_len > packet_received) { + bytes_received = + libssh2_channel_read_ex(channel, 0, + (char *) packet + packet_received, + packet_len - packet_received); + + if (bytes_received == PACKET_EAGAIN) { + /* + * We received EAGAIN, save what we have and + * return to EAGAIN to the caller + */ + sftp->partial_packet = packet; + sftp->partial_len = packet_len; + sftp->partial_received = packet_received; + packet = NULL; + + return PACKET_EAGAIN; + } + else if (bytes_received < 0) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Receive error waiting for SFTP packet", 0); + LIBSSH2_FREE(session, packet); + return -1; + } + packet_received += bytes_received; + } + + if (sftp_packet_add(sftp, packet, packet_len)) { + LIBSSH2_FREE(session, packet); + return -1; + } + + return packet[0]; +} + +/* }}} */ + +/* {{{ sftp_packet_ask + * A la libssh2_packet_ask() + */ +static int +sftp_packet_ask(LIBSSH2_SFTP * sftp, unsigned char packet_type, + unsigned long request_id, unsigned char **data, + unsigned long *data_len, int poll_channel) +{ + LIBSSH2_SESSION *session = sftp->channel->session; + LIBSSH2_PACKET *packet = sftp->packets.head; + unsigned char match_buf[5]; + int match_len = 5; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Asking for %d packet", + (int) packet_type); + if (poll_channel) { + int ret = sftp_packet_read(sftp); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (ret < 0) { + return -1; + } + } + + match_buf[0] = packet_type; + if (packet_type == SSH_FXP_VERSION) { + /* Special consideration when matching VERSION packet */ + match_len = 1; + } else { + libssh2_htonu32(match_buf + 1, request_id); + } + + while (packet) { + if (strncmp((char *) packet->data, (char *) match_buf, match_len) == 0) { + *data = packet->data; + *data_len = packet->data_len; + + if (packet->prev) { + packet->prev->next = packet->next; + } else { + sftp->packets.head = packet->next; + } + + if (packet->next) { + packet->next->prev = packet->prev; + } else { + sftp->packets.tail = packet->prev; + } + + LIBSSH2_FREE(session, packet); + + return 0; + } + packet = packet->next; + } + return -1; +} + +/* }}} */ + +/* {{{ sftp_packet_require + * A la libssh2_packet_require + */ +static int +sftp_packet_require(LIBSSH2_SFTP * sftp, unsigned char packet_type, + unsigned long request_id, unsigned char **data, + unsigned long *data_len) +{ + LIBSSH2_SESSION *session = sftp->channel->session; + int ret; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Requiring %d packet", + (int) packet_type); + + if (sftp_packet_ask(sftp, packet_type, request_id, data, + data_len, 0) == 0) { + /* The right packet was available in the packet brigade */ + return 0; + } + + while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + ret = sftp_packet_read(sftp); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (ret <= 0) { + return -1; + } + + if (packet_type == ret) { + /* Be lazy, let packet_ask pull it out of the brigade */ + return sftp_packet_ask(sftp, packet_type, request_id, data, + data_len, 0); + } + } + + /* Only reached if the socket died */ + return -1; +} + +/* }}} */ + +/* {{{ sftp_packet_requirev + * Require one of N possible reponses + */ +static int +sftp_packet_requirev(LIBSSH2_SFTP * sftp, int num_valid_responses, + const unsigned char *valid_responses, + unsigned long request_id, unsigned char **data, + unsigned long *data_len) +{ + int i; + int ret; + + /* If no timeout is active, start a new one */ + if (sftp->requirev_start == 0) { + sftp->requirev_start = time(NULL); + } + + while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) { + for(i = 0; i < num_valid_responses; i++) { + if (sftp_packet_ask(sftp, valid_responses[i], request_id, + data, data_len, 0) == 0) { + /* + * Set to zero before all returns to say + * the timeout is not active + */ + sftp->requirev_start = 0; + return 0; + } + } + + ret = sftp_packet_read(sftp); + if ((ret < 0) && (ret != PACKET_EAGAIN)) { + sftp->requirev_start = 0; + return -1; + } else if (ret <= 0) { + /* prevent busy-looping */ + long left = + LIBSSH2_READ_TIMEOUT - (time(NULL) - sftp->requirev_start); + + if (left <= 0) { + sftp->requirev_start = 0; + return PACKET_TIMEOUT; + } else if (sftp->channel->session->socket_block + && (libssh2_waitsocket(sftp->channel->session, left) <= + 0)) { + sftp->requirev_start = 0; + return PACKET_TIMEOUT; + } else if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + } + + sftp->requirev_start = 0; + return -1; +} + +/* }}} */ + +/* {{{ libssh2_sftp_attrsize + * Size that attr will occupy when turned into a bin struct + */ +static int +libssh2_sftp_attrsize(const LIBSSH2_SFTP_ATTRIBUTES * attrs) +{ + int attrsize = 4; /* flags(4) */ + + if (!attrs) { + return attrsize; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) + attrsize += 8; + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) + attrsize += 8; + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) + attrsize += 4; + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) + attrsize += 8; /* atime + mtime as u32 */ + + return attrsize; +} + +/* }}} */ + +/* {{{ libssh2_sftp_attr2bin + * Populate attributes into an SFTP block + */ +static int +libssh2_sftp_attr2bin(unsigned char *p, const LIBSSH2_SFTP_ATTRIBUTES * attrs) +{ + unsigned char *s = p; + unsigned long flag_mask = + LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID | + LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME; + + /* TODO: When we add SFTP4+ functionality flag_mask can get additional bits */ + + if (!attrs) { + libssh2_htonu32(s, 0); + return 4; + } + + libssh2_htonu32(s, attrs->flags & flag_mask); + s += 4; + + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + libssh2_htonu64(s, attrs->filesize); + s += 8; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + libssh2_htonu32(s, attrs->uid); + s += 4; + libssh2_htonu32(s, attrs->gid); + s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + libssh2_htonu32(s, attrs->permissions); + s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + libssh2_htonu32(s, attrs->atime); + s += 4; + libssh2_htonu32(s, attrs->mtime); + s += 4; + } + + return (s - p); +} + +/* }}} */ + +/* {{{ libssh2_sftp_bin2attr + */ +static int +libssh2_sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES * attrs, const unsigned char *p) +{ + const unsigned char *s = p; + + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + attrs->flags = libssh2_ntohu32(s); + s += 4; + + if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) { + attrs->filesize = libssh2_ntohu64(s); + s += 8; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) { + attrs->uid = libssh2_ntohu32(s); + s += 4; + attrs->gid = libssh2_ntohu32(s); + s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) { + attrs->permissions = libssh2_ntohu32(s); + s += 4; + } + + if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) { + attrs->atime = libssh2_ntohu32(s); + s += 4; + attrs->mtime = libssh2_ntohu32(s); + s += 4; + } + + return (s - p); +} + +/* }}} */ + +/* ************ + * SFTP API * + ************ */ + +LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor); + +/* {{{ libssh2_sftp_dtor + * Shutdown an SFTP stream when the channel closes + */ +LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor) +{ + LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP *) (*channel_abstract); + + (void) session_abstract; + (void) channel; + + /* Loop through handles closing them */ + while (sftp->handles) { + libssh2_sftp_close_handle(sftp->handles); + } + + /* Free the partial packet storage for sftp_packet_read */ + if (sftp->partial_packet) { + LIBSSH2_FREE(session, sftp->partial_packet); + } + + /* Free the packet storage for _libssh2_sftp_packet_readdir */ + if (sftp->readdir_packet) { + LIBSSH2_FREE(session, sftp->readdir_packet); + } + + LIBSSH2_FREE(session, sftp); +} + +/* }}} */ + +/* {{{ libssh2_sftp_init + * Startup an SFTP session + * + * NOTE: Will block in a busy loop on error. This has to be done, + * otherwise the blocking error code would erase the true + * cause of the error. + */ +LIBSSH2_API LIBSSH2_SFTP * +libssh2_sftp_init(LIBSSH2_SESSION * session) +{ + unsigned char *data, *s; + unsigned long data_len; + int rc; + + if (session->sftpInit_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Initializing SFTP subsystem"); + + session->sftpInit_sftp = NULL; + + session->sftpInit_state = libssh2_NB_state_created; + } + + if (session->sftpInit_state == libssh2_NB_state_created) { + session->sftpInit_channel = + libssh2_channel_open_ex(session, "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); + if (!session->sftpInit_channel) { + if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block starting up channel", 0); + return NULL; + } else if (libssh2_session_last_errno(session) != + LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, + "Unable to startup channel", 0); + session->sftpInit_state = libssh2_NB_state_idle; + return NULL; + } + } + + session->sftpInit_state = libssh2_NB_state_sent; + } + + if (session->sftpInit_state == libssh2_NB_state_sent) { + rc = libssh2_channel_process_startup(session->sftpInit_channel, + "subsystem", + sizeof("subsystem") - 1, "sftp", + strlen("sftp")); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block to request SFTP subsystem", 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, + "Unable to request SFTP subsystem", 0); + goto sftp_init_error; + } + + session->sftpInit_state = libssh2_NB_state_sent1; + } + + if (session->sftpInit_state == libssh2_NB_state_sent1) { + rc = libssh2_channel_handle_extended_data2(session->sftpInit_channel, + LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block requesting handle extended data", 0); + return NULL; + } + + session->sftpInit_sftp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP)); + if (!session->sftpInit_sftp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a new SFTP structure", 0); + goto sftp_init_error; + } + memset(session->sftpInit_sftp, 0, sizeof(LIBSSH2_SFTP)); + session->sftpInit_sftp->channel = session->sftpInit_channel; + session->sftpInit_sftp->request_id = 0; + + libssh2_htonu32(session->sftpInit_buffer, 5); + session->sftpInit_buffer[4] = SSH_FXP_INIT; + libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION); + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Sending FXP_INIT packet advertising version %d support", + (int) LIBSSH2_SFTP_VERSION); + + session->sftpInit_state = libssh2_NB_state_sent2; + } + + if (session->sftpInit_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_write_ex(session->sftpInit_channel, 0, + (char *) session->sftpInit_buffer, 9); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending SSH_FXP_INIT", 0); + return NULL; + } else if (9 != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send SSH_FXP_INIT", 0); + goto sftp_init_error; + } + + session->sftpInit_state = libssh2_NB_state_sent3; + } + + /* For initiallization we are requiring blocking, probably reasonable */ + rc = sftp_packet_require(session->sftpInit_sftp, SSH_FXP_VERSION, + 0, &data, &data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for response from SFTP subsystem", + 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for response from SFTP subsystem", 0); + goto sftp_init_error; + } + if (data_len < 5) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "Invalid SSH_FXP_VERSION response", 0); + goto sftp_init_error; + } + + s = data + 1; + session->sftpInit_sftp->version = libssh2_ntohu32(s); + s += 4; + if (session->sftpInit_sftp->version > LIBSSH2_SFTP_VERSION) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Truncating remote SFTP version from %lu", + session->sftpInit_sftp->version); + session->sftpInit_sftp->version = LIBSSH2_SFTP_VERSION; + } + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Enabling SFTP version %lu compatability", + session->sftpInit_sftp->version); + while (s < (data + data_len)) { + unsigned char *extension_name, *extension_data; + unsigned long extname_len, extdata_len; + + extname_len = libssh2_ntohu32(s); + s += 4; + extension_name = s; + s += extname_len; + + extdata_len = libssh2_ntohu32(s); + s += 4; + extension_data = s; + s += extdata_len; + + /* TODO: Actually process extensions */ + } + LIBSSH2_FREE(session, data); + + /* Make sure that when the channel gets closed, the SFTP service is shut down too */ + session->sftpInit_sftp->channel->abstract = session->sftpInit_sftp; + session->sftpInit_sftp->channel->close_cb = libssh2_sftp_dtor; + + session->sftpInit_state = libssh2_NB_state_idle; + return session->sftpInit_sftp; + + sftp_init_error: + while (libssh2_channel_free(session->sftpInit_channel) == PACKET_EAGAIN); + session->sftpInit_channel = NULL; + if (session->sftpInit_sftp) { + LIBSSH2_FREE(session, session->sftpInit_sftp); + session->sftpInit_sftp = NULL; + } + session->sftpInit_state = libssh2_NB_state_idle; + return NULL; +} + +/* }}} */ + +/* {{{ libssh2_sftp_shutdown + * Shutsdown the SFTP subsystem + */ +LIBSSH2_API int +libssh2_sftp_shutdown(LIBSSH2_SFTP * sftp) +{ + /* + * Make sure all memory used in the state variables are free + */ + if (sftp->partial_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->partial_packet); + sftp->partial_packet = NULL; + } + if (sftp->open_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->open_packet); + sftp->open_packet = NULL; + } + if (sftp->readdir_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->readdir_packet); + sftp->readdir_packet = NULL; + } + if (sftp->write_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->write_packet); + sftp->write_packet = NULL; + } + if (sftp->fstat_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->fstat_packet); + sftp->fstat_packet = NULL; + } + if (sftp->unlink_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->unlink_packet); + sftp->unlink_packet = NULL; + } + if (sftp->rename_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->rename_packet); + sftp->rename_packet = NULL; + } + if (sftp->mkdir_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->mkdir_packet); + sftp->mkdir_packet = NULL; + } + if (sftp->rmdir_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->rmdir_packet); + sftp->rmdir_packet = NULL; + } + if (sftp->stat_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->stat_packet); + sftp->stat_packet = NULL; + } + if (sftp->symlink_packet) { + LIBSSH2_FREE(sftp->channel->session, sftp->symlink_packet); + sftp->symlink_packet = NULL; + } + + return libssh2_channel_free(sftp->channel); +} + +/* }}} */ + +/* ******************************* + * SFTP File and Directory Ops * + ******************************* */ + +/* {{{ libssh2_sftp_open_ex + */ +LIBSSH2_API LIBSSH2_SFTP_HANDLE * +libssh2_sftp_open_ex(LIBSSH2_SFTP * sftp, const char *filename, + unsigned int filename_len, unsigned long flags, long mode, + int open_type) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_HANDLE *fp; + LIBSSH2_SFTP_ATTRIBUTES attrs = { + LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 + }; + unsigned long data_len; + unsigned char *data, *s; + static const unsigned char fopen_responses[2] = + { SSH_FXP_HANDLE, SSH_FXP_STATUS }; + int rc; + + if (sftp->open_state == libssh2_NB_state_idle) { + /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) + + flags(4) */ + sftp->open_packet_len = filename_len + 13 + + ((open_type == + LIBSSH2_SFTP_OPENFILE) ? (4 + + libssh2_sftp_attrsize(&attrs)) : 0); + + s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len); + if (!sftp->open_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_OPEN or " + "FXP_OPENDIR packet", + 0); + return NULL; + } + /* Filetype in SFTP 3 and earlier */ + attrs.permissions = mode | + ((open_type == + LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE : + LIBSSH2_SFTP_ATTR_PFILETYPE_DIR); + + libssh2_htonu32(s, sftp->open_packet_len - 4); + s += 4; + *(s++) = + (open_type == + LIBSSH2_SFTP_OPENFILE) ? SSH_FXP_OPEN : SSH_FXP_OPENDIR; + sftp->open_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->open_request_id); + s += 4; + libssh2_htonu32(s, filename_len); + s += 4; + memcpy(s, filename, filename_len); + s += filename_len; + if (open_type == LIBSSH2_SFTP_OPENFILE) { + libssh2_htonu32(s, flags); + s += 4; + s += libssh2_sftp_attr2bin(s, &attrs); + } + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Sending %s open request", + (open_type == + LIBSSH2_SFTP_OPENFILE) ? "file" : "directory"); + + sftp->open_state = libssh2_NB_state_created; + } + + if (sftp->open_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) sftp->open_packet, + sftp->open_packet_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending FXP_OPEN or FXP_OPENDIR command", + 0); + return NULL; + } + else if (sftp->open_packet_len != rc) { + /* TODO: partial writes should be fine too and is not a sign of + an error when in non-blocking mode! */ + + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_OPEN or FXP_OPENDIR command", 0); + LIBSSH2_FREE(session, sftp->open_packet); + sftp->open_packet = NULL; + sftp->open_state = libssh2_NB_state_idle; + return NULL; + } + LIBSSH2_FREE(session, sftp->open_packet); + sftp->open_packet = NULL; + + sftp->open_state = libssh2_NB_state_sent; + } + + if (sftp->open_state == libssh2_NB_state_sent) { + rc = sftp_packet_requirev(sftp, 2, fopen_responses, + sftp->open_request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for status message", 0); + return NULL; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->open_state = libssh2_NB_state_idle; + return NULL; + } + } + + sftp->open_state = libssh2_NB_state_idle; + + /* OPEN can basically get STATUS or HANDLE back, where HANDLE implies a + fine response while STATUS means error. It seems though that at times + we get an SSH_FX_OK back in a STATUS, followed the "real" HANDLE so + we need to properly deal with that. */ + if (data[0] == SSH_FXP_STATUS) { + int badness = 1; + sftp->last_errno = libssh2_ntohu32(data + 5); + + if(LIBSSH2_FX_OK == sftp->last_errno) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "got HANDLE FXOK!"); + + /* silly situation, but check for a HANDLE */ + rc = sftp_packet_require(sftp, SSH_FXP_HANDLE, + sftp->open_request_id, &data, &data_len); + if(rc == PACKET_EAGAIN) { + /* go back to sent state and wait for something else */ + sftp->open_state = libssh2_NB_state_sent; + return NULL; + } + else if(!rc) + /* we got the handle so this is not a bad situation */ + badness = 0; + } + + if(badness) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "Failed opening remote file", 0); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "got FXP_STATUS %d", + sftp->last_errno); + LIBSSH2_FREE(session, data); + return NULL; + } + } + + fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE)); + if (!fp) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate new SFTP handle structure", 0); + LIBSSH2_FREE(session, data); + return NULL; + } + memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE)); + fp->handle_type = + (open_type == + LIBSSH2_SFTP_OPENFILE) ? LIBSSH2_SFTP_HANDLE_FILE : + LIBSSH2_SFTP_HANDLE_DIR; + + fp->handle_len = libssh2_ntohu32(data + 5); + if (fp->handle_len > SFTP_HANDLE_MAXLEN) { + /* SFTP doesn't allow handles longer than 256 characters */ + fp->handle_len = SFTP_HANDLE_MAXLEN; + } + + memcpy(fp->handle, data + 9, fp->handle_len); + LIBSSH2_FREE(session, data); + + /* Link the file and the sftp session together */ + fp->next = sftp->handles; + if (fp->next) { + fp->next->prev = fp; + } + fp->sftp = sftp; + + fp->u.file.offset = 0; + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Open command successful"); + return fp; +} + +/* }}} */ + +/* {{{ libssh2_sftp_read + * Read from an SFTP file handle + */ +LIBSSH2_API ssize_t +libssh2_sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer, + size_t buffer_maxlen) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, request_id = 0; + /* 25 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + + offset(8) + length(4) */ + ssize_t packet_len = handle->handle_len + 25; + unsigned char *packet, *s, *data; + static const unsigned char read_responses[2] = + { SSH_FXP_DATA, SSH_FXP_STATUS }; + size_t bytes_read = 0; + size_t bytes_requested = 0; + size_t total_read = 0; + int retcode; + + if (sftp->read_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Reading %lu bytes from SFTP handle", + (unsigned long) buffer_maxlen); + packet = handle->request_packet; + sftp->read_state = libssh2_NB_state_allocated; + } else { + packet = sftp->read_packet; + request_id = sftp->read_request_id; + total_read = sftp->read_total_read; + } + + while (total_read < buffer_maxlen) { + s = packet; + /* + * If buffer_maxlen bytes will be requested, server may return all + * with one packet. But libssh2 have packet length limit. + * So we request data by pieces. + */ + bytes_requested = buffer_maxlen - total_read; + /* 10 = packet_type(1) + request_id(4) + data_length(4) + + end_of_line_flag(1) + + 10 is changed to 13 below simple because it seems there's a + "GlobalScape" SFTP server that responds with a slightly too big + buffer at times and we can apparently compensate for that by doing + this trick. + + Further details on this issue: + + https://sourceforge.net/mailarchive/forum.php?thread_name=9c3275a90811261517v6c0b1da2u918cc1b8370abf83%40mail.gmail.com&forum_name=libssh2-devel + + http://forums.globalscape.com/tm.aspx?m=15249 + + */ + if (bytes_requested > LIBSSH2_SFTP_PACKET_MAXLEN - 13) { + bytes_requested = LIBSSH2_SFTP_PACKET_MAXLEN - 13; + } +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Requesting %lu bytes from SFTP handle", + (unsigned long) bytes_requested); +#endif + + if (sftp->read_state == libssh2_NB_state_allocated) { + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_READ; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; + + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; + + libssh2_htonu64(s, handle->u.file.offset); + s += 8; + + libssh2_htonu32(s, bytes_requested); + s += 4; + + sftp->read_state = libssh2_NB_state_created; + } + + if (sftp->read_state == libssh2_NB_state_created) { + retcode = + libssh2_channel_write_ex(channel, 0, (char *) packet, + packet_len); + if (retcode == PACKET_EAGAIN) { + sftp->read_packet = packet; + sftp->read_request_id = request_id; + sftp->read_total_read = total_read; + return PACKET_EAGAIN; + } else if (packet_len != retcode) { + /* TODO: a partial write is not a critical error when in + non-blocking mode! */ + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_READ command", 0); + sftp->read_packet = NULL; + sftp->read_state = libssh2_NB_state_idle; + return -1; + } + sftp->read_packet = packet; + sftp->read_request_id = request_id; + sftp->read_total_read = total_read; + sftp->read_state = libssh2_NB_state_sent; + } + + if (sftp->read_state == libssh2_NB_state_sent) { + retcode = + sftp_packet_requirev(sftp, 2, read_responses, + request_id, &data, &data_len); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (retcode) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->read_packet = NULL; + sftp->read_state = libssh2_NB_state_idle; + return -1; + } + + sftp->read_state = libssh2_NB_state_sent1; + } + + switch (data[0]) { + case SSH_FXP_STATUS: + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + sftp->read_packet = NULL; + sftp->read_state = libssh2_NB_state_idle; + + if (retcode == LIBSSH2_FX_EOF) { + return total_read; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + return -1; + } + + case SSH_FXP_DATA: + bytes_read = libssh2_ntohu32(data + 5); + if (bytes_read > (data_len - 9)) { + sftp->read_packet = NULL; + sftp->read_state = libssh2_NB_state_idle; + return -1; + } +#ifdef LIBSSH2_DEBUG_SFTP + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu bytes returned", + (unsigned long) bytes_read); +#endif + memcpy(buffer + total_read, data + 9, bytes_read); + handle->u.file.offset += bytes_read; + total_read += bytes_read; + LIBSSH2_FREE(session, data); + /* + * Set the state back to allocated, so a new one will be + * created to either request more data or get EOF + */ + sftp->read_state = libssh2_NB_state_allocated; + } + } + + sftp->read_packet = NULL; + sftp->read_state = libssh2_NB_state_idle; + return total_read; +} + +/* }}} */ + +/* {{{ libssh2_sftp_readdir + * Read from an SFTP directory handle + */ +LIBSSH2_API int +libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE * handle, char *buffer, + size_t buffer_maxlen, char *longentry, + size_t longentry_maxlen, + LIBSSH2_SFTP_ATTRIBUTES * attrs) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs_dummy; + unsigned long data_len, filename_len, longentry_len, num_names; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + ssize_t packet_len = handle->handle_len + 13; + unsigned char *s, *data; + unsigned char read_responses[2] = { SSH_FXP_NAME, SSH_FXP_STATUS }; + int retcode; + + if (sftp->readdir_state == libssh2_NB_state_idle) { + if (handle->u.dir.names_left) { + /* + * A prior request returned more than one directory entry, + * feed it back from the buffer + */ + unsigned char *s = (unsigned char *) handle->u.dir.next_name; + unsigned long real_filename_len = libssh2_ntohu32(s); + + filename_len = real_filename_len; + s += 4; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, s, filename_len); + s += real_filename_len; + + /* The filename is not null terminated, make it so if possible */ + if (filename_len < buffer_maxlen) { + buffer[filename_len] = '\0'; + } + + if ((longentry == NULL) || (longentry_maxlen == 0)) { + /* Skip longname */ + s += 4 + libssh2_ntohu32(s); + } else { + unsigned long real_longentry_len = libssh2_ntohu32(s); + + longentry_len = real_longentry_len; + s += 4; + if (longentry_len > longentry_maxlen) { + longentry_len = longentry_maxlen; + } + memcpy(longentry, s, longentry_len); + s += real_longentry_len; + + /* The longentry is not null terminated, make it so if possible */ + if (longentry_len < longentry_maxlen) { + longentry[longentry_len] = '\0'; + } + } + + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + } + s += libssh2_sftp_bin2attr(attrs ? attrs : &attrs_dummy, s); + + handle->u.dir.next_name = (char *) s; + if ((--handle->u.dir.names_left) == 0) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "libssh2_sftp_readdir_ex() return %d", + filename_len); + return filename_len; + } + + /* Request another entry(entries?) */ + + s = sftp->readdir_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->readdir_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_READDIR packet", + 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_READDIR; + sftp->readdir_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->readdir_request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; + + sftp->readdir_state = libssh2_NB_state_created; + } + + if (sftp->readdir_state == libssh2_NB_state_created) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Reading entries from directory handle"); + if ((retcode = + libssh2_channel_write_ex(channel, 0, + (char *) sftp->readdir_packet, + packet_len)) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != retcode) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, sftp->readdir_packet); + sftp->readdir_packet = NULL; + sftp->readdir_state = libssh2_NB_state_idle; + return -1; + } + + LIBSSH2_FREE(session, sftp->readdir_packet); + sftp->readdir_packet = NULL; + + sftp->readdir_state = libssh2_NB_state_sent; + } + + retcode = + sftp_packet_requirev(sftp, 2, read_responses, + sftp->readdir_request_id, &data, + &data_len); + if (retcode == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (retcode) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->readdir_state = libssh2_NB_state_idle; + return -1; + } + + if (data[0] == SSH_FXP_STATUS) { + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_EOF) { + sftp->readdir_state = libssh2_NB_state_idle; + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + sftp->readdir_state = libssh2_NB_state_idle; + return -1; + } + } + + num_names = libssh2_ntohu32(data + 5); + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%lu entries returned", + num_names); + if (num_names <= 0) { + LIBSSH2_FREE(session, data); + sftp->readdir_state = libssh2_NB_state_idle; + return (num_names == 0) ? 0 : -1; + } + + if (num_names == 1) { + unsigned long real_filename_len = libssh2_ntohu32(data + 9); + + filename_len = real_filename_len; + if (filename_len > buffer_maxlen) { + filename_len = buffer_maxlen; + } + memcpy(buffer, data + 13, filename_len); + + /* The filename is not null terminated, make it so if possible */ + if (filename_len < buffer_maxlen) { + buffer[filename_len] = '\0'; + } + + if (attrs) { + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 13 + real_filename_len + + (4 + + libssh2_ntohu32(data + 13 + + real_filename_len))); + } + LIBSSH2_FREE(session, data); + + sftp->readdir_state = libssh2_NB_state_idle; + return filename_len; + } + + handle->u.dir.names_left = num_names; + handle->u.dir.names_packet = data; + handle->u.dir.next_name = (char *) data + 9; + + sftp->readdir_state = libssh2_NB_state_idle; + + /* Be lazy, just use the name popping mechanism from the start of the function */ + return libssh2_sftp_readdir_ex(handle, buffer, buffer_maxlen, longentry, + longentry_maxlen, attrs); +} + +/* }}} */ + +/* {{{ libssh2_sftp_write + * Write data to a file handle + */ +LIBSSH2_API ssize_t +libssh2_sftp_write(LIBSSH2_SFTP_HANDLE * handle, const char *buffer, + size_t count) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode; + /* 25 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) + + offset(8) + count(4) */ + ssize_t packet_len = handle->handle_len + count + 25; + unsigned char *s, *data; + int rc; + + if (sftp->write_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Writing %lu bytes", + (unsigned long) count); + s = sftp->write_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->write_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_WRITE packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_WRITE; + sftp->write_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->write_request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; + libssh2_htonu64(s, handle->u.file.offset); + s += 8; + libssh2_htonu32(s, count); + s += 4; + memcpy(s, buffer, count); + s += count; + + sftp->write_state = libssh2_NB_state_created; + } + + if (sftp->write_state == libssh2_NB_state_created) { + if ((rc = + libssh2_channel_write_ex(channel, 0, (char *) sftp->write_packet, + packet_len)) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, sftp->write_packet); + sftp->write_packet = NULL; + sftp->write_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->write_packet); + sftp->write_packet = NULL; + sftp->write_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, + sftp->write_request_id, &data, &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->write_state = libssh2_NB_state_idle; + return -1; + } + + sftp->write_state = libssh2_NB_state_idle; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + handle->u.file.offset += count; + return count; + } + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, "SFTP Protocol Error", + 0); + sftp->last_errno = retcode; + + return -1; +} + +/* }}} */ + +/* {{{ libssh2_sftp_fstat_ex + * Get or Set stat on a file + */ +LIBSSH2_API int +libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE * handle, + LIBSSH2_SFTP_ATTRIBUTES * attrs, int setstat) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + ssize_t packet_len = + handle->handle_len + 13 + (setstat ? libssh2_sftp_attrsize(attrs) : 0); + unsigned char *s, *data; + static const unsigned char fstat_responses[2] = + { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + int rc; + + if (sftp->fstat_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Issuing %s command", + setstat ? "set-stat" : "stat"); + s = sftp->fstat_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->fstat_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FSTAT/FSETSTAT packet", + 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT; + sftp->fstat_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->fstat_request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; + if (setstat) { + s += libssh2_sftp_attr2bin(s, attrs); + } + + sftp->fstat_state = libssh2_NB_state_created; + } + + if (sftp->fstat_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) sftp->fstat_packet, + packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + (setstat ? "Unable to send FXP_FSETSTAT" + : "Unable to send FXP_FSTAT command"), 0); + LIBSSH2_FREE(session, sftp->fstat_packet); + sftp->fstat_packet = NULL; + sftp->fstat_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->fstat_packet); + sftp->fstat_packet = NULL; + + sftp->fstat_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_requirev(sftp, 2, fstat_responses, + sftp->fstat_request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->fstat_state = libssh2_NB_state_idle; + return -1; + } + + sftp->fstat_state = libssh2_NB_state_idle; + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + return -1; + } + } + + libssh2_sftp_bin2attr(attrs, data + 5); + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_sftp_seek + * Set the read/write pointer to an arbitrary position within the file + */ +LIBSSH2_API void +libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE * handle, size_t offset) +{ + handle->u.file.offset = offset; +} + +/* }}} */ + +/* {{{ libssh2_sftp_seek64 + * Set the read/write pointer to an arbitrary position within the file + */ +LIBSSH2_API void +libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE * handle, libssh2_uint64_t offset) +{ + handle->u.file.offset = offset; +} + +/* }}} */ + +/* {{{ libssh2_sftp_tell + * Return the current read/write pointer's offset + */ +LIBSSH2_API size_t +libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE * handle) +{ + return handle->u.file.offset; +} + +/* {{{ libssh2_sftp_tell64 + * Return the current read/write pointer's offset + */ +LIBSSH2_API libssh2_uint64_t +libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE * handle) +{ + return handle->u.file.offset; +} + +/* }}} */ + + +/* {{{ libssh2_sftp_close_handle + * Close a file or directory handle + * Also frees handle resource and unlinks it from the SFTP structure + */ +LIBSSH2_API int +libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE * handle) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */ + ssize_t packet_len = handle->handle_len + 13; + unsigned char *s, *data; + int rc; + + if (handle->close_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Closing handle"); + s = handle->close_packet = LIBSSH2_ALLOC(session, packet_len); + if (!handle->close_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_CLOSE packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_CLOSE; + handle->close_request_id = sftp->request_id++; + libssh2_htonu32(s, handle->close_request_id); + s += 4; + libssh2_htonu32(s, handle->handle_len); + s += 4; + memcpy(s, handle->handle, handle->handle_len); + s += handle->handle_len; + + handle->close_state = libssh2_NB_state_created; + } + + if (handle->close_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, + (char *) handle->close_packet, + packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_CLOSE command", 0); + LIBSSH2_FREE(session, handle->close_packet); + handle->close_packet = NULL; + handle->close_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, handle->close_packet); + handle->close_packet = NULL; + + handle->close_state = libssh2_NB_state_sent; + } + + if (handle->close_state == libssh2_NB_state_sent) { + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, + handle->close_request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + handle->close_state = libssh2_NB_state_idle; + return -1; + } + + handle->close_state = libssh2_NB_state_sent1; + } + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode != LIBSSH2_FX_OK) { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + handle->close_state = libssh2_NB_state_idle; + return -1; + } + + if (handle == sftp->handles) { + sftp->handles = handle->next; + } + if (handle->next) { + handle->next->prev = NULL; + } + + if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) + && handle->u.dir.names_left) { + LIBSSH2_FREE(session, handle->u.dir.names_packet); + } + + handle->close_state = libssh2_NB_state_idle; + + LIBSSH2_FREE(session, handle); + + return 0; +} + +/* }}} */ + +/* ********************** + * SFTP Miscellaneous * + ********************** */ + +/* {{{ libssh2_sftp_unlink_ex + * Delete a file from the remote server + */ +/* libssh2_sftp_unlink_ex - NB-UNSAFE?? */ +LIBSSH2_API int +libssh2_sftp_unlink_ex(LIBSSH2_SFTP * sftp, const char *filename, + unsigned int filename_len) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */ + ssize_t packet_len = filename_len + 13; + unsigned char *s, *data; + int rc; + + if (sftp->unlink_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Unlinking %s", filename); + s = sftp->unlink_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->unlink_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_REMOVE packet", + 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_REMOVE; + sftp->unlink_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->unlink_request_id); + s += 4; + libssh2_htonu32(s, filename_len); + s += 4; + memcpy(s, filename, filename_len); + s += filename_len; + + sftp->unlink_state = libssh2_NB_state_created; + } + + if (sftp->unlink_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) sftp->unlink_packet, + packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_REMOVE command", 0); + LIBSSH2_FREE(session, sftp->unlink_packet); + sftp->unlink_packet = NULL; + sftp->unlink_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->unlink_packet); + sftp->unlink_packet = NULL; + + sftp->unlink_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, + sftp->unlink_request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->unlink_state = libssh2_NB_state_idle; + return -1; + } + + sftp->unlink_state = libssh2_NB_state_idle; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + return -1; + } +} + +/* }}} */ + +/* {{{ libssh2_sftp_rename_ex + * Rename a file on the remote server + */ +LIBSSH2_API int +libssh2_sftp_rename_ex(LIBSSH2_SFTP * sftp, const char *source_filename, + unsigned int source_filename_len, + const char *dest_filename, + unsigned int dest_filename_len, long flags) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode; + ssize_t packet_len = + source_filename_len + dest_filename_len + 17 + (sftp->version >= + 5 ? 4 : 0); + /* packet_len(4) + packet_type(1) + request_id(4) + + source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */ + unsigned char *data; + int rc; + + if (sftp->version < 2) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "Server does not support RENAME", 0); + return -1; + } + + if (sftp->rename_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Renaming %s to %s", + source_filename, dest_filename); + sftp->rename_s = sftp->rename_packet = + LIBSSH2_ALLOC(session, packet_len); + if (!sftp->rename_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_RENAME packet", + 0); + return -1; + } + + libssh2_htonu32(sftp->rename_s, packet_len - 4); + sftp->rename_s += 4; + *(sftp->rename_s++) = SSH_FXP_RENAME; + sftp->rename_request_id = sftp->request_id++; + libssh2_htonu32(sftp->rename_s, sftp->rename_request_id); + sftp->rename_s += 4; + libssh2_htonu32(sftp->rename_s, source_filename_len); + sftp->rename_s += 4; + memcpy(sftp->rename_s, source_filename, source_filename_len); + sftp->rename_s += source_filename_len; + libssh2_htonu32(sftp->rename_s, dest_filename_len); + sftp->rename_s += 4; + memcpy(sftp->rename_s, dest_filename, dest_filename_len); + sftp->rename_s += dest_filename_len; + + if (sftp->version >= 5) { + libssh2_htonu32(sftp->rename_s, flags); + sftp->rename_s += 4; + } + + sftp->rename_state = libssh2_NB_state_created; + } + + if (sftp->rename_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) sftp->rename_packet, + sftp->rename_s - sftp->rename_packet); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_RENAME command", 0); + LIBSSH2_FREE(session, sftp->rename_packet); + sftp->rename_packet = NULL; + sftp->rename_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->rename_packet); + sftp->rename_packet = NULL; + + sftp->rename_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, + sftp->rename_request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->rename_state = libssh2_NB_state_idle; + return -1; + } + + sftp->rename_state = libssh2_NB_state_idle; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + switch (retcode) { + case LIBSSH2_FX_OK: + retcode = 0; + break; + + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "File already exists and SSH_FXP_RENAME_OVERWRITE not specified", + 0); + sftp->last_errno = retcode; + retcode = -1; + break; + + case LIBSSH2_FX_OP_UNSUPPORTED: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "Operation Not Supported", 0); + sftp->last_errno = retcode; + retcode = -1; + break; + + default: + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + retcode = -1; + } + + return retcode; +} + +/* }}} */ + +/* {{{ libssh2_sftp_mkdir_ex + * Create an SFTP directory + */ +LIBSSH2_API int +libssh2_sftp_mkdir_ex(LIBSSH2_SFTP * sftp, const char *path, + unsigned int path_len, long mode) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_SFTP_ATTRIBUTES attrs = { + LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0 + }; + unsigned long data_len, retcode, request_id; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + ssize_t packet_len = path_len + 13 + libssh2_sftp_attrsize(&attrs); + unsigned char *packet, *s, *data; + int rc; + + if (sftp->mkdir_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, + "Creating directory %s with mode 0%lo", path, mode); + s = packet = LIBSSH2_ALLOC(session, packet_len); + if (!packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + /* Filetype in SFTP 3 and earlier */ + attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR; + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_MKDIR; + request_id = sftp->request_id++; + libssh2_htonu32(s, request_id); + s += 4; + libssh2_htonu32(s, path_len); + s += 4; + memcpy(s, path, path_len); + s += path_len; + s += libssh2_sftp_attr2bin(s, &attrs); + + sftp->mkdir_state = libssh2_NB_state_created; + } else { + packet = sftp->mkdir_packet; + request_id = sftp->mkdir_request_id; + } + + if (sftp->mkdir_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) packet, packet_len); + if (rc == PACKET_EAGAIN) { + sftp->mkdir_packet = packet; + sftp->mkdir_request_id = request_id; + return PACKET_EAGAIN; + } + if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_READ command", 0); + LIBSSH2_FREE(session, packet); + sftp->mkdir_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, packet); + sftp->mkdir_state = libssh2_NB_state_sent; + sftp->mkdir_packet = NULL; + } + + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->mkdir_state = libssh2_NB_state_idle; + return -1; + } + + sftp->mkdir_state = libssh2_NB_state_idle; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + sftp->last_errno = retcode; + return -1; + } +} + +/* }}} */ + +/* {{{ libssh2_sftp_rmdir_ex + * Remove a directory + */ +/* libssh2_sftp_rmdir_ex - NB-UNSAFE?? */ +LIBSSH2_API int +libssh2_sftp_rmdir_ex(LIBSSH2_SFTP * sftp, const char *path, + unsigned int path_len) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, retcode; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + ssize_t packet_len = path_len + 13; + unsigned char *s, *data; + int rc; + + if (sftp->rmdir_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "Removing directory: %s", + path); + s = sftp->rmdir_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->rmdir_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + *(s++) = SSH_FXP_RMDIR; + sftp->rmdir_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->rmdir_request_id); + s += 4; + libssh2_htonu32(s, path_len); + s += 4; + memcpy(s, path, path_len); + s += path_len; + + sftp->rmdir_state = libssh2_NB_state_created; + } + + if (sftp->rmdir_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) sftp->rmdir_packet, + packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send FXP_MKDIR command", 0); + LIBSSH2_FREE(session, sftp->rmdir_packet); + sftp->rmdir_packet = NULL; + sftp->rmdir_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->rmdir_packet); + sftp->rmdir_packet = NULL; + + sftp->rmdir_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, + sftp->rmdir_request_id, &data, &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->rmdir_state = libssh2_NB_state_idle; + return -1; + } + + sftp->rmdir_state = libssh2_NB_state_idle; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + return -1; + } +} + +/* }}} */ + +/* {{{ libssh2_sftp_stat_ex + * Stat a file or symbolic link + */ +/* libssh2_sftp_stat_ex - NB-UNSAFE?? */ +LIBSSH2_API int +libssh2_sftp_stat_ex(LIBSSH2_SFTP * sftp, const char *path, + unsigned int path_len, int stat_type, + LIBSSH2_SFTP_ATTRIBUTES * attrs) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + ssize_t packet_len = + path_len + 13 + + ((stat_type == + LIBSSH2_SFTP_SETSTAT) ? libssh2_sftp_attrsize(attrs) : 0); + unsigned char *s, *data; + static const unsigned char stat_responses[2] = + { SSH_FXP_ATTRS, SSH_FXP_STATUS }; + int rc; + + if (sftp->stat_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s", + (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" : + (stat_type == + LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path); + s = sftp->stat_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->stat_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for FXP_MKDIR packet", 0); + return -1; + } + + libssh2_htonu32(s, packet_len - 4); + s += 4; + switch (stat_type) { + case LIBSSH2_SFTP_SETSTAT: + *(s++) = SSH_FXP_SETSTAT; + break; + + case LIBSSH2_SFTP_LSTAT: + *(s++) = SSH_FXP_LSTAT; + break; + + case LIBSSH2_SFTP_STAT: + default: + *(s++) = SSH_FXP_STAT; + } + sftp->stat_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->stat_request_id); + s += 4; + libssh2_htonu32(s, path_len); + s += 4; + memcpy(s, path, path_len); + s += path_len; + if (stat_type == LIBSSH2_SFTP_SETSTAT) { + s += libssh2_sftp_attr2bin(s, attrs); + } + + sftp->stat_state = libssh2_NB_state_created; + } + + if (sftp->stat_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) sftp->stat_packet, + packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send STAT/LSTAT/SETSTAT command", 0); + LIBSSH2_FREE(session, sftp->stat_packet); + sftp->stat_packet = NULL; + sftp->stat_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->stat_packet); + sftp->stat_packet = NULL; + + sftp->stat_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_requirev(sftp, 2, stat_responses, + sftp->stat_request_id, &data, &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->stat_state = libssh2_NB_state_idle; + return -1; + } + + sftp->stat_state = libssh2_NB_state_idle; + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + return -1; + } + } + + memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + libssh2_sftp_bin2attr(attrs, data + 5); + LIBSSH2_FREE(session, data); + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_sftp_symlink_ex + * Read or set a symlink + */ +LIBSSH2_API int +libssh2_sftp_symlink_ex(LIBSSH2_SFTP * sftp, const char *path, + unsigned int path_len, char *target, + unsigned int target_len, int link_type) +{ + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned long data_len, link_len; + /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */ + ssize_t packet_len = + path_len + 13 + + ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0); + unsigned char *s, *data; + static const unsigned char link_responses[2] = + { SSH_FXP_NAME, SSH_FXP_STATUS }; + int rc; + + if ((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "Server does not support SYMLINK or READLINK", 0); + return -1; + } + + if (sftp->symlink_state == libssh2_NB_state_idle) { + s = sftp->symlink_packet = LIBSSH2_ALLOC(session, packet_len); + if (!sftp->symlink_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for SYMLINK/READLINK" + "/REALPATH packet", 0); + return -1; + } + + _libssh2_debug(session, LIBSSH2_DBG_SFTP, "%s %s on %s", + (link_type == + LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading", + (link_type == + LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path); + + libssh2_htonu32(s, packet_len - 4); + s += 4; + switch (link_type) { + case LIBSSH2_SFTP_REALPATH: + *(s++) = SSH_FXP_REALPATH; + break; + + case LIBSSH2_SFTP_SYMLINK: + *(s++) = SSH_FXP_SYMLINK; + break; + + case LIBSSH2_SFTP_READLINK: + default: + *(s++) = SSH_FXP_READLINK; + } + sftp->symlink_request_id = sftp->request_id++; + libssh2_htonu32(s, sftp->symlink_request_id); + s += 4; + libssh2_htonu32(s, path_len); + s += 4; + memcpy(s, path, path_len); + s += path_len; + if (link_type == LIBSSH2_SFTP_SYMLINK) { + libssh2_htonu32(s, target_len); + s += 4; + memcpy(s, target, target_len); + s += target_len; + } + + sftp->symlink_state = libssh2_NB_state_created; + } + + if (sftp->symlink_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, + (char *) sftp->symlink_packet, + packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (packet_len != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send SYMLINK/READLINK command", 0); + LIBSSH2_FREE(session, sftp->symlink_packet); + sftp->symlink_packet = NULL; + sftp->symlink_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, sftp->symlink_packet); + sftp->symlink_packet = NULL; + + sftp->symlink_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_requirev(sftp, 2, link_responses, + sftp->symlink_request_id, &data, + &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for status message", 0); + sftp->symlink_state = libssh2_NB_state_idle; + return -1; + } + + sftp->symlink_state = libssh2_NB_state_idle; + + if (data[0] == SSH_FXP_STATUS) { + int retcode; + + retcode = libssh2_ntohu32(data + 5); + LIBSSH2_FREE(session, data); + if (retcode == LIBSSH2_FX_OK) { + return 0; + } else { + sftp->last_errno = retcode; + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "SFTP Protocol Error", 0); + return -1; + } + } + + if (libssh2_ntohu32(data + 5) < 1) { + libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL, + "Invalid READLINK/REALPATH response, no name entries", + 0); + LIBSSH2_FREE(session, data); + return -1; + } + + link_len = libssh2_ntohu32(data + 9); + if (link_len >= target_len) { + link_len = target_len - 1; + } + memcpy(target, data + 13, link_len); + target[link_len] = 0; + LIBSSH2_FREE(session, data); + + return link_len; +} + +/* }}} */ + +/* {{{ libssh2_sftp_last_error + * Returns the last error code reported by SFTP + */ +LIBSSH2_API unsigned long +libssh2_sftp_last_error(LIBSSH2_SFTP * sftp) +{ + return sftp->last_errno; +} + +/* }}} */ Property changes on: libssh2/src/sftp.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/libssh2_config.h.in.w32 =================================================================== --- libssh2/src/libssh2_config.h.in.w32 (.../tags/RELEASE_0_11_0) +++ libssh2/src/libssh2_config.h.in.w32 (.../trunk) @@ -0,0 +1,44 @@ +#ifndef LIBSSH2_CONFIG_H +#define LIBSSH2_CONFIG_H + +#ifndef WIN32 +#define WIN32 +#endif +#include +#include +#include + +#ifdef __MINGW32__ +#define HAVE_UNISTD_H +#define HAVE_INTTYPES_H +#define HAVE_SYS_TIME_H +#endif + +#define HAVE_WINSOCK2_H +#define HAVE_IOCTLSOCKET +#define HAVE_SELECT + +#ifdef _MSC_VER +#define snprintf _snprintf +#if _MSC_VER < 1500 +#define vsnprintf _vsnprintf +#else +#define ssize_t SSIZE_T +#define uint32_t UINT32 +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#define strncasecmp strnicmp +#define strcasecmp stricmp +#endif /* _MSC_VER */ + +/* Compile in zlib support */ +#define LIBSSH2_HAVE_ZLIB 1 + +/* Enable newer diffie-hellman-group-exchange-sha1 syntax */ +#define LIBSSH2_DH_GEX_NEW 1 + +#endif /* LIBSSH2_CONFIG_H */ + + Property changes on: libssh2/src/libssh2_config.h.in.w32 ___________________________________________________________________ Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/pem.c =================================================================== --- libssh2/src/pem.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/pem.c (.../trunk) @@ -0,0 +1,209 @@ +/* Copyright (C) 2007 The Written Word, Inc. + * Copyright (C) 2008, Simon Josefsson + * 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" + +static int +readline(char *line, int line_size, FILE * fp) +{ + if (!fgets(line, line_size, fp)) { + return -1; + } + if (*line && line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + if (*line && line[strlen(line) - 1] == '\r') { + line[strlen(line) - 1] = '\0'; + } + return 0; +} + +#define LINE_SIZE 128 + +int +_libssh2_pem_parse(LIBSSH2_SESSION * session, + const char *headerbegin, + const char *headerend, + FILE * fp, unsigned char **data, unsigned int *datalen) +{ + char line[LINE_SIZE]; + char *b64data = NULL; + unsigned int b64datalen = 0; + int ret; + + do { + if (readline(line, LINE_SIZE, fp)) { + return -1; + } + } + while (strcmp(line, headerbegin) != 0); + + *line = '\0'; + + do { + if (*line) { + char *tmp; + size_t linelen; + + linelen = strlen(line); + tmp = LIBSSH2_REALLOC(session, b64data, b64datalen + linelen); + if (!tmp) { + ret = -1; + goto out; + } + memcpy(tmp + b64datalen, line, linelen); + b64data = tmp; + b64datalen += linelen; + } + + if (readline(line, LINE_SIZE, fp)) { + ret = -1; + goto out; + } + } while (strcmp(line, headerend) != 0); + + if (libssh2_base64_decode(session, (char**) data, datalen, + b64data, b64datalen)) { + ret = -1; + goto out; + } + + ret = 0; + out: + if (b64data) { + LIBSSH2_FREE(session, b64data); + } + return ret; +} + +static int +read_asn1_length(const unsigned char *data, + unsigned int datalen, unsigned int *len) +{ + unsigned int lenlen; + int nextpos; + + if (datalen < 1) { + return -1; + } + *len = data[0]; + + if (*len >= 0x80) { + lenlen = *len & 0x7F; + *len = data[1]; + if (1 + lenlen > datalen) { + return -1; + } + if (lenlen > 1) { + *len <<= 8; + *len |= data[2]; + } + } else { + lenlen = 0; + } + + nextpos = 1 + lenlen; + if (lenlen > 2 || 1 + lenlen + *len > datalen) { + return -1; + } + + return nextpos; +} + +int +_libssh2_pem_decode_sequence(unsigned char **data, unsigned int *datalen) +{ + unsigned int len; + int lenlen; + + if (*datalen < 1) { + return -1; + } + + if ((*data)[0] != '\x30') { + return -1; + } + + (*data)++; + (*datalen)--; + + lenlen = read_asn1_length(*data, *datalen, &len); + if (lenlen < 0 || lenlen + len != *datalen) { + return -1; + } + + *data += lenlen; + *datalen -= lenlen; + + return 0; +} + +int +_libssh2_pem_decode_integer(unsigned char **data, unsigned int *datalen, + unsigned char **i, unsigned int *ilen) +{ + unsigned int len; + int lenlen; + + if (*datalen < 1) { + return -1; + } + + if ((*data)[0] != '\x02') { + return -1; + } + + (*data)++; + (*datalen)--; + + lenlen = read_asn1_length(*data, *datalen, &len); + if (lenlen < 0 || lenlen + len > *datalen) { + return -1; + } + + *data += lenlen; + *datalen -= lenlen; + + *i = *data; + *ilen = len; + + *data += len; + *datalen -= len; + + return 0; +} Property changes on: libssh2/src/pem.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/session.c =================================================================== --- libssh2/src/session.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/session.c (.../trunk) @@ -0,0 +1,1554 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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 +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include + +#ifdef HAVE_GETTIMEOFDAY +#include +#endif +#ifdef HAVE_ALLOCA_H +#include +#endif + +/* {{{ libssh2_default_alloc + */ +static +LIBSSH2_ALLOC_FUNC(libssh2_default_alloc) +{ + (void) abstract; + return malloc(count); +} + +/* }}} */ + +/* {{{ libssh2_default_free + */ +static +LIBSSH2_FREE_FUNC(libssh2_default_free) +{ + (void) abstract; + free(ptr); +} + +/* }}} */ + +/* {{{ libssh2_default_realloc + */ +static +LIBSSH2_REALLOC_FUNC(libssh2_default_realloc) +{ + (void) abstract; + 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, PACKET_EAGAIN if read would block, 1 on failure + */ +static int +libssh2_banner_receive(LIBSSH2_SESSION * session) +{ + int ret; + int banner_len; + + if (session->banner_TxRx_state == libssh2_NB_state_idle) { + banner_len = 0; + + session->banner_TxRx_state = libssh2_NB_state_created; + } else { + banner_len = session->banner_TxRx_total_send; + } + + while ((banner_len < (int) sizeof(session->banner_TxRx_banner)) && + ((banner_len == 0) + || (session->banner_TxRx_banner[banner_len - 1] != '\n'))) { + char c = '\0'; + + ret = + recv(session->socket_fd, &c, 1, + LIBSSH2_SOCKET_RECV_FLAGS(session)); + + if (ret < 0) { +#ifdef WIN32 + switch (WSAGetLastError()) { + case WSAEWOULDBLOCK: + errno = EAGAIN; + break; + + case WSAENOTSOCK: + errno = EBADF; + break; + + case WSAENOTCONN: + case WSAECONNABORTED: + errno = WSAENOTCONN; + break; + + case WSAEINTR: + errno = EINTR; + break; + } +#endif /* WIN32 */ + if (errno == EAGAIN) { + session->socket_block_directions = + LIBSSH2_SESSION_BLOCK_INBOUND; + session->banner_TxRx_total_send = banner_len; + return PACKET_EAGAIN; + } + + /* Some kinda error */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + return 1; + } + + if (ret == 0) { + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED; + return PACKET_FAIL; + } + + if (c == '\0') { + /* NULLs are not allowed in SSH banners */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + return 1; + } + + session->banner_TxRx_banner[banner_len++] = c; + } + + while (banner_len && + ((session->banner_TxRx_banner[banner_len - 1] == '\n') || + (session->banner_TxRx_banner[banner_len - 1] == '\r'))) { + banner_len--; + } + + /* From this point on, we are done here */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + + if (!banner_len) + return 1; + + session->remote.banner = LIBSSH2_ALLOC(session, banner_len + 1); + if (!session->remote.banner) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Error allocating space for remote banner", 0); + return 1; + } + memcpy(session->remote.banner, session->banner_TxRx_banner, banner_len); + session->remote.banner[banner_len] = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Received Banner: %s", + session->remote.banner); + return 0; +} + +/* }}} */ + +/* {{{ libssh2_banner_send + * Send the default banner, or the one set via libssh2_setopt_string + * + * Returns PACKET_EAGAIN if it would block - and if it does so, you should + * call this function again as soon as it is likely that more data can be + * sent, and this function should then be called with the same argument set + * (same data pointer and same data_len) until zero or failure is returned. + */ +static int +libssh2_banner_send(LIBSSH2_SESSION * session) +{ + char *banner = (char *) LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF; + int banner_len = sizeof(LIBSSH2_SSH_DEFAULT_BANNER_WITH_CRLF) - 1; + ssize_t ret; +#ifdef LIBSSH2DEBUG + char banner_dup[256]; +#endif + + if (session->banner_TxRx_state == libssh2_NB_state_idle) { + if (session->local.banner) { + /* setopt_string will have given us our \r\n characters */ + banner_len = strlen((char *) session->local.banner); + banner = (char *) session->local.banner; + } +#ifdef LIBSSH2DEBUG + /* Hack and slash to avoid sending CRLF in debug output */ + if (banner_len < 256) { + memcpy(banner_dup, banner, banner_len - 2); + banner_dup[banner_len - 2] = '\0'; + } else { + memcpy(banner_dup, banner, 255); + banner[255] = '\0'; + } + + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Sending Banner: %s", + banner_dup); +#endif + + session->banner_TxRx_state = libssh2_NB_state_created; + } + + ret = + send(session->socket_fd, banner + session->banner_TxRx_total_send, + banner_len - session->banner_TxRx_total_send, + LIBSSH2_SOCKET_SEND_FLAGS(session)); + + if (ret != (banner_len - session->banner_TxRx_total_send)) { + if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) { + /* the whole packet could not be sent, save the what was */ + session->socket_block_directions = + LIBSSH2_SESSION_BLOCK_OUTBOUND; + session->banner_TxRx_total_send += ret; + return PACKET_EAGAIN; + } + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + return PACKET_FAIL; + } + + /* Set the state back to idle */ + session->banner_TxRx_state = libssh2_NB_state_idle; + session->banner_TxRx_total_send = 0; + + return 0; +} + +/* }}} */ + +/* + * _libssh2_nonblock() sets the given socket to either blocking or + * non-blocking mode based on the 'nonblock' boolean argument. This function + * is copied from the libcurl sources with permission. + */ +static int +_libssh2_nonblock(int sockfd, /* operate on this */ + int nonblock /* TRUE or FALSE */ ) +{ +#undef SETBLOCK +#define SETBLOCK 0 +#ifdef HAVE_O_NONBLOCK + /* most recent unix versions */ + int flags; + + flags = fcntl(sockfd, F_GETFL, 0); + if (nonblock) + return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + else + return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); +#undef SETBLOCK +#define SETBLOCK 1 +#endif + +#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) + /* older unix versions */ + int flags; + + flags = nonblock; + return ioctl(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 2 +#endif + +#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) + /* Windows? */ + unsigned long flags; + flags = nonblock; + + return ioctlsocket(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 3 +#endif + +#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) + /* presumably for Amiga */ + return IoctlSocket(sockfd, FIONBIO, (long) nonblock); +#undef SETBLOCK +#define SETBLOCK 4 +#endif + +#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) + /* BeOS */ + long b = nonblock ? 1 : 0; + return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +#undef SETBLOCK +#define SETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 0; /* returns success */ +#undef SETBLOCK +#define SETBLOCK 6 +#endif + +#if (SETBLOCK == 0) +#error "no non-blocking method was found/used/set" +#endif +} + +/* + * _libssh2_get_socket_nonblocking() gets the given blocking or non-blocking + * state of the socket. + */ +static int +_libssh2_get_socket_nonblocking(int sockfd) +{ /* operate on this */ +#undef GETBLOCK +#define GETBLOCK 0 +#ifdef HAVE_O_NONBLOCK + /* most recent unix versions */ + int flags; + + if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) { + /* Assume blocking on error */ + return 1; + } + return (flags & O_NONBLOCK); +#undef GETBLOCK +#define GETBLOCK 1 +#endif + +#if defined(WSAEWOULDBLOCK) && (GETBLOCK == 0) + /* Windows? */ + unsigned int option_value; + socklen_t option_len = sizeof(option_value); + + if (getsockopt + (sockfd, SOL_SOCKET, SO_ERROR, (void *) &option_value, &option_len)) { + /* Assume blocking on error */ + return 1; + } + return (int) option_value; +#undef GETBLOCK +#define GETBLOCK 2 +#endif + +#if defined(HAVE_SO_NONBLOCK) && (GETBLOCK == 0) + /* BeOS */ + long b; + if (getsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b))) { + /* Assume blocking on error */ + return 1; + } + return (int) b; +#undef GETBLOCK +#define GETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 1; /* returns blocking */ +#undef GETBLOCK +#define GETBLOCK 6 +#endif + +#if (GETBLOCK == 0) +#error "no non-blocking method was found/used/get" +#endif +} + +/* {{{ libssh2_banner_set + * Set the local banner + */ +LIBSSH2_API int +libssh2_banner_set(LIBSSH2_SESSION * session, const char *banner) +{ + int banner_len = banner ? strlen(banner) : 0; + + if (session->local.banner) { + LIBSSH2_FREE(session, session->local.banner); + session->local.banner = NULL; + } + + if (!banner_len) { + return 0; + } + + session->local.banner = LIBSSH2_ALLOC(session, banner_len + 3); + if (!session->local.banner) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for local banner", 0); + return -1; + } + + memcpy(session->local.banner, banner, banner_len); + session->local.banner[banner_len] = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting local Banner: %s", + session->local.banner); + session->local.banner[banner_len++] = '\r'; + session->local.banner[banner_len++] = '\n'; + session->local.banner[banner_len++] = '\0'; + + return 0; +} + +/* }}} */ + +/* {{{ 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); + if (session) { + memset(session, 0, sizeof(LIBSSH2_SESSION)); + session->alloc = local_alloc; + session->free = local_free; + session->realloc = local_realloc; + session->abstract = abstract; + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "New session resource allocated"); + libssh2_crypto_init(); + } + return session; +} + +/* }}} */ + +/* {{{ libssh2_session_callback_set + * Set (or reset) a callback function + * Returns the prior address + * + * FIXME: this function relies on that we can typecast function pointers + * to void pointers, which isn't allowed in ISO C! + */ +LIBSSH2_API void * +libssh2_session_callback_set(LIBSSH2_SESSION * session, + int cbtype, void *callback) +{ + void *oldcb; + + switch (cbtype) { + case LIBSSH2_CALLBACK_IGNORE: + oldcb = session->ssh_msg_ignore; + session->ssh_msg_ignore = callback; + return oldcb; + + case LIBSSH2_CALLBACK_DEBUG: + oldcb = session->ssh_msg_debug; + session->ssh_msg_debug = callback; + return oldcb; + + case LIBSSH2_CALLBACK_DISCONNECT: + oldcb = session->ssh_msg_disconnect; + session->ssh_msg_disconnect = callback; + return oldcb; + + case LIBSSH2_CALLBACK_MACERROR: + oldcb = session->macerror; + session->macerror = callback; + return oldcb; + + case LIBSSH2_CALLBACK_X11: + oldcb = session->x11; + session->x11 = callback; + return oldcb; + + } + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Setting Callback %d", cbtype); + + return NULL; +} + +/* }}} */ + +/* {{{ 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 and connected socket. + */ +LIBSSH2_API int +libssh2_session_startup(LIBSSH2_SESSION * session, int sock) +{ + int rc; + + if (session->startup_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "session_startup for socket %d", sock); + /* FIXME: on some platforms (like win32) sockets are unsigned */ + if (sock < 0) { + /* Did we forget something? */ + libssh2_error(session, LIBSSH2_ERROR_SOCKET_NONE, + "Bad socket provided", 0); + return LIBSSH2_ERROR_SOCKET_NONE; + } + session->socket_fd = sock; + + session->socket_block = + !_libssh2_get_socket_nonblocking(session->socket_fd); + if (session->socket_block) { + /* + * Since we can't be sure that we are in blocking or there + * was an error detecting the state, so set to blocking to + * be sure + */ + _libssh2_nonblock(session->socket_fd, 0); + } + + session->startup_state = libssh2_NB_state_created; + } + + /* TODO: Liveness check */ + + if (session->startup_state == libssh2_NB_state_created) { + rc = libssh2_banner_send(session); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending banner to remote host", 0); + return LIBSSH2_ERROR_EAGAIN; + } else if (rc) { + /* Unable to send banner? */ + libssh2_error(session, LIBSSH2_ERROR_BANNER_SEND, + "Error sending banner to remote host", 0); + return LIBSSH2_ERROR_BANNER_SEND; + } + + session->startup_state = libssh2_NB_state_sent; + } + + if (session->startup_state == libssh2_NB_state_sent) { + rc = libssh2_banner_receive(session); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for banner", 0); + return LIBSSH2_ERROR_EAGAIN; + } else if (rc) { + /* Unable to receive banner from remote */ + libssh2_error(session, LIBSSH2_ERROR_BANNER_NONE, + "Timeout waiting for banner", 0); + return LIBSSH2_ERROR_BANNER_NONE; + } + + session->startup_state = libssh2_NB_state_sent1; + } + + if (session->startup_state == libssh2_NB_state_sent1) { + rc = libssh2_kex_exchange(session, 0, &session->startup_key_state); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block exchanging encryption keys", 0); + return LIBSSH2_ERROR_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, + "Unable to exchange encryption keys", 0); + return LIBSSH2_ERROR_KEX_FAILURE; + } + + session->startup_state = libssh2_NB_state_sent2; + } + + if (session->startup_state == libssh2_NB_state_sent2) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Requesting userauth service"); + + /* Request the userauth service */ + session->startup_service[0] = SSH_MSG_SERVICE_REQUEST; + libssh2_htonu32(session->startup_service + 1, + sizeof("ssh-userauth") - 1); + memcpy(session->startup_service + 5, "ssh-userauth", + sizeof("ssh-userauth") - 1); + + session->startup_state = libssh2_NB_state_sent3; + } + + if (session->startup_state == libssh2_NB_state_sent3) { + rc = libssh2_packet_write(session, session->startup_service, + sizeof("ssh-userauth") + 5 - 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block asking for ssh-userauth service", 0); + return LIBSSH2_ERROR_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to ask for ssh-userauth service", 0); + return LIBSSH2_ERROR_SOCKET_SEND; + } + + session->startup_state = libssh2_NB_state_sent4; + } + + if (session->startup_state == libssh2_NB_state_sent4) { + rc = libssh2_packet_require_ex(session, SSH_MSG_SERVICE_ACCEPT, + &session->startup_data, + &session->startup_data_len, 0, NULL, 0, + &session->startup_req_state); + if (rc == PACKET_EAGAIN) { + return LIBSSH2_ERROR_EAGAIN; + } else if (rc) { + return LIBSSH2_ERROR_SOCKET_DISCONNECT; + } + session->startup_service_length = + libssh2_ntohu32(session->startup_data + 1); + + if ((session->startup_service_length != (sizeof("ssh-userauth") - 1)) + || strncmp("ssh-userauth", (char *) session->startup_data + 5, + session->startup_service_length)) { + LIBSSH2_FREE(session, session->startup_data); + session->startup_data = NULL; + libssh2_error(session, LIBSSH2_ERROR_PROTO, + "Invalid response received from server", 0); + return LIBSSH2_ERROR_PROTO; + } + LIBSSH2_FREE(session, session->startup_data); + session->startup_data = NULL; + + session->startup_state = libssh2_NB_state_idle; + + return 0; + } + + /* just for safety return some error */ + return LIBSSH2_ERROR_INVAL; +} + +/* }}} */ + +/* {{{ proto libssh2_session_free + * Frees the memory allocated to the session + * Also closes and frees any channels attached to this session + */ +LIBSSH2_API int +libssh2_session_free(LIBSSH2_SESSION * session) +{ + int rc; + + if (session->free_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Freeing session resource", + session->remote.banner); + + session->state = libssh2_NB_state_created; + } + + if (session->free_state == libssh2_NB_state_created) { + while (session->channels.head) { + LIBSSH2_CHANNEL *tmp = session->channels.head; + + rc = libssh2_channel_free(session->channels.head); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + 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 */ + } + } + + session->state = libssh2_NB_state_sent; + } + + if (session->state == libssh2_NB_state_sent) { + while (session->listeners) { + rc = libssh2_channel_forward_cancel(session->listeners); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + } + + session->state = libssh2_NB_state_sent1; + } + + if (session->state & LIBSSH2_STATE_NEWKEYS) { + /* hostkey */ + if (session->hostkey && session->hostkey->dtor) { + session->hostkey->dtor(session, &session->server_hostkey_abstract); + } + + /* Client to Server */ + /* crypt */ + if (session->local.crypt && 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 && 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); + } + } + + /* Free banner(s) */ + if (session->remote.banner) { + LIBSSH2_FREE(session, session->remote.banner); + } + if (session->local.banner) { + LIBSSH2_FREE(session, session->local.banner); + } + + /* Free preference(s) */ + if (session->kex_prefs) { + LIBSSH2_FREE(session, session->kex_prefs); + } + if (session->hostkey_prefs) { + LIBSSH2_FREE(session, session->hostkey_prefs); + } + + if (session->local.crypt_prefs) { + LIBSSH2_FREE(session, session->local.crypt_prefs); + } + if (session->local.mac_prefs) { + LIBSSH2_FREE(session, session->local.mac_prefs); + } + if (session->local.comp_prefs) { + LIBSSH2_FREE(session, session->local.comp_prefs); + } + if (session->local.lang_prefs) { + LIBSSH2_FREE(session, session->local.lang_prefs); + } + + if (session->remote.crypt_prefs) { + LIBSSH2_FREE(session, session->remote.crypt_prefs); + } + if (session->remote.mac_prefs) { + LIBSSH2_FREE(session, session->remote.mac_prefs); + } + if (session->remote.comp_prefs) { + LIBSSH2_FREE(session, session->remote.comp_prefs); + } + if (session->remote.lang_prefs) { + LIBSSH2_FREE(session, session->remote.lang_prefs); + } + + /* + * Make sure all memory used in the state variables are free + */ + if (session->startup_data) { + LIBSSH2_FREE(session, session->startup_data); + } + if (session->disconnect_data) { + LIBSSH2_FREE(session, session->disconnect_data); + } + if (session->userauth_list_data) { + LIBSSH2_FREE(session, session->userauth_list_data); + } + if (session->userauth_pswd_data) { + LIBSSH2_FREE(session, session->userauth_pswd_data); + } + if (session->userauth_pswd_newpw) { + LIBSSH2_FREE(session, session->userauth_pswd_newpw); + } + if (session->userauth_host_packet) { + LIBSSH2_FREE(session, session->userauth_host_packet); + } + if (session->userauth_host_method) { + LIBSSH2_FREE(session, session->userauth_host_method); + } + if (session->userauth_host_data) { + LIBSSH2_FREE(session, session->userauth_host_data); + } + if (session->userauth_pblc_data) { + LIBSSH2_FREE(session, session->userauth_pblc_data); + } + if (session->userauth_pblc_packet) { + LIBSSH2_FREE(session, session->userauth_pblc_packet); + } + if (session->userauth_pblc_method) { + LIBSSH2_FREE(session, session->userauth_pblc_method); + } + if (session->userauth_kybd_data) { + LIBSSH2_FREE(session, session->userauth_kybd_data); + } + if (session->userauth_kybd_packet) { + LIBSSH2_FREE(session, session->userauth_kybd_packet); + } + if (session->userauth_kybd_auth_instruction) { + LIBSSH2_FREE(session, session->userauth_kybd_auth_instruction); + } + if (session->open_packet) { + LIBSSH2_FREE(session, session->open_packet); + } + if (session->open_data) { + LIBSSH2_FREE(session, session->open_data); + } + if (session->direct_message) { + LIBSSH2_FREE(session, session->direct_message); + } + if (session->fwdLstn_packet) { + LIBSSH2_FREE(session, session->fwdLstn_packet); + } + if (session->pkeyInit_data) { + LIBSSH2_FREE(session, session->pkeyInit_data); + } + if (session->scpRecv_command) { + LIBSSH2_FREE(session, session->scpRecv_command); + } + if (session->scpSend_command) { + LIBSSH2_FREE(session, session->scpSend_command); + } + if (session->scpRecv_err_msg) { + LIBSSH2_FREE(session, session->scpRecv_err_msg); + } + if (session->scpSend_err_msg) { + LIBSSH2_FREE(session, session->scpSend_err_msg); + } + + /* Free the error message, if we ar supposed to */ + if (session->err_msg && session->err_should_free) { + LIBSSH2_FREE(session, session->err_msg); + } + + /* 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); + } + + LIBSSH2_FREE(session, session); + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_session_disconnect_ex + */ +LIBSSH2_API int +libssh2_session_disconnect_ex(LIBSSH2_SESSION * session, int reason, + const char *description, const char *lang) +{ + unsigned char *s; + unsigned long descr_len = 0, lang_len = 0; + int rc; + + if (session->disconnect_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_DBG_TRANS, + "Disconnecting: reason=%d, desc=%s, lang=%s", reason, + description, lang); + if (description) { + descr_len = strlen(description); + } + if (lang) { + lang_len = strlen(lang); + } + /* 13 = packet_type(1) + reason code(4) + descr_len(4) + lang_len(4) */ + session->disconnect_data_len = descr_len + lang_len + 13; + + s = session->disconnect_data = + LIBSSH2_ALLOC(session, session->disconnect_data_len); + if (!session->disconnect_data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for disconnect packet", + 0); + session->disconnect_state = libssh2_NB_state_idle; + return -1; + } + + *(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; + } + + session->disconnect_state = libssh2_NB_state_created; + } + + rc = libssh2_packet_write(session, session->disconnect_data, + session->disconnect_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, session->disconnect_data); + session->disconnect_data = NULL; + session->disconnect_state = libssh2_NB_state_idle; + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_session_methods + * Return the currently active methods for method_type + * NOTE: Currently lang_cs and lang_sc are ALWAYS set to empty string regardless of actual negotiation + * Strings should NOT be freed + */ +LIBSSH2_API const char * +libssh2_session_methods(LIBSSH2_SESSION * session, int method_type) +{ + /* All methods have char *name as their first element */ + const LIBSSH2_KEX_METHOD *method = NULL; + + switch (method_type) { + case LIBSSH2_METHOD_KEX: + method = session->kex; + break; + + case LIBSSH2_METHOD_HOSTKEY: + method = (LIBSSH2_KEX_METHOD *) session->hostkey; + break; + + case LIBSSH2_METHOD_CRYPT_CS: + method = (LIBSSH2_KEX_METHOD *) session->local.crypt; + break; + + case LIBSSH2_METHOD_CRYPT_SC: + method = (LIBSSH2_KEX_METHOD *) session->remote.crypt; + break; + + case LIBSSH2_METHOD_MAC_CS: + method = (LIBSSH2_KEX_METHOD *) session->local.mac; + break; + + case LIBSSH2_METHOD_MAC_SC: + method = (LIBSSH2_KEX_METHOD *) session->remote.mac; + break; + + case LIBSSH2_METHOD_COMP_CS: + method = (LIBSSH2_KEX_METHOD *) session->local.comp; + break; + + case LIBSSH2_METHOD_COMP_SC: + method = (LIBSSH2_KEX_METHOD *) session->remote.comp; + break; + + case LIBSSH2_METHOD_LANG_CS: + return ""; + break; + + case LIBSSH2_METHOD_LANG_SC: + return ""; + break; + + default: + libssh2_error(session, LIBSSH2_ERROR_INVAL, + "Invalid parameter specified for method_type", 0); + return NULL; + break; + } + + if (!method) { + libssh2_error(session, LIBSSH2_ERROR_METHOD_NONE, + "No method negotiated", 0); + return NULL; + } + + return method->name; +} + +/* }}} */ + +/* {{{ libssh2_session_abstract + * Retrieve a pointer to the abstract property + */ +LIBSSH2_API void ** +libssh2_session_abstract(LIBSSH2_SESSION * session) +{ + return &session->abstract; +} + +/* }}} */ + +/* {{{ libssh2_session_last_error + * Returns error code and populates an error string into errmsg + * If want_buf is non-zero then the string placed into errmsg must be freed by the calling program + * Otherwise it is assumed to be owned by libssh2 + */ +LIBSSH2_API int +libssh2_session_last_error(LIBSSH2_SESSION * session, char **errmsg, + int *errmsg_len, int want_buf) +{ + /* No error to report */ + if (!session->err_code) { + if (errmsg) { + if (want_buf) { + *errmsg = LIBSSH2_ALLOC(session, 1); + if (*errmsg) { + **errmsg = 0; + } + } else { + *errmsg = (char *) ""; + } + } + if (errmsg_len) { + *errmsg_len = 0; + } + return 0; + } + + if (errmsg) { + char *serrmsg = session->err_msg ? session->err_msg : (char *) ""; + int ownbuf = session->err_msg ? session->err_should_free : 0; + + if (want_buf) { + if (ownbuf) { + /* Just give the calling program the buffer */ + *errmsg = serrmsg; + session->err_should_free = 0; + } else { + /* Make a copy so the calling program can own it */ + *errmsg = LIBSSH2_ALLOC(session, session->err_msglen + 1); + if (*errmsg) { + memcpy(*errmsg, session->err_msg, session->err_msglen); + (*errmsg)[session->err_msglen] = 0; + } + } + } else { + *errmsg = serrmsg; + } + } + + if (errmsg_len) { + *errmsg_len = session->err_msglen; + } + + return session->err_code; +} + +/* }}} */ + +/* {{{ libssh2_session_last_error +* Returns error code +*/ +LIBSSH2_API int +libssh2_session_last_errno(LIBSSH2_SESSION * session) +{ + return session->err_code; +} + +/* }}} */ + +/* {{{ libssh2_session_flag + * Set/Get session flags + * Passing flag==0 will avoid changing session->flags while still returning its current value + */ +LIBSSH2_API int +libssh2_session_flag(LIBSSH2_SESSION * session, int flag, int value) +{ + if (value) { + session->flags |= flag; + } else { + session->flags &= ~flag; + } + + return session->flags; +} + +/* }}} */ + +/* {{{ _libssh2_session_set_blocking + * Set a session's blocking mode on or off, return the previous status + * when this function is called. + */ +int +_libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking) +{ + int bl = session->socket_block; + _libssh2_debug(session, LIBSSH2_DBG_CONN, + "Setting blocking mode on session %d", blocking); + if (blocking == session->socket_block) { + /* avoid if already correct */ + return bl; + } + session->socket_block = blocking; + + _libssh2_nonblock(session->socket_fd, !blocking); + + return bl; +} + +/* }}} */ + +/* {{{ libssh2_session_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_session_set_blocking(LIBSSH2_SESSION * session, int blocking) +{ + (void) _libssh2_session_set_blocking(session, blocking); +} + +/* }}} */ + +/* {{{ libssh2_session_get_blocking +* Returns a session's blocking mode on or off +*/ +LIBSSH2_API int +libssh2_session_get_blocking(LIBSSH2_SESSION * session) +{ + return session->socket_block; +} + +/* }}} */ + +/* {{{ libssh2_poll_channel_read + * Returns 0 if no data is waiting on channel, + * non-0 if data is available + */ +LIBSSH2_API int +libssh2_poll_channel_read(LIBSSH2_CHANNEL * channel, int extended) +{ + LIBSSH2_SESSION *session = channel->session; + LIBSSH2_PACKET *packet = session->packets.head; + + while (packet) + { + if ( channel->local.id == libssh2_ntohu32(packet->data + 1)) { + if ( extended == 1 && + (packet->data[0] == SSH_MSG_CHANNEL_EXTENDED_DATA + || packet->data[0] == SSH_MSG_CHANNEL_DATA )) { + return 1; + } else if ( extended == 0 && + packet->data[0] == SSH_MSG_CHANNEL_DATA) { + return 1; + } + /* else - no data of any type is ready to be read */ + } + packet = packet->next; + } + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_poll_channel_write + * Returns 0 if writing to channel would block, + * non-0 if data can be written without blocking + */ +static inline int +libssh2_poll_channel_write(LIBSSH2_CHANNEL * channel) +{ + return channel->local.window_size ? 1 : 0; +} + +/* }}} */ + +/* {{{ libssh2_poll_listener_queued + * Returns 0 if no connections are waiting to be accepted + * non-0 if one or more connections are available + */ +static inline int +libssh2_poll_listener_queued(LIBSSH2_LISTENER * listener) +{ + return listener->queue ? 1 : 0; +} + +/* }}} */ + +/* {{{ libssh2_poll + * Poll sockets, channels, and listeners for activity + */ +LIBSSH2_API int +libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) +{ + long timeout_remaining; + unsigned int i, active_fds; +#ifdef HAVE_POLL + LIBSSH2_SESSION *session = NULL; +#ifdef HAVE_ALLOCA + struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds); +#else + struct pollfd sockets[256]; + + if (nfds > 256) + /* systems without alloca use a fixed-size array, this can be fixed + if we really want to, at least if the compiler is a C99 capable one */ + return -1; +#endif + /* Setup sockets for polling */ + for(i = 0; i < nfds; i++) { + fds[i].revents = 0; + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + sockets[i].fd = fds[i].fd.socket; + sockets[i].events = fds[i].events; + sockets[i].revents = 0; + break; + + case LIBSSH2_POLLFD_CHANNEL: + sockets[i].fd = fds[i].fd.channel->session->socket_fd; + sockets[i].events = POLLIN; + sockets[i].revents = 0; + if (!session) + session = fds[i].fd.channel->session; + break; + + case LIBSSH2_POLLFD_LISTENER: + sockets[i].fd = fds[i].fd.listener->session->socket_fd; + sockets[i].events = POLLIN; + sockets[i].revents = 0; + if (!session) + session = fds[i].fd.listener->session; + break; + + default: + if (session) + libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, + "Invalid descriptor passed to libssh2_poll()", + 0); + return -1; + } + } +#elif defined(HAVE_SELECT) + LIBSSH2_SESSION *session = NULL; + int maxfd = 0; + fd_set rfds, wfds; + struct timeval tv; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + for(i = 0; i < nfds; i++) { + fds[i].revents = 0; + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + if (fds[i].events & LIBSSH2_POLLFD_POLLIN) { + FD_SET(fds[i].fd.socket, &rfds); + if (fds[i].fd.socket > maxfd) + maxfd = fds[i].fd.socket; + } + if (fds[i].events & LIBSSH2_POLLFD_POLLOUT) { + FD_SET(fds[i].fd.socket, &wfds); + if (fds[i].fd.socket > maxfd) + maxfd = fds[i].fd.socket; + } + break; + + case LIBSSH2_POLLFD_CHANNEL: + FD_SET(fds[i].fd.channel->session->socket_fd, &rfds); + if (fds[i].fd.channel->session->socket_fd > maxfd) + maxfd = fds[i].fd.channel->session->socket_fd; + if (!session) + session = fds[i].fd.channel->session; + break; + + case LIBSSH2_POLLFD_LISTENER: + FD_SET(fds[i].fd.listener->session->socket_fd, &rfds); + if (fds[i].fd.listener->session->socket_fd > maxfd) + maxfd = fds[i].fd.listener->session->socket_fd; + if (!session) + session = fds[i].fd.listener->session; + break; + + default: + if (session) + libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, + "Invalid descriptor passed to libssh2_poll()", + 0); + return -1; + } + } +#else + /* No select() or poll() + * no sockets sturcture to setup + */ + + timeout = 0; +#endif /* HAVE_POLL or HAVE_SELECT */ + + timeout_remaining = timeout; + do { +#if defined(HAVE_POLL) || defined(HAVE_SELECT) + int sysret; +#endif + + active_fds = 0; + + for(i = 0; i < nfds; i++) { + if (fds[i].events != fds[i].revents) { + switch (fds[i].type) { + case LIBSSH2_POLLFD_CHANNEL: + if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want to be ready for read */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* Not yet known to be ready for read */ + fds[i].revents |= + libssh2_poll_channel_read(fds[i].fd.channel, + 0) ? + LIBSSH2_POLLFD_POLLIN : 0; + } + if ((fds[i].events & LIBSSH2_POLLFD_POLLEXT) && /* Want to be ready for extended read */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) { /* Not yet known to be ready for extended read */ + fds[i].revents |= + libssh2_poll_channel_read(fds[i].fd.channel, + 1) ? + LIBSSH2_POLLFD_POLLEXT : 0; + } + if ((fds[i].events & LIBSSH2_POLLFD_POLLOUT) && /* Want to be ready for write */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { /* Not yet known to be ready for write */ + fds[i].revents |= + libssh2_poll_channel_write(fds[i].fd. + channel) ? + LIBSSH2_POLLFD_POLLOUT : 0; + } + if (fds[i].fd.channel->remote.close + || fds[i].fd.channel->local.close) { + fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED; + } + if (fds[i].fd.channel->session->socket_state == + LIBSSH2_SOCKET_DISCONNECTED) { + fds[i].revents |= + LIBSSH2_POLLFD_CHANNEL_CLOSED | + LIBSSH2_POLLFD_SESSION_CLOSED; + } + break; + + case LIBSSH2_POLLFD_LISTENER: + if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */ + ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */ + fds[i].revents |= + libssh2_poll_listener_queued(fds[i].fd. + listener) ? + LIBSSH2_POLLFD_POLLIN : 0; + } + if (fds[i].fd.listener->session->socket_state == + LIBSSH2_SOCKET_DISCONNECTED) { + fds[i].revents |= + LIBSSH2_POLLFD_LISTENER_CLOSED | + LIBSSH2_POLLFD_SESSION_CLOSED; + } + break; + } + } + if (fds[i].revents) { + active_fds++; + } + } + + if (active_fds) { + /* Don't block on the sockets if we have channels/listeners which are ready */ + timeout_remaining = 0; + } +#ifdef HAVE_POLL + +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval tv_begin, tv_end; + + gettimeofday((struct timeval *) &tv_begin, NULL); + sysret = poll(sockets, nfds, timeout_remaining); + gettimeofday((struct timeval *) &tv_end, NULL); + timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; + timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; + } +#else + /* If the platform doesn't support gettimeofday, + * then just make the call non-blocking and walk away + */ + sysret = poll(sockets, nfds, timeout_remaining); + timeout_remaining = 0; +#endif /* HAVE_GETTIMEOFDAY */ + + if (sysret > 0) { + for(i = 0; i < nfds; i++) { + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + fds[i].revents = sockets[i].revents; + sockets[i].revents = 0; /* In case we loop again, be nice */ + if (fds[i].revents) { + active_fds++; + } + break; + case LIBSSH2_POLLFD_CHANNEL: + if (sockets[i].events & POLLIN) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.channel->session) + > 0); + } + if (sockets[i].revents & POLLHUP) { + fds[i].revents |= + LIBSSH2_POLLFD_CHANNEL_CLOSED | + LIBSSH2_POLLFD_SESSION_CLOSED; + } + sockets[i].revents = 0; + break; + case LIBSSH2_POLLFD_LISTENER: + if (sockets[i].events & POLLIN) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.listener->session) + > 0); + } + if (sockets[i].revents & POLLHUP) { + fds[i].revents |= + LIBSSH2_POLLFD_LISTENER_CLOSED | + LIBSSH2_POLLFD_SESSION_CLOSED; + } + sockets[i].revents = 0; + break; + } + } + } +#elif defined(HAVE_SELECT) + tv.tv_sec = timeout_remaining / 1000; + tv.tv_usec = (timeout_remaining % 1000) * 1000; +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval tv_begin, tv_end; + + gettimeofday((struct timeval *) &tv_begin, NULL); + sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv); + gettimeofday((struct timeval *) &tv_end, NULL); + + timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; + timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; + } +#else + /* If the platform doesn't support gettimeofday, + * then just make the call non-blocking and walk away + */ + sysret = select(maxfd+1, &rfds, &wfds, NULL, &tv); + timeout_remaining = 0; +#endif + + if (sysret > 0) { + for(i = 0; i < nfds; i++) { + switch (fds[i].type) { + case LIBSSH2_POLLFD_SOCKET: + if (FD_ISSET(fds[i].fd.socket, &rfds)) { + fds[i].revents |= LIBSSH2_POLLFD_POLLIN; + } + if (FD_ISSET(fds[i].fd.socket, &wfds)) { + fds[i].revents |= LIBSSH2_POLLFD_POLLOUT; + } + if (fds[i].revents) { + active_fds++; + } + break; + + case LIBSSH2_POLLFD_CHANNEL: + if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.channel->session) + > 0); + } + break; + + case LIBSSH2_POLLFD_LISTENER: + if (FD_ISSET + (fds[i].fd.listener->session->socket_fd, &rfds)) { + /* Spin session until no data available */ + while (libssh2_packet_read(fds[i].fd.listener->session) + > 0); + } + break; + } + } + } +#endif /* else no select() or poll() -- timeout (and by extension timeout_remaining) will be equal to 0 */ + } while ((timeout_remaining > 0) && !active_fds); + + return active_fds; +} + +/* {{{ libssh2_session_block_direction + * Get blocked direction when a function returns LIBSSH2_ERROR_EAGAIN + * Returns LIBSSH2_SOCKET_BLOCK_INBOUND if recv() blocked + * or LIBSSH2_SOCKET_BLOCK_OUTBOUND if send() blocked + */ +LIBSSH2_API int +libssh2_session_block_directions(LIBSSH2_SESSION *session) +{ + return session->socket_block_directions; +} + +/* }}} */ Property changes on: libssh2/src/session.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.3 Added: svn:eol-style + native Index: libssh2/src/openssl.c =================================================================== --- libssh2/src/openssl.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/openssl.c (.../trunk) @@ -0,0 +1,316 @@ +/* Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. + * Author: Simon Josefsson + * Copyright (c) 2004-2006, Sara Golemon + * + * 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 + +#ifndef EVP_MAX_BLOCK_LENGTH +#define EVP_MAX_BLOCK_LENGTH 32 +#endif + +int +_libssh2_rsa_new(libssh2_rsa_ctx ** rsa, + const unsigned char *edata, + unsigned long elen, + const unsigned char *ndata, + unsigned long nlen, + const unsigned char *ddata, + unsigned long dlen, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *e1data, + unsigned long e1len, + const unsigned char *e2data, + unsigned long e2len, + const unsigned char *coeffdata, unsigned long coefflen) +{ + *rsa = RSA_new(); + + (*rsa)->e = BN_new(); + BN_bin2bn(edata, elen, (*rsa)->e); + + (*rsa)->n = BN_new(); + BN_bin2bn(ndata, nlen, (*rsa)->n); + + if (ddata) { + (*rsa)->d = BN_new(); + BN_bin2bn(ddata, dlen, (*rsa)->d); + + (*rsa)->p = BN_new(); + BN_bin2bn(pdata, plen, (*rsa)->p); + + (*rsa)->q = BN_new(); + BN_bin2bn(qdata, qlen, (*rsa)->q); + + (*rsa)->dmp1 = BN_new(); + BN_bin2bn(e1data, e1len, (*rsa)->dmp1); + + (*rsa)->dmq1 = BN_new(); + BN_bin2bn(e2data, e2len, (*rsa)->dmq1); + + (*rsa)->iqmp = BN_new(); + BN_bin2bn(coeffdata, coefflen, (*rsa)->iqmp); + } + return 0; +} + +int +_libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsactx, + const unsigned char *sig, + unsigned long sig_len, + const unsigned char *m, unsigned long m_len) +{ + unsigned char hash[SHA_DIGEST_LENGTH]; + int ret; + + SHA1(m, m_len, hash); + ret = RSA_verify(NID_sha1, hash, SHA_DIGEST_LENGTH, + (unsigned char *) sig, sig_len, rsactx); + return (ret == 1) ? 0 : -1; +} + +int +_libssh2_dsa_new(libssh2_dsa_ctx ** dsactx, + const unsigned char *p, + unsigned long p_len, + const unsigned char *q, + unsigned long q_len, + const unsigned char *g, + unsigned long g_len, + const unsigned char *y, + unsigned long y_len, + const unsigned char *x, unsigned long x_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); + + if (x_len) { + (*dsactx)->priv_key = BN_new(); + BN_bin2bn(x, x_len, (*dsactx)->priv_key); + } + + return 0; +} + +int +_libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, + const unsigned char *sig, + const unsigned char *m, unsigned long m_len) +{ + unsigned char hash[SHA_DIGEST_LENGTH]; + DSA_SIG dsasig; + int ret; + + dsasig.r = BN_new(); + BN_bin2bn(sig, 20, dsasig.r); + dsasig.s = BN_new(); + BN_bin2bn(sig + 20, 20, dsasig.s); + + libssh2_sha1(m, m_len, hash); + ret = DSA_do_verify(hash, SHA_DIGEST_LENGTH, &dsasig, dsactx); + BN_clear_free(dsasig.s); + BN_clear_free(dsasig.r); + + return (ret == 1) ? 0 : -1; +} + +int +_libssh2_cipher_init(_libssh2_cipher_ctx * h, + _libssh2_cipher_type(algo), + unsigned char *iv, unsigned char *secret, int encrypt) +{ + EVP_CIPHER_CTX_init(h); + EVP_CipherInit(h, algo(), secret, iv, encrypt); + return 0; +} + +int +_libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, + _libssh2_cipher_type(algo), + int encrypt, unsigned char *block) +{ + int blocksize = ctx->cipher->block_size; + unsigned char buf[EVP_MAX_BLOCK_LENGTH]; + int ret; + (void) algo; + (void) encrypt; + + if (blocksize == 1) { +/* Hack for arcfour. */ + blocksize = 8; + } + ret = EVP_Cipher(ctx, buf, block, blocksize); + if (ret == 1) { + memcpy(block, buf, blocksize); + } + return ret == 1 ? 0 : 1; +} + +/* TODO: Optionally call a passphrase callback specified by the + * calling program + */ +static int +passphrase_cb(char *buf, int size, int rwflag, char *passphrase) +{ + int passphrase_len = strlen(passphrase); + (void) rwflag; + + if (passphrase_len > (size - 1)) { + passphrase_len = size - 1; + } + memcpy(buf, passphrase, passphrase_len); + buf[passphrase_len] = '\0'; + + return passphrase_len; +} + +int +_libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase) +{ + (void) session; + if (!EVP_get_cipherbyname("des")) { +/* If this cipher isn't loaded it's a pretty good indication that none are. + * I have *NO DOUBT* that there's a better way to deal with this ($#&%#$(%$#( + * Someone buy me an OpenSSL manual and I'll read up on it. + */ + OpenSSL_add_all_ciphers(); + } + *rsa = PEM_read_RSAPrivateKey(fp, NULL, (void *) passphrase_cb, + (void *) passphrase); + if (!*rsa) { + return -1; + } + return 0; +} + +int +_libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase) +{ + (void) session; + if (!EVP_get_cipherbyname("des")) { +/* If this cipher isn't loaded it's a pretty good indication that none are. + * I have *NO DOUBT* that there's a better way to deal with this ($#&%#$(%$#( + * Someone buy me an OpenSSL manual and I'll read up on it. + */ + OpenSSL_add_all_ciphers(); + } + *dsa = PEM_read_DSAPrivateKey(fp, NULL, (void *) passphrase_cb, + (void *) passphrase); + if (!*dsa) { + return -1; + } + return 0; +} + +int +_libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, + libssh2_rsa_ctx * rsactx, + const unsigned char *hash, + unsigned long hash_len, + unsigned char **signature, unsigned long *signature_len) +{ + int ret; + unsigned char *sig; + unsigned int sig_len; + + sig_len = RSA_size(rsactx); + sig = LIBSSH2_ALLOC(session, sig_len); + + if (!sig) { + return -1; + } + + ret = RSA_sign(NID_sha1, hash, hash_len, sig, &sig_len, rsactx); + + if (!ret) { + LIBSSH2_FREE(session, sig); + return -1; + } + + *signature = sig; + *signature_len = sig_len; + + return 0; +} + +int +_libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, + const unsigned char *hash, + unsigned long hash_len, unsigned char *signature) +{ + DSA_SIG *sig; + int r_len, s_len, rs_pad; + (void) hash_len; + + sig = DSA_do_sign(hash, SHA_DIGEST_LENGTH, dsactx); + if (!sig) { + return -1; + } + + r_len = BN_num_bytes(sig->r); + s_len = BN_num_bytes(sig->s); + rs_pad = (2 * SHA_DIGEST_LENGTH) - (r_len + s_len); + if (rs_pad < 0) { + DSA_SIG_free(sig); + return -1; + } + + BN_bn2bin(sig->r, signature + rs_pad); + BN_bn2bin(sig->s, signature + rs_pad + r_len); + + DSA_SIG_free(sig); + + return 0; +} Property changes on: libssh2/src/openssl.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/scp.c =================================================================== --- libssh2/src/scp.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/scp.c (.../trunk) @@ -0,0 +1,811 @@ +/* Copyright (c) 2004-2008, Sara Golemon + * 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 +#include + +/* {{{ libssh2_scp_recv + * Open a channel and request a remote file via SCP + * + * NOTE: Will block in a busy loop on error. This has to be done, + * otherwise the blocking error code would erase the true + * cause of the error. + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb) +{ + int path_len = strlen(path); + int rc; + + if (session->scpRecv_state == libssh2_NB_state_idle) { + session->scpRecv_mode = 0; + session->scpRecv_size = 0; + session->scpRecv_mtime = 0; + session->scpRecv_atime = 0; + + session->scpRecv_command_len = path_len + sizeof("scp -f "); + + if (sb) { + session->scpRecv_command_len++; + } + + session->scpRecv_command = + LIBSSH2_ALLOC(session, session->scpRecv_command_len); + if (!session->scpRecv_command) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a command buffer for SCP session", + 0); + return NULL; + } + if (sb) { + memcpy(session->scpRecv_command, "scp -pf ", + sizeof("scp -pf ") - 1); + memcpy(session->scpRecv_command + sizeof("scp -pf ") - 1, path, + path_len); + } else { + memcpy(session->scpRecv_command, "scp -f ", sizeof("scp -f ") - 1); + memcpy(session->scpRecv_command + sizeof("scp -f ") - 1, path, + path_len); + } + session->scpRecv_command[session->scpRecv_command_len - 1] = '\0'; + + _libssh2_debug(session, LIBSSH2_DBG_SCP, + "Opening channel for SCP receive"); + + session->scpRecv_state = libssh2_NB_state_created; + } + + if (session->scpRecv_state == libssh2_NB_state_created) { + /* Allocate a channel */ + do { + session->scpRecv_channel = + libssh2_channel_open_ex(session, "session", + sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, + 0); + if (!session->scpRecv_channel) { + if (libssh2_session_last_errno(session) != + LIBSSH2_ERROR_EAGAIN) { + LIBSSH2_FREE(session, session->scpRecv_command); + session->scpRecv_command = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; + } else if (libssh2_session_last_errno(session) == + LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block starting up channel", 0); + return NULL; + } + } + } while (!session->scpRecv_channel); + + session->scpRecv_state = libssh2_NB_state_sent; + } + + if (session->scpRecv_state == libssh2_NB_state_sent) { + /* Request SCP for the desired file */ + rc = libssh2_channel_process_startup(session->scpRecv_channel, "exec", + sizeof("exec") - 1, + (char *) session->scpRecv_command, + session->scpRecv_command_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block requesting SCP startup", 0); + return NULL; + } else if (rc) { + LIBSSH2_FREE(session, session->scpRecv_command); + session->scpRecv_command = NULL; + goto scp_recv_error; + } + LIBSSH2_FREE(session, session->scpRecv_command); + session->scpRecv_command = NULL; + + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup"); + /* SCP ACK */ + session->scpRecv_response[0] = '\0'; + + session->scpRecv_state = libssh2_NB_state_sent1; + } + + if (session->scpRecv_state == libssh2_NB_state_sent1) { + rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, + (char *) session->scpRecv_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending initial wakeup", 0); + return NULL; + } else if (rc != 1) { + goto scp_recv_error; + } + + /* Parse SCP response */ + session->scpRecv_response_len = 0; + + session->scpRecv_state = libssh2_NB_state_sent2; + } + + if ((session->scpRecv_state == libssh2_NB_state_sent2) + || (session->scpRecv_state == libssh2_NB_state_sent3)) { + while (sb + && (session->scpRecv_response_len < + LIBSSH2_SCP_RESPONSE_BUFLEN)) { + unsigned char *s, *p; + + if (session->scpRecv_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, + (char *) session-> + scpRecv_response + + session->scpRecv_response_len, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for SCP response", 0); + return NULL; + } else if (rc <= 0) { + /* Timeout, give up */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Timed out waiting for SCP response", 0); + goto scp_recv_error; + } + session->scpRecv_response_len++; + + if (session->scpRecv_response[0] != 'T') { + /* + * Set this as the default error for here, if + * we are successful it will be replaced + */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid data in SCP response, missing Time data", + 0); + + session->scpRecv_err_len = + libssh2_channel_packet_data_len(session-> + scpRecv_channel, 0); + session->scpRecv_err_msg = + LIBSSH2_ALLOC(session, session->scpRecv_err_len + 1); + if (!session->scpRecv_err_msg) { + goto scp_recv_error; + } + memset(session->scpRecv_err_msg, 0, + session->scpRecv_err_len + 1); + + /* Read the remote error message */ + rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, + session->scpRecv_err_msg, + session->scpRecv_err_len); + if (rc <= 0) { + /* + * Since we have alread started reading this packet, it is + * already in the systems so it can't return PACKET_EAGAIN + */ + LIBSSH2_FREE(session, session->scpRecv_err_msg); + session->scpRecv_err_msg = NULL; + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Unknown error while getting error string", + 0); + goto scp_recv_error; + } + + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + session->scpRecv_err_msg, 1); + session->scpRecv_err_msg = NULL; + goto scp_recv_error; + } + + if ((session->scpRecv_response_len > 1) && + ((session-> + scpRecv_response[session->scpRecv_response_len - 1] < + '0') + || (session-> + scpRecv_response[session->scpRecv_response_len - 1] > + '9')) + && (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + ' ') + && (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + '\r') + && (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + '\n')) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid data in SCP response", 0); + goto scp_recv_error; + } + + if ((session->scpRecv_response_len < 9) + || (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + '\n')) { + if (session->scpRecv_response_len == + LIBSSH2_SCP_RESPONSE_BUFLEN) { + /* You had your chance */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Unterminated response from SCP server", + 0); + goto scp_recv_error; + } + /* 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 ((session-> + scpRecv_response[session->scpRecv_response_len - 1] == + '\r') + || (session-> + scpRecv_response[session->scpRecv_response_len - + 1] == '\n')) + session->scpRecv_response_len--; + session->scpRecv_response[session->scpRecv_response_len] = + '\0'; + + if (session->scpRecv_response_len < 8) { + /* EOL came too soon */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server, too short", + 0); + goto scp_recv_error; + } + + s = session->scpRecv_response + 1; + + p = (unsigned char *) strchr((char *) 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); + goto scp_recv_error; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_mtime = strtol((char *) s, NULL, 10); + if (errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server, invalid mtime", + 0); + goto scp_recv_error; + } + s = (unsigned char *) strchr((char *) 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); + goto scp_recv_error; + } + + /* Ignore mtime.usec */ + s++; + p = (unsigned char *) strchr((char *) 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); + goto scp_recv_error; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_atime = strtol((char *) s, NULL, 10); + if (errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server, invalid atime", + 0); + goto scp_recv_error; + } + + /* SCP ACK */ + session->scpRecv_response[0] = '\0'; + + session->scpRecv_state = libssh2_NB_state_sent3; + } + + if (session->scpRecv_state == libssh2_NB_state_sent3) { + rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, + (char *) session-> + scpRecv_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting to send SCP ACK", 0); + return NULL; + } else if (rc != 1) { + goto scp_recv_error; + } + + _libssh2_debug(session, LIBSSH2_DBG_SCP, + "mtime = %ld, atime = %ld", + session->scpRecv_mtime, session->scpRecv_atime); + + /* We *should* check that atime.usec is valid, but why let that stop use? */ + break; + } + } + + session->scpRecv_state = libssh2_NB_state_sent4; + } + + if (session->scpRecv_state == libssh2_NB_state_sent4) { + session->scpRecv_response_len = 0; + + session->scpRecv_state = libssh2_NB_state_sent5; + } + + if ((session->scpRecv_state == libssh2_NB_state_sent5) + || (session->scpRecv_state == libssh2_NB_state_sent6)) { + while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) { + char *s, *p, *e = NULL; + + if (session->scpRecv_state == libssh2_NB_state_sent5) { + rc = libssh2_channel_read_ex(session->scpRecv_channel, 0, + (char *) session-> + scpRecv_response + + session->scpRecv_response_len, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for SCP response", 0); + return NULL; + } else if (rc <= 0) { + /* Timeout, give up */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Timed out waiting for SCP response", 0); + goto scp_recv_error; + } + session->scpRecv_response_len++; + + if (session->scpRecv_response[0] != 'C') { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server", 0); + goto scp_recv_error; + } + + if ((session->scpRecv_response_len > 1) && + (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + '\r') + && (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + '\n') + && + ((session-> + scpRecv_response[session->scpRecv_response_len - 1] < 32) + || (session-> + scpRecv_response[session->scpRecv_response_len - 1] > + 126))) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid data in SCP response", 0); + goto scp_recv_error; + } + + if ((session->scpRecv_response_len < 7) + || (session-> + scpRecv_response[session->scpRecv_response_len - 1] != + '\n')) { + if (session->scpRecv_response_len == + LIBSSH2_SCP_RESPONSE_BUFLEN) { + /* You had your chance */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Unterminated response from SCP server", + 0); + goto scp_recv_error; + } + /* 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 ((session-> + scpRecv_response[session->scpRecv_response_len - 1] == + '\r') + || (session-> + scpRecv_response[session->scpRecv_response_len - + 1] == '\n')) { + session->scpRecv_response_len--; + } + session->scpRecv_response[session->scpRecv_response_len] = + '\0'; + + if (session->scpRecv_response_len < 6) { + /* EOL came too soon */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server, too short", + 0); + goto scp_recv_error; + } + + s = (char *) session->scpRecv_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); + goto scp_recv_error; + } + + *(p++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_mode = strtol(s, &e, 8); + if ((e && *e) || errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server, invalid mode", + 0); + goto scp_recv_error; + } + + 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); + goto scp_recv_error; + } + + *(s++) = '\0'; + /* Make sure we don't get fooled by leftover values */ + errno = 0; + session->scpRecv_size = scpsize_strtol(p, &e, 10); + if ((e && *e) || errno) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid response from SCP server, invalid size", + 0); + goto scp_recv_error; + } + + /* SCP ACK */ + session->scpRecv_response[0] = '\0'; + + session->scpRecv_state = libssh2_NB_state_sent6; + } + + if (session->scpRecv_state == libssh2_NB_state_sent6) { + rc = libssh2_channel_write_ex(session->scpRecv_channel, 0, + (char *) session-> + scpRecv_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending SCP ACK", 0); + return NULL; + } else if (rc != 1) { + goto scp_recv_error; + } + _libssh2_debug(session, LIBSSH2_DBG_SCP, + "mode = 0%lo size = %ld", session->scpRecv_mode, + session->scpRecv_size); + + /* We *should* check that basename is valid, but why let that stop us? */ + break; + } + } + + session->scpRecv_state = libssh2_NB_state_sent7; + } + + if (sb) { + memset(sb, 0, sizeof(struct stat)); + + sb->st_mtime = session->scpRecv_mtime; + sb->st_atime = session->scpRecv_atime; + sb->st_size = session->scpRecv_size; + sb->st_mode = session->scpRecv_mode; + } + + session->scpRecv_state = libssh2_NB_state_idle; + return session->scpRecv_channel; + + scp_recv_error: + while (libssh2_channel_free(session->scpRecv_channel) == PACKET_EAGAIN); + session->scpRecv_channel = NULL; + session->scpRecv_state = libssh2_NB_state_idle; + return NULL; +} + +/* }}} */ + +/* {{{ libssh2_scp_send_ex + * Send a file using SCP + * + * NOTE: Will block in a busy loop on error. This has to be done, + * otherwise the blocking error code would erase the true + * cause of the error. + */ +LIBSSH2_API LIBSSH2_CHANNEL * +libssh2_scp_send_ex(LIBSSH2_SESSION * session, const char *path, int mode, + size_t size, long mtime, long atime) +{ + int path_len = strlen(path); + unsigned const char *base; + int rc; + + if (session->scpSend_state == libssh2_NB_state_idle) { + session->scpSend_command_len = path_len + sizeof("scp -t "); + + if (mtime || atime) { + session->scpSend_command_len++; + } + + session->scpSend_command = + LIBSSH2_ALLOC(session, session->scpSend_command_len); + if (!session->scpSend_command) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a command buffer for scp session", + 0); + return NULL; + } + + if (mtime || atime) { + memcpy(session->scpSend_command, "scp -pt ", + sizeof("scp -pt ") - 1); + memcpy(session->scpSend_command + sizeof("scp -pt ") - 1, path, + path_len); + } else { + memcpy(session->scpSend_command, "scp -t ", sizeof("scp -t ") - 1); + memcpy(session->scpSend_command + sizeof("scp -t ") - 1, path, + path_len); + } + session->scpSend_command[session->scpSend_command_len - 1] = '\0'; + + _libssh2_debug(session, LIBSSH2_DBG_SCP, + "Opening channel for SCP send"); + /* Allocate a channel */ + + session->scpSend_state = libssh2_NB_state_created; + } + + if (session->scpSend_state == libssh2_NB_state_created) { + session->scpSend_channel = + libssh2_channel_open_ex(session, "session", sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0); + if (!session->scpSend_channel) { + if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { + /* previous call set libssh2_session_last_error(), pass it through */ + LIBSSH2_FREE(session, session->scpSend_command); + session->scpSend_command = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; + } else if (libssh2_session_last_errno(session) == + LIBSSH2_ERROR_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block starting up channel", 0); + return NULL; + } + } + + session->scpSend_state = libssh2_NB_state_sent; + } + + if (session->scpSend_state == libssh2_NB_state_sent) { + /* Request SCP for the desired file */ + rc = libssh2_channel_process_startup(session->scpSend_channel, "exec", + sizeof("exec") - 1, + (char *) session->scpSend_command, + session->scpSend_command_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block requesting SCP startup", 0); + return NULL; + } else if (rc) { + /* previous call set libssh2_session_last_error(), pass it through */ + LIBSSH2_FREE(session, session->scpSend_command); + session->scpSend_command = NULL; + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Unknown error while getting error string", 0); + goto scp_send_error; + } + LIBSSH2_FREE(session, session->scpSend_command); + session->scpSend_command = NULL; + + session->scpSend_state = libssh2_NB_state_sent1; + } + + if (session->scpSend_state == libssh2_NB_state_sent1) { + /* Wait for ACK */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, + (char *) session->scpSend_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for response from remote", 0); + return NULL; + } else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid ACK response from remote", 0); + goto scp_send_error; + } + + if (mtime || atime) { + /* Send mtime and atime to be used for file */ + session->scpSend_response_len = + snprintf((char *) session->scpSend_response, + LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime, + atime); + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", + session->scpSend_response); + } + + session->scpSend_state = libssh2_NB_state_sent2; + } + + /* Send mtime and atime to be used for file */ + if (mtime || atime) { + if (session->scpSend_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_write_ex(session->scpSend_channel, 0, + (char *) session->scpSend_response, + session->scpSend_response_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending time data for SCP file", 0); + return NULL; + } else if (rc != session->scpSend_response_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send time data for SCP file", 0); + goto scp_send_error; + } + + session->scpSend_state = libssh2_NB_state_sent3; + } + + if (session->scpSend_state == libssh2_NB_state_sent3) { + /* Wait for ACK */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, + (char *) session->scpSend_response, + 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for response", 0); + return NULL; + } else if ((rc <= 0) || (session->scpSend_response[0] != 0)) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid ACK response from remote", 0); + goto scp_send_error; + } + + session->scpSend_state = libssh2_NB_state_sent4; + } + } else { + if (session->scpSend_state == libssh2_NB_state_sent2) { + session->scpSend_state = libssh2_NB_state_sent4; + } + } + + if (session->scpSend_state == libssh2_NB_state_sent4) { + /* Send mode, size, and basename */ + base = (unsigned char *) strrchr(path, '/'); + if (base) { + base++; + } else { + base = (unsigned char *) path; + } + + session->scpSend_response_len = + snprintf((char *) session->scpSend_response, + LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode, + (unsigned long) size, base); + _libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s", + session->scpSend_response); + + session->scpSend_state = libssh2_NB_state_sent5; + } + + if (session->scpSend_state == libssh2_NB_state_sent5) { + rc = libssh2_channel_write_ex(session->scpSend_channel, 0, + (char *) session->scpSend_response, + session->scpSend_response_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block send core file data for SCP file", 0); + return NULL; + } else if (rc != session->scpSend_response_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send core file data for SCP file", 0); + goto scp_send_error; + } + + session->scpSend_state = libssh2_NB_state_sent6; + } + + if (session->scpSend_state == libssh2_NB_state_sent6) { + /* Wait for ACK */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, + (char *) session->scpSend_response, 1); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for response", 0); + return NULL; + } else if (rc <= 0) { + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid ACK response from remote", 0); + goto scp_send_error; + } else if (session->scpSend_response[0] != 0) { + /* + * Set this as the default error for here, if + * we are successful it will be replaced + */ + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + "Invalid ACK response from remote", 0); + + session->scpSend_err_len = + libssh2_channel_packet_data_len(session->scpSend_channel, 0); + session->scpSend_err_msg = + LIBSSH2_ALLOC(session, session->scpSend_err_len + 1); + if (!session->scpSend_err_msg) { + goto scp_send_error; + } + memset(session->scpSend_err_msg, 0, session->scpSend_err_len + 1); + + /* Read the remote error message */ + rc = libssh2_channel_read_ex(session->scpSend_channel, 0, + session->scpSend_err_msg, + session->scpSend_err_len); + if (rc <= 0) { + /* + * Since we have alread started reading this packet, it is + * already in the systems so it can't return PACKET_EAGAIN + */ + LIBSSH2_FREE(session, session->scpSend_err_msg); + session->scpSend_err_msg = NULL; + goto scp_send_error; + } + + libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL, + session->scpSend_err_msg, 1); + session->scpSend_err_msg = NULL; + goto scp_send_error; + } + } + + session->scpSend_state = libssh2_NB_state_idle; + + return session->scpSend_channel; + + scp_send_error: + while (libssh2_channel_free(session->scpSend_channel) == PACKET_EAGAIN); + session->scpSend_channel = NULL; + session->scpSend_state = libssh2_NB_state_idle; + return NULL; +} + +/* }}} */ Property changes on: libssh2/src/scp.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/hostkey.c =================================================================== --- libssh2/src/hostkey.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/hostkey.c (.../trunk) @@ -0,0 +1,455 @@ +/* Copyright (c) 2004-2006, Sara Golemon + * 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" + +/* Needed for struct iovec on some platforms */ +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#if LIBSSH2_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, + const unsigned char *hostkey_data, + unsigned long hostkey_data_len, + void **abstract) +{ + libssh2_rsa_ctx *rsactx; + const unsigned char *s, *e, *n; + unsigned long len, e_len, n_len; + + (void) hostkey_data_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((char *) 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; + + if (_libssh2_rsa_new(&rsactx, e, e_len, n, n_len, NULL, 0, + NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0)) + return -1; + + *abstract = rsactx; + + return 0; +} + +/* }}} */ + +/* {{{ 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, + const char *privkeyfile, + unsigned const char *passphrase, + void **abstract) +{ + libssh2_rsa_ctx *rsactx; + FILE *fp; + int ret; + + if (*abstract) { + libssh2_hostkey_method_ssh_rsa_dtor(session, abstract); + *abstract = NULL; + } + + fp = fopen(privkeyfile, "r"); + if (!fp) { + return -1; + } + + ret = _libssh2_rsa_new_private(&rsactx, session, fp, passphrase); + fclose(fp); + if (ret) { + return -1; + } + + *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) +{ + libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract); + (void) session; + + /* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */ + sig += 15; + sig_len -= 15; + return _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len); +} + +/* }}} */ + +/* {{{ 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) +{ + libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract); + int ret; + unsigned int i; + unsigned char hash[SHA_DIGEST_LENGTH]; + libssh2_sha1_ctx ctx; + + libssh2_sha1_init(&ctx); + for(i = 0; i < veccount; i++) { + libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len); + } + libssh2_sha1_final(ctx, hash); + + ret = _libssh2_rsa_sha1_sign(session, rsactx, hash, SHA_DIGEST_LENGTH, + signature, signature_len); + if (ret) { + return -1; + } + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_hostkey_method_ssh_rsa_dtor + * Shutdown the hostkey + */ +static int +libssh2_hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session, void **abstract) +{ + libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract); + (void) session; + + _libssh2_rsa_free(rsactx); + + *abstract = NULL; + + return 0; +} + +/* }}} */ + +static const 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_signv, + NULL, /* encrypt */ + libssh2_hostkey_method_ssh_rsa_dtor, +}; +#endif /* LIBSSH2_RSA */ + +#if LIBSSH2_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, + const unsigned char *hostkey_data, + unsigned long hostkey_data_len, + void **abstract) +{ + libssh2_dsa_ctx *dsactx; + const unsigned char *p, *q, *g, *y, *s; + unsigned long p_len, q_len, g_len, y_len, len; + (void) hostkey_data_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((char *) 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; + + _libssh2_dsa_new(&dsactx, p, p_len, q, q_len, g, g_len, y, y_len, NULL, 0); + + *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, + const char *privkeyfile, + unsigned const char *passphrase, + void **abstract) +{ + libssh2_dsa_ctx *dsactx; + FILE *fp; + int ret; + + if (*abstract) { + libssh2_hostkey_method_ssh_dss_dtor(session, abstract); + *abstract = NULL; + } + + fp = fopen(privkeyfile, "r"); + if (!fp) { + return -1; + } + + ret = _libssh2_dsa_new_private(&dsactx, session, fp, passphrase); + fclose(fp); + if (ret) { + return -1; + } + + *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) +{ + libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); + + /* 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; + } + return _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len); +} + +/* }}} */ + +/* {{{ 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) +{ + libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); + unsigned char hash[SHA_DIGEST_LENGTH]; + libssh2_sha1_ctx ctx; + unsigned int i; + + *signature = LIBSSH2_ALLOC(session, 2 * SHA_DIGEST_LENGTH); + if (!*signature) { + return -1; + } + + *signature_len = 2 * SHA_DIGEST_LENGTH; + memset(*signature, 0, 2 * SHA_DIGEST_LENGTH); + + libssh2_sha1_init(&ctx); + for(i = 0; i < veccount; i++) { + libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len); + } + libssh2_sha1_final(ctx, hash); + + if (_libssh2_dsa_sha1_sign(dsactx, hash, SHA_DIGEST_LENGTH, *signature)) { + LIBSSH2_FREE(session, *signature); + return -1; + } + + 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) +{ + libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract); + (void) session; + + _libssh2_dsa_free(dsactx); + + *abstract = NULL; + + return 0; +} + +/* }}} */ + +static const 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_signv, + NULL, /* encrypt */ + libssh2_hostkey_method_ssh_dss_dtor, +}; +#endif /* LIBSSH2_DSA */ + +static const LIBSSH2_HOSTKEY_METHOD *_libssh2_hostkey_methods[] = { +#if LIBSSH2_RSA + &libssh2_hostkey_method_ssh_rsa, +#endif /* LIBSSH2_RSA */ +#if LIBSSH2_DSA + &libssh2_hostkey_method_ssh_dss, +#endif /* LIBSSH2_DSA */ + NULL +}; + +const LIBSSH2_HOSTKEY_METHOD ** +libssh2_hostkey_methods(void) +{ + return _libssh2_hostkey_methods; +} + +/* {{{ libssh2_hostkey_hash + * Returns hash signature + * Returned buffer should NOT be freed + * Length of buffer is determined by hash type + * i.e. MD5 == 16, SHA1 == 20 + */ +LIBSSH2_API const char * +libssh2_hostkey_hash(LIBSSH2_SESSION * session, int hash_type) +{ + switch (hash_type) { +#if LIBSSH2_MD5 + case LIBSSH2_HOSTKEY_HASH_MD5: + return (char *) session->server_hostkey_md5; + break; +#endif /* LIBSSH2_MD5 */ + case LIBSSH2_HOSTKEY_HASH_SHA1: + return (char *) session->server_hostkey_sha1; + break; + default: + return NULL; + } +} + +/* }}} */ Property changes on: libssh2/src/hostkey.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.2 Added: svn:eol-style + native Index: libssh2/src/publickey.c =================================================================== --- libssh2/src/publickey.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/publickey.c (.../trunk) @@ -0,0 +1,1094 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * Neither the name of the copyright holder nor the names + * of any other contributors may be used to endorse or + * promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include "libssh2_priv.h" +#include "libssh2_publickey.h" + +#define LIBSSH2_PUBLICKEY_VERSION 2 + +/* Numericised response codes -- Not IETF standard, just a local representation */ +#define LIBSSH2_PUBLICKEY_RESPONSE_STATUS 0 +#define LIBSSH2_PUBLICKEY_RESPONSE_VERSION 1 +#define LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY 2 + +typedef struct _LIBSSH2_PUBLICKEY_CODE_LIST +{ + int code; + const char *name; + int name_len; +} LIBSSH2_PUBLICKEY_CODE_LIST; + +static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_response_codes[] = { + {LIBSSH2_PUBLICKEY_RESPONSE_STATUS, "status", sizeof("status") - 1} + , + {LIBSSH2_PUBLICKEY_RESPONSE_VERSION, "version", sizeof("version") - 1} + , + {LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY, "publickey", sizeof("publickey") - 1} + , + {0, NULL, 0} +}; + +/* PUBLICKEY status codes -- IETF defined */ +#define LIBSSH2_PUBLICKEY_SUCCESS 0 +#define LIBSSH2_PUBLICKEY_ACCESS_DENIED 1 +#define LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED 2 +#define LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED 3 +#define LIBSSH2_PUBLICKEY_KEY_NOT_FOUND 4 +#define LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED 5 +#define LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT 6 +#define LIBSSH2_PUBLICKEY_GENERAL_FAILURE 7 +#define LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED 8 + +#define LIBSSH2_PUBLICKEY_STATUS_CODE_MAX 8 + +static const LIBSSH2_PUBLICKEY_CODE_LIST libssh2_publickey_status_codes[] = { + {LIBSSH2_PUBLICKEY_SUCCESS, "success", sizeof("success") - 1} + , + {LIBSSH2_PUBLICKEY_ACCESS_DENIED, "access denied", + sizeof("access denied") - 1} + , + {LIBSSH2_PUBLICKEY_STORAGE_EXCEEDED, "storage exceeded", + sizeof("storage exceeded") - 1} + , + {LIBSSH2_PUBLICKEY_VERSION_NOT_SUPPORTED, "version not supported", + sizeof("version not supported") - 1} + , + {LIBSSH2_PUBLICKEY_KEY_NOT_FOUND, "key not found", + sizeof("key not found") - 1} + , + {LIBSSH2_PUBLICKEY_KEY_NOT_SUPPORTED, "key not supported", + sizeof("key not supported") - 1} + , + {LIBSSH2_PUBLICKEY_KEY_ALREADY_PRESENT, "key already present", + sizeof("key already present") - 1} + , + {LIBSSH2_PUBLICKEY_GENERAL_FAILURE, "general failure", + sizeof("general failure") - 1} + , + {LIBSSH2_PUBLICKEY_REQUEST_NOT_SUPPORTED, "request not supported", + sizeof("request not supported") - 1} + , + {0, NULL, 0} +}; + +/* {{{ libssh2_publickey_status_error + * Format an error message from a status code + */ +#define LIBSSH2_PUBLICKEY_STATUS_TEXT_START "Publickey Subsystem Error: \"" +#define LIBSSH2_PUBLICKEY_STATUS_TEXT_MID "\" Server Reports: \"" +#define LIBSSH2_PUBLICKEY_STATUS_TEXT_END "\"" +static void +libssh2_publickey_status_error(const LIBSSH2_PUBLICKEY * pkey, + LIBSSH2_SESSION * session, int status, + const unsigned char *message, int message_len) +{ + const char *status_text; + int status_text_len; + char *m, *s; + int m_len; + + /* GENERAL_FAILURE got remapped between version 1 and 2 */ + if (status == 6 && pkey && pkey->version == 1) { + status = 7; + } + + if (status < 0 || status > LIBSSH2_PUBLICKEY_STATUS_CODE_MAX) { + status_text = "unknown"; + status_text_len = sizeof("unknown") - 1; + } else { + status_text = libssh2_publickey_status_codes[status].name; + status_text_len = libssh2_publickey_status_codes[status].name_len; + } + + m_len = + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1) + status_text_len + + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1) + message_len + + (sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1); + m = LIBSSH2_ALLOC(session, m_len + 1); + if (!m) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for status message", 0); + return; + } + s = m; + memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_START, + sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1); + s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_START) - 1; + memcpy(s, status_text, status_text_len); + s += status_text_len; + memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_MID, + sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1); + s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_MID) - 1; + memcpy(s, message, message_len); + s += message_len; + memcpy(s, LIBSSH2_PUBLICKEY_STATUS_TEXT_END, + sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END) - 1); + s += sizeof(LIBSSH2_PUBLICKEY_STATUS_TEXT_END); + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, m, 1); +} + +/* }}} */ + +/* {{{ libssh2_publickey_packet_receive + * Read a packet from the subsystem + */ +static int +libssh2_publickey_packet_receive(LIBSSH2_PUBLICKEY * pkey, + unsigned char **data, unsigned long *data_len) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + unsigned char buffer[4]; + int rc; + + if (pkey->receive_state == libssh2_NB_state_idle) { + rc = libssh2_channel_read_ex(channel, 0, (char *) buffer, 4); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc != 4) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Invalid response from publickey subsystem", 0); + return -1; + } + + pkey->receive_packet_len = libssh2_ntohu32(buffer); + pkey->receive_packet = + LIBSSH2_ALLOC(session, pkey->receive_packet_len); + if (!pkey->receive_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate publickey response buffer", 0); + return -1; + } + + pkey->receive_state = libssh2_NB_state_sent; + } + + if (pkey->receive_state == libssh2_NB_state_sent) { + rc = libssh2_channel_read_ex(channel, 0, (char *) pkey->receive_packet, + pkey->receive_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc != (int)pkey->receive_packet_len) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for publickey subsystem response packet", + 0); + LIBSSH2_FREE(session, pkey->receive_packet); + pkey->receive_packet = NULL; + pkey->receive_state = libssh2_NB_state_idle; + return -1; + } + + *data = pkey->receive_packet; + *data_len = pkey->receive_packet_len; + } + + pkey->receive_state = libssh2_NB_state_idle; + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_publickey_response_id + * Translate a string response name to a numeric code + * Data will be incremented by 4 + response_len on success only + */ +static int +libssh2_publickey_response_id(unsigned char **pdata, int data_len) +{ + unsigned long response_len; + unsigned char *data = *pdata; + const LIBSSH2_PUBLICKEY_CODE_LIST *codes = + libssh2_publickey_response_codes; + + if (data_len < 4) { + /* Malformed response */ + return -1; + } + response_len = libssh2_ntohu32(data); + data += 4; + data_len -= 4; + if (data_len < (int)response_len) { + /* Malformed response */ + return -1; + } + + while (codes->name) { + if ((unsigned long)codes->name_len == response_len && + strncmp(codes->name, (char *) data, response_len) == 0) { + *pdata = data + response_len; + return codes->code; + } + codes++; + } + + return -1; +} + +/* }}} */ + +/* {{{ libssh2_publickey_response_success + * Generic helper routine to wait for success response and nothing else + */ +static int +libssh2_publickey_response_success(LIBSSH2_PUBLICKEY * pkey) +{ + LIBSSH2_SESSION *session = pkey->channel->session; + unsigned char *data, *s; + unsigned long data_len; + int response; + int rc; + + while (1) { + rc = libssh2_publickey_packet_receive(pkey, &data, &data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for response from publickey subsystem", + 0); + return -1; + } + + s = data; + if ((response = libssh2_publickey_response_id(&s, data_len)) < 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Invalid publickey subsystem response code", 0); + LIBSSH2_FREE(session, data); + return -1; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error, or processing complete */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(s); + s += 4; + descr_len = libssh2_ntohu32(s); + s += 4; + descr = s; + s += descr_len; + lang_len = libssh2_ntohu32(s); + s += 4; + lang = s; + s += lang_len; + + if (s > data + data_len) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Malformed publickey subsystem packet", 0); + LIBSSH2_FREE(session, data); + return -1; + } + + if (status == LIBSSH2_PUBLICKEY_SUCCESS) { + LIBSSH2_FREE(session, data); + return 0; + } + + libssh2_publickey_status_error(pkey, session, status, descr, + descr_len); + LIBSSH2_FREE(session, data); + return -1; + } + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Unexpected publickey subsystem response, ignoring", + 0); + LIBSSH2_FREE(session, data); + data = NULL; + } + } + /* never reached, but include `return` to silence compiler warnings */ + return -1; +} + +/* }}} */ + + +/* ***************** + * Publickey API * + ***************** */ + +/* {{{ libssh2_publickey_init + * Startup the publickey subsystem + */ +LIBSSH2_API LIBSSH2_PUBLICKEY * +libssh2_publickey_init(LIBSSH2_SESSION * session) +{ + /* 19 = packet_len(4) + version_len(4) + "version"(7) + version_num(4) */ + unsigned char buffer[19]; + unsigned char *s; + int response; + int rc; + + if (session->pkeyInit_state == libssh2_NB_state_idle) { + session->pkeyInit_data = NULL; + session->pkeyInit_pkey = NULL; + session->pkeyInit_channel = NULL; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Initializing publickey subsystem"); + + session->pkeyInit_state = libssh2_NB_state_allocated; + } + + if (session->pkeyInit_state == libssh2_NB_state_allocated) { + do { + session->pkeyInit_channel = + libssh2_channel_open_ex(session, "session", + sizeof("session") - 1, + LIBSSH2_CHANNEL_WINDOW_DEFAULT, + LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, + 0); + if (!session->pkeyInit_channel + && (libssh2_session_last_errno(session) == + LIBSSH2_ERROR_EAGAIN)) { + /* The error state is already set, so leave it */ + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block to startup channel", 0); + return NULL; + } else if (!session->pkeyInit_channel + && (libssh2_session_last_errno(session) != + LIBSSH2_ERROR_EAGAIN)) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, + "Unable to startup channel", 0); + goto err_exit; + } + } while (!session->pkeyInit_channel); + + session->pkeyInit_state = libssh2_NB_state_sent; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent) { + rc = libssh2_channel_process_startup(session->pkeyInit_channel, + "subsystem", + sizeof("subsystem") - 1, + "publickey", strlen("publickey")); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block starting publickey subsystem", 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE, + "Unable to request publickey subsystem", 0); + goto err_exit; + } + + session->pkeyInit_state = libssh2_NB_state_sent1; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent1) { + rc = libssh2_channel_handle_extended_data2(session->pkeyInit_channel, + LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block starting publickey subsystem", 0); + return NULL; + } + + session->pkeyInit_pkey = + LIBSSH2_ALLOC(session, sizeof(LIBSSH2_PUBLICKEY)); + if (!session->pkeyInit_pkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate a new publickey structure", 0); + goto err_exit; + } + memset(session->pkeyInit_pkey, 0, sizeof(LIBSSH2_PUBLICKEY)); + session->pkeyInit_pkey->channel = session->pkeyInit_channel; + session->pkeyInit_pkey->version = 0; + + s = buffer; + libssh2_htonu32(s, 4 + (sizeof("version") - 1) + 4); + s += 4; + libssh2_htonu32(s, sizeof("version") - 1); + s += 4; + memcpy(s, "version", sizeof("version") - 1); + s += sizeof("version") - 1; + libssh2_htonu32(s, LIBSSH2_PUBLICKEY_VERSION); + s += 4; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Sending publickey version packet advertising version %d support", + (int) LIBSSH2_PUBLICKEY_VERSION); + + session->pkeyInit_state = libssh2_NB_state_sent2; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent2) { + rc = libssh2_channel_write_ex(session->pkeyInit_channel, 0, + (char *) buffer, (s - buffer)); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block sending publickey version packet", 0); + return NULL; + } else if ((s - buffer) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send publickey version packet", 0); + goto err_exit; + } + + session->pkeyInit_state = libssh2_NB_state_sent3; + } + + if (session->pkeyInit_state == libssh2_NB_state_sent3) { + while (1) { + rc = libssh2_publickey_packet_receive(session->pkeyInit_pkey, + &session->pkeyInit_data, + &session->pkeyInit_data_len); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block waiting for response from publickey subsystem", + 0); + return NULL; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for response from publickey subsystem", + 0); + goto err_exit; + } + + s = session->pkeyInit_data; + if ((response = + libssh2_publickey_response_id(&s, + session->pkeyInit_data_len)) < + 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Invalid publickey subsystem response code", 0); + goto err_exit; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(s); + s += 4; + descr_len = libssh2_ntohu32(s); + s += 4; + descr = s; + s += descr_len; + lang_len = libssh2_ntohu32(s); + s += 4; + lang = s; + s += lang_len; + + if (s > + session->pkeyInit_data + session->pkeyInit_data_len) { + libssh2_error(session, + LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Malformed publickey subsystem packet", + 0); + goto err_exit; + } + + libssh2_publickey_status_error(NULL, session, status, + descr, descr_len); + goto err_exit; + } + + case LIBSSH2_PUBLICKEY_RESPONSE_VERSION: + /* What we want */ + session->pkeyInit_pkey->version = libssh2_ntohu32(s); + if (session->pkeyInit_pkey->version > + LIBSSH2_PUBLICKEY_VERSION) { + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Truncating remote publickey version from %lu", + session->pkeyInit_pkey->version); + session->pkeyInit_pkey->version = + LIBSSH2_PUBLICKEY_VERSION; + } + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Enabling publickey subsystem version %lu", + session->pkeyInit_pkey->version); + LIBSSH2_FREE(session, session->pkeyInit_data); + session->pkeyInit_data = NULL; + session->pkeyInit_state = libssh2_NB_state_idle; + return session->pkeyInit_pkey; + + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Unexpected publickey subsystem response, ignoring", + 0); + LIBSSH2_FREE(session, session->pkeyInit_data); + session->pkeyInit_data = NULL; + } + } + } + + /* Never reached except by direct goto */ + err_exit: + session->pkeyInit_state = libssh2_NB_state_sent4; + if (session->pkeyInit_channel) { + rc = libssh2_channel_close(session->pkeyInit_channel); + if (rc == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block closing channel", 0); + return NULL; + } + } + if (session->pkeyInit_pkey) { + LIBSSH2_FREE(session, session->pkeyInit_pkey); + session->pkeyInit_pkey = NULL; + } + if (session->pkeyInit_data) { + LIBSSH2_FREE(session, session->pkeyInit_data); + session->pkeyInit_data = NULL; + } + session->pkeyInit_state = libssh2_NB_state_idle; + return NULL; +} + +/* }}} */ + +/* {{{ libssh2_publickey_add_ex + * Add a new public key entry + */ +LIBSSH2_API int +libssh2_publickey_add_ex(LIBSSH2_PUBLICKEY * pkey, const unsigned char *name, + unsigned long name_len, const unsigned char *blob, + unsigned long blob_len, char overwrite, + unsigned long num_attrs, + const libssh2_publickey_attribute attrs[]) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + /* 19 = packet_len(4) + add_len(4) + "add"(3) + name_len(4) + {name} blob_len(4) + {blob} */ + unsigned long i, packet_len = 19 + name_len + blob_len; + unsigned char *comment = NULL; + unsigned long comment_len = 0; + int rc; + + if (pkey->add_state == libssh2_NB_state_idle) { + pkey->add_packet = NULL; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, "Adding %s publickey", + name); + + if (pkey->version == 1) { + for(i = 0; i < num_attrs; i++) { + /* Search for a comment attribute */ + if (attrs[i].name_len == (sizeof("comment") - 1) && + strncmp(attrs[i].name, "comment", + sizeof("comment") - 1) == 0) { + comment = (unsigned char *) attrs[i].value; + comment_len = attrs[i].value_len; + break; + } + } + packet_len += 4 + comment_len; + } else { + packet_len += 5; /* overwrite(1) + attribute_count(4) */ + for(i = 0; i < num_attrs; i++) { + packet_len += 9 + attrs[i].name_len + attrs[i].value_len; + /* name_len(4) + value_len(4) + mandatory(1) */ + } + } + + pkey->add_packet = LIBSSH2_ALLOC(session, packet_len); + if (!pkey->add_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for publickey \"add\" packet", + 0); + return -1; + } + + pkey->add_s = pkey->add_packet; + libssh2_htonu32(pkey->add_s, packet_len - 4); + pkey->add_s += 4; + libssh2_htonu32(pkey->add_s, sizeof("add") - 1); + pkey->add_s += 4; + memcpy(pkey->add_s, "add", sizeof("add") - 1); + pkey->add_s += sizeof("add") - 1; + if (pkey->version == 1) { + libssh2_htonu32(pkey->add_s, comment_len); + pkey->add_s += 4; + if (comment) { + memcpy(pkey->add_s, comment, comment_len); + pkey->add_s += comment_len; + } + + libssh2_htonu32(pkey->add_s, name_len); + pkey->add_s += 4; + memcpy(pkey->add_s, name, name_len); + pkey->add_s += name_len; + libssh2_htonu32(pkey->add_s, blob_len); + pkey->add_s += 4; + memcpy(pkey->add_s, blob, blob_len); + pkey->add_s += blob_len; + } else { + /* Version == 2 */ + + libssh2_htonu32(pkey->add_s, name_len); + pkey->add_s += 4; + memcpy(pkey->add_s, name, name_len); + pkey->add_s += name_len; + libssh2_htonu32(pkey->add_s, blob_len); + pkey->add_s += 4; + memcpy(pkey->add_s, blob, blob_len); + pkey->add_s += blob_len; + *(pkey->add_s++) = overwrite ? 0x01 : 0; + libssh2_htonu32(pkey->add_s, num_attrs); + pkey->add_s += 4; + for(i = 0; i < num_attrs; i++) { + libssh2_htonu32(pkey->add_s, attrs[i].name_len); + pkey->add_s += 4; + memcpy(pkey->add_s, attrs[i].name, attrs[i].name_len); + pkey->add_s += attrs[i].name_len; + libssh2_htonu32(pkey->add_s, attrs[i].value_len); + pkey->add_s += 4; + memcpy(pkey->add_s, attrs[i].value, attrs[i].value_len); + pkey->add_s += attrs[i].value_len; + *(pkey->add_s++) = attrs[i].mandatory ? 0x01 : 0; + } + } + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Sending publickey \"add\" packet: type=%s blob_len=%ld num_attrs=%ld", + name, blob_len, num_attrs); + + pkey->add_state = libssh2_NB_state_created; + } + + if (pkey->add_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) pkey->add_packet, + (pkey->add_s - pkey->add_packet)); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if ((pkey->add_s - pkey->add_packet) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send publickey add packet", 0); + LIBSSH2_FREE(session, pkey->add_packet); + pkey->add_packet = NULL; + return -1; + } + LIBSSH2_FREE(session, pkey->add_packet); + pkey->add_packet = NULL; + + pkey->add_state = libssh2_NB_state_sent; + } + + rc = libssh2_publickey_response_success(pkey); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + pkey->add_state = libssh2_NB_state_idle; + + return rc; +} + +/* }}} */ + +/* {{{ libssh2_publickey_remove_ex + * Remove an existing publickey so that authentication can no longer be performed using it + */ +LIBSSH2_API int +libssh2_publickey_remove_ex(LIBSSH2_PUBLICKEY * pkey, + const unsigned char *name, unsigned long name_len, + const unsigned char *blob, unsigned long blob_len) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + /* 22 = packet_len(4) + remove_len(4) + "remove"(6) + name_len(4) + {name} + blob_len(4) + {blob} */ + unsigned long packet_len = 22 + name_len + blob_len; + int rc; + + if (pkey->remove_state == libssh2_NB_state_idle) { + pkey->remove_packet = NULL; + + pkey->remove_packet = LIBSSH2_ALLOC(session, packet_len); + if (!pkey->remove_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for publickey \"remove\" packet", + 0); + return -1; + } + + pkey->remove_s = pkey->remove_packet; + libssh2_htonu32(pkey->remove_s, packet_len - 4); + pkey->remove_s += 4; + libssh2_htonu32(pkey->remove_s, sizeof("remove") - 1); + pkey->remove_s += 4; + memcpy(pkey->remove_s, "remove", sizeof("remove") - 1); + pkey->remove_s += sizeof("remove") - 1; + libssh2_htonu32(pkey->remove_s, name_len); + pkey->remove_s += 4; + memcpy(pkey->remove_s, name, name_len); + pkey->remove_s += name_len; + libssh2_htonu32(pkey->remove_s, blob_len); + pkey->remove_s += 4; + memcpy(pkey->remove_s, blob, blob_len); + pkey->remove_s += blob_len; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Sending publickey \"remove\" packet: type=%s blob_len=%ld", + name, blob_len); + + pkey->remove_state = libssh2_NB_state_created; + } + + if (pkey->remove_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, (char *) pkey->remove_packet, + (pkey->remove_s - pkey->remove_packet)); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if ((pkey->remove_s - pkey->remove_packet) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send publickey remove packet", 0); + LIBSSH2_FREE(session, pkey->remove_packet); + pkey->remove_packet = NULL; + pkey->remove_state = libssh2_NB_state_idle; + return -1; + } + LIBSSH2_FREE(session, pkey->remove_packet); + pkey->remove_packet = NULL; + + pkey->remove_state = libssh2_NB_state_sent; + } + + rc = libssh2_publickey_response_success(pkey); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + pkey->remove_state = libssh2_NB_state_idle; + + return rc; +} + +/* }}} */ + +/* {{{ libssh2_publickey_list_fetch + * Fetch a list of supported public key from a server + */ +LIBSSH2_API int +libssh2_publickey_list_fetch(LIBSSH2_PUBLICKEY * pkey, unsigned long *num_keys, + libssh2_publickey_list ** pkey_list) +{ + LIBSSH2_CHANNEL *channel = pkey->channel; + LIBSSH2_SESSION *session = channel->session; + libssh2_publickey_list *list = NULL; + unsigned long buffer_len = 12, keys = 0, max_keys = 0, i; + /* 12 = packet_len(4) + list_len(4) + "list"(4) */ + int response; + int rc; + + if (pkey->listFetch_state == libssh2_NB_state_idle) { + pkey->listFetch_data = NULL; + + pkey->listFetch_s = pkey->listFetch_buffer; + libssh2_htonu32(pkey->listFetch_s, buffer_len - 4); + pkey->listFetch_s += 4; + libssh2_htonu32(pkey->listFetch_s, sizeof("list") - 1); + pkey->listFetch_s += 4; + memcpy(pkey->listFetch_s, "list", sizeof("list") - 1); + pkey->listFetch_s += sizeof("list") - 1; + + _libssh2_debug(session, LIBSSH2_DBG_PUBLICKEY, + "Sending publickey \"list\" packet"); + + pkey->listFetch_state = libssh2_NB_state_created; + } + + if (pkey->listFetch_state == libssh2_NB_state_created) { + rc = libssh2_channel_write_ex(channel, 0, + (char *) pkey->listFetch_buffer, + (pkey->listFetch_s - + pkey->listFetch_buffer)); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if ((pkey->listFetch_s - pkey->listFetch_buffer) != rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send publickey list packet", 0); + pkey->listFetch_state = libssh2_NB_state_idle; + return -1; + } + + pkey->listFetch_state = libssh2_NB_state_sent; + } + + while (1) { + rc = libssh2_publickey_packet_receive(pkey, &pkey->listFetch_data, + &pkey->listFetch_data_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_TIMEOUT, + "Timeout waiting for response from publickey subsystem", + 0); + goto err_exit; + } + + pkey->listFetch_s = pkey->listFetch_data; + if ((response = + libssh2_publickey_response_id(&pkey->listFetch_s, + pkey->listFetch_data_len)) < 0) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Invalid publickey subsystem response code", 0); + goto err_exit; + } + + switch (response) { + case LIBSSH2_PUBLICKEY_RESPONSE_STATUS: + /* Error, or processing complete */ + { + unsigned long status, descr_len, lang_len; + unsigned char *descr, *lang; + + status = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + descr_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + descr = pkey->listFetch_s; + pkey->listFetch_s += descr_len; + lang_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + lang = pkey->listFetch_s; + pkey->listFetch_s += lang_len; + + if (pkey->listFetch_s > + pkey->listFetch_data + pkey->listFetch_data_len) { + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Malformed publickey subsystem packet", 0); + goto err_exit; + } + + if (status == LIBSSH2_PUBLICKEY_SUCCESS) { + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; + *pkey_list = list; + *num_keys = keys; + pkey->listFetch_state = libssh2_NB_state_idle; + return 0; + } + + libssh2_publickey_status_error(pkey, session, status, descr, + descr_len); + goto err_exit; + } + case LIBSSH2_PUBLICKEY_RESPONSE_PUBLICKEY: + /* What we want */ + if (keys >= max_keys) { + libssh2_publickey_list *newlist; + /* Grow the key list if necessary */ + max_keys += 8; + newlist = + LIBSSH2_REALLOC(session, list, + (max_keys + + 1) * sizeof(libssh2_publickey_list)); + if (!newlist) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for publickey list", + 0); + goto err_exit; + } + list = newlist; + } + if (pkey->version == 1) { + unsigned long comment_len; + + comment_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + if (comment_len) { + list[keys].num_attrs = 1; + list[keys].attrs = + LIBSSH2_ALLOC(session, + sizeof(libssh2_publickey_attribute)); + if (!list[keys].attrs) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for publickey attributes", + 0); + goto err_exit; + } + list[keys].attrs[0].name = "comment"; + list[keys].attrs[0].name_len = sizeof("comment") - 1; + list[keys].attrs[0].value = (char *) pkey->listFetch_s; + list[keys].attrs[0].value_len = comment_len; + list[keys].attrs[0].mandatory = 0; + + pkey->listFetch_s += comment_len; + } else { + list[keys].num_attrs = 0; + list[keys].attrs = NULL; + } + list[keys].name_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].name = pkey->listFetch_s; + pkey->listFetch_s += list[keys].name_len; + list[keys].blob_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].blob = pkey->listFetch_s; + pkey->listFetch_s += list[keys].blob_len; + } else { + /* Version == 2 */ + list[keys].name_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].name = pkey->listFetch_s; + pkey->listFetch_s += list[keys].name_len; + list[keys].blob_len = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].blob = pkey->listFetch_s; + pkey->listFetch_s += list[keys].blob_len; + list[keys].num_attrs = libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + if (list[keys].num_attrs) { + list[keys].attrs = + LIBSSH2_ALLOC(session, + list[keys].num_attrs * + sizeof(libssh2_publickey_attribute)); + if (!list[keys].attrs) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for publickey attributes", + 0); + goto err_exit; + } + for(i = 0; i < list[keys].num_attrs; i++) { + list[keys].attrs[i].name_len = + libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].attrs[i].name = (char *) pkey->listFetch_s; + pkey->listFetch_s += list[keys].attrs[i].name_len; + list[keys].attrs[i].value_len = + libssh2_ntohu32(pkey->listFetch_s); + pkey->listFetch_s += 4; + list[keys].attrs[i].value = (char *) pkey->listFetch_s; + pkey->listFetch_s += list[keys].attrs[i].value_len; + list[keys].attrs[i].mandatory = 0; /* actually an ignored value */ + } + } else { + list[keys].attrs = NULL; + } + } + list[keys].packet = pkey->listFetch_data; /* To be FREEd in libssh2_publickey_list_free() */ + keys++; + + list[keys].packet = NULL; /* Terminate the list */ + pkey->listFetch_data = NULL; + break; + default: + /* Unknown/Unexpected */ + libssh2_error(session, LIBSSH2_ERROR_PUBLICKEY_PROTOCOL, + "Unexpected publickey subsystem response, ignoring", + 0); + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; + } + } + + /* Only reached via explicit goto */ + err_exit: + if (pkey->listFetch_data) { + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; + } + if (list) { + libssh2_publickey_list_free(pkey, list); + } + pkey->listFetch_state = libssh2_NB_state_idle; + return -1; +} + +/* }}} */ + +/* {{{ libssh2_publickey_list_free + * Free a previously fetched list of public keys + */ +LIBSSH2_API void +libssh2_publickey_list_free(LIBSSH2_PUBLICKEY * pkey, + libssh2_publickey_list * pkey_list) +{ + LIBSSH2_SESSION *session = pkey->channel->session; + libssh2_publickey_list *p = pkey_list; + + while (p->packet) { + if (p->attrs) { + LIBSSH2_FREE(session, p->attrs); + } + LIBSSH2_FREE(session, p->packet); + p++; + } + + LIBSSH2_FREE(session, pkey_list); +} + +/* }}} */ + +/* {{{ libssh2_publickey_shutdown + * Shutdown the publickey subsystem + */ +LIBSSH2_API int +libssh2_publickey_shutdown(LIBSSH2_PUBLICKEY * pkey) +{ + LIBSSH2_SESSION *session = pkey->channel->session; + + /* + * Make sure all memory used in the state variables are free + */ + if (pkey->receive_packet) { + LIBSSH2_FREE(session, pkey->receive_packet); + pkey->receive_packet = NULL; + } + if (pkey->add_packet) { + LIBSSH2_FREE(session, pkey->add_packet); + pkey->add_packet = NULL; + } + if (pkey->remove_packet) { + LIBSSH2_FREE(session, pkey->remove_packet); + pkey->remove_packet = NULL; + } + if (pkey->listFetch_data) { + LIBSSH2_FREE(session, pkey->listFetch_data); + pkey->listFetch_data = NULL; + } + + if (libssh2_channel_free(pkey->channel) == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, pkey); + return 0; +} + +/* }}} */ Property changes on: libssh2/src/publickey.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.4 Added: svn:eol-style + native Index: libssh2/src/kex.c =================================================================== --- libssh2/src/kex.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/kex.c (.../trunk) @@ -0,0 +1,1916 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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" + +/* TODO: Switch this to an inline and handle alloc() failures */ +/* Helper macro called from libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange */ +#define LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(value, reqlen, version) \ +{ \ + libssh2_sha1_ctx hash; \ + unsigned long len = 0; \ + if (!(value)) { \ + value = LIBSSH2_ALLOC(session, reqlen + SHA_DIGEST_LENGTH); \ + } \ + if (value) \ + while (len < (unsigned long)reqlen) { \ + libssh2_sha1_init(&hash); \ + libssh2_sha1_update(hash, exchange_state->k_value, \ + exchange_state->k_value_len); \ + libssh2_sha1_update(hash, exchange_state->h_sig_comp, \ + SHA_DIGEST_LENGTH); \ + if (len > 0) { \ + libssh2_sha1_update(hash, value, len); \ + } else { \ + libssh2_sha1_update(hash, (version), 1); \ + libssh2_sha1_update(hash, session->session_id, \ + session->session_id_len); \ + } \ + libssh2_sha1_final(hash, (value) + len); \ + len += SHA_DIGEST_LENGTH; \ + } \ +} + +/* {{{ libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange + * Diffie Hellman Key Exchange, Group Agnostic + */ +static int +libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_SESSION * + session, + _libssh2_bn * g, + _libssh2_bn * p, + int group_order, + unsigned char + packet_type_init, + unsigned char + packet_type_reply, + unsigned char + *midhash, + unsigned long + midhash_len, + kmdhgGPsha1kex_state_t + * exchange_state) +{ + int ret = 0; + int rc; + + if (exchange_state->state == libssh2_NB_state_idle) { + /* Setup initial values */ + exchange_state->e_packet = NULL; + exchange_state->s_packet = NULL; + exchange_state->k_value = NULL; + exchange_state->ctx = _libssh2_bn_ctx_new(); + exchange_state->x = _libssh2_bn_init(); /* Random from client */ + exchange_state->e = _libssh2_bn_init(); /* g^x mod p */ + exchange_state->f = _libssh2_bn_init(); /* g^(Random from server) mod p */ + exchange_state->k = _libssh2_bn_init(); /* The shared secret: f^x mod p */ + + /* Zero the whole thing out */ + memset(&exchange_state->req_state, 0, sizeof(packet_require_state_t)); + + /* Generate x and e */ + _libssh2_bn_rand(exchange_state->x, group_order, 0, -1); + _libssh2_bn_mod_exp(exchange_state->e, g, exchange_state->x, p, + exchange_state->ctx); + + /* Send KEX init */ + /* packet_type(1) + String Length(4) + leading 0(1) */ + exchange_state->e_packet_len = + _libssh2_bn_bytes(exchange_state->e) + 6; + if (_libssh2_bn_bits(exchange_state->e) % 8) { + /* Leading 00 not needed */ + exchange_state->e_packet_len--; + } + + exchange_state->e_packet = + LIBSSH2_ALLOC(session, exchange_state->e_packet_len); + if (!exchange_state->e_packet) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, "Out of memory error", + 0); + ret = -1; + goto clean_exit; + } + exchange_state->e_packet[0] = packet_type_init; + libssh2_htonu32(exchange_state->e_packet + 1, + exchange_state->e_packet_len - 5); + if (_libssh2_bn_bits(exchange_state->e) % 8) { + _libssh2_bn_to_bin(exchange_state->e, + exchange_state->e_packet + 5); + } else { + exchange_state->e_packet[5] = 0; + _libssh2_bn_to_bin(exchange_state->e, + exchange_state->e_packet + 6); + } + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending KEX packet %d", + (int) packet_type_init); + exchange_state->state = libssh2_NB_state_created; + } + + if (exchange_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, exchange_state->e_packet, + exchange_state->e_packet_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send KEX init message", 0); + ret = -1; + goto clean_exit; + } + exchange_state->state = libssh2_NB_state_sent; + } + + if (exchange_state->state == libssh2_NB_state_sent) { + if (session->burn_optimistic_kexinit) { + /* The first KEX packet to come along will be the guess initially + * sent by the server. That guess turned out to be wrong so we + * need to silently ignore it */ + int burn_type; + + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Waiting for badly guessed KEX packet (to be ignored)"); + burn_type = + libssh2_packet_burn(session, &exchange_state->burn_state); + if (burn_type == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (burn_type <= 0) { + /* Failed to receive a packet */ + ret = -1; + goto clean_exit; + } + session->burn_optimistic_kexinit = 0; + + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Burnt packet of type: %02x", + (unsigned int) burn_type); + } + + exchange_state->state = libssh2_NB_state_sent1; + } + + if (exchange_state->state == libssh2_NB_state_sent1) { + /* Wait for KEX reply */ + rc = libssh2_packet_require_ex(session, packet_type_reply, + &exchange_state->s_packet, + &exchange_state->s_packet_len, 0, NULL, + 0, &exchange_state->req_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + if (rc) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, + "Timed out waiting for KEX reply", 0); + ret = -1; + goto clean_exit; + } + + /* Parse KEXDH_REPLY */ + exchange_state->s = exchange_state->s_packet + 1; + + session->server_hostkey_len = libssh2_ntohu32(exchange_state->s); + exchange_state->s += 4; + session->server_hostkey = + LIBSSH2_ALLOC(session, session->server_hostkey_len); + if (!session->server_hostkey) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory for a copy of the host key", + 0); + ret = -1; + goto clean_exit; + } + memcpy(session->server_hostkey, exchange_state->s, + session->server_hostkey_len); + exchange_state->s += session->server_hostkey_len; + +#if LIBSSH2_MD5 + { + libssh2_md5_ctx fingerprint_ctx; + + libssh2_md5_init(&fingerprint_ctx); + libssh2_md5_update(fingerprint_ctx, session->server_hostkey, + session->server_hostkey_len); + libssh2_md5_final(fingerprint_ctx, session->server_hostkey_md5); + } +#ifdef LIBSSH2DEBUG + { + char fingerprint[50], *fprint = fingerprint; + int i; + for(i = 0; i < 16; i++, fprint += 3) { + snprintf(fprint, 4, "%02x:", session->server_hostkey_md5[i]); + } + *(--fprint) = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Server's MD5 Fingerprint: %s", fingerprint); + } +#endif /* LIBSSH2DEBUG */ +#endif /* ! LIBSSH2_MD5 */ + + { + libssh2_sha1_ctx fingerprint_ctx; + + libssh2_sha1_init(&fingerprint_ctx); + libssh2_sha1_update(fingerprint_ctx, session->server_hostkey, + session->server_hostkey_len); + libssh2_sha1_final(fingerprint_ctx, session->server_hostkey_sha1); + } +#ifdef LIBSSH2DEBUG + { + char fingerprint[64], *fprint = fingerprint; + int i; + + for(i = 0; i < 20; i++, fprint += 3) { + snprintf(fprint, 4, "%02x:", session->server_hostkey_sha1[i]); + } + *(--fprint) = '\0'; + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Server's SHA1 Fingerprint: %s", fingerprint); + } +#endif /* LIBSSH2DEBUG */ + + if (session->hostkey-> + init(session, session->server_hostkey, session->server_hostkey_len, + &session->server_hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_INIT, + "Unable to initialize hostkey importer", 0); + ret = -1; + goto clean_exit; + } + + exchange_state->f_value_len = libssh2_ntohu32(exchange_state->s); + exchange_state->s += 4; + exchange_state->f_value = exchange_state->s; + exchange_state->s += exchange_state->f_value_len; + _libssh2_bn_from_bin(exchange_state->f, exchange_state->f_value_len, + exchange_state->f_value); + + exchange_state->h_sig_len = libssh2_ntohu32(exchange_state->s); + exchange_state->s += 4; + exchange_state->h_sig = exchange_state->s; + + /* Compute the shared secret */ + _libssh2_bn_mod_exp(exchange_state->k, exchange_state->f, + exchange_state->x, p, exchange_state->ctx); + exchange_state->k_value_len = _libssh2_bn_bytes(exchange_state->k) + 5; + if (_libssh2_bn_bits(exchange_state->k) % 8) { + /* don't need leading 00 */ + exchange_state->k_value_len--; + } + exchange_state->k_value = + LIBSSH2_ALLOC(session, exchange_state->k_value_len); + if (!exchange_state->k_value) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate buffer for K", 0); + ret = -1; + goto clean_exit; + } + libssh2_htonu32(exchange_state->k_value, + exchange_state->k_value_len - 4); + if (_libssh2_bn_bits(exchange_state->k) % 8) { + _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 4); + } else { + exchange_state->k_value[4] = 0; + _libssh2_bn_to_bin(exchange_state->k, exchange_state->k_value + 5); + } + + libssh2_sha1_init(&exchange_state->exchange_hash); + if (session->local.banner) { + libssh2_htonu32(exchange_state->h_sig_comp, + strlen((char *) session->local.banner) - 2); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + (char *) session->local.banner, + strlen((char *) session->local.banner) - 2); + } else { + libssh2_htonu32(exchange_state->h_sig_comp, + sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + LIBSSH2_SSH_DEFAULT_BANNER, + sizeof(LIBSSH2_SSH_DEFAULT_BANNER) - 1); + } + + libssh2_htonu32(exchange_state->h_sig_comp, + strlen((char *) session->remote.banner)); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + session->remote.banner, + strlen((char *) session->remote.banner)); + + libssh2_htonu32(exchange_state->h_sig_comp, + session->local.kexinit_len); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + session->local.kexinit, + session->local.kexinit_len); + + libssh2_htonu32(exchange_state->h_sig_comp, + session->remote.kexinit_len); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + session->remote.kexinit, + session->remote.kexinit_len); + + libssh2_htonu32(exchange_state->h_sig_comp, + session->server_hostkey_len); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + session->server_hostkey, + session->server_hostkey_len); + + if (packet_type_init == SSH_MSG_KEX_DH_GEX_INIT) { + /* diffie-hellman-group-exchange hashes additional fields */ +#ifdef LIBSSH2_DH_GEX_NEW + libssh2_htonu32(exchange_state->h_sig_comp, + LIBSSH2_DH_GEX_MINGROUP); + libssh2_htonu32(exchange_state->h_sig_comp + 4, + LIBSSH2_DH_GEX_OPTGROUP); + libssh2_htonu32(exchange_state->h_sig_comp + 8, + LIBSSH2_DH_GEX_MAXGROUP); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 12); +#else + libssh2_htonu32(exchange_state->h_sig_comp, + LIBSSH2_DH_GEX_OPTGROUP); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); +#endif + } + + if (midhash) { + libssh2_sha1_update(exchange_state->exchange_hash, midhash, + midhash_len); + } + + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->e_packet + 1, + exchange_state->e_packet_len - 1); + + libssh2_htonu32(exchange_state->h_sig_comp, + exchange_state->f_value_len); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->h_sig_comp, 4); + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->f_value, + exchange_state->f_value_len); + + libssh2_sha1_update(exchange_state->exchange_hash, + exchange_state->k_value, + exchange_state->k_value_len); + + libssh2_sha1_final(exchange_state->exchange_hash, + exchange_state->h_sig_comp); + + if (session->hostkey-> + sig_verify(session, exchange_state->h_sig, + exchange_state->h_sig_len, exchange_state->h_sig_comp, + 20, &session->server_hostkey_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_HOSTKEY_SIGN, + "Unable to verify hostkey signature", 0); + ret = -1; + goto clean_exit; + } + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sending NEWKEYS message"); + exchange_state->c = SSH_MSG_NEWKEYS; + + exchange_state->state = libssh2_NB_state_sent2; + } + + if (exchange_state->state == libssh2_NB_state_sent2) { + rc = libssh2_packet_write(session, &exchange_state->c, 1); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send NEWKEYS message", 0); + ret = -1; + goto clean_exit; + } + + exchange_state->state = libssh2_NB_state_sent3; + } + + if (exchange_state->state == libssh2_NB_state_sent3) { + rc = libssh2_packet_require_ex(session, SSH_MSG_NEWKEYS, + &exchange_state->tmp, + &exchange_state->tmp_len, 0, NULL, 0, + &exchange_state->req_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, + "Timed out waiting for NEWKEYS", 0); + ret = -1; + goto clean_exit; + } + /* The first key exchange has been performed, + switch to active crypt/comp/mac mode */ + session->state |= LIBSSH2_STATE_NEWKEYS; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Received NEWKEYS message"); + + /* This will actually end up being just packet_type(1) + for this packet type anyway */ + LIBSSH2_FREE(session, exchange_state->tmp); + + if (!session->session_id) { + session->session_id = LIBSSH2_ALLOC(session, SHA_DIGEST_LENGTH); + if (!session->session_id) { + ret = -1; + goto clean_exit; + } + memcpy(session->session_id, exchange_state->h_sig_comp, + SHA_DIGEST_LENGTH); + session->session_id_len = SHA_DIGEST_LENGTH; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "session_id calculated"); + } + + /* Cleanup any existing cipher */ + if (session->local.crypt->dtor) { + session->local.crypt->dtor(session, + &session->local.crypt_abstract); + } + + /* Calculate IV/Secret/Key for each direction */ + if (session->local.crypt->init) { + unsigned char *iv = NULL, *secret = NULL; + int free_iv = 0, free_secret = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, + session->local.crypt-> + iv_len, "A"); + if (!iv) { + ret = -1; + goto clean_exit; + } + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, + session->local.crypt-> + secret_len, "C"); + if (!secret) { + LIBSSH2_FREE(session, iv); + ret = -1; + goto clean_exit; + } + if (session->local.crypt-> + init(session, session->local.crypt, iv, &free_iv, secret, + &free_secret, 1, &session->local.crypt_abstract)) { + LIBSSH2_FREE(session, iv); + LIBSSH2_FREE(session, secret); + ret = -1; + goto clean_exit; + } + + if (free_iv) { + memset(iv, 0, session->local.crypt->iv_len); + LIBSSH2_FREE(session, iv); + } + + if (free_secret) { + memset(secret, 0, session->local.crypt->secret_len); + LIBSSH2_FREE(session, secret); + } + } + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Client to Server IV and Key calculated"); + + if (session->remote.crypt->dtor) { + /* Cleanup any existing cipher */ + session->remote.crypt->dtor(session, + &session->remote.crypt_abstract); + } + + if (session->remote.crypt->init) { + unsigned char *iv = NULL, *secret = NULL; + int free_iv = 0, free_secret = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(iv, + session->remote.crypt-> + iv_len, "B"); + if (!iv) { + ret = -1; + goto clean_exit; + } + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(secret, + session->remote.crypt-> + secret_len, "D"); + if (!secret) { + LIBSSH2_FREE(session, iv); + ret = -1; + goto clean_exit; + } + if (session->remote.crypt-> + init(session, session->remote.crypt, iv, &free_iv, secret, + &free_secret, 0, &session->remote.crypt_abstract)) { + LIBSSH2_FREE(session, iv); + LIBSSH2_FREE(session, secret); + ret = -1; + goto clean_exit; + } + + if (free_iv) { + memset(iv, 0, session->remote.crypt->iv_len); + LIBSSH2_FREE(session, iv); + } + + if (free_secret) { + memset(secret, 0, session->remote.crypt->secret_len); + LIBSSH2_FREE(session, secret); + } + } + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Server to Client IV and Key calculated"); + + if (session->local.mac->dtor) { + session->local.mac->dtor(session, &session->local.mac_abstract); + } + + if (session->local.mac->init) { + unsigned char *key = NULL; + int free_key = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, + session->local.mac-> + key_len, "E"); + if (!key) { + ret = -1; + goto clean_exit; + } + session->local.mac->init(session, key, &free_key, + &session->local.mac_abstract); + + if (free_key) { + memset(key, 0, session->local.mac->key_len); + LIBSSH2_FREE(session, key); + } + } + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Client to Server HMAC Key calculated"); + + if (session->remote.mac->dtor) { + session->remote.mac->dtor(session, &session->remote.mac_abstract); + } + + if (session->remote.mac->init) { + unsigned char *key = NULL; + int free_key = 0; + + LIBSSH2_KEX_METHOD_DIFFIE_HELLMAN_SHA1_HASH(key, + session->remote.mac-> + key_len, "F"); + if (!key) { + ret = -1; + goto clean_exit; + } + session->remote.mac->init(session, key, &free_key, + &session->remote.mac_abstract); + + if (free_key) { + memset(key, 0, session->remote.mac->key_len); + LIBSSH2_FREE(session, key); + } + } + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Server to Client HMAC Key calculated"); + } + + clean_exit: + _libssh2_bn_free(exchange_state->x); + exchange_state->x = NULL; + _libssh2_bn_free(exchange_state->e); + exchange_state->e = NULL; + _libssh2_bn_free(exchange_state->f); + exchange_state->f = NULL; + _libssh2_bn_free(exchange_state->k); + exchange_state->k = NULL; + _libssh2_bn_ctx_free(exchange_state->ctx); + exchange_state->ctx = NULL; + + if (exchange_state->e_packet) { + LIBSSH2_FREE(session, exchange_state->e_packet); + exchange_state->e_packet = NULL; + } + + if (exchange_state->s_packet) { + LIBSSH2_FREE(session, exchange_state->s_packet); + exchange_state->s_packet = NULL; + } + + if (exchange_state->k_value) { + LIBSSH2_FREE(session, exchange_state->k_value); + exchange_state->k_value = NULL; + } + + if (session->server_hostkey) { + LIBSSH2_FREE(session, session->server_hostkey); + session->server_hostkey = NULL; + } + + exchange_state->state = libssh2_NB_state_idle; + + return ret; +} + +/* }}} */ + +/* {{{ libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange + * Diffie-Hellman Group1 (Actually Group2) Key Exchange using SHA1 + */ +static int +libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange(LIBSSH2_SESSION * + session, + key_exchange_state_low_t + * key_state) +{ + static const unsigned char p_value[128] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + int ret; + + if (key_state->state == libssh2_NB_state_idle) { + /* g == 2 */ + key_state->p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ + key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */ + + /* Initialize P and G */ + _libssh2_bn_set_word(key_state->g, 2); + _libssh2_bn_from_bin(key_state->p, 128, p_value); + + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Initiating Diffie-Hellman Group1 Key Exchange"); + + key_state->state = libssh2_NB_state_created; + } + + ret = + libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, + key_state-> + g, + key_state-> + p, 128, + SSH_MSG_KEXDH_INIT, + SSH_MSG_KEXDH_REPLY, + NULL, 0, + &key_state-> + exchange_state); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + _libssh2_bn_free(key_state->p); + key_state->p = NULL; + _libssh2_bn_free(key_state->g); + key_state->g = NULL; + key_state->state = libssh2_NB_state_idle; + + return ret; +} + +/* }}} */ + +/* {{{ libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange + * Diffie-Hellman Group14 Key Exchange using SHA1 + */ +static int +libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange(LIBSSH2_SESSION * + session, + key_exchange_state_low_t + * key_state) +{ + static const unsigned char p_value[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, + 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, + 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37, + 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, + 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, + 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, + 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, + 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, + 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, + 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, + 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, + 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, + 0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, + 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, + 0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, + 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, + 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, + 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, + 0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, + 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10, + 0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + int ret; + + if (key_state->state == libssh2_NB_state_idle) { + key_state->p = _libssh2_bn_init(); /* SSH2 defined value (p_value) */ + key_state->g = _libssh2_bn_init(); /* SSH2 defined value (2) */ + + /* g == 2 */ + /* Initialize P and G */ + _libssh2_bn_set_word(key_state->g, 2); + _libssh2_bn_from_bin(key_state->p, 256, p_value); + + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Initiating Diffie-Hellman Group14 Key Exchange"); + + key_state->state = libssh2_NB_state_created; + } + ret = + libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(session, + key_state-> + g, + key_state-> + p, 256, + SSH_MSG_KEXDH_INIT, + SSH_MSG_KEXDH_REPLY, + NULL, 0, + &key_state-> + exchange_state); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + key_state->state = libssh2_NB_state_idle; + _libssh2_bn_free(key_state->p); + key_state->p = NULL; + _libssh2_bn_free(key_state->g); + key_state->g = NULL; + + return ret; +} + +/* }}} */ + +/* {{{ libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange + * Diffie-Hellman Group Exchange Key Exchange using SHA1 + * Negotiates random(ish) group for secret derivation + */ +static int + libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange + (LIBSSH2_SESSION * session, key_exchange_state_low_t * key_state) +{ + unsigned char *s; + unsigned long p_len, g_len; + int ret = 0; + int rc; + + if (key_state->state == libssh2_NB_state_idle) { + key_state->p = _libssh2_bn_init(); + key_state->g = _libssh2_bn_init(); + /* Ask for a P and G pair */ +#ifdef LIBSSH2_DH_GEX_NEW + key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST; + libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_MINGROUP); + libssh2_htonu32(key_state->request + 5, LIBSSH2_DH_GEX_OPTGROUP); + libssh2_htonu32(key_state->request + 9, LIBSSH2_DH_GEX_MAXGROUP); + key_state->request_len = 13; + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Initiating Diffie-Hellman Group-Exchange (New Method)"); +#else + key_state->request[0] = SSH_MSG_KEX_DH_GEX_REQUEST_OLD; + libssh2_htonu32(key_state->request + 1, LIBSSH2_DH_GEX_OPTGROUP); + key_state->request_len = 5; + _libssh2_debug(session, LIBSSH2_DBG_KEX, + "Initiating Diffie-Hellman Group-Exchange (Old Method)"); +#endif + + key_state->state = libssh2_NB_state_created; + } + + if (key_state->state == libssh2_NB_state_created) { + rc = libssh2_packet_write(session, key_state->request, + key_state->request_len); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send Group Exchange Request", 0); + ret = -1; + goto dh_gex_clean_exit; + } + + key_state->state = libssh2_NB_state_sent; + } + + if (key_state->state == libssh2_NB_state_sent) { + rc = libssh2_packet_require_ex(session, SSH_MSG_KEX_DH_GEX_GROUP, + &key_state->data, &key_state->data_len, + 0, NULL, 0, &key_state->req_state); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc) { + libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, + "Timeout waiting for GEX_GROUP reply", 0); + ret = -1; + goto dh_gex_clean_exit; + } + + key_state->state = libssh2_NB_state_sent1; + } + + if (key_state->state == libssh2_NB_state_sent1) { + s = key_state->data + 1; + p_len = libssh2_ntohu32(s); + s += 4; + _libssh2_bn_from_bin(key_state->p, p_len, s); + s += p_len; + + g_len = libssh2_ntohu32(s); + s += 4; + _libssh2_bn_from_bin(key_state->g, g_len, s); + s += g_len; + + ret = + libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange + (session, key_state->g, key_state->p, p_len, + SSH_MSG_KEX_DH_GEX_INIT, SSH_MSG_KEX_DH_GEX_REPLY, + key_state->data + 1, key_state->data_len - 1, + &key_state->exchange_state); + if (ret == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } + + LIBSSH2_FREE(session, key_state->data); + } + + dh_gex_clean_exit: + key_state->state = libssh2_NB_state_idle; + _libssh2_bn_free(key_state->g); + key_state->g = NULL; + _libssh2_bn_free(key_state->p); + key_state->p = NULL; + + return ret; +} + +/* }}} */ + +#define LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY 0x0001 +#define LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY 0x0002 + +static const LIBSSH2_KEX_METHOD libssh2_kex_method_diffie_helman_group1_sha1 = { + "diffie-hellman-group1-sha1", + libssh2_kex_method_diffie_hellman_group1_sha1_key_exchange, + LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, +}; + +static const LIBSSH2_KEX_METHOD libssh2_kex_method_diffie_helman_group14_sha1 = { + "diffie-hellman-group14-sha1", + libssh2_kex_method_diffie_hellman_group14_sha1_key_exchange, + LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, +}; + +static const LIBSSH2_KEX_METHOD + libssh2_kex_method_diffie_helman_group_exchange_sha1 = { + "diffie-hellman-group-exchange-sha1", + libssh2_kex_method_diffie_hellman_group_exchange_sha1_key_exchange, + LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY, +}; + +static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = { + &libssh2_kex_method_diffie_helman_group14_sha1, + &libssh2_kex_method_diffie_helman_group_exchange_sha1, + &libssh2_kex_method_diffie_helman_group1_sha1, + NULL +}; + +typedef struct _LIBSSH2_COMMON_METHOD +{ + const char *name; +} LIBSSH2_COMMON_METHOD; + +/* {{{ libssh2_kex_method_strlen + * Calculate the length of a particular method list's resulting string + * Includes SUM(strlen() of each individual method plus 1 (for coma)) - 1 (because the last coma isn't used) + * Another sign of bad coding practices gone mad. Pretend you don't see this. + */ +static size_t +libssh2_kex_method_strlen(LIBSSH2_COMMON_METHOD ** method) +{ + size_t len = 0; + + if (!method || !*method) { + return 0; + } + + while (*method && (*method)->name) { + len += strlen((*method)->name) + 1; + method++; + } + + return len - 1; +} + +/* }}} */ + +/* {{{ libssh2_kex_method_list + * Generate formatted preference list in buf + */ +static size_t +libssh2_kex_method_list(unsigned char *buf, size_t list_strlen, + LIBSSH2_COMMON_METHOD ** method) +{ + libssh2_htonu32(buf, list_strlen); + buf += 4; + + if (!method || !*method) { + return 4; + } + + while (*method && (*method)->name) { + int mlen = strlen((*method)->name); + memcpy(buf, (*method)->name, mlen); + buf += mlen; + *(buf++) = ','; + method++; + } + + return list_strlen + 4; +} + +/* }}} */ + +#define LIBSSH2_METHOD_PREFS_LEN(prefvar, defaultvar) ((prefvar) ? strlen(prefvar) : libssh2_kex_method_strlen((LIBSSH2_COMMON_METHOD**)(defaultvar))) +#define LIBSSH2_METHOD_PREFS_STR(buf, prefvarlen, prefvar, defaultvar) \ + if (prefvar) { \ + libssh2_htonu32((buf), (prefvarlen)); \ + buf += 4; \ + memcpy((buf), (prefvar), (prefvarlen)); \ + buf += (prefvarlen); \ + } else { \ + buf += libssh2_kex_method_list((buf), (prefvarlen), (LIBSSH2_COMMON_METHOD**)(defaultvar)); \ + } + +/* {{{ libssh2_kexinit + * Send SSH_MSG_KEXINIT packet + */ +static int +libssh2_kexinit(LIBSSH2_SESSION * session) +{ + /* 62 = packet_type(1) + cookie(16) + first_packet_follows(1) + + reserved(4) + length longs(40) */ + size_t data_len = 62; + size_t kex_len, hostkey_len = 0; + size_t crypt_cs_len, crypt_sc_len; + size_t comp_cs_len, comp_sc_len; + size_t mac_cs_len, mac_sc_len; + size_t lang_cs_len, lang_sc_len; + unsigned char *data, *s; + int rc; + + if (session->kexinit_state == libssh2_NB_state_idle) { + kex_len = + LIBSSH2_METHOD_PREFS_LEN(session->kex_prefs, libssh2_kex_methods); + hostkey_len = + LIBSSH2_METHOD_PREFS_LEN(session->hostkey_prefs, + libssh2_hostkey_methods()); + crypt_cs_len = + LIBSSH2_METHOD_PREFS_LEN(session->local.crypt_prefs, + libssh2_crypt_methods()); + crypt_sc_len = + LIBSSH2_METHOD_PREFS_LEN(session->remote.crypt_prefs, + libssh2_crypt_methods()); + mac_cs_len = + LIBSSH2_METHOD_PREFS_LEN(session->local.mac_prefs, + libssh2_mac_methods()); + mac_sc_len = + LIBSSH2_METHOD_PREFS_LEN(session->remote.mac_prefs, + libssh2_mac_methods()); + comp_cs_len = + LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, + libssh2_comp_methods()); + comp_sc_len = + LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, + libssh2_comp_methods()); + lang_cs_len = + LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); + lang_sc_len = + LIBSSH2_METHOD_PREFS_LEN(session->remote.lang_prefs, NULL); + + data_len += kex_len + hostkey_len + crypt_cs_len + crypt_sc_len + + comp_cs_len + comp_sc_len + mac_cs_len + mac_sc_len + + lang_cs_len + lang_sc_len; + + s = data = LIBSSH2_ALLOC(session, data_len); + if (!data) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Unable to allocate memory", 0); + return -1; + } + + *(s++) = SSH_MSG_KEXINIT; + + libssh2_random(s, 16); + s += 16; + + /* Ennumerating through these lists twice is probably (certainly?) + inefficient from a CPU standpoint, but it saves multiple + malloc/realloc calls */ + LIBSSH2_METHOD_PREFS_STR(s, kex_len, session->kex_prefs, + libssh2_kex_methods); + LIBSSH2_METHOD_PREFS_STR(s, hostkey_len, session->hostkey_prefs, + libssh2_hostkey_methods()); + LIBSSH2_METHOD_PREFS_STR(s, crypt_cs_len, session->local.crypt_prefs, + libssh2_crypt_methods()); + LIBSSH2_METHOD_PREFS_STR(s, crypt_sc_len, session->remote.crypt_prefs, + libssh2_crypt_methods()); + LIBSSH2_METHOD_PREFS_STR(s, mac_cs_len, session->local.mac_prefs, + libssh2_mac_methods()); + LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, + libssh2_mac_methods()); + LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, + libssh2_comp_methods()); + LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, + libssh2_comp_methods()); + LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, + NULL); + LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, + NULL); + + /* No optimistic KEX packet follows */ + /* Deal with optimistic packets + * session->flags |= KEXINIT_OPTIMISTIC + * session->flags |= KEXINIT_METHODSMATCH + */ + *(s++) = 0; + + /* Reserved == 0 */ + *(s++) = 0; + *(s++) = 0; + *(s++) = 0; + *(s++) = 0; + +#ifdef LIBSSH2DEBUG + { + /* Funnily enough, they'll all "appear" to be '\0' terminated */ + unsigned char *p = data + 21; /* type(1) + cookie(16) + len(4) */ + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent KEX: %s", p); + p += kex_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent HOSTKEY: %s", p); + p += hostkey_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_CS: %s", p); + p += crypt_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent CRYPT_SC: %s", p); + p += crypt_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_CS: %s", p); + p += mac_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent MAC_SC: %s", p); + p += mac_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_CS: %s", p); + p += comp_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent COMP_SC: %s", p); + p += comp_sc_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_CS: %s", p); + p += lang_cs_len + 4; + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Sent LANG_SC: %s", p); + p += lang_sc_len + 4; + } +#endif /* LIBSSH2DEBUG */ + + session->kexinit_state = libssh2_NB_state_created; + } else { + data = session->kexinit_data; + data_len = session->kexinit_data_len; + } + + if ((rc = libssh2_packet_write(session, data, data_len)) == PACKET_EAGAIN) { + session->kexinit_data = data; + session->kexinit_data_len = data_len; + return PACKET_EAGAIN; + } else if (rc) { + LIBSSH2_FREE(session, data); + libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "Unable to send KEXINIT packet to remote host", 0); + session->kexinit_state = libssh2_NB_state_idle; + return -1; + } + + if (session->local.kexinit) { + LIBSSH2_FREE(session, session->local.kexinit); + } + + session->local.kexinit = data; + session->local.kexinit_len = data_len; + + session->kexinit_state = libssh2_NB_state_idle; + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_kex_agree_instr + * Kex specific variant of strstr() + * Needle must be preceed by BOL or ',', and followed by ',' or EOL + */ +static unsigned char * +libssh2_kex_agree_instr(unsigned char *haystack, unsigned long haystack_len, + const unsigned char *needle, unsigned long needle_len) +{ + unsigned char *s; + + /* Haystack too short to bother trying */ + if (haystack_len < needle_len) { + return NULL; + } + + /* Needle at start of haystack */ + if ((strncmp((char *) haystack, (char *) needle, needle_len) == 0) && + (needle_len == haystack_len || haystack[needle_len] == ',')) { + return haystack; + } + + s = haystack; + /* Search until we run out of comas or we run out of haystack, + whichever comes first */ + while ((s = (unsigned char *) strchr((char *) s, ',')) + && ((haystack_len - (s - haystack)) > needle_len)) { + s++; + /* Needle at X position */ + if ((strncmp((char *) s, (char *) needle, needle_len) == 0) && + (((s - haystack) + needle_len) == haystack_len + || s[needle_len] == ',')) { + return s; + } + } + + return NULL; +} + +/* }}} */ + +/* {{{ libssh2_get_method_by_name + */ +static const LIBSSH2_COMMON_METHOD * +libssh2_get_method_by_name(const char *name, int name_len, + const LIBSSH2_COMMON_METHOD ** methodlist) +{ + while (*methodlist) { + if ((strlen((*methodlist)->name) == name_len) && + (strncmp((*methodlist)->name, name, name_len) == 0)) { + return *methodlist; + } + methodlist++; + } + return NULL; +} + +/* }}} */ + +/* {{{ libssh2_kex_agree_hostkey + * Agree on a Hostkey which works with this kex + */ +static int +libssh2_kex_agree_hostkey(LIBSSH2_SESSION * session, unsigned long kex_flags, + unsigned char *hostkey, unsigned long hostkey_len) +{ + const LIBSSH2_HOSTKEY_METHOD **hostkeyp = libssh2_hostkey_methods(); + unsigned char *s; + + if (session->hostkey_prefs) { + s = (unsigned char *) session->hostkey_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *) s, ','); + int method_len = (p ? (p - s) : strlen((char *) s)); + if (libssh2_kex_agree_instr(hostkey, hostkey_len, s, method_len)) { + const LIBSSH2_HOSTKEY_METHOD *method = + (const LIBSSH2_HOSTKEY_METHOD *) + libssh2_get_method_by_name((char *) s, method_len, + (const LIBSSH2_COMMON_METHOD **) + hostkeyp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + /* So far so good, but does it suit our purposes? (Encrypting vs Signing) */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == + 0) || (method->encrypt)) { + /* Either this hostkey can do encryption or this kex just doesn't require it */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) + == 0) || (method->sig_verify)) { + /* Either this hostkey can do signing or this kex just doesn't require it */ + session->hostkey = method; + return 0; + } + } + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (hostkeyp && (*hostkeyp)->name) { + s = libssh2_kex_agree_instr(hostkey, hostkey_len, + (unsigned char *) (*hostkeyp)->name, + strlen((*hostkeyp)->name)); + if (s) { + /* So far so good, but does it suit our purposes? (Encrypting vs Signing) */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_ENC_HOSTKEY) == 0) || + ((*hostkeyp)->encrypt)) { + /* Either this hostkey can do encryption or this kex just doesn't require it */ + if (((kex_flags & LIBSSH2_KEX_METHOD_FLAG_REQ_SIGN_HOSTKEY) == + 0) || ((*hostkeyp)->sig_verify)) { + /* Either this hostkey can do signing or this kex just doesn't require it */ + session->hostkey = *hostkeyp; + return 0; + } + } + } + hostkeyp++; + } + + return -1; +} + +/* }}} */ + +/* {{{ libssh2_kex_agree_kex_hostkey + * Agree on a Key Exchange method and a hostkey encoding type + */ +static int +libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex, + unsigned long kex_len, unsigned char *hostkey, + unsigned long hostkey_len) +{ + const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods; + unsigned char *s; + + if (session->kex_prefs) { + s = (unsigned char *) session->kex_prefs; + + while (s && *s) { + unsigned char *q, *p = (unsigned char *) strchr((char *) s, ','); + int method_len = (p ? (p - s) : strlen((char *) s)); + if ((q = libssh2_kex_agree_instr(kex, kex_len, s, method_len))) { + const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *) + libssh2_get_method_by_name((char *) s, method_len, + (const LIBSSH2_COMMON_METHOD **) + kexp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + /* We've agreed on a key exchange method, + * Can we agree on a hostkey that works with this kex? + */ + if (libssh2_kex_agree_hostkey + (session, method->flags, hostkey, hostkey_len) == 0) { + session->kex = method; + if (session->burn_optimistic_kexinit && (kex == q)) { + /* Server sent an optimistic packet, + * and client agrees with preference + * cancel burning the first KEX_INIT packet that comes in */ + session->burn_optimistic_kexinit = 0; + } + return 0; + } + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*kexp && (*kexp)->name) { + s = libssh2_kex_agree_instr(kex, kex_len, + (unsigned char *) (*kexp)->name, + strlen((*kexp)->name)); + if (s) { + /* We've agreed on a key exchange method, + * Can we agree on a hostkey that works with this kex? + */ + if (libssh2_kex_agree_hostkey + (session, (*kexp)->flags, hostkey, hostkey_len) == 0) { + session->kex = *kexp; + if (session->burn_optimistic_kexinit && (kex == s)) { + /* Server sent an optimistic packet, + * and client agrees with preference + * cancel burning the first KEX_INIT packet that comes in */ + session->burn_optimistic_kexinit = 0; + } + return 0; + } + } + kexp++; + } + return -1; +} + +/* }}} */ + +/* {{{ libssh2_kex_agree_crypt + * Agree on a cipher algo + */ +static int +libssh2_kex_agree_crypt(LIBSSH2_SESSION * session, + libssh2_endpoint_data * endpoint, unsigned char *crypt, + unsigned long crypt_len) +{ + const LIBSSH2_CRYPT_METHOD **cryptp = libssh2_crypt_methods(); + unsigned char *s; + + (void) session; + + if (endpoint->crypt_prefs) { + s = (unsigned char *) endpoint->crypt_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *) s, ','); + int method_len = (p ? (p - s) : strlen((char *) s)); + + if (libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) { + const LIBSSH2_CRYPT_METHOD *method = + (const LIBSSH2_CRYPT_METHOD *) + libssh2_get_method_by_name((char *) s, method_len, + (const LIBSSH2_COMMON_METHOD **) + cryptp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + endpoint->crypt = method; + return 0; + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*cryptp && (*cryptp)->name) { + s = libssh2_kex_agree_instr(crypt, crypt_len, + (unsigned char *) (*cryptp)->name, + strlen((*cryptp)->name)); + if (s) { + endpoint->crypt = *cryptp; + return 0; + } + cryptp++; + } + + return -1; +} + +/* }}} */ + +/* {{{ libssh2_kex_agree_mac + * Agree on a message authentication hash + */ +static int +libssh2_kex_agree_mac(LIBSSH2_SESSION * session, + libssh2_endpoint_data * endpoint, unsigned char *mac, + unsigned long mac_len) +{ + const LIBSSH2_MAC_METHOD **macp = libssh2_mac_methods(); + unsigned char *s; + (void) session; + + if (endpoint->mac_prefs) { + s = (unsigned char *) endpoint->mac_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *) s, ','); + int method_len = (p ? (p - s) : strlen((char *) s)); + + if (libssh2_kex_agree_instr(mac, mac_len, s, method_len)) { + const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *) + libssh2_get_method_by_name((char *) s, method_len, + (const LIBSSH2_COMMON_METHOD **) + macp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + endpoint->mac = method; + return 0; + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*macp && (*macp)->name) { + s = libssh2_kex_agree_instr(mac, mac_len, + (unsigned char *) (*macp)->name, + strlen((*macp)->name)); + if (s) { + endpoint->mac = *macp; + return 0; + } + macp++; + } + + return -1; +} + +/* }}} */ + +/* {{{ libssh2_kex_agree_comp + * Agree on a compression scheme + */ +static int +libssh2_kex_agree_comp(LIBSSH2_SESSION * session, + libssh2_endpoint_data * endpoint, unsigned char *comp, + unsigned long comp_len) +{ + const LIBSSH2_COMP_METHOD **compp = libssh2_comp_methods(); + unsigned char *s; + (void) session; + + if (endpoint->comp_prefs) { + s = (unsigned char *) endpoint->comp_prefs; + + while (s && *s) { + unsigned char *p = (unsigned char *) strchr((char *) s, ','); + int method_len = (p ? (p - s) : strlen((char *) s)); + + if (libssh2_kex_agree_instr(comp, comp_len, s, method_len)) { + const LIBSSH2_COMP_METHOD *method = + (const LIBSSH2_COMP_METHOD *) + libssh2_get_method_by_name((char *) s, method_len, + (const LIBSSH2_COMMON_METHOD **) + compp); + + if (!method) { + /* Invalid method -- Should never be reached */ + return -1; + } + + endpoint->comp = method; + return 0; + } + + s = p ? p + 1 : NULL; + } + return -1; + } + + while (*compp && (*compp)->name) { + s = libssh2_kex_agree_instr(comp, comp_len, + (unsigned char *) (*compp)->name, + strlen((*compp)->name)); + if (s) { + endpoint->comp = *compp; + return 0; + } + compp++; + } + + return -1; +} + +/* }}} */ + +/* TODO: When in server mode we need to turn this logic on its head + * The Client gets to make the final call on "agreed methods" + */ + +/* {{{ libssh2_kex_agree_methods + * Decide which specific method to use of the methods offered by each party + */ +static int +libssh2_kex_agree_methods(LIBSSH2_SESSION * session, unsigned char *data, + unsigned data_len) +{ + unsigned char *kex, *hostkey, *crypt_cs, *crypt_sc, *comp_cs, *comp_sc, + *mac_cs, *mac_sc, *lang_cs, *lang_sc; + size_t kex_len, hostkey_len, crypt_cs_len, crypt_sc_len, comp_cs_len; + size_t comp_sc_len, mac_cs_len, mac_sc_len, lang_cs_len, lang_sc_len; + unsigned char *s = data; + + /* Skip packet_type, we know it already */ + s++; + + /* Skip cookie, don't worry, it's preserved in the kexinit field */ + s += 16; + + /* Locate each string */ + kex_len = libssh2_ntohu32(s); + kex = s + 4; + s += 4 + kex_len; + hostkey_len = libssh2_ntohu32(s); + hostkey = s + 4; + s += 4 + hostkey_len; + crypt_cs_len = libssh2_ntohu32(s); + crypt_cs = s + 4; + s += 4 + crypt_cs_len; + crypt_sc_len = libssh2_ntohu32(s); + crypt_sc = s + 4; + s += 4 + crypt_sc_len; + mac_cs_len = libssh2_ntohu32(s); + mac_cs = s + 4; + s += 4 + mac_cs_len; + mac_sc_len = libssh2_ntohu32(s); + mac_sc = s + 4; + s += 4 + mac_sc_len; + comp_cs_len = libssh2_ntohu32(s); + comp_cs = s + 4; + s += 4 + comp_cs_len; + comp_sc_len = libssh2_ntohu32(s); + comp_sc = s + 4; + s += 4 + comp_sc_len; + lang_cs_len = libssh2_ntohu32(s); + lang_cs = s + 4; + s += 4 + lang_cs_len; + lang_sc_len = libssh2_ntohu32(s); + lang_sc = s + 4; + s += 4 + lang_sc_len; + + /* If the server sent an optimistic packet, assume that it guessed wrong. + * If the guess is determined to be right (by libssh2_kex_agree_kex_hostkey) + * This flag will be reset to zero so that it's not ignored */ + session->burn_optimistic_kexinit = *(s++); + /* Next uint32 in packet is all zeros (reserved) */ + + if (data_len < (unsigned) (s - data)) + return -1; /* short packet */ + + if (libssh2_kex_agree_kex_hostkey + (session, kex, kex_len, hostkey, hostkey_len)) { + return -1; + } + + if (libssh2_kex_agree_crypt + (session, &session->local, crypt_cs, crypt_cs_len) + || libssh2_kex_agree_crypt(session, &session->remote, crypt_sc, + crypt_sc_len)) { + return -1; + } + + if (libssh2_kex_agree_mac(session, &session->local, mac_cs, mac_cs_len) || + libssh2_kex_agree_mac(session, &session->remote, mac_sc, mac_sc_len)) { + return -1; + } + + if (libssh2_kex_agree_comp(session, &session->local, comp_cs, comp_cs_len) + || libssh2_kex_agree_comp(session, &session->remote, comp_sc, + comp_sc_len)) { + return -1; + } + + if (libssh2_kex_agree_lang(session, &session->local, lang_cs, lang_cs_len) + || libssh2_kex_agree_lang(session, &session->remote, lang_sc, + lang_sc_len)) { + return -1; + } + + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on KEX method: %s", + session->kex->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on HOSTKEY method: %s", + session->hostkey->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_CS method: %s", + session->local.crypt->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on CRYPT_SC method: %s", + session->remote.crypt->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_CS method: %s", + session->local.mac->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on MAC_SC method: %s", + session->remote.mac->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_CS method: %s", + session->local.comp->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on COMP_SC method: %s", + session->remote.comp->name); + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_CS method:"); /* None yet */ + _libssh2_debug(session, LIBSSH2_DBG_KEX, "Agreed on LANG_SC method:"); /* None yet */ + + /* Initialize compression layer */ + if (session->local.comp && session->local.comp->init && + session->local.comp->init(session, 1, &session->local.comp_abstract)) { + return -1; + } + + if (session->remote.comp && session->remote.comp->init && + session->remote.comp->init(session, 0, + &session->remote.comp_abstract)) { + return -1; + } + + return 0; +} + +/* }}} */ + +/* {{{ libssh2_kex_exchange + * Exchange keys + * Returns 0 on success, non-zero on failure + */ +int +libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange, + key_exchange_state_t * key_state) +{ + int rc = 0; + int retcode; + + session->state |= LIBSSH2_STATE_KEX_ACTIVE; + + if (key_state->state == libssh2_NB_state_idle) { + /* Prevent loop in packet_add() */ + session->state |= LIBSSH2_STATE_EXCHANGING_KEYS; + + if (reexchange) { + session->kex = NULL; + + if (session->hostkey && session->hostkey->dtor) { + session->hostkey->dtor(session, + &session->server_hostkey_abstract); + } + session->hostkey = NULL; + } + + key_state->state = libssh2_NB_state_created; + } + + if (!session->kex || !session->hostkey) { + if (key_state->state == libssh2_NB_state_created) { + /* Preserve in case of failure */ + key_state->oldlocal = session->local.kexinit; + key_state->oldlocal_len = session->local.kexinit_len; + + session->local.kexinit = NULL; + + key_state->state = libssh2_NB_state_sent; + } + + if (key_state->state == libssh2_NB_state_sent) { + retcode = libssh2_kexinit(session); + if (retcode == PACKET_EAGAIN) { + session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; + return PACKET_EAGAIN; + } else if (retcode) { + session->local.kexinit = key_state->oldlocal; + session->local.kexinit_len = key_state->oldlocal_len; + key_state->state = libssh2_NB_state_idle; + session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; + session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; + return -1; + } + + key_state->state = libssh2_NB_state_sent1; + } + + if (key_state->state == libssh2_NB_state_sent1) { + retcode = + libssh2_packet_require_ex(session, SSH_MSG_KEXINIT, + &key_state->data, + &key_state->data_len, 0, NULL, 0, + &key_state->req_state); + if (retcode == PACKET_EAGAIN) { + session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; + return PACKET_EAGAIN; + } else if (retcode) { + if (session->local.kexinit) { + LIBSSH2_FREE(session, session->local.kexinit); + } + session->local.kexinit = key_state->oldlocal; + session->local.kexinit_len = key_state->oldlocal_len; + key_state->state = libssh2_NB_state_idle; + session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; + session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; + return -1; + } + + if (session->remote.kexinit) { + LIBSSH2_FREE(session, session->remote.kexinit); + } + session->remote.kexinit = key_state->data; + session->remote.kexinit_len = key_state->data_len; + + if (libssh2_kex_agree_methods + (session, key_state->data, key_state->data_len)) { + rc = -1; + } + + key_state->state = libssh2_NB_state_sent2; + } + } else { + key_state->state = libssh2_NB_state_sent2; + } + + if (rc == 0) { + if (key_state->state == libssh2_NB_state_sent2) { + retcode = + session->kex->exchange_keys(session, + &key_state->key_state_low); + if (retcode == PACKET_EAGAIN) { + session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; + return PACKET_EAGAIN; + } else if (retcode) { + libssh2_error(session, LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE, + "Unrecoverable error exchanging keys", 0); + rc = -1; + } + } + } + + /* Done with kexinit buffers */ + if (session->local.kexinit) { + LIBSSH2_FREE(session, session->local.kexinit); + session->local.kexinit = NULL; + } + if (session->remote.kexinit) { + LIBSSH2_FREE(session, session->remote.kexinit); + session->remote.kexinit = NULL; + } + + session->state &= ~LIBSSH2_STATE_KEX_ACTIVE; + session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS; + + key_state->state = libssh2_NB_state_idle; + + return rc; +} + +/* }}} */ + +/* {{{ libssh2_session_method_pref + * Set preferred method + */ +LIBSSH2_API int +libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type, + const char *prefs) +{ + char **prefvar, *s, *newprefs; + int prefs_len = strlen(prefs); + const LIBSSH2_COMMON_METHOD **mlist; + + switch (method_type) { + case LIBSSH2_METHOD_KEX: + prefvar = &session->kex_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_kex_methods; + break; + + case LIBSSH2_METHOD_HOSTKEY: + prefvar = &session->hostkey_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_hostkey_methods(); + break; + + case LIBSSH2_METHOD_CRYPT_CS: + prefvar = &session->local.crypt_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods(); + break; + + case LIBSSH2_METHOD_CRYPT_SC: + prefvar = &session->remote.crypt_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_crypt_methods(); + break; + + case LIBSSH2_METHOD_MAC_CS: + prefvar = &session->local.mac_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_mac_methods(); + break; + + case LIBSSH2_METHOD_MAC_SC: + prefvar = &session->remote.mac_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_mac_methods(); + break; + + case LIBSSH2_METHOD_COMP_CS: + prefvar = &session->local.comp_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_comp_methods(); + break; + + case LIBSSH2_METHOD_COMP_SC: + prefvar = &session->remote.comp_prefs; + mlist = (const LIBSSH2_COMMON_METHOD **) libssh2_comp_methods(); + break; + + case LIBSSH2_METHOD_LANG_CS: + prefvar = &session->local.lang_prefs; + mlist = NULL; + break; + + case LIBSSH2_METHOD_LANG_SC: + prefvar = &session->remote.lang_prefs; + mlist = NULL; + break; + + default: + libssh2_error(session, LIBSSH2_ERROR_INVAL, + "Invalid parameter specified for method_type", 0); + return -1; + } + + s = newprefs = LIBSSH2_ALLOC(session, prefs_len + 1); + if (!newprefs) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, + "Error allocated space for method preferences", 0); + return -1; + } + memcpy(s, prefs, prefs_len + 1); + + while (s && *s) { + char *p = strchr(s, ','); + int method_len = p ? (p - s) : (int) strlen(s); + + if (!libssh2_get_method_by_name(s, method_len, mlist)) { + /* Strip out unsupported method */ + if (p) { + memcpy(s, p + 1, strlen(s) - method_len); + } else { + if (s > newprefs) { + *(--s) = '\0'; + } else { + *s = '\0'; + } + } + } + + s = p ? (p + 1) : NULL; + } + + if (strlen(newprefs) == 0) { + libssh2_error(session, LIBSSH2_ERROR_METHOD_NOT_SUPPORTED, + "The requested method(s) are not currently supported", + 0); + LIBSSH2_FREE(session, newprefs); + return -1; + } + + if (*prefvar) { + LIBSSH2_FREE(session, *prefvar); + } + *prefvar = newprefs; + + return 0; +} + +/* }}} */ Property changes on: libssh2/src/kex.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.3 Added: svn:eol-style + native Index: libssh2/src/openssl.h =================================================================== --- libssh2/src/openssl.h (.../tags/RELEASE_0_11_0) +++ libssh2/src/openssl.h (.../trunk) @@ -0,0 +1,224 @@ +/* Copyright (C) 2006, 2007 The Written Word, Inc. All rights reserved. + * Author: Simon Josefsson + * + * 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 +#include +#ifndef OPENSSL_NO_MD5 +#include +#endif +#include +#include +#include +#include +#include + +#ifdef OPENSSL_NO_RSA +# define LIBSSH2_RSA 0 +#else +# define LIBSSH2_RSA 1 +#endif + +#ifdef OPENSSL_NO_DSA +# define LIBSSH2_DSA 0 +#else +# define LIBSSH2_DSA 1 +#endif + +#ifdef OPENSSL_NO_MD5 +# define LIBSSH2_MD5 0 +#else +# define LIBSSH2_MD5 1 +#endif + +#ifdef OPENSSL_NO_RIPEMD +# define LIBSSH2_HMAC_RIPEMD 0 +#else +# define LIBSSH2_HMAC_RIPEMD 1 +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x00907000L && !defined(OPENSSL_NO_AES) +# define LIBSSH2_AES 1 +#else +# define LIBSSH2_AES 0 +#endif + +#ifdef OPENSSL_NO_BLOWFISH +# define LIBSSH2_BLOWFISH 0 +#else +# define LIBSSH2_BLOWFISH 1 +#endif + +#ifdef OPENSSL_NO_RC4 +# define LIBSSH2_RC4 0 +#else +# define LIBSSH2_RC4 1 +#endif + +#ifdef OPENSSL_NO_CAST +# define LIBSSH2_CAST 0 +#else +# define LIBSSH2_CAST 1 +#endif + +#ifdef OPENSSL_NO_DES +# define LIBSSH2_3DES 0 +#else +# define LIBSSH2_3DES 1 +#endif + +#define libssh2_random(buf, len) \ + RAND_bytes ((buf), (len)) + +#define libssh2_sha1_ctx SHA_CTX +#define libssh2_sha1_init(ctx) SHA1_Init(ctx) +#define libssh2_sha1_update(ctx, data, len) SHA1_Update(&(ctx), data, len) +#define libssh2_sha1_final(ctx, out) SHA1_Final(out, &(ctx)) +#define libssh2_sha1(message, len, out) SHA1(message, len, out) + +#define libssh2_md5_ctx MD5_CTX +#define libssh2_md5_init(ctx) MD5_Init(ctx) +#define libssh2_md5_update(ctx, data, len) MD5_Update(&(ctx), data, len) +#define libssh2_md5_final(ctx, out) MD5_Final(out, &(ctx)) +#define libssh2_md5(message, len, out) MD5(message, len, out) + +#define libssh2_hmac_ctx HMAC_CTX +#define libssh2_hmac_sha1_init(ctx, key, keylen) \ + HMAC_Init(ctx, key, keylen, EVP_sha1()) +#define libssh2_hmac_md5_init(ctx, key, keylen) \ + HMAC_Init(ctx, key, keylen, EVP_md5()) +#define libssh2_hmac_ripemd160_init(ctx, key, keylen) \ + HMAC_Init(ctx, key, keylen, EVP_ripemd160()) +#define libssh2_hmac_update(ctx, data, datalen) \ + HMAC_Update(&(ctx), data, datalen) +#define libssh2_hmac_final(ctx, data) HMAC_Final(&(ctx), data, NULL) +#define libssh2_hmac_cleanup(ctx) HMAC_cleanup(ctx) + +#define libssh2_crypto_init() + +#define libssh2_rsa_ctx RSA + +int _libssh2_rsa_new(libssh2_rsa_ctx ** rsa, + const unsigned char *edata, + unsigned long elen, + const unsigned char *ndata, + unsigned long nlen, + const unsigned char *ddata, + unsigned long dlen, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *e1data, + unsigned long e1len, + const unsigned char *e2data, + unsigned long e2len, + const unsigned char *coeffdata, unsigned long coefflen); +int _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase); +int _libssh2_rsa_sha1_verify(libssh2_rsa_ctx * rsa, + const unsigned char *sig, + unsigned long sig_len, + const unsigned char *m, unsigned long m_len); +int _libssh2_rsa_sha1_sign(LIBSSH2_SESSION * session, + libssh2_rsa_ctx * rsactx, + const unsigned char *hash, + unsigned long hash_len, + unsigned char **signature, + unsigned long *signature_len); + +#define _libssh2_rsa_free(rsactx) RSA_free(rsactx) + +#define libssh2_dsa_ctx DSA + +int _libssh2_dsa_new(libssh2_dsa_ctx ** dsa, + const unsigned char *pdata, + unsigned long plen, + const unsigned char *qdata, + unsigned long qlen, + const unsigned char *gdata, + unsigned long glen, + const unsigned char *ydata, + unsigned long ylen, + const unsigned char *x, unsigned long x_len); +int _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, + LIBSSH2_SESSION * session, + FILE * fp, unsigned const char *passphrase); +int _libssh2_dsa_sha1_verify(libssh2_dsa_ctx * dsactx, + const unsigned char *sig, + const unsigned char *m, unsigned long m_len); +int _libssh2_dsa_sha1_sign(libssh2_dsa_ctx * dsactx, + const unsigned char *hash, + unsigned long hash_len, unsigned char *sig); + +#define _libssh2_dsa_free(dsactx) DSA_free(dsactx) + +#define _libssh2_cipher_type(name) const EVP_CIPHER *(*name)(void) +#define _libssh2_cipher_ctx EVP_CIPHER_CTX + +#define _libssh2_cipher_aes256 EVP_aes_256_cbc +#define _libssh2_cipher_aes192 EVP_aes_192_cbc +#define _libssh2_cipher_aes128 EVP_aes_128_cbc +#define _libssh2_cipher_blowfish EVP_bf_cbc +#define _libssh2_cipher_arcfour EVP_rc4 +#define _libssh2_cipher_cast5 EVP_cast5_cbc +#define _libssh2_cipher_3des EVP_des_ede3_cbc + +int _libssh2_cipher_init(_libssh2_cipher_ctx * h, + _libssh2_cipher_type(algo), + unsigned char *iv, + unsigned char *secret, int encrypt); + +int _libssh2_cipher_crypt(_libssh2_cipher_ctx * ctx, + _libssh2_cipher_type(algo), + int encrypt, unsigned char *block); + +#define _libssh2_cipher_dtor(ctx) EVP_CIPHER_CTX_cleanup(ctx) + +#define _libssh2_bn BIGNUM +#define _libssh2_bn_ctx BN_CTX +#define _libssh2_bn_ctx_new() BN_CTX_new() +#define _libssh2_bn_ctx_free(bnctx) BN_CTX_free(bnctx) +#define _libssh2_bn_init() BN_new() +#define _libssh2_bn_rand(bn, bits, top, bottom) BN_rand(bn, bits, top, bottom) +#define _libssh2_bn_mod_exp(r, a, p, m, ctx) BN_mod_exp(r, a, p, m, ctx) +#define _libssh2_bn_set_word(bn, val) BN_set_word(bn, val) +#define _libssh2_bn_from_bin(bn, len, val) BN_bin2bn(val, len, bn) +#define _libssh2_bn_to_bin(bn, val) BN_bn2bin(bn, val) +#define _libssh2_bn_bytes(bn) BN_num_bytes(bn) +#define _libssh2_bn_bits(bn) BN_num_bits(bn) +#define _libssh2_bn_free(bn) BN_clear_free(bn) Property changes on: libssh2/src/openssl.h ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Index: libssh2/src/misc.c =================================================================== --- libssh2/src/misc.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/misc.c (.../trunk) @@ -0,0 +1,240 @@ +/* Copyright (c) 2004-2007, Sara Golemon + * 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" +#ifdef HAVE_UNISTD_H +#include +#endif + +/* {{{ libssh2_ntohu32 + */ +unsigned long +libssh2_ntohu32(const unsigned char *buf) +{ + return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +} + +/* }}} */ + +/* {{{ libssh2_ntohu64 + * + */ +libssh2_uint64_t +libssh2_ntohu64(const unsigned char *buf) +{ + unsigned long msl, lsl; + + msl = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + lsl = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; + + return ((libssh2_uint64_t)msl <<32) | lsl; +} + +/* }}} */ + +/* {{{ 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; +} + +/* }}} */ + +/* {{{ libssh2_htonu64 + */ +void +libssh2_htonu64(unsigned char *buf, libssh2_uint64_t value) +{ + unsigned long msl = ((libssh2_uint64_t)value >> 32); + + buf[0] = (msl >> 24) & 0xFF; + buf[1] = (msl >> 16) & 0xFF; + buf[2] = (msl >> 8) & 0xFF; + buf[3] = msl & 0xFF; + + buf[4] = (value >> 24) & 0xFF; + buf[5] = (value >> 16) & 0xFF; + buf[6] = (value >> 8) & 0xFF; + buf[7] = 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, + unsigned int *datalen, const char *src, + unsigned int src_len) +{ + unsigned char *s, *d; + short v; + int i = 0, len = 0; + + *data = LIBSSH2_ALLOC(session, (3 * src_len / 4) + 1); + d = (unsigned char *) *data; + if (!d) { + return -1; + } + + for(s = (unsigned char *) 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; +} + +/* }}} */ + +#ifdef LIBSSH2DEBUG +LIBSSH2_API int +libssh2_trace(LIBSSH2_SESSION * session, int bitmask) +{ + session->showmask = bitmask; + return 0; +} + +void +_libssh2_debug(LIBSSH2_SESSION * session, int context, const char *format, ...) +{ + char buffer[1536]; + int len; + va_list vargs; + static const char *const contexts[9] = { + "Unknown", + "Transport", + "Key Exchange", + "Userauth", + "Connection", + "scp", + "SFTP Subsystem", + "Failure Event", + "Publickey Subsystem", + }; + + if (context < 1 || context > 8) { + context = 0; + } + if (!(session->showmask & (1 << context))) { + /* no such output asked for */ + return; + } + + len = snprintf(buffer, 1535, "[libssh2] %s: ", contexts[context]); + + va_start(vargs, format); + len += vsnprintf(buffer + len, 1535 - len, format, vargs); + buffer[len] = '\n'; + va_end(vargs); + write(2, buffer, len + 1); + +} + +#else +LIBSSH2_API int +libssh2_trace(LIBSSH2_SESSION * session, int bitmask) +{ + (void) session; + (void) bitmask; + return 0; +} +#endif Property changes on: libssh2/src/misc.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.3 Added: svn:eol-style + native Index: libssh2/src/transport.c =================================================================== --- libssh2/src/transport.c (.../tags/RELEASE_0_11_0) +++ libssh2/src/transport.c (.../trunk) @@ -0,0 +1,815 @@ +/* Copyright (C) 2007 The Written Word, Inc. All rights reserved. + * Author: Daniel Stenberg + * + * 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. + * + * This file handles reading and writing to the SECSH transport layer. RFC4253. + */ + +#include "libssh2_priv.h" +#include +#include + +#include + +#define MAX_BLOCKSIZE 32 /* MUST fit biggest crypto block size we use/get */ +#define MAX_MACSIZE 20 /* MUST fit biggest MAC length we support */ + +#ifdef LIBSSH2DEBUG +#define UNPRINTABLE_CHAR '.' +static void +debugdump(LIBSSH2_SESSION * session, + const char *desc, unsigned char *ptr, unsigned long size) +{ + size_t i; + size_t c; + FILE *stream = stdout; + unsigned int width = 0x10; + + if (!(session->showmask & (1 << LIBSSH2_DBG_TRANS))) { + /* not asked for, bail out */ + return; + } + + fprintf(stream, "=> %s (%d bytes)\n", desc, (int) size); + + for(i = 0; i < size; i += width) { + + fprintf(stream, "%04lx: ", (long)i); + + /* hex not disabled, show it */ + for(c = 0; c < width; c++) { + if (i + c < size) + fprintf(stream, "%02x ", ptr[i + c]); + else + fputs(" ", stream); + } + + for(c = 0; (c < width) && (i + c < size); c++) { + fprintf(stream, "%c", + (ptr[i + c] >= 0x20) && + (ptr[i + c] < 0x80) ? ptr[i + c] : UNPRINTABLE_CHAR); + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} +#else +#define debugdump(a,x,y,z) +#endif + + +/* decrypt() decrypts 'len' bytes from 'source' to 'dest'. + * + * returns PACKET_NONE on success and PACKET_FAIL on failure + */ + +static libssh2pack_t +decrypt(LIBSSH2_SESSION * session, unsigned char *source, + unsigned char *dest, int len) +{ + struct transportpacket *p = &session->packet; + int blocksize = session->remote.crypt->blocksize; + + /* if we get called with a len that isn't an even number of blocksizes + we risk losing those extra bytes */ + assert((len % blocksize) == 0); + + while (len >= blocksize) { + if (session->remote.crypt->crypt(session, source, + &session->remote.crypt_abstract)) { + libssh2_error(session, LIBSSH2_ERROR_DECRYPT, + (char *) "Error decrypting packet", 0); + LIBSSH2_FREE(session, p->payload); + return PACKET_FAIL; + } + + /* if the crypt() function would write to a given address it + wouldn't have to memcpy() and we could avoid this memcpy() + too */ + memcpy(dest, source, blocksize); + + len -= blocksize; /* less bytes left */ + dest += blocksize; /* advance write pointer */ + source += blocksize; /* advance read pointer */ + } + return PACKET_NONE; /* all is fine */ +} + +/* + * fullpacket() gets called when a full packet has been received and properly + * collected. + */ +static libssh2pack_t +fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ ) +{ + unsigned char macbuf[MAX_MACSIZE]; + struct transportpacket *p = &session->packet; + int rc; + + if (session->fullpacket_state == libssh2_NB_state_idle) { + session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED; + session->fullpacket_payload_len = p->packet_length - 1; + + if (encrypted) { + + /* Calculate MAC hash */ + session->remote.mac->hash(session, macbuf, /* store hash here */ + session->remote.seqno, + p->init, 5, + p->payload, + session->fullpacket_payload_len, + &session->remote.mac_abstract); + + /* Compare the calculated hash with the MAC we just read from + * the network. The read one is at the very end of the payload + * buffer. Note that 'payload_len' here is the packet_length + * field which includes the padding but not the MAC. + */ + if (memcmp(macbuf, p->payload + session->fullpacket_payload_len, + session->remote.mac->mac_len)) { + session->fullpacket_macstate = LIBSSH2_MAC_INVALID; + } + } + + session->remote.seqno++; + + /* ignore the padding */ + session->fullpacket_payload_len -= p->padding_length; + + /* Check for and deal with decompression */ + if (session->remote.comp && strcmp(session->remote.comp->name, "none")) { + unsigned char *data; + unsigned long data_len; + int free_payload = 1; + + if (session->remote.comp->comp(session, 0, + &data, &data_len, + LIBSSH2_PACKET_MAXDECOMP, + &free_payload, + p->payload, + session->fullpacket_payload_len, + &session->remote.comp_abstract)) { + LIBSSH2_FREE(session, p->payload); + return PACKET_FAIL; + } + + if (free_payload) { + LIBSSH2_FREE(session, p->payload); + p->payload = data; + session->fullpacket_payload_len = data_len; + } else { + if (data == p->payload) { + /* It's not to be freed, because the + * compression layer reused payload, So let's + * do the same! + */ + session->fullpacket_payload_len = data_len; + } else { + /* No comp_method actually lets this happen, + * but let's prepare for the future */ + + LIBSSH2_FREE(session, p->payload); + + /* We need a freeable struct otherwise the + * brigade won't know what to do with it */ + p->payload = LIBSSH2_ALLOC(session, data_len); + if (!p->payload) { + libssh2_error(session, LIBSSH2_ERROR_ALLOC, (char *) + "Unable to allocate memory for copy of uncompressed data", + 0); + return PACKET_ENOMEM; + } + memcpy(p->payload, data, data_len); + session->fullpacket_payload_len = data_len; + } + } + } + + session->fullpacket_packet_type = p->payload[0]; + + debugdump(session, "libssh2_packet_read() plain", + p->payload, session->fullpacket_payload_len); + + session->fullpacket_state = libssh2_NB_state_created; + } + + if (session->fullpacket_state == libssh2_NB_state_created) { + rc = libssh2_packet_add(session, p->payload, + session->fullpacket_payload_len, + session->fullpacket_macstate); + if (rc == PACKET_EAGAIN) { + return PACKET_EAGAIN; + } else if (rc < 0) { + return PACKET_FAIL; + } + } + + session->fullpacket_state = libssh2_NB_state_idle; + + return session->fullpacket_packet_type; +} + + +/* {{{ 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 (PACKET_NONE if nothing added), + * or PACKET_FAIL on failure and PACKET_EAGAIN if it couldn't process a full + * packet. + */ + +/* + * This function reads the binary stream as specified in chapter 6 of RFC4253 + * "The Secure Shell (SSH) Transport Layer Protocol" + */ +libssh2pack_t +libssh2_packet_read(LIBSSH2_SESSION * session) +{ + libssh2pack_t rc; + struct transportpacket *p = &session->packet; + int remainbuf; + int remainpack; + int numbytes; + int numdecrypt; + unsigned char block[MAX_BLOCKSIZE]; + int blocksize; + int encrypted = 1; + + int status; + + /* + * All channels, systems, subsystems, etc eventually make it down here + * when looking for more incoming data. If a key exchange is going on + * (LIBSSH2_STATE_EXCHANGING_KEYS bit is set) then the remote end + * will ONLY send key exchange related traffic. In non-blocking mode, + * there is a chance to break out of the kex_exchange function with an + * EAGAIN status, and never come back to it. If LIBSSH2_STATE_EXCHANGING_KEYS + * is active, then we must redirect to the key exchange. However, + * if kex_exchange is active (as in it is the one that calls this execution + * of packet_read, then don't redirect, as that would be an infinite loop! + */ + + if (session->state & LIBSSH2_STATE_EXCHANGING_KEYS && + !(session->state & LIBSSH2_STATE_KEX_ACTIVE)) { + + /* Whoever wants a packet won't get anything until the key re-exchange + * is done! + */ + _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Redirecting into the" + " key re-exchange"); + status = libssh2_kex_exchange(session, 1, &session->startup_key_state); + if (status == PACKET_EAGAIN) { + libssh2_error(session, LIBSSH2_ERROR_EAGAIN, + "Would block exchanging encryption keys", 0); + return PACKET_EAGAIN; + } else if (status) { + libssh2_error(session, LIBSSH2_ERROR_KEX_FAILURE, + "Unable to exchange encryption keys",0); + return LIBSSH2_ERROR_KEX_FAILURE; + } + } + + /* + * =============================== NOTE =============================== + * I know this is very ugly and not a really good use of "goto", but + * this case statement would be even uglier to do it any other way + */ + if (session->readPack_state == libssh2_NB_state_jump1) { + session->readPack_state = libssh2_NB_state_idle; + encrypted = session->readPack_encrypted; + goto libssh2_packet_read_point1; + } + + do { + if (session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { + return PACKET_NONE; + } + + if (session->state & LIBSSH2_STATE_NEWKEYS) { + blocksize = session->remote.crypt->blocksize; + } else { + encrypted = 0; /* not encrypted */ + blocksize = 5; /* not strictly true, but we can use 5 here to + make the checks below work fine still */ + } + + /* read/use a whole big chunk into a temporary area stored in + the LIBSSH2_SESSION struct. We will decrypt data from that + buffer into the packet buffer so this temp one doesn't have + to be able to keep a whole SSH packet, just be large enough + so that we can read big chunks from the network layer. */ + + /* how much data there is remaining in the buffer to deal with + before we should read more from the network */ + remainbuf = p->writeidx - p->readidx; + + /* if remainbuf turns negative we have a bad internal error */ + assert(remainbuf >= 0); + + if (remainbuf < blocksize) { + /* If we have less than a blocksize left, it is too + little data to deal with, read more */ + ssize_t nread; + + /* move any remainder to the start of the buffer so + that we can do a full refill */ + if (remainbuf) { + memmove(p->buf, &p->buf[p->readidx], remainbuf); + p->readidx = 0; + p->writeidx = remainbuf; + } else { + /* nothing to move, just zero the indexes */ + p->readidx = p->writeidx = 0; + } + + /* now read a big chunk from the network into the temp buffer */ + nread = + recv(session->socket_fd, &p->buf[remainbuf], + PACKETBUFSIZE - remainbuf, + LIBSSH2_SOCKET_RECV_FLAGS(session)); + if (nread <= 0) { + /* check if this is due to EAGAIN and return the special + return code if so, error out normally otherwise */ +#ifdef WIN32 + switch (WSAGetLastError()) { + case WSAEWOULDBLOCK: + errno = EAGAIN; + break; + + case WSAENOTSOCK: + errno = EBADF; + break; + + case WSAENOTCONN: + case WSAECONNABORTED: + errno = WSAENOTCONN; + break; + + case WSAEINTR: + errno = EINTR; + break; + } +#endif /* WIN32 */ + if ((nread < 0) && (errno == EAGAIN)) { + session->socket_block_directions = + LIBSSH2_SESSION_BLOCK_INBOUND; + return PACKET_EAGAIN; + } + return PACKET_FAIL; + } + debugdump(session, "libssh2_packet_read() raw", + &p->buf[remainbuf], nread); + /* advance write pointer */ + p->writeidx += nread; + + /* update remainbuf counter */ + remainbuf = p->writeidx - p->readidx; + } + + /* how much data to deal with from the buffer */ + numbytes = remainbuf; + + if (!p->total_num) { + /* No payload package area allocated yet. To know the + size of this payload, we need to decrypt the first + blocksize data. */ + + if (numbytes < blocksize) { + /* we can't act on anything less than blocksize, but this + check is only done for the initial block since once we have + got the start of a block we can in fact deal with fractions + */ + return PACKET_EAGAIN; + } + + if (encrypted) { + rc = decrypt(session, &p->buf[p->readidx], block, blocksize); + if (rc != PACKET_NONE) { + return rc; + } + /* save the first 5 bytes of the decrypted package, to be + used in the hash calculation later down. */ + memcpy(p->init, &p->buf[p->readidx], 5); + } else { + /* the data is plain, just copy it verbatim to + the working block buffer */ + memcpy(block, &p->buf[p->readidx], blocksize); + } + + /* advance the read pointer */ + p->readidx += blocksize; + + /* we now have the initial blocksize bytes decrypted, + * and we can extract packet and padding length from it + */ + p->packet_length = libssh2_ntohu32(block); + p->padding_length = block[4]; + + /* total_num is the number of bytes following the initial + (5 bytes) packet length and padding length fields */ + p->total_num = + p->packet_length - 1 + + (encrypted ? session->remote.mac->mac_len : 0); + + /* RFC4253 section 6.1 Maximum Packet Length says: + * + * "All implementations MUST be able to process + * packets with uncompressed payload length of 32768 + * bytes or less and total packet size of 35000 bytes + * or less (including length, padding length, payload, + * padding, and MAC.)." + */ + if (p->total_num > LIBSSH2_PACKET_MAXPAYLOAD) { + return PACKET_TOOBIG; + } + + /* Get a packet handle put data into. We get one to + hold all data, including padding and MAC. */ + p->payload = LIBSSH2_ALLOC(session, p->total_num); + if (!p->payload) { + return PACKET_ENOMEM; + } + /* init write pointer to start of payload buffer */ + p->wptr = p->payload; + + if (blocksize > 5) { + /* copy the data from index 5 to the end of + the blocksize from the temporary buffer to + the start of the decrypted buffer */ + memcpy(p->wptr, &block[5], blocksize - 5); + p->wptr += blocksize - 5; /* advance write pointer */ + } + + /* init the data_num field to the number of bytes of + the package read so far */ + p->data_num = p->wptr - p->payload; + + /* we already dealt with a blocksize worth of data */ + numbytes -= blocksize; + } + + /* how much there is left to add to the current payload + package */ + remainpack = p->total_num - p->data_num; + + if (numbytes > remainpack) { + /* if we have more data in the buffer than what is going into this + particular packet, we limit this round to this packet only */ + numbytes = remainpack; + } + + if (encrypted) { + /* At the end of the incoming stream, there is a MAC, + and we don't want to decrypt that since we need it + "raw". We MUST however decrypt the padding data + since it is used for the hash later on. */ + int skip = session->remote.mac->mac_len; + + /* if what we have plus numbytes is bigger than the + total minus the skip margin, we should lower the + amount to decrypt even more */ + if ((p->data_num + numbytes) > (p->total_num - skip)) { + numdecrypt = (p->total_num - skip) - p->data_num; + } else { + int frac; + numdecrypt = numbytes; + frac = numdecrypt % blocksize; + if (frac) { + /* not an aligned amount of blocks, + align it */ + numdecrypt -= frac; + /* and make it no unencrypted data + after it */ + numbytes = 0; + } + } + } else { + /* unencrypted data should not be decrypted at all */ + numdecrypt = 0; + } + + /* if there are bytes to decrypt, do that */ + if (numdecrypt > 0) { + /* now decrypt the lot */ + rc = decrypt(session, &p->buf[p->readidx], p->wptr, numdecrypt); + if (rc != PACKET_NONE) { + return rc; + } + + /* advance the read pointer */ + p->readidx += numdecrypt; + /* advance write pointer */ + p->wptr += numdecrypt; + /* increse data_num */ + p->data_num += numdecrypt; + + /* bytes left to take care of without decryption */ + numbytes -= numdecrypt; + } + + /* if there are bytes to copy that aren't decrypted, simply + copy them as-is to the target buffer */ + if (numbytes > 0) { + memcpy(p->wptr, &p->buf[p->readidx], numbytes); + + /* advance the read pointer */ + p->readidx += numbytes; + /* advance write pointer */ + p->wptr += numbytes; + /* increse data_num */ + p->data_num += numbytes; + } + + /* now check how much data there's left to read to finish the + current packet */ + remainpack = p->total_num - p->data_num; + + if (!remainpack) { + /* we have a full packet */ + libssh2_packet_read_point1: + rc = fullpacket(session, encrypted); + if (rc == PACKET_EAGAIN) { + + if (session->packAdd_state != libssh2_NB_state_idle) + { + /* fullpacket only returns PACKET_EAGAIN if + * libssh2_packet_add returns PACKET_EAGAIN. If that + * returns PACKET_EAGAIN but the packAdd_state is idle, + * then the packet has been added to the brigade, but some + * immediate action that was taken based on the packet + * type (such as key re-exchange) is not yet complete. + * Clear the way for a new packet to be read in. + */ + session->readPack_encrypted = encrypted; + session->readPack_state = libssh2_NB_state_jump1; + } + + return PACKET_EAGAIN; + } + + p->total_num = 0; /* no packet buffer available */ + + return rc; + } + } while (1); /* loop */ + + return PACKET_FAIL; /* we never reach this point */ +} + +/* }}} */ + +static libssh2pack_t +send_existing(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long data_len, ssize_t * ret) +{ + ssize_t rc; + ssize_t length; + struct transportpacket *p = &session->packet; + + if (!p->outbuf) { + *ret = 0; + return PACKET_NONE; + } + + /* send as much as possible of the existing packet */ + if ((data != p->odata) || (data_len != p->olen)) { + /* When we are about to complete the sending of a packet, it is vital + that the caller doesn't try to send a new/different packet since + we don't add this one up until the previous one has been sent. To + make the caller really notice his/hers flaw, we return error for + this case */ + return PACKET_BADUSE; + } + + *ret = 1; /* set to make our parent return */ + + /* number of bytes left to send */ + length = p->ototal_num - p->osent; + + rc = send(session->socket_fd, &p->outbuf[p->osent], length, + LIBSSH2_SOCKET_SEND_FLAGS(session)); + + if (rc == length) { + /* the remainder of the package was sent */ + LIBSSH2_FREE(session, p->outbuf); + p->outbuf = NULL; + p->ototal_num = 0; + } else if (rc < 0) { + /* nothing was sent */ + if (errno != EAGAIN) { + /* send failure! */ + return PACKET_FAIL; + } + session->socket_block_directions = LIBSSH2_SESSION_BLOCK_OUTBOUND; + return PACKET_EAGAIN; + } + + debugdump(session, "libssh2_packet_write send()", &p->outbuf[p->osent], + length); + p->osent += length; /* we sent away this much data */ + + return PACKET_NONE; +} + +/* {{{ libssh2_packet_write + * Send a packet, encrypting it and adding a MAC code if necessary + * Returns 0 on success, non-zero on failure. + * + * Returns PACKET_EAGAIN if it would block - and if it does so, you should + * call this function again as soon as it is likely that more data can be + * sent, and this function should then be called with the same argument set + * (same data pointer and same data_len) until zero or failure is returned. + * + * NOTE: this function does not verify that 'data_len' is less than ~35000 + * which is what all implementations should support at least as packet size. + * (RFC4253 section 6.1) + */ +int +libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data, + unsigned long data_len) +{ + int blocksize = + (session->state & LIBSSH2_STATE_NEWKEYS) ? session->local.crypt-> + blocksize : 8; + int padding_length; + int packet_length; + int total_length; + int free_data = 0; +#ifdef RANDOM_PADDING + int rand_max; + int seed = data[0]; /* FIXME: make this random */ +#endif + struct transportpacket *p = &session->packet; + int encrypted; + int i; + ssize_t ret; + libssh2pack_t rc; + unsigned char *orgdata = data; + unsigned long orgdata_len = data_len; + + debugdump(session, "libssh2_packet_write plain", data, data_len); + + /* FIRST, check if we have a pending write to complete */ + rc = send_existing(session, data, data_len, &ret); + if (rc || ret) { + return rc; + } + + encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0; + + /* check if we should compress */ + if (encrypted && 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 PACKET_COMPRESS; /* compression failure */ + } + } + + /* RFC4253 says: Note that the length of the concatenation of + 'packet_length', 'padding_length', 'payload', and 'random padding' + MUST be a multiple of the cipher block size or 8, whichever is + larger. */ + + /* Plain math: (4 + 1 + packet_length + padding_length) % blocksize == 0 */ + + packet_length = data_len + 1 + 4; /* 1 is for padding_length field + 4 for the packet_length field */ + + /* at this point we have it all except the padding */ + + /* first figure out our minimum padding amount to make it an even + block size */ + padding_length = blocksize - (packet_length % blocksize); + + /* if the padding becomes too small we add another blocksize worth + of it (taken from the original libssh2 where it didn't have any + real explanation) */ + if (padding_length < 4) { + padding_length += blocksize; + } +#ifdef RANDOM_PADDING + /* FIXME: we can add padding here, but that also makes the packets + bigger etc */ + + /* now we can add 'blocksize' to the padding_length N number of times + (to "help thwart traffic analysis") but it must be less than 255 in + total */ + rand_max = (255 - padding_length) / blocksize + 1; + padding_length += blocksize * (seed % rand_max); +#endif + + packet_length += padding_length; + + /* append the MAC length to the total_length size */ + total_length = + packet_length + (encrypted ? session->local.mac->mac_len : 0); + + /* allocate memory to store the outgoing packet in, in case we can't + send the whole one and thus need to keep it after this function + returns. */ + p->outbuf = LIBSSH2_ALLOC(session, total_length); + if (!p->outbuf) { + return PACKET_ENOMEM; + } + + /* store packet_length, which is the size of the whole packet except + the MAC and the packet_length field itself */ + libssh2_htonu32(p->outbuf, packet_length - 4); + /* store padding_length */ + p->outbuf[4] = padding_length; + /* copy the payload data */ + memcpy(p->outbuf + 5, data, data_len); + /* fill the padding area with random junk */ + libssh2_random(p->outbuf + 5 + data_len, padding_length); + if (free_data) { + LIBSSH2_FREE(session, data); + } + + if (encrypted) { + /* Calculate MAC hash. Put the output at index packet_length, + since that size includes the whole packet. The MAC is + calculated on the entire unencrypted packet, including all + fields except the MAC field itself. */ + session->local.mac->hash(session, p->outbuf + packet_length, + session->local.seqno, p->outbuf, + packet_length, NULL, 0, + &session->local.mac_abstract); + + /* Encrypt the whole packet data, one block size at a time. + The MAC field is not encrypted. */ + for(i = 0; i < packet_length; i += session->local.crypt->blocksize) { + unsigned char *ptr = &p->outbuf[i]; + if (session->local.crypt->crypt(session, ptr, + &session->local.crypt_abstract)) + return PACKET_FAIL; /* encryption failure */ + } + } + + session->local.seqno++; + + ret = send(session->socket_fd, p->outbuf, total_length, + LIBSSH2_SOCKET_SEND_FLAGS(session)); + + if (ret != -1) { + debugdump(session, "libssh2_packet_write send()", p->outbuf, ret); + } + if (ret != total_length) { + if ((ret > 0) || ((ret == -1) && (errno == EAGAIN))) { + /* the whole packet could not be sent, save the rest */ + session->socket_block_directions = LIBSSH2_SESSION_BLOCK_OUTBOUND; + p->odata = orgdata; + p->olen = orgdata_len; + p->osent = (ret == -1) ? 0 : ret; + p->ototal_num = total_length; + return PACKET_EAGAIN; + } + return PACKET_FAIL; + } + + /* the whole thing got sent away */ + p->odata = NULL; + p->olen = 0; + LIBSSH2_FREE(session, p->outbuf); + p->outbuf = NULL; + + return PACKET_NONE; /* all is good */ +} + +/* }}} */ + Property changes on: libssh2/src/transport.c ___________________________________________________________________ Added: svn:mime-type + text/x-c Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Added: svn:eol-style + native Added: svn:executable + * Property changes on: libssh2/src ___________________________________________________________________ Added: svn:ignore + libssh2_config.h Index: package.xml =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/xml Property changes on: package.xml ___________________________________________________________________ Deleted: svn:mime-type - application/xml Index: ssh2_fopen_wrappers.c =================================================================== --- ssh2_fopen_wrappers.c (.../tags/RELEASE_0_11_0) +++ ssh2_fopen_wrappers.c (.../trunk) @@ -46,7 +46,7 @@ libssh2_channel_set_blocking(abstract->channel, abstract->is_blocking); readstate = libssh2_channel_read_ex(abstract->channel, abstract->streamid, buf, count); - return (readstate == LIBSSH2_ERROR_EAGAIN ? 0 : readstate); + return (readstate < 0 ? 0 : readstate); } static int php_ssh2_channel_stream_close(php_stream *stream, int close_handle TSRMLS_DC) Property changes on: ssh2_fopen_wrappers.c ___________________________________________________________________ Modified: cvs2svn:cvs-rev - 1.15 + 1.16 Index: php_ssh2.h =================================================================== --- php_ssh2.h (.../tags/RELEASE_0_11_0) +++ php_ssh2.h (.../trunk) @@ -101,6 +101,10 @@ } php_ssh2_pkey_subsys_data; #endif +#ifndef PHP_WIN32 +#define closesocket(s) close(s) +#endif + #ifdef ZTS #define SSH2_TSRMLS_SET(datap) ((php_ssh2_session_data*)(datap))->tsrm_ls = TSRMLS_C #define SSH2_TSRMLS_FETCH(datap) TSRMLS_D = ((php_ssh2_session_data*)(datap))->tsrm_ls @@ -109,6 +113,12 @@ #define SSH2_TSRMLS_FETCH(datap) #endif +#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION >= 3) +#define ZEND_IS_CALLABLE_TSRMLS_CC TSRMLS_CC +#else +#define ZEND_IS_CALLABLE_TSRMLS_CC +#endif + /* < 5.3 compatibility */ #ifndef Z_REFCOUNT_P #define Z_REFCOUNT_P(pz) (pz)->refcount Property changes on: php_ssh2.h ___________________________________________________________________ Modified: cvs2svn:cvs-rev - 1.12 + 1.14 Index: EXPERIMENTAL =================================================================== Property changes on: EXPERIMENTAL ___________________________________________________________________ Added: svn:eol-style + native Added: svn:keywords + Id Rev Revision Date LastChangedDate LastChangedRevision Author LastChangedBy HeadURL URL Added: cvs2svn:cvs-rev + 1.1 Property changes on: . ___________________________________________________________________ Added: svn:ignore + #*# *.dsw *.la *.lo *.ncb *.opt *.plg *.tgz *~ .#* .deps .libs Debug Debug_TS Makefile Makefile.fragments Makefile.global Makefile.objects Release Release_TS Release_TSDbg Release_TS_inline Release_inline acinclude.m4 aclocal.m4 autom4te.cache build config.cache config.guess config.h config.h.in config.log config.nice config.status config.sub configure configure.in conftest conftest.c include install-sh libs.mk libtool ltmain.sh missing mkinstalldirs modules scan_makefile_in.awk *.gcda *.gcno run-tests.php