/*
* messages.c - message parsing for client and server
*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2013 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>
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/channels.h"
#include "libssh/session.h"
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/dh.h"
#include "libssh/messages.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/gssapi.h"
#endif
/**
* @defgroup libssh_messages The SSH message functions
* @ingroup libssh
*
* This file contains the message parsing utilities for client and server
* programs using libssh.
*
* On the server the the main loop of the program will call
* ssh_message_get(session) to get messages as they come. They are not 1-1 with
* the protocol messages. Then, the user will know what kind of a message it is
* and use the appropriate functions to handle it (or use the default handlers
* if you don't know what to do).
*
* @{
*/
static ssh_message ssh_message_new(ssh_session session){
ssh_message msg = malloc(sizeof(struct ssh_message_struct));
if (msg == NULL) {
return NULL;
}
ZERO_STRUCTP(msg);
msg->session = session;
return msg;
}
#ifndef WITH_SERVER
/* Reduced version of the reply default that only reply with
* SSH_MSG_UNIMPLEMENTED
*/
static int ssh_message_reply_default(ssh_message msg) {
SSH_LOG(SSH_LOG_FUNCTIONS, "Reporting unknown packet");
if (buffer_add_u8(msg->session->out_buffer, SSH2_MSG_UNIMPLEMENTED) < 0)
goto error;
if (buffer_add_u32(msg->session->out_buffer,
htonl(msg->session->recv_seq-1)) < 0)
goto error;
return packet_send(msg->session);
error:
return SSH_ERROR;
}
#endif
#ifdef WITH_SERVER
static int ssh_execute_server_request(ssh_session session, ssh_message msg)
{
ssh_channel channel = NULL;
int rc;
switch(msg->type) {
case SSH_REQUEST_AUTH:
if (msg->auth_request.method == SSH_AUTH_METHOD_PASSWORD &&
ssh_callbacks_exists(session->server_callbacks, auth_password_function)) {
rc = session->server_callbacks->auth_password_function(session,
msg->auth_request.username, msg->auth_request.password,
session->server_callbacks->userdata);
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) {
ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
} else if(msg->auth_request.method == SSH_AUTH_METHOD_PUBLICKEY &&
ssh_callbacks_exists(session->server_callbacks, auth_pubkey_function)) {
rc = session->server_callbacks->auth_pubkey_function(session,
msg->auth_request.username, msg->auth_request.pubkey,
msg->auth_request.signature_state,
session->server_callbacks->userdata);
if (msg->auth_request.signature_state != SSH_PUBLICKEY_STATE_NONE) {
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) {
ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL);
} else {
ssh_message_reply_default(msg);
}
} else {
if (rc == SSH_AUTH_SUCCESS) {
ssh_message_auth_reply_pk_ok_simple(msg);
} else {
ssh_message_reply_default(msg);
}
}
return SSH_OK;
} else if (msg->auth_request.method == SSH_AUTH_METHOD_NONE &&
ssh_callbacks_exists(session->server_callbacks, auth_none_function)) {
rc = session->server_callbacks->auth_none_function(session,
msg->auth_request.username, session->server_callbacks->userdata);
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL){
ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
}
break;
case SSH_REQUEST_CHANNEL_OPEN:
if (msg->channel_request_open.type == SSH_CHANNEL_SESSION &&
ssh_callbacks_exists(session->server_callbacks, channel_open_request_session_function)) {
channel = session->server_callbacks->channel_open_request_session_function(session,
session->server_callbacks->userdata);
if (channel != NULL) {
rc = ssh_message_channel_request_open_reply_accept_channel(msg, channel);
return SSH_OK;
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
}
break;
case SSH_REQUEST_CHANNEL:
channel = msg->channel_request.channel;
if (msg->channel_request.type == SSH_CHANNEL_REQUEST_PTY &&
ssh_callbacks_exists(channel->callbacks, channel_pty_request_function)) {
rc = channel->callbacks->channel_pty_request_function(session, channel,
msg->channel_request.TERM,
msg->channel_request.width, msg->channel_request.height,
msg->channel_request.pxwidth, msg->channel_request.pxheight,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SHELL &&
ssh_callbacks_exists(channel->callbacks, channel_shell_request_function)) {
rc = channel->callbacks->channel_shell_request_function(session,
channel,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_X11 &&
ssh_callbacks_exists(channel->callbacks, channel_x11_req_function)) {
channel->callbacks->channel_x11_req_function(session,
channel,
msg->channel_request.x11_single_connection,
msg->channel_request.x11_auth_protocol,
msg->channel_request.x11_auth_cookie,
msg->channel_request.x11_screen_number,
channel->callbacks->userdata);
ssh_message_channel_request_reply_success(msg);
return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_WINDOW_CHANGE &&
ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) {
rc = channel->callbacks->channel_pty_window_change_function(session,
channel,
msg->channel_request.width, msg->channel_request.height,
msg->channel_request.pxwidth, msg->channel_request.pxheight,
channel->callbacks->userdata);
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC &&
ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) {
rc = channel->callbacks->channel_exec_request_function(session,
channel,
msg->channel_request.command,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_ENV &&
ssh_callbacks_exists(channel->callbacks, channel_env_request_function)) {
rc = channel->callbacks->channel_env_request_function(session,
channel,
msg->channel_request.var_name, msg->channel_request.var_value,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_SUBSYSTEM &&
ssh_callbacks_exists(channel->callbacks, channel_subsystem_request_function)) {
rc = channel->callbacks->channel_subsystem_request_function(session,
channel,
msg->channel_request.subsystem,
channel->callbacks->userdata);
if (rc == 0) {
ssh_message_channel_request_reply_success(msg);
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
}
break;
case SSH_REQUEST_SERVICE:
if (ssh_callbacks_exists(session->server_callbacks, service_request_function)) {
rc = session->server_callbacks->service_request_function(session,
msg->service_request.service, session->server_callbacks->userdata);
if (rc == 0) {
ssh_message_reply_default(msg);
} else {
ssh_disconnect(session);
}
return SSH_OK;
}
return SSH_AGAIN;
case SSH_REQUEST_GLOBAL:
break;
}
return SSH_AGAIN;
}
static int ssh_execute_client_request(ssh_session session, ssh_message msg)
{
ssh_channel channel = NULL;
int rc = SSH_AGAIN;
|