Blame SOURCES/cups-filters-CVE-2014-4337.patch

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