cups/cups-1.5.4-CVE-2012-5519.patch
Johannes Meixner ad3ceda791 Accepting request 222281 from home:jsmeix:branches:Printing
CUPS security fix for CVE-2012-5519 to have better default protection against misuse of CUPS admin privileges (bnc#789566) plus clean up of cups.spec by having strictly separated sections how cupsd is launched (either via SysVinit or via systemd)

OBS-URL: https://build.opensuse.org/request/show/222281
OBS-URL: https://build.opensuse.org/package/show/Printing/cups?expand=0&rev=276
2014-02-14 08:37:00 +00:00

409 lines
11 KiB
Diff

--- 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.
*/