Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • irc_commands.c

    r9076a1c rc4e61db  
    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[3];
     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) || (parts[0][0] && strcmp(parts[0], parts[1]) != 0)) {
     83                g_free(decoded);
     84                return FALSE;
     85        } else {
     86                *user = g_strdup(parts[1]);
     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 "
     
    166287                           showed an error message, or is doing some work
    167288                           before the join should be confirmed. (In the
    168                            latter case, the callee should take care of that
     289                           latter case, the caller should take care of that
    169290                           confirmation.) TRUE means all's good, let the
    170291                           user join the channel right away. */
     
    685806
    686807static const command_t irc_commands[] = {
     808        { "cap",         1, irc_cmd_cap,         0 },
    687809        { "pass",        1, irc_cmd_pass,        0 },
    688810        { "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
     
    721843        { "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
    722844        { "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
     845        { "authenticate", 1, irc_cmd_authenticate, 0 },
    723846        { NULL }
    724847};
Note: See TracChangeset for help on using the changeset viewer.