--- a/configure.ac Fri Dec 16 21:41:46 2005 +0000
+++ b/configure.ac Sat Dec 17 02:24:05 2005 +0000
@@ -1402,6 +1402,11 @@
dnl AC_CHECK_SIZEOF(short)
AC_CHECK_FUNCS(snprintf connect)
+AC_ARG_ENABLE(cyrus-sasl, AC_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for jabberd (no)]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no) +if test "x-$enable_cyrus_sasl" = "x-yes" ; then + AC_CHECK_LIB(sasl2, sasl_client_init, [AC_DEFINE(HAVE_CYRUS_SASL, [1], [Define to 1 if Cyrus SASL is present]) SASL_LIBS=-"lsasl2"], [AC_ERROR(Cyrus SASL library not found)]) AC_DEFINE(ZEPHYR_INT32, long, [Size of an int32.])
@@ -1676,11 +1681,13 @@
echo Build with GtkSpell support... : $enable_gtkspell
echo Build with Voice/Video support : $enable_vv
echo Build with DBUS support....... : $enable_dbus
+echo Build with Cyrus SASL support. : $enable_cyrus_sasl if test x$enable_dbus = xyes ; then
echo DBUS session directory........ : $DBUS_SESSION_DIR
echo Has you....................... : yes
echo Use kerberos 4 with zephyr.... : $kerberos
echo Use external libzephyr........ : $zephyr
--- a/src/protocols/jabber/Makefile.am Fri Dec 16 21:41:46 2005 +0000
+++ b/src/protocols/jabber/Makefile.am Sat Dec 17 02:24:05 2005 +0000
@@ -36,7 +36,7 @@
-libjabber_la_LDFLAGS = -module -avoid-version $(GLIB_LIBS)
+libjabber_la_LDFLAGS = -module -avoid-version $(GLIB_LIBS) $(SASL_LIBS) --- a/src/protocols/jabber/auth.c Fri Dec 16 21:41:46 2005 +0000
+++ b/src/protocols/jabber/auth.c Sat Dec 17 02:24:05 2005 +0000
@@ -114,12 +114,188 @@
gaim_connection_error(account->gc, _("Server requires plaintext authentication over an unencrypted stream"));
+static void jabber_auth_start_cyrus(JabberStream *); +/* Callbacks for Cyrus SASL */ +static int jabber_sasl_cb_realm(void *ctx, int id, const char **avail, const char **result) + JabberStream *js = (JabberStream *)ctx; + if (id != SASL_CB_GETREALM || !result) return SASL_BADPARAM; + *result = js->user->domain; +static int jabber_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len) + JabberStream *js = (JabberStream *)ctx; + if (len) *len = strlen((char *)*res); +static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret) + JabberStream *js = (JabberStream *)ctx; + const char *pw = gaim_account_get_password(js->gc->account); + static sasl_secret_t *x = NULL; + if (!conn || !secret || id != SASL_CB_PASS) + x = (sasl_secret_t *) realloc(x, sizeof(sasl_secret_t) + len); + strcpy((char*)x->data, pw); +static void allow_cyrus_plaintext_auth(GaimAccount *account) + gaim_account_set_bool(account, "auth_plain_in_clear", TRUE); + jabber_auth_start_cyrus(account->gc->proto_data); +static void jabber_auth_start_cyrus(JabberStream *js) + const char *clientout, *mech; + sasl_security_properties_t secprops; + gboolean plaintext = TRUE; + /* Set up security properties and options */ + secprops.security_flags = SASL_SEC_NOANONYMOUS; + plaintext = gaim_account_get_bool(js->gc->account, "auth_plain_in_clear", FALSE); + secprops.security_flags |= SASL_SEC_NOPLAINTEXT; + secprops.maxbufsize = 4096; + secprops.maxbufsize = 0; + secprops.property_names = 0; + secprops.property_values = 0; + /* Use the user's domain for compatibility with the old + * DIGESTMD5 code. Note that this may cause problems where + * the user's domain doesn't match the FQDN of the jabber + js->sasl_state = sasl_client_new("xmpp", js->user->domain, NULL, NULL, js->sasl_cb, 0, &js->sasl); + if (js->sasl_state==SASL_OK) { + sasl_setprop(js->sasl, SASL_SEC_PROPS, &secprops); + js->sasl_state = sasl_client_start(js->sasl, js->sasl_mechs->str, NULL, &clientout, &coutlen, &mech); + switch (js->sasl_state) { + /* No mechanisms do what we want. See if we can add + * plaintext ones to the list. */ + if (!gaim_account_get_password(js->gc->account)) { + gaim_connection_error(js->gc, _("Server couldn't authenticate you without a password")); + } else if (!plaintext) { + gaim_request_yes_no(js->gc, _("Plaintext Authentication"), + _("Plaintext Authentication"), + _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), + allow_cyrus_plaintext_auth, + disallow_plaintext_auth); + gaim_connection_error(js->gc, _("Server does not use any supported authentication method")); + /* Fatal errors. Give up and go home */ + /* For everything else, fail the mechanism and try again */ + pos = strstr(js->sasl_mechs->str,mech); + g_string_erase(js->sasl_mechs, pos-js->sasl_mechs->str,strlen(mech)); + sasl_dispose(&js->sasl); + if (js->sasl_state == SASL_CONTINUE) { + auth = xmlnode_new("auth"); + xmlnode_set_attrib(auth, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); + xmlnode_set_attrib(auth,"mechanism", mech); + xmlnode_insert_data(auth, "=", -1); + enc_out = gaim_base64_encode((unsigned char*)clientout, coutlen); + xmlnode_insert_data(auth, enc_out, -1); + gaim_connection_error(js->gc, "SASL authentication failed\n"); jabber_auth_start(JabberStream *js, xmlnode *packet)
- xmlnode *mechs, *mechnode;
+ gboolean digest_md5 = FALSE, plain=FALSE; - gboolean digest_md5 = FALSE, plain=FALSE;
+ xmlnode *mechs, *mechnode; @@ -134,17 +310,59 @@
+ js->sasl_mechs = g_string_new(""); for(mechnode = xmlnode_get_child(mechs, "mechanism"); mechnode;
mechnode = xmlnode_get_next_twin(mechnode))
char *mech_name = xmlnode_get_data(mechnode);
+ g_string_append(js->sasl_mechs, mech_name); + g_string_append_c(js->sasl_mechs,' '); if(mech_name && !strcmp(mech_name, "DIGEST-MD5"))
else if(mech_name && !strcmp(mech_name, "PLAIN"))
+ js->auth_type = JABBER_AUTH_CYRUS; + /* Set up our callbacks structure */ + js->sasl_cb = g_new0(sasl_callback_t,5); + js->sasl_cb[id].id = SASL_CB_GETREALM; + js->sasl_cb[id].proc = jabber_sasl_cb_realm; + js->sasl_cb[id].context = (void *)js; + js->sasl_cb[id].id = SASL_CB_AUTHNAME; + js->sasl_cb[id].proc = jabber_sasl_cb_simple; + js->sasl_cb[id].context = (void *)js; + js->sasl_cb[id].id = SASL_CB_USER; + js->sasl_cb[id].proc = jabber_sasl_cb_simple; + js->sasl_cb[id].context = (void *)js; + if (gaim_account_get_password(js->gc->account)) { + js->sasl_cb[id].id = SASL_CB_PASS; + js->sasl_cb[id].proc = jabber_sasl_cb_secret; + js->sasl_cb[id].context = (void *)js; + js->sasl_cb[id].id = SASL_CB_LIST_END; + jabber_auth_start_cyrus(js); @@ -172,6 +390,7 @@
gaim_connection_error(js->gc,
_("Server does not use any supported authentication method"));
static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
@@ -463,17 +682,70 @@
g_hash_table_destroy(parts);
+ else if (js->auth_type == JABBER_AUTH_CYRUS) { + char *enc_in = xmlnode_get_data(packet); + unsigned int clen,declen; + dec_in = gaim_base64_decode(enc_in, &declen); + js->sasl_state = sasl_client_step(js->sasl, (char*)dec_in, declen, + if (js->sasl_state != SASL_CONTINUE && js->sasl_state != SASL_OK) { + gaim_debug_error("jabber", "Error is %d : %s\n",js->sasl_state,sasl_errdetail(js->sasl)); + gaim_connection_error(js->gc, _("SASL error")); + response = xmlnode_new("response"); + xmlnode_set_attrib(response, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl"); + enc_out = gaim_base64_encode((unsigned char*)c_out, clen); + xmlnode_insert_data(response, enc_out, -1); + jabber_send(js, response); + xmlnode_free(response); void jabber_auth_handle_success(JabberStream *js, xmlnode *packet)
const char *ns = xmlnode_get_attrib(packet, "xmlns");
if(!ns || strcmp(ns, "urn:ietf:params:xml:ns:xmpp-sasl")) {
gaim_connection_error(js->gc, _("Invalid response from server."));
+ /* The SASL docs say that if the client hasn't returned OK yet, we + * should try one more round against it + if (js->sasl_state != SASL_OK) { + js->sasl_state = sasl_client_step(js->sasl, NULL, 0, NULL, NULL, NULL); + if (js->sasl_state != SASL_OK) { + /* This should never happen! */ + gaim_connection_error(js->gc, _("Invalid response from server.")); + /* If we've negotiated a security layer, we need to enable it */ + sasl_getprop(js->sasl, SASL_SSF, (const void **)&x); + sasl_getprop(js->sasl, SASL_MAXOUTBUF, (const void **)&x); jabber_stream_set_state(js, JABBER_STREAM_REINITIALIZING);
--- a/src/protocols/jabber/jabber.c Fri Dec 16 21:41:46 2005 +0000
+++ b/src/protocols/jabber/jabber.c Sat Dec 17 02:24:05 2005 +0000
@@ -203,6 +203,42 @@
gaim_debug(GAIM_DEBUG_MISC, "jabber", "Sending%s: %s\n",
js->gsc ? " (ssl)" : "", data);
+ /* If we've got a security layer, we need to encode the data, + * splitting it on the maximum buffer length negotiated */ + if (js->sasl_maxbuf>0) { + if (!js->gsc && js->fd<0) + if ((len - pos) < js->sasl_maxbuf) + towrite = js->sasl_maxbuf; + sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); + ret = gaim_ssl_write(js->gsc, out, olen); + ret = write(js->fd, out, olen); + gaim_connection_error(js->gc, _("Write error")); ret = gaim_ssl_write(js->gsc, data, len == -1 ? strlen(data) : len);
@@ -266,6 +302,18 @@
if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) {
+ if (js->sasl_maxbuf>0) { + sasl_decode(js->sasl, buf, len, &out, &olen); + gaim_debug(GAIM_DEBUG_INFO, "jabber", "RecvSASL (%d): %s\n", olen, out); + jabber_parser_process(js,out,olen); gaim_debug(GAIM_DEBUG_INFO, "jabber", "Recv (%d): %s\n", len, buf);
jabber_parser_process(js, buf, len);
@@ -819,6 +867,14 @@
jabber_id_free(js->user);
+ sasl_dispose(&js->sasl); + g_string_free(js->sasl_mechs, TRUE); @@ -1608,7 +1664,8 @@
static GaimPluginProtocolInfo prpl_info =
- OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME,
+ OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | + OPT_PROTO_PASSWORD_OPTIONAL, NULL, /* protocol_options */
{"jpeg,gif,png", 0, 0, 96, 96, GAIM_ICON_SCALE_DISPLAY}, /* icon_spec */
@@ -1743,6 +1800,10 @@
gaim_prefs_remove("/plugins/prpl/jabber");
+ /* XXX - If any other plugin wants SASL this won't be good ... */ + sasl_client_init(NULL); jabber_register_commands();
--- a/src/protocols/jabber/jabber.h Fri Dec 16 21:41:46 2005 +0000
+++ b/src/protocols/jabber/jabber.h Sat Dec 17 02:24:05 2005 +0000
@@ -30,6 +30,10 @@
JABBER_CAP_XHTML = 1 << 0,
@@ -68,7 +72,8 @@
@@ -102,6 +107,18 @@
GSList *pending_avatar_requests;
+ /* OK, this stays at the end of the struct, so plugins can depend + * on the rest of the stuff being in the right place + sasl_callback_t *sasl_cb; void jabber_process_packet(JabberStream *js, xmlnode *packet);