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;
diff --git a/secure/telnetneg.c b/secure/telnetneg.c
index 42d18c4..1b175a5 100644
--- a/secure/telnetneg.c
+++ b/secure/telnetneg.c
@@ -511,7 +511,7 @@
}
}
-// Der Handler fuer die BINARY option, wenn sie auf/fuer unserere Seite
+// Der Handler fuer die CHARSET option, wenn sie auf/fuer unserere Seite
// aktiviert/deaktivert wird.
private void _std_lo_handler_charset(struct telopt_s opt, int action,
int *data)
@@ -544,6 +544,58 @@
#undef TTABLE-IS
#undef TTABLE-REJECTED
+// Called from the telnetneg handler for TELOPT_STARTTLS to initiate the TLS
+// connection negotiation.
+protected void init_tls()
+{
+ // Dabei muss unser ganzer Telnet-Option-State muss zurueckgesetzt werden.
+ // Ja, wirklich! (Keine Sorge, der client muss das auch tun.)
+ TN = ([]);
+}
+
+#ifdef __TLS__
+// Der Handler fuer STARTTLS, wenn es auf der Clientseite
+// deaktiviert/aktiviert wird. Es wird nur auf der Clientseite aktiviert, der
+// Server darf kein WILL senden. Nach Aktivierung muessen wir ein FOLLOWS
+// senden.
+#define FOLLOWS 1
+private void _std_re_handler_starttls(struct telopt_s opt, int action,
+ int *data)
+{
+ DTN("starttls handler client",({action}));
+
+ // Wenn action == REMOTEON: Ab diesem Moment darf uns der Client einen
+ // STARTTLS FOLLOWS senden (weil wir haben ihm auch schon ein DO
+ // geschickt). Wir sollen ihm aber jetzt auch ein FOLLOWS senden. Sobald wir
+ // das gesendet haben und ein FOLLOWS erhalten haben, geht die Negotiation
+ // los.
+ if (action == REMOTEON)
+ {
+ send_telnet_neg(({ SB, TELOPT_STARTTLS, FOLLOWS }));
+ opt->data = 1; // Nur ein Flag, dass wir es gesendet haben.
+ }
+ else if (action == REMOTEOFF)
+ {
+ // data zuruecksetzen, sonst muessen wir nix machen.
+ opt->data = 0;
+ }
+ else if (action == SB)
+ {
+ if (data[0] == FOLLOWS)
+ {
+ // FOLLOWS empfangen. Wenn wir noch kein FOLLOWS gesendet haben, tun wir
+ // das jetzt.
+ if (!opt->data)
+ send_telnet_neg(({ SB, TELOPT_STARTTLS, FOLLOWS }));
+ // Jetzt wird die Verhandlung auf unserer Seite gestartet, der Client
+ // macht das entweder schon oder spaetestens, wenn er unser FOLLOWS
+ // empfangen kann.
+ init_tls();
+ }
+ }
+}
+#undef FOLLOWS
+#endif // __TLS__
// Bindet/registriert Handler fuer die jew. Telnet Option. (Oder loescht sie
// auch wieder.) Je nach <initneg> wird versucht, die Option neu zu
@@ -593,6 +645,15 @@
// laufen.
protected void SendTelopts()
{
+#if __TLS__
+ // If this is a non-TLS-connection, we offer STARTTLS, but wait for the
+ // client to ask for it.
+ if (tls_available() && tls_query_connection_state() == 0)
+ {
+ bind_telneg_handler(TELOPT_STARTTLS, #'_std_re_handler_starttls,
+ 0, 0);
+ }
+#endif
bind_telneg_handler(TELOPT_BINARY, #'_std_re_handler_binary,
#'_std_lo_handler_binary, 1);
bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 1);
@@ -604,12 +665,14 @@
// und auch CHARSET wird verzoegert bis das Spielerobjekt da ist.
}
-
// Bindet die Standardhandler _aus diesem_ Programm (und ueberschreibt dabei
// ggf. andere). Hierbei werden nur die Handler neu gebunden, keine neuen
// Verhandlungen initiiert.
// gerufen aus base.c indirekt via startup_telnet_negs().
-protected void _bind_telneg_std_handlers() {
+protected void _bind_telneg_std_handlers()
+{
+ // BTW: es ist absicht, im Spielerobjekt keinen Support fuer STARTTLS mehr
+ // anzubieten.
bind_telneg_handler(TELOPT_BINARY, #'_std_re_handler_binary,
#'_std_lo_handler_binary, 0);
bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 0);