]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/process.c
Full sweep of all Clang warnings, plus some bug fixes for incorrect memcpy usage.
[thirdparty/cups.git] / scheduler / process.c
index 153ba2746e05925e419174db66bbdf04ccc5e1f0..44ebf7324f80f8f8d7bb30e896a5a19c80c47625 100644 (file)
@@ -1,26 +1,16 @@
 /*
- * "$Id: process.c 7256 2008-01-25 00:48:54Z mike $"
+ * "$Id$"
  *
- *   Process management routines for the CUPS scheduler.
+ * Process management routines for the CUPS scheduler.
  *
- *   Copyright 2007-2010 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   which should have been included with this file.  If this file is
- *   file is missing or damaged, see the license at "http://www.cups.org/".
- *
- * Contents:
- *
- *   cupsdCreateProfile()  - Create an execution profile for a subprocess.
- *   cupsdDestroyProfile() - Delete an execution profile.
- *   cupsdEndProcess()     - End a process.
- *   cupsdFinishProcess()  - Finish a process and get its name.
- *   cupsdStartProcess()   - Start a process.
- *   compare_procs()       - Compare two processes.
- *   cupsd_requote()       - Make a regular-expression version of a string.
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file.  If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
  */
 
 /*
 #ifdef __APPLE__
 #  include <libgen.h>
 #endif /* __APPLE__ */
-#ifdef HAVE_SANDBOX_H
-#  define __APPLE_API_PRIVATE
-#  include <sandbox.h>
-#endif /* HAVE_SANDBOX_H */
 
 
 /*
@@ -81,9 +67,10 @@ cupsdCreateProfile(int job_id)               /* I - Job ID or 0 for none */
                request[1024],          /* Quoted RequestRoot */
                root[1024],             /* Quoted ServerRoot */
                temp[1024];             /* Quoted TempDir */
+  const char   *nodebug;               /* " (with no-log)" for no debug */
 
 
-  if (!UseProfiles || RunUser)
+  if (!UseProfiles)
   {
    /*
     * Only use sandbox profiles as root...
@@ -104,24 +91,32 @@ cupsdCreateProfile(int job_id)             /* I - Job ID or 0 for none */
     return (NULL);
   }
 
+  fchown(cupsFileNumber(fp), RunUser, Group);
+  fchmod(cupsFileNumber(fp), 0640);
+
   cupsd_requote(cache, CacheDir, sizeof(cache));
   cupsd_requote(request, RequestRoot, sizeof(request));
   cupsd_requote(root, ServerRoot, sizeof(root));
   cupsd_requote(temp, TempDir, sizeof(temp));
 
+  nodebug = LogLevel < CUPSD_LOG_DEBUG ? " (with no-log)" : "";
+
   cupsFilePuts(fp, "(version 1)\n");
-  if (LogLevel >= CUPSD_LOG_DEBUG)
-    cupsFilePuts(fp, "(debug deny)\n");
   cupsFilePuts(fp, "(allow default)\n");
   cupsFilePrintf(fp,
                  "(deny file-write* file-read-data file-read-metadata\n"
                  "  (regex"
                 " #\"^%s$\""           /* RequestRoot */
                 " #\"^%s/\""           /* RequestRoot/... */
-                " #\"^/Users$\""
-                " #\"^/Users/\""
-                "))\n",
-                request, request);
+                ")%s)\n",
+                request, request, nodebug);
+  if (!RunUser)
+    cupsFilePrintf(fp,
+                  "(deny file-write* file-read-data file-read-metadata\n"
+                  "  (regex"
+                  " #\"^/Users$\""
+                  " #\"^/Users/\""
+                  ")%s)\n", nodebug);
   cupsFilePrintf(fp,
                  "(deny file-write*\n"
                  "  (regex"
@@ -135,8 +130,8 @@ cupsdCreateProfile(int job_id)              /* I - Job ID or 0 for none */
                 " #\"^/Library/\""
                 " #\"^/System$\""
                 " #\"^/System/\""
-                "))\n",
-                root, root);
+                ")%s)\n",
+                root, root, nodebug);
   /* Specifically allow applications to stat RequestRoot */
   cupsFilePrintf(fp,
                  "(allow file-read-metadata\n"
@@ -160,19 +155,37 @@ cupsdCreateProfile(int job_id)            /* I - Job ID or 0 for none */
                 " #\"^/Users/Shared/\""
                 "))\n",
                 temp, temp, cache, cache, request, request);
-  cupsFilePuts(fp,
-              "(deny file-write*\n"
-              "  (regex"
-              " #\"^/Library/Printers/PPDs$\""
-              " #\"^/Library/Printers/PPDs/\""
-              " #\"^/Library/Printers/PPD Plugins$\""
-              " #\"^/Library/Printers/PPD Plugins/\""
-              "))\n");
+  cupsFilePrintf(fp,
+                "(deny file-write*\n"
+                "  (regex"
+                " #\"^/Library/Printers/PPDs$\""
+                " #\"^/Library/Printers/PPDs/\""
+                " #\"^/Library/Printers/PPD Plugins$\""
+                " #\"^/Library/Printers/PPD Plugins/\""
+                ")%s)\n", nodebug);
   if (job_id)
+  {
+   /*
+    * Allow job filters to read the spool file(s)...
+    */
+
     cupsFilePrintf(fp,
                    "(allow file-read-data file-read-metadata\n"
                    "  (regex #\"^%s/([ac]%05d|d%05d-[0-9][0-9][0-9])$\"))\n",
                   request, job_id, job_id);
+  }
+  else
+  {
+   /*
+    * Allow email notifications from notifiers...
+    */
+
+    cupsFilePuts(fp,
+                "(allow process-exec\n"
+                "  (literal \"/usr/sbin/sendmail\")\n"
+                "  (with no-sandbox)\n"
+                ")\n");
+  }
 
   cupsFileClose(fp);
 
@@ -222,7 +235,18 @@ cupsdEndProcess(int pid,           /* I - Process ID */
 
   if (!pid)
     return (0);
-  else if (force)
+
+  if (!RunUser)
+  {
+   /*
+    * When running as root, cupsd puts child processes in their own process
+    * group.  Using "-pid" sends a signal to all processes in the group.
+    */
+
+    pid = -pid;
+  }
+
+  if (force)
     return (kill(pid, SIGKILL));
   else
     return (kill(pid, SIGTERM));
@@ -290,8 +314,11 @@ cupsdStartProcess(
     cupsd_job_t *job,                  /* I - Job associated with process */
     int         *pid)                  /* O - Process ID */
 {
-  int          user;                   /* Command UID */
-  struct stat  commandinfo;            /* Command file information */
+  int          i;                      /* Looping var */
+  const char   *exec_path = command;   /* Command to be exec'd */
+  char         *real_argv[103],        /* Real command-line arguments */
+               cups_exec[1024];        /* Path to "cups-exec" program */
+  uid_t                user;                   /* Command UID */
   cupsd_proc_t *proc;                  /* New process record */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* POSIX signal handler */
@@ -303,6 +330,12 @@ cupsdStartProcess(
 #endif /* __APPLE__ */
 
 
+  *pid = 0;
+
+ /*
+  * Figure out the UID for the child process...
+  */
+
   if (RunUser)
     user = RunUser;
   else if (root)
@@ -310,82 +343,20 @@ cupsdStartProcess(
   else
     user = User;
 
-  if (stat(command, &commandinfo))
-  {
-    *pid = 0;
-
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                   "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
-                   "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
-                   "profile=%p, job=%p(%d), pid=%p) = %d",
-                   command, argv, envp, infd, outfd, errfd, backfd, sidefd,
-                   root, profile, job, job ? job->id : 0, pid, *pid);
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to execute %s: %s", command,
-                    strerror(errno));
-
-    if (job && job->printer)
-    {
-      if (cupsdSetPrinterReasons(job->printer, "+cups-missing-filter-warning"))
-       cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
-                     "Printer driver %s is missing.", command);
-    }
-
-    return (0);
-  }
-  else if (!RunUser &&
-           ((commandinfo.st_mode & (S_ISUID | S_IWGRP | S_IWOTH)) ||
-            commandinfo.st_uid))
-  {
-    *pid = 0;
-
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                   "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
-                   "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
-                   "profile=%p, job=%p(%d), pid=%p) = %d",
-                   command, argv, envp, infd, outfd, errfd, backfd, sidefd,
-                   root, profile, job, job ? job->id : 0, pid, *pid);
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to execute %s: insecure file permissions (0%o)",
-                   command, commandinfo.st_mode);
-
-    if (job && job->printer)
-    {
-      if (cupsdSetPrinterReasons(job->printer, "+cups-insecure-filter-warning"))
-       cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
-                     "Printer driver %s has insecure file permissions (0%o).",
-                     command, commandinfo.st_mode);
-    }
-
-    errno = EPERM;
-
-    return (0);
-  }
-  else if ((commandinfo.st_uid != user || !(commandinfo.st_mode & S_IXUSR)) &&
-           (commandinfo.st_gid != Group || !(commandinfo.st_mode & S_IXGRP)) &&
-           !(commandinfo.st_mode & S_IXOTH))
-  {
-    *pid = 0;
+ /*
+  * Check the permissions of the command we are running...
+  */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                   "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
-                   "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
-                   "profile=%p, job=%p(%d), pid=%p) = %d",
-                   command, argv, envp, infd, outfd, errfd, backfd, sidefd,
-                   root, profile, job, job ? job->id : 0, pid, *pid);
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to execute %s: no execute permissions (0%o)",
-                   command, commandinfo.st_mode);
-
-    errno = EPERM;
+  if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
+                     cupsdLogFCMessage, job ? job->printer : NULL))
     return (0);
-  }
 
 #if defined(__APPLE__)
   if (envp)
   {
    /*
-    * Add special voodoo magic for Mac OS X - this allows Mac OS X
-    * programs to access their bundle resources properly...
+    * Add special voodoo magic for OS X - this allows OS X programs to access
+    * their bundle resources properly...
     */
 
     if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
@@ -411,6 +382,29 @@ cupsdStartProcess(
   }
 #endif /* __APPLE__ */
 
+ /*
+  * Use helper program when we have a sandbox profile...
+  */
+
+  if (profile)
+  {
+    snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin);
+
+    real_argv[0] = cups_exec;
+    real_argv[1] = profile;
+    real_argv[2] = (char *)command;
+
+    for (i = 0;
+         i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 4) && argv[i];
+        i ++)
+      real_argv[i + 3] = argv[i];
+
+    real_argv[i + 3] = NULL;
+
+    argv      = real_argv;
+    exec_path = cups_exec;
+  }
+
  /*
   * Block signals before forking...
   */
@@ -420,9 +414,36 @@ cupsdStartProcess(
   if ((*pid = fork()) == 0)
   {
    /*
-    * Child process goes here...
-    *
-    * Update stdin/stdout/stderr as needed...
+    * Child process goes here; update stderr as needed...
+    */
+
+    if (errfd != 2)
+    {
+      if (errfd < 0)
+        errfd = open("/dev/null", O_WRONLY);
+
+      if (errfd != 2)
+      {
+        dup2(errfd, 2);
+       close(errfd);
+      }
+    }
+
+   /*
+    * Put this process in its own process group so that we can kill any child
+    * processes it creates.
+    */
+
+#ifdef HAVE_SETPGID
+    if (!RunUser && setpgid(0, 0))
+      exit(errno + 100);
+#else
+    if (!RunUser && setpgrp())
+      exit(errno + 100);
+#endif /* HAVE_SETPGID */
+
+   /*
+    * Update the remaining file descriptors as needed...
     */
 
     if (infd != 0)
@@ -449,18 +470,6 @@ cupsdStartProcess(
       }
     }
 
-    if (errfd != 2)
-    {
-      if (errfd < 0)
-        errfd = open("/dev/null", O_WRONLY);
-
-      if (errfd != 2)
-      {
-        dup2(errfd, 2);
-       close(errfd);
-      }
-    }
-
     if (backfd != 3 && backfd >= 0)
     {
       dup2(backfd, 3);
@@ -483,55 +492,22 @@ cupsdStartProcess(
     if (!root)
       nice(FilterNice);
 
-#ifdef HAVE_SANDBOX_H
    /*
-    * Run in a separate security profile...
+    * Reset group membership to just the main one we belong to.
     */
 
-    if (profile)
-    {
-      char *error = NULL;              /* Sandbox error, if any */
+    if (!RunUser && setgid(Group))
+      exit(errno + 100);
 
-      if (sandbox_init((char *)profile, SANDBOX_NAMED_EXTERNAL, &error))
-      {
-        fprintf(stderr, "ERROR: sandbox_init failed: %s (%s)\n", error,
-               strerror(errno));
-       sandbox_free_error(error);
-      }
-    }
-#endif /* HAVE_SANDBOX_H */
+    if (!RunUser && setgroups(1, &Group))
+      exit(errno + 100);
 
    /*
     * Change user to something "safe"...
     */
 
-    if (!root && !RunUser)
-    {
-     /*
-      * Running as root, so change to non-priviledged user...
-      */
-
-      if (setgid(Group))
-       exit(errno);
-
-      if (setgroups(1, &Group))
-       exit(errno);
-
-      if (setuid(User))
-        exit(errno);
-    }
-    else
-    {
-     /*
-      * Reset group membership to just the main one we belong to.
-      */
-
-      if (setgid(Group) && !RunUser)
-        exit(errno);
-
-      if (setgroups(1, &Group) && !RunUser)
-        exit(errno);
-    }
+    if (!RunUser && user && setuid(user))
+      exit(errno + 100);
 
    /*
     * Change umask to restrict permissions on created files...
@@ -565,18 +541,16 @@ cupsdStartProcess(
     cupsdReleaseSignals();
 
    /*
-    * Execute the command; if for some reason this doesn't work,
-    * return the error code...
+    * Execute the command; if for some reason this doesn't work, log an error
+    * exit with a non-zero value...
     */
 
     if (envp)
-      execve(command, argv, envp);
+      execve(exec_path, argv, envp);
     else
-      execv(command, argv);
+      execv(exec_path, argv);
 
-    perror(command);
-
-    exit(errno);
+    exit(errno + 100);
   }
   else if (*pid < 0)
   {
@@ -593,14 +567,14 @@ cupsdStartProcess(
   {
     if (!process_array)
       process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL);
+
     if (process_array)
     {
       if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL)
       {
         proc->pid    = *pid;
        proc->job_id = job ? job->id : 0;
-       strcpy(proc->name, command);
+       _cups_strcpy(proc->name, command);
 
        cupsArrayAdd(process_array, proc);
       }
@@ -654,10 +628,13 @@ cupsd_requote(char       *dst,            /* I - Destination buffer */
   {
     ch = *src++;
 
+    if (ch == '/' && !*src)
+      break;                           /* Don't add trailing slash */
+
     if (strchr(".?*()[]^$\\", ch))
       *dstptr++ = '\\';
 
-    *dstptr++ = ch;
+    *dstptr++ = (char)ch;
   }
 
   *dstptr = '\0';
@@ -668,5 +645,5 @@ cupsd_requote(char       *dst,              /* I - Destination buffer */
 
 
 /*
- * End of "$Id: process.c 7256 2008-01-25 00:48:54Z mike $".
+ * End of "$Id$".
  */