diff --git a/repos/gems/recipes/src/ssh_terminal/content.mk b/repos/gems/recipes/src/ssh_terminal/content.mk deleted file mode 100644 index b52845e48d..0000000000 --- a/repos/gems/recipes/src/ssh_terminal/content.mk +++ /dev/null @@ -1,8 +0,0 @@ -SRC_DIR := src/server/ssh_terminal -include $(GENODE_DIR)/repos/base/recipes/src/content.inc - -content: $(MIRROR_FROM_LIBPORTS) - -$(MIRROR_FROM_LIBPORTS): - mkdir -p $(dir $@) - cp -r $(GENODE_DIR)/repos/libports/$@ $(dir $@) diff --git a/repos/gems/recipes/src/ssh_terminal/hash b/repos/gems/recipes/src/ssh_terminal/hash deleted file mode 100644 index ac60fc1176..0000000000 --- a/repos/gems/recipes/src/ssh_terminal/hash +++ /dev/null @@ -1 +0,0 @@ -2021-10-13 c49961ec747733def5b1d6c7f0a0fca302ecda8f diff --git a/repos/gems/recipes/src/ssh_terminal/used_apis b/repos/gems/recipes/src/ssh_terminal/used_apis deleted file mode 100644 index 5a72c98ae9..0000000000 --- a/repos/gems/recipes/src/ssh_terminal/used_apis +++ /dev/null @@ -1,10 +0,0 @@ -base -gems -libc -libssh -nic_session -report_session -os -terminal_session -timer_session -vfs diff --git a/repos/gems/run/ssh_exec_channel.run b/repos/gems/run/ssh_exec_channel.run deleted file mode 100644 index 1206d00e49..0000000000 --- a/repos/gems/run/ssh_exec_channel.run +++ /dev/null @@ -1,328 +0,0 @@ -# -# \brief Test for the SSH terminal -# - -assert_spec x86 - -if {[have_spec linux]} { - puts "Run script is not supported on this platform." - exit 0 -} - -# Build -# - - -source ${genode_dir}/repos/base/run/platform_drv.inc -append_platform_drv_build_components - -lappend build_components test/exec_terminal - -build $build_components - -create_boot_directory - -import_from_depot [depot_user]/src/[base_src] \ - [depot_user]/src/bash \ - [depot_user]/src/coreutils-minimal \ - [depot_user]/src/exec_terminal \ - [depot_user]/src/init \ - [depot_user]/src/ipxe_nic_drv \ - [depot_user]/src/libc \ - [depot_user]/src/openssl \ - [depot_user]/src/libssh \ - [depot_user]/src/platform_drv \ - [depot_user]/src/posix \ - [depot_user]/src/fs_rom \ - [depot_user]/src/nic_router \ - [depot_user]/src/rtc_drv \ - [depot_user]/src/ssh_terminal \ - [depot_user]/src/vfs \ - [depot_user]/src/vfs_jitterentropy \ - [depot_user]/src/vfs_lxip \ - [depot_user]/src/vfs_pipe \ - [depot_user]/src/vim-minimal \ - [depot_user]/src/zlib - -# -# Generate config -# - -set config { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2000-01-01 00:00 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -} - -append_platform_drv_config - -append config { -} - -install_config $config - -# -# Generate a new host key -# -if {![file exists bin/ed25519_key]} { - exec ssh-keygen -t ed25519 -f bin/ed25519_key -q -N "" -} - -# -# Boot modules -# - -# generic modules -set boot_modules { - ed25519_key - exec_terminal -} - -# platform-specific modules -append_platform_drv_boot_modules - -build_boot_image $boot_modules - -# -# Execute test -# - -append qemu_args " -nographic " -append_qemu_nic_args "hostfwd=tcp::5555-:22" - -set nic_router_match_string ".uplink. dynamic IP config. interface (\[0-9\]+\.\[0-9\]+\.\[0-9\]+\.\[0-9\]+).*\n" - -if {[get_cmd_switch --autopilot]} { - run_genode_until $nic_router_match_string 60 - set serial_id [output_spawn_id] - - if {[have_include "power_on/qemu"]} { - set host "localhost" - set port "5555" - } else { - regexp $nic_router_match_string $output all host - set port "22" - } - # wait for ssh_terminal to come up - run_genode_until "--- SSH terminal started ---.*\n" 15 $serial_id - - for {set index 0} {$index < 3} {incr index} { - puts "test interactive channel" - spawn sshpass -p xuon ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l noux $host -p $port - set ssh_id $spawn_id - run_genode_until {/bin/bash] Hello from Genode!.*\n} 15 $serial_id - send -i $ssh_id "ls\r" - run_genode_until "bin" 15 $ssh_id - send -i $ssh_id "exit\r" - run_genode_until "child \"init\" exited with exit value 0.*\n" 15 $serial_id - - puts "test exec channel echo" - set echo_text "The quick brown fox jumps over the lazy dog" - spawn sshpass -p xuon ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l noux $host -p $port echo "$echo_text" - set ssh_id $spawn_id - run_genode_until ".*$echo_text.*\n" 15 $ssh_id - run_genode_until "child \"init\" exited with exit value 0.*\n" 15 $serial_id - - puts "test exec channel ls" - spawn sshpass -p xuon ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l noux $host -p $port "ls" - set ssh_id $spawn_id - run_genode_until "bin" 15 $ssh_id - run_genode_until "child \"init\" exited with exit value 0.*\n" 15 $serial_id - - puts "test exec channel with empty command will not hang" - spawn sshpass -p xuon ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l noux $host -p $port " " - set ssh_id $spawn_id - run_genode_until "child \"init\" exited with exit value.*\n" 15 $serial_id - } - - puts "" - puts "" -} else { - run_genode_until forever -} - -exec rm bin/ed25519_key bin/ed25519_key.pub - -# vi: set ft=tcl : diff --git a/repos/gems/run/ssh_terminal.run b/repos/gems/run/ssh_terminal.run deleted file mode 100644 index b570dac8de..0000000000 --- a/repos/gems/run/ssh_terminal.run +++ /dev/null @@ -1,273 +0,0 @@ -# -# \brief Test for the SSH terminal -# - -assert_spec x86 - -if {[have_spec linux]} { - puts "Run script is not supported on this platform." - exit 0 -} - -# Build -# - -set build_components { - core init timer - drivers/nic - drivers/rtc - server/ssh_terminal - server/fs_rom - server/vfs - lib/vfs/jitterentropy - lib/vfs/lxip - lib/vfs/pipe - test/libports/ncurses - test/terminal_echo - noux-pkg/bash - server/nic_router -} - -source ${genode_dir}/repos/base/run/platform_drv.inc -append_platform_drv_build_components - -build $build_components - -create_boot_directory - -# -# Generate config -# - -set config { - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - echo Welcome to Genode! > /dev/log - - - - - - - - - - - - - - -} - -append_platform_drv_config - -append config { -} - -install_config $config - -# -# Generate a new host key -# -if {![file exists bin/ed25519_key]} { - exec ssh-keygen -t ed25519 -f bin/ed25519_key -q -N "" -} - -# -# Boot modules -# - -# generic modules -set boot_modules { - core ld.lib.so init timer ipxe_nic_drv rtc_drv report_rom vfs fs_rom - test-terminal_echo nic_router - - libc.lib.so libm.lib.so vfs.lib.so - vfs_lxip.lib.so lxip.lib.so - posix.lib.so libcrypto.lib.so libssh.lib.so zlib.lib.so ncurses.lib.so - vfs_jitterentropy.lib.so vfs_pipe.lib.so ssh_terminal - - bash.tar ed25519_key -} - -# platform-specific modules -append_platform_drv_boot_modules - -build_boot_image $boot_modules - -# -# Execute test -# - -append qemu_args " -nographic " -append_qemu_nic_args "hostfwd=tcp::5555-:22" - -set lxip_match_string "ipaddr=(\[0-9\]+\.\[0-9\]+\.\[0-9\]+\.\[0-9\]+).*\n" - -if {[get_cmd_switch --autopilot]} { - run_genode_until $lxip_match_string 60 - set serial_id [output_spawn_id] - - if {[have_include "power_on/qemu"]} { - set host "localhost" - set port "5555" - } else { - regexp $lxip_match_string $output all host - puts "" - set port "22" - } - # wait for ssh_terminal to come up - sleep 5 - spawn sshpass -p xuon ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l genode $host -p $port - set ssh_id $spawn_id - set spawn_id_list [list $ssh_id $serial_id] - run_genode_until {.*\[init -> /bin/bash.*\] Welcome to Genode!.*\n} 15 $spawn_id_list - puts "" - puts "" -} else { - run_genode_until forever -} - -exec rm bin/ed25519_key bin/ed25519_key.pub - -# vi: set ft=tcl : diff --git a/repos/gems/src/server/ssh_terminal/README b/repos/gems/src/server/ssh_terminal/README deleted file mode 100644 index 7797e36725..0000000000 --- a/repos/gems/src/server/ssh_terminal/README +++ /dev/null @@ -1,121 +0,0 @@ -This directory contains an minimal SSH Terminal server. It provides -concurrent access to multiple Terminal sessions via interactive SSH sessions. -Before using the component, please consult the _Notes_ section to learn -about the current subtleties. - -For an example on how to use the server please look at the run script -provided by _repos/gems/run/ssh_terminal_ or _repos/gems/run/ssh_exec_channel_. - - -Configuration -~~~~~~~~~~~~~ - -Examplary configuration: - -! -! -! -! -! -! -! -! 012345678 -! -! -! -! -! -! - -The above config snippet shows a configuration where two Noux clients may -connect to the Terminal server and SSH connections with either a password or a -public are processed on port 2022. All SSH logins for the user 'root' are -linked to the 'noux-system' session, while all 'user' logins are forwarded -to the 'noux-user' session. - -The '' node has several mandatory attributes: - -* 'port' specfies the TCP port the SSH server attempts to listen on. - -* 'ecdsa_key', 'ed25519_key' and 'rsa_key' set the respective - path to the host key. At least one of them must be specified. - -* 'allow_password' enables password logins while 'allow_publickey' - enables public-key logins. At least one of them must be specified. - Those Authentication methods are set for all logins even if they - are not useable by one or the other. - -Aside from the mandatory attributes there are optional ones: - -* 'log_logins' enables logging of login attempts. These attempts will - be printed to the LOG session. The default is 'yes'. - -The relation between a Terminal session and a SSH session is established -by a '' node. It first specifies which Terminal client may access -the server. Second and more importantly it specifies which SSH session has -access to which Terminal session. The non policy-label specific attributes -of the node form a login that is used to authenticate access via the SSH -session: - -* 'user' sets the name of a login and is used throughout the component - to establish the relation between Terminal and SSH session and is therefore - mandatory. - -* 'password' sets the password of the login, which is used to authenticate - a login attempt. - -* 'pub_key' sets the path to the public-key file, which is used to authenticate - a login attempt. - -* 'multi_login' allows one Terminal session to be used by mutliple SSH - sessions concurrently, the default is false. - -Either one of the 'password' or 'pub_key' attributes must be set in order for -the login to be valid. - -Normally, all Terminal sessions are expected to be established before an SSH -connection is made. To accommodate the use-case where the Terminal client is -made available in a dynamic fashion, the 'request_terminal' attribute can by -set to 'yes'. In this case the SSH Terminal server will generate a report, -which then can be inspected and reacted upon by a management component, -whenever a SSH connection for a detached Terminal session is made. Data coming -from the SSH session will be ignored as long as the Terminal client is not -attached to the server. This mechanism is useful in cases where the attached -Terminal might terminate itself, e.g. a shell when receiving EOF, but a -subsequent login attempt expects the Terminal client to be still attached. - -The following snippet shows such a report: - -! - -The component will generate the report only once when the first login -via SSH is made and the corresponding Terminal session is not available. -All subsequent logins have to wait until the session is provided. Once the -Terminal session has been detached an 'exit' report is generated. Detaching a -terminal client will lead to a disconnect for the corresponding SSH session If -there are active SSH session after the Terminal in question has been detached, a -new report will be generated. - - -Notes -~~~~~ - -* A helper component is needed, in case an exec channel is required or when - Terminal sessions need to be started *after* the SSH connection is - established. An example can be found here: _repos/gems/src/test/exec_terminal/ - -* The SSH connection MUST be forcefully cut at the client in case the Terminal - session is established *before* the ssh channel is open. This can be done - when using OpenSSH by entering '~.'. - -* Host keys can be generated by using ssh-keygen(1) on your host system - (e.g., 'ssh-keygen -t ed25519_key -f ed25519_key' without a passphrase and - then use the 'ed25519_key' file). - -* Reports from concurrent logins will override each other and potentially lead - to lost reports. - -* Although concurrent access to one Terminal session via multiple SSH sessions - at the same time is possible, it should better be avoided as there are no - safety measures in place. diff --git a/repos/gems/src/server/ssh_terminal/login.h b/repos/gems/src/server/ssh_terminal/login.h deleted file mode 100644 index b2df1bebd6..0000000000 --- a/repos/gems/src/server/ssh_terminal/login.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * \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_LOGIN_H_ -#define _SSH_TERMINAL_LOGIN_H_ - -/* Genode includes */ -#include -#include -#include - -/* libssh includes */ -#include - -/* local includes */ -#include "util.h" - - -namespace Ssh { - - using namespace Genode; - using namespace Util; - - using User = String<32>; - using Password = String<64>; - using Hash = String<65>; - - struct Login; - struct Login_registry; -} - - -struct Ssh::Login : Genode::Registry::Element -{ - Ssh::User user { }; - Ssh::Password password { }; - Ssh::Hash pub_key_hash { }; - ssh_key pub_key { nullptr }; - bool multi_login { false }; - bool request_terminal { false }; - - /** - * Constructor - */ - Login(Genode::Registry ®, - Ssh::User const &user, - Ssh::Password const &pw, - Filename const &pk_file, - bool const multi_login, - bool const request_terminal) - : - Element(reg, *this), - user(user), password(pw), multi_login(multi_login), - request_terminal(request_terminal) - { - Libc::with_libc([&] { - - if (pk_file.valid() && - ssh_pki_import_pubkey_file(pk_file.string(), &pub_key)) { - Genode::error("could not import public key for user '", - user, "'"); - } - - if (pub_key) { - unsigned char *h = nullptr; - size_t hlen = 0; - /* pray and assume both calls never fail */ - ssh_get_publickey_hash(pub_key, SSH_PUBLICKEY_HASH_SHA256, - &h, &hlen); - char const *p = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256, - h, hlen); - if (p) { - pub_key_hash = Ssh::Hash(p); - } - - ssh_clean_pubkey_hash(&h); - - /* abuse function to free fingerprint */ - ssh_clean_pubkey_hash((unsigned char**)&p); - } - }); /* Libc::with_libc */ - } - - virtual ~Login() { ssh_key_free(pub_key); } - - bool auth_password() const { return password.valid(); } - bool auth_publickey() const { return pub_key != nullptr; } - - void print(Genode::Output &out) const - { - Genode::print(out, "user ", user, ": "); - if (auth_password()) { Genode::print(out, "password "); } - if (auth_publickey()) { Genode::print(out, "public-key"); } - } -}; - - -struct Ssh::Login_registry : Genode::Registry -{ - Genode::Allocator &_alloc; - Util::Pthread_mutex _mutex { }; - - /** - * Import one login from node - */ - bool _import_single(Genode::Xml_node const &node) - { - User user = node.attribute_value("user", User()); - Password pw = node.attribute_value("password", Password()); - Filename pub = node.attribute_value("pub_key", Filename()); - bool multi_login = node.attribute_value("multi_login", false); - bool req_term = node.attribute_value("request_terminal", false); - - if (!user.valid() || (!pw.valid() && !pub.valid())) { - Genode::warning("ignoring invalid policy"); - return false; - } - - if (lookup(user.string())) { - Genode::warning("ignoring already imported login ", user.string()); - return false; - } - - try { - new (&_alloc) Login(*this, user, pw, pub, multi_login, req_term); - return true; - } catch (...) { return false; } - } - - void _remove_all() - { - for_each([&] (Login &login) { - Genode::destroy(&_alloc, &login); - }); - } - - /** - * Constructor - * - * \param alloc allocator for registry elements - */ - Login_registry(Genode::Allocator &alloc) : _alloc(alloc) { } - - /** - * Return registry mutex - */ - Util::Pthread_mutex &mutex() { return _mutex; } - - /** - * Import all login information from config - */ - void import(Genode::Xml_node const &node) - { - _remove_all(); - - try { - node.for_each_sub_node("policy", - [&] (Genode::Xml_node const &node) { - _import_single(node); - }); - } catch (...) { } - } - - /** - * Look up login information by user name - */ - Ssh::Login const *lookup(char const *user) const - { - Login const *p = nullptr; - auto lookup_user = [&] (Login const &login) { - if (login.user == user) { p = &login; } - }; - for_each(lookup_user); - return p; - } -}; - -#endif /* _SSH_TERMINAL_LOGIN_H_ */ diff --git a/repos/gems/src/server/ssh_terminal/main.cc b/repos/gems/src/server/ssh_terminal/main.cc deleted file mode 100644 index cc73dbbadf..0000000000 --- a/repos/gems/src/server/ssh_terminal/main.cc +++ /dev/null @@ -1,52 +0,0 @@ -/* - * \brief Component providing a Terminal session via SSH - * \author Josef Soentgen - * \date 2018-09-25 - * - * On the local side this component provides Terminal sessions to its - * configured clients while it also provides SSH sessions on the remote side. - * The relation between both sides is establish via the policy settings that - * determine which Terminal session may be accessed by a SSH login and the - * other way around. - * - * When the component gets started it will create a read-only login database. - * A login consists of a username and either a password or public-key or both. - * The username is the unique primary key and is used to identify the right - * Terminal session when a login is attempted. In return, it is also used to - * attach a Terminal session to an (existing) SSH session. The SSH protocol - * processing is done via libssh running in its own event thread while the - * EP handles the Terminal session. Locking is done at the appropriate places - * to synchronize both threads. - */ - -/* - * Copyright (C) 2018 Genode Labs GmbH - * - * This file is part of the Genode OS framework, which is distributed - * under the terms of the GNU Affero General Public License version 3. - */ - -/* Genode includes */ -#include -#include - -/* local includes */ -#include "root_component.h" - - -struct Main -{ - Genode::Env &_env; - - Genode::Sliced_heap _sliced_heap { _env.ram(), _env.rm() }; - Terminal::Root_component _root { _env, _sliced_heap }; - - Main(Genode::Env &env) : _env(env) - { - Genode::log("--- SSH terminal started ---"); - _env.parent().announce(env.ep().manage(_root)); - } -}; - - -void Libc::Component::construct(Libc::Env &env) { static Main main(env); } diff --git a/repos/gems/src/server/ssh_terminal/root_component.h b/repos/gems/src/server/ssh_terminal/root_component.h deleted file mode 100644 index 2c19bd90da..0000000000 --- a/repos/gems/src/server/ssh_terminal/root_component.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * \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_ROOT_COMPONENT_H_ -#define _SSH_TERMINAL_ROOT_COMPONENT_H_ - -/* Genode includes */ -#include -#include - -/* local includes */ -#include "session_component.h" -#include "server.h" - - -namespace Terminal -{ - using namespace Genode; - - class Root_component; -} - - -class Terminal::Root_component : public Genode::Root_component -{ - private: - - Genode::Env &_env; - - Genode::Attached_rom_dataspace _config_rom { _env, "config" }; - Genode::Xml_node _config { _config_rom.xml() }; - - Genode::Heap _logins_alloc { _env.ram(), _env.rm() }; - Ssh::Login_registry _logins { _logins_alloc }; - - Ssh::Server _server { _env, _config, _logins }; - - Genode::Signal_handler _config_sigh { - _env.ep(), *this, &Terminal::Root_component::_handle_config_update }; - - void _handle_config_update() - { - _config_rom.update(); - if (!_config_rom.valid()) { return; } - - Libc::with_libc([&] () { - { - Util::Pthread_mutex::Guard guard(_logins.mutex()); - _logins.import(_config_rom.xml()); - } - - _server.update_config(_config_rom.xml()); - }); - } - - protected: - - Session_component *_create_session(const char *args) - { - try { - Session_label const label = label_from_args(args); - Session_policy policy(label, _config); - - Ssh::User const user = policy.attribute_value("user", Ssh::User()); - if (!user.valid()) { throw -1; } - - Ssh::Login const *login = _logins.lookup(user.string()); - if (!login) { throw -1; } - - Session_component *s = nullptr; - s = new (md_alloc()) Session_component(_env, 4096, login->user); - - try { - Libc::with_libc([&] () { _server.attach_terminal(*s); }); - return s; - } catch (...) { - Genode::destroy(md_alloc(), s); - throw; - } - } catch (...) { throw Service_denied(); } - } - - void _destroy_session(Session_component *s) - { - Libc::with_libc([&] () { _server.detach_terminal(*s); }); - Genode::destroy(md_alloc(), s); - } - - public: - - Root_component(Genode::Env &env, - Genode::Allocator &md_alloc) - : - Genode::Root_component(&env.ep().rpc_ep(), - &md_alloc), - _env(env) - { - _config_rom.sigh(_config_sigh); - _handle_config_update(); - } -}; - -#endif /* _SSH_TERMINAL_ROOT_COMPONENT_H_ */ diff --git a/repos/gems/src/server/ssh_terminal/server.cc b/repos/gems/src/server/ssh_terminal/server.cc deleted file mode 100644 index 33d6e98fb7..0000000000 --- a/repos/gems/src/server/ssh_terminal/server.cc +++ /dev/null @@ -1,707 +0,0 @@ -/* - * \brief Component providing a Terminal session via SSH - * \author Josef Soentgen - * \author Pirmin Duss - * \author Sid Hussmann - * \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. - */ - - -/* local includes */ -#include "server.h" - -/* - * Add the libssh callback forward declarations here so that we can use them - * from within the classes and thereby document the ones currently implemented. - */ - -extern int channel_data_cb(ssh_session, ssh_channel, void *, uint32_t, int, void *); -extern int channel_env_request_cb(ssh_session, ssh_channel, char const *, char const *, void *); -extern int channel_pty_request_cb(ssh_session, ssh_channel, char const *, int, int, int, int, void *); -extern int channel_pty_window_change_cb(ssh_session, ssh_channel, int, int, int, int, void *); -extern int channel_shell_request_cb(ssh_session, ssh_channel, void *); -extern int channel_exec_request_cb(ssh_session, ssh_channel, char const *, void *); - -extern void bind_incoming_connection(ssh_bind, void *); -extern int session_service_request_cb(ssh_session, char const *, void *); -extern int session_auth_password_cb(ssh_session, char const *, char const *, void *); -extern int session_auth_pubkey_cb(ssh_session, char const *, struct ssh_key_struct *, char, void *); -extern ssh_channel session_channel_open_request_cb(ssh_session, void *); - -/** - * forward declaration of the write available callback. - */ -static int write_avail_cb(socket_t fd, int revents, void *userdata); - - -Ssh::Terminal_session::Terminal_session(Genode::Registry ®, - Ssh::Terminal &conn, - ssh_event event_loop) -: - Element(reg, *this), conn(conn), _event_loop(event_loop) -{ - if (pipe(_fds)) { - Genode::error("Failed to create wakeup pipe"); - throw -1; - } - conn.write_avail_fd = _fds[1]; - - _state = PIPE_INITIALIZED; -} - -void Ssh::Terminal_session::initialize_ssh_event_fds() -{ - if (_state != PIPE_INITIALIZED || - ssh_event_add_fd(_event_loop, - _fds[0], - POLLIN, - write_avail_cb, - this) != SSH_OK) { - Genode::error("Failed to initialize ssh event file descriptors"); - throw -1; - } - - _state = SSH_INITIALIZED; -} - - -Ssh::Server::Server(Genode::Env &env, - Genode::Xml_node const &config, - Ssh::Login_registry &logins) -: - _env(env), _heap(env.ram(), env.rm()), _logins(logins) -{ - Libc::with_libc([&] { - - _parse_config(config); - - if (ssh_init() < 0) { - Genode::error("ssh_init failed."); - throw Init_failed(); - } - - _ssh_bind = ssh_bind_new(); - if (!_ssh_bind) { - Genode::error("ssh_bind failed."); - throw Init_failed(); - } - - ssh_bind_options_set(_ssh_bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &_log_level); - ssh_bind_options_set(_ssh_bind, SSH_BIND_OPTIONS_BINDPORT, &_port); - - _initialize_bind_callbacks(); - _initialize_session_callbacks(); - _initialize_channel_callbacks(); - - /* - * Always try to load all types of host key and error-out if - * the file is set but could not be loaded. - */ - try { - _load_hostkey(_rsa_key); - _load_hostkey(_ecdsa_key); - _load_hostkey(_ed25519_key); - } catch (...) { - Genode::error("loading keys failed."); - throw Init_failed(); - } - - _event_loop = ssh_event_new(); - - if (ssh_bind_listen(_ssh_bind) < 0) { - Genode::error("could not listen on port ", _port, ": ", - ssh_get_error(_ssh_bind)); - throw Init_failed(); - } - - /* add AFTER(!) ssh_bind_listen call */ - if (ssh_event_add_bind(_event_loop, _ssh_bind) < 0) { - Genode::error("unable to add server to event loop: ", - ssh_get_error(_ssh_bind)); - throw Init_failed(); - } - - /* add pipe to wake up loop on late connecting terminal */ - if (pipe(_server_fds) || - ssh_event_add_fd(_event_loop, - _server_fds[0], - POLLIN, - write_avail_cb, - this) != SSH_OK ) { - Genode::error("Failed to create wakeup pipe"); - throw -1; - } - - if (pthread_create(&_event_thread, nullptr, _server_loop, this)) { - Genode::error("could not create event thread"); - throw Init_failed(); - } - - Genode::log("Listen on port: ", _port); - }); /* Libc::with_libc */ -} - - -Ssh::Server::~Server() -{ - close(_server_fds[0]); - close(_server_fds[1]); -} - - -void Ssh::Server::_initialize_channel_callbacks() -{ - Genode::memset(&_channel_cb, 0, sizeof(_channel_cb)); - - _channel_cb.userdata = this; - _channel_cb.channel_data_function = channel_data_cb; - _channel_cb.channel_env_request_function = channel_env_request_cb; - _channel_cb.channel_pty_request_function = channel_pty_request_cb; - _channel_cb.channel_pty_window_change_function = channel_pty_window_change_cb; - _channel_cb.channel_shell_request_function = channel_shell_request_cb; - _channel_cb.channel_exec_request_function = channel_exec_request_cb; - - ssh_callbacks_init(&_channel_cb); -} - - -void Ssh::Server::_initialize_session_callbacks() -{ - Genode::memset(&_session_cb, 0, sizeof(_session_cb)); - - _session_cb.userdata = this; - _session_cb.auth_password_function = session_auth_password_cb; - _session_cb.auth_pubkey_function = session_auth_pubkey_cb; - _session_cb.service_request_function = session_service_request_cb; - _session_cb.channel_open_request_session_function = session_channel_open_request_cb; - - ssh_callbacks_init(&_session_cb); -} - - -void Ssh::Server::_initialize_bind_callbacks() -{ - Genode::memset(&_bind_cb, 0, sizeof(_bind_cb)); - _bind_cb.incoming_connection = bind_incoming_connection; - ssh_callbacks_init(&_bind_cb); - ssh_bind_set_callbacks(_ssh_bind, &_bind_cb, this); -} - - -void Ssh::Server::_cleanup_session(Session &s) -{ - if (s.auth_sucessful) { - _log_logout(s); - } - - ssh_channel_free(s.channel); - s.channel = nullptr; - - ssh_blocking_flush(s.session, 5*1000); - ssh_event_remove_session(_event_loop, s.session); - ssh_disconnect(s.session); - ssh_free(s.session); - s.session = nullptr; - - if (s.terminal) { - s.terminal->detach_channel(); - } - - try { - _request_terminal_reporter.generate([&] (Xml_generator& xml) { - xml.attribute("user", s.user()); - xml.attribute("exit", "now"); - }); - } catch (...) { - Genode::warning("could not enable exit reporting"); - } - - Genode::destroy(&_heap, &s); -} - - -void Ssh::Server::_cleanup_sessions() -{ - auto cleanup = [&] (Session &s) { - if (!ssh_is_connected(s.session)) { - _cleanup_session(s); - } - }; - _sessions.for_each(cleanup); -} - - -void Ssh::Server::_parse_config(Genode::Xml_node const &config) -{ - using Util::Filename; - - _verbose = config.attribute_value("verbose", false); - _log_level = config.attribute_value("debug", 0u); - _log_logins = config.attribute_value("log_logins", true); - - { - Util::Pthread_mutex::Guard guard(_logins.mutex()); - auto print = [&] (Login const &login) { - Genode::log("Login configured: ", login); - }; - _logins.for_each(print); - } - - if (_config_once) { return; } - - _config_once = true; - - _port = config.attribute_value("port", 0u); - if (!_port) { - error("port invalid"); - throw Invalid_config(); - } - - _allow_password = config.attribute_value("allow_password", false); - _allow_publickey = config.attribute_value("allow_publickey", false); - if (!_allow_password && !_allow_publickey) { - error("authentication methods missing"); - throw Invalid_config(); - } - - _rsa_key = config.attribute_value("rsa_key", Filename()); - _ecdsa_key = config.attribute_value("ecdsa_key", Filename()); - _ed25519_key = config.attribute_value("ed25519_key", Filename()); - - Genode::log("Allowed auth methods: ", - _allow_password ? "password " : "", - _allow_publickey ? "public-key" : ""); -} - - -void Ssh::Server::_load_hostkey(Util::Filename const &file) -{ - if (file.valid() && - ssh_bind_options_set(_ssh_bind, SSH_BIND_OPTIONS_HOSTKEY, - file.string()) < 0) { - Genode::error("could not load hostkey '", file, "'"); - throw -1; - } -} - - -void *Ssh::Server::_server_loop(void *arg) -{ - Ssh::Server *server = reinterpret_cast(arg); - server->loop(); - return nullptr; -} - - -bool Ssh::Server::_allow_multi_login(ssh_session s, Login const &login) -{ - if (login.multi_login) { return true; } - - bool found = false; - auto lookup = [&] (Session const &s) { - if (s.user() == login.user) { found = true; } - }; - _sessions.for_each(lookup); - return !found; -} - - -void Ssh::Server::_log_failed(char const *user, Session const &s, bool pubkey) -{ - if (!_log_logins) { return; } - - char const *date = Util::get_time(); - Genode::log(date, " failed user ", user, " (", s.id(), ") ", - "with ", pubkey ? "public-key" : "password"); -} - - -void Ssh::Server::_log_logout(Session const &s) -{ - if (!_log_logins) { return; } - - char const *date = Util::get_time(); - Genode::log(date, " logout user ", s.user(), " (", s.id(), ")"); -} - - -void Ssh::Server::_log_login(User const &user, Session const &s, bool pubkey) -{ - if (!_log_logins) { return; } - - char const *date = Util::get_time(); - Genode::log(date, " login user ", user, " (", s.id(), ") ", - "with ", pubkey ? "public-key" : "password"); -} - - -void Ssh::Server::attach_terminal(Ssh::Terminal &conn) -{ - Util::Pthread_mutex::Guard guard(_terminals.mutex()); - - try { - new (&_heap) Terminal_session(_terminals, - conn, _event_loop); - } catch (...) { - Genode::error("could not attach Terminal for user ", - conn.user()); - throw -1; - } - - /* there might be sessions already waiting on the terminal */ - bool attached = false; - auto lookup = [&] (Session &s) { - if (s.user() == conn.user() && !s.terminal) { - s.terminal = &conn; - s.terminal->attach_channel(); - attached = true; - } - }; - _sessions.for_each(lookup); - - _wake_loop(); -} - - -void Ssh::Server::detach_terminal(Ssh::Terminal &conn) -{ - Util::Pthread_mutex::Guard guard(_terminals.mutex()); - - Terminal_session *p = nullptr; - auto lookup = [&] (Terminal_session &t) { - - if (&t.conn == &conn) { - p = &t; - } - }; - _terminals.for_each(lookup); - - if (!p) { - Genode::error("could not detach Terminal for user ", conn.user()); - return; - } - - auto invalidate_terminal = [&] (Session &sess) { - if (sess.terminal != &conn) { return; } - sess.terminal_detached = true; - - /* flush before destroying the terminal */ - try { sess.terminal->send(sess.channel); } - catch (...) { } - }; - _sessions.for_each(invalidate_terminal); - - _wake_loop(); -} - - -void Ssh::Server::update_config(Genode::Xml_node const &config) -{ - Util::Pthread_mutex::Guard guard(_terminals.mutex()); - - _parse_config(config); - ssh_bind_options_set(_ssh_bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &_log_level); -} - - -Ssh::Terminal *Ssh::Server::lookup_terminal(Session &s) -{ - Ssh::Terminal *p = nullptr; - auto lookup = [&] (Terminal_session &t) { - if (t.conn.user() == s.user()) { p = &t.conn; } - }; - _terminals.for_each(lookup); - return p; -} - - -Ssh::Session *Ssh::Server::lookup_session(ssh_session s) -{ - Session *p = nullptr; - auto lookup = [&] (Session &sess) { - if (sess.session == s) { p = &sess; } - }; - _sessions.for_each(lookup); - return p; -} - - -bool Ssh::Server::request_terminal(Session &session, - const char* command) -{ - Util::Pthread_mutex::Guard guard(_logins.mutex()); - Login const *l = _logins.lookup(session.user().string()); - if (!l || !l->request_terminal) { - return false; - } - - try { - _request_terminal_reporter.generate([&] (Xml_generator& xml) { - xml.attribute("user", session.user()); - if (command) { - xml.attribute("command", command); - } - }); - } catch (...) { - Genode::warning("could not enable login reporting"); - return false; - } - - if (_log_logins) { - char const *date = Util::get_time(); - Genode::log(date, " request Terminal for user ", session.user(), - " (", session.session, ")"); - } - - return true; -} - - -void Ssh::Server::incoming_connection(ssh_session s) -{ - /* - * In case we get bombarded by incoming connections, deny - * all attempts when this arbritray level is reached. - */ - enum { MEM_RESERVE = 128u * 1024, }; - if (_env.pd().avail_ram().value < (size_t)MEM_RESERVE) { - error("Too many connections"); - throw -1; - } - - /* - * Queue up new ssh_session to be enabled later in pthread ssh loop. - * We can't directly add the new Session object to the _session registry, - * because this ssh callback may be invoked from within a - * _session.for_each(...) invocation. The internal _session Genode::Mutex - * is taken during _session.for_each(...) and during a 'new' here, - * which would lead to a deadlock. - */ - new (&_heap) Session(_new_sessions, s, &_channel_cb, ++_session_id); -} - - -bool Ssh::Server::auth_password(ssh_session s, char const *u, char const *pass) -{ - Session *p = lookup_session(s); - if (!p || p->session != s) { - Genode::warning("session not found"); - return false; - } - Session &session = *p; - - /* - * Even if there is no valid login for the user, let - * the client try anyway and check multi login afterwards. - */ - Util::Pthread_mutex::Guard guard(_logins.mutex()); - Login const *l = _logins.lookup(u); - if (l && l->user == u && l->password == pass) { - if (_allow_multi_login(s, *l)) { - session.bad_auth_attempts = 0; - session.auth_sucessful = true; - session.adopt(l->user); - _log_login(l->user, session, false); - return true; - } else { - ssh_disconnect(session.session); - _log_failed(u, session, false); - return false; - } - } - - _log_failed(u, *p, false); - - int &i = session.bad_auth_attempts; - if (++i >= _max_auth_attempts) { - if (_log_logins) { - char const *date = Util::get_time(); - Genode::log(date, " disconnect user ", u, " (", session.id(), - ") after ", i, " failed authentication attempts" - " with password"); - } - ssh_disconnect(session.session); - } - return false; -} - - -bool Ssh::Server::auth_pubkey(ssh_session s, char const *u, - struct ssh_key_struct *pubkey, - char signature_state) -{ - Session *p = lookup_session(s); - if (!p || p->session != s) { - Genode::warning("session not found"); - return false; - } - Session &session = *p; - - /* - * In this first state the given pubkey is solely probed. - * Ideally we would check here if the given pubkey is in fact to the - * configured one, i.e., reading a 'authorized_keys' like file and - * check its entries. - * - * For now we simple accept all keys and reject them in the later - * state. - */ - if (signature_state == SSH_PUBLICKEY_STATE_NONE) { - return true; - } - - /* - * In this second state we check the provided pubkey and if it - * matches allow authentication to proceed. - */ - if (signature_state == SSH_PUBLICKEY_STATE_VALID) { - Util::Pthread_mutex::Guard guard(_logins.mutex()); - Login const *l = _logins.lookup(u); - if (l && !ssh_key_cmp(pubkey, l->pub_key, - SSH_KEY_CMP_PUBLIC)) { - if (_allow_multi_login(s, *l)) { - session.auth_sucessful = true; - session.adopt(l->user); - _log_login(l->user, session, true); - return true; - } - } - } - - _log_failed(u, session, true); - return false; -} - - -void Ssh::Server::loop() -{ - while (true) { - - int const events = ssh_event_dopoll(_event_loop, -1); - if (events == SSH_ERROR) { - _cleanup_sessions(); - } - - { - Util::Pthread_mutex::Guard guard(_terminals.mutex()); - - /* finish pending initialization of terminal sessions */ - auto initialize = [&] (Terminal_session &t) { - try { - if (t._state == Terminal_session::PIPE_INITIALIZED) { - t.initialize_ssh_event_fds(); - } - } catch (...) { - /* Not sure what to do here - terminal is "almost" attached. - Previously service was denied in that case but as - descriptor handling must be performed in ssh loop thread - it is too late for that. */ - } - }; - _terminals.for_each(initialize); - - /* remove all stale sessions */ - auto cleanup = [&] (Session &s) { - if (s.terminal_detached) { - Terminal_session *p = nullptr; - auto lookup = [&] (Terminal_session &t) { - if (&t.conn == s.terminal) { - p = &t; - s.terminal = nullptr; - } - }; - _terminals.for_each(lookup); - - if (p) - Genode::destroy(&_heap, p); - } - - if (!s.terminal_detached - && ssh_is_connected(s.session)) { return ; } - _cleanup_session(s); - }; - _sessions.for_each(cleanup); - - /* second reset all active terminals */ - auto reset_pending = [&] (Terminal_session &t) { - if (!t.conn.attached_channels()) { return; } - t.conn.reset_pending(); - }; - _terminals.for_each(reset_pending); - - /* - * third send data on all sessions being attached - * to a terminal. - */ - auto send = [&] (Session &s) { - if (!s.terminal) { return; } - - try { s.terminal->send(s.channel); } - catch (...) { _cleanup_session(s); } - }; - _sessions.for_each(send); - } - - /* enable all new sessions that got added by ssh callbacks */ - auto activate = [&] (Session &inactive_session) { - /* re-queue session object */ - new (&_heap) Session(_sessions, - inactive_session.session, - inactive_session.channel_cb, - inactive_session.id()); - - ssh_session s = inactive_session.session; - - /* remove temporary object */ - Genode::destroy(&_heap, &inactive_session); - - /* activate session */ - ssh_set_server_callbacks(s, &_session_cb); - - int auth_methods = SSH_AUTH_METHOD_UNKNOWN; - auth_methods += _allow_password ? SSH_AUTH_METHOD_PASSWORD : 0; - auth_methods += _allow_publickey ? SSH_AUTH_METHOD_PUBLICKEY : 0; - ssh_set_auth_methods(s, auth_methods); - - /* - * Normally we would check the result of the key exchange - * function but for better or worse using callbacks leads to - * a false negative. So ignore any result and move on in hope - * that the callsbacks will handle the situation. - * - * FIXME investigate why it somtimes fails in the first place. - */ - int key_exchange_result = ssh_handle_key_exchange(s); - - if (SSH_OK != key_exchange_result) { - Genode::warning("key exchange returned ", key_exchange_result); - } - - ssh_event_add_session(_event_loop, s); - }; - _new_sessions.for_each(activate); - } -} - - -void Ssh::Server::_wake_loop() -{ - /* wake the event loop up */ - char c = 1; - ::write(_server_fds[1], &c, sizeof(c)); -} - - -static int write_avail_cb(socket_t fd, int revents, void *userdata) -{ - char c; - return ::read(fd, &c, sizeof(char)); -} diff --git a/repos/gems/src/server/ssh_terminal/server.h b/repos/gems/src/server/ssh_terminal/server.h deleted file mode 100644 index 55d6b89bbf..0000000000 --- a/repos/gems/src/server/ssh_terminal/server.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * \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 -#include -#include - -/* libc includes */ -#include -#include - -/* libssh includes */ -#include -#include -#include - -/* local includes */ -#include "login.h" -#include "terminal.h" - - -namespace Ssh { - - using namespace Genode; - - struct Server; - struct Session; - struct Terminal_session; - struct Terminal_registry; -} - - -struct Ssh::Session : Genode::Registry::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 }; - bool terminal_detached { false }; - - Session(Genode::Registry ®, - 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::Element -{ - Ssh::Terminal &conn; - - ssh_event _event_loop; - - int _fds[2] { -1, -1 }; - - enum State { UNINITIALIZED, - PIPE_INITIALIZED, - SSH_INITIALIZED } _state = UNINITIALIZED; - - Terminal_session(Genode::Registry ®, - Ssh::Terminal &conn, - ssh_event event_loop); - - ~Terminal_session() - { - switch (_state) { - case SSH_INITIALIZED: - ssh_event_remove_fd(_event_loop, _fds[0]); - [[fallthrough]]; - case PIPE_INITIALIZED: - close(_fds[0]); - close(_fds[1]); - [[fallthrough]]; - case UNINITIALIZED: - break; - } - } - - void initialize_ssh_event_fds(); -}; - - -struct Ssh::Terminal_registry : Genode::Registry -{ - Util::Pthread_mutex _mutex { }; - Util::Pthread_mutex &mutex() { return _mutex; } -}; - - -class Ssh::Server -{ - public: - - struct Init_failed : Genode::Exception { }; - struct Invalid_config : Genode::Exception { }; - - private: - - using Session_registry = Genode::Registry; - - 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 { }; - Session_registry _new_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_ */ diff --git a/repos/gems/src/server/ssh_terminal/session_component.h b/repos/gems/src/server/ssh_terminal/session_component.h deleted file mode 100644 index c3c91ad86c..0000000000 --- a/repos/gems/src/server/ssh_terminal/session_component.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * \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_SESSION_COMPONENT_H_ -#define _SSH_TERMINAL_SESSION_COMPONENT_H_ - -/* Genode includes */ -#include -#include - -/* local includes */ -#include "util.h" -#include "terminal.h" - - -namespace Terminal { - class Session_component; -}; - -class Terminal::Session_component : public Genode::Rpc_object, - public Ssh::Terminal -{ - private: - - Genode::Attached_ram_dataspace _io_buffer; - - public: - - Session_component(Genode::Env &env, - Genode::size_t io_buffer_size, - Ssh::User const &user) - : - Ssh::Terminal(user), - _io_buffer(env.ram(), env.rm(), io_buffer_size) - { } - - virtual ~Session_component() = default; - - /******************************** - ** Terminal session interface ** - ********************************/ - - Genode::size_t read(void *buf, Genode::size_t) override { return 0; } - Genode::size_t write(void const *buf, Genode::size_t) override { return 0; } - - Size size() override { return Ssh::Terminal::size(); } - bool avail() override { return !Ssh::Terminal::read_buffer_empty(); } - - void read_avail_sigh(Genode::Signal_context_capability sigh) override { - Ssh::Terminal::read_avail_sigh(sigh); - } - - void connected_sigh(Genode::Signal_context_capability sigh) override { - Ssh::Terminal::connected_sigh(sigh); - } - - void size_changed_sigh(Genode::Signal_context_capability sigh) override { - Ssh::Terminal::size_changed_sigh(sigh); - } - - Genode::Dataspace_capability _dataspace() { return _io_buffer.cap(); } - - Genode::size_t _read(Genode::size_t num) - { - Genode::size_t num_bytes = 0; - Libc::with_libc([&] () { - char *buf = _io_buffer.local_addr(); - num = Genode::min(_io_buffer.size(), num); - num_bytes = Ssh::Terminal::read(buf, num); - }); - return num_bytes; - } - - Genode::size_t _write(Genode::size_t num) - { - ssize_t written = 0; - - char *buf = _io_buffer.local_addr(); - num = Genode::min(num, _io_buffer.size()); - written = Ssh::Terminal::write(buf, num); - - if (written < 0) { - Genode::error("write error, dropping data"); - written = 0; - } - - return written; - } -}; - -#endif /* _SSH_TERMINAL_SESSION_COMPONENT_H_ */ diff --git a/repos/gems/src/server/ssh_terminal/ssh_callbacks.cc b/repos/gems/src/server/ssh_terminal/ssh_callbacks.cc deleted file mode 100644 index e9bf6075b5..0000000000 --- a/repos/gems/src/server/ssh_terminal/ssh_callbacks.cc +++ /dev/null @@ -1,304 +0,0 @@ -/* - * \brief Component providing a Terminal session via SSH - * \author Josef Soentgen - * \author Pirmin Duss - * \author Sid Hussmann - * \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. - */ - - -/* libssh includes */ -#include -#include -#include - -/* local includes */ -#include "server.h" - - -/*********************** - ** Channel callbacks ** - ***********************/ - -/** - * Handle SSH channel data request - */ -int channel_data_cb(ssh_session session, ssh_channel channel, - void *data, uint32_t len, int is_stderr, - void *userdata) -{ - using Genode::error; - - if (len == 0) { - return 0; - } - - Ssh::Server &server = *reinterpret_cast(userdata); - Ssh::Session *p = server.lookup_session(session); - if (!p) { - error("session not found"); - return SSH_ERROR; - } - - if (p->channel != channel) { - error("wrong channel"); - return SSH_ERROR; - } - - if (!p->terminal) { - error("no terminal"); - return SSH_ERROR; - } - - Ssh::Terminal &conn { *p->terminal }; - Util::Pthread_mutex::Guard guard { conn.read_buf.mutex() }; - char const *src { reinterpret_cast(data) }; - size_t num_bytes { 0 }; - - while ((conn.read_buf.write_avail() > 0) && (num_bytes < len)) { - - char c = src[num_bytes]; - - /* replace ^? with ^H and let's hope we do not break anything */ - enum { DEL = 0x7f, BS = 0x08, }; - if (c == DEL) { - conn.read_buf.append(BS); - } else { - conn.read_buf.append(c); - } - - num_bytes++; - } - conn.notify_read_avail(); - return num_bytes; -} - - -/** - * Handle SSH channel shell request - * - * For now we ignore this request because there is no way to change the - * $ENV of the Terminal::Session client currently. - */ -int channel_env_request_cb(ssh_session session, ssh_channel channel, - char const *env_name, char const *env_value, - void *userdata) -{ - return SSH_OK; -} - - -/** - * Handle SSH channel PTY request - */ -int channel_pty_request_cb(ssh_session session, ssh_channel channel, - char const *term, - int cols, int rows, int py, int px, - void *userdata) -{ - using namespace Genode; - Ssh::Server &server = *reinterpret_cast(userdata); - Ssh::Session *p = server.lookup_session(session); - if (!p || p->channel != channel) { return SSH_ERROR; } - - /* - * Look up terminal and in case there is none, check - * if we have to wait for another subsystem to come up. - * In this case we return successfully to the client - * and wait for a Terminal session to be established. - */ - if (!p->terminal) { - p->terminal = server.lookup_terminal(*p); - if (!p->terminal) { - return server.request_terminal(*p) ? SSH_OK - : SSH_ERROR; - } - } - - p->terminal->attach_channel(); - - Ssh::Terminal &conn = *p->terminal; - conn.size(Terminal::Session::Size(cols, rows)); - conn.notify_size_changed(); - - /* session handling already takes care of having a terminal attached */ - conn.notify_connected(); - return SSH_OK; -} - - -/** - * Handle SSH channel PTY resize request - */ -int channel_pty_window_change_cb(ssh_session session, ssh_channel channel, - int width, int height, int pxwidth, int pwheight, - void *userdata) -{ - (void)pxwidth; - (void)pwheight; - - using namespace Genode; - Ssh::Server &server = *reinterpret_cast(userdata); - Ssh::Session *p = server.lookup_session(session); - if (!p || p->channel != channel || !p->terminal) { return SSH_ERROR; } - - Ssh::Terminal &conn = *p->terminal; - conn.size(Terminal::Session::Size(width, height)); - conn.notify_size_changed(); - return SSH_OK; -} - - -/** - * Handle SSH channel shell request - * - * For now we ignore this request as the shell is implicitly provided when - * the PTY request is handled. - */ -int channel_shell_request_cb(ssh_session session, ssh_channel channel, - void *userdata) -{ - return SSH_OK; -} - - -/** - * Handle SSH channel exec request - * - * Exec requests provide a command that needs to be executed. - * The command is provided while starting a new terminal using - * request_terminal(). - */ -int channel_exec_request_cb(ssh_session session, ssh_channel channel, - const char *command, - void *userdata) -{ - using namespace Genode; - - Ssh::Server &server = *reinterpret_cast(userdata); - Ssh::Session *p = server.lookup_session(session); - if (!p || p->channel != channel) { return SSH_ERROR; } - - /* - * Look up terminal and in case there is none, check - * if we have to wait for another subsystem to come up. - * In this case we return successfully to the client - * and wait for a Terminal session to be established. - */ - if (!p->terminal) { - p->terminal = server.lookup_terminal(*p); - if (!p->terminal) { - return server.request_terminal(*p, command) ? SSH_OK - : SSH_ERROR; - } - } - /* exec commands can only be done with newly started terminals */ - return SSH_ERROR; -} - - -/*********************** - ** Session callbacks ** - ***********************/ - -/** - * Handle SSH session service requests - */ -int session_service_request_cb(ssh_session, - char const *service, void*) -{ - return Genode::strcmp(service, "ssh-userauth") == 0 ? 0 : -1; -} - - -/** - * Handle SSH session password authentication requests - */ -int session_auth_password_cb(ssh_session session, - char const *user, char const *password, - void *userdata) -{ - Ssh::Server &server = *reinterpret_cast(userdata); - return server.auth_password(session, user, password) ? SSH_AUTH_SUCCESS - : SSH_AUTH_DENIED; -} - - -/** - * Handle SSH session public-key authentication requests - */ -int session_auth_pubkey_cb(ssh_session session, char const *user, - struct ssh_key_struct *pubkey, - char state, void *userdata) -{ - Ssh::Server &server = *reinterpret_cast(userdata); - return server.auth_pubkey(session, user, pubkey, state) ? SSH_AUTH_SUCCESS - : SSH_AUTH_DENIED; - -} - - -/** - * Handle SSH session open channel requests - */ -ssh_channel session_channel_open_request_cb(ssh_session session, - void *userdata) -{ - using namespace Genode; - - Ssh::Server &server = *reinterpret_cast(userdata); - Ssh::Session *p = server.lookup_session(session); - if (!p) { - error("could not look up session"); - return nullptr; - } - - /* for now only one channel */ - if (p->channel) { - log("Only one channel per session supported"); - return nullptr; - } - - ssh_channel channel = ssh_channel_new(p->session); - - if (!channel) { - error("could not create new channel: '", ssh_get_error(p->session)); - return nullptr; - } - - p->add_channel(channel); - return channel; -} - - -/** - * Handle new incoming SSH session requests - */ -void bind_incoming_connection(ssh_bind sshbind, void *userdata) -{ - using namespace Genode; - - ssh_session session = ssh_new(); - if (!session || ssh_bind_accept(sshbind, session)) { - error("could not accept session: '", ssh_get_error(session), "'"); - ssh_free(session); - return; - } - - Ssh::Server &server = *reinterpret_cast(userdata); - try { - server.incoming_connection(session); - } - catch (...) { - ssh_disconnect(session); - ssh_free(session); - } -} diff --git a/repos/gems/src/server/ssh_terminal/target.mk b/repos/gems/src/server/ssh_terminal/target.mk deleted file mode 100644 index c3fb9d0e0c..0000000000 --- a/repos/gems/src/server/ssh_terminal/target.mk +++ /dev/null @@ -1,8 +0,0 @@ -TARGET = ssh_terminal -SRC_CC = main.cc -SRC_CC += server.cc -SRC_CC += ssh_callbacks.cc -SRC_CC += util.cc -LIBS = base libc libssh - -CC_CXX_WARN_STRICT = diff --git a/repos/gems/src/server/ssh_terminal/terminal.h b/repos/gems/src/server/ssh_terminal/terminal.h deleted file mode 100644 index 5b78629dd9..0000000000 --- a/repos/gems/src/server/ssh_terminal/terminal.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * \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_TERMINAL_H_ -#define _SSH_TERMINAL_TERMINAL_H_ - -/* Genode includes */ -#include -#include -#include -#include -#include - -/* local includes */ -#include "login.h" - - -namespace Ssh -{ - using namespace Genode; - - class Terminal; -} - - -class Ssh::Terminal -{ - private: - - typedef Util::Buffer<4096u> Buffer; - - Mutex _write_buf_swap { }; - - Buffer _write_buf_a { }; - Buffer _write_buf_b { }; - Buffer *_write_buf_ep { &_write_buf_a }; - Buffer *_write_buf_pthread { &_write_buf_b }; - - ::Terminal::Session::Size _size { 0, 0 }; - - Signal_context_capability _size_changed_sigh; - Signal_context_capability _connected_sigh; - Signal_context_capability _read_avail_sigh; - - Ssh::User const _user { }; - - unsigned _attached_channels { 0u }; - unsigned _pending_channels { 0u }; - - public: - - Buffer read_buf { }; - - int write_avail_fd { -1 }; - - /** - * Constructor - */ - Terminal(Ssh::User const &user) : _user(user) { } - - virtual ~Terminal() = default; - - Ssh::User const &user() const { return _user; } - - unsigned attached_channels() const { return _attached_channels; } - - void attach_channel() { ++_attached_channels; } - void detach_channel() { --_attached_channels; } - void reset_pending() { _pending_channels = 0; } - - /********************************* - ** Terminal::Session interface ** - *********************************/ - - /** - * Register signal handler to be notified once the size was changed - */ - void size_changed_sigh(Signal_context_capability sigh) { - _size_changed_sigh = sigh; } - - /** - * Register signal handler to be notified once we accepted the TCP - * connection - */ - void connected_sigh(Signal_context_capability sigh) - { - _connected_sigh = sigh; - - if (_attached_channels > 0) { - notify_connected(); - } - } - - /** - * Register signal handler to be notified when data is available for - * reading - */ - void read_avail_sigh(Signal_context_capability sigh) - { - _read_avail_sigh = sigh; - - /* if read data is available right now, deliver signal immediately */ - if (read_buffer_empty() && _read_avail_sigh.valid()) { - Signal_transmitter(_read_avail_sigh).submit(); - } - } - - /** - * Inform client about the finished initialization of the SSH - * session - */ - void notify_connected() - { - if (!_connected_sigh.valid()) { return; } - Signal_transmitter(_connected_sigh).submit(); - } - - /** - * Inform client about avail data - */ - void notify_read_avail() - { - if (!_read_avail_sigh.valid()) { return; } - Signal_transmitter(_read_avail_sigh).submit(); - } - - /** - * Inform client about the changed size of the remote terminal - */ - void notify_size_changed() - { - if (!_size_changed_sigh.valid()) { return; } - Signal_transmitter(_size_changed_sigh).submit(); - } - - /** - * Set size of the Terminal session to match remote terminal - */ - void size(::Terminal::Session::Size size) { _size = size; } - - /** - * Return size of the Terminal session - */ - ::Terminal::Session::Size size() const { return _size; } - - /***************** - ** I/O methods ** - *****************/ - - /** - * Send internal write buffer content to SSH channel - */ - void send(ssh_channel channel) - { - { - /* swap write buffers if current is empty */ - Mutex::Guard guard(_write_buf_swap); - - if (!_write_buf_pthread->read_avail()) { - auto buffer_tmp = _write_buf_ep; - _write_buf_ep = _write_buf_pthread; - _write_buf_pthread = buffer_tmp; - } - } - - /* write buffer for pthread is used w/o mutex, it's not used by EP */ - Buffer &write_buf = *_write_buf_pthread; - - if (!write_buf.read_avail()) { return; } - - /* ignore send request */ - if (!channel || !ssh_channel_is_open(channel)) { return; } - - char const *src = write_buf.content(); - size_t const len = write_buf.read_avail(); - /* XXX we do not handle partial writes */ - int const num_bytes = ssh_channel_write(channel, src, len); - - if (num_bytes && (size_t)num_bytes < len) { - warning("send on channel was truncated"); - } - - if (++_pending_channels >= _attached_channels) { - write_buf.reset(); - } - - /* at this point the client might have disconnected */ - if (num_bytes < 0) { throw -1; } - } - - /****************************************** - ** Methods called by Terminal front end ** - ******************************************/ - - /** - * Read out internal read buffer and copy into destination buffer. - */ - size_t read(char *dst, size_t dst_len) - { - Util::Pthread_mutex::Guard guard(read_buf.mutex()); - - size_t const num_bytes = min(dst_len, read_buf.read_avail()); - Genode::memcpy(dst, read_buf.content(), num_bytes); - read_buf.consume(num_bytes); - - /* notify client if there are still bytes available for reading */ - if (!read_buf.read_avail()) { read_buf.reset(); } - else { - if (_read_avail_sigh.valid()) { - Signal_transmitter(_read_avail_sigh).submit(); - } - } - - return num_bytes; - } - - /** - * Write into internal buffer and copy to underlying socket - */ - size_t write(char const *src, Genode::size_t src_len) - { - size_t num_bytes = 0; - - { - Mutex::Guard guard(_write_buf_swap); - - Buffer &write_buf = *_write_buf_ep; - - size_t i = 0; - - while (write_buf.write_avail() > 0 && i < src_len) { - - char c = src[i]; - if (c == '\n') { - write_buf.append('\r'); - } - - write_buf.append(c); - num_bytes++; - - i++; - } - } - - /* wake the event loop up */ - Libc::with_libc([&] { - char c = 1; - ::write(write_avail_fd, &c, sizeof(c)); - }); - - return num_bytes; - } - - /** - * Return true if the internal read buffer is ready to receive data - */ - bool read_buffer_empty() - { - bool empty = true; - - Libc::with_libc([&] { - Util::Pthread_mutex::Guard guard(read_buf.mutex()); - empty = !read_buf.read_avail(); - }); - - return empty; - } -}; - -#endif /* _SSH_TERMINAL_TERMINAL_H_ */ diff --git a/repos/gems/src/server/ssh_terminal/util.cc b/repos/gems/src/server/ssh_terminal/util.cc deleted file mode 100644 index 36ddfd059b..0000000000 --- a/repos/gems/src/server/ssh_terminal/util.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* - * \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. - */ - -/* local includes */ -#include "util.h" - -char const *Util::get_time() -{ - static char buffer[32]; - - char const *p = ""; - Libc::with_libc([&] { - struct timespec ts; - if (clock_gettime(0, &ts)) { return; } - - struct tm *tm = localtime((time_t*)&ts.tv_sec); - if (!tm) { return; } - - size_t const n = strftime(buffer, sizeof(buffer), "%F %H:%M:%S", tm); - if (n > 0 && n < sizeof(buffer)) { p = buffer; } - }); /* Libc::with_libc */ - - return p; -} diff --git a/repos/gems/src/server/ssh_terminal/util.h b/repos/gems/src/server/ssh_terminal/util.h deleted file mode 100644 index 84be040f34..0000000000 --- a/repos/gems/src/server/ssh_terminal/util.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * \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_UTIL_H_ -#define _SSH_TERMINAL_UTIL_H_ - -/* Genode includes */ -#include -#include - -/* libc includes */ -#include -#include -#include - - -namespace Util -{ - using Filename = Genode::String<256>; - - template - struct Buffer; - - /* - * get the current time from the libc backend. - */ - char const *get_time(); - - struct Pthread_mutex; -} - - -struct Util::Pthread_mutex -{ - public: - - class Guard - { - private: - - Pthread_mutex &_mutex; - - public: - - explicit Guard(Pthread_mutex &mutex) : _mutex(mutex) { _mutex.lock(); } - - ~Guard() { _mutex.unlock(); } - }; - - private: - - pthread_mutex_t _mutex; - - public: - - Pthread_mutex() { pthread_mutex_init(&_mutex, nullptr); } - - ~Pthread_mutex() { pthread_mutex_destroy(&_mutex); } - - void lock() { pthread_mutex_lock(&_mutex); } - void unlock() { pthread_mutex_unlock(&_mutex); } -}; - - -template -struct Util::Buffer -{ - Util::Pthread_mutex _mutex { }; - char _data[C] { }; - size_t _head { 0 }; - size_t _tail { 0 }; - - size_t read_avail() const { return _head > _tail ? _head - _tail : 0; } - size_t write_avail() const { return _head <= C ? C - _head : 0; } - char const *content() const { return &_data[_tail]; } - - void append(char c) { _data[_head++] = c; } - void consume(size_t n) { _tail += n; } - void reset() { _head = _tail = 0; } - - Util::Pthread_mutex &mutex() { return _mutex; } -}; - -#endif /* _SSH_TERMINAL_UTIL_H_ */