Experimenteller Support fuer Telnetoption START_TLS.

Diese Telnetoption kann von vom Client benutzt werden,
um eine unsichere Verbindung zu einer TLS-Verbindung
zu machen. Nach Aktivierung beim Client synchronieren
sich Server und Client und starten dann die TLS-
Verhandlung. Anschliessend wird die nun TLS-
Verbindung als neue Verbindung behandelt und der
Login-Prozess neu gestartet.

Change-Id: I9bf79d611311c46b1ef8317e26806a95fc94379d
diff --git a/secure/login.c b/secure/login.c
index a958b91..bd8726e 100644
--- a/secure/login.c
+++ b/secure/login.c
@@ -33,9 +33,11 @@
 #include <properties.h>
 #include <moving.h>
 #include "/secure/wizlevels.h"
+
 #include <telnet.h>
 #include <defines.h>
 #include <input_to.h>
+#include <configuration.h>
 
 inherit "/secure/mini_props.c";
 inherit "/secure/telnetneg.c";
@@ -135,47 +137,109 @@
         return 0;
 }
 
+// Callback when a TLS connection negotiation was finished. This handler is
+// called for negotiations startet by the telnet option START_TLS.
+protected void tls_init_callback(int handshake_result)
+{
+  if (handshake_result < 0)
+  {
+    // Fehler im Vebindungsaufbau
+    write(break_string(sprintf(
+           "Can't establish a TLS/SSL encrypted connection: %s."
+           "Disconnecting now. If this error persists, please "
+           "disable the usage of TLS or STARTTLS in your client.",
+           tls_error(handshake_result)),78));
+    // Disconnect
+    destruct(this_object());
+    return;
+  }
+  // In this case, we will treat the newly negotiated TLS connection as as new
+  // connection and just start over again by calling logon(). And re-enable
+  // the telnet machine of the driver of course.
+  configure_interactive(this_object(), IC_TELNET_ENABLED, 1);
+  logon();
+}
+
+// Called from the telnetneg handler for TELOPT_STARTTLS to initiate the TLS
+// connection negotiation.
+protected void init_tls()
+{
+  ::init_tls();
+  configure_interactive(this_object(), IC_TELNET_ENABLED, 0);
+  tls_init_connection(this_object(), #'tls_init_callback);
+}
+
+// Wenn der Client via STARTTLS eine TLS negotiation angestossen hat und
+// die noch laeuft, darf keine Ausgabe erfolgen. In diesem Fall wird das
+// Loginverfahren ausgesetzt, bis die TLS-Verhandlung abgeschlossen ist.
+// Danach wird es fortgesetzt bzw. neugestartet. Dies gilt auch fuer Fall,
+// dass STARTTLS verhandelt wurde, aber die TLS-Verhandlung noch nicht
+// laeuft. (Bemerkung: beides pruefen ist nicht ueberfluessig. Den Zustand
+// der Telnet-Option muss man pruefen, weil der Client evtl. seine
+// Verhandlung noch nicht signalisiert hat (FOLLOWS vom Client) und die
+// efun muss man pruefen, weil nach Empfang von FOLLOWS vom Client der
+// Status der Telnet-Optiosn resettet wurde - standardkonform.)
+private int check_tls_negotiation()
+{
+  struct telopt_s s_tls = query_telnet_neg()[TELOPT_STARTTLS];
+  if (tls_query_connection_state(this_object()) < 0
+      || (structp(s_tls) && s_tls->state->remoteside) )
+    return 1;
+
+  return 0;
+}
 
 /*
  * This is the function that gets called by /secure/master for every user
  */
 public nomask int logon()
 {
+    set_next_reset(300); // Timeout fuer Loginverfahren
     loginname = "logon";
     newbie=0;
     realip="";
 
-    // als erstes wird ein Lookup gemacht, ob die Quelladresse ein
-    // Tor-Exitnode ist, der erlaubt, zu uns zu kommunizieren. Das Lookup ist
-    // asynchron und braucht eine Weile, wenn das Ergebnis noch nicht gecacht
-    // ist. An dieser Stelle wird das Ergebnis nicht ausgewertet. Achja, wie
+    SendTelopts();
+    // In theory, we should not send anything if SendTelops() offers
+    // TELOPT_STARTTLS. However, some clients to not answer unknown telnet
+    // options and it would introduce a delay in any case. Therefore we send
+    // the welcome message anway, unless we received a WILL from the Client.
+
+    // Es wird ein Lookup gemacht, ob die Quelladresse ein Tor-Exitnode ist,
+    // der erlaubt, zu uns zu kommunizieren. Das Lookup ist asynchron und
+    // braucht eine Weile, wenn das Ergebnis noch nicht gecacht ist.
+    // An dieser Stelle wird das Ergebnis nicht ausgewertet. Achja, wie
     // machen das natuerlich nicht fuer die IP vom Mudrechner...
     if (query_ip_number(this_object()) != "87.79.24.60")
     {
-      "/p/daemon/dnslookup"->check_tor(query_ip_number(this_object()),query_mud_port());
+      "/p/daemon/dnslookup"->check_tor(query_ip_number(this_object()),
+          query_mud_port());
       "/p/daemon/dnslookup"->check_dnsbl(query_ip_number(this_object()));
     }
+
+    // ggf. muss TLS (initiiert durch STARTTLS) noch ausverhandelt werden.
+    if (check_tls_negotiation())
+      return 1; // Verbindung behalten
+
     printf("HTTP/1.0 302 Found\n"
          "Location: http://mg.mud.de/\n\n"
          "NetCologne, Koeln, Germany. Local time: %s\n\n"
          MUDNAME" LDmud, NATIVE mode, driver version %s\n\n",
          strftime("%c"), __VERSION__);
 
-    SendTelopts();
-
     if ( check_too_many_logons() ){
         destruct(this_object());
         return 0;
     }
 
     // ist die Verbindung schon wieder weg?
-    if (objectp(this_object()) && interactive(this_object())) {
-      cat( "/etc/WELCOME" );
-    }
+    if (!objectp(this_object()) || !interactive(this_object()))
+      return 0;
 
+
+    cat( "/etc/WELCOME" );
     input_to( "logon2", INPUT_PROMPT,
         "Wie heisst Du denn (\"neu\" fuer neuen Spieler)? ");
-    set_next_reset(300);
     return 1;
 }
 
@@ -279,7 +343,6 @@
     return 1;
 }
 
-
 static void logon2( string str )
 {
     int i, arg;