/pidgin/main: bbd52f93184e: Implement SASL support for IRC, usin...
Mark Doliner
mark at kingant.net
Sun Sep 30 02:36:57 EDT 2012
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