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
409 lines
11 KiB
Diff
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.
|
|
*/
|