aboutsummaryrefslogtreecommitdiff
path: root/src/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/session.c')
-rw-r--r--src/session.c520
1 files changed, 520 insertions, 0 deletions
diff --git a/src/session.c b/src/session.c
new file mode 100644
index 00000000..42f5e772
--- /dev/null
+++ b/src/session.c
@@ -0,0 +1,520 @@
+/*
+ * 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/libssh.h"
+#include "libssh/priv.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;
+ 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;
+ session->blocking = 1;
+ session->log_indent = 0;
+ session->maxchannel = FIRST_CHANNEL;
+
+ /* options */
+ session->StrictHostKeyChecking = 1;
+ session->port = 22;
+ session->fd = -1;
+ session->ssh2 = 1;
+#ifdef WITH_SSH1
+ session->ssh1 = 1;
+#else
+ session->ssh1 = 0;
+#endif
+
+#ifndef _WIN32
+ session->agent = agent_new(session);
+ if (session->agent == NULL) {
+ goto err;
+ }
+#endif /* _WIN32 */
+
+ session->identity = ssh_list_new();
+ if (session->identity == NULL) {
+ goto err;
+ }
+
+ id = strdup("%d/id_rsa");
+ if (id == NULL) {
+ goto err;
+ }
+ rc = ssh_list_append(session->identity, id);
+ if (rc == SSH_ERROR) {
+ goto err;
+ }
+
+ id = strdup("%d/id_dsa");
+ if (id == NULL) {
+ goto err;
+ }
+ rc = ssh_list_append(session->identity, id);
+ if (rc == SSH_ERROR) {
+ goto err;
+ }
+
+ id = strdup("%d/identity");
+ if (id == NULL) {
+ goto err;
+ }
+ rc = ssh_list_append(session->identity, id);
+ if (rc == SSH_ERROR) {
+ goto err;
+ }
+
+ return session;
+
+err:
+ 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;
+ enter_function();
+
+ if (session == NULL) {
+ return;
+ }
+
+ SAFE_FREE(session->serverbanner);
+ SAFE_FREE(session->clientbanner);
+ SAFE_FREE(session->banner);
+#ifdef WITH_PCAP
+ if(session->pcap_ctx){
+ ssh_pcap_context_free(session->pcap_ctx);
+ session->pcap_ctx=NULL;
+ }
+#endif
+ ssh_buffer_free(session->in_buffer);
+ ssh_buffer_free(session->out_buffer);
+ session->in_buffer=session->out_buffer=NULL;
+ crypto_free(session->current_crypto);
+ crypto_free(session->next_crypto);
+ ssh_socket_free(session->socket);
+ /* delete all channels */
+ while (session->channels) {
+ ssh_channel_free(session->channels);
+ }
+#ifndef _WIN32
+ agent_free(session->agent);
+#endif /* _WIN32 */
+ if (session->client_kex.methods) {
+ for (i = 0; i < 10; i++) {
+ SAFE_FREE(session->client_kex.methods[i]);
+ }
+ }
+
+ if (session->server_kex.methods) {
+ for (i = 0; i < 10; i++) {
+ SAFE_FREE(session->server_kex.methods[i]);
+ }
+ }
+ SAFE_FREE(session->client_kex.methods);
+ SAFE_FREE(session->server_kex.methods);
+
+ privatekey_free(session->dsa_key);
+ privatekey_free(session->rsa_key);
+ 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);
+ }
+
+ if (session->packet_callbacks)
+ ssh_list_free(session->packet_callbacks);
+
+ if (session->identity) {
+ char *id;
+
+ for (id = ssh_list_pop_head(char *, session->identity);
+ id != NULL;
+ id = ssh_list_pop_head(char *, session->identity)) {
+ SAFE_FREE(id);
+ }
+ ssh_list_free(session->identity);
+ }
+
+ /* options */
+ SAFE_FREE(session->username);
+ SAFE_FREE(session->host);
+ SAFE_FREE(session->sshdir);
+ SAFE_FREE(session->knownhosts);
+ SAFE_FREE(session->ProxyCommand);
+
+ for (i = 0; i < 10; i++) {
+ if (session->wanted_methods[i]) {
+ SAFE_FREE(session->wanted_methods[i]);
+ }
+ }
+
+ /* burn connection, it could hang sensitive datas */
+ ZERO_STRUCTP(session);
+ SAFE_FREE(session);
+}
+
+/**
+ * @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->blocking = blocking ? 1 : 0;
+}
+
+/**
+ * @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_toread(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_towrite(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 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 a negative value
+ * means an infinite timeout. 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;
+ 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);
+ ssh_poll_set_events(spoll_in, POLLIN | POLLERR);
+ ctx=ssh_poll_get_ctx(spoll_in);
+ if(ctx==NULL){
+ ctx=ssh_get_global_poll_ctx(session);
+ ssh_poll_ctx_add(ctx,spoll_in);
+ if(spoll_in != spoll_out)
+ ssh_poll_ctx_add(ctx,spoll_out);
+ }
+ ssh_poll_ctx_dopoll(ctx,timeout);
+ leave_function();
+ if (session->session_state != SSH_SESSION_STATE_ERROR)
+ return SSH_OK;
+ else
+ return SSH_ERROR;
+}
+
+/**
+ * @brief Get session status
+ *
+ * @param session The ssh session to use.
+ *
+ * @returns A bitmask including SSH_CLOSED, SSH_READ_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 (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 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;
+ /* 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;
+}
+
+/**
+ * @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();
+}
+
+/** @} */
+
+/* vim: set ts=4 sw=4 et cindent: */