]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Fix local privilege escalation to root and sandbox bypasses in scheduler
authorMichael R Sweet <michael.r.sweet@gmail.com>
Tue, 8 May 2018 21:59:50 +0000 (14:59 -0700)
committerMichael R Sweet <michael.r.sweet@gmail.com>
Tue, 5 Jun 2018 12:52:17 +0000 (08:52 -0400)
(rdar://37836779, rdar://37836995, rdar://37837252, rdar://37837581)

CHANGES.md
doc/help/man-cups-files.conf.html
doc/help/man-cupsd.conf.html
man/cups-files.conf.man.in
man/cupsd.conf.man.in
scheduler/conf.c
scheduler/job.c
scheduler/process.c
scheduler/server.c
test/run-stp-tests.sh

index 1574ac3d55193e36b3daa661957ed72af9707677..a852d0dffe94ed7ce23e64f29faced472a90e256 100644 (file)
@@ -5,6 +5,10 @@ CHANGES - 2.2.8 - 2018-06-04
 Changes in CUPS v2.2.8
 ----------------------
 
+- SECURITY: Fixed local privilege escalation to root and sandbox bypasses in
+  scheduler (rdar://37836779, rdar://37836995, rdar://37837252, rdar://37837581)
+- SECURITY: The scheduler did not validate notify-recipient-uri schemes properly
+  (rdar://40068936)
 - Additional changes for the scheduler to substitute default values for invalid
   job attributes when running in "relaxed conformance" mode (Issue #5229)
 - The `ipptool` program no longer checks for duplicate attributes when running
index 6dd442ef4d445644cb6a450fe2e998837aaf21f2..e298d8824468f790077e29584a9e75f7a4e438e2 100644 (file)
@@ -115,6 +115,9 @@ The server name may be included in filenames using the string "%s", for example:
 
 </pre>
 The default is "/var/log/cups/page_log".
+<dt><a name="PassEnv"></a><b>PassEnv </b><i>variable </i>[ ... <i>variable </i>]
+<dd style="margin-left: 5.0em">Passes the specified environment variable(s) to child processes.
+Note: the standard CUPS filter and backend environment variables cannot be overridden using this directive.
 <dt><a name="RemoteRoot"></a><b>RemoteRoot </b><i>username</i>
 <dd style="margin-left: 5.0em">Specifies the username that is associated with unauthenticated accesses by clients claiming to be the root user.
 The default is "remroot".
@@ -136,6 +139,9 @@ macOS uses its keychain database to store certificates and keys while other plat
 <dt><a name="ServerRoot"></a><b>ServerRoot </b><i>directory</i>
 <dd style="margin-left: 5.0em">Specifies the directory containing the server configuration files.
 The default is "/etc/cups".
+<dt><a name="SetEnv"></a><b>SetEnv </b><i>variable value</i>
+<dd style="margin-left: 5.0em">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.
 <dt><a name="StateDir"></a><b>StateDir </b><i>directory</i>
 <dd style="margin-left: 5.0em">Specifies the directory to use for PID and local certificate files.
 The default is "/var/run/cups" or "/etc/cups" depending on the platform.
index 9438953872c091d11c8cfad6ee86f7ce2e12ffed..4ba6ecf8f62ff09374de66e1ae14c256932eea19 100644 (file)
@@ -220,8 +220,6 @@ The default is "1048576" (1MB).
 <dt><a name="MultipleOperationTimeout"></a><b>MultipleOperationTimeout </b><i>seconds</i>
 <dd style="margin-left: 5.0em">Specifies the maximum amount of time to allow between files in a multiple file print job.
 The default is "300" (5 minutes).
-<dt><a name="PassEnv"></a><b>PassEnv </b><i>variable </i>[ ... <i>variable </i>]
-<dd style="margin-left: 5.0em">Passes the specified environment variable(s) to child processes.
 <dt><a name="Policy"></a><b>&lt;Policy </b><i>name</i><b>> </b>... <b>&lt;/Policy></b>
 <dd style="margin-left: 5.0em">Specifies access control for the named policy.
 <dt><a name="Port"></a><b>Port </b><i>number</i>
@@ -273,8 +271,6 @@ command.
 command.
 "Full" reports "CUPS 2.0.0 (UNAME) IPP/2.0".
 The default is "Minimal".
-<dt><a name="SetEnv"></a><b>SetEnv </b><i>variable value</i>
-<dd style="margin-left: 5.0em">Set the specified environment variable to be passed to child processes.
 <dt><a name="SSLListen"></a><b>SSLListen </b><i>ipv4-address</i><b>:</b><i>port</i>
 <dd style="margin-left: 5.0em"><dt><b>SSLListen [</b><i>ipv6-address</i><b>]:</b><i>port</i>
 <dd style="margin-left: 5.0em"><dt><b>SSLListen *:</b><i>port</i>
index 2ed4686616ff738834f6fafe9e2c5ab5792bbbfa..6ac4e72d36aa389ea9742b69bb36166cf450775c 100644 (file)
@@ -157,6 +157,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
@@ -191,6 +196,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
index aa59a27e02f1b0b6b2d46442142d265b9504b5f9..15a5fa9e64d1255b613365a918440cf3601dad00 100644 (file)
@@ -342,10 +342,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
@@ -426,10 +422,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
index 11ad2c0248f4e6e04ea5f54185a321a1700b64a1..9c1be708999d54df6307050fff0913fc04a96453 100644 (file)
@@ -2928,13 +2928,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 */
 
 
  /*
@@ -2949,28 +2946,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>
@@ -3366,31 +3342,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)
     {
      /*
@@ -3419,30 +3370,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") ||
@@ -3456,6 +3383,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") ||
@@ -3465,6 +3393,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") ||
@@ -3494,10 +3423,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"
+  };
 
 
  /*
@@ -3535,6 +3503,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)
     {
      /*
@@ -3580,6 +3589,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)
     {
      /*
index 86e75e65ce9bd366f2d60b87538cf05fe61d0025..ed8267d5d94b66b6768232d3fbe6754f2481e8ca 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Job management routines for the CUPS scheduler.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -4774,6 +4774,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...
   */
index 5c01b4b11815ecf21abe01ef53894f7d98b724b0..a09d4988414ccbdf6e293b769bb3879fdcd616a3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Process management routines for the CUPS scheduler.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -102,9 +102,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);
   }
 
@@ -201,10 +205,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"
@@ -246,8 +248,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/... */
index d28cd4a0b6527f239279e33b43ca247d7fcb175b..63fcf90bf5be12f2510c49f7b5c6f8f08748f308 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Server start/stop routines for the CUPS scheduler.
  *
- * Copyright 2007-2017 by Apple Inc.
+ * Copyright 2007-2018 by Apple Inc.
  * Copyright 1997-2006 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
@@ -38,16 +38,28 @@ void
 cupsdStartServer(void)
 {
  /*
-  * Start color management (as needed)...
+  * Create the default security profile...
   */
 
-  cupsdStartColor();
+  DefaultProfile = cupsdCreateProfile(0, 1);
+
+#ifdef HAVE_SANDBOX_H
+  if (!DefaultProfile && 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 default security profile...
+  * Start color management (as needed)...
   */
 
-  DefaultProfile = cupsdCreateProfile(0, 1);
+  cupsdStartColor();
 
  /*
   * Startup all the networking stuff...
index 1063be4d09e8a1ccbededbb7dd077341c53bd370..056e8e5c8f906bd31dd1f5a07bc2a0cfe20e76b7 100755 (executable)
@@ -492,11 +492,6 @@ StrictConformance Yes
 Browsing Off
 Listen localhost:$port
 Listen $BASE/sock
-PassEnv DYLD_LIBRARY_PATH
-PassEnv LD_LIBRARY_PATH
-PassEnv LD_PRELOAD
-PassEnv LOCALEDIR
-PassEnv SHLIB_PATH
 MaxSubscriptions 3
 MaxLogSize 0
 AccessLogLevel actions
@@ -532,6 +527,12 @@ TempDir $BASE/spool/temp
 AccessLog $BASE/log/access_log
 ErrorLog $BASE/log/error_log
 PageLog $BASE/log/page_log
+
+PassEnv DYLD_LIBRARY_PATH
+PassEnv LD_LIBRARY_PATH
+PassEnv LD_PRELOAD
+PassEnv LOCALEDIR
+PassEnv SHLIB_PATH
 EOF
 
 if test $ssltype != 0 -a `uname` = Darwin; then