diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 15:47:44 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 15:47:44 -0700 |
commit | 4c8dd44ec57d63e728bda99034c043b8941419df (patch) | |
tree | 5531f93b7c293790552944e6981a1745baaf7226 /scheduler/process.c | |
download | cups-4c8dd44ec57d63e728bda99034c043b8941419df.tar.gz cups-4c8dd44ec57d63e728bda99034c043b8941419df.tar.bz2 cups-4c8dd44ec57d63e728bda99034c043b8941419df.zip |
Imported Upstream version 1.5.3upstream/1.5.3
Diffstat (limited to 'scheduler/process.c')
-rw-r--r-- | scheduler/process.c | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/scheduler/process.c b/scheduler/process.c new file mode 100644 index 0000000..c1ca526 --- /dev/null +++ b/scheduler/process.c @@ -0,0 +1,656 @@ +/* + * "$Id: process.c 9790 2011-05-19 22:40:03Z mike $" + * + * Process management routines for the CUPS scheduler. + * + * Copyright 2007-2011 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. + */ + +/* + * Include necessary headers... + */ + +#include "cupsd.h" +#include <grp.h> +#ifdef __APPLE__ +# include <libgen.h> +#endif /* __APPLE__ */ + + +/* + * Process structure... + */ + +typedef struct +{ + int pid, /* Process ID */ + job_id; /* Job associated with process */ + char name[1]; /* Name of process */ +} cupsd_proc_t; + + +/* + * Local globals... + */ + +static cups_array_t *process_array = NULL; + + +/* + * Local functions... + */ + +static int compare_procs(cupsd_proc_t *a, cupsd_proc_t *b); +#ifdef HAVE_SANDBOX_H +static char *cupsd_requote(char *dst, const char *src, size_t dstsize); +#endif /* HAVE_SANDBOX_H */ + + +/* + * 'cupsdCreateProfile()' - Create an execution profile for a subprocess. + */ + +void * /* O - Profile or NULL on error */ +cupsdCreateProfile(int job_id) /* I - Job ID or 0 for none */ +{ +#ifdef HAVE_SANDBOX_H + cups_file_t *fp; /* File pointer */ + char profile[1024], /* File containing the profile */ + cache[1024], /* Quoted CacheDir */ + request[1024], /* Quoted RequestRoot */ + root[1024], /* Quoted ServerRoot */ + temp[1024]; /* Quoted TempDir */ + const char *nodebug; /* " (with no-log)" for no debug */ + + + if (!UseProfiles) + { + /* + * Only use sandbox profiles as root... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = NULL", + job_id); + + return (NULL); + } + + if ((fp = cupsTempFile2(profile, sizeof(profile))) == NULL) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = NULL", + job_id); + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create security profile: %s", + strerror(errno)); + 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"); + cupsFilePuts(fp, "(allow default)\n"); + cupsFilePrintf(fp, + "(deny file-write* file-read-data file-read-metadata\n" + " (regex" + " #\"^%s$\"" /* RequestRoot */ + " #\"^%s/\"" /* RequestRoot/... */ + ")%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" + " #\"^%s$\"" /* ServerRoot */ + " #\"^%s/\"" /* ServerRoot/... */ + " #\"^/private/etc$\"" + " #\"^/private/etc/\"" + " #\"^/usr/local/etc$\"" + " #\"^/usr/local/etc/\"" + " #\"^/Library$\"" + " #\"^/Library/\"" + " #\"^/System$\"" + " #\"^/System/\"" + ")%s)\n", + root, root, nodebug); + /* Specifically allow applications to stat RequestRoot */ + cupsFilePrintf(fp, + "(allow file-read-metadata\n" + " (regex" + " #\"^%s$\"" /* RequestRoot */ + "))\n", + request); + cupsFilePrintf(fp, + "(allow file-write* file-read-data file-read-metadata\n" + " (regex" + " #\"^%s$\"" /* TempDir */ + " #\"^%s/\"" /* TempDir/... */ + " #\"^%s$\"" /* CacheDir */ + " #\"^%s/\"" /* CacheDir/... */ + " #\"^%s/Library$\"" /* RequestRoot/Library */ + " #\"^%s/Library/\"" /* RequestRoot/Library/... */ + " #\"^/Library/Application Support/\"" + " #\"^/Library/Caches/\"" + " #\"^/Library/Preferences/\"" + " #\"^/Library/Printers/.*/\"" + " #\"^/Users/Shared/\"" + "))\n", + temp, temp, cache, cache, request, request); + 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); + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = \"%s\"", + job_id, profile); + return ((void *)strdup(profile)); + +#else + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCreateProfile(job_id=%d) = NULL", + job_id); + + return (NULL); +#endif /* HAVE_SANDBOX_H */ +} + + +/* + * 'cupsdDestroyProfile()' - Delete an execution profile. + */ + +void +cupsdDestroyProfile(void *profile) /* I - Profile */ +{ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteProfile(profile=\"%s\")", + profile ? (char *)profile : "(null)"); + +#ifdef HAVE_SANDBOX_H + if (profile) + { + unlink((char *)profile); + free(profile); + } +#endif /* HAVE_SANDBOX_H */ +} + + +/* + * 'cupsdEndProcess()' - End a process. + */ + +int /* O - 0 on success, -1 on failure */ +cupsdEndProcess(int pid, /* I - Process ID */ + int force) /* I - Force child to die */ +{ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdEndProcess(pid=%d, force=%d)", pid, + force); + + if (!pid) + return (0); + + 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)); +} + + +/* + * 'cupsdFinishProcess()' - Finish a process and get its name. + */ + +const char * /* O - Process name */ +cupsdFinishProcess(int pid, /* I - Process ID */ + char *name, /* I - Name buffer */ + int namelen, /* I - Size of name buffer */ + int *job_id) /* O - Job ID pointer or NULL */ +{ + cupsd_proc_t key, /* Search key */ + *proc; /* Matching process */ + + + key.pid = pid; + + if ((proc = (cupsd_proc_t *)cupsArrayFind(process_array, &key)) != NULL) + { + if (job_id) + *job_id = proc->job_id; + + strlcpy(name, proc->name, namelen); + cupsArrayRemove(process_array, proc); + free(proc); + } + else + { + if (job_id) + *job_id = 0; + + strlcpy(name, "unknown", namelen); + } + + cupsdLogMessage(CUPSD_LOG_DEBUG2, + "cupsdFinishProcess(pid=%d, name=%p, namelen=%d, " + "job_id=%p(%d)) = \"%s\"", pid, name, namelen, job_id, + job_id ? *job_id : 0, name); + + return (name); +} + + +/* + * 'cupsdStartProcess()' - Start a process. + */ + +int /* O - Process ID or 0 */ +cupsdStartProcess( + const char *command, /* I - Full path to command */ + char *argv[], /* I - Command-line arguments */ + char *envp[], /* I - Environment */ + int infd, /* I - Standard input file descriptor */ + int outfd, /* I - Standard output file descriptor */ + int errfd, /* I - Standard error file descriptor */ + int backfd, /* I - Backchannel file descriptor */ + int sidefd, /* I - Sidechannel file descriptor */ + int root, /* I - Run as root? */ + void *profile, /* I - Security profile to use */ + cupsd_job_t *job, /* I - Job associated with process */ + int *pid) /* O - Process ID */ +{ + 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 */ + int user; /* Command UID */ + cupsd_proc_t *proc; /* New process record */ +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) + struct sigaction action; /* POSIX signal handler */ +#endif /* HAVE_SIGACTION && !HAVE_SIGSET */ +#if defined(__APPLE__) + char processPath[1024], /* CFProcessPath environment variable */ + linkpath[1024]; /* Link path for symlinks... */ + int linkbytes; /* Bytes for link path */ +#endif /* __APPLE__ */ + + + *pid = 0; + + /* + * Figure out the UID for the child process... + */ + + if (RunUser) + user = RunUser; + else if (root) + user = 0; + else + user = User; + + /* + * Check the permissions of the command we are running... + */ + + 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... + */ + + if ((linkbytes = readlink(command, linkpath, sizeof(linkpath) - 1)) > 0) + { + /* + * Yes, this is a symlink to the actual program, nul-terminate and + * use it... + */ + + linkpath[linkbytes] = '\0'; + + if (linkpath[0] == '/') + snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", + linkpath); + else + snprintf(processPath, sizeof(processPath), "CFProcessPath=%s/%s", + dirname((char *)command), linkpath); + } + else + snprintf(processPath, sizeof(processPath), "CFProcessPath=%s", command); + + envp[0] = processPath; /* Replace <CFProcessPath> string */ + } +#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... + */ + + cupsdHoldSignals(); + + if ((*pid = fork()) == 0) + { + /* + * 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) + { + if (infd < 0) + infd = open("/dev/null", O_RDONLY); + + if (infd != 0) + { + dup2(infd, 0); + close(infd); + } + } + + if (outfd != 1) + { + if (outfd < 0) + outfd = open("/dev/null", O_WRONLY); + + if (outfd != 1) + { + dup2(outfd, 1); + close(outfd); + } + } + + if (backfd != 3 && backfd >= 0) + { + dup2(backfd, 3); + close(backfd); + fcntl(3, F_SETFL, O_NDELAY); + } + + if (sidefd != 4 && sidefd >= 0) + { + dup2(sidefd, 4); + close(sidefd); + fcntl(4, F_SETFL, O_NDELAY); + } + + /* + * Change the priority of the process based on the FilterNice setting. + * (this is not done for root processes...) + */ + + if (!root) + nice(FilterNice); + + /* + * Reset group membership to just the main one we belong to. + */ + + if (!RunUser && setgid(Group)) + exit(errno + 100); + + if (!RunUser && setgroups(1, &Group)) + exit(errno + 100); + + /* + * Change user to something "safe"... + */ + + if (!RunUser && user && setuid(user)) + exit(errno + 100); + + /* + * Change umask to restrict permissions on created files... + */ + + umask(077); + + /* + * Unblock signals before doing the exec... + */ + +#ifdef HAVE_SIGSET + sigset(SIGTERM, SIG_DFL); + sigset(SIGCHLD, SIG_DFL); + sigset(SIGPIPE, SIG_DFL); +#elif defined(HAVE_SIGACTION) + memset(&action, 0, sizeof(action)); + + sigemptyset(&action.sa_mask); + action.sa_handler = SIG_DFL; + + sigaction(SIGTERM, &action, NULL); + sigaction(SIGCHLD, &action, NULL); + sigaction(SIGPIPE, &action, NULL); +#else + signal(SIGTERM, SIG_DFL); + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +#endif /* HAVE_SIGSET */ + + cupsdReleaseSignals(); + + /* + * Execute the command; if for some reason this doesn't work, log an error + * exit with a non-zero value... + */ + + if (envp) + execve(exec_path, argv, envp); + else + execv(exec_path, argv); + + exit(errno + 100); + } + else if (*pid < 0) + { + /* + * Error - couldn't fork a new process! + */ + + cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork %s - %s.", command, + strerror(errno)); + + *pid = 0; + } + else + { + 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; + _cups_strcpy(proc->name, command); + + cupsArrayAdd(process_array, proc); + } + } + } + + cupsdReleaseSignals(); + + 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); + + return (*pid); +} + + +/* + * 'compare_procs()' - Compare two processes. + */ + +static int /* O - Result of comparison */ +compare_procs(cupsd_proc_t *a, /* I - First process */ + cupsd_proc_t *b) /* I - Second process */ +{ + return (a->pid - b->pid); +} + + +#ifdef HAVE_SANDBOX_H +/* + * 'cupsd_requote()' - Make a regular-expression version of a string. + */ + +static char * /* O - Quoted string */ +cupsd_requote(char *dst, /* I - Destination buffer */ + const char *src, /* I - Source string */ + size_t dstsize) /* I - Size of destination buffer */ +{ + int ch; /* Current character */ + char *dstptr, /* Current position in buffer */ + *dstend; /* End of destination buffer */ + + + dstptr = dst; + dstend = dst + dstsize - 2; + + while (*src && dstptr < dstend) + { + ch = *src++; + + if (strchr(".?*()[]^$\\", ch)) + *dstptr++ = '\\'; + + *dstptr++ = ch; + } + + *dstptr = '\0'; + + return (dst); +} +#endif /* HAVE_SANDBOX_H */ + + +/* + * End of "$Id: process.c 9790 2011-05-19 22:40:03Z mike $". + */ |