/pidgin/main: bbd52f93184e: Implement SASL support for IRC, usin...

Thijs Alkemade thijsalkemade at gmail.com
Sun Sep 30 05:32:01 EDT 2012


I fully agree with that, a relatively large portion of the code is still his.

Thijs


On 30 sep. 2012, at 08:36, Mark Doliner <mark at kingant.net> wrote:

> Should Andy Spencer share credit for this in ChangeLog?  It looks like
> he's already listed in COPYRIGHT?
> 
> On Tue, Sep 25, 2012 at 5:30 PM, Thijs Alkemade <thijsalkemade at gmail.com> wrote:
>> Changeset: bbd52f93184ef305cfb8e5400c3941ad40ca89c4
>> Author:  Thijs Alkemade <thijsalkemade at gmail.com>
>> Date:    2012-09-25 22:58 +0200
>> Branch:  release-2.x.y
>> URL: http://hg.pidgin.im/pidgin/main/rev/bbd52f93184e
>> 
>> Description:
>> 
>> Implement SASL support for IRC, using Cyrus.
>> 
>> This is compatible with Freenode etc. That means
>> only PLAIN currently works, as Freenode only
>> offers PLAIN and DH-BLOWFISH, but we try a number
>> of others (as we can't query what the server
>> supports...) in case a server adds them.
>> 
>> Credit goes to Andy Spencer for the original patch.
>> 
>> Fixes #13270
>> 
>> diffstat:
>> 
>> libpurple/protocols/irc/irc.c   |   32 +++-
>> libpurple/protocols/irc/irc.h   |   18 +
>> libpurple/protocols/irc/msgs.c  |  383 ++++++++++++++++++++++++++++++++++++++++
>> libpurple/protocols/irc/parse.c |   13 +
>> 4 files changed, 445 insertions(+), 1 deletions(-)
>> 
>> diffs (truncated from 544 to 300 lines):
>> 
>> diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
>> --- a/libpurple/protocols/irc/irc.c
>> +++ b/libpurple/protocols/irc/irc.c
>> @@ -155,6 +155,7 @@ int irc_send_len(struct irc_conn *irc, c
>>        char *tosend= g_strdup(buf);
>> 
>>        purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend);
>> +
>>        if (tosend == NULL)
>>                return 0;
>> 
>> @@ -392,9 +393,17 @@ static gboolean do_login(PurpleConnectio
>>        const char *username, *realname;
>>        struct irc_conn *irc = gc->proto_data;
>>        const char *pass = purple_connection_get_password(gc);
>> +#ifdef HAVE_CYRUS_SASL
>> +       const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE);
>> +#endif
>> 
>>        if (pass && *pass) {
>> -               buf = irc_format(irc, "v:", "PASS", pass);
>> +#ifdef HAVE_CYRUS_SASL
>> +               if (use_sasl)
>> +                       buf = irc_format(irc, "vv:", "CAP", "REQ", "sasl");
>> +               else /* intended to fall through */
>> +#endif
>> +                       buf = irc_format(irc, "v:", "PASS", pass);
>>                if (irc_send(irc, buf) < 0) {
>>                        g_free(buf);
>>                        return FALSE;
>> @@ -526,6 +535,17 @@ static void irc_close(PurpleConnection *
>>        g_free(irc->mode_chars);
>>        g_free(irc->reqnick);
>> 
>> +#ifdef HAVE_CYRUS_SASL
>> +       if (irc->sasl_conn) {
>> +               sasl_dispose(&irc->sasl_conn);
>> +               irc->sasl_conn = NULL;
>> +       }
>> +       g_free(irc->sasl_cb);
>> +       if(irc->sasl_mechs)
>> +               g_string_free(irc->sasl_mechs, TRUE);
>> +#endif
>> +
>> +
>>        g_free(irc);
>> }
>> 
>> @@ -1045,6 +1065,16 @@ static void _init_plugin(PurplePlugin *p
>>        option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE);
>>        prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
>> 
>> +#ifdef HAVE_CYRUS_SASL
>> +       option = purple_account_option_bool_new(_("Authenticate with SASL"), "sasl", FALSE);
>> +       prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
>> +
>> +       option = purple_account_option_bool_new(
>> +                                               _("Allow plaintext SASL auth over unencrypted connection"),
>> +                                               "auth_plain_in_clear", FALSE);
>> +       prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
>> +#endif
>> +
>>        _irc_plugin = plugin;
>> 
>>        purple_prefs_remove("/plugins/prpl/irc/quitmsg");
>> diff --git a/libpurple/protocols/irc/irc.h b/libpurple/protocols/irc/irc.h
>> --- a/libpurple/protocols/irc/irc.h
>> +++ b/libpurple/protocols/irc/irc.h
>> @@ -25,6 +25,10 @@
>> 
>> #include <glib.h>
>> 
>> +#ifdef HAVE_CYRUS_SASL
>> +#include <sasl/sasl.h>
>> +#endif
>> +
>> #include "circbuffer.h"
>> #include "ft.h"
>> #include "roomlist.h"
>> @@ -92,6 +96,13 @@ struct irc_conn {
>>        char *mode_chars;
>>        char *reqnick;
>>        gboolean nickused;
>> +#ifdef HAVE_CYRUS_SASL
>> +       sasl_conn_t *sasl_conn;
>> +       const char *current_mech;
>> +       GString *sasl_mechs;
>> +       gboolean mech_works;
>> +       sasl_callback_t *sasl_cb;
>> +#endif
>> };
>> 
>> struct irc_buddy {
>> @@ -167,6 +178,13 @@ void irc_msg_unknown(struct irc_conn *ir
>> void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, char **args);
>> void irc_msg_whois(struct irc_conn *irc, const char *name, const char *from, char **args);
>> void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char **args);
>> +#ifdef HAVE_CYRUS_SASL
>> +void irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **args);
>> +void irc_msg_auth(struct irc_conn *irc, char *arg);
>> +void irc_msg_authok(struct irc_conn *irc, const char *name, const char *from, char **args);
>> +void irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, char **args);
>> +void irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char **args);
>> +#endif
>> 
>> void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args);
>> 
>> diff --git a/libpurple/protocols/irc/msgs.c b/libpurple/protocols/irc/msgs.c
>> --- a/libpurple/protocols/irc/msgs.c
>> +++ b/libpurple/protocols/irc/msgs.c
>> @@ -32,6 +32,10 @@
>> #include <stdio.h>
>> #include <stdlib.h>
>> 
>> +#ifdef HAVE_CYRUS_SASL
>> +#include <sasl/sasl.h>
>> +#endif
>> +
>> static char *irc_mask_nick(const char *mask);
>> static char *irc_mask_userhost(const char *mask);
>> static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2]);
>> @@ -42,6 +46,10 @@ static void irc_msg_handle_privmsg(struc
>>                                    const char *from, const char *to,
>>                                    const char *rawmsg, gboolean notice);
>> 
>> +#ifdef HAVE_CYRUS_SASL
>> +static void irc_sasl_finish(struct irc_conn *irc);
>> +#endif
>> +
>> static char *irc_mask_nick(const char *mask)
>> {
>>        char *end, *buf;
>> @@ -1426,6 +1434,381 @@ void irc_msg_wallops(struct irc_conn *ir
>>        g_free(msg);
>> }
>> 
>> +#ifdef HAVE_CYRUS_SASL
>> +static int
>> +irc_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret)
>> +{
>> +       struct irc_conn *irc = ctx;
>> +       const char *pw;
>> +       size_t len;
>> +
>> +       pw = purple_account_get_password(irc->account);
>> +
>> +       if (!conn || !secret || id != SASL_CB_PASS)
>> +               return SASL_BADPARAM;
>> +
>> +       len = strlen(pw);
>> +       /* Not an off-by-one because sasl_secret_t defines char data[1] */
>> +       /* TODO: This can probably be moved to glib's allocator */
>> +       sasl_secret_t *sasl_secret = malloc(sizeof(sasl_secret_t) + len);
>> +       if (!sasl_secret)
>> +               return SASL_NOMEM;
>> +
>> +       sasl_secret->len = len;
>> +       strcpy((char*)sasl_secret->data, pw);
>> +
>> +       *secret = sasl_secret;
>> +       return SASL_OK;
>> +}
>> +
>> +static int
>> +irc_sasl_cb_log(void *context, int level, const char *message)
>> +{
>> +       if(level <= SASL_LOG_TRACE)
>> +               purple_debug_info("sasl", "%s\n", message);
>> +
>> +       return SASL_OK;
>> +}
>> +
>> +static int
>> +irc_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len)
>> +{
>> +       struct irc_conn *irc = ctx;
>> +       PurpleConnection *gc = purple_account_get_connection(irc->account);
>> +
>> +       switch(id) {
>> +               case SASL_CB_AUTHNAME:
>> +                       *res = purple_connection_get_display_name(gc);
>> +                       break;
>> +               case SASL_CB_USER:
>> +                       *res = "";
>> +                       break;
>> +               default:
>> +                       return SASL_BADPARAM;
>> +       }
>> +       if (len) *len = strlen((char *)*res);
>> +       return SASL_OK;
>> +}
>> +
>> +static void
>> +irc_auth_start_cyrus(struct irc_conn *irc)
>> +{
>> +       int ret = 0;
>> +       char *buf;
>> +       sasl_security_properties_t secprops;
>> +       PurpleAccount *account = irc->account;
>> +       PurpleConnection *gc = purple_account_get_connection(account);
>> +
>> +       gboolean plaintext;
>> +       gboolean again = FALSE;
>> +
>> +       /* Set up security properties and options */
>> +       secprops.min_ssf = 0;
>> +       secprops.security_flags = SASL_SEC_NOANONYMOUS;
>> +
>> +       if (!irc->gsc) {
>> +               secprops.max_ssf = -1;
>> +               secprops.maxbufsize = 4096;
>> +               plaintext = purple_account_get_bool(account, "auth_plain_in_clear", FALSE);
>> +               if (!plaintext)
>> +                       secprops.security_flags |= SASL_SEC_NOPLAINTEXT;
>> +       } else {
>> +               secprops.max_ssf = 0;
>> +               secprops.maxbufsize = 0;
>> +               plaintext = TRUE;
>> +       }
>> +
>> +       secprops.property_names = 0;
>> +       secprops.property_values = 0;
>> +
>> +       do {
>> +               gchar *tmp = NULL;
>> +               again = FALSE;
>> +
>> +               ret = sasl_client_new("irc", irc->server, NULL, NULL, irc->sasl_cb, 0, &irc->sasl_conn);
>> +
>> +               if (ret != SASL_OK) {
>> +                       purple_debug_error("irc", "sasl_client_new failed: %d\n", ret);
>> +                       tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"),
>> +                               sasl_errdetail(irc->sasl_conn));
>> +                       purple_connection_error_reason (gc,
>> +                               PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
>> +                       g_free(tmp);
>> +                       return;
>> +               }
>> +
>> +               sasl_setprop(irc->sasl_conn, SASL_AUTH_EXTERNAL, irc->account->username);
>> +               sasl_setprop(irc->sasl_conn, SASL_SEC_PROPS, &secprops);
>> +
>> +               ret = sasl_client_start(irc->sasl_conn, irc->sasl_mechs->str, NULL, NULL, NULL, &irc->current_mech);
>> +
>> +               switch (ret) {
>> +                       case SASL_OK:
>> +                       case SASL_CONTINUE:
>> +                               irc->mech_works = FALSE;
>> +                               break;
>> +                       case SASL_NOMECH:
>> +                               purple_connection_error_reason (gc,
>> +                                       PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
>> +                                       _("SASL authentication failed: No worthy authentication mechanisms found."));
>> +
>> +                               irc_sasl_finish(irc);
>> +                               return;
>> +                       case SASL_BADPARAM:
>> +                       case SASL_NOMEM:
>> +                               tmp = g_strdup_printf(_("SASL authentication failed: %s"), sasl_errdetail(irc->sasl_conn));
>> +                               purple_connection_error_reason (gc,
>> +                                       PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp);
>> +                               g_free(tmp);
>> +
>> +                               irc_sasl_finish(irc);
>> +                               return;
>> +                       default:
>> +                               purple_debug_error("irc", "sasl_client_start failed: %s\n", sasl_errdetail(irc->sasl_conn));
>> +
>> +                               if (irc->current_mech && *irc->current_mech) {
>> +                                       char *pos;
>> +                                       if ((pos = strstr(irc->sasl_mechs->str, irc->current_mech))) {
>> +                                               size_t index = pos - irc->sasl_mechs->str;
>> +                                               g_string_erase(irc->sasl_mechs, index, strlen(irc->current_mech));
>> +
>> +                                               /* Remove space which separated this mech from the next */
>> +                                               if ((irc->sasl_mechs->str)[index] == ' ') {
>> +                                                       g_string_erase(irc->sasl_mechs, index, 1);
>> +                                               }
>> +                                       }
>> +
>> +                                       again = TRUE;
>> +                               }
>> +                               irc_sasl_finish(irc);
>> +               }
>> +       } while (again);
>> +
>> +       purple_debug_info("irc", "Using SASL: %s\n", irc->current_mech);
>> +
>> +       buf = irc_format(irc, "vv", "AUTHENTICATE", irc->current_mech);
>> +       irc_send(irc, buf);
>> +       g_free(buf);
>> +}
>> +
>> +/* SASL authentication */
>> +void
>> +irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **args)
>> +{
>> +       int ret = 0;
>> +       int id = 0;
>> +       PurpleConnection *gc = purple_account_get_connection(irc->account);
>> 
>> _______________________________________________
>> Commits mailing list
>> Commits at pidgin.im
>> http://pidgin.im/cgi-bin/mailman/listinfo/commits




More information about the Devel mailing list