--- a/ChangeLog Sun Jan 12 19:29:36 2014 -0800
+++ b/ChangeLog Sun Jan 12 22:51:33 2014 -0800
@@ -69,6 +69,10 @@
(Discovered by Sourcefire VRT) (CVE-2014-NNNN)
+ * Prevent spoofing of iq replies by verifying that the 'from' address + matches the 'to' address of the iq request. (Discovered by Fabian + Yamaguchi and Christian Wressnegger of the University of Goettingen) * Fix possible crash or other erratic behavior when selecting a very
small file for your own buddy icon.
* Fix crash if the user tries to initiate a voice/video session with a
--- a/libpurple/protocols/jabber/iq.c Sun Jan 12 19:29:36 2014 -0800
+++ b/libpurple/protocols/jabber/iq.c Sun Jan 12 22:51:33 2014 -0800
@@ -49,6 +49,18 @@
static GHashTable *iq_handlers = NULL;
static GHashTable *signal_iq_handlers = NULL;
+struct _JabberIqCallbackData { + JabberIqCallback *callback; +void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd) + jabber_id_free(jcd->to); JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
@@ -98,11 +110,6 @@
-typedef struct _JabberCallbackData {
- JabberIqCallback *callback;
jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *callback, gpointer data)
@@ -125,15 +132,17 @@
void jabber_iq_send(JabberIq *iq)
- JabberCallbackData *jcd;
+ JabberIqCallbackData *jcd; g_return_if_fail(iq != NULL);
jabber_send(iq->js, iq->node);
if(iq->id && iq->callback) {
- jcd = g_new0(JabberCallbackData, 1);
+ jcd = g_new0(JabberIqCallbackData, 1); jcd->callback = iq->callback;
jcd->data = iq->callback_data;
+ jcd->to = jabber_id_new(xmlnode_get_attrib(iq->node, "to")); g_hash_table_insert(iq->js->iq_callbacks, g_strdup(iq->id), jcd);
@@ -276,18 +285,30 @@
void jabber_iq_parse(JabberStream *js, xmlnode *packet)
- JabberCallbackData *jcd;
+ JabberIqCallbackData *jcd; xmlnode *child, *error, *x;
const char *iq_type, *id, *from;
JabberIqType type = JABBER_IQ_NONE;
from = xmlnode_get_attrib(packet, "from");
id = xmlnode_get_attrib(packet, "id");
iq_type = xmlnode_get_attrib(packet, "type");
+ * Ensure the 'from' attribute is valid. No point in handling a stanza + * of which we don't understand where it came from. + from_id = jabber_id_new(from); + if (from && !from_id) { + purple_debug_error("jabber", "Received an iq with an invalid from: %s\n", from); * child will be either the first tag child or NULL if there is no child.
* Historically, we used just the 'query' subchild, but newer XEPs use
* differently named children. Grabbing the first child is (for the time
@@ -312,6 +333,7 @@
if (type == JABBER_IQ_NONE) {
purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n",
iq_type ? iq_type : "(null)");
+ jabber_id_free(from_id); @@ -342,20 +364,38 @@
purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n",
+ jabber_id_free(from_id); signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc),
"jabber-receiving-iq", js->gc, iq_type, id, from, packet));
+ jabber_id_free(from_id); /* First, lets see if a special callback got registered */
if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) {
if((jcd = g_hash_table_lookup(js->iq_callbacks, id))) {
- jcd->callback(js, from, type, id, packet, jcd->data);
- jabber_iq_remove_callback_by_id(js, id);
+ if(jabber_id_equal(js, jcd->to, from_id)) { + jcd->callback(js, from, type, id, packet, jcd->data); + jabber_iq_remove_callback_by_id(js, id); + jabber_id_free(from_id); + expected_to = jabber_id_get_full_jid(jcd->to); + expected_to = jabber_id_get_bare_jid(js->user); + purple_debug_error("jabber", "Got a result iq with id %s from %s instead of expected %s!\n", id, from ? from : "(null)", expected_to); @@ -372,12 +412,15 @@
signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), "jabber-watched-iq",
js->gc, iq_type, id, from, child));
+ jabber_id_free(from_id); jih(js, from, type, id, child);
+ jabber_id_free(from_id); @@ -404,6 +447,8 @@
+ jabber_id_free(from_id); void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc)
--- a/libpurple/protocols/jabber/iq.h Sun Jan 12 19:29:36 2014 -0800
+++ b/libpurple/protocols/jabber/iq.h Sun Jan 12 22:51:33 2014 -0800
@@ -36,6 +36,7 @@
typedef struct _JabberIq JabberIq;
+typedef struct _JabberIqCallbackData JabberIqCallbackData; * A JabberIqHandler is called to process an incoming IQ stanza.
@@ -96,6 +97,7 @@
void jabber_iq_parse(JabberStream *js, xmlnode *packet);
+void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd); void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id);
void jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *cb, gpointer data);
void jabber_iq_set_id(JabberIq *iq, const char *id);
--- a/libpurple/protocols/jabber/jabber.c Sun Jan 12 19:29:36 2014 -0800
+++ b/libpurple/protocols/jabber/jabber.c Sun Jan 12 22:51:33 2014 -0800
@@ -988,7 +988,7 @@
js->user_jb->subscription |= JABBER_SUB_BOTH;
js->iq_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify)jabber_iq_callbackdata_free); js->chats = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, (GDestroyNotify)jabber_chat_free);
js->next_id = g_random_int();
--- a/libpurple/protocols/jabber/jutil.c Sun Jan 12 19:29:36 2014 -0800
+++ b/libpurple/protocols/jabber/jutil.c Sun Jan 12 22:51:33 2014 -0800
@@ -508,6 +508,34 @@
+jabber_id_equal(JabberStream *js, const JabberID *jid1, const JabberID *jid2) + const JabberID *j1, *j2; + JabberID *bare_user_jid; + /* If an outgoing stanza has no 'to', or an incoming has no 'from', + * then those are "the server acting as my account". This function will + * handle that correctly. + bare_user_jid = jabber_id_to_bare_jid(js->user); + j1 = jid1 ? jid1 : bare_user_jid; + j2 = jid2 ? jid2 : bare_user_jid; + equal = purple_strequal(j1->node, j2->node) && + purple_strequal(j1->domain, j2->domain) && + purple_strequal(j1->resource, j2->resource); + jabber_id_free(bare_user_jid); char *jabber_get_domain(const char *in)
JabberID *jid = jabber_id_new(in);
@@ -536,6 +564,17 @@
+jabber_id_to_bare_jid(const JabberID *jid) + JabberID *result = g_new0(JabberID, 1); + result->node = g_strdup(jid->node); + result->domain = g_strdup(jid->domain); jabber_get_bare_jid(const char *in)
@@ -561,6 +600,19 @@
+jabber_id_get_full_jid(const JabberID *jid) + g_return_val_if_fail(jid != NULL, NULL); + return g_strconcat(jid->node ? jid->node : "", + jid->resource ? "/" : "", + jid->resource ? jid->resource : "", jabber_jid_is_domain(const char *jid)
--- a/libpurple/protocols/jabber/jutil.h Sun Jan 12 19:29:36 2014 -0800
+++ b/libpurple/protocols/jabber/jutil.h Sun Jan 12 22:51:33 2014 -0800
@@ -44,12 +44,23 @@
JabberID* jabber_id_new(const char *str);
+ * Compare two JIDs for equality. + * Warning: If either JID is NULL then this function uses the user's +gboolean jabber_id_equal(JabberStream *js, const JabberID *jid1, const JabberID *jid2); void jabber_id_free(JabberID *jid);
char *jabber_get_domain(const char *jid);
char *jabber_get_resource(const char *jid);
char *jabber_get_bare_jid(const char *jid);
char *jabber_id_get_bare_jid(const JabberID *jid);
+char *jabber_id_get_full_jid(const JabberID *jid); +JabberID *jabber_id_to_bare_jid(const JabberID *jid); gboolean jabber_jid_is_domain(const char *jid);