diff options
author | Andreas Schneider <asn@cynapses.org> | 2010-09-06 14:28:38 +0200 |
---|---|---|
committer | Andreas Schneider <asn@cynapses.org> | 2010-09-06 14:28:38 +0200 |
commit | f7842e3a4b9acea2126ff725f993c299aef0e6db (patch) | |
tree | 18239f819a5edbcfc7f2961c48f3f9297314ef22 /src/packet1.c | |
parent | 38421403d2dc45636e597f2a909daa6ae31976de (diff) | |
download | libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.tar.gz libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.tar.xz libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.zip |
misc: Rename libssh/ to src/
Diffstat (limited to 'src/packet1.c')
-rw-r--r-- | src/packet1.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/packet1.c b/src/packet1.c new file mode 100644 index 0000000..d4b2eae --- /dev/null +++ b/src/packet1.c @@ -0,0 +1,362 @@ +/* + * 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/priv.h" +#include "libssh/ssh1.h" +#include "libssh/packet.h" +#include "libssh/session.h" +#include "libssh/buffer.h" +#include "libssh/socket.h" +#include "libssh/kex.h" +#ifdef WITH_SSH1 + +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 + NULL, //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 rc = SSH_ERROR; + 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); + rc = SSH_ERROR; + + /* 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; + rc = SSH_ERROR; + + 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 +#if defined(HAVE_LIBZ) && defined(WITH_LIBZ) + 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){ + /* 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 +#if defined(HAVE_LIBZ) && defined(WITH_LIBZ) + 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: */ |