diff options
| -rw-r--r-- | irc.h | 2 | ||||
| -rw-r--r-- | irc_cap.c | 7 | ||||
| -rw-r--r-- | irc_commands.c | 122 | ||||
| -rw-r--r-- | root_commands.c | 12 | 
4 files changed, 143 insertions, 0 deletions
| @@ -49,6 +49,7 @@ typedef enum {  	USTATUS_SHUTDOWN = 8,   /* Now used to indicate we're shutting down.  	                           Currently just blocks irc_vawrite(). */  	USTATUS_CAP_PENDING = 16, +	USTATUS_SASL_PLAIN_PENDING = 32,  	/* Not really status stuff, but other kinds of flags: For slightly  	   better password security, since the only way to send passwords @@ -66,6 +67,7 @@ typedef enum {  } irc_status_t;  typedef enum { +	CAP_SASL = (1 << 0),  	CAP_MULTI_PREFIX = (1 << 1),  } irc_cap_flag_t; @@ -37,6 +37,7 @@ typedef struct {  } cap_info_t;  static const cap_info_t supported_caps[] = { +	{"sasl", CAP_SASL},  	{"multi-prefix", CAP_MULTI_PREFIX},  	{NULL},  }; @@ -169,6 +170,12 @@ void irc_cmd_cap(irc_t *irc, char **cmd)  	} else if (g_strcasecmp(cmd[1], "END") == 0) {  		irc->status &= ~USTATUS_CAP_PENDING; + +		if (irc->status & USTATUS_SASL_PLAIN_PENDING) { +			irc_send_num(irc, 906, ":SASL authentication aborted"); +			irc->status &= ~USTATUS_SASL_PLAIN_PENDING; +		} +  		irc_check_login(irc);  	} else { diff --git a/irc_commands.c b/irc_commands.c index 14a3fd9d..aa0ecb73 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -27,6 +27,7 @@  #include "bitlbee.h"  #include "help.h"  #include "ipc.h" +#include "base64.h"  static void irc_cmd_pass(irc_t *irc, char **cmd)  { @@ -57,6 +58,120 @@ static void irc_cmd_pass(irc_t *irc, char **cmd)  	}  } +static gboolean irc_sasl_plain_parse(char *input, char **user, char **pass) +{ +	int i, part, len; +	guint8 *decoded; +	char *parts[2]; + +	/* bitlbee's base64_decode wrapper adds an extra null terminator at the end */ +	len = base64_decode(input, &decoded); + +	/* this loop splits the decoded string into the parts array, like this: +	   "username\0username\0password" -> {"username", "username", "password"} */ + +	for (i = 0, part = 0; i < len && part < 3; part++) { +		/* set each of parts[] to point to the beginning of a string */ +		parts[part] = (char *) decoded + i; + +		/* move the cursor forward to the next null terminator*/ +		i += strlen(parts[part]) + 1; +	} + +	/* sanity checks */ +	if (part != 3 || i != (len + 1) || strcmp(parts[0], parts[1]) != 0) { +		g_free(decoded); +		return FALSE; +	} else { +		*user = g_strdup(parts[0]); +		*pass = g_strdup(parts[2]); +		g_free(decoded); +		return TRUE; +	} +} + +static gboolean irc_sasl_check_pass(irc_t *irc, char *user, char *pass) +{ +	storage_status_t status; + +	/* just check the password here to be able to reply with useful numerics +	 * the actual identification will be handled later */ +	status = storage_check_pass(user, pass); + +	if (status == STORAGE_OK) { +		if (!irc->user->nick) { +			/* set the nick here so we have it for the following numeric */ +			irc->user->nick = g_strdup(user); +		} +		irc_send_num(irc, 903, ":Password accepted"); +		return TRUE; + +	} else if (status == STORAGE_INVALID_PASSWORD) { +		irc_send_num(irc, 904, ":Incorrect password"); +	} else if (status == STORAGE_NO_SUCH_USER) { +		irc_send_num(irc, 904, ":The nick is (probably) not registered"); +	} else { +		irc_send_num(irc, 904, ":Unknown SASL authentication error"); +	} + +	return FALSE; +} + +static void irc_cmd_authenticate(irc_t *irc, char **cmd) +{ +	/* require the CAP to be enabled, and don't allow authentication before server password */ +	if (!(irc->caps & CAP_SASL) || +	    (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED))) { +		return; +	} + +	if (irc->status & USTATUS_SASL_PLAIN_PENDING) { +		char *user, *pass; + +		irc->status &= ~USTATUS_SASL_PLAIN_PENDING; + +		if (!irc_sasl_plain_parse(cmd[1], &user, &pass)) { +			irc_send_num(irc, 904, ":SASL authentication failed"); +			return; +		} + +		/* let's not support the nick != user case +		 * if NICK is received after SASL, it will just fail after registration */ +		if (user && irc->user->nick && strcmp(user, irc->user->nick) != 0) { +			irc_send_num(irc, 902, ":Your SASL username does not match your nickname"); + +		} else if (irc_sasl_check_pass(irc, user, pass)) { +			/* and here we do the same thing as the PASS command*/ +			if (irc->status & USTATUS_LOGGED_IN) { +				char *send_cmd[] = { "identify", pass, NULL }; +				root_command(irc, send_cmd); +			} else { +				/* no check_login here - wait for CAP END */ +				irc_setpass(irc, pass); +			} +		} + +		g_free(user); +		g_free(pass); + +	} else if (irc->status & USTATUS_IDENTIFIED) { +		irc_send_num(irc, 907, ":You have already authenticated"); + +	} else if (strcmp(cmd[1], "*") == 0) { +		irc_send_num(irc, 906, ":SASL authentication aborted"); +		irc->status &= ~USTATUS_SASL_PLAIN_PENDING; + +	} else if (g_strcasecmp(cmd[1], "PLAIN") == 0) { +		irc_write(irc, "AUTHENTICATE +"); +		irc->status |= USTATUS_SASL_PLAIN_PENDING; + +	} else { +		irc_send_num(irc, 908, "PLAIN :is the available SASL mechanism"); +		irc_send_num(irc, 904, ":SASL authentication failed"); +		irc->status &= ~USTATUS_SASL_PLAIN_PENDING; +	} +} +  static void irc_cmd_user(irc_t *irc, char **cmd)  {  	irc->user->user = g_strdup(cmd[1]); @@ -82,6 +197,12 @@ static void irc_cmd_nick(irc_t *irc, char **cmd)  			irc_setpass(irc, NULL);  			irc->status &= ~USTATUS_IDENTIFIED;  			irc_umode_set(irc, "-R", 1); + +			if (irc->caps & CAP_SASL) { +				irc_send_num(irc, 901, "%s!%s@%s :You are now logged out", +					irc->user->nick, irc->user->user, irc->user->host); +			} +  			irc_rootmsg(irc, "Changing nicks resets your identify status. "  			            "Re-identify or register a new account if you want "  			            "your configuration to be saved. See \x02help " @@ -721,6 +842,7 @@ static const command_t irc_commands[] = {  	{ "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },  	{ "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },  	{ "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER }, +	{ "authenticate", 1, irc_cmd_authenticate, 0 },  	{ NULL }  }; diff --git a/root_commands.c b/root_commands.c index 7e1dc2a7..b5d0aef1 100644 --- a/root_commands.c +++ b/root_commands.c @@ -162,6 +162,12 @@ static void cmd_identify(irc_t *irc, char **cmd)  		irc->status |= USTATUS_IDENTIFIED;  		irc_umode_set(irc, "+R", 1); +		if (irc->caps & CAP_SASL) { +			irc_user_t *iu = irc->user; +			irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s", +				iu->nick, iu->user, iu->host, iu->nick, iu->nick); +		} +  		bitlbee_whatsnew(irc);  		/* The following code is a bit hairy now. With takeover @@ -238,6 +244,12 @@ static void cmd_register(irc_t *irc, char **cmd)  		irc->status |= USTATUS_IDENTIFIED;  		irc_umode_set(irc, "+R", 1); +		if (irc->caps & CAP_SASL) { +			irc_user_t *iu = irc->user; +			irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s", +				iu->nick, iu->user, iu->host, iu->nick, iu->nick); +		} +  		/* Set this var now, or anyone who logs in to his/her  		   newly created account for the first time gets the  		   whatsnew story. */ | 
