xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/exec.c (revision 18c2aff776a775d34a4c9893a4c72e0434d68e36)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <pwd.h>
32 #include <zone.h>
33 #if defined PS_FAULTED
34 #undef  PS_FAULTED
35 #endif /* PS_FAULTED */
36 #include <dial.h>
37 
38 #include <stdlib.h>
39 #include "limits.h"
40 #include "stdarg.h"
41 #include "wait.h"
42 #include "dial.h"
43 #include "lpsched.h"
44 #include <syslog.h>
45 #include "tsol/label.h"
46 
47 #define Done(EC,ERRNO)	done(((EC) << 8),ERRNO)
48 
49 #define	STRLCAT(dst, src, size) \
50 	if (strlcat((dst), (src), (size)) >= (size)) { \
51 		errno = EINVAL; \
52 		return (-1); \
53 	}
54 
55 static MESG *		ChildMd;
56 
57 static int		ChildPid;
58 static int		WaitedChildPid;
59 static int		slot;
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 static char *
138 arg_string(enum trust type, char *fmt, ...)
139 {
140 	char buf[BUFSIZ];
141 	va_list	args;
142 
143 	va_start(args, fmt);
144 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
145 	va_end(args);
146 
147 	/*
148 	 * If the string contains data from an untrusted origin (user supplied),
149 	 * clean it up in case one of our progeny is a shell script and isn't
150 	 * careful about checking its input.
151 	 */
152 	if (type == UNTRUSTED)
153 		clean_string(buf);
154 
155 	return (strdup(buf));
156 }
157 
158 /* stolen from libc/gen/port/gen/execvp.c */
159 static const char *
160 execat(const char *s1, const char *s2, char *si)
161 {
162         char    *s;
163         int cnt = PATH_MAX + 1; /* number of characters in s2 */
164 
165         s = si;
166         while (*s1 && *s1 != ':') {
167                 if (cnt > 0) {
168                         *s++ = *s1++;
169                         cnt--;
170                 } else
171                         s1++;
172         }
173         if (si != s && cnt > 0) {
174                 *s++ = '/';
175                 cnt--;
176         }
177         while (*s2 && cnt > 0) {
178                 *s++ = *s2++;
179                 cnt--;
180         }
181         *s = '\0';
182         return (*s1 ? ++s1: 0);
183 }
184 
185 /*
186  * Similiar to execvp(), execpt you can supply an environment and we always
187  * use /bin/sh for shell scripts.  The PATH searched is the PATH in the
188  * current environment, not the environment in the argument list.
189  * This was pretty much stolen from libc/gen/port/execvp.c
190  */
191 static int
192 execvpe(char *name, char *const argv[], char *const envp[])
193 {
194 	char *path;
195 	char fname[PATH_MAX+2];
196 	char *newargs[256];
197 	int i;
198 	const char *cp;
199 	unsigned etxtbsy = 1;
200         int eacces = 0;
201 
202 	if (*name == '\0') {
203 		errno = ENOENT;
204 		return (-1);
205 	}
206 
207 	if ((path = getenv("PATH")) == NULL)
208 		path = "/usr/bin:/bin";
209 
210         cp = strchr(name, '/')? (const char *)"": path;
211 
212         do {
213                 cp = execat(cp, name, fname);
214         retry:
215                 /*
216                  * 4025035 and 4038378
217                  * if a filename begins with a "-" prepend "./" so that
218                  * the shell can't interpret it as an option
219                  */
220                 if (*fname == '-') {
221                         size_t size = strlen(fname) + 1;
222                         if ((size + 2) > sizeof (fname)) {
223                                 errno = E2BIG;
224                                 return (-1);
225                         }
226                         (void) memmove(fname + 2, fname, size);
227                         fname[0] = '.';
228                         fname[1] = '/';
229                 }
230                 (void) execve(fname, argv, envp);
231                 switch (errno) {
232                 case ENOEXEC:
233                         newargs[0] = "sh";
234                         newargs[1] = fname;
235                         for (i = 1; (newargs[i + 1] = argv[i]) != NULL; ++i) {
236                                 if (i >= 254) {
237                                         errno = E2BIG;
238                                         return (-1);
239                                 }
240                         }
241                         (void) execve("/bin/sh", newargs, envp);
242                         return (-1);
243                 case ETXTBSY:
244                         if (++etxtbsy > 5)
245                                 return (-1);
246                         (void) sleep(etxtbsy);
247                         goto retry;
248                 case EACCES:
249                         ++eacces;
250                         break;
251                 case ENOMEM:
252                 case E2BIG:
253                 case EFAULT:
254                         return (-1);
255                 }
256         } while (cp);
257         if (eacces)
258                 errno = EACCES;
259         return (-1);
260 }
261 
262 static char time_buf[50];
263 
264 /**
265  ** exec() - FORK AND EXEC CHILD PROCESS
266  **/
267 
268 /*VARARGS1*/
269 int
270 exec(int type, ...)
271 {
272 	va_list			args;
273 
274 	int			i;
275 	int			procuid;
276 	int			procgid;
277 	int			ret;
278 	int			fr_flg;
279 
280 	char			*cp;
281 	char			*infile;
282 	char			*outfile;
283 	char			*errfile;
284 	char			*sep;
285 
286 	char			**listp;
287 	char			**file_list;
288 	char			*printerName;
289 	char			*printerNameToShow;
290 	static char		nameBuf[100];
291 	char			*clean_title;
292 
293 	PSTATUS			*printer;
294 
295 	RSTATUS			*request;
296 
297 	FSTATUS			*form;
298 
299 	EXEC			*ep;
300 
301 	PWSTATUS		*pwheel;
302 	time_t			now;
303 	struct passwd		*pwp;
304 #ifdef LP_USE_PAPI_ATTR
305 	struct stat		tmpBuf;
306 	char 			tmpName[BUFSIZ];
307 	char			*path = NULL;
308 #endif
309 	char *av[ARG_MAX];
310 	char **envp = NULL;
311 	int ac = 0;
312 	char	*mail_zonename = NULL;
313 	char	*slabel = NULL;
314 
315 	syslog(LOG_DEBUG, "exec(%s)", _exec_name(type));
316 
317 	memset(av, 0, sizeof (*av));
318 
319 	va_start (args, type);
320 
321 	switch (type) {
322 
323 	case EX_INTERF:
324 		printer = va_arg(args, PSTATUS *);
325 		if (printer->status & PS_REMOTE) {
326 			errno = EINVAL;
327 			return (-1);
328 		}
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 	slot = ep - Exec_Table;
407 
408 	switch ((ep->pid = Fork1(ep))) {
409 
410 	case -1:
411 		relock ();
412 		return(-1);
413 
414 	case 0:
415 		/*
416 		 * We want to be able to tell our parent how we died.
417 		 */
418 		lp_alloc_fail_handler = child_mallocfail;
419 		break;
420 
421 	default:
422 		switch(type) {
423 
424 		case EX_INTERF:
425 			request->request->outcome |= RS_PRINTING;
426 			break;
427 
428 		case EX_NOTIFY:
429 			request->request->outcome |= RS_NOTIFYING;
430 			break;
431 
432 		case EX_SLOWF:
433 			request->request->outcome |= RS_FILTERING;
434 			request->request->outcome &= ~RS_REFILTER;
435 			break;
436 
437 		}
438 		return(0);
439 
440 	}
441 
442 	for (i = 0; i < NSIG; i++)
443 		(void)signal (i, SIG_DFL);
444 	(void)signal (SIGALRM, SIG_IGN);
445 	(void)signal (SIGTERM, sigtrap);
446 
447 	for (i = 0; i < OpenMax; i++)
448 		if (i != ChildMd->writefd)
449 			Close (i);
450 	setpgrp();
451 
452 	/* Set a default path */
453 	addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin");
454 	/* copy locale related variables */
455 	addenv (&envp, "TZ", getenv("TZ"));
456 	addenv (&envp, "LANG", getenv("LANG"));
457 	addenv (&envp, "LC_ALL", getenv("LC_ALL"));
458 	addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE"));
459 	addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE"));
460 	addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES"));
461 	addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY"));
462 	addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC"));
463 	addenv (&envp, "LC_TIME", getenv("LC_TIME"));
464 
465 	sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key);
466 	addenv (&envp, "SPOOLER_KEY", cp);
467 
468 #if	defined(DEBUG)
469 	addenv (&envp, "LPDEBUG", (debug? "1" : "0"));
470 #endif
471 
472 	/*
473 	 * Open the standard input, standard output, and standard error.
474 	 */
475 	switch (type) {
476 
477 	case EX_SLOWF:
478 	case EX_INTERF:
479 		/*
480 		 * stdin:  /dev/null
481 		 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF)
482 		 * stderr: req#
483 		 */
484 		infile = 0;
485 		outfile = 0;
486 		errfile = makereqerr(request);
487 		break;
488 
489 	case EX_NOTIFY:
490 		/*
491 		 * stdin:  req#
492 		 * stdout: /dev/null
493 		 * stderr: /dev/null
494 		 */
495 		infile = makereqerr(request);
496 		outfile = 0;
497 		errfile = 0;
498 
499 		break;
500 
501 	case EX_ALERT:
502 	case EX_FALERT:
503 	case EX_PALERT:
504 	case EX_FAULT_MESSAGE:
505 	case EX_FORM_MESSAGE:
506 		/*
507 		 * stdin:  /dev/null
508 		 * stdout: /dev/null
509 		 * stderr: /dev/null
510 		 */
511 		infile = 0;
512 		outfile = 0;
513 		errfile = 0;
514 		break;
515 
516 	}
517 
518 	if (infile) {
519 		if (Open(infile, O_RDONLY) == -1)
520 			Done (EXEC_EXIT_NOPEN, errno);
521 	} else {
522 		if (Open("/dev/null", O_RDONLY) == -1)
523 			Done (EXEC_EXIT_NOPEN, errno);
524 	}
525 
526 	if (outfile) {
527 		if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
528 			Done (EXEC_EXIT_NOPEN, errno);
529 	} else {
530 		/*
531 		 * If EX_INTERF, this is still needed to cause the
532 		 * standard error channel to be #2.
533 		 */
534 		if (Open("/dev/null", O_WRONLY) == -1)
535 			Done (EXEC_EXIT_NOPEN, errno);
536 	}
537 
538 	if (errfile) {
539 		if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
540 			Done (EXEC_EXIT_NOPEN, errno);
541 	} else {
542 		if (Open("/dev/null", O_WRONLY) == -1)
543 			Done (EXEC_EXIT_NOPEN, errno);
544 	}
545 
546 	switch (type) {
547 
548 	case EX_INTERF:
549 		/*
550 		 * Opening a ``port'' can be dangerous to our health:
551 		 *
552 		 *	- Hangups can occur if the line is dropped.
553 		 *	- The printer may send an interrupt.
554 		 *	- A FIFO may be closed, generating SIGPIPE.
555 		 *
556 		 * We catch these so we can complain nicely.
557 		 */
558 		trap_fault_signals ();
559 
560 		(void)Close (1);
561 
562 		if (strchr (request->secure->user, '!'))
563 		{
564 			procuid = Lp_Uid;
565 			procgid = Lp_Gid;
566 		}
567 		else
568 		{
569 			procuid = request->secure->uid;
570 			procgid = request->secure->gid;
571 		}
572 		if (printer->printer->dial_info)
573 		{
574 			ret = open_dialup(request->printer_type,
575 				printer->printer);
576 			if (ret == 0)
577 				do_undial = 1;
578 		}
579 		else
580 		{
581 			ret = open_direct(request->printer_type,
582 				printer->printer);
583 			do_undial = 0;
584 			/* this is a URI */
585 			if (is_printer_uri(printer->printer->device) == 0)
586 				addenv(&envp, "DEVICE_URI",
587 					 printer->printer->device);
588 		}
589 				addenv(&envp, "DEVICE_URI",
590 					 printer->printer->device);
591 		if (ret != 0)
592 			Done (ret, errno);
593 
594 		if (!(request->request->outcome & RS_FILTERED))
595 			file_list = request->request->file_list;
596 
597 		else {
598 			register int		count	= 0;
599 			register char *		num	= BIGGEST_REQID_S;
600 			register char *		prefix;
601 
602 			prefix = makestr(
603 				Lp_Tmp,
604 				"/",
605 				(request->secure && request->secure->system ?
606 					request->secure->system : Local_System),
607 				"/F",
608 				getreqno(request->secure->req_id),
609 				"-",
610 				(char *)0
611 			);
612 
613 			file_list = (char **)Malloc(
614 				(lenlist(request->request->file_list) + 1)
615 			      * sizeof(char *)
616 			);
617 
618 			for (
619 				listp = request->request->file_list;
620 				*listp;
621 				listp++
622 			) {
623 				sprintf (num, "%d", count + 1);
624 				file_list[count] = makestr(
625 					prefix,
626 					num,
627 					(char *)0
628 				);
629 				count++;
630 			}
631 			file_list[count] = 0;
632 		}
633 
634 #ifdef LP_USE_PAPI_ATTR
635 		/*
636 		 * Check if the PAPI job attribute file exists, if it does
637 		 * pass the file's pathname to the printer interface script
638 		 * in an environment variable. This file is created when
639 		 * print jobs are submitted via the PAPI interface.
640 		 */
641 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
642 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
643 		path = makepath(SPOOLDIR, "temp", tmpName, (char *)0);
644 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
645 		{
646 			/*
647 			 * IPP job attribute file exists for this job so
648 			 * set the environment variable
649 			 */
650 			syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path);
651 			addenv(&envp, "ATTRPATH", path);
652 		}
653 		Free(path);
654 
655 		/*
656 		 * now set environment variable for the printer's PostScript
657 		 * Printer Description (PPD) file, this is used by the filter
658 		 * when forming the print data for this printer.
659 		 */
660 		if ((request->printer != NULL) &&
661 		    (request->printer->printer != NULL) &&
662 		    (request->printer->printer->name != NULL))
663 		{
664 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
665 				request->printer->printer->name);
666 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
667 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
668 			{
669 				syslog(LOG_DEBUG,
670 					"exec(): Printer PPD='%s'", path);
671 				addenv(&envp, "PPD", path);
672 			}
673 			Free(path);
674 		}
675 #endif
676 
677 		if (request->printer_type)
678 			addenv(&envp, "TERM", request->printer_type);
679 
680 		if (!(printer->printer->daisy)) {
681 			register char *	chset = 0;
682 			register char *	csp;
683 
684 			if (
685 				request->form
686 			     && request->form->form->chset
687 			     && request->form->form->mandatory
688 			     && !STREQU(NAME_ANY, request->form->form->chset)
689 			)
690 				chset = request->form->form->chset;
691 
692 			else if (
693 				request->request->charset
694 			     && !STREQU(NAME_ANY, request->request->charset)
695 			)
696 				chset = request->request->charset;
697 
698 			if (chset) {
699 				csp = search_cslist(
700 					chset,
701 					printer->printer->char_sets
702 				);
703 
704 				/*
705 				 * The "strtok()" below wrecks the string
706 				 * for future use, but this is a child
707 				 * process where it won't be needed again.
708 				 */
709 				addenv (&envp, "CHARSET",
710 					(csp? strtok(csp, "=") : chset)
711 				);
712 			}
713 		}
714 
715 		if (request->fast)
716 			addenv(&envp, "FILTER", request->fast);
717 
718 		/*
719 		*/
720 		if (strcmp (request->secure->user, request->request->user))
721 		{
722 			addenv (&envp, "ALIAS_USERNAME",
723 				request->request->user);
724 		}
725 		/*
726 		 * Add the sensitivity label to the environment for
727 		 * banner page and header/footer processing
728 		 */
729 
730 		if (is_system_labeled() && request->secure->slabel != NULL)
731 			addenv(&envp, "SLABEL", request->secure->slabel);
732 
733 		/*
734 		 * Add the system name to the user name (ala system!user)
735 		 * unless it is already there. RFS users may have trouble
736 		 * here, sorry!
737 		 */
738 		cp = strchr(request->secure->user, '@');
739 
740 		allTraysWithForm(printer, request->form);
741 
742 		/*
743 		 * Fix for 4137389
744 		 * Remove double quotes from title string.
745 		 */
746 		fr_flg = 1;
747 		clean_title = strdup(NB(request->request->title));
748 		if (clean_title == NULL) {
749 			/*
750 			 * strdup failed. We're probably hosed
751 			 * but try setting clean_title
752 			 * to original title and continuing.
753 			 */
754 			clean_title = NB(request->request->title);
755 			fr_flg = 0;
756 		} else if (strcmp(clean_title, "") != 0) {
757 			char *ct_p;
758 
759 			for (ct_p = clean_title; *ct_p != NULL; ct_p++) {
760 				if (*ct_p == '"')
761 					*ct_p = ' ';
762 			}
763 		}
764 
765 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
766 					printer->printer->name);
767 		av[ac++] = arg_string(TRUSTED, "%s", request->secure->req_id);
768 		av[ac++] = arg_string(UNTRUSTED, "%s%s%s",
769 					request->secure->user,
770 					(cp? "" : "@"),
771 					(cp? "" : request->secure->system));
772 		av[ac++] = arg_string(TRUSTED, "%s", clean_title);
773 		av[ac++] = arg_string(TRUSTED, "%d", request->copies);
774 
775 		if (fr_flg)
776 			free (clean_title);
777 
778 		sep = "";
779 
780 		/*
781 		 * Do the administrator defined key=value pair options
782 		 */
783 
784 		argbuf[0] = '\0';
785 
786 		if (printer->printer->options) {
787 			char **tmp = printer->printer->options;
788 			while(*tmp != NULL) {
789 				STRLCAT(argbuf, sep, sizeof (argbuf));
790 				sep = " ";
791 				STRLCAT(argbuf, *tmp++, sizeof (argbuf));
792 			}
793 		}
794 
795 		/*
796 		 * Do the administrator defined ``stty'' stuff before
797 		 * the user's -o options, to allow the user to override.
798 		 */
799 		if (printer->printer->stty) {
800 			STRLCAT (argbuf, sep, sizeof (argbuf));
801 			sep = " ";
802 			STRLCAT (argbuf, "stty='", sizeof (argbuf));
803 			STRLCAT (argbuf, printer->printer->stty,
804 			    sizeof (argbuf));
805 			STRLCAT (argbuf, "'", sizeof (argbuf));
806 		}
807 
808 		/*
809 		 * Do all of the user's options except the cpi/lpi/etc.
810 		 * stuff, which is done separately.
811 		 */
812 		if (request->request->options) {
813 			listp = dashos(request->request->options);
814 			while (*listp) {
815 				if (
816 					!STRNEQU(*listp, "cpi=", 4)
817 				     && !STRNEQU(*listp, "lpi=", 4)
818 				     && !STRNEQU(*listp, "width=", 6)
819 				     && !STRNEQU(*listp, "length=", 7)
820 				) {
821 					STRLCAT (argbuf, sep, sizeof (argbuf));
822 					sep = " ";
823 					STRLCAT (argbuf, *listp,
824 					    sizeof (argbuf));
825 				}
826 				listp++;
827 			}
828 		}
829 
830 		/*
831 		 * The "pickfilter()" routine (from "validate()")
832 		 * stored the cpi/lpi/etc. stuff that should be
833 		 * used for this request. It chose form over user,
834 		 * and user over printer.
835 		 */
836 		if (request->cpi) {
837 			STRLCAT (argbuf, sep, sizeof (argbuf));
838 			sep = " ";
839 			STRLCAT (argbuf, "cpi=", sizeof (argbuf));
840 			STRLCAT (argbuf, request->cpi, sizeof (argbuf));
841 		}
842 		if (request->lpi) {
843 			STRLCAT (argbuf, sep, sizeof (argbuf));
844 			sep = " ";
845 			STRLCAT (argbuf, "lpi=", sizeof (argbuf));
846 			STRLCAT (argbuf, request->lpi, sizeof (argbuf));
847 		}
848 		if (request->pwid) {
849 			STRLCAT (argbuf, sep, sizeof (argbuf));
850 			sep = " ";
851 			STRLCAT (argbuf, "width=", sizeof (argbuf));
852 			STRLCAT (argbuf, request->pwid, sizeof (argbuf));
853 		}
854 		if (request->plen) {
855 			STRLCAT (argbuf, sep, sizeof (argbuf));
856 			sep = " ";
857 			STRLCAT (argbuf, "length=", sizeof (argbuf));
858 			STRLCAT (argbuf, request->plen, sizeof (argbuf));
859 		}
860 
861 		/*
862 		 * Do the ``raw'' bit last, to ensure it gets
863 		 * done. If the user doesn't want this, then he or
864 		 * she can do the correct thing using -o stty=
865 		 * and leaving out the -r option.
866 		 */
867 		if (request->request->actions & ACT_RAW) {
868 			STRLCAT (argbuf, sep, sizeof (argbuf));
869 			sep = " ";
870 			STRLCAT (argbuf, "stty=-opost", sizeof (argbuf));
871 		}
872 
873 
874 		/* the "options" */
875 		av[ac++] = arg_string(UNTRUSTED, "%s", argbuf);
876 
877 		for (listp = file_list; *listp; listp++)
878 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
879 
880 		(void)chfiles (file_list, procuid, procgid);
881 
882 		break;
883 
884 
885 	case EX_SLOWF:
886 		if (request->slow)
887 			addenv(&envp, "FILTER", request->slow);
888 
889 		if (strchr (request->secure->user, '!'))
890 		{
891 			procuid = Lp_Uid;
892 			procgid = Lp_Gid;
893 		}
894 		else
895 		{
896 			procuid = request->secure->uid;
897 			procgid = request->secure->gid;
898 		}
899 		cp = _alloc_files(
900 			lenlist(request->request->file_list),
901 			getreqno(request->secure->req_id),
902 			procuid,
903 			procgid,
904 			(request->secure && request->secure->system ?
905 				request->secure->system : NULL )
906 		);
907 
908 		av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter);
909 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_Tmp,
910 				(request->secure && request->secure->system ?
911 					request->secure->system : Local_System),
912 				cp);
913 		for (listp = request->request->file_list; *listp; listp++)
914 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
915 
916 		(void)chfiles (request->request->file_list, procuid, procgid);
917 
918 #ifdef LP_USE_PAPI_ATTR
919 		/*
920 		 * Check if the PAPI job attribute file exists, if it does
921 		 * pass the file's pathname to the slow-filters in an
922 		 * environment variable. Note: this file is created when
923 		 * print jobs are submitted via the PAPI interface.
924 		 */
925 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
926 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
927 		path = makepath(SPOOLDIR, "temp", tmpName, (char *)0);
928 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
929 		{
930 			/*
931 			 * IPP job attribute file exists for this job so
932 			 * set the environment variable
933 			 */
934 			syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path);
935 			addenv(&envp, "ATTRPATH", path);
936 		}
937 		Free(path);
938 
939 
940 		/*
941 		 * now set environment variable for the printer's PostScript
942 		 * Printer Description (PPD) file, this is used by the filter
943 		 * when forming the print data for this printer.
944 		 */
945 		if ((request->printer != NULL) &&
946 		    (request->printer->printer != NULL) &&
947 		    (request->printer->printer->name != NULL))
948 		{
949 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
950 				request->printer->printer->name);
951 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
952 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
953 			{
954 				syslog(LOG_DEBUG,
955 					"exec(): Printer PPD='%s'", path);
956 				addenv(&envp, "PPD", path);
957 			}
958 			Free(path);
959 		}
960 #endif
961 		break;
962 
963 	case EX_ALERT:
964 		procuid = Lp_Uid;
965 		procgid = Lp_Gid;
966 		(void)Chown (printer->alert->msgfile, procuid, procgid);
967 
968 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
969 				printer->printer->name, ALERTSHFILE);
970 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
971 
972 		break;
973 
974 	case EX_PALERT:
975 		procuid = Lp_Uid;
976 		procgid = Lp_Gid;
977 		(void)Chown (pwheel->alert->msgfile, procuid, procgid);
978 
979 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels,
980 				pwheel->pwheel->name, ALERTSHFILE);
981 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
982 
983 		break;
984 
985 	case EX_FALERT:
986 		procuid = Lp_Uid;
987 		procgid = Lp_Gid;
988 		(void)Chown (form->alert->msgfile, procuid, procgid);
989 
990 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
991 				form->form->name, ALERTSHFILE);
992 		av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile);
993 
994 		break;
995 
996 	case EX_FORM_MESSAGE:
997 		procuid = Lp_Uid;
998 		procgid = Lp_Gid;
999 
1000 		av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults);
1001 		av[ac++] = arg_string(TRUSTED, "%s", form->form->name);
1002 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
1003 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms,
1004 				form->form->name, FORMMESSAGEFILE);
1005 
1006 		break;
1007 
1008 	case EX_FAULT_MESSAGE:
1009 		procuid = Lp_Uid;
1010 		procgid = Lp_Gid;
1011 
1012 		av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults);
1013 		av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow);
1014 		av[ac++] = arg_string(TRUSTED, "%s", time_buf);
1015 		av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers,
1016 				printerName, FAULTMESSAGEFILE);
1017 
1018 		break;
1019 
1020 	case EX_NOTIFY:
1021 		if (request->request->alert) {
1022 			if (strchr(request->secure->user, '!')) {
1023 				procuid = Lp_Uid;
1024 				procgid = Lp_Gid;
1025 			} else {
1026 				procuid = request->secure->uid;
1027 				procgid = request->secure->gid;
1028 			}
1029 			av[ac++] = arg_string(TRUSTED, "%s",
1030 					request->request->alert);
1031 		} else {
1032 			char *user = strdup(request->secure->user);
1033 			clean_string(user);
1034 			slabel = request->secure->slabel;
1035 
1036 			if ((request->request->actions & ACT_WRITE) &&
1037 			    (!request->secure->system ||
1038 			    STREQU(request->secure->system, Local_System))) {
1039 				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
1040 				snprintf(argbuf, sizeof (argbuf),
1041 					"%s %s || %s %s",
1042 					BINWRITE, user,
1043 					BINMAIL, user
1044 				);
1045 				av[ac++] = arg_string(TRUSTED, "/bin/sh");
1046 				av[ac++] = arg_string(TRUSTED, "-c");
1047 				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
1048 			} else if ((getzoneid() == GLOBAL_ZONEID) &&
1049 				   is_system_labeled() && (slabel != NULL)) {
1050 				/*
1051 				 * If in the global zone and the system is
1052 				 * labeled, mail is handled via a local
1053 				 * labeled zone that is the same label as
1054 				 * the request.
1055 				 */
1056 				if ((mail_zonename =
1057 				    get_labeled_zonename(slabel)) ==
1058 				    (char *)-1) {
1059 					/*
1060 					 * Cannot find labeled zone, just
1061 					 * return 0.
1062 					 */
1063 					return(0);
1064 				}
1065 			}
1066 			if (mail_zonename == NULL) {
1067 				procuid = Lp_Uid;
1068 				procgid = Lp_Gid;
1069 				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
1070 				av[ac++] = arg_string(UNTRUSTED, "%s", user);
1071 			} else {
1072 				procuid = getuid();
1073 				procgid = getgid();
1074 				av[ac++] = arg_string(TRUSTED, "%s",
1075 				    "/usr/sbin/zlogin");
1076 				av[ac++] = arg_string(TRUSTED, "%s",
1077 				    mail_zonename);
1078 				av[ac++] = arg_string(TRUSTED, "%s",
1079 				    BINMAIL);
1080 				av[ac++] = arg_string(UNTRUSTED, "%s",
1081 				    user);
1082 				Free(mail_zonename);
1083 			}
1084 
1085 			free(user);
1086 		}
1087 		break;
1088 	}
1089 
1090 	av[ac++] = NULL;
1091 
1092 	Fork2 ();
1093 	/* only the child returns */
1094 
1095 	/*
1096 	 * Correctly set up the supplemental group list
1097 	 * for proper file access (before execl the interface program)
1098 	 */
1099 
1100 	pwp = getpwuid(procuid);
1101 	if (pwp == NULL) {
1102 		note("getpwuid(%d) call failed\n", procuid);
1103 	} else if (initgroups(pwp->pw_name, procgid) < 0) {
1104 		note("initgroups() call failed %d\n", errno);
1105 	}
1106 
1107 	setgid (procgid);
1108 	setuid (procuid);
1109 
1110 	/*
1111 	 * The shell doesn't allow the "trap" builtin to set a trap
1112 	 * for a signal ignored when the shell is started. Thus, don't
1113 	 * turn off signals in the last child!
1114 	 */
1115 
1116 	for (i = 0; av[i] != NULL; i++)
1117 		syslog(LOG_DEBUG, "exec: av[%d] = %s", i, av[i]);
1118 	for (i = 0; envp[i] != NULL; i++)
1119 		syslog(LOG_DEBUG, "exec: envp[%d] = %s", i, envp[i]);
1120 
1121 	execvpe(av[0], av, envp);
1122 	Done (EXEC_EXIT_NEXEC, errno);
1123 	/*NOTREACHED*/
1124 	return (0);
1125 }
1126 
1127 /**
1128  ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT
1129  **/
1130 
1131 static void
1132 addenv(char ***envp, char *name, char *value)
1133 {
1134 	register char *		cp;
1135 
1136 	if ((name == NULL) || (value == NULL))
1137 		return;
1138 
1139 	if ((cp = makestr(name, "=", value, (char *)0)))
1140 		addlist(envp, cp);
1141 	return;
1142 }
1143 
1144 /**
1145  ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT
1146  **/
1147 
1148 static int
1149 Fork1(EXEC *ep)
1150 {
1151 	int			pid;
1152 	int			fds[2];
1153 
1154 	if (pipe(fds) == -1) {
1155 		note("Failed to create pipe for child process (%s).\n", PERROR);
1156 		errno = EAGAIN ;
1157 		return(-1);
1158 	}
1159 
1160 	ep->md = mconnect((char *)0, fds[0], fds[1]);
1161 
1162 	switch (pid = fork()) {
1163 
1164 	case -1:
1165 		mdisconnect(ep->md);
1166 		close(fds[0]);
1167 		close(fds[1]);
1168 		ep->md = 0;
1169 		return (-1);
1170 
1171 	case 0:
1172 		ChildMd = mconnect(NULL, fds[1], fds[1]);
1173 		return (0);
1174 
1175 	default:
1176 		mlistenadd(ep->md, POLLIN);
1177 		return (pid);
1178 	}
1179 }
1180 
1181 /**
1182  ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT
1183  **/
1184 
1185 static void
1186 Fork2(void)
1187 {
1188 	switch ((ChildPid = fork())) {
1189 
1190 	case -1:
1191 		Done (EXEC_EXIT_NFORK, errno);
1192 		/*NOTREACHED*/
1193 
1194 	case 0:
1195 		return;
1196 
1197 	default:
1198 		/*
1199 		 * Delay calling "ignore_fault_signals()" as long
1200 		 * as possible, to give the child a chance to exec
1201 		 * the interface program and turn on traps.
1202 		 */
1203 
1204 		cool_heels ();
1205 		/*NOTREACHED*/
1206 
1207 	}
1208 }
1209 
1210 
1211 /**
1212  ** cool_heels() - WAIT FOR CHILD TO "DIE"
1213  **/
1214 
1215 static void
1216 cool_heels(void)
1217 {
1218 	int			status;
1219 
1220 	/*
1221 	 * At this point our only job is to wait for the child process.
1222 	 * If we hang out for a bit longer, that's okay.
1223 	 * By delaying before turning off the fault signals,
1224 	 * we increase the chance that the child process has completed
1225 	 * its exec and has turned on the fault traps. Nonetheless,
1226 	 * we can't guarantee a zero chance of missing a fault.
1227 	 * (We don't want to keep trapping the signals because the
1228 	 * interface program is likely to have a better way to handle
1229 	 * them; this process provides only rudimentary handling.)
1230 	 *
1231 	 * Note that on a very busy system, or with a very fast interface
1232 	 * program, the tables could be turned: Our sleep below (coupled
1233 	 * with a delay in the kernel scheduling us) may cause us to
1234 	 * detect the fault instead of the interface program.
1235 	 *
1236 	 * What we need is a way to synchronize with the child process.
1237 	 */
1238 	sleep (1);
1239 	ignore_fault_signals ();
1240 
1241 	WaitedChildPid = 0;
1242 	while ((WaitedChildPid = wait(&status)) != ChildPid)
1243 		;
1244 
1245 	if (
1246 		EXITED(status) > EXEC_EXIT_USER
1247 	     && EXITED(status) != EXEC_EXIT_FAULT
1248 	)
1249 		Done (EXEC_EXIT_EXIT, EXITED(status));
1250 
1251 	done (status, 0);	/* Don't use Done() */
1252 	/*NOTREACHED*/
1253 }
1254 
1255 
1256 /**
1257  ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT
1258  ** ignore_fault_signals() - IGNORE SAME
1259  **/
1260 
1261 static void
1262 trap_fault_signals(void)
1263 {
1264 	signal (SIGHUP, sigtrap);
1265 	signal (SIGINT, sigtrap);
1266 	signal (SIGQUIT, sigtrap);
1267 	signal (SIGPIPE, sigtrap);
1268 	return;
1269 }
1270 
1271 static void
1272 ignore_fault_signals(void)
1273 {
1274 	signal (SIGHUP, SIG_IGN);
1275 	signal (SIGINT, SIG_IGN);
1276 	signal (SIGQUIT, SIG_IGN);
1277 	signal (SIGPIPE, SIG_IGN);
1278 	return;
1279 }
1280 
1281 /**
1282  ** sigtrap() - TRAP VARIOUS SIGNALS
1283  **/
1284 
1285 static void
1286 sigtrap(int sig)
1287 {
1288 	signal (sig, SIG_IGN);
1289 	switch (sig) {
1290 
1291 	case SIGHUP:
1292 		Done (EXEC_EXIT_HUP, 0);
1293 		/*NOTREACHED*/
1294 
1295 	case SIGQUIT:
1296 	case SIGINT:
1297 		Done (EXEC_EXIT_INTR, 0);
1298 		/*NOTREACHED*/
1299 
1300 	case SIGPIPE:
1301 		Done (EXEC_EXIT_PIPE, 0);
1302 		/*NOTREACHED*/
1303 
1304 	case SIGTERM:
1305 		/*
1306 		 * If we were killed with SIGTERM, it should have been
1307 		 * via the Spooler who should have killed the entire
1308 		 * process group. We have to wait for the children,
1309 		 * since we're their parent, but WE MAY HAVE WAITED
1310 		 * FOR THEM ALREADY (in cool_heels()).
1311 		 */
1312 		if (ChildPid != WaitedChildPid) {
1313 			register int		cpid;
1314 
1315 			while (
1316 				(cpid = wait((int *)0)) != ChildPid
1317 			     && (cpid != -1 || errno != ECHILD)
1318 			)
1319 				;
1320 		}
1321 
1322 		/*
1323 		 * We can't rely on getting SIGTERM back in the wait()
1324 		 * above, because, for instance, some shells trap SIGTERM
1325 		 * and exit instead. Thus we force it.
1326 		 */
1327 		done (SIGTERM, 0);	/* Don't use Done() */
1328 		/*NOTREACHED*/
1329 	}
1330 }
1331 
1332 /**
1333  ** done() - TELL SPOOLER THIS CHILD IS DONE
1334  **/
1335 
1336 static void
1337 done(int status, int err)
1338 {
1339 	if (do_undial)
1340 		undial (1);
1341 
1342 	mputm (ChildMd, S_CHILD_DONE, key, slot, status, err);
1343 	mdisconnect (ChildMd);
1344 
1345 	exit (0);
1346 	/*NOTREACHED*/
1347 }
1348 
1349 /**
1350  ** child_mallocfail()
1351  **/
1352 
1353 static void
1354 child_mallocfail(void)
1355 {
1356 	Done (EXEC_EXIT_NOMEM, ENOMEM);
1357 }
1358