CVE-2022-24976: Improper Authentication (CWE-287)
value: Atheme IRC Services before 7.2.12, when used in conjunction with InspIRCd, allows authentication bypass by ending an IRC handshake at a certain point during a challenge-response login sequence.
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
CVSS v3 base score: 9.1
Published: 2022-02-14T12:15Z
Repository: https://github.com/atheme/atheme
Language: C
Files changed: include/atheme/sasl.h, modules/saslserv/main.c
Commit date: 2021-10-17 23:38:01 +0100
Commit message: saslserv/main: Track EID we're pending login to

The existing model does not remember that we've sent a SVSLOGIN for a
given SASL session, and simply assumes that if a client is introduced
with a SASL session open, that session must have succeeded. The security
of this approach requires ircd to implicitly abort SASL sessions on
client registration.

This also means that if a client successfully authenticates and then
does something else its pending login is forgotten about, even though a
SVSLOGIN has been sent for it, and the ircd is going to think it's
logged in.

This change removes the dependency on ircd's state machine by keeping
explicit track of the pending login, i.e. the one we've most recently
sent a SVSLOGIN for. The next commit will ensure that a client abort
(even an implicit one) doesn't blow that information away.

Diff: diff --git a/include/atheme/sasl.h b/include/atheme/sasl.h
index 65bf170ea..c9a09c604 100644
--- a/include/atheme/sasl.h
+++ b/include/atheme/sasl.h
@@ -60,10 +60,11 @@ struct sasl_session
 	unsigned int                    flags;                  // Flags (described above)
 	char                            authcid[NICKLEN + 1];   // Authentication identity (user having credentials verified)
 	char                            authzid[NICKLEN + 1];   // Authorization identity (user being logged in)
 	char                            authceid[IDLEN + 1];    // Entity ID for authcid
 	char                            authzeid[IDLEN + 1];    // Entity ID for authzid
+	char                            pendingeid[IDLEN + 1];  // Entity ID for pending login (for pre-reg clients)
 	char                            uid[UIDLEN + 1];        // Network UID
 };
 
 struct sasl_sourceinfo
 {
diff --git a/modules/saslserv/main.c b/modules/saslserv/main.c
index 1bf9702df..03721195d 100644
--- a/modules/saslserv/main.c
+++ b/modules/saslserv/main.c
@@ -383,19 +383,19 @@ sasl_handle_login(struct sasl_session *const restrict p, struct user *const u, s
 	bool was_killed = false;
 
 	// Find the account if necessary
 	if (! mu)
 	{
-		if (! *p->authzeid)
+		if (! *p->pendingeid)
 		{
-			(void) slog(LG_INFO, "%s: session for '%s' without an authzeid (BUG)",
+			(void) slog(LG_INFO, "%s: session for '%s' without an pendingeid (BUG)",
 			                     MOWGLI_FUNC_NAME, u->nick);
 			(void) notice(saslsvs->nick, u->nick, LOGIN_CANCELLED_STR);
 			return false;
 		}
 
-		if (! (mu = myuser_find_uid(p->authzeid)))
+		if (! (mu = myuser_find_uid(p->pendingeid)))
 		{
 			if (*p->authzid)
 				(void) notice(saslsvs->nick, u->nick, "Account %s dropped; login cancelled",
 				                                      p->authzid);
 			else
@@ -636,10 +636,12 @@ sasl_process_packet(struct sasl_session *const restrict p, char *const restrict
 					(void) notice(saslsvs->nick, u->nick, LOGIN_CANCELLED_STR);
 
 				return false;
 			}
 
+			(void) mowgli_strlcpy(p->pendingeid, p->authzeid, sizeof p->pendingeid);
+
 			/* If the user is already on the network, attempt to log them in immediately.
 			 * Otherwise, we will log them in on introduction of user to network
 			 */
 			if (u && ! sasl_handle_login(p, u, mu))
 				return false;

Vulnerable code: 		if (! *p->authzeid)
			(void) slog(LG_INFO, "%s: session for '%s' without an authzeid (BUG)",
		if (! (mu = myuser_find_uid(p->authzeid)))
Fixed code: 	char                            pendingeid[IDLEN + 1];  // Entity ID for pending login (for pre-reg clients)
		if (! *p->pendingeid)
			(void) slog(LG_INFO, "%s: session for '%s' without an pendingeid (BUG)",
		if (! (mu = myuser_find_uid(p->pendingeid)))
			(void) mowgli_strlcpy(p->pendingeid, p->authzeid, sizeof p->pendingeid);