Changeset 58b63de6 for irc_commands.c


Ignore:
Timestamp:
2015-10-08T08:34:18Z (9 years ago)
Author:
dequis <dx@…>
Branches:
master
Children:
c788e15
Parents:
2f73692
git-author:
dequis <dx@…> (11-09-15 22:07:10)
git-committer:
dequis <dx@…> (08-10-15 08:34:18)
Message:

IRCv3 SASL capability + PLAIN method

Only plain, no other methods. We don't have built-in SSL to implement
EXTERNAL (certfp) and nothing else is worth implementing.

The actual authentication is pretty much like sending a server password
(when the server's authmode isn't closed), which means it happens in
cmd_identify, but this code also calls storage_check_pass() to send the
required success/failure replies.

SASL doesn't give us much benefit other than standards compliance, but
some clients might appreciate it.

And having a fifth way to do the same thing doesn't hurt! Now we have:

  • identify in &bitlbee
  • identify to nickserv (alias for root)
  • 'nickserv' and 'ns' irc commands
  • server password
  • sasl plain
File:
1 edited

Legend:

Unmodified
Added
Removed
  • irc_commands.c

    r2f73692 r58b63de6  
    2828#include "help.h"
    2929#include "ipc.h"
     30#include "base64.h"
    3031
    3132static void irc_cmd_pass(irc_t *irc, char **cmd)
     
    5556                irc_setpass(irc, cmd[1]);
    5657                irc_check_login(irc);
     58        }
     59}
     60
     61static gboolean irc_sasl_plain_parse(char *input, char **user, char **pass)
     62{
     63        int i, part, len;
     64        guint8 *decoded;
     65        char *parts[2];
     66
     67        /* bitlbee's base64_decode wrapper adds an extra null terminator at the end */
     68        len = base64_decode(input, &decoded);
     69
     70        /* this loop splits the decoded string into the parts array, like this:
     71           "username\0username\0password" -> {"username", "username", "password"} */
     72
     73        for (i = 0, part = 0; i < len && part < 3; part++) {
     74                /* set each of parts[] to point to the beginning of a string */
     75                parts[part] = (char *) decoded + i;
     76
     77                /* move the cursor forward to the next null terminator*/
     78                i += strlen(parts[part]) + 1;
     79        }
     80
     81        /* sanity checks */
     82        if (part != 3 || i != (len + 1) || strcmp(parts[0], parts[1]) != 0) {
     83                g_free(decoded);
     84                return FALSE;
     85        } else {
     86                *user = g_strdup(parts[0]);
     87                *pass = g_strdup(parts[2]);
     88                g_free(decoded);
     89                return TRUE;
     90        }
     91}
     92
     93static gboolean irc_sasl_check_pass(irc_t *irc, char *user, char *pass)
     94{
     95        storage_status_t status;
     96
     97        /* just check the password here to be able to reply with useful numerics
     98         * the actual identification will be handled later */
     99        status = storage_check_pass(user, pass);
     100
     101        if (status == STORAGE_OK) {
     102                if (!irc->user->nick) {
     103                        /* set the nick here so we have it for the following numeric */
     104                        irc->user->nick = g_strdup(user);
     105                }
     106                irc_send_num(irc, 903, ":Password accepted");
     107                return TRUE;
     108
     109        } else if (status == STORAGE_INVALID_PASSWORD) {
     110                irc_send_num(irc, 904, ":Incorrect password");
     111        } else if (status == STORAGE_NO_SUCH_USER) {
     112                irc_send_num(irc, 904, ":The nick is (probably) not registered");
     113        } else {
     114                irc_send_num(irc, 904, ":Unknown SASL authentication error");
     115        }
     116
     117        return FALSE;
     118}
     119
     120static void irc_cmd_authenticate(irc_t *irc, char **cmd)
     121{
     122        /* require the CAP to be enabled, and don't allow authentication before server password */
     123        if (!(irc->caps & CAP_SASL) ||
     124            (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED))) {
     125                return;
     126        }
     127
     128        if (irc->status & USTATUS_SASL_PLAIN_PENDING) {
     129                char *user, *pass;
     130
     131                irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
     132
     133                if (!irc_sasl_plain_parse(cmd[1], &user, &pass)) {
     134                        irc_send_num(irc, 904, ":SASL authentication failed");
     135                        return;
     136                }
     137
     138                /* let's not support the nick != user case
     139                 * if NICK is received after SASL, it will just fail after registration */
     140                if (user && irc->user->nick && strcmp(user, irc->user->nick) != 0) {
     141                        irc_send_num(irc, 902, ":Your SASL username does not match your nickname");
     142
     143                } else if (irc_sasl_check_pass(irc, user, pass)) {
     144                        /* and here we do the same thing as the PASS command*/
     145                        if (irc->status & USTATUS_LOGGED_IN) {
     146                                char *send_cmd[] = { "identify", pass, NULL };
     147                                root_command(irc, send_cmd);
     148                        } else {
     149                                /* no check_login here - wait for CAP END */
     150                                irc_setpass(irc, pass);
     151                        }
     152                }
     153
     154                g_free(user);
     155                g_free(pass);
     156
     157        } else if (irc->status & USTATUS_IDENTIFIED) {
     158                irc_send_num(irc, 907, ":You have already authenticated");
     159
     160        } else if (strcmp(cmd[1], "*") == 0) {
     161                irc_send_num(irc, 906, ":SASL authentication aborted");
     162                irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
     163
     164        } else if (g_strcasecmp(cmd[1], "PLAIN") == 0) {
     165                irc_write(irc, "AUTHENTICATE +");
     166                irc->status |= USTATUS_SASL_PLAIN_PENDING;
     167
     168        } else {
     169                irc_send_num(irc, 908, "PLAIN :is the available SASL mechanism");
     170                irc_send_num(irc, 904, ":SASL authentication failed");
     171                irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
    57172        }
    58173}
     
    83198                        irc->status &= ~USTATUS_IDENTIFIED;
    84199                        irc_umode_set(irc, "-R", 1);
     200
     201                        if (irc->caps & CAP_SASL) {
     202                                irc_send_num(irc, 901, "%s!%s@%s :You are now logged out",
     203                                        irc->user->nick, irc->user->user, irc->user->host);
     204                        }
     205
    85206                        irc_rootmsg(irc, "Changing nicks resets your identify status. "
    86207                                    "Re-identify or register a new account if you want "
     
    722843        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
    723844        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
     845        { "authenticate", 1, irc_cmd_authenticate, 0 },
    724846        { NULL }
    725847};
Note: See TracChangeset for help on using the changeset viewer.