From 29500b32c66dff16ec4aabf119a5772f007a007e Mon Sep 17 00:00:00 2001
From: Martin Pitt <mpitt@redhat.com>
Date: Wed, 5 Apr 2023 17:03:45 +0200
Subject: [PATCH] ws: Disallow direct URL logins with LoginTo=false

The current documentation of LoginTo= isn't very specific about what
exactly happens with a "false" value; but it is plausible for an admin
to assume that "false" would disallow logging into a remote host
completely -- not merely hide the "Connect to:" field and then allowing
a direct URL login anyway.

It is sometimes important to disallow direct SSH logins from the login
page on publicly exposed bastion hosts, as this functionality allows
unauthenticated remote users to:

 - scan the internal network for existing hosts, which might otherwise
   not be accessible directly from the internet
   (Fixes #18540, https://bugzilla.redhat.com/show_bug.cgi?id=2167006)

 - scan the cockpit-ws host or internal network hosts for open ports
   (Fixes #15077, https://bugzilla.redhat.com/show_bug.cgi?id=2018741)

So change ws to reject direct URL logins with `LoginTo=false`. This
happens most naturally in cockpit_session_launch(), as we still want to
allow remote URLs from the shell's host switcher in already
authenticated sessions. This will not produce a very friendly error
message, but it doesn't have to be -- at that point specifying direct
URLs can be considered hacking anyway.

Clarify the documentation accordingly.
Reference:https://github.com/cockpit-project/cockpit/commit/29500b32c66dff16ec4aabf119a5772f007a007e
Conflict:return NULL -> goto out;adapt context;delete test
---
 doc/man/cockpit.conf.xml       | 12 +++++++++---
 src/ws/cockpitauth.c           |  7 +++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/doc/man/cockpit.conf.xml b/doc/man/cockpit.conf.xml
index 798e1f3f5bf..eced0ebaaa2 100644
--- a/doc/man/cockpit.conf.xml
+++ b/doc/man/cockpit.conf.xml
@@ -87,9 +87,15 @@ ForwardedForHeader = X-Forwarded-For
         <term><option>LoginTo</option></term>
         <listitem>
           <para>When set to <literal>true</literal> the <emphasis>Connect to</emphasis> option
-            on the login screen is visible and allows logging into another server. If this
-            option is not specified then it will be automatically detected based on whether
-            the <command>cockpit-ssh</command> process is available or not.</para>
+            on the login screen is visible and allows logging into another server. When set to
+            <literal>false</literal>, direct remote logins are disallowed. If this option is not specified
+            then it will be automatically detected based on whether the
+            <command>cockpit-ssh</command> process is available or not.</para>
+
+          <para>If cockpit-ws is exposed to the public internet, and also has access to a private
+            internal network, it is recommended to explicitly set <literal>LoginTo=false</literal>. This prevents
+            unauthenticated remote attackers from scanning the internal network for existing machines
+            and open ports.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
diff --git a/src/ws/cockpitauth.c b/src/ws/cockpitauth.c
index bc62663d78a..9639a9c84de 100644
--- a/src/ws/cockpitauth.c
+++ b/src/ws/cockpitauth.c
@@ -1011,6 +1011,13 @@ cockpit_session_create (CockpitAuth *self,
       goto out;
     }
 
+  /* this might be unset, which means "allow if cockpit-ssh is installed"; if it isn't, this will fail later on */
+  if (host && !cockpit_conf_bool ("WebService", "LoginTo", TRUE)) {
+      g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED,
+                   "Direct remote login is disabled");
+      goto out;
+    }
+
   /* These are the credentials we'll carry around for this session */
   creds = build_session_credentials (self, connection, headers,
                                      application, type, authorization);
