From 26d40b5354d6760472127f2c16045fbbb2f12fee Mon Sep 17 00:00:00 2001 From: milo Date: Wed, 29 Sep 2010 17:45:04 +0200 Subject: [PATCH] Handle global requests and reverse forwarding --- include/libssh/libssh.h | 6 +++ include/libssh/messages.h | 9 ++++ include/libssh/server.h | 9 ++++ src/channels.c | 73 +++++++++++++++++++++++++++++++- src/messages.c | 87 +++++++++++++++++++++++++++++++++++++++ src/packet.c | 2 +- src/server.c | 58 ++++++++++++++++++++++++++ 7 files changed, 242 insertions(+), 2 deletions(-) diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index 22957c38..0471c40c 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -193,6 +193,12 @@ enum ssh_channel_requests_e { SSH_CHANNEL_REQUEST_WINDOW_CHANGE }; +enum ssh_global_requests_e { + SSH_GLOBAL_REQUEST_UNKNOWN=0, + SSH_GLOBAL_REQUEST_TCPIP_FORWARD, + SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD, +}; + enum ssh_publickey_state_e { SSH_PUBLICKEY_STATE_ERROR=-1, SSH_PUBLICKEY_STATE_NONE=0, diff --git a/include/libssh/messages.h b/include/libssh/messages.h index 3029ae88..419c3cbb 100644 --- a/include/libssh/messages.h +++ b/include/libssh/messages.h @@ -47,6 +47,13 @@ struct ssh_service_request { char *service; }; +struct ssh_global_request { + int type; + uint8_t want_reply; + char *bind_address; + uint16_t bind_port; +}; + struct ssh_channel_request { int type; ssh_channel channel; @@ -75,11 +82,13 @@ struct ssh_message_struct { struct ssh_channel_request_open channel_request_open; struct ssh_channel_request channel_request; struct ssh_service_request service_request; + struct ssh_global_request global_request; }; SSH_PACKET_CALLBACK(ssh_packet_channel_open); SSH_PACKET_CALLBACK(ssh_packet_service_request); SSH_PACKET_CALLBACK(ssh_packet_userauth_request); +SSH_PACKET_CALLBACK(ssh_packet_global_request); int ssh_message_handle_channel_request(ssh_session session, ssh_channel channel, ssh_buffer packet, const char *request, uint8_t want_reply); diff --git a/include/libssh/server.h b/include/libssh/server.h index 47a84a6c..b4116dc2 100644 --- a/include/libssh/server.h +++ b/include/libssh/server.h @@ -158,6 +158,9 @@ LIBSSH_API int ssh_message_auth_set_methods(ssh_message msg, int methods); LIBSSH_API int ssh_message_service_reply_success(ssh_message msg); LIBSSH_API char *ssh_message_service_service(ssh_message msg); +LIBSSH_API int ssh_message_global_request_reply_success(ssh_message msg, + uint16_t bound_port); + LIBSSH_API void ssh_set_message_callback(ssh_session session, int(*ssh_bind_message_callback)(ssh_session session, ssh_message msg, void *data), void *data); @@ -183,6 +186,12 @@ LIBSSH_API char *ssh_message_channel_request_command(ssh_message msg); LIBSSH_API char *ssh_message_channel_request_subsystem(ssh_message msg); +LIBSSH_API char *ssh_message_global_request_address(ssh_message msg); +LIBSSH_API int ssh_message_global_request_port(ssh_message msg); + +LIBSSH_API int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport); + /* deprecated functions */ SSH_DEPRECATED LIBSSH_API int ssh_accept(ssh_session session); diff --git a/src/channels.c b/src/channels.c index 83d01023..b91db723 100644 --- a/src/channels.c +++ b/src/channels.c @@ -41,6 +41,9 @@ #include "libssh/session.h" #include "libssh/misc.h" #include "libssh/messages.h" +#if WITH_SERVER +#include "libssh/server.h" +#endif #define WINDOWBASE 128000 #define WINDOWLIMIT (WINDOWBASE/2) @@ -2503,6 +2506,74 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans, return 0; } -/** @} */ +#if WITH_SERVER +/** + * @brief Open a TCP/IP reverse forwarding channel. + * + * @param[in] channel An allocated channel. + * + * @param[in] remotehost The remote host to connected (host name or IP). + * + * @param[in] remoteport The remote port. + * + * @param[in] sourcehost The source host (your local computer). It's optional + * and for logging purpose. + * + * @param[in] localport The source port (your local computer). It's optional + * and for logging purpose. + * + * @return SSH_OK on success, SSH_ERROR if an error occured. + * + * @warning This function does not bind the local port and does not automatically + * forward the content of a socket to the channel. You still have to + * use channel_read and channel_write for this. + */ +int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost, + int remoteport, const char *sourcehost, int localport) { + ssh_session session = channel->session; + ssh_buffer payload = NULL; + ssh_string str = NULL; + int rc = SSH_ERROR; + + enter_function(); + + payload = ssh_buffer_new(); + if (payload == NULL) { + goto error; + } + str = ssh_string_from_char(remotehost); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(remoteport)) < 0) { + goto error; + } + + ssh_string_free(str); + str = ssh_string_from_char(sourcehost); + if (str == NULL) { + goto error; + } + + if (buffer_add_ssh_string(payload, str) < 0 || + buffer_add_u32(payload,htonl(localport)) < 0) { + goto error; + } + + rc = channel_open(channel, "forwarded-tcpip", 64000, 32000, payload); + +error: + ssh_buffer_free(payload); + ssh_string_free(str); + + leave_function(); + return rc; +} + +#endif + +/* @} */ /* vim: set ts=4 sw=4 et cindent: */ diff --git a/src/messages.c b/src/messages.c index 027daf24..e1297c2c 100644 --- a/src/messages.c +++ b/src/messages.c @@ -39,6 +39,9 @@ #include "libssh/keys.h" #include "libssh/dh.h" #include "libssh/messages.h" +#if WITH_SERVER +#include "libssh/server.h" +#endif /** * @defgroup libssh_messages The SSH message functions @@ -64,6 +67,85 @@ static ssh_message ssh_message_new(ssh_session session){ return msg; } +SSH_PACKET_CALLBACK(ssh_packet_global_request){ + ssh_message msg = NULL; + ssh_string request_s=NULL; + char *request=NULL; + ssh_string bind_addr_s=NULL; + char *bind_addr=NULL; + uint32_t bind_port; + uint8_t want_reply; + (void)user; + (void)type; + (void)packet; + + request_s = buffer_get_ssh_string(packet); + if (request_s != NULL) { + request = ssh_string_to_char(request_s); + ssh_string_free(request_s); + } + + buffer_get_u8(packet, &want_reply); + + ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_MSG_GLOBAL_REQUEST packet"); + + msg = ssh_message_new(session); + msg->type = SSH_REQUEST_GLOBAL; + + if(!strcmp(request, "tcpip-forward")) { + bind_addr_s = buffer_get_ssh_string(packet); + if (bind_addr_s != NULL) { + bind_addr = ssh_string_to_char(bind_addr_s); + ssh_string_free(bind_addr_s); + } + + buffer_get_u32(packet, &bind_port); + bind_port = ntohl(bind_port); + + msg->global_request.type = SSH_GLOBAL_REQUEST_TCPIP_FORWARD; + msg->global_request.want_reply = want_reply; + msg->global_request.bind_address = bind_addr; + msg->global_request.bind_port = bind_port; + + ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + + if(ssh_callbacks_exists(session->callbacks, global_request_function)) { + ssh_log(session, SSH_LOG_PROTOCOL, "Calling callback for SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + session->callbacks->global_request_function(session, msg, session->callbacks->userdata); + } else { + ssh_message_reply_default(msg); + } + } else if(!strcmp(request, "cancel-tcpip-forward")) { + bind_addr_s = buffer_get_ssh_string(packet); + if (bind_addr_s != NULL) { + bind_addr = ssh_string_to_char(bind_addr_s); + ssh_string_free(bind_addr_s); + } + buffer_get_u32(packet, &bind_port); + bind_port = ntohl(bind_port); + + msg->global_request.type = SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD; + msg->global_request.want_reply = want_reply; + msg->global_request.bind_address = bind_addr; + msg->global_request.bind_port = bind_port; + + ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_GLOBAL_REQUEST %s %d %s:%d", request, want_reply, bind_addr, bind_port); + + if(ssh_callbacks_exists(session->callbacks, global_request_function)) { + session->callbacks->global_request_function(session, msg, session->callbacks->userdata); + } else { + ssh_message_reply_default(msg); + } + } else { + ssh_log(session, SSH_LOG_PROTOCOL, "UNKNOWN SSH_MSG_GLOBAL_REQUEST %s %d", request, want_reply); + } + + SAFE_FREE(msg); + SAFE_FREE(request); + SAFE_FREE(bind_addr); + return SSH_PACKET_USED; +} + SSH_PACKET_CALLBACK(ssh_packet_service_request){ ssh_string service = NULL; char *service_c = NULL; @@ -763,6 +845,8 @@ int ssh_message_subtype(ssh_message msg) { return msg->channel_request_open.type; case SSH_REQUEST_CHANNEL: return msg->channel_request.type; + case SSH_REQUEST_GLOBAL: + return msg->global_request.type; } return -1; @@ -798,6 +882,9 @@ void ssh_message_free(ssh_message msg){ case SSH_REQUEST_SERVICE: SAFE_FREE(msg->service_request.service); break; + case SSH_REQUEST_GLOBAL: + SAFE_FREE(msg->global_request.bind_address); + break; } ZERO_STRUCTP(msg); SAFE_FREE(msg); diff --git a/src/packet.c b/src/packet.c index 6acb0f3f..ed645780 100644 --- a/src/packet.c +++ b/src/packet.c @@ -85,7 +85,7 @@ ssh_packet_callback default_packet_handlers[]= { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 62-79 - NULL, // SSH2_MSG_GLOBAL_REQUEST 80 + ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80 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 diff --git a/src/server.c b/src/server.c index ac5eb05f..dcd4c9f7 100644 --- a/src/server.c +++ b/src/server.c @@ -890,6 +890,54 @@ int ssh_message_service_reply_success(ssh_message msg) { return packet_send(msg->session); } +int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) { + ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Accepting a global request"); + + if (msg->global_request.want_reply) { + if (buffer_add_u8(msg->session->out_buffer + , SSH2_MSG_REQUEST_SUCCESS) < 0) { + goto error; + } + + if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD + && msg->global_request.bind_port == 0) { + if (buffer_add_u32(msg->session->out_buffer, htonl(bound_port)) < 0) { + goto error; + } + } + + return packet_send(msg->session); + } + + if(msg->global_request.type == SSH_GLOBAL_REQUEST_TCPIP_FORWARD + && msg->global_request.bind_port == 0) { + ssh_log(msg->session, SSH_LOG_PACKET, + "The client doesn't want to know the remote port!"); + } + + return SSH_OK; +error: + return SSH_ERROR; +} + +static int ssh_message_global_request_reply_default(ssh_message msg) { + ssh_log(msg->session, SSH_LOG_FUNCTIONS, "Refusing a global request"); + + if (msg->global_request.want_reply) { + if (buffer_add_u8(msg->session->out_buffer + , SSH2_MSG_REQUEST_FAILURE) < 0) { + goto error; + } + return packet_send(msg->session); + } + ssh_log(msg->session, SSH_LOG_PACKET, + "The client doesn't want to know the request failed!"); + + return SSH_OK; +error: + return SSH_ERROR; +} + int ssh_message_reply_default(ssh_message msg) { if (msg == NULL) { return -1; @@ -904,6 +952,8 @@ int ssh_message_reply_default(ssh_message msg) { return ssh_message_channel_request_reply_default(msg); case SSH_REQUEST_SERVICE: return ssh_message_service_request_reply_default(msg); + case SSH_REQUEST_GLOBAL: + return ssh_message_global_request_reply_default(msg); default: ssh_log(msg->session, SSH_LOG_PACKET, "Don't know what to default reply to %d type", @@ -1063,6 +1113,14 @@ char *ssh_message_channel_request_subsystem(ssh_message msg){ return msg->channel_request.subsystem; } +char *ssh_message_global_request_address(ssh_message msg){ + return msg->global_request.bind_address; +} + +int ssh_message_global_request_port(ssh_message msg){ + return msg->global_request.bind_port; +} + /** @brief defines the SSH_MESSAGE callback * @param session the current ssh session * @param[in] ssh_message_callback_ a function pointer to a callback taking the