Files
genode/repos/gems/src/server/ssh_terminal/server.h
Sid Hussmann eaefcc2c6f ssh: add exec channel and exit on interactive
This commit implements the ssh exec channel request. It also handles
some shortcommings on the interactive channel like exit and concurrent
session establishments.

Pipes into the channel do not work yet. E.g.:
echo foobar | ssh noux@localhost -p 5555 "cat > /rw/test.txt"

The issue described with FIXME in Ssh::Server::incoming_connection()
could not be reproduced and might have been fixed with the improved
file descriptor handling.

Fixes #3401
2019-08-13 12:02:03 +02:00

281 lines
6.4 KiB
C++

/*
* \brief Component providing a Terminal session via SSH
* \author Josef Soentgen
* \author Pirmin Duss
* \date 2019-05-29
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
* Copyright (C) 2019 gapfruit AG
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _SSH_TERMINAL_SERVER_H_
#define _SSH_TERMINAL_SERVER_H_
/* Genode includes */
#include <base/log.h>
#include <base/registry.h>
#include <os/reporter.h>
/* libc includes */
#include <poll.h>
#include <pthread.h>
/* libssh includes */
#include <libssh/callbacks.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
/* local includes */
#include "login.h"
#include "terminal.h"
namespace Ssh {
using namespace Genode;
struct Server;
struct Session;
struct Terminal_session;
struct Terminal_registry;
}
/**
* forward declaration of the write available callback.
*/
static int write_avail_cb(socket_t fd, int revents, void *userdata);
struct Ssh::Session : Genode::Registry<Session>::Element
{
User _user { };
uint32_t _id { 0 };
int bad_auth_attempts { 0 };
bool auth_sucessful { false };
ssh_session session { nullptr };
ssh_channel channel { nullptr };
ssh_channel_callbacks channel_cb { nullptr };
Ssh::Terminal *terminal { nullptr };
Genode::Lock _access_lock { };
Genode::Lock &lock_terminal()
{
return _access_lock;
}
bool spawn_terminal { false };
Session(Genode::Registry<Session> &reg,
ssh_session s,
ssh_channel_callbacks ccb,
uint32_t id)
: Element(reg, *this), _id(id), session(s), channel_cb(ccb) { }
void adopt(User const &user) { _user = user; }
User const &user() const { return _user; }
uint32_t id() const { return _id; }
void add_channel(ssh_channel c)
{
ssh_set_channel_callbacks(c, channel_cb);
channel = c;
}
};
struct Ssh::Terminal_session : Genode::Registry<Terminal_session>::Element
{
Ssh::Terminal &conn;
ssh_event _event_loop;
int _fds[2] { -1, -1 };
Terminal_session(Genode::Registry<Terminal_session> &reg,
Ssh::Terminal &conn,
ssh_event event_loop)
: Element(reg, *this), conn(conn), _event_loop(event_loop)
{
if (pipe(_fds) ||
ssh_event_add_fd(_event_loop,
_fds[0],
POLLIN,
write_avail_cb,
this) != SSH_OK ) {
Genode::error("Failed to create wakeup pipe");
throw -1;
}
conn.write_avail_fd = _fds[1];
}
~Terminal_session()
{
ssh_event_remove_fd(_event_loop, _fds[0]);
close(_fds[0]);
close(_fds[1]);
}
};
struct Ssh::Terminal_registry : Genode::Registry<Terminal_session>
{
Genode::Lock _lock { };
Genode::Lock &lock() { return _lock; }
};
class Ssh::Server
{
public:
struct Init_failed : Genode::Exception { };
struct Invalid_config : Genode::Exception { };
private:
using Session_registry = Genode::Registry<Session>;
Genode::Env &_env;
Genode::Heap _heap;
bool _verbose { false };
bool _allow_password { false };
bool _allow_publickey { false };
bool _log_logins { false };
int _max_auth_attempts { 3 };
unsigned _port { 0u };
unsigned _log_level { 0u };
int _server_fds[2] { -1, -1 };
bool _config_once { false };
ssh_bind _ssh_bind;
ssh_event _event_loop;
Util::Filename _rsa_key { };
Util::Filename _ecdsa_key { };
Util::Filename _ed25519_key { };
Expanding_reporter _request_terminal_reporter { _env,
"request_terminal",
"request_terminal" };
Terminal_registry _terminals { };
Login_registry &_logins;
pthread_t _event_thread;
/*
* Since we always pass ourself as userdata pointer, we may
* safely use the same callback for all sessions and channels.
*/
ssh_channel_callbacks_struct _channel_cb { };
ssh_server_callbacks_struct _session_cb { };
ssh_bind_callbacks_struct _bind_cb { };
Session_registry _sessions { };
uint32_t _session_id { 0 };
void _initialize_channel_callbacks();
void _initialize_session_callbacks();
void _initialize_bind_callbacks();
void _cleanup_session(Session &s);
void _cleanup_sessions();
void _parse_config(Genode::Xml_node const &config);
void _load_hostkey(Util::Filename const &file);
/*
* Event execution
*/
static void *_server_loop(void *arg);
bool _allow_multi_login(ssh_session s, Login const &login);
/********************
** Login messages **
********************/
void _log_failed(char const *user, Session const &s, bool pubkey);
void _log_logout(Session const &s);
void _log_login(User const &user, Session const &s, bool pubkey);
void _wake_loop();
public:
Server(Genode::Env &env,
Genode::Xml_node const &config,
Ssh::Login_registry &logins);
virtual ~Server();
void loop();
/***************************************************************
** Methods below are only used by Terminal session front end **
***************************************************************/
/**
* Attach Terminal session
*/
void attach_terminal(Ssh::Terminal &conn);
/**
* Detach Terminal session
*/
void detach_terminal(Ssh::Terminal &conn);
/**
* Update config
*/
void update_config(Genode::Xml_node const &config);
/*******************************************************
** Methods below are only used by callback back ends **
*******************************************************/
/**
* Look up Terminal for session
*/
Ssh::Terminal *lookup_terminal(Session &s);
/**
* Look up Session for SSH session
*/
Session *lookup_session(ssh_session s);
/**
* Request Terminal
*/
bool request_terminal(Session &session, const char* command = nullptr);
/**
* Handle new incoming connections
*/
void incoming_connection(ssh_session s);
/**
* Handle password authentication
*/
bool auth_password(ssh_session s, char const *u, char const *pass);
/**
* Handle public-key authentication
*/
bool auth_pubkey(ssh_session s, char const *u,
struct ssh_key_struct *pubkey,
char signature_state);
};
#endif /* _SSH_TERMINAL_SERVER_H_ */