diff -up cups-filters-1.0.35/NEWS.CVE-2014-4337 cups-filters-1.0.35/NEWS
diff -up cups-filters-1.0.35/utils/cups-browsed.c.CVE-2014-4337 cups-filters-1.0.35/utils/cups-browsed.c
--- cups-filters-1.0.35/utils/cups-browsed.c.CVE-2014-4337 2014-10-08 13:33:12.192067445 +0100
+++ cups-filters-1.0.35/utils/cups-browsed.c 2014-10-08 13:54:45.742569979 +0100
@@ -298,6 +298,75 @@ create_local_queue (const char *name,
return p;
}
+/*
+ * Remove all illegal characters and replace each group of such characters
+ * by a single dash, return a free()-able string.
+ *
+ * mode = 0: Only allow letters, numbers, and dashes, for turning make/model
+ * info into a valid print queue name or into a string which can
+ * be supplied as option value in a filter command line without
+ * need of quoting
+ * mode = 1: Allow also '/', '.', ',', '_', for cleaning up MIME type
+ * strings (here available Page Description Languages, PDLs) to
+ * supply them on a filter command line without quoting
+ *
+ * Especially this prevents from arbitrary code execution by interface scripts
+ * generated for print queues to native IPP printers when a malicious IPP
+ * print service with forged PDL and/or make/model info gets broadcasted into
+ * the local network.
+ */
+
+char * /* O - Cleaned string */
+remove_bad_chars(const char *str_orig, /* I - Original string */
+ int mode) /* I - 0: Make/Model, queue name */
+ /* 1: MIME types/PDLs */
+{
+ int i, j;
+ int havedash = 0;
+ char *str;
+
+ if (str_orig == NULL)
+ return NULL;
+
+ str = strdup(str_orig);
+
+ /* for later str[strlen(str)-1] access */
+ if (strlen(str) < 1)
+ return str;
+
+ for (i = 0, j = 0; i < strlen(str); i++, j++) {
+ if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
+ ((str[i] >= 'a') && (str[i] <= 'z')) ||
+ ((str[i] >= '0') && (str[i] <= '9')) ||
+ (mode == 1 && (str[i] == '/' || str[i] == '_' ||
+ str[i] == '.' || str[i] == ','))) {
+ /* Letter or number, keep it */
+ havedash = 0;
+ } else {
+ /* Replace all other characters by a single '-' */
+ if (havedash == 1)
+ j --;
+ else {
+ havedash = 1;
+ str[j] = '-';
+ }
+ }
+ }
+ /* Add terminating zero */
+ str[j] = '\0';
+ /* Cut off trailing dashes */
+ while (str[strlen(str)-1] == '-')
+ str[strlen(str)-1] = '\0';
+
+ /* Cut off leading dashes */
+ i = 0;
+ while (str[i] == '-')
+ ++i;
+
+ /* keep a free()-able string. +1 for trailing \0 */
+ return memmove(str, str + i, strlen(str) - i + 1);
+}
+
gboolean handle_cups_queues(gpointer unused) {
remote_printer_t *p;
http_t *http;
@@ -392,7 +461,7 @@ gboolean handle_cups_queues(gpointer unu
}
if (default_printer_name)
break;
- }
+ }
}
if (default_printer_name &&
!strcasecmp(default_printer_name, p->name)) {
@@ -572,19 +641,30 @@ void generate_local_queue(const char *ho
const char *name,
const char *type,
const char *domain) {
- char *remote_queue, *remote_host;
+ char *remote_queue = NULL, *remote_host = NULL;
remote_printer_t *p;
- char *backup_queue_name, *local_queue_name = NULL;
- cups_dest_t *dests, *dest;
+ char *backup_queue_name = NULL, *local_queue_name = NULL;
+ cups_dest_t *dests = NULL, *dest = NULL;
int i, num_dests;
- const char *val;
+ size_t hl = 0;
+ const char *val = NULL;
/* This is a remote CUPS queue, find queue name and host name */
- remote_queue = resource + 9;
- remote_host = strdup(host);
- if (!strcmp(remote_host + strlen(remote_host) - 6, ".local"))
+ if (strncasecmp(resource, "printers/", 9)) {
+ debug_printf("cups-browsed: resource does not begin 'printers/'\n");
+ return;
+ }
+
+ remote_queue = remove_bad_chars(resource + 9, 0);
+ /* Find the remote host name.
+ * Used in constructing backup_queue_name, so need to sanitize.
+ * strdup() is called inside remove_bad_chars() and result is free()-able.
+ */
+ remote_host = remove_bad_chars(host, 1);
+ hl = strlen(remote_host);
+ if (hl > 6 && !strcmp(remote_host + strlen(remote_host) - 6, ".local"))
remote_host[strlen(remote_host) - 6] = '\0';
- if (!strcmp(remote_host + strlen(remote_host) - 7, ".local."))
+ if (hl > 7 && !strcmp(remote_host + strlen(remote_host) - 7, ".local."))
remote_host[strlen(remote_host) - 7] = '\0';
debug_printf("cups-browsed: Found CUPS queue: %s on host %s.\n",
remote_queue, remote_host);
@@ -592,7 +672,7 @@ void generate_local_queue(const char *ho
/* Check if there exists already a CUPS queue with the
requested name Try name@host in such a case and if
this is also taken, ignore the printer */
- if ((backup_queue_name = malloc((strlen(remote_queue) +
+ if ((backup_queue_name = malloc((strlen(remote_queue) +
strlen(remote_host) + 2) *
sizeof(char))) == NULL) {
debug_printf("cups-browsed: ERROR: Unable to allocate memory.\n");
@@ -639,6 +719,7 @@ void generate_local_queue(const char *ho
local_queue_name);
free (backup_queue_name);
free (remote_host);
+ free (remote_queue);
cupsFreeDests(num_dests, dests);
return;
}
@@ -724,6 +805,7 @@ void generate_local_queue(const char *ho
free (backup_queue_name);
free (remote_host);
+ free (remote_queue);
if (p)
debug_printf("cups-browsed: Bonjour IDs: Service name: \"%s\", "
@@ -958,12 +1040,18 @@ found_cups_printer (const char *remote_h
char local_resource[HTTP_MAX_URI];
char *c;
+ memset(scheme, 0, sizeof(scheme));
+ memset(username, 0, sizeof(username));
+ memset(host, 0, sizeof(host));
+ memset(resource, 0, sizeof(resource));
+ memset(local_resource, 0, sizeof(local_resource));
+
httpSeparateURI (HTTP_URI_CODING_ALL, uri,
- scheme, sizeof(scheme),
- username, sizeof(username),
- host, sizeof(host),
+ scheme, sizeof(scheme) - 1,
+ username, sizeof(username) - 1,
+ host, sizeof(host) - 1,
&port,
- resource, sizeof(resource));
+ resource, sizeof(resource)- 1);
/* Check this isn't one of our own broadcasts */
for (iface = cupsArrayFirst (netifs);
@@ -1071,7 +1159,12 @@ process_browse_data (GIOChannel *source,
char remote_host[256];
char uri[1024];
char info[1024];
- char *c;
+ char *c = NULL, *end = NULL;
+
+ memset(packet, 0, sizeof(packet));
+ memset(remote_host, 0, sizeof(remote_host));
+ memset(uri, 0, sizeof(uri));
+ memset(info, 0, sizeof(info));
srclen = sizeof (srcaddr);
got = recvfrom (browsesocket, packet, sizeof (packet) - 1, 0,
@@ -1084,7 +1177,7 @@ process_browse_data (GIOChannel *source,
}
packet[got] = '\0';
- httpAddrString (&srcaddr, remote_host, sizeof (remote_host));
+ httpAddrString (&srcaddr, remote_host, sizeof (remote_host) - 1);
/* Check this packet is allowed */
if (!allowed ((struct sockaddr *) &srcaddr)) {
@@ -1102,28 +1195,42 @@ process_browse_data (GIOChannel *source,
}
info[0] = '\0';
+
+ /* do not read OOB */
+ end = packet + sizeof(packet);
c = strchr (packet, '\"');
+ if (c >= end)
+ return TRUE;
+
if (c) {
/* Skip location field */
- for (c++; *c != '\"'; c++)
+ for (c++; c < end && *c != '\"'; c++)
;
+ if (c >= end)
+ return TRUE;
+
if (*c == '\"') {
- for (c++; isspace(*c); c++)
+ for (c++; c < end && isspace(*c); c++)
;
}
+ if (c >= end)
+ return TRUE;
+
/* Is there an info field? */
if (*c == '\"') {
int i;
c++;
for (i = 0;
- i < sizeof (info) - 1 && *c != '\"';
+ i < sizeof (info) - 1 && *c != '\"' && c < end;
i++, c++)
info[i] = *c;
info[i] = '\0';
}
}
+ if (c >= end)
+ return TRUE;
found_cups_printer (remote_host, uri, info);
recheck_timer ();
@@ -1332,7 +1439,7 @@ send_browse_data (gpointer data)
while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
const char *attrname = ippGetName(attr);
int value_tag = ippGetValueTag(attr);
-
+
if (!strcmp(attrname, "printer-type") &&
value_tag == IPP_TAG_ENUM) {
type = ippGetInteger(attr, 0);