Adding libssh

This commit is contained in:
Nicolas Viennot 2013-06-10 00:58:12 -04:00
parent 220b2afb3c
commit 0f7ccda4fb
211 changed files with 59656 additions and 2 deletions

254
libssh/src/CMakeLists.txt Normal file
View file

@ -0,0 +1,254 @@
project(libssh-library C)
set(LIBSSH_PUBLIC_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/include
CACHE INTERNAL "libssh public include directories"
)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${CMAKE_BINARY_DIR}
${OPENSSL_INCLUDE_DIRS}
)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_REQUIRED_LIBRARIES}
)
if (WIN32)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
ws2_32
)
endif (WIN32)
if (HAVE_LIBSOCKET)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
socket
)
endif (HAVE_LIBSOCKET)
if (OPENSSL_LIBRARIES)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${LIBSSH_PRIVATE_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIRS}
)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
${OPENSSL_LIBRARIES}
)
endif (OPENSSL_LIBRARIES)
if (GCRYPT_LIBRARY)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${LIBSSH_PRIVATE_INCLUDE_DIRS}
${GCRYPT_INCLUDE_DIR}
)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
${GCRYPT_LIBRARY}
)
endif (GCRYPT_LIBRARY)
if (WITH_ZLIB)
set(LIBSSH_PRIVATE_INCLUDE_DIRS
${LIBSSH_PRIVATE_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
${ZLIB_LIBRARY}
)
endif (WITH_ZLIB)
set(LIBSSH_LINK_LIBRARIES
${LIBSSH_LINK_LIBRARIES}
CACHE INTERNAL "libssh link libraries"
)
set(LIBSSH_SHARED_LIBRARY
ssh_shared
CACHE INTERNAL "libssh shared library"
)
if (WITH_STATIC_LIB)
set(LIBSSH_STATIC_LIBRARY
ssh_static
CACHE INTERNAL "libssh static library"
)
endif (WITH_STATIC_LIB)
set(libssh_SRCS
agent.c
auth.c
base64.c
buffer.c
callbacks.c
channels.c
client.c
config.c
connect.c
dh.c
ecdh.c
error.c
getpass.c
init.c
kex.c
known_hosts.c
legacy.c
libcrypto.c
log.c
match.c
messages.c
misc.c
options.c
packet.c
packet_cb.c
packet_crypt.c
pcap.c
pki.c
poll.c
session.c
scp.c
socket.c
string.c
threads.c
wrapper.c
)
if (WITH_GCRYPT)
set(libssh_SRCS
${libssh_SRCS}
libgcrypt.c
gcrypt_missing.c
pki_gcrypt.c
)
else (WITH_GCRYPT)
set(libssh_SRCS
${libssh_SRCS}
pki_crypto.c
)
endif (WITH_GCRYPT)
if (WITH_SFTP)
set(libssh_SRCS
${libssh_SRCS}
sftp.c
)
if (WITH_SERVER)
set(libssh_SRCS
${libssh_SRCS}
sftpserver.c
)
endif (WITH_SERVER)
endif (WITH_SFTP)
if (WITH_SSH1)
set(libssh_SRCS
${libssh_SRCS}
auth1.c
channels1.c
crc32.c
kex1.c
packet1.c
)
endif (WITH_SSH1)
if (WITH_SERVER)
set(libssh_SRCS
${libssh_SRCS}
server.c
bind.c
)
endif (WITH_SERVER)
if (WITH_ZLIB)
set(libssh_SRCS
${libssh_SRCS}
gzip.c
)
endif(WITH_ZLIB)
include_directories(
${LIBSSH_PUBLIC_INCLUDE_DIRS}
${LIBSSH_PRIVATE_INCLUDE_DIRS}
)
add_library(${LIBSSH_SHARED_LIBRARY} SHARED ${libssh_SRCS})
target_link_libraries(${LIBSSH_SHARED_LIBRARY} ${LIBSSH_LINK_LIBRARIES})
set_target_properties(
${LIBSSH_SHARED_LIBRARY}
PROPERTIES
VERSION
${LIBRARY_VERSION}
SOVERSION
${LIBRARY_SOVERSION}
OUTPUT_NAME
ssh
DEFINE_SYMBOL
LIBSSH_EXPORTS
)
if (WITH_VISIBILITY_HIDDEN)
set_target_properties(${LIBSSH_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
endif (WITH_VISIBILITY_HIDDEN)
install(
TARGETS
${LIBSSH_SHARED_LIBRARY}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
COMPONENT libraries
)
if (WITH_STATIC_LIB)
add_library(${LIBSSH_STATIC_LIBRARY} STATIC ${libssh_SRCS})
if (MSVC)
set(OUTPUT_SUFFIX static)
else (MSVC)
set(OUTPUT_SUFFIX )
endif (MSVC)
set_target_properties(
${LIBSSH_STATIC_LIBRARY}
PROPERTIES
VERSION
${LIBRARY_VERSION}
SOVERSION
${LIBRARY_SOVERSION}
OUTPUT_NAME
ssh
ARCHIVE_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX}
)
if (WIN32)
set_target_properties(
${LIBSSH_STATIC_LIBRARY}
PROPERTIES
COMPILE_FLAGS
"-DLIBSSH_STATIC"
)
endif (WIN32)
install(
TARGETS
${LIBSSH_STATIC_LIBRARY}
DESTINATION
${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX}
COMPONENT
libraries
)
endif (WITH_STATIC_LIB)
if (CMAKE_HAVE_THREADS_LIBRARY)
add_subdirectory(threads)
endif (CMAKE_HAVE_THREADS_LIBRARY)

516
libssh/src/agent.c Normal file
View file

@ -0,0 +1,516 @@
/*
* agent.c - ssh agent functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2008-2009 by Andreas Schneider <asn@cryptomilk.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* This file is based on authfd.c from OpenSSH */
/*
* How does the ssh-agent work?
*
* a) client sends a request to get a list of all keys
* the agent returns the count and all public keys
* b) iterate over them to check if the server likes one
* c) the client sends a sign request to the agent
* type, pubkey as blob, data to sign, flags
* the agent returns the signed data
*/
#ifndef _WIN32
#include "config.h"
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/agent.h"
#include "libssh/priv.h"
#include "libssh/socket.h"
#include "libssh/buffer.h"
#include "libssh/session.h"
#include "libssh/poll.h"
#include "libssh/pki.h"
/* macro to check for "agent failure" message */
#define agent_failed(x) \
(((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \
((x) == SSH2_AGENT_FAILURE))
static uint32_t agent_get_u32(const void *vp) {
const uint8_t *p = (const uint8_t *)vp;
uint32_t v;
v = (uint32_t)p[0] << 24;
v |= (uint32_t)p[1] << 16;
v |= (uint32_t)p[2] << 8;
v |= (uint32_t)p[3];
return v;
}
static void agent_put_u32(void *vp, uint32_t v) {
uint8_t *p = (uint8_t *)vp;
p[0] = (uint8_t)(v >> 24) & 0xff;
p[1] = (uint8_t)(v >> 16) & 0xff;
p[2] = (uint8_t)(v >> 8) & 0xff;
p[3] = (uint8_t)v & 0xff;
}
static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) {
char *b = buf;
size_t pos = 0;
ssize_t res;
ssh_pollfd_t pfd;
socket_t fd = ssh_socket_get_fd_in(s);
pfd.fd = fd;
pfd.events = do_read ? POLLIN : POLLOUT;
while (n > pos) {
if (do_read) {
res = read(fd, b + pos, n - pos);
} else {
res = write(fd, b + pos, n - pos);
}
switch (res) {
case -1:
if (errno == EINTR) {
continue;
}
#ifdef EWOULDBLOCK
if (errno == EAGAIN || errno == EWOULDBLOCK) {
#else
if (errno == EAGAIN) {
#endif
(void) ssh_poll(&pfd, 1, -1);
continue;
}
return 0;
case 0:
/* read returns 0 on end-of-file */
errno = do_read ? 0 : EPIPE;
return pos;
default:
pos += (size_t) res;
}
}
return pos;
}
ssh_agent agent_new(struct ssh_session_struct *session) {
ssh_agent agent = NULL;
agent = malloc(sizeof(struct ssh_agent_struct));
if (agent == NULL) {
return NULL;
}
ZERO_STRUCTP(agent);
agent->count = 0;
agent->sock = ssh_socket_new(session);
if (agent->sock == NULL) {
SAFE_FREE(agent);
return NULL;
}
return agent;
}
void agent_close(struct ssh_agent_struct *agent) {
if (agent == NULL) {
return;
}
if (getenv("SSH_AUTH_SOCK")) {
ssh_socket_close(agent->sock);
}
}
void agent_free(ssh_agent agent) {
if (agent) {
if (agent->ident) {
ssh_buffer_free(agent->ident);
}
if (agent->sock) {
agent_close(agent);
ssh_socket_free(agent->sock);
}
SAFE_FREE(agent);
}
}
static int agent_connect(ssh_session session) {
const char *auth_sock = NULL;
if (session == NULL || session->agent == NULL) {
return -1;
}
auth_sock = getenv("SSH_AUTH_SOCK");
if (auth_sock && *auth_sock) {
if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) {
return -1;
}
return 0;
}
return -1;
}
#if 0
static int agent_decode_reply(struct ssh_session_struct *session, int type) {
switch (type) {
case SSH_AGENT_FAILURE:
case SSH2_AGENT_FAILURE:
case SSH_COM_AGENT2_FAILURE:
ssh_log(session, SSH_LOG_RARE, "SSH_AGENT_FAILURE");
return 0;
case SSH_AGENT_SUCCESS:
return 1;
default:
ssh_set_error(session, SSH_FATAL,
"Bad response from authentication agent: %d", type);
break;
}
return -1;
}
#endif
static int agent_talk(struct ssh_session_struct *session,
struct ssh_buffer_struct *request, struct ssh_buffer_struct *reply) {
uint32_t len = 0;
uint8_t payload[1024] = {0};
len = buffer_get_rest_len(request);
SSH_LOG(session, SSH_LOG_TRACE, "Request length: %u", len);
agent_put_u32(payload, len);
/* send length and then the request packet */
if (atomicio(session->agent->sock, payload, 4, 0) == 4) {
if (atomicio(session->agent->sock, buffer_get_rest(request), len, 0)
!= len) {
SSH_LOG(session, SSH_LOG_WARN, "atomicio sending request failed: %s",
strerror(errno));
return -1;
}
} else {
SSH_LOG(session, SSH_LOG_WARN,
"atomicio sending request length failed: %s",
strerror(errno));
return -1;
}
/* wait for response, read the length of the response packet */
if (atomicio(session->agent->sock, payload, 4, 1) != 4) {
SSH_LOG(session, SSH_LOG_WARN, "atomicio read response length failed: %s",
strerror(errno));
return -1;
}
len = agent_get_u32(payload);
if (len > 256 * 1024) {
ssh_set_error(session, SSH_FATAL,
"Authentication response too long: %u", len);
return -1;
}
SSH_LOG(session, SSH_LOG_TRACE, "Response length: %u", len);
while (len > 0) {
size_t n = len;
if (n > sizeof(payload)) {
n = sizeof(payload);
}
if (atomicio(session->agent->sock, payload, n, 1) != n) {
SSH_LOG(session, SSH_LOG_WARN,
"Error reading response from authentication socket.");
return -1;
}
if (buffer_add_data(reply, payload, n) < 0) {
SSH_LOG(session, SSH_LOG_WARN, "Not enough space");
return -1;
}
len -= n;
}
return 0;
}
int ssh_agent_get_ident_count(struct ssh_session_struct *session) {
ssh_buffer request = NULL;
ssh_buffer reply = NULL;
unsigned int type = 0;
unsigned int c1 = 0, c2 = 0;
uint8_t buf[4] = {0};
switch (session->version) {
case 1:
c1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES;
c2 = SSH_AGENT_RSA_IDENTITIES_ANSWER;
break;
case 2:
c1 = SSH2_AGENTC_REQUEST_IDENTITIES;
c2 = SSH2_AGENT_IDENTITIES_ANSWER;
break;
default:
return 0;
}
/* send message to the agent requesting the list of identities */
request = ssh_buffer_new();
if (buffer_add_u8(request, c1) < 0) {
ssh_set_error(session, SSH_FATAL, "Not enough space");
return -1;
}
reply = ssh_buffer_new();
if (reply == NULL) {
ssh_buffer_free(request);
ssh_set_error(session, SSH_FATAL, "Not enough space");
return -1;
}
if (agent_talk(session, request, reply) < 0) {
ssh_buffer_free(request);
ssh_buffer_free(reply);
return 0;
}
ssh_buffer_free(request);
/* get message type and verify the answer */
buffer_get_u8(reply, (uint8_t *) &type);
SSH_LOG(session, SSH_LOG_WARN,
"Answer type: %d, expected answer: %d",
type, c2);
if (agent_failed(type)) {
return 0;
} else if (type != c2) {
ssh_set_error(session, SSH_FATAL,
"Bad authentication reply message type: %d", type);
return -1;
}
buffer_get_u32(reply, (uint32_t *) buf);
session->agent->count = agent_get_u32(buf);
SSH_LOG(session, SSH_LOG_DEBUG, "Agent count: %d",
session->agent->count);
if (session->agent->count > 1024) {
ssh_set_error(session, SSH_FATAL,
"Too many identities in authentication reply: %d",
session->agent->count);
ssh_buffer_free(reply);
return -1;
}
if (session->agent->ident) {
buffer_reinit(session->agent->ident);
}
session->agent->ident = reply;
return session->agent->count;
}
/* caller has to free commment */
ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
char **comment) {
if (ssh_agent_get_ident_count(session) > 0) {
return ssh_agent_get_next_ident(session, comment);
}
return NULL;
}
/* caller has to free commment */
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
char **comment) {
struct ssh_key_struct *key;
struct ssh_string_struct *blob = NULL;
struct ssh_string_struct *tmp = NULL;
int rc;
if (session->agent->count == 0) {
return NULL;
}
switch(session->version) {
case 1:
return NULL;
case 2:
/* get the blob */
blob = buffer_get_ssh_string(session->agent->ident);
if (blob == NULL) {
return NULL;
}
/* get the comment */
tmp = buffer_get_ssh_string(session->agent->ident);
if (tmp == NULL) {
ssh_string_free(blob);
return NULL;
}
if (comment) {
*comment = ssh_string_to_char(tmp);
} else {
ssh_string_free(blob);
ssh_string_free(tmp);
return NULL;
}
ssh_string_free(tmp);
/* get key from blob */
rc = ssh_pki_import_pubkey_blob(blob, &key);
ssh_string_free(blob);
if (rc == SSH_ERROR) {
return NULL;
}
break;
default:
return NULL;
}
return key;
}
int agent_is_running(ssh_session session) {
if (session == NULL || session->agent == NULL) {
return 0;
}
if (ssh_socket_is_open(session->agent->sock)) {
return 1;
} else {
if (agent_connect(session) < 0) {
return 0;
} else {
return 1;
}
}
return 0;
}
ssh_string ssh_agent_sign_data(ssh_session session,
const ssh_key pubkey,
struct ssh_buffer_struct *data)
{
ssh_buffer request;
ssh_buffer reply;
ssh_string key_blob;
ssh_string sig_blob;
int type = SSH2_AGENT_FAILURE;
int flags = 0;
uint32_t dlen;
int rc;
request = ssh_buffer_new();
if (request == NULL) {
return NULL;
}
/* create request */
if (buffer_add_u8(request, SSH2_AGENTC_SIGN_REQUEST) < 0) {
ssh_buffer_free(request);
return NULL;
}
rc = ssh_pki_export_pubkey_blob(pubkey, &key_blob);
if (rc < 0) {
ssh_buffer_free(request);
return NULL;
}
/* adds len + blob */
rc = buffer_add_ssh_string(request, key_blob);
ssh_string_free(key_blob);
if (rc < 0) {
ssh_buffer_free(request);
return NULL;
}
/* Add data */
dlen = buffer_get_rest_len(data);
if (buffer_add_u32(request, htonl(dlen)) < 0) {
ssh_buffer_free(request);
return NULL;
}
if (buffer_add_data(request, buffer_get_rest(data), dlen) < 0) {
ssh_buffer_free(request);
return NULL;
}
if (buffer_add_u32(request, htonl(flags)) < 0) {
ssh_buffer_free(request);
return NULL;
}
reply = ssh_buffer_new();
if (reply == NULL) {
ssh_buffer_free(request);
return NULL;
}
/* send the request */
if (agent_talk(session, request, reply) < 0) {
ssh_buffer_free(request);
ssh_buffer_free(reply);
return NULL;
}
ssh_buffer_free(request);
/* check if reply is valid */
if (buffer_get_u8(reply, (uint8_t *) &type) != sizeof(uint8_t)) {
ssh_buffer_free(reply);
return NULL;
}
if (agent_failed(type)) {
SSH_LOG(session, SSH_LOG_WARN, "Agent reports failure in signing the key");
ssh_buffer_free(reply);
return NULL;
} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
ssh_set_error(session, SSH_FATAL, "Bad authentication response: %d", type);
ssh_buffer_free(reply);
return NULL;
}
sig_blob = buffer_get_ssh_string(reply);
ssh_buffer_free(reply);
return sig_blob;
}
#endif /* _WIN32 */
/* vim: set ts=4 sw=4 et cindent: */

2116
libssh/src/auth.c Normal file

File diff suppressed because it is too large Load diff

235
libssh/src/auth1.c Normal file
View file

@ -0,0 +1,235 @@
/*
* auth1.c - authentication with SSH-1 protocol
*
* This file is part of the SSH Library
*
* Copyright (c) 2005-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/ssh1.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/session.h"
#include "libssh/string.h"
#ifdef WITH_SSH1
static int ssh_auth_status_termination(void *s){
ssh_session session=s;
if(session->auth_state != SSH_AUTH_STATE_NONE ||
session->session_state == SSH_SESSION_STATE_ERROR)
return 1;
return 0;
}
static int wait_auth1_status(ssh_session session) {
enter_function();
/* wait for a packet */
if (ssh_handle_packets_termination(session,SSH_TIMEOUT_USER,
ssh_auth_status_termination, session) != SSH_OK){
leave_function();
return SSH_AUTH_ERROR;
}
ssh_log(session,SSH_LOG_PROTOCOL,"Auth state : %d",session->auth_state);
leave_function();
switch(session->auth_state) {
case SSH_AUTH_STATE_SUCCESS:
return SSH_AUTH_SUCCESS;
case SSH_AUTH_STATE_FAILED:
return SSH_AUTH_DENIED;
default:
return SSH_AUTH_AGAIN;
}
return SSH_AUTH_ERROR;
}
void ssh_auth1_handler(ssh_session session, uint8_t type){
if(session->session_state != SSH_SESSION_STATE_AUTHENTICATING){
ssh_set_error(session,SSH_FATAL,"SSH_SMSG_SUCCESS or FAILED received in wrong state");
return;
}
if(type==SSH_SMSG_SUCCESS){
session->auth_state=SSH_AUTH_STATE_SUCCESS;
session->session_state=SSH_SESSION_STATE_AUTHENTICATED;
} else if(type==SSH_SMSG_FAILURE)
session->auth_state=SSH_AUTH_STATE_FAILED;
}
static int send_username(ssh_session session, const char *username) {
ssh_string user = NULL;
int rc;
/* returns SSH_AUTH_SUCCESS or SSH_AUTH_DENIED */
if(session->auth_service_state == SSH_AUTH_SERVICE_USER_SENT) {
if(session->auth_state == SSH_AUTH_STATE_FAILED)
return SSH_AUTH_DENIED;
if(session->auth_state == SSH_AUTH_STATE_SUCCESS)
return SSH_AUTH_SUCCESS;
return SSH_AUTH_ERROR;
}
if (session->auth_service_state == SSH_AUTH_SERVICE_SENT)
goto pending;
if (!username) {
if(!(username = session->opts.username)) {
if (ssh_options_set(session, SSH_OPTIONS_USER, NULL) < 0) {
session->auth_service_state = SSH_AUTH_SERVICE_DENIED;
return SSH_ERROR;
} else {
username = session->opts.username;
}
}
}
user = ssh_string_from_char(username);
if (user == NULL) {
return SSH_AUTH_ERROR;
}
if (buffer_add_u8(session->out_buffer, SSH_CMSG_USER) < 0) {
ssh_string_free(user);
return SSH_AUTH_ERROR;
}
if (buffer_add_ssh_string(session->out_buffer, user) < 0) {
ssh_string_free(user);
return SSH_AUTH_ERROR;
}
ssh_string_free(user);
session->auth_state=SSH_AUTH_STATE_NONE;
session->auth_service_state = SSH_AUTH_SERVICE_SENT;
if (packet_send(session) == SSH_ERROR) {
return SSH_AUTH_ERROR;
}
pending:
rc = wait_auth1_status(session);
switch (rc){
case SSH_AUTH_SUCCESS:
session->auth_service_state=SSH_AUTH_SERVICE_USER_SENT;
session->auth_state=SSH_AUTH_STATE_SUCCESS;
ssh_set_error(session, SSH_NO_ERROR, "Authentication successful");
return SSH_AUTH_SUCCESS;
case SSH_AUTH_DENIED:
session->auth_service_state=SSH_AUTH_SERVICE_USER_SENT;
ssh_set_error(session,SSH_REQUEST_DENIED,"Password authentication necessary for user %s",username);
return SSH_AUTH_DENIED;
case SSH_AUTH_AGAIN:
return SSH_AUTH_AGAIN;
default:
session->auth_service_state = SSH_AUTH_SERVICE_NONE;
session->auth_state=SSH_AUTH_STATE_ERROR;
return SSH_AUTH_ERROR;
}
}
/* use the "none" authentication question */
int ssh_userauth1_none(ssh_session session, const char *username){
return send_username(session, username);
}
/** \internal
* \todo implement ssh1 public key
*/
int ssh_userauth1_offer_pubkey(ssh_session session, const char *username,
int type, ssh_string pubkey) {
(void) session;
(void) username;
(void) type;
(void) pubkey;
enter_function();
leave_function();
return SSH_AUTH_DENIED;
}
int ssh_userauth1_password(ssh_session session, const char *username,
const char *password) {
ssh_string pwd = NULL;
int rc;
enter_function();
rc = send_username(session, username);
if (rc != SSH_AUTH_DENIED) {
leave_function();
return rc;
}
if (session->pending_call_state == SSH_PENDING_CALL_AUTH_PASSWORD)
goto pending;
/* we trick a bit here. A known flaw in SSH1 protocol is that it's
* easy to guess password sizes.
* not that sure ...
*/
/* XXX fix me here ! */
/* cisco IOS doesn't like when a password is followed by zeroes and random pad. */
if(1 || strlen(password) >= 128) {
/* not risky to disclose the size of such a big password .. */
pwd = ssh_string_from_char(password);
if (pwd == NULL) {
leave_function();
return SSH_AUTH_ERROR;
}
} else {
char buf[128] = {0};
/* fill the password string from random things. the strcpy
* ensure there is at least a nul byte after the password.
* most implementation won't see the garbage at end.
* why garbage ? because nul bytes will be compressed by
* gzip and disclose password len.
*/
pwd = ssh_string_new(sizeof(buf));
if (pwd == NULL) {
leave_function();
return SSH_AUTH_ERROR;
}
ssh_get_random(buf, sizeof(buf), 0);
strcpy(buf, password);
ssh_string_fill(pwd, buf, sizeof(buf));
}
if (buffer_add_u8(session->out_buffer, SSH_CMSG_AUTH_PASSWORD) < 0) {
ssh_string_burn(pwd);
ssh_string_free(pwd);
leave_function();
return SSH_AUTH_ERROR;
}
if (buffer_add_ssh_string(session->out_buffer, pwd) < 0) {
ssh_string_burn(pwd);
ssh_string_free(pwd);
leave_function();
return SSH_AUTH_ERROR;
}
ssh_string_burn(pwd);
ssh_string_free(pwd);
session->auth_state=SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_AUTH_PASSWORD;
if (packet_send(session) == SSH_ERROR) {
leave_function();
return SSH_AUTH_ERROR;
}
pending:
rc = wait_auth1_status(session);
if (rc != SSH_AUTH_AGAIN)
session->pending_call_state = SSH_PENDING_CALL_NONE;
leave_function();
return rc;
}
#endif /* WITH_SSH1 */
/* vim: set ts=2 sw=2 et cindent: */

287
libssh/src/base64.c Normal file
View file

@ -0,0 +1,287 @@
/*
* base64.c - support for base64 alphabet system, described in RFC1521
*
* This file is part of the SSH Library
*
* Copyright (c) 2005-2005 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/* just the dirtiest part of code i ever made */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/buffer.h"
static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* Transformations */
#define SET_A(n, i) do { (n) |= ((i) & 63) <<18; } while (0)
#define SET_B(n, i) do { (n) |= ((i) & 63) <<12; } while (0)
#define SET_C(n, i) do { (n) |= ((i) & 63) << 6; } while (0)
#define SET_D(n, i) do { (n) |= ((i) & 63); } while (0)
#define GET_A(n) (unsigned char) (((n) & 0xff0000) >> 16)
#define GET_B(n) (unsigned char) (((n) & 0xff00) >> 8)
#define GET_C(n) (unsigned char) ((n) & 0xff)
static int _base64_to_bin(unsigned char dest[3], const char *source, int num);
static int get_equals(char *string);
/* First part: base64 to binary */
/**
* @internal
*
* @brief Translates a base64 string into a binary one.
*
* @returns A buffer containing the decoded string, NULL if something went
* wrong (e.g. incorrect char).
*/
ssh_buffer base64_to_bin(const char *source) {
ssh_buffer buffer = NULL;
unsigned char block[3];
char *base64;
char *ptr;
size_t len;
int equals;
base64 = strdup(source);
if (base64 == NULL) {
return NULL;
}
ptr = base64;
/* Get the number of equals signs, which mirrors the padding */
equals = get_equals(ptr);
if (equals > 2) {
SAFE_FREE(base64);
return NULL;
}
buffer = ssh_buffer_new();
if (buffer == NULL) {
SAFE_FREE(base64);
return NULL;
}
len = strlen(ptr);
while (len > 4) {
if (_base64_to_bin(block, ptr, 3) < 0) {
goto error;
}
if (buffer_add_data(buffer, block, 3) < 0) {
goto error;
}
len -= 4;
ptr += 4;
}
/*
* Depending on the number of bytes resting, there are 3 possibilities
* from the RFC.
*/
switch (len) {
/*
* (1) The final quantum of encoding input is an integral multiple of
* 24 bits. Here, the final unit of encoded output will be an integral
* multiple of 4 characters with no "=" padding
*/
case 4:
if (equals != 0) {
goto error;
}
if (_base64_to_bin(block, ptr, 3) < 0) {
goto error;
}
if (buffer_add_data(buffer, block, 3) < 0) {
goto error;
}
SAFE_FREE(base64);
return buffer;
/*
* (2) The final quantum of encoding input is exactly 8 bits; here, the
* final unit of encoded output will be two characters followed by
* two "=" padding characters.
*/
case 2:
if (equals != 2){
goto error;
}
if (_base64_to_bin(block, ptr, 1) < 0) {
goto error;
}
if (buffer_add_data(buffer, block, 1) < 0) {
goto error;
}
SAFE_FREE(base64);
return buffer;
/*
* The final quantum of encoding input is exactly 16 bits. Here, the final
* unit of encoded output will be three characters followed by one "="
* padding character.
*/
case 3:
if (equals != 1) {
goto error;
}
if (_base64_to_bin(block, ptr, 2) < 0) {
goto error;
}
if (buffer_add_data(buffer,block,2) < 0) {
goto error;
}
SAFE_FREE(base64);
return buffer;
default:
/* 4,3,2 are the only padding size allowed */
goto error;
}
error:
SAFE_FREE(base64);
ssh_buffer_free(buffer);
return NULL;
}
#define BLOCK(letter, n) do {ptr = strchr(alphabet, source[n]); \
if(!ptr) return -1; \
i = ptr - alphabet; \
SET_##letter(*block, i); \
} while(0)
/* Returns 0 if ok, -1 if not (ie invalid char into the stuff) */
static int to_block4(unsigned long *block, const char *source, int num) {
char *ptr;
unsigned int i;
*block = 0;
if (num < 1) {
return 0;
}
BLOCK(A, 0); /* 6 bit */
BLOCK(B,1); /* 12 bit */
if (num < 2) {
return 0;
}
BLOCK(C, 2); /* 18 bit */
if (num < 3) {
return 0;
}
BLOCK(D, 3); /* 24 bit */
return 0;
}
/* num = numbers of final bytes to be decoded */
static int _base64_to_bin(unsigned char dest[3], const char *source, int num) {
unsigned long block;
if (to_block4(&block, source, num) < 0) {
return -1;
}
dest[0] = GET_A(block);
dest[1] = GET_B(block);
dest[2] = GET_C(block);
return 0;
}
/* Count the number of "=" signs and replace them by zeroes */
static int get_equals(char *string) {
char *ptr = string;
int num = 0;
while ((ptr=strchr(ptr,'=')) != NULL) {
num++;
*ptr = '\0';
ptr++;
}
return num;
}
/* thanks sysk for debugging my mess :) */
#define BITS(n) ((1 << (n)) - 1)
static void _bin_to_base64(unsigned char *dest, const unsigned char source[3],
int len) {
switch (len) {
case 1:
dest[0] = alphabet[(source[0] >> 2)];
dest[1] = alphabet[((source[0] & BITS(2)) << 4)];
dest[2] = '=';
dest[3] = '=';
break;
case 2:
dest[0] = alphabet[source[0] >> 2];
dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)];
dest[2] = alphabet[(source[1] & BITS(4)) << 2];
dest[3] = '=';
break;
case 3:
dest[0] = alphabet[(source[0] >> 2)];
dest[1] = alphabet[(source[1] >> 4) | ((source[0] & BITS(2)) << 4)];
dest[2] = alphabet[ (source[2] >> 6) | (source[1] & BITS(4)) << 2];
dest[3] = alphabet[source[2] & BITS(6)];
break;
}
}
/**
* @internal
*
* @brief Converts binary data to a base64 string.
*
* @returns the converted string
*/
unsigned char *bin_to_base64(const unsigned char *source, int len) {
unsigned char *base64;
unsigned char *ptr;
int flen = len + (3 - (len % 3)); /* round to upper 3 multiple */
flen = (4 * flen) / 3 + 1;
base64 = malloc(flen);
if (base64 == NULL) {
return NULL;
}
ptr = base64;
while(len > 0){
_bin_to_base64(ptr, source, len > 3 ? 3 : len);
ptr += 4;
source += 3;
len -= 3;
}
ptr[0] = '\0';
return base64;
}
/* vim: set ts=2 sw=2 et cindent: */

466
libssh/src/bind.c Normal file
View file

@ -0,0 +1,466 @@
/*
* bind.c : all ssh_bind functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2004-2005 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/bind.h"
#include "libssh/libssh.h"
#include "libssh/server.h"
#include "libssh/pki.h"
#include "libssh/buffer.h"
#include "libssh/socket.h"
#include "libssh/session.h"
/**
* @addtogroup libssh_server
*
* @{
*/
#ifdef _WIN32
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
/*
* <wspiapi.h> is necessary for getaddrinfo before Windows XP, but it isn't
* available on some platforms like MinGW.
*/
#ifdef HAVE_WSPIAPI_H
# include <wspiapi.h>
#endif
#define SOCKOPT_TYPE_ARG4 char
#else /* _WIN32 */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define SOCKOPT_TYPE_ARG4 int
#endif /* _WIN32 */
static socket_t bind_socket(ssh_bind sshbind, const char *hostname,
int port) {
char port_c[6];
struct addrinfo *ai;
struct addrinfo hints;
int opt = 1;
socket_t s;
int rc;
ZERO_STRUCT(hints);
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
snprintf(port_c, 6, "%d", port);
rc = getaddrinfo(hostname, port_c, &hints, &ai);
if (rc != 0) {
ssh_set_error(sshbind,
SSH_FATAL,
"Resolving %s: %s", hostname, gai_strerror(rc));
return -1;
}
s = socket (ai->ai_family,
ai->ai_socktype,
ai->ai_protocol);
if (s == SSH_INVALID_SOCKET) {
ssh_set_error(sshbind, SSH_FATAL, "%s", strerror(errno));
freeaddrinfo (ai);
return -1;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *)&opt, sizeof(opt)) < 0) {
ssh_set_error(sshbind,
SSH_FATAL,
"Setting socket options failed: %s",
strerror(errno));
freeaddrinfo (ai);
close(s);
return -1;
}
if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) {
ssh_set_error(sshbind,
SSH_FATAL,
"Binding to %s:%d: %s",
hostname,
port,
strerror(errno));
freeaddrinfo (ai);
close(s);
return -1;
}
freeaddrinfo (ai);
return s;
}
ssh_bind ssh_bind_new(void) {
ssh_bind ptr;
ptr = malloc(sizeof(struct ssh_bind_struct));
if (ptr == NULL) {
return NULL;
}
ZERO_STRUCTP(ptr);
ptr->bindfd = SSH_INVALID_SOCKET;
ptr->bindport= 22;
ptr->common.log_verbosity = 0;
return ptr;
}
int ssh_bind_listen(ssh_bind sshbind) {
const char *host;
socket_t fd;
int rc;
if (ssh_init() < 0) {
ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed");
return -1;
}
if (sshbind->ecdsakey == NULL &&
sshbind->dsakey == NULL &&
sshbind->rsakey == NULL) {
ssh_set_error(sshbind, SSH_FATAL,
"DSA or RSA host key file must be set before listen()");
return SSH_ERROR;
}
#ifdef HAVE_ECC
if (sshbind->ecdsakey) {
rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
NULL,
NULL,
NULL,
&sshbind->ecdsa);
if (rc == SSH_ERROR) {
ssh_set_error(sshbind, SSH_FATAL,
"Failed to import private ECDSA host key");
return SSH_ERROR;
}
if (ssh_key_type(sshbind->ecdsa) != SSH_KEYTYPE_ECDSA) {
ssh_set_error(sshbind, SSH_FATAL,
"The ECDSA host key has the wrong type");
ssh_key_free(sshbind->ecdsa);
return SSH_ERROR;
}
}
#endif
if (sshbind->dsakey) {
rc = ssh_pki_import_privkey_file(sshbind->dsakey,
NULL,
NULL,
NULL,
&sshbind->dsa);
if (rc == SSH_ERROR) {
ssh_set_error(sshbind, SSH_FATAL,
"Failed to import private DSA host key");
return SSH_ERROR;
}
if (ssh_key_type(sshbind->dsa) != SSH_KEYTYPE_DSS) {
ssh_set_error(sshbind, SSH_FATAL,
"The DSA host key has the wrong type: %d",
ssh_key_type(sshbind->dsa));
ssh_key_free(sshbind->dsa);
return SSH_ERROR;
}
}
if (sshbind->rsakey) {
rc = ssh_pki_import_privkey_file(sshbind->rsakey,
NULL,
NULL,
NULL,
&sshbind->rsa);
if (rc == SSH_ERROR) {
ssh_set_error(sshbind, SSH_FATAL,
"Failed to import private RSA host key");
return SSH_ERROR;
}
if (ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA &&
ssh_key_type(sshbind->rsa) != SSH_KEYTYPE_RSA1) {
ssh_set_error(sshbind, SSH_FATAL,
"The RSA host key has the wrong type");
ssh_key_free(sshbind->rsa);
return SSH_ERROR;
}
}
if (sshbind->bindfd == SSH_INVALID_SOCKET) {
host = sshbind->bindaddr;
if (host == NULL) {
host = "0.0.0.0";
}
fd = bind_socket(sshbind, host, sshbind->bindport);
if (fd == SSH_INVALID_SOCKET) {
ssh_key_free(sshbind->dsa);
ssh_key_free(sshbind->rsa);
return -1;
}
sshbind->bindfd = fd;
if (listen(fd, 10) < 0) {
ssh_set_error(sshbind, SSH_FATAL,
"Listening to socket %d: %s",
fd, strerror(errno));
close(fd);
ssh_key_free(sshbind->dsa);
ssh_key_free(sshbind->rsa);
return -1;
}
} else {
SSH_LOG(sshbind, SSH_LOG_INFO, "Using app-provided bind socket");
}
return 0;
}
int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks,
void *userdata){
if (sshbind == NULL) {
return SSH_ERROR;
}
if (callbacks == NULL) {
ssh_set_error_invalid(sshbind);
return SSH_ERROR;
}
if(callbacks->size <= 0 || callbacks->size > 1024 * sizeof(void *)){
ssh_set_error(sshbind,SSH_FATAL,
"Invalid callback passed in (badly initialized)");
return SSH_ERROR;
}
sshbind->bind_callbacks = callbacks;
sshbind->bind_callbacks_userdata=userdata;
return 0;
}
/** @internal
* @brief callback being called by poll when an event happens
*
*/
static int ssh_bind_poll_callback(ssh_poll_handle sshpoll,
socket_t fd, int revents, void *user){
ssh_bind sshbind=(ssh_bind)user;
(void)sshpoll;
(void)fd;
if(revents & POLLIN){
/* new incoming connection */
if(ssh_callbacks_exists(sshbind->bind_callbacks,incoming_connection)){
sshbind->bind_callbacks->incoming_connection(sshbind,
sshbind->bind_callbacks_userdata);
}
}
return 0;
}
/** @internal
* @brief returns the current poll handle, or create it
* @param sshbind the ssh_bind object
* @returns a ssh_poll handle suitable for operation
*/
ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind){
if(sshbind->poll)
return sshbind->poll;
sshbind->poll=ssh_poll_new(sshbind->bindfd,POLLIN,
ssh_bind_poll_callback,sshbind);
return sshbind->poll;
}
void ssh_bind_set_blocking(ssh_bind sshbind, int blocking) {
sshbind->blocking = blocking ? 1 : 0;
}
socket_t ssh_bind_get_fd(ssh_bind sshbind) {
return sshbind->bindfd;
}
void ssh_bind_set_fd(ssh_bind sshbind, socket_t fd) {
sshbind->bindfd = fd;
}
void ssh_bind_fd_toaccept(ssh_bind sshbind) {
sshbind->toaccept = 1;
}
void ssh_bind_free(ssh_bind sshbind){
int i;
if (sshbind == NULL) {
return;
}
if (sshbind->bindfd >= 0) {
#ifdef _WIN32
closesocket(sshbind->bindfd);
#else
close(sshbind->bindfd);
#endif
}
sshbind->bindfd = SSH_INVALID_SOCKET;
/* options */
SAFE_FREE(sshbind->banner);
SAFE_FREE(sshbind->dsakey);
SAFE_FREE(sshbind->rsakey);
SAFE_FREE(sshbind->dsa);
SAFE_FREE(sshbind->rsa);
SAFE_FREE(sshbind->bindaddr);
for (i = 0; i < 10; i++) {
if (sshbind->wanted_methods[i]) {
SAFE_FREE(sshbind->wanted_methods[i]);
}
}
SAFE_FREE(sshbind);
}
int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
int i;
if (session == NULL){
ssh_set_error(sshbind, SSH_FATAL,"session is null");
return SSH_ERROR;
}
session->server = 1;
session->version = 2;
/* copy options */
for (i = 0; i < 10; ++i) {
if (sshbind->wanted_methods[i]) {
session->opts.wanted_methods[i] = strdup(sshbind->wanted_methods[i]);
if (session->opts.wanted_methods[i] == NULL) {
return SSH_ERROR;
}
}
}
if (sshbind->bindaddr == NULL)
session->opts.bindaddr = NULL;
else {
SAFE_FREE(session->opts.bindaddr);
session->opts.bindaddr = strdup(sshbind->bindaddr);
if (session->opts.bindaddr == NULL) {
return SSH_ERROR;
}
}
session->common.log_verbosity = sshbind->common.log_verbosity;
ssh_socket_free(session->socket);
session->socket = ssh_socket_new(session);
if (session->socket == NULL) {
/* perhaps it may be better to copy the error from session to sshbind */
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
ssh_socket_set_fd(session->socket, fd);
ssh_socket_get_poll_handle_out(session->socket);
#ifdef HAVE_ECC
if (sshbind->ecdsa) {
session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa);
if (session->srv.ecdsa_key == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
}
#endif
if (sshbind->dsa) {
session->srv.dsa_key = ssh_key_dup(sshbind->dsa);
if (session->srv.dsa_key == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
}
if (sshbind->rsa) {
session->srv.rsa_key = ssh_key_dup(sshbind->rsa);
if (session->srv.rsa_key == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
}
return SSH_OK;
}
int ssh_bind_accept(ssh_bind sshbind, ssh_session session) {
socket_t fd = SSH_INVALID_SOCKET;
int rc;
if (sshbind->bindfd == SSH_INVALID_SOCKET) {
ssh_set_error(sshbind, SSH_FATAL,
"Can't accept new clients on a not bound socket.");
return SSH_ERROR;
}
if (session == NULL){
ssh_set_error(sshbind, SSH_FATAL,"session is null");
return SSH_ERROR;
}
fd = accept(sshbind->bindfd, NULL, NULL);
if (fd == SSH_INVALID_SOCKET) {
ssh_set_error(sshbind, SSH_FATAL,
"Accepting a new connection: %s",
strerror(errno));
return SSH_ERROR;
}
rc = ssh_bind_accept_fd(sshbind, session, fd);
if(rc == SSH_ERROR){
#ifdef _WIN32
closesocket(fd);
#else
close(fd);
#endif
if (session->socket)
ssh_socket_close(session->socket);
}
return rc;
}
/**
* @}
*/

626
libssh/src/buffer.c Normal file
View file

@ -0,0 +1,626 @@
/*
* buffer.c - buffer functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2009 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/buffer.h"
/**
* @defgroup libssh_buffer The SSH buffer functions.
* @ingroup libssh
*
* Functions to handle SSH buffers.
*
* @{
*/
#ifdef DEBUG_BUFFER
/**
* @internal
*
* @brief Check that preconditions and postconditions are valid.
*
* @param[in] buf The buffer to check.
*/
static void buffer_verify(ssh_buffer buf){
int doabort=0;
if(buf->data == NULL)
return;
if(buf->used > buf->allocated){
fprintf(stderr,"Buffer error : allocated %u, used %u\n",buf->allocated, buf->used);
doabort=1;
}
if(buf->pos > buf->used){
fprintf(stderr,"Buffer error : position %u, used %u\n",buf->pos, buf->used);
doabort=1;
}
if(buf->pos > buf->allocated){
fprintf(stderr,"Buffer error : position %u, allocated %u\n",buf->pos, buf->allocated);
doabort=1;
}
if(doabort)
abort();
}
#else
#define buffer_verify(x)
#endif
/**
* @brief Create a new SSH buffer.
*
* @return A newly initialized SSH buffer, NULL on error.
*/
struct ssh_buffer_struct *ssh_buffer_new(void) {
struct ssh_buffer_struct *buf = malloc(sizeof(struct ssh_buffer_struct));
if (buf == NULL) {
return NULL;
}
memset(buf, 0, sizeof(struct ssh_buffer_struct));
buffer_verify(buf);
return buf;
}
/**
* @brief Deallocate a SSH buffer.
*
* \param[in] buffer The buffer to free.
*/
void ssh_buffer_free(struct ssh_buffer_struct *buffer) {
if (buffer == NULL) {
return;
}
buffer_verify(buffer);
if (buffer->data) {
/* burn the data */
memset(buffer->data, 0, buffer->allocated);
SAFE_FREE(buffer->data);
}
memset(buffer, 'X', sizeof(*buffer));
SAFE_FREE(buffer);
}
static int realloc_buffer(struct ssh_buffer_struct *buffer, size_t needed) {
size_t smallest = 1;
char *new;
buffer_verify(buffer);
/* Find the smallest power of two which is greater or equal to needed */
while(smallest <= needed) {
if (smallest == 0) {
return -1;
}
smallest <<= 1;
}
needed = smallest;
new = realloc(buffer->data, needed);
if (new == NULL) {
return -1;
}
buffer->data = new;
buffer->allocated = needed;
buffer_verify(buffer);
return 0;
}
/** @internal
* @brief shifts a buffer to remove unused data in the beginning
* @param buffer SSH buffer
*/
static void buffer_shift(ssh_buffer buffer){
buffer_verify(buffer);
if(buffer->pos==0)
return;
memmove(buffer->data, buffer->data + buffer->pos, buffer->used - buffer->pos);
buffer->used -= buffer->pos;
buffer->pos=0;
buffer_verify(buffer);
}
/**
* @internal
*
* @brief Reinitialize a SSH buffer.
*
* @param[in] buffer The buffer to reinitialize.
*
* @return 0 on success, < 0 on error.
*/
int buffer_reinit(struct ssh_buffer_struct *buffer) {
buffer_verify(buffer);
memset(buffer->data, 0, buffer->used);
buffer->used = 0;
buffer->pos = 0;
if(buffer->allocated > 127) {
if (realloc_buffer(buffer, 127) < 0) {
return -1;
}
}
buffer_verify(buffer);
return 0;
}
/**
* @internal
*
* @brief Add data at the tail of a buffer.
*
* @param[in] buffer The buffer to add the data.
*
* @param[in] data A pointer to the data to add.
*
* @param[in] len The length of the data to add.
*
* @return 0 on success, < 0 on error.
*/
int buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint32_t len) {
buffer_verify(buffer);
if (buffer->used + len < len) {
return -1;
}
if (buffer->allocated < (buffer->used + len)) {
if(buffer->pos > 0)
buffer_shift(buffer);
if (realloc_buffer(buffer, buffer->used + len) < 0) {
return -1;
}
}
memcpy(buffer->data+buffer->used, data, len);
buffer->used+=len;
buffer_verify(buffer);
return 0;
}
/**
* @internal
*
* @brief Add a SSH string to the tail of a buffer.
*
* @param[in] buffer The buffer to add the string.
*
* @param[in] string The SSH String to add.
*
* @return 0 on success, < 0 on error.
*/
int buffer_add_ssh_string(struct ssh_buffer_struct *buffer,
struct ssh_string_struct *string) {
uint32_t len = 0;
len = ssh_string_len(string);
if (buffer_add_data(buffer, string, len + sizeof(uint32_t)) < 0) {
return -1;
}
return 0;
}
/**
* @internal
*
* @brief Add a 32 bits unsigned integer to the tail of a buffer.
*
* @param[in] buffer The buffer to add the integer.
*
* @param[in] data The 32 bits integer to add.
*
* @return 0 on success, -1 on error.
*/
int buffer_add_u32(struct ssh_buffer_struct *buffer,uint32_t data){
if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
return -1;
}
return 0;
}
/**
* @internal
*
* @brief Add a 16 bits unsigned integer to the tail of a buffer.
*
* @param[in] buffer The buffer to add the integer.
*
* @param[in] data The 16 bits integer to add.
*
* @return 0 on success, -1 on error.
*/
int buffer_add_u16(struct ssh_buffer_struct *buffer,uint16_t data){
if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
return -1;
}
return 0;
}
/**
* @internal
*
* @brief Add a 64 bits unsigned integer to the tail of a buffer.
*
* @param[in] buffer The buffer to add the integer.
*
* @param[in] data The 64 bits integer to add.
*
* @return 0 on success, -1 on error.
*/
int buffer_add_u64(struct ssh_buffer_struct *buffer, uint64_t data){
if (buffer_add_data(buffer, &data, sizeof(data)) < 0) {
return -1;
}
return 0;
}
/**
* @internal
*
* @brief Add a 8 bits unsigned integer to the tail of a buffer.
*
* @param[in] buffer The buffer to add the integer.
*
* @param[in] data The 8 bits integer to add.
*
* @return 0 on success, -1 on error.
*/
int buffer_add_u8(struct ssh_buffer_struct *buffer,uint8_t data){
if (buffer_add_data(buffer, &data, sizeof(uint8_t)) < 0) {
return -1;
}
return 0;
}
/**
* @internal
*
* @brief Add data at the head of a buffer.
*
* @param[in] buffer The buffer to add the data.
*
* @param[in] data The data to prepend.
*
* @param[in] len The length of data to prepend.
*
* @return 0 on success, -1 on error.
*/
int buffer_prepend_data(struct ssh_buffer_struct *buffer, const void *data,
uint32_t len) {
buffer_verify(buffer);
if(len <= buffer->pos){
/* It's possible to insert data between begin and pos */
memcpy(buffer->data + (buffer->pos - len), data, len);
buffer->pos -= len;
buffer_verify(buffer);
return 0;
}
/* pos isn't high enough */
if (buffer->used - buffer->pos + len < len) {
return -1;
}
if (buffer->allocated < (buffer->used - buffer->pos + len)) {
if (realloc_buffer(buffer, buffer->used - buffer->pos + len) < 0) {
return -1;
}
}
memmove(buffer->data + len, buffer->data + buffer->pos, buffer->used - buffer->pos);
memcpy(buffer->data, data, len);
buffer->used += len - buffer->pos;
buffer->pos = 0;
buffer_verify(buffer);
return 0;
}
/**
* @internal
*
* @brief Append data from a buffer to the tail of another buffer.
*
* @param[in] buffer The destination buffer.
*
* @param[in] source The source buffer to append. It doesn't take the
* position of the buffer into account.
*
* @return 0 on success, -1 on error.
*/
int buffer_add_buffer(struct ssh_buffer_struct *buffer,
struct ssh_buffer_struct *source) {
if (buffer_add_data(buffer, buffer_get_rest(source), buffer_get_rest_len(source)) < 0) {
return -1;
}
return 0;
}
/**
* @brief Get a pointer on the head of a buffer.
*
* @param[in] buffer The buffer to get the head pointer.
*
* @return A data pointer on the head. It doesn't take the position
* into account.
*
* @warning Don't expect data to be nul-terminated.
*
* @see buffer_get_rest()
* @see buffer_get_len()
*/
void *ssh_buffer_get_begin(struct ssh_buffer_struct *buffer){
return buffer->data;
}
/**
* @internal
*
* @brief Get a pointer to the head of a buffer at the current position.
*
* @param[in] buffer The buffer to get the head pointer.
*
* @return A pointer to the data from current position.
*
* @see buffer_get_rest_len()
* @see buffer_get()
*/
void *buffer_get_rest(struct ssh_buffer_struct *buffer){
return buffer->data + buffer->pos;
}
/**
* @brief Get the length of the buffer, not counting position.
*
* @param[in] buffer The buffer to get the length from.
*
* @return The length of the buffer.
*
* @see buffer_get()
*/
uint32_t ssh_buffer_get_len(struct ssh_buffer_struct *buffer){
return buffer->used;
}
/**
* @internal
*
* @brief Get the length of the buffer from the current position.
*
* @param[in] buffer The buffer to get the length from.
*
* @return The length of the buffer.
*
* @see buffer_get_rest()
*/
uint32_t buffer_get_rest_len(struct ssh_buffer_struct *buffer){
buffer_verify(buffer);
return buffer->used - buffer->pos;
}
/**
* @internal
*
* @brief Advance the position in the buffer.
*
* This has effect to "eat" bytes at head of the buffer.
*
* @param[in] buffer The buffer to advance the position.
*
* @param[in] len The number of bytes to eat.
*
* @return The new size of the buffer.
*/
uint32_t buffer_pass_bytes(struct ssh_buffer_struct *buffer, uint32_t len){
buffer_verify(buffer);
if (buffer->pos + len < len || buffer->used < buffer->pos + len) {
return 0;
}
buffer->pos+=len;
/* if the buffer is empty after having passed the whole bytes into it, we can clean it */
if(buffer->pos==buffer->used){
buffer->pos=0;
buffer->used=0;
}
buffer_verify(buffer);
return len;
}
/**
* @internal
*
* @brief Cut the end of the buffer.
*
* @param[in] buffer The buffer to cut.
*
* @param[in] len The number of bytes to remove from the tail.
*
* @return The new size of the buffer.
*/
uint32_t buffer_pass_bytes_end(struct ssh_buffer_struct *buffer, uint32_t len){
buffer_verify(buffer);
if (buffer->used < len) {
return 0;
}
buffer->used-=len;
buffer_verify(buffer);
return len;
}
/**
* @internal
*
* @brief Get the remaining data out of the buffer and adjust the read pointer.
*
* @param[in] buffer The buffer to read.
*
* @param[in] data The data buffer where to store the data.
*
* @param[in] len The length to read from the buffer.
*
* @returns 0 if there is not enough data in buffer, len otherwise.
*/
uint32_t buffer_get_data(struct ssh_buffer_struct *buffer, void *data, uint32_t len){
/*
* Check for a integer overflow first, then check if not enough data is in
* the buffer.
*/
if (buffer->pos + len < len || buffer->pos + len > buffer->used) {
return 0;
}
memcpy(data,buffer->data+buffer->pos,len);
buffer->pos+=len;
return len; /* no yet support for partial reads (is it really needed ?? ) */
}
/**
* @internal
*
* @brief Get a 8 bits unsigned int out of the buffer and adjusts the read
* pointer.
*
* @param[in] buffer The buffer to read.
*
* @param[in] data A pointer to a uint8_t where to store the data.
*
* @returns 0 if there is not enough data in buffer, 1 otherwise.
*/
int buffer_get_u8(struct ssh_buffer_struct *buffer, uint8_t *data){
return buffer_get_data(buffer,data,sizeof(uint8_t));
}
/** \internal
* \brief gets a 32 bits unsigned int out of the buffer. Adjusts the read pointer.
* \param buffer Buffer to read
* \param data pointer to a uint32_t where to store the data
* \returns 0 if there is not enough data in buffer
* \returns 4 otherwise.
*/
int buffer_get_u32(struct ssh_buffer_struct *buffer, uint32_t *data){
return buffer_get_data(buffer,data,sizeof(uint32_t));
}
/**
* @internal
*
* @brief Get a 64 bits unsigned int out of the buffer and adjusts the read
* pointer.
*
* @param[in] buffer The buffer to read.
*
* @param[in] data A pointer to a uint64_t where to store the data.
*
* @returns 0 if there is not enough data in buffer, 8 otherwise.
*/
int buffer_get_u64(struct ssh_buffer_struct *buffer, uint64_t *data){
return buffer_get_data(buffer,data,sizeof(uint64_t));
}
/**
* @internal
*
* @brief Get a SSH String out of the buffer and adjusts the read pointer.
*
* @param[in] buffer The buffer to read.
*
* @returns The SSH String, NULL on error.
*/
struct ssh_string_struct *buffer_get_ssh_string(struct ssh_buffer_struct *buffer) {
uint32_t stringlen;
uint32_t hostlen;
struct ssh_string_struct *str = NULL;
if (buffer_get_u32(buffer, &stringlen) == 0) {
return NULL;
}
hostlen = ntohl(stringlen);
/* verify if there is enough space in buffer to get it */
if (buffer->pos + hostlen < hostlen || buffer->pos + hostlen > buffer->used) {
return NULL; /* it is indeed */
}
str = ssh_string_new(hostlen);
if (str == NULL) {
return NULL;
}
if (buffer_get_data(buffer, ssh_string_data(str), hostlen) != hostlen) {
/* should never happen */
SAFE_FREE(str);
return NULL;
}
return str;
}
/**
* @internal
*
* @brief Get a mpint out of the buffer and adjusts the read pointer.
*
* @note This function is SSH-1 only.
*
* @param[in] buffer The buffer to read.
*
* @returns The SSH String containing the mpint, NULL on error.
*/
struct ssh_string_struct *buffer_get_mpint(struct ssh_buffer_struct *buffer) {
uint16_t bits;
uint32_t len;
struct ssh_string_struct *str = NULL;
if (buffer_get_data(buffer, &bits, sizeof(uint16_t)) != sizeof(uint16_t)) {
return NULL;
}
bits = ntohs(bits);
len = (bits + 7) / 8;
if (buffer->pos + len < len || buffer->pos + len > buffer->used) {
return NULL;
}
str = ssh_string_new(len);
if (str == NULL) {
return NULL;
}
if (buffer_get_data(buffer, ssh_string_data(str), len) != len) {
SAFE_FREE(str);
return NULL;
}
return str;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

61
libssh/src/callbacks.c Normal file
View file

@ -0,0 +1,61 @@
/*
* callbacks.c - callback functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/callbacks.h"
#include "libssh/session.h"
int ssh_set_callbacks(ssh_session session, ssh_callbacks cb) {
if (session == NULL || cb == NULL) {
return SSH_ERROR;
}
enter_function();
if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){
ssh_set_error(session,SSH_FATAL,
"Invalid callback passed in (badly initialized)");
leave_function();
return SSH_ERROR;
}
session->common.callbacks = cb;
leave_function();
return 0;
}
int ssh_set_channel_callbacks(ssh_channel channel, ssh_channel_callbacks cb) {
ssh_session session = NULL;
if (channel == NULL || cb == NULL) {
return SSH_ERROR;
}
session = channel->session;
enter_function();
if(cb->size <= 0 || cb->size > 1024 * sizeof(void *)){
ssh_set_error(session,SSH_FATAL,
"Invalid channel callback passed in (badly initialized)");
leave_function();
return SSH_ERROR;
}
channel->callbacks = cb;
leave_function();
return 0;
}

3461
libssh/src/channels.c Normal file

File diff suppressed because it is too large Load diff

385
libssh/src/channels1.c Normal file
View file

@ -0,0 +1,385 @@
/*
* channels1.c - Support for SSH-1 type channels
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
* Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef _WIN32
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include "libssh/priv.h"
#include "libssh/ssh1.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/channels.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#ifdef WITH_SSH1
/*
* This is a big hack. In fact, SSH1 doesn't make a clever use of channels.
* The whole packets concerning shells are sent outside of a channel.
* Thus, an inside limitation of this behavior is that you can't only
* request one shell.
* The question is still how they managed to embed two "channel" into one
* protocol.
*/
int channel_open_session1(ssh_channel chan) {
ssh_session session;
if (chan == NULL) {
return -1;
}
session = chan->session;
/*
* We guess we are requesting an *exec* channel. It can only have one exec
* channel. So we abort with an error if we need more than one.
*/
if (session->exec_channel_opened) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"SSH1 supports only one execution channel. "
"One has already been opened");
return -1;
}
session->exec_channel_opened = 1;
chan->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED;
chan->state = SSH_CHANNEL_STATE_OPEN;
chan->local_maxpacket = 32000;
chan->local_window = 64000;
ssh_log(session, SSH_LOG_PACKET, "Opened a SSH1 channel session");
return 0;
}
/* 10 SSH_CMSG_REQUEST_PTY
*
* string TERM environment variable value (e.g. vt100)
* 32-bit int terminal height, rows (e.g., 24)
* 32-bit int terminal width, columns (e.g., 80)
* 32-bit int terminal width, pixels (0 if no graphics) (e.g., 480)
* 32-bit int terminal height, pixels (0 if no graphics) (e.g., 640)
* n bytes tty modes encoded in binary
* Some day, someone should have a look at that nasty tty encoded. It's
* much simplier under ssh2. I just hope the defaults values are ok ...
*/
int channel_request_pty_size1(ssh_channel channel, const char *terminal, int col,
int row) {
ssh_session session;
ssh_string str = NULL;
if (channel == NULL) {
return SSH_ERROR;
}
session = channel->session;
if(channel->request_state != SSH_CHANNEL_REQ_STATE_NONE){
ssh_set_error(session,SSH_REQUEST_DENIED,"Wrong request state");
return SSH_ERROR;
}
str = ssh_string_from_char(terminal);
if (str == NULL) {
ssh_set_error_oom(session);
return -1;
}
if (buffer_add_u8(session->out_buffer, SSH_CMSG_REQUEST_PTY) < 0 ||
buffer_add_ssh_string(session->out_buffer, str) < 0) {
ssh_string_free(str);
return -1;
}
ssh_string_free(str);
if (buffer_add_u32(session->out_buffer, ntohl(row)) < 0 ||
buffer_add_u32(session->out_buffer, ntohl(col)) < 0 ||
buffer_add_u32(session->out_buffer, 0) < 0 || /* x */
buffer_add_u32(session->out_buffer, 0) < 0 || /* y */
buffer_add_u8(session->out_buffer, 0) < 0) { /* tty things */
return -1;
}
ssh_log(session, SSH_LOG_FUNCTIONS, "Opening a ssh1 pty");
channel->request_state = SSH_CHANNEL_REQ_STATE_PENDING;
if (packet_send(session) == SSH_ERROR) {
return -1;
}
while (channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING) {
ssh_handle_packets(session, SSH_TIMEOUT_INFINITE);
}
switch(channel->request_state){
case SSH_CHANNEL_REQ_STATE_ERROR:
case SSH_CHANNEL_REQ_STATE_PENDING:
case SSH_CHANNEL_REQ_STATE_NONE:
channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
return SSH_ERROR;
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
ssh_log(session, SSH_LOG_RARE, "PTY: Success");
return SSH_OK;
case SSH_CHANNEL_REQ_STATE_DENIED:
channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
ssh_set_error(session, SSH_REQUEST_DENIED,
"Server denied PTY allocation");
ssh_log(session, SSH_LOG_RARE, "PTY: denied\n");
return SSH_ERROR;
}
// Not reached
return SSH_ERROR;
}
int channel_change_pty_size1(ssh_channel channel, int cols, int rows) {
ssh_session session;
if (channel == NULL) {
return SSH_ERROR;
}
session = channel->session;
if(channel->request_state != SSH_CHANNEL_REQ_STATE_NONE){
ssh_set_error(session,SSH_REQUEST_DENIED,"Wrong request state");
return SSH_ERROR;
}
if (buffer_add_u8(session->out_buffer, SSH_CMSG_WINDOW_SIZE) < 0 ||
buffer_add_u32(session->out_buffer, ntohl(rows)) < 0 ||
buffer_add_u32(session->out_buffer, ntohl(cols)) < 0 ||
buffer_add_u32(session->out_buffer, 0) < 0 ||
buffer_add_u32(session->out_buffer, 0) < 0) {
return SSH_ERROR;
}
channel->request_state=SSH_CHANNEL_REQ_STATE_PENDING;
if (packet_send(session) == SSH_ERROR) {
return SSH_ERROR;
}
ssh_log(session, SSH_LOG_PROTOCOL, "Change pty size send");
while(channel->request_state==SSH_CHANNEL_REQ_STATE_PENDING){
ssh_handle_packets(session, SSH_TIMEOUT_INFINITE);
}
switch(channel->request_state){
case SSH_CHANNEL_REQ_STATE_ERROR:
case SSH_CHANNEL_REQ_STATE_PENDING:
case SSH_CHANNEL_REQ_STATE_NONE:
channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
return SSH_ERROR;
case SSH_CHANNEL_REQ_STATE_ACCEPTED:
channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
ssh_log(session, SSH_LOG_PROTOCOL, "pty size changed");
return SSH_OK;
case SSH_CHANNEL_REQ_STATE_DENIED:
channel->request_state=SSH_CHANNEL_REQ_STATE_NONE;
ssh_log(session, SSH_LOG_RARE, "pty size change denied");
ssh_set_error(session, SSH_REQUEST_DENIED, "pty size change denied");
return SSH_ERROR;
}
// Not reached
return SSH_ERROR;
}
int channel_request_shell1(ssh_channel channel) {
ssh_session session;
if (channel == NULL) {
return -1;
}
session = channel->session;
if (buffer_add_u8(session->out_buffer,SSH_CMSG_EXEC_SHELL) < 0) {
return -1;
}
if (packet_send(session) == SSH_ERROR) {
return -1;
}
ssh_log(session, SSH_LOG_RARE, "Launched a shell");
return 0;
}
int channel_request_exec1(ssh_channel channel, const char *cmd) {
ssh_session session;
ssh_string command = NULL;
if (channel == NULL) {
return -1;
}
session = channel->session;
command = ssh_string_from_char(cmd);
if (command == NULL) {
return -1;
}
if (buffer_add_u8(session->out_buffer, SSH_CMSG_EXEC_CMD) < 0 ||
buffer_add_ssh_string(session->out_buffer, command) < 0) {
ssh_string_free(command);
return -1;
}
ssh_string_free(command);
if(packet_send(session) == SSH_ERROR) {
return -1;
}
ssh_log(session, SSH_LOG_RARE, "Executing %s ...", cmd);
return 0;
}
SSH_PACKET_CALLBACK(ssh_packet_data1){
ssh_channel channel = ssh_get_channel1(session);
ssh_string str = NULL;
int is_stderr=(type==SSH_SMSG_STDOUT_DATA ? 0 : 1);
(void)user;
if (channel == NULL) {
return SSH_PACKET_NOT_USED;
}
str = buffer_get_ssh_string(packet);
if (str == NULL) {
ssh_log(session, SSH_LOG_FUNCTIONS, "Invalid data packet !\n");
return SSH_PACKET_USED;
}
ssh_log(session, SSH_LOG_PROTOCOL,
"Adding %" PRIdS " bytes data in %d",
ssh_string_len(str), is_stderr);
if (channel_default_bufferize(channel, ssh_string_data(str), ssh_string_len(str),
is_stderr) < 0) {
ssh_string_free(str);
return SSH_PACKET_USED;
}
ssh_string_free(str);
return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(ssh_packet_close1){
ssh_channel channel = ssh_get_channel1(session);
uint32_t status;
(void)type;
(void)user;
if (channel == NULL) {
return SSH_PACKET_NOT_USED;
}
buffer_get_u32(packet, &status);
/*
* It's much more than a channel closing. spec says it's the last
* message sent by server (strange)
*/
/* actually status is lost somewhere */
channel->state = SSH_CHANNEL_STATE_CLOSED;
channel->remote_eof = 1;
buffer_add_u8(session->out_buffer, SSH_CMSG_EXIT_CONFIRMATION);
packet_send(session);
return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(ssh_packet_exist_status1){
ssh_channel channel = ssh_get_channel1(session);
uint32_t status;
(void)type;
(void)user;
if (channel == NULL) {
return SSH_PACKET_NOT_USED;
}
buffer_get_u32(packet, &status);
channel->state = SSH_CHANNEL_STATE_CLOSED;
channel->remote_eof = 1;
channel->exit_status = ntohl(status);
return SSH_PACKET_USED;
}
int channel_write1(ssh_channel channel, const void *data, int len) {
ssh_session session;
int origlen = len;
int effectivelen;
const unsigned char *ptr=data;
if (channel == NULL) {
return -1;
}
session = channel->session;
while (len > 0) {
if (buffer_add_u8(session->out_buffer, SSH_CMSG_STDIN_DATA) < 0) {
return -1;
}
effectivelen = len > 32000 ? 32000 : len;
if (buffer_add_u32(session->out_buffer, htonl(effectivelen)) < 0 ||
buffer_add_data(session->out_buffer, ptr, effectivelen) < 0) {
return -1;
}
ptr += effectivelen;
len -= effectivelen;
if (packet_send(session) == SSH_ERROR) {
return -1;
}
ssh_handle_packets(session, SSH_TIMEOUT_NONBLOCKING);
}
if (ssh_blocking_flush(session,SSH_TIMEOUT_USER) == SSH_ERROR)
return -1;
return origlen;
}
ssh_channel ssh_get_channel1(ssh_session session){
struct ssh_iterator *it;
if (session == NULL) {
return NULL;
}
/* With SSH1, the channel is always the first one */
if(session->channels != NULL){
it = ssh_list_get_iterator(session->channels);
if(it)
return ssh_iterator_value(ssh_channel, it);
}
return NULL;
}
#endif /* WITH_SSH1 */
/* vim: set ts=2 sw=2 et cindent: */

696
libssh/src/client.c Normal file
View file

@ -0,0 +1,696 @@
/*
* client.c - SSH client functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/options.h"
#include "libssh/socket.h"
#include "libssh/session.h"
#include "libssh/dh.h"
#include "libssh/ecdh.h"
#include "libssh/threads.h"
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/kex.h"
#define set_status(session, status) do {\
if (session->common.callbacks && session->common.callbacks->connect_status_function) \
session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \
} while (0)
/**
* @internal
* @brief Callback to be called when the socket is connected or had a
* connection error. Changes the state of the session and updates the error
* message.
* @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR
* @param user is a pointer to session
*/
static void socket_callback_connected(int code, int errno_code, void *user){
ssh_session session=(ssh_session)user;
enter_function();
if(session->session_state != SSH_SESSION_STATE_CONNECTING){
ssh_set_error(session,SSH_FATAL, "Wrong state in socket_callback_connected : %d",
session->session_state);
leave_function();
return;
}
ssh_log(session,SSH_LOG_RARE,"Socket connection callback: %d (%d)",code, errno_code);
if(code == SSH_SOCKET_CONNECTED_OK)
session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED;
else {
session->session_state=SSH_SESSION_STATE_ERROR;
ssh_set_error(session,SSH_FATAL,"%s",strerror(errno_code));
}
session->ssh_connection_callback(session);
leave_function();
}
/**
* @internal
*
* @brief Gets the banner from socket and saves it in session.
* Updates the session state
*
* @param data pointer to the beginning of header
* @param len size of the banner
* @param user is a pointer to session
* @returns Number of bytes processed, or zero if the banner is not complete.
*/
static int callback_receive_banner(const void *data, size_t len, void *user) {
char *buffer = (char *)data;
ssh_session session=(ssh_session) user;
char *str = NULL;
size_t i;
int ret=0;
enter_function();
if(session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED){
ssh_set_error(session,SSH_FATAL,"Wrong state in callback_receive_banner : %d",session->session_state);
leave_function();
return SSH_ERROR;
}
for(i=0;i<len;++i){
#ifdef WITH_PCAP
if(session->pcap_ctx && buffer[i] == '\n'){
ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_IN,buffer,i+1,i+1);
}
#endif
if(buffer[i]=='\r')
buffer[i]='\0';
if(buffer[i]=='\n'){
buffer[i]='\0';
str=strdup(buffer);
/* number of bytes read */
ret=i+1;
session->serverbanner=str;
session->session_state=SSH_SESSION_STATE_BANNER_RECEIVED;
ssh_log(session,SSH_LOG_PACKET,"Received banner: %s",str);
session->ssh_connection_callback(session);
leave_function();
return ret;
}
if(i>127){
/* Too big banner */
session->session_state=SSH_SESSION_STATE_ERROR;
ssh_set_error(session,SSH_FATAL,"Receiving banner: too large banner");
leave_function();
return 0;
}
}
leave_function();
return ret;
}
/** @internal
* @brief Sends a SSH banner to the server.
*
* @param session The SSH session to use.
*
* @param server Send client or server banner.
*
* @return 0 on success, < 0 on error.
*/
int ssh_send_banner(ssh_session session, int server) {
const char *banner = NULL;
char buffer[128] = {0};
int err=SSH_ERROR;
enter_function();
banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2;
if (server) {
session->serverbanner = strdup(banner);
if (session->serverbanner == NULL) {
goto end;
}
} else {
session->clientbanner = strdup(banner);
if (session->clientbanner == NULL) {
goto end;
}
}
snprintf(buffer, 128, "%s\n", banner);
if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) {
goto end;
}
#ifdef WITH_PCAP
if(session->pcap_ctx)
ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,buffer,strlen(buffer),strlen(buffer));
#endif
err=SSH_OK;
end:
leave_function();
return err;
}
/** @internal
* @brief launches the DH handshake state machine
* @param session session handle
* @returns SSH_OK or SSH_ERROR
* @warning this function returning is no proof that DH handshake is
* completed
*/
static int dh_handshake(ssh_session session) {
int rc = SSH_AGAIN;
enter_function();
switch (session->dh_handshake_state) {
case DH_STATE_INIT:
switch(session->next_crypto->kex_type){
case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1:
rc = ssh_client_dh_init(session);
break;
#ifdef HAVE_ECDH
case SSH_KEX_ECDH_SHA2_NISTP256:
rc = ssh_client_ecdh_init(session);
break;
#endif
default:
rc=SSH_ERROR;
goto error;
}
if (rc == SSH_ERROR) {
goto error;
}
session->dh_handshake_state = DH_STATE_INIT_SENT;
case DH_STATE_INIT_SENT:
/* wait until ssh_packet_dh_reply is called */
break;
case DH_STATE_NEWKEYS_SENT:
/* wait until ssh_packet_newkeys is called */
break;
case DH_STATE_FINISHED:
leave_function();
return SSH_OK;
default:
ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d",
session->dh_handshake_state);
leave_function();
return SSH_ERROR;
}
error:
leave_function();
return rc;
}
static int ssh_service_request_termination(void *s){
ssh_session session = (ssh_session)s;
if(session->session_state == SSH_SESSION_STATE_ERROR ||
session->auth_service_state != SSH_AUTH_SERVICE_SENT)
return 1;
else
return 0;
}
/**
* @internal
*
* @brief Request a service from the SSH server.
*
* Service requests are for example: ssh-userauth, ssh-connection, etc.
*
* @param session The session to use to ask for a service request.
* @param service The service request.
*
* @return SSH_OK on success
* @return SSH_ERROR on error
* @return SSH_AGAIN No response received yet
* @bug actually only works with ssh-userauth
*/
int ssh_service_request(ssh_session session, const char *service) {
ssh_string service_s = NULL;
int rc=SSH_ERROR;
enter_function();
if(session->auth_service_state != SSH_AUTH_SERVICE_NONE)
goto pending;
if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) {
goto error;
}
service_s = ssh_string_from_char(service);
if (service_s == NULL) {
goto error;
}
if (buffer_add_ssh_string(session->out_buffer,service_s) < 0) {
ssh_string_free(service_s);
goto error;
}
ssh_string_free(service_s);
session->auth_service_state=SSH_AUTH_SERVICE_SENT;
if (packet_send(session) == SSH_ERROR) {
ssh_set_error(session, SSH_FATAL,
"Sending SSH2_MSG_SERVICE_REQUEST failed.");
goto error;
}
ssh_log(session, SSH_LOG_PACKET,
"Sent SSH_MSG_SERVICE_REQUEST (service %s)", service);
pending:
rc=ssh_handle_packets_termination(session,SSH_TIMEOUT_USER,
ssh_service_request_termination, session);
if(rc == SSH_ERROR)
goto error;
switch(session->auth_service_state){
case SSH_AUTH_SERVICE_DENIED:
ssh_set_error(session,SSH_FATAL,"ssh_auth_service request denied");
break;
case SSH_AUTH_SERVICE_ACCEPTED:
rc=SSH_OK;
break;
case SSH_AUTH_SERVICE_SENT:
rc=SSH_AGAIN;
break;
case SSH_AUTH_SERVICE_NONE:
case SSH_AUTH_SERVICE_USER_SENT:
/* Invalid state, SSH1 specific */
rc=SSH_ERROR;
break;
}
error:
leave_function();
return rc;
}
/**
* @addtogroup libssh_session
*
* @{
*/
/**
* @internal
*
* @brief A function to be called each time a step has been done in the
* connection.
*/
static void ssh_client_connection_callback(ssh_session session){
int ssh1,ssh2;
enter_function();
switch(session->session_state){
case SSH_SESSION_STATE_NONE:
case SSH_SESSION_STATE_CONNECTING:
case SSH_SESSION_STATE_SOCKET_CONNECTED:
break;
case SSH_SESSION_STATE_BANNER_RECEIVED:
if (session->serverbanner == NULL) {
goto error;
}
set_status(session, 0.4f);
ssh_log(session, SSH_LOG_RARE,
"SSH server banner: %s", session->serverbanner);
/* Here we analyze the different protocols the server allows. */
if (ssh_analyze_banner(session, 0, &ssh1, &ssh2) < 0) {
goto error;
}
/* Here we decide which version of the protocol to use. */
if (ssh2 && session->opts.ssh2) {
session->version = 2;
#ifdef WITH_SSH1
} else if(ssh1 && session->opts.ssh1) {
session->version = 1;
#endif
} else if(ssh1 && !session->opts.ssh1){
#ifdef WITH_SSH1
ssh_set_error(session, SSH_FATAL,
"SSH-1 protocol not available (configure session to allow SSH-1)");
goto error;
#else
ssh_set_error(session, SSH_FATAL,
"SSH-1 protocol not available (libssh compiled without SSH-1 support)");
goto error;
#endif
} else {
ssh_set_error(session, SSH_FATAL,
"No version of SSH protocol usable (banner: %s)",
session->serverbanner);
goto error;
}
/* from now, the packet layer is handling incoming packets */
if(session->version==2)
session->socket_callbacks.data=ssh_packet_socket_callback;
#ifdef WITH_SSH1
else
session->socket_callbacks.data=ssh_packet_socket_callback1;
#endif
ssh_packet_set_default_callbacks(session);
session->session_state=SSH_SESSION_STATE_INITIAL_KEX;
ssh_send_banner(session, 0);
set_status(session, 0.5f);
break;
case SSH_SESSION_STATE_INITIAL_KEX:
/* TODO: This state should disappear in favor of get_key handle */
#ifdef WITH_SSH1
if(session->version==1){
if (ssh_get_kex1(session) < 0)
goto error;
set_status(session,0.6f);
session->connected = 1;
break;
}
#endif
break;
case SSH_SESSION_STATE_KEXINIT_RECEIVED:
set_status(session,0.6f);
ssh_list_kex(session, &session->next_crypto->server_kex);
if (set_client_kex(session) < 0) {
goto error;
}
if (ssh_kex_select_methods(session) == SSH_ERROR)
goto error;
if (ssh_send_kex(session, 0) < 0) {
goto error;
}
set_status(session,0.8f);
session->session_state=SSH_SESSION_STATE_DH;
if (dh_handshake(session) == SSH_ERROR) {
goto error;
}
case SSH_SESSION_STATE_DH:
if(session->dh_handshake_state==DH_STATE_FINISHED){
set_status(session,1.0f);
session->connected = 1;
if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
else
session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
}
break;
case SSH_SESSION_STATE_AUTHENTICATING:
break;
case SSH_SESSION_STATE_ERROR:
goto error;
default:
ssh_set_error(session,SSH_FATAL,"Invalid state %d",session->session_state);
}
leave_function();
return;
error:
ssh_socket_close(session->socket);
session->alive = 0;
session->session_state=SSH_SESSION_STATE_ERROR;
leave_function();
}
/** @internal
* @brief describe under which conditions the ssh_connect function may stop
*/
static int ssh_connect_termination(void *user){
ssh_session session = (ssh_session)user;
switch(session->session_state){
case SSH_SESSION_STATE_ERROR:
case SSH_SESSION_STATE_AUTHENTICATING:
case SSH_SESSION_STATE_DISCONNECTED:
return 1;
default:
return 0;
}
}
/**
* @brief Connect to the ssh server.
*
* @param[in] session The ssh session to connect.
*
* @returns SSH_OK on success, SSH_ERROR on error.
* @returns SSH_AGAIN, if the session is in nonblocking mode,
* and call must be done again.
*
* @see ssh_new()
* @see ssh_disconnect()
*/
int ssh_connect(ssh_session session) {
int ret;
if (session == NULL) {
return SSH_ERROR;
}
enter_function();
switch(session->pending_call_state){
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_CONNECT:
goto pending;
default:
ssh_set_error(session,SSH_FATAL,"Bad call during pending SSH call in ssh_connect");
leave_function();
return SSH_ERROR;
}
session->alive = 0;
session->client = 1;
if (ssh_init() < 0) {
leave_function();
return SSH_ERROR;
}
if (session->opts.fd == SSH_INVALID_SOCKET &&
session->opts.host == NULL &&
session->opts.ProxyCommand == NULL) {
ssh_set_error(session, SSH_FATAL, "Hostname required");
leave_function();
return SSH_ERROR;
}
ret = ssh_options_apply(session);
if (ret < 0) {
ssh_set_error(session, SSH_FATAL, "Couldn't apply options");
leave_function();
return SSH_ERROR;
}
ssh_log(session,SSH_LOG_RARE,"libssh %s, using threading %s", ssh_copyright(), ssh_threads_get_type());
session->ssh_connection_callback = ssh_client_connection_callback;
session->session_state=SSH_SESSION_STATE_CONNECTING;
ssh_socket_set_callbacks(session->socket,&session->socket_callbacks);
session->socket_callbacks.connected=socket_callback_connected;
session->socket_callbacks.data=callback_receive_banner;
session->socket_callbacks.exception=ssh_socket_exception_callback;
session->socket_callbacks.userdata=session;
if (session->opts.fd != SSH_INVALID_SOCKET) {
ssh_socket_set_fd(session->socket, session->opts.fd);
ret=SSH_OK;
#ifndef _WIN32
} else if (session->opts.ProxyCommand != NULL){
ret = ssh_socket_connect_proxycommand(session->socket,
session->opts.ProxyCommand);
#endif
} else {
ret=ssh_socket_connect(session->socket,
session->opts.host,
session->opts.port,
session->opts.bindaddr);
}
if (ret == SSH_ERROR) {
leave_function();
return SSH_ERROR;
}
set_status(session, 0.2f);
session->alive = 1;
ssh_log(session,SSH_LOG_PROTOCOL,"Socket connecting, now waiting for the callbacks to work");
pending:
session->pending_call_state=SSH_PENDING_CALL_CONNECT;
if(ssh_is_blocking(session)) {
int timeout = (session->opts.timeout * 1000) +
(session->opts.timeout_usec / 1000);
if (timeout == 0) {
timeout = 10 * 1000;
}
ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout);
ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session);
if (ret == SSH_ERROR || !ssh_connect_termination(session)) {
ssh_set_error(session, SSH_FATAL,
"Timeout connecting to %s", session->opts.host);
session->session_state = SSH_SESSION_STATE_ERROR;
}
}
else {
ret = ssh_handle_packets_termination(session,
SSH_TIMEOUT_NONBLOCKING,
ssh_connect_termination,
session);
if (ret == SSH_ERROR) {
session->session_state = SSH_SESSION_STATE_ERROR;
}
}
ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual state : %d",session->session_state);
if(!ssh_is_blocking(session) && !ssh_connect_termination(session)){
leave_function();
return SSH_AGAIN;
}
leave_function();
session->pending_call_state=SSH_PENDING_CALL_NONE;
if(session->session_state == SSH_SESSION_STATE_ERROR || session->session_state == SSH_SESSION_STATE_DISCONNECTED)
return SSH_ERROR;
return SSH_OK;
}
/**
* @brief Get the issue banner from the server.
*
* This is the banner showing a disclaimer to users who log in,
* typically their right or the fact that they will be monitored.
*
* @param[in] session The SSH session to use.
*
* @return A newly allocated string with the banner, NULL on error.
*/
char *ssh_get_issue_banner(ssh_session session) {
if (session == NULL || session->banner == NULL) {
return NULL;
}
return ssh_string_to_char(session->banner);
}
/**
* @brief Get the version of the OpenSSH server, if it is not an OpenSSH server
* then 0 will be returned.
*
* You can use the SSH_VERSION_INT macro to compare version numbers.
*
* @param[in] session The SSH session to use.
*
* @return The version number if available, 0 otherwise.
*/
int ssh_get_openssh_version(ssh_session session) {
if (session == NULL) {
return 0;
}
return session->openssh;
}
/**
* @brief Disconnect from a session (client or server).
* The session can then be reused to open a new session.
*
* @param[in] session The SSH session to use.
*/
void ssh_disconnect(ssh_session session) {
ssh_string str = NULL;
struct ssh_iterator *it;
if (session == NULL) {
return;
}
enter_function();
if (ssh_socket_is_open(session->socket)) {
if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) {
goto error;
}
if (buffer_add_u32(session->out_buffer,
htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) {
goto error;
}
str = ssh_string_from_char("Bye Bye");
if (str == NULL) {
goto error;
}
if (buffer_add_ssh_string(session->out_buffer,str) < 0) {
ssh_string_free(str);
goto error;
}
ssh_string_free(str);
packet_send(session);
ssh_socket_close(session->socket);
}
error:
session->alive = 0;
if(session->socket){
ssh_socket_reset(session->socket);
}
session->opts.fd = SSH_INVALID_SOCKET;
session->session_state=SSH_SESSION_STATE_DISCONNECTED;
while ((it=ssh_list_get_iterator(session->channels)) != NULL) {
ssh_channel_free(ssh_iterator_value(ssh_channel,it));
ssh_list_remove(session->channels, it);
}
if(session->current_crypto){
crypto_free(session->current_crypto);
session->current_crypto=NULL;
}
if(session->in_buffer)
buffer_reinit(session->in_buffer);
if(session->out_buffer)
buffer_reinit(session->out_buffer);
if(session->in_hashbuf)
buffer_reinit(session->in_hashbuf);
if(session->out_hashbuf)
buffer_reinit(session->out_hashbuf);
session->auth_methods = 0;
SAFE_FREE(session->serverbanner);
SAFE_FREE(session->clientbanner);
if(session->ssh_message_list){
ssh_message msg;
while((msg=ssh_list_pop_head(ssh_message ,session->ssh_message_list))
!= NULL){
ssh_message_free(msg);
}
ssh_list_free(session->ssh_message_list);
session->ssh_message_list=NULL;
}
if (session->packet_callbacks){
ssh_list_free(session->packet_callbacks);
session->packet_callbacks=NULL;
}
leave_function();
}
const char *ssh_copyright(void) {
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2010 Aris Adamantiadis "
"(aris@0xbadc0de.be) Distributed under the LGPL, please refer to COPYING "
"file for information about your rights";
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

366
libssh/src/config.c Normal file
View file

@ -0,0 +1,366 @@
/*
* config.c - parse the ssh config file
*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/options.h"
enum ssh_config_opcode_e {
SOC_UNSUPPORTED = -1,
SOC_HOST,
SOC_HOSTNAME,
SOC_PORT,
SOC_USERNAME,
SOC_IDENTITY,
SOC_CIPHERS,
SOC_COMPRESSION,
SOC_TIMEOUT,
SOC_PROTOCOL,
SOC_STRICTHOSTKEYCHECK,
SOC_KNOWNHOSTS,
SOC_PROXYCOMMAND
};
struct ssh_config_keyword_table_s {
const char *name;
enum ssh_config_opcode_e opcode;
};
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "host", SOC_HOST },
{ "hostname", SOC_HOSTNAME },
{ "port", SOC_PORT },
{ "user", SOC_USERNAME },
{ "identityfile", SOC_IDENTITY },
{ "ciphers", SOC_CIPHERS },
{ "compression", SOC_COMPRESSION },
{ "connecttimeout", SOC_TIMEOUT },
{ "protocol", SOC_PROTOCOL },
{ "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK },
{ "userknownhostsfile", SOC_KNOWNHOSTS },
{ "proxycommand", SOC_PROXYCOMMAND },
{ NULL, SOC_UNSUPPORTED }
};
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) {
int i;
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
return ssh_config_keyword_table[i].opcode;
}
}
return SOC_UNSUPPORTED;
}
static char *ssh_config_get_cmd(char **str) {
register char *c;
char *r;
/* Ignore leading spaces */
for (c = *str; *c; c++) {
if (! isblank(*c)) {
break;
}
}
if (*c == '\"') {
for (r = ++c; *c; c++) {
if (*c == '\"') {
*c = '\0';
goto out;
}
}
}
for (r = c; *c; c++) {
if (*c == '\n') {
*c = '\0';
goto out;
}
}
out:
*str = c + 1;
return r;
}
static char *ssh_config_get_token(char **str) {
register char *c;
char *r;
c = ssh_config_get_cmd(str);
for (r = c; *c; c++) {
if (isblank(*c)) {
*c = '\0';
goto out;
}
}
out:
*str = c + 1;
return r;
}
static int ssh_config_get_int(char **str, int notfound) {
char *p, *endp;
int i;
p = ssh_config_get_token(str);
if (p && *p) {
i = strtol(p, &endp, 10);
if (p == endp) {
return notfound;
}
return i;
}
return notfound;
}
static const char *ssh_config_get_str_tok(char **str, const char *def) {
char *p;
p = ssh_config_get_token(str);
if (p && *p) {
return p;
}
return def;
}
static int ssh_config_get_yesno(char **str, int notfound) {
const char *p;
p = ssh_config_get_str_tok(str, NULL);
if (p == NULL) {
return notfound;
}
if (strncasecmp(p, "yes", 3) == 0) {
return 1;
} else if (strncasecmp(p, "no", 2) == 0) {
return 0;
}
return notfound;
}
static int ssh_config_parse_line(ssh_session session, const char *line,
unsigned int count, int *parsing) {
enum ssh_config_opcode_e opcode;
const char *p;
char *s, *x;
char *keyword;
char *lowerhost;
size_t len;
int i;
x = s = strdup(line);
if (s == NULL) {
ssh_set_error_oom(session);
return -1;
}
/* Remove trailing spaces */
for (len = strlen(s) - 1; len > 0; len--) {
if (! isspace(s[len])) {
break;
}
s[len] = '\0';
}
keyword = ssh_config_get_token(&s);
if (keyword == NULL || *keyword == '#' ||
*keyword == '\0' || *keyword == '\n') {
SAFE_FREE(x);
return 0;
}
opcode = ssh_config_get_opcode(keyword);
switch (opcode) {
case SOC_HOST:
*parsing = 0;
lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL;
for (p = ssh_config_get_str_tok(&s, NULL); p && *p;
p = ssh_config_get_str_tok(&s, NULL)) {
if (match_hostname(lowerhost, p, strlen(p))) {
*parsing = 1;
}
}
SAFE_FREE(lowerhost);
break;
case SOC_HOSTNAME:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_HOST, p);
}
break;
case SOC_PORT:
if (session->opts.port == 22) {
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
}
}
break;
case SOC_USERNAME:
if (session->opts.username == NULL) {
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_USER, p);
}
}
break;
case SOC_IDENTITY:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
}
break;
case SOC_CIPHERS:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
}
break;
case SOC_COMPRESSION:
i = ssh_config_get_yesno(&s, -1);
if (i >= 0 && *parsing) {
if (i) {
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
} else {
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
}
}
break;
case SOC_PROTOCOL:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
char *a, *b;
b = strdup(p);
if (b == NULL) {
SAFE_FREE(x);
ssh_set_error_oom(session);
return -1;
}
i = 0;
ssh_options_set(session, SSH_OPTIONS_SSH1, &i);
ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
for (a = strtok(b, ","); a; a = strtok(NULL, ",")) {
switch (atoi(a)) {
case 1:
i = 1;
ssh_options_set(session, SSH_OPTIONS_SSH1, &i);
break;
case 2:
i = 1;
ssh_options_set(session, SSH_OPTIONS_SSH2, &i);
break;
default:
break;
}
}
SAFE_FREE(b);
}
break;
case SOC_TIMEOUT:
i = ssh_config_get_int(&s, -1);
if (i >= 0 && *parsing) {
ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &i);
}
break;
case SOC_STRICTHOSTKEYCHECK:
i = ssh_config_get_yesno(&s, -1);
if (i >= 0 && *parsing) {
ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
}
break;
case SOC_KNOWNHOSTS:
p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
}
break;
case SOC_PROXYCOMMAND:
p = ssh_config_get_cmd(&s);
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
}
break;
case SOC_UNSUPPORTED:
ssh_log(session, SSH_LOG_RARE, "Unsupported option: %s, line: %d\n",
keyword, count);
break;
default:
ssh_set_error(session, SSH_FATAL, "ERROR - unimplemented opcode: %d\n",
opcode);
SAFE_FREE(x);
return -1;
break;
}
SAFE_FREE(x);
return 0;
}
/* ssh_config_parse_file */
int ssh_config_parse_file(ssh_session session, const char *filename) {
char line[1024] = {0};
unsigned int count = 0;
FILE *f;
int parsing;
if ((f = fopen(filename, "r")) == NULL) {
return 0;
}
ssh_log(session, SSH_LOG_RARE, "Reading configuration data from %s", filename);
parsing = 1;
while (fgets(line, sizeof(line), f)) {
count++;
if (ssh_config_parse_line(session, line, count, &parsing) < 0) {
fclose(f);
return -1;
}
}
fclose(f);
return 0;
}

484
libssh/src/connect.c Normal file
View file

@ -0,0 +1,484 @@
/*
* connect.c - handles connections to ssh servers
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2009 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libssh/libssh.h"
#include "libssh/misc.h"
#ifdef _WIN32
/*
* Only use Windows API functions available on Windows 2000 SP4 or later.
* The available constants are in <sdkddkver.h>.
* http://msdn.microsoft.com/en-us/library/aa383745.aspx
* http://blogs.msdn.com/oldnewthing/archive/2007/04/11/2079137.aspx
*/
#undef _WIN32_WINNT
#ifdef HAVE_WSPIAPI_H
#define _WIN32_WINNT 0x0500 /* _WIN32_WINNT_WIN2K */
#undef NTDDI_VERSION
#define NTDDI_VERSION 0x05000400 /* NTDDI_WIN2KSP4 */
#else
#define _WIN32_WINNT 0x0501 /* _WIN32_WINNT_WINXP */
#undef NTDDI_VERSION
#define NTDDI_VERSION 0x05010000 /* NTDDI_WINXP */
#endif
#if _MSC_VER >= 1400
#include <io.h>
#undef close
#define close _close
#endif /* _MSC_VER */
#include <winsock2.h>
#include <ws2tcpip.h>
/* <wspiapi.h> is necessary for getaddrinfo before Windows XP, but it isn't
* available on some platforms like MinGW. */
#ifdef HAVE_WSPIAPI_H
#include <wspiapi.h>
#endif
#else /* _WIN32 */
#include <netdb.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#endif /* _WIN32 */
#include "libssh/priv.h"
#include "libssh/socket.h"
#include "libssh/channels.h"
#include "libssh/session.h"
#include "libssh/poll.h"
#ifndef HAVE_GETADDRINFO
#error "Your system must have getaddrinfo()"
#endif
#ifdef _WIN32
#ifndef gai_strerror
char WSAAPI *gai_strerrorA(int code) {
static char buf[256];
snprintf(buf, sizeof(buf), "Undetermined error code (%d)", code);
return buf;
}
#endif /* gai_strerror */
#endif /* _WIN32 */
static int ssh_connect_socket_close(socket_t s){
#ifdef _WIN32
return closesocket(s);
#else
return close(s);
#endif
}
static int getai(ssh_session session, const char *host, int port, struct addrinfo **ai) {
const char *service = NULL;
struct addrinfo hints;
char s_port[10];
ZERO_STRUCT(hints);
hints.ai_protocol = IPPROTO_TCP;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (port == 0) {
hints.ai_flags = AI_PASSIVE;
} else {
snprintf(s_port, sizeof(s_port), "%hu", (unsigned short)port);
service = s_port;
#ifdef AI_NUMERICSERV
hints.ai_flags=AI_NUMERICSERV;
#endif
}
if (ssh_is_ipaddr(host)) {
/* this is an IP address */
ssh_log(session,SSH_LOG_PACKET,"host %s matches an IP address",host);
hints.ai_flags |= AI_NUMERICHOST;
}
return getaddrinfo(host, service, &hints, ai);
}
static int ssh_connect_ai_timeout(ssh_session session, const char *host,
int port, struct addrinfo *ai, long timeout, long usec, socket_t s) {
int timeout_ms;
ssh_pollfd_t fds;
int rc = 0;
socklen_t len = sizeof(rc);
enter_function();
/* I know we're losing some precision. But it's not like poll-like family
* type of mechanisms are precise up to the microsecond.
*/
timeout_ms=timeout * 1000 + usec / 1000;
ssh_socket_set_nonblocking(s);
ssh_log(session, SSH_LOG_RARE, "Trying to connect to host: %s:%d with "
"timeout %d ms", host, port, timeout_ms);
/* The return value is checked later */
connect(s, ai->ai_addr, ai->ai_addrlen);
freeaddrinfo(ai);
fds.fd=s;
fds.revents=0;
fds.events=POLLOUT;
#ifdef _WIN32
fds.events |= POLLWRNORM;
#endif
rc = ssh_poll(&fds,1,timeout_ms);
if (rc == 0) {
/* timeout */
ssh_set_error(session, SSH_FATAL,
"Timeout while connecting to %s:%d", host, port);
ssh_connect_socket_close(s);
leave_function();
return -1;
}
if (rc < 0) {
ssh_set_error(session, SSH_FATAL,
"poll error: %s", strerror(errno));
ssh_connect_socket_close(s);
leave_function();
return -1;
}
rc = 0;
/* Get connect(2) return code. Zero means no error */
getsockopt(s, SOL_SOCKET, SO_ERROR,(char *) &rc, &len);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Connect to %s:%d failed: %s", host, port, strerror(rc));
ssh_connect_socket_close(s);
leave_function();
return -1;
}
/* s is connected ? */
ssh_log(session, SSH_LOG_PACKET, "Socket connected with timeout\n");
ssh_socket_set_blocking(s);
leave_function();
return s;
}
/**
* @internal
*
* @brief Connect to an IPv4 or IPv6 host specified by its IP address or
* hostname.
*
* @returns A file descriptor, < 0 on error.
*/
socket_t ssh_connect_host(ssh_session session, const char *host,
const char *bind_addr, int port, long timeout, long usec) {
socket_t s = -1;
int rc;
struct addrinfo *ai;
struct addrinfo *itr;
enter_function();
rc = getai(session,host, port, &ai);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to resolve hostname %s (%s)", host, gai_strerror(rc));
leave_function();
return -1;
}
for (itr = ai; itr != NULL; itr = itr->ai_next){
/* create socket */
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
if (s < 0) {
ssh_set_error(session, SSH_FATAL,
"Socket create failed: %s", strerror(errno));
continue;
}
if (bind_addr) {
struct addrinfo *bind_ai;
struct addrinfo *bind_itr;
ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr);
rc = getai(session,bind_addr, 0, &bind_ai);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to resolve bind address %s (%s)",
bind_addr,
gai_strerror(rc));
freeaddrinfo(ai);
close(s);
leave_function();
return -1;
}
for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) {
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
ssh_set_error(session, SSH_FATAL,
"Binding local address: %s", strerror(errno));
continue;
} else {
break;
}
}
freeaddrinfo(bind_ai);
/* Cannot bind to any local addresses */
if (bind_itr == NULL) {
ssh_connect_socket_close(s);
s = -1;
continue;
}
}
if (timeout || usec) {
socket_t ret = ssh_connect_ai_timeout(session, host, port, itr,
timeout, usec, s);
leave_function();
return ret;
}
if (connect(s, itr->ai_addr, itr->ai_addrlen) < 0) {
ssh_set_error(session, SSH_FATAL, "Connect failed: %s", strerror(errno));
ssh_connect_socket_close(s);
s = -1;
leave_function();
continue;
} else {
/* We are connected */
break;
}
}
freeaddrinfo(ai);
leave_function();
return s;
}
/**
* @internal
*
* @brief Launches a nonblocking connect to an IPv4 or IPv6 host
* specified by its IP address or hostname.
*
* @returns A file descriptor, < 0 on error.
* @warning very ugly !!!
*/
socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
const char *bind_addr, int port) {
socket_t s = -1;
int rc;
struct addrinfo *ai;
struct addrinfo *itr;
enter_function();
rc = getai(session,host, port, &ai);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to resolve hostname %s (%s)", host, gai_strerror(rc));
leave_function();
return -1;
}
for (itr = ai; itr != NULL; itr = itr->ai_next){
/* create socket */
s = socket(itr->ai_family, itr->ai_socktype, itr->ai_protocol);
if (s < 0) {
ssh_set_error(session, SSH_FATAL,
"Socket create failed: %s", strerror(errno));
continue;
}
if (bind_addr) {
struct addrinfo *bind_ai;
struct addrinfo *bind_itr;
ssh_log(session, SSH_LOG_PACKET, "Resolving %s\n", bind_addr);
rc = getai(session,bind_addr, 0, &bind_ai);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to resolve bind address %s (%s)",
bind_addr,
gai_strerror(rc));
close(s);
s=-1;
break;
}
for (bind_itr = bind_ai; bind_itr != NULL; bind_itr = bind_itr->ai_next) {
if (bind(s, bind_itr->ai_addr, bind_itr->ai_addrlen) < 0) {
ssh_set_error(session, SSH_FATAL,
"Binding local address: %s", strerror(errno));
continue;
} else {
break;
}
}
freeaddrinfo(bind_ai);
/* Cannot bind to any local addresses */
if (bind_itr == NULL) {
ssh_connect_socket_close(s);
s = -1;
continue;
}
}
ssh_socket_set_nonblocking(s);
connect(s, itr->ai_addr, itr->ai_addrlen);
break;
}
freeaddrinfo(ai);
leave_function();
return s;
}
/**
* @addtogroup libssh_session
*
* @{
*/
static int ssh_select_cb (socket_t fd, int revents, void *userdata){
fd_set *set = (fd_set *)userdata;
if(revents & POLLIN)
FD_SET(fd, set);
return 0;
}
/**
* @brief A wrapper for the select syscall
*
* This functions acts more or less like the select(2) syscall.\n
* There is no support for writing or exceptions.\n
*
* @param[in] channels Arrays of channels pointers terminated by a NULL.
* It is never rewritten.
*
* @param[out] outchannels Arrays of same size that "channels", there is no need
* to initialize it.
*
* @param[in] maxfd Maximum +1 file descriptor from readfds.
*
* @param[in] readfds A fd_set of file descriptors to be select'ed for
* reading.
*
* @param[in] timeout A timeout for the select.
*
* @return SSH_OK on success,
* SSH_ERROR on error,
* SSH_EINTR if it was interrupted. In that case,
* just restart it.
*
* @warning libssh is not reentrant here. That means that if a signal is caught
* during the processing of this function, you cannot call libssh
* functions on sessions that are busy with ssh_select().
*
* @see select(2)
*/
int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd,
fd_set *readfds, struct timeval *timeout) {
int i,j;
int rc;
int base_tm, tm;
struct ssh_timestamp ts;
ssh_event event = ssh_event_new();
int firstround=1;
base_tm = tm=timeout->tv_sec * 1000 + timeout->tv_usec/1000;
for (i=0 ; channels[i] != NULL; ++i){
ssh_event_add_session(event, channels[i]->session);
}
for (i=0; i<maxfd ; ++i){
if(FD_ISSET(i, readfds)){
ssh_event_add_fd(event, i, POLLIN, ssh_select_cb, readfds);
}
}
outchannels[0] = NULL;
FD_ZERO(readfds);
ssh_timestamp_init(&ts);
do {
/* Poll every channel */
j = 0;
for (i = 0; channels[i]; i++) {
if(ssh_channel_poll(channels[i], 0) != 0) {
outchannels[j] = channels[i];
j++;
} else if(ssh_channel_poll(channels[i], 1) != 0) {
outchannels[j] = channels[i];
j++;
}
}
outchannels[j] = NULL;
if(j != 0)
break;
/* watch if a user socket was triggered */
for(i = 0;i<maxfd;++i)
if(FD_ISSET(i, readfds))
goto out;
/* If the timeout is elapsed, we should go out */
if(!firstround && ssh_timeout_elapsed(&ts, base_tm))
goto out;
/* since there's nothing, let's fire the polling */
rc = ssh_event_dopoll(event,tm);
if (rc == SSH_ERROR){
ssh_event_free(event);
return SSH_ERROR;
}
tm = ssh_timeout_update(&ts, base_tm);
firstround=0;
} while (1);
out:
ssh_event_free(event);
return SSH_OK;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

95
libssh/src/crc32.c Normal file
View file

@ -0,0 +1,95 @@
/*
* crc32.c - simple CRC32 code
*
* This file is part of the SSH Library
*
* Copyright (c) 2005 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/priv.h"
#include "libssh/crc32.h"
static uint32_t crc_table[] = {
0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
0x2d02ef8dUL
};
uint32_t ssh_crc32(const char *buf, uint32_t len) {
uint32_t ret = 0;
while(len > 0) {
ret = crc_table[(ret ^ *buf) & 0xff] ^ (ret >> 8);
--len;
++buf;
}
return ret;
}
/* vim: set ts=2 sw=2 et cindent: */

1136
libssh/src/dh.c Normal file

File diff suppressed because it is too large Load diff

278
libssh/src/ecdh.c Normal file
View file

@ -0,0 +1,278 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2011 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/session.h"
#include "libssh/ecdh.h"
#include "libssh/dh.h"
#include "libssh/buffer.h"
#include "libssh/ssh2.h"
#include "libssh/pki.h"
#ifdef HAVE_ECDH
#include <openssl/ecdh.h>
#define NISTP256 NID_X9_62_prime256v1
#define NISTP384 NID_secp384r1
#define NISTP521 NID_secp521r1
/** @internal
* @brief Starts ecdh-sha2-nistp256 key exchange
*/
int ssh_client_ecdh_init(ssh_session session){
EC_KEY *key=NULL;
const EC_GROUP *group;
const EC_POINT *pubkey;
ssh_string client_pubkey;
int len;
int rc;
bignum_CTX ctx=BN_CTX_new();
enter_function();
if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT) < 0) {
goto error;
}
key = EC_KEY_new_by_curve_name(NISTP256);
group = EC_KEY_get0_group(key);
EC_KEY_generate_key(key);
pubkey=EC_KEY_get0_public_key(key);
len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
NULL,0,ctx);
client_pubkey=ssh_string_new(len);
EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED,
ssh_string_data(client_pubkey),len,ctx);
buffer_add_ssh_string(session->out_buffer,client_pubkey);
BN_CTX_free(ctx);
session->next_crypto->ecdh_privkey = key;
session->next_crypto->ecdh_client_pubkey = client_pubkey;
rc = packet_send(session);
leave_function();
return rc;
error:
leave_function();
return SSH_ERROR;
}
static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) {
session->next_crypto->server_pubkey = pubkey_string;
}
static int ecdh_build_k(ssh_session session) {
const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey);
EC_POINT *pubkey;
void *buffer;
int len = (EC_GROUP_get_degree(group) + 7) / 8;
bignum_CTX ctx = bignum_ctx_new();
if (ctx == NULL) {
return -1;
}
session->next_crypto->k = bignum_new();
if (session->next_crypto->k == NULL) {
bignum_ctx_free(ctx);
return -1;
}
pubkey = EC_POINT_new(group);
if (pubkey == NULL) {
bignum_ctx_free(ctx);
return -1;
}
if (session->server)
EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_client_pubkey),
ssh_string_len(session->next_crypto->ecdh_client_pubkey),ctx);
else
EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_server_pubkey),
ssh_string_len(session->next_crypto->ecdh_server_pubkey),ctx);
buffer = malloc(len);
ECDH_compute_key(buffer,len,pubkey,session->next_crypto->ecdh_privkey,NULL);
EC_POINT_free(pubkey);
BN_bin2bn(buffer,len,session->next_crypto->k);
free(buffer);
EC_KEY_free(session->next_crypto->ecdh_privkey);
session->next_crypto->ecdh_privkey=NULL;
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Session server cookie",
session->next_crypto->server_kex.cookie, 16);
ssh_print_hexa("Session client cookie",
session->next_crypto->client_kex.cookie, 16);
ssh_print_bignum("Shared secret key", session->next_crypto->k);
#endif
#ifdef HAVE_LIBCRYPTO
bignum_ctx_free(ctx);
#endif
return 0;
}
/** @internal
* @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back
* a SSH_MSG_NEWKEYS
*/
int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){
ssh_string q_s_string = NULL;
ssh_string pubkey = NULL;
ssh_string signature = NULL;
int rc;
pubkey = buffer_get_ssh_string(packet);
if (pubkey == NULL){
ssh_set_error(session,SSH_FATAL, "No public key in packet");
goto error;
}
ecdh_import_pubkey(session, pubkey);
q_s_string = buffer_get_ssh_string(packet);
if (q_s_string == NULL) {
ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet");
goto error;
}
session->next_crypto->ecdh_server_pubkey = q_s_string;
signature = buffer_get_ssh_string(packet);
if (signature == NULL) {
ssh_set_error(session, SSH_FATAL, "No signature in packet");
goto error;
}
session->next_crypto->dh_server_signature = signature;
signature=NULL; /* ownership changed */
/* TODO: verify signature now instead of waiting for NEWKEYS */
if (ecdh_build_k(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
goto error;
}
/* Send the MSG_NEWKEYS */
if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
goto error;
}
rc=packet_send(session);
ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return rc;
error:
return SSH_ERROR;
}
#ifdef WITH_SERVER
/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a
* SSH_MSG_KEXDH_REPLY
*/
int ssh_server_ecdh_init(ssh_session session, ssh_buffer packet){
/* ECDH keys */
ssh_string q_c_string = NULL;
ssh_string q_s_string = NULL;
EC_KEY *ecdh_key=NULL;
const EC_GROUP *group;
const EC_POINT *ecdh_pubkey;
bignum_CTX ctx;
/* SSH host keys (rsa,dsa,ecdsa) */
ssh_key privkey;
ssh_string sig_blob = NULL;
int len;
int rc;
enter_function();
/* Extract the client pubkey from the init packet */
q_c_string = buffer_get_ssh_string(packet);
if (q_c_string == NULL) {
ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet");
goto error;
}
session->next_crypto->ecdh_client_pubkey = q_c_string;
/* Build server's keypair */
ctx = BN_CTX_new();
ecdh_key = EC_KEY_new_by_curve_name(NISTP256);
group = EC_KEY_get0_group(ecdh_key);
EC_KEY_generate_key(ecdh_key);
ecdh_pubkey=EC_KEY_get0_public_key(ecdh_key);
len = EC_POINT_point2oct(group,ecdh_pubkey,POINT_CONVERSION_UNCOMPRESSED,
NULL,0,ctx);
q_s_string=ssh_string_new(len);
EC_POINT_point2oct(group,ecdh_pubkey,POINT_CONVERSION_UNCOMPRESSED,
ssh_string_data(q_s_string),len,ctx);
BN_CTX_free(ctx);
session->next_crypto->ecdh_privkey = ecdh_key;
session->next_crypto->ecdh_server_pubkey = q_s_string;
buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_REPLY);
/* build k and session_id */
if (ecdh_build_k(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
goto error;
}
if (ssh_get_key_params(session, &privkey) == SSH_ERROR)
goto error;
if (make_sessionid(session) != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not create a session id");
goto error;
}
/* add host's public key */
buffer_add_ssh_string(session->out_buffer, session->next_crypto->server_pubkey);
/* add ecdh public key */
buffer_add_ssh_string(session->out_buffer,q_s_string);
/* add signature blob */
sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey);
if (sig_blob == NULL) {
ssh_set_error(session, SSH_FATAL, "Could not sign the session id");
goto error;
}
buffer_add_ssh_string(session->out_buffer, sig_blob);
ssh_string_free(sig_blob);
/* Free private keys as they should not be readable after this point */
if (session->srv.rsa_key) {
ssh_key_free(session->srv.rsa_key);
session->srv.rsa_key = NULL;
}
if (session->srv.dsa_key) {
ssh_key_free(session->srv.dsa_key);
session->srv.dsa_key = NULL;
}
ssh_log(session,SSH_LOG_PROTOCOL, "SSH_MSG_KEXDH_REPLY sent");
rc = packet_send(session);
if (rc == SSH_ERROR)
goto error;
/* Send the MSG_NEWKEYS */
if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
goto error;
}
session->dh_handshake_state=DH_STATE_NEWKEYS_SENT;
rc=packet_send(session);
ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent");
return rc;
error:
return SSH_ERROR;
}
#endif /* WITH_SERVER */
#endif /* HAVE_ECDH */

139
libssh/src/error.c Normal file
View file

@ -0,0 +1,139 @@
/*
* error.c - functions for ssh error handling
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdarg.h>
#include "libssh/priv.h"
#include "libssh/session.h"
/**
* @defgroup libssh_error The SSH error functions.
* @ingroup libssh
*
* Functions for error handling.
*
* @{
*/
/**
* @internal
*
* @brief Registers an error with a description.
*
* @param error The place to store the error.
*
* @param code The class of error.
*
* @param descr The description, which can be a format string.
*
* @param ... The arguments for the format string.
*/
void _ssh_set_error(void *error,
int code,
const char *function,
const char *descr, ...)
{
struct ssh_common_struct *err = error;
va_list va;
va_start(va, descr);
vsnprintf(err->error.error_buffer, ERROR_BUFFERLEN, descr, va);
va_end(va);
err->error.error_code = code;
ssh_log_common(err,
SSH_LOG_WARN,
function,
"Error: %s",
err->error.error_buffer);
}
/**
* @internal
*
* @brief Registers an out of memory error
*
* @param error The place to store the error.
*
*/
void _ssh_set_error_oom(void *error, const char *function)
{
struct error_struct *err = error;
snprintf(err->error_buffer, sizeof(err->error_buffer),
"%s: Out of memory", function);
err->error_code = SSH_FATAL;
}
/**
* @internal
*
* @brief Registers an invalid argument error
*
* @param error The place to store the error.
*
* @param function The function the error happened in.
*
*/
void _ssh_set_error_invalid(void *error, const char *function)
{
_ssh_set_error(error, SSH_FATAL, function,
"Invalid argument in %s", function);
}
/**
* @brief Retrieve the error text message from the last error.
*
* @param error The SSH session pointer.
*
* @return A static string describing the error.
*/
const char *ssh_get_error(void *error) {
struct error_struct *err = error;
return err->error_buffer;
}
/**
* @brief Retrieve the error code from the last error.
*
* @param error The SSH session pointer.
*
* \return SSH_NO_ERROR No error occurred\n
* SSH_REQUEST_DENIED The last request was denied but situation is
* recoverable\n
* SSH_FATAL A fatal error occurred. This could be an unexpected
* disconnection\n
*
* Other error codes are internal but can be considered same than
* SSH_FATAL.
*/
int ssh_get_error_code(void *error) {
struct error_struct *err = error;
return err->error_code;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

101
libssh/src/gcrypt_missing.c Normal file
View file

@ -0,0 +1,101 @@
/*
* gcrypt_missing.c - routines that are in OpenSSL but not in libgcrypt.
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2006 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/libgcrypt.h"
#ifdef HAVE_LIBGCRYPT
int my_gcry_dec2bn(bignum *bn, const char *data) {
int count;
*bn = bignum_new();
if (*bn == NULL) {
return 0;
}
gcry_mpi_set_ui(*bn, 0);
for (count = 0; data[count]; count++) {
gcry_mpi_mul_ui(*bn, *bn, 10);
gcry_mpi_add_ui(*bn, *bn, data[count] - '0');
}
return count;
}
char *my_gcry_bn2dec(bignum bn) {
bignum bndup, num, ten;
char *ret;
int count, count2;
int size, rsize;
char decnum;
size = gcry_mpi_get_nbits(bn) * 3;
rsize = size / 10 + size / 1000 + 2;
ret = malloc(rsize + 1);
if (ret == NULL) {
return NULL;
}
if (!gcry_mpi_cmp_ui(bn, 0)) {
strcpy(ret, "0");
} else {
ten = bignum_new();
if (ten == NULL) {
SAFE_FREE(ret);
return NULL;
}
num = bignum_new();
if (num == NULL) {
SAFE_FREE(ret);
bignum_free(ten);
return NULL;
}
for (bndup = gcry_mpi_copy(bn), bignum_set_word(ten, 10), count = rsize;
count; count--) {
gcry_mpi_div(bndup, num, bndup, ten, 0);
for (decnum = 0, count2 = gcry_mpi_get_nbits(num); count2;
decnum *= 2, decnum += (gcry_mpi_test_bit(num, count2 - 1) ? 1 : 0),
count2--)
;
ret[count - 1] = decnum + '0';
}
for (count = 0; count < rsize && ret[count] == '0'; count++)
;
for (count2 = 0; count2 < rsize - count; ++count2) {
ret[count2] = ret[count2 + count];
}
ret[count2] = 0;
bignum_free(num);
bignum_free(bndup);
bignum_free(ten);
}
return ret;
}
#endif
/* vim: set ts=2 sw=2 et cindent: */

282
libssh/src/getpass.c Normal file
View file

@ -0,0 +1,282 @@
/*
* getpass.c - platform independent getpass function.
*
* This file is part of the SSH Library
*
* Copyright (c) 2011 by Andreas Schneider <mail@cryptomilk.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libssh/priv.h>
/**
* @internal
*
* @brief Get the password from the console.
*
* @param[in] prompt The prompt to display.
*
* @param[in] buf The buffer to fill.
*
* @param[in] len The length of the buffer.
*
* @param[in] verify Should the password be verified?
*
* @return 1 on success, 0 on error.
*/
static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) {
char *tmp;
char *ptr = NULL;
int ok = 0;
tmp = malloc(len);
if (tmp == NULL) {
return 0;
}
memset(tmp,'\0',len);
/* read the password */
while (!ok) {
if (buf[0] != '\0') {
fprintf(stdout, "%s[%s] ", prompt, buf);
} else {
fprintf(stdout, "%s", prompt);
}
fflush(stdout);
if (fgets(tmp, len, stdin) == NULL) {
free(tmp);
return 0;
}
if ((ptr = strchr(tmp, '\n'))) {
*ptr = '\0';
}
fprintf(stdout, "\n");
if (*tmp) {
strncpy(buf, tmp, len);
}
if (verify) {
char *key_string;
key_string = malloc(len);
if (key_string == NULL) {
break;
}
memset(key_string, '\0', len);
fprintf(stdout, "\nVerifying, please re-enter. %s", prompt);
fflush(stdout);
if (! fgets(key_string, len, stdin)) {
memset(key_string, '\0', len);
SAFE_FREE(key_string);
clearerr(stdin);
continue;
}
if ((ptr = strchr(key_string, '\n'))) {
*ptr = '\0';
}
fprintf(stdout, "\n");
if (strcmp(buf, key_string)) {
printf("\n\07\07Mismatch - try again\n");
memset(key_string, '\0', len);
SAFE_FREE(key_string);
fflush(stdout);
continue;
}
memset(key_string, '\0', len);
SAFE_FREE(key_string);
}
ok = 1;
}
memset(tmp, '\0', len);
free(tmp);
return ok;
}
#ifdef _WIN32
#include <windows.h>
int ssh_getpass(const char *prompt,
char *buf,
size_t len,
int echo,
int verify) {
HANDLE h;
DWORD mode = 0;
int ok;
/* fgets needs at least len - 1 */
if (prompt == NULL || buf == NULL || len < 2) {
return -1;
}
/* get stdin and mode */
h = GetStdHandle(STD_INPUT_HANDLE);
if (!GetConsoleMode(h, &mode)) {
return -1;
}
/* disable echo */
if (!echo) {
if (!SetConsoleMode(h, mode & ~ENABLE_ECHO_INPUT)) {
return -1;
}
}
ok = ssh_gets(prompt, buf, len, verify);
/* reset echo */
SetConsoleMode(h, mode);
if (!ok) {
memset (buf, '\0', len);
return -1;
}
/* force termination */
buf[len - 1] = '\0';
return 0;
}
#else
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
/**
* @ingroup libssh_misc
*
* @brief Get a password from the console.
*
* You should make sure that the buffer is an empty string!
*
* You can also use this function to ask for a username. Then you can fill the
* buffer with the username and it is shows to the users. If the users just
* presses enter the buffer will be untouched.
*
* @code
* char username[128];
*
* snprintf(username, sizeof(username), "john");
*
* ssh_getpass("Username:", username, sizeof(username), 1, 0);
* @endcode
*
* The prompt will look like this:
*
* Username: [john]
*
* If you press enter then john is used as the username, or you can type it in
* to change it.
*
* @param[in] prompt The prompt to show to ask for the password.
*
* @param[out] buf The buffer the password should be stored. It NEEDS to be
* empty or filled out.
*
* @param[in] len The length of the buffer.
*
* @param[in] echo Should we echo what you type.
*
* @param[in] verify Should we ask for the password twice.
*
* @return 0 on success, -1 on error.
*/
int ssh_getpass(const char *prompt,
char *buf,
size_t len,
int echo,
int verify) {
struct termios attr;
struct termios old_attr;
int ok = 0;
int fd = -1;
/* fgets needs at least len - 1 */
if (prompt == NULL || buf == NULL || len < 2) {
return -1;
}
if (isatty(STDIN_FILENO)) {
ZERO_STRUCT(attr);
ZERO_STRUCT(old_attr);
/* get local terminal attributes */
if (tcgetattr(STDIN_FILENO, &attr) < 0) {
perror("tcgetattr");
return -1;
}
/* save terminal attributes */
memcpy(&old_attr, &attr, sizeof(attr));
if((fd = fcntl(0, F_GETFL, 0)) < 0) {
perror("fcntl");
return -1;
}
/* disable echo */
if (!echo) {
attr.c_lflag &= ~(ECHO);
}
/* write attributes to terminal */
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) {
perror("tcsetattr");
return -1;
}
}
/* disable nonblocking I/O */
if (fd & O_NDELAY) {
fcntl(0, F_SETFL, fd & ~O_NDELAY);
}
ok = ssh_gets(prompt, buf, len, verify);
if (isatty(STDIN_FILENO)) {
/* reset terminal */
tcsetattr(STDIN_FILENO, TCSANOW, &old_attr);
}
/* close fd */
if (fd & O_NDELAY) {
fcntl(0, F_SETFL, fd);
}
if (!ok) {
memset (buf, '\0', len);
return -1;
}
/* force termination */
buf[len - 1] = '\0';
return 0;
}
#endif
/* vim: set ts=4 sw=4 et cindent syntax=c.doxygen: */

221
libssh/src/gzip.c Normal file
View file

@ -0,0 +1,221 @@
/*
* gzip.c - hooks for compression of packets
*
* This file is part of the SSH Library
*
* Copyright (c) 2003 by Aris Adamantiadis
* Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/session.h"
#define BLOCKSIZE 4092
static z_stream *initcompress(ssh_session session, int level) {
z_stream *stream = NULL;
int status;
stream = malloc(sizeof(z_stream));
if (stream == NULL) {
return NULL;
}
memset(stream, 0, sizeof(z_stream));
status = deflateInit(stream, level);
if (status != Z_OK) {
SAFE_FREE(stream);
ssh_set_error(session, SSH_FATAL,
"status %d inititalising zlib deflate", status);
return NULL;
}
return stream;
}
static ssh_buffer gzip_compress(ssh_session session,ssh_buffer source,int level){
z_stream *zout = session->current_crypto->compress_out_ctx;
void *in_ptr = buffer_get_rest(source);
unsigned long in_size = buffer_get_rest_len(source);
ssh_buffer dest = NULL;
unsigned char out_buf[BLOCKSIZE] = {0};
unsigned long len;
int status;
if(zout == NULL) {
zout = session->current_crypto->compress_out_ctx = initcompress(session, level);
if (zout == NULL) {
return NULL;
}
}
dest = ssh_buffer_new();
if (dest == NULL) {
return NULL;
}
zout->next_out = out_buf;
zout->next_in = in_ptr;
zout->avail_in = in_size;
do {
zout->avail_out = BLOCKSIZE;
status = deflate(zout, Z_PARTIAL_FLUSH);
if (status != Z_OK) {
ssh_buffer_free(dest);
ssh_set_error(session, SSH_FATAL,
"status %d deflating zlib packet", status);
return NULL;
}
len = BLOCKSIZE - zout->avail_out;
if (buffer_add_data(dest, out_buf, len) < 0) {
ssh_buffer_free(dest);
return NULL;
}
zout->next_out = out_buf;
} while (zout->avail_out == 0);
return dest;
}
int compress_buffer(ssh_session session, ssh_buffer buf) {
ssh_buffer dest = NULL;
dest = gzip_compress(session, buf, session->opts.compressionlevel);
if (dest == NULL) {
return -1;
}
if (buffer_reinit(buf) < 0) {
ssh_buffer_free(dest);
return -1;
}
if (buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) {
ssh_buffer_free(dest);
return -1;
}
ssh_buffer_free(dest);
return 0;
}
/* decompression */
static z_stream *initdecompress(ssh_session session) {
z_stream *stream = NULL;
int status;
stream = malloc(sizeof(z_stream));
if (stream == NULL) {
return NULL;
}
memset(stream,0,sizeof(z_stream));
status = inflateInit(stream);
if (status != Z_OK) {
SAFE_FREE(stream);
ssh_set_error(session, SSH_FATAL,
"Status = %d initiating inflate context!", status);
return NULL;
}
return stream;
}
static ssh_buffer gzip_decompress(ssh_session session, ssh_buffer source, size_t maxlen) {
z_stream *zin = session->current_crypto->compress_in_ctx;
void *in_ptr = buffer_get_rest(source);
unsigned long in_size = buffer_get_rest_len(source);
unsigned char out_buf[BLOCKSIZE] = {0};
ssh_buffer dest = NULL;
unsigned long len;
int status;
if (zin == NULL) {
zin = session->current_crypto->compress_in_ctx = initdecompress(session);
if (zin == NULL) {
return NULL;
}
}
dest = ssh_buffer_new();
if (dest == NULL) {
return NULL;
}
zin->next_out = out_buf;
zin->next_in = in_ptr;
zin->avail_in = in_size;
do {
zin->avail_out = BLOCKSIZE;
status = inflate(zin, Z_PARTIAL_FLUSH);
if (status != Z_OK && status != Z_BUF_ERROR) {
ssh_set_error(session, SSH_FATAL,
"status %d inflating zlib packet", status);
ssh_buffer_free(dest);
return NULL;
}
len = BLOCKSIZE - zin->avail_out;
if (buffer_add_data(dest,out_buf,len) < 0) {
ssh_buffer_free(dest);
return NULL;
}
if (buffer_get_rest_len(dest) > maxlen){
/* Size of packet exceeded, avoid a denial of service attack */
ssh_buffer_free(dest);
return NULL;
}
zin->next_out = out_buf;
} while (zin->avail_out == 0);
return dest;
}
int decompress_buffer(ssh_session session,ssh_buffer buf, size_t maxlen){
ssh_buffer dest = NULL;
dest = gzip_decompress(session,buf, maxlen);
if (dest == NULL) {
return -1;
}
if (buffer_reinit(buf) < 0) {
ssh_buffer_free(dest);
return -1;
}
if (buffer_add_data(buf, buffer_get_rest(dest), buffer_get_rest_len(dest)) < 0) {
ssh_buffer_free(dest);
return -1;
}
ssh_buffer_free(dest);
return 0;
}
/* vim: set ts=2 sw=2 et cindent: */

85
libssh/src/init.c Normal file
View file

@ -0,0 +1,85 @@
/*
* init.c - initialization and finalization of the library
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2009 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/priv.h"
#include "libssh/socket.h"
#include "libssh/dh.h"
#include "libssh/poll.h"
#include "libssh/threads.h"
#ifdef _WIN32
#include <winsock2.h>
#endif
/**
* @defgroup libssh The libssh API
*
* The libssh library is implementing the SSH protocols and some of its
* extensions. This group of functions is mostly used to implment a SSH client.
* Some function are needed to implement a SSH server too.
*
* @{
*/
/**
* @brief Initialize global cryptographic data structures.
*
* This function should only be called once, at the beginning of the program, in
* the main thread. It may be omitted if your program is not multithreaded.
*
* @returns 0 on success, -1 if an error occured.
*/
int ssh_init(void) {
if(ssh_threads_init())
return -1;
if(ssh_crypto_init())
return -1;
if(ssh_socket_init())
return -1;
return 0;
}
/**
* @brief Finalize and cleanup all libssh and cryptographic data structures.
*
* This function should only be called once, at the end of the program!
*
* @returns 0 on succes, -1 if an error occured.
*
@returns 0 otherwise
*/
int ssh_finalize(void) {
ssh_crypto_finalize();
ssh_socket_cleanup();
/* It is important to finalize threading after CRYPTO because
* it still depends on it */
ssh_threads_finalize();
return 0;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

501
libssh/src/kex.c Normal file
View file

@ -0,0 +1,501 @@
/*
* kex.c - key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/dh.h"
#include "libssh/kex.h"
#include "libssh/session.h"
#include "libssh/ssh2.h"
#include "libssh/string.h"
#ifdef HAVE_LIBGCRYPT
# define BLOWFISH "blowfish-cbc,"
# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
# define DES "3des-cbc,des-cbc-ssh1"
#elif defined(HAVE_LIBCRYPTO)
# ifdef HAVE_OPENSSL_BLOWFISH_H
# define BLOWFISH "blowfish-cbc,"
# else
# define BLOWFISH ""
# endif
# ifdef HAVE_OPENSSL_AES_H
# ifdef BROKEN_AES_CTR
# define AES "aes256-cbc,aes192-cbc,aes128-cbc,"
# else
# define AES "aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,"
# endif /* BROKEN_AES_CTR */
# else
# define AES ""
# endif
# define DES "3des-cbc,des-cbc-ssh1"
#endif
#ifdef WITH_ZLIB
#define ZLIB "none,zlib,zlib@openssh.com"
#else
#define ZLIB "none"
#endif
#ifdef HAVE_ECDH
#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss"
#else
#define KEY_EXCHANGE "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
#define HOSTKEYS "ssh-rsa,ssh-dss"
#endif
#define KEX_METHODS_SIZE 10
/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
static const char *default_methods[] = {
KEY_EXCHANGE,
HOSTKEYS,
AES BLOWFISH DES,
AES BLOWFISH DES,
"hmac-sha1",
"hmac-sha1",
"none",
"none",
"",
"",
NULL
};
/* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */
static const char *supported_methods[] = {
KEY_EXCHANGE,
HOSTKEYS,
AES BLOWFISH DES,
AES BLOWFISH DES,
"hmac-sha1",
"hmac-sha1",
ZLIB,
ZLIB,
"",
"",
NULL
};
/* descriptions of the key exchange packet */
static const char *ssh_kex_descriptions[] = {
"kex algos",
"server host key algo",
"encryption client->server",
"encryption server->client",
"mac algo client->server",
"mac algo server->client",
"compression algo client->server",
"compression algo server->client",
"languages client->server",
"languages server->client",
NULL
};
/* tokenize will return a token of strings delimited by ",". the first element has to be freed */
static char **tokenize(const char *chain){
char **tokens;
int n=1;
int i=0;
char *tmp;
char *ptr;
tmp = strdup(chain);
if (tmp == NULL) {
return NULL;
}
ptr = tmp;
while(*ptr){
if(*ptr==','){
n++;
*ptr=0;
}
ptr++;
}
/* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
tokens=malloc(sizeof(char *) * (n+1) ); /* +1 for the null */
if (tokens == NULL) {
SAFE_FREE(tmp);
return NULL;
}
ptr=tmp;
for(i=0;i<n;i++){
tokens[i]=ptr;
while(*ptr)
ptr++; // find a zero
ptr++; // then go one step further
}
tokens[i]=NULL;
return tokens;
}
/* same as tokenize(), but with spaces instead of ',' */
/* TODO FIXME rewrite me! */
char **space_tokenize(const char *chain){
char **tokens;
int n=1;
int i=0;
char *tmp;
char *ptr;
tmp = strdup(chain);
if (tmp == NULL) {
return NULL;
}
ptr = tmp;
while(*ptr==' ')
++ptr; /* skip initial spaces */
while(*ptr){
if(*ptr==' '){
n++; /* count one token per word */
*ptr=0;
while(*(ptr+1)==' '){ /* don't count if the tokens have more than 2 spaces */
*(ptr++)=0;
}
}
ptr++;
}
/* now n contains the number of tokens, the first possibly empty if the list was empty too e.g. "" */
tokens = malloc(sizeof(char *) * (n + 1)); /* +1 for the null */
if (tokens == NULL) {
SAFE_FREE(tmp);
return NULL;
}
ptr=tmp; /* we don't pass the initial spaces because the "tmp" pointer is needed by the caller */
/* function to free the tokens. */
for(i=0;i<n;i++){
tokens[i]=ptr;
if(i!=n-1){
while(*ptr)
ptr++; // find a zero
while(!*(ptr+1))
++ptr; /* if the zero is followed by other zeros, go through them */
ptr++; // then go one step further
}
}
tokens[i]=NULL;
return tokens;
}
const char *ssh_kex_get_supported_method(uint32_t algo) {
if (algo >= KEX_METHODS_SIZE) {
return NULL;
}
return supported_methods[algo];
}
const char *ssh_kex_get_description(uint32_t algo) {
if (algo >= KEX_METHODS_SIZE) {
return NULL;
}
return ssh_kex_descriptions[algo];
}
/* find_matching gets 2 parameters : a list of available objects (available_d), separated by colons,*/
/* and a list of preferred objects (preferred_d) */
/* it will return a strduped pointer on the first preferred object found in the available objects list */
char *ssh_find_matching(const char *available_d, const char *preferred_d){
char ** tok_available, **tok_preferred;
int i_avail, i_pref;
char *ret;
if ((available_d == NULL) || (preferred_d == NULL)) {
return NULL; /* don't deal with null args */
}
tok_available = tokenize(available_d);
if (tok_available == NULL) {
return NULL;
}
tok_preferred = tokenize(preferred_d);
if (tok_preferred == NULL) {
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_available);
return NULL;
}
for(i_pref=0; tok_preferred[i_pref] ; ++i_pref){
for(i_avail=0; tok_available[i_avail]; ++i_avail){
if(strcmp(tok_available[i_avail],tok_preferred[i_pref]) == 0){
/* match */
ret=strdup(tok_available[i_avail]);
/* free the tokens */
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_preferred[0]);
SAFE_FREE(tok_available);
SAFE_FREE(tok_preferred);
return ret;
}
}
}
SAFE_FREE(tok_available[0]);
SAFE_FREE(tok_preferred[0]);
SAFE_FREE(tok_available);
SAFE_FREE(tok_preferred);
return NULL;
}
SSH_PACKET_CALLBACK(ssh_packet_kexinit){
int server_kex=session->server;
ssh_string str = NULL;
char *strings[KEX_METHODS_SIZE];
int i;
enter_function();
(void)type;
(void)user;
memset(strings, 0, sizeof(strings));
if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED){
ssh_log(session,SSH_LOG_WARNING, "Other side initiating key re-exchange");
} else if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){
ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
goto error;
}
if (server_kex) {
if (buffer_get_data(packet,session->next_crypto->client_kex.cookie,16) != 16) {
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
goto error;
}
if (hashbufin_add_cookie(session, session->next_crypto->client_kex.cookie) < 0) {
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
goto error;
}
} else {
if (buffer_get_data(packet,session->next_crypto->server_kex.cookie,16) != 16) {
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: no cookie in packet");
goto error;
}
if (hashbufin_add_cookie(session, session->next_crypto->server_kex.cookie) < 0) {
ssh_set_error(session, SSH_FATAL, "ssh_packet_kexinit: adding cookie failed");
goto error;
}
}
for (i = 0; i < KEX_METHODS_SIZE; i++) {
str = buffer_get_ssh_string(packet);
if (str == NULL) {
break;
}
if (buffer_add_ssh_string(session->in_hashbuf, str) < 0) {
ssh_set_error(session, SSH_FATAL, "Error adding string in hash buffer");
goto error;
}
strings[i] = ssh_string_to_char(str);
if (strings[i] == NULL) {
ssh_set_error_oom(session);
goto error;
}
ssh_string_free(str);
str = NULL;
}
/* copy the server kex info into an array of strings */
if (server_kex) {
for (i = 0; i < SSH_KEX_METHODS; i++) {
session->next_crypto->client_kex.methods[i] = strings[i];
}
} else { /* client */
for (i = 0; i < SSH_KEX_METHODS; i++) {
session->next_crypto->server_kex.methods[i] = strings[i];
}
}
leave_function();
session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED;
session->dh_handshake_state=DH_STATE_INIT;
session->ssh_connection_callback(session);
return SSH_PACKET_USED;
error:
ssh_string_free(str);
for (i = 0; i < SSH_KEX_METHODS; i++) {
SAFE_FREE(strings[i]);
}
session->session_state = SSH_SESSION_STATE_ERROR;
leave_function();
return SSH_PACKET_USED;
}
void ssh_list_kex(ssh_session session, struct ssh_kex_struct *kex) {
int i = 0;
#ifdef DEBUG_CRYPTO
ssh_print_hexa("session cookie", kex->cookie, 16);
#endif
for(i = 0; i < SSH_KEX_METHODS; i++) {
if (kex->methods[i] == NULL) {
continue;
}
ssh_log(session, SSH_LOG_FUNCTIONS, "%s: %s",
ssh_kex_descriptions[i], kex->methods[i]);
}
}
/**
* @brief sets the key exchange parameters to be sent to the server,
* in function of the options and available methods.
*/
int set_client_kex(ssh_session session){
struct ssh_kex_struct *client= &session->next_crypto->client_kex;
const char *wanted;
int i;
ssh_get_random(client->cookie, 16, 0);
memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **));
for (i = 0; i < KEX_METHODS_SIZE; i++) {
wanted = session->opts.wanted_methods[i];
if (wanted == NULL)
wanted = default_methods[i];
client->methods[i] = strdup(wanted);
}
return SSH_OK;
}
/** @brief Select the different methods on basis of client's and
* server's kex messages, and watches out if a match is possible.
*/
int ssh_kex_select_methods (ssh_session session){
struct ssh_kex_struct *server = &session->next_crypto->server_kex;
struct ssh_kex_struct *client = &session->next_crypto->client_kex;
int rc = SSH_ERROR;
int i;
enter_function();
for (i = 0; i < KEX_METHODS_SIZE; i++) {
session->next_crypto->kex_methods[i]=ssh_find_matching(server->methods[i],client->methods[i]);
if(session->next_crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S){
ssh_set_error(session,SSH_FATAL,"kex error : no match for method %s: server [%s], client [%s]",
ssh_kex_descriptions[i],server->methods[i],client->methods[i]);
goto error;
} else if ((i >= SSH_LANG_C_S) && (session->next_crypto->kex_methods[i] == NULL)) {
/* we can safely do that for languages */
session->next_crypto->kex_methods[i] = strdup("");
}
}
if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){
session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1;
} else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "diffie-hellman-group14-sha1") == 0){
session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1;
} else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){
session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256;
}
rc = SSH_OK;
error:
leave_function();
return rc;
}
/* this function only sends the predefined set of kex methods */
int ssh_send_kex(ssh_session session, int server_kex) {
struct ssh_kex_struct *kex = (server_kex ? &session->next_crypto->server_kex :
&session->next_crypto->client_kex);
ssh_string str = NULL;
int i;
enter_function();
if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXINIT) < 0) {
goto error;
}
if (buffer_add_data(session->out_buffer, kex->cookie, 16) < 0) {
goto error;
}
if (hashbufout_add_cookie(session) < 0) {
goto error;
}
ssh_list_kex(session, kex);
for (i = 0; i < KEX_METHODS_SIZE; i++) {
str = ssh_string_from_char(kex->methods[i]);
if (str == NULL) {
goto error;
}
if (buffer_add_ssh_string(session->out_hashbuf, str) < 0) {
goto error;
}
if (buffer_add_ssh_string(session->out_buffer, str) < 0) {
goto error;
}
ssh_string_free(str);
}
if (buffer_add_u8(session->out_buffer, 0) < 0) {
goto error;
}
if (buffer_add_u32(session->out_buffer, 0) < 0) {
goto error;
}
if (packet_send(session) == SSH_ERROR) {
leave_function();
return -1;
}
leave_function();
return 0;
error:
buffer_reinit(session->out_buffer);
buffer_reinit(session->out_hashbuf);
ssh_string_free(str);
leave_function();
return -1;
}
/* returns 1 if at least one of the name algos is in the default algorithms table */
int verify_existing_algo(int algo, const char *name){
char *ptr;
if(algo>9 || algo <0)
return -1;
ptr=ssh_find_matching(supported_methods[algo],name);
if(ptr){
free(ptr);
return 1;
}
return 0;
}
/* vim: set ts=2 sw=2 et cindent: */

496
libssh/src/kex1.c Normal file
View file

@ -0,0 +1,496 @@
/*
* kex.c - key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#ifndef _WIN32
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/kex.h"
#include "libssh/keys.h"
#include "libssh/session.h"
#include "libssh/ssh1.h"
#include "libssh/wrapper.h"
/* SSHv1 functions */
/* makes a STRING contating 3 strings : ssh-rsa1,e and n */
/* this is a public key in openssh's format */
static ssh_string make_rsa1_string(ssh_string e, ssh_string n){
ssh_buffer buffer = NULL;
ssh_string rsa = NULL;
ssh_string ret = NULL;
buffer = ssh_buffer_new();
rsa = ssh_string_from_char("ssh-rsa1");
if (buffer_add_ssh_string(buffer, rsa) < 0) {
goto error;
}
if (buffer_add_ssh_string(buffer, e) < 0) {
goto error;
}
if (buffer_add_ssh_string(buffer, n) < 0) {
goto error;
}
ret = ssh_string_new(ssh_buffer_get_len(buffer));
if (ret == NULL) {
goto error;
}
ssh_string_fill(ret, ssh_buffer_get_begin(buffer), ssh_buffer_get_len(buffer));
error:
ssh_buffer_free(buffer);
ssh_string_free(rsa);
return ret;
}
static int build_session_id1(ssh_session session, ssh_string servern,
ssh_string hostn) {
MD5CTX md5 = NULL;
md5 = md5_init();
if (md5 == NULL) {
return -1;
}
#ifdef DEBUG_CRYPTO
ssh_print_hexa("host modulus",ssh_string_data(hostn),ssh_string_len(hostn));
ssh_print_hexa("server modulus",ssh_string_data(servern),ssh_string_len(servern));
#endif
md5_update(md5,ssh_string_data(hostn),ssh_string_len(hostn));
md5_update(md5,ssh_string_data(servern),ssh_string_len(servern));
md5_update(md5,session->next_crypto->server_kex.cookie,8);
if(session->next_crypto->session_id != NULL)
SAFE_FREE(session->next_crypto->session_id);
session->next_crypto->session_id = malloc(MD5_DIGEST_LEN);
if(session->next_crypto->session_id == NULL){
ssh_set_error_oom(session);
return SSH_ERROR;
}
md5_final(session->next_crypto->session_id,md5);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("session_id",session->next_crypto->session_id,MD5_DIGEST_LEN);
#endif
return 0;
}
/* returns 1 if the modulus of k1 is < than the one of k2 */
static int modulus_smaller(ssh_public_key k1, ssh_public_key k2){
bignum n1;
bignum n2;
int res;
#ifdef HAVE_LIBGCRYPT
gcry_sexp_t sexp;
sexp=gcry_sexp_find_token(k1->rsa_pub,"n",0);
n1=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG);
gcry_sexp_release(sexp);
sexp=gcry_sexp_find_token(k2->rsa_pub,"n",0);
n2=gcry_sexp_nth_mpi(sexp,1,GCRYMPI_FMT_USG);
gcry_sexp_release(sexp);
#elif defined HAVE_LIBCRYPTO
n1=k1->rsa_pub->n;
n2=k2->rsa_pub->n;
#endif
if(bignum_cmp(n1,n2)<0)
res=1;
else
res=0;
#ifdef HAVE_LIBGCRYPT
bignum_free(n1);
bignum_free(n2);
#endif
return res;
}
static ssh_string ssh_encrypt_rsa1(ssh_session session,
ssh_string data,
ssh_public_key key) {
ssh_string str = NULL;
size_t len = ssh_string_len(data);
size_t size = 0;
#ifdef HAVE_LIBGCRYPT
const char *tmp = NULL;
gcry_sexp_t ret_sexp;
gcry_sexp_t data_sexp;
if (gcry_sexp_build(&data_sexp, NULL, "(data(flags pkcs1)(value %b))",
len, ssh_string_data(data))) {
ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error");
return NULL;
}
if (gcry_pk_encrypt(&ret_sexp, data_sexp, key->rsa_pub)) {
gcry_sexp_release(data_sexp);
ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error");
return NULL;
}
gcry_sexp_release(data_sexp);
data_sexp = gcry_sexp_find_token(ret_sexp, "a", 0);
if (data_sexp == NULL) {
ssh_set_error(session, SSH_FATAL, "RSA1 encrypt: libgcrypt error");
gcry_sexp_release(ret_sexp);
return NULL;
}
tmp = gcry_sexp_nth_data(data_sexp, 1, &size);
if (*tmp == 0) {
size--;
tmp++;
}
str = ssh_string_new(size);
if (str == NULL) {
ssh_set_error(session, SSH_FATAL, "Not enough space");
gcry_sexp_release(data_sexp);
gcry_sexp_release(ret_sexp);
return NULL;
}
ssh_string_fill(str, tmp, size);
gcry_sexp_release(data_sexp);
gcry_sexp_release(ret_sexp);
#elif defined HAVE_LIBCRYPTO
size = RSA_size(key->rsa_pub);
str = ssh_string_new(size);
if (str == NULL) {
ssh_set_error(session, SSH_FATAL, "Not enough space");
return NULL;
}
if (RSA_public_encrypt(len, ssh_string_data(data), ssh_string_data(str), key->rsa_pub,
RSA_PKCS1_PADDING) < 0) {
ssh_string_free(str);
return NULL;
}
#endif
return str;
}
#define ABS(A) ( (A)<0 ? -(A):(A) )
static ssh_string encrypt_session_key(ssh_session session, ssh_public_key srvkey,
ssh_public_key hostkey, int slen, int hlen) {
unsigned char buffer[32] = {0};
int i;
ssh_string data1 = NULL;
ssh_string data2 = NULL;
if(session->next_crypto->encryptkey != NULL)
SAFE_FREE(session->next_crypto->encryptkey);
if(session->next_crypto->decryptkey != NULL)
SAFE_FREE(session->next_crypto->decryptkey);
if(session->next_crypto->encryptIV != NULL)
SAFE_FREE(session->next_crypto->encryptIV);
if(session->next_crypto->decryptIV != NULL)
SAFE_FREE(session->next_crypto->decryptIV);
session->next_crypto->encryptkey = malloc(32);
session->next_crypto->decryptkey = malloc(32);
session->next_crypto->encryptIV = malloc(32);
session->next_crypto->decryptIV = malloc(32);
if(session->next_crypto->encryptkey == NULL ||
session->next_crypto->decryptkey == NULL ||
session->next_crypto->encryptIV == NULL ||
session->next_crypto->decryptIV == NULL){
ssh_set_error_oom(session);
return NULL;
}
/* first, generate a session key */
ssh_get_random(session->next_crypto->encryptkey, 32, 1);
memcpy(buffer, session->next_crypto->encryptkey, 32);
memcpy(session->next_crypto->decryptkey, session->next_crypto->encryptkey, 32);
memset(session->next_crypto->encryptIV, 0, 32);
memset(session->next_crypto->decryptIV, 0, 32);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("session key",buffer,32);
#endif
/* xor session key with session_id */
for (i = 0; i < 16; i++) {
buffer[i] ^= session->next_crypto->session_id[i];
}
data1 = ssh_string_new(32);
if (data1 == NULL) {
return NULL;
}
ssh_string_fill(data1, buffer, 32);
if (ABS(hlen - slen) < 128){
ssh_log(session, SSH_LOG_FUNCTIONS,
"Difference between server modulus and host modulus is only %d. "
"It's illegal and may not work",
ABS(hlen - slen));
}
if (modulus_smaller(srvkey, hostkey)) {
data2 = ssh_encrypt_rsa1(session, data1, srvkey);
ssh_string_free(data1);
data1 = NULL;
if (data2 == NULL) {
return NULL;
}
data1 = ssh_encrypt_rsa1(session, data2, hostkey);
ssh_string_free(data2);
if (data1 == NULL) {
return NULL;
}
} else {
data2 = ssh_encrypt_rsa1(session, data1, hostkey);
ssh_string_free(data1);
data1 = NULL;
if (data2 == NULL) {
return NULL;
}
data1 = ssh_encrypt_rsa1(session, data2, srvkey);
ssh_string_free(data2);
if (data1 == NULL) {
return NULL;
}
}
return data1;
}
/* 2 SSH_SMSG_PUBLIC_KEY
*
* 8 bytes anti_spoofing_cookie
* 32-bit int server_key_bits
* mp-int server_key_public_exponent
* mp-int server_key_public_modulus
* 32-bit int host_key_bits
* mp-int host_key_public_exponent
* mp-int host_key_public_modulus
* 32-bit int protocol_flags
* 32-bit int supported_ciphers_mask
* 32-bit int supported_authentications_mask
*/
/**
* @brief Wait for a SSH_SMSG_PUBLIC_KEY and does the key exchange
*/
SSH_PACKET_CALLBACK(ssh_packet_publickey1){
ssh_string server_exp = NULL;
ssh_string server_mod = NULL;
ssh_string host_exp = NULL;
ssh_string host_mod = NULL;
ssh_string serverkey = NULL;
ssh_string hostkey = NULL;
ssh_public_key srv = NULL;
ssh_public_key host = NULL;
uint32_t server_bits;
uint32_t host_bits;
uint32_t protocol_flags;
uint32_t supported_ciphers_mask;
uint32_t supported_authentications_mask;
ssh_string enc_session = NULL;
uint16_t bits;
int ko;
uint32_t support_3DES = 0;
uint32_t support_DES = 0;
enter_function();
(void)type;
(void)user;
ssh_log(session, SSH_LOG_PROTOCOL, "Got a SSH_SMSG_PUBLIC_KEY");
if(session->session_state != SSH_SESSION_STATE_INITIAL_KEX){
ssh_set_error(session,SSH_FATAL,"SSH_KEXINIT received in wrong state");
goto error;
}
if (buffer_get_data(packet, session->next_crypto->server_kex.cookie, 8) != 8) {
ssh_set_error(session, SSH_FATAL, "Can't get cookie in buffer");
goto error;
}
buffer_get_u32(packet, &server_bits);
server_exp = buffer_get_mpint(packet);
if (server_exp == NULL) {
goto error;
}
server_mod = buffer_get_mpint(packet);
if (server_mod == NULL) {
goto error;
}
buffer_get_u32(packet, &host_bits);
host_exp = buffer_get_mpint(packet);
if (host_exp == NULL) {
goto error;
}
host_mod = buffer_get_mpint(packet);
if (host_mod == NULL) {
goto error;
}
buffer_get_u32(packet, &protocol_flags);
buffer_get_u32(packet, &supported_ciphers_mask);
ko = buffer_get_u32(packet, &supported_authentications_mask);
if ((ko != sizeof(uint32_t)) || !host_mod || !host_exp
|| !server_mod || !server_exp) {
ssh_log(session, SSH_LOG_RARE, "Invalid SSH_SMSG_PUBLIC_KEY packet");
ssh_set_error(session, SSH_FATAL, "Invalid SSH_SMSG_PUBLIC_KEY packet");
goto error;
}
server_bits = ntohl(server_bits);
host_bits = ntohl(host_bits);
protocol_flags = ntohl(protocol_flags);
supported_ciphers_mask = ntohl(supported_ciphers_mask);
supported_authentications_mask = ntohl(supported_authentications_mask);
ssh_log(session, SSH_LOG_PROTOCOL,
"Server bits: %d; Host bits: %d; Protocol flags: %.8lx; "
"Cipher mask: %.8lx; Auth mask: %.8lx",
server_bits,
host_bits,
(unsigned long int) protocol_flags,
(unsigned long int) supported_ciphers_mask,
(unsigned long int) supported_authentications_mask);
serverkey = make_rsa1_string(server_exp, server_mod);
if (serverkey == NULL) {
goto error;
}
hostkey = make_rsa1_string(host_exp,host_mod);
if (serverkey == NULL) {
goto error;
}
if (build_session_id1(session, server_mod, host_mod) < 0) {
goto error;
}
srv = publickey_from_string(session, serverkey);
if (srv == NULL) {
goto error;
}
host = publickey_from_string(session, hostkey);
if (host == NULL) {
goto error;
}
session->next_crypto->server_pubkey = ssh_string_copy(hostkey);
if (session->next_crypto->server_pubkey == NULL) {
goto error;
}
session->next_crypto->server_pubkey_type = "ssh-rsa1";
/* now, we must choose an encryption algo */
/* hardcode 3des */
//
support_3DES = (supported_ciphers_mask & (1<<SSH_CIPHER_3DES));
support_DES = (supported_ciphers_mask & (1<<SSH_CIPHER_DES));
if(!support_3DES && !support_DES){
ssh_set_error(session, SSH_FATAL, "Remote server doesn't accept 3DES");
goto error;
}
ssh_log(session, SSH_LOG_PROTOCOL, "Sending SSH_CMSG_SESSION_KEY");
if (buffer_add_u8(session->out_buffer, SSH_CMSG_SESSION_KEY) < 0) {
goto error;
}
if (buffer_add_u8(session->out_buffer, support_3DES ? SSH_CIPHER_3DES : SSH_CIPHER_DES) < 0) {
goto error;
}
if (buffer_add_data(session->out_buffer, session->next_crypto->server_kex.cookie, 8) < 0) {
goto error;
}
enc_session = encrypt_session_key(session, srv, host, server_bits, host_bits);
if (enc_session == NULL) {
goto error;
}
bits = ssh_string_len(enc_session) * 8 - 7;
ssh_log(session, SSH_LOG_PROTOCOL, "%d bits, %" PRIdS " bytes encrypted session",
bits, ssh_string_len(enc_session));
bits = htons(bits);
/* the encrypted mpint */
if (buffer_add_data(session->out_buffer, &bits, sizeof(uint16_t)) < 0) {
goto error;
}
if (buffer_add_data(session->out_buffer, ssh_string_data(enc_session),
ssh_string_len(enc_session)) < 0) {
goto error;
}
/* the protocol flags */
if (buffer_add_u32(session->out_buffer, 0) < 0) {
goto error;
}
session->session_state=SSH_SESSION_STATE_KEXINIT_RECEIVED;
if (packet_send(session) == SSH_ERROR) {
goto error;
}
/* we can set encryption */
if(crypt_set_algorithms(session, support_3DES ? SSH_3DES : SSH_DES)){
goto error;
}
session->current_crypto = session->next_crypto;
session->next_crypto = NULL;
goto end;
error:
session->session_state=SSH_SESSION_STATE_ERROR;
end:
ssh_string_free(host_mod);
ssh_string_free(host_exp);
ssh_string_free(server_mod);
ssh_string_free(server_exp);
ssh_string_free(serverkey);
ssh_string_free(hostkey);
ssh_string_free(enc_session);
publickey_free(srv);
publickey_free(host);
leave_function();
return SSH_PACKET_USED;
}
int ssh_get_kex1(ssh_session session) {
int ret=SSH_ERROR;
enter_function();
ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_PUBLIC_KEY");
/* Here the callback is called */
while(session->session_state==SSH_SESSION_STATE_INITIAL_KEX){
ssh_handle_packets(session, SSH_TIMEOUT_USER);
}
if(session->session_state==SSH_SESSION_STATE_ERROR)
goto error;
ssh_log(session, SSH_LOG_PROTOCOL, "Waiting for a SSH_SMSG_SUCCESS");
/* Waiting for SSH_SMSG_SUCCESS */
while(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){
ssh_handle_packets(session, SSH_TIMEOUT_USER);
}
if(session->session_state==SSH_SESSION_STATE_ERROR)
goto error;
ssh_log(session, SSH_LOG_PROTOCOL, "received SSH_SMSG_SUCCESS\n");
ret=SSH_OK;
error:
leave_function();
return ret;
}

666
libssh/src/known_hosts.c Normal file
View file

@ -0,0 +1,666 @@
/*
* keyfiles.c - private and public key handling for authentication.
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2009 by Aris Adamantiadis
* Copyright (c) 2009 by Andreas Schneider <mail@cynapses.org>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/buffer.h"
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/options.h"
/*todo: remove this include */
#include "libssh/string.h"
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
#elif defined HAVE_LIBCRYPTO
#include <openssl/pem.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#endif /* HAVE_LIBCRYPTO */
#ifndef _WIN32
# include <netinet/in.h>
# include <arpa/inet.h>
#endif
/**
* @addtogroup libssh_session
*
* @{
*/
static int alldigits(const char *s) {
while (*s) {
if (isdigit(*s)) {
s++;
} else {
return 0;
}
}
return 1;
}
/**
* @internal
*
* @brief Free a token array.
*/
static void tokens_free(char **tokens) {
if (tokens == NULL) {
return;
}
SAFE_FREE(tokens[0]);
/* It's not needed to free other pointers because tokens generated by
* space_tokenize fit all in one malloc
*/
SAFE_FREE(tokens);
}
/**
* @internal
*
* @brief Return one line of known host file.
*
* This will return a token array containing (host|ip), keytype and key.
*
* @param[out] file A pointer to the known host file. Could be pointing to
* NULL at start.
*
* @param[in] filename The file name of the known host file.
*
* @param[out] found_type A pointer to a string to be set with the found key
* type.
*
* @returns The found_type type of key (ie "dsa","ssh-rsa1"). Don't
* free that value. NULL if no match was found or the file
* was not found.
*/
static char **ssh_get_knownhost_line(ssh_session session, FILE **file,
const char *filename, const char **found_type) {
char buffer[4096] = {0};
char *ptr;
char **tokens;
enter_function();
if(*file == NULL){
*file = fopen(filename,"r");
if (*file == NULL) {
leave_function();
return NULL;
}
}
while (fgets(buffer, sizeof(buffer), *file)) {
ptr = strchr(buffer, '\n');
if (ptr) {
*ptr = '\0';
}
ptr = strchr(buffer,'\r');
if (ptr) {
*ptr = '\0';
}
if (!buffer[0] || buffer[0] == '#') {
continue; /* skip empty lines */
}
tokens = space_tokenize(buffer);
if (tokens == NULL) {
fclose(*file);
*file = NULL;
leave_function();
return NULL;
}
if(!tokens[0] || !tokens[1] || !tokens[2]) {
/* it should have at least 3 tokens */
tokens_free(tokens);
continue;
}
*found_type = tokens[1];
if (tokens[3]) {
/* openssh rsa1 format has 4 tokens on the line. Recognize it
by the fact that everything is all digits */
if (tokens[4]) {
/* that's never valid */
tokens_free(tokens);
continue;
}
if (alldigits(tokens[1]) && alldigits(tokens[2]) && alldigits(tokens[3])) {
*found_type = "ssh-rsa1";
} else {
/* 3 tokens only, not four */
tokens_free(tokens);
continue;
}
}
leave_function();
return tokens;
}
fclose(*file);
*file = NULL;
/* we did not find anything, end of file*/
leave_function();
return NULL;
}
/**
* @brief Check the public key in the known host line matches the public key of
* the currently connected server.
*
* @param[in] session The SSH session to use.
*
* @param[in] tokens A list of tokens in the known_hosts line.
*
* @returns 1 if the key matches, 0 if the key doesn't match and -1
* on error.
*/
static int check_public_key(ssh_session session, char **tokens) {
ssh_string pubkey = session->current_crypto->server_pubkey;
ssh_buffer pubkey_buffer;
char *pubkey_64;
/* ok we found some public key in known hosts file. now un-base64it */
if (alldigits(tokens[1])) {
/* openssh rsa1 format */
bignum tmpbn;
ssh_string tmpstring;
unsigned int len;
int i;
pubkey_buffer = ssh_buffer_new();
if (pubkey_buffer == NULL) {
return -1;
}
tmpstring = ssh_string_from_char("ssh-rsa1");
if (tmpstring == NULL) {
ssh_buffer_free(pubkey_buffer);
return -1;
}
if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) {
ssh_buffer_free(pubkey_buffer);
ssh_string_free(tmpstring);
return -1;
}
ssh_string_free(tmpstring);
for (i = 2; i < 4; i++) { /* e, then n */
tmpbn = NULL;
bignum_dec2bn(tokens[i], &tmpbn);
if (tmpbn == NULL) {
ssh_buffer_free(pubkey_buffer);
return -1;
}
/* for some reason, make_bignum_string does not work
because of the padding which it does --kv */
/* tmpstring = make_bignum_string(tmpbn); */
/* do it manually instead */
len = bignum_num_bytes(tmpbn);
tmpstring = malloc(4 + len);
if (tmpstring == NULL) {
ssh_buffer_free(pubkey_buffer);
bignum_free(tmpbn);
return -1;
}
/* TODO: fix the hardcoding */
tmpstring->size = htonl(len);
#ifdef HAVE_LIBGCRYPT
bignum_bn2bin(tmpbn, len, string_data(tmpstring));
#elif defined HAVE_LIBCRYPTO
bignum_bn2bin(tmpbn, string_data(tmpstring));
#endif
bignum_free(tmpbn);
if (buffer_add_ssh_string(pubkey_buffer, tmpstring) < 0) {
ssh_buffer_free(pubkey_buffer);
ssh_string_free(tmpstring);
bignum_free(tmpbn);
return -1;
}
ssh_string_free(tmpstring);
}
} else {
/* ssh-dss or ssh-rsa */
pubkey_64 = tokens[2];
pubkey_buffer = base64_to_bin(pubkey_64);
}
if (pubkey_buffer == NULL) {
ssh_set_error(session, SSH_FATAL,
"Verifying that server is a known host: base64 error");
return -1;
}
if (buffer_get_rest_len(pubkey_buffer) != ssh_string_len(pubkey)) {
ssh_buffer_free(pubkey_buffer);
return 0;
}
/* now test that they are identical */
if (memcmp(buffer_get_rest(pubkey_buffer), ssh_string_data(pubkey),
buffer_get_rest_len(pubkey_buffer)) != 0) {
ssh_buffer_free(pubkey_buffer);
return 0;
}
ssh_buffer_free(pubkey_buffer);
return 1;
}
/**
* @brief Check if a hostname matches a openssh-style hashed known host.
*
* @param[in] host The host to check.
*
* @param[in] hashed The hashed value.
*
* @returns 1 if it matches, 0 otherwise.
*/
static int match_hashed_host(ssh_session session, const char *host,
const char *sourcehash) {
/* Openssh hash structure :
* |1|base64 encoded salt|base64 encoded hash
* hash is produced that way :
* hash := HMAC_SHA1(key=salt,data=host)
*/
unsigned char buffer[256] = {0};
ssh_buffer salt;
ssh_buffer hash;
HMACCTX mac;
char *source;
char *b64hash;
int match;
unsigned int size;
enter_function();
if (strncmp(sourcehash, "|1|", 3) != 0) {
leave_function();
return 0;
}
source = strdup(sourcehash + 3);
if (source == NULL) {
leave_function();
return 0;
}
b64hash = strchr(source, '|');
if (b64hash == NULL) {
/* Invalid hash */
SAFE_FREE(source);
leave_function();
return 0;
}
*b64hash = '\0';
b64hash++;
salt = base64_to_bin(source);
if (salt == NULL) {
SAFE_FREE(source);
leave_function();
return 0;
}
hash = base64_to_bin(b64hash);
SAFE_FREE(source);
if (hash == NULL) {
ssh_buffer_free(salt);
leave_function();
return 0;
}
mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), SSH_HMAC_SHA1);
if (mac == NULL) {
ssh_buffer_free(salt);
ssh_buffer_free(hash);
leave_function();
return 0;
}
size = sizeof(buffer);
hmac_update(mac, host, strlen(host));
hmac_final(mac, buffer, &size);
if (size == buffer_get_rest_len(hash) &&
memcmp(buffer, buffer_get_rest(hash), size) == 0) {
match = 1;
} else {
match = 0;
}
ssh_buffer_free(salt);
ssh_buffer_free(hash);
ssh_log(session, SSH_LOG_PACKET,
"Matching a hashed host: %s match=%d", host, match);
leave_function();
return match;
}
/* How it's working :
* 1- we open the known host file and bitch if it doesn't exist
* 2- we need to examine each line of the file, until going on state SSH_SERVER_KNOWN_OK:
* - there's a match. if the key is good, state is SSH_SERVER_KNOWN_OK,
* else it's SSH_SERVER_KNOWN_CHANGED (or SSH_SERVER_FOUND_OTHER)
* - there's no match : no change
*/
/**
* @brief Check if the server is known.
*
* Checks the user's known host file for a previous connection to the
* current server.
*
* @param[in] session The SSH session to use.
*
* @returns SSH_SERVER_KNOWN_OK: The server is known and has not changed.\n
* SSH_SERVER_KNOWN_CHANGED: The server key has changed. Either you
* are under attack or the administrator
* changed the key. You HAVE to warn the
* user about a possible attack.\n
* SSH_SERVER_FOUND_OTHER: The server gave use a key of a type while
* we had an other type recorded. It is a
* possible attack.\n
* SSH_SERVER_NOT_KNOWN: The server is unknown. User should
* confirm the MD5 is correct.\n
* SSH_SERVER_FILE_NOT_FOUND: The known host file does not exist. The
* host is thus unknown. File will be
* created if host key is accepted.\n
* SSH_SERVER_ERROR: Some error happened.
*
* @see ssh_get_pubkey_hash()
*
* @bug There is no current way to remove or modify an entry into the known
* host table.
*/
int ssh_is_server_known(ssh_session session) {
FILE *file = NULL;
char **tokens;
char *host;
char *hostport;
const char *type;
int match;
int ret = SSH_SERVER_NOT_KNOWN;
enter_function();
if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Can't find a known_hosts file");
leave_function();
return SSH_SERVER_FILE_NOT_FOUND;
}
}
if (session->opts.host == NULL) {
ssh_set_error(session, SSH_FATAL,
"Can't verify host in known hosts if the hostname isn't known");
leave_function();
return SSH_SERVER_ERROR;
}
if (session->current_crypto == NULL){
ssh_set_error(session, SSH_FATAL,
"ssh_is_host_known called without cryptographic context");
leave_function();
return SSH_SERVER_ERROR;
}
host = ssh_lowercase(session->opts.host);
hostport = ssh_hostport(host, session->opts.port);
if (host == NULL || hostport == NULL) {
ssh_set_error_oom(session);
SAFE_FREE(host);
SAFE_FREE(hostport);
leave_function();
return SSH_SERVER_ERROR;
}
do {
tokens = ssh_get_knownhost_line(session,
&file,
session->opts.knownhosts,
&type);
/* End of file, return the current state */
if (tokens == NULL) {
break;
}
match = match_hashed_host(session, host, tokens[0]);
if (match == 0){
match = match_hostname(hostport, tokens[0], strlen(tokens[0]));
}
if (match == 0) {
match = match_hostname(host, tokens[0], strlen(tokens[0]));
}
if (match == 0) {
match = match_hashed_host(session, hostport, tokens[0]);
}
if (match) {
/* We got a match. Now check the key type */
if (strcmp(session->current_crypto->server_pubkey_type, type) != 0) {
ssh_log(session,
SSH_LOG_PACKET,
"ssh_is_server_known: server type [%s] doesn't match the "
"type [%s] in known_hosts file",
session->current_crypto->server_pubkey_type,
type);
/* Different type. We don't override the known_changed error which is
* more important */
if (ret != SSH_SERVER_KNOWN_CHANGED)
ret = SSH_SERVER_FOUND_OTHER;
tokens_free(tokens);
continue;
}
/* so we know the key type is good. We may get a good key or a bad key. */
match = check_public_key(session, tokens);
tokens_free(tokens);
if (match < 0) {
ret = SSH_SERVER_ERROR;
break;
} else if (match == 1) {
ret = SSH_SERVER_KNOWN_OK;
break;
} else if(match == 0) {
/* We override the status with the wrong key state */
ret = SSH_SERVER_KNOWN_CHANGED;
}
} else {
tokens_free(tokens);
}
} while (1);
if ((ret == SSH_SERVER_NOT_KNOWN) &&
(session->opts.StrictHostKeyChecking == 0)) {
ssh_write_knownhost(session);
ret = SSH_SERVER_KNOWN_OK;
}
SAFE_FREE(host);
SAFE_FREE(hostport);
if (file != NULL) {
fclose(file);
}
/* Return the current state at end of file */
leave_function();
return ret;
}
/**
* @brief Write the current server as known in the known hosts file.
*
* This will create the known hosts file if it does not exist. You generaly use
* it when ssh_is_server_known() answered SSH_SERVER_NOT_KNOWN.
*
* @param[in] session The ssh session to use.
*
* @return SSH_OK on success, SSH_ERROR on error.
*/
int ssh_write_knownhost(ssh_session session) {
ssh_key key;
ssh_string pubkey_s;
char *b64_key;
char buffer[4096] = {0};
FILE *file;
char *dir;
char *host;
char *hostport;
int rc;
if (session->opts.host == NULL) {
ssh_set_error(session, SSH_FATAL,
"Can't write host in known hosts if the hostname isn't known");
return SSH_ERROR;
}
host = ssh_lowercase(session->opts.host);
/* If using a nonstandard port, save the host in the [host]:port format */
if(session->opts.port != 22) {
hostport = ssh_hostport(host, session->opts.port);
SAFE_FREE(host);
if (hostport == NULL) {
return SSH_ERROR;
}
host = hostport;
hostport = NULL;
}
if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) {
ssh_set_error(session, SSH_FATAL, "Can't find a known_hosts file");
SAFE_FREE(host);
return SSH_ERROR;
}
}
if (session->current_crypto==NULL) {
ssh_set_error(session, SSH_FATAL, "No current crypto context");
SAFE_FREE(host);
return SSH_ERROR;
}
pubkey_s = session->current_crypto->server_pubkey;
if (pubkey_s == NULL){
ssh_set_error(session, SSH_FATAL, "No public key present");
SAFE_FREE(host);
return SSH_ERROR;
}
/* Check if ~/.ssh exists and create it if not */
dir = ssh_dirname(session->opts.knownhosts);
if (dir == NULL) {
ssh_set_error(session, SSH_FATAL, "%s", strerror(errno));
SAFE_FREE(host);
return SSH_ERROR;
}
if (!ssh_file_readaccess_ok(dir)) {
if (ssh_mkdir(dir, 0700) < 0) {
ssh_set_error(session, SSH_FATAL,
"Cannot create %s directory.", dir);
SAFE_FREE(dir);
SAFE_FREE(host);
return SSH_ERROR;
}
}
SAFE_FREE(dir);
file = fopen(session->opts.knownhosts, "a");
if (file == NULL) {
ssh_set_error(session, SSH_FATAL,
"Couldn't open known_hosts file %s for appending: %s",
session->opts.knownhosts, strerror(errno));
SAFE_FREE(host);
return SSH_ERROR;
}
rc = ssh_pki_import_pubkey_blob(pubkey_s, &key);
if (rc < 0) {
fclose(file);
SAFE_FREE(host);
return -1;
}
if (strcmp(session->current_crypto->server_pubkey_type, "ssh-rsa1") == 0) {
/* openssh uses a different format for ssh-rsa1 keys.
Be compatible --kv */
rc = ssh_pki_export_pubkey_rsa1(key, host, buffer, sizeof(buffer));
ssh_key_free(key);
SAFE_FREE(host);
if (rc < 0) {
fclose(file);
return -1;
}
} else {
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
if (rc < 0) {
ssh_key_free(key);
fclose(file);
SAFE_FREE(host);
return -1;
}
snprintf(buffer, sizeof(buffer),
"%s %s %s\n",
host,
key->type_c,
b64_key);
ssh_key_free(key);
SAFE_FREE(host);
SAFE_FREE(b64_key);
}
if (fwrite(buffer, strlen(buffer), 1, file) != 1 || ferror(file)) {
fclose(file);
return -1;
}
fclose(file);
return 0;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

731
libssh/src/legacy.c Normal file
View file

@ -0,0 +1,731 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/** functions in that file are wrappers to the newly named functions. All
* of them are depreciated, but these wrapper will avoid breaking backward
* compatibility
*/
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <libssh/priv.h>
#include <libssh/session.h>
#include <libssh/server.h>
#include <libssh/buffer.h>
#include <libssh/pki.h>
#include "libssh/pki_priv.h"
#include <libssh/misc.h>
#include <libssh/keys.h>
#include "libssh/options.h"
/* AUTH FUNCTIONS */
int ssh_auth_list(ssh_session session) {
return ssh_userauth_list(session, NULL);
}
int ssh_userauth_offer_pubkey(ssh_session session, const char *username,
int type, ssh_string publickey)
{
ssh_key key;
int rc;
(void) type; /* unused */
rc = ssh_pki_import_pubkey_blob(publickey, &key);
if (rc < 0) {
ssh_set_error(session, SSH_FATAL, "Failed to convert public key");
return SSH_AUTH_ERROR;
}
rc = ssh_userauth_try_publickey(session, username, key);
ssh_key_free(key);
return rc;
}
int ssh_userauth_pubkey(ssh_session session,
const char *username,
ssh_string publickey,
ssh_private_key privatekey)
{
ssh_key key;
int rc;
(void) publickey; /* unused */
key = ssh_key_new();
if (key == NULL) {
return SSH_AUTH_ERROR;
}
key->type = privatekey->type;
key->type_c = ssh_key_type_to_char(key->type);
key->flags = SSH_KEY_FLAG_PRIVATE|SSH_KEY_FLAG_PUBLIC;
key->dsa = privatekey->dsa_priv;
key->rsa = privatekey->rsa_priv;
rc = ssh_userauth_publickey(session, username, key);
key->dsa = NULL;
key->rsa = NULL;
ssh_key_free(key);
return rc;
}
int ssh_userauth_autopubkey(ssh_session session, const char *passphrase) {
return ssh_userauth_publickey_auto(session, NULL, passphrase);
}
int ssh_userauth_privatekey_file(ssh_session session,
const char *username,
const char *filename,
const char *passphrase) {
char *pubkeyfile = NULL;
ssh_string pubkey = NULL;
ssh_private_key privkey = NULL;
int type = 0;
int rc = SSH_AUTH_ERROR;
size_t klen = strlen(filename) + 4 + 1;
enter_function();
pubkeyfile = malloc(klen);
if (pubkeyfile == NULL) {
ssh_set_error_oom(session);
leave_function();
return SSH_AUTH_ERROR;
}
snprintf(pubkeyfile, klen, "%s.pub", filename);
pubkey = publickey_from_file(session, pubkeyfile, &type);
if (pubkey == NULL) {
ssh_log(session, SSH_LOG_RARE, "Public key file %s not found. Trying to generate it.", pubkeyfile);
/* auto-detect the key type with type=0 */
privkey = privatekey_from_file(session, filename, 0, passphrase);
} else {
ssh_log(session, SSH_LOG_RARE, "Public key file %s loaded.", pubkeyfile);
privkey = privatekey_from_file(session, filename, type, passphrase);
}
if (privkey == NULL) {
goto error;
}
/* ssh_userauth_pubkey is responsible for taking care of null-pubkey */
rc = ssh_userauth_pubkey(session, username, pubkey, privkey);
privatekey_free(privkey);
error:
SAFE_FREE(pubkeyfile);
ssh_string_free(pubkey);
leave_function();
return rc;
}
/* BUFFER FUNCTIONS */
void buffer_free(ssh_buffer buffer){
ssh_buffer_free(buffer);
}
void *buffer_get(ssh_buffer buffer){
return ssh_buffer_get_begin(buffer);
}
uint32_t buffer_get_len(ssh_buffer buffer){
return ssh_buffer_get_len(buffer);
}
ssh_buffer buffer_new(void){
return ssh_buffer_new();
}
ssh_channel channel_accept_x11(ssh_channel channel, int timeout_ms){
return ssh_channel_accept_x11(channel, timeout_ms);
}
int channel_change_pty_size(ssh_channel channel,int cols,int rows){
return ssh_channel_change_pty_size(channel,cols,rows);
}
ssh_channel channel_forward_accept(ssh_session session, int timeout_ms){
return ssh_forward_accept(session,timeout_ms);
}
int channel_close(ssh_channel channel){
return ssh_channel_close(channel);
}
int channel_forward_cancel(ssh_session session, const char *address, int port){
return ssh_forward_cancel(session, address, port);
}
int channel_forward_listen(ssh_session session, const char *address,
int port, int *bound_port){
return ssh_forward_listen(session, address, port, bound_port);
}
void channel_free(ssh_channel channel){
ssh_channel_free(channel);
}
int channel_get_exit_status(ssh_channel channel){
return ssh_channel_get_exit_status(channel);
}
ssh_session channel_get_session(ssh_channel channel){
return ssh_channel_get_session(channel);
}
int channel_is_closed(ssh_channel channel){
return ssh_channel_is_closed(channel);
}
int channel_is_eof(ssh_channel channel){
return ssh_channel_is_eof(channel);
}
int channel_is_open(ssh_channel channel){
return ssh_channel_is_open(channel);
}
ssh_channel channel_new(ssh_session session){
return ssh_channel_new(session);
}
int channel_open_forward(ssh_channel channel, const char *remotehost,
int remoteport, const char *sourcehost, int localport){
return ssh_channel_open_forward(channel, remotehost, remoteport,
sourcehost,localport);
}
int channel_open_session(ssh_channel channel){
return ssh_channel_open_session(channel);
}
int channel_poll(ssh_channel channel, int is_stderr){
return ssh_channel_poll(channel, is_stderr);
}
int channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr){
return ssh_channel_read(channel, dest, count, is_stderr);
}
/*
* This function will completely be depreciated. The old implementation was not
* renamed.
* int channel_read_buffer(ssh_channel channel, ssh_buffer buffer, uint32_t count,
* int is_stderr);
*/
int channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count,
int is_stderr){
return ssh_channel_read_nonblocking(channel, dest, count, is_stderr);
}
int channel_request_env(ssh_channel channel, const char *name, const char *value){
return ssh_channel_request_env(channel, name, value);
}
int channel_request_exec(ssh_channel channel, const char *cmd){
return ssh_channel_request_exec(channel, cmd);
}
int channel_request_pty(ssh_channel channel){
return ssh_channel_request_pty(channel);
}
int channel_request_pty_size(ssh_channel channel, const char *term,
int cols, int rows){
return ssh_channel_request_pty_size(channel, term, cols, rows);
}
int channel_request_shell(ssh_channel channel){
return ssh_channel_request_shell(channel);
}
int channel_request_send_signal(ssh_channel channel, const char *signum){
return ssh_channel_request_send_signal(channel, signum);
}
int channel_request_sftp(ssh_channel channel){
return ssh_channel_request_sftp(channel);
}
int channel_request_subsystem(ssh_channel channel, const char *subsystem){
return ssh_channel_request_subsystem(channel, subsystem);
}
int channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
const char *cookie, int screen_number){
return ssh_channel_request_x11(channel, single_connection, protocol, cookie,
screen_number);
}
int channel_send_eof(ssh_channel channel){
return ssh_channel_send_eof(channel);
}
int channel_select(ssh_channel *readchans, ssh_channel *writechans, ssh_channel *exceptchans, struct
timeval * timeout){
return ssh_channel_select(readchans, writechans, exceptchans, timeout);
}
void channel_set_blocking(ssh_channel channel, int blocking){
ssh_channel_set_blocking(channel, blocking);
}
int channel_write(ssh_channel channel, const void *data, uint32_t len){
return ssh_channel_write(channel, data, len);
}
/*
* These functions have to be wrapped around the pki.c functions.
void privatekey_free(ssh_private_key prv);
ssh_private_key privatekey_from_file(ssh_session session, const char *filename,
int type, const char *passphrase);
int ssh_publickey_to_file(ssh_session session, const char *file,
ssh_string pubkey, int type);
ssh_string publickey_to_string(ssh_public_key key);
*
*/
void string_burn(ssh_string str){
ssh_string_burn(str);
}
ssh_string string_copy(ssh_string str){
return ssh_string_copy(str);
}
void *string_data(ssh_string str){
return ssh_string_data(str);
}
int string_fill(ssh_string str, const void *data, size_t len){
return ssh_string_fill(str,data,len);
}
void string_free(ssh_string str){
ssh_string_free(str);
}
ssh_string string_from_char(const char *what){
return ssh_string_from_char(what);
}
size_t string_len(ssh_string str){
return ssh_string_len(str);
}
ssh_string string_new(size_t size){
return ssh_string_new(size);
}
char *string_to_char(ssh_string str){
return ssh_string_to_char(str);
}
/* OLD PKI FUNCTIONS */
void publickey_free(ssh_public_key key) {
if (key == NULL) {
return;
}
switch(key->type) {
case SSH_KEYTYPE_DSS:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(key->dsa_pub);
#elif HAVE_LIBCRYPTO
DSA_free(key->dsa_pub);
#endif
break;
case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1:
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(key->rsa_pub);
#elif defined HAVE_LIBCRYPTO
RSA_free(key->rsa_pub);
#endif
break;
default:
break;
}
SAFE_FREE(key);
}
ssh_public_key publickey_from_privatekey(ssh_private_key prv) {
struct ssh_public_key_struct *p;
ssh_key privkey;
ssh_key pubkey;
int rc;
privkey = ssh_key_new();
if (privkey == NULL) {
return NULL;
}
privkey->type = prv->type;
privkey->type_c = ssh_key_type_to_char(privkey->type);
privkey->flags = SSH_KEY_FLAG_PRIVATE | SSH_KEY_FLAG_PUBLIC;
privkey->dsa = prv->dsa_priv;
privkey->rsa = prv->rsa_priv;
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
privkey->dsa = NULL;
privkey->rsa = NULL;
ssh_key_free(privkey);
if (rc < 0) {
return NULL;
}
p = ssh_pki_convert_key_to_publickey(pubkey);
ssh_key_free(pubkey);
return p;
}
ssh_private_key privatekey_from_file(ssh_session session,
const char *filename,
int type,
const char *passphrase) {
ssh_auth_callback auth_fn = NULL;
void *auth_data = NULL;
ssh_private_key privkey;
ssh_key key;
int rc;
(void) type; /* unused */
if (session->common.callbacks) {
auth_fn = session->common.callbacks->auth_function;
auth_data = session->common.callbacks->userdata;
}
rc = ssh_pki_import_privkey_file(filename,
passphrase,
auth_fn,
auth_data,
&key);
if (rc == SSH_ERROR) {
return NULL;
}
privkey = malloc(sizeof(struct ssh_private_key_struct));
if (privkey == NULL) {
ssh_key_free(key);
return NULL;
}
privkey->type = key->type;
privkey->dsa_priv = key->dsa;
privkey->rsa_priv = key->rsa;
key->dsa = NULL;
key->rsa = NULL;
ssh_key_free(key);
return privkey;
}
enum ssh_keytypes_e ssh_privatekey_type(ssh_private_key privatekey){
if (privatekey==NULL)
return SSH_KEYTYPE_UNKNOWN;
return privatekey->type;
}
void privatekey_free(ssh_private_key prv) {
if (prv == NULL) {
return;
}
#ifdef HAVE_LIBGCRYPT
gcry_sexp_release(prv->dsa_priv);
gcry_sexp_release(prv->rsa_priv);
#elif defined HAVE_LIBCRYPTO
DSA_free(prv->dsa_priv);
RSA_free(prv->rsa_priv);
#endif
memset(prv, 0, sizeof(struct ssh_private_key_struct));
SAFE_FREE(prv);
}
ssh_string publickey_from_file(ssh_session session, const char *filename,
int *type) {
ssh_key key;
ssh_string key_str = NULL;
int rc;
(void) session; /* unused */
rc = ssh_pki_import_pubkey_file(filename, &key);
if (rc < 0) {
return NULL;
}
rc = ssh_pki_export_pubkey_blob(key, &key_str);
if (rc < 0) {
ssh_key_free(key);
return NULL;
}
if (type) {
*type = key->type;
}
ssh_key_free(key);
return key_str;
}
const char *ssh_type_to_char(int type) {
return ssh_key_type_to_char(type);
}
int ssh_type_from_name(const char *name) {
return ssh_key_type_from_name(name);
}
ssh_public_key publickey_from_string(ssh_session session, ssh_string pubkey_s) {
struct ssh_public_key_struct *pubkey;
ssh_key key;
int rc;
(void) session; /* unused */
rc = ssh_pki_import_pubkey_blob(pubkey_s, &key);
if (rc < 0) {
return NULL;
}
pubkey = malloc(sizeof(struct ssh_public_key_struct));
if (pubkey == NULL) {
ssh_key_free(key);
return NULL;
}
pubkey->type = key->type;
pubkey->type_c = key->type_c;
pubkey->dsa_pub = key->dsa;
key->dsa = NULL;
pubkey->rsa_pub = key->rsa;
key->rsa = NULL;
ssh_key_free(key);
return pubkey;
}
ssh_string publickey_to_string(ssh_public_key pubkey) {
ssh_key key;
ssh_string key_blob;
int rc;
key = ssh_key_new();
if (key == NULL) {
return NULL;
}
key->type = pubkey->type;
key->type_c = pubkey->type_c;
key->dsa = pubkey->dsa_pub;
key->rsa = pubkey->rsa_pub;
rc = ssh_pki_export_pubkey_blob(key, &key_blob);
if (rc < 0) {
key_blob = NULL;
}
key->dsa = NULL;
key->rsa = NULL;
ssh_key_free(key);
return key_blob;
}
int ssh_publickey_to_file(ssh_session session,
const char *file,
ssh_string pubkey,
int type)
{
FILE *fp;
char *user;
char buffer[1024];
char host[256];
unsigned char *pubkey_64;
size_t len;
int rc;
if(session==NULL)
return SSH_ERROR;
if(file==NULL || pubkey==NULL){
ssh_set_error(session, SSH_FATAL, "Invalid parameters");
return SSH_ERROR;
}
pubkey_64 = bin_to_base64(string_data(pubkey), ssh_string_len(pubkey));
if (pubkey_64 == NULL) {
return SSH_ERROR;
}
user = ssh_get_local_username();
if (user == NULL) {
SAFE_FREE(pubkey_64);
return SSH_ERROR;
}
rc = gethostname(host, sizeof(host));
if (rc < 0) {
SAFE_FREE(user);
SAFE_FREE(pubkey_64);
return SSH_ERROR;
}
snprintf(buffer, sizeof(buffer), "%s %s %s@%s\n",
ssh_type_to_char(type),
pubkey_64,
user,
host);
SAFE_FREE(pubkey_64);
SAFE_FREE(user);
ssh_log(session, SSH_LOG_RARE, "Trying to write public key file: %s", file);
ssh_log(session, SSH_LOG_PACKET, "public key file content: %s", buffer);
fp = fopen(file, "w+");
if (fp == NULL) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Error opening %s: %s", file, strerror(errno));
return SSH_ERROR;
}
len = strlen(buffer);
if (fwrite(buffer, len, 1, fp) != 1 || ferror(fp)) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Unable to write to %s", file);
fclose(fp);
unlink(file);
return SSH_ERROR;
}
fclose(fp);
return SSH_OK;
}
int ssh_try_publickey_from_file(ssh_session session,
const char *keyfile,
ssh_string *publickey,
int *type) {
char *pubkey_file;
size_t len;
ssh_string pubkey_string;
int pubkey_type;
if (session == NULL || keyfile == NULL || publickey == NULL || type == NULL) {
return -1;
}
if (session->opts.sshdir == NULL) {
if (ssh_options_apply(session) < 0) {
return -1;
}
}
ssh_log(session, SSH_LOG_PACKET, "Trying to open privatekey %s", keyfile);
if (!ssh_file_readaccess_ok(keyfile)) {
ssh_log(session, SSH_LOG_PACKET, "Failed to open privatekey %s", keyfile);
return -1;
}
len = strlen(keyfile) + 5;
pubkey_file = malloc(len);
if (pubkey_file == NULL) {
return -1;
}
snprintf(pubkey_file, len, "%s.pub", keyfile);
ssh_log(session, SSH_LOG_PACKET, "Trying to open publickey %s",
pubkey_file);
if (!ssh_file_readaccess_ok(pubkey_file)) {
ssh_log(session, SSH_LOG_PACKET, "Failed to open publickey %s",
pubkey_file);
SAFE_FREE(pubkey_file);
return 1;
}
ssh_log(session, SSH_LOG_PACKET, "Success opening public and private key");
/*
* We are sure both the private and public key file is readable. We return
* the public as a string, and the private filename as an argument
*/
pubkey_string = publickey_from_file(session, pubkey_file, &pubkey_type);
if (pubkey_string == NULL) {
ssh_log(session, SSH_LOG_PACKET,
"Wasn't able to open public key file %s: %s",
pubkey_file,
ssh_get_error(session));
SAFE_FREE(pubkey_file);
return -1;
}
SAFE_FREE(pubkey_file);
*publickey = pubkey_string;
*type = pubkey_type;
return 0;
}
ssh_string ssh_get_pubkey(ssh_session session){
if(session==NULL || session->current_crypto ==NULL ||
session->current_crypto->server_pubkey==NULL)
return NULL;
else
return ssh_string_copy(session->current_crypto->server_pubkey);
}
/****************************************************************************
* SERVER SUPPORT
****************************************************************************/
#ifdef WITH_SERVER
int ssh_accept(ssh_session session) {
return ssh_handle_key_exchange(session);
}
int channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
return ssh_channel_write(channel, data, len);
}
/** @deprecated
* @brief Interface previously exported by error.
*/
ssh_message ssh_message_retrieve(ssh_session session, uint32_t packettype){
(void) packettype;
ssh_set_error(session, SSH_FATAL, "ssh_message_retrieve: obsolete libssh call");
return NULL;
}
#endif /* WITH_SERVER */

600
libssh/src/libcrypto.c Normal file
View file

@ -0,0 +1,600 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/wrapper.h"
#include "libssh/libcrypto.h"
#ifdef HAVE_LIBCRYPTO
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/hmac.h>
#include <openssl/opensslv.h>
#ifdef HAVE_OPENSSL_AES_H
#define HAS_AES
#include <openssl/aes.h>
#endif
#ifdef HAVE_OPENSSL_BLOWFISH_H
#define HAS_BLOWFISH
#include <openssl/blowfish.h>
#endif
#ifdef HAVE_OPENSSL_DES_H
#define HAS_DES
#include <openssl/des.h>
#endif
#if (OPENSSL_VERSION_NUMBER<0x00907000L)
#define OLD_CRYPTO
#endif
#include "libssh/crypto.h"
struct ssh_mac_ctx_struct {
enum ssh_mac_e mac_type;
union {
SHACTX sha1_ctx;
SHA256CTX sha256_ctx;
} ctx;
};
static int alloc_key(struct ssh_cipher_struct *cipher) {
cipher->key = malloc(cipher->keylen);
if (cipher->key == NULL) {
return -1;
}
return 0;
}
SHACTX sha1_init(void) {
SHACTX c = malloc(sizeof(*c));
if (c == NULL) {
return NULL;
}
SHA1_Init(c);
return c;
}
void sha1_update(SHACTX c, const void *data, unsigned long len) {
SHA1_Update(c,data,len);
}
void sha1_final(unsigned char *md, SHACTX c) {
SHA1_Final(md, c);
SAFE_FREE(c);
}
void sha1(unsigned char *digest, int len, unsigned char *hash) {
SHA1(digest, len, hash);
}
#ifdef HAVE_OPENSSL_ECC
static const EVP_MD *nid_to_evpmd(int nid)
{
switch (nid) {
case NID_X9_62_prime256v1:
return EVP_sha256();
case NID_secp384r1:
return EVP_sha384();
case NID_secp521r1:
return EVP_sha512();
default:
return NULL;
}
return NULL;
}
void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen)
{
const EVP_MD *evp_md = nid_to_evpmd(nid);
EVP_MD_CTX md;
EVP_DigestInit(&md, evp_md);
EVP_DigestUpdate(&md, digest, len);
EVP_DigestFinal(&md, hash, hlen);
}
#endif
SHA256CTX sha256_init(void){
SHA256CTX c = malloc(sizeof(*c));
if (c == NULL) {
return NULL;
}
SHA256_Init(c);
return c;
}
void sha256_update(SHA256CTX c, const void *data, unsigned long len){
SHA256_Update(c,data,len);
}
void sha256_final(unsigned char *md, SHA256CTX c) {
SHA256_Final(md, c);
SAFE_FREE(c);
}
void sha256(unsigned char *digest, int len, unsigned char *hash) {
SHA256(digest, len, hash);
}
MD5CTX md5_init(void) {
MD5CTX c = malloc(sizeof(*c));
if (c == NULL) {
return NULL;
}
MD5_Init(c);
return c;
}
void md5_update(MD5CTX c, const void *data, unsigned long len) {
MD5_Update(c, data, len);
}
void md5_final(unsigned char *md, MD5CTX c) {
MD5_Final(md,c);
SAFE_FREE(c);
}
ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){
ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct));
ctx->mac_type=type;
switch(type){
case SSH_MAC_SHA1:
ctx->ctx.sha1_ctx = sha1_init();
return ctx;
case SSH_MAC_SHA256:
ctx->ctx.sha256_ctx = sha256_init();
return ctx;
case SSH_MAC_SHA384:
case SSH_MAC_SHA512:
default:
SAFE_FREE(ctx);
return NULL;
}
}
void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) {
switch(ctx->mac_type){
case SSH_MAC_SHA1:
sha1_update(ctx->ctx.sha1_ctx, data, len);
break;
case SSH_MAC_SHA256:
sha256_update(ctx->ctx.sha256_ctx, data, len);
break;
case SSH_MAC_SHA384:
case SSH_MAC_SHA512:
default:
break;
}
}
void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) {
switch(ctx->mac_type){
case SSH_MAC_SHA1:
sha1_final(md,ctx->ctx.sha1_ctx);
break;
case SSH_MAC_SHA256:
sha256_final(md,ctx->ctx.sha256_ctx);
break;
case SSH_MAC_SHA384:
case SSH_MAC_SHA512:
default:
break;
}
SAFE_FREE(ctx);
}
HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
HMACCTX ctx = NULL;
ctx = malloc(sizeof(*ctx));
if (ctx == NULL) {
return NULL;
}
#ifndef OLD_CRYPTO
HMAC_CTX_init(ctx); // openssl 0.9.7 requires it.
#endif
switch(type) {
case SSH_HMAC_SHA1:
HMAC_Init(ctx, key, len, EVP_sha1());
break;
case SSH_HMAC_MD5:
HMAC_Init(ctx, key, len, EVP_md5());
break;
default:
SAFE_FREE(ctx);
ctx = NULL;
}
return ctx;
}
void hmac_update(HMACCTX ctx, const void *data, unsigned long len) {
HMAC_Update(ctx, data, len);
}
void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) {
HMAC_Final(ctx,hashmacbuf,len);
#ifndef OLD_CRYPTO
HMAC_CTX_cleanup(ctx);
#else
HMAC_cleanup(ctx);
#endif
SAFE_FREE(ctx);
}
#ifdef HAS_BLOWFISH
/* the wrapper functions for blowfish */
static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
BF_set_key(cipher->key, 16, key);
}
cipher->IV = IV;
return 0;
}
static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_ENCRYPT);
}
static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_DECRYPT);
}
#endif /* HAS_BLOWFISH */
#ifdef HAS_AES
static int aes_set_encrypt_key(struct ssh_cipher_struct *cipher, void *key,
void *IV) {
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
if (AES_set_encrypt_key(key,cipher->keysize,cipher->key) < 0) {
SAFE_FREE(cipher->key);
return -1;
}
}
cipher->IV=IV;
return 0;
}
static int aes_set_decrypt_key(struct ssh_cipher_struct *cipher, void *key,
void *IV) {
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
if (AES_set_decrypt_key(key,cipher->keysize,cipher->key) < 0) {
SAFE_FREE(cipher->key);
return -1;
}
}
cipher->IV=IV;
return 0;
}
static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len) {
AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_ENCRYPT);
}
static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len) {
AES_cbc_encrypt(in, out, len, cipher->key, cipher->IV, AES_DECRYPT);
}
#ifndef BROKEN_AES_CTR
/* OpenSSL until 0.9.7c has a broken AES_ctr128_encrypt implementation which
* increments the counter from 2^64 instead of 1. It's better not to use it
*/
/** @internal
* @brief encrypts/decrypts data with stream cipher AES_ctr128. 128 bits is actually
* the size of the CTR counter and incidentally the blocksize, but not the keysize.
* @param len[in] must be a multiple of AES128 block size.
*/
static void aes_ctr128_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len) {
unsigned char tmp_buffer[128/8];
unsigned int num=0;
/* Some things are special with ctr128 :
* In this case, tmp_buffer is not being used, because it is used to store temporary data
* when an encryption is made on lengths that are not multiple of blocksize.
* Same for num, which is being used to store the current offset in blocksize in CTR
* function.
*/
AES_ctr128_encrypt(in, out, len, cipher->key, cipher->IV, tmp_buffer, &num);
}
#endif /* BROKEN_AES_CTR */
#endif /* HAS_AES */
#ifdef HAS_DES
static int des3_set_key(struct ssh_cipher_struct *cipher, void *key,void *IV) {
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
DES_set_odd_parity(key);
DES_set_odd_parity((void*)((uint8_t*)key + 8));
DES_set_odd_parity((void*)((uint8_t*)key + 16));
DES_set_key_unchecked(key, cipher->key);
DES_set_key_unchecked((void*)((uint8_t*)key + 8), (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)));
DES_set_key_unchecked((void*)((uint8_t*)key + 16), (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)));
}
cipher->IV=IV;
return 0;
}
static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
DES_ede3_cbc_encrypt(in, out, len, cipher->key,
(void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
(void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
cipher->IV, 1);
}
static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
DES_ede3_cbc_encrypt(in, out, len, cipher->key,
(void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
(void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
cipher->IV, 0);
}
static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Encrypt IV before", cipher->IV, 24);
#endif
DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1);
DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
(void*)((uint8_t*)cipher->IV + 8), 0);
DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
(void*)((uint8_t*)cipher->IV + 16), 1);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Encrypt IV after", cipher->IV, 24);
#endif
}
static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Decrypt IV before", cipher->IV, 24);
#endif
DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
cipher->IV, 0);
DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
(void*)((uint8_t*)cipher->IV + 8), 1);
DES_ncbc_encrypt(in, out, len, cipher->key, (void*)((uint8_t*)cipher->IV + 16), 0);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Decrypt IV after", cipher->IV, 24);
#endif
}
static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){
if(!cipher->key){
if (alloc_key(cipher) < 0) {
return -1;
}
DES_set_odd_parity(key);
DES_set_key_unchecked(key,cipher->key);
}
cipher->IV=IV;
return 0;
}
static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len){
DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1);
}
static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len){
DES_ncbc_encrypt(in,out,len, cipher->key, cipher->IV, 0);
}
#endif /* HAS_DES */
/*
* The table of supported ciphers
*
* WARNING: If you modify ssh_cipher_struct, you must make sure the order is
* correct!
*/
static struct ssh_cipher_struct ssh_ciphertab[] = {
#ifdef HAS_BLOWFISH
{
"blowfish-cbc",
8,
sizeof (BF_KEY),
NULL,
NULL,
128,
blowfish_set_key,
blowfish_set_key,
blowfish_encrypt,
blowfish_decrypt
},
#endif /* HAS_BLOWFISH */
#ifdef HAS_AES
#ifndef BROKEN_AES_CTR
{
"aes128-ctr",
16,
sizeof(AES_KEY),
NULL,
NULL,
128,
aes_set_encrypt_key,
aes_set_encrypt_key,
aes_ctr128_encrypt,
aes_ctr128_encrypt
},
{
"aes192-ctr",
16,
sizeof(AES_KEY),
NULL,
NULL,
192,
aes_set_encrypt_key,
aes_set_encrypt_key,
aes_ctr128_encrypt,
aes_ctr128_encrypt
},
{
"aes256-ctr",
16,
sizeof(AES_KEY),
NULL,
NULL,
256,
aes_set_encrypt_key,
aes_set_encrypt_key,
aes_ctr128_encrypt,
aes_ctr128_encrypt
},
#endif /* BROKEN_AES_CTR */
{
"aes128-cbc",
16,
sizeof(AES_KEY),
NULL,
NULL,
128,
aes_set_encrypt_key,
aes_set_decrypt_key,
aes_encrypt,
aes_decrypt
},
{
"aes192-cbc",
16,
sizeof(AES_KEY),
NULL,
NULL,
192,
aes_set_encrypt_key,
aes_set_decrypt_key,
aes_encrypt,
aes_decrypt
},
{
"aes256-cbc",
16,
sizeof(AES_KEY),
NULL,
NULL,
256,
aes_set_encrypt_key,
aes_set_decrypt_key,
aes_encrypt,
aes_decrypt
},
#endif /* HAS_AES */
#ifdef HAS_DES
{
"3des-cbc",
8,
sizeof(DES_key_schedule) * 3,
NULL,
NULL,
192,
des3_set_key,
des3_set_key,
des3_encrypt,
des3_decrypt
},
{
"3des-cbc-ssh1",
8,
sizeof(DES_key_schedule) * 3,
NULL,
NULL,
192,
des3_set_key,
des3_set_key,
des3_1_encrypt,
des3_1_decrypt
},
{
"des-cbc-ssh1",
8,
sizeof(DES_key_schedule),
NULL,
NULL,
64,
des1_set_key,
des1_set_key,
des1_1_encrypt,
des1_1_decrypt
},
#endif /* HAS_DES */
{
NULL,
0,
0,
NULL,
NULL,
0,
NULL,
NULL,
NULL,
NULL
}
};
struct ssh_cipher_struct *ssh_get_ciphertab(void)
{
return ssh_ciphertab;
}
#endif /* LIBCRYPTO */

526
libssh/src/libgcrypt.c Normal file
View file

@ -0,0 +1,526 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/wrapper.h"
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
struct ssh_mac_ctx_struct {
enum ssh_mac_e mac_type;
gcry_md_hd_t ctx;
};
static int alloc_key(struct ssh_cipher_struct *cipher) {
cipher->key = malloc(cipher->keylen);
if (cipher->key == NULL) {
return -1;
}
return 0;
}
SHACTX sha1_init(void) {
SHACTX ctx = NULL;
gcry_md_open(&ctx, GCRY_MD_SHA1, 0);
return ctx;
}
void sha1_update(SHACTX c, const void *data, unsigned long len) {
gcry_md_write(c, data, len);
}
void sha1_final(unsigned char *md, SHACTX c) {
gcry_md_final(c);
memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN);
gcry_md_close(c);
}
void sha1(unsigned char *digest, int len, unsigned char *hash) {
gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len);
}
void sha256(unsigned char *digest, int len, unsigned char *hash){
gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len);
}
MD5CTX md5_init(void) {
MD5CTX c = NULL;
gcry_md_open(&c, GCRY_MD_MD5, 0);
return c;
}
void md5_update(MD5CTX c, const void *data, unsigned long len) {
gcry_md_write(c,data,len);
}
void md5_final(unsigned char *md, MD5CTX c) {
gcry_md_final(c);
memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN);
gcry_md_close(c);
}
ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){
ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct));
ctx->mac_type=type;
switch(type){
case SSH_MAC_SHA1:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0);
break;
case SSH_MAC_SHA256:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0);
break;
case SSH_MAC_SHA384:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0);
break;
case SSH_MAC_SHA512:
gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0);
break;
default:
SAFE_FREE(ctx);
return NULL;
}
return ctx;
}
void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) {
gcry_md_write(ctx->ctx,data,len);
}
void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) {
size_t len;
switch(ctx->mac_type){
case SSH_MAC_SHA1:
len=SHA_DIGEST_LEN;
break;
case SSH_MAC_SHA256:
len=SHA256_DIGEST_LENGTH;
break;
case SSH_MAC_SHA384:
len=SHA384_DIGEST_LENGTH;
break;
case SSH_MAC_SHA512:
len=SHA512_DIGEST_LENGTH;
break;
}
gcry_md_final(ctx->ctx);
memcpy(md, gcry_md_read(ctx->ctx, 0), len);
gcry_md_close(ctx->ctx);
SAFE_FREE(ctx);
}
HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
HMACCTX c = NULL;
switch(type) {
case SSH_HMAC_SHA1:
gcry_md_open(&c, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC);
break;
case SSH_HMAC_MD5:
gcry_md_open(&c, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC);
break;
default:
c = NULL;
}
gcry_md_setkey(c, key, len);
return c;
}
void hmac_update(HMACCTX c, const void *data, unsigned long len) {
gcry_md_write(c, data, len);
}
void hmac_final(HMACCTX c, unsigned char *hashmacbuf, unsigned int *len) {
*len = gcry_md_get_algo_dlen(gcry_md_get_algo(c));
memcpy(hashmacbuf, gcry_md_read(c, 0), *len);
gcry_md_close(c);
}
/* the wrapper functions for blowfish */
static int blowfish_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_BLOWFISH,
GCRY_CIPHER_MODE_CBC, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setkey(cipher->key[0], key, 16)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setiv(cipher->key[0], IV, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
}
return 0;
}
static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
static int aes_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
int mode=GCRY_CIPHER_MODE_CBC;
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
if(strstr(cipher->name,"-ctr"))
mode=GCRY_CIPHER_MODE_CTR;
switch (cipher->keysize) {
case 128:
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES128,
mode, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
break;
case 192:
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES192,
mode, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
break;
case 256:
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_AES256,
mode, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
break;
}
if (gcry_cipher_setkey(cipher->key[0], key, cipher->keysize / 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if(mode == GCRY_CIPHER_MODE_CBC){
if (gcry_cipher_setiv(cipher->key[0], IV, 16)) {
SAFE_FREE(cipher->key);
return -1;
}
} else {
if(gcry_cipher_setctr(cipher->key[0],IV,16)){
SAFE_FREE(cipher->key);
return -1;
}
}
}
return 0;
}
static void aes_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void aes_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
unsigned long len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV){
if(!cipher->key){
if (alloc_key(cipher) < 0) {
return -1;
}
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES,
GCRY_CIPHER_MODE_CBC, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setkey(cipher->key[0], key, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setiv(cipher->key[0], IV, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
}
return 0;
}
static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_3DES,
GCRY_CIPHER_MODE_CBC, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setkey(cipher->key[0], key, 24)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setiv(cipher->key[0], IV, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
}
return 0;
}
static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
static void des3_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
}
static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
static int des3_1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV) {
if (cipher->key == NULL) {
if (alloc_key(cipher) < 0) {
return -1;
}
if (gcry_cipher_open(&cipher->key[0], GCRY_CIPHER_DES,
GCRY_CIPHER_MODE_CBC, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setkey(cipher->key[0], key, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setiv(cipher->key[0], IV, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_open(&cipher->key[1], GCRY_CIPHER_DES,
GCRY_CIPHER_MODE_CBC, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setkey(cipher->key[1], (unsigned char *)key + 8, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setiv(cipher->key[1], (unsigned char *)IV + 8, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_open(&cipher->key[2], GCRY_CIPHER_DES,
GCRY_CIPHER_MODE_CBC, 0)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setkey(cipher->key[2], (unsigned char *)key + 16, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
if (gcry_cipher_setiv(cipher->key[2], (unsigned char *)IV + 16, 8)) {
SAFE_FREE(cipher->key);
return -1;
}
}
return 0;
}
static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_encrypt(cipher->key[0], out, len, in, len);
gcry_cipher_decrypt(cipher->key[1], in, len, out, len);
gcry_cipher_encrypt(cipher->key[2], out, len, in, len);
}
static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in,
void *out, unsigned long len) {
gcry_cipher_decrypt(cipher->key[2], out, len, in, len);
gcry_cipher_encrypt(cipher->key[1], in, len, out, len);
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
/* the table of supported ciphers */
static struct ssh_cipher_struct ssh_ciphertab[] = {
{
.name = "blowfish-cbc",
.blocksize = 8,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 128,
.set_encrypt_key = blowfish_set_key,
.set_decrypt_key = blowfish_set_key,
.cbc_encrypt = blowfish_encrypt,
.cbc_decrypt = blowfish_decrypt
},
{
.name = "aes128-ctr",
.blocksize = 16,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 128,
.set_encrypt_key = aes_set_key,
.set_decrypt_key = aes_set_key,
.cbc_encrypt = aes_encrypt,
.cbc_decrypt = aes_encrypt
},
{
.name = "aes192-ctr",
.blocksize = 16,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 192,
.set_encrypt_key = aes_set_key,
.set_decrypt_key = aes_set_key,
.cbc_encrypt = aes_encrypt,
.cbc_decrypt = aes_encrypt
},
{
.name = "aes256-ctr",
.blocksize = 16,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 256,
.set_encrypt_key = aes_set_key,
.set_decrypt_key = aes_set_key,
.cbc_encrypt = aes_encrypt,
.cbc_decrypt = aes_encrypt
},
{
.name = "aes128-cbc",
.blocksize = 16,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 128,
.set_encrypt_key = aes_set_key,
.set_decrypt_key = aes_set_key,
.cbc_encrypt = aes_encrypt,
.cbc_decrypt = aes_decrypt
},
{
.name = "aes192-cbc",
.blocksize = 16,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 192,
.set_encrypt_key = aes_set_key,
.set_decrypt_key = aes_set_key,
.cbc_encrypt = aes_encrypt,
.cbc_decrypt = aes_decrypt
},
{
.name = "aes256-cbc",
.blocksize = 16,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 256,
.set_encrypt_key = aes_set_key,
.set_decrypt_key = aes_set_key,
.cbc_encrypt = aes_encrypt,
.cbc_decrypt = aes_decrypt
},
{
.name = "3des-cbc",
.blocksize = 8,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 192,
.set_encrypt_key = des3_set_key,
.set_decrypt_key = des3_set_key,
.cbc_encrypt = des3_encrypt,
.cbc_decrypt = des3_decrypt
},
{
.name = "3des-cbc-ssh1",
.blocksize = 8,
.keylen = sizeof(gcry_cipher_hd_t) * 3,
.key = NULL,
.keysize = 192,
.set_encrypt_key = des3_1_set_key,
.set_decrypt_key = des3_1_set_key,
.cbc_encrypt = des3_1_encrypt,
.cbc_decrypt = des3_1_decrypt
},
{
.name = "des-cbc-ssh1",
.blocksize = 8,
.keylen = sizeof(gcry_cipher_hd_t),
.key = NULL,
.keysize = 64,
.set_encrypt_key = des1_set_key,
.set_decrypt_key = des1_set_key,
.cbc_encrypt = des1_1_encrypt,
.cbc_decrypt = des1_1_decrypt
},
{
.name = NULL,
.blocksize = 0,
.keylen = 0,
.key = NULL,
.keysize = 0,
.set_encrypt_key = NULL,
.set_decrypt_key = NULL,
.cbc_encrypt = NULL,
.cbc_decrypt = NULL
}
};
struct ssh_cipher_struct *ssh_get_ciphertab(void)
{
return ssh_ciphertab;
}
#endif

152
libssh/src/log.c Normal file
View file

@ -0,0 +1,152 @@
/*
* log.c - logging and debugging functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef _WIN32
#include <sys/time.h>
#else
#include <sys/utime.h>
#endif
#include <time.h>
#include "libssh/priv.h"
#include "libssh/misc.h"
#include "libssh/session.h"
/**
* @defgroup libssh_log The SSH logging functions.
* @ingroup libssh
*
* Logging functions for debugging and problem resolving.
*
* @{
*/
static int current_timestring(int hires, char *buf, size_t len)
{
char tbuf[64];
struct timeval tv;
struct tm *tm;
time_t t;
gettimeofday(&tv, NULL);
t = (time_t) tv.tv_sec;
tm = localtime(&t);
if (tm == NULL) {
return -1;
}
if (hires) {
strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm);
snprintf(buf, len, "%s.%06ld", tbuf, tv.tv_usec);
} else {
strftime(tbuf, sizeof(tbuf) - 1, "%Y/%m/%d %H:%M:%S", tm);
snprintf(buf, len, "%s", tbuf);
}
return 0;
}
void ssh_log_function(int verbosity,
const char *function,
const char *buffer)
{
char date[64] = {0};
int rc;
rc = current_timestring(1, date, sizeof(date));
if (rc == 0) {
fprintf(stderr, "[%s, %d] %s", date, verbosity, function);
} else {
fprintf(stderr, "[%d] %s", verbosity, function);
}
fprintf(stderr, " %s\n", buffer);
}
/** @internal
* @brief do the actual work of logging an event
*/
static void do_ssh_log(struct ssh_common_struct *common,
int verbosity,
const char *function,
const char *buffer) {
if (common->callbacks && common->callbacks->log_function) {
char buf[1024];
snprintf(buf, sizeof(buf), "%s: %s", function, buffer);
common->callbacks->log_function((ssh_session)common,
verbosity,
buf,
common->callbacks->userdata);
return;
}
ssh_log_function(verbosity, function, buffer);
}
/* legacy function */
void ssh_log(ssh_session session,
int verbosity,
const char *format, ...)
{
char buffer[1024];
va_list va;
if (verbosity <= session->common.log_verbosity) {
va_start(va, format);
vsnprintf(buffer, sizeof(buffer), format, va);
va_end(va);
do_ssh_log(&session->common, verbosity, "", buffer);
}
}
/** @internal
* @brief log a SSH event with a common pointer
* @param common The SSH/bind session.
* @param verbosity The verbosity of the event.
* @param format The format string of the log entry.
*/
void ssh_log_common(struct ssh_common_struct *common,
int verbosity,
const char *function,
const char *format, ...)
{
char buffer[1024];
va_list va;
if (verbosity <= common->log_verbosity) {
va_start(va, format);
vsnprintf(buffer, sizeof(buffer), format, va);
va_end(va);
do_ssh_log(common, verbosity, function, buffer);
}
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

189
libssh/src/match.c Normal file
View file

@ -0,0 +1,189 @@
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
* Simple pattern matching, with '*' and '?' as wildcards.
*
* As far as I am concerned, the code I have written for this software
* can be used freely for any purpose. Any derived versions of this
* software must be clearly marked as such, and if the derived work is
* incompatible with the protocol description in the RFC file, it must be
* called by a name other than "ssh" or "Secure Shell".
*/
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <ctype.h>
#include <string.h>
#include <sys/types.h>
#include "libssh/priv.h"
/*
* Returns true if the given string matches the pattern (which may contain ?
* and * as wildcards), and zero if it does not match.
*/
static int match_pattern(const char *s, const char *pattern) {
if (s == NULL || pattern == NULL) {
return 0;
}
for (;;) {
/* If at end of pattern, accept if also at end of string. */
if (*pattern == '\0') {
return (*s == '\0');
}
if (*pattern == '*') {
/* Skip the asterisk. */
pattern++;
/* If at end of pattern, accept immediately. */
if (!*pattern)
return 1;
/* If next character in pattern is known, optimize. */
if (*pattern != '?' && *pattern != '*') {
/*
* Look instances of the next character in
* pattern, and try to match starting from
* those.
*/
for (; *s; s++)
if (*s == *pattern && match_pattern(s + 1, pattern + 1)) {
return 1;
}
/* Failed. */
return 0;
}
/*
* Move ahead one character at a time and try to
* match at each position.
*/
for (; *s; s++) {
if (match_pattern(s, pattern)) {
return 1;
}
}
/* Failed. */
return 0;
}
/*
* There must be at least one more character in the string.
* If we are at the end, fail.
*/
if (!*s) {
return 0;
}
/* Check if the next character of the string is acceptable. */
if (*pattern != '?' && *pattern != *s) {
return 0;
}
/* Move to the next character, both in string and in pattern. */
s++;
pattern++;
}
/* NOTREACHED */
}
/*
* Tries to match the string against the comma-separated sequence of subpatterns
* (each possibly preceded by ! to indicate negation).
* Returns -1 if negation matches, 1 if there is a positive match, 0 if there is
* no match at all.
*/
static int match_pattern_list(const char *string, const char *pattern,
unsigned int len, int dolower) {
char sub[1024];
int negated;
int got_positive;
unsigned int i, subi;
got_positive = 0;
for (i = 0; i < len;) {
/* Check if the subpattern is negated. */
if (pattern[i] == '!') {
negated = 1;
i++;
} else {
negated = 0;
}
/*
* Extract the subpattern up to a comma or end. Convert the
* subpattern to lowercase.
*/
for (subi = 0;
i < len && subi < sizeof(sub) - 1 && pattern[i] != ',';
subi++, i++) {
sub[subi] = dolower && isupper(pattern[i]) ?
(char)tolower(pattern[i]) : pattern[i];
}
/* If subpattern too long, return failure (no match). */
if (subi >= sizeof(sub) - 1) {
return 0;
}
/* If the subpattern was terminated by a comma, skip the comma. */
if (i < len && pattern[i] == ',') {
i++;
}
/* Null-terminate the subpattern. */
sub[subi] = '\0';
/* Try to match the subpattern against the string. */
if (match_pattern(string, sub)) {
if (negated) {
return -1; /* Negative */
} else {
got_positive = 1; /* Positive */
}
}
}
/*
* Return success if got a positive match. If there was a negative
* match, we have already returned -1 and never get here.
*/
return got_positive;
}
/*
* Tries to match the host name (which must be in all lowercase) against the
* comma-separated sequence of subpatterns (each possibly preceded by ! to
* indicate negation).
* Returns -1 if negation matches, 1 if there is a positive match, 0 if there
* is no match at all.
*/
int match_hostname(const char *host, const char *pattern, unsigned int len) {
return match_pattern_list(host, pattern, len, 1);
}
/* vim: set ts=2 sw=2 et cindent: */

1319
libssh/src/messages.c Normal file

File diff suppressed because it is too large Load diff

1036
libssh/src/misc.c Normal file

File diff suppressed because it is too large Load diff

1424
libssh/src/options.c Normal file

File diff suppressed because it is too large Load diff

531
libssh/src/packet.c Normal file
View file

@ -0,0 +1,531 @@
/*
* packet.c - packet building functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/ssh2.h"
#include "libssh/crypto.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/socket.h"
#include "libssh/channels.h"
#include "libssh/misc.h"
#include "libssh/session.h"
#include "libssh/messages.h"
#include "libssh/pcap.h"
#include "libssh/kex.h"
#include "libssh/auth.h"
#define MACSIZE SHA_DIGEST_LEN
static ssh_packet_callback default_packet_handlers[]= {
ssh_packet_disconnect_callback, // SSH2_MSG_DISCONNECT 1
ssh_packet_ignore_callback, // SSH2_MSG_IGNORE 2
ssh_packet_unimplemented, // SSH2_MSG_UNIMPLEMENTED 3
ssh_packet_ignore_callback, // SSH2_MSG_DEBUG 4
ssh_packet_service_request, // SSH2_MSG_SERVICE_REQUEST 5
ssh_packet_service_accept, // SSH2_MSG_SERVICE_ACCEPT 6
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, // 7-19
ssh_packet_kexinit, // SSH2_MSG_KEXINIT 20
ssh_packet_newkeys, // SSH2_MSG_NEWKEYS 21
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, // 22-29
#if WITH_SERVER
ssh_packet_kexdh_init, // SSH2_MSG_KEXDH_INIT 30
// SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30
#else
NULL,
#endif
ssh_packet_dh_reply, // SSH2_MSG_KEXDH_REPLY 31
// SSH2_MSG_KEX_DH_GEX_GROUP 31
NULL, // SSH2_MSG_KEX_DH_GEX_INIT 32
NULL, // SSH2_MSG_KEX_DH_GEX_REPLY 33
NULL, // SSH2_MSG_KEX_DH_GEX_REQUEST 34
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, // 35-49
ssh_packet_userauth_request, // SSH2_MSG_USERAUTH_REQUEST 50
ssh_packet_userauth_failure, // SSH2_MSG_USERAUTH_FAILURE 51
ssh_packet_userauth_success, // SSH2_MSG_USERAUTH_SUCCESS 52
ssh_packet_userauth_banner, // SSH2_MSG_USERAUTH_BANNER 53
NULL,NULL,NULL,NULL,NULL,NULL, // 54-59
ssh_packet_userauth_pk_ok, // SSH2_MSG_USERAUTH_PK_OK 60
// SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
// SSH2_MSG_USERAUTH_INFO_REQUEST 60
ssh_packet_userauth_info_response, // SSH2_MSG_USERAUTH_INFO_RESPONSE 61
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, // 62-79
#ifdef WITH_SERVER
ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80
#else /* WITH_SERVER */
NULL,
#endif /* WITH_SERVER */
ssh_request_success, // SSH2_MSG_REQUEST_SUCCESS 81
ssh_request_denied, // SSH2_MSG_REQUEST_FAILURE 82
NULL, NULL, NULL, NULL, NULL, NULL, NULL,// 83-89
ssh_packet_channel_open, // SSH2_MSG_CHANNEL_OPEN 90
ssh_packet_channel_open_conf, // SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
ssh_packet_channel_open_fail, // SSH2_MSG_CHANNEL_OPEN_FAILURE 92
channel_rcv_change_window, // SSH2_MSG_CHANNEL_WINDOW_ADJUST 93
channel_rcv_data, // SSH2_MSG_CHANNEL_DATA 94
channel_rcv_data, // SSH2_MSG_CHANNEL_EXTENDED_DATA 95
channel_rcv_eof, // SSH2_MSG_CHANNEL_EOF 96
channel_rcv_close, // SSH2_MSG_CHANNEL_CLOSE 97
channel_rcv_request, // SSH2_MSG_CHANNEL_REQUEST 98
ssh_packet_channel_success, // SSH2_MSG_CHANNEL_SUCCESS 99
ssh_packet_channel_failure, // SSH2_MSG_CHANNEL_FAILURE 100
};
/* in nonblocking mode, socket_read will read as much as it can, and return */
/* SSH_OK if it has read at least len bytes, otherwise, SSH_AGAIN. */
/* in blocking mode, it will read at least len bytes and will block until it's ok. */
/** @internal
* @handles a data received event. It then calls the handlers for the different packet types
* or and exception handler callback.
* @param user pointer to current ssh_session
* @param data pointer to the data received
* @len length of data received. It might not be enough for a complete packet
* @returns number of bytes read and processed.
*/
int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user){
ssh_session session=(ssh_session) user;
unsigned int blocksize = (session->current_crypto ?
session->current_crypto->in_cipher->blocksize : 8);
int current_macsize = session->current_crypto ? MACSIZE : 0;
unsigned char mac[30] = {0};
char buffer[16] = {0};
const void *packet = NULL;
int to_be_read;
int rc;
uint32_t len, compsize, payloadsize;
uint8_t padding;
size_t processed=0; /* number of byte processed from the callback */
if (data == NULL) {
goto error;
}
enter_function();
if (session->session_state == SSH_SESSION_STATE_ERROR)
goto error;
switch(session->packet_state) {
case PACKET_STATE_INIT:
if(receivedlen < blocksize){
/* We didn't receive enough data to read at least one block size, give up */
leave_function();
return 0;
}
memset(&session->in_packet, 0, sizeof(PACKET));
if (session->in_buffer) {
if (buffer_reinit(session->in_buffer) < 0) {
goto error;
}
} else {
session->in_buffer = ssh_buffer_new();
if (session->in_buffer == NULL) {
goto error;
}
}
memcpy(buffer,data,blocksize);
processed += blocksize;
len = packet_decrypt_len(session, buffer);
if (buffer_add_data(session->in_buffer, buffer, blocksize) < 0) {
goto error;
}
if(len > MAX_PACKET_LEN) {
ssh_set_error(session, SSH_FATAL,
"read_packet(): Packet len too high(%u %.4x)", len, len);
goto error;
}
to_be_read = len - blocksize + sizeof(uint32_t);
if (to_be_read < 0) {
/* remote sshd sends invalid sizes? */
ssh_set_error(session, SSH_FATAL,
"given numbers of bytes left to be read < 0 (%d)!", to_be_read);
goto error;
}
/* saves the status of the current operations */
session->in_packet.len = len;
session->packet_state = PACKET_STATE_SIZEREAD;
case PACKET_STATE_SIZEREAD:
len = session->in_packet.len;
to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize;
/* if to_be_read is zero, the whole packet was blocksize bytes. */
if (to_be_read != 0) {
if(receivedlen - processed < (unsigned int)to_be_read){
/* give up, not enough data in buffer */
ssh_log(session,SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len);
return processed;
}
packet = ((unsigned char *)data) + processed;
// ssh_socket_read(session->socket,packet,to_be_read-current_macsize);
if (buffer_add_data(session->in_buffer, packet,
to_be_read - current_macsize) < 0) {
goto error;
}
processed += to_be_read - current_macsize;
}
if (session->current_crypto) {
/*
* decrypt the rest of the packet (blocksize bytes already
* have been decrypted)
*/
if (packet_decrypt(session,
((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize),
buffer_get_rest_len(session->in_buffer) - blocksize) < 0) {
ssh_set_error(session, SSH_FATAL, "Decrypt error");
goto error;
}
/* copy the last part from the incoming buffer */
memcpy(mac,(unsigned char *)packet + to_be_read - current_macsize, MACSIZE);
if (packet_hmac_verify(session, session->in_buffer, mac) < 0) {
ssh_set_error(session, SSH_FATAL, "HMAC error");
goto error;
}
processed += current_macsize;
}
/* skip the size field which has been processed before */
buffer_pass_bytes(session->in_buffer, sizeof(uint32_t));
if (buffer_get_u8(session->in_buffer, &padding) == 0) {
ssh_set_error(session, SSH_FATAL, "Packet too short to read padding");
goto error;
}
if (padding > buffer_get_rest_len(session->in_buffer)) {
ssh_set_error(session, SSH_FATAL,
"Invalid padding: %d (%d left)",
padding,
buffer_get_rest_len(session->in_buffer));
goto error;
}
buffer_pass_bytes_end(session->in_buffer, padding);
compsize = buffer_get_rest_len(session->in_buffer);
#ifdef WITH_ZLIB
if (session->current_crypto
&& session->current_crypto->do_compress_in
&& buffer_get_rest_len(session->in_buffer)) {
if (decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN) < 0) {
goto error;
}
}
#endif /* WITH_ZLIB */
payloadsize=buffer_get_rest_len(session->in_buffer);
session->recv_seq++;
/* We don't want to rewrite a new packet while still executing the packet callbacks */
session->packet_state = PACKET_STATE_PROCESSING;
ssh_packet_parse_type(session);
ssh_log(session,SSH_LOG_PACKET,
"packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]",
session->in_packet.type, len, padding, compsize, payloadsize);
/* execute callbacks */
ssh_packet_process(session, session->in_packet.type);
session->packet_state = PACKET_STATE_INIT;
if(processed < receivedlen){
/* Handle a potential packet left in socket buffer */
ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer",
receivedlen-processed);
rc = ssh_packet_socket_callback(((unsigned char *)data) + processed,
receivedlen - processed,user);
processed += rc;
}
leave_function();
return processed;
case PACKET_STATE_PROCESSING:
ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying.");
return 0;
}
ssh_set_error(session, SSH_FATAL,
"Invalid state into packet_read2(): %d",
session->packet_state);
error:
session->session_state= SSH_SESSION_STATE_ERROR;
leave_function();
return processed;
}
void ssh_packet_register_socket_callback(ssh_session session, ssh_socket s){
session->socket_callbacks.data=ssh_packet_socket_callback;
session->socket_callbacks.connected=NULL;
session->socket_callbacks.controlflow=NULL;
session->socket_callbacks.exception=NULL;
session->socket_callbacks.userdata=session;
ssh_socket_set_callbacks(s,&session->socket_callbacks);
}
/** @internal
* @brief sets the callbacks for the packet layer
*/
void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
if(session->packet_callbacks == NULL){
session->packet_callbacks = ssh_list_new();
}
ssh_list_append(session->packet_callbacks, callbacks);
}
/** @internal
* @brief sets the default packet handlers
*/
void ssh_packet_set_default_callbacks(ssh_session session){
#ifdef WITH_SSH1
if(session->version==1){
ssh_packet_set_default_callbacks1(session);
return;
}
#endif
session->default_packet_callbacks.start=1;
session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback);
session->default_packet_callbacks.user=session;
session->default_packet_callbacks.callbacks=default_packet_handlers;
ssh_packet_set_callbacks(session, &session->default_packet_callbacks);
}
/** @internal
* @brief dispatch the call of packet handlers callbacks for a received packet
* @param type type of packet
*/
void ssh_packet_process(ssh_session session, uint8_t type){
struct ssh_iterator *i;
int r=SSH_PACKET_NOT_USED;
ssh_packet_callbacks cb;
enter_function();
ssh_log(session,SSH_LOG_PACKET, "Dispatching handler for packet type %d",type);
if(session->packet_callbacks == NULL){
ssh_log(session,SSH_LOG_RARE,"Packet callback is not initialized !");
goto error;
}
i=ssh_list_get_iterator(session->packet_callbacks);
while(i != NULL){
cb=ssh_iterator_value(ssh_packet_callbacks,i);
i=i->next;
if(!cb)
continue;
if(cb->start > type)
continue;
if(cb->start + cb->n_callbacks <= type)
continue;
if(cb->callbacks[type - cb->start]==NULL)
continue;
r=cb->callbacks[type - cb->start](session,type,session->in_buffer,cb->user);
if(r==SSH_PACKET_USED)
break;
}
if(r==SSH_PACKET_NOT_USED){
ssh_log(session,SSH_LOG_RARE,"Couldn't do anything with packet type %d",type);
ssh_packet_send_unimplemented(session, session->recv_seq-1);
}
error:
leave_function();
}
/** @internal
* @brief sends a SSH_MSG_UNIMPLEMENTED answer to an unhandled packet
* @param session the SSH session
* @param seqnum the sequence number of the unknown packet
* @return SSH_ERROR on error, else SSH_OK
*/
int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum){
int r;
enter_function();
r = buffer_add_u8(session->out_buffer, SSH2_MSG_UNIMPLEMENTED);
if (r < 0) {
return SSH_ERROR;
}
r = buffer_add_u32(session->out_buffer, htonl(seqnum));
if (r < 0) {
return SSH_ERROR;
}
r = packet_send(session);
leave_function();
return r;
}
/** @internal
* @brief handles a SSH_MSG_UNIMPLEMENTED packet
*/
SSH_PACKET_CALLBACK(ssh_packet_unimplemented){
uint32_t seq;
(void)type;
(void)user;
buffer_get_u32(packet,&seq);
seq=ntohl(seq);
ssh_log(session,SSH_LOG_RARE,
"Received SSH_MSG_UNIMPLEMENTED (sequence number %d)",seq);
return SSH_PACKET_USED;
}
/** @internal
* @parse the "Type" header field of a packet and updates the session
*/
int ssh_packet_parse_type(ssh_session session) {
enter_function();
memset(&session->in_packet, 0, sizeof(PACKET));
if(session->in_buffer == NULL) {
leave_function();
return SSH_ERROR;
}
if(buffer_get_u8(session->in_buffer, &session->in_packet.type) == 0) {
ssh_set_error(session, SSH_FATAL, "Packet too short to read type");
leave_function();
return SSH_ERROR;
}
session->in_packet.valid = 1;
leave_function();
return SSH_OK;
}
/*
* This function places the outgoing packet buffer into an outgoing
* socket buffer
*/
static int ssh_packet_write(ssh_session session) {
int rc = SSH_ERROR;
enter_function();
rc=ssh_socket_write(session->socket,
buffer_get_rest(session->out_buffer),
buffer_get_rest_len(session->out_buffer));
leave_function();
return rc;
}
static int packet_send2(ssh_session session) {
unsigned int blocksize = (session->current_crypto ?
session->current_crypto->out_cipher->blocksize : 8);
uint32_t currentlen = buffer_get_rest_len(session->out_buffer);
unsigned char *hmac = NULL;
char padstring[32] = {0};
int rc = SSH_ERROR;
uint32_t finallen,payloadsize,compsize;
uint8_t padding;
enter_function();
payloadsize = currentlen;
#ifdef WITH_ZLIB
if (session->current_crypto
&& session->current_crypto->do_compress_out
&& buffer_get_rest_len(session->out_buffer)) {
if (compress_buffer(session,session->out_buffer) < 0) {
goto error;
}
currentlen = buffer_get_rest_len(session->out_buffer);
}
#endif /* WITH_ZLIB */
compsize = currentlen;
padding = (blocksize - ((currentlen +5) % blocksize));
if(padding < 4) {
padding += blocksize;
}
if (session->current_crypto) {
ssh_get_random(padstring, padding, 0);
} else {
memset(padstring,0,padding);
}
finallen = htonl(currentlen + padding + 1);
if (buffer_prepend_data(session->out_buffer, &padding, sizeof(uint8_t)) < 0) {
goto error;
}
if (buffer_prepend_data(session->out_buffer, &finallen, sizeof(uint32_t)) < 0) {
goto error;
}
if (buffer_add_data(session->out_buffer, padstring, padding) < 0) {
goto error;
}
#ifdef WITH_PCAP
if(session->pcap_ctx){
ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,
buffer_get_rest(session->out_buffer),buffer_get_rest_len(session->out_buffer)
,buffer_get_rest_len(session->out_buffer));
}
#endif
hmac = packet_encrypt(session, buffer_get_rest(session->out_buffer),
buffer_get_rest_len(session->out_buffer));
if (hmac) {
if (buffer_add_data(session->out_buffer, hmac, 20) < 0) {
goto error;
}
}
rc = ssh_packet_write(session);
session->send_seq++;
ssh_log(session,SSH_LOG_PACKET,
"packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]",
ntohl(finallen), padding, compsize, payloadsize);
if (buffer_reinit(session->out_buffer) < 0) {
rc = SSH_ERROR;
}
error:
leave_function();
return rc; /* SSH_OK, AGAIN or ERROR */
}
int packet_send(ssh_session session) {
#ifdef WITH_SSH1
if (session->version == 1) {
return packet_send1(session);
}
#endif
return packet_send2(session);
}
/* vim: set ts=2 sw=2 et cindent: */

370
libssh/src/packet1.c Normal file
View file

@ -0,0 +1,370 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#ifndef _WIN32
#include <netinet/in.h>
#endif /* _WIN32 */
#include "libssh/priv.h"
#include "libssh/ssh1.h"
#include "libssh/crc32.h"
#include "libssh/packet.h"
#include "libssh/session.h"
#include "libssh/buffer.h"
#include "libssh/socket.h"
#include "libssh/kex.h"
#include "libssh/crypto.h"
#ifdef WITH_SSH1
static ssh_packet_callback default_packet_handlers1[]= {
NULL, //SSH_MSG_NONE 0
ssh_packet_disconnect1, //SSH_MSG_DISCONNECT 1
ssh_packet_publickey1, //SSH_SMSG_PUBLIC_KEY 2
NULL, //SSH_CMSG_SESSION_KEY 3
NULL, //SSH_CMSG_USER 4
NULL, //SSH_CMSG_AUTH_RHOSTS 5
NULL, //SSH_CMSG_AUTH_RSA 6
NULL, //SSH_SMSG_AUTH_RSA_CHALLENGE 7
NULL, //SSH_CMSG_AUTH_RSA_RESPONSE 8
NULL, //SSH_CMSG_AUTH_PASSWORD 9
NULL, //SSH_CMSG_REQUEST_PTY 10
NULL, //SSH_CMSG_WINDOW_SIZE 11
NULL, //SSH_CMSG_EXEC_SHELL 12
NULL, //SSH_CMSG_EXEC_CMD 13
ssh_packet_smsg_success1, //SSH_SMSG_SUCCESS 14
ssh_packet_smsg_failure1, //SSH_SMSG_FAILURE 15
NULL, //SSH_CMSG_STDIN_DATA 16
ssh_packet_data1, //SSH_SMSG_STDOUT_DATA 17
ssh_packet_data1, //SSH_SMSG_STDERR_DATA 18
NULL, //SSH_CMSG_EOF 19
ssh_packet_exist_status1, //SSH_SMSG_EXITSTATUS 20
NULL, //SSH_MSG_CHANNEL_OPEN_CONFIRMATION 21
NULL, //SSH_MSG_CHANNEL_OPEN_FAILURE 22
NULL, //SSH_MSG_CHANNEL_DATA 23
ssh_packet_close1, //SSH_MSG_CHANNEL_CLOSE 24
NULL, //SSH_MSG_CHANNEL_CLOSE_CONFIRMATION 25
NULL, //SSH_CMSG_X11_REQUEST_FORWARDING 26
NULL, //SSH_SMSG_X11_OPEN 27
NULL, //SSH_CMSG_PORT_FORWARD_REQUEST 28
NULL, //SSH_MSG_PORT_OPEN 29
NULL, //SSH_CMSG_AGENT_REQUEST_FORWARDING 30
NULL, //SSH_SMSG_AGENT_OPEN 31
ssh_packet_ignore_callback, //SSH_MSG_IGNORE 32
NULL, //SSH_CMSG_EXIT_CONFIRMATION 33
NULL, //SSH_CMSG_X11_REQUEST_FORWARDING 34
NULL, //SSH_CMSG_AUTH_RHOSTS_RSA 35
ssh_packet_ignore_callback, //SSH_MSG_DEBUG 36
};
/** @internal
* @brief sets the default packet handlers
*/
void ssh_packet_set_default_callbacks1(ssh_session session){
session->default_packet_callbacks.start=0;
session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers1)/sizeof(ssh_packet_callback);
session->default_packet_callbacks.user=session;
session->default_packet_callbacks.callbacks=default_packet_handlers1;
ssh_packet_set_callbacks(session, &session->default_packet_callbacks);
}
/** @internal
* @handles a data received event. It then calls the handlers for the different packet types
* or and exception handler callback. Adapted for SSH-1 packets.
* @param user pointer to current ssh_session
* @param data pointer to the data received
* @len length of data received. It might not be enough for a complete packet
* @returns number of bytes read and processed.
*/
int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user) {
void *packet = NULL;
int to_be_read;
size_t processed=0;
uint32_t padding;
uint32_t crc;
uint32_t len;
ssh_session session=(ssh_session)user;
enter_function();
switch (session->packet_state){
case PACKET_STATE_INIT:
memset(&session->in_packet, 0, sizeof(PACKET));
if (session->in_buffer) {
if (buffer_reinit(session->in_buffer) < 0) {
goto error;
}
} else {
session->in_buffer = ssh_buffer_new();
if (session->in_buffer == NULL) {
goto error;
}
}
/* must have at least enough bytes for size */
if(receivedlen < sizeof(uint32_t)){
leave_function();
return 0;
}
memcpy(&len,data,sizeof(uint32_t));
processed += sizeof(uint32_t);
/* len is not encrypted */
len = ntohl(len);
if (len > MAX_PACKET_LEN) {
ssh_set_error(session, SSH_FATAL,
"read_packet(): Packet len too high (%u %.8x)", len, len);
goto error;
}
ssh_log(session, SSH_LOG_PACKET, "Reading a %d bytes packet", len);
session->in_packet.len = len;
session->packet_state = PACKET_STATE_SIZEREAD;
case PACKET_STATE_SIZEREAD:
len = session->in_packet.len;
/* SSH-1 has a fixed padding lenght */
padding = 8 - (len % 8);
to_be_read = len + padding;
if(to_be_read + processed > receivedlen){
/* wait for rest of packet */
leave_function();
return processed;
}
/* it is _not_ possible that to_be_read be < 8. */
packet = (char *)data + processed;
if (buffer_add_data(session->in_buffer,packet,to_be_read) < 0) {
SAFE_FREE(packet);
goto error;
}
processed += to_be_read;
#ifdef DEBUG_CRYPTO
ssh_print_hexa("read packet:", ssh_buffer_get_begin(session->in_buffer),
ssh_buffer_get_len(session->in_buffer));
#endif
if (session->current_crypto) {
/*
* We decrypt everything, missing the lenght part (which was
* previously read, unencrypted, and is not part of the buffer
*/
if (packet_decrypt(session,
ssh_buffer_get_begin(session->in_buffer),
ssh_buffer_get_len(session->in_buffer)) < 0) {
ssh_set_error(session, SSH_FATAL, "Packet decrypt error");
goto error;
}
}
#ifdef DEBUG_CRYPTO
ssh_print_hexa("read packet decrypted:", ssh_buffer_get_begin(session->in_buffer),
ssh_buffer_get_len(session->in_buffer));
#endif
ssh_log(session, SSH_LOG_PACKET, "%d bytes padding", padding);
if(((len + padding) != buffer_get_rest_len(session->in_buffer)) ||
((len + padding) < sizeof(uint32_t))) {
ssh_log(session, SSH_LOG_RARE, "no crc32 in packet");
ssh_set_error(session, SSH_FATAL, "no crc32 in packet");
goto error;
}
memcpy(&crc,
(unsigned char *)buffer_get_rest(session->in_buffer) + (len+padding) - sizeof(uint32_t),
sizeof(uint32_t));
buffer_pass_bytes_end(session->in_buffer, sizeof(uint32_t));
crc = ntohl(crc);
if (ssh_crc32(buffer_get_rest(session->in_buffer),
(len + padding) - sizeof(uint32_t)) != crc) {
#ifdef DEBUG_CRYPTO
ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer),
len + padding - sizeof(uint32_t));
#endif
ssh_log(session, SSH_LOG_RARE, "Invalid crc32");
ssh_set_error(session, SSH_FATAL,
"Invalid crc32: expected %.8x, got %.8x",
crc,
ssh_crc32(buffer_get_rest(session->in_buffer),
len + padding - sizeof(uint32_t)));
goto error;
}
/* pass the padding */
buffer_pass_bytes(session->in_buffer, padding);
ssh_log(session, SSH_LOG_PACKET, "The packet is valid");
/* TODO FIXME
#ifdef WITH_ZLIB
if(session->current_crypto && session->current_crypto->do_compress_in){
decompress_buffer(session,session->in_buffer);
}
#endif
*/
session->recv_seq++;
/* We don't want to rewrite a new packet while still executing the packet callbacks */
session->packet_state = PACKET_STATE_PROCESSING;
ssh_packet_parse_type(session);
/* execute callbacks */
ssh_packet_process(session, session->in_packet.type);
session->packet_state = PACKET_STATE_INIT;
if(processed < receivedlen){
int rc;
/* Handle a potential packet left in socket buffer */
ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer",
receivedlen-processed);
rc = ssh_packet_socket_callback1((char *)data + processed,
receivedlen - processed,user);
processed += rc;
}
leave_function();
return processed;
case PACKET_STATE_PROCESSING:
ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying.");
return 0;
}
error:
session->session_state=SSH_SESSION_STATE_ERROR;
leave_function();
return processed;
}
int packet_send1(ssh_session session) {
unsigned int blocksize = (session->current_crypto ?
session->current_crypto->out_cipher->blocksize : 8);
uint32_t currentlen = ssh_buffer_get_len(session->out_buffer) + sizeof(uint32_t);
char padstring[32] = {0};
int rc = SSH_ERROR;
uint32_t finallen;
uint32_t crc;
uint8_t padding;
enter_function();
ssh_log(session,SSH_LOG_PACKET,"Sending a %d bytes long packet",currentlen);
/* TODO FIXME
#ifdef WITH_ZLIB
if (session->current_crypto && session->current_crypto->do_compress_out) {
if (compress_buffer(session, session->out_buffer) < 0) {
goto error;
}
currentlen = buffer_get_len(session->out_buffer);
}
#endif
*/
padding = blocksize - (currentlen % blocksize);
if (session->current_crypto) {
ssh_get_random(padstring, padding, 0);
} else {
memset(padstring, 0, padding);
}
finallen = htonl(currentlen);
ssh_log(session, SSH_LOG_PACKET,
"%d bytes after comp + %d padding bytes = %d bytes packet",
currentlen, padding, ntohl(finallen));
if (buffer_prepend_data(session->out_buffer, &padstring, padding) < 0) {
goto error;
}
if (buffer_prepend_data(session->out_buffer, &finallen, sizeof(uint32_t)) < 0) {
goto error;
}
crc = ssh_crc32((char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t),
ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t));
if (buffer_add_u32(session->out_buffer, ntohl(crc)) < 0) {
goto error;
}
#ifdef DEBUG_CRYPTO
ssh_print_hexa("Clear packet", ssh_buffer_get_begin(session->out_buffer),
ssh_buffer_get_len(session->out_buffer));
#endif
packet_encrypt(session, (unsigned char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t),
ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t));
#ifdef DEBUG_CRYPTO
ssh_print_hexa("encrypted packet",ssh_buffer_get_begin(session->out_buffer),
ssh_buffer_get_len(session->out_buffer));
#endif
rc=ssh_socket_write(session->socket, ssh_buffer_get_begin(session->out_buffer),
ssh_buffer_get_len(session->out_buffer));
if(rc== SSH_ERROR) {
goto error;
}
session->send_seq++;
if (buffer_reinit(session->out_buffer) < 0) {
rc = SSH_ERROR;
}
error:
leave_function();
return rc; /* SSH_OK, AGAIN or ERROR */
}
SSH_PACKET_CALLBACK(ssh_packet_disconnect1){
(void)packet;
(void)user;
(void)type;
ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT");
ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_DISCONNECT");
ssh_socket_close(session->socket);
session->alive = 0;
session->session_state=SSH_SESSION_STATE_DISCONNECTED;
return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(ssh_packet_smsg_success1){
if(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){
session->session_state=SSH_SESSION_STATE_AUTHENTICATING;
return SSH_PACKET_USED;
} else if(session->session_state==SSH_SESSION_STATE_AUTHENTICATING){
ssh_auth1_handler(session,type);
return SSH_PACKET_USED;
} else {
return ssh_packet_channel_success(session,type,packet,user);
}
}
SSH_PACKET_CALLBACK(ssh_packet_smsg_failure1){
if(session->session_state==SSH_SESSION_STATE_KEXINIT_RECEIVED){
session->session_state=SSH_SESSION_STATE_ERROR;
ssh_set_error(session,SSH_FATAL,"Key exchange failed: received SSH_SMSG_FAILURE");
return SSH_PACKET_USED;
} else if(session->session_state==SSH_SESSION_STATE_AUTHENTICATING){
ssh_auth1_handler(session,type);
return SSH_PACKET_USED;
} else {
return ssh_packet_channel_failure(session,type,packet,user);
}
}
#endif /* WITH_SSH1 */
/* vim: set ts=2 sw=2 et cindent: */

244
libssh/src/packet_cb.c Normal file
View file

@ -0,0 +1,244 @@
/*
* packet.c - packet building functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2011 Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/dh.h"
#include "libssh/misc.h"
#include "libssh/packet.h"
#include "libssh/pki.h"
#include "libssh/session.h"
#include "libssh/socket.h"
#include "libssh/ssh2.h"
/**
* @internal
*
* @brief Handle a SSH_DISCONNECT packet.
*/
SSH_PACKET_CALLBACK(ssh_packet_disconnect_callback){
uint32_t code;
char *error=NULL;
ssh_string error_s;
(void)user;
(void)type;
buffer_get_u32(packet, &code);
error_s = buffer_get_ssh_string(packet);
if (error_s != NULL) {
error = ssh_string_to_char(error_s);
ssh_string_free(error_s);
}
ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT %d:%s",code,
error != NULL ? error : "no error");
ssh_set_error(session, SSH_FATAL,
"Received SSH_MSG_DISCONNECT: %d:%s",code,
error != NULL ? error : "no error");
SAFE_FREE(error);
ssh_socket_close(session->socket);
session->alive = 0;
session->session_state= SSH_SESSION_STATE_ERROR;
/* TODO: handle a graceful disconnect */
return SSH_PACKET_USED;
}
/**
* @internal
*
* @brief Handle a SSH_IGNORE and SSH_DEBUG packet.
*/
SSH_PACKET_CALLBACK(ssh_packet_ignore_callback){
(void)user;
(void)type;
(void)packet;
ssh_log(session,SSH_LOG_PROTOCOL,"Received %s packet",type==SSH2_MSG_IGNORE ? "SSH_MSG_IGNORE" : "SSH_MSG_DEBUG");
/* TODO: handle a graceful disconnect */
return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
int rc;
(void)type;
(void)user;
ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY");
if(session->session_state!= SSH_SESSION_STATE_DH &&
session->dh_handshake_state != DH_STATE_INIT_SENT){
ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_reply called in wrong state : %d:%d",
session->session_state,session->dh_handshake_state);
goto error;
}
switch(session->next_crypto->kex_type){
case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1:
rc=ssh_client_dh_reply(session, packet);
break;
#ifdef HAVE_ECDH
case SSH_KEX_ECDH_SHA2_NISTP256:
rc = ssh_client_ecdh_reply(session, packet);
break;
#endif
default:
ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply");
goto error;
}
if(rc==SSH_OK) {
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
}
error:
session->session_state=SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(ssh_packet_newkeys){
ssh_string sig_blob = NULL;
int rc;
(void)packet;
(void)user;
(void)type;
ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS");
if(session->session_state!= SSH_SESSION_STATE_DH &&
session->dh_handshake_state != DH_STATE_NEWKEYS_SENT){
ssh_set_error(session,SSH_FATAL,"ssh_packet_newkeys called in wrong state : %d:%d",
session->session_state,session->dh_handshake_state);
goto error;
}
if(session->server){
/* server things are done in server.c */
session->dh_handshake_state=DH_STATE_FINISHED;
} else {
ssh_key key;
/* client */
rc = make_sessionid(session);
if (rc != SSH_OK) {
goto error;
}
/*
* Set the cryptographic functions for the next crypto
* (it is needed for generate_session_keys for key lengths)
*/
if (crypt_set_algorithms(session, SSH_3DES) /* knows nothing about DES*/ ) {
goto error;
}
if (generate_session_keys(session) < 0) {
goto error;
}
/* Verify the host's signature. FIXME do it sooner */
sig_blob = session->next_crypto->dh_server_signature;
session->next_crypto->dh_server_signature = NULL;
/* get the server public key */
rc = ssh_pki_import_pubkey_blob(session->next_crypto->server_pubkey, &key);
if (rc < 0) {
return SSH_ERROR;
}
/* check if public key from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
if(!ssh_match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
key->type_c)) {
ssh_set_error(session,
SSH_FATAL,
"Public key from server (%s) doesn't match user "
"preference (%s)",
key->type_c,
session->opts.wanted_methods[SSH_HOSTKEYS]);
ssh_key_free(key);
return -1;
}
}
rc = ssh_pki_signature_verify_blob(session,
sig_blob,
key,
session->next_crypto->secret_hash,
session->next_crypto->digest_len);
/* Set the server public key type for known host checking */
session->next_crypto->server_pubkey_type = key->type_c;
ssh_key_free(key);
ssh_string_burn(sig_blob);
ssh_string_free(sig_blob);
sig_blob = NULL;
if (rc == SSH_ERROR) {
goto error;
}
ssh_log(session,SSH_LOG_PROTOCOL,"Signature verified and valid");
/*
* Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
* current_crypto
*/
if (session->current_crypto) {
crypto_free(session->current_crypto);
session->current_crypto=NULL;
}
/* FIXME later, include a function to change keys */
session->current_crypto = session->next_crypto;
session->next_crypto = crypto_new();
if (session->next_crypto == NULL) {
ssh_set_error_oom(session);
goto error;
}
session->next_crypto->session_id = malloc(session->current_crypto->digest_len);
if (session->next_crypto->session_id == NULL) {
ssh_set_error_oom(session);
goto error;
}
memcpy(session->next_crypto->session_id, session->current_crypto->session_id,
session->current_crypto->digest_len);
}
session->dh_handshake_state = DH_STATE_FINISHED;
session->ssh_connection_callback(session);
return SSH_PACKET_USED;
error:
session->session_state=SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
/**
* @internal
* @brief handles a SSH_SERVICE_ACCEPT packet
*
*/
SSH_PACKET_CALLBACK(ssh_packet_service_accept){
(void)packet;
(void)type;
(void)user;
enter_function();
session->auth_service_state=SSH_AUTH_SERVICE_ACCEPTED;
ssh_log(session, SSH_LOG_PACKET,
"Received SSH_MSG_SERVICE_ACCEPT");
leave_function();
return SSH_PACKET_USED;
}

186
libssh/src/packet_crypt.c Normal file
View file

@ -0,0 +1,186 @@
/*
* crypt.c - blowfish-cbc code
*
* This file is part of the SSH Library
*
* Copyright (c) 2003 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#ifdef OPENSSL_CRYPTO
#include <openssl/blowfish.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#endif
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/wrapper.h"
#include "libssh/crypto.h"
#include "libssh/buffer.h"
uint32_t packet_decrypt_len(ssh_session session, char *crypted){
uint32_t decrypted;
if (session->current_crypto) {
if (packet_decrypt(session, crypted,
session->current_crypto->in_cipher->blocksize) < 0) {
return 0;
}
}
memcpy(&decrypted,crypted,sizeof(decrypted));
return ntohl(decrypted);
}
int packet_decrypt(ssh_session session, void *data,uint32_t len) {
struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher;
char *out = NULL;
if(len % session->current_crypto->in_cipher->blocksize != 0){
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len);
return SSH_ERROR;
}
out = malloc(len);
if (out == NULL) {
return -1;
}
if (crypto->set_decrypt_key(crypto, session->current_crypto->decryptkey,
session->current_crypto->decryptIV) < 0) {
SAFE_FREE(out);
return -1;
}
crypto->cbc_decrypt(crypto,data,out,len);
memcpy(data,out,len);
memset(out,0,len);
SAFE_FREE(out);
return 0;
}
unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) {
struct ssh_cipher_struct *crypto = NULL;
HMACCTX ctx = NULL;
char *out = NULL;
unsigned int finallen;
uint32_t seq;
if (!session->current_crypto) {
return NULL; /* nothing to do here */
}
if(len % session->current_crypto->in_cipher->blocksize != 0){
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len);
return NULL;
}
out = malloc(len);
if (out == NULL) {
return NULL;
}
seq = ntohl(session->send_seq);
crypto = session->current_crypto->out_cipher;
if (crypto->set_encrypt_key(crypto, session->current_crypto->encryptkey,
session->current_crypto->encryptIV) < 0) {
SAFE_FREE(out);
return NULL;
}
if (session->version == 2) {
ctx = hmac_init(session->current_crypto->encryptMAC,20,SSH_HMAC_SHA1);
if (ctx == NULL) {
SAFE_FREE(out);
return NULL;
}
hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t));
hmac_update(ctx,data,len);
hmac_final(ctx,session->current_crypto->hmacbuf,&finallen);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("mac: ",data,len);
if (finallen != 20) {
printf("Final len is %d\n",finallen);
}
ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, 20);
#endif
}
crypto->cbc_encrypt(crypto, data, out, len);
memcpy(data, out, len);
memset(out, 0, len);
SAFE_FREE(out);
if (session->version == 2) {
return session->current_crypto->hmacbuf;
}
return NULL;
}
/**
* @internal
*
* @brief Verify the hmac of a packet
*
* @param session The session to use.
* @param buffer The buffer to verify the hmac from.
* @param mac The mac to compare with the hmac.
*
* @return 0 if hmac and mac are equal, < 0 if not or an error
* occurred.
*/
int packet_hmac_verify(ssh_session session, ssh_buffer buffer,
unsigned char *mac) {
unsigned char hmacbuf[EVP_MAX_MD_SIZE] = {0};
HMACCTX ctx;
unsigned int len;
uint32_t seq;
ctx = hmac_init(session->current_crypto->decryptMAC, 20, SSH_HMAC_SHA1);
if (ctx == NULL) {
return -1;
}
seq = htonl(session->recv_seq);
hmac_update(ctx, (unsigned char *) &seq, sizeof(uint32_t));
hmac_update(ctx, buffer_get_rest(buffer), buffer_get_rest_len(buffer));
hmac_final(ctx, hmacbuf, &len);
#ifdef DEBUG_CRYPTO
ssh_print_hexa("received mac",mac,len);
ssh_print_hexa("Computed mac",hmacbuf,len);
ssh_print_hexa("seq",(unsigned char *)&seq,sizeof(uint32_t));
#endif
if (memcmp(mac, hmacbuf, len) == 0) {
return 0;
}
return -1;
}
/* vim: set ts=2 sw=2 et cindent: */

554
libssh/src/pcap.c Normal file
View file

@ -0,0 +1,554 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/* pcap.c */
#include "config.h"
#ifdef WITH_PCAP
#include <stdio.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/socket.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include "libssh/libssh.h"
#include "libssh/pcap.h"
#include "libssh/session.h"
#include "libssh/buffer.h"
#include "libssh/socket.h"
/**
* @internal
*
* @defgroup libssh_pcap The libssh pcap functions
* @ingroup libssh
*
* The pcap file generation
*
*
* @{
*/
/* The header of a pcap file is the following. We are not going to make it
* very complicated.
* Just for information.
*/
struct pcap_hdr_s {
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
int32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
};
#define PCAP_MAGIC 0xa1b2c3d4
#define PCAP_VERSION_MAJOR 2
#define PCAP_VERSION_MINOR 4
#define DLT_RAW 12 /* raw IP */
/* TCP flags */
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
/* The header of a pcap packet.
* Just for information.
*/
struct pcaprec_hdr_s {
uint32_t ts_sec; /* timestamp seconds */
uint32_t ts_usec; /* timestamp microseconds */
uint32_t incl_len; /* number of octets of packet saved in file */
uint32_t orig_len; /* actual length of packet */
};
/** @private
* @brief a pcap context expresses the state of a pcap dump
* in a SSH session only. Multiple pcap contexts may be used into
* a single pcap file.
*/
struct ssh_pcap_context_struct {
ssh_session session;
ssh_pcap_file file;
int connected;
/* All of these information are useful to generate
* the dummy IP and TCP packets
*/
uint32_t ipsource;
uint32_t ipdest;
uint16_t portsource;
uint16_t portdest;
uint32_t outsequence;
uint32_t insequence;
};
/** @private
* @brief a pcap file expresses the state of a pcap file which may
* contain several streams.
*/
struct ssh_pcap_file_struct {
FILE *output;
uint16_t ipsequence;
};
/**
* @brief create a new ssh_pcap_file object
*/
ssh_pcap_file ssh_pcap_file_new(void) {
struct ssh_pcap_file_struct *pcap;
pcap = (struct ssh_pcap_file_struct *) malloc(sizeof(struct ssh_pcap_file_struct));
if (pcap == NULL) {
return NULL;
}
ZERO_STRUCTP(pcap);
return pcap;
}
/** @internal
* @brief writes a packet on file
*/
static int ssh_pcap_file_write(ssh_pcap_file pcap, ssh_buffer packet){
int err;
uint32_t len;
if(pcap == NULL || pcap->output==NULL)
return SSH_ERROR;
len=buffer_get_rest_len(packet);
err=fwrite(buffer_get_rest(packet),len,1,pcap->output);
if(err<0)
return SSH_ERROR;
else
return SSH_OK;
}
/** @internal
* @brief prepends a packet with the pcap header and writes packet
* on file
*/
int ssh_pcap_file_write_packet(ssh_pcap_file pcap, ssh_buffer packet, uint32_t original_len){
ssh_buffer header=ssh_buffer_new();
struct timeval now;
int err;
if(header == NULL)
return SSH_ERROR;
gettimeofday(&now,NULL);
err = buffer_add_u32(header,htonl(now.tv_sec));
if (err < 0) {
goto error;
}
err = buffer_add_u32(header,htonl(now.tv_usec));
if (err < 0) {
goto error;
}
err = buffer_add_u32(header,htonl(buffer_get_rest_len(packet)));
if (err < 0) {
goto error;
}
err = buffer_add_u32(header,htonl(original_len));
if (err < 0) {
goto error;
}
err = buffer_add_buffer(header,packet);
if (err < 0) {
goto error;
}
err=ssh_pcap_file_write(pcap,header);
error:
ssh_buffer_free(header);
return err;
}
/**
* @brief opens a new pcap file and create header
*/
int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){
ssh_buffer header;
int err;
if(pcap == NULL)
return SSH_ERROR;
if(pcap->output){
fclose(pcap->output);
pcap->output=NULL;
}
pcap->output=fopen(filename,"wb");
if(pcap->output==NULL)
return SSH_ERROR;
header=ssh_buffer_new();
if(header==NULL)
return SSH_ERROR;
err = buffer_add_u32(header,htonl(PCAP_MAGIC));
if (err < 0) {
goto error;
}
err = buffer_add_u16(header,htons(PCAP_VERSION_MAJOR));
if (err < 0) {
goto error;
}
err = buffer_add_u16(header,htons(PCAP_VERSION_MINOR));
if (err < 0) {
goto error;
}
/* currently hardcode GMT to 0 */
err = buffer_add_u32(header,htonl(0));
if (err < 0) {
goto error;
}
/* accuracy */
err = buffer_add_u32(header,htonl(0));
if (err < 0) {
goto error;
}
/* size of the biggest packet */
err = buffer_add_u32(header,htonl(MAX_PACKET_LEN));
if (err < 0) {
goto error;
}
/* we will write sort-of IP */
err = buffer_add_u32(header,htonl(DLT_RAW));
if (err < 0) {
goto error;
}
err=ssh_pcap_file_write(pcap,header);
error:
ssh_buffer_free(header);
return err;
}
int ssh_pcap_file_close(ssh_pcap_file pcap){
int err;
if(pcap ==NULL || pcap->output==NULL)
return SSH_ERROR;
err=fclose(pcap->output);
pcap->output=NULL;
if(err != 0)
return SSH_ERROR;
else
return SSH_OK;
}
void ssh_pcap_file_free(ssh_pcap_file pcap){
ssh_pcap_file_close(pcap);
SAFE_FREE(pcap);
}
/** @internal
* @brief allocates a new ssh_pcap_context object
*/
ssh_pcap_context ssh_pcap_context_new(ssh_session session){
ssh_pcap_context ctx = (struct ssh_pcap_context_struct *) malloc(sizeof(struct ssh_pcap_context_struct));
if(ctx==NULL){
ssh_set_error_oom(session);
return NULL;
}
ZERO_STRUCTP(ctx);
ctx->session=session;
return ctx;
}
void ssh_pcap_context_free(ssh_pcap_context ctx){
SAFE_FREE(ctx);
}
void ssh_pcap_context_set_file(ssh_pcap_context ctx, ssh_pcap_file pcap){
ctx->file=pcap;
}
/** @internal
* @brief sets the IP and port parameters in the connection
*/
static int ssh_pcap_context_connect(ssh_pcap_context ctx){
ssh_session session=ctx->session;
struct sockaddr_in local, remote;
socket_t fd;
socklen_t len;
if(session==NULL)
return SSH_ERROR;
if(session->socket==NULL)
return SSH_ERROR;
fd=ssh_socket_get_fd_in(session->socket);
/* TODO: adapt for windows */
if(fd<0)
return SSH_ERROR;
len=sizeof(local);
if(getsockname(fd,(struct sockaddr *)&local,&len)<0){
ssh_set_error(session,SSH_REQUEST_DENIED,"Getting local IP address: %s",strerror(errno));
return SSH_ERROR;
}
len=sizeof(remote);
if(getpeername(fd,(struct sockaddr *)&remote,&len)<0){
ssh_set_error(session,SSH_REQUEST_DENIED,"Getting remote IP address: %s",strerror(errno));
return SSH_ERROR;
}
if(local.sin_family != AF_INET){
ssh_set_error(session,SSH_REQUEST_DENIED,"Only IPv4 supported for pcap logging");
return SSH_ERROR;
}
memcpy(&ctx->ipsource,&local.sin_addr,sizeof(ctx->ipsource));
memcpy(&ctx->ipdest,&remote.sin_addr,sizeof(ctx->ipdest));
memcpy(&ctx->portsource,&local.sin_port,sizeof(ctx->portsource));
memcpy(&ctx->portdest,&remote.sin_port,sizeof(ctx->portdest));
ctx->connected=1;
return SSH_OK;
}
#define IPHDR_LEN 20
#define TCPHDR_LEN 20
#define TCPIPHDR_LEN (IPHDR_LEN + TCPHDR_LEN)
/** @internal
* @brief write a SSH packet as a TCP over IP in a pcap file
* @param ctx open pcap context
* @param direction SSH_PCAP_DIRECTION_IN if the packet has been received
* @param direction SSH_PCAP_DIRECTION_OUT if the packet has been emitted
* @param data pointer to the data to write
* @param len data to write in the pcap file. May be smaller than origlen.
* @param origlen number of bytes of complete data.
* @returns SSH_OK write is successful
* @returns SSH_ERROR an error happened.
*/
int ssh_pcap_context_write(ssh_pcap_context ctx,enum ssh_pcap_direction direction
, void *data, uint32_t len, uint32_t origlen){
ssh_buffer ip;
int err;
if(ctx==NULL || ctx->file ==NULL)
return SSH_ERROR;
if(ctx->connected==0)
if(ssh_pcap_context_connect(ctx)==SSH_ERROR)
return SSH_ERROR;
ip=ssh_buffer_new();
if(ip==NULL){
ssh_set_error_oom(ctx->session);
return SSH_ERROR;
}
/* build an IP packet */
/* V4, 20 bytes */
err = buffer_add_u8(ip,4 << 4 | 5);
if (err < 0) {
goto error;
}
/* tos */
err = buffer_add_u8(ip,0);
if (err < 0) {
goto error;
}
/* total len */
err = buffer_add_u16(ip,htons(origlen + TCPIPHDR_LEN));
if (err < 0) {
goto error;
}
/* IP id number */
err = buffer_add_u16(ip,htons(ctx->file->ipsequence));
if (err < 0) {
goto error;
}
ctx->file->ipsequence++;
/* fragment offset */
err = buffer_add_u16(ip,htons(0));
if (err < 0) {
goto error;
}
/* TTL */
err = buffer_add_u8(ip,64);
if (err < 0) {
goto error;
}
/* protocol TCP=6 */
err = buffer_add_u8(ip,6);
if (err < 0) {
goto error;
}
/* checksum */
err = buffer_add_u16(ip,0);
if (err < 0) {
goto error;
}
if(direction==SSH_PCAP_DIR_OUT){
err = buffer_add_u32(ip,ctx->ipsource);
if (err < 0) {
goto error;
}
err = buffer_add_u32(ip,ctx->ipdest);
if (err < 0) {
goto error;
}
} else {
err = buffer_add_u32(ip,ctx->ipdest);
if (err < 0) {
goto error;
}
err = buffer_add_u32(ip,ctx->ipsource);
if (err < 0) {
goto error;
}
}
/* TCP */
if(direction==SSH_PCAP_DIR_OUT){
err = buffer_add_u16(ip,ctx->portsource);
if (err < 0) {
goto error;
}
err = buffer_add_u16(ip,ctx->portdest);
if (err < 0) {
goto error;
}
} else {
err = buffer_add_u16(ip,ctx->portdest);
if (err < 0) {
goto error;
}
err = buffer_add_u16(ip,ctx->portsource);
if (err < 0) {
goto error;
}
}
/* sequence number */
if(direction==SSH_PCAP_DIR_OUT){
err = buffer_add_u32(ip,ntohl(ctx->outsequence));
if (err < 0) {
goto error;
}
ctx->outsequence+=origlen;
} else {
err = buffer_add_u32(ip,ntohl(ctx->insequence));
if (err < 0) {
goto error;
}
ctx->insequence+=origlen;
}
/* ack number */
if(direction==SSH_PCAP_DIR_OUT){
err = buffer_add_u32(ip,ntohl(ctx->insequence));
if (err < 0) {
goto error;
}
} else {
err = buffer_add_u32(ip,ntohl(ctx->outsequence));
if (err < 0) {
goto error;
}
}
/* header len = 20 = 5 * 32 bits, at offset 4*/
err = buffer_add_u8(ip,5 << 4);
if (err < 0) {
goto error;
}
/* flags */
err = buffer_add_u8(ip,TH_PUSH | TH_ACK);
if (err < 0) {
goto error;
}
/* window */
err = buffer_add_u16(ip,htons(65535));
if (err < 0) {
goto error;
}
/* checksum */
err = buffer_add_u16(ip,htons(0));
if (err < 0) {
goto error;
}
/* urgent data ptr */
err = buffer_add_u16(ip,0);
if (err < 0) {
goto error;
}
/* actual data */
err = buffer_add_data(ip,data,len);
if (err < 0) {
goto error;
}
err=ssh_pcap_file_write_packet(ctx->file,ip,origlen + TCPIPHDR_LEN);
error:
ssh_buffer_free(ip);
return err;
}
/** @brief sets the pcap file used to trace the session
* @param current session
* @param pcap an handler to a pcap file. A pcap file may be used in several
* sessions.
* @returns SSH_ERROR in case of error, SSH_OK otherwise.
*/
int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcap){
ssh_pcap_context ctx=ssh_pcap_context_new(session);
if(ctx==NULL){
ssh_set_error_oom(session);
return SSH_ERROR;
}
ctx->file=pcap;
if(session->pcap_ctx)
ssh_pcap_context_free(session->pcap_ctx);
session->pcap_ctx=ctx;
return SSH_OK;
}
#else /* WITH_PCAP */
/* Simple stub returning errors when no pcap compiled in */
#include "libssh/libssh.h"
#include "libssh/priv.h"
int ssh_pcap_file_close(ssh_pcap_file pcap){
(void) pcap;
return SSH_ERROR;
}
void ssh_pcap_file_free(ssh_pcap_file pcap){
(void) pcap;
}
ssh_pcap_file ssh_pcap_file_new(void){
return NULL;
}
int ssh_pcap_file_open(ssh_pcap_file pcap, const char *filename){
(void) pcap;
(void) filename;
return SSH_ERROR;
}
int ssh_set_pcap_file(ssh_session session, ssh_pcap_file pcapfile){
(void) pcapfile;
ssh_set_error(session,SSH_REQUEST_DENIED,"Pcap support not compiled in");
return SSH_ERROR;
}
#endif
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

1410
libssh/src/pki.c Normal file

File diff suppressed because it is too large Load diff

1403
libssh/src/pki_crypto.c Normal file

File diff suppressed because it is too large Load diff

1699
libssh/src/pki_gcrypt.c Normal file

File diff suppressed because it is too large Load diff

926
libssh/src/poll.c Normal file
View file

@ -0,0 +1,926 @@
/*
* poll.c - poll wrapper
*
* This file is part of the SSH Library
*
* Copyright (c) 2009-2010 by Andreas Schneider <mail@cynapses.org>
* Copyright (c) 2003-2009 by Aris Adamantiadis
* Copyright (c) 2009 Aleksandar Kanchev
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*
* vim: ts=2 sw=2 et cindent
*/
#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/libssh.h"
#include "libssh/poll.h"
#include "libssh/socket.h"
#include "libssh/session.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/misc.h"
#endif
#ifndef SSH_POLL_CTX_CHUNK
#define SSH_POLL_CTX_CHUNK 5
#endif
/**
* @defgroup libssh_poll The SSH poll functions.
* @ingroup libssh
*
* Add a generic way to handle sockets asynchronously.
*
* It's based on poll objects, each of which store a socket, its events and a
* callback, which gets called whenever an event is set. The poll objects are
* attached to a poll context, which should be allocated on per thread basis.
*
* Polling the poll context will poll all the attached poll objects and call
* their callbacks (handlers) if any of the socket events are set. This should
* be done within the main loop of an application.
*
* @{
*/
struct ssh_poll_handle_struct {
ssh_poll_ctx ctx;
union {
socket_t fd;
size_t idx;
} x;
short events;
ssh_poll_callback cb;
void *cb_data;
};
struct ssh_poll_ctx_struct {
ssh_poll_handle *pollptrs;
ssh_pollfd_t *pollfds;
size_t polls_allocated;
size_t polls_used;
size_t chunk_size;
};
#ifdef HAVE_POLL
#include <poll.h>
void ssh_poll_init(void) {
return;
}
void ssh_poll_cleanup(void) {
return;
}
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
return poll((struct pollfd *) fds, nfds, timeout);
}
#else /* HAVE_POLL */
typedef int (*poll_fn)(ssh_pollfd_t *, nfds_t, int);
static poll_fn ssh_poll_emu;
#include <sys/types.h>
#ifdef _WIN32
#ifndef STRICT
#define STRICT
#endif /* STRICT */
#include <time.h>
#include <windows.h>
#include <winsock2.h>
#else /* _WIN32 */
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#endif /* _WIN32 */
/*
* This is a poll(2)-emulation using select for systems not providing a native
* poll implementation.
*
* Keep in mind that select is terribly inefficient. The interface is simply not
* meant to be used with maximum descriptor value greater, say, 32 or so. With
* a value as high as 1024 on Linux you'll pay dearly in every single call.
* poll() will be orders of magnitude faster.
*/
static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
fd_set readfds, writefds, exceptfds;
struct timeval tv, *ptv;
socket_t max_fd;
int rc;
nfds_t i;
if (fds == NULL) {
errno = EFAULT;
return -1;
}
FD_ZERO (&readfds);
FD_ZERO (&writefds);
FD_ZERO (&exceptfds);
/* compute fd_sets and find largest descriptor */
for (rc = -1, max_fd = 0, i = 0; i < nfds; i++) {
if (fds[i].fd == SSH_INVALID_SOCKET) {
continue;
}
#ifndef _WIN32
if (fds[i].fd >= FD_SETSIZE) {
rc = -1;
break;
}
#endif
if (fds[i].events & (POLLIN | POLLRDNORM)) {
FD_SET (fds[i].fd, &readfds);
}
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
FD_SET (fds[i].fd, &writefds);
}
if (fds[i].events & (POLLPRI | POLLRDBAND)) {
FD_SET (fds[i].fd, &exceptfds);
}
if (fds[i].fd > max_fd &&
(fds[i].events & (POLLIN | POLLOUT | POLLPRI |
POLLRDNORM | POLLRDBAND |
POLLWRNORM | POLLWRBAND))) {
max_fd = fds[i].fd;
rc = 0;
}
}
if (max_fd == SSH_INVALID_SOCKET || rc == -1) {
errno = EINVAL;
return -1;
}
if (timeout < 0) {
ptv = NULL;
} else {
ptv = &tv;
if (timeout == 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
} else {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
}
}
rc = select (max_fd + 1, &readfds, &writefds, &exceptfds, ptv);
if (rc < 0) {
return -1;
}
for (rc = 0, i = 0; i < nfds; i++)
if (fds[i].fd >= 0) {
fds[i].revents = 0;
if (FD_ISSET(fds[i].fd, &readfds)) {
int save_errno = errno;
char data[64] = {0};
int ret;
/* support for POLLHUP */
ret = recv(fds[i].fd, data, 64, MSG_PEEK);
#ifdef _WIN32
if ((ret == -1) &&
(errno == WSAESHUTDOWN || errno == WSAECONNRESET ||
errno == WSAECONNABORTED || errno == WSAENETRESET)) {
#else
if ((ret == -1) &&
(errno == ESHUTDOWN || errno == ECONNRESET ||
errno == ECONNABORTED || errno == ENETRESET)) {
#endif
fds[i].revents |= POLLHUP;
} else {
fds[i].revents |= fds[i].events & (POLLIN | POLLRDNORM);
}
errno = save_errno;
}
if (FD_ISSET(fds[i].fd, &writefds)) {
fds[i].revents |= fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND);
}
if (FD_ISSET(fds[i].fd, &exceptfds)) {
fds[i].revents |= fds[i].events & (POLLPRI | POLLRDBAND);
}
if (fds[i].revents & ~POLLHUP) {
rc++;
}
} else {
fds[i].revents = POLLNVAL;
}
return rc;
}
void ssh_poll_init(void) {
ssh_poll_emu = bsd_poll;
}
void ssh_poll_cleanup(void) {
ssh_poll_emu = bsd_poll;
}
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) {
return (ssh_poll_emu)(fds, nfds, timeout);
}
#endif /* HAVE_POLL */
/**
* @brief Allocate a new poll object, which could be used within a poll context.
*
* @param fd Socket that will be polled.
* @param events Poll events that will be monitored for the socket. i.e.
* POLLIN, POLLPRI, POLLOUT
* @param cb Function to be called if any of the events are set.
* The prototype of cb is:
* int (*ssh_poll_callback)(ssh_poll_handle p, socket_t fd,
* int revents, void *userdata);
* @param userdata Userdata to be passed to the callback function. NULL if
* not needed.
*
* @return A new poll object, NULL on error
*/
ssh_poll_handle ssh_poll_new(socket_t fd, short events, ssh_poll_callback cb,
void *userdata) {
ssh_poll_handle p;
p = malloc(sizeof(struct ssh_poll_handle_struct));
if (p == NULL) {
return NULL;
}
ZERO_STRUCTP(p);
p->x.fd = fd;
p->events = events;
p->cb = cb;
p->cb_data = userdata;
return p;
}
/**
* @brief Free a poll object.
*
* @param p Pointer to an already allocated poll object.
*/
void ssh_poll_free(ssh_poll_handle p) {
if(p->ctx != NULL){
ssh_poll_ctx_remove(p->ctx,p);
p->ctx=NULL;
}
SAFE_FREE(p);
}
/**
* @brief Get the poll context of a poll object.
*
* @param p Pointer to an already allocated poll object.
*
* @return Poll context or NULL if the poll object isn't attached.
*/
ssh_poll_ctx ssh_poll_get_ctx(ssh_poll_handle p) {
return p->ctx;
}
/**
* @brief Get the events of a poll object.
*
* @param p Pointer to an already allocated poll object.
*
* @return Poll events.
*/
short ssh_poll_get_events(ssh_poll_handle p) {
return p->events;
}
/**
* @brief Set the events of a poll object. The events will also be propagated
* to an associated poll context.
*
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
void ssh_poll_set_events(ssh_poll_handle p, short events) {
p->events = events;
if (p->ctx != NULL) {
p->ctx->pollfds[p->x.idx].events = events;
}
}
/**
* @brief Set the file descriptor of a poll object. The FD will also be propagated
* to an associated poll context.
*
* @param p Pointer to an already allocated poll object.
* @param fd New file descriptor.
*/
void ssh_poll_set_fd(ssh_poll_handle p, socket_t fd) {
if (p->ctx != NULL) {
p->ctx->pollfds[p->x.idx].fd = fd;
} else {
p->x.fd = fd;
}
}
/**
* @brief Add extra events to a poll object. Duplicates are ignored.
* The events will also be propagated to an associated poll context.
*
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
void ssh_poll_add_events(ssh_poll_handle p, short events) {
ssh_poll_set_events(p, ssh_poll_get_events(p) | events);
}
/**
* @brief Remove events from a poll object. Non-existent are ignored.
* The events will also be propagated to an associated poll context.
*
* @param p Pointer to an already allocated poll object.
* @param events Poll events.
*/
void ssh_poll_remove_events(ssh_poll_handle p, short events) {
ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events);
}
/**
* @brief Get the raw socket of a poll object.
*
* @param p Pointer to an already allocated poll object.
*
* @return Raw socket.
*/
socket_t ssh_poll_get_fd(ssh_poll_handle p) {
if (p->ctx != NULL) {
return p->ctx->pollfds[p->x.idx].fd;
}
return p->x.fd;
}
/**
* @brief Set the callback of a poll object.
*
* @param p Pointer to an already allocated poll object.
* @param cb Function to be called if any of the events are set.
* @param userdata Userdata to be passed to the callback function. NULL if
* not needed.
*/
void ssh_poll_set_callback(ssh_poll_handle p, ssh_poll_callback cb, void *userdata) {
if (cb != NULL) {
p->cb = cb;
p->cb_data = userdata;
}
}
/**
* @brief Create a new poll context. It could be associated with many poll object
* which are going to be polled at the same time as the poll context. You
* would need a single poll context per thread.
*
* @param chunk_size The size of the memory chunk that will be allocated, when
* more memory is needed. This is for efficiency reasons,
* i.e. don't allocate memory for each new poll object, but
* for the next 5. Set it to 0 if you want to use the
* library's default value.
*/
ssh_poll_ctx ssh_poll_ctx_new(size_t chunk_size) {
ssh_poll_ctx ctx;
ctx = malloc(sizeof(struct ssh_poll_ctx_struct));
if (ctx == NULL) {
return NULL;
}
ZERO_STRUCTP(ctx);
if (chunk_size == 0) {
chunk_size = SSH_POLL_CTX_CHUNK;
}
ctx->chunk_size = chunk_size;
return ctx;
}
/**
* @brief Free a poll context.
*
* @param ctx Pointer to an already allocated poll context.
*/
void ssh_poll_ctx_free(ssh_poll_ctx ctx) {
if (ctx->polls_allocated > 0) {
while (ctx->polls_used > 0){
ssh_poll_handle p = ctx->pollptrs[0];
ssh_poll_ctx_remove(ctx, p);
}
SAFE_FREE(ctx->pollptrs);
SAFE_FREE(ctx->pollfds);
}
SAFE_FREE(ctx);
}
static int ssh_poll_ctx_resize(ssh_poll_ctx ctx, size_t new_size) {
ssh_poll_handle *pollptrs;
ssh_pollfd_t *pollfds;
pollptrs = realloc(ctx->pollptrs, sizeof(ssh_poll_handle) * new_size);
if (pollptrs == NULL) {
return -1;
}
pollfds = realloc(ctx->pollfds, sizeof(ssh_pollfd_t) * new_size);
if (pollfds == NULL) {
ctx->pollptrs = realloc(pollptrs, sizeof(ssh_poll_handle) * ctx->polls_allocated);
return -1;
}
ctx->pollptrs = pollptrs;
ctx->pollfds = pollfds;
ctx->polls_allocated = new_size;
return 0;
}
/**
* @brief Add a poll object to a poll context.
*
* @param ctx Pointer to an already allocated poll context.
* @param p Pointer to an already allocated poll object.
*
* @return 0 on success, < 0 on error
*/
int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) {
socket_t fd;
if (p->ctx != NULL) {
/* already attached to a context */
return -1;
}
if (ctx->polls_used == ctx->polls_allocated &&
ssh_poll_ctx_resize(ctx, ctx->polls_allocated + ctx->chunk_size) < 0) {
return -1;
}
fd = p->x.fd;
p->x.idx = ctx->polls_used++;
ctx->pollptrs[p->x.idx] = p;
ctx->pollfds[p->x.idx].fd = fd;
ctx->pollfds[p->x.idx].events = p->events;
ctx->pollfds[p->x.idx].revents = 0;
p->ctx = ctx;
return 0;
}
/**
* @brief Add a socket object to a poll context.
*
* @param ctx Pointer to an already allocated poll context.
* @param s A SSH socket handle
*
* @return 0 on success, < 0 on error
*/
int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s) {
ssh_poll_handle p_in, p_out;
int ret;
p_in=ssh_socket_get_poll_handle_in(s);
if(p_in==NULL)
return -1;
ret = ssh_poll_ctx_add(ctx,p_in);
if(ret != 0)
return ret;
p_out=ssh_socket_get_poll_handle_out(s);
if(p_in != p_out)
ret = ssh_poll_ctx_add(ctx,p_out);
return ret;
}
/**
* @brief Remove a poll object from a poll context.
*
* @param ctx Pointer to an already allocated poll context.
* @param p Pointer to an already allocated poll object.
*/
void ssh_poll_ctx_remove(ssh_poll_ctx ctx, ssh_poll_handle p) {
size_t i;
i = p->x.idx;
p->x.fd = ctx->pollfds[i].fd;
p->ctx = NULL;
ctx->polls_used--;
/* fill the empty poll slot with the last one */
if (ctx->polls_used > 0 && ctx->polls_used != i) {
ctx->pollfds[i] = ctx->pollfds[ctx->polls_used];
ctx->pollptrs[i] = ctx->pollptrs[ctx->polls_used];
ctx->pollptrs[i]->x.idx = i;
}
/* this will always leave at least chunk_size polls allocated */
if (ctx->polls_allocated - ctx->polls_used > ctx->chunk_size) {
ssh_poll_ctx_resize(ctx, ctx->polls_allocated - ctx->chunk_size);
}
}
/**
* @brief Poll all the sockets associated through a poll object with a
* poll context. If any of the events are set after the poll, the
* call back function of the socket will be called.
* This function should be called once within the programs main loop.
*
* @param ctx Pointer to an already allocated poll context.
* @param timeout An upper limit on the time for which ssh_poll_ctx() will
* block, in milliseconds. Specifying a negative value
* means an infinite timeout. This parameter is passed to
* the poll() function.
* @returns SSH_OK No error.
* SSH_ERROR Error happened during the poll.
* SSH_AGAIN Timeout occured
*/
int ssh_poll_ctx_dopoll(ssh_poll_ctx ctx, int timeout) {
int rc;
int i, used;
ssh_poll_handle p;
socket_t fd;
int revents;
if (!ctx->polls_used)
return SSH_ERROR;
rc = ssh_poll(ctx->pollfds, ctx->polls_used, timeout);
if(rc < 0)
return SSH_ERROR;
if (rc == 0)
return SSH_AGAIN;
used = ctx->polls_used;
for (i = 0; i < used && rc > 0; ) {
if (!ctx->pollfds[i].revents) {
i++;
} else {
int ret;
p = ctx->pollptrs[i];
fd = ctx->pollfds[i].fd;
revents = ctx->pollfds[i].revents;
if (p->cb && (ret = p->cb(p, fd, revents, p->cb_data)) < 0) {
if (ret == -2) {
return -1;
}
/* the poll was removed, reload the used counter and start again */
used = ctx->polls_used;
i=0;
} else {
ctx->pollfds[i].revents = 0;
i++;
}
rc--;
}
}
return rc;
}
/**
* @internal
* @brief gets the default poll structure for the current session,
* when used in blocking mode.
* @param session SSH session
* @returns the default ssh_poll_ctx
*/
ssh_poll_ctx ssh_poll_get_default_ctx(ssh_session session){
if(session->default_poll_ctx != NULL)
return session->default_poll_ctx;
/* 2 is enough for the default one */
session->default_poll_ctx = ssh_poll_ctx_new(2);
return session->default_poll_ctx;
}
/* public event API */
struct ssh_event_fd_wrapper {
ssh_event_callback cb;
void * userdata;
};
struct ssh_event_struct {
ssh_poll_ctx ctx;
#ifdef WITH_SERVER
struct ssh_list *sessions;
#endif
};
/**
* @brief Create a new event context. It could be associated with many
* ssh_session objects and socket fd which are going to be polled at the
* same time as the event context. You would need a single event context
* per thread.
*
* @return The ssh_event object on success, NULL on failure.
*/
ssh_event ssh_event_new(void) {
ssh_event event;
event = malloc(sizeof(struct ssh_event_struct));
if (event == NULL) {
return NULL;
}
ZERO_STRUCTP(event);
event->ctx = ssh_poll_ctx_new(2);
if(event->ctx == NULL) {
free(event);
return NULL;
}
#ifdef WITH_SERVER
event->sessions = ssh_list_new();
if(event->sessions == NULL) {
ssh_poll_ctx_free(event->ctx);
free(event);
return NULL;
}
#endif
return event;
}
static int ssh_event_fd_wrapper_callback(ssh_poll_handle p, socket_t fd, int revents,
void *userdata) {
struct ssh_event_fd_wrapper *pw = (struct ssh_event_fd_wrapper *)userdata;
(void)p;
if(pw->cb != NULL) {
return pw->cb(fd, revents, pw->userdata);
}
return 0;
}
/**
* @brief Add a fd to the event and assign it a callback,
* when used in blocking mode.
* @param event The ssh_event
* @param fd Socket that will be polled.
* @param events Poll events that will be monitored for the socket. i.e.
* POLLIN, POLLPRI, POLLOUT
* @param cb Function to be called if any of the events are set.
* The prototype of cb is:
* int (*ssh_event_callback)(socket_t fd, int revents,
* void *userdata);
* @param userdata Userdata to be passed to the callback function. NULL if
* not needed.
*
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
int ssh_event_add_fd(ssh_event event, socket_t fd, short events,
ssh_event_callback cb, void *userdata) {
ssh_poll_handle p;
struct ssh_event_fd_wrapper *pw;
if(event == NULL || event->ctx == NULL || cb == NULL
|| fd == SSH_INVALID_SOCKET) {
return SSH_ERROR;
}
pw = malloc(sizeof(struct ssh_event_fd_wrapper));
if(pw == NULL) {
return SSH_ERROR;
}
pw->cb = cb;
pw->userdata = userdata;
/* pw is freed by ssh_event_remove_fd */
p = ssh_poll_new(fd, events, ssh_event_fd_wrapper_callback, pw);
if(p == NULL) {
free(pw);
return SSH_ERROR;
}
if(ssh_poll_ctx_add(event->ctx, p) < 0) {
free(pw);
ssh_poll_free(p);
return SSH_ERROR;
}
return SSH_OK;
}
/**
* @brief remove the poll handle from session and assign them to a event,
* when used in blocking mode.
*
* @param event The ssh_event object
* @param session The session to add to the event.
*
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
int ssh_event_add_session(ssh_event event, ssh_session session) {
unsigned int i;
ssh_poll_handle p;
#ifdef WITH_SERVER
struct ssh_iterator *iterator;
#endif
if(event == NULL || event->ctx == NULL || session == NULL) {
return SSH_ERROR;
}
if(session->default_poll_ctx == NULL) {
return SSH_ERROR;
}
for(i = 0; i < session->default_poll_ctx->polls_used; i++) {
p = session->default_poll_ctx->pollptrs[i];
ssh_poll_ctx_remove(session->default_poll_ctx, p);
ssh_poll_ctx_add(event->ctx, p);
}
#ifdef WITH_SERVER
iterator = ssh_list_get_iterator(event->sessions);
while(iterator != NULL) {
if((ssh_session)iterator->data == session) {
/* allow only one instance of this session */
return SSH_OK;
}
iterator = iterator->next;
}
if(ssh_list_append(event->sessions, session) == SSH_ERROR) {
return SSH_ERROR;
}
#endif
return SSH_OK;
}
/**
* @brief Poll all the sockets and sessions associated through an event object.
* If any of the events are set after the poll, the
* call back functions of the sessions or sockets will be called.
* This function should be called once within the programs main loop.
*
* @param event The ssh_event object to poll.
* @param timeout An upper limit on the time for which the poll will
* block, in milliseconds. Specifying a negative value
* means an infinite timeout. This parameter is passed to
* the poll() function.
* @returns SSH_OK No error.
* SSH_ERROR Error happened during the poll.
*/
int ssh_event_dopoll(ssh_event event, int timeout) {
int rc;
if(event == NULL || event->ctx == NULL) {
return SSH_ERROR;
}
rc = ssh_poll_ctx_dopoll(event->ctx, timeout);
return rc;
}
/**
* @brief Remove a socket fd from an event context.
*
* @param event The ssh_event object.
* @param fd The fd to remove.
*
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
int ssh_event_remove_fd(ssh_event event, socket_t fd) {
register size_t i, used;
int rc = SSH_ERROR;
if(event == NULL || event->ctx == NULL) {
return SSH_ERROR;
}
used = event->ctx->polls_used;
for (i = 0; i < used; i++) {
if(fd == event->ctx->pollfds[i].fd) {
ssh_poll_handle p = event->ctx->pollptrs[i];
struct ssh_event_fd_wrapper *pw = p->cb_data;
ssh_poll_ctx_remove(event->ctx, p);
free(pw);
ssh_poll_free(p);
rc = SSH_OK;
/* restart the loop */
used = event->ctx->polls_used;
i = 0;
}
}
return rc;
}
/**
* @brief Remove a session object from an event context.
*
* @param event The ssh_event object.
* @param session The session to remove.
*
* @returns SSH_OK on success
* SSH_ERROR on failure
*/
int ssh_event_remove_session(ssh_event event, ssh_session session) {
ssh_poll_handle p;
register size_t i, used;
int rc = SSH_ERROR;
socket_t session_fd;
#ifdef WITH_SERVER
struct ssh_iterator *iterator;
#endif
if(event == NULL || event->ctx == NULL || session == NULL) {
return SSH_ERROR;
}
session_fd = ssh_get_fd(session);
used = event->ctx->polls_used;
for(i = 0; i < used; i++) {
if(session_fd == event->ctx->pollfds[i].fd) {
p = event->ctx->pollptrs[i];
ssh_poll_ctx_remove(event->ctx, p);
ssh_poll_ctx_add(session->default_poll_ctx, p);
rc = SSH_OK;
}
}
#ifdef WITH_SERVER
iterator = ssh_list_get_iterator(event->sessions);
while(iterator != NULL) {
if((ssh_session)iterator->data == session) {
ssh_list_remove(event->sessions, iterator);
/* there should be only one instance of this session */
break;
}
iterator = iterator->next;
}
#endif
return rc;
}
/**
* @brief Free an event context.
*
* @param event The ssh_event object to free.
* Note: you have to manually remove sessions and socket
* fds before freeing the event object.
*
*/
void ssh_event_free(ssh_event event) {
if(event == NULL) {
return;
}
if(event->ctx != NULL) {
ssh_poll_ctx_free(event->ctx);
}
#ifdef WITH_SERVER
if(event->sessions != NULL) {
ssh_list_free(event->sessions);
}
#endif
free(event);
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

809
libssh/src/scp.c Normal file
View file

@ -0,0 +1,809 @@
/*
* scp - SSH scp wrapper functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2009 by Aris Adamantiadis <aris@0xbadc0de.be>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/scp.h"
/**
* @defgroup libssh_scp The SSH scp functions
* @ingroup libssh
*
* SCP protocol over SSH functions
*
* @{
*/
/**
* @brief Create a new scp session.
*
* @param[in] session The SSH session to use.
*
* @param[in] mode One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you
* need to drop files remotely or read them.
* It is not possible to combine read and write.
* SSH_SCP_RECURSIVE Flag can be or'ed to this to indicate
* that you're going to use recursion. Browsing through
* directories is not possible without this.
*
* @param[in] location The directory in which write or read will be done. Any
* push or pull will be relative to this place.
* This can also be a pattern of files to download (read).
*
* @returns A ssh_scp handle, NULL if the creation was impossible.
*/
ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){
ssh_scp scp=malloc(sizeof(struct ssh_scp_struct));
if(scp == NULL){
ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp");
return NULL;
}
ZERO_STRUCTP(scp);
if((mode&~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && (mode &~SSH_SCP_RECURSIVE) != SSH_SCP_READ){
ssh_set_error(session,SSH_FATAL,"Invalid mode %d for ssh_scp_new()",mode);
ssh_scp_free(scp);
return NULL;
}
scp->location=strdup(location);
if (scp->location == NULL) {
ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp");
ssh_scp_free(scp);
return NULL;
}
scp->session=session;
scp->mode=mode & ~SSH_SCP_RECURSIVE;
scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0;
scp->channel=NULL;
scp->state=SSH_SCP_NEW;
return scp;
}
int ssh_scp_init(ssh_scp scp){
int r;
char execbuffer[1024];
uint8_t code;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_NEW){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_init called under invalid state");
return SSH_ERROR;
}
ssh_log(scp->session,SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'",
scp->mode==SSH_SCP_WRITE?"write":"read",
scp->recursive?"recursive ":"",
scp->location);
scp->channel=ssh_channel_new(scp->session);
if(scp->channel == NULL){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
r= ssh_channel_open_session(scp->channel);
if(r==SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
if(scp->mode == SSH_SCP_WRITE)
snprintf(execbuffer,sizeof(execbuffer),"scp -t %s %s",
scp->recursive ? "-r":"", scp->location);
else
snprintf(execbuffer,sizeof(execbuffer),"scp -f %s %s",
scp->recursive ? "-r":"", scp->location);
if(ssh_channel_request_exec(scp->channel,execbuffer) == SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
if(scp->mode == SSH_SCP_WRITE){
r=ssh_channel_read(scp->channel,&code,1,0);
if(r<=0){
ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
if(code != 0){
ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
} else {
ssh_channel_write(scp->channel,"",1);
}
if(scp->mode == SSH_SCP_WRITE)
scp->state=SSH_SCP_WRITE_INITED;
else
scp->state=SSH_SCP_READ_INITED;
return SSH_OK;
}
int ssh_scp_close(ssh_scp scp){
char buffer[128];
int err;
if(scp==NULL)
return SSH_ERROR;
if(scp->channel != NULL){
if(ssh_channel_send_eof(scp->channel) == SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
/* avoid situations where data are buffered and
* not yet stored on disk. This can happen if the close is sent
* before we got the EOF back
*/
while(!ssh_channel_is_eof(scp->channel)){
err=ssh_channel_read(scp->channel,buffer,sizeof(buffer),0);
if(err==SSH_ERROR || err==0)
break;
}
if(ssh_channel_close(scp->channel) == SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
ssh_channel_free(scp->channel);
scp->channel=NULL;
}
scp->state=SSH_SCP_NEW;
return SSH_OK;
}
void ssh_scp_free(ssh_scp scp){
if(scp==NULL)
return;
if(scp->state != SSH_SCP_NEW)
ssh_scp_close(scp);
if(scp->channel)
ssh_channel_free(scp->channel);
SAFE_FREE(scp->location);
SAFE_FREE(scp->request_name);
SAFE_FREE(scp->warning);
SAFE_FREE(scp);
}
/**
* @brief Create a directory in a scp in sink mode.
*
* @param[in] scp The scp handle.
*
* @param[in] dirname The name of the directory being created.
*
* @param[in] mode The UNIX permissions for the new directory, e.g. 0755.
*
* @returns SSH_OK if the directory has been created, SSH_ERROR if
* an error occured.
*
* @see ssh_scp_leave_directory()
*/
int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode){
char buffer[1024];
int r;
uint8_t code;
char *dir;
char *perms;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_WRITE_INITED){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_directory called under invalid state");
return SSH_ERROR;
}
dir=ssh_basename(dirname);
perms=ssh_scp_string_mode(mode);
snprintf(buffer, sizeof(buffer), "D%s 0 %s\n", perms, dir);
SAFE_FREE(dir);
SAFE_FREE(perms);
r=ssh_channel_write(scp->channel,buffer,strlen(buffer));
if(r==SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
r=ssh_channel_read(scp->channel,&code,1,0);
if(r<=0){
ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
if(code != 0){
ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
return SSH_OK;
}
/**
* @brief Leave a directory.
*
* @returns SSH_OK if the directory has been left,SSH_ERROR if an
* error occured.
*
* @see ssh_scp_push_directory()
*/
int ssh_scp_leave_directory(ssh_scp scp){
char buffer[]="E\n";
int r;
uint8_t code;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_WRITE_INITED){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_leave_directory called under invalid state");
return SSH_ERROR;
}
r=ssh_channel_write(scp->channel,buffer,strlen(buffer));
if(r==SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
r=ssh_channel_read(scp->channel,&code,1,0);
if(r<=0){
ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
if(code != 0){
ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
return SSH_OK;
}
/**
* @brief Initialize the sending of a file to a scp in sink mode, using a 64-bit size.
*
* @param[in] scp The scp handle.
*
* @param[in] filename The name of the file being sent. It should not contain
* any path indicator
*
* @param[in] size Exact size in bytes of the file being sent.
*
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
*
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
* error occured.
*
* @see ssh_scp_push_file()
*/
int ssh_scp_push_file64(ssh_scp scp, const char *filename, uint64_t size, int mode){
char buffer[1024];
int r;
uint8_t code;
char *file;
char *perms;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_WRITE_INITED){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_file called under invalid state");
return SSH_ERROR;
}
file=ssh_basename(filename);
perms=ssh_scp_string_mode(mode);
ssh_log(scp->session,SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIu64 " with permissions '%s'",file,size,perms);
snprintf(buffer, sizeof(buffer), "C%s %" PRIu64 " %s\n", perms, size, file);
SAFE_FREE(file);
SAFE_FREE(perms);
r=ssh_channel_write(scp->channel,buffer,strlen(buffer));
if(r==SSH_ERROR){
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
r=ssh_channel_read(scp->channel,&code,1,0);
if(r<=0){
ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
if(code != 0){
ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
scp->filelen = size;
scp->processed = 0;
scp->state=SSH_SCP_WRITE_WRITING;
return SSH_OK;
}
/**
* @brief Initialize the sending of a file to a scp in sink mode.
*
* @param[in] scp The scp handle.
*
* @param[in] filename The name of the file being sent. It should not contain
* any path indicator
*
* @param[in] size Exact size in bytes of the file being sent.
*
* @param[in] mode The UNIX permissions for the new file, e.g. 0644.
*
* @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
* error occured.
*/
int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode){
return ssh_scp_push_file64(scp, filename, (uint64_t) size, mode);
}
/**
* @internal
*
* @brief Wait for a response of the scp server.
*
* @param[in] scp The scp handle.
*
* @param[out] response A pointer where the response message must be copied if
* any. This pointer must then be free'd.
*
* @returns The return code, SSH_ERROR a error occured.
*/
int ssh_scp_response(ssh_scp scp, char **response){
unsigned char code;
int r;
char msg[128];
if(scp==NULL)
return SSH_ERROR;
r=ssh_channel_read(scp->channel,&code,1,0);
if(r == SSH_ERROR)
return SSH_ERROR;
if(code == 0)
return 0;
if(code > 2){
ssh_set_error(scp->session,SSH_FATAL, "SCP: invalid status code %ud received", code);
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
r=ssh_scp_read_string(scp,msg,sizeof(msg));
if(r==SSH_ERROR)
return r;
/* Warning */
if(code == 1){
ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Warning: status code 1 received: %s", msg);
ssh_log(scp->session,SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg);
if(response)
*response=strdup(msg);
return 1;
}
if(code == 2){
ssh_set_error(scp->session,SSH_FATAL, "SCP: Error: status code 2 received: %s", msg);
if(response)
*response=strdup(msg);
return 2;
}
/* Not reached */
return SSH_ERROR;
}
/**
* @brief Write into a remote scp file.
*
* @param[in] scp The scp handle.
*
* @param[in] buffer The buffer to write.
*
* @param[in] len The number of bytes to write.
*
* @returns SSH_OK if the write was successful, SSH_ERROR an error
* occured while writing.
*/
int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len){
int w;
int r;
uint8_t code;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_WRITE_WRITING){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_write called under invalid state");
return SSH_ERROR;
}
if(scp->processed + len > scp->filelen)
len = (size_t) (scp->filelen - scp->processed);
/* hack to avoid waiting for window change */
r = ssh_channel_poll(scp->channel, 0);
if (r == SSH_ERROR) {
scp->state = SSH_SCP_ERROR;
return SSH_ERROR;
}
w=ssh_channel_write(scp->channel,buffer,len);
if(w != SSH_ERROR)
scp->processed += w;
else {
scp->state=SSH_SCP_ERROR;
//return=channel_get_exit_status(scp->channel);
return SSH_ERROR;
}
/* Far end sometimes send a status message, which we need to read
* and handle */
r = ssh_channel_poll(scp->channel,0);
if(r > 0){
r = ssh_channel_read(scp->channel, &code, 1, 0);
if(r == SSH_ERROR){
return SSH_ERROR;
}
if(code == 1 || code == 2){
ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Error: status code %i received", code);
return SSH_ERROR;
}
}
/* Check if we arrived at end of file */
if(scp->processed == scp->filelen) {
code = 0;
w = ssh_channel_write(scp->channel, &code, 1);
if(w == SSH_ERROR){
scp->state = SSH_SCP_ERROR;
return SSH_ERROR;
}
scp->processed=scp->filelen=0;
scp->state=SSH_SCP_WRITE_INITED;
}
return SSH_OK;
}
/**
* @brief Read a string on a channel, terminated by '\n'
*
* @param[in] scp The scp handle.
*
* @param[out] buffer A pointer to a buffer to place the string.
*
* @param[in] len The size of the buffer in bytes. If the string is bigger
* than len-1, only len-1 bytes are read and the string is
* null-terminated.
*
* @returns SSH_OK if the string was read, SSH_ERROR if an error
* occured while reading.
*/
int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){
size_t r=0;
int err=SSH_OK;
if(scp==NULL)
return SSH_ERROR;
while(r<len-1){
err=ssh_channel_read(scp->channel,&buffer[r],1,0);
if(err==SSH_ERROR){
break;
}
if(err==0){
ssh_set_error(scp->session,SSH_FATAL,"End of file while reading string");
err=SSH_ERROR;
break;
}
r++;
if(buffer[r-1] == '\n')
break;
}
buffer[r]=0;
return err;
}
/**
* @brief Wait for a scp request (file, directory).
*
* @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending
* a file
* SSH_SCP_REQUEST_NEWDIR: The other side is sending
* a directory
* SSH_SCP_REQUEST_ENDDIR: The other side has
* finished with the current
* directory
* SSH_SCP_REQUEST_WARNING: The other side sent us a warning
* SSH_SCP_REQUEST_EOF: The other side finished sending us
* files and data.
* SSH_ERROR: Some error happened
*
* @see ssh_scp_read()
* @see ssh_scp_deny_request()
* @see ssh_scp_accept_request()
* @see ssh_scp_request_get_warning()
*/
int ssh_scp_pull_request(ssh_scp scp){
char buffer[4096] = {0};
char *mode=NULL;
char *p,*tmp;
uint64_t size;
char *name=NULL;
int err;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_READ_INITED){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_pull_request called under invalid state");
return SSH_ERROR;
}
err=ssh_scp_read_string(scp,buffer,sizeof(buffer));
if(err==SSH_ERROR){
if(ssh_channel_is_eof(scp->channel)){
scp->state=SSH_SCP_TERMINATED;
return SSH_SCP_REQUEST_EOF;
}
return err;
}
p=strchr(buffer,'\n');
if(p!=NULL)
*p='\0';
ssh_log(scp->session,SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer);
switch(buffer[0]){
case 'C':
/* File */
case 'D':
/* Directory */
p=strchr(buffer,' ');
if(p==NULL)
goto error;
*p='\0';
p++;
//mode=strdup(&buffer[1]);
scp->request_mode=ssh_scp_integer_mode(&buffer[1]);
tmp=p;
p=strchr(p,' ');
if(p==NULL)
goto error;
*p=0;
size = strtoull(tmp,NULL,10);
p++;
name=strdup(p);
SAFE_FREE(scp->request_name);
scp->request_name=name;
if(buffer[0]=='C'){
scp->filelen=size;
scp->request_type=SSH_SCP_REQUEST_NEWFILE;
} else {
scp->filelen='0';
scp->request_type=SSH_SCP_REQUEST_NEWDIR;
}
scp->state=SSH_SCP_READ_REQUESTED;
scp->processed = 0;
return scp->request_type;
break;
case 'E':
scp->request_type=SSH_SCP_REQUEST_ENDDIR;
ssh_channel_write(scp->channel,"",1);
return scp->request_type;
case 0x1:
ssh_set_error(scp->session,SSH_REQUEST_DENIED,"SCP: Warning: %s",&buffer[1]);
scp->request_type=SSH_SCP_REQUEST_WARNING;
SAFE_FREE(scp->warning);
scp->warning=strdup(&buffer[1]);
return scp->request_type;
case 0x2:
ssh_set_error(scp->session,SSH_FATAL,"SCP: Error: %s",&buffer[1]);
return SSH_ERROR;
case 'T':
/* Timestamp */
default:
ssh_set_error(scp->session,SSH_FATAL,"Unhandled message: (%d)%s",buffer[0],buffer);
return SSH_ERROR;
}
/* a parsing error occured */
error:
SAFE_FREE(name);
SAFE_FREE(mode);
ssh_set_error(scp->session,SSH_FATAL,"Parsing error while parsing message: %s",buffer);
return SSH_ERROR;
}
/**
* @brief Deny the transfer of a file or creation of a directory coming from the
* remote party.
*
* @param[in] scp The scp handle.
* @param[in] reason A nul-terminated string with a human-readable
* explanation of the deny.
*
* @returns SSH_OK if the message was sent, SSH_ERROR if the sending
* the message failed, or sending it in a bad state.
*/
int ssh_scp_deny_request(ssh_scp scp, const char *reason){
char buffer[4096];
int err;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_READ_REQUESTED){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state");
return SSH_ERROR;
}
snprintf(buffer,sizeof(buffer),"%c%s\n",2,reason);
err=ssh_channel_write(scp->channel,buffer,strlen(buffer));
if(err==SSH_ERROR) {
return SSH_ERROR;
}
else {
scp->state=SSH_SCP_READ_INITED;
return SSH_OK;
}
}
/**
* @brief Accepts transfer of a file or creation of a directory coming from the
* remote party.
*
* @param[in] scp The scp handle.
*
* @returns SSH_OK if the message was sent, SSH_ERROR if sending the
* message failed, or sending it in a bad state.
*/
int ssh_scp_accept_request(ssh_scp scp){
char buffer[]={0x00};
int err;
if(scp==NULL)
return SSH_ERROR;
if(scp->state != SSH_SCP_READ_REQUESTED){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state");
return SSH_ERROR;
}
err=ssh_channel_write(scp->channel,buffer,1);
if(err==SSH_ERROR) {
return SSH_ERROR;
}
if(scp->request_type==SSH_SCP_REQUEST_NEWFILE)
scp->state=SSH_SCP_READ_READING;
else
scp->state=SSH_SCP_READ_INITED;
return SSH_OK;
}
/** @brief Read from a remote scp file
* @param[in] scp The scp handle.
*
* @param[in] buffer The destination buffer.
*
* @param[in] size The size of the buffer.
*
* @returns The nNumber of bytes read, SSH_ERROR if an error occured
* while reading.
*/
int ssh_scp_read(ssh_scp scp, void *buffer, size_t size){
int r;
int code;
if(scp==NULL)
return SSH_ERROR;
if(scp->state == SSH_SCP_READ_REQUESTED && scp->request_type == SSH_SCP_REQUEST_NEWFILE){
r=ssh_scp_accept_request(scp);
if(r==SSH_ERROR)
return r;
}
if(scp->state != SSH_SCP_READ_READING){
ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_read called under invalid state");
return SSH_ERROR;
}
if(scp->processed + size > scp->filelen)
size = (size_t) (scp->filelen - scp->processed);
if(size > 65536)
size=65536; /* avoid too large reads */
r=ssh_channel_read(scp->channel,buffer,size,0);
if(r != SSH_ERROR)
scp->processed += r;
else {
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
/* Check if we arrived at end of file */
if(scp->processed == scp->filelen) {
scp->processed=scp->filelen=0;
ssh_channel_write(scp->channel,"",1);
code=ssh_scp_response(scp,NULL);
if(code == 0){
scp->state=SSH_SCP_READ_INITED;
return r;
}
if(code==1){
scp->state=SSH_SCP_READ_INITED;
return SSH_ERROR;
}
scp->state=SSH_SCP_ERROR;
return SSH_ERROR;
}
return r;
}
/**
* @brief Get the name of the directory or file being pushed from the other
* party.
*
* @returns The file name, NULL on error. The string should not be
* freed.
*/
const char *ssh_scp_request_get_filename(ssh_scp scp){
if(scp==NULL)
return NULL;
return scp->request_name;
}
/**
* @brief Get the permissions of the directory or file being pushed from the
* other party.
*
* @returns The UNIX permission, e.g 0644, -1 on error.
*/
int ssh_scp_request_get_permissions(ssh_scp scp){
if(scp==NULL)
return -1;
return scp->request_mode;
}
/** @brief Get the size of the file being pushed from the other party.
*
* @returns The numeric size of the file being read.
* @warning The real size may not fit in a 32 bits field and may
* be truncated.
* @see ssh_scp_request_get_size64()
*/
size_t ssh_scp_request_get_size(ssh_scp scp){
if(scp==NULL)
return 0;
return scp->filelen;
}
/** @brief Get the size of the file being pushed from the other party.
*
* @returns The numeric size of the file being read.
*/
uint64_t ssh_scp_request_get_size64(ssh_scp scp){
if(scp==NULL)
return 0;
return scp->filelen;
}
/**
* @brief Convert a scp text mode to an integer.
*
* @param[in] mode The mode to convert, e.g. "0644".
*
* @returns An integer value, e.g. 420 for "0644".
*/
int ssh_scp_integer_mode(const char *mode){
int value=strtoul(mode,NULL,8) & 0xffff;
return value;
}
/**
* @brief Convert a unix mode into a scp string.
*
* @param[in] mode The mode to convert, e.g. 420 or 0644.
*
* @returns A pointer to a malloc'ed string containing the scp mode,
* e.g. "0644".
*/
char *ssh_scp_string_mode(int mode){
char buffer[16];
snprintf(buffer,sizeof(buffer),"%.4o",mode);
return strdup(buffer);
}
/**
* @brief Get the warning string from a scp handle.
*
* @param[in] scp The scp handle.
*
* @returns A warning string, or NULL on error. The string should
* not be freed.
*/
const char *ssh_scp_request_get_warning(ssh_scp scp){
if(scp==NULL)
return NULL;
return scp->warning;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

1205
libssh/src/server.c Normal file

File diff suppressed because it is too large Load diff

737
libssh/src/session.c Normal file
View file

@ -0,0 +1,737 @@
/*
* session.c - non-networking functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2005-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include "libssh/priv.h"
#include "libssh/libssh.h"
#include "libssh/crypto.h"
#include "libssh/server.h"
#include "libssh/socket.h"
#include "libssh/ssh2.h"
#include "libssh/agent.h"
#include "libssh/packet.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/buffer.h"
#include "libssh/poll.h"
#define FIRST_CHANNEL 42 // why not ? it helps to find bugs.
/**
* @defgroup libssh_session The SSH session functions.
* @ingroup libssh
*
* Functions that manage a session.
*
* @{
*/
/**
* @brief Create a new ssh session.
*
* @returns A new ssh_session pointer, NULL on error.
*/
ssh_session ssh_new(void) {
ssh_session session;
char *id = NULL;
int rc;
session = malloc(sizeof (struct ssh_session_struct));
if (session == NULL) {
return NULL;
}
ZERO_STRUCTP(session);
session->next_crypto = crypto_new();
if (session->next_crypto == NULL) {
goto err;
}
session->socket = ssh_socket_new(session);
if (session->socket == NULL) {
goto err;
}
session->out_buffer = ssh_buffer_new();
if (session->out_buffer == NULL) {
goto err;
}
session->in_buffer=ssh_buffer_new();
if (session->in_buffer == NULL) {
goto err;
}
session->alive = 0;
session->auth_methods = 0;
ssh_set_blocking(session, 1);
session->common.log_indent = 0;
session->maxchannel = FIRST_CHANNEL;
#ifndef _WIN32
session->agent = agent_new(session);
if (session->agent == NULL) {
goto err;
}
#endif /* _WIN32 */
/* OPTIONS */
session->opts.StrictHostKeyChecking = 1;
session->opts.port = 22;
session->opts.fd = -1;
session->opts.ssh2 = 1;
session->opts.compressionlevel=7;
#ifdef WITH_SSH1
session->opts.ssh1 = 1;
#else
session->opts.ssh1 = 0;
#endif
session->opts.identity = ssh_list_new();
if (session->opts.identity == NULL) {
goto err;
}
id = strdup("%d/id_rsa");
if (id == NULL) {
goto err;
}
rc = ssh_list_append(session->opts.identity, id);
if (rc == SSH_ERROR) {
goto err;
}
id = strdup("%d/id_dsa");
if (id == NULL) {
goto err;
}
rc = ssh_list_append(session->opts.identity, id);
if (rc == SSH_ERROR) {
goto err;
}
id = strdup("%d/identity");
if (id == NULL) {
goto err;
}
rc = ssh_list_append(session->opts.identity, id);
if (rc == SSH_ERROR) {
goto err;
}
return session;
err:
free(id);
ssh_free(session);
return NULL;
}
/**
* @brief Deallocate a SSH session handle.
*
* @param[in] session The SSH session to free.
*
* @see ssh_disconnect()
* @see ssh_new()
*/
void ssh_free(ssh_session session) {
int i;
struct ssh_iterator *it;
if (session == NULL) {
return;
}
/*
* Delete all channels
*
* This needs the first thing we clean up cause if there is still an open
* channel we call ssh_channel_close() first. So we need a working socket
* and poll context for it.
*/
for (it = ssh_list_get_iterator(session->channels);
it != NULL;
it = ssh_list_get_iterator(session->channels)) {
ssh_channel_do_free(ssh_iterator_value(ssh_channel,it));
ssh_list_remove(session->channels, it);
}
ssh_list_free(session->channels);
session->channels = NULL;
#ifdef WITH_PCAP
if (session->pcap_ctx) {
ssh_pcap_context_free(session->pcap_ctx);
session->pcap_ctx = NULL;
}
#endif
ssh_socket_free(session->socket);
session->socket = NULL;
if (session->default_poll_ctx) {
ssh_poll_ctx_free(session->default_poll_ctx);
}
ssh_buffer_free(session->in_buffer);
ssh_buffer_free(session->out_buffer);
session->in_buffer = session->out_buffer = NULL;
if (session->in_hashbuf != NULL) {
ssh_buffer_free(session->in_hashbuf);
}
if (session->out_hashbuf != NULL) {
ssh_buffer_free(session->out_hashbuf);
}
crypto_free(session->current_crypto);
crypto_free(session->next_crypto);
#ifndef _WIN32
agent_free(session->agent);
#endif /* _WIN32 */
ssh_key_free(session->srv.dsa_key);
ssh_key_free(session->srv.rsa_key);
if (session->ssh_message_list) {
ssh_message msg;
for (msg = ssh_list_pop_head(ssh_message, session->ssh_message_list);
msg != NULL;
msg = ssh_list_pop_head(ssh_message, session->ssh_message_list)) {
ssh_message_free(msg);
}
ssh_list_free(session->ssh_message_list);
}
if (session->packet_callbacks) {
ssh_list_free(session->packet_callbacks);
}
/* options */
if (session->opts.identity) {
char *id;
for (id = ssh_list_pop_head(char *, session->opts.identity);
id != NULL;
id = ssh_list_pop_head(char *, session->opts.identity)) {
SAFE_FREE(id);
}
ssh_list_free(session->opts.identity);
}
SAFE_FREE(session->serverbanner);
SAFE_FREE(session->clientbanner);
SAFE_FREE(session->banner);
SAFE_FREE(session->opts.bindaddr);
SAFE_FREE(session->opts.username);
SAFE_FREE(session->opts.host);
SAFE_FREE(session->opts.sshdir);
SAFE_FREE(session->opts.knownhosts);
SAFE_FREE(session->opts.ProxyCommand);
for (i = 0; i < 10; i++) {
if (session->opts.wanted_methods[i]) {
SAFE_FREE(session->opts.wanted_methods[i]);
}
}
/* burn connection, it could hang sensitive datas */
ZERO_STRUCTP(session);
SAFE_FREE(session);
}
/**
* @brief get the server banner
* @param[in] session The SSH session
*/
const char* ssh_get_serverbanner(ssh_session session) {
if(!session) {
return NULL;
}
return session->serverbanner;
}
/**
* @brief Disconnect impolitely from a remote host by closing the socket.
*
* Suitable if you forked and want to destroy this session.
*
* @param[in] session The SSH session to disconnect.
*/
void ssh_silent_disconnect(ssh_session session) {
enter_function();
if (session == NULL) {
return;
}
ssh_socket_close(session->socket);
session->alive = 0;
ssh_disconnect(session);
leave_function();
}
/**
* @brief Set the session in blocking/nonblocking mode.
*
* @param[in] session The ssh session to change.
*
* @param[in] blocking Zero for nonblocking mode.
*
* \bug nonblocking code is in development and won't work as expected
*/
void ssh_set_blocking(ssh_session session, int blocking) {
if (session == NULL) {
return;
}
session->flags &= ~SSH_SESSION_FLAG_BLOCKING;
session->flags |= blocking ? SSH_SESSION_FLAG_BLOCKING : 0;
}
/**
* @brief Return the blocking mode of libssh
* @param[in] session The SSH session
* @returns 0 if the session is nonblocking,
* @returns 1 if the functions may block.
*/
int ssh_is_blocking(ssh_session session){
return (session->flags&SSH_SESSION_FLAG_BLOCKING) ? 1 : 0;
}
/* Waits until the output socket is empty */
static int ssh_flush_termination(void *c){
ssh_session session = c;
if (ssh_socket_buffered_write_bytes(session->socket) == 0 ||
session->session_state == SSH_SESSION_STATE_ERROR)
return 1;
else
return 0;
}
/**
* @brief Blocking flush of the outgoing buffer
* @param[in] session The SSH session
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying -1
* means an infinite timeout. This parameter is passed to
* the poll() function.
* @returns SSH_OK on success, SSH_AGAIN if timeout occurred,
* SSH_ERROR otherwise.
*/
int ssh_blocking_flush(ssh_session session, int timeout){
int rc;
if(!session)
return SSH_ERROR;
enter_function();
rc = ssh_handle_packets_termination(session, timeout,
ssh_flush_termination, session);
if (rc == SSH_ERROR)
goto end;
if (!ssh_flush_termination(session))
rc = SSH_AGAIN;
end:
leave_function();
return rc;
}
/**
* @brief Check if we are connected.
*
* @param[in] session The session to check if it is connected.
*
* @return 1 if we are connected, 0 if not.
*/
int ssh_is_connected(ssh_session session) {
if (session == NULL) {
return 0;
}
return session->alive;
}
/**
* @brief Get the fd of a connection.
*
* In case you'd need the file descriptor of the connection to the server/client.
*
* @param[in] session The ssh session to use.
*
* @return The file descriptor of the connection, or -1 if it is
* not connected
*/
socket_t ssh_get_fd(ssh_session session) {
if (session == NULL) {
return -1;
}
return ssh_socket_get_fd_in(session->socket);
}
/**
* @brief Tell the session it has data to read on the file descriptor without
* blocking.
*
* @param[in] session The ssh session to use.
*/
void ssh_set_fd_toread(ssh_session session) {
if (session == NULL) {
return;
}
ssh_socket_set_read_wontblock(session->socket);
}
/**
* @brief Tell the session it may write to the file descriptor without blocking.
*
* @param[in] session The ssh session to use.
*/
void ssh_set_fd_towrite(ssh_session session) {
if (session == NULL) {
return;
}
ssh_socket_set_write_wontblock(session->socket);
}
/**
* @brief Tell the session it has an exception to catch on the file descriptor.
*
* \param[in] session The ssh session to use.
*/
void ssh_set_fd_except(ssh_session session) {
if (session == NULL) {
return;
}
ssh_socket_set_except(session->socket);
}
/**
* @internal
*
* @brief Poll the current session for an event and call the appropriate
* callbacks. This function will not loop until the timeout is expired.
*
* This will block until one event happens.
*
* @param[in] session The session handle to use.
*
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE
* (-1) means an infinite timeout.
* Specifying SSH_TIMEOUT_USER means to use the timeout
* specified in options. 0 means poll will return immediately.
* This parameter is passed to the poll() function.
*
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
int ssh_handle_packets(ssh_session session, int timeout) {
ssh_poll_handle spoll_in,spoll_out;
ssh_poll_ctx ctx;
int tm = timeout;
int rc;
if (session == NULL || session->socket == NULL) {
return SSH_ERROR;
}
enter_function();
spoll_in = ssh_socket_get_poll_handle_in(session->socket);
spoll_out = ssh_socket_get_poll_handle_out(session->socket);
if (session->server) {
ssh_poll_add_events(spoll_in, POLLIN);
}
ctx = ssh_poll_get_ctx(spoll_in);
if (!ctx) {
ctx = ssh_poll_get_default_ctx(session);
ssh_poll_ctx_add(ctx, spoll_in);
if (spoll_in != spoll_out) {
ssh_poll_ctx_add(ctx, spoll_out);
}
}
if (timeout == SSH_TIMEOUT_USER) {
if (ssh_is_blocking(session))
tm = ssh_make_milliseconds(session->opts.timeout,
session->opts.timeout_usec);
else
tm = 0;
}
rc = ssh_poll_ctx_dopoll(ctx, tm);
if (rc == SSH_ERROR) {
session->session_state = SSH_SESSION_STATE_ERROR;
}
leave_function();
return rc;
}
/**
* @internal
*
* @brief Poll the current session for an event and call the appropriate
* callbacks.
*
* This will block until termination function returns true, or timeout expired.
*
* @param[in] session The session handle to use.
*
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE
* (-1) means an infinite timeout.
* Specifying SSH_TIMEOUT_USER means to use the timeout
* specified in options. 0 means poll will return immediately.
* This parameter is passed to the poll() function.
*
* @param[in] fct Termination function to be used to determine if it is
* possible to stop polling.
* @param[in] user User parameter to be passed to fct termination function.
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
int ssh_handle_packets_termination(ssh_session session, int timeout,
ssh_termination_function fct, void *user){
int ret = SSH_OK;
struct ssh_timestamp ts;
int tm;
if (timeout == SSH_TIMEOUT_USER) {
if (ssh_is_blocking(session))
timeout = ssh_make_milliseconds(session->opts.timeout,
session->opts.timeout_usec);
else
timeout = SSH_TIMEOUT_NONBLOCKING;
}
ssh_timestamp_init(&ts);
tm = timeout;
while(!fct(user)){
ret = ssh_handle_packets(session, tm);
if(ret == SSH_ERROR)
break;
if(ssh_timeout_elapsed(&ts,timeout))
break;
tm = ssh_timeout_update(&ts, timeout);
}
return ret;
}
/**
* @brief Get session status
*
* @param session The ssh session to use.
*
* @returns A bitmask including SSH_CLOSED, SSH_READ_PENDING, SSH_WRITE_PENDING
* or SSH_CLOSED_ERROR which respectively means the session is closed,
* has data to read on the connection socket and session was closed
* due to an error.
*/
int ssh_get_status(ssh_session session) {
int socketstate;
int r = 0;
if (session == NULL) {
return 0;
}
socketstate = ssh_socket_get_status(session->socket);
if (session->closed) {
r |= SSH_CLOSED;
}
if (socketstate & SSH_READ_PENDING) {
r |= SSH_READ_PENDING;
}
if (socketstate & SSH_WRITE_PENDING) {
r |= SSH_WRITE_PENDING;
}
if (session->closed && (socketstate & SSH_CLOSED_ERROR)) {
r |= SSH_CLOSED_ERROR;
}
return r;
}
/**
* @brief Get the disconnect message from the server.
*
* @param[in] session The ssh session to use.
*
* @return The message sent by the server along with the
* disconnect, or NULL in which case the reason of the
* disconnect may be found with ssh_get_error.
*
* @see ssh_get_error()
*/
const char *ssh_get_disconnect_message(ssh_session session) {
if (session == NULL) {
return NULL;
}
if (!session->closed) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Connection not closed yet");
} else if(session->closed_by_except) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Connection closed by socket error");
} else if(!session->discon_msg) {
ssh_set_error(session, SSH_FATAL,
"Connection correctly closed but no disconnect message");
} else {
return session->discon_msg;
}
return NULL;
}
/**
* @brief Get the protocol version of the session.
*
* @param session The ssh session to use.
*
* @return 1 or 2, for ssh1 or ssh2, < 0 on error.
*/
int ssh_get_version(ssh_session session) {
if (session == NULL) {
return -1;
}
return session->version;
}
/**
* @internal
* @brief Callback to be called when the socket received an exception code.
* @param user is a pointer to session
*/
void ssh_socket_exception_callback(int code, int errno_code, void *user){
ssh_session session=(ssh_session)user;
enter_function();
ssh_log(session,SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code);
session->session_state=SSH_SESSION_STATE_ERROR;
ssh_set_error(session,SSH_FATAL,"Socket error: %s",strerror(errno_code));
session->ssh_connection_callback(session);
leave_function();
}
/**
* @brief Send a message that should be ignored
*
* @param[in] session The SSH session
* @param[in] data Data to be sent
*
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
int ssh_send_ignore (ssh_session session, const char *data) {
ssh_string str;
if (ssh_socket_is_open(session->socket)) {
if (buffer_add_u8(session->out_buffer, SSH2_MSG_IGNORE) < 0) {
goto error;
}
str = ssh_string_from_char(data);
if (str == NULL) {
goto error;
}
if (buffer_add_ssh_string(session->out_buffer, str) < 0) {
ssh_string_free(str);
goto error;
}
packet_send(session);
ssh_handle_packets(session, 0);
ssh_string_free(str);
}
return SSH_OK;
error:
buffer_reinit(session->out_buffer);
return SSH_ERROR;
}
/**
* @brief Send a debug message
*
* @param[in] session The SSH session
* @param[in] message Data to be sent
* @param[in] always_display Message SHOULD be displayed by the server. It
* SHOULD NOT be displayed unless debugging
* information has been explicitly requested.
*
* @return SSH_OK on success, SSH_ERROR otherwise.
*/
int ssh_send_debug (ssh_session session, const char *message, int always_display) {
ssh_string str;
int rc;
if (ssh_socket_is_open(session->socket)) {
if (buffer_add_u8(session->out_buffer, SSH2_MSG_DEBUG) < 0) {
goto error;
}
if (buffer_add_u8(session->out_buffer, always_display) < 0) {
goto error;
}
str = ssh_string_from_char(message);
if (str == NULL) {
goto error;
}
rc = buffer_add_ssh_string(session->out_buffer, str);
ssh_string_free(str);
if (rc < 0) {
goto error;
}
/* Empty language tag */
if (buffer_add_u32(session->out_buffer, 0) < 0) {
goto error;
}
packet_send(session);
ssh_handle_packets(session, 0);
}
return SSH_OK;
error:
buffer_reinit(session->out_buffer);
return SSH_ERROR;
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

3199
libssh/src/sftp.c Normal file

File diff suppressed because it is too large Load diff

518
libssh/src/sftpserver.c Normal file
View file

@ -0,0 +1,518 @@
/*
* sftpserver.c - server based function for the sftp protocol
*
* This file is part of the SSH Library
*
* Copyright (c) 2005 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/libssh.h"
#include "libssh/sftp.h"
#include "libssh/ssh2.h"
#include "libssh/priv.h"
#include "libssh/buffer.h"
#include "libssh/misc.h"
sftp_client_message sftp_get_client_message(sftp_session sftp) {
ssh_session session = sftp->session;
sftp_packet packet;
sftp_client_message msg;
ssh_buffer payload;
ssh_string tmp;
msg = malloc(sizeof (struct sftp_client_message_struct));
if (msg == NULL) {
ssh_set_error_oom(session);
return NULL;
}
ZERO_STRUCTP(msg);
packet = sftp_packet_read(sftp);
if (packet == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
payload = packet->payload;
msg->type = packet->type;
msg->sftp = sftp;
buffer_get_u32(payload, &msg->id);
switch(msg->type) {
case SSH_FXP_CLOSE:
case SSH_FXP_READDIR:
msg->handle = buffer_get_ssh_string(payload);
if (msg->handle == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_READ:
msg->handle = buffer_get_ssh_string(payload);
if (msg->handle == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
buffer_get_u64(payload, &msg->offset);
buffer_get_u32(payload, &msg->len);
break;
case SSH_FXP_WRITE:
msg->handle = buffer_get_ssh_string(payload);
if (msg->handle == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
buffer_get_u64(payload, &msg->offset);
msg->data = buffer_get_ssh_string(payload);
if (msg->data == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_REMOVE:
case SSH_FXP_RMDIR:
case SSH_FXP_OPENDIR:
case SSH_FXP_READLINK:
case SSH_FXP_REALPATH:
tmp = buffer_get_ssh_string(payload);
if (tmp == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->filename = ssh_string_to_char(tmp);
ssh_string_free(tmp);
if (msg->filename == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_RENAME:
case SSH_FXP_SYMLINK:
tmp = buffer_get_ssh_string(payload);
if (tmp == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->filename = ssh_string_to_char(tmp);
ssh_string_free(tmp);
if (msg->filename == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->data = buffer_get_ssh_string(payload);
if (msg->data == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_MKDIR:
case SSH_FXP_SETSTAT:
tmp = buffer_get_ssh_string(payload);
if (tmp == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->filename=ssh_string_to_char(tmp);
ssh_string_free(tmp);
if (msg->filename == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->attr = sftp_parse_attr(sftp, payload, 0);
if (msg->attr == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_FSETSTAT:
msg->handle = buffer_get_ssh_string(payload);
if (msg->handle == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->attr = sftp_parse_attr(sftp, payload, 0);
if (msg->attr == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_LSTAT:
case SSH_FXP_STAT:
tmp = buffer_get_ssh_string(payload);
if (tmp == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->filename = ssh_string_to_char(tmp);
ssh_string_free(tmp);
if (msg->filename == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
if(sftp->version > 3) {
buffer_get_u32(payload,&msg->flags);
}
break;
case SSH_FXP_OPEN:
tmp=buffer_get_ssh_string(payload);
if (tmp == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
msg->filename = ssh_string_to_char(tmp);
ssh_string_free(tmp);
if (msg->filename == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
buffer_get_u32(payload,&msg->flags);
msg->attr = sftp_parse_attr(sftp, payload, 0);
if (msg->attr == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
break;
case SSH_FXP_FSTAT:
msg->handle = buffer_get_ssh_string(payload);
if (msg->handle == NULL) {
ssh_set_error_oom(session);
sftp_client_message_free(msg);
return NULL;
}
buffer_get_u32(payload, &msg->flags);
break;
default:
ssh_set_error(sftp->session, SSH_FATAL,
"Received unhandled sftp message %d\n", msg->type);
sftp_client_message_free(msg);
return NULL;
}
msg->flags = ntohl(msg->flags);
msg->offset = ntohll(msg->offset);
msg->len = ntohl(msg->len);
sftp_packet_free(packet);
return msg;
}
void sftp_client_message_free(sftp_client_message msg) {
if (msg == NULL) {
return;
}
SAFE_FREE(msg->filename);
ssh_string_free(msg->data);
ssh_string_free(msg->handle);
sftp_attributes_free(msg->attr);
ZERO_STRUCTP(msg);
SAFE_FREE(msg);
}
int sftp_reply_name(sftp_client_message msg, const char *name,
sftp_attributes attr) {
ssh_buffer out;
ssh_string file;
out = ssh_buffer_new();
if (out == NULL) {
return -1;
}
file = ssh_string_from_char(name);
if (file == NULL) {
ssh_buffer_free(out);
return -1;
}
if (buffer_add_u32(out, msg->id) < 0 ||
buffer_add_u32(out, htonl(1)) < 0 ||
buffer_add_ssh_string(out, file) < 0 ||
buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */
buffer_add_attributes(out, attr) < 0 ||
sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
ssh_buffer_free(out);
ssh_string_free(file);
return -1;
}
ssh_buffer_free(out);
ssh_string_free(file);
return 0;
}
int sftp_reply_handle(sftp_client_message msg, ssh_string handle){
ssh_buffer out;
out = ssh_buffer_new();
if (out == NULL) {
return -1;
}
if (buffer_add_u32(out, msg->id) < 0 ||
buffer_add_ssh_string(out, handle) < 0 ||
sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) {
ssh_buffer_free(out);
return -1;
}
ssh_buffer_free(out);
return 0;
}
int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) {
ssh_buffer out;
out = ssh_buffer_new();
if (out == NULL) {
return -1;
}
if (buffer_add_u32(out, msg->id) < 0 ||
buffer_add_attributes(out, attr) < 0 ||
sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) {
ssh_buffer_free(out);
return -1;
}
ssh_buffer_free(out);
return 0;
}
int sftp_reply_names_add(sftp_client_message msg, const char *file,
const char *longname, sftp_attributes attr) {
ssh_string name;
name = ssh_string_from_char(file);
if (name == NULL) {
return -1;
}
if (msg->attrbuf == NULL) {
msg->attrbuf = ssh_buffer_new();
if (msg->attrbuf == NULL) {
ssh_string_free(name);
return -1;
}
}
if (buffer_add_ssh_string(msg->attrbuf, name) < 0) {
ssh_string_free(name);
return -1;
}
ssh_string_free(name);
name = ssh_string_from_char(longname);
if (name == NULL) {
return -1;
}
if (buffer_add_ssh_string(msg->attrbuf,name) < 0 ||
buffer_add_attributes(msg->attrbuf,attr) < 0) {
ssh_string_free(name);
return -1;
}
ssh_string_free(name);
msg->attr_num++;
return 0;
}
int sftp_reply_names(sftp_client_message msg) {
ssh_buffer out;
out = ssh_buffer_new();
if (out == NULL) {
ssh_buffer_free(msg->attrbuf);
return -1;
}
if (buffer_add_u32(out, msg->id) < 0 ||
buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
buffer_add_data(out, buffer_get_rest(msg->attrbuf),
buffer_get_rest_len(msg->attrbuf)) < 0 ||
sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
ssh_buffer_free(out);
ssh_buffer_free(msg->attrbuf);
return -1;
}
ssh_buffer_free(out);
ssh_buffer_free(msg->attrbuf);
msg->attr_num = 0;
msg->attrbuf = NULL;
return 0;
}
int sftp_reply_status(sftp_client_message msg, uint32_t status,
const char *message) {
ssh_buffer out;
ssh_string s;
out = ssh_buffer_new();
if (out == NULL) {
return -1;
}
s = ssh_string_from_char(message ? message : "");
if (s == NULL) {
ssh_buffer_free(out);
return -1;
}
if (buffer_add_u32(out, msg->id) < 0 ||
buffer_add_u32(out, htonl(status)) < 0 ||
buffer_add_ssh_string(out, s) < 0 ||
buffer_add_u32(out, 0) < 0 || /* language string */
sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) {
ssh_buffer_free(out);
ssh_string_free(s);
return -1;
}
ssh_buffer_free(out);
ssh_string_free(s);
return 0;
}
int sftp_reply_data(sftp_client_message msg, const void *data, int len) {
ssh_buffer out;
out = ssh_buffer_new();
if (out == NULL) {
return -1;
}
if (buffer_add_u32(out, msg->id) < 0 ||
buffer_add_u32(out, ntohl(len)) < 0 ||
buffer_add_data(out, data, len) < 0 ||
sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) {
ssh_buffer_free(out);
return -1;
}
ssh_buffer_free(out);
return 0;
}
/*
* This function will return you a new handle to give the client.
* the function accepts an info that can be retrieved later with
* the handle. Care is given that a corrupted handle won't give a
* valid info (or worse).
*/
ssh_string sftp_handle_alloc(sftp_session sftp, void *info) {
ssh_string ret;
uint32_t val;
int i;
if (sftp->handles == NULL) {
sftp->handles = malloc(sizeof(void *) * SFTP_HANDLES);
if (sftp->handles == NULL) {
return NULL;
}
memset(sftp->handles, 0, sizeof(void *) * SFTP_HANDLES);
}
for (i = 0; i < SFTP_HANDLES; i++) {
if (sftp->handles[i] == NULL) {
break;
}
}
if (i == SFTP_HANDLES) {
return NULL; /* no handle available */
}
val = i;
ret = ssh_string_new(4);
if (ret == NULL) {
return NULL;
}
memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
sftp->handles[i] = info;
return ret;
}
void *sftp_handle(sftp_session sftp, ssh_string handle){
uint32_t val;
if (sftp->handles == NULL) {
return NULL;
}
if (ssh_string_len(handle) != sizeof(uint32_t)) {
return NULL;
}
memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
if (val > SFTP_HANDLES) {
return NULL;
}
return sftp->handles[val];
}
void sftp_handle_remove(sftp_session sftp, void *handle) {
int i;
for (i = 0; i < SFTP_HANDLES; i++) {
if (sftp->handles[i] == handle) {
sftp->handles[i] = NULL;
break;
}
}
}
/* vim: set ts=2 sw=2 et cindent: */

846
libssh/src/socket.c Normal file
View file

@ -0,0 +1,846 @@
/*
* socket.c - socket functions for the library
*
* This file is part of the SSH Library
*
* Copyright (c) 2008-2010 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#if _MSC_VER >= 1400
#include <io.h>
#undef open
#define open _open
#undef close
#define close _close
#undef read
#define read _read
#undef write
#define write _write
#endif /* _MSC_VER */
#else /* _WIN32 */
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#endif /* _WIN32 */
#include "libssh/priv.h"
#include "libssh/callbacks.h"
#include "libssh/socket.h"
#include "libssh/buffer.h"
#include "libssh/poll.h"
#include "libssh/session.h"
/**
* @internal
*
* @defgroup libssh_socket The SSH socket functions.
* @ingroup libssh
*
* Functions for handling sockets.
*
* @{
*/
enum ssh_socket_states_e {
SSH_SOCKET_NONE,
SSH_SOCKET_CONNECTING,
SSH_SOCKET_CONNECTED,
SSH_SOCKET_EOF,
SSH_SOCKET_ERROR,
SSH_SOCKET_CLOSED
};
struct ssh_socket_struct {
socket_t fd_in;
socket_t fd_out;
int fd_is_socket;
int last_errno;
int read_wontblock; /* reading now on socket will
not block */
int write_wontblock;
int data_except;
enum ssh_socket_states_e state;
ssh_buffer out_buffer;
ssh_buffer in_buffer;
ssh_session session;
ssh_socket_callbacks callbacks;
ssh_poll_handle poll_in;
ssh_poll_handle poll_out;
};
static int sockets_initialized = 0;
static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len);
static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer,
uint32_t len);
/**
* \internal
* \brief inits the socket system (windows specific)
*/
int ssh_socket_init(void) {
if (sockets_initialized == 0) {
#ifdef _WIN32
struct WSAData wsaData;
/* Initiates use of the Winsock DLL by a process. */
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) {
return -1;
}
#endif
ssh_poll_init();
sockets_initialized = 1;
}
return 0;
}
/**
* @brief Cleanup the socket system.
*/
void ssh_socket_cleanup(void) {
if (sockets_initialized == 1) {
ssh_poll_cleanup();
#ifdef _WIN32
WSACleanup();
#endif
sockets_initialized = 0;
}
}
/**
* \internal
* \brief creates a new Socket object
*/
ssh_socket ssh_socket_new(ssh_session session) {
ssh_socket s;
s = malloc(sizeof(struct ssh_socket_struct));
if (s == NULL) {
ssh_set_error_oom(session);
return NULL;
}
s->fd_in = SSH_INVALID_SOCKET;
s->fd_out= SSH_INVALID_SOCKET;
s->last_errno = -1;
s->fd_is_socket = 1;
s->session = session;
s->in_buffer = ssh_buffer_new();
if (s->in_buffer == NULL) {
ssh_set_error_oom(session);
SAFE_FREE(s);
return NULL;
}
s->out_buffer=ssh_buffer_new();
if (s->out_buffer == NULL) {
ssh_set_error_oom(session);
ssh_buffer_free(s->in_buffer);
SAFE_FREE(s);
return NULL;
}
s->read_wontblock = 0;
s->write_wontblock = 0;
s->data_except = 0;
s->poll_in=s->poll_out=NULL;
s->state=SSH_SOCKET_NONE;
return s;
}
/**
* @internal
* @brief Reset the state of a socket so it looks brand-new
* @param[in] s socket to rest
*/
void ssh_socket_reset(ssh_socket s){
s->fd_in = SSH_INVALID_SOCKET;
s->fd_out= SSH_INVALID_SOCKET;
s->last_errno = -1;
s->fd_is_socket = 1;
buffer_reinit(s->in_buffer);
buffer_reinit(s->out_buffer);
s->read_wontblock = 0;
s->write_wontblock = 0;
s->data_except = 0;
s->poll_in=s->poll_out=NULL;
s->state=SSH_SOCKET_NONE;
}
/**
* @internal
* @brief the socket callbacks, i.e. callbacks to be called
* upon a socket event.
* @param s socket to set callbacks on.
* @param callbacks a ssh_socket_callback object reference.
*/
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){
s->callbacks=callbacks;
}
/**
* @brief SSH poll callback. This callback will be used when an event
* caught on the socket.
*
* @param p Poll object this callback belongs to.
* @param fd The raw socket.
* @param revents The current poll events on the socket.
* @param userdata Userdata to be passed to the callback function,
* in this case the socket object.
*
* @return 0 on success, < 0 when the poll object has been removed
* from its poll context.
*/
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){
ssh_socket s=(ssh_socket )v_s;
char buffer[4096];
int r;
int err=0;
socklen_t errlen=sizeof(err);
/* Do not do anything if this socket was already closed */
if(!ssh_socket_is_open(s)){
return -1;
}
if(revents & POLLERR || revents & POLLHUP){
/* Check if we are in a connecting state */
if(s->state==SSH_SOCKET_CONNECTING){
s->state=SSH_SOCKET_ERROR;
getsockopt(fd,SOL_SOCKET,SO_ERROR,(char *)&err,&errlen);
s->last_errno=err;
ssh_socket_close(s);
if(s->callbacks && s->callbacks->connected)
s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err,
s->callbacks->userdata);
return -1;
}
/* Then we are in a more standard kind of error */
/* force a read to get an explanation */
revents |= POLLIN;
}
if(revents & POLLIN){
s->read_wontblock=1;
r=ssh_socket_unbuffered_read(s,buffer,sizeof(buffer));
if(r<0){
if(p != NULL) {
ssh_poll_remove_events(p, POLLIN);
}
if(s->callbacks && s->callbacks->exception){
s->callbacks->exception(
SSH_SOCKET_EXCEPTION_ERROR,
s->last_errno,s->callbacks->userdata);
/* p may have been freed, so don't use it
* anymore in this function */
p = NULL;
return -2;
}
}
if(r==0){
if(p != NULL) {
ssh_poll_remove_events(p, POLLIN);
}
if(p != NULL) {
ssh_poll_remove_events(p, POLLIN);
}
if(s->callbacks && s->callbacks->exception){
s->callbacks->exception(
SSH_SOCKET_EXCEPTION_EOF,
0,s->callbacks->userdata);
/* p may have been freed, so don't use it
* anymore in this function */
p = NULL;
return -2;
}
}
if(r>0){
/* Bufferize the data and then call the callback */
r = buffer_add_data(s->in_buffer,buffer,r);
if (r < 0) {
return -1;
}
if(s->callbacks && s->callbacks->data){
do {
r= s->callbacks->data(buffer_get_rest(s->in_buffer),
buffer_get_rest_len(s->in_buffer),
s->callbacks->userdata);
buffer_pass_bytes(s->in_buffer,r);
} while (r > 0);
/* p may have been freed, so don't use it
* anymore in this function */
p = NULL;
}
}
}
#ifdef _WIN32
if(revents & POLLOUT || revents & POLLWRNORM){
#else
if(revents & POLLOUT){
#endif
/* First, POLLOUT is a sign we may be connected */
if(s->state == SSH_SOCKET_CONNECTING){
ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state");
s->state = SSH_SOCKET_CONNECTED;
ssh_poll_set_events(p,POLLOUT | POLLIN);
ssh_socket_set_blocking(ssh_socket_get_fd_in(s));
if(s->callbacks && s->callbacks->connected)
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata);
return 0;
}
/* So, we can write data */
s->write_wontblock=1;
if(p != NULL) {
ssh_poll_remove_events(p, POLLOUT);
}
/* If buffered data is pending, write it */
if(buffer_get_rest_len(s->out_buffer) > 0){
ssh_socket_nonblocking_flush(s);
} else if(s->callbacks && s->callbacks->controlflow){
/* Otherwise advertise the upper level that write can be done */
s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,s->callbacks->userdata);
}
/* TODO: Find a way to put back POLLOUT when buffering occurs */
}
/* Return -1 if one of the poll handlers disappeared */
return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0;
}
/** @internal
* @brief returns the input poll handle corresponding to the socket,
* creates it if it does not exist.
* @returns allocated and initialized ssh_poll_handle object
*/
ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){
if(s->poll_in)
return s->poll_in;
s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s);
if(s->fd_in == s->fd_out && s->poll_out == NULL)
s->poll_out=s->poll_in;
return s->poll_in;
}
/** @internal
* @brief returns the output poll handle corresponding to the socket,
* creates it if it does not exist.
* @returns allocated and initialized ssh_poll_handle object
*/
ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){
if(s->poll_out)
return s->poll_out;
s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s);
if(s->fd_in == s->fd_out && s->poll_in == NULL)
s->poll_in=s->poll_out;
return s->poll_out;
}
/** \internal
* \brief Deletes a socket object
*/
void ssh_socket_free(ssh_socket s){
if (s == NULL) {
return;
}
ssh_socket_close(s);
ssh_buffer_free(s->in_buffer);
ssh_buffer_free(s->out_buffer);
SAFE_FREE(s);
}
#ifndef _WIN32
int ssh_socket_unix(ssh_socket s, const char *path) {
struct sockaddr_un sunaddr;
socket_t fd;
sunaddr.sun_family = AF_UNIX;
snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == SSH_INVALID_SOCKET) {
ssh_set_error(s->session, SSH_FATAL,
"Error from socket(AF_UNIX, SOCK_STREAM, 0): %s",
strerror(errno));
return -1;
}
if (fcntl(fd, F_SETFD, 1) == -1) {
ssh_set_error(s->session, SSH_FATAL,
"Error from fcntl(fd, F_SETFD, 1): %s",
strerror(errno));
close(fd);
return -1;
}
if (connect(fd, (struct sockaddr *) &sunaddr,
sizeof(sunaddr)) < 0) {
ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s",
strerror(errno));
close(fd);
return -1;
}
ssh_socket_set_fd(s,fd);
return 0;
}
#endif
/** \internal
* \brief closes a socket
*/
void ssh_socket_close(ssh_socket s){
if (ssh_socket_is_open(s)) {
#ifdef _WIN32
closesocket(s->fd_in);
/* fd_in = fd_out under win32 */
s->last_errno = WSAGetLastError();
#else
close(s->fd_in);
if(s->fd_out != s->fd_in && s->fd_out != -1)
close(s->fd_out);
s->last_errno = errno;
#endif
s->fd_in = s->fd_out = SSH_INVALID_SOCKET;
}
if(s->poll_in != NULL){
if(s->poll_out == s->poll_in)
s->poll_out = NULL;
ssh_poll_free(s->poll_in);
s->poll_in=NULL;
}
if(s->poll_out != NULL){
ssh_poll_free(s->poll_out);
s->poll_out=NULL;
}
}
/**
* @internal
* @brief sets the file descriptor of the socket.
* @param[out] s ssh_socket to update
* @param[in] fd file descriptor to set
* @warning this function updates boths the input and output
* file descriptors
*/
void ssh_socket_set_fd(ssh_socket s, socket_t fd) {
s->fd_in = s->fd_out = fd;
if(s->poll_in)
ssh_poll_set_fd(s->poll_in,fd);
}
/**
* @internal
* @brief sets the input file descriptor of the socket.
* @param[out] s ssh_socket to update
* @param[in] fd file descriptor to set
*/
void ssh_socket_set_fd_in(ssh_socket s, socket_t fd) {
s->fd_in = fd;
if(s->poll_in)
ssh_poll_set_fd(s->poll_in,fd);
}
/**
* @internal
* @brief sets the output file descriptor of the socket.
* @param[out] s ssh_socket to update
* @param[in] fd file descriptor to set
*/
void ssh_socket_set_fd_out(ssh_socket s, socket_t fd) {
s->fd_out = fd;
if(s->poll_out)
ssh_poll_set_fd(s->poll_out,fd);
}
/** \internal
* \brief returns the input file descriptor of the socket
*/
socket_t ssh_socket_get_fd_in(ssh_socket s) {
return s->fd_in;
}
/** \internal
* \brief returns nonzero if the socket is open
*/
int ssh_socket_is_open(ssh_socket s) {
return s->fd_in != SSH_INVALID_SOCKET;
}
/** \internal
* \brief read len bytes from socket into buffer
*/
static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len) {
int rc = -1;
if (s->data_except) {
return -1;
}
if(s->fd_is_socket)
rc = recv(s->fd_in,buffer, len, 0);
else
rc = read(s->fd_in,buffer, len);
#ifdef _WIN32
s->last_errno = WSAGetLastError();
#else
s->last_errno = errno;
#endif
s->read_wontblock = 0;
if (rc < 0) {
s->data_except = 1;
}
return rc;
}
/** \internal
* \brief writes len bytes from buffer to socket
*/
static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer,
uint32_t len) {
int w = -1;
if (s->data_except) {
return -1;
}
if (s->fd_is_socket)
w = send(s->fd_out,buffer, len, 0);
else
w = write(s->fd_out, buffer, len);
#ifdef _WIN32
s->last_errno = WSAGetLastError();
#else
s->last_errno = errno;
#endif
s->write_wontblock = 0;
/* Reactive the POLLOUT detector in the poll multiplexer system */
if(s->poll_out){
ssh_log(s->session, SSH_LOG_PACKET, "Enabling POLLOUT for socket");
ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT);
}
if (w < 0) {
s->data_except = 1;
}
return w;
}
/** \internal
* \brief returns nonzero if the current socket is in the fd_set
*/
int ssh_socket_fd_isset(ssh_socket s, fd_set *set) {
if(s->fd_in == SSH_INVALID_SOCKET) {
return 0;
}
return FD_ISSET(s->fd_in,set) || FD_ISSET(s->fd_out,set);
}
/** \internal
* \brief sets the current fd in a fd_set and updates the max_fd
*/
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd) {
if (s->fd_in == SSH_INVALID_SOCKET) {
return;
}
FD_SET(s->fd_in,set);
FD_SET(s->fd_out,set);
if (s->fd_in >= 0 &&
s->fd_in >= *max_fd &&
s->fd_in != SSH_INVALID_SOCKET) {
*max_fd = s->fd_in + 1;
}
if (s->fd_out >= 0 &&
s->fd_out >= *max_fd &&
s->fd_out != SSH_INVALID_SOCKET) {
*max_fd = s->fd_out + 1;
}
}
/** \internal
* \brief buffered write of data
* \returns SSH_OK, or SSH_ERROR
* \warning has no effect on socket before a flush
*/
int ssh_socket_write(ssh_socket s, const void *buffer, int len) {
ssh_session session = s->session;
enter_function();
if(len > 0) {
if (buffer_add_data(s->out_buffer, buffer, len) < 0) {
ssh_set_error_oom(s->session);
return SSH_ERROR;
}
ssh_socket_nonblocking_flush(s);
}
leave_function();
return SSH_OK;
}
/** \internal
* \brief starts a nonblocking flush of the output buffer
*
*/
int ssh_socket_nonblocking_flush(ssh_socket s) {
ssh_session session = s->session;
uint32_t len;
int w;
enter_function();
if (!ssh_socket_is_open(s)) {
session->alive = 0;
/* FIXME use ssh_socket_get_errno */
ssh_set_error(session, SSH_FATAL,
"Writing packet: error on socket (or connection closed): %s",
strerror(s->last_errno));
leave_function();
return SSH_ERROR;
}
len = buffer_get_rest_len(s->out_buffer);
if (!s->write_wontblock && s->poll_out && len > 0) {
/* force the poll system to catch pollout events */
ssh_poll_add_events(s->poll_out, POLLOUT);
leave_function();
return SSH_AGAIN;
}
if (s->write_wontblock && len > 0) {
w = ssh_socket_unbuffered_write(s, buffer_get_rest(s->out_buffer), len);
if (w < 0) {
session->alive = 0;
ssh_socket_close(s);
/* FIXME use ssh_socket_get_errno() */
/* FIXME use callback for errors */
ssh_set_error(session, SSH_FATAL,
"Writing packet: error on socket (or connection closed): %s",
strerror(s->last_errno));
leave_function();
return SSH_ERROR;
}
buffer_pass_bytes(s->out_buffer, w);
}
/* Is there some data pending? */
len = buffer_get_rest_len(s->out_buffer);
if (s->poll_out && len > 0) {
/* force the poll system to catch pollout events */
ssh_poll_add_events(s->poll_out, POLLOUT);
leave_function();
return SSH_AGAIN;
}
/* all data written */
leave_function();
return SSH_OK;
}
void ssh_socket_set_write_wontblock(ssh_socket s) {
s->write_wontblock = 1;
}
void ssh_socket_set_read_wontblock(ssh_socket s) {
s->read_wontblock = 1;
}
void ssh_socket_set_except(ssh_socket s) {
s->data_except = 1;
}
int ssh_socket_data_available(ssh_socket s) {
return s->read_wontblock;
}
int ssh_socket_data_writable(ssh_socket s) {
return s->write_wontblock;
}
/** @internal
* @brief returns the number of outgoing bytes currently buffered
* @param s the socket
* @returns numbers of bytes buffered, or 0 if the socket isn't connected
*/
int ssh_socket_buffered_write_bytes(ssh_socket s){
if(s==NULL || s->out_buffer == NULL)
return 0;
return buffer_get_rest_len(s->out_buffer);
}
int ssh_socket_get_status(ssh_socket s) {
int r = 0;
if (s->read_wontblock) {
r |= SSH_READ_PENDING;
}
if (s->write_wontblock) {
r |= SSH_WRITE_PENDING;
}
if (s->data_except) {
r |= SSH_CLOSED_ERROR;
}
return r;
}
#ifdef _WIN32
void ssh_socket_set_nonblocking(socket_t fd) {
u_long nonblocking = 1;
ioctlsocket(fd, FIONBIO, &nonblocking);
}
void ssh_socket_set_blocking(socket_t fd) {
u_long nonblocking = 0;
ioctlsocket(fd, FIONBIO, &nonblocking);
}
#else /* _WIN32 */
void ssh_socket_set_nonblocking(socket_t fd) {
fcntl(fd, F_SETFL, O_NONBLOCK);
}
void ssh_socket_set_blocking(socket_t fd) {
fcntl(fd, F_SETFL, 0);
}
#endif /* _WIN32 */
/**
* @internal
* @brief Launches a socket connection
* If a the socket connected callback has been defined and
* a poll object exists, this call will be non blocking.
* @param s socket to connect.
* @param host hostname or ip address to connect to.
* @param port port number to connect to.
* @param bind_addr address to bind to, or NULL for default.
* @returns SSH_OK socket is being connected.
* @returns SSH_ERROR error while connecting to remote host.
* @bug It only tries connecting to one of the available AI's
* which is problematic for hosts having DNS fail-over.
*/
int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bind_addr){
socket_t fd;
ssh_session session=s->session;
enter_function();
if(s->state != SSH_SOCKET_NONE) {
ssh_set_error(s->session, SSH_FATAL,
"ssh_socket_connect called on socket not unconnected");
return SSH_ERROR;
}
fd=ssh_connect_host_nonblocking(s->session,host,bind_addr,port);
ssh_log(session,SSH_LOG_PROTOCOL,"Nonblocking connection socket: %d",fd);
if(fd == SSH_INVALID_SOCKET)
return SSH_ERROR;
ssh_socket_set_fd(s,fd);
s->state=SSH_SOCKET_CONNECTING;
/* POLLOUT is the event to wait for in a nonblocking connect */
ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLOUT);
#ifdef _WIN32
ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM);
#endif
leave_function();
return SSH_OK;
}
#ifndef _WIN32
/**
* @internal
* @brief executes a command and redirect input and outputs
* @param command command to execute
* @param in input file descriptor
* @param out output file descriptor
*/
void ssh_execute_command(const char *command, socket_t in, socket_t out){
const char *args[]={"/bin/sh","-c",command,NULL};
/* redirect in and out to stdin, stdout and stderr */
dup2(in, 0);
dup2(out,1);
dup2(out,2);
close(in);
close(out);
execv(args[0],(char * const *)args);
exit(1);
}
/**
* @internal
* @brief Open a socket on a ProxyCommand
* This call will always be nonblocking.
* @param s socket to connect.
* @param command Command to execute.
* @returns SSH_OK socket is being connected.
* @returns SSH_ERROR error while executing the command.
*/
int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){
socket_t in_pipe[2];
socket_t out_pipe[2];
int pid;
int rc;
ssh_session session=s->session;
enter_function();
if(s->state != SSH_SOCKET_NONE)
return SSH_ERROR;
rc = pipe(in_pipe);
if (rc < 0) {
return SSH_ERROR;
}
rc = pipe(out_pipe);
if (rc < 0) {
return SSH_ERROR;
}
ssh_log(session,SSH_LOG_PROTOCOL,"Executing proxycommand '%s'",command);
pid = fork();
if(pid == 0){
ssh_execute_command(command,out_pipe[0],in_pipe[1]);
}
close(in_pipe[1]);
close(out_pipe[0]);
ssh_log(session,SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]);
ssh_socket_set_fd_in(s,in_pipe[0]);
ssh_socket_set_fd_out(s,out_pipe[1]);
s->state=SSH_SOCKET_CONNECTED;
s->fd_is_socket=0;
/* POLLOUT is the event to wait for in a nonblocking connect */
ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLIN);
ssh_poll_set_events(ssh_socket_get_poll_handle_out(s),POLLOUT);
if(s->callbacks && s->callbacks->connected)
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata);
leave_function();
return SSH_OK;
}
#endif /* _WIN32 */
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

270
libssh/src/string.c Normal file
View file

@ -0,0 +1,270 @@
/*
* string.c - ssh string functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2008 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/string.h"
/**
* @defgroup libssh_string The SSH string functions
* @ingroup libssh
*
* @brief String manipulations used in libssh.
*
* @{
*/
/**
* @brief Create a new SSH String object.
*
* @param[in] size The size of the string.
*
* @return The newly allocated string, NULL on error.
*/
struct ssh_string_struct *ssh_string_new(size_t size) {
struct ssh_string_struct *str = NULL;
if (size > UINT_MAX - sizeof(struct ssh_string_struct)) {
return NULL;
}
str = malloc(sizeof(struct ssh_string_struct) + size);
if (str == NULL) {
return NULL;
}
str->size = htonl(size);
str->data[0] = 0;
return str;
}
/**
* @brief Fill a string with given data. The string should be big enough.
*
* @param s An allocated string to fill with data.
*
* @param data The data to fill the string with.
*
* @param len Size of data.
*
* @return 0 on success, < 0 on error.
*/
int ssh_string_fill(struct ssh_string_struct *s, const void *data, size_t len) {
if ((s == NULL) || (data == NULL) ||
(len == 0) || (len > ssh_string_len(s))) {
return -1;
}
memcpy(s->data, data, len);
return 0;
}
/**
* @brief Create a ssh string using a C string
*
* @param[in] what The source 0-terminated C string.
*
* @return The newly allocated string, NULL on error with errno
* set.
*
* @note The nul byte is not copied nor counted in the ouput string.
*/
struct ssh_string_struct *ssh_string_from_char(const char *what) {
struct ssh_string_struct *ptr;
size_t len;
if(what == NULL) {
errno = EINVAL;
return NULL;
}
len = strlen(what);
ptr = ssh_string_new(len);
if (ptr == NULL) {
return NULL;
}
memcpy(ptr->data, what, len);
return ptr;
}
/**
* @brief Return the size of a SSH string.
*
* @param[in] s The the input SSH string.
*
* @return The size of the content of the string, 0 on error.
*/
size_t ssh_string_len(struct ssh_string_struct *s) {
if (s == NULL) {
return ntohl(0);
}
return ntohl(s->size);
}
/**
* @brief Get the the string as a C nul-terminated string.
*
* This is only available as long as the SSH string exists.
*
* @param[in] s The SSH string to get the C string from.
*
* @return The char pointer, NULL on error.
*/
const char *ssh_string_get_char(struct ssh_string_struct *s)
{
if (s == NULL) {
return NULL;
}
s->data[ssh_string_len(s)] = '\0';
return (const char *) s->data;
}
/**
* @brief Convert a SSH string to a C nul-terminated string.
*
* @param[in] s The SSH input string.
*
* @return An allocated string pointer, NULL on error with errno
* set.
*
* @note If the input SSH string contains zeroes, some parts of the output
* string may not be readable with regular libc functions.
*/
char *ssh_string_to_char(struct ssh_string_struct *s) {
size_t len;
char *new;
if (s == NULL) {
return NULL;
}
len = ssh_string_len(s);
if (len + 1 < len) {
return NULL;
}
new = malloc(len + 1);
if (new == NULL) {
return NULL;
}
memcpy(new, s->data, len);
new[len] = '\0';
return new;
}
/**
* @brief Deallocate a char string object.
*
* @param[in] s The string to delete.
*/
void ssh_string_free_char(char *s) {
SAFE_FREE(s);
}
/**
* @brief Copy a string, return a newly allocated string. The caller has to
* free the string.
*
* @param[in] s String to copy.
*
* @return Newly allocated copy of the string, NULL on error.
*/
struct ssh_string_struct *ssh_string_copy(struct ssh_string_struct *s) {
struct ssh_string_struct *new;
size_t len;
if (s == NULL) {
return NULL;
}
len = ssh_string_len(s);
if (len == 0) {
return NULL;
}
new = ssh_string_new(len);
if (new == NULL) {
return NULL;
}
memcpy(new->data, s->data, len);
return new;
}
/**
* @brief Destroy the data in a string so it couldn't appear in a core dump.
*
* @param[in] s The string to burn.
*/
void ssh_string_burn(struct ssh_string_struct *s) {
if (s == NULL) {
return;
}
memset(s->data, 'X', ssh_string_len(s));
}
/**
* @brief Get the payload of the string.
*
* @param s The string to get the data from.
*
* @return Return the data of the string or NULL on error.
*/
void *ssh_string_data(struct ssh_string_struct *s) {
if (s == NULL) {
return NULL;
}
return s->data;
}
/**
* @brief Deallocate a SSH string object.
*
* \param[in] s The SSH string to delete.
*/
void ssh_string_free(struct ssh_string_struct *s) {
SAFE_FREE(s);
}
/** @} */
/* vim: set ts=4 sw=4 et cindent: */

176
libssh/src/threads.c Normal file
View file

@ -0,0 +1,176 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/**
* @defgroup libssh_threads The SSH threading functions.
* @ingroup libssh
*
* Threading with libssh
* @{
*/
#include "config.h"
#include "libssh/priv.h"
#include "libssh/crypto.h"
#include "libssh/threads.h"
static int threads_noop (void **lock){
(void)lock;
return 0;
}
static unsigned long threads_id_noop (void){
return 1;
}
static struct ssh_threads_callbacks_struct ssh_threads_noop =
{
"threads_noop",
threads_noop,
threads_noop,
threads_noop,
threads_noop,
threads_id_noop
};
struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) {
return &ssh_threads_noop;
}
static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop;
#ifdef HAVE_LIBGCRYPT
/* Libgcrypt specific way of handling thread callbacks */
static struct gcry_thread_cbs gcrypt_threads_callbacks;
static int libgcrypt_thread_init(void){
if(user_callbacks == NULL)
return SSH_ERROR;
if(user_callbacks == &ssh_threads_noop){
gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_DEFAULT;
} else {
gcrypt_threads_callbacks.option= GCRY_THREAD_OPTION_VERSION << 8 || GCRY_THREAD_OPTION_USER;
}
gcrypt_threads_callbacks.mutex_init=user_callbacks->mutex_init;
gcrypt_threads_callbacks.mutex_destroy=user_callbacks->mutex_destroy;
gcrypt_threads_callbacks.mutex_lock=user_callbacks->mutex_lock;
gcrypt_threads_callbacks.mutex_unlock=user_callbacks->mutex_unlock;
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks);
return SSH_OK;
}
#else
/* Libcrypto specific stuff */
static void **libcrypto_mutexes;
static void libcrypto_lock_callback(int mode, int i, const char *file, int line){
(void)file;
(void)line;
if(mode & CRYPTO_LOCK){
user_callbacks->mutex_lock(&libcrypto_mutexes[i]);
} else {
user_callbacks->mutex_unlock(&libcrypto_mutexes[i]);
}
}
static int libcrypto_thread_init(void){
int n=CRYPTO_num_locks();
int i;
if(user_callbacks == &ssh_threads_noop)
return SSH_OK;
libcrypto_mutexes=malloc(sizeof(void *) * n);
if (libcrypto_mutexes == NULL)
return SSH_ERROR;
for (i=0;i<n;++i){
user_callbacks->mutex_init(&libcrypto_mutexes[i]);
}
CRYPTO_set_id_callback(user_callbacks->thread_id);
CRYPTO_set_locking_callback(libcrypto_lock_callback);
return SSH_OK;
}
static void libcrypto_thread_finalize(void){
int n=CRYPTO_num_locks();
int i;
if (libcrypto_mutexes==NULL)
return;
for (i=0;i<n;++i){
user_callbacks->mutex_destroy(&libcrypto_mutexes[i]);
}
SAFE_FREE(libcrypto_mutexes);
}
#endif
/** @internal
* @brief inits the threading with the backend cryptographic libraries
*/
int ssh_threads_init(void){
static int threads_initialized=0;
int ret;
if(threads_initialized)
return SSH_OK;
/* first initialize the user_callbacks with our default handlers if not
* already the case
*/
if(user_callbacks == NULL){
user_callbacks=&ssh_threads_noop;
}
/* Then initialize the crypto libraries threading callbacks */
#ifdef HAVE_LIBGCRYPT
ret = libgcrypt_thread_init();
#else /* Libcrypto */
ret = libcrypto_thread_init();
#endif
if(ret == SSH_OK)
threads_initialized=1;
return ret;
}
void ssh_threads_finalize(void){
#ifdef HAVE_LIBGCRYPT
#else
libcrypto_thread_finalize();
#endif
}
int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb){
user_callbacks=cb;
return SSH_OK;
}
const char *ssh_threads_get_type(void) {
if(user_callbacks != NULL)
return user_callbacks->type;
return NULL;
}
/**
* @}
*/

View file

@ -0,0 +1,125 @@
project(libssh-threads C)
set(LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS
${CMAKE_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}
CACHE INTERNAL "libssh public include directories"
)
set(LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS
${CMAKE_BINARY_DIR}
)
set(LIBSSH_THREADS_SHARED_LIBRARY
ssh_threads_shared
CACHE INTERNAL "libssh threads shared library"
)
if (WITH_STATIC_LIB)
set(LIBSSH_THREADS_STATIC_LIBRARY
ssh_threads_static
CACHE INTERNAL "libssh threads static library"
)
endif (WITH_STATIC_LIB)
set(LIBSSH_THREADS_LINK_LIBRARIES
${LIBSSH_SHARED_LIBRARY}
)
set(libssh_threads_SRCS
)
# build and link pthread
if (CMAKE_USE_PTHREADS_INIT)
set(libssh_threads_SRCS
${libssh_threads_SRCS}
pthread.c
)
set(LIBSSH_THREADS_LINK_LIBRARIES
${LIBSSH_THREADS_LINK_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
endif (CMAKE_USE_PTHREADS_INIT)
set(LIBSSH_THREADS_LINK_LIBRARIES
${LIBSSH_THREADS_LINK_LIBRARIES}
CACHE INTERNAL "libssh threads link libraries"
)
include_directories(
${LIBSSH_THREADS_PUBLIC_INCLUDE_DIRS}
${LIBSSH_THREADS_PRIVATE_INCLUDE_DIRS}
)
add_library(${LIBSSH_THREADS_SHARED_LIBRARY} SHARED ${libssh_threads_SRCS})
target_link_libraries(${LIBSSH_THREADS_SHARED_LIBRARY} ${LIBSSH_THREADS_LINK_LIBRARIES})
set_target_properties(
${LIBSSH_THREADS_SHARED_LIBRARY}
PROPERTIES
VERSION
${LIBRARY_VERSION}
SOVERSION
${LIBRARY_SOVERSION}
OUTPUT_NAME
ssh_threads
DEFINE_SYMBOL
LIBSSH_EXPORTS
)
if (WITH_VISIBILITY_HIDDEN)
set_target_properties(${LIBSSH_THREADS_SHARED_LIBRARY} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
endif (WITH_VISIBILITY_HIDDEN)
install(
TARGETS
${LIBSSH_THREADS_SHARED_LIBRARY}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
COMPONENT libraries
)
if (WITH_STATIC_LIB)
add_library(${LIBSSH_THREADS_STATIC_LIBRARY} STATIC ${libssh_threads_SRCS})
if (MSVC)
set(OUTPUT_SUFFIX static)
else (MSVC)
set(OUTPUT_SUFFIX )
endif (MSVC)
set_target_properties(
${LIBSSH_THREADS_STATIC_LIBRARY}
PROPERTIES
VERSION
${LIBRARY_VERSION}
SOVERSION
${LIBRARY_SOVERSION}
OUTPUT_NAME
ssh_threads
ARCHIVE_OUTPUT_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_SUFFIX}
)
if (WIN32)
set_target_properties(
${LIBSSH_THREADS_STATIC_LIBRARY}
PROPERTIES
COMPILE_FLAGS
"-DLIBSSH_STATIC"
)
endif (WIN32)
install(
TARGETS
${LIBSSH_THREADS_STATIC_LIBRARY}
DESTINATION
${LIB_INSTALL_DIR}/${OUTPUT_SUFFIX}
COMPONENT
libraries
)
endif (WITH_STATIC_LIB)

View file

@ -0,0 +1,99 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2010 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include <libssh/callbacks.h>
#ifdef HAVE_PTHREAD
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
/** @brief Defines the needed callbacks for pthread. Use this if your
* OS supports libpthread and want to use it for threading.
* @code
* #include <libssh/callbacks.h>
* #include <errno.h>
* #include <pthread.h>
* SSH_THREADS_PTHREAD(ssh_pthread_callbacks);
* int main(){
* ssh_init_set_threads_callbacks(&ssh_pthread_callbacks);
* ssh_init();
* ...
* }
* @endcode
* @param name name of the structure to be declared, containing the
* callbacks for threading
*
*/
static int ssh_pthread_mutex_init (void **priv){
int err = 0;
*priv = malloc (sizeof (pthread_mutex_t));
if (*priv==NULL)
return ENOMEM;
err = pthread_mutex_init (*priv, NULL);
if (err != 0){
free (*priv);
*priv=NULL;
}
return err;
}
static int ssh_pthread_mutex_destroy (void **lock) {
int err = pthread_mutex_destroy (*lock);
free (*lock);
*lock=NULL;
return err;
}
static int ssh_pthread_mutex_lock (void **lock) {
return pthread_mutex_lock (*lock);
}
static int ssh_pthread_mutex_unlock (void **lock){
return pthread_mutex_unlock (*lock);
}
static unsigned long ssh_pthread_thread_id (void){
#if _WIN32
return (unsigned long) pthread_self().p;
#else
return (unsigned long) pthread_self();
#endif
}
static struct ssh_threads_callbacks_struct ssh_threads_pthread =
{
.type="threads_pthread",
.mutex_init=ssh_pthread_mutex_init,
.mutex_destroy=ssh_pthread_mutex_destroy,
.mutex_lock=ssh_pthread_mutex_lock,
.mutex_unlock=ssh_pthread_mutex_unlock,
.thread_id=ssh_pthread_thread_id
};
struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void) {
return &ssh_threads_pthread;
}
#endif /* HAVE_PTHREAD */

345
libssh/src/wrapper.c Normal file
View file

@ -0,0 +1,345 @@
/*
* wrapper.c - wrapper for crytpo functions
*
* This file is part of the SSH Library
*
* Copyright (c) 2003 by Aris Adamantiadis
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/*
* Why a wrapper?
*
* Let's say you want to port libssh from libcrypto of openssl to libfoo
* you are going to spend hours to remove every references to SHA1_Update()
* to libfoo_sha1_update after the work is finished, you're going to have
* only this file to modify it's not needed to say that your modifications
* are welcome.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef WITH_ZLIB
#include <zlib.h>
#endif
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/wrapper.h"
#include "libssh/pki.h"
/* it allocates a new cipher structure based on its offset into the global table */
static struct ssh_cipher_struct *cipher_new(int offset) {
struct ssh_cipher_struct *cipher = NULL;
cipher = malloc(sizeof(struct ssh_cipher_struct));
if (cipher == NULL) {
return NULL;
}
/* note the memcpy will copy the pointers : so, you shouldn't free them */
memcpy(cipher, &ssh_get_ciphertab()[offset], sizeof(*cipher));
return cipher;
}
static void cipher_free(struct ssh_cipher_struct *cipher) {
#ifdef HAVE_LIBGCRYPT
unsigned int i;
#endif
if (cipher == NULL) {
return;
}
if(cipher->key) {
#ifdef HAVE_LIBGCRYPT
for (i = 0; i < (cipher->keylen / sizeof(gcry_cipher_hd_t)); i++) {
gcry_cipher_close(cipher->key[i]);
}
#elif defined HAVE_LIBCRYPTO
/* destroy the key */
memset(cipher->key, 0, cipher->keylen);
#endif
SAFE_FREE(cipher->key);
}
SAFE_FREE(cipher);
}
struct ssh_crypto_struct *crypto_new(void) {
struct ssh_crypto_struct *crypto;
crypto = malloc(sizeof(struct ssh_crypto_struct));
if (crypto == NULL) {
return NULL;
}
ZERO_STRUCTP(crypto);
return crypto;
}
void crypto_free(struct ssh_crypto_struct *crypto){
int i;
if (crypto == NULL) {
return;
}
SAFE_FREE(crypto->server_pubkey);
cipher_free(crypto->in_cipher);
cipher_free(crypto->out_cipher);
bignum_free(crypto->e);
bignum_free(crypto->f);
bignum_free(crypto->x);
bignum_free(crypto->y);
bignum_free(crypto->k);
#ifdef HAVE_ECDH
SAFE_FREE(crypto->ecdh_client_pubkey);
SAFE_FREE(crypto->ecdh_server_pubkey);
#endif
if(crypto->session_id != NULL){
memset(crypto->session_id, '\0', crypto->digest_len);
SAFE_FREE(crypto->session_id);
}
if(crypto->secret_hash != NULL){
memset(crypto->secret_hash, '\0', crypto->digest_len);
SAFE_FREE(crypto->secret_hash);
}
#ifdef WITH_ZLIB
if (crypto->compress_out_ctx &&
(deflateEnd(crypto->compress_out_ctx) != 0)) {
inflateEnd(crypto->compress_out_ctx);
}
if (crypto->compress_in_ctx &&
(deflateEnd(crypto->compress_in_ctx) != 0)) {
inflateEnd(crypto->compress_in_ctx);
}
#endif /* WITH_ZLIB */
if(crypto->encryptIV)
SAFE_FREE(crypto->encryptIV);
if(crypto->decryptIV)
SAFE_FREE(crypto->decryptIV);
if(crypto->encryptMAC)
SAFE_FREE(crypto->encryptMAC);
if(crypto->decryptMAC)
SAFE_FREE(crypto->decryptMAC);
if(crypto->encryptkey){
memset(crypto->encryptkey, 0, crypto->digest_len);
SAFE_FREE(crypto->encryptkey);
}
if(crypto->decryptkey){
memset(crypto->decryptkey, 0, crypto->digest_len);
SAFE_FREE(crypto->decryptkey);
}
for (i = 0; i < SSH_KEX_METHODS; i++) {
SAFE_FREE(crypto->client_kex.methods[i]);
SAFE_FREE(crypto->server_kex.methods[i]);
SAFE_FREE(crypto->kex_methods[i]);
}
memset(crypto,0,sizeof(*crypto));
SAFE_FREE(crypto);
}
static int crypt_set_algorithms2(ssh_session session){
const char *wanted;
int i = 0;
int rc = SSH_ERROR;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
enter_function();
/* we must scan the kex entries to find crypto algorithms and set their appropriate structure */
/* out */
wanted = session->next_crypto->kex_methods[SSH_CRYPT_C_S];
while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) {
i++;
}
if (ssh_ciphertab[i].name == NULL) {
ssh_set_error(session, SSH_FATAL,
"crypt_set_algorithms2: no crypto algorithm function found for %s",
wanted);
goto error;
}
ssh_log(session, SSH_LOG_PACKET, "Set output algorithm to %s", wanted);
session->next_crypto->out_cipher = cipher_new(i);
if (session->next_crypto->out_cipher == NULL) {
ssh_set_error_oom(session);
goto error;
}
i = 0;
/* in */
wanted = session->next_crypto->kex_methods[SSH_CRYPT_S_C];
while (ssh_ciphertab[i].name && strcmp(wanted, ssh_ciphertab[i].name)) {
i++;
}
if (ssh_ciphertab[i].name == NULL) {
ssh_set_error(session, SSH_FATAL,
"Crypt_set_algorithms: no crypto algorithm function found for %s",
wanted);
goto error;
}
ssh_log(session, SSH_LOG_PACKET, "Set input algorithm to %s", wanted);
session->next_crypto->in_cipher = cipher_new(i);
if (session->next_crypto->in_cipher == NULL) {
ssh_set_error_oom(session);
goto error;
}
/* compression */
if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib") == 0) {
session->next_crypto->do_compress_out = 1;
}
if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib") == 0) {
session->next_crypto->do_compress_in = 1;
}
if (strcmp(session->next_crypto->kex_methods[SSH_COMP_C_S], "zlib@openssh.com") == 0) {
session->next_crypto->delayed_compress_out = 1;
}
if (strcmp(session->next_crypto->kex_methods[SSH_COMP_S_C], "zlib@openssh.com") == 0) {
session->next_crypto->delayed_compress_in = 1;
}
rc = SSH_OK;
error:
leave_function();
return rc;
}
static int crypt_set_algorithms1(ssh_session session, enum ssh_des_e des_type) {
int i = 0;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
/* right now, we force 3des-cbc to be taken */
while (ssh_ciphertab[i].name && strcmp(ssh_ciphertab[i].name,
des_type == SSH_DES ? "des-cbc-ssh1" : "3des-cbc-ssh1")) {
i++;
}
if (ssh_ciphertab[i].name == NULL) {
ssh_set_error(session, SSH_FATAL, "cipher 3des-cbc-ssh1 or des-cbc-ssh1 not found!");
return SSH_ERROR;
}
session->next_crypto->out_cipher = cipher_new(i);
if (session->next_crypto->out_cipher == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
session->next_crypto->in_cipher = cipher_new(i);
if (session->next_crypto->in_cipher == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
return SSH_OK;
}
int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type) {
return (session->version == 1) ? crypt_set_algorithms1(session, des_type) :
crypt_set_algorithms2(session);
}
#ifdef WITH_SERVER
int crypt_set_algorithms_server(ssh_session session){
char *method = NULL;
int i = 0;
int rc = SSH_ERROR;
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
if (session == NULL) {
return SSH_ERROR;
}
/* we must scan the kex entries to find crypto algorithms and set their appropriate structure */
enter_function();
/* out */
method = session->next_crypto->kex_methods[SSH_CRYPT_S_C];
while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name))
i++;
if(!ssh_ciphertab[i].name){
ssh_set_error(session,SSH_FATAL,"crypt_set_algorithms_server : "
"no crypto algorithm function found for %s",method);
goto error;
}
ssh_log(session,SSH_LOG_PACKET,"Set output algorithm %s",method);
session->next_crypto->out_cipher = cipher_new(i);
if (session->next_crypto->out_cipher == NULL) {
ssh_set_error_oom(session);
goto error;
}
i=0;
/* in */
method = session->next_crypto->kex_methods[SSH_CRYPT_C_S];
while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name))
i++;
if(!ssh_ciphertab[i].name){
ssh_set_error(session,SSH_FATAL,"Crypt_set_algorithms_server :"
"no crypto algorithm function found for %s",method);
goto error;
}
ssh_log(session,SSH_LOG_PACKET,"Set input algorithm %s",method);
session->next_crypto->in_cipher = cipher_new(i);
if (session->next_crypto->in_cipher == NULL) {
ssh_set_error_oom(session);
goto error;
}
/* compression */
method = session->next_crypto->kex_methods[SSH_CRYPT_C_S];
if(strcmp(method,"zlib") == 0){
ssh_log(session,SSH_LOG_PACKET,"enabling C->S compression");
session->next_crypto->do_compress_in=1;
}
if(strcmp(method,"zlib@openssh.com") == 0){
ssh_set_error(session,SSH_FATAL,"zlib@openssh.com not supported");
goto error;
}
method = session->next_crypto->kex_methods[SSH_CRYPT_S_C];
if(strcmp(method,"zlib") == 0){
ssh_log(session,SSH_LOG_PACKET,"enabling S->C compression\n");
session->next_crypto->do_compress_out=1;
}
if(strcmp(method,"zlib@openssh.com") == 0){
ssh_set_error(session,SSH_FATAL,"zlib@openssh.com not supported");
goto error;
}
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];
session->srv.hostkey = ssh_key_type_from_name(method);
rc = SSH_OK;
error:
leave_function();
return rc;
}
#endif /* WITH_SERVER */
/* vim: set ts=2 sw=2 et cindent: */