• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Process management routines for the CUPS scheduler.
3  *
4  * Copyright © 2020-2024 by OpenPrinting.
5  * Copyright © 2007-2017 by Apple Inc.
6  * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7  *
8  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
9  * information.
10  */
11 
12 /*
13  * Include necessary headers...
14  */
15 
16 #include "cupsd.h"
17 #include <grp.h>
18 #ifdef __APPLE__
19 #  include <libgen.h>
20 #endif /* __APPLE__ */
21 #ifdef HAVE_POSIX_SPAWN
22 #  include <spawn.h>
23 extern char **environ;
24 /* Don't use posix_spawn on systems with bugs in their implementations... */
25 #  if defined(OpenBSD) && OpenBSD < 201505
26 #    define USE_POSIX_SPAWN 0
27 #  elif defined(__UCLIBC__) && __UCLIBC_MAJOR__ == 1 && __UCLIBC_MINOR__ == 0 && __UCLIBC_SUBLEVEL__ < 27
28 #    define USE_POSIX_SPAWN 0
29 #  elif defined(__UCLIBC__) && __UCLIBC_MAJOR__ < 1
30 #    define USE_POSIX_SPAWN 0
31 #  else /* All other platforms */
32 #    define USE_POSIX_SPAWN 1
33 #  endif /* ... */
34 #else
35 #  define USE_POSIX_SPAWN 0
36 #endif /* HAVE_POSIX_SPAWN */
37 
38 
39 /*
40  * Process structure...
41  */
42 
43 typedef struct
44 {
45   int	pid,				/* Process ID */
46 	job_id;				/* Job associated with process */
47   char	name[1];			/* Name of process */
48 } cupsd_proc_t;
49 
50 
51 /*
52  * Local globals...
53  */
54 
55 static cups_array_t	*process_array = NULL;
56 
57 
58 /*
59  * Local functions...
60  */
61 
62 static int	compare_procs(cupsd_proc_t *a, cupsd_proc_t *b);
63 #ifdef HAVE_SANDBOX_H
64 static char	*cupsd_requote(char *dst, const char *src, size_t dstsize);
65 #endif /* HAVE_SANDBOX_H */
66 
67 
68 /*
69  * 'cupsdCreateProfile()' - Create an execution profile for a subprocess.
70  */
71 
72 void *					/* O - Profile or NULL on error */
cupsdCreateProfile(int job_id,int allow_networking)73 cupsdCreateProfile(int job_id,		/* I - Job ID or 0 for none */
74                    int allow_networking)/* I - Allow networking off machine? */
75 {
76 #ifdef HAVE_SANDBOX_H
77   cups_file_t		*fp;		/* File pointer */
78   char			profile[1024],	/* File containing the profile */
79 			bin[1024],	/* Quoted ServerBin */
80 			cache[1024],	/* Quoted CacheDir */
81 			domain[1024],	/* Domain socket, if any */
82 			request[1024],	/* Quoted RequestRoot */
83 			root[1024],	/* Quoted ServerRoot */
84 			state[1024],	/* Quoted StateDir */
85 			temp[1024];	/* Quoted TempDir */
86   const char		*nodebug;	/* " (with no-log)" for no debug */
87   cupsd_listener_t	*lis;		/* Current listening socket */
88 
89 
90   if (!UseSandboxing || Sandboxing == CUPSD_SANDBOXING_OFF)
91   {
92    /*
93     * Only use sandbox profiles as root...
94     */
95 
96     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking);
97 
98     return (NULL);
99   }
100 
101   if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL)
102   {
103    /*
104     * This should never happen, and is fatal when sandboxing is enabled.
105     */
106 
107     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking);
108     cupsdLogMessage(CUPSD_LOG_EMERG, "Unable to create security profile: %s", strerror(errno));
109     kill(getpid(), SIGTERM);
110     return (NULL);
111   }
112 
113   fchown(cupsFileNumber(fp), RunUser, Group);
114   fchmod(cupsFileNumber(fp), 0640);
115 
116   cupsd_requote(bin, ServerBin, sizeof(bin));
117   cupsd_requote(cache, CacheDir, sizeof(cache));
118   cupsd_requote(request, RequestRoot, sizeof(request));
119   cupsd_requote(root, ServerRoot, sizeof(root));
120   cupsd_requote(state, StateDir, sizeof(state));
121   cupsd_requote(temp, TempDir, sizeof(temp));
122 
123   nodebug = LogLevel < CUPSD_LOG_DEBUG ? " (with no-log)" : "";
124 
125   cupsFilePuts(fp, "(version 1)\n");
126   if (Sandboxing == CUPSD_SANDBOXING_STRICT)
127     cupsFilePuts(fp, "(deny default)\n");
128   else
129     cupsFilePuts(fp, "(allow default)\n");
130   if (LogLevel >= CUPSD_LOG_DEBUG)
131     cupsFilePuts(fp, "(debug deny)\n");
132   cupsFilePuts(fp, "(import \"system.sb\")\n");
133   cupsFilePuts(fp, "(import \"com.apple.corefoundation.sb\")\n");
134   cupsFilePuts(fp, "(system-network)\n");
135   cupsFilePuts(fp, "(allow mach-per-user-lookup)\n");
136   cupsFilePuts(fp, "(allow ipc-posix-sem)\n");
137   cupsFilePuts(fp, "(allow ipc-posix-shm)\n");
138   cupsFilePuts(fp, "(allow ipc-sysv-shm)\n");
139   cupsFilePuts(fp, "(allow mach-lookup)\n");
140   if (!RunUser)
141     cupsFilePrintf(fp,
142 		   "(deny file-write* file-read-data file-read-metadata\n"
143 		   "  (regex"
144 		   " #\"^/Users$\""
145 		   " #\"^/Users/\""
146 		   ")%s)\n", nodebug);
147   cupsFilePrintf(fp,
148                  "(deny file-write*\n"
149                  "  (regex"
150 		 " #\"^%s$\""		/* ServerRoot */
151 		 " #\"^%s/\""		/* ServerRoot/... */
152 		 " #\"^/private/etc$\""
153 		 " #\"^/private/etc/\""
154 		 " #\"^/usr/local/etc$\""
155 		 " #\"^/usr/local/etc/\""
156 		 " #\"^/Library$\""
157 		 " #\"^/Library/\""
158 		 " #\"^/System$\""
159 		 " #\"^/System/\""
160 		 ")%s)\n",
161 		 root, root, nodebug);
162   /* Specifically allow applications to stat RequestRoot and some other system folders */
163   cupsFilePrintf(fp,
164                  "(allow file-read-metadata\n"
165                  "  (regex"
166 		 " #\"^/$\""		/* / */
167 		 " #\"^/usr$\""		/* /usr */
168 		 " #\"^/Library$\""	/* /Library */
169 		 " #\"^/Library/Printers$\""	/* /Library/Printers */
170 		 " #\"^%s$\""		/* RequestRoot */
171 		 "))\n",
172 		 request);
173   /* Read and write TempDir, CacheDir, and other common folders */
174   cupsFilePuts(fp,
175 	       "(allow file-write* file-read-data file-read-metadata\n"
176 	       "  (regex"
177 	       " #\"^/private/var/db/\""
178 	       " #\"^/private/var/folders/\""
179 	       " #\"^/private/var/lib/\""
180 	       " #\"^/private/var/log/\""
181 	       " #\"^/private/var/mysql/\""
182 	       " #\"^/private/var/run/\""
183 	       " #\"^/private/var/spool/\""
184 	       " #\"^/Library/Application Support/\""
185 	       " #\"^/Library/Caches/\""
186 	       " #\"^/Library/Logs/\""
187 	       " #\"^/Library/Preferences/\""
188 	       " #\"^/Library/WebServer/\""
189 	       " #\"^/Users/Shared/\""
190 	       "))\n");
191   cupsFilePrintf(fp,
192 		 "(deny file-write*\n"
193 		 "       (regex #\"^%s$\")%s)\n",
194 		 request, nodebug);
195   cupsFilePrintf(fp,
196 		 "(deny file-write* file-read-data file-read-metadata\n"
197 		 "       (regex #\"^%s/\")%s)\n",
198 		 request, nodebug);
199   cupsFilePrintf(fp,
200                  "(allow file-write* file-read-data file-read-metadata\n"
201                  "  (regex"
202 		 " #\"^%s$\""		/* TempDir */
203 		 " #\"^%s/\""		/* TempDir/... */
204 		 " #\"^%s$\""		/* CacheDir */
205 		 " #\"^%s/\""		/* CacheDir/... */
206 		 "))\n",
207 		 temp, temp, cache, cache);
208   /* Read common folders */
209   cupsFilePrintf(fp,
210                  "(allow file-read-data file-read-metadata\n"
211                  "  (regex"
212                  " #\"^/AppleInternal$\""
213                  " #\"^/AppleInternal/\""
214                  " #\"^/bin$\""		/* /bin */
215                  " #\"^/bin/\""		/* /bin/... */
216                  " #\"^/private$\""
217                  " #\"^/private/etc$\""
218                  " #\"^/private/etc/\""
219                  " #\"^/private/tmp$\""
220                  " #\"^/private/tmp/\""
221                  " #\"^/private/var$\""
222                  " #\"^/private/var/db$\""
223                  " #\"^/private/var/folders$\""
224                  " #\"^/private/var/lib$\""
225                  " #\"^/private/var/log$\""
226                  " #\"^/private/var/mysql$\""
227                  " #\"^/private/var/run$\""
228                  " #\"^/private/var/spool$\""
229                  " #\"^/private/var/tmp$\""
230                  " #\"^/private/var/tmp/\""
231                  " #\"^/usr/bin$\""	/* /usr/bin */
232                  " #\"^/usr/bin/\""	/* /usr/bin/... */
233                  " #\"^/usr/libexec/cups$\""	/* /usr/libexec/cups */
234                  " #\"^/usr/libexec/cups/\""	/* /usr/libexec/cups/... */
235                  " #\"^/usr/libexec/fax$\""	/* /usr/libexec/fax */
236                  " #\"^/usr/libexec/fax/\""	/* /usr/libexec/fax/... */
237                  " #\"^/usr/sbin$\""	/* /usr/sbin */
238                  " #\"^/usr/sbin/\""	/* /usr/sbin/... */
239 		 " #\"^/Library$\""	/* /Library */
240 		 " #\"^/Library/\""	/* /Library/... */
241 		 " #\"^/System$\""	/* /System */
242 		 " #\"^/System/\""	/* /System/... */
243 		 " #\"^%s/Library$\""	/* RequestRoot/Library */
244 		 " #\"^%s/Library/\""	/* RequestRoot/Library/... */
245 		 " #\"^%s$\""		/* ServerBin */
246 		 " #\"^%s/\""		/* ServerBin/... */
247 		 " #\"^%s$\""		/* ServerRoot */
248 		 " #\"^%s/\""		/* ServerRoot/... */
249 		 " #\"^%s$\""		/* StateDir */
250 		 " #\"^%s/\""		/* StateDir/... */
251 		 "))\n",
252 		 request, request, bin, bin, root, root, state, state);
253   if (Sandboxing == CUPSD_SANDBOXING_RELAXED)
254   {
255     /* Limited write access to /Library/Printers/... */
256     cupsFilePuts(fp,
257 		 "(allow file-write*\n"
258 		 "  (regex"
259 		 " #\"^/Library/Printers/.*/\""
260 		 "))\n");
261     cupsFilePrintf(fp,
262 		   "(deny file-write*\n"
263 		   "  (regex"
264 		   " #\"^/Library/Printers/PPDs$\""
265 		   " #\"^/Library/Printers/PPDs/\""
266 		   " #\"^/Library/Printers/PPD Plugins$\""
267 		   " #\"^/Library/Printers/PPD Plugins/\""
268 		   ")%s)\n", nodebug);
269   }
270   /* Allow execution of child processes as long as the programs are not in a user directory */
271   cupsFilePuts(fp, "(allow process*)\n");
272   cupsFilePuts(fp, "(deny process-exec (regex #\"^/Users/\"))\n");
273   if (RunUser && getenv("CUPS_TESTROOT"))
274   {
275     /* Allow source directory access in "make test" environment */
276     char	testroot[1024];		/* Root directory of test files */
277 
278     cupsd_requote(testroot, getenv("CUPS_TESTROOT"), sizeof(testroot));
279 
280     cupsFilePrintf(fp,
281 		   "(allow file-write* file-read-data file-read-metadata\n"
282 		   "  (regex"
283 		   " #\"^%s$\""		/* CUPS_TESTROOT */
284 		   " #\"^%s/\""		/* CUPS_TESTROOT/... */
285 		   "))\n",
286 		   testroot, testroot);
287     cupsFilePrintf(fp,
288 		   "(allow process-exec\n"
289 		   "  (regex"
290 		   " #\"^%s/\""		/* CUPS_TESTROOT/... */
291 		   "))\n",
292 		   testroot);
293     cupsFilePrintf(fp, "(allow sysctl*)\n");
294   }
295   if (job_id)
296   {
297     /* Allow job filters to read the current job files... */
298     cupsFilePrintf(fp,
299                    "(allow file-read-data file-read-metadata\n"
300                    "       (regex #\"^%s/([ac]%05d|d%05d-[0-9][0-9][0-9])$\"))\n",
301 		   request, job_id, job_id);
302   }
303   else
304   {
305     /* Allow email notifications from notifiers... */
306     cupsFilePuts(fp,
307 		 "(allow process-exec\n"
308 		 "  (literal \"/usr/sbin/sendmail\")\n"
309 		 "  (with no-sandbox))\n");
310   }
311   /* Allow access to Bluetooth, USB, and notify_post. */
312   cupsFilePuts(fp, "(allow iokit*)\n");
313   cupsFilePuts(fp, "(allow distributed-notification-post)\n");
314   /* Allow outbound networking to local services */
315   cupsFilePuts(fp, "(allow network-outbound"
316 		   "\n       (regex #\"^/private/var/run/\" #\"^/private/tmp/\" #\"^/private/var/tmp/\")");
317   for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
318        lis;
319        lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
320   {
321     if (httpAddrFamily(&(lis->address)) == AF_LOCAL)
322     {
323       httpAddrString(&(lis->address), domain, sizeof(domain));
324       cupsFilePrintf(fp, "\n       (literal \"%s\")", domain);
325     }
326   }
327   if (allow_networking)
328   {
329     /* Allow TCP and UDP networking off the machine... */
330     cupsFilePuts(fp, "\n       (remote tcp))\n");
331     cupsFilePuts(fp, "(allow network-bind)\n"); /* for LPD resvport */
332     cupsFilePuts(fp, "(allow network*\n"
333 		     "       (local udp \"*:*\")\n"
334 		     "       (remote udp \"*:*\"))\n");
335 
336     /* Also allow access to device files... */
337     cupsFilePuts(fp, "(allow file-write* file-read-data file-read-metadata file-ioctl\n"
338                      "       (regex #\"^/dev/\"))\n");
339 
340     /* And allow kernel extensions to be loaded, e.g., SMB */
341     cupsFilePuts(fp, "(allow system-kext-load)\n");
342   }
343   else
344   {
345     /* Only allow SNMP (UDP) and LPD (TCP) off the machine... */
346     cupsFilePuts(fp, ")\n");
347     cupsFilePuts(fp, "(allow network-outbound\n"
348 		     "       (remote udp \"*:161\")\n"
349 		     "       (remote tcp \"*:515\"))\n");
350     cupsFilePuts(fp, "(allow network-inbound\n"
351 		     "       (local udp \"localhost:*\"))\n");
352   }
353   cupsFileClose(fp);
354 
355   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d,allow_networking=%d) = \"%s\"", job_id, allow_networking, profile);
356   return ((void *)strdup(profile));
357 
358 #else
359   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d, allow_networking=%d) = NULL", job_id, allow_networking);
360 
361   return (NULL);
362 #endif /* HAVE_SANDBOX_H */
363 }
364 
365 
366 /*
367  * 'cupsdDestroyProfile()' - Delete an execution profile.
368  */
369 
370 void
cupsdDestroyProfile(void * profile)371 cupsdDestroyProfile(void *profile)	/* I - Profile */
372 {
373   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteProfile(profile=\"%s\")",
374 		  profile ? (char *)profile : "(null)");
375 
376 #ifdef HAVE_SANDBOX_H
377   if (profile)
378   {
379     unlink((char *)profile);
380     free(profile);
381   }
382 #endif /* HAVE_SANDBOX_H */
383 }
384 
385 
386 /*
387  * 'cupsdEndProcess()' - End a process.
388  */
389 
390 int					/* O - 0 on success, -1 on failure */
cupsdEndProcess(int pid,int force)391 cupsdEndProcess(int pid,		/* I - Process ID */
392                 int force)		/* I - Force child to die */
393 {
394   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdEndProcess(pid=%d, force=%d)", pid,
395                   force);
396 
397   if (!pid)
398     return (0);
399 
400   if (!RunUser)
401   {
402    /*
403     * When running as root, cupsd puts child processes in their own process
404     * group.  Using "-pid" sends a signal to all processes in the group.
405     */
406 
407     pid = -pid;
408   }
409 
410   if (force)
411     return (kill(pid, SIGKILL));
412   else
413     return (kill(pid, SIGTERM));
414 }
415 
416 
417 /*
418  * 'cupsdFinishProcess()' - Finish a process and get its name.
419  */
420 
421 const char *				/* O - Process name */
cupsdFinishProcess(int pid,char * name,size_t namelen,int * job_id)422 cupsdFinishProcess(int    pid,		/* I - Process ID */
423                    char   *name,	/* I - Name buffer */
424 		   size_t namelen,	/* I - Size of name buffer */
425 		   int    *job_id)	/* O - Job ID pointer or NULL */
426 {
427   cupsd_proc_t	key,			/* Search key */
428 		*proc;			/* Matching process */
429 
430 
431   key.pid = pid;
432 
433   if ((proc = (cupsd_proc_t *)cupsArrayFind(process_array, &key)) != NULL)
434   {
435     if (job_id)
436       *job_id = proc->job_id;
437 
438     strlcpy(name, proc->name, namelen);
439     cupsArrayRemove(process_array, proc);
440     free(proc);
441   }
442   else
443   {
444     if (job_id)
445       *job_id = 0;
446 
447     strlcpy(name, "unknown", namelen);
448   }
449 
450   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFinishProcess(pid=%d, name=%p, namelen=" CUPS_LLFMT ", job_id=%p(%d)) = \"%s\"", pid, name, CUPS_LLCAST namelen, job_id, job_id ? *job_id : 0, name);
451 
452   return (name);
453 }
454 
455 
456 /*
457  * 'cupsdStartProcess()' - Start a process.
458  */
459 
460 int					/* O - Process ID or 0 */
cupsdStartProcess(const char * command,char * argv[],char * envp[],int infd,int outfd,int errfd,int backfd,int sidefd,int root,void * profile,cupsd_job_t * job,int * pid)461 cupsdStartProcess(
462     const char  *command,		/* I - Full path to command */
463     char        *argv[],		/* I - Command-line arguments */
464     char        *envp[],		/* I - Environment */
465     int         infd,			/* I - Standard input file descriptor */
466     int         outfd,			/* I - Standard output file descriptor */
467     int         errfd,			/* I - Standard error file descriptor */
468     int         backfd,			/* I - Backchannel file descriptor */
469     int         sidefd,			/* I - Sidechannel file descriptor */
470     int         root,			/* I - Run as root? */
471     void        *profile,		/* I - Security profile to use */
472     cupsd_job_t *job,			/* I - Job associated with process */
473     int         *pid)			/* O - Process ID */
474 {
475   int		i;			/* Looping var */
476   const char	*exec_path = command;	/* Command to be exec'd */
477   char		*real_argv[110],	/* Real command-line arguments */
478 		cups_exec[1024],	/* Path to "cups-exec" program */
479 		user_str[16],		/* User string */
480 		group_str[16],		/* Group string */
481 		nice_str[16];		/* FilterNice string */
482   uid_t		user;			/* Command UID */
483   cupsd_proc_t	*proc;			/* New process record */
484 #if USE_POSIX_SPAWN
485   posix_spawn_file_actions_t actions;	/* Spawn file actions */
486   posix_spawnattr_t attrs;		/* Spawn attributes */
487   sigset_t	defsignals;		/* Default signals */
488 #elif defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
489   struct sigaction action;		/* POSIX signal handler */
490 #endif /* USE_POSIX_SPAWN */
491 #if defined(__APPLE__)
492   char		processPath[1024],	/* CFProcessPath environment variable */
493 		linkpath[1024];		/* Link path for symlinks... */
494   int		linkbytes;		/* Bytes for link path */
495 #endif /* __APPLE__ */
496 
497 
498   *pid = 0;
499 
500  /*
501   * Figure out the UID for the child process...
502   */
503 
504   if (RunUser)
505     user = RunUser;
506   else if (root)
507     user = 0;
508   else
509     user = User;
510 
511  /*
512   * Check the permissions of the command we are running...
513   */
514 
515   if (_cupsFileCheck(command, _CUPS_FILE_CHECK_PROGRAM, !RunUser,
516                      cupsdLogFCMessage, job ? job->printer : NULL))
517     return (0);
518 
519 #if defined(__APPLE__)
520   if (envp)
521   {
522    /*
523     * Add special voodoo magic for macOS - this allows macOS programs to access
524     * their bundle resources properly...
525     */
526 
527     if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0)
528     {
529      /*
530       * Yes, this is a symlink to the actual program, nul-terminate and
531       * use it...
532       */
533 
534       linkpath[linkbytes] = '\0';
535 
536       if (linkpath[0] == '/')
537 	snprintf(processPath, sizeof(processPath), "CFProcessPath=%s",
538 		 linkpath);
539       else
540 	snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s",
541 		 dirname((char *)command), linkpath);
542     }
543     else
544       snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command);
545 
546     envp[0] = processPath;		/* Replace <CFProcessPath> string */
547   }
548 #endif	/* __APPLE__ */
549 
550  /*
551   * Use helper program when we have a sandbox profile...
552   */
553 
554 #if !USE_POSIX_SPAWN
555   if (profile)
556 #endif /* !USE_POSIX_SPAWN */
557   {
558     snprintf(cups_exec, sizeof(cups_exec), "%s/daemon/cups-exec", ServerBin);
559     snprintf(user_str, sizeof(user_str), "%d", user);
560     snprintf(group_str, sizeof(group_str), "%d", Group);
561     snprintf(nice_str, sizeof(nice_str), "%d", FilterNice);
562 
563     real_argv[0] = cups_exec;
564     real_argv[1] = (char *)"-g";
565     real_argv[2] = group_str;
566     real_argv[3] = (char *)"-n";
567     real_argv[4] = nice_str;
568     real_argv[5] = (char *)"-u";
569     real_argv[6] = user_str;
570     real_argv[7] = profile ? profile : "none";
571     real_argv[8] = (char *)command;
572 
573     for (i = 0;
574          i < (int)(sizeof(real_argv) / sizeof(real_argv[0]) - 10) && argv[i];
575 	 i ++)
576       real_argv[i + 9] = argv[i];
577 
578     real_argv[i + 9] = NULL;
579 
580     argv      = real_argv;
581     exec_path = cups_exec;
582   }
583 
584   if (LogLevel == CUPSD_LOG_DEBUG2)
585   {
586     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Preparing to start \"%s\", arguments:", command);
587 
588     for (i = 0; argv[i]; i ++)
589       cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: argv[%d] = \"%s\"", i, argv[i]);
590   }
591 
592 #if USE_POSIX_SPAWN
593  /*
594   * Setup attributes and file actions for the spawn...
595   */
596 
597   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting spawn attributes.");
598   sigemptyset(&defsignals);
599   sigaddset(&defsignals, SIGTERM);
600   sigaddset(&defsignals, SIGCHLD);
601   sigaddset(&defsignals, SIGPIPE);
602 
603   posix_spawnattr_init(&attrs);
604   posix_spawnattr_setflags(&attrs, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF);
605   posix_spawnattr_setpgroup(&attrs, 0);
606   posix_spawnattr_setsigdefault(&attrs, &defsignals);
607 
608   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Setting file actions.");
609   posix_spawn_file_actions_init(&actions);
610   if (infd != 0)
611   {
612     if (infd < 0)
613       posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDONLY, 0);
614     else
615       posix_spawn_file_actions_adddup2(&actions, infd, 0);
616   }
617 
618   if (outfd != 1)
619   {
620     if (outfd < 0)
621       posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_WRONLY, 0);
622     else
623       posix_spawn_file_actions_adddup2(&actions, outfd, 1);
624   }
625 
626   if (errfd != 2)
627   {
628     if (errfd < 0)
629       posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_WRONLY, 0);
630     else
631       posix_spawn_file_actions_adddup2(&actions, errfd, 2);
632   }
633 
634   if (backfd != 3 && backfd >= 0)
635     posix_spawn_file_actions_adddup2(&actions, backfd, 3);
636 
637   if (sidefd != 4 && sidefd >= 0)
638     posix_spawn_file_actions_adddup2(&actions, sidefd, 4);
639 
640   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: Calling posix_spawn.");
641 
642   if (posix_spawn(pid, exec_path, &actions, &attrs, argv, envp ? envp : environ))
643   {
644     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, strerror(errno));
645 
646     *pid = 0;
647   }
648   else
649     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartProcess: pid=%d", (int)*pid);
650 
651   posix_spawn_file_actions_destroy(&actions);
652   posix_spawnattr_destroy(&attrs);
653 
654 #else
655  /*
656   * Block signals before forking...
657   */
658 
659   cupsdHoldSignals();
660 
661   if ((*pid = fork()) == 0)
662   {
663    /*
664     * Child process goes here; update stderr as needed...
665     */
666 
667     if (errfd != 2)
668     {
669       if (errfd < 0)
670         errfd = open("/dev/null", O_WRONLY);
671 
672       if (errfd != 2)
673       {
674         dup2(errfd, 2);
675 	close(errfd);
676       }
677     }
678 
679    /*
680     * Put this process in its own process group so that we can kill any child
681     * processes it creates.
682     */
683 
684 #  ifdef HAVE_SETPGID
685     if (!RunUser && setpgid(0, 0))
686       exit(errno + 100);
687 #  else
688     if (!RunUser && setpgrp())
689       exit(errno + 100);
690 #  endif /* HAVE_SETPGID */
691 
692    /*
693     * Update the remaining file descriptors as needed...
694     */
695 
696     if (infd != 0)
697     {
698       if (infd < 0)
699         infd = open("/dev/null", O_RDONLY);
700 
701       if (infd != 0)
702       {
703         dup2(infd, 0);
704 	close(infd);
705       }
706     }
707 
708     if (outfd != 1)
709     {
710       if (outfd < 0)
711         outfd = open("/dev/null", O_WRONLY);
712 
713       if (outfd != 1)
714       {
715         dup2(outfd, 1);
716 	close(outfd);
717       }
718     }
719 
720     if (backfd != 3 && backfd >= 0)
721     {
722       dup2(backfd, 3);
723       close(backfd);
724       fcntl(3, F_SETFL, O_NDELAY);
725     }
726 
727     if (sidefd != 4 && sidefd >= 0)
728     {
729       dup2(sidefd, 4);
730       close(sidefd);
731       fcntl(4, F_SETFL, O_NDELAY);
732     }
733 
734    /*
735     * Change the priority of the process based on the FilterNice setting.
736     * (this is not done for root processes...)
737     */
738 
739     if (!root)
740       nice(FilterNice);
741 
742    /*
743     * Reset group membership to just the main one we belong to.
744     */
745 
746     if (!RunUser && setgid(Group))
747       exit(errno + 100);
748 
749 #  if CUPS_SNAP
750     if (!RunUser && setgroups(0, NULL))
751 #  else
752     if (!RunUser && setgroups(1, &Group))
753 #  endif /* CUPS_SNAP */
754       exit(errno + 100);
755 
756    /*
757     * Change user to something "safe"...
758     */
759 
760     if (!RunUser && user && setuid(user))
761       exit(errno + 100);
762 
763    /*
764     * Change umask to restrict permissions on created files...
765     */
766 
767     umask(077);
768 
769    /*
770     * Unblock signals before doing the exec...
771     */
772 
773 #  ifdef HAVE_SIGSET
774     sigset(SIGTERM, SIG_DFL);
775     sigset(SIGCHLD, SIG_DFL);
776     sigset(SIGPIPE, SIG_DFL);
777 #  elif defined(HAVE_SIGACTION)
778     memset(&action, 0, sizeof(action));
779 
780     sigemptyset(&action.sa_mask);
781     action.sa_handler = SIG_DFL;
782 
783     sigaction(SIGTERM, &action, NULL);
784     sigaction(SIGCHLD, &action, NULL);
785     sigaction(SIGPIPE, &action, NULL);
786 #  else
787     signal(SIGTERM, SIG_DFL);
788     signal(SIGCHLD, SIG_DFL);
789     signal(SIGPIPE, SIG_DFL);
790 #  endif /* HAVE_SIGSET */
791 
792     cupsdReleaseSignals();
793 
794    /*
795     * Execute the command; if for some reason this doesn't work, log an error
796     * exit with a non-zero value...
797     */
798 
799     if (envp)
800       execve(exec_path, argv, envp);
801     else
802       execv(exec_path, argv);
803 
804     exit(errno + 100);
805   }
806   else if (*pid < 0)
807   {
808    /*
809     * Error - couldn't fork a new process!
810     */
811 
812     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command,
813                     strerror(errno));
814 
815     *pid = 0;
816   }
817 
818   cupsdReleaseSignals();
819 #endif /* USE_POSIX_SPAWN */
820 
821   if (*pid)
822   {
823     if (!process_array)
824       process_array = cupsArrayNew((cups_array_func_t)compare_procs, NULL);
825 
826     if (process_array)
827     {
828       if ((proc = calloc(1, sizeof(cupsd_proc_t) + strlen(command))) != NULL)
829       {
830         proc->pid    = *pid;
831 	proc->job_id = job ? job->id : 0;
832 	_cups_strcpy(proc->name, command);
833 
834 	cupsArrayAdd(process_array, proc);
835       }
836     }
837   }
838 
839   cupsdLogMessage(CUPSD_LOG_DEBUG2,
840 		  "cupsdStartProcess(command=\"%s\", argv=%p, envp=%p, "
841 		  "infd=%d, outfd=%d, errfd=%d, backfd=%d, sidefd=%d, root=%d, "
842 		  "profile=%p, job=%p(%d), pid=%p) = %d",
843 		  command, argv, envp, infd, outfd, errfd, backfd, sidefd,
844 		  root, profile, job, job ? job->id : 0, pid, *pid);
845 
846   return (*pid);
847 }
848 
849 
850 /*
851  * 'compare_procs()' - Compare two processes.
852  */
853 
854 static int				/* O - Result of comparison */
compare_procs(cupsd_proc_t * a,cupsd_proc_t * b)855 compare_procs(cupsd_proc_t *a,		/* I - First process */
856               cupsd_proc_t *b)		/* I - Second process */
857 {
858   return (a->pid - b->pid);
859 }
860 
861 
862 #ifdef HAVE_SANDBOX_H
863 /*
864  * 'cupsd_requote()' - Make a regular-expression version of a string.
865  */
866 
867 static char *				/* O - Quoted string */
cupsd_requote(char * dst,const char * src,size_t dstsize)868 cupsd_requote(char       *dst,		/* I - Destination buffer */
869               const char *src,		/* I - Source string */
870 	      size_t     dstsize)	/* I - Size of destination buffer */
871 {
872   int	ch;				/* Current character */
873   char	*dstptr,			/* Current position in buffer */
874 	*dstend;			/* End of destination buffer */
875 
876 
877   dstptr = dst;
878   dstend = dst + dstsize - 2;
879 
880   while (*src && dstptr < dstend)
881   {
882     ch = *src++;
883 
884     if (ch == '/' && !*src)
885       break;				/* Don't add trailing slash */
886 
887     if (strchr(".?*()[]^$\\\"", ch))
888       *dstptr++ = '\\';
889 
890     *dstptr++ = (char)ch;
891   }
892 
893   *dstptr = '\0';
894 
895   return (dst);
896 }
897 #endif /* HAVE_SANDBOX_H */
898