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