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