diff -up cups-1.3.7/doc/help/ref-cupsd-conf.html.in.CVE-2012-5519 cups-1.3.7/doc/help/ref-cupsd-conf.html.in --- cups-1.3.7/doc/help/ref-cupsd-conf.html.in.CVE-2012-5519 2013-02-15 09:55:37.506200796 +0000 +++ cups-1.3.7/doc/help/ref-cupsd-conf.html.in 2013-02-15 09:56:23.088451695 +0000 @@ -840,6 +840,29 @@ permissions to use when writing configur is @CUPS_CONFIG_FILE_PERM@.</P> +<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> <H3>Examples</H3> diff -up cups-1.3.7/man/cupsctl.man.CVE-2012-5519 cups-1.3.7/man/cupsctl.man --- cups-1.3.7/man/cupsctl.man.CVE-2012-5519 2007-07-11 22:46:42.000000000 +0100 +++ cups-1.3.7/man/cupsctl.man 2013-02-15 09:56:23.089451701 +0000 @@ -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 diff -up cups-1.3.7/man/cupsd.conf.man.in.CVE-2012-5519 cups-1.3.7/man/cupsd.conf.man.in --- cups-1.3.7/man/cupsd.conf.man.in.CVE-2012-5519 2013-02-15 09:55:37.507200802 +0000 +++ cups-1.3.7/man/cupsd.conf.man.in 2013-02-15 09:56:23.090451706 +0000 @@ -204,6 +204,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. diff -up cups-1.3.7/scheduler/client.c.CVE-2012-5519 cups-1.3.7/scheduler/client.c --- cups-1.3.7/scheduler/client.c.CVE-2012-5519 2013-02-15 09:55:37.683201770 +0000 +++ cups-1.3.7/scheduler/client.c 2013-02-15 09:55:37.718201963 +0000 @@ -1671,13 +1671,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... */ if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) @@ -3667,6 +3664,9 @@ install_conf_file(cupsd_client_t *con) / snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11); snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11); + if (!cupsdCheckConfigurationAllowed (con)) + return (HTTP_FORBIDDEN); + cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", conffile); /* diff -up cups-1.3.7/scheduler/conf.c.CVE-2012-5519 cups-1.3.7/scheduler/conf.c --- cups-1.3.7/scheduler/conf.c.CVE-2012-5519 2013-02-15 09:55:37.650201589 +0000 +++ cups-1.3.7/scheduler/conf.c 2013-02-15 09:55:37.719201968 +0000 @@ -2878,6 +2878,23 @@ 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); + if (FatalErrors & CUPSD_FATAL_CONFIG) + return (0); + } + } else if (!strcasecmp(line, "DefaultAuthType")) { /* @@ -3263,6 +3280,245 @@ read_configuration(cups_file_t *fp) /* I return (1); } +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", + "LogFilePerm", + "LPDConfigFile", + "PageLog", + "Printcap", + "PrintcapFormat", + "PrintcapGUI", + "RemoteRoot", + "RequestRoot", + "ServerBin", + "ServerCertificate", + "ServerKey", + "ServerRoot", + "SMBConfigFile", + "StateDir", + "SystemGroup", + "SystemGroupAuthKey", + "TempDir", + "User", + 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. diff -up cups-1.3.7/scheduler/conf.h.CVE-2012-5519 cups-1.3.7/scheduler/conf.h --- cups-1.3.7/scheduler/conf.h.CVE-2012-5519 2013-02-15 09:55:37.650201589 +0000 +++ cups-1.3.7/scheduler/conf.h 2013-02-15 09:55:37.719201968 +0000 @@ -59,6 +59,19 @@ typedef enum /* + * 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... */ @@ -137,6 +150,8 @@ VAR int ClassifyOverride VALUE(0), /* Allow overrides? */ ConfigFilePerm VALUE(0640), /* Permissions for config files */ + ConfigurationChangeRestriction VALUE(CUPSD_CONFRESTRICT_ALL), + /* CVE-2012-5519 protection */ FatalErrors VALUE(CUPSD_FATAL_CONFIG), /* Which errors are fatal? */ LogFilePerm VALUE(0644), @@ -258,6 +273,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); /*