Home | History | Annotate | Download | only in lpsched
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 #include <pwd.h>
     33 #include <zone.h>
     34 #if defined PS_FAULTED
     35 #undef  PS_FAULTED
     36 #endif /* PS_FAULTED */
     37 #include <dial.h>
     38 
     39 #include <stdlib.h>
     40 #include "limits.h"
     41 #include "stdarg.h"
     42 #include "wait.h"
     43 #include "dial.h"
     44 #include "lpsched.h"
     45 #include <syslog.h>
     46 #include "tsol/label.h"
     47 
     48 #define Done(EC,ERRNO)	done(((EC) << 8),ERRNO)
     49 
     50 #define	STRLCAT(dst, src, size) \
     51 	if (strlcat((dst), (src), (size)) >= (size)) { \
     52 		errno = EINVAL; \
     53 		return (-1); \
     54 	}
     55 
     56 static MESG *		ChildMd;
     57 
     58 static int		ChildPid;
     59 static int		WaitedChildPid;
     60 static int		do_undial;
     61 
     62 static char		argbuf[ARG_MAX];
     63 
     64 static long		key;
     65 
     66 static void		sigtrap ( int );
     67 static void		done ( int , int );
     68 static void		cool_heels ( void );
     69 static void		addenv (char ***envp, char * , char * );
     70 static void		trap_fault_signals ( void );
     71 static void		ignore_fault_signals ( void );
     72 static void		child_mallocfail ( void );
     73 static void		Fork2 ( void );
     74 
     75 static int		Fork1 ( EXEC * );
     76 
     77 static void
     78 relock(void)
     79 {
     80 	struct flock		l;
     81 
     82 	l.l_type = F_WRLCK;
     83 	l.l_whence = 1;
     84 	l.l_start = 0;
     85 	l.l_len = 0;
     86 	(void)Fcntl (lock_fd, F_SETLK, &l);
     87 	return;
     88 }
     89 
     90 static char *_exec_name(int type)
     91 {
     92 	static char *_names[] = {
     93 	"", "EX_INTERF", "EX_SLOWF", "EX_ALERT", "EX_FALERT", "EX_PALERT",
     94 	"EX_NOTIFY", "EX_FAULT_MESSAGE", "EX_FORM_MESSAGE", NULL };
     95 
     96 	if ((type < 0) || (type > EX_FORM_MESSAGE))
     97 		return ("BAD_EXEC_TYPE");
     98 	else
     99 		return (_names[type]);
    100 }
    101 
    102 /*
    103  * This function replaces characters in a string that might be used
    104  * to exploit a security hole.  Replace command seperators (`, &, ;, |, ^),
    105  * output redirection (>, |), variable expansion ($), and character
    106  * escape (\).
    107  *
    108  * Bugid 4141687
    109  * Add ( ) < * ? [
    110  * Bugid 4139071
    111  * Remove \
    112  */
    113 void clean_string(char *ptr)
    114 {
    115 	char *cp;
    116 	wchar_t wc;
    117 	size_t len;
    118 
    119 	for (cp = ptr; *cp != NULL; ) {
    120 		if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) {
    121 			cp++;
    122 			continue;
    123 		}
    124 
    125 		if (len == 1 &&
    126 		    ((wc == L'`') || (wc == L'&') || (wc == L';') ||
    127 		    (wc == L'|') || (wc == L'>') || (wc == L'^') ||
    128 		    (wc == L'$') || (wc == L'(') || (wc == L')') ||
    129 		    (wc == L'<') || (wc == L'*') || (wc == L'?') ||
    130 		    (wc == L'[')))
    131 			*cp = '_';
    132 		cp += len;
    133 	}
    134 }
    135 
    136 enum trust {TRUSTED, UNTRUSTED};
    137 
    138 static char *arg_string(enum trust type, char *fmt, ...) __PRINTFLIKE(2);
    139 
    140 /* PRINTFLIKE2 */
    141 static char *
    142 arg_string(enum trust type, char *fmt, ...)
    143 {
    144 	char buf[BUFSIZ];
    145 	va_list	args;
    146 
    147 	va_start(args, fmt);
    148 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
    149 	va_end(args);
    150 
    151 	/*
    152 	 * If the string contains data from an untrusted origin (user supplied),
    153 	 * clean it up in case one of our progeny is a shell script and isn't
    154 	 * careful about checking its input.
    155 	 */
    156 	if (type == UNTRUSTED)
    157 		clean_string(buf);
    158 
    159 	return (strdup(buf));
    160 }
    161 
    162 /* stolen from libc/gen/port/gen/execvp.c */
    163 static const char *
    164 execat(const char *s1, const char *s2, char *si)
    165 {
    166         char    *s;
    167         int cnt = PATH_MAX + 1; /* number of characters in s2 */
    168 
    169         s = si;
    170         while (*s1 && *s1 != ':') {
    171                 if (cnt > 0) {
    172                         *s++ = *s1++;
    173                         cnt--;
    174                 } else
    175                         s1++;
    176         }
    177         if (si != s && cnt > 0) {
    178                 *s++ = '/';
    179                 cnt--;
    180         }
    181         while (*s2 && cnt > 0) {
    182                 *s++ = *s2++;
    183                 cnt--;
    184         }
    185         *s = '\0';
    186         return (*s1 ? ++s1: 0);
    187 }
    188 
    189 /*
    190  * Similiar to execvp(), execpt you can supply an environment and we always
    191  * use /bin/sh for shell scripts.  The PATH searched is the PATH in the
    192  * current environment, not the environment in the argument list.
    193  * This was pretty much stolen from libc/gen/port/execvp.c
    194  */
    195 static int
    196 execvpe(char *name, char *const argv[], char *const envp[])
    197 {
    198 	char *path;
    199 	char fname[PATH_MAX+2];
    200 	char *newargs[256];
    201 	int i;
    202 	const char *cp;
    203 	unsigned etxtbsy = 1;
    204         int eacces = 0;
    205 
    206 	if (*name == '\0') {
    207 		errno = ENOENT;
    208 		return (-1);
    209 	}
    210 
    211 	if ((path = getenv("PATH")) == NULL)
    212 		path = "/usr/bin:/bin";
    213 
    214         cp = strchr(name, '/')? (const char *)"": path;
    215 
    216         do {
    217                 cp = execat(cp, name, fname);
    218         retry:
    219                 /*
    220                  * 4025035 and 4038378
    221                  * if a filename begins with a "-" prepend "./" so that
    222                  * the shell can't interpret it as an option
    223                  */
    224                 if (*fname == '-') {
    225                         size_t size = strlen(fname) + 1;
    226                         if ((size + 2) > sizeof (fname)) {
    227                                 errno = E2BIG;
    228                                 return (-1);
    229                         }
    230                         (void) memmove(fname + 2, fname, size);
    231                         fname[0] = '.';
    232                         fname[1] = '/';
    233                 }
    234                 (void) execve(fname, argv, envp);
    235                 switch (errno) {
    236                 case ENOEXEC:
    237                         newargs[0] = "sh";
    238                         newargs[1] = fname;
    239                         for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
    240                                 if (i >= 254) {
    241                                         errno = E2BIG;
    242                                         return (-1);
    243                                 }
    244                         }
    245                         (void) execve("/bin/sh", newargs, envp);
    246                         return (-1);
    247                 case ETXTBSY:
    248                         if (++etxtbsy > 5)
    249                                 return (-1);
    250                         (void) sleep(etxtbsy);
    251                         goto retry;
    252                 case EACCES:
    253                         ++eacces;
    254                         break;
    255                 case ENOMEM:
    256                 case E2BIG:
    257                 case EFAULT:
    258                         return (-1);
    259                 }
    260         } while (cp);
    261         if (eacces)
    262                 errno = EACCES;
    263         return (-1);
    264 }
    265 
    266 static char time_buf[50];
    267 
    268 /**
    269  ** exec() - FORK AND EXEC CHILD PROCESS
    270  **/
    271 
    272 /*VARARGS1*/
    273 int
    274 exec(int type, ...)
    275 {
    276 	va_list			args;
    277 
    278 	int			i;
    279 	int			procuid;
    280 	int			procgid;
    281 	int			ret;
    282 	int			fr_flg;
    283 
    284 	char			*cp;
    285 	char			*infile;
    286 	char			*outfile;
    287 	char			*errfile;
    288 	char			*sep;
    289 
    290 	char			**listp;
    291 	char			**file_list;
    292 	char			*printerName;
    293 	char			*printerNameToShow;
    294 	static char		nameBuf[100];
    295 	char			*clean_title;
    296 
    297 	PSTATUS			*printer;
    298 
    299 	RSTATUS			*request;
    300 
    301 	FSTATUS			*form;
    302 
    303 	EXEC			*ep;
    304 
    305 	PWSTATUS		*pwheel;
    306 	time_t			now;
    307 	struct passwd		*pwp;
    308 #ifdef LP_USE_PAPI_ATTR
    309 	struct stat		tmpBuf;
    310 	char 			tmpName[BUFSIZ];
    311 	char			*path = NULL;
    312 #endif
    313 	char *av[ARG_MAX];
    314 	char **envp = NULL;
    315 	int ac = 0;
    316 	char	*mail_zonename = NULL;
    317 	char	*slabel = NULL;
    318 
    319 	syslog(LOG_DEBUG, "exec(%s)", _exec_name(type));
    320 
    321 	memset(av, 0, sizeof (*av));
    322 
    323 	va_start (args, type);
    324 
    325 	switch (type) {
    326 
    327 	case EX_INTERF:
    328 		printer = va_arg(args, PSTATUS *);
    329 		request = printer->request;
    330 		ep = printer->exec;
    331 		break;
    332 
    333 	case EX_FAULT_MESSAGE:
    334 		printer = va_arg(args, PSTATUS *);
    335 		request = va_arg(args, RSTATUS *);
    336 		if (! ( printer->status & (PS_FORM_FAULT | PS_SHOW_FAULT))) {
    337 			return(0);
    338 		}
    339 		ep = printer->fault_exec;
    340 		printerName = (printer->printer && printer->printer->name
    341 				  ? printer->printer->name : "??");
    342 			snprintf(nameBuf, sizeof (nameBuf),
    343 				"%s (on %s)\n", printerName, Local_System);
    344 
    345 		printerNameToShow = nameBuf;
    346 
    347 		(void) time(&now);
    348 		(void) strftime(time_buf, sizeof (time_buf),
    349 			NULL, localtime(&now));
    350 		break;
    351 
    352 	case EX_SLOWF:
    353 		request = va_arg(args, RSTATUS *);
    354 		ep = request->exec;
    355 		break;
    356 
    357 	case EX_NOTIFY:
    358 		request = va_arg(args, RSTATUS *);
    359 		if (request->request->actions & ACT_NOTIFY) {
    360 			errno = EINVAL;
    361 			return (-1);
    362 		}
    363 		ep = request->exec;
    364 		break;
    365 
    366 	case EX_ALERT:
    367 		printer = va_arg(args, PSTATUS *);
    368 		if (!(printer->printer->fault_alert.shcmd)) {
    369 			errno = EINVAL;
    370 			return(-1);
    371 		}
    372 		ep = printer->alert->exec;
    373 		break;
    374 
    375 	case EX_PALERT:
    376 		pwheel = va_arg(args, PWSTATUS *);
    377 		ep = pwheel->alert->exec;
    378 		break;
    379 
    380 	case EX_FORM_MESSAGE:
    381 		(void) time(&now);
    382 		(void) strftime(time_buf, sizeof (time_buf),
    383 			NULL, localtime(&now));
    384 
    385 		/*FALLTHRU*/
    386 	case EX_FALERT:
    387 		form = va_arg(args, FSTATUS *);
    388 		ep = form->alert->exec;
    389 		break;
    390 
    391 	default:
    392 		errno = EINVAL;
    393 		return(-1);
    394 
    395 	}
    396 	va_end (args);
    397 
    398 	if (!ep || (ep->pid > 0)) {
    399 		errno = EBUSY;
    400 		return(-1);
    401 	}
    402 
    403 	ep->flags = 0;
    404 
    405 	key = ep->key = getkey();
    406 
    407 	switch ((ep->pid = Fork1(ep))) {
    408 
    409 	case -1:
    410 		relock ();
    411 		return(-1);
    412 
    413 	case 0:
    414 		/*
    415 		 * We want to be able to tell our parent how we died.
    416 		 */
    417 		lp_alloc_fail_handler = child_mallocfail;
    418 		break;
    419 
    420 	default:
    421 		switch(type) {
    422 
    423 		case EX_INTERF:
    424 			request->request->outcome |= RS_PRINTING;
    425 			break;
    426 
    427 		case EX_NOTIFY:
    428 			request->request->outcome |= RS_NOTIFYING;
    429 			break;
    430 
    431 		case EX_SLOWF:
    432 			request->request->outcome |= RS_FILTERING;
    433 			request->request->outcome &= ~RS_REFILTER;
    434 			break;
    435 
    436 		}
    437 		return(0);
    438 
    439 	}
    440 
    441 	for (i = 0; i < NSIG; i++)
    442 		(void)signal (i, SIG_DFL);
    443 	(void)signal (SIGALRM, SIG_IGN);
    444 	(void)signal (SIGTERM, sigtrap);
    445 
    446 	closelog();
    447 	for (i = 0; i < OpenMax; i++)
    448 		if (i != ChildMd->writefd)
    449 			Close (i);
    450 	openlog("lpsched", LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR);
    451 
    452 	setpgrp();
    453 
    454 	/* Set a default path */
    455 	addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin");
    456 	/* copy locale related variables */
    457 	addenv (&envp, "TZ", getenv("TZ"));
    458 	addenv (&envp, "LANG", getenv("LANG"));
    459 	addenv (&envp, "LC_ALL", getenv("LC_ALL"));
    460 	addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE"));
    461 	addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE"));
    462 	addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES"));
    463 	addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY"));
    464 	addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC"));
    465 	addenv (&envp, "LC_TIME", getenv("LC_TIME"));
    466 
    467 	sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key);
    468 	addenv (&envp, "SPOOLER_KEY", cp);
    469 
    470 #if	defined(DEBUG)
    471 	addenv (&envp, "LPDEBUG", (debug? "1" : "0"));
    472 #endif
    473 
    474 	/*
    475 	 * Open the standard input, standard output, and standard error.
    476 	 */
    477 	switch (type) {
    478 
    479 	case EX_SLOWF:
    480 	case EX_INTERF:
    481 		/*
    482 		 * stdin:  /dev/null
    483 		 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF)
    484 		 * stderr: req#
    485 		 */
    486 		infile = 0;
    487 		outfile = 0;
    488 		errfile = makereqerr(request);
    489 		break;
    490 
    491 	case EX_NOTIFY:
    492 		/*
    493 		 * stdin:  req#
    494 		 * stdout: /dev/null
    495 		 * stderr: /dev/null
    496 		 */
    497 		infile = makereqerr(request);
    498 		outfile = 0;
    499 		errfile = 0;
    500 
    501 		break;
    502 
    503 	case EX_ALERT:
    504 	case EX_FALERT:
    505 	case EX_PALERT:
    506 	case EX_FAULT_MESSAGE:
    507 	case EX_FORM_MESSAGE:
    508 		/*
    509 		 * stdin:  /dev/null
    510 		 * stdout: /dev/null
    511 		 * stderr: /dev/null
    512 		 */
    513 		infile = 0;
    514 		outfile = 0;
    515 		errfile = 0;
    516 		break;
    517 
    518 	}
    519 
    520 	if (infile) {
    521 		if (Open(infile, O_RDONLY) == -1)
    522 			Done (EXEC_EXIT_NOPEN, errno);
    523 	} else {
    524 		if (Open("/dev/null", O_RDONLY) == -1)
    525 			Done (EXEC_EXIT_NOPEN, errno);
    526 	}
    527 
    528 	if (outfile) {
    529 		if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
    530 			Done (EXEC_EXIT_NOPEN, errno);
    531 	} else {
    532 		/*
    533 		 * If EX_INTERF, this is still needed to cause the
    534 		 * standard error channel to be #2.
    535 		 */
    536 		if (Open("/dev/null", O_WRONLY) == -1)
    537 			Done (EXEC_EXIT_NOPEN, errno);
    538 	}
    539 
    540 	if (errfile) {
    541 		if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
    542 			Done (EXEC_EXIT_NOPEN, errno);
    543 	} else {
    544 		if (Open("/dev/null", O_WRONLY) == -1)
    545 			Done (EXEC_EXIT_NOPEN, errno);
    546 	}
    547 
    548 	switch (type) {
    549 
    550 	case EX_INTERF:
    551 		/*
    552 		 * Opening a ``port'' can be dangerous to our health:
    553 		 *
    554 		 *	- Hangups can occur if the line is dropped.
    555 		 *	- The printer may send an interrupt.
    556 		 *	- A FIFO may be closed, generating SIGPIPE.
    557 		 *
    558 		 * We catch these so we can complain nicely.
    559 		 */
    560 		trap_fault_signals ();
    561 
    562 		(void)Close (1);
    563 
    564 		if (strchr (request->request->user, '@'))
    565 		{
    566 			procuid = Lp_Uid;
    567 			procgid = Lp_Gid;
    568 		}
    569 		else
    570 		{
    571 			procuid = request->secure->uid;
    572 			procgid = request->secure->gid;
    573 		}
    574 		if (printer->printer->dial_info)
    575 		{
    576 			ret = open_dialup(request->printer_type,
    577 				printer->printer);
    578 			if (ret == 0)
    579 				do_undial = 1;
    580 		}
    581 		else
    582 		{
    583 			ret = open_direct(request->printer_type,
    584 				printer->printer);
    585 			do_undial = 0;
    586 			/* this is a URI */
    587 			if (is_printer_uri(printer->printer->device) == 0)
    588 				addenv(&envp, "DEVICE_URI",
    589 					 printer->printer->device);
    590 		}
    591 				addenv(&envp, "DEVICE_URI",
    592 					 printer->printer->device);
    593 		if (ret != 0)
    594 			Done (ret, errno);
    595 
    596 		if (!(request->request->outcome & RS_FILTERED))
    597 			file_list = request->request->file_list;
    598 
    599 		else {
    600 			register int		count	= 0;
    601 			register char *		num	= BIGGEST_REQID_S;
    602 			register char *		prefix;
    603 
    604 			prefix = makestr(
    605 				Lp_Temp,
    606 				"/F",
    607 				getreqno(request->secure->req_id),
    608 				"-",
    609 				(char *)0
    610 			);
    611 
    612 			file_list = (char **)Malloc(
    613 				(lenlist(request->request->file_list) + 1)
    614 			      * sizeof(char *)
    615 			);
    616 
    617 			for (
    618 				listp = request->request->file_list;
    619 				*listp;
    620 				listp++
    621 			) {
    622 				sprintf (num, "%d", count + 1);
    623 				file_list[count] = makestr(
    624 					prefix,
    625 					num,
    626 					(char *)0
    627 				);
    628 				count++;
    629 			}
    630 			file_list[count] = 0;
    631 		}
    632 
    633 #ifdef LP_USE_PAPI_ATTR
    634 		/*
    635 		 * Check if the PAPI job attribute file exists, if it does
    636 		 * pass the file's pathname to the printer interface script
    637 		 * in an environment variable. This file is created when
    638 		 * print jobs are submitted via the PAPI interface.
    639 		 */
    640 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
    641 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
    642 		path = makepath(Lp_Temp, tmpName, (char *)0);
    643 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
    644 		{
    645 			/*
    646 			 * IPP job attribute file exists for this job so
    647 			 * set the environment variable
    648 			 */
    649 			addenv(&envp, "ATTRPATH", path);
    650 		}
    651 		Free(path);
    652 
    653 		/*
    654 		 * now set environment variable for the printer's PostScript
    655 		 * Printer Description (PPD) file, this is used by the filter
    656 		 * when forming the print data for this printer.
    657 		 */
    658 		if ((request->printer != NULL) &&
    659 		    (request->printer->printer != NULL) &&
    660 		    (request->printer->printer->name != NULL))
    661 		{
    662 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
    663 				request->printer->printer->name);
    664 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
    665 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
    666 			{
    667 				addenv(&envp, "PPD", path);
    668 			}
    669 			Free(path);
    670 		}
    671 #endif
    672 
    673 		if (request->printer_type)
    674 			addenv(&envp, "TERM", request->printer_type);
    675 
    676 		if (!(printer->printer->daisy)) {
    677 			register char *	chset = 0;
    678 			register char *	csp;
    679 
    680 			if (
    681 				request->form
    682 			     && request->form->form->chset
    683 			     && request->form->form->mandatory
    684 			     && !STREQU(NAME_ANY, request->form->form->chset)
    685 			)
    686 				chset = request->form->form->chset;
    687 
    688 			else if (
    689 				request->request->charset
    690 			     && !STREQU(NAME_ANY, request->request->charset)
    691 			)
    692 				chset = request->request->charset;
    693 
    694 			if (chset) {
    695 				csp = search_cslist(
    696 					chset,
    697 					printer->printer->char_sets
    698 				);
    699 
    700 				/*
    701 				 * The "strtok()" below wrecks the string
    702 				 * for future use, but this is a child
    703 				 * process where it won't be needed again.
    704 				 */
    705 				addenv (&envp, "CHARSET",
    706 					(csp? strtok(csp, "=") : chset)
    707 				);
    708 			}
    709 		}
    710 
    711 		if (request->fast)
    712 			addenv(&envp, "FILTER", request->fast);
    713 
    714 		/*
    715 		 * Add the sensitivity label to the environment for
    716 		 * banner page and header/footer processing
    717 		 */
    718 
    719 		if (is_system_labeled() && request->secure->slabel != NULL)
    720 			addenv(&envp, "SLABEL", request->secure->slabel);
    721 
    722 		/*
    723 		 * Add the system name to the user name (ala system!user)
    724 		 * unless it is already there. RFS users may have trouble
    725 		 * here, sorry!
    726 		 */
    727 		cp = strchr(request->secure->user, '@');
    728 
    729 		allTraysWithForm(printer, request->form);
    730 
    731 		/*
    732 		 * Fix for 4137389
    733 		 * Remove double quotes from title string.
    734 		 */
    735 		fr_flg = 1;
    736 		clean_title = strdup(NB(request->request->title));
    737 		if (clean_title == NULL) {
    738 			/*
    739 			 * strdup failed. We're probably hosed
    740 			 * but try setting clean_title
    741 			 * to original title and continuing.
    742 			 */
    743 			clean_title = NB(request->request->title);
    744 			fr_flg = 0;
    745 		} else if (strcmp(clean_title, "") != 0) {
    746 			char *ct_p;
    747 
    748 			for (ct_p = clean_title; *ct_p != NULL; ct_p++) {
    749 				if (*ct_p == '"')
    750 					*ct_p = ' ';
    751 			}
    752 		}
    753 
    754 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
    755 					printer->printer->name);
    756 		av[ac++] = arg_string(TRUSTED, "%s", request->secure->req_id);
    757 		av[ac++] = arg_string(UNTRUSTED, "%s", request->request->user);
    758 		av[ac++] = arg_string(TRUSTED, "%s", clean_title);
    759 		av[ac++] = arg_string(TRUSTED, "%d", request->copies);
    760 
    761 		if (fr_flg)
    762 			free (clean_title);
    763 
    764 		sep = "";
    765 
    766 		/*
    767 		 * Do the administrator defined key=value pair options
    768 		 */
    769 
    770 		argbuf[0] = '\0';
    771 
    772 		if (printer->printer->options) {
    773 			char **tmp = printer->printer->options;
    774 			while(*tmp != NULL) {
    775 				STRLCAT(argbuf, sep, sizeof (argbuf));
    776 				sep = " ";
    777 				STRLCAT(argbuf, *tmp++, sizeof (argbuf));
    778 			}
    779 		}
    780 
    781 		/*
    782 		 * Do the administrator defined ``stty'' stuff before
    783 		 * the user's -o options, to allow the user to override.
    784 		 */
    785 		if (printer->printer->stty) {
    786 			STRLCAT (argbuf, sep, sizeof (argbuf));
    787 			sep = " ";
    788 			STRLCAT (argbuf, "stty='", sizeof (argbuf));
    789 			STRLCAT (argbuf, printer->printer->stty,
    790 			    sizeof (argbuf));
    791 			STRLCAT (argbuf, "'", sizeof (argbuf));
    792 		}
    793 
    794 		/*
    795 		 * Do all of the user's options except the cpi/lpi/etc.
    796 		 * stuff, which is done separately.
    797 		 */
    798 		if (request->request->options) {
    799 			listp = dashos(request->request->options);
    800 			while (*listp) {
    801 				if (
    802 					!STRNEQU(*listp, "cpi=", 4)
    803 				     && !STRNEQU(*listp, "lpi=", 4)
    804 				     && !STRNEQU(*listp, "width=", 6)
    805 				     && !STRNEQU(*listp, "length=", 7)
    806 				) {
    807 					STRLCAT (argbuf, sep, sizeof (argbuf));
    808 					sep = " ";
    809 					STRLCAT (argbuf, *listp,
    810 					    sizeof (argbuf));
    811 				}
    812 				listp++;
    813 			}
    814 		}
    815 
    816 		/*
    817 		 * The "pickfilter()" routine (from "validate()")
    818 		 * stored the cpi/lpi/etc. stuff that should be
    819 		 * used for this request. It chose form over user,
    820 		 * and user over printer.
    821 		 */
    822 		if (request->cpi) {
    823 			STRLCAT (argbuf, sep, sizeof (argbuf));
    824 			sep = " ";
    825 			STRLCAT (argbuf, "cpi=", sizeof (argbuf));
    826 			STRLCAT (argbuf, request->cpi, sizeof (argbuf));
    827 		}
    828 		if (request->lpi) {
    829 			STRLCAT (argbuf, sep, sizeof (argbuf));
    830 			sep = " ";
    831 			STRLCAT (argbuf, "lpi=", sizeof (argbuf));
    832 			STRLCAT (argbuf, request->lpi, sizeof (argbuf));
    833 		}
    834 		if (request->pwid) {
    835 			STRLCAT (argbuf, sep, sizeof (argbuf));
    836 			sep = " ";
    837 			STRLCAT (argbuf, "width=", sizeof (argbuf));
    838 			STRLCAT (argbuf, request->pwid, sizeof (argbuf));
    839 		}
    840 		if (request->plen) {
    841 			STRLCAT (argbuf, sep, sizeof (argbuf));
    842 			sep = " ";
    843 			STRLCAT (argbuf, "length=", sizeof (argbuf));
    844 			STRLCAT (argbuf, request->plen, sizeof (argbuf));
    845 		}
    846 
    847 		/*
    848 		 * Do the ``raw'' bit last, to ensure it gets
    849 		 * done. If the user doesn't want this, then he or
    850 		 * she can do the correct thing using -o stty=
    851 		 * and leaving out the -r option.
    852 		 */
    853 		if (request->request->actions & ACT_RAW) {
    854 			STRLCAT (argbuf, sep, sizeof (argbuf));
    855 			sep = " ";
    856 			STRLCAT (argbuf, "stty=-opost", sizeof (argbuf));
    857 		}
    858 
    859 
    860 		/* the "options" */
    861 		av[ac++] = arg_string(UNTRUSTED, "%s", argbuf);
    862 
    863 		for (listp = file_list; *listp; listp++)
    864 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
    865 
    866 		(void)chfiles (file_list, procuid, procgid);
    867 
    868 		break;
    869 
    870 
    871 	case EX_SLOWF:
    872 		if (request->slow)
    873 			addenv(&envp, "FILTER", request->slow);
    874 
    875 		if (strchr (request->request->user, '@'))
    876 		{
    877 			procuid = Lp_Uid;
    878 			procgid = Lp_Gid;
    879 		}
    880 		else
    881 		{
    882 			procuid = request->secure->uid;
    883 			procgid = request->secure->gid;
    884 		}
    885 		cp = _alloc_files(
    886 			lenlist(request->request->file_list),
    887 			getreqno(request->secure->req_id),
    888 			procuid, procgid);
    889 
    890 		av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter);
    891 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_Temp, cp);
    892 		for (listp = request->request->file_list; *listp; listp++)
    893 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
    894 
    895 		(void)chfiles (request->request->file_list, procuid, procgid);
    896 
    897 #ifdef LP_USE_PAPI_ATTR
    898 		/*
    899 		 * Check if the PAPI job attribute file exists, if it does
    900 		 * pass the file's pathname to the slow-filters in an
    901 		 * environment variable. Note: this file is created when
    902 		 * print jobs are submitted via the PAPI interface.
    903 		 */
    904 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
    905 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
    906 		path = makepath(Lp_Temp, tmpName, (char *)0);
    907 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
    908 		{
    909 			/*
    910 			 * IPP job attribute file exists for this job so
    911 			 * set the environment variable
    912 			 */
    913 			addenv(&envp, "ATTRPATH", path);
    914 		}
    915 		Free(path);
    916 
    917 
    918 		/*
    919 		 * now set environment variable for the printer's PostScript
    920 		 * Printer Description (PPD) file, this is used by the filter
    921 		 * when forming the print data for this printer.
    922 		 */
    923 		if ((request->printer != NULL) &&
    924 		    (request->printer->printer != NULL) &&
    925 		    (request->printer->printer->name != NULL))
    926 		{
    927 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
    928 				request->printer->printer->name);
    929 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
    930 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
    931 			{
    932 				addenv(&envp, "PPD", path);
    933 			}
    934 			Free(path);
    935 		}
    936 #endif
    937 		break;
    938 
    939 	case EX_ALERT:
    940 		procuid = Lp_Uid;
    941 		procgid = Lp_Gid;
    942 		(void)Chown (printer->alert->msgfile, procuid, procgid);
    943 
    944 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
    945 				printer->printer->name, ALERTSHFILE);
    946 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
    947 
    948 		break;
    949 
    950 	case EX_PALERT:
    951 		procuid = Lp_Uid;
    952 		procgid = Lp_Gid;
    953 		(void)Chown (pwheel->alert->msgfile, procuid, procgid);
    954 
    955 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels,
    956 				pwheel->pwheel->name, ALERTSHFILE);
    957 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
    958 
    959 		break;
    960 
    961 	case EX_FALERT:
    962 		procuid = Lp_Uid;
    963 		procgid = Lp_Gid;
    964 		(void)Chown (form->alert->msgfile, procuid, procgid);
    965 
    966 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
    967 				form->form->name, ALERTSHFILE);
    968 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
    969 
    970 		break;
    971 
    972 	case EX_FORM_MESSAGE:
    973 		procuid = Lp_Uid;
    974 		procgid = Lp_Gid;
    975 
    976 		av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults);
    977 		av[ac++] = arg_string(TRUSTED, "%s", form->form->name);
    978 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
    979 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
    980 				form->form->name, FORMMESSAGEFILE);
    981 
    982 		break;
    983 
    984 	case EX_FAULT_MESSAGE:
    985 		procuid = Lp_Uid;
    986 		procgid = Lp_Gid;
    987 
    988 		av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults);
    989 		av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow);
    990 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
    991 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
    992 				printerName, FAULTMESSAGEFILE);
    993 
    994 		break;
    995 
    996 	case EX_NOTIFY:
    997 		if (request->request->alert) {
    998 			if (strchr(request->request->user, '@')) {
    999 				procuid = Lp_Uid;
   1000 				procgid = Lp_Gid;
   1001 			} else {
   1002 				procuid = request->secure->uid;
   1003 				procgid = request->secure->gid;
   1004 			}
   1005 			av[ac++] = arg_string(TRUSTED, "%s",
   1006 					request->request->alert);
   1007 		} else {
   1008 			char *user = strdup(request->request->user);
   1009 			clean_string(user);
   1010 			slabel = request->secure->slabel;
   1011 
   1012 			if (request->request->actions & ACT_WRITE) {
   1013 				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
   1014 				snprintf(argbuf, sizeof (argbuf),
   1015 					"%s %s || %s %s",
   1016 					BINWRITE, user,
   1017 					BINMAIL, user
   1018 				);
   1019 				av[ac++] = arg_string(TRUSTED, "/bin/sh");
   1020 				av[ac++] = arg_string(TRUSTED, "-c");
   1021 				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
   1022 			} else if ((getzoneid() == GLOBAL_ZONEID) &&
   1023 				   is_system_labeled() && (slabel != NULL)) {
   1024 				/*
   1025 				 * If in the global zone and the system is
   1026 				 * labeled, mail is handled via a local
   1027 				 * labeled zone that is the same label as
   1028 				 * the request.
   1029 				 */
   1030 				if ((mail_zonename =
   1031 				    get_labeled_zonename(slabel)) ==
   1032 				    (char *)-1) {
   1033 					/*
   1034 					 * Cannot find labeled zone, just
   1035 					 * return 0.
   1036 					 */
   1037 					return(0);
   1038 				}
   1039 			}
   1040 			if (mail_zonename == NULL) {
   1041 				procuid = Lp_Uid;
   1042 				procgid = Lp_Gid;
   1043 				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
   1044 				av[ac++] = arg_string(UNTRUSTED, "%s", user);
   1045 			} else {
   1046 				procuid = getuid();
   1047 				procgid = getgid();
   1048 				av[ac++] = arg_string(TRUSTED, "%s",
   1049 				    "/usr/sbin/zlogin");
   1050 				av[ac++] = arg_string(TRUSTED, "%s",
   1051 				    mail_zonename);
   1052 				av[ac++] = arg_string(TRUSTED, "%s",
   1053 				    BINMAIL);
   1054 				av[ac++] = arg_string(UNTRUSTED, "%s",
   1055 				    user);
   1056 				Free(mail_zonename);
   1057 			}
   1058 
   1059 			free(user);
   1060 		}
   1061 		break;
   1062 	}
   1063 
   1064 	av[ac++] =