Description: fix iq reply spoofing via incorrect from verification
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/93d4bff19574
Origin: upstream, http://hg.pidgin.im/pidgin/main/rev/b8e2a5fbffd3
Bug: https://developer.pidgin.im/ticket/15879 (regression fix)

Index: pidgin-2.10.7/libpurple/protocols/jabber/iq.c
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/jabber/iq.c	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/jabber/iq.c	2014-02-05 15:04:35.776164795 -0500
@@ -49,6 +49,18 @@
 static GHashTable *iq_handlers = NULL;
 static GHashTable *signal_iq_handlers = NULL;
 
+struct _JabberIqCallbackData {
+	JabberIqCallback *callback;
+	gpointer data;
+	JabberID *to;
+};
+
+void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd)
+{
+	jabber_id_free(jcd->to);
+	g_free(jcd);
+}
+
 JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
 {
 	JabberIq *iq;
@@ -98,11 +110,6 @@
 	return iq;
 }
 
-typedef struct _JabberCallbackData {
-	JabberIqCallback *callback;
-	gpointer data;
-} JabberCallbackData;
-
 void
 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);
 	}
 
@@ -274,20 +283,78 @@
 	g_hash_table_remove(js->iq_callbacks, id);
 }
 
+/**
+ * Verify that the 'from' attribute of an IQ reply is a valid match for
+ * a given IQ request. The expected behavior is outlined in section
+ * 8.1.2.1 of the XMPP CORE spec (RFC 6120). We consider the reply to
+ * be a valid match if any of the following is true:
+ * - Request 'to' matches reply 'from' (including the case where
+ *   neither are set).
+ * - Request 'to' was empty and reply 'from' is server JID.
+ * - Request 'to' was empty and reply 'from' is my JID. The spec says
+ *   we should only allow bare JID, but we also allow full JID for
+ *   compatibility with some servers.
+ *
+ * These rules should allow valid IQ replies while preventing spoofed
+ * ones.
+ *
+ * For more discussion see the "Spoofing of iq ids and misbehaving
+ * servers" email thread from January 2014 on the jdev and security
+ * mailing lists.
+ *
+ * @return TRUE if this reply is valid for the given request.
+ */
+static gboolean does_reply_from_match_request_to(JabberStream *js, JabberID *to, JabberID *from)
+{
+	if (jabber_id_equal(to, from)) {
+		/* Request 'to' matches reply 'from' */
+		return TRUE;
+	}
+
+	if (!to && purple_strequal(from->domain, js->user->domain)) {
+		/* Request 'to' is empty and reply 'from' domain matches our domain */
+
+		if (!from->node && !from->resource) {
+			/* Reply 'from' is server bare JID */
+			return TRUE;
+		}
+
+		if (purple_strequal(from->node, js->user->node)
+				&& (!from->resource || purple_strequal(from->resource, js->user->resource))) {
+			/* Reply 'from' is my full or bare JID */
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
 void jabber_iq_parse(JabberStream *js, xmlnode *packet)
 {
-	JabberCallbackData *jcd;
+	JabberIqCallbackData *jcd;
 	xmlnode *child, *error, *x;
 	const char *xmlns;
 	const char *iq_type, *id, *from;
 	JabberIqType type = JABBER_IQ_NONE;
 	gboolean signal_return;
+	JabberID *from_id;
 
 	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);
+		return;
+	}
+
+	/*
 	 * 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 +379,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);
 		return;
 	}
 
@@ -342,20 +410,39 @@
 			purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n",
 			                   iq_type);
 
+		jabber_id_free(from_id);
 		return;
 	}
 
 	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));
-	if (signal_return)
+	if (signal_return) {
+		jabber_id_free(from_id);
 		return;
+	}
 
 	/* 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);
-			return;
+		jcd = g_hash_table_lookup(js->iq_callbacks, id);
+		if (jcd) {
+			if (does_reply_from_match_request_to(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);
+				return;
+			} else {
+				char *expected_to;
+
+				if (jcd->to) {
+					expected_to = jabber_id_get_full_jid(jcd->to);
+				} else {
+					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);
+
+				g_free(expected_to);
+			}
 		}
 	}
 
@@ -372,12 +459,15 @@
 		if (signal_ref > 0) {
 			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));
-			if (signal_return)
+			if (signal_return) {
+				jabber_id_free(from_id);
 				return;
+			}
 		}
 
 		if(jih) {
 			jih(js, from, type, id, child);
+			jabber_id_free(from_id);
 			return;
 		}
 	}
@@ -404,6 +494,8 @@
 
 		jabber_iq_send(iq);
 	}
+
+	jabber_id_free(from_id);
 }
 
 void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc)
Index: pidgin-2.10.7/libpurple/protocols/jabber/iq.h
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/jabber/iq.h	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/jabber/iq.h	2014-02-05 15:04:33.256164728 -0500
@@ -36,6 +36,7 @@
 #include "connection.h"
 
 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);
Index: pidgin-2.10.7/libpurple/protocols/jabber/jabber.c
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/jabber/jabber.c	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/jabber/jabber.c	2014-02-05 15:04:33.256164728 -0500
@@ -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, g_free);
+			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();
Index: pidgin-2.10.7/libpurple/protocols/jabber/jutil.c
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/jabber/jutil.c	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/jabber/jutil.c	2014-02-05 15:04:35.776164795 -0500
@@ -508,6 +508,25 @@
 	}
 }
 
+
+gboolean
+jabber_id_equal(const JabberID *jid1, const JabberID *jid2)
+{
+	if (!jid1 && !jid2) {
+		/* Both are null therefore equal */
+		return TRUE;
+	}
+
+	if (!jid1 || !jid2) {
+		/* One is null, other is non-null, therefore not equal */
+		return FALSE;
+	}
+
+	return purple_strequal(jid1->node, jid2->node) &&
+			purple_strequal(jid1->domain, jid2->domain) &&
+			purple_strequal(jid1->resource, jid2->resource);
+}
+
 char *jabber_get_domain(const char *in)
 {
 	JabberID *jid = jabber_id_new(in);
@@ -536,6 +555,17 @@
 	return out;
 }
 
+JabberID *
+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);
+
+	return result;
+}
+
 char *
 jabber_get_bare_jid(const char *in)
 {
@@ -561,6 +591,19 @@
 	                   NULL);
 }
 
+char *
+jabber_id_get_full_jid(const JabberID *jid)
+{
+	g_return_val_if_fail(jid != NULL, NULL);
+
+	return g_strconcat(jid->node ? jid->node : "",
+	                   jid->node ? "@" : "",
+	                   jid->domain,
+	                   jid->resource ? "/" : "",
+	                   jid->resource ? jid->resource : "",
+	                   NULL);
+}
+
 gboolean
 jabber_jid_is_domain(const char *jid)
 {
Index: pidgin-2.10.7/libpurple/protocols/jabber/jutil.h
===================================================================
--- pidgin-2.10.7.orig/libpurple/protocols/jabber/jutil.h	2013-02-11 04:16:52.000000000 -0500
+++ pidgin-2.10.7/libpurple/protocols/jabber/jutil.h	2014-02-05 15:04:35.776164795 -0500
@@ -44,12 +44,21 @@
 #include "jabber.h"
 
 JabberID* jabber_id_new(const char *str);
+
+/**
+ * Compare two JIDs for equality. In addition to the node and domain,
+ * the resources of the two JIDs must also be equal (or both absent).
+ */
+gboolean jabber_id_equal(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);
 
