2019-02-05 04:03:44 +03:00
|
|
|
/* This is a sample implementation of a libssh based SSH server */
|
|
|
|
/*
|
|
|
|
Copyright 2003-2009 Aris Adamantiadis
|
|
|
|
Copyright 2018 T. Wimmer
|
|
|
|
|
|
|
|
This file is part of the SSH Library
|
|
|
|
|
|
|
|
You are free to copy this file, modify it in any way, consider it being public
|
|
|
|
domain. This does not apply to the rest of the library though, but it is
|
|
|
|
allowed to cut-and-paste working code from this file to any license of
|
|
|
|
program.
|
|
|
|
The goal is to show the API in action. It's not a reference on how terminal
|
|
|
|
clients must be made or how a client should react.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
Example:
|
|
|
|
./sshd_direct-tcpip -v -p 2022 -d serverkey.dsa -r serverkey.rsa 127.0.0.1
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <libssh/libssh.h>
|
|
|
|
#include <libssh/server.h>
|
|
|
|
#include <libssh/callbacks.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_ARGP_H
|
|
|
|
#include <argp.h>
|
|
|
|
#endif
|
2020-04-09 12:46:18 +03:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
2019-02-05 04:03:44 +03:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <poll.h>
|
|
|
|
|
2021-05-09 21:00:24 +03:00
|
|
|
#ifndef BUF_SIZE
|
|
|
|
#define BUF_SIZE 16384
|
|
|
|
#endif
|
|
|
|
|
2019-11-08 14:13:23 +03:00
|
|
|
#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
|
|
|
|
|
|
|
|
#ifndef __unused__
|
|
|
|
# ifdef HAVE_UNUSED_ATTRIBUTE
|
|
|
|
# define __unused__ __attribute__((unused))
|
|
|
|
# else /* HAVE_UNUSED_ATTRIBUTE */
|
|
|
|
# define __unused__
|
|
|
|
# endif /* HAVE_UNUSED_ATTRIBUTE */
|
|
|
|
#endif /* __unused__ */
|
|
|
|
|
|
|
|
#ifndef UNUSED_PARAM
|
|
|
|
#define UNUSED_PARAM(param) param __unused__
|
|
|
|
#endif /* UNUSED_PARAM */
|
|
|
|
|
2019-02-05 04:03:44 +03:00
|
|
|
#ifndef KEYS_FOLDER
|
|
|
|
#ifdef _WIN32
|
|
|
|
#define KEYS_FOLDER
|
|
|
|
#else
|
|
|
|
#define KEYS_FOLDER "/etc/ssh/"
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define USER "user"
|
|
|
|
#define PASSWORD "pwd"
|
|
|
|
|
|
|
|
struct event_fd_data_struct {
|
|
|
|
int *p_fd;
|
|
|
|
ssh_channel channel;
|
|
|
|
struct ssh_channel_callbacks_struct *cb_chan;
|
|
|
|
int stacked;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cleanup_node_struct {
|
|
|
|
struct event_fd_data_struct *data;
|
|
|
|
struct cleanup_node_struct *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool authenticated = false;
|
|
|
|
static int tries = 0;
|
|
|
|
static bool error_set = false;
|
|
|
|
static int sockets_cnt = 0;
|
|
|
|
static ssh_event mainloop = NULL;
|
|
|
|
static struct cleanup_node_struct *cleanup_stack = NULL;
|
|
|
|
|
|
|
|
static void _close_socket(struct event_fd_data_struct event_fd_data);
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
cleanup_push(struct cleanup_node_struct** head_ref,
|
|
|
|
struct event_fd_data_struct *new_data)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
// Allocate memory for node
|
|
|
|
struct cleanup_node_struct *new_node = malloc(sizeof *new_node);
|
|
|
|
|
2022-08-11 18:03:26 +03:00
|
|
|
if (*head_ref != NULL) {
|
2021-08-04 10:22:08 +03:00
|
|
|
new_node->next = *head_ref;
|
|
|
|
} else {
|
|
|
|
new_node->next = NULL;
|
|
|
|
}
|
2019-02-05 04:03:44 +03:00
|
|
|
|
|
|
|
// Copy new_data
|
|
|
|
new_node->data = new_data;
|
|
|
|
|
|
|
|
// Change head pointer as new node is added at the beginning
|
|
|
|
(*head_ref) = new_node;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
do_cleanup(struct cleanup_node_struct **head_ref)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
struct cleanup_node_struct *current = (*head_ref);
|
|
|
|
struct cleanup_node_struct *previous = NULL, *gone = NULL;
|
|
|
|
|
|
|
|
while (current != NULL) {
|
|
|
|
if (ssh_channel_is_closed(current->data->channel)) {
|
|
|
|
if (current == (*head_ref)) {
|
|
|
|
(*head_ref) = current->next;
|
|
|
|
}
|
|
|
|
if (previous != NULL) {
|
|
|
|
previous->next = current->next;
|
|
|
|
}
|
|
|
|
gone = current;
|
|
|
|
current = current->next;
|
|
|
|
|
|
|
|
if (gone->data->channel) {
|
|
|
|
_close_socket(*gone->data);
|
|
|
|
ssh_remove_channel_callbacks(gone->data->channel, gone->data->cb_chan);
|
|
|
|
ssh_channel_free(gone->data->channel);
|
|
|
|
gone->data->channel = NULL;
|
|
|
|
|
|
|
|
SAFE_FREE(gone->data->p_fd);
|
|
|
|
SAFE_FREE(gone->data->cb_chan);
|
|
|
|
SAFE_FREE(gone->data);
|
|
|
|
SAFE_FREE(gone);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fprintf(stderr, "channel already freed!\n");
|
|
|
|
}
|
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== do_cleanup", "Freed.");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ssh_channel_close(current->data->channel);
|
|
|
|
previous = current;
|
|
|
|
current = current->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
auth_password(ssh_session session,
|
|
|
|
const char *user,
|
|
|
|
const char *password,
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== auth_password", "Authenticating user %s pwd %s",
|
|
|
|
user,
|
|
|
|
password);
|
|
|
|
if (strcmp(user, USER) == 0 && strcmp(password, PASSWORD) == 0) {
|
2019-02-05 04:03:44 +03:00
|
|
|
authenticated = true;
|
|
|
|
printf("Authenticated\n");
|
|
|
|
return SSH_AUTH_SUCCESS;
|
|
|
|
}
|
2020-04-16 09:16:28 +03:00
|
|
|
if (tries >= 3) {
|
2019-02-05 04:03:44 +03:00
|
|
|
printf("Too many authentication tries\n");
|
|
|
|
ssh_disconnect(session);
|
|
|
|
error_set = true;
|
|
|
|
return SSH_AUTH_DENIED;
|
|
|
|
}
|
|
|
|
tries++;
|
|
|
|
return SSH_AUTH_DENIED;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
auth_gssapi_mic(ssh_session session,
|
|
|
|
const char *user,
|
|
|
|
const char *principal,
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
ssh_gssapi_creds creds = ssh_gssapi_get_creds(session);
|
2020-04-16 09:16:28 +03:00
|
|
|
printf("Authenticating user %s with gssapi principal %s\n",
|
|
|
|
user, principal);
|
|
|
|
if (creds != NULL) {
|
2019-02-05 04:03:44 +03:00
|
|
|
printf("Received some gssapi credentials\n");
|
2020-04-16 09:16:28 +03:00
|
|
|
} else {
|
2019-02-05 04:03:44 +03:00
|
|
|
printf("Not received any forwardable creds\n");
|
2020-04-16 09:16:28 +03:00
|
|
|
}
|
2019-02-05 04:03:44 +03:00
|
|
|
printf("authenticated\n");
|
|
|
|
authenticated = true;
|
|
|
|
return SSH_AUTH_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
subsystem_request(UNUSED_PARAM(ssh_session session),
|
|
|
|
UNUSED_PARAM(ssh_channel channel),
|
|
|
|
const char *subsystem,
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== subsystem_request", "Channel subsystem reqeuest: %s",
|
|
|
|
subsystem);
|
2019-02-05 04:03:44 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ssh_channel_callbacks_struct channel_cb = {
|
|
|
|
.channel_subsystem_request_function = subsystem_request
|
|
|
|
};
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static ssh_channel
|
|
|
|
new_session_channel(UNUSED_PARAM(ssh_session session),
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== subsystem_request", "Session channel request");
|
|
|
|
/* For TCP forward only there seems to be no need for a session channel */
|
|
|
|
/*if(chan != NULL)
|
|
|
|
return NULL;
|
|
|
|
printf("Session channel request\n");
|
|
|
|
chan = ssh_channel_new(session);
|
|
|
|
ssh_callbacks_init(&channel_cb);
|
|
|
|
ssh_set_channel_callbacks(chan, &channel_cb);
|
|
|
|
return chan;*/
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
stack_socket_close(UNUSED_PARAM(ssh_session session),
|
|
|
|
struct event_fd_data_struct *event_fd_data)
|
2019-02-20 19:13:44 +03:00
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
if (event_fd_data->stacked != 1) {
|
2020-04-16 09:16:28 +03:00
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== stack_socket_close",
|
|
|
|
"Closing fd = %d sockets_cnt = %d", *event_fd_data->p_fd,
|
|
|
|
sockets_cnt);
|
2019-02-05 04:03:44 +03:00
|
|
|
event_fd_data->stacked = 1;
|
|
|
|
cleanup_push(&cleanup_stack, event_fd_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
_close_socket(struct event_fd_data_struct event_fd_data)
|
|
|
|
{
|
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== close_socket",
|
|
|
|
"Closing fd = %d sockets_cnt = %d", *event_fd_data.p_fd,
|
|
|
|
sockets_cnt);
|
2019-02-05 04:03:44 +03:00
|
|
|
ssh_event_remove_fd(mainloop, *event_fd_data.p_fd);
|
|
|
|
sockets_cnt--;
|
|
|
|
#ifdef _WIN32
|
|
|
|
closesocket(*event_fd_data.p_fd);
|
|
|
|
#else
|
|
|
|
close(*event_fd_data.p_fd);
|
|
|
|
#endif // _WIN32
|
|
|
|
(*event_fd_data.p_fd) = SSH_INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
service_request(UNUSED_PARAM(ssh_session session),
|
|
|
|
const char *service,
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== service_request", "Service request: %s", service);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
global_request(UNUSED_PARAM(ssh_session session),
|
|
|
|
ssh_message message,
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== global_request", "Global request, message type: %d",
|
|
|
|
ssh_message_type(message));
|
2019-02-05 04:03:44 +03:00
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
my_channel_close_function(ssh_session session,
|
|
|
|
UNUSED_PARAM(ssh_channel channel),
|
|
|
|
void *userdata)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata;
|
|
|
|
|
2019-11-08 14:13:23 +03:00
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== my_channel_close_function",
|
|
|
|
"Channel closed by remote.");
|
2019-02-05 04:03:44 +03:00
|
|
|
|
|
|
|
stack_socket_close(session, event_fd_data);
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
my_channel_eof_function(ssh_session session,
|
|
|
|
UNUSED_PARAM(ssh_channel channel),
|
|
|
|
void *userdata)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata;
|
|
|
|
|
2019-11-08 14:13:23 +03:00
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== my_channel_eof_function",
|
|
|
|
"Got EOF on channel. Shuting down write on socket (fd = %d).",
|
|
|
|
*event_fd_data->p_fd);
|
2019-02-05 04:03:44 +03:00
|
|
|
|
|
|
|
stack_socket_close(session, event_fd_data);
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static void
|
|
|
|
my_channel_exit_status_function(UNUSED_PARAM(ssh_session session),
|
|
|
|
UNUSED_PARAM(ssh_channel channel),
|
|
|
|
int exit_status,
|
|
|
|
void *userdata)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata;
|
|
|
|
|
2019-11-08 14:13:23 +03:00
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== my_channel_exit_status_function",
|
|
|
|
"Got exit status %d on channel fd = %d.",
|
|
|
|
exit_status, *event_fd_data->p_fd);
|
2019-02-05 04:03:44 +03:00
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
my_channel_data_function(ssh_session session,
|
|
|
|
UNUSED_PARAM(ssh_channel channel),
|
|
|
|
void *data,
|
|
|
|
uint32_t len,
|
|
|
|
UNUSED_PARAM(int is_stderr),
|
|
|
|
void *userdata)
|
2019-02-20 19:13:44 +03:00
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
int i = 0;
|
|
|
|
struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata;
|
|
|
|
|
|
|
|
if (event_fd_data->channel == NULL) {
|
|
|
|
fprintf(stderr, "Why we're here? Stacked = %d\n", event_fd_data->stacked);
|
|
|
|
}
|
|
|
|
|
2019-11-08 14:13:23 +03:00
|
|
|
_ssh_log(SSH_LOG_PROTOCOL,
|
|
|
|
"=== my_channel_data_function",
|
|
|
|
"%d bytes waiting on channel for reading. Fd = %d",
|
|
|
|
len,
|
|
|
|
*event_fd_data->p_fd);
|
2019-02-05 04:03:44 +03:00
|
|
|
if (len > 0) {
|
|
|
|
i = send(*event_fd_data->p_fd, data, len, 0);
|
|
|
|
}
|
|
|
|
if (i < 0) {
|
2020-04-16 09:16:28 +03:00
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== my_channel_data_function",
|
|
|
|
"Writing to tcp socket %d: %s", *event_fd_data->p_fd,
|
|
|
|
strerror(errno));
|
2019-02-05 04:03:44 +03:00
|
|
|
stack_socket_close(session, event_fd_data);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== my_channel_data_function", "Sent %d bytes", i);
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
my_fd_data_function(UNUSED_PARAM(socket_t fd),
|
|
|
|
int revents,
|
|
|
|
void *userdata)
|
2019-02-20 19:13:44 +03:00
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
struct event_fd_data_struct *event_fd_data = (struct event_fd_data_struct *)userdata;
|
|
|
|
ssh_channel channel = event_fd_data->channel;
|
|
|
|
ssh_session session;
|
|
|
|
int len, i, wr;
|
2021-05-09 21:00:24 +03:00
|
|
|
char buf[BUF_SIZE];
|
2019-02-05 04:03:44 +03:00
|
|
|
int blocking;
|
|
|
|
|
|
|
|
if (channel == NULL) {
|
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== my_fd_data_function", "channel == NULL!");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
session = ssh_channel_get_session(channel);
|
|
|
|
|
|
|
|
if (ssh_channel_is_closed(channel)) {
|
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== my_fd_data_function", "channel is closed!");
|
|
|
|
stack_socket_close(session, event_fd_data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(revents & POLLIN)) {
|
|
|
|
if (revents & POLLPRI) {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== my_fd_data_function", "poll revents & POLLPRI");
|
|
|
|
}
|
|
|
|
if (revents & POLLOUT) {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== my_fd_data_function", "poll revents & POLLOUT");
|
|
|
|
}
|
|
|
|
if (revents & POLLHUP) {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== my_fd_data_function", "poll revents & POLLHUP");
|
|
|
|
}
|
|
|
|
if (revents & POLLNVAL) {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== my_fd_data_function", "poll revents & POLLNVAL");
|
|
|
|
}
|
|
|
|
if (revents & POLLERR) {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== my_fd_data_function", "poll revents & POLLERR");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
blocking = ssh_is_blocking(session);
|
|
|
|
ssh_set_blocking(session, 0);
|
|
|
|
|
2019-11-08 14:13:23 +03:00
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS,
|
|
|
|
"=== my_fd_data_function",
|
|
|
|
"Trying to read from tcp socket fd = %d",
|
|
|
|
*event_fd_data->p_fd);
|
2019-02-05 04:03:44 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
struct sockaddr from;
|
|
|
|
int fromlen = sizeof(from);
|
|
|
|
len = recvfrom(*event_fd_data->p_fd, buf, sizeof(buf), 0, &from, &fromlen);
|
|
|
|
#else
|
|
|
|
len = recv(*event_fd_data->p_fd, buf, sizeof(buf), 0);
|
|
|
|
#endif // _WIN32
|
|
|
|
if (len < 0) {
|
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== my_fd_data_function", "Reading from tcp socket: %s", strerror(errno));
|
|
|
|
|
|
|
|
ssh_channel_send_eof(channel);
|
|
|
|
}
|
|
|
|
else if (len > 0) {
|
|
|
|
if (ssh_channel_is_open(channel)) {
|
|
|
|
wr = 0;
|
|
|
|
do {
|
|
|
|
i = ssh_channel_write(channel, buf, len);
|
|
|
|
if (i < 0) {
|
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== my_fd_data_function", "Error writing on the direct-tcpip channel: %d", i);
|
|
|
|
len = wr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wr += i;
|
2020-09-16 20:03:39 +03:00
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== my_fd_data_function", "ssh_channel_write (%d from %d)", wr, len);
|
2019-02-05 04:03:44 +03:00
|
|
|
} while (i > 0 && wr < len);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== my_fd_data_function", "Can't write on closed channel!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== my_fd_data_function", "The destination host has disconnected!");
|
|
|
|
|
|
|
|
ssh_channel_close(channel);
|
|
|
|
#ifdef _WIN32
|
|
|
|
shutdown(*event_fd_data->p_fd, SD_RECEIVE);
|
|
|
|
#else
|
|
|
|
shutdown(*event_fd_data->p_fd, SHUT_RD);
|
|
|
|
#endif // _WIN32
|
|
|
|
}
|
|
|
|
ssh_set_blocking(session, blocking);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
open_tcp_socket(ssh_message msg)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
struct sockaddr_in sin;
|
|
|
|
int forwardsock = -1;
|
|
|
|
struct hostent *host;
|
|
|
|
const char *dest_hostname;
|
|
|
|
int dest_port;
|
|
|
|
|
|
|
|
forwardsock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (forwardsock < 0) {
|
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== open_tcp_socket", "ERROR opening socket: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest_hostname = ssh_message_channel_request_open_destination(msg);
|
|
|
|
dest_port = ssh_message_channel_request_open_destination_port(msg);
|
|
|
|
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== open_tcp_socket", "Connecting to %s on port %d", dest_hostname, dest_port);
|
|
|
|
|
|
|
|
host = gethostbyname(dest_hostname);
|
|
|
|
if (host == NULL) {
|
2019-05-28 09:36:21 +03:00
|
|
|
close(forwardsock);
|
2019-02-05 04:03:44 +03:00
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== open_tcp_socket", "ERROR, no such host: %s", dest_hostname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset((char *)&sin, '\0', sizeof(sin));
|
|
|
|
sin.sin_family = AF_INET;
|
|
|
|
memcpy((char *)&sin.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
|
|
|
|
sin.sin_port = htons(dest_port);
|
|
|
|
|
|
|
|
if (connect(forwardsock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
|
2019-05-28 09:36:21 +03:00
|
|
|
close(forwardsock);
|
2019-02-05 04:03:44 +03:00
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== open_tcp_socket", "ERROR connecting: %s", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sockets_cnt++;
|
|
|
|
_ssh_log(SSH_LOG_FUNCTIONS, "=== open_tcp_socket", "Connected. sockets_cnt = %d", sockets_cnt);
|
|
|
|
return forwardsock;
|
|
|
|
}
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
static int
|
|
|
|
message_callback(UNUSED_PARAM(ssh_session session),
|
|
|
|
ssh_message message,
|
|
|
|
UNUSED_PARAM(void *userdata))
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
ssh_channel channel;
|
|
|
|
int socket_fd, *pFd;
|
|
|
|
struct ssh_channel_callbacks_struct *cb_chan;
|
|
|
|
struct event_fd_data_struct *event_fd_data;
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
_ssh_log(SSH_LOG_PACKET, "=== message_callback", "Message type: %d",
|
|
|
|
ssh_message_type(message));
|
|
|
|
_ssh_log(SSH_LOG_PACKET, "=== message_callback", "Message Subtype: %d",
|
|
|
|
ssh_message_subtype(message));
|
2019-02-05 04:03:44 +03:00
|
|
|
if (ssh_message_type(message) == SSH_REQUEST_CHANNEL_OPEN) {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== message_callback", "channel_request_open");
|
|
|
|
|
|
|
|
if (ssh_message_subtype(message) == SSH_CHANNEL_DIRECT_TCPIP) {
|
|
|
|
channel = ssh_message_channel_request_open_reply_accept(message);
|
|
|
|
|
|
|
|
if (channel == NULL) {
|
|
|
|
_ssh_log(SSH_LOG_WARNING, "=== message_callback", "Accepting direct-tcpip channel failed!");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_ssh_log(SSH_LOG_PROTOCOL, "=== message_callback", "Connected to channel!");
|
|
|
|
|
|
|
|
socket_fd = open_tcp_socket(message);
|
|
|
|
if (-1 == socket_fd) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pFd = malloc(sizeof *pFd);
|
|
|
|
cb_chan = malloc(sizeof *cb_chan);
|
|
|
|
event_fd_data = malloc(sizeof *event_fd_data);
|
2021-08-04 10:22:08 +03:00
|
|
|
if (pFd == NULL || cb_chan == NULL || event_fd_data == NULL) {
|
|
|
|
SAFE_FREE(pFd);
|
|
|
|
SAFE_FREE(cb_chan);
|
|
|
|
SAFE_FREE(event_fd_data);
|
2021-11-04 12:50:53 +03:00
|
|
|
close(socket_fd);
|
2021-08-04 10:22:08 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2019-02-05 04:03:44 +03:00
|
|
|
|
|
|
|
(*pFd) = socket_fd;
|
|
|
|
event_fd_data->channel = channel;
|
|
|
|
event_fd_data->p_fd = pFd;
|
|
|
|
event_fd_data->stacked = 0;
|
|
|
|
event_fd_data->cb_chan = cb_chan;
|
|
|
|
|
|
|
|
cb_chan->userdata = event_fd_data;
|
|
|
|
cb_chan->channel_eof_function = my_channel_eof_function;
|
|
|
|
cb_chan->channel_close_function = my_channel_close_function;
|
|
|
|
cb_chan->channel_data_function = my_channel_data_function;
|
|
|
|
cb_chan->channel_exit_status_function = my_channel_exit_status_function;
|
|
|
|
|
|
|
|
ssh_callbacks_init(cb_chan);
|
|
|
|
ssh_set_channel_callbacks(channel, cb_chan);
|
|
|
|
|
|
|
|
ssh_event_add_fd(mainloop, (socket_t)*pFd, POLLIN, my_fd_data_function, event_fd_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_ARGP_H
|
|
|
|
const char *argp_program_version = "libssh server example "
|
|
|
|
SSH_STRINGIFY(LIBSSH_VERSION);
|
|
|
|
const char *argp_program_bug_address = "<libssh@libssh.org>";
|
|
|
|
|
|
|
|
/* Program documentation. */
|
|
|
|
static char doc[] = "libssh -- a Secure Shell protocol implementation";
|
|
|
|
|
|
|
|
/* A description of the arguments we accept. */
|
|
|
|
static char args_doc[] = "BINDADDR";
|
|
|
|
|
|
|
|
/* The options we understand. */
|
|
|
|
static struct argp_option options[] = {
|
|
|
|
{
|
|
|
|
.name = "port",
|
|
|
|
.key = 'p',
|
|
|
|
.arg = "PORT",
|
|
|
|
.flags = 0,
|
|
|
|
.doc = "Set the port to bind.",
|
|
|
|
.group = 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "hostkey",
|
|
|
|
.key = 'k',
|
|
|
|
.arg = "FILE",
|
|
|
|
.flags = 0,
|
|
|
|
.doc = "Set the host key.",
|
|
|
|
.group = 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "dsakey",
|
|
|
|
.key = 'd',
|
|
|
|
.arg = "FILE",
|
|
|
|
.flags = 0,
|
|
|
|
.doc = "Set the dsa key.",
|
|
|
|
.group = 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "rsakey",
|
|
|
|
.key = 'r',
|
|
|
|
.arg = "FILE",
|
|
|
|
.flags = 0,
|
|
|
|
.doc = "Set the rsa key.",
|
|
|
|
.group = 0
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "verbose",
|
|
|
|
.key = 'v',
|
|
|
|
.arg = NULL,
|
|
|
|
.flags = 0,
|
|
|
|
.doc = "Get verbose output.",
|
|
|
|
.group = 0
|
|
|
|
},
|
|
|
|
{NULL, 0, NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Parse a single option. */
|
2020-04-16 09:16:28 +03:00
|
|
|
static error_t
|
|
|
|
parse_opt (int key, char *arg, struct argp_state *state)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
/* Get the input argument from argp_parse, which we
|
|
|
|
* know is a pointer to our arguments structure.
|
|
|
|
*/
|
|
|
|
ssh_bind sshbind = state->input;
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case 'p':
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, arg);
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, arg);
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, arg);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, arg);
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, "1");
|
|
|
|
break;
|
|
|
|
case ARGP_KEY_ARG:
|
|
|
|
if (state->arg_num >= 1) {
|
|
|
|
/* Too many arguments. */
|
|
|
|
argp_usage (state);
|
|
|
|
}
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, arg);
|
|
|
|
break;
|
|
|
|
case ARGP_KEY_END:
|
|
|
|
if (state->arg_num < 1) {
|
|
|
|
/* Not enough arguments. */
|
|
|
|
argp_usage (state);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return ARGP_ERR_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Our argp parser. */
|
|
|
|
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
|
|
|
|
#endif /* HAVE_ARGP_H */
|
|
|
|
|
2020-04-16 09:16:28 +03:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2019-02-05 04:03:44 +03:00
|
|
|
ssh_session session;
|
|
|
|
ssh_bind sshbind;
|
|
|
|
struct ssh_server_callbacks_struct cb = {
|
|
|
|
.userdata = NULL,
|
|
|
|
.auth_password_function = auth_password,
|
|
|
|
.auth_gssapi_mic_function = auth_gssapi_mic,
|
|
|
|
.channel_open_request_session_function = new_session_channel,
|
|
|
|
.service_request_function = service_request
|
|
|
|
};
|
|
|
|
struct ssh_callbacks_struct cb_gen = {
|
|
|
|
.userdata = NULL,
|
|
|
|
.global_request_function = global_request
|
|
|
|
};
|
|
|
|
|
|
|
|
int ret = 1;
|
|
|
|
|
|
|
|
sshbind = ssh_bind_new();
|
|
|
|
session = ssh_new();
|
|
|
|
mainloop = ssh_event_new();
|
|
|
|
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, KEYS_FOLDER "ssh_host_dsa_key");
|
|
|
|
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, KEYS_FOLDER "ssh_host_rsa_key");
|
|
|
|
|
|
|
|
#ifdef HAVE_ARGP_H
|
|
|
|
/*
|
|
|
|
* Parse our arguments; every option seen by parse_opt will
|
|
|
|
* be reflected in arguments.
|
|
|
|
*/
|
|
|
|
argp_parse (&argp, argc, argv, 0, 0, sshbind);
|
|
|
|
#else
|
|
|
|
(void)argc;
|
|
|
|
(void)argv;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ssh_bind_listen(sshbind) < 0) {
|
|
|
|
printf("Error listening to socket: %s\n", ssh_get_error(sshbind));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ssh_bind_accept(sshbind, session) == SSH_ERROR) {
|
|
|
|
printf("error accepting a connection : %s\n", ssh_get_error(sshbind));
|
|
|
|
ret = 1;
|
|
|
|
goto shutdown;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssh_callbacks_init(&cb);
|
|
|
|
ssh_callbacks_init(&cb_gen);
|
|
|
|
ssh_set_server_callbacks(session, &cb);
|
|
|
|
ssh_set_callbacks(session, &cb_gen);
|
|
|
|
ssh_set_message_callback(session, message_callback, (void *)NULL);
|
|
|
|
|
|
|
|
if (ssh_handle_key_exchange(session)) {
|
|
|
|
printf("ssh_handle_key_exchange: %s\n", ssh_get_error(session));
|
|
|
|
ret = 1;
|
|
|
|
goto shutdown;
|
|
|
|
}
|
|
|
|
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_GSSAPI_MIC);
|
|
|
|
ssh_event_add_session(mainloop, session);
|
|
|
|
|
|
|
|
while (!authenticated) {
|
|
|
|
if (error_set) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ssh_event_dopoll(mainloop, -1) == SSH_ERROR) {
|
|
|
|
printf("Error : %s\n", ssh_get_error(session));
|
|
|
|
ret = 1;
|
|
|
|
goto shutdown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error_set) {
|
|
|
|
printf("Error, exiting loop\n");
|
|
|
|
} else {
|
|
|
|
printf("Authenticated and got a channel\n");
|
|
|
|
|
|
|
|
while (!error_set) {
|
|
|
|
if (ssh_event_dopoll(mainloop, 100) == SSH_ERROR) {
|
|
|
|
printf("Error : %s\n", ssh_get_error(session));
|
|
|
|
ret = 1;
|
|
|
|
goto shutdown;
|
|
|
|
}
|
|
|
|
do_cleanup(&cleanup_stack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
shutdown:
|
|
|
|
ssh_disconnect(session);
|
|
|
|
ssh_bind_free(sshbind);
|
|
|
|
ssh_finalize();
|
|
|
|
return ret;
|
|
|
|
}
|