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