diff --git a/.gitignore b/.gitignore index bafb6ddf70a..086ba2532ab 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,6 @@ Makefile.in /cockpit-certificate-ensure /cockpit-pcp /cockpit-session -/cockpit-ssh /cockpit-tls /cockpit-ws /cockpit-wsinstance-factory diff --git a/Makefile.am b/Makefile.am index 3ca3fa0f727..2aa715633d3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -174,7 +174,6 @@ include src/client/Makefile.am include src/common/Makefile-common.am include src/pam-ssh-add/Makefile.am include src/session/Makefile-session.am -include src/ssh/Makefile-ssh.am include src/systemd/Makefile.am include src/tls/Makefile-tls.am include src/websocket/Makefile-websocket.am diff --git a/configure.ac b/configure.ac index 1887a462cc2..25549037b4d 100644 --- a/configure.ac +++ b/configure.ac @@ -70,12 +70,6 @@ if test "$enable_polkit" != 'no'; then fi AC_MSG_RESULT(${enable_polkit:=yes}) -# --disable-ssh -AC_MSG_CHECKING([whether to build cockpit-ssh]) -AC_ARG_ENABLE(ssh, AS_HELP_STRING([--disable-ssh], [Disable cockpit-ssh build and libssh dependency])) -AM_CONDITIONAL(WITH_COCKPIT_SSH, test "$enable_ssh" != "no") -AC_MSG_RESULT(${enable_ssh:=yes}) - # TODO: remove this and clean up the old bridge in src/bridge/ AM_CONDITIONAL(WITH_OLD_BRIDGE, false) @@ -108,14 +102,6 @@ PKG_CHECK_MODULES(krb5, [krb5-gssapi >= 1.11 krb5 >= 1.11]) if test "$enable_polkit" != "no"; then PKG_CHECK_MODULES(polkit, [polkit-agent-1 >= 0.105]) fi -if test "$enable_ssh" != "no"; then - PKG_CHECK_MODULES(libssh, [libssh >= 0.8.5]) - old_CFLAGS=$CFLAGS; CFLAGS=$libssh_CFLAGS - old_LIBS=$LIBS; LIBS=$libssh_LIBS - AC_CHECK_FUNCS(ssh_userauth_publickey_auto_get_current_identity) - CFLAGS=$old_CFLAGS - LIBS=$old_LIBS -fi # pam AC_CHECK_HEADER([security/pam_appl.h], , @@ -442,7 +428,6 @@ echo " SELinux Policy: ${enable_selinux_policy} cockpit-client: ${enable_cockpit_client} - cockpit-ssh: ${enable_ssh} ssh-add: ${SSH_ADD} ssh-agent: ${SSH_AGENT} diff --git a/containers/flatpak/prepare b/containers/flatpak/prepare index 54818fbdca6..b8fd1efb006 100755 --- a/containers/flatpak/prepare +++ b/containers/flatpak/prepare @@ -104,7 +104,6 @@ def create_manifest( 'config-opts': [ '--enable-cockpit-client', '--disable-polkit', - '--disable-ssh', '--disable-pcp', '--with-systemdunitdir=/invalid', 'CPPFLAGS=-Itools/mock-build-env', diff --git a/doc/authentication.md b/doc/authentication.md index 0a1d34d2e95..976fbd00480 100644 --- a/doc/authentication.md +++ b/doc/authentication.md @@ -192,7 +192,7 @@ The following environment variables are set by cockpit-ws when spawning an auth * **COCKPIT_REMOTE_PEER** Set to the ip address of the connecting user. -The following environment variables are used to set options for the `cockpit-ssh` process: +The following environment variables are used to set options for SSH connections: * **COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS** Set to `1` to allow connecting to hosts that are not present in the current `known_hosts` files. If not set, diff --git a/doc/protocol.md b/doc/protocol.md index bddd0481ab3..dd16ee9f436 100644 --- a/doc/protocol.md +++ b/doc/protocol.md @@ -361,7 +361,7 @@ Example authorize challenge and response messages: ``` Authorize messages are used during authentication by authentication -commands (ei: cockpit-session, cockpit-ssh) to obtain the users credentials +commands like `cockpit-session` to obtain the users credentials from cockpit-ws. An authentication command can send a authorize message with a response but no cookie. For example diff --git a/selinux/cockpit.fc b/selinux/cockpit.fc index 5fb9ca03de5..0d0938d3098 100644 --- a/selinux/cockpit.fc +++ b/selinux/cockpit.fc @@ -6,7 +6,6 @@ /usr/libexec/cockpit-wsinstance-factory -- gen_context(system_u:object_r:cockpit_ws_exec_t,s0) /usr/libexec/cockpit-session -- gen_context(system_u:object_r:cockpit_session_exec_t,s0) -/usr/libexec/cockpit-ssh -- gen_context(system_u:object_r:cockpit_session_exec_t,s0) /usr/share/cockpit/motd/update-motd -- gen_context(system_u:object_r:cockpit_ws_exec_t,s0) diff --git a/selinux/cockpit_session_selinux.8cockpit b/selinux/cockpit_session_selinux.8cockpit index cbf7a1b5ed2..1e030ba1fb4 100644 --- a/selinux/cockpit_session_selinux.8cockpit +++ b/selinux/cockpit_session_selinux.8cockpit @@ -16,9 +16,8 @@ For example: The cockpit_session_t SELinux type can be entered via the \fBcockpit_session_exec_t\fP file type. -The default entrypoint paths for the cockpit_session_t domain are the following: +The default entrypoint paths for the cockpit_session_t domain is \fB/usr/libexec/cockpit-session\fP. -/usr/libexec/cockpit-ssh, /usr/libexec/cockpit-session .SH PROCESS TYPES SELinux defines process types (domains) for each process running on the system .PP @@ -221,7 +220,7 @@ SELinux cockpit_session policy is very flexible allowing users to setup their co .br .TP 5 Paths: -/usr/libexec/cockpit-ssh, /usr/libexec/cockpit-session +/usr/libexec/cockpit-session .PP Note: File context can be temporarily modified with the chcon command. If you want to permanently change the file context you need to use the @@ -252,4 +251,4 @@ This manual page was auto-generated using .B "sepolicy manpage". .SH "SEE ALSO" -selinux(8), cockpit_session(8), semanage(8), restorecon(8), chcon(1), sepolicy(8), setsebool(8) \ No newline at end of file +selinux(8), cockpit_session(8), semanage(8), restorecon(8), chcon(1), sepolicy(8), setsebool(8) diff --git a/src/bridge/Makefile.am b/src/bridge/Makefile.am index f88ae0dd2f6..d9808572756 100644 --- a/src/bridge/Makefile.am +++ b/src/bridge/Makefile.am @@ -236,7 +236,6 @@ endif # ----------------------------------------------------------------------------- # polkit -# make sure this ends up in the tarball, even with --disable-ssh polkit_in_files = src/bridge/org.cockpit-project.cockpit-bridge.policy.in EXTRA_DIST += $(polkit_in_files) diff --git a/src/ssh/Makefile-ssh.am b/src/ssh/Makefile-ssh.am deleted file mode 100644 index 195eec6e9c0..00000000000 --- a/src/ssh/Makefile-ssh.am +++ /dev/null @@ -1,78 +0,0 @@ -if WITH_COCKPIT_SSH - -# ----------------------------------------------------------------------------- -# libcockpit-ssh.a: code used in cockpit-ssh and its tests - -noinst_LIBRARIES += libcockpit-ssh.a - -libcockpit_ssh_a_CPPFLAGS = \ - -DG_LOG_DOMAIN=\"cockpit-ssh\" \ - $(glib_CFLAGS) \ - $(json_glib_CFLAGS) \ - $(libssh_CFLAGS) \ - $(AM_CPPFLAGS) - -libcockpit_ssh_a_LIBS = \ - libcockpit-ssh.a \ - $(libcockpit_common_a_LIBS) \ - $(libssh_LIBS) \ - $(NULL) - -libcockpit_ssh_a_SOURCES = \ - src/ssh/cockpitsshoptions.c \ - src/ssh/cockpitsshoptions.h \ - src/ssh/cockpitsshrelay.h \ - src/ssh/cockpitsshrelay.c \ - $(NULL) - -# ----------------------------------------------------------------------------- -# cockpit-ssh - -libexec_PROGRAMS += cockpit-ssh -cockpit_ssh_CPPFLAGS = $(libcockpit_ssh_a_CPPFLAGS) -cockpit_ssh_LDADD = $(libcockpit_ssh_a_LIBS) -cockpit_ssh_SOURCES = src/ssh/ssh.c - -# ----------------------------------------------------------------------------- -# mock-ssh - -check_PROGRAMS += mock-sshd -mock_sshd_CPPFLAGS = $(libcockpit_ssh_a_CPPFLAGS) $(TEST_CPP) -mock_sshd_LDADD = $(libcockpit_ssh_a_LIBS) $(TEST_LIBS) -mock_sshd_SOURCES = src/ssh/mock-sshd.c - -# ----------------------------------------------------------------------------- -# Unit tests - -dist_check_DATA += \ - src/ssh/mock_rsa_key \ - src/ssh/mock_ecdsa_key \ - src/ssh/test_rsa \ - src/ssh/test_rsa.pub \ - src/ssh/mock_known_hosts \ - src/ssh/mock-pid-cat \ - src/ssh/mock-config \ - src/ssh/invalid_known_hosts \ - $(NULL) - -TEST_PROGRAM += test-sshbridge -test_sshbridge_CPPFLAGS = $(libcockpit_ssh_a_CPPFLAGS) $(TEST_CPP) -test_sshbridge_LDADD = $(libcockpit_ssh_a_LIBS) $(TEST_LIBS) -test_sshbridge_SOURCES = src/ssh/test-sshbridge.c - -TEST_PROGRAM += test-sshoptions -test_sshoptions_CPPFLAGS = $(libcockpit_ssh_a_CPPFLAGS) $(TEST_CPP) -test_sshoptions_LDADD = $(libcockpit_ssh_a_LIBS) $(TEST_LIBS) -test_sshoptions_SOURCES = src/ssh/test-sshoptions.c - -check_DATA += test_rsa_key -CLEANFILES += test_rsa_key -test_rsa_key: src/ssh/test_rsa - $(AM_V_GEN) cp $< $@ && chmod 600 $@ - -update-known-hosts: - cat $(srcdir)/src/ssh/mock_*.pub | \ - sed -ne 's/\(.*\) [^ ]\+$$/[localhost]:*,[127.0.0.1]:* \1/p' > \ - $(srcdir)/src/ssh/mock_known_hosts - -endif diff --git a/src/ssh/cockpitsshoptions.c b/src/ssh/cockpitsshoptions.c deleted file mode 100644 index cdf1c4bc5de..00000000000 --- a/src/ssh/cockpitsshoptions.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2016 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#include "config.h" - -#include "common/cockpitconf.h" - -#include "cockpitsshoptions.h" - -static const gchar *default_command = "cockpit-bridge"; - -static gboolean -has_environment_val (gchar **env, - const gchar *name) -{ - const gchar *v = g_environ_getenv (env, name); - return v != NULL && v[0] != '\0'; -} - -static const gchar * -get_environment_val (gchar **env, - const gchar *name, - const gchar *defawlt) -{ - if (has_environment_val (env, name)) - return g_environ_getenv (env, name); - else - return defawlt; -} - -static gchar ** -set_environment_val (gchar **env, - const gchar *name, - const gchar *val) -{ - return g_environ_setenv (env, name, val ? val : "", TRUE); -} - -static gboolean -get_environment_bool (gchar **env, - const gchar *name, - gboolean defawlt) -{ - const gchar *value = get_environment_val (env, name, NULL); - - if (!value) - return defawlt; - - return g_strcmp0 (value, "yes") == 0 || - g_strcmp0 (value, "true") == 0 || - g_strcmp0 (value, "1") == 0; -} - -static gchar ** -set_environment_bool (gchar **env, - const gchar *name, - gboolean val) -{ - return g_environ_setenv (env, name, val ? "1" : "", TRUE); -} - -static gboolean -get_connect_to_unknown_hosts (gchar **env) -{ - /* Fallback to deprecated allowUnknown option; use cockpit_conf_string() to - * test for existence as _bool() cannot tell apart "unset" from "set to false". */ - if (!cockpit_conf_string (COCKPIT_CONF_SSH_SECTION, "connectToUnknownHosts") && - cockpit_conf_bool (COCKPIT_CONF_SSH_SECTION, "allowUnknown", FALSE)) - return TRUE; - - if (cockpit_conf_bool (COCKPIT_CONF_SSH_SECTION, "connectToUnknownHosts", FALSE)) - return TRUE; - - if (!has_environment_val (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS")) - return get_environment_bool (env, "COCKPIT_SSH_ALLOW_UNKNOWN", FALSE); - return get_environment_bool (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", FALSE); -} - -CockpitSshOptions * -cockpit_ssh_options_from_env (gchar **env) -{ - - CockpitSshOptions *options = g_new0 (CockpitSshOptions, 1); - options->knownhosts_file = get_environment_val (env, "COCKPIT_SSH_KNOWN_HOSTS_FILE", NULL); - options->command = get_environment_val (env, "COCKPIT_SSH_BRIDGE_COMMAND", default_command); - options->remote_peer = get_environment_val (env, "COCKPIT_REMOTE_PEER", "localhost"); - options->connect_to_unknown_hosts = get_connect_to_unknown_hosts (env); - - return options; -} - -gchar ** -cockpit_ssh_options_to_env (CockpitSshOptions *options, - gchar **env) -{ - env = set_environment_bool (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", - options->connect_to_unknown_hosts); - env = set_environment_val (env, "COCKPIT_SSH_KNOWN_HOSTS_FILE", - options->knownhosts_file); - env = set_environment_val (env, "COCKPIT_REMOTE_PEER", - options->remote_peer); - - /* Don't reset these vars unless we have values for them */ - if (options->command) - { - env = set_environment_val (env, "COCKPIT_SSH_BRIDGE_COMMAND", - options->command); - } - - return env; -} diff --git a/src/ssh/cockpitsshoptions.h b/src/ssh/cockpitsshoptions.h deleted file mode 100644 index 794e98dc730..00000000000 --- a/src/ssh/cockpitsshoptions.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2016 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#ifndef __COCKPIT_SSH_OPTIONS_H__ -#define __COCKPIT_SSH_OPTIONS_H__ - -#include - -G_BEGIN_DECLS - -typedef struct { - const gchar *knownhosts_file; - const gchar *command; - const gchar *remote_peer; - gboolean connect_to_unknown_hosts; -} CockpitSshOptions; - -CockpitSshOptions * cockpit_ssh_options_from_env (gchar **env); - -gchar ** cockpit_ssh_options_to_env (CockpitSshOptions *options, - gchar **env); - -G_END_DECLS - -#endif diff --git a/src/ssh/cockpitsshrelay.c b/src/ssh/cockpitsshrelay.c deleted file mode 100644 index 80334a364b7..00000000000 --- a/src/ssh/cockpitsshrelay.c +++ /dev/null @@ -1,2303 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2016 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#include "config.h" - -#include "common/cockpitauthorize.h" -#include "common/cockpitconf.h" -#include "common/cockpithex.h" -#include "common/cockpitframe.h" -#include "common/cockpitjson.h" -#include "common/cockpitmemory.h" -#include "common/cockpitpipe.h" -#include "common/cockpittransport.h" - -#include "cockpitsshrelay.h" -#include "cockpitsshoptions.h" - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -typedef struct { - const gchar *logname; - gchar *initial_auth_data; - gchar *auth_type; - - gchar **env; - CockpitSshOptions *ssh_options; - - gchar *username; - gboolean in_bridge; - - ssh_session session; - - gchar *conversation; - - gchar *host_key; - gchar *host_fingerprint; - const gchar *host_key_type; - GHashTable *auth_results; - gchar *user_known_hosts; - - gchar *problem_error; -} CockpitSshData; - -static gchar *tmp_knownhost_file; - -static const gchar* -exit_code_problem (int exit_code) -{ - switch (exit_code) - { - case 0: - return NULL; - case AUTHENTICATION_FAILED: - return "authentication-failed"; - case DISCONNECTED: - return "disconnected"; - case TERMINATED: - return "terminated"; - case NO_COCKPIT: - return "no-cockpit"; - default: - return "internal-error"; - } -} - -static const gchar * -auth_method_description (int method) -{ - if (method == SSH_AUTH_METHOD_NONE) - return "none"; - else if (method == SSH_AUTH_METHOD_PASSWORD || method == SSH_AUTH_METHOD_INTERACTIVE) - return "password"; - else if (method == SSH_AUTH_METHOD_PUBLICKEY) - return "public-key"; - else if (method == SSH_AUTH_METHOD_HOSTBASED) - return "host-based"; - else if (method == SSH_AUTH_METHOD_GSSAPI_MIC) - return "gssapi-mic"; - else - return "unknown"; -} - -static gchar * -auth_methods_line (int methods) -{ - GString *string; - int i = 0; - int check[6] = { - SSH_AUTH_METHOD_NONE, - SSH_AUTH_METHOD_INTERACTIVE, - SSH_AUTH_METHOD_PASSWORD, - SSH_AUTH_METHOD_PUBLICKEY, - SSH_AUTH_METHOD_HOSTBASED, - SSH_AUTH_METHOD_GSSAPI_MIC - }; - - string = g_string_new (""); - for (i = 0; i < G_N_ELEMENTS (check); i++) - { - if (methods & check[i]) - { - g_string_append (string, auth_method_description (check[i])); - g_string_append (string, " "); - } - } - - return g_string_free (string, FALSE); -} - -static gboolean -ssh_msg_is_disconnected (const gchar *msg) -{ - return msg && (strstr (msg, "disconnected") || - strstr (msg, "SSH_MSG_DISCONNECT") || - strstr (msg, "Socket error: Success") || - strstr (msg, "Socket error: Connection reset by peer")); -} - -static gboolean -write_control_message (int fd, - JsonObject *options) -{ - gboolean ret = TRUE; - gchar *payload; - gchar *prefixed; - gsize length; - - payload = cockpit_json_write_object (options, &length); - prefixed = g_strdup_printf ("\n%s", payload); - if (cockpit_frame_write (fd, (unsigned char *)prefixed, length + 1) < 0) - { - g_message ("couldn't write control message: %s", g_strerror (errno)); - ret = FALSE; - } - g_free (prefixed); - g_free (payload); - - return ret; -} - -static void -byte_array_clear_and_free (gpointer data) -{ - GByteArray *buffer = data; - cockpit_memory_clear (buffer->data, buffer->len); - g_byte_array_free (buffer, TRUE); -} - -static JsonObject * -read_control_message (int fd) -{ - JsonObject *options = NULL; - GBytes *payload = NULL; - GBytes *bytes = NULL; - gchar *channel = NULL; - guchar *data = NULL; - gssize length = 0; - - length = cockpit_frame_read (fd, &data); - if (length < 0) - { - g_message ("couldn't read control message: %s", g_strerror (errno)); - length = 0; - } - else if (length > 0) - { - /* This could have a password, so clear it when freeing */ - bytes = g_bytes_new_with_free_func (data, length, byte_array_clear_and_free, - g_byte_array_new_take (data, length)); - payload = cockpit_transport_parse_frame (bytes, &channel); - data = NULL; - } - - if (payload == NULL) - { - if (length > 0) - g_message ("cockpit-ssh did not receive valid message"); - } - else if (channel != NULL) - { - g_message ("cockpit-ssh did not receive a control message"); - } - else if (!cockpit_transport_parse_command (payload, NULL, NULL, &options)) - { - g_message ("cockpit-ssh did not receive a valid control message"); - } - - g_free (channel); - - if (bytes) - g_bytes_unref (bytes); - if (payload) - g_bytes_unref (payload); - free (data); - return options; -} - -static void -send_authorize_challenge (const gchar *challenge) -{ - gchar *cookie = NULL; - JsonObject *object = json_object_new (); - - cookie = g_strdup_printf ("session%u%u", - (unsigned int)getpid(), - (unsigned int)time (NULL)); - json_object_set_string_member (object, "command", "authorize"); - json_object_set_string_member (object, "challenge", challenge); - json_object_set_string_member (object, "cookie", cookie); - - write_control_message (STDOUT_FILENO, object); - - g_free (cookie); - json_object_unref (object); -} - -static gchar * -challenge_for_auth_data (const gchar *challenge, - gchar **ret_type) -{ - const gchar *response = NULL; - const gchar *command; - gchar *ptr = NULL; - gchar *type = NULL; - JsonObject *reply; - - send_authorize_challenge (challenge ? challenge : "*"); - reply = read_control_message (STDIN_FILENO); - if (!reply) - goto out; - - if (!cockpit_json_get_string (reply, "command", "", &command) || - !g_str_equal (command, "authorize")) - { - g_message ("received \"%s\" control message instead of \"authorize\"", command); - } - else if (!cockpit_json_get_string (reply, "response", NULL, &response)) - { - g_message ("received unexpected \"authorize\" control message: %s", response); - } - - if (response) - cockpit_authorize_type (response, &type); - -out: - if (ret_type) - *ret_type = type; - else - g_free (type); - - if (response && !g_str_equal (response, "")) - ptr = g_strdup (response); - - if (reply) - json_object_unref (reply); - return ptr; -} - -static gchar * -challenge_for_knownhosts_data (CockpitSshData *data) -{ - const gchar *value = NULL; - gchar *ret = NULL; - gchar *response = NULL; - - response = challenge_for_auth_data ("x-host-key", NULL); - if (response) - { - value = cockpit_authorize_type (response, NULL); - /* Legacy blank string means force fail */ - if (value && value[0] == '\0') - ret = g_strdup ("* invalid key"); - else - ret = g_strdup (value); - } - - - g_free (response); - return ret; -} - -static gchar * -prompt_with_authorize (CockpitSshData *data, - const gchar *prompt, - const gchar *msg, - const gchar *default_value, - const gchar *host_key, - gboolean echo) -{ - JsonObject *request = NULL; - JsonObject *reply = NULL; - const gchar *command = NULL; - const char *response = NULL; - char *challenge = NULL; - gchar *result = NULL; - gboolean ret; - - challenge = cockpit_authorize_build_x_conversation (prompt, &data->conversation); - if (!challenge) - return NULL; - - request = json_object_new (); - json_object_set_string_member (request, "command", "authorize"); - json_object_set_string_member (request, "cookie", data->conversation); - json_object_set_string_member (request, "challenge", challenge); - cockpit_memory_clear (challenge, -1); - free (challenge); - - if (msg) - json_object_set_string_member (request, "message", msg); - if (default_value) - json_object_set_string_member (request, "default", default_value); - if (host_key) - json_object_set_string_member (request, "host-key", host_key); - - json_object_set_boolean_member (request, "echo", echo); - - ret = write_control_message (STDOUT_FILENO, request); - json_object_unref (request); - - if (!ret) - return NULL; - - reply = read_control_message (STDIN_FILENO); - if (!reply) - return NULL; - - if (!cockpit_json_get_string (reply, "command", "", &command) || - !g_str_equal (command, "authorize")) - { - g_message ("received \"%s\" control message instead of \"authorize\"", command); - } - else if (!cockpit_json_get_string (reply, "response", "", &response)) - { - g_message ("received unexpected \"authorize\" control message"); - } - else if (!g_str_equal (response, "")) - { - result = cockpit_authorize_parse_x_conversation (response, NULL); - if (!result) - g_message ("received unexpected \"authorize\" control message \"response\""); - } - - json_object_unref (reply); - return result; -} - -static const gchar * -prompt_for_host_key (CockpitSshData *data) -{ - const gchar *ret; - gchar *host = NULL; - guint port = 22; - gchar *message = NULL; - gchar *prompt = NULL; - gchar *reply = NULL; - - if (ssh_options_get (data->session, SSH_OPTIONS_HOST, &host) < 0) - { - g_warning ("Failed to get host"); - goto out; - } - - if (ssh_options_get_port (data->session, &port) < 0) - { - g_warning ("Failed to get port"); - goto out; - } - - message = g_strdup_printf ("The authenticity of host '%s:%d' can't be established. Do you want to proceed this time?", - host, port); - prompt = g_strdup_printf ("SHA256 Fingerprint (%s):", data->host_key_type); - - reply = prompt_with_authorize (data, prompt, message, data->host_fingerprint, data->host_key, TRUE); - -out: - if (g_strcmp0 (reply, data->host_fingerprint) == 0 || g_strcmp0 (reply, data->host_key) == 0) - ret = NULL; - else - ret = "unknown-hostkey"; - - g_free (reply); - g_free (message); - g_free (prompt); - g_free (host); - return ret; -} - -static void cleanup_knownhosts_file (void) -{ - if (tmp_knownhost_file) - { - g_unlink (tmp_knownhost_file); - g_free (tmp_knownhost_file); - } -} - -static gboolean -write_tmp_knownhosts_file (CockpitSshData *data, - const gchar *content, - const gchar **problem) -{ - int fd; - g_autoptr(GError) error = NULL; - - fd = g_file_open_tmp ("known-hosts.XXXXXX", &tmp_knownhost_file, &error); - if (fd < 0) - { - g_warning ("%s: couldn't open temporary known host file for data: %s", - data->logname, error->message); - *problem = "internal-error"; - return FALSE; - } - /* now we own the file; let g_file_set_contents() do the safe writing, instead of bothering with a write() loop */ - close (fd); - - atexit (cleanup_knownhosts_file); - - if (!g_file_set_contents (tmp_knownhost_file, content, -1, &error)) - { - g_warning ("%s: couldn't write data to temporary known host file %s: %s", data->logname, tmp_knownhost_file, error->message); - *problem = "internal-error"; - return FALSE; - } - - return TRUE; -} - -static gboolean -session_has_known_host_in_file (const gchar *file, - CockpitSshData *data, - const gchar *host, - const guint port) -{ - /* HACK - https://gitlab.com/libssh/libssh-mirror/-/issues/156 - - Calling ssh_session_has_known_hosts_entry will call - ssh_options_apply, after which the ssh_session structure can no - longer be used with ssh_session_connect. So we make a copy and - call ssh_session_has_known_hosts_entry on that. - */ - - ssh_session tmp_session; - gboolean result; - g_warn_if_fail (ssh_options_set (data->session, SSH_OPTIONS_KNOWNHOSTS, file) == 0); - ssh_options_copy (data->session, &tmp_session); - result = ssh_session_has_known_hosts_entry (tmp_session) == SSH_KNOWN_HOSTS_OK; - ssh_free (tmp_session); - return result; -} - -static gboolean -is_localhost (const char *host) -{ - return g_strcmp0 (host, "127.0.0.1") == 0 || - g_strcmp0 (host, "::1") == 0 || - g_strcmp0 (host, "localhost") == 0 || - g_strcmp0 (host, "localhost4") == 0 || - g_strcmp0 (host, "localhost6") == 0; -} - -/** - * set_knownhosts_file: - * - * Check the various ssh known hosts locations and set the appropriate one into - * SSH_OPTIONS_KNOWNHOSTS. - * - * Returns: error string or %NULL on success. - */ -static const gchar * -set_knownhosts_file (CockpitSshData *data, - const gchar* host, - const guint port) -{ - gboolean host_known; - const gchar *problem = NULL; - gchar *sout = NULL; - gchar *serr = NULL; - gchar *authorize_knownhosts_data = NULL; - - /* first check the libssh defaults including local and global file */ - host_known = session_has_known_host_in_file (NULL, data, host, port); - - /* check file set by COCKPIT_SSH_KNOWN_HOSTS_FILE */ - if (!host_known) - host_known = session_has_known_host_in_file (data->ssh_options->knownhosts_file, data, host, port); - - if (!host_known) - { - authorize_knownhosts_data = challenge_for_knownhosts_data (data); - if (authorize_knownhosts_data) - { - if (write_tmp_knownhosts_file (data, authorize_knownhosts_data, &problem)) - { - host_known = session_has_known_host_in_file (tmp_knownhost_file, data, host, port); - if (host_known) - data->ssh_options->knownhosts_file = tmp_knownhost_file; - else - g_warning ("authorize challenge reported key for %s:%u which is not known to cockpit_is_host_known()", host, port); - } - else - goto out; - } - } - - g_debug ("%s: using known hosts file %s; host known: %i; connect to unknown hosts: %i", - data->logname, data->ssh_options->knownhosts_file, host_known, data->ssh_options->connect_to_unknown_hosts); - if (!data->ssh_options->connect_to_unknown_hosts && !host_known && !is_localhost (host)) - { - g_message ("%s: refusing to connect to unknown host: %s:%d", - data->logname, host, port); - problem = "unknown-host"; - goto out; - } - - problem = NULL; -out: - g_free (authorize_knownhosts_data); - g_free (sout); - g_free (serr); - return problem; -} - -static const gchar * -verify_knownhost (CockpitSshData *data, - const gchar* host, - const guint port) -{ - const gchar *ret = "invalid-hostkey"; - ssh_key key = NULL; - unsigned char *hash = NULL; - enum ssh_known_hosts_e state; - gsize len; - - g_warn_if_fail (ssh_session_export_known_hosts_entry(data->session, &data->host_key) == SSH_OK); - if (data->host_key == NULL) - { - ret = "internal-error"; - goto done; - } - - if (ssh_get_server_publickey (data->session, &key) != SSH_OK) - { - g_warning ("Couldn't look up ssh host key"); - ret = "internal-error"; - goto done; - } - - data->host_key_type = ssh_key_type_to_char (ssh_key_type (key)); - if (data->host_key_type == NULL) - { - g_warning ("Couldn't lookup host key type"); - ret = "internal-error"; - goto done; - } - - if (ssh_get_publickey_hash (key, SSH_PUBLICKEY_HASH_SHA256, &hash, &len) < 0) - { - g_warning ("Couldn't hash ssh public key"); - ret = "internal-error"; - goto done; - } - else - { - data->host_fingerprint = ssh_get_fingerprint_hash (SSH_PUBLICKEY_HASH_SHA256, hash, len); - ssh_clean_pubkey_hash (&hash); - } - - state = ssh_session_is_known_server (data->session); - if (state == SSH_KNOWN_HOSTS_OK) - { - g_debug ("%s: verified host key", data->logname); - ret = NULL; /* success */ - goto done; - } - else if (state == SSH_KNOWN_HOSTS_ERROR) - { - g_warning ("%s: couldn't check host key: %s", data->logname, - ssh_get_error (data->session)); - ret = "internal-error"; - goto done; - } - - switch (state) - { - case SSH_KNOWN_HOSTS_OK: - case SSH_KNOWN_HOSTS_ERROR: - g_assert_not_reached (); - break; - case SSH_KNOWN_HOSTS_CHANGED: - g_message ("%s: %s host key for server has changed to: %s", - data->logname, data->host_key_type, data->host_fingerprint); - break; - case SSH_KNOWN_HOSTS_OTHER: - g_message ("%s: host key for this server changed key type: %s", - data->logname, data->host_key_type); - break; - case SSH_KNOWN_HOSTS_NOT_FOUND: - g_debug ("%s: Couldn't find the known hosts file", data->logname); - /* fall through */ - case SSH_KNOWN_HOSTS_UNKNOWN: - ret = prompt_for_host_key (data); - if (ret) - { - g_message ("%s: %s host key for server is not known: %s", - data->logname, data->host_key_type, data->host_fingerprint); - } - break; - } - -done: - if (key) - ssh_key_free (key); - return ret; -} - -static const gchar * -auth_result_string (int rc) -{ - switch (rc) - { - case SSH_AUTH_SUCCESS: - return "succeeded"; - case SSH_AUTH_DENIED: - return "denied"; - case SSH_AUTH_PARTIAL: - return "partial"; - break; - case SSH_AUTH_AGAIN: - return "again"; - default: - return "error"; - } -} - -static gchar * -parse_auth_password (const gchar *auth_type, - const gchar *auth_data) -{ - gchar *password = NULL; - - g_assert (auth_data != NULL); - g_assert (auth_type != NULL); - - if (g_strcmp0 (auth_type, "basic") == 0) - password = cockpit_authorize_parse_basic (auth_data, NULL); - else - password = g_strdup (cockpit_authorize_type (auth_data, NULL)); - - if (password == NULL) - password = g_strdup (""); - - return password; -} - -static int -do_interactive_auth (CockpitSshData *data) -{ - int rc; - gboolean sent_pw = FALSE; - gchar *password = NULL; - - password = parse_auth_password (data->auth_type, - data->initial_auth_data); - rc = ssh_userauth_kbdint (data->session, NULL, NULL); - while (rc == SSH_AUTH_INFO) - { - const gchar *msg; - int n, i; - - msg = ssh_userauth_kbdint_getinstruction (data->session); - n = ssh_userauth_kbdint_getnprompts (data->session); - - for (i = 0; i < n && rc == SSH_AUTH_INFO; i++) - { - const char *prompt; - char *answer = NULL; - char echo = '\0'; - int status = 0; - prompt = ssh_userauth_kbdint_getprompt (data->session, i, &echo); - g_debug ("%s: Got prompt %s prompt", data->logname, prompt); - if (!sent_pw) - { - status = ssh_userauth_kbdint_setanswer (data->session, i, password); - sent_pw = TRUE; - } - else - { - answer = prompt_with_authorize (data, prompt, msg, NULL, NULL, echo != '\0'); - if (answer) - status = ssh_userauth_kbdint_setanswer (data->session, i, answer); - else - rc = SSH_AUTH_ERROR; - - g_free (answer); - } - - if (status < 0) - { - g_warning ("%s: failed to set answer for %s", data->logname, prompt); - rc = SSH_AUTH_ERROR; - } - } - - if (rc == SSH_AUTH_INFO) - rc = ssh_userauth_kbdint (data->session, NULL, NULL); - } - - cockpit_memory_clear (password, strlen (password)); - g_free (password); - return rc; -} - -static int -do_password_auth (CockpitSshData *data) -{ - gchar *password = NULL; - const gchar *msg; - int rc; - - password = parse_auth_password (data->auth_type, - data->initial_auth_data); - - rc = ssh_userauth_password (data->session, NULL, password); - switch (rc) - { - case SSH_AUTH_SUCCESS: - g_debug ("%s: password auth succeeded", data->logname); - break; - case SSH_AUTH_DENIED: - g_debug ("%s: password auth failed", data->logname); - break; - case SSH_AUTH_PARTIAL: - g_message ("%s: password auth worked, but server wants more authentication", - data->logname); - break; - case SSH_AUTH_AGAIN: - g_message ("%s: password auth failed: server asked for retry", - data->logname); - break; - default: - msg = ssh_get_error (data->session); - g_message ("%s: couldn't authenticate: %s", data->logname, msg); - } - - cockpit_memory_clear (password, strlen (password)); - g_free (password); - return rc; -} - -#ifdef HAVE_SSH_USERAUTH_PUBLICKEY_AUTO_GET_CURRENT_IDENTITY - -static int -intercept_prompt (const char *prompt, char *buf, size_t len, - int echo, int verify, void *userdata) -{ - CockpitSshData *data = userdata; - char *identity = NULL; - if (ssh_userauth_publickey_auto_get_current_identity (data->session, &identity) == SSH_OK) - { - data->problem_error = g_strdup_printf ("locked identity: %s", identity); - ssh_string_free_char (identity); - } - return -1; -} - -static int -do_auto_auth (CockpitSshData *data) -{ - struct ssh_callbacks_struct cb = { .userdata = data, .auth_function = intercept_prompt }; - ssh_callbacks_init (&cb); - ssh_set_callbacks (data->session, &cb); - int rc = ssh_userauth_publickey_auto (data->session, NULL, NULL); - ssh_set_callbacks (data->session, NULL); - return rc; -} - -#else - -/* When prompting for a key passphrase, versions of libssh without - ssh_userauth_publickey_auto_get_current_identity don't provide - enough information to say which key it is for. We need that - information since Cockpit will offer to load the key into the agent - in order to log in. - - Thus, we have to reimplement ssh_userauth_publickey_auto to get the - necessary information. - - We would like to iterate over all configured identities, the same - way that the real ssh_userauth_publickey does, but there is no - API to do that either. So we hard code all the names, based on - what ssh-add would add to the agent. -*/ - -struct CockpitSshPromptData { - CockpitSshData *data; - const gchar *identity; - gboolean did_prompt; -}; - -/* We don't support unlocking identities within cockpit-ssh so fail here */ -static int -prompt_for_identity_password (const char *prompt, char *buf, size_t len, - int echo, int verify, void *userdata) -{ - struct CockpitSshPromptData *prompt_data = userdata; - prompt_data->data->problem_error = g_strdup_printf ("locked identity: %s", prompt_data->identity); - prompt_data->did_prompt = TRUE; - return -1; -} - -static int -do_auto_auth (CockpitSshData *data) -{ - - int rc; - const gchar *msg; - - rc = ssh_userauth_agent (data->session, NULL); - if (rc == SSH_AUTH_SUCCESS || - rc == SSH_AUTH_PARTIAL || - rc == SSH_AUTH_AGAIN ) { - return rc; - } - - /* See "man ssh-add" for the list of default identities. - */ - gchar *libssh_identity = NULL; - gchar *default_identities[] = { "id_dsa", "id_ecdsa", "id_ecdsa_sk", "id_ed25519", "id_ed25519_sk", "id_rsa", NULL }; - - rc = ssh_options_get (data->session, SSH_OPTIONS_IDENTITY, &libssh_identity); - if (rc != SSH_OK) - { - g_debug ("Unable to get identity from config"); - return rc; - } - - for (int i = -1; i < 0 || default_identities[i]; i++) - { - g_autofree gchar *identity = NULL; - g_autofree gchar *pub_key_path = NULL; - ssh_key priv_key = NULL; - ssh_key pub_key = NULL; - - if (i == -1) - identity = g_strdup (libssh_identity); - else - { - identity = g_strdup_printf ("%s/.ssh/%s", g_get_home_dir (), default_identities[i]); - // No need to try the libssh identity twice, and we need to - // be precious with our tries because when we run into - // MaxAuthTries, libssh will hang. - if (g_strcmp0 (identity, libssh_identity) == 0) - continue; - } - - pub_key_path = g_strconcat (identity, ".pub", NULL); - rc = ssh_pki_import_pubkey_file (pub_key_path, &pub_key); - /* If the public key file exist and is readable, see if the identity is accepted by the server */ - if (rc == SSH_OK) - { - rc = ssh_userauth_try_publickey (data->session, NULL, pub_key); - if (rc != SSH_AUTH_SUCCESS) - { - g_debug ("%s isn't accepted by the server", identity); - ssh_key_free (pub_key); - continue; - } - } - else if (rc == SSH_EOF) - { - g_debug ("Public key file %s doesn't exist or isn't readable", pub_key_path); - } - else - { - msg = ssh_get_error (data->session); - g_warning ("Error importing public key %s: %s", pub_key_path, msg); - } - - struct CockpitSshPromptData pd = { data, identity, FALSE }; - rc = ssh_pki_import_privkey_file (identity, NULL, prompt_for_identity_password, &pd, &priv_key); - if (rc == SSH_ERROR) - { - if (pd.did_prompt) - rc = SSH_AUTH_DENIED; - } - else if (rc == SSH_EOF) - { - rc = SSH_AUTH_DENIED; - } - else if (rc == SSH_OK) - { - rc = ssh_userauth_publickey (data->session, NULL, priv_key); - ssh_key_free (priv_key); - - if (rc == SSH_AUTH_SUCCESS) - { - g_debug ("%s: key auth succeeded", data->logname); - ssh_key_free (pub_key); - break; - } - else - { - switch (rc) - { - case SSH_AUTH_DENIED: - g_debug ("%s: key auth failed", data->logname); - break; - case SSH_AUTH_PARTIAL: - g_message ("%s: key auth worked, but server wants more authentication", - data->logname); - break; - case SSH_AUTH_AGAIN: - g_message ("%s: key auth failed: server asked for retry", - data->logname); - break; - default: - msg = ssh_get_error (data->session); - g_message ("%s: couldn't key authenticate: %s", data->logname, msg); - } - } - } - - ssh_key_free (pub_key); - } - - ssh_string_free_char (libssh_identity); - return rc; -} - -#endif - -static int -do_key_auth (CockpitSshData *data) -{ - int rc; - const gchar *msg; - - g_assert (data->initial_auth_data != NULL); - - rc = do_auto_auth (data); - if (rc != SSH_AUTH_SUCCESS) - { - const gchar *key_data; - ssh_key key; - - key_data = cockpit_authorize_type (data->initial_auth_data, NULL); - if (!key_data) - { - g_message ("%s: Got invalid private-key data, %s", data->logname, data->initial_auth_data); - return SSH_AUTH_DENIED; - } - - rc = ssh_pki_import_privkey_base64 (key_data, NULL, NULL, NULL, &key); - if (rc != SSH_OK) - { - g_message ("%s: Got invalid key data: %s\n%s", data->logname, ssh_get_error (data->session), data->initial_auth_data); - return rc; - } - rc = ssh_userauth_publickey (data->session, NULL, key); - ssh_key_free (key); - } - - switch (rc) - { - case SSH_AUTH_SUCCESS: - g_debug ("%s: key auth succeeded", data->logname); - break; - case SSH_AUTH_DENIED: - g_debug ("%s: key auth failed", data->logname); - break; - case SSH_AUTH_PARTIAL: - g_message ("%s: key auth worked, but server wants more authentication", - data->logname); - break; - case SSH_AUTH_AGAIN: - g_message ("%s: key auth failed: server asked for retry", - data->logname); - break; - default: - msg = ssh_get_error (data->session); - g_message ("%s: couldn't key authenticate: %s", data->logname, msg); - } - - return rc; -} - -static int -do_gss_auth (CockpitSshData *data) -{ - int rc; - const gchar *msg; - - rc = ssh_userauth_gssapi (data->session); - - switch (rc) - { - case SSH_AUTH_SUCCESS: - g_debug ("%s: gssapi auth succeeded", data->logname); - break; - case SSH_AUTH_DENIED: - g_debug ("%s: gssapi auth failed", data->logname); - break; - case SSH_AUTH_PARTIAL: - g_message ("%s: gssapi auth worked, but server wants more authentication", - data->logname); - break; - default: - msg = ssh_get_error (data->session); - g_message ("%s: couldn't authenticate: %s", data->logname, msg); - } - - return rc; -} - -static gboolean -has_password (CockpitSshData *data) -{ - if (data->auth_type == NULL && - data->initial_auth_data == NULL) - { - data->initial_auth_data = challenge_for_auth_data ("basic", &data->auth_type); - } - - return (data->initial_auth_data != NULL && - (g_strcmp0 (data->auth_type, "basic") == 0 || - g_strcmp0 (data->auth_type, "password") == 0)); -} - -static const gchar * -cockpit_ssh_authenticate (CockpitSshData *data) -{ - const gchar *problem; - gboolean have_final_result = FALSE; - gchar *description; - const gchar *msg; - int rc; - int methods_server; - int methods_tried = 0; - int methods_to_try = SSH_AUTH_METHOD_INTERACTIVE | - SSH_AUTH_METHOD_GSSAPI_MIC | - SSH_AUTH_METHOD_PUBLICKEY; - - problem = "authentication-failed"; - - rc = ssh_userauth_none (data->session, NULL); - if (rc == SSH_AUTH_ERROR) - { - g_message ("%s: server authentication handshake failed: %s", - data->logname, ssh_get_error (data->session)); - problem = "internal-error"; - goto out; - } - - if (rc == SSH_AUTH_SUCCESS) - { - problem = NULL; - goto out; - } - - methods_server = ssh_userauth_list (data->session, NULL); - - /* If interactive isn't supported try password instead */ - if (!(methods_server & SSH_AUTH_METHOD_INTERACTIVE)) - { - methods_to_try = methods_to_try | SSH_AUTH_METHOD_PASSWORD; - methods_to_try = methods_to_try & ~SSH_AUTH_METHOD_INTERACTIVE; - } - - while (methods_to_try != 0) - { - int (*auth_func)(CockpitSshData *data); - const gchar *result_string; - int method; - gboolean has_creds = FALSE; - - if (methods_to_try & SSH_AUTH_METHOD_PUBLICKEY) - { - method = SSH_AUTH_METHOD_PUBLICKEY; - if (g_strcmp0 (data->auth_type, "private-key") == 0) - { - auth_func = do_key_auth; - has_creds = data->initial_auth_data != NULL; - } - else - { - auth_func = do_auto_auth; - has_creds = TRUE; - } - } - else if (methods_to_try & SSH_AUTH_METHOD_INTERACTIVE) - { - auth_func = do_interactive_auth; - method = SSH_AUTH_METHOD_INTERACTIVE; - has_creds = has_password(data); - } - else if (methods_to_try & SSH_AUTH_METHOD_PASSWORD) - { - auth_func = do_password_auth; - method = SSH_AUTH_METHOD_PASSWORD; - has_creds = has_password(data); - } - else - { - auth_func = do_gss_auth; - method = SSH_AUTH_METHOD_GSSAPI_MIC; - has_creds = TRUE; - } - - methods_to_try = methods_to_try & ~method; - - if (!(methods_server & method)) - { - result_string = "no-server-support"; - } - else if (!has_creds) - { - result_string = "not-provided"; - methods_tried = methods_tried | method; - } - else - { - methods_tried = methods_tried | method; - if (!have_final_result) - { - rc = auth_func (data); - result_string = auth_result_string (rc); - - if (rc == SSH_AUTH_SUCCESS) - { - have_final_result = TRUE; - problem = NULL; - } - else if (rc == SSH_AUTH_ERROR) - { - have_final_result = TRUE; - msg = ssh_get_error (data->session); - g_message ("%s: couldn't authenticate: %s", data->logname, msg); - - if (ssh_msg_is_disconnected (msg)) - problem = "terminated"; - else - problem = "internal-error"; - } - } - else - { - result_string = "not-tried"; - } - } - - g_hash_table_insert (data->auth_results, - g_strdup (auth_method_description (method)), - g_strdup (result_string)); - } - - if (have_final_result) - goto out; - - if (methods_tried == 0) - { - if (methods_server == 0) - { - g_message ("%s: server offered no authentication methods", data->logname); - } - else - { - description = auth_methods_line (methods_server); - g_message ("%s: server offered unsupported authentication methods: %s", - data->logname, description); - g_free (description); - } - } - -out: - return problem; -} - -static gboolean -send_auth_reply (CockpitSshData *data, - const gchar *problem) -{ - GHashTableIter auth_iter; - JsonObject *auth_json = NULL; // consumed by object - JsonObject *object = NULL; - gboolean ret; - gpointer hkey; - gpointer hvalue; - object = json_object_new (); - auth_json = json_object_new (); - - g_assert (problem != NULL); - - json_object_set_string_member (object, "command", "init"); - if (data->host_key) - json_object_set_string_member (object, "host-key", data->host_key); - if (data->host_fingerprint) - json_object_set_string_member (object, "host-fingerprint", data->host_fingerprint); - - json_object_set_string_member (object, "problem", problem); - if (data->problem_error) - json_object_set_string_member (object, "error", data->problem_error); - else - json_object_set_string_member (object, "error", problem); - - if (data->auth_results) - { - g_hash_table_iter_init (&auth_iter, data->auth_results); - while (g_hash_table_iter_next (&auth_iter, &hkey, &hvalue)) - json_object_set_string_member (auth_json, hkey, hvalue); - } - - json_object_set_object_member (object, "auth-method-results", auth_json); - ret = write_control_message (STDOUT_FILENO, object); - json_object_unref (object); - - if (!ret) - g_message ("couldn't write authorize message: %s", g_strerror (errno)); - - return ret; -} - -static gboolean -parse_host (const gchar *host, - gchar **hostname, - gchar **username, - guint *port) -{ - GError *error = NULL; - g_autoptr (GRegex) regex = g_regex_new ("^" - "(?:(.+)@)?" /* optional username */ - "(?|" /* one of... */ - "\\[([^]@]+)\\]" /* hostname in square brackets, no @ */ - "(?::([1-9][0-9]*))?" /* optional port number */ - "|" /* or */ - "([^@:]+)" /* hostname with no : or @ */ - "(?::([1-9][0-9]*))?" /* optional port number */ - "|" /* or */ - "([^@]+)" /* hostname with no @ but : (IPv6 address), and no port */ - ")" /* . */ - "$", - 0, 0, &error); - g_assert_no_error (error); - - g_autoptr(GMatchInfo) info = NULL; - - if (g_regex_match (regex, host, 0, &info)) - { - g_autofree gchar *port_str = g_match_info_fetch (info, 3); - /* regexp makes sure that it's a positive number, so don't need much error checking */ - guint value = atoi (port_str ?: ""); - if (value < 65536) - { - *port = value; - } - else - { - g_message ("invalid port: %s", port_str); - return FALSE; - } - - *hostname = g_match_info_fetch (info, 2); - - *username = g_match_info_fetch (info, 1); - if ((*username)[0] == '\0') - { - g_free (*username); - *username = g_strdup (g_get_user_name ()); - } - - return TRUE; - } - else - { - g_message ("invalid host: %s", host); - return FALSE; - } -} - -static gchar * -username_from_basic (const gchar *basic_data) -{ - gchar *user = NULL; - gchar *password; - - password = cockpit_authorize_parse_basic (basic_data, &user); - if (password) - { - cockpit_memory_clear (password, -1); - free (password); - } - return user; -} - -static const gchar* -cockpit_ssh_connect (CockpitSshData *data, - const gchar *host_arg, - ssh_channel *out_channel) -{ - const gchar *ignore_hostkey; - gboolean host_is_whitelisted; - const gchar *problem; - g_autofree gchar *username = NULL; - - guint port = 0; - gchar *host = NULL; - - ssh_channel channel; - int rc; - - if (!parse_host (host_arg, &host, &data->username, &port)) - { - problem = "no-host"; - goto out; - } - g_debug ("%s: host argument '%s', host '%s', username '%s', port '%u'", data->logname, host_arg, host, data->username, port); - - g_warn_if_fail (ssh_options_set (data->session, SSH_OPTIONS_HOST, host) == 0); - g_warn_if_fail (ssh_options_parse_config (data->session, NULL) == 0); - - if (strrchr (host_arg, '@')) - { - g_warn_if_fail (ssh_options_set (data->session, SSH_OPTIONS_USER, data->username) == 0); - } - else if (ssh_options_get (data->session, SSH_OPTIONS_USER, &username) != 0) - { - /* User comes from auth message when using basic if it's not set in ssh config */ - if (g_strcmp0 (data->auth_type, "basic") == 0) - { - g_free (data->username); - data->username = username_from_basic (data->initial_auth_data); - } - - if (!data->username || *data->username == '\0') - { - g_message ("%s: No username provided", data->logname); - problem = "authentication-failed"; - goto out; - } - g_warn_if_fail (ssh_options_set (data->session, SSH_OPTIONS_USER, data->username) == 0); - } - - /* If the user specifies a port explicitly, overwrite the config */ - if (port != 0) - g_warn_if_fail (ssh_options_set (data->session, SSH_OPTIONS_PORT, &port) == 0); - - /* Parsing the config might have changed the host or port */ - gchar *new_host; - if (ssh_options_get (data->session, SSH_OPTIONS_HOST, &new_host) == 0) - { - g_free (host); - host = new_host; - } - g_warn_if_fail (ssh_options_get_port (data->session, &port) == 0); - - /* This is a single host, for which we have been told to ignore the host key */ - ignore_hostkey = cockpit_conf_string (COCKPIT_CONF_SSH_SECTION, "host"); - if (!ignore_hostkey) - ignore_hostkey = "127.0.0.1"; - host_is_whitelisted = g_str_equal (ignore_hostkey, host); - - if (!host_is_whitelisted) - { - problem = set_knownhosts_file (data, host, port); - if (problem != NULL) - goto out; - } - - rc = ssh_connect (data->session); - if (rc != SSH_OK) - { - g_message ("%s: %d couldn't connect: %s '%s' '%d'", data->logname, rc, - ssh_get_error (data->session), host, port); - problem = "no-host"; - goto out; - } - - g_debug ("%s: connected", data->logname); - if (!host_is_whitelisted) - { - problem = verify_knownhost (data, host, port); - if (problem != NULL) - goto out; - } - - /* The problem returned when auth failure */ - problem = cockpit_ssh_authenticate (data); - if (problem != NULL) - goto out; - - channel = ssh_channel_new (data->session); - rc = ssh_channel_open_session (channel); - if (rc != SSH_OK) - { - g_message ("%s: couldn't open session: %s", data->logname, - ssh_get_error (data->session)); - problem = "internal-error"; - goto out; - } - - if (data->ssh_options->remote_peer) - { - /* Try to set the remote peer env var, this will - * often fail as ssh servers have to be configured - * to allow it. - */ - rc = ssh_channel_request_env (channel, "COCKPIT_REMOTE_PEER", - data->ssh_options->remote_peer); - if (rc != SSH_OK) - { - g_debug ("%s: Couldn't set COCKPIT_REMOTE_PEER: %s", - data->logname, - ssh_get_error (data->session)); - } - } - - g_debug ("%s: opened channel", data->logname); - - *out_channel = channel; -out: - g_free (host); - return problem; -} - -static void -cockpit_ssh_data_free (CockpitSshData *data) -{ - if (data->initial_auth_data) - { - memset (data->initial_auth_data, 0, strlen (data->initial_auth_data)); - free (data->initial_auth_data); - } - - g_free (data->host_key); - if (data->host_fingerprint) - ssh_string_free_char (data->host_fingerprint); - - if (data->auth_results) - g_hash_table_destroy (data->auth_results); - - g_free (data->problem_error); - g_free (data->conversation); - g_free (data->username); - g_free (data->ssh_options); - g_free (data->user_known_hosts); - g_free (data->auth_type); - g_strfreev (data->env); - g_free (data); -} - - -#define COCKPIT_SSH_RELAY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), COCKPIT_TYPE_SSH_RELAY, CockpitSshRelay)) - -struct _CockpitSshRelay { - GObject parent_instance; - - CockpitSshData *ssh_data; - - gboolean sent_disconnect; - gboolean received_eof; - gboolean received_frame; - gboolean received_close; - gboolean received_exit; - - gboolean sent_close; - gboolean sent_eof; - - guint exit_code; - guint sig_read; - guint sig_close; - gboolean pipe_closed; - CockpitPipe *pipe; - - GQueue *queue; - gsize partial; - - gchar *logname; - gchar *connection_string; - - ssh_session session; - ssh_channel channel; - ssh_event event; - - GSource *io; - - struct ssh_channel_callbacks_struct channel_cbs; -}; - -struct _CockpitSshRelayClass { - GObjectClass parent_class; -}; - -static guint sig_disconnect = 0; - -enum { - PROP_0, - PROP_CONNECTION_STRING -}; - -G_DEFINE_TYPE (CockpitSshRelay, cockpit_ssh_relay, G_TYPE_OBJECT); - -static void -cockpit_ssh_relay_dispose (GObject *object) -{ - CockpitSshRelay *self = COCKPIT_SSH_RELAY (object); - - g_assert (self->ssh_data == NULL); - - if (self->sig_read > 0) - g_signal_handler_disconnect (self->pipe, self->sig_read); - self->sig_read = 0; - - if (self->sig_close > 0) - g_signal_handler_disconnect (self->pipe, self->sig_close); - self->sig_close = 0; - - if (self->io) - g_source_destroy (self->io); - - G_OBJECT_CLASS (cockpit_ssh_relay_parent_class)->dispose (object); -} - -static void -cockpit_ssh_relay_finalize (GObject *object) -{ - CockpitSshRelay *self = COCKPIT_SSH_RELAY (object); - - if (self->pipe) - g_object_unref (self->pipe); - - g_queue_free_full (self->queue, (GDestroyNotify)g_bytes_unref); - - if (self->event) - ssh_event_free (self->event); - - /* libssh channels like to hang around even after they're freed */ - if (self->channel) - memset (&self->channel_cbs, 0, sizeof (self->channel_cbs)); - - g_free (self->logname); - g_free (self->connection_string); - - if (self->io) - g_source_unref (self->io); - - ssh_disconnect (self->session); - ssh_free (self->session); - - G_OBJECT_CLASS (cockpit_ssh_relay_parent_class)->finalize (object); -} - -static gboolean -emit_disconnect (gpointer user_data) -{ - CockpitSshRelay *self = user_data; - - if (!self->sent_disconnect) - { - self->sent_disconnect = TRUE; - g_signal_emit (self, sig_disconnect, 0); - } - - return FALSE; -} - -static void -cockpit_relay_disconnect (CockpitSshRelay *self, - const gchar *problem) -{ - if (self->ssh_data) - { - send_auth_reply (self->ssh_data, problem ? problem : exit_code_problem (self->exit_code)); - cockpit_ssh_data_free (self->ssh_data); - self->ssh_data = NULL; - } - - /* libssh channels like to hang around even after they're freed */ - if (self->channel) - memset (&self->channel_cbs, 0, sizeof (self->channel_cbs)); - self->channel = NULL; - - if (self->io) - g_source_destroy (self->io); - - g_timeout_add (0, emit_disconnect, self); -} - -static int -on_channel_data (ssh_session session, - ssh_channel channel, - void *data, - uint32_t len, - int is_stderr, - void *userdata) -{ - CockpitSshRelay *self = userdata; - gint ret = 0; - guint8 *bdata = data; - - if (!self->received_frame && !is_stderr) - { - guint32 i; - - for (i = 0; i < len; i++) - { - /* Check invalid characters, prevent integer overflow, limit max length */ - if (i > 7 || bdata[i] < '0' || bdata[i] > '9') - break; - } - - /* If we don't have enough data return 0 bytes processed - * so that this data will be included in the next callback - */ - if (i == len) - goto out; - - /* - * So we may be talking to a process that's not cockpit-bridge. How does - * that happen? ssh always executes commands inside of a shell ... and - * bash prints its 'cockpit-bridge: not found' message on stdout (!) - * - * So we degrade gracefully in this case, and start to treat output as - * error output. - */ - if (bdata[i] != '\n') - { - self->exit_code = NO_COCKPIT; - } - else - { - self->received_frame = TRUE; - cockpit_ssh_data_free (self->ssh_data); - self->ssh_data = NULL; - } - } - - if (is_stderr || self->exit_code == NO_COCKPIT) - { - g_printerr ("%.*s", (int) len, bdata); - ret = len; - } - else if (self->received_frame) - { - if (!self->pipe_closed) - { - g_autoptr(GBytes) bytes = g_bytes_new (bdata, len); - cockpit_pipe_write (self->pipe, bytes); - ret = len; - } - else - { - g_debug ("%s: dropping %d incoming bytes, pipe is closed", self->logname, len); - ret = len; - } - } -out: - return ret; -} - -static void -on_channel_eof (ssh_session session, - ssh_channel channel, - void *userdata) -{ - CockpitSshRelay *self = userdata; - g_debug ("%s: received eof", self->logname); - self->received_eof = TRUE; -} - -static void -on_channel_close (ssh_session session, - ssh_channel channel, - void *userdata) -{ - CockpitSshRelay *self = userdata; - g_debug ("%s: received close", self->logname); - self->received_close = TRUE; -} - -static void -on_channel_exit_signal (ssh_session session, - ssh_channel channel, - const char *signal, - int core, - const char *errmsg, - const char *lang, - void *userdata) -{ - CockpitSshRelay *self = userdata; - guint exit_code; - g_return_if_fail (signal != NULL); - self->received_exit = TRUE; - - if (g_ascii_strcasecmp (signal, "TERM") == 0 || - g_ascii_strcasecmp (signal, "Terminated") == 0) - { - g_debug ("%s: received TERM signal", self->logname); - exit_code = TERMINATED; - } - else - { - g_warning ("%s: bridge killed%s%s%s%s", self->logname, - signal ? " by signal " : "", signal ? signal : "", - errmsg && errmsg[0] ? ": " : "", errmsg ? errmsg : ""); - exit_code = INTERNAL_ERROR; - } - - if (!self->exit_code) - self->exit_code = exit_code; - - cockpit_relay_disconnect (self, NULL); -} - -static void -on_channel_signal (ssh_session session, - ssh_channel channel, - const char *signal, - void *userdata) -{ - /* - * HACK: So it looks like libssh is buggy and is confused about - * the difference between "exit-signal" and "signal" in section 6.10 - * of the RFC. Accept signal as a usable substitute - */ - if (g_ascii_strcasecmp (signal, "TERM") == 0 || - g_ascii_strcasecmp (signal, "Terminated") == 0) - on_channel_exit_signal (session, channel, signal, 0, NULL, NULL, userdata); -} - -static void -on_channel_exit_status (ssh_session session, - ssh_channel channel, - int exit_status, - void *userdata) -{ - CockpitSshRelay *self = userdata; - guint exit_code = 0; - - self->received_exit = TRUE; - if (exit_status == 127) - { - g_debug ("%s: received exit status %d", self->logname, exit_status); - exit_code = NO_COCKPIT; /* cockpit-bridge not installed */ - } - else if (!self->received_frame) - { - g_message ("%s: spawning remote bridge failed with %d status", self->logname, exit_status); - exit_code = NO_COCKPIT; - } - else if (exit_status) - { - g_message ("%s: remote bridge exited with %d status", self->logname, exit_status); - exit_code = INTERNAL_ERROR; - } - if (!self->exit_code && exit_code) - self->exit_code = exit_code; - - cockpit_relay_disconnect (self, NULL); -} - -static gboolean -dispatch_queue (CockpitSshRelay *self) -{ - GBytes *block; - const guchar *data; - const gchar *msg; - gsize length; - gsize want; - int rc; - - if (self->sent_eof) - return FALSE; - if (self->received_close) - return FALSE; - - for (;;) - { - block = g_queue_peek_head (self->queue); - if (!block) - return FALSE; - - data = g_bytes_get_data (block, &length); - g_assert (self->partial <= length); - - want = length - self->partial; - rc = ssh_channel_write (self->channel, data + self->partial, want); - if (rc < 0) - { - msg = ssh_get_error (self->session); - if (ssh_get_error_code (self->session) == SSH_REQUEST_DENIED) - { - g_debug ("%s: couldn't write: %s", self->logname, msg); - return FALSE; - } - else if (ssh_msg_is_disconnected (msg)) - { - g_message ("%s: couldn't write: %s", self->logname, msg); - self->received_close = TRUE; - self->received_eof = TRUE; - return FALSE; - } - else - { - g_warning ("%s: couldn't write: %s", self->logname, msg); - return FALSE; - } - break; - } - - if (rc == want) - { - g_debug ("%s: wrote %d bytes", self->logname, rc); - g_queue_pop_head (self->queue); - g_bytes_unref (block); - self->partial = 0; - } - else - { - g_debug ("%s: wrote %d of %d bytes", self->logname, rc, (int)want); - g_return_val_if_fail (rc < want, FALSE); - self->partial += rc; - if (rc == 0) - break; - } - } - - return TRUE; -} - -static void -dispatch_close (CockpitSshRelay *self) -{ - g_assert (!self->sent_close); - - switch (ssh_channel_close (self->channel)) - { - case SSH_AGAIN: - g_debug ("%s: will send close later", self->logname); - break; - case SSH_OK: - g_debug ("%s: sent close", self->logname); - self->sent_close = TRUE; - break; - default: - if (ssh_get_error_code (self->session) == SSH_REQUEST_DENIED) - { - g_debug ("%s: couldn't send close: %s", self->logname, - ssh_get_error (self->session)); - self->sent_close = TRUE; /* channel is already closed */ - } - else - { - g_warning ("%s: couldn't send close: %s", self->logname, - ssh_get_error (self->session)); - self->received_exit = TRUE; - if (!self->exit_code) - self->exit_code = INTERNAL_ERROR; - cockpit_relay_disconnect (self, NULL); - } - break; - } -} - -static void -dispatch_eof (CockpitSshRelay *self) -{ - g_assert (!self->sent_eof); - - switch (ssh_channel_send_eof (self->channel)) - { - case SSH_AGAIN: - g_debug ("%s: will send eof later", self->logname); - break; - case SSH_OK: - g_debug ("%s: sent eof", self->logname); - self->sent_eof = TRUE; - break; - default: - if (ssh_get_error_code (self->session) == SSH_REQUEST_DENIED) - { - g_debug ("%s: couldn't send eof: %s", self->logname, - ssh_get_error (self->session)); - self->sent_eof = TRUE; /* channel is already closed */ - } - else - { - g_warning ("%s: couldn't send eof: %s", self->logname, - ssh_get_error (self->session)); - self->received_exit = TRUE; - if (!self->exit_code) - self->exit_code = INTERNAL_ERROR; - cockpit_relay_disconnect (self, NULL); - } - break; - } -} - -static void -on_pipe_read (CockpitPipe *pipe, - GByteArray *input, - gboolean end_of_data, - gpointer user_data) -{ - CockpitSshRelay *self = user_data; - GByteArray *buf = NULL; - - buf = cockpit_pipe_get_buffer (pipe); - g_byte_array_ref (buf); - - if (!self->sent_eof && !self->received_close && buf->len > 0) - { - g_debug ("%s: queued %d bytes", self->logname, buf->len); - g_queue_push_tail (self->queue, g_byte_array_free_to_bytes (buf)); - } - else - { - g_debug ("%s: dropping %d bytes", self->logname, buf->len); - g_byte_array_free (buf, TRUE); - } - - if (end_of_data) - cockpit_pipe_close (pipe, NULL); -} - -static void -on_pipe_close (CockpitPipe *pipe, - const gchar *problem, - gpointer user_data) -{ - CockpitSshRelay *self = user_data; - - self->pipe_closed = TRUE; - // Pipe closing before data was received doesn't mean no-cockpit - self->received_frame = TRUE; - - if (!self->received_eof) - dispatch_eof (self); - - cockpit_relay_disconnect (self, NULL); -} - -typedef struct { - GSource source; - GPollFD pfd; - CockpitSshRelay *relay; -} CockpitSshSource; - -static gboolean -cockpit_ssh_source_check (GSource *source) -{ - CockpitSshSource *cs = (CockpitSshSource *)source; - return (cs->pfd.events & cs->pfd.revents) != 0; -} - -static gboolean -cockpit_ssh_source_prepare (GSource *source, - gint *timeout) -{ - CockpitSshSource *cs = (CockpitSshSource *)source; - CockpitSshRelay *self = cs->relay; - gint status; - - *timeout = 1; - - status = ssh_get_status (self->session); - - cs->pfd.revents = 0; - cs->pfd.events = G_IO_IN | G_IO_ERR | G_IO_NVAL | G_IO_HUP; - - /* libssh has something in its buffer: want to write */ - if (status & SSH_WRITE_PENDING) - cs->pfd.events |= G_IO_OUT; - - /* We have something in our queue: want to write */ - else if (!g_queue_is_empty (self->queue)) - cs->pfd.events |= G_IO_OUT; - - /* We are closing and need to send eof: want to write */ - else if (self->pipe_closed && !self->sent_eof) - cs->pfd.events |= G_IO_OUT; - - /* Need to reply to an EOF or close */ - if ((self->received_eof && self->sent_eof && !self->sent_close) || - (self->received_close && !self->sent_close)) - cs->pfd.events |= G_IO_OUT; - - return cockpit_ssh_source_check (source); -} - -static gboolean -cockpit_ssh_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - CockpitSshSource *cs = (CockpitSshSource *)source; - int rc; - const gchar *msg; - gboolean ret = TRUE; - CockpitSshRelay *self = cs->relay; - GIOCondition cond = cs->pfd.revents; - - if (cond & (G_IO_HUP | G_IO_ERR)) - { - if (self->sent_close || self->sent_eof) - { - self->received_eof = TRUE; - self->received_close = TRUE; - } - } - - if (self->received_exit) - return FALSE; - - g_return_val_if_fail ((cond & G_IO_NVAL) == 0, FALSE); - - /* - * HACK: Yes this is another poll() call. The async support in - * libssh is quite hacky right now. - * - * https://red.libssh.org/issues/155 - */ - rc = ssh_event_dopoll (self->event, 0); - switch (rc) - { - case SSH_OK: - case SSH_AGAIN: - break; - case SSH_ERROR: - msg = ssh_get_error (self->session); - - /* - * HACK: There doesn't seem to be a way to get at the original socket errno - * here. So we have to screen scrape. - * - * https://red.libssh.org/issues/158 - */ - if (ssh_msg_is_disconnected (msg)) - { - g_debug ("%s: failed to process channel: %s", self->logname, msg); - self->received_exit = TRUE; - if (!self->exit_code) - self->exit_code = TERMINATED; - } - else - { - g_message ("%s: failed to process channel: %s", self->logname, msg); - self->received_exit = TRUE; - if (!self->exit_code) - self->exit_code = INTERNAL_ERROR; - } - ret = FALSE; - break; - default: - self->received_exit = TRUE; - if (!self->exit_code) - self->exit_code = INTERNAL_ERROR; - g_critical ("%s: ssh_event_dopoll() returned %d", self->logname, rc); - ret = FALSE; - } - - if (!ret) - goto out; - - if (cond & G_IO_ERR) - { - g_message ("%s: error reading from ssh", self->logname); - ret = FALSE; - self->received_exit = TRUE; - if (!self->exit_code) - self->exit_code = DISCONNECTED; - goto out; - } - - if (cond & G_IO_OUT) - { - if (!dispatch_queue (self) && self->pipe_closed && !self->sent_eof) - dispatch_eof (self); - if (self->received_eof && self->sent_eof && !self->sent_close) - dispatch_close (self); - if (self->received_eof && !self->received_close && !self->sent_close) - dispatch_close (self); - } - -out: - if (self->received_exit) - cockpit_relay_disconnect (self, NULL); - return ret; -} - -static GSource * -cockpit_ssh_relay_start_source (CockpitSshRelay *self) { - static GSourceFuncs source_funcs = { - cockpit_ssh_source_prepare, - cockpit_ssh_source_check, - cockpit_ssh_source_dispatch, - NULL, - }; - GSource *source = g_source_new (&source_funcs, sizeof (CockpitSshSource)); - CockpitSshSource *cs = (CockpitSshSource *)source; - cs->relay = self; - cs->pfd.fd = ssh_get_fd (self->session); - g_source_add_poll (source, &cs->pfd); - g_source_attach (source, g_main_context_default ()); - - return source; -} - -static void -cockpit_ssh_relay_start (CockpitSshRelay *self) -{ - const gchar *problem; - int in; - int out; - int rc; - - static struct ssh_channel_callbacks_struct channel_cbs = { - .channel_data_function = on_channel_data, - .channel_eof_function = on_channel_eof, - .channel_close_function = on_channel_close, - .channel_signal_function = on_channel_signal, - .channel_exit_signal_function = on_channel_exit_signal, - .channel_exit_status_function = on_channel_exit_status, - }; - - self->ssh_data->initial_auth_data = challenge_for_auth_data ("*", &self->ssh_data->auth_type); - - problem = cockpit_ssh_connect (self->ssh_data, self->connection_string, &self->channel); - if (problem) - goto out; - - self->event = ssh_event_new (); - memcpy (&self->channel_cbs, &channel_cbs, sizeof (channel_cbs)); - self->channel_cbs.userdata = self; - ssh_callbacks_init (&self->channel_cbs); - ssh_set_channel_callbacks (self->channel, &self->channel_cbs); - ssh_set_blocking (self->session, 0); - ssh_event_add_session (self->event, self->session); - - in = dup (0); - g_assert (in >= 0); - out = dup (1); - g_assert (out >= 0); - - self->pipe = g_object_new (COCKPIT_TYPE_PIPE, - "in-fd", in, - "out-fd", out, - "name", self->logname, - NULL); - self->sig_read = g_signal_connect (self->pipe, - "read", - G_CALLBACK (on_pipe_read), - self); - self->sig_close = g_signal_connect (self->pipe, - "close", - G_CALLBACK (on_pipe_close), - self); - - for (rc = SSH_AGAIN; rc == SSH_AGAIN; ) - rc = ssh_channel_request_exec (self->channel, self->ssh_data->ssh_options->command); - - if (rc != SSH_OK) - { - g_message ("%s: couldn't execute command: %s: %s", self->logname, - self->ssh_data->ssh_options->command, - ssh_get_error (self->session)); - problem = "internal-error"; - goto out; - } - - self->io = cockpit_ssh_relay_start_source (self); - -out: - if (problem) - { - self->exit_code = AUTHENTICATION_FAILED; - cockpit_relay_disconnect (self, problem); - } -} - -static void -cockpit_ssh_relay_init (CockpitSshRelay *self) -{ - const gchar *debug; - - ssh_init (); - - self->queue = g_queue_new (); - debug = g_getenv ("G_MESSAGES_DEBUG"); - - if (debug && (strstr (debug, "libssh") || g_strcmp0 (debug, "all") == 0)) - ssh_set_log_level (SSH_LOG_FUNCTIONS); -} - -static void -cockpit_ssh_relay_set_property (GObject *obj, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - CockpitSshRelay *self = COCKPIT_SSH_RELAY (obj); - - switch (prop_id) - { - case PROP_CONNECTION_STRING: - self->connection_string = g_value_dup_string (value); - self->logname = g_strdup_printf ("cockpit-ssh %s", self->connection_string); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); - break; - } -} - -static void -cockpit_ssh_relay_constructed (GObject *object) -{ - CockpitSshRelay *self = COCKPIT_SSH_RELAY (object); - - G_OBJECT_CLASS (cockpit_ssh_relay_parent_class)->constructed (object); - - self->session = ssh_new (); - self->ssh_data = g_new0 (CockpitSshData, 1); - self->ssh_data->env = g_get_environ (); - self->ssh_data->session = self->session; - self->ssh_data->logname = self->logname; - self->ssh_data->auth_results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - self->ssh_data->ssh_options = cockpit_ssh_options_from_env (self->ssh_data->env); - self->ssh_data->user_known_hosts = g_build_filename (g_get_home_dir (), ".ssh/known_hosts", NULL); -} - -static void -authorize_logger (const char *data) -{ - g_message ("%s", data); -} - -static void -cockpit_ssh_relay_class_init (CockpitSshRelayClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->dispose = cockpit_ssh_relay_dispose; - object_class->finalize = cockpit_ssh_relay_finalize; - object_class->constructed = cockpit_ssh_relay_constructed; - object_class->set_property = cockpit_ssh_relay_set_property; - - g_object_class_install_property (object_class, PROP_CONNECTION_STRING, - g_param_spec_string ("connection-string", NULL, NULL, "localhost", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - sig_disconnect = g_signal_new ("disconnect", COCKPIT_TYPE_SSH_RELAY, - G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); - - cockpit_authorize_logger (authorize_logger, 0); -} - -CockpitSshRelay * -cockpit_ssh_relay_new (const gchar *connection_string) -{ - - CockpitSshRelay *self = g_object_new (COCKPIT_TYPE_SSH_RELAY, - "connection-string", connection_string, - NULL); - cockpit_ssh_relay_start (self); - return self; -} - -gint -cockpit_ssh_relay_result (CockpitSshRelay* self) -{ - return self->exit_code; -} diff --git a/src/ssh/cockpitsshrelay.h b/src/ssh/cockpitsshrelay.h deleted file mode 100644 index 5cc64be9550..00000000000 --- a/src/ssh/cockpitsshrelay.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2017 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#ifndef __COCKPIT_SSH_RELAY_H__ -#define __COCKPIT_SSH_RELAY_H__ - -#include -#include - -G_BEGIN_DECLS - -/* EXIT CODE CONSTANTS */ -#define INTERNAL_ERROR 1 -#define AUTHENTICATION_FAILED 2 -#define DISCONNECTED 254 -#define TERMINATED 255 -#define NO_COCKPIT 127 - -#define COCKPIT_TYPE_SSH_RELAY (cockpit_ssh_relay_get_type ()) - -typedef struct _CockpitSshRelay CockpitSshRelay; -typedef struct _CockpitSshRelay CockpitSshRelayClass; - -GType cockpit_ssh_relay_get_type (void) G_GNUC_CONST; - -CockpitSshRelay * cockpit_ssh_relay_new (const gchar *connection_string); - -gint cockpit_ssh_relay_result (CockpitSshRelay* self); - -G_END_DECLS - -#endif diff --git a/src/ssh/invalid_known_hosts b/src/ssh/invalid_known_hosts deleted file mode 100644 index a84193e67d4..00000000000 --- a/src/ssh/invalid_known_hosts +++ /dev/null @@ -1 +0,0 @@ -[localhost]:*,[127.0.0.1]:* ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+Q8SP0Zf0jS//d3EJQHvdOA5iPGRcH+bZ5wqdRiZi0TCCKsCicKBlEwC8HtRo/l7pT+k0Je5n5SBLTJTq2oRcPsxDhhmKI1TVFot7+BDbU0THbuGEIZNk4spxJ84W/Znvs5RxdKinY9nFDB5hofX5EHuGiM53tlyE3+0gHG95rklKD/TAsLwubrmCkJkLEbZM2B/Kai+fKtgHjgefASbiMIxTi4/ryHT4Pb3tRLihbBylyoTZLgs7FrOPDUGu+e2mrC2rrbi1RvVBVgzF0ekJHtC7zjevddKlLgHCuc6SztJuDKC0eIqCiBjleG7tmqxM4O3PIn3nRgPSxydjjWod diff --git a/src/ssh/mock-config/cockpit/cockpit.conf b/src/ssh/mock-config/cockpit/cockpit.conf deleted file mode 100644 index 1e952404cbe..00000000000 --- a/src/ssh/mock-config/cockpit/cockpit.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Ssh-Login] -host = 127.0.0.2 diff --git a/src/ssh/mock-pid-cat b/src/ssh/mock-pid-cat deleted file mode 100755 index ece40232d88..00000000000 --- a/src/ssh/mock-pid-cat +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -# Here we send our PID as a message on channel '11x', which we happen to know will be -# 31 bytes long. After that we echo anything sent - -/usr/bin/printf '37\n\n{ \"command\" : \"init\", \"version\": 1 }31\n11x\n{ "pid": % 16s }' $$ -exec /bin/cat diff --git a/src/ssh/mock-sshd.c b/src/ssh/mock-sshd.c deleted file mode 100644 index 476a269b63d..00000000000 --- a/src/ssh/mock-sshd.c +++ /dev/null @@ -1,1096 +0,0 @@ -/* Based on the sample implementation of a libssh based SSH server: - - https://git.libssh.org/projects/libssh.git/plain/examples/ssh_server.c?id=23cebfadea156aea377462eaf5971955c77d2d61 - - The main changes are: - - - Command line options and server configuration have been changed - to match our established mock-sshd conventions. - - - The port is printed on stdout and stdout is closed afterwards. - - - The specific interactive authorization that is expected by the - tests is implemented by hooking into the message callbacks. - There doesn't seem to be a dedicated callback for this. - - - If this child exits with a signal, this is also reported back. - - - Dead locks while writing to the child stdin are avoided by - polling for writability. -*/ - -/* -Copyright 2014 Audrius Butkevicius - -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. -*/ - -#include "config.h" -#define HAVE_ARGP_H -#define HAVE_PTY_H -#define HAVE_UTMP_H -#define WITH_FORK - -#include -#include - -#include -#ifdef HAVE_ARGP_H -#include -#endif -#include -#ifdef HAVE_LIBUTIL_H -#include -#endif -#include -#ifdef HAVE_PTY_H -#include -#endif -#include -#include -#ifdef HAVE_UTMP_H -#include -#endif -#ifdef HAVE_UTIL_H -#include -#endif -#include -#include -#include -#include -#include - -#ifndef BUF_SIZE -#define BUF_SIZE 1048576 -#endif - -#ifndef KEYS_FOLDER -#ifdef _WIN32 -#define KEYS_FOLDER -#else -#define KEYS_FOLDER "/etc/ssh/" -#endif -#endif - -#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR) -#define SFTP_SERVER_PATH "/usr/lib/sftp-server" - -#define DEF_STR_SIZE 1024 -bool broken_auth = false; -bool multi_step = false; -char authorizedkeys[DEF_STR_SIZE] = {0}; -char username[128] = "myuser"; -char password[128] = "mypassword"; -#ifdef HAVE_ARGP_H -const char *argp_program_version = "libssh server example " -SSH_STRINGIFY(LIBSSH_VERSION); -const char *argp_program_bug_address = ""; - -/* Program documentation. */ -static char doc[] = "libssh -- a Secure Shell protocol implementation"; - -/* 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 = "bind", - .key = 'b', - .arg = "BIND", - .flags = 0, - .doc = "Set the address to bind.", - .group = 0 - }, - { - .name = "hostkey", - .key = 'k', - .arg = "FILE", - .flags = 0, - .doc = "Set a host key. Can be used multiple times. " - "Implies no default keys.", - .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 = "ecdsakey", - .key = 'e', - .arg = "FILE", - .flags = 0, - .doc = "Set the ecdsa key.", - .group = 0 - }, - { - .name = "import-pubkey", - .key = 'a', - .arg = "FILE", - .flags = 0, - .doc = "Set the authorized keys file.", - .group = 0 - }, - { - .name = "user", - .key = 'u', - .arg = "USERNAME", - .flags = 0, - .doc = "Set expected username.", - .group = 0 - }, - { - .name = "password", - .key = 'P', - .arg = "PASSWORD", - .flags = 0, - .doc = "Set expected password.", - .group = 0 - }, - { - .name = "broken-auth", - .key = 't', - .arg = NULL, - .flags = 0, - .doc = "Break authentication", - .group = 0 - }, - { - .name = "multi-step", - .key = 'm', - .arg = NULL, - .flags = 0, - .doc = "Multi Step Auth", - .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. */ -static error_t parse_opt (int key, char *arg, struct argp_state *state) { - /* 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 'b': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, 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 'e': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, arg); - break; - case 'a': - strncpy(authorizedkeys, arg, DEF_STR_SIZE-1); - break; - case 'u': - strncpy(username, arg, sizeof(username) - 1); - break; - case 'P': - strncpy(password, arg, sizeof(password) - 1); - break; - case 't': - broken_auth = true; - break; - case 'm': - multi_step = true; - break; - case 'v': - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, - "3"); - break; - case ARGP_KEY_ARG: - /* Too many arguments. */ - argp_usage (state); - break; - case ARGP_KEY_END: - break; - default: - return ARGP_ERR_UNKNOWN; - } - return 0; -} - -/* Our argp parser. */ -static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; -#else -static int parse_opt(int argc, char **argv, ssh_bind sshbind) { - int no_default_keys = 0; - int rsa_already_set = 0; - int dsa_already_set = 0; - int ecdsa_already_set = 0; - int key; - - while((key = getopt(argc, argv, "a:d:e:k:np:P:r:u:v")) != -1) { - if (key == 'n') { - no_default_keys = 1; - } else if (key == 'p') { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, optarg); - } else if (key == 'd') { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_DSAKEY, optarg); - dsa_already_set = 1; - } else if (key == 'k') { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg); - /* We can't track the types of keys being added with this - option, so let's ensure we keep the keys we're adding - by just not setting the default keys */ - no_default_keys = 1; - } else if (key == 'r') { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, optarg); - rsa_already_set = 1; - } else if (key == 'e') { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_ECDSAKEY, optarg); - ecdsa_already_set = 1; - } else if (key == 'a') { - strncpy(authorizedkeys, optarg, DEF_STR_SIZE-1); - } else if (key == 'u') { - strncpy(username, optarg, sizeof(username) - 1); - } else if (key == 'P') { - strncpy(password, optarg, sizeof(password) - 1); - } else if (key == 'v') { - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_LOG_VERBOSITY_STR, - "3"); - } else { - break; - } - } - - if (key != -1) { - printf("Usage: %s [OPTION...] BINDADDR\n" - "libssh %s -- a Secure Shell protocol implementation\n" - "\n" - " -a, --authorizedkeys=FILE Set the authorized keys file.\n" - " -d, --dsakey=FILE Set the dsa key.\n" - " -e, --ecdsakey=FILE Set the ecdsa key.\n" - " -k, --hostkey=FILE Set a host key. Can be used multiple times.\n" - " Implies no default keys.\n" - " -n, --no-default-keys Do not set default key locations.\n" - " -p, --port=PORT Set the port to bind.\n" - " -P, --pass=PASSWORD Set expected password.\n" - " -r, --rsakey=FILE Set the rsa key.\n" - " -u, --user=USERNAME Set expected username.\n" - " -v, --verbose Get verbose output.\n" - " -?, --help Give this help list\n" - "\n" - "Mandatory or optional arguments to long options are also mandatory or optional\n" - "for any corresponding short options.\n" - "\n" - "Report bugs to .\n", - argv[0], SSH_STRINGIFY(LIBSSH_VERSION)); - return -1; - } - - if (optind != argc - 1) { - printf("Usage: %s [OPTION...] BINDADDR\n", argv[0]); - return -1; - } - - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDADDR, argv[optind]); - - if (!no_default_keys) { - set_default_keys(sshbind, - rsa_already_set, - dsa_already_set, - ecdsa_already_set); - } - - return 0; -} -#endif /* HAVE_ARGP_H */ - -/* A userdata struct for channel. */ -struct channel_data_struct { - /* pid of the child process the channel will spawn. */ - pid_t pid; - /* For PTY allocation */ - socket_t pty_master; - socket_t pty_slave; - /* For communication with the child process. */ - socket_t child_stdin; - socket_t child_stdout; - /* Only used for subsystem and exec requests. */ - socket_t child_stderr; - /* Event which is used to poll the above descriptors. */ - ssh_event event; - /* Terminal size struct. */ - struct winsize *winsize; - /* Data we want to send */ - uint8_t *stdin_buf; - uint32_t stdin_len; -}; - -/* A userdata struct for session. */ -struct session_data_struct { - /* Pointer to the channel the session will allocate. */ - ssh_channel channel; - int auth_attempts; - int authenticated; - int multi_step_state; -}; - -static int process_child_stdin(socket_t fd, int revents, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - if (cdata->stdin_len == 0) - return 0; - - fcntl (cdata->child_stdin, F_SETFL, O_NONBLOCK); - int ret = write(cdata->child_stdin, cdata->stdin_buf, cdata->stdin_len); - if (ret > 0) { - memmove (cdata->stdin_buf, cdata->stdin_buf + ret, cdata->stdin_len - ret); - cdata->stdin_len -= ret; - if (cdata->stdin_len == 0) - ssh_event_remove_fd(cdata->event, cdata->child_stdin); - } - - return 0; -} - -static void queue_child_stdin(struct channel_data_struct *cdata, void *data, uint32_t len) -{ - fcntl (cdata->child_stdin, F_SETFL, O_NONBLOCK); - int ret = write(cdata->child_stdin, data, len); - if (ret < 0) { - if (errno == EAGAIN) { - ret = 0; - } else { - perror("write"); - exit(1); - } - } - if (ret >= 0 && ret < len) { - len -= ret; - data = (uint8_t *)data + ret; - cdata->stdin_buf = realloc (cdata->stdin_buf, cdata->stdin_len + len); - memcpy (cdata->stdin_buf + cdata->stdin_len, data, len); - if (cdata->stdin_len == 0) - ssh_event_add_fd(cdata->event, cdata->child_stdin, POLLOUT, process_child_stdin, cdata); - cdata->stdin_len += len; - } -} - -static int data_function(ssh_session session, ssh_channel channel, void *data, - uint32_t len, int is_stderr, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - (void) session; - (void) channel; - (void) is_stderr; - - if (len == 0 || cdata->pid < 1 || kill(cdata->pid, 0) < 0) { - return 0; - } - - queue_child_stdin (cdata, data, len); - return len; -} - -static int pty_request(ssh_session session, ssh_channel channel, - const char *term, int cols, int rows, int py, int px, - void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; - - (void) session; - (void) channel; - (void) term; - - cdata->winsize->ws_row = rows; - cdata->winsize->ws_col = cols; - cdata->winsize->ws_xpixel = px; - cdata->winsize->ws_ypixel = py; - - if (openpty(&cdata->pty_master, &cdata->pty_slave, NULL, NULL, - cdata->winsize) != 0) { - fprintf(stderr, "Failed to open pty\n"); - return SSH_ERROR; - } - return SSH_OK; -} - -static int pty_resize(ssh_session session, ssh_channel channel, int cols, - int rows, int py, int px, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *)userdata; - - (void) session; - (void) channel; - - cdata->winsize->ws_row = rows; - cdata->winsize->ws_col = cols; - cdata->winsize->ws_xpixel = px; - cdata->winsize->ws_ypixel = py; - - if (cdata->pty_master != -1) { - return ioctl(cdata->pty_master, TIOCSWINSZ, cdata->winsize); - } - - return SSH_ERROR; -} - -static int exec_pty(const char *mode, const char *command, - struct channel_data_struct *cdata) { - switch(cdata->pid = fork()) { - case -1: - close(cdata->pty_master); - close(cdata->pty_slave); - fprintf(stderr, "Failed to fork\n"); - return SSH_ERROR; - case 0: - close(cdata->pty_master); - if (login_tty(cdata->pty_slave) != 0) { - exit(1); - } - execl("/bin/sh", "sh", mode, command, NULL); - exit(0); - default: - close(cdata->pty_slave); - /* pty fd is bi-directional */ - cdata->child_stdout = cdata->child_stdin = cdata->pty_master; - } - return SSH_OK; -} - -static int exec_nopty(const char *command, struct channel_data_struct *cdata) { - int in[2], out[2], err[2]; - - /* Do the plumbing to be able to talk with the child process. */ - if (pipe(in) != 0) { - goto stdin_failed; - } - if (pipe(out) != 0) { - goto stdout_failed; - } - if (pipe(err) != 0) { - goto stderr_failed; - } - - switch(cdata->pid = fork()) { - case -1: - goto fork_failed; - case 0: - /* Finish the plumbing in the child process. */ - close(in[1]); - close(out[0]); - close(err[0]); - dup2(in[0], STDIN_FILENO); - dup2(out[1], STDOUT_FILENO); - dup2(err[1], STDERR_FILENO); - close(in[0]); - close(out[1]); - close(err[1]); - /* exec the requested command. */ - execl("/bin/sh", "sh", "-c", command, NULL); - exit(0); - } - - close(in[0]); - close(out[1]); - close(err[1]); - - cdata->child_stdin = in[1]; - cdata->child_stdout = out[0]; - cdata->child_stderr = err[0]; - - return SSH_OK; - -fork_failed: - close(err[0]); - close(err[1]); -stderr_failed: - close(out[0]); - close(out[1]); -stdout_failed: - close(in[0]); - close(in[1]); -stdin_failed: - return SSH_ERROR; -} - -static int exec_request(ssh_session session, ssh_channel channel, - const char *command, void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - - (void) session; - (void) channel; - - if(cdata->pid > 0) { - return SSH_ERROR; - } - - if (cdata->pty_master != -1 && cdata->pty_slave != -1) { - return exec_pty("-c", command, cdata); - } - return exec_nopty(command, cdata); -} - -static int shell_request(ssh_session session, ssh_channel channel, - void *userdata) { - struct channel_data_struct *cdata = (struct channel_data_struct *) userdata; - - (void) session; - (void) channel; - - if(cdata->pid > 0) { - return SSH_ERROR; - } - - if (cdata->pty_master != -1 && cdata->pty_slave != -1) { - return exec_pty("-l", NULL, cdata); - } - /* Client requested a shell without a pty, let's pretend we allow that */ - return SSH_OK; -} - -static int subsystem_request(ssh_session session, ssh_channel channel, - const char *subsystem, void *userdata) { - /* subsystem requests behave simillarly to exec requests. */ - if (strcmp(subsystem, "sftp") == 0) { - return exec_request(session, channel, SFTP_SERVER_PATH, userdata); - } - return SSH_ERROR; -} - -static int auth_password(ssh_session session, const char *user, - const char *pass, void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - (void) session; - - if (strcmp(user, username) == 0 && strcmp(pass, password) == 0) { - sdata->authenticated = 1; - return SSH_AUTH_SUCCESS; - } - - sdata->auth_attempts++; - return SSH_AUTH_DENIED; -} - -static int auth_publickey(ssh_session session, - const char *user, - struct ssh_key_struct *pubkey, - char signature_state, - void *userdata) -{ - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - (void) user; - (void) session; - - if (signature_state == SSH_PUBLICKEY_STATE_NONE) { - return SSH_AUTH_SUCCESS; - } - - if (signature_state != SSH_PUBLICKEY_STATE_VALID) { - return SSH_AUTH_DENIED; - } - - // valid so far. Now look through authorized keys for a match - if (authorizedkeys[0]) { - ssh_key key = NULL; - int result; - struct stat buf; - - if (stat(authorizedkeys, &buf) == 0) { - result = ssh_pki_import_pubkey_file( authorizedkeys, &key ); - if ((result != SSH_OK) || (key==NULL)) { - fprintf(stderr, - "Unable to import public key file %s\n", - authorizedkeys); - } else { - result = ssh_key_cmp( key, pubkey, SSH_KEY_CMP_PUBLIC ); - ssh_key_free(key); - if (result == 0) { - sdata->authenticated = 1; - return SSH_AUTH_SUCCESS; - } - } - } - } - - // no matches - sdata->authenticated = 0; - return SSH_AUTH_DENIED; -} - -static int -auth_message_callback (ssh_session session, - ssh_message message, - void *user_data) -{ - static const char *prompts[2] = { "Password", "Token" }; - static char echo[] = { 0, 1 }; - static const char *again[1] = { "So Close" }; - static char again_echo[] = { 0 }; - - struct session_data_struct *sdata = (struct session_data_struct *) user_data; - - if (ssh_message_type (message) != SSH_REQUEST_AUTH - || ssh_message_subtype (message) != SSH_AUTH_METHOD_INTERACTIVE) - return 1; - - switch (sdata->multi_step_state) { - case 1: - if (strcmp (ssh_message_auth_user (message), username) == 0) - { - ssh_message_auth_interactive_request (message, "Test Interactive", - "Password and Token", - 2, prompts, echo); - sdata->multi_step_state = 2; - return 0; - } - else - return 1; - break; - - case 2: - if (ssh_userauth_kbdint_getnanswers(session) != 2) - break; - - if (strcmp (ssh_userauth_kbdint_getanswer(session, 0), password) != 0) - break; - - if (strcmp (ssh_userauth_kbdint_getanswer(session, 1), "5") == 0) { - ssh_message_auth_reply_success (message, 0); - sdata->authenticated = 1; - return 0; - } else if (strcmp (ssh_userauth_kbdint_getanswer(session, 1), "6") == 0) { - ssh_message_auth_interactive_request (message, "Test Interactive", - "Again", 1, again, again_echo); - sdata->multi_step_state = 3; - return 0; - } - - break; - - case 3: - if (ssh_userauth_kbdint_getnanswers(session) != 1) - break; - - if (strcmp (ssh_userauth_kbdint_getanswer(session, 0), "5") == 0) { - ssh_message_auth_reply_success (message, 0); - sdata->authenticated = 1; - return 0; - } - - break; - } - - return 1; -} - -static ssh_channel channel_open(ssh_session session, void *userdata) { - struct session_data_struct *sdata = (struct session_data_struct *) userdata; - - sdata->channel = ssh_channel_new(session); - return sdata->channel; -} - -static int process_stdout(socket_t fd, int revents, void *userdata) { - char buf[BUF_SIZE]; - int n = -1; - ssh_channel channel = (ssh_channel) userdata; - - if (channel != NULL && (revents & POLLIN) != 0) { - n = read(fd, buf, BUF_SIZE); - if (n > 0) { - ssh_channel_write(channel, buf, n); - } - } - - return n; -} - -static int process_stderr(socket_t fd, int revents, void *userdata) { - char buf[BUF_SIZE]; - int n = -1; - ssh_channel channel = (ssh_channel) userdata; - - if (channel != NULL && (revents & POLLIN) != 0) { - n = read(fd, buf, BUF_SIZE); - if (n > 0) { - ssh_channel_write_stderr(channel, buf, n); - } - } - - return n; -} - -static void handle_session(ssh_event event, ssh_session session) { - int n; - int rc = 0; - - /* Structure for storing the pty size. */ - struct winsize wsize = { - .ws_row = 0, - .ws_col = 0, - .ws_xpixel = 0, - .ws_ypixel = 0 - }; - - /* Our struct holding information about the channel. */ - struct channel_data_struct cdata = { - .pid = 0, - .pty_master = -1, - .pty_slave = -1, - .child_stdin = -1, - .child_stdout = -1, - .child_stderr = -1, - .event = NULL, - .winsize = &wsize, - .stdin_buf = NULL, - .stdin_len = 0, - }; - - /* Our struct holding information about the session. */ - struct session_data_struct sdata = { - .channel = NULL, - .auth_attempts = 0, - .authenticated = 0, - .multi_step_state = 1, - }; - - struct ssh_channel_callbacks_struct channel_cb = { - .userdata = &cdata, - .channel_pty_request_function = pty_request, - .channel_pty_window_change_function = pty_resize, - .channel_shell_request_function = shell_request, - .channel_exec_request_function = exec_request, - .channel_data_function = data_function, - .channel_subsystem_request_function = subsystem_request - }; - - struct ssh_server_callbacks_struct server_cb = { - .userdata = &sdata, - .auth_password_function = auth_password, - .channel_open_request_session_function = channel_open, - }; - - int auth_methods = SSH_AUTH_METHOD_PASSWORD; - if (broken_auth) { - auth_methods = SSH_AUTH_METHOD_HOSTBASED; - } else { - if (authorizedkeys[0]) { - server_cb.auth_pubkey_function = auth_publickey; - auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - if (multi_step) - auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; - } - - ssh_set_auth_methods (session, auth_methods); - - ssh_callbacks_init(&server_cb); - ssh_callbacks_init(&channel_cb); - - /* The server callbacks handle password and publickey - authentication, the message callback handles interactive - authentication. - */ - ssh_set_server_callbacks(session, &server_cb); - ssh_set_message_callback (session, auth_message_callback, &sdata); - - if (ssh_handle_key_exchange(session) != SSH_OK) { - fprintf(stderr, "%s\n", ssh_get_error(session)); - return; - } - - ssh_event_add_session(event, session); - - n = 0; - while (sdata.authenticated == 0 || sdata.channel == NULL) { - /* If the user has used up all attempts, or if he hasn't been able to - * authenticate in 10 seconds (n * 100ms), disconnect. */ - if (sdata.auth_attempts >= 3 || n >= 100) { - return; - } - - if (ssh_event_dopoll(event, 100) == SSH_ERROR) { - fprintf(stderr, "%s\n", ssh_get_error(session)); - return; - } - n++; - } - - ssh_set_channel_callbacks(sdata.channel, &channel_cb); - ssh_set_message_callback (session, NULL, NULL); - - do { - /* Poll the main event which takes care of the session, the channel and - * even our child process's stdout/stderr (once it's started). */ - if (ssh_event_dopoll(event, -1) == SSH_ERROR) { - ssh_channel_close(sdata.channel); - } - - /* If child process's stdout/stderr has been registered with the event, - * or the child process hasn't started yet, continue. */ - if (cdata.event != NULL || cdata.pid == 0) { - continue; - } - /* Executed only once, once the child process starts. */ - cdata.event = event; - /* If stdout valid, add stdout to be monitored by the poll event. */ - if (cdata.child_stdout != -1) { - if (ssh_event_add_fd(event, cdata.child_stdout, POLLIN, process_stdout, - sdata.channel) != SSH_OK) { - fprintf(stderr, "Failed to register stdout to poll context\n"); - ssh_channel_close(sdata.channel); - } - } - - /* If stderr valid, add stderr to be monitored by the poll event. */ - if (cdata.child_stderr != -1){ - if (ssh_event_add_fd(event, cdata.child_stderr, POLLIN, process_stderr, - sdata.channel) != SSH_OK) { - fprintf(stderr, "Failed to register stderr to poll context\n"); - ssh_channel_close(sdata.channel); - } - } - } while(ssh_channel_is_open(sdata.channel) && - (cdata.pid == 0 || waitpid(cdata.pid, &rc, WNOHANG) == 0)); - - close(cdata.pty_master); - close(cdata.child_stdin); - close(cdata.child_stdout); - close(cdata.child_stderr); - - /* Remove the descriptors from the polling context, since they are now - * closed, they will always trigger during the poll calls. */ - ssh_event_remove_fd(event, cdata.child_stdout); - ssh_event_remove_fd(event, cdata.child_stderr); - - /* If the child process exited. */ - if (kill(cdata.pid, 0) < 0 && (WIFEXITED(rc) || WIFSIGNALED(rc))) { - if (WIFSIGNALED (rc)) - ssh_channel_request_send_exit_signal (sdata.channel, strsignal (WTERMSIG (rc)), 0, "", ""); - else - ssh_channel_request_send_exit_status (sdata.channel, WEXITSTATUS (rc)); - /* If client terminated the channel or the process did not exit nicely, - * but only if something has been forked. */ - } else if (cdata.pid > 0) { - kill(cdata.pid, SIGKILL); - } - - ssh_channel_send_eof(sdata.channel); - ssh_channel_close(sdata.channel); - - /* Wait up to 5 seconds for the client to terminate the session. */ - for (n = 0; n < 50 && (ssh_get_status(session) & SESSION_END) == 0; n++) { - ssh_event_dopoll(event, 100); - } -} - -#ifdef WITH_FORK -/* SIGCHLD handler for cleaning up dead children. */ -static void sigchld_handler(int signo) { - (void) signo; - while (waitpid(-1, NULL, WNOHANG) > 0); -} -#else -static void *session_thread(void *arg) { - ssh_session session = arg; - ssh_event event; - - event = ssh_event_new(); - if (event != NULL) { - /* Blocks until the SSH session ends by either - * child thread exiting, or client disconnecting. */ - handle_session(event, session); - ssh_event_free(event); - } else { - fprintf(stderr, "Could not create polling context\n"); - } - ssh_disconnect(session); - ssh_free(session); - return NULL; -} -#endif - -int main(int argc, char **argv) { - ssh_bind sshbind; - ssh_session session; - int rc; -#ifdef WITH_FORK - struct sigaction sa; - - /* Set up SIGCHLD handler. */ - sa.sa_handler = sigchld_handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; - if (sigaction(SIGCHLD, &sa, NULL) != 0) { - fprintf(stderr, "Failed to register SIGCHLD handler\n"); - return 1; - } -#endif - - rc = ssh_init(); - if (rc < 0) { - fprintf(stderr, "ssh_init failed\n"); - return 1; - } - - sshbind = ssh_bind_new(); - if (sshbind == NULL) { - fprintf(stderr, "ssh_bind_new failed\n"); - ssh_finalize(); - return 1; - } - - { - // Set mock defaults - int port = 0; - ssh_bind_options_set (sshbind, SSH_BIND_OPTIONS_BINDPORT, &port); - ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_RSAKEY, SRCDIR "/src/ssh/mock_rsa_key"); - strncpy(authorizedkeys, SRCDIR "/src/ssh/test_rsa.pub", DEF_STR_SIZE-1); - } - -#ifdef HAVE_ARGP_H - argp_parse(&argp, argc, argv, 0, 0, sshbind); -#else - if (parse_opt(argc, argv, sshbind) < 0) { - ssh_bind_free(sshbind); - ssh_finalize(); - return 1; - } -#endif /* HAVE_ARGP_H */ - - if(ssh_bind_listen(sshbind) < 0) { - fprintf(stderr, "%s\n", ssh_get_error(sshbind)); - ssh_bind_free(sshbind); - ssh_finalize(); - return 1; - } - - /* Print out the port */ - { - int bind_fd; - int r; - char portname[16]; - char addrname[16]; - struct sockaddr_storage addr; - socklen_t addrlen; - - bind_fd = ssh_bind_get_fd (sshbind); - - addrlen = sizeof (addr); - if (getsockname (bind_fd, (struct sockaddr *)&addr, &addrlen) < 0) - { - fprintf (stderr, "couldn't get local address: %s\n", strerror (errno)); - return 1; - } - r = getnameinfo ((struct sockaddr *)&addr, addrlen, addrname, sizeof (addrname), - portname, sizeof (portname), NI_NUMERICHOST | NI_NUMERICSERV); - if (r != 0) - { - fprintf (stderr, "couldn't get local port: %s\n", gai_strerror (r)); - return 1; - } - - /* Caller wants to know the port */ - printf ("%s\n", portname); - } - - /* Close stdout to signal startup is complete (once above info is printed) */ - fflush(stdout); - close (1); - - while (1) { - session = ssh_new(); - if (session == NULL) { - fprintf(stderr, "Failed to allocate session\n"); - continue; - } - - /* Blocks until there is a new incoming connection. */ - if(ssh_bind_accept(sshbind, session) != SSH_ERROR) { -#ifdef WITH_FORK - ssh_event event; - - switch(fork()) { - case 0: - /* Remove the SIGCHLD handler inherited from parent. */ - sa.sa_handler = SIG_DFL; - sigaction(SIGCHLD, &sa, NULL); - /* Remove socket binding, which allows us to restart the - * parent process, without terminating existing sessions. */ - ssh_bind_free(sshbind); - - event = ssh_event_new(); - if (event != NULL) { - /* Blocks until the SSH session ends by either - * child process exiting, or client disconnecting. */ - handle_session(event, session); - ssh_event_free(event); - } else { - fprintf(stderr, "Could not create polling context\n"); - } - ssh_disconnect(session); - ssh_free(session); - - exit(0); - case -1: - fprintf(stderr, "Failed to fork\n"); - } -#else - pthread_t tid; - - rc = pthread_create(&tid, NULL, session_thread, session); - if (rc == 0) { - pthread_detach(tid); - continue; - } - fprintf(stderr, "Failed to pthread_create\n"); -#endif - } else { - fprintf(stderr, "%s\n", ssh_get_error(sshbind)); - } - /* Since the session has been passed to a child fork, do some cleaning - * up at the parent process. */ - ssh_disconnect(session); - ssh_free(session); - } - - ssh_bind_free(sshbind); - ssh_finalize(); - return 0; -} diff --git a/src/ssh/mock_ecdsa_key b/src/ssh/mock_ecdsa_key deleted file mode 100644 index 6cab9f69edd..00000000000 --- a/src/ssh/mock_ecdsa_key +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS -1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSuD2S/4gae/4UIUb7AYejIP1LC1xiE -RVk3pBatZyV5twpAZMrGtycYvTFJDaNISAx5ctmzjCgijUqTdlOohgo8AAAAqDO+d0Uzvn -dFAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK4PZL/iBp7/hQhR -vsBh6Mg/UsLXGIRFWTekFq1nJXm3CkBkysa3Jxi9MUkNo0hIDHly2bOMKCKNSpN2U6iGCj -wAAAAhAOviSdIlwJIeAAbittvzOo4OmXrWyvLYt1VwEtmYScTSAAAADm1hcnRpbkB0b29s -Ym94AQ== ------END OPENSSH PRIVATE KEY----- diff --git a/src/ssh/mock_ecdsa_key.pub b/src/ssh/mock_ecdsa_key.pub deleted file mode 100644 index fcd482d6ba8..00000000000 --- a/src/ssh/mock_ecdsa_key.pub +++ /dev/null @@ -1 +0,0 @@ -ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK4PZL/iBp7/hQhRvsBh6Mg/UsLXGIRFWTekFq1nJXm3CkBkysa3Jxi9MUkNo0hIDHly2bOMKCKNSpN2U6iGCjw= dev@cockpit-project.org diff --git a/src/ssh/mock_known_hosts b/src/ssh/mock_known_hosts deleted file mode 100644 index 820bc283966..00000000000 --- a/src/ssh/mock_known_hosts +++ /dev/null @@ -1 +0,0 @@ -[localhost]:*,[127.0.0.1]:*,[::1]:* ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYzo07OA0H6f7orVun9nIVjGYrkf8AuPDScqWGzlKpAqSipoQ9oY/mwONwIOu4uhKh7FTQCq5p+NaOJ6+Q4z++xBzSOLFseKX+zyLxgNG28jnF06WSmrMsSfvPdNuZKt9rZcQFKn9fRNa8oixa+RsqEEVEvTYhGtRf7w2wsV49xIoIza/bln1ABX1YLaCByZow+dK3ZlHn/UU0r4ewpAIZhve4vCvAsMe5+6KJH8ft/OKXXQY06h6jCythLV4h18gY/sYosOa+/4XgpmBiE7fDeFRKVjP3mvkxMpxce+ckOFae2+aJu51h513S9kxY2PmKaV/JU9HBYO+yO4j+j24v diff --git a/src/ssh/mock_rsa_key b/src/ssh/mock_rsa_key deleted file mode 100644 index df9ea2cdfe3..00000000000 --- a/src/ssh/mock_rsa_key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAmM6NOzgNB+n+6K1bp/ZyFYxmK5H/ALjw0nKlhs5SqQKkoqaE -PaGP5sDjcCDruLoSoexU0AquafjWjievkOM/vsQc0jixbHil/s8i8YDRtvI5xdOl -kpqzLEn7z3TbmSrfa2XEBSp/X0TWvKIsWvkbKhBFRL02IRrUX+8NsLFePcSKCM2v -25Z9QAV9WC2ggcmaMPnSt2ZR5/1FNK+HsKQCGYb3uLwrwLDHufuiiR/H7fzil10G -NOoeowsrYS1eIdfIGP7GKLDmvv+F4KZgYhO3w3hUSlYz95r5MTKcXHvnJDhWntvm -ibudYedd0vZMWNj5imlfyVPRwWDvsjuI/o9uLwIDAQABAoIBAGIbc2fLA+rJ3ITN -EOTVAMg+/TYKJVv0YYHsY8QaYc3rSYK4QH1FZpuzyhKqwE05Ak996bIsuXCGeFKK -vlja3ol/ZjW+eoN3LrRbj0bY+0xnVpph2ZM3ycOsuISotXkwooNUsjbS4zZqfyhb -QvkhqMQn8CFDDibRD/uMAxEnv6cNs8zyJu+RgfA1kxW/BAKjKg0mf5EwltMwfLCc -O+3tQZR0P8w6UXSBLrGZvPUvWjLZHuhSAzeRiA4deEerX5tEOwkx/iUbyma2HOh/ -p9BKpaJ67RH+UdwysV6x8JRRXAhTt8jy+5T1OdaOYYisn71M4PVnajmWwTbZ7Vy6 -XJx1MVECgYEAxlTXF1JPyMoxqgWhV222f+UONyuNnB1mKNP5kSI1WIBrsWSkOqvQ -a6pihSGKaQFLMVzBkfrOAkSGJTtJuOrhA3lk3thJsG4ACuX+MgfXi/xqQpvX3SBx -QS23YfxC86HOBkDUKr/xHSHJPKidNVlOtQbyDioxuqR2Vk2IrsvJmZkCgYEAxT0B -7mP0JypL576pW1CE2LhrqTmmTBy3qVPAjV4fnv7KduH2I/a88WVmOWRSDd14RCBf -h1JG/mUkWgKaiY8Dh9dCGiJBe06OHKRFjmC+AXx/R0IwOHD1i1kVIRz5t5nzNhLs -cXWYNIFDxW9zN+z8aNiRZ0+0oGvD6MGZI5FA8wcCgYAgEO+F0cUcjbRh7O8dF5v+ -KaaWvO/0Ybx2tW8QTBub54eB8ueqpMTZ435yT+309ESYAos5cveD3lhiBKfywecH -jMUGUqn3YJGZMX5b0HDFLVZw1omcMvactyuDKCobAfoxgKHNF6OANko3CHwCeCIF -ms3gBGpt5tFLOtXyjPhXYQKBgHwzt6SbkqKbEuNi/5h98rnaIAmXQO4K42igUc9z -puvjPDFyhyIMfNxx4tZfIwCSJuqXjDBVklkd7a80loXq5elDlt+IFL4GMJ0+oIJQ -zzV2ZVvFBUJ1d1oBhbmtWl7QdgPmFLg5udfOUpPgY6ii9is7vQxWd8LRObO3PazV -ChY/AoGBAI9N9tF49xDcyJDEmS7X2LQu81QqGQuKRlv0/z/jqUjqWsDVnvNdKePC -bs+yHRQciDYQB/g+FSUHE7bh8CN0qrQrttEk69sfm7gK8rusK16RIrQ7cHo2WnjG -82WGZ9YvmnUq3qkCzyS/po1TI3Mr1sHnY2xwcTS/DOzAnD2wNsU0 ------END RSA PRIVATE KEY----- diff --git a/src/ssh/mock_rsa_key.pub b/src/ssh/mock_rsa_key.pub deleted file mode 100644 index 794c74bcd67..00000000000 --- a/src/ssh/mock_rsa_key.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYzo07OA0H6f7orVun9nIVjGYrkf8AuPDScqWGzlKpAqSipoQ9oY/mwONwIOu4uhKh7FTQCq5p+NaOJ6+Q4z++xBzSOLFseKX+zyLxgNG28jnF06WSmrMsSfvPdNuZKt9rZcQFKn9fRNa8oixa+RsqEEVEvTYhGtRf7w2wsV49xIoIza/bln1ABX1YLaCByZow+dK3ZlHn/UU0r4ewpAIZhve4vCvAsMe5+6KJH8ft/OKXXQY06h6jCythLV4h18gY/sYosOa+/4XgpmBiE7fDeFRKVjP3mvkxMpxce+ckOFae2+aJu51h513S9kxY2PmKaV/JU9HBYO+yO4j+j24v dev@cockpit-project.org diff --git a/src/ssh/ssh.c b/src/ssh/ssh.c deleted file mode 100644 index 58f5a6c075c..00000000000 --- a/src/ssh/ssh.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2016 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#include "config.h" - -#include -#include - -#include "common/cockpithacks-glib.h" -#include "common/cockpitsystem.h" - -#include "cockpitsshrelay.h" - -static gboolean -on_exit_signal (gpointer data) -{ - GMainLoop *loop = data; - g_debug ("Received exit signal, shutting down"); - g_main_loop_quit (loop); - return TRUE; -} - -int -main (int argc, - char *argv[]) -{ - gint ret = 1; - CockpitSshRelay *relay; - GOptionContext *context; - GError *error = NULL; - GMainLoop *loop = NULL; - - cockpit_hacks_redirect_gdebug_to_stderr (); - - signal (SIGALRM, SIG_DFL); - signal (SIGQUIT, SIG_DFL); - signal (SIGTSTP, SIG_IGN); - signal (SIGHUP, SIG_IGN); - signal (SIGPIPE, SIG_IGN); - - cockpit_setenv_check ("GSETTINGS_BACKEND", "memory", TRUE); - cockpit_setenv_check ("GIO_USE_PROXY_RESOLVER", "dummy", TRUE); - cockpit_setenv_check ("GIO_USE_VFS", "local", TRUE); - - context = g_option_context_new ("- cockpit-ssh [user@]host[:port]"); - - if (!g_option_context_parse (context, &argc, &argv, &error)) - { - ret = INTERNAL_ERROR; - goto out; - } - - if (argc != 2) - { - g_printerr ("cockpit-ssh: unexpected additional arguments, see --help\n"); - ret = INTERNAL_ERROR; - goto out; - } - - loop = g_main_loop_new (NULL, FALSE); - - relay = cockpit_ssh_relay_new (argv[1]); - g_signal_connect_swapped (relay, "disconnect", G_CALLBACK (g_main_loop_quit), loop); - - guint sig_term = g_unix_signal_add (SIGTERM, on_exit_signal, loop); - guint sig_int = g_unix_signal_add (SIGINT, on_exit_signal, loop); - - g_main_loop_run (loop); - - ret = cockpit_ssh_relay_result (relay); - g_object_unref (relay); - - g_source_remove (sig_term); - g_source_remove (sig_int); - -out: - g_option_context_free (context); - - if (error) - { - g_printerr ("cockpit-ssh: %s\n", error->message); - g_error_free (error); - } - - if (loop) - g_main_loop_unref (loop); - - return ret; -} diff --git a/src/ssh/test-sshbridge.c b/src/ssh/test-sshbridge.c deleted file mode 100644 index 0e9be750b25..00000000000 --- a/src/ssh/test-sshbridge.c +++ /dev/null @@ -1,1485 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2017 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#include "config.h" - -#include "common/cockpitauthorize.h" -#include "testlib/cockpittest.h" -#include "common/cockpiterror.h" -#include "common/cockpitpipe.h" -#include "common/cockpitpipetransport.h" -#include "common/cockpitjson.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#define TIMEOUT 120 - -#define WAIT_UNTIL(cond) \ - G_STMT_START \ - while (!(cond)) g_main_context_iteration (NULL, TRUE); \ - G_STMT_END - -#define PASSWORD "this is the password" - -#define INVALID_KEY "" - -typedef struct { - CockpitTransport *transport; - gboolean closed; - - /* setup_mock_sshd */ - GPid mock_sshd; - guint16 ssh_port; - gchar *home_dir; - gchar *home_ssh_dir; - gchar *home_knownhosts_file; - gchar *home_ssh_config_file; -} TestCase; - -typedef struct { - const char *ssh_command; - const char *mock_sshd_arg; - const char *mock_sshd_arg_value; - const char *client_password; - const char *hostname; - const char *username; - const char *knownhosts_file; - const char *knownhosts_home; - const char *host_key_authorize; /* authorize x-host-key response for test_problem() */ - const char *config; - const char *problem; - const char *ssh_config_identity_file; - gboolean allow_unknown; - gboolean test_home_ssh_config; - enum { USER_NONE = 0, USER_INVALID, USER_INVALID_HOST_PRIORITY, USER_ME } ssh_config_user; - enum { PORT_VALID = 0, PORT_INVALID_HOST_PRIORITY } ssh_config_port; -} TestFixture; - -/* check if /proc/net/if_inet6 is non-empty, otherwise there is no IPv6 support */ -static gboolean -have_ipv6 (void) -{ - int fd; - gboolean avail = FALSE; - - fd = open ("/proc/net/if_inet6", O_RDONLY); - if (fd >= 0) - { - char c; - avail = read (fd, &c, 1) == 1; - close (fd); - } - return avail; -} - -static GString * -read_all_into_string (int fd) -{ - GString *input = g_string_new (""); - gsize len; - gssize ret; - - for (;;) - { - len = input->len; - g_string_set_size (input, len + 256); - ret = read (fd, input->str + len, 256); - if (ret < 0) - { - if (errno != EAGAIN) - { - g_critical ("couldn't read from mock input: %s", g_strerror (errno)); - g_string_free (input, TRUE); - return NULL; - } - } - else if (ret == 0) - { - return input; - } - else - { - input->len = len + ret; - input->str[input->len] = '\0'; - } - } -} - -static void -spawn_setup (gpointer data) -{ - int fd = GPOINTER_TO_INT (data); - - /* Send this signal to all direct child processes, when bridge dies */ - prctl (PR_SET_PDEATHSIG, SIGHUP); - - g_assert_cmpint (dup2 (fd, 0), >, -1); - g_assert_cmpint (dup2 (fd, 1), >, -1); - - close (fd); -} - -static void -setup_mock_sshd (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fixture = data; - GError *error = NULL; - GString *port; - gchar *endptr; - guint64 value; - gint out_fd; - - const gchar *argv[] = { - BUILDDIR "/mock-sshd", - "--bind", fixture->hostname ?: "127.0.0.1", - "--user", g_get_user_name (), - "--password", PASSWORD, - fixture->mock_sshd_arg, fixture->mock_sshd_arg_value, - NULL - }; - - g_spawn_async_with_pipes (BUILDDIR, (gchar **)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, - &tc->mock_sshd, NULL, &out_fd, NULL, &error); - g_assert_no_error (error); - - /* - * mock-sshd prints its port on stdout, and then closes stdout - * This also lets us know when it has initialized. - */ - - port = read_all_into_string (out_fd); - g_assert (port != NULL); - close (out_fd); - g_assert_no_error (error); - - g_strstrip (port->str); - value = g_ascii_strtoull (port->str, &endptr, 10); - if (!endptr || *endptr != '\0' || value == 0 || value > G_MAXUSHORT) - g_critical ("invalid port printed by mock-sshd: %s", port->str); - - tc->ssh_port = (gushort)value; - g_string_free (port, TRUE); -} - -static const TestFixture fixture_mock_echo = { - .ssh_command = BUILDDIR "/mock-echo" -}; - -static const TestFixture fixture_cat = { - .ssh_command = SRCDIR "/src/ws/mock-cat-with-init" -}; - -static const TestFixture fixture_ipv6_address = { - .ssh_command = BUILDDIR "/mock-echo", - .hostname = "::1", -}; - -static gchar ** -setup_env (const TestFixture *fix) -{ - const gchar *command; - const gchar *knownhosts_file; - const gchar *config; - gchar **env = g_get_environ (); - - config = fix ? fix->config : NULL; - if (!config) - config = SRCDIR "/src/ssh/mock-config"; - env = g_environ_setenv (env, "XDG_CONFIG_DIRS", config, TRUE); - - command = fix ? fix->ssh_command : NULL; - if (!command) - command = fixture_cat.ssh_command; - env = g_environ_setenv (env, "COCKPIT_SSH_BRIDGE_COMMAND", command, TRUE); - - if (fix && fix->allow_unknown) - { - env = g_environ_setenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", - "true", TRUE); - } - - knownhosts_file = fix ? fix->knownhosts_file : NULL; - if (!knownhosts_file) - knownhosts_file = SRCDIR "/src/ssh/mock_known_hosts"; - - env = g_environ_setenv (env, "COCKPIT_SSH_KNOWN_HOSTS_FILE", - knownhosts_file, TRUE); - return env; -} - -static CockpitTransport * -start_bridge (gchar **env, - gchar **argv) -{ - GError *error = NULL; - int fds[2]; - - g_assert_cmpint (socketpair (PF_LOCAL, SOCK_STREAM, 0, fds), ==, 0); - g_spawn_async_with_pipes (BUILDDIR, argv, env, - G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, - spawn_setup, GINT_TO_POINTER (fds[0]), - NULL, NULL, NULL, NULL, &error); - g_assert_no_error (error); - close (fds[0]); - - return cockpit_pipe_transport_new_fds ("test-ssh", fds[1], fds[1]); -} - -static void -on_closed_set_flag (CockpitTransport *transport, - const gchar *problem, - gpointer user_data) -{ - gboolean *flag = user_data; - g_assert_cmpstr (problem, ==, NULL); - g_assert (*flag == FALSE); - *flag = TRUE; -} - -static void -setup (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fixture = data; - const gchar *argv[] = { BUILDDIR "/cockpit-ssh", NULL, NULL }; - const gchar *hostname = fixture->hostname ?: "127.0.0.1"; - gchar **env = NULL; - gchar *host = NULL; - gchar *path = NULL; - - alarm (TIMEOUT); - - g_assert (fixture != NULL); - - env = setup_env (fixture); - setup_mock_sshd (tc, data); - - if (tc->ssh_port && strchr (hostname, ':') != NULL) /* bracket IPv6 addresses */ - host = g_strdup_printf ("[%s]:%d", hostname, tc->ssh_port); - else if (tc->ssh_port) - host = g_strdup_printf ("%s:%d", hostname, tc->ssh_port); - else - host = g_strdup (hostname); - argv[1] = host; - - /* run our tests with temp home dir, to avoid influence from the real ~/.ssh */ - tc->home_dir = g_dir_make_tmp ("home.XXXXXX", NULL); - g_assert (tc->home_dir != NULL); - env = g_environ_setenv (env, "HOME", tc->home_dir, TRUE); - /* use preload library to bend getpwuid_r home dir to the temporary one */ - env = g_environ_setenv (env, "LD_PRELOAD", BUILDDIR "/libpreload-temp-home.so", TRUE); - - tc->home_ssh_dir = g_build_filename (tc->home_dir, ".ssh", NULL); - g_assert (tc->home_ssh_dir != NULL); - - if (fixture->knownhosts_home) - { - gchar *content; - - tc->home_knownhosts_file = g_build_filename (tc->home_ssh_dir, "known_hosts", NULL); - g_assert (tc->home_knownhosts_file != NULL); - g_assert_cmpint (mkdir (tc->home_ssh_dir, 0700), ==, 0); - - content = g_strdup_printf ("[%s]:%d %s\n", - fixture->hostname ?: "127.0.0.1", - (int)tc->ssh_port, - fixture->knownhosts_home); - - g_assert (g_file_set_contents (tc->home_knownhosts_file, content, -1, NULL)); - - g_free (content); - } - - if (fixture->test_home_ssh_config) - { - g_autoptr(GString) content = g_string_new (NULL); - g_autoptr(GString) new_host = g_string_new (NULL); - - tc->home_ssh_config_file = g_build_filename (tc->home_ssh_dir, "config", NULL); - if (!fixture->knownhosts_home) - g_assert_cmpint (mkdir (tc->home_ssh_dir, 0700), ==, 0); - - g_string_append (content, "Host somehost\n"); - g_string_append_printf (content, "\tHostname %s\n", hostname); - - if (fixture->ssh_config_port == PORT_VALID) - g_string_append_printf (content, "\tPort %hu\n", tc->ssh_port); - else if (fixture->ssh_config_port == PORT_INVALID_HOST_PRIORITY) - g_string_append_printf (content, "\tPort %d\n", (tc->ssh_port - 1)); - - if (fixture->ssh_config_user == USER_ME) - g_string_append_printf (content, "\tUser %s\n", g_get_user_name ()); - else if (fixture->ssh_config_user == USER_INVALID || fixture->ssh_config_user == USER_INVALID_HOST_PRIORITY) - g_string_append (content, "\tUser invalid\n"); - - if (fixture->ssh_config_identity_file) - g_string_append_printf (content, "\tIdentityFile %s\n", fixture->ssh_config_identity_file); - - g_assert (g_file_set_contents (tc->home_ssh_config_file, content->str, -1, NULL)); - - g_free (host); - /* The user in host should take priority over the user in ssh config */ - if (fixture->ssh_config_user == USER_INVALID_HOST_PRIORITY) - g_string_append_printf (new_host, "%s@", g_get_user_name ()); - /* Host in the ssh config file */ - g_string_append (new_host, "somehost"); - /* The port in host should take priority over the port in ssh config */ - if (fixture->ssh_config_port == PORT_INVALID_HOST_PRIORITY) - g_string_append_printf (new_host, ":%hu", tc->ssh_port); - - host = g_strdup (new_host->str); - argv[1] = host; - } - - tc->transport = start_bridge (env, (gchar **) argv); - g_signal_connect (tc->transport, "closed", G_CALLBACK (on_closed_set_flag), &tc->closed); - g_strfreev (env); - g_free (host); - g_free (path); -} - -static void -teardown (TestCase *tc, - gconstpointer data) -{ - if (tc->home_knownhosts_file) - { - unlink (tc->home_knownhosts_file); - g_free (tc->home_knownhosts_file); - } - if (tc->home_ssh_config_file) - { - unlink (tc->home_ssh_config_file); - g_free (tc->home_ssh_config_file); - } - if (tc->home_ssh_dir) - { - rmdir (tc->home_ssh_dir); - g_free (tc->home_ssh_dir); - } - rmdir (tc->home_dir); - g_free (tc->home_dir); - - WAIT_UNTIL (tc->closed == TRUE); - g_object_add_weak_pointer (G_OBJECT (tc->transport), (gpointer*)&tc->transport); - g_object_unref (tc->transport); - - /* If this asserts, outstanding references */ - g_assert (tc->transport == NULL); - - if (tc->mock_sshd) - { - kill (tc->mock_sshd, SIGTERM); - g_assert_cmpint (waitpid (tc->mock_sshd, 0, 0), ==, tc->mock_sshd); - g_spawn_close_pid (tc->mock_sshd); - } - - alarm (0); -} - -static gboolean -on_recv_get_payload (CockpitTransport *transport, - const gchar *channel, - GBytes *message, - gpointer user_data) -{ - GBytes **received = user_data; - if (channel == NULL) - return FALSE; - g_assert_cmpstr (channel, ==, "546"); - g_assert (*received == NULL); - *received = g_bytes_ref (message); - return TRUE; -} - -static gboolean -on_recv_multiple (CockpitTransport *transport, - const gchar *channel, - GBytes *message, - gpointer user_data) -{ - gint *state = user_data; - GBytes *check = NULL; - - if (channel == NULL) - return FALSE; - - g_assert_cmpstr (channel, ==, "9"); - - if (*state == 0) - check = g_bytes_new_static ("one", 3); - else if (*state == 1) - check = g_bytes_new_static ("two", 3); - else - g_assert_not_reached (); - - (*state)++; - g_assert (g_bytes_equal (message, check)); - g_bytes_unref (check); - - return TRUE; -} - -static gboolean -on_control_get_options (CockpitTransport *transport, - const gchar *command, - const gchar *channel, - JsonObject *options, - GBytes *payload, - gpointer user_data) -{ - JsonObject **ret_options = user_data; - g_assert (ret_options); - g_assert (*ret_options == NULL); - *ret_options = json_object_ref (options); - return TRUE; -} - -static void -do_auth_response (CockpitTransport *transport, - const gchar *challenge, - const gchar *response) -{ - JsonObject *auth = NULL; - GBytes *payload = NULL; - const gchar *cookie; - guint sig = 0; - - sig = g_signal_connect (transport, "control", - G_CALLBACK (on_control_get_options), - &auth); - WAIT_UNTIL (auth != NULL); - g_signal_handler_disconnect (transport, sig); - g_assert (cockpit_json_get_string (auth, "cookie", NULL, &cookie)); - g_assert_cmpstr (json_object_get_string_member (auth, "command"), - ==, "authorize"); - g_assert_cmpstr (json_object_get_string_member (auth, "challenge"), - ==, challenge); - g_assert_cmpstr (cookie, !=, NULL); - - payload = cockpit_transport_build_control ("command", "authorize", - "cookie", cookie, - "response", response, - NULL); - cockpit_transport_send (transport, NULL, payload); - g_bytes_unref (payload); - - json_object_unref (auth); -} - -static void -do_basic_auth (CockpitTransport *transport, - const gchar *challenge, - const gchar *user, - const gchar *password) -{ - gchar *userpass = NULL; - gchar *encoded = NULL; - gchar *response = NULL; - - userpass = g_strdup_printf ("%s:%s", user, password); - encoded = g_base64_encode ((guchar *)userpass, strlen (userpass)); - response = g_strdup_printf ("Basic %s", encoded); - - do_auth_response (transport, challenge, response); - - g_free (userpass); - g_free (response); - g_free (encoded); -} - -static void -do_fixture_auth (CockpitTransport *transport, - gconstpointer data) -{ - const TestFixture *fixture = data; - const gchar *user; - const gchar *password; - - password = fixture->client_password ? fixture->client_password : PASSWORD; - user = fixture->username ? fixture->username : g_get_user_name (); - do_basic_auth (transport, "*", user, password); -} - -static JsonObject * -wait_until_transport_init (CockpitTransport *transport, - const gchar *expect_problem) -{ - JsonObject *init = NULL; - guint sig; - const gchar *problem; - - sig = g_signal_connect (transport, "control", - G_CALLBACK (on_control_get_options), - &init); - WAIT_UNTIL (init != NULL); - g_signal_handler_disconnect (transport, sig); - - g_assert_cmpstr (json_object_get_string_member (init, "command"), - ==, "init"); - g_assert (cockpit_json_get_string (init, "problem", NULL, &problem)); - g_assert_cmpstr (problem, ==, expect_problem); - return init; -} - -static void -do_echo_and_close (TestCase *tc) -{ - GBytes *received = NULL; - GBytes *sent; - gboolean closed = FALSE; - - sent = g_bytes_new_static ("the message", 11); - g_signal_connect (tc->transport, "recv", G_CALLBACK (on_recv_get_payload), &received); - g_signal_connect (tc->transport, "closed", G_CALLBACK (on_closed_set_flag), &closed); - cockpit_transport_send (tc->transport, "546", sent); - - while (received == NULL && !closed) - g_main_context_iteration (NULL, TRUE); - - g_assert (!closed); - g_assert (g_bytes_equal (received, sent)); - g_bytes_unref (sent); - g_bytes_unref (received); - received = NULL; - - cockpit_transport_close (tc->transport, NULL); - - while (received == NULL && !closed) - g_main_context_iteration (NULL, TRUE); - - g_assert (closed); - g_assert (received == NULL); -} - -static void -test_echo_and_close (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - - do_fixture_auth (tc->transport, data); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - json_object_unref (init); -} - -static void -test_echo_queue (TestCase *tc, - gconstpointer data) -{ - GBytes *sent; - gint state = 0; - gboolean closed = FALSE; - JsonObject *init = NULL; - - do_fixture_auth (tc->transport, data); - init = wait_until_transport_init (tc->transport, NULL); - - g_signal_connect (tc->transport, "recv", G_CALLBACK (on_recv_multiple), &state); - g_signal_connect (tc->transport, "closed", G_CALLBACK (on_closed_set_flag), &closed); - - sent = g_bytes_new_static ("one", 3); - cockpit_transport_send (tc->transport, "9", sent); - g_bytes_unref (sent); - sent = g_bytes_new_static ("two", 3); - cockpit_transport_send (tc->transport, "9", sent); - g_bytes_unref (sent); - - while (state != 2) - g_main_context_iteration (NULL, TRUE); - - /* Only closes after above are sent */ - cockpit_transport_close (tc->transport, NULL); - - while (!closed) - g_main_context_iteration (NULL, TRUE); - json_object_unref (init); -} - -static void -test_echo_large (TestCase *tc, - gconstpointer data) -{ - GBytes *received = NULL; - GBytes *sent; - JsonObject *init = NULL; - - /* HACK: TODO: find out exactly why this test is so slow under Valgrind */ - if (cockpit_test_skip_slow ()) - { - tc->closed = TRUE; - return; - } - - do_fixture_auth (tc->transport, data); - init = wait_until_transport_init (tc->transport, NULL); - - g_signal_connect (tc->transport, "recv", G_CALLBACK (on_recv_get_payload), &received); - - /* Medium length */ - sent = g_bytes_new_take (g_strnfill (1020, '!'), 1020); - cockpit_transport_send (tc->transport, "546", sent); - while (received == NULL) - g_main_context_iteration (NULL, TRUE); - g_assert (g_bytes_equal (received, sent)); - g_bytes_unref (sent); - g_bytes_unref (received); - received = NULL; - - /* Extra large */ - sent = g_bytes_new_take (g_strnfill (10 * 1000 * 1000, '?'), 10 * 1000 * 1000); - cockpit_transport_send (tc->transport, "546", sent); - while (received == NULL) - g_main_context_iteration (NULL, TRUE); - g_assert (g_bytes_equal (received, sent)); - g_bytes_unref (sent); - g_bytes_unref (received); - received = NULL; - - /* Double check that didn't screw things up */ - sent = g_bytes_new_static ("yello", 5); - cockpit_transport_send (tc->transport, "546", sent); - while (received == NULL) - g_main_context_iteration (NULL, TRUE); - g_assert (g_bytes_equal (received, sent)); - g_bytes_unref (sent); - g_bytes_unref (received); - received = NULL; - - cockpit_transport_close (tc->transport, NULL); - json_object_unref (init); -} - -#define MOCK_RSA_KEY "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCYzo07OA0H6f7orVun9nIVjGYrkf8AuPDScqWGzlKpAqSipoQ9oY/mwONwIOu4uhKh7FTQCq5p+NaOJ6+Q4z++xBzSOLFseKX+zyLxgNG28jnF06WSmrMsSfvPdNuZKt9rZcQFKn9fRNa8oixa+RsqEEVEvTYhGtRf7w2wsV49xIoIza/bln1ABX1YLaCByZow+dK3ZlHn/UU0r4ewpAIZhve4vCvAsMe5+6KJH8ft/OKXXQY06h6jCythLV4h18gY/sYosOa+/4XgpmBiE7fDeFRKVjP3mvkxMpxce+ckOFae2+aJu51h513S9kxY2PmKaV/JU9HBYO+yO4j+j24v" -#define MOCK_RSA_KEY_INVALID "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7YmnYAJaC579hyNFzcszH+ZFQeDuR8I2li1vCgKeM0lOIkV5TwCY4Tl1lbXI7NNffDACQnUrJfNNm6FamdhVzFEvyQAk+iQz/Wz6lHbDlY2dVvoVaJzNWyqXu/qaYs8Mb2QUmNXKtYk4IuM8PH88z5L4JwZXRbOEPOxnJNcaazP9pBhN/0TrHALaXwW29BR0SIJicJqK2r/mPuDovg/SWs8NdgY9DTAAfzdELshTigVXlc1AX6vo71x3O9NWMaPKZuy88o0BeQNI+mkVeV04Pewm3bUlDsr3VeEcd4D+Ixdyfg4+S57K1in0kHQD4PXrd/x5GoCZekxgUuBoE7HVB" - -static const gchar MOCK_RSA_FP[] = "SHA256:XQ8a7zGxMFstDrGecBRUP9OMnOUXd/T3vkNGtYShs2w"; -#define SSH_PUBLICKEY_HASH_NAME "SHA256" - -#define MOCK_ECDSA_PUB_KEY "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK4PZL/iBp7/hQhRvsBh6Mg/UsLXGIRFWTekFq1nJXm3CkBkysa3Jxi9MUkNo0hIDHly2bOMKCKNSpN2U6iGCjw=" - -static void -do_auth_conversation (CockpitTransport *transport, - const gchar *expect_prompt, - const gchar *expect_json, - const gchar *response, - gboolean add_header) -{ - JsonObject *auth = NULL; - GBytes *payload = NULL; - const gchar *cookie; - const gchar *challenge = NULL; - guint sig = 0; - gchar *encoded = NULL; - gchar *full = NULL; - gchar *result = NULL; - - if (add_header) - { - encoded = g_base64_encode ((guchar *)response, strlen (response)); - full = g_strdup_printf ("x-conversation id %s", encoded); - } - else - { - full = g_strdup (response); - } - - sig = g_signal_connect (transport, "control", - G_CALLBACK (on_control_get_options), - &auth); - WAIT_UNTIL (auth != NULL); - g_signal_handler_disconnect (transport, sig); - - g_assert (cockpit_json_get_string (auth, "cookie", NULL, &cookie)); - g_assert_cmpstr (json_object_get_string_member (auth, "command"), - ==, "authorize"); - g_assert_cmpstr (cookie, !=, NULL); - - challenge = json_object_get_string_member (auth, "challenge"); - result = cockpit_authorize_parse_x_conversation (challenge, NULL); - g_assert_cmpstr (result, ==, expect_prompt); - - json_object_remove_member (auth, "cookie"); - json_object_remove_member (auth, "command"); - json_object_remove_member (auth, "challenge"); - cockpit_assert_json_eq (auth, expect_json); - - payload = cockpit_transport_build_control ("command", "authorize", - "cookie", "cookie", - "response", full, - NULL); - cockpit_transport_send (transport, NULL, payload); - g_bytes_unref (payload); - g_free (full); - g_free (encoded); - g_free (result); - json_object_unref (auth); -} - -static void -do_hostkey_conversation (TestCase *tc, - const gchar *response, - gboolean add_header) -{ - gchar *expect_json = NULL; - expect_json = g_strdup_printf ("{\"message\": \"The authenticity of host '127.0.0.1:%d' can't be established. Do you want to proceed this time?\", \"default\": \"%s\", \"host-key\": \"[127.0.0.1]:%d %s\\n\", \"echo\": true }", - (int)tc->ssh_port, MOCK_RSA_FP, - (int)tc->ssh_port, MOCK_RSA_KEY); - - do_auth_conversation (tc->transport, SSH_PUBLICKEY_HASH_NAME " Fingerprint (ssh-rsa):", - expect_json, response, add_header); - g_free (expect_json); -} - -static void -check_host_key_values (TestCase *tc, - JsonObject *init, - const char *hostname) -{ - gchar *knownhosts = g_strdup_printf ("[%s]:%d %s\n", - hostname ?: "127.0.0.1", - (int)tc->ssh_port, - MOCK_RSA_KEY); - - g_assert_cmpstr (json_object_get_string_member (init, "host-key"), - ==, knownhosts); - g_assert_cmpstr (json_object_get_string_member (init, "host-fingerprint"), - ==, MOCK_RSA_FP); - - g_free (knownhosts); -} - -static void -test_problem (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - const TestFixture *fix = data; - - do_fixture_auth (tc->transport, data); - if (fix->host_key_authorize) - do_auth_response (tc->transport, "x-host-key", fix->host_key_authorize); - init = wait_until_transport_init (tc->transport, fix->problem); - json_object_unref (init); -} - -static const TestFixture fixture_unknown_localhost = { - .knownhosts_file = "/dev/null", - .host_key_authorize = INVALID_KEY, - .ssh_command = BUILDDIR "/mock-echo" -}; - -static const TestFixture fixture_unknown_host = { - .knownhosts_file = "/dev/null", - .hostname = "127.0.0.99", - .host_key_authorize = INVALID_KEY, - .problem = "unknown-host" -}; - -static const TestFixture fixture_known_host_home = { - .knownhosts_file = "/dev/null", - .knownhosts_home = MOCK_RSA_KEY, - .ssh_command = BUILDDIR "/mock-echo" -}; - -static const TestFixture fixture_home_ssh_config = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo" -}; - -static const TestFixture fixture_ssh_config_valid_user = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_ME, - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo" -}; - -static const TestFixture fixture_ssh_config_invalid_user = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_INVALID, - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", - .problem = "authentication-failed" -}; - -static const TestFixture fixture_ssh_config_invalid_user_host_priority = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_INVALID_HOST_PRIORITY, - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", - .problem = "authentication-failed" -}; - -static const TestFixture fixture_ssh_config_invalid_port_host_priority = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", - .ssh_config_port = PORT_INVALID_HOST_PRIORITY -}; - -static const TestFixture fixture_ssh_config_good_key = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_ME, - .ssh_config_identity_file = SRCDIR "/src/ssh/test_rsa", - .client_password = "bad password", /* we don't need this password because the key will work */ - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", -}; - -static const TestFixture fixture_ssh_config_good_key_password_protected = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_ME, - .ssh_config_identity_file = SRCDIR "/src/ssh/test_rsa_password_protected", - .client_password = "bad password", - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", - .mock_sshd_arg = "--import-pubkey", - .mock_sshd_arg_value = SRCDIR "/src/ssh/test_rsa_password_protected.pub", - .problem = "authentication-failed", -}; - -static const TestFixture fixture_ssh_config_bad_key = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_ME, - .ssh_config_identity_file = SRCDIR "/src/ssh/mock_rsa_key", - .client_password = "bad password", - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", - .problem = "authentication-failed", -}; - -static const TestFixture fixture_ssh_config_key_password_fallback = { - .knownhosts_file = "/dev/null", - .test_home_ssh_config = TRUE, - .ssh_config_user = USER_ME, - .ssh_config_identity_file = SRCDIR "/src/ssh/mock_rsa_key", - .knownhosts_home = MOCK_RSA_KEY, - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo", - .problem = "authentication-failed", -}; - -static const TestFixture fixture_knownhost_challenge_preconnect = { - .knownhosts_file = "/dev/null", - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo" -}; - -static const TestFixture fixture_host_key_invalid = { - .knownhosts_file = SRCDIR "/src/ssh/invalid_known_hosts", -}; - -static const TestFixture fixture_prompt_host_key = { - .knownhosts_file = "/dev/null", - .allow_unknown = TRUE, - .ssh_command = BUILDDIR "/mock-echo" -}; - -static void -test_invalid_knownhost (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fix = data; - JsonObject *init = NULL; - - g_assert_cmpstr (fix->knownhosts_file, ==, SRCDIR "/src/ssh/invalid_known_hosts"); - do_auth_response (tc->transport, "*", ""); - - init = wait_until_transport_init (tc->transport, "invalid-hostkey"); - - json_object_unref (init); -} - -static void -test_knownhost_data_prompt (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fix = data; - JsonObject *init = NULL; - gchar *knownhosts = g_strdup_printf ("x-host-key [%s]:%d %s", - fix->hostname ?: "127.0.0.1", - (int)tc->ssh_port, - MOCK_RSA_KEY); - - g_assert_cmpstr (fix->knownhosts_file, ==, "/dev/null"); - - do_fixture_auth (tc->transport, data); - do_auth_response (tc->transport, "x-host-key", knownhosts); - - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); - g_free (knownhosts); -} - -static void -test_hostkey_unknown (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fix = data; - JsonObject *init = NULL; - - g_assert_cmpstr (fix->knownhosts_file, ==, "/dev/null"); - - do_auth_response (tc->transport, "*", ""); - do_auth_response (tc->transport, "x-host-key", INVALID_KEY); - do_hostkey_conversation (tc, "", FALSE); - - init = wait_until_transport_init (tc->transport, "unknown-hostkey"); - check_host_key_values (tc, init, fix->hostname); - json_object_unref (init); -} - -static void -test_hostkey_conversation (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fix = data; - JsonObject *init = NULL; - - g_assert_cmpstr (fix->knownhosts_file, ==, "/dev/null"); - - do_fixture_auth (tc->transport, data); - do_auth_response (tc->transport, "x-host-key", INVALID_KEY); - do_hostkey_conversation (tc, MOCK_RSA_FP, TRUE); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); -} - -static void -test_hostkey_conversation_bad (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fix = data; - JsonObject *init = NULL; - - g_assert_cmpstr (fix->knownhosts_file, ==, "/dev/null"); - - do_auth_response (tc->transport, "*", ""); - do_auth_response (tc->transport, "x-host-key", INVALID_KEY); - do_hostkey_conversation (tc, "other-value", TRUE); - init = wait_until_transport_init (tc->transport, "unknown-hostkey"); - check_host_key_values (tc, init, fix->hostname); - json_object_unref (init); -} - -static void -test_hostkey_conversation_invalid (TestCase *tc, - gconstpointer data) -{ - const TestFixture *fix = data; - JsonObject *init = NULL; - - g_assert_cmpstr (fix->knownhosts_file, ==, "/dev/null"); - - do_auth_response (tc->transport, "*", ""); - do_auth_response (tc->transport, "x-host-key", INVALID_KEY); - do_hostkey_conversation (tc, "other-value", FALSE); - init = wait_until_transport_init (tc->transport, "unknown-hostkey"); - check_host_key_values (tc, init, fix->hostname); - json_object_unref (init); -} - -/* The output from this will go to stderr */ -static const TestFixture fixture_bad_command = { - .ssh_command = "/nonexistent", - .problem = "no-cockpit" -}; - -/* Yes this makes a difference with bash, output goes to stdout */ -static const TestFixture fixture_command_not_found = { - .ssh_command = "nonexistant-command", - .problem = "no-cockpit" -}; - -/* A valid command that exits with 0 */ -static const TestFixture fixture_command_exits = { - .ssh_command = "/usr/bin/true", - .problem = "no-cockpit" -}; - -/* A valid command that exits with 1 */ -static const TestFixture fixture_command_fails = { - .ssh_command = "/usr/bin/false", - .problem = "no-cockpit" -}; - -/* An ssh command that just kills itself with SIGTERM */ -static const TestFixture fixture_terminate_problem = { - .ssh_command = "kill $$", - .problem = "terminated" -}; - - -static const TestFixture fixture_unsupported_auth = { - .mock_sshd_arg = "--broken-auth", -}; - -static void -test_unsupported_auth (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_fixture_auth (tc->transport, data); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"no-server-support\",\"public-key\":\"no-server-support\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - - -static const TestFixture fixture_auth_failed = { - .client_password = "bad password", -}; - -static void -test_auth_failed (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_fixture_auth (tc->transport, data); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"denied\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_cannot_connect (void) -{ - const gchar *argv[] = { - BUILDDIR "/cockpit-ssh", - "localhost:65533", - NULL, - }; - - JsonObject *init = NULL; - gchar **env = setup_env (NULL); - CockpitTransport *transport = start_bridge (env, (gchar **) argv); - do_basic_auth (transport, "*", "user", "unused"); - init = wait_until_transport_init (transport, "no-host"); - - g_object_unref (transport); - json_object_unref (init); - g_strfreev (env); -} - -static void -test_key_good (TestCase *tc, - gconstpointer data) -{ - g_autofree gchar *privkey = NULL; - g_assert (g_file_get_contents (SRCDIR "/src/ssh/test_rsa", &privkey, NULL, NULL)); - - g_autofree gchar *msg = g_strdup_printf ("private-key %s", privkey); - - do_auth_response (tc->transport, "*", msg); - g_autoptr(JsonObject) init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); -} - -static void -test_key_fail (TestCase *tc, - gconstpointer data) -{ - g_autofree gchar *privkey = NULL; - g_assert (g_file_get_contents (SRCDIR "/src/ssh/mock_ecdsa_key", &privkey, NULL, NULL)); - - g_autofree gchar *msg = g_strdup_printf ("private-key %s", privkey); - - do_auth_response (tc->transport, "*", msg); - g_autoptr(JsonObject) init = wait_until_transport_init (tc->transport, "authentication-failed"); - JsonObject *auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"not-provided\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); -} - -static void -test_key_invalid (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_auth_response (tc->transport, "*", "private-key invalid"); - init = wait_until_transport_init (tc->transport, "internal-error"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"not-provided\",\"public-key\":\"error\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_password_good (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - gchar *msg = g_strdup_printf ("password %s", PASSWORD); - - do_auth_response (tc->transport, "*", msg); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); - g_free (msg); -} - -static void -test_password_fail (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_auth_response (tc->transport, "*", "password bad"); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"denied\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_basic_no_user (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_basic_auth (tc->transport, "*", "", PASSWORD); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{}"); - - json_object_unref (init); -} - -static void -test_basic_user_mismatch (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - /* Auth fails because user doesn't match */ - do_basic_auth (tc->transport, "*", "other", PASSWORD); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"denied\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_basic_secondary_no_user (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - - do_auth_response (tc->transport, "*", ""); - /* Auth succeeds because user is already set */ - do_basic_auth (tc->transport, "basic", "", PASSWORD); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); -} - - -static void -test_basic_secondary_user_mismatch (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - - do_auth_response (tc->transport, "*", ""); - /* Auth succeeds because secondary user is ignored */ - do_basic_auth (tc->transport, "basic", "bad-user", PASSWORD); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); -} - -static const TestFixture fixture_multi_auth = { - .mock_sshd_arg = "--multi-step", -}; - -static void -test_multi_auth (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - - do_fixture_auth (tc->transport, data); - do_auth_conversation (tc->transport, "Token", - "{\"message\":\"Password and Token\",\"echo\":true}", - "5", TRUE); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); -} - -static void -test_multi_auth_fail (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_fixture_auth (tc->transport, data); - do_auth_conversation (tc->transport, "Token", - "{\"message\":\"Password and Token\",\"echo\":true}", - "4", TRUE); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"denied\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_multi_auth_empty (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_fixture_auth (tc->transport, data); - do_auth_conversation (tc->transport, "Token", - "{\"message\":\"Password and Token\",\"echo\":true}", - "", FALSE); - init = wait_until_transport_init (tc->transport, "internal-error"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"error\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_multi_auth_bad (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_fixture_auth (tc->transport, data); - do_auth_conversation (tc->transport, "Token", - "{\"message\":\"Password and Token\",\"echo\":true}", - "invalid", FALSE); - init = wait_until_transport_init (tc->transport, "internal-error"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"error\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - -static void -test_multi_auth_3 (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - - do_fixture_auth (tc->transport, data); - do_auth_conversation (tc->transport, "Token", - "{\"message\":\"Password and Token\",\"echo\":true}", - "6", TRUE); - do_auth_conversation (tc->transport, "So Close", - "{\"message\":\"Again\",\"echo\":false}", - "5", TRUE); - init = wait_until_transport_init (tc->transport, NULL); - do_echo_and_close (tc); - - json_object_unref (init); -} - -static void -test_multi_auth_3_fail (TestCase *tc, - gconstpointer data) -{ - JsonObject *init = NULL; - JsonObject *auth_results = NULL; - - do_fixture_auth (tc->transport, data); - do_auth_conversation (tc->transport, "Token", - "{\"message\":\"Password and Token\",\"echo\":true}", - "6", TRUE); - do_auth_conversation (tc->transport, "So Close", - "{\"message\":\"Again\",\"echo\":false}", - "4", TRUE); - init = wait_until_transport_init (tc->transport, "authentication-failed"); - auth_results = json_object_get_object_member (init, "auth-method-results"); - cockpit_assert_json_eq (auth_results, "{\"password\":\"denied\",\"public-key\":\"denied\",\"gssapi-mic\":\"no-server-support\"}"); - - json_object_unref (init); -} - - -int -main (int argc, - char *argv[]) -{ - - cockpit_test_init (&argc, &argv); - - g_test_add ("/ssh-bridge/echo-message", TestCase, &fixture_mock_echo, - setup, test_echo_and_close, teardown); - g_test_add ("/ssh-bridge/echo-queue", TestCase, &fixture_mock_echo, - setup, test_echo_queue, teardown); - g_test_add ("/ssh-bridge/echo-large", TestCase, &fixture_cat, - setup, test_echo_large, teardown); - - if (have_ipv6 ()) - g_test_add ("/ssh-bridge/ipv6-address", TestCase, &fixture_ipv6_address, - setup, test_echo_and_close, teardown); - else - g_message ("No IPv6 support, skipping IPv6 tests"); - - g_test_add ("/ssh-bridge/bad-command", TestCase, &fixture_bad_command, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/command-not-found", TestCase, &fixture_command_not_found, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/command-not-cockpit", TestCase, &fixture_command_exits, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/command-just-fails", TestCase, &fixture_command_fails, - setup, test_problem, teardown); - g_test_add_func ("/ssh-bridge/cannot-connect", test_cannot_connect); - g_test_add ("/ssh-bridge/ssh-config-home", TestCase, &fixture_home_ssh_config, - setup, test_echo_and_close, teardown); - g_test_add ("/ssh-bridge/ssh-config-valid-user", TestCase, &fixture_ssh_config_valid_user, - setup, test_echo_and_close, teardown); - g_test_add ("/ssh-bridge/ssh-config-invalid-user", TestCase, &fixture_ssh_config_invalid_user, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/ssh-config-host-user-priority", TestCase, &fixture_ssh_config_invalid_user_host_priority, - setup, test_echo_and_close, teardown); - g_test_add ("/ssh-bridge/ssh-config-host-port-priority", TestCase, &fixture_ssh_config_invalid_port_host_priority, - setup, test_echo_and_close, teardown); - g_test_add ("/ssh-bridge/ssh-config-home-good-key", TestCase, &fixture_ssh_config_good_key, - setup, test_echo_and_close, teardown); - g_test_add ("/ssh-bridge/ssh-config-home-good-key-password-protected", TestCase, &fixture_ssh_config_good_key_password_protected, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/ssh-config-home-bad-key", TestCase, &fixture_ssh_config_bad_key, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/ssh-config-home-bad-key-good-key-fallback", TestCase, &fixture_ssh_config_bad_key, - setup, test_key_good, teardown); - g_test_add ("/ssh-bridge/ssh-config-home-key-password-fallback", TestCase, &fixture_ssh_config_key_password_fallback, - setup, test_echo_and_close, teardown); - - g_test_add ("/ssh-bridge/terminate-problem", TestCase, &fixture_terminate_problem, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/unsupported-auth", TestCase, &fixture_unsupported_auth, - setup, test_unsupported_auth, teardown); - g_test_add ("/ssh-bridge/auth-failed", TestCase, - &fixture_auth_failed, setup, - test_auth_failed, teardown); - g_test_add ("/ssh-bridge/key-good", TestCase, &fixture_mock_echo, - setup, test_key_good, teardown); - g_test_add ("/ssh-bridge/key-invalid", TestCase, &fixture_mock_echo, - setup, test_key_invalid, teardown); - g_test_add ("/ssh-bridge/key-fail", TestCase, &fixture_mock_echo, - setup, test_key_fail, teardown); - g_test_add ("/ssh-bridge/password-fail", TestCase, &fixture_mock_echo, - setup, test_password_fail, teardown); - g_test_add ("/ssh-bridge/password-good", TestCase, &fixture_mock_echo, - setup, test_password_good, teardown); - g_test_add ("/ssh-bridge/basic-no-user", TestCase, &fixture_mock_echo, - setup, test_basic_no_user, teardown); - g_test_add ("/ssh-bridge/basic-secondary-no-user", TestCase, &fixture_mock_echo, - setup, test_basic_secondary_no_user, teardown); - g_test_add ("/ssh-bridge/basic-user-mismatch", TestCase, &fixture_mock_echo, - setup, test_basic_user_mismatch, teardown); - g_test_add ("/ssh-bridge/basic-secondary-user-mismatch", TestCase, &fixture_mock_echo, - setup, test_basic_secondary_user_mismatch, teardown); - g_test_add ("/ssh-bridge/kb-multi-bad", TestCase, - &fixture_multi_auth, - setup, test_multi_auth_bad, teardown); - g_test_add ("/ssh-bridge/kb-multi-empty", TestCase, - &fixture_multi_auth, - setup, test_multi_auth_empty, teardown); - g_test_add ("/ssh-bridge/kb-multi-fail", TestCase, - &fixture_multi_auth, - setup, test_multi_auth_fail, teardown); - g_test_add ("/ssh-bridge/kb-multi-echo-message", TestCase, - &fixture_multi_auth, - setup, test_multi_auth, teardown); - g_test_add ("/ssh-bridge/kb-multi-3-fail", TestCase, - &fixture_multi_auth, - setup, test_multi_auth_3_fail, teardown); - g_test_add ("/ssh-bridge/kb-multi-3-echo-message", TestCase, - &fixture_multi_auth, - setup, test_multi_auth_3, teardown); - - g_test_add ("/ssh-bridge/unknown-host", TestCase, &fixture_unknown_host, - setup, test_problem, teardown); - g_test_add ("/ssh-bridge/unknown-localhost", TestCase, &fixture_unknown_localhost, - setup, test_hostkey_unknown, teardown); - g_test_add ("/ssh-bridge/knownhost-challenge-preconnect", TestCase, - &fixture_knownhost_challenge_preconnect, - setup, test_knownhost_data_prompt, teardown); - g_test_add ("/ssh-bridge/knownhost-invalid", TestCase, &fixture_host_key_invalid, - setup, test_invalid_knownhost, teardown); - g_test_add ("/ssh-bridge/knownhost-home", TestCase, &fixture_known_host_home, - setup, test_echo_and_close, teardown); - - g_test_add ("/ssh-bridge/hostkey-unknown", TestCase, &fixture_prompt_host_key, - setup, test_hostkey_unknown, teardown); - g_test_add ("/ssh-bridge/hostkey-conversation", TestCase, &fixture_prompt_host_key, - setup, test_hostkey_conversation, teardown); - g_test_add ("/ssh-bridge/hostkey-conversation-bad", TestCase, &fixture_prompt_host_key, - setup, test_hostkey_conversation_bad, teardown); - g_test_add ("/ssh-bridge/hostkey-conversation-invalid", TestCase, &fixture_prompt_host_key, - setup, test_hostkey_conversation_invalid, teardown); - - return g_test_run (); -} diff --git a/src/ssh/test-sshoptions.c b/src/ssh/test-sshoptions.c deleted file mode 100644 index f04c939279e..00000000000 --- a/src/ssh/test-sshoptions.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This file is part of Cockpit. - * - * Copyright (C) 2016 Red Hat, Inc. - * - * Cockpit is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or - * (at your option) any later version. - * - * Cockpit is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Cockpit; If not, see . - */ - -#include "config.h" - -#include "common/cockpitconf.h" -#include "testlib/cockpittest.h" - -#include "cockpitsshoptions.h" - -/* Mock override these from other files */ -extern const gchar *cockpit_config_file; - -static void -test_ssh_options (void) -{ - gchar **env = NULL; - CockpitSshOptions *options = NULL; - - options = cockpit_ssh_options_from_env (env); - g_assert_cmpstr (options->remote_peer, ==, "localhost"); - g_assert_cmpstr (options->knownhosts_file, ==, NULL); - g_assert_cmpstr (options->command, ==, "cockpit-bridge"); - - options->knownhosts_file = "other-known"; - options->command = "other-command"; - options->remote_peer = "other"; - - env = cockpit_ssh_options_to_env (options, NULL); - - g_assert_cmpstr (g_environ_getenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS"), ==, ""); - g_assert_cmpstr (g_environ_getenv (env, "COCKPIT_SSH_KNOWN_HOSTS_FILE"), ==, "other-known"); - g_assert_cmpstr (g_environ_getenv (env, "COCKPIT_SSH_BRIDGE_COMMAND"), ==, "other-command"); - g_assert_cmpstr (g_environ_getenv (env, "COCKPIT_REMOTE_PEER"), ==, "other"); - - options->connect_to_unknown_hosts = TRUE; - - g_strfreev (env); - env = cockpit_ssh_options_to_env (options, NULL); - g_assert_cmpstr (g_environ_getenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS"), ==, "1"); - - g_free (options); - g_strfreev (env); - - /* Start with a clean env */ - env = g_environ_setenv (NULL, "COCKPIT_SSH_KNOWN_HOSTS_FILE", "other-known", TRUE); - env = g_environ_setenv (env, "COCKPIT_SSH_BRIDGE_COMMAND", "other-command", TRUE); - env = g_environ_setenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", "", TRUE); - - options = cockpit_ssh_options_from_env (env); - g_assert_false (options->connect_to_unknown_hosts); - g_assert_cmpstr (options->knownhosts_file, ==, "other-known"); - g_assert_cmpstr (options->command, ==, "other-command"); - - g_free (options); - - env = g_environ_setenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", "bogus", TRUE); - options = cockpit_ssh_options_from_env (env); - g_assert_false (options->connect_to_unknown_hosts); - g_free (options); - - env = g_environ_setenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", "yes", TRUE); - options = cockpit_ssh_options_from_env (env); - g_assert_true (options->connect_to_unknown_hosts); - g_free (options); - - env = g_environ_setenv (env, "COCKPIT_SSH_CONNECT_TO_UNKNOWN_HOSTS", "", TRUE); - options = cockpit_ssh_options_from_env (env); - g_assert_false (options->connect_to_unknown_hosts); - g_free (options); - g_strfreev (env); -} - -static void -test_ssh_options_deprecated (void) -{ - gchar **env = NULL; - CockpitSshOptions *options = NULL; - - env = g_environ_setenv (NULL, "COCKPIT_SSH_ALLOW_UNKNOWN", "yes", TRUE); - options = cockpit_ssh_options_from_env (env); - g_assert_true (options->connect_to_unknown_hosts); - g_free (options); - - env = g_environ_setenv (env, "COCKPIT_SSH_KNOWN_HOSTS_DATA", "authorize", TRUE); - options = cockpit_ssh_options_from_env (env); - g_free (options); - - env = g_environ_setenv (env, "COCKPIT_SSH_KNOWN_HOSTS_DATA", "", TRUE); - options = cockpit_ssh_options_from_env (env); - g_free (options); - - g_strfreev (env); -} - -static void -test_ssh_options_alt_conf (void) -{ - CockpitSshOptions *options = NULL; - - cockpit_config_file = SRCDIR "/src/ws/mock-config/cockpit/cockpit-alt.conf"; - cockpit_conf_cleanup (); - - options = cockpit_ssh_options_from_env (NULL); - g_assert_true (options->connect_to_unknown_hosts); - g_free (options); -} - -static void -test_ssh_options_conf_deprecated (void) -{ - CockpitSshOptions *options = NULL; - - cockpit_config_file = SRCDIR "/src/ws/mock-config/cockpit/cockpit-deprecated.conf"; - cockpit_conf_cleanup (); - - options = cockpit_ssh_options_from_env (NULL); - g_assert_true (options->connect_to_unknown_hosts); - g_free (options); -} - -int -main (int argc, - char *argv[]) -{ - cockpit_test_init (&argc, &argv); - - g_test_add_func ("/ssh-options/basic", test_ssh_options); - g_test_add_func ("/ssh-options/deprecated", test_ssh_options_deprecated); - g_test_add_func ("/ssh-options/alt-conf", test_ssh_options_alt_conf); - g_test_add_func ("/ssh-options/deprecated-conf", test_ssh_options_conf_deprecated); - - return g_test_run (); -} diff --git a/src/ssh/test_rsa b/src/ssh/test_rsa deleted file mode 100644 index b1ab61d4787..00000000000 --- a/src/ssh/test_rsa +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAvkPEj9GX9I0v/3dxCUB73TgOYjxkXB/m2ecKnUYmYtEwgirA -onCgZRMAvB7UaP5e6U/pNCXuZ+UgS0yU6tqEXD7MQ4YZiiNU1RaLe/gQ21NEx27h -hCGTZOLKcSfOFv2Z77OUcXSop2PZxQweYaH1+RB7hojOd7ZchN/tIBxvea5JSg/0 -wLC8Lm65gpCZCxG2TNgfymovnyrYB44HnwEm4jCMU4uP68h0+D297US4oWwcpcqE -2S4LOxazjw1Brvntpqwtq624tUb1QVYMxdHpCR7Qu843r3XSpS4BwrnOks7Sbgyg -tHiKgogY5Xhu7ZqsTODtzyJ950YD0scnY41qHQIDAQABAoIBAFlQHnkUfixCCoH1 -Y45gQsS5h6b9im7kWs128ziYsXQ5lnfD8eFO1TwdC39DSZpvrcX/yQy9sYf7uoke -Tdlg8jkLEX+w91Qs+al9h8SN0fvivqqPljUcPcBh5X3wnYGVUil/NvN7O6A38wXY -hnp2OKzN2+5vUdxIMm39X6ZvMrT/FyQjvdp393G4f0blYl7Npdc+HYPNnhHdgi4I -NUa32pG3ypoWkQRAYApaG2RXPTWQXTM2w4CFK5uJx/pB3r5NidU/H0XAl4TAuw9M -V9hrIPAOh5zKvHcPv8xOwR0Bt36F+/QATjO9pvlzQO6Rn3x2dyAVdaFMgdYTNpQQ -t0ZYsYECgYEA8yAhKUnArEQ4A+AI+pCtZuftzkXmnQ5SHNUtF2GeR5tRZ1PBF/tp -zoVRW+5ge1hI2VEx3ziGHEIBr7FfVej7twQ3URv5ILYj6CoNOf+HxkZgkTDGpYdj -AVvyjeD5qJEwCSeJ2bxD5LmxS9is8b8rXjVKRuPxwLeWqEjemPb0KNUCgYEAyFcL -TdN9cZghuzLZ0vfP4k9Hratunskz5njTFKnJx90riE7VqPH9OHvTeHn1xJ5WACnb -mFpAUG1v7BmC+WLEIPnKRKvuzL5C1yr+mntwTZsrwsLDdT/nfTS9hWzk9U6ykhJA -De8nNfxHuCoqM++CNvh+rA4W2Zc6WmE0uCwXYCkCgYEA70KMP+Sb3yvXcEDWtTcR -3raZ+agitib0ufk0QdFIgbGhH6111lMOIjZjBbSGcHxGXM8h5Ens+PwgSrWkW5hH -tylIAuMjfYShu4U+tPf6ty5lNB0rMJUW4qyI/AUNzEztV+T4LTWwHvR7PWgDcniu -hiytZyxFqmFBu2TS4vgM+e0CgYAvAL0WNVhpHlhLo1KXvKx5XEBk7qO1fV8/43ki -j/NXgPyFrnlSefP/HI4w5exThRKIV0m+JO6R8BsiOZoRCKsbUX+zPON6BemIsf2q -IOvoSU+rEibpi2S0a3tLopDVPPGIc9+zZTi94cKx4rKkHL1gSEzv8R5LTr/SFJxZ -2X5igQKBgBTkIeB0yI2PGf1drI+YbhDfgIphEeSCPbWxcUzPCcwLqXGo41cr8RXY -TgWtKk0gXhJWkMSIIXrfucCvXHTkk8wlqqgAVwrTgq4Q16LfBuucLwSe4TLp4SJZ -Lko5CzOq+EIv6DIlZ3tRHeDFatWe+41w27KhrV9yxB6Ay0MalP4i ------END RSA PRIVATE KEY----- diff --git a/src/ssh/test_rsa.pub b/src/ssh/test_rsa.pub deleted file mode 100644 index 6707911a7d3..00000000000 --- a/src/ssh/test_rsa.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+Q8SP0Zf0jS//d3EJQHvdOA5iPGRcH+bZ5wqdRiZi0TCCKsCicKBlEwC8HtRo/l7pT+k0Je5n5SBLTJTq2oRcPsxDhhmKI1TVFot7+BDbU0THbuGEIZNk4spxJ84W/Znvs5RxdKinY9nFDB5hofX5EHuGiM53tlyE3+0gHG95rklKD/TAsLwubrmCkJkLEbZM2B/Kai+fKtgHjgefASbiMIxTi4/ryHT4Pb3tRLihbBylyoTZLgs7FrOPDUGu+e2mrC2rrbi1RvVBVgzF0ekJHtC7zjevddKlLgHCuc6SztJuDKC0eIqCiBjleG7tmqxM4O3PIn3nRgPSxydjjWod diff --git a/src/ssh/test_rsa_password_protected b/src/ssh/test_rsa_password_protected deleted file mode 100644 index be1f2737154..00000000000 --- a/src/ssh/test_rsa_password_protected +++ /dev/null @@ -1,39 +0,0 @@ ------BEGIN OPENSSH PRIVATE KEY----- -b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBgu8TlNL -abjyeEucxdc6/GAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC/YTIbXU4c -/jDRAYqz3wHqYNvicXO8tVhZekQmDed0BxeV6j9v80PNGml/q16+bkWuVGGoTwV5wwVNvW -gywPIJYmm4aMbxQhM6iBjvQZFtm+EoKzjOTxCinh9o9RuuaaMuh31/Z/9QqgwNVWtSIlts -JeWijiye0JDCq93h1DlwWvr/MFzkJPZf1jfvxvA7Twt+JL/RLEhR5b+qzdc6qH8sxYX8ve -QDGCuQP8ogAgsI9VrNIzjfZhW5ybGGpFmeO52XRNyh44ZatfHmhQTipvyrkQvCsPS5qn0A -SIDmF0CU5elTYGLdtAf9pgXkD0xXhOjVKhgpQdkf2oj9xMrc2bww/CM/9ns3nNNGnLorFu -VLAU24uBSb52AaYvujLoEnn1IT8rLIvSAKhXzTylg4iVC0JNjhEkgm+qjHeRMAWpTO3HhM -eXDkERV6QwIWlruxiS8q/Kci9u5J36KG00ghd6jVn/bK5xTnYu+TUyG6Q6vPp7DWBXhkJ5 -XibqUUL17j60MAAAWQUyu8AEpQWha8mU3Cg2WsManyAJrmHTZ1SqI2f/RotUkn23ii7DV9 -Q7blQjED9iKX2sWbjs0VM3Jxw7YiVY3x7LkcGv5Vua0fY7TBa6TYlYTpj++YZ5OEZnt+z6 -Xp8lkKpZFtEdrd/3+NF8jkbf8S2s+7SYt7SKWRKmC8sbN2D0O7HafV58gRjgoYkh39Z+XB -mC8TxShGTRdZxPBSPOssIuBQiA/r+f9QzH4vaNtiI/LaR7XlOEUcsA2IIvkOEcZYMSh6bO -ViR3iiqSgGTtkojAVTE0Hq0pahaxgr6FpDr4E69I+OSo0pCAKqUfxq4GT2Th6BrjhAsnpj -4wMxL/Xn/wPdj9yyCu+cXgqAh+Dl8pcZnT3enQqiu243OeCA8qMk/X/aQK1W2VkYUL4AKU -SePX18kx26XDdx7V2bpriOGKgozGKh/G/LABsz4D0vjHDL1rctMi0qPvW+Esz5MxGQBDyM -zMuFrdNKT7M5WZi3Qn7x5j6XN2xidUZ7LK9r4OoUdrWzrszTlpD4pHPcHNcSvJ5RvycHQp -7rVsX1JhdcsFav/VzsQD70atsioJF25OaRNcp7/flyo2sVisWp4PY8OuCrvwINNxb7bqzY -u8AZneob66YRnIYD6c2kOh8pkdcdXrtSOxZE9/RPVkLg/5mDzNMj1CXkqS5h6Fpj6AGR6f -bN7FyCzMbi6vWmai1s2pFBRTARrRjphZFQyIUsuwbhii2cDSYkqMfFNnahms1t983Xa+aM -gFabLqv6KSUkwaatHRbmJCQ7iYUspId2oQ/7Wh9As6+vQUCd1HR7CWrE7FfscHdcza09Ys -6yWDuLyQivQd/6eF6DUIb64e0Q2dK/JcQOy1fxpHAxO1LBWEzFi8Z5KvfXJ1H1r96aNPbi -wvwjibOP9YFpALPNe8WtL/zsQ7z3QLYUcyojHWE6zvjfA3xMiZ0cXZI8byLeUZbPKWDiCm -Rsc9phColDeHz6SKrIXHsmLK5Y2I+zVtEsQi1ohK0z0Yw8lTMoNHSN+qAaFYhlL3Ewx6HM -XITlX0AL1JqhQCtC1FGFE64etaeifJgau70Iz/u1jLnMfIItWJtLouiyEXoiIfyKMG1iwM -9mzoGgiJm7D3464XnnCK/MZ2RpHQlZy+FiEE8f6lFC26PQrvF7mBMRVUSleWUPWXW0zha/ -4ACTyoZbUB3W66f/Agz0J+WXv2jq6G+hWOE+xR6mXgnEVKP6AQU/7m0O3C8NSOzyf6b0nE -+wF54shWTqdpKWLQdq8ttA82AsvubfAKHkw8AI8uU5I0Pbg0xuwHJApQNZbUIr9+3/P/du -X2pivKCzpcLG0c24293M/KeZk/mGyxJ+OnAUG0wXmdI7hZda3A8KeQJk/ls9DhzZniuJ1G -F9SoiXKFbCUF0oMUNrOkDkHXaVGI7s/A1H+ip47Br+XuJkNAZXxHqH2zusWjFisSWxWzG1 -Pku6t8fKSWRbnIUZQ89bf+rZlvqnMCEOzLizPVSzeERXFakplxaBgxHiWIk8koIGkuvKln -WNS1u+uvIGCToNsWyd5OyG+ZCKLfk7gI7+91ARJZ8fBk/hVFVJRB6Q9VPnXveY5J/W4xs1 -CDDaNARufOaVxNAHK3/VPMzqPJvuVvlJt+FqdffWi1VsjfUpTANM8z9Ynbr5WjDpPm4muE -GlYt2EsyC1SDa8nj82S9UgGYB0cPY4aq8BfDI4HpkB7Ba0reVq0L6WuWWcBt1PuUSmDbCh -bFHb3scpk9ZPjjfGhNaDay6HVNzMwzccphOIDaNhTcER4JKA2sTEcmRh1NuU1XnbPtaVOC -S/U9pFMI++6GZBL07O0lCEmfOM9S2fxJ+/pGfjqRZCRqTQTmY+anoEfiOSB9bwFYur8yBf -4xkIKGTjOI5y+dbPNEuND78Ae14= ------END OPENSSH PRIVATE KEY----- diff --git a/src/ssh/test_rsa_password_protected.README b/src/ssh/test_rsa_password_protected.README deleted file mode 100644 index e7a56b7ec9d..00000000000 --- a/src/ssh/test_rsa_password_protected.README +++ /dev/null @@ -1 +0,0 @@ -Generated with: ssh-keygen -q -f src/ssh/test_rsa_password_protected -N sshfoobar diff --git a/src/ssh/test_rsa_password_protected.pub b/src/ssh/test_rsa_password_protected.pub deleted file mode 100644 index 32153dc5867..00000000000 --- a/src/ssh/test_rsa_password_protected.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/YTIbXU4c/jDRAYqz3wHqYNvicXO8tVhZekQmDed0BxeV6j9v80PNGml/q16+bkWuVGGoTwV5wwVNvWgywPIJYmm4aMbxQhM6iBjvQZFtm+EoKzjOTxCinh9o9RuuaaMuh31/Z/9QqgwNVWtSIltsJeWijiye0JDCq93h1DlwWvr/MFzkJPZf1jfvxvA7Twt+JL/RLEhR5b+qzdc6qH8sxYX8veQDGCuQP8ogAgsI9VrNIzjfZhW5ybGGpFmeO52XRNyh44ZatfHmhQTipvyrkQvCsPS5qn0ASIDmF0CU5elTYGLdtAf9pgXkD0xXhOjVKhgpQdkf2oj9xMrc2bww/CM/9ns3nNNGnLorFuVLAU24uBSb52AaYvujLoEnn1IT8rLIvSAKhXzTylg4iVC0JNjhEkgm+qjHeRMAWpTO3HhMeXDkERV6QwIWlruxiS8q/Kci9u5J36KG00ghd6jVn/bK5xTnYu+TUyG6Q6vPp7DWBXhkJ5XibqUUL17j60M= martin@toolbox diff --git a/tools/cockpit.spec b/tools/cockpit.spec index 51b2053197d..c8e79922b64 100644 --- a/tools/cockpit.spec +++ b/tools/cockpit.spec @@ -84,7 +84,6 @@ BuildRequires: autoconf automake BuildRequires: make BuildRequires: python3-devel BuildRequires: gettext >= 0.21 -BuildRequires: libssh-devel >= 0.8.5 BuildRequires: openssl-devel BuildRequires: gnutls-devel >= 3.4.3 BuildRequires: zlib-devel @@ -198,7 +197,6 @@ echo '%dir %{_datadir}/cockpit/base1' >> base.list find %{buildroot}%{_datadir}/cockpit/base1 -type f -o -type l >> base.list echo '%{_sysconfdir}/cockpit/machines.d' >> base.list echo %{buildroot}%{_datadir}/polkit-1/actions/org.cockpit-project.cockpit-bridge.policy >> base.list -echo '%{_libexecdir}/cockpit-ssh' >> base.list %if %{build_pcp} echo '%dir %{_datadir}/cockpit/pcp' > pcp.list @@ -289,7 +287,6 @@ troubleshooting, interactive command-line sessions, and more. %package bridge Summary: Cockpit bridge server-side component Requires: glib-networking -Provides: cockpit-ssh = %{version}-%{release} # 233 dropped jquery.js, pages started to bundle it (commit 049e8b8dce) Conflicts: cockpit-dashboard < 233 Conflicts: cockpit-networkmanager < 233 diff --git a/tools/debian/cockpit-bridge.install b/tools/debian/cockpit-bridge.install index 2b68a23dbb5..6dc6a0cee24 100644 --- a/tools/debian/cockpit-bridge.install +++ b/tools/debian/cockpit-bridge.install @@ -1,7 +1,6 @@ etc/cockpit/machines.d usr/bin/cockpit-bridge usr/lib/cockpit/cockpit-askpass -usr/lib/cockpit/cockpit-ssh usr/lib/python* usr/share/cockpit/base1/ usr/share/man/man1/cockpit-bridge.1 diff --git a/tools/debian/control b/tools/debian/control index 4066af04b12..436c20e1b38 100644 --- a/tools/debian/control +++ b/tools/debian/control @@ -6,7 +6,6 @@ Build-Depends: debhelper-compat (= 13), dh-python, gettext (>= 0.19.7), gettext (>= 0.21) | appstream, - libssh-dev (>= 0.8.5), zlib1g-dev, libkrb5-dev (>= 1.11), libxslt1-dev, @@ -67,7 +66,6 @@ Depends: ${misc:Depends}, ${python3:Depends}, glib-networking Recommends: openssh-client -Provides: cockpit-ssh Breaks: cockpit-ws (<< 181.x), Replaces: cockpit-dashboard (<< 170.x), cockpit-ws (<< 181.x) Description: Cockpit bridge server-side component diff --git a/tools/debian/copyright.template b/tools/debian/copyright.template index 06d68339ca3..738c63fa41f 100644 --- a/tools/debian/copyright.template +++ b/tools/debian/copyright.template @@ -36,12 +36,6 @@ Copyright: Copyright (c) 2014 - 2016 Red Hat Copyright (c) 1995,2004 Silicon Graphics, Inc. All Rights Reserved. License: GPL-2+ -Files: src/ssh/mock-sshd.c -Copyright: Copyright 2003-2011 Aris Adamantiadis -License: public-domain - You are free to copy this file, modify it in any way, consider it being public - domain. - Files: tools/install-sh Copyright: Copyright (C) 1994 X Consortium License: MIT/X11 diff --git a/tools/debian/source/options b/tools/debian/source/options deleted file mode 100644 index a06658fc0f9..00000000000 --- a/tools/debian/source/options +++ /dev/null @@ -1,2 +0,0 @@ -# FIXME: Running the unit tests changes the identifier in that file -extend-diff-ignore = "src/ssh/mock_rsa_key.pub$" diff --git a/tools/make-dist b/tools/make-dist index d2edb8ef062..7c66ca8e9f2 100755 --- a/tools/make-dist +++ b/tools/make-dist @@ -47,7 +47,6 @@ else --enable-prefix-only \ --disable-pcp \ --disable-polkit \ - --disable-ssh \ > /dev/null fi