Skip to content

Commit

Permalink
Fix local privilege escalation to root and sandbox bypasses in scheduler
Browse files Browse the repository at this point in the history
(rdar://37836779, rdar://37836995, rdar://37837252, rdar://37837581)
  • Loading branch information
michaelrsweet committed Jun 5, 2018
1 parent be7581b commit d47f6ae
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 99 deletions.
10 changes: 10 additions & 0 deletions man/cups-files.conf.man.in
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ The server name may be included in filenames using the string "%s", for example:

.fi
The default is "/var/log/cups/page_log".
.\"#PassEnv
.TP 5
\fBPassEnv \fIvariable \fR[ ... \fIvariable \fR]
Passes the specified environment variable(s) to child processes.
Note: the standard CUPS filter and backend environment variables cannot be overridden using this directive.
.\"#RemoteRoot
.TP 5
\fBRemoteRoot \fIusername\fR
Expand Down Expand Up @@ -187,6 +192,11 @@ macOS uses its keychain database to store certificates and keys while other plat
\fBServerRoot \fIdirectory\fR
Specifies the directory containing the server configuration files.
The default is "/etc/cups".
.\"#SetEnv
.TP 5
\fBSetEnv \fIvariable value\fR
Set the specified environment variable to be passed to child processes.
Note: the standard CUPS filter and backend environment variables cannot be overridden using this directive.
.\"#StateDir
.TP 5
\fBStateDir \fIdirectory\fR
Expand Down
8 changes: 0 additions & 8 deletions man/cupsd.conf.man.in
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,6 @@ The default is "1048576" (1MB).
\fBMultipleOperationTimeout \fIseconds\fR
Specifies the maximum amount of time to allow between files in a multiple file print job.
The default is "300" (5 minutes).
.\"#PassEnv
.TP 5
\fBPassEnv \fIvariable \fR[ ... \fIvariable \fR]
Passes the specified environment variable(s) to child processes.
.\"#Policy
.TP 5
\fB<Policy \fIname\fB> \fR... \fB</Policy>\fR
Expand Down Expand Up @@ -433,10 +429,6 @@ Specifies what information is included in the Server header of HTTP responses.
command.
"Full" reports "CUPS 2.0.0 (UNAME) IPP/2.0".
The default is "Minimal".
.\"#SetEnv
.TP 5
\fBSetEnv \fIvariable value\fR
Set the specified environment variable to be passed to child processes.
.\"#SSLListen
.TP 5
\fBSSLListen \fIipv4-address\fB:\fIport\fR
Expand Down
201 changes: 125 additions & 76 deletions scheduler/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2929,13 +2929,10 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
/* Line from file */
temp[HTTP_MAX_BUFFER],
/* Temporary buffer for value */
*value, /* Pointer to value */
*valueptr; /* Pointer into value */
*value; /* Pointer to value */
int valuelen; /* Length of value */
http_addrlist_t *addrlist, /* Address list */
*addr; /* Current address */
cups_file_t *incfile; /* Include file */
char incname[1024]; /* Include filename */


/*
Expand All @@ -2950,28 +2947,7 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
* Decode the directive...
*/

if (!_cups_strcasecmp(line, "Include") && value)
{
/*
* Include filename
*/

if (value[0] == '/')
strlcpy(incname, value, sizeof(incname));
else
snprintf(incname, sizeof(incname), "%s/%s", ServerRoot, value);

if ((incfile = cupsFileOpen(incname, "rb")) == NULL)
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to include config file \"%s\" - %s",
incname, strerror(errno));
else
{
read_cupsd_conf(incfile);
cupsFileClose(incfile);
}
}
else if (!_cups_strcasecmp(line, "<Location") && value)
if (!_cups_strcasecmp(line, "<Location") && value)
{
/*
* <Location path>
Expand Down Expand Up @@ -3367,31 +3343,6 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
cupsdLogMessage(CUPSD_LOG_WARN, "Unknown ServerTokens %s on line %d of %s.",
value, linenum, ConfigurationFile);
}
else if (!_cups_strcasecmp(line, "PassEnv") && value)
{
/*
* PassEnv variable [... variable]
*/

for (; *value;)
{
for (valuelen = 0; value[valuelen]; valuelen ++)
if (_cups_isspace(value[valuelen]) || value[valuelen] == ',')
break;

if (value[valuelen])
{
value[valuelen] = '\0';
valuelen ++;
}

cupsdSetEnv(value, NULL);

for (value += valuelen; *value; value ++)
if (!_cups_isspace(*value) || *value != ',')
break;
}
}
else if (!_cups_strcasecmp(line, "ServerAlias") && value)
{
/*
Expand Down Expand Up @@ -3420,30 +3371,6 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
break;
}
}
else if (!_cups_strcasecmp(line, "SetEnv") && value)
{
/*
* SetEnv variable value
*/

for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);

if (*valueptr)
{
/*
* Found a value...
*/

while (isspace(*valueptr & 255))
*valueptr++ = '\0';

cupsdSetEnv(value, valueptr);
}
else
cupsdLogMessage(CUPSD_LOG_ERROR,
"Missing value for SetEnv directive on line %d of %s.",
linenum, ConfigurationFile);
}
else if (!_cups_strcasecmp(line, "AccessLog") ||
!_cups_strcasecmp(line, "CacheDir") ||
!_cups_strcasecmp(line, "ConfigFilePerm") ||
Expand All @@ -3457,6 +3384,7 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
!_cups_strcasecmp(line, "LogFilePerm") ||
!_cups_strcasecmp(line, "LPDConfigFile") ||
!_cups_strcasecmp(line, "PageLog") ||
!_cups_strcasecmp(line, "PassEnv") ||
!_cups_strcasecmp(line, "Printcap") ||
!_cups_strcasecmp(line, "PrintcapFormat") ||
!_cups_strcasecmp(line, "RemoteRoot") ||
Expand All @@ -3466,6 +3394,7 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
!_cups_strcasecmp(line, "ServerKey") ||
!_cups_strcasecmp(line, "ServerKeychain") ||
!_cups_strcasecmp(line, "ServerRoot") ||
!_cups_strcasecmp(line, "SetEnv") ||
!_cups_strcasecmp(line, "SMBConfigFile") ||
!_cups_strcasecmp(line, "StateDir") ||
!_cups_strcasecmp(line, "SystemGroup") ||
Expand Down Expand Up @@ -3495,10 +3424,49 @@ read_cupsd_conf(cups_file_t *fp) /* I - File to read from */
static int /* O - 1 on success, 0 on failure */
read_cups_files_conf(cups_file_t *fp) /* I - File to read from */
{
int linenum; /* Current line number */
int i, /* Looping var */
linenum; /* Current line number */
char line[HTTP_MAX_BUFFER], /* Line from file */
*value; /* Value from line */
struct group *group; /* Group */
static const char * const prohibited_env[] =
{ /* Prohibited environment variables */
"APPLE_LANGUAGE",
"AUTH_DOMAIN",
"AUTH_INFO_REQUIRED",
"AUTH_NEGOTIATE",
"AUTH_PASSWORD",
"AUTH_UID",
"AUTH_USERNAME",
"CHARSET",
"CLASS",
"CLASSIFICATION",
"CONTENT_TYPE",
"CUPS_CACHEDIR",
"CUPS_DATADIR",
"CUPS_DOCROOT",
"CUPS_FILETYPE",
"CUPS_FONTPATH",
"CUPS_MAX_MESSAGE",
"CUPS_REQUESTROOT",
"CUPS_SERVERBIN",
"CUPS_SERVERROOT",
"CUPS_STATEDIR",
"DEVICE_URI",
"FINAL_CONTENT_TYPE",
"HOME",
"LANG",
"PPD",
"PRINTER",
"PRINTER_INFO",
"PRINTER_LOCATION",
"PRINTER_STATE_REASONS",
"RIP_CACHE",
"SERVER_ADMIN",
"SOFTWARE",
"TMPDIR",
"USER"
};


/*
Expand Down Expand Up @@ -3536,6 +3504,47 @@ read_cups_files_conf(cups_file_t *fp) /* I - File to read from */
}
}
}
else if (!_cups_strcasecmp(line, "PassEnv") && value)
{
/*
* PassEnv variable [... variable]
*/

int valuelen; /* Length of variable name */

for (; *value;)
{
for (valuelen = 0; value[valuelen]; valuelen ++)
if (_cups_isspace(value[valuelen]) || value[valuelen] == ',')
break;

if (value[valuelen])
{
value[valuelen] = '\0';
valuelen ++;
}

for (i = 0; i < (int)(sizeof(prohibited_env) / sizeof(prohibited_env[0])); i ++)
{
if (!strcmp(value, prohibited_env[i]))
{
cupsdLogMessage(CUPSD_LOG_ERROR, "Environment variable \"%s\" cannot be passed through on line %d of %s.", value, linenum, CupsFilesFile);

if (FatalErrors & CUPSD_FATAL_CONFIG)
return (0);
else
break;
}
}

if (i >= (int)(sizeof(prohibited_env) / sizeof(prohibited_env[0])))
cupsdSetEnv(value, NULL);

for (value += valuelen; *value; value ++)
if (!_cups_isspace(*value) || *value != ',')
break;
}
}
else if (!_cups_strcasecmp(line, "PrintcapFormat") && value)
{
/*
Expand Down Expand Up @@ -3581,6 +3590,46 @@ read_cups_files_conf(cups_file_t *fp) /* I - File to read from */
return (0);
}
}
else if (!_cups_strcasecmp(line, "SetEnv") && value)
{
/*
* SetEnv variable value
*/

char *valueptr; /* Pointer to environment variable value */

for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);

if (*valueptr)
{
/*
* Found a value...
*/

while (isspace(*valueptr & 255))
*valueptr++ = '\0';

for (i = 0; i < (int)(sizeof(prohibited_env) / sizeof(prohibited_env[0])); i ++)
{
if (!strcmp(value, prohibited_env[i]))
{
cupsdLogMessage(CUPSD_LOG_ERROR, "Environment variable \"%s\" cannot be set on line %d of %s.", value, linenum, CupsFilesFile);

if (FatalErrors & CUPSD_FATAL_CONFIG)
return (0);
else
break;
}
}

if (i >= (int)(sizeof(prohibited_env) / sizeof(prohibited_env[0])))
cupsdSetEnv(value, valueptr);
}
else
cupsdLogMessage(CUPSD_LOG_ERROR,
"Missing value for SetEnv directive on line %d of %s.",
linenum, ConfigurationFile);
}
else if (!_cups_strcasecmp(line, "SystemGroup") && value)
{
/*
Expand Down
12 changes: 12 additions & 0 deletions scheduler/job.c
Original file line number Diff line number Diff line change
Expand Up @@ -4779,6 +4779,18 @@ start_job(cupsd_job_t *job, /* I - Job ID */
job->profile = cupsdCreateProfile(job->id, 0);
job->bprofile = cupsdCreateProfile(job->id, 1);

#ifdef HAVE_SANDBOX_H
if ((!job->profile || !job->bprofile) && UseSandboxing && Sandboxing != CUPSD_SANDBOXING_OFF)
{
/*
* Failure to create the sandbox profile means something really bad has
* happened and we need to shutdown immediately.
*/

return;
}
#endif /* HAVE_SANDBOX_H */

/*
* Create the status pipes and buffer...
*/
Expand Down
16 changes: 10 additions & 6 deletions scheduler/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ cupsdCreateProfile(int job_id, /* I - Job ID or 0 for none */

if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL)
{
/*
* This should never happen, and is fatal when sandboxing is enabled.
*/

cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking);
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create security profile: %s",
strerror(errno));
cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to create security profile: %s", strerror(errno));
kill(getpid(), SIGTERM);
return (NULL);
}

Expand Down Expand Up @@ -197,10 +201,8 @@ cupsdCreateProfile(int job_id, /* I - Job ID or 0 for none */
" #\"^%s/\"" /* TempDir/... */
" #\"^%s$\"" /* CacheDir */
" #\"^%s/\"" /* CacheDir/... */
" #\"^%s$\"" /* StateDir */
" #\"^%s/\"" /* StateDir/... */
"))\n",
temp, temp, cache, cache, state, state);
temp, temp, cache, cache);
/* Read common folders */
cupsFilePrintf(fp,
"(allow file-read-data file-read-metadata\n"
Expand Down Expand Up @@ -242,8 +244,10 @@ cupsdCreateProfile(int job_id, /* I - Job ID or 0 for none */
" #\"^%s/\"" /* ServerBin/... */
" #\"^%s$\"" /* ServerRoot */
" #\"^%s/\"" /* ServerRoot/... */
" #\"^%s$\"" /* StateDir */
" #\"^%s/\"" /* StateDir/... */
"))\n",
request, request, bin, bin, root, root);
request, request, bin, bin, root, root, state, state);
if (Sandboxing == CUPSD_SANDBOXING_RELAXED)
{
/* Limited write access to /Library/Printers/... */
Expand Down
Loading

0 comments on commit d47f6ae

Please sign in to comment.