| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ |
| /* All Rights Reserved */ |
| |
| #pragma ident "%Z%%M% %I% %E% SMI" |
| |
| #include <pwd.h> |
| #include <zone.h> |
| #if defined PS_FAULTED |
| #undef PS_FAULTED |
| #endif /* PS_FAULTED */ |
| #include <dial.h> |
| |
| #include <stdlib.h> |
| #include "limits.h" |
| #include "stdarg.h" |
| #include "wait.h" |
| #include "dial.h" |
| #include "lpsched.h" |
| #include <syslog.h> |
| #include "tsol/label.h" |
| |
| #define Done(EC,ERRNO) done(((EC) << 8),ERRNO) |
| |
| #define STRLCAT(dst, src, size) \ |
| if (strlcat((dst), (src), (size)) >= (size)) { \ |
| errno = EINVAL; \ |
| return (-1); \ |
| } |
| |
| static MESG * ChildMd; |
| |
| static int ChildPid; |
| static int WaitedChildPid; |
| static int slot; |
| static int do_undial; |
| |
| static char argbuf[ARG_MAX]; |
| |
| static long key; |
| |
| static void sigtrap ( int ); |
| static void done ( int , int ); |
| static void cool_heels ( void ); |
| static void addenv (char ***envp, char * , char * ); |
| static void trap_fault_signals ( void ); |
| static void ignore_fault_signals ( void ); |
| static void child_mallocfail ( void ); |
| static void Fork2 ( void ); |
| |
| static int Fork1 ( EXEC * ); |
| |
| static void |
| relock(void) |
| { |
| struct flock l; |
| |
| l.l_type = F_WRLCK; |
| l.l_whence = 1; |
| l.l_start = 0; |
| l.l_len = 0; |
| (void)Fcntl (lock_fd, F_SETLK, &l); |
| return; |
| } |
| |
| static char *_exec_name(int type) |
| { |
| static char *_names[] = { |
| "", "EX_INTERF", "EX_SLOWF", "EX_ALERT", "EX_FALERT", "EX_PALERT", |
| "EX_NOTIFY", "EX_FAULT_MESSAGE", "EX_FORM_MESSAGE", NULL }; |
| |
| if ((type < 0) || (type > EX_FORM_MESSAGE)) |
| return ("BAD_EXEC_TYPE"); |
| else |
| return (_names[type]); |
| } |
| |
| /* |
| * This function replaces characters in a string that might be used |
| * to exploit a security hole. Replace command seperators (`, &, ;, |, ^), |
| * output redirection (>, |), variable expansion ($), and character |
| * escape (\). |
| * |
| * Bugid 4141687 |
| * Add ( ) < * ? [ |
| * Bugid 4139071 |
| * Remove \ |
| */ |
| void clean_string(char *ptr) |
| { |
| char *cp; |
| wchar_t wc; |
| size_t len; |
| |
| for (cp = ptr; *cp != NULL; ) { |
| if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) { |
| cp++; |
| continue; |
| } |
| |
| if (len == 1 && |
| ((wc == L'`') || (wc == L'&') || (wc == L';') || |
| (wc == L'|') || (wc == L'>') || (wc == L'^') || |
| (wc == L'$') || (wc == L'(') || (wc == L')') || |
| (wc == L'<') || (wc == L'*') || (wc == L'?') || |
| (wc == L'['))) |
| *cp = '_'; |
| cp += len; |
| } |
| } |
| |
| enum trust {TRUSTED, UNTRUSTED}; |
| static char * |
| arg_string(enum trust type, char *fmt, ...) |
| { |
| char buf[BUFSIZ]; |
| va_list args; |
| |
| va_start(args, fmt); |
| (void) vsnprintf(buf, sizeof(buf), fmt, args); |
| va_end(args); |
| |
| /* |
| * If the string contains data from an untrusted origin (user supplied), |
| * clean it up in case one of our progeny is a shell script and isn't |
| * careful about checking its input. |
| */ |
| if (type == UNTRUSTED) |
| clean_string(buf); |
| |
| return (strdup(buf)); |
| } |
| |
| /* stolen from libc/gen/port/gen/execvp.c */ |
| static const char * |
| execat(const char *s1, const char *s2, char *si) |
| { |
| char *s; |
| int cnt = PATH_MAX + 1; /* number of characters in s2 */ |
| |
| s = si; |
| while (*s1 && *s1 != ':') { |
| if (cnt > 0) { |
| *s++ = *s1++; |
| cnt--; |
| } else |
| s1++; |
| } |
| if (si != s && cnt > 0) { |
| *s++ = '/'; |
| cnt--; |
| } |
| while (*s2 && cnt > 0) { |
| *s++ = *s2++; |
| cnt--; |
| } |
| *s = '\0'; |
| return (*s1 ? ++s1: 0); |
| } |
| |
| /* |
| * Similiar to execvp(), execpt you can supply an environment and we always |
| * use /bin/sh for shell scripts. The PATH searched is the PATH in the |
| * current environment, not the environment in the argument list. |
| * This was pretty much stolen from libc/gen/port/execvp.c |
| */ |
| static int |
| execvpe(char *name, char *const argv[], char *const envp[]) |
| { |
| char *path; |
| char fname[PATH_MAX+2]; |
| char *newargs[256]; |
| int i; |
| const char *cp; |
| unsigned etxtbsy = 1; |
| int eacces = 0; |
| |
| if (*name == '\0') { |
| errno = ENOENT; |
| return (-1); |
| } |
| |
| if ((path = getenv("PATH")) == NULL) |
| path = "/usr/bin:/bin"; |
| |
| cp = strchr(name, '/')? (const char *)"": path; |
| |
| do { |
| cp = execat(cp, name, fname); |
| retry: |
| /* |
| * 4025035 and 4038378 |
| * if a filename begins with a "-" prepend "./" so that |
| * the shell can't interpret it as an option |
| */ |
| if (*fname == '-') { |
| size_t size = strlen(fname) + 1; |
| if ((size + 2) > sizeof (fname)) { |
| errno = E2BIG; |
| return (-1); |
| } |
| (void) memmove(fname + 2, fname, size); |
| fname[0] = '.'; |
| fname[1] = '/'; |
| } |
| (void) execve(fname, argv, envp); |
| switch (errno) { |
| case ENOEXEC: |
| newargs[0] = "sh"; |
| newargs[1] = fname; |
| for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) { |
| if (i >= 254) { |
| errno = E2BIG; |
| return (-1); |
| } |
| } |
| (void) execve("/bin/sh", newargs, envp); |
| return (-1); |
| case ETXTBSY: |
| if (++etxtbsy > 5) |
| return (-1); |
| (void) sleep(etxtbsy); |
| goto retry; |
| case EACCES: |
| ++eacces; |
| break; |
| case ENOMEM: |
| case E2BIG: |
| case EFAULT: |
| return (-1); |
| } |
| } while (cp); |
| if (eacces) |
| errno = EACCES; |
| return (-1); |
| } |
| |
| static char time_buf[50]; |
| |
| /** |
| ** exec() - FORK AND EXEC CHILD PROCESS |
| **/ |
| |
| /*VARARGS1*/ |
| int |
| exec(int type, ...) |
| { |
| va_list args; |
| |
| int i; |
| int procuid; |
| int procgid; |
| int ret; |
| int fr_flg; |
| |
| char *cp; |
| char *infile; |
| char *outfile; |
| char *errfile; |
| char *sep; |
| |
| char **listp; |
| char **file_list; |
| char *printerName; |
| char *printerNameToShow; |
| static char nameBuf[100]; |
| char *clean_title; |
| |
| PSTATUS *printer; |
| |
| RSTATUS *request; |
| |
| FSTATUS *form; |
| |
| EXEC *ep; |
| |
| PWSTATUS *pwheel; |
| time_t now; |
| struct passwd *pwp; |
| #ifdef LP_USE_PAPI_ATTR |
| struct stat tmpBuf; |
| char tmpName[BUFSIZ]; |
| char *path = NULL; |
| #endif |
| char *av[ARG_MAX]; |
| char **envp = NULL; |
| int ac = 0; |
| char *mail_zonename = NULL; |
| char *slabel = NULL; |
| |
| syslog(LOG_DEBUG, "exec(%s)", _exec_name(type)); |
| |
| memset(av, 0, sizeof (*av)); |
| |
| va_start (args, type); |
| |
| switch (type) { |
| |
| case EX_INTERF: |
| printer = va_arg(args, PSTATUS *); |
| if (printer->status & PS_REMOTE) { |
| errno = EINVAL; |
| return (-1); |
| } |
| request = printer->request; |
| ep = printer->exec; |
| break; |
| |
| case EX_FAULT_MESSAGE: |
| printer = va_arg(args, PSTATUS *); |
| request = va_arg(args, RSTATUS *); |
| if (! ( printer->status & (PS_FORM_FAULT | PS_SHOW_FAULT))) { |
| return(0); |
| } |
| ep = printer->fault_exec; |
| printerName = (printer->printer && printer->printer->name |
| ? printer->printer->name : "??"); |
| snprintf(nameBuf, sizeof (nameBuf), |
| "%s (on %s)\n", printerName, Local_System); |
| |
| printerNameToShow = nameBuf; |
| |
| (void) time(&now); |
| (void) strftime(time_buf, sizeof (time_buf), |
| NULL, localtime(&now)); |
| break; |
| |
| case EX_SLOWF: |
| request = va_arg(args, RSTATUS *); |
| ep = request->exec; |
| break; |
| |
| case EX_NOTIFY: |
| request = va_arg(args, RSTATUS *); |
| if (request->request->actions & ACT_NOTIFY) { |
| errno = EINVAL; |
| return (-1); |
| } |
| ep = request->exec; |
| break; |
| |
| case EX_ALERT: |
| printer = va_arg(args, PSTATUS *); |
| if (!(printer->printer->fault_alert.shcmd)) { |
| errno = EINVAL; |
| return(-1); |
| } |
| ep = printer->alert->exec; |
| break; |
| |
| case EX_PALERT: |
| pwheel = va_arg(args, PWSTATUS *); |
| ep = pwheel->alert->exec; |
| break; |
| |
| case EX_FORM_MESSAGE: |
| (void) time(&now); |
| (void) strftime(time_buf, sizeof (time_buf), |
| NULL, localtime(&now)); |
| |
| /*FALLTHRU*/ |
| case EX_FALERT: |
| form = va_arg(args, FSTATUS *); |
| ep = form->alert->exec; |
| break; |
| |
| default: |
| errno = EINVAL; |
| return(-1); |
| |
| } |
| va_end (args); |
| |
| if (!ep || (ep->pid > 0)) { |
| errno = EBUSY; |
| return(-1); |
| } |
| |
| ep->flags = 0; |
| |
| key = ep->key = getkey(); |
| slot = ep - Exec_Table; |
| |
| switch ((ep->pid = Fork1(ep))) { |
| |
| case -1: |
| relock (); |
| return(-1); |
| |
| case 0: |
| /* |
| * We want to be able to tell our parent how we died. |
| */ |
| lp_alloc_fail_handler = child_mallocfail; |
| break; |
| |
| default: |
| switch(type) { |
| |
| case EX_INTERF: |
| request->request->outcome |= RS_PRINTING; |
| break; |
| |
| case EX_NOTIFY: |
| request->request->outcome |= RS_NOTIFYING; |
| break; |
| |
| case EX_SLOWF: |
| request->request->outcome |= RS_FILTERING; |
| request->request->outcome &= ~RS_REFILTER; |
| break; |
| |
| } |
| return(0); |
| |
| } |
| |
| for (i = 0; i < NSIG; i++) |
| (void)signal (i, SIG_DFL); |
| (void)signal (SIGALRM, SIG_IGN); |
| (void)signal (SIGTERM, sigtrap); |
| |
| for (i = 0; i < OpenMax; i++) |
| if (i != ChildMd->writefd) |
| Close (i); |
| setpgrp(); |
| |
| /* Set a default path */ |
| addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin"); |
| /* copy locale related variables */ |
| addenv (&envp, "TZ", getenv("TZ")); |
| addenv (&envp, "LANG", getenv("LANG")); |
| addenv (&envp, "LC_ALL", getenv("LC_ALL")); |
| addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE")); |
| addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE")); |
| addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES")); |
| addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY")); |
| addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC")); |
| addenv (&envp, "LC_TIME", getenv("LC_TIME")); |
| |
| sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key); |
| addenv (&envp, "SPOOLER_KEY", cp); |
| |
| #if defined(DEBUG) |
| addenv (&envp, "LPDEBUG", (debug? "1" : "0")); |
| #endif |
| |
| /* |
| * Open the standard input, standard output, and standard error. |
| */ |
| switch (type) { |
| |
| case EX_SLOWF: |
| case EX_INTERF: |
| /* |
| * stdin: /dev/null |
| * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF) |
| * stderr: req# |
| */ |
| infile = 0; |
| outfile = 0; |
| errfile = makereqerr(request); |
| break; |
| |
| case EX_NOTIFY: |
| /* |
| * stdin: req# |
| * stdout: /dev/null |
| * stderr: /dev/null |
| */ |
| infile = makereqerr(request); |
| outfile = 0; |
| errfile = 0; |
| |
| break; |
| |
| case EX_ALERT: |
| case EX_FALERT: |
| case EX_PALERT: |
| case EX_FAULT_MESSAGE: |
| case EX_FORM_MESSAGE: |
| /* |
| * stdin: /dev/null |
| * stdout: /dev/null |
| * stderr: /dev/null |
| */ |
| infile = 0; |
| outfile = 0; |
| errfile = 0; |
| break; |
| |
| } |
| |
| if (infile) { |
| if (Open(infile, O_RDONLY) == -1) |
| Done (EXEC_EXIT_NOPEN, errno); |
| } else { |
| if (Open("/dev/null", O_RDONLY) == -1) |
| Done (EXEC_EXIT_NOPEN, errno); |
| } |
| |
| if (outfile) { |
| if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1) |
| Done (EXEC_EXIT_NOPEN, errno); |
| } else { |
| /* |
| * If EX_INTERF, this is still needed to cause the |
| * standard error channel to be #2. |
| */ |
| if (Open("/dev/null", O_WRONLY) == -1) |
| Done (EXEC_EXIT_NOPEN, errno); |
| } |
| |
| if (errfile) { |
| if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1) |
| Done (EXEC_EXIT_NOPEN, errno); |
| } else { |
| if (Open("/dev/null", O_WRONLY) == -1) |
| Done (EXEC_EXIT_NOPEN, errno); |
| } |
| |
| switch (type) { |
| |
| case EX_INTERF: |
| /* |
| * Opening a ``port'' can be dangerous to our health: |
| * |
| * - Hangups can occur if the line is dropped. |
| * - The printer may send an interrupt. |
| * - A FIFO may be closed, generating SIGPIPE. |
| * |
| * We catch these so we can complain nicely. |
| */ |
| trap_fault_signals (); |
| |
| (void)Close (1); |
| |
| if (strchr (request->secure->user, '!')) |
| { |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| } |
| else |
| { |
| procuid = request->secure->uid; |
| procgid = request->secure->gid; |
| } |
| if (printer->printer->dial_info) |
| { |
| ret = open_dialup(request->printer_type, |
| printer->printer); |
| if (ret == 0) |
| do_undial = 1; |
| } |
| else |
| { |
| ret = open_direct(request->printer_type, |
| printer->printer); |
| do_undial = 0; |
| /* this is a URI */ |
| if (is_printer_uri(printer->printer->device) == 0) |
| addenv(&envp, "DEVICE_URI", |
| printer->printer->device); |
| } |
| addenv(&envp, "DEVICE_URI", |
| printer->printer->device); |
| if (ret != 0) |
| Done (ret, errno); |
| |
| if (!(request->request->outcome & RS_FILTERED)) |
| file_list = request->request->file_list; |
| |
| else { |
| register int count = 0; |
| register char * num = BIGGEST_REQID_S; |
| register char * prefix; |
| |
| prefix = makestr( |
| Lp_Tmp, |
| "/", |
| (request->secure && request->secure->system ? |
| request->secure->system : Local_System), |
| "/F", |
| getreqno(request->secure->req_id), |
| "-", |
| (char *)0 |
| ); |
| |
| file_list = (char **)Malloc( |
| (lenlist(request->request->file_list) + 1) |
| * sizeof(char *) |
| ); |
| |
| for ( |
| listp = request->request->file_list; |
| *listp; |
| listp++ |
| ) { |
| sprintf (num, "%d", count + 1); |
| file_list[count] = makestr( |
| prefix, |
| num, |
| (char *)0 |
| ); |
| count++; |
| } |
| file_list[count] = 0; |
| } |
| |
| #ifdef LP_USE_PAPI_ATTR |
| /* |
| * Check if the PAPI job attribute file exists, if it does |
| * pass the file's pathname to the printer interface script |
| * in an environment variable. This file is created when |
| * print jobs are submitted via the PAPI interface. |
| */ |
| snprintf(tmpName, sizeof (tmpName), "%s-%s", |
| getreqno(request->secure->req_id), LP_PAPIATTRNAME); |
| path = makepath(SPOOLDIR, "temp", tmpName, (char *)0); |
| if ((path != NULL) && (stat(path, &tmpBuf) == 0)) |
| { |
| /* |
| * IPP job attribute file exists for this job so |
| * set the environment variable |
| */ |
| syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path); |
| addenv(&envp, "ATTRPATH", path); |
| } |
| Free(path); |
| |
| /* |
| * now set environment variable for the printer's PostScript |
| * Printer Description (PPD) file, this is used by the filter |
| * when forming the print data for this printer. |
| */ |
| if ((request->printer != NULL) && |
| (request->printer->printer != NULL) && |
| (request->printer->printer->name != NULL)) |
| { |
| snprintf(tmpName, sizeof (tmpName), "%s.ppd", |
| request->printer->printer->name); |
| path = makepath(ETCDIR, "ppd", tmpName, (char *)0); |
| if ((path != NULL) && (stat(path, &tmpBuf) == 0)) |
| { |
| syslog(LOG_DEBUG, |
| "exec(): Printer PPD='%s'", path); |
| addenv(&envp, "PPD", path); |
| } |
| Free(path); |
| } |
| #endif |
| |
| if (request->printer_type) |
| addenv(&envp, "TERM", request->printer_type); |
| |
| if (!(printer->printer->daisy)) { |
| register char * chset = 0; |
| register char * csp; |
| |
| if ( |
| request->form |
| && request->form->form->chset |
| && request->form->form->mandatory |
| && !STREQU(NAME_ANY, request->form->form->chset) |
| ) |
| chset = request->form->form->chset; |
| |
| else if ( |
| request->request->charset |
| && !STREQU(NAME_ANY, request->request->charset) |
| ) |
| chset = request->request->charset; |
| |
| if (chset) { |
| csp = search_cslist( |
| chset, |
| printer->printer->char_sets |
| ); |
| |
| /* |
| * The "strtok()" below wrecks the string |
| * for future use, but this is a child |
| * process where it won't be needed again. |
| */ |
| addenv (&envp, "CHARSET", |
| (csp? strtok(csp, "=") : chset) |
| ); |
| } |
| } |
| |
| if (request->fast) |
| addenv(&envp, "FILTER", request->fast); |
| |
| /* |
| */ |
| if (strcmp (request->secure->user, request->request->user)) |
| { |
| addenv (&envp, "ALIAS_USERNAME", |
| request->request->user); |
| } |
| /* |
| * Add the sensitivity label to the environment for |
| * banner page and header/footer processing |
| */ |
| |
| if (is_system_labeled() && request->secure->slabel != NULL) |
| addenv(&envp, "SLABEL", request->secure->slabel); |
| |
| /* |
| * Add the system name to the user name (ala system!user) |
| * unless it is already there. RFS users may have trouble |
| * here, sorry! |
| */ |
| cp = strchr(request->secure->user, '@'); |
| |
| allTraysWithForm(printer, request->form); |
| |
| /* |
| * Fix for 4137389 |
| * Remove double quotes from title string. |
| */ |
| fr_flg = 1; |
| clean_title = strdup(NB(request->request->title)); |
| if (clean_title == NULL) { |
| /* |
| * strdup failed. We're probably hosed |
| * but try setting clean_title |
| * to original title and continuing. |
| */ |
| clean_title = NB(request->request->title); |
| fr_flg = 0; |
| } else if (strcmp(clean_title, "") != 0) { |
| char *ct_p; |
| |
| for (ct_p = clean_title; *ct_p != NULL; ct_p++) { |
| if (*ct_p == '"') |
| *ct_p = ' '; |
| } |
| } |
| |
| av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces, |
| printer->printer->name); |
| av[ac++] = arg_string(TRUSTED, "%s", request->secure->req_id); |
| av[ac++] = arg_string(UNTRUSTED, "%s%s%s", |
| request->secure->user, |
| (cp? "" : "@"), |
| (cp? "" : request->secure->system)); |
| av[ac++] = arg_string(TRUSTED, "%s", clean_title); |
| av[ac++] = arg_string(TRUSTED, "%d", request->copies); |
| |
| if (fr_flg) |
| free (clean_title); |
| |
| sep = ""; |
| |
| /* |
| * Do the administrator defined key=value pair options |
| */ |
| |
| argbuf[0] = '\0'; |
| |
| if (printer->printer->options) { |
| char **tmp = printer->printer->options; |
| while(*tmp != NULL) { |
| STRLCAT(argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT(argbuf, *tmp++, sizeof (argbuf)); |
| } |
| } |
| |
| /* |
| * Do the administrator defined ``stty'' stuff before |
| * the user's -o options, to allow the user to override. |
| */ |
| if (printer->printer->stty) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, "stty='", sizeof (argbuf)); |
| STRLCAT (argbuf, printer->printer->stty, |
| sizeof (argbuf)); |
| STRLCAT (argbuf, "'", sizeof (argbuf)); |
| } |
| |
| /* |
| * Do all of the user's options except the cpi/lpi/etc. |
| * stuff, which is done separately. |
| */ |
| if (request->request->options) { |
| listp = dashos(request->request->options); |
| while (*listp) { |
| if ( |
| !STRNEQU(*listp, "cpi=", 4) |
| && !STRNEQU(*listp, "lpi=", 4) |
| && !STRNEQU(*listp, "width=", 6) |
| && !STRNEQU(*listp, "length=", 7) |
| ) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, *listp, |
| sizeof (argbuf)); |
| } |
| listp++; |
| } |
| } |
| |
| /* |
| * The "pickfilter()" routine (from "validate()") |
| * stored the cpi/lpi/etc. stuff that should be |
| * used for this request. It chose form over user, |
| * and user over printer. |
| */ |
| if (request->cpi) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, "cpi=", sizeof (argbuf)); |
| STRLCAT (argbuf, request->cpi, sizeof (argbuf)); |
| } |
| if (request->lpi) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, "lpi=", sizeof (argbuf)); |
| STRLCAT (argbuf, request->lpi, sizeof (argbuf)); |
| } |
| if (request->pwid) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, "width=", sizeof (argbuf)); |
| STRLCAT (argbuf, request->pwid, sizeof (argbuf)); |
| } |
| if (request->plen) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, "length=", sizeof (argbuf)); |
| STRLCAT (argbuf, request->plen, sizeof (argbuf)); |
| } |
| |
| /* |
| * Do the ``raw'' bit last, to ensure it gets |
| * done. If the user doesn't want this, then he or |
| * she can do the correct thing using -o stty= |
| * and leaving out the -r option. |
| */ |
| if (request->request->actions & ACT_RAW) { |
| STRLCAT (argbuf, sep, sizeof (argbuf)); |
| sep = " "; |
| STRLCAT (argbuf, "stty=-opost", sizeof (argbuf)); |
| } |
| |
| |
| /* the "options" */ |
| av[ac++] = arg_string(UNTRUSTED, "%s", argbuf); |
| |
| for (listp = file_list; *listp; listp++) |
| av[ac++] = arg_string(TRUSTED, "%s", *listp); |
| |
| (void)chfiles (file_list, procuid, procgid); |
| |
| break; |
| |
| |
| case EX_SLOWF: |
| if (request->slow) |
| addenv(&envp, "FILTER", request->slow); |
| |
| if (strchr (request->secure->user, '!')) |
| { |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| } |
| else |
| { |
| procuid = request->secure->uid; |
| procgid = request->secure->gid; |
| } |
| cp = _alloc_files( |
| lenlist(request->request->file_list), |
| getreqno(request->secure->req_id), |
| procuid, |
| procgid, |
| (request->secure && request->secure->system ? |
| request->secure->system : NULL ) |
| ); |
| |
| av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter); |
| av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_Tmp, |
| (request->secure && request->secure->system ? |
| request->secure->system : Local_System), |
| cp); |
| for (listp = request->request->file_list; *listp; listp++) |
| av[ac++] = arg_string(TRUSTED, "%s", *listp); |
| |
| (void)chfiles (request->request->file_list, procuid, procgid); |
| |
| #ifdef LP_USE_PAPI_ATTR |
| /* |
| * Check if the PAPI job attribute file exists, if it does |
| * pass the file's pathname to the slow-filters in an |
| * environment variable. Note: this file is created when |
| * print jobs are submitted via the PAPI interface. |
| */ |
| snprintf(tmpName, sizeof (tmpName), "%s-%s", |
| getreqno(request->secure->req_id), LP_PAPIATTRNAME); |
| path = makepath(SPOOLDIR, "temp", tmpName, (char *)0); |
| if ((path != NULL) && (stat(path, &tmpBuf) == 0)) |
| { |
| /* |
| * IPP job attribute file exists for this job so |
| * set the environment variable |
| */ |
| syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path); |
| addenv(&envp, "ATTRPATH", path); |
| } |
| Free(path); |
| |
| |
| /* |
| * now set environment variable for the printer's PostScript |
| * Printer Description (PPD) file, this is used by the filter |
| * when forming the print data for this printer. |
| */ |
| if ((request->printer != NULL) && |
| (request->printer->printer != NULL) && |
| (request->printer->printer->name != NULL)) |
| { |
| snprintf(tmpName, sizeof (tmpName), "%s.ppd", |
| request->printer->printer->name); |
| path = makepath(ETCDIR, "ppd", tmpName, (char *)0); |
| if ((path != NULL) && (stat(path, &tmpBuf) == 0)) |
| { |
| syslog(LOG_DEBUG, |
| "exec(): Printer PPD='%s'", path); |
| addenv(&envp, "PPD", path); |
| } |
| Free(path); |
| } |
| #endif |
| break; |
| |
| case EX_ALERT: |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| (void)Chown (printer->alert->msgfile, procuid, procgid); |
| |
| av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers, |
| printer->printer->name, ALERTSHFILE); |
| av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); |
| |
| break; |
| |
| case EX_PALERT: |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| (void)Chown (pwheel->alert->msgfile, procuid, procgid); |
| |
| av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels, |
| pwheel->pwheel->name, ALERTSHFILE); |
| av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); |
| |
| break; |
| |
| case EX_FALERT: |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| (void)Chown (form->alert->msgfile, procuid, procgid); |
| |
| av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms, |
| form->form->name, ALERTSHFILE); |
| av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); |
| |
| break; |
| |
| case EX_FORM_MESSAGE: |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| |
| av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults); |
| av[ac++] = arg_string(TRUSTED, "%s", form->form->name); |
| av[ac++] = arg_string(TRUSTED, "%s", time_buf); |
| av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms, |
| form->form->name, FORMMESSAGEFILE); |
| |
| break; |
| |
| case EX_FAULT_MESSAGE: |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| |
| av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults); |
| av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow); |
| av[ac++] = arg_string(TRUSTED, "%s", time_buf); |
| av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers, |
| printerName, FAULTMESSAGEFILE); |
| |
| break; |
| |
| case EX_NOTIFY: |
| if (request->request->alert) { |
| if (strchr(request->secure->user, '!')) { |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| } else { |
| procuid = request->secure->uid; |
| procgid = request->secure->gid; |
| } |
| av[ac++] = arg_string(TRUSTED, "%s", |
| request->request->alert); |
| } else { |
| char *user = strdup(request->secure->user); |
| clean_string(user); |
| slabel = request->secure->slabel; |
| |
| if ((request->request->actions & ACT_WRITE) && |
| (!request->secure->system || |
| STREQU(request->secure->system, Local_System))) { |
| av[ac++] = arg_string(TRUSTED, "%s", BINWRITE); |
| snprintf(argbuf, sizeof (argbuf), |
| "%s %s || %s %s", |
| BINWRITE, user, |
| BINMAIL, user |
| ); |
| av[ac++] = arg_string(TRUSTED, "/bin/sh"); |
| av[ac++] = arg_string(TRUSTED, "-c"); |
| av[ac++] = arg_string(TRUSTED, "%s", argbuf); |
| } else if ((getzoneid() == GLOBAL_ZONEID) && |
| is_system_labeled() && (slabel != NULL)) { |
| /* |
| * If in the global zone and the system is |
| * labeled, mail is handled via a local |
| * labeled zone that is the same label as |
| * the request. |
| */ |
| if ((mail_zonename = |
| get_labeled_zonename(slabel)) == |
| (char *)-1) { |
| /* |
| * Cannot find labeled zone, just |
| * return 0. |
| */ |
| return(0); |
| } |
| } |
| if (mail_zonename == NULL) { |
| procuid = Lp_Uid; |
| procgid = Lp_Gid; |
| av[ac++] = arg_string(TRUSTED, "%s", BINMAIL); |
| av[ac++] = arg_string(UNTRUSTED, "%s", user); |
| } else { |
| procuid = getuid(); |
| procgid = getgid(); |
| av[ac++] = arg_string(TRUSTED, "%s", |
| "/usr/sbin/zlogin"); |
| av[ac++] = arg_string(TRUSTED, "%s", |
| mail_zonename); |
| av[ac++] = arg_string(TRUSTED, "%s", |
| BINMAIL); |
| av[ac++] = arg_string(UNTRUSTED, "%s", |
| user); |
| Free(mail_zonename); |
| } |
| |
| free(user); |
| } |
| break; |
| } |
| |
| av[ac++] = NULL; |
| |
| Fork2 (); |
| /* only the child returns */ |
| |
| /* |
| * Correctly set up the supplemental group list |
| * for proper file access (before execl the interface program) |
| */ |
| |
| pwp = getpwuid(procuid); |
| if (pwp == NULL) { |
| note("getpwuid(%d) call failed\n", procuid); |
| } else if (initgroups(pwp->pw_name, procgid) < 0) { |
| note("initgroups() call failed %d\n", errno); |
| } |
| |
| setgid (procgid); |
| setuid (procuid); |
| |
| /* |
| * The shell doesn't allow the "trap" builtin to set a trap |
| * for a signal ignored when the shell is started. Thus, don't |
| * turn off signals in the last child! |
| */ |
| |
| for (i = 0; av[i] != NULL; i++) |
| syslog(LOG_DEBUG, "exec: av[%d] = %s", i, av[i]); |
| for (i = 0; envp[i] != NULL; i++) |
| syslog(LOG_DEBUG, "exec: envp[%d] = %s", i, envp[i]); |
| |
| execvpe(av[0], av, envp); |
| Done (EXEC_EXIT_NEXEC, errno); |
| /*NOTREACHED*/ |
| return (0); |
| } |
| |
| /** |
| ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT |
| **/ |
| |
| static void |
| addenv(char ***envp, char *name, char *value) |
| { |
| register char * cp; |
| |
| if ((name == NULL) || (value == NULL)) |
| return; |
| |
| if ((cp = makestr(name, "=", value, (char *)0))) |
| addlist(envp, cp); |
| return; |
| } |
| |
| /** |
| ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT |
| **/ |
| |
| static int |
| Fork1(EXEC *ep) |
| { |
| int pid; |
| int fds[2]; |
| |
| if (pipe(fds) == -1) { |
| note("Failed to create pipe for child process (%s).\n", PERROR); |
| errno = EAGAIN ; |
| return(-1); |
| } |
| |
| ep->md = mconnect((char *)0, fds[0], fds[1]); |
| |
| switch (pid = fork()) { |
| |
| case -1: |
| mdisconnect(ep->md); |
| close(fds[0]); |
| close(fds[1]); |
| ep->md = 0; |
| return (-1); |
| |
| case 0: |
| ChildMd = mconnect(NULL, fds[1], fds[1]); |
| return (0); |
| |
| default: |
| mlistenadd(ep->md, POLLIN); |
| return (pid); |
| } |
| } |
| |
| /** |
| ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT |
| **/ |
| |
| static void |
| Fork2(void) |
| { |
| switch ((ChildPid = fork())) { |
| |
| case -1: |
| Done (EXEC_EXIT_NFORK, errno); |
| /*NOTREACHED*/ |
| |
| case 0: |
| return; |
| |
| default: |
| /* |
| * Delay calling "ignore_fault_signals()" as long |
| * as possible, to give the child a chance to exec |
| * the interface program and turn on traps. |
| */ |
| |
| cool_heels (); |
| /*NOTREACHED*/ |
| |
| } |
| } |
| |
| |
| /** |
| ** cool_heels() - WAIT FOR CHILD TO "DIE" |
| **/ |
| |
| static void |
| cool_heels(void) |
| { |
| int status; |
| |
| /* |
| * At this point our only job is to wait for the child process. |
| * If we hang out for a bit longer, that's okay. |
| * By delaying before turning off the fault signals, |
| * we increase the chance that the child process has completed |
| * its exec and has turned on the fault traps. Nonetheless, |
| * we can't guarantee a zero chance of missing a fault. |
| * (We don't want to keep trapping the signals because the |
| * interface program is likely to have a better way to handle |
| * them; this process provides only rudimentary handling.) |
| * |
| * Note that on a very busy system, or with a very fast interface |
| * program, the tables could be turned: Our sleep below (coupled |
| * with a delay in the kernel scheduling us) may cause us to |
| * detect the fault instead of the interface program. |
| * |
| * What we need is a way to synchronize with the child process. |
| */ |
| sleep (1); |
| ignore_fault_signals (); |
| |
| WaitedChildPid = 0; |
| while ((WaitedChildPid = wait(&status)) != ChildPid) |
| ; |
| |
| if ( |
| EXITED(status) > EXEC_EXIT_USER |
| && EXITED(status) != EXEC_EXIT_FAULT |
| ) |
| Done (EXEC_EXIT_EXIT, EXITED(status)); |
| |
| done (status, 0); /* Don't use Done() */ |
| /*NOTREACHED*/ |
| } |
| |
| |
| /** |
| ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT |
| ** ignore_fault_signals() - IGNORE SAME |
| **/ |
| |
| static void |
| trap_fault_signals(void) |
| { |
| signal (SIGHUP, sigtrap); |
| signal (SIGINT, sigtrap); |
| signal (SIGQUIT, sigtrap); |
| signal (SIGPIPE, sigtrap); |
| return; |
| } |
| |
| static void |
| ignore_fault_signals(void) |
| { |
| signal (SIGHUP, SIG_IGN); |
| signal (SIGINT, SIG_IGN); |
| signal (SIGQUIT, SIG_IGN); |
| signal (SIGPIPE, SIG_IGN); |
| return; |
| } |
| |
| /** |
| ** sigtrap() - TRAP VARIOUS SIGNALS |
| **/ |
| |
| static void |
| sigtrap(int sig) |
| { |
| signal (sig, SIG_IGN); |
| switch (sig) { |
| |
| case SIGHUP: |
| Done (EXEC_EXIT_HUP, 0); |
| /*NOTREACHED*/ |
| |
| case SIGQUIT: |
| case SIGINT: |
| Done (EXEC_EXIT_INTR, 0); |
| /*NOTREACHED*/ |
| |
| case SIGPIPE: |
| Done (EXEC_EXIT_PIPE, 0); |
| /*NOTREACHED*/ |
| |
| case SIGTERM: |
| /* |
| * If we were killed with SIGTERM, it should have been |
| * via the Spooler who should have killed the entire |
| * process group. We have to wait for the children, |
| * since we're their parent, but WE MAY HAVE WAITED |
| * FOR THEM ALREADY (in cool_heels()). |
| */ |
| if (ChildPid != WaitedChildPid) { |
| register int cpid; |
| |
| while ( |
| (cpid = wait((int *)0)) != ChildPid |
| && (cpid != -1 || errno != ECHILD) |
| ) |
| ; |
| } |
| |
| /* |
| * We can't rely on getting SIGTERM back in the wait() |
| * above, because, for instance, some shells trap SIGTERM |
| * and exit instead. Thus we force it. |
| */ |
| done (SIGTERM, 0); /* Don't use Done() */ |
| /*NOTREACHED*/ |
| } |
| } |
| |
| /** |
| ** done() - TELL SPOOLER THIS CHILD IS DONE |
| **/ |
| |
| static void |
| done(int status, int err) |
| { |
| if (do_undial) |
| undial (1); |
| |
| mputm (ChildMd, S_CHILD_DONE, key, slot, status, err); |
| mdisconnect (ChildMd); |
| |
| exit (0); |
| /*NOTREACHED*/ |
| } |
| |
| /** |
| ** child_mallocfail() |
| **/ |
| |
| static void |
| child_mallocfail(void) |
| { |
| Done (EXEC_EXIT_NOMEM, ENOMEM); |
| } |