From 162433398e320c45f3c8a523814518aa6b78372e Mon Sep 17 00:00:00 2001
From: Alexander Barton <alex@barton.de>
Date: Sun, 27 Mar 2011 19:33:48 +0200
Subject: [PATCH 1/2] New configuration option "RequireAuthPing": PING-PONG on
 login

When enabled, this configuration option lets ngIRCd send a PING with an
numeric "token" to clients logging in; and it will not become registered
in the network until the client responds with the correct PONG.

This is used by QuakeNet for example (ircu/snircd), and looks like this:

  NICK nick
  :irc.example.net PING :1858979527
  USER user . . :real name
  PONG 1858979527
  :irc.example.net 001 nick :Welcome to the Internet Relay Network ...
---
 src/ngircd/client.h    |  3 ++
 src/ngircd/conf.c      | 19 ++++++++--
 src/ngircd/conf.h      |  7 ++++
 src/ngircd/conn.c      | 19 ++++++++++
 src/ngircd/conn.h      |  8 +++++
 src/ngircd/irc-login.c | 82 +++++++++++++++++++++++++++++++++++-------
 src/ngircd/ngircd.c    |  2 ++
 src/ngircd/parse.c     |  2 +-
 8 files changed, 126 insertions(+), 16 deletions(-)

diff --git a/src/ngircd/client.h b/src/ngircd/client.h
index ff4ff2ff..fecf5d97 100644
--- a/src/ngircd/client.h
+++ b/src/ngircd/client.h
@@ -26,6 +26,9 @@
 #define CLIENT_SERVICE 64		/* client is a service */
 #define CLIENT_UNKNOWNSERVER 128	/* unregistered server connection */
 #define CLIENT_GOTPASS_2813 256		/* client did send PASS, RFC 2813 style */
+#ifndef STRICT_RFC
+# define CLIENT_WAITAUTHPING 512	/* waiting for AUTH PONG from client */
+#endif
 
 #define CLIENT_TYPE int
 
diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c
index 32461f35..452f744f 100644
--- a/src/ngircd/conf.c
+++ b/src/ngircd/conf.c
@@ -353,9 +353,12 @@ Conf_Test( void )
 	printf("  MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
 	printf("  MaxNickLength = %u\n", Conf_MaxNickLength - 1);
 	printf("  CloakHost = %s\n", Conf_CloakHost);
-	printf("  CloakUserToNick = %s\n\n", yesno_to_str(Conf_CloakUserToNick));
+	printf("  CloakUserToNick = %s\n", yesno_to_str(Conf_CloakUserToNick));
+#ifndef STRICT_RFC
+	printf("  RequireAuthPing = %s\n", yesno_to_str(Conf_AuthPing));
+#endif
 
-	puts("[FEATURES]");
+	printf("\n[FEATURES]\n");
 	printf("  DNS = %s\n", yesno_to_str(Conf_DNS));
 	printf("  Ident = %s\n", yesno_to_str(Conf_Ident));
 	printf("  PAM = %s\n", yesno_to_str(Conf_PAM));
@@ -641,6 +644,11 @@ Set_Defaults(bool InitServers)
 	Conf_SyslogFacility = 0;
 #endif
 #endif
+
+#ifndef STRICT_RFC
+	Conf_AuthPing = false;
+#endif
+
 	Set_Defaults_Optional();
 
 	/* Initialize server configuration structures */
@@ -1248,6 +1256,13 @@ Handle_GLOBAL( int Line, char *Var, char *Arg )
 							   Conf_SyslogFacility);
 		return;
 	}
+#endif
+#ifndef STRICT_RFC
+	if (strcasecmp(Var, "RequireAuthPing") == 0 ) {
+		/* Require new clients to do an "autheticatin PING-PONG" */
+		Conf_AuthPing = Check_ArgIsTrue(Arg);
+		return;
+	}
 #endif
 	Config_Error(LOG_ERR, "%s, line %d (section \"Global\"): Unknown variable \"%s\"!",
 								NGIRCd_ConfFile, Line, Var);
diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h
index 305ccaa1..a183fcec 100644
--- a/src/ngircd/conf.h
+++ b/src/ngircd/conf.h
@@ -199,6 +199,13 @@ GLOBAL int Conf_MaxConnectionsIP;
 /** Maximum length of a nick name */
 GLOBAL unsigned int Conf_MaxNickLength;
 
+#ifndef STRICT_RFC
+
+/** Require "AUTH PING-PONG" on login */
+GLOBAL bool Conf_AuthPing;
+
+#endif
+
 #ifdef SYSLOG
 
 /* Syslog "facility" */
diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c
index 63093c25..275215d6 100644
--- a/src/ngircd/conn.c
+++ b/src/ngircd/conn.c
@@ -2283,6 +2283,25 @@ Conn_GetFromProc(int fd)
 } /* Conn_GetFromProc */
 
 
+#ifndef STRICT_RFC
+
+GLOBAL long
+Conn_GetAuthPing(CONN_ID Idx)
+{
+	assert (Idx != NONE);
+	return My_Connections[Idx].auth_ping;
+} /* Conn_GetAuthPing */
+
+GLOBAL void
+Conn_SetAuthPing(CONN_ID Idx, long ID)
+{
+	assert (Idx != NONE);
+	My_Connections[Idx].auth_ping = ID;
+} /* Conn_SetAuthPing */
+
+#endif
+
+
 #ifdef SSL_SUPPORT
 
 /**
diff --git a/src/ngircd/conn.h b/src/ngircd/conn.h
index 4228c9e4..c813729f 100644
--- a/src/ngircd/conn.h
+++ b/src/ngircd/conn.h
@@ -91,6 +91,9 @@ typedef struct _Connection
 #ifdef SSL_SUPPORT
 	struct ConnSSL_State ssl_state;	/* SSL/GNUTLS state information */
 #endif
+#ifndef STRICT_RFC
+	long auth_ping;			/** PING response expected on login */
+#endif
 } CONNECTION;
 
 GLOBAL CONNECTION *My_Connections;
@@ -132,6 +135,11 @@ GLOBAL long Conn_Count PARAMS((void));
 GLOBAL long Conn_CountMax PARAMS((void));
 GLOBAL long Conn_CountAccepted PARAMS((void));
 
+#ifndef STRICT_RFC
+GLOBAL long Conn_GetAuthPing PARAMS((CONN_ID Idx));
+GLOBAL void Conn_SetAuthPing PARAMS((CONN_ID Idx, long ID));
+#endif
+
 #ifdef DEBUG
 GLOBAL void Conn_DebugDump PARAMS((void));
 #endif
diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c
index 92d54ab1..2e99d66e 100644
--- a/src/ngircd/irc-login.c
+++ b/src/ngircd/irc-login.c
@@ -271,6 +271,17 @@ IRC_NICK( CLIENT *Client, REQUEST *Req )
 			/* Register new nickname of this client */
 			Client_SetID( target, Req->argv[0] );
 
+#ifndef STRICT_RFC
+			if (Conf_AuthPing) {
+				Conn_SetAuthPing(Client_Conn(Client), random());
+				IRC_WriteStrClient(Client, "PING :%ld",
+					Conn_GetAuthPing(Client_Conn(Client)));
+				LogDebug("Connection %d: sent AUTH PING %ld ...",
+					Client_Conn(Client),
+					Conn_GetAuthPing(Client_Conn(Client)));
+			}
+#endif
+
 			/* If we received a valid USER command already then
 			 * register the new client! */
 			if( Client_Type( Client ) == CLIENT_GOTUSER )
@@ -797,18 +808,32 @@ GLOBAL bool
 IRC_PONG(CLIENT *Client, REQUEST *Req)
 {
 	CLIENT *target, *from;
+	CONN_ID conn;
+#ifndef STRICT_RFC
+	long auth_ping;
+#endif
 	char *s;
 
 	assert(Client != NULL);
 	assert(Req != NULL);
 
 	/* Wrong number of arguments? */
-	if (Req->argc < 1)
-		return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
-					  Client_ID(Client));
-	if (Req->argc > 2)
-		return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
-					  Client_ID(Client), Req->command);
+	if (Req->argc < 1) {
+		if (Client_Type(Client) == CLIENT_USER)
+			return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
+						  Client_ID(Client));
+		else
+			return CONNECTED;
+	}
+	if (Req->argc > 2) {
+		if (Client_Type(Client) == CLIENT_USER)
+			return IRC_WriteStrClient(Client,
+						  ERR_NEEDMOREPARAMS_MSG,
+						  Client_ID(Client),
+						  Req->command);
+		else
+			return CONNECTED;
+	}
 
 	/* Forward? */
 	if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
@@ -837,15 +862,35 @@ IRC_PONG(CLIENT *Client, REQUEST *Req)
 
 	/* The connection timestamp has already been updated when the data has
 	 * been read from so socket, so we don't need to update it here. */
+
+	conn = Client_Conn(Client);
+
+#ifndef STRICT_RFC
+	/* Check authentication PING-PONG ... */
+	auth_ping = Conn_GetAuthPing(conn);
+	if (auth_ping) {
+		LogDebug("AUTH PONG: waiting for token \"%ld\", got \"%s\" ...",
+			 auth_ping, Req->argv[0]);
+		if (auth_ping == atoi(Req->argv[0])) {
+			Conn_SetAuthPing(conn, 0);
+			if (Client_Type(Client) == CLIENT_WAITAUTHPING)
+				Hello_User(Client);
+		} else
+			if (!IRC_WriteStrClient(Client,
+					"To connect, type /QUOTE PONG %ld",
+					auth_ping))
+				return DISCONNECTED;
+	}
+#endif
+
 #ifdef DEBUG
-	if (Client_Conn(Client) > NONE)
+	if (conn > NONE)
 		Log(LOG_DEBUG,
-			"Connection %d: received PONG. Lag: %ld seconds.",
-			Client_Conn(Client),
+			"Connection %d: received PONG. Lag: %ld seconds.", conn,
 			time(NULL) - Conn_LastPing(Client_Conn(Client)));
 	else
 		 Log(LOG_DEBUG,
-			"Connection %d: received PONG.", Client_Conn(Client));
+			"Connection %d: received PONG.", conn);
 #endif
 	return CONNECTED;
 } /* IRC_PONG */
@@ -867,12 +912,25 @@ Hello_User(CLIENT * Client)
 {
 #ifdef PAM
 	int pipefd[2], result;
-	CONN_ID conn;
 	pid_t pid;
+#endif
+	CONN_ID conn;
 
 	assert(Client != NULL);
 	conn = Client_Conn(Client);
 
+#ifndef STRICT_RFC
+	if (Conf_AuthPing) {
+		/* Did we receive the "auth PONG" already? */
+		if (Conn_GetAuthPing(conn)) {
+			Client_SetType(Client, CLIENT_WAITAUTHPING);
+			LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
+			return CONNECTED;
+		}
+	}
+#endif
+
+#ifdef PAM
 	if (!Conf_PAM) {
 		/* Don't do any PAM authentication at all, instead emulate
 		 * the beahiour of the daemon compiled without PAM support:
@@ -903,8 +961,6 @@ Hello_User(CLIENT * Client)
 		exit(0);
 	}
 #else
-	assert(Client != NULL);
-
 	/* Check global server password ... */
 	if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
 		/* Bad password! */
diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c
index 4b49ec6c..74a99880 100644
--- a/src/ngircd/ngircd.c
+++ b/src/ngircd/ngircd.c
@@ -289,6 +289,8 @@ main( int argc, const char *argv[] )
 			exit(1);
 		}
 
+		srandom(getpid());
+
 		/* Create protocol and server identification. The syntax
 		 * used by ngIRCd in PASS commands and the known "extended
 		 * flags" are described in doc/Protocol.txt. */
diff --git a/src/ngircd/parse.c b/src/ngircd/parse.c
index 31f048cf..8f5e6019 100644
--- a/src/ngircd/parse.c
+++ b/src/ngircd/parse.c
@@ -82,7 +82,7 @@ static COMMAND My_Commands[] =
 	{ "PART", IRC_PART, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "PASS", IRC_PASS, 0xFFFF, 0, 0, 0 },
 	{ "PING", IRC_PING, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
-	{ "PONG", IRC_PONG, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
+	{ "PONG", IRC_PONG, 0xFFFF, 0, 0, 0 },
 	{ "PRIVMSG", IRC_PRIVMSG, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 },
 	{ "QUIT", IRC_QUIT, 0xFFFF, 0, 0, 0 },
 	{ "REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0 },

From fc0b0261496d2251e9917da96d5741da771f4bc1 Mon Sep 17 00:00:00 2001
From: Alexander Barton <alex@barton.de>
Date: Sun, 27 Mar 2011 20:34:44 +0200
Subject: [PATCH 2/2] Add documentation for "RequireAuthPing" configuration
 option

---
 doc/sample-ngircd.conf.tmpl | 5 +++++
 man/ngircd.conf.5.tmpl      | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/doc/sample-ngircd.conf.tmpl b/doc/sample-ngircd.conf.tmpl
index e07b5205..6e02048f 100644
--- a/doc/sample-ngircd.conf.tmpl
+++ b/doc/sample-ngircd.conf.tmpl
@@ -154,6 +154,11 @@
 	# maximum nick name length!
 	;MaxNickLength = 9
 
+	# Let ngIRCd send an "authentication PING" when a new client connects,
+	# and register this client only after receiving the corresponding
+	# "PONG" reply.
+	;RequireAuthPing = no
+
 	# Set this hostname for every client instead of the real one.
 	# Please note: don't use the percentage sign ("%"), it is reserved for
 	# future extensions!
diff --git a/man/ngircd.conf.5.tmpl b/man/ngircd.conf.5.tmpl
index e53cf3cc..bcdad1f8 100644
--- a/man/ngircd.conf.5.tmpl
+++ b/man/ngircd.conf.5.tmpl
@@ -251,6 +251,11 @@ Maximum length of an user nick name (Default: 9, as in RFC 2812). Please
 note that all servers in an IRC network MUST use the same maximum nick name
 length!
 .TP
+\fBRequireAuthPing\fR
+Let ngIRCd send an "authentication PING" when a new client connects, and
+register this client only after receiving the corresponding "PONG" reply.
+Default: no.
+.TP
 \fBCloakHost\fR
 Set this hostname for every client instead of the real one. Default: empty,
 don't change.