diff -up pidgin-2.10.7/libpurple/util.c.CVE-2013-6479 pidgin-2.10.7/libpurple/util.c
--- pidgin-2.10.7/libpurple/util.c.CVE-2013-6479 2013-02-11 04:16:53.000000000 -0500
+++ pidgin-2.10.7/libpurple/util.c 2014-01-28 19:09:20.896950189 -0500
@@ -33,6 +33,10 @@
#include "prefs.h"
#include "util.h"
+/* 512KiB Default value for maximum HTTP download size (when the client hasn't
+ specified a length) */
+#define DEFAULT_MAX_HTTP_DOWNLOAD (512 * 1024)
+
struct _PurpleUtilFetchUrlData
{
PurpleUtilFetchUrlCallback callback;
@@ -68,7 +72,7 @@ struct _PurpleUtilFetchUrlData
char *webdata;
gsize len;
unsigned long data_len;
- gssize max_len;
+ gsize max_len;
gboolean chunked;
PurpleAccount *account;
};
@@ -3239,24 +3243,26 @@ purple_strcasereplace(const char *string
return ret;
}
-const char *
-purple_strcasestr(const char *haystack, const char *needle)
+/** TODO: Expose this when we can add API */
+static const char *
+purple_strcasestr_len(const char *haystack, gssize hlen, const char *needle, gssize nlen)
{
- size_t hlen, nlen;
const char *tmp, *ret;
g_return_val_if_fail(haystack != NULL, NULL);
g_return_val_if_fail(needle != NULL, NULL);
- hlen = strlen(haystack);
- nlen = strlen(needle);
+ if (hlen == -1)
+ hlen = strlen(haystack);
+ if (nlen == -1)
+ nlen = strlen(needle);
tmp = haystack,
ret = NULL;
g_return_val_if_fail(hlen > 0, NULL);
g_return_val_if_fail(nlen > 0, NULL);
- while (*tmp && !ret) {
+ while (*tmp && !ret && (hlen - (tmp - haystack)) >= nlen) {
if (!g_ascii_strncasecmp(needle, tmp, nlen))
ret = tmp;
else
@@ -3266,6 +3272,12 @@ purple_strcasestr(const char *haystack,
return ret;
}
+const char *
+purple_strcasestr(const char *haystack, const char *needle)
+{
+ return purple_strcasestr_len(haystack, -1, needle, -1);
+}
+
char *
purple_str_size_to_units(size_t size)
{
@@ -3575,7 +3587,7 @@ static void ssl_url_fetch_connect_cb(gpo
static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data);
static gboolean
-parse_redirect(const char *data, size_t data_len,
+parse_redirect(const char *data, gsize data_len,
PurpleUtilFetchUrlData *gfud)
{
gchar *s;
@@ -3680,20 +3692,21 @@ parse_redirect(const char *data, size_t
return TRUE;
}
+/* find the starting point of the content for the specified header and make
+ * sure that the content is safe to pass to sscanf */
static const char *
-find_header_content(const char *data, size_t data_len, const char *header, size_t header_len)
+find_header_content(const char *data, gsize data_len, const char *header)
{
const char *p = NULL;
- if (header_len <= 0)
- header_len = strlen(header);
+ gsize header_len = strlen(header);
- /* Note: data is _not_ nul-terminated. */
if (data_len > header_len) {
+ /* Check if the first header matches (data won't start with a \n") */
if (header[0] == '\n')
p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL;
if (!p)
- p = purple_strcasestr(data, header);
+ p = purple_strcasestr_len(data, data_len, header, header_len);
if (p)
p += header_len;
}
@@ -3709,13 +3722,13 @@ find_header_content(const char *data, si
return NULL;
}
-static size_t
-parse_content_len(const char *data, size_t data_len)
+static gsize
+parse_content_len(const char *data, gsize data_len)
{
- size_t content_len = 0;
+ gsize content_len = 0;
const char *p = NULL;
- p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1);
+ p = find_header_content(data, data_len, "\nContent-Length: ");
if (p) {
sscanf(p, "%" G_GSIZE_FORMAT, &content_len);
purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len);
@@ -3725,9 +3738,9 @@ parse_content_len(const char *data, size
}
static gboolean
-content_is_chunked(const char *data, size_t data_len)
+content_is_chunked(const char *data, gsize data_len)
{
- const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1);
+ const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ");
if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0)
return TRUE;
@@ -3810,7 +3823,7 @@ url_fetch_recv_cb(gpointer url_data, gin
while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) ||
(!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0))
{
- if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) {
+ if((gfud->len + len) > gfud->max_len) {
purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"),
gfud->website.address, gfud->max_len);
return;
@@ -3838,9 +3851,8 @@ url_fetch_recv_cb(gpointer url_data, gin
/* See if we've reached the end of the headers yet */
end_of_headers = strstr(gfud->webdata, "\r\n\r\n");
if (end_of_headers) {
- char *new_data;
guint header_len = (end_of_headers + 4 - gfud->webdata);
- size_t content_len;
+ gsize content_len;
purple_debug_misc("util", "Response headers: '%.*s'\n",
header_len, gfud->webdata);
@@ -3860,15 +3872,36 @@ url_fetch_recv_cb(gpointer url_data, gin
content_len = 8192;
} else {
gfud->has_explicit_data_len = TRUE;
+ if (content_len > gfud->max_len) {
+ purple_debug_error("util",
+ "Overriding explicit Content-Length of %" G_GSIZE_FORMAT " with max of %" G_GSSIZE_FORMAT "\n",
+ content_len, gfud->max_len);
+ content_len = gfud->max_len;
+ }
}
/* If we're returning the headers too, we don't need to clean them out */
if (gfud->include_headers) {
+ char *new_data;
gfud->data_len = content_len + header_len;
- gfud->webdata = g_realloc(gfud->webdata, gfud->data_len);
+ new_data = g_try_realloc(gfud->webdata, gfud->data_len);
+ if (new_data == NULL) {
+ purple_debug_error("util",
+ "Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n",
+ content_len, g_strerror(errno));
+ purple_util_fetch_url_error(gfud,
+ _("Unable to allocate enough memory to hold "
+ "the contents from %s. The web server may "
+ "be trying something malicious."),
+ gfud->website.address);
+
+ return;
+ }
+ gfud->webdata = new_data;
} else {
- size_t body_len = gfud->len - header_len;
+ char *new_data;
+ gsize body_len = gfud->len - header_len;
content_len = MAX(content_len, body_len);
@@ -4154,7 +4187,11 @@ purple_util_fetch_url_request_len_with_a
gfud->request = g_strdup(request);
gfud->include_headers = include_headers;
gfud->fd = -1;
- gfud->max_len = max_len;
+ if (max_len <= 0) {
+ max_len = DEFAULT_MAX_HTTP_DOWNLOAD;
+ purple_debug_error("util", "Defaulting max download from %s to %" G_GSSIZE_FORMAT "\n", url, max_len);
+ }
+ gfud->max_len = (gsize) max_len;
gfud->account = account;
purple_url_parse(url, &gfud->website.address, &gfud->website.port,