LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File cups-1.5.4-CVE-2012-5519.patch of Package cups154 (Project home:mgoppold:printing)

--- doc/help/ref-cupsd-conf.html.in.orig	2012-01-30 22:40:21.000000000 +0100
+++ doc/help/ref-cupsd-conf.html.in	2014-02-05 14:13:23.000000000 +0100
@@ -917,6 +917,28 @@ ConfigFilePerm 0640
 
 </BLOCKQUOTE>
 
+<H2 CLASS="title"><A NAME="ConfigurationChangeRestriction">ConfigurationChangeRestriction</A></H2>
+
+<H3>Examples</H3>
+
+<PRE CLASS="command">
+ConfigurationChangeRestriction all
+ConfigurationChangeRestriction root-only
+ConfigurationChangeRestriction none
+</PRE>
+
+<H3>Description</H3>
+
+<P>The <CODE>ConfigurationChangeRestriction</CODE> directive specifies
+the degree of restriction for changes to cupsd.conf.  Keywords dealing
+with filenames, paths, and users are security-sensitive. Changes to
+them via HTTP are forbidden by default (<CODE>all</CODE>). The value
+<CODE>none</CODE> removes any restriction altogether (note that this
+is unsafe). The value <CODE>root-only</CODE> allows only users
+authorised as user "root" to adjust security-sensitive configuration
+settings, but note that users adjusting settings using polkit (via
+cups-pk-helper) are authenticated as user "root".</P>
+
 
 <H2 CLASS="title"><A NAME="DataDir">DataDir</A></H2>
 
--- man/cupsctl.man.orig	2011-01-11 04:04:04.000000000 +0100
+++ man/cupsctl.man	2014-02-05 14:15:23.000000000 +0100
@@ -90,7 +90,8 @@ Disable printer sharing:
     cupsctl --no-shared-printers
 .fi
 .LP
-Enable printing using the file: pseudo-device:
+Enable printing using the file: pseudo-device (note that this is
+forbidden by default):
 .nf
     cupsctl FileDevice=Yes
 .fi
--- man/cupsd.conf.man.in.orig	2011-05-18 23:33:35.000000000 +0200
+++ man/cupsd.conf.man.in	2014-02-05 14:16:58.000000000 +0100
@@ -238,6 +238,21 @@ ConfigFilePerm mode
 Specifies the permissions for all configuration files that the scheduler
 writes.
 .TP 5
+ConfigurationChangeRestriction all
+.TP 5
+ConfigurationChangeRestriction root-only
+.TP 5
+ConfigurationChangeRestriction none
+.br
+Specifies the degree of restriction for changes to cupsd.conf.
+Keywords dealing with filenames, paths, and users are
+security-sensitive. Changes to them via HTTP are forbidden by default
+("all"). The value "none" removes any restriction altogether (note
+that this is unsafe). The value "root-only" allows only users
+authorised as user "root" to adjust security-sensitive configuration
+settings, but note that users adjusting settings using polkit (via
+cups-pk-helper) are authenticated as user "root".
+.TP 5
 DataDir path
 .br
 Specified the directory where data files can be found.
--- scheduler/client.c.orig	2012-03-07 07:05:39.000000000 +0100
+++ scheduler/client.c	2014-02-05 14:32:49.000000000 +0100
@@ -1685,13 +1685,10 @@ cupsdReadClient(cupsd_client_t *con)	/*
 	    * Validate the resource name...
 	    */
 
-            if (strncmp(con->uri, "/admin/conf/", 12) ||
-	        strchr(con->uri + 12, '/') ||
-		strlen(con->uri) == 12)
+            if (strcmp(con->uri, "/admin/conf/cupsd.conf"))
 	    {
 	     /*
-	      * PUT can only be done to configuration files under
-	      * /admin/conf...
+	      * PUT can only be done to the cupsd.conf file...
 	      */
 
 	      cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -3827,6 +3824,8 @@ install_conf_file(cupsd_client_t *con)	/
   char		buffer[16384];		/* Copy buffer */
   ssize_t	bytes;			/* Number of bytes */
 
+  if (!cupsdCheckConfigurationAllowed (con))
+    return (HTTP_FORBIDDEN);
 
  /*
   * Open the request file...
--- scheduler/conf.h.orig	2011-04-22 19:47:03.000000000 +0200
+++ scheduler/conf.h	2014-02-05 14:44:49.000000000 +0100
@@ -92,6 +92,18 @@ typedef struct
 
 
 /*
+ * Configuration change restriction (CVE-2012-5519)
+ */
+
+typedef enum
+{
+  CUPSD_CONFRESTRICT_NONE,     /* No checking of PUT cupsd.conf */
+  CUPSD_CONFRESTRICT_ROOT,     /* Only allow root to change all opts */
+  CUPSD_CONFRESTRICT_ALL,      /* Restricted keywords not to be changed */
+} cupsd_confrestrict_t;
+
+
+/*
  * Globals...
  */
 
@@ -165,6 +177,8 @@ VAR int			ClassifyOverride	VALUE(0),
 					/* Allow overrides? */
 			ConfigFilePerm		VALUE(0640),
 					/* Permissions for config files */
+                        ConfigurationChangeRestriction VALUE(CUPSD_CONFRESTRICT_ALL),
+                                        /* CVE-2012-5519 protection */
 			LogDebugHistory		VALUE(200),
 					/* Amount of automatic debug history */
 			FatalErrors		VALUE(CUPSD_FATAL_CONFIG),
@@ -291,6 +305,7 @@ __attribute__ ((__format__ (__printf__,
 extern int	cupsdLogPage(cupsd_job_t *job, const char *page);
 extern int	cupsdLogRequest(cupsd_client_t *con, http_status_t code);
 extern int	cupsdReadConfiguration(void);
+extern int     cupsdCheckConfigurationAllowed(cupsd_client_t *con);
 extern int	cupsdWriteErrorLog(int level, const char *message);
 
 
--- scheduler/conf.c.orig	2011-11-16 16:28:11.000000000 +0100
+++ scheduler/conf.c	2014-02-05 15:03:28.000000000 +0100
@@ -3196,6 +3196,22 @@ read_configuration(cups_file_t *fp)	/* I
       cupsdLogMessage(CUPSD_LOG_INFO, "Polling %s:%d", pollp->hostname,
 	              pollp->port);
     }
+    else if (!strcasecmp(line, "ConfigurationChangeRestriction") && value)
+    {
+      if (!strcasecmp(value, "none"))
+       ConfigurationChangeRestriction = CUPSD_CONFRESTRICT_NONE;
+      else if (!strcasecmp(value, "root-only"))
+       ConfigurationChangeRestriction = CUPSD_CONFRESTRICT_ROOT;
+      else if (!strcasecmp(value, "all"))
+       ConfigurationChangeRestriction = CUPSD_CONFRESTRICT_ALL;
+      else
+      {
+       cupsdLogMessage(CUPSD_LOG_WARN,
+                       "Unknown restriction type %s on line %d.",
+                       value, linenum);
+       return (0);
+      }
+    }
     else if (!_cups_strcasecmp(line, "DefaultAuthType") && value)
     {
      /*
@@ -3657,6 +3673,250 @@ read_configuration(cups_file_t *fp)	/* I
 }
 
 
+static cups_array_t *
+_cupsdGetBlacklistedConfLines(cups_file_t *fp)
+{
+  cups_array_t *conf;
+  int linenum;
+  char keyword[HTTP_MAX_BUFFER],
+    *temp,
+    *value;
+  const char **kw;
+  size_t len;
+  const char *blacklist[] = {
+    "ConfigurationChangeRestriction",
+    "AccessLog",
+    "BrowseLDAPCACertFile",
+    "CacheDir",
+    "ConfigFilePerm",
+    "DataDir",
+    "DocumentRoot",
+    "ErrorLog",
+    "FatalErrors",
+    "FileDevice",
+    "FontPath",
+    "Group",
+    "JobPrivateAccess",
+    "JobPrivateValues",
+    "LogFilePerm",
+    "PageLog",
+    "Printcap",
+    "PrintcapFormat",
+    "PrintcapGUI",
+    "RemoteRoot",
+    "RequestRoot",
+    "ServerBin",
+    "ServerCertificate",
+    "ServerKey",
+    "ServerRoot",
+    "StateDir",
+    "SubscriptionPrivateAccess",
+    "SubscriptionPrivateValues",
+    "SystemGroup",
+    "SystemGroupAuthKey",
+    "TempDir",
+    "User",
+    "WebInterface",
+    NULL
+  };
+
+  conf = cupsArrayNew (NULL, NULL);
+
+ /*
+  * Loop through each line in the file...
+  */
+
+  linenum = 0;
+
+  while (cupsFileGetConf(fp, keyword, sizeof(keyword), &value, &linenum))
+  {
+    for (kw = blacklist; *kw; kw++)
+      if (!strcasecmp (keyword, *kw))
+       break;
+
+    if (*kw == NULL)
+      continue;
+
+   /*
+    * Remember lines we might need to compare against, but only the
+    * last occurrence of each keyword, except for
+    * SystemGroup. SystemGroup is special because it is cumulative:
+    * each SystemGroup line adds groups to the list. For that reason,
+    * we remember multiple SystemGroup lines and don't care about the
+    * order...
+    */
+
+    len = strlen (keyword);
+    if (strcasecmp(keyword, "SystemGroup") != 0)
+    {
+      for (temp = (char *) cupsArrayFirst(conf);
+          temp;
+          temp = (char *) cupsArrayNext(conf))
+      {
+       if (!strncasecmp (temp, keyword, len) && temp[len] == ' ')
+       {
+         cupsArrayRemove(conf, temp);
+
+         /*
+          * There can only be one such line because we do this for each
+          * line containing a blacklisted keyword
+          */
+
+         break;
+       }
+      }
+    }
+
+    len += (value ? strlen (value) : 0) + 2;
+    temp = malloc (len);
+    if (!temp)
+      goto fail;
+
+    snprintf (temp, len, "%s %s", keyword, value ? value : "");
+    cupsArrayAdd(conf, temp);
+  }
+
+  return conf;
+
+fail:
+  for (temp = (char *) cupsArrayFirst(conf);
+       temp;
+       temp = (char *) cupsArrayNext(conf))
+    free(temp);
+  cupsArrayDelete(conf);
+  return NULL;
+}
+
+
+/*
+ * 'cupsdCheckConfigurationAllowed()' - Check whether the new configuration
+ *                                      file can be installed
+ */
+
+int                                    /* O - 1 if allowed, 0 otherwise */
+cupsdCheckConfigurationAllowed(cupsd_client_t *con)
+{
+  int status = 0;
+  cups_file_t *fp;
+  cups_array_t *oldconf,
+    *newconf = NULL;
+  char *oldline,
+    *newline;
+
+  if (ConfigurationChangeRestriction == CUPSD_CONFRESTRICT_NONE)
+   /*
+    * Option checking disabled...
+    */
+    return (1);
+
+  if (ConfigurationChangeRestriction == CUPSD_CONFRESTRICT_ROOT &&
+      !strcmp (con->username, "root"))
+   /*
+    * This is requested by root and our configuration tells us to
+    * accept it.
+    */
+    return (1);
+
+ /*
+  * First read the current cupsd.conf...
+  */
+
+  if ((fp = cupsFileOpen (ConfigurationFile, "r")) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_WARN, "Unable to open configuration file?!");
+    return (0);
+  }
+
+  oldconf = _cupsdGetBlacklistedConfLines(fp);
+  cupsFileClose(fp);
+  if (!oldconf)
+    return (0);
+
+ /*
+  * Now take a look at the proposed new cupsd.conf...
+  */
+
+  if ((fp = cupsFileOpen(con->filename, "r")) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_WARN, "Unable to examine new config file");
+    goto fail;
+  }
+
+  newconf = _cupsdGetBlacklistedConfLines(fp);
+  cupsFileClose(fp);
+  if (!newconf)
+    goto fail;
+
+ /*
+  * Now compare the blacklisted directives in each.
+  */
+
+  status = 1;
+  for (oldline = (char *) cupsArrayFirst(oldconf);
+       oldline;
+       oldline = (char *) cupsArrayNext(oldconf))
+  {
+    for (newline = (char *) cupsArrayFirst(newconf);
+        newline;
+        newline = (char *) cupsArrayNext(newconf))
+      if (!strcmp (oldline, newline))
+       break;
+
+    if (newline == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "Attempt to remove or change '%s' denied", oldline);
+      status = 0;
+      break;
+    }
+
+    cupsArrayRemove(newconf, newline);
+    free(newline);
+  }
+
+  if (status)
+  {
+   /*
+    * All the original directives are still present. Have any been added?
+    */
+
+    newline = (char *) cupsArrayFirst(newconf);
+    if (newline != NULL)
+    {
+      char *p;
+
+      cupsArrayRemove(newconf, newline);      
+
+      p = strchr (newline, ' ');
+      if (p)
+       *p = '\0';
+
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Attempt to add '%s' directive denied", newline);
+      free(newline);
+      status = 0;
+    }
+  }
+
+fail:
+  for (oldline = (char *) cupsArrayFirst(oldconf);
+       oldline;
+       oldline = (char *) cupsArrayNext(oldconf))
+    free(oldline);
+  cupsArrayDelete(oldconf);
+
+  if (newconf)
+  {
+    for (newline = (char *) cupsArrayFirst(newconf);
+        newline;
+        newline = (char *) cupsArrayNext(newconf))
+      free(newline);
+    cupsArrayDelete(newconf);
+  }
+
+  return (status);
+}
+
+
 /*
  * 'read_location()' - Read a <Location path> definition.
  */