xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/exec.c (revision 6faf52448e142b151fa3deade474be359e7c8698)
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 (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 #include <pwd.h>
31 #include <zone.h>
32 #include <dial.h>
33 
34 #include <stdlib.h>
35 #include "limits.h"
36 #include "stdarg.h"
37 #include "wait.h"
38 #include "dial.h"
39 #include "lpsched.h"
40 #include <syslog.h>
41 #include "tsol/label.h"
42 
43 #define Done(EC,ERRNO)	done(((EC) << 8),ERRNO)
44 
45 #define	STRLCAT(dst, src, size) \
46 	if (strlcat((dst), (src), (size)) >= (size)) { \
47 		errno = EINVAL; \
48 		return (-1); \
49 	}
50 
51 static MESG *		ChildMd;
52 
53 static int		ChildPid;
54 static int		WaitedChildPid;
55 static int		do_undial;
56 
57 static char		argbuf[ARG_MAX];
58 
59 static long		key;
60 
61 static void		sigtrap ( int );
62 static void		done ( int , int );
63 static void		cool_heels ( void );
64 static void		addenv (char ***envp, char * , char * );
65 static void		trap_fault_signals ( void );
66 static void		ignore_fault_signals ( void );
67 static void		child_mallocfail ( void );
68 static void		Fork2 ( void );
69 
70 static int		Fork1 ( EXEC * );
71 
72 static void
73 relock(void)
74 {
75 	struct flock		l;
76 
77 	l.l_type = F_WRLCK;
78 	l.l_whence = 1;
79 	l.l_start = 0;
80 	l.l_len = 0;
81 	(void)Fcntl (lock_fd, F_SETLK, &l);
82 	return;
83 }
84 
85 static char *_exec_name(int type)
86 {
87 	static char *_names[] = {
88 	"", "EX_INTERF", "EX_SLOWF", "EX_ALERT", "EX_FALERT", "EX_PALERT",
89 	"EX_NOTIFY", "EX_FAULT_MESSAGE", "EX_FORM_MESSAGE", NULL };
90 
91 	if ((type < 0) || (type > EX_FORM_MESSAGE))
92 		return ("BAD_EXEC_TYPE");
93 	else
94 		return (_names[type]);
95 }
96 
97 /*
98  * This function replaces characters in a string that might be used
99  * to exploit a security hole.  Replace command seperators (`, &, ;, |, ^),
100  * output redirection (>, |), variable expansion ($), and character
101  * escape (\).
102  *
103  * Bugid 4141687
104  * Add ( ) < * ? [
105  * Bugid 4139071
106  * Remove \
107  */
108 void clean_string(char *ptr)
109 {
110 	char *cp;
111 	wchar_t wc;
112 	size_t len;
113 
114 	for (cp = ptr; *cp != '\0'; ) {
115 		if ((len = mbtowc(&wc, cp, MB_CUR_MAX)) == -1) {
116 			cp++;
117 			continue;
118 		}
119 
120 		if (len == 1 &&
121 		    ((wc == L'`') || (wc == L'&') || (wc == L';') ||
122 		    (wc == L'|') || (wc == L'>') || (wc == L'^') ||
123 		    (wc == L'$') || (wc == L'(') || (wc == L')') ||
124 		    (wc == L'<') || (wc == L'*') || (wc == L'?') ||
125 		    (wc == L'[')))
126 			*cp = '_';
127 		cp += len;
128 	}
129 }
130 
131 enum trust {TRUSTED, UNTRUSTED};
132 
133 static char *arg_string(enum trust type, char *fmt, ...) __PRINTFLIKE(2);
134 
135 /* PRINTFLIKE2 */
136 static char *
137 arg_string(enum trust type, char *fmt, ...)
138 {
139 	char buf[BUFSIZ];
140 	va_list	args;
141 
142 	va_start(args, fmt);
143 	(void) vsnprintf(buf, sizeof(buf), fmt, args);
144 	va_end(args);
145 
146 	/*
147 	 * If the string contains data from an untrusted origin (user supplied),
148 	 * clean it up in case one of our progeny is a shell script and isn't
149 	 * careful about checking its input.
150 	 */
151 	if (type == UNTRUSTED)
152 		clean_string(buf);
153 
154 	return (strdup(buf));
155 }
156 
157 static char time_buf[50];
158 
159 /**
160  ** exec() - FORK AND EXEC CHILD PROCESS
161  **/
162 
163 /*VARARGS1*/
164 int
165 exec(int type, ...)
166 {
167 	va_list			args;
168 
169 	int			i;
170 	int			procuid;
171 	int			procgid;
172 	int			ret;
173 	int			fr_flg;
174 
175 	char			*cp;
176 	char			*infile;
177 	char			*outfile;
178 	char			*errfile;
179 	char			*sep;
180 
181 	char			**listp;
182 	char			**file_list;
183 	char			*printerName;
184 	char			*printerNameToShow;
185 	static char		nameBuf[100];
186 	char			*clean_title;
187 
188 	PSTATUS			*printer;
189 
190 	RSTATUS			*request;
191 
192 	FSTATUS			*form;
193 
194 	EXEC			*ep;
195 
196 	PWSTATUS		*pwheel;
197 	time_t			now;
198 	struct passwd		*pwp;
199 #ifdef LP_USE_PAPI_ATTR
200 	struct stat		tmpBuf;
201 	char 			tmpName[BUFSIZ];
202 	char			*path = NULL;
203 #endif
204 	char *av[ARG_MAX];
205 	char **envp = NULL;
206 	int ac = 0;
207 	char	*mail_zonename = NULL;
208 	char	*slabel = NULL;
209 	int	setid = 1;
210 	char	*ridno = NULL, *tmprid = NULL;
211 
212 	syslog(LOG_DEBUG, "exec(%s)", _exec_name(type));
213 
214 	memset(av, 0, sizeof (*av));
215 
216 	va_start (args, type);
217 
218 	switch (type) {
219 
220 	case EX_INTERF:
221 		printer = va_arg(args, PSTATUS *);
222 		request = printer->request;
223 		ep = printer->exec;
224 		break;
225 
226 	case EX_FAULT_MESSAGE:
227 		printer = va_arg(args, PSTATUS *);
228 		request = va_arg(args, RSTATUS *);
229 		if (! ( printer->status & (PS_FORM_FAULT | PS_SHOW_FAULT))) {
230 			return(0);
231 		}
232 		ep = printer->fault_exec;
233 		printerName = (printer->printer && printer->printer->name
234 				  ? printer->printer->name : "??");
235 			snprintf(nameBuf, sizeof (nameBuf),
236 				"%s (on %s)\n", printerName, Local_System);
237 
238 		printerNameToShow = nameBuf;
239 
240 		(void) time(&now);
241 		(void) strftime(time_buf, sizeof (time_buf),
242 			NULL, localtime(&now));
243 		break;
244 
245 	case EX_SLOWF:
246 		request = va_arg(args, RSTATUS *);
247 		ep = request->exec;
248 		break;
249 
250 	case EX_NOTIFY:
251 		request = va_arg(args, RSTATUS *);
252 		if (request->request->actions & ACT_NOTIFY) {
253 			errno = EINVAL;
254 			return (-1);
255 		}
256 		ep = request->exec;
257 		break;
258 
259 	case EX_ALERT:
260 		printer = va_arg(args, PSTATUS *);
261 		if (!(printer->printer->fault_alert.shcmd)) {
262 			errno = EINVAL;
263 			return(-1);
264 		}
265 		ep = printer->alert->exec;
266 		break;
267 
268 	case EX_PALERT:
269 		pwheel = va_arg(args, PWSTATUS *);
270 		ep = pwheel->alert->exec;
271 		break;
272 
273 	case EX_FORM_MESSAGE:
274 		(void) time(&now);
275 		(void) strftime(time_buf, sizeof (time_buf),
276 			NULL, localtime(&now));
277 
278 		/*FALLTHRU*/
279 	case EX_FALERT:
280 		form = va_arg(args, FSTATUS *);
281 		ep = form->alert->exec;
282 		break;
283 
284 	default:
285 		errno = EINVAL;
286 		return(-1);
287 
288 	}
289 	va_end (args);
290 
291 	if (!ep || (ep->pid > 0)) {
292 		errno = EBUSY;
293 		return(-1);
294 	}
295 
296 	ep->flags = 0;
297 
298 	key = ep->key = getkey();
299 
300 	switch ((ep->pid = Fork1(ep))) {
301 
302 	case -1:
303 		relock ();
304 		return(-1);
305 
306 	case 0:
307 		/*
308 		 * We want to be able to tell our parent how we died.
309 		 */
310 		lp_alloc_fail_handler = child_mallocfail;
311 		break;
312 
313 	default:
314 		switch(type) {
315 
316 		case EX_INTERF:
317 			request->request->outcome |= RS_PRINTING;
318 			break;
319 
320 		case EX_NOTIFY:
321 			request->request->outcome |= RS_NOTIFYING;
322 			break;
323 
324 		case EX_SLOWF:
325 			request->request->outcome |= RS_FILTERING;
326 			request->request->outcome &= ~RS_REFILTER;
327 			break;
328 
329 		}
330 		return(0);
331 
332 	}
333 
334 	for (i = 0; i < NSIG; i++)
335 		(void)signal (i, SIG_DFL);
336 	(void)signal (SIGALRM, SIG_IGN);
337 	(void)signal (SIGTERM, sigtrap);
338 
339 	closelog();
340 	for (i = 0; i < OpenMax; i++)
341 		if (i != ChildMd->writefd)
342 			Close (i);
343 	openlog("lpsched", LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR);
344 
345 	setpgrp();
346 
347 	/* Set a default path */
348 	addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin");
349 	/* copy locale related variables */
350 	addenv (&envp, "TZ", getenv("TZ"));
351 	addenv (&envp, "LANG", getenv("LANG"));
352 	addenv (&envp, "LC_ALL", getenv("LC_ALL"));
353 	addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE"));
354 	addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE"));
355 	addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES"));
356 	addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY"));
357 	addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC"));
358 	addenv (&envp, "LC_TIME", getenv("LC_TIME"));
359 
360 	sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key);
361 	addenv (&envp, "SPOOLER_KEY", cp);
362 
363 #if	defined(DEBUG)
364 	addenv (&envp, "LPDEBUG", (debug? "1" : "0"));
365 #endif
366 
367 	/*
368 	 * Open the standard input, standard output, and standard error.
369 	 */
370 	switch (type) {
371 
372 	case EX_SLOWF:
373 	case EX_INTERF:
374 		/*
375 		 * stdin:  /dev/null
376 		 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF)
377 		 * stderr: req#
378 		 */
379 		infile = 0;
380 		outfile = 0;
381 		errfile = makereqerr(request);
382 		break;
383 
384 	case EX_NOTIFY:
385 		/*
386 		 * stdin:  req#
387 		 * stdout: /dev/null
388 		 * stderr: /dev/null
389 		 */
390 		infile = makereqerr(request);
391 		outfile = 0;
392 		errfile = 0;
393 
394 		break;
395 
396 	case EX_ALERT:
397 	case EX_FALERT:
398 	case EX_PALERT:
399 	case EX_FAULT_MESSAGE:
400 	case EX_FORM_MESSAGE:
401 		/*
402 		 * stdin:  /dev/null
403 		 * stdout: /dev/null
404 		 * stderr: /dev/null
405 		 */
406 		infile = 0;
407 		outfile = 0;
408 		errfile = 0;
409 		break;
410 
411 	}
412 
413 	if (infile) {
414 		if (Open(infile, O_RDONLY) == -1)
415 			Done (EXEC_EXIT_NOPEN, errno);
416 	} else {
417 		if (Open("/dev/null", O_RDONLY) == -1)
418 			Done (EXEC_EXIT_NOPEN, errno);
419 	}
420 
421 	if (outfile) {
422 		if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
423 			Done (EXEC_EXIT_NOPEN, errno);
424 	} else {
425 		/*
426 		 * If EX_INTERF, this is still needed to cause the
427 		 * standard error channel to be #2.
428 		 */
429 		if (Open("/dev/null", O_WRONLY) == -1)
430 			Done (EXEC_EXIT_NOPEN, errno);
431 	}
432 
433 	if (errfile) {
434 		if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1)
435 			Done (EXEC_EXIT_NOPEN, errno);
436 	} else {
437 		if (Open("/dev/null", O_WRONLY) == -1)
438 			Done (EXEC_EXIT_NOPEN, errno);
439 	}
440 
441 	switch (type) {
442 
443 	case EX_INTERF:
444 		/*
445 		 * Opening a ``port'' can be dangerous to our health:
446 		 *
447 		 *	- Hangups can occur if the line is dropped.
448 		 *	- The printer may send an interrupt.
449 		 *	- A FIFO may be closed, generating SIGPIPE.
450 		 *
451 		 * We catch these so we can complain nicely.
452 		 */
453 		trap_fault_signals ();
454 
455 		(void)Close (1);
456 
457 		procuid = request->secure->uid;
458 		procgid = request->secure->gid;
459 
460 		if (printer->printer->dial_info)
461 		{
462 			ret = open_dialup(request->printer_type,
463 				printer->printer);
464 			if (ret == 0)
465 				do_undial = 1;
466 		}
467 		else
468 		{
469 			ret = open_direct(request->printer_type,
470 				printer->printer);
471 			do_undial = 0;
472 			/* this is a URI */
473 			if (is_printer_uri(printer->printer->device) == 0)
474 				addenv(&envp, "DEVICE_URI",
475 					 printer->printer->device);
476 		}
477 				addenv(&envp, "DEVICE_URI",
478 					 printer->printer->device);
479 		if (ret != 0)
480 			Done (ret, errno);
481 
482 		if (!(request->request->outcome & RS_FILTERED))
483 			file_list = request->request->file_list;
484 
485 		else {
486 			register int		count	= 0;
487 			register char *		num	= BIGGEST_REQID_S;
488 			register char *		prefix;
489 
490 			prefix = makestr(
491 				Lp_Temp,
492 				"/F",
493 				getreqno(request->secure->req_id),
494 				"-",
495 				(char *)0
496 			);
497 
498 			file_list = (char **)Malloc(
499 				(lenlist(request->request->file_list) + 1)
500 			      * sizeof(char *)
501 			);
502 
503 			for (
504 				listp = request->request->file_list;
505 				*listp;
506 				listp++
507 			) {
508 				sprintf (num, "%d", count + 1);
509 				file_list[count] = makestr(
510 					prefix,
511 					num,
512 					(char *)0
513 				);
514 				count++;
515 			}
516 			file_list[count] = 0;
517 		}
518 
519 #ifdef LP_USE_PAPI_ATTR
520 		/*
521 		 * Check if the PAPI job attribute file exists, if it does
522 		 * pass the file's pathname to the printer interface script
523 		 * in an environment variable. This file is created when
524 		 * print jobs are submitted via the PAPI interface.
525 		 */
526 		snprintf(tmpName, sizeof (tmpName), "%s-%s",
527 			getreqno(request->secure->req_id), LP_PAPIATTRNAME);
528 		path = makepath(Lp_Temp, tmpName, (char *)0);
529 		if ((path != NULL) && (stat(path, &tmpBuf) == 0))
530 		{
531 			/*
532 			 * IPP job attribute file exists for this job so
533 			 * set the environment variable
534 			 */
535 			addenv(&envp, "ATTRPATH", path);
536 		}
537 		Free(path);
538 
539 		/*
540 		 * now set environment variable for the printer's PostScript
541 		 * Printer Description (PPD) file, this is used by the filter
542 		 * when forming the print data for this printer.
543 		 */
544 		if ((request->printer != NULL) &&
545 		    (request->printer->printer != NULL) &&
546 		    (request->printer->printer->name != NULL))
547 		{
548 			snprintf(tmpName, sizeof (tmpName), "%s.ppd",
549 				request->printer->printer->name);
550 			path = makepath(ETCDIR, "ppd", tmpName, (char *)0);
551 			if ((path != NULL) && (stat(path, &tmpBuf) == 0))
552 			{
553 				addenv(&envp, "PPD", path);
554 			}
555 			Free(path);
556 		}
557 #endif
558 
559 		if (request->printer_type)
560 			addenv(&envp, "TERM", request->printer_type);
561 
562 		if (!(printer->printer->daisy)) {
563 			register char *	chset = 0;
564 			register char *	csp;
565 
566 			if (
567 				request->form
568 			     && request->form->form->chset
569 			     && request->form->form->mandatory
570 			     && !STREQU(NAME_ANY, request->form->form->chset)
571 			)
572 				chset = request->form->form->chset;
573 
574 			else if (
575 				request->request->charset
576 			     && !STREQU(NAME_ANY, request->request->charset)
577 			)
578 				chset = request->request->charset;
579 
580 			if (chset) {
581 				csp = search_cslist(
582 					chset,
583 					printer->printer->char_sets
584 				);
585 
586 				/*
587 				 * The "strtok()" below wrecks the string
588 				 * for future use, but this is a child
589 				 * process where it won't be needed again.
590 				 */
591 				addenv (&envp, "CHARSET",
592 					(csp? strtok(csp, "=") : chset)
593 				);
594 			}
595 		}
596 
597 		if (request->fast)
598 			addenv(&envp, "FILTER", request->fast);
599 
600 		/*
601 		 * Add the sensitivity label to the environment for
602 		 * banner page and header/footer processing
603 		 */
604 
605 		if (is_system_labeled() && request->secure->slabel != NULL)
606 			addenv(&envp, "SLABEL", request->secure->slabel);
607 
608 		/*
609 		 * Add the system name to the user name (ala system!user)
610 		 * unless it is already there. RFS users may have trouble
611 		 * here, sorry!
612 		 */
613 		cp = strchr(request->secure->user, '@');
614 
615 		allTraysWithForm(printer, request->form);
616 
617 		/*
618 		 * Fix for 4137389
619 		 * Remove double quotes from title string.
620 		 */
621 		fr_flg = 1;
622 		clean_title = strdup(NB(request->request->title));
623 		if (clean_title == NULL) {
624 			/*
625 			 * strdup failed. We're probably hosed
626 			 * but try setting clean_title
627 			 * to original title and continuing.
628 			 */
629 			clean_title = NB(request->request->title);
630 			fr_flg = 0;
631 		} else if (strcmp(clean_title, "") != 0) {
632 			char *ct_p;
633 
634 			for (ct_p = clean_title; *ct_p != '\0'; ct_p++) {
635 				if (*ct_p == '"')
636 					*ct_p = ' ';
637 			}
638 		}
639 
640 		av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces,
641 					printer->printer->name);
642 		/*
643 		 * Read the options field of the request
644 		 * In case of remote lpd request
645 		 * the options field will have
646 		 * job-id-requested. This is the
647 		 * id sent by the client
648 		 */
649 		if (request->request->options != NULL) {
650 			char *options = NULL, *temp = NULL;
651 			options = temp = strdup(request->request->options);
652 
653 			/*
654 			 * Search for job-id-requested in
655 			 * options string
656 			 */
657 			options = strstr(options, "job-id-requested");
658 			if (options != NULL) {
659 				/*
660 				 * Extract the ridno from the string
661 				 * job-id-requested=xxx
662 				 * In this case ridno = xxx
663 				 */
664 				if (STRNEQU(options, "job-id-requested=", 17)) {
665 					ridno = strdup(options + 17);
666 					tmprid = strstr(ridno, " ");
667 					if (ridno != NULL) {
668 						/*
669 						 * Read job-id-requested
670 						 * successfully
671 						 */
672 						tmprid = strstr(ridno, " ");
673 						if (tmprid != NULL)
674 							*tmprid = '\0';
675 
676 						setid = 0;
677 					} else
678 						/*
679 						 * could not read
680 						 * ridno from the string
681 						 * job-id-requested=xxx
682 						 */
683 						setid = 1;
684 				} else
685 					/*
686 					 * could not read
687 					 * ridno from the string
688 					 * job-id-requested=xxx
689 					 */
690 					setid = 1;
691 			} else
692 				/*
693 				 * No job-id-requested in
694 				 * request options
695 				 */
696 				setid = 1;
697 
698 			if (temp != NULL)
699 				free(temp);
700 
701 		} else
702 			/*
703 			 * options field in request structure
704 			 * not set
705 			 */
706 			setid = 1;
707 
708 
709 		/*
710 		 * setid = 1 means the job-id-requested attribute
711 		 * is not set so read the request->secure->req_id
712 		 */
713 		if (setid)
714 			av[ac++] = arg_string(TRUSTED, "%s",
715 			    request->secure->req_id);
716 		else {
717 			/*
718 			 * From request->secure->req_id extract the
719 			 * printer-name.
720 			 * request->secure->req_id = <printer-name>-<req_id>
721 			 * The final req-id will be
722 			 * <printer-name>-<ridno>
723 			 */
724 			char *r1 = NULL, *r2 = NULL, *tmp = NULL;
725 			r1 = r2 = tmp = strdup(request->secure->req_id);
726 			r2 = strrchr(r1, '-');
727 			if (r2 != NULL) {
728 				char *r3 = NULL;
729 				int lr1 = strlen(r1);
730 				int lr2 = strlen(r2);
731 				r1[lr1 - lr2 + 1] = '\0';
732 
733 				/*
734 				 * Now r1 = <printer-name>-
735 				 */
736 				lr1 = strlen(r1);
737 				lr2 = strlen(ridno);
738 
739 				r3 = (char *)malloc(lr1+lr2+1);
740 				if (r3 != NULL) {
741 					strcpy(r3, r1);
742 					strcat(r3, ridno);
743 					/*
744 					 * Here r3 = <printer-name>-<ridno>
745 					 */
746 					av[ac++] = arg_string(TRUSTED,
747 					    "%s", r3);
748 					free(r3);
749 				} else
750 					av[ac++] = arg_string(TRUSTED, "%s",
751 					    request->secure->req_id);
752 
753 			} else
754 				av[ac++] = arg_string(TRUSTED, "%s",
755 				    request->secure->req_id);
756 
757 			if (tmp != NULL)
758 				free(tmp);
759 
760 			if (ridno != NULL)
761 				free(ridno);
762 		}
763 
764 		av[ac++] = arg_string(UNTRUSTED, "%s", request->request->user);
765 		av[ac++] = arg_string(TRUSTED, "%s", clean_title);
766 		av[ac++] = arg_string(TRUSTED, "%d", request->copies);
767 
768 		if (fr_flg)
769 			free (clean_title);
770 
771 		sep = "";
772 
773 		/*
774 		 * Do the administrator defined key=value pair options
775 		 */
776 
777 		argbuf[0] = '\0';
778 
779 		if (printer->printer->options) {
780 			char **tmp = printer->printer->options;
781 			while(*tmp != NULL) {
782 				STRLCAT(argbuf, sep, sizeof (argbuf));
783 				sep = " ";
784 				STRLCAT(argbuf, *tmp++, sizeof (argbuf));
785 			}
786 		}
787 
788 		/*
789 		 * Do the administrator defined ``stty'' stuff before
790 		 * the user's -o options, to allow the user to override.
791 		 */
792 		if (printer->printer->stty) {
793 			STRLCAT (argbuf, sep, sizeof (argbuf));
794 			sep = " ";
795 			STRLCAT (argbuf, "stty='", sizeof (argbuf));
796 			STRLCAT (argbuf, printer->printer->stty,
797 			    sizeof (argbuf));
798 			STRLCAT (argbuf, "'", sizeof (argbuf));
799 		}
800 
801 		/*
802 		 * Do all of the user's options except the cpi/lpi/etc.
803 		 * stuff, which is done separately.
804 		 */
805 		if (request->request->options) {
806 			listp = dashos(request->request->options);
807 			while (*listp) {
808 				if (
809 					!STRNEQU(*listp, "cpi=", 4)
810 				     && !STRNEQU(*listp, "lpi=", 4)
811 				     && !STRNEQU(*listp, "width=", 6)
812 				     && !STRNEQU(*listp, "length=", 7)
813 				) {
814 					STRLCAT (argbuf, sep, sizeof (argbuf));
815 					sep = " ";
816 					STRLCAT (argbuf, *listp,
817 					    sizeof (argbuf));
818 				}
819 				listp++;
820 			}
821 		}
822 
823 		/*
824 		 * The "pickfilter()" routine (from "validate()")
825 		 * stored the cpi/lpi/etc. stuff that should be
826 		 * used for this request. It chose form over user,
827 		 * and user over printer.
828 		 */
829 		if (request->cpi) {
830 			STRLCAT (argbuf, sep, sizeof (argbuf));
831 			sep = " ";
832 			STRLCAT (argbuf, "cpi=", sizeof (argbuf));
833 			STRLCAT (argbuf, request->cpi, sizeof (argbuf));
834 		}
835 		if (request->lpi) {
836 			STRLCAT (argbuf, sep, sizeof (argbuf));
837 			sep = " ";
838 			STRLCAT (argbuf, "lpi=", sizeof (argbuf));
839 			STRLCAT (argbuf, request->lpi, sizeof (argbuf));
840 		}
841 		if (request->pwid) {
842 			STRLCAT (argbuf, sep, sizeof (argbuf));
843 			sep = " ";
844 			STRLCAT (argbuf, "width=", sizeof (argbuf));
845 			STRLCAT (argbuf, request->pwid, sizeof (argbuf));
846 		}
847 		if (request->plen) {
848 			STRLCAT (argbuf, sep, sizeof (argbuf));
849 			sep = " ";
850 			STRLCAT (argbuf, "length=", sizeof (argbuf));
851 			STRLCAT (argbuf, request->plen, sizeof (argbuf));
852 		}
853 
854 		/*
855 		 * Do the ``raw'' bit last, to ensure it gets
856 		 * done. If the user doesn't want this, then they
857 		 * can do the correct thing using -o stty=
858 		 * and leaving out the -r option.
859 		 */
860 		if (request->request->actions & ACT_RAW) {
861 			STRLCAT (argbuf, sep, sizeof (argbuf));
862 			sep = " ";
863 			STRLCAT (argbuf, "stty=-opost", sizeof (argbuf));
864 		}
865 
866 
867 		/* the "options" */
868 		av[ac++] = arg_string(UNTRUSTED, "%s", argbuf);
869 
870 		for (listp = file_list; *listp; listp++)
871 			av[ac++] = arg_string(TRUSTED, "%s", *listp);
872 
873 		(void)chfiles (file_list, procuid, procgid);
874 
875 		break;
876 
877 
878 	case EX_SLOWF:
879 		if (request->slow)
880 			addenv(&envp, "FILTER", request->slow);
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 			procuid = request->secure->uid;
999 			procgid = request->secure->gid;
1000 
1001 			av[ac++] = arg_string(TRUSTED, "%s",
1002 					request->request->alert);
1003 		} else {
1004 			char *user = strdup(request->request->user);
1005 			clean_string(user);
1006 			slabel = request->secure->slabel;
1007 
1008 			if (request->request->actions & ACT_WRITE) {
1009 				av[ac++] = arg_string(TRUSTED, "%s", BINWRITE);
1010 				snprintf(argbuf, sizeof (argbuf),
1011 					"%s %s || %s %s",
1012 					BINWRITE, user,
1013 					BINMAIL, user
1014 				);
1015 				av[ac++] = arg_string(TRUSTED, "/bin/sh");
1016 				av[ac++] = arg_string(TRUSTED, "-c");
1017 				av[ac++] = arg_string(TRUSTED, "%s", argbuf);
1018 			} else if ((getzoneid() == GLOBAL_ZONEID) &&
1019 				   is_system_labeled() && (slabel != NULL)) {
1020 				/*
1021 				 * If in the global zone and the system is
1022 				 * labeled, mail is handled via a local
1023 				 * labeled zone that is the same label as
1024 				 * the request.
1025 				 */
1026 				if ((mail_zonename =
1027 				    get_labeled_zonename(slabel)) ==
1028 				    (char *)-1) {
1029 					/*
1030 					 * Cannot find labeled zone, just
1031 					 * return 0.
1032 					 */
1033 					return(0);
1034 				}
1035 			}
1036 			if (mail_zonename == NULL) {
1037 				procuid = Lp_Uid;
1038 				procgid = Lp_Gid;
1039 				av[ac++] = arg_string(TRUSTED, "%s", BINMAIL);
1040 				av[ac++] = arg_string(UNTRUSTED, "%s", user);
1041 			} else {
1042 				procuid = getuid();
1043 				procgid = getgid();
1044 				av[ac++] = arg_string(TRUSTED, "%s",
1045 				    "/usr/sbin/zlogin");
1046 				av[ac++] = arg_string(TRUSTED, "%s",
1047 				    mail_zonename);
1048 				av[ac++] = arg_string(TRUSTED, "%s",
1049 				    BINMAIL);
1050 				av[ac++] = arg_string(UNTRUSTED, "%s",
1051 				    user);
1052 				Free(mail_zonename);
1053 			}
1054 
1055 			free(user);
1056 		}
1057 		break;
1058 	}
1059 
1060 	av[ac++] = NULL;
1061 
1062 	Fork2 ();
1063 	/* only the child returns */
1064 
1065 	/*
1066 	 * Correctly set up the supplemental group list
1067 	 * for proper file access (before execl the interface program)
1068 	 */
1069 
1070 	pwp = getpwuid(procuid);
1071 	if (pwp == NULL) {
1072 		note("getpwuid(%d) call failed\n", procuid);
1073 	} else if (initgroups(pwp->pw_name, procgid) < 0) {
1074 		note("initgroups() call failed %d\n", errno);
1075 	}
1076 
1077 	setgid (procgid);
1078 	setuid (procuid);
1079 
1080 	/*
1081 	 * The shell doesn't allow the "trap" builtin to set a trap
1082 	 * for a signal ignored when the shell is started. Thus, don't
1083 	 * turn off signals in the last child!
1084 	 */
1085 
1086 #ifdef DEBUG
1087 	for (i = 0; av[i] != NULL; i++)
1088 		note("exec(%s): av[%d] = %s", _exec_name(type), i, av[i]);
1089 	for (i = 0; envp[i] != NULL; i++)
1090 		note("exec(%s): envp[%d] = %s", _exec_name(type), i, envp[i]);
1091 #endif
1092 
1093 	execvpe(av[0], av, envp);
1094 	Done (EXEC_EXIT_NEXEC, errno);
1095 	/*NOTREACHED*/
1096 	return (0);
1097 }
1098 
1099 /**
1100  ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT
1101  **/
1102 
1103 static void
1104 addenv(char ***envp, char *name, char *value)
1105 {
1106 	register char *		cp;
1107 
1108 	if ((name == NULL) || (value == NULL))
1109 		return;
1110 
1111 	if ((cp = makestr(name, "=", value, (char *)0)))
1112 		addlist(envp, cp);
1113 	return;
1114 }
1115 
1116 /**
1117  ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT
1118  **/
1119 
1120 static int
1121 Fork1(EXEC *ep)
1122 {
1123 	int			pid;
1124 	int			fds[2];
1125 
1126 	if (pipe(fds) == -1) {
1127 		note("Failed to create pipe for child process (%s).\n", PERROR);
1128 		errno = EAGAIN ;
1129 		return(-1);
1130 	}
1131 
1132 	ep->md = mconnect((char *)0, fds[0], fds[1]);
1133 
1134 	switch (pid = fork()) {
1135 
1136 	case -1:
1137 		mdisconnect(ep->md);
1138 		close(fds[0]);
1139 		close(fds[1]);
1140 		ep->md = 0;
1141 		return (-1);
1142 
1143 	case 0:
1144 		ChildMd = mconnect(NULL, fds[1], fds[1]);
1145 		return (0);
1146 
1147 	default:
1148 		mlistenadd(ep->md, POLLIN);
1149 		return (pid);
1150 	}
1151 }
1152 
1153 /**
1154  ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT
1155  **/
1156 
1157 static void
1158 Fork2(void)
1159 {
1160 	switch ((ChildPid = fork())) {
1161 
1162 	case -1:
1163 		Done (EXEC_EXIT_NFORK, errno);
1164 		/*NOTREACHED*/
1165 
1166 	case 0:
1167 		return;
1168 
1169 	default:
1170 		/*
1171 		 * Delay calling "ignore_fault_signals()" as long
1172 		 * as possible, to give the child a chance to exec
1173 		 * the interface program and turn on traps.
1174 		 */
1175 
1176 		cool_heels ();
1177 		/*NOTREACHED*/
1178 
1179 	}
1180 }
1181 
1182 
1183 /**
1184  ** cool_heels() - WAIT FOR CHILD TO "DIE"
1185  **/
1186 
1187 static void
1188 cool_heels(void)
1189 {
1190 	int			status;
1191 
1192 	/*
1193 	 * At this point our only job is to wait for the child process.
1194 	 * If we hang out for a bit longer, that's okay.
1195 	 * By delaying before turning off the fault signals,
1196 	 * we increase the chance that the child process has completed
1197 	 * its exec and has turned on the fault traps. Nonetheless,
1198 	 * we can't guarantee a zero chance of missing a fault.
1199 	 * (We don't want to keep trapping the signals because the
1200 	 * interface program is likely to have a better way to handle
1201 	 * them; this process provides only rudimentary handling.)
1202 	 *
1203 	 * Note that on a very busy system, or with a very fast interface
1204 	 * program, the tables could be turned: Our sleep below (coupled
1205 	 * with a delay in the kernel scheduling us) may cause us to
1206 	 * detect the fault instead of the interface program.
1207 	 *
1208 	 * What we need is a way to synchronize with the child process.
1209 	 */
1210 	sleep (1);
1211 	ignore_fault_signals ();
1212 
1213 	WaitedChildPid = 0;
1214 	while ((WaitedChildPid = wait(&status)) != ChildPid)
1215 		;
1216 
1217 	if (
1218 		EXITED(status) > EXEC_EXIT_USER
1219 	     && EXITED(status) != EXEC_EXIT_FAULT
1220 	)
1221 		Done (EXEC_EXIT_EXIT, EXITED(status));
1222 
1223 	done (status, 0);	/* Don't use Done() */
1224 	/*NOTREACHED*/
1225 }
1226 
1227 
1228 /**
1229  ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT
1230  ** ignore_fault_signals() - IGNORE SAME
1231  **/
1232 
1233 static void
1234 trap_fault_signals(void)
1235 {
1236 	signal (SIGHUP, sigtrap);
1237 	signal (SIGINT, sigtrap);
1238 	signal (SIGQUIT, sigtrap);
1239 	signal (SIGPIPE, sigtrap);
1240 	return;
1241 }
1242 
1243 static void
1244 ignore_fault_signals(void)
1245 {
1246 	signal (SIGHUP, SIG_IGN);
1247 	signal (SIGINT, SIG_IGN);
1248 	signal (SIGQUIT, SIG_IGN);
1249 	signal (SIGPIPE, SIG_IGN);
1250 	return;
1251 }
1252 
1253 /**
1254  ** sigtrap() - TRAP VARIOUS SIGNALS
1255  **/
1256 
1257 static void
1258 sigtrap(int sig)
1259 {
1260 	signal (sig, SIG_IGN);
1261 	switch (sig) {
1262 
1263 	case SIGHUP:
1264 		Done (EXEC_EXIT_HUP, 0);
1265 		/*NOTREACHED*/
1266 
1267 	case SIGQUIT:
1268 	case SIGINT:
1269 		Done (EXEC_EXIT_INTR, 0);
1270 		/*NOTREACHED*/
1271 
1272 	case SIGPIPE:
1273 		Done (EXEC_EXIT_PIPE, 0);
1274 		/*NOTREACHED*/
1275 
1276 	case SIGTERM:
1277 		/*
1278 		 * If we were killed with SIGTERM, it should have been
1279 		 * via the Spooler who should have killed the entire
1280 		 * process group. We have to wait for the children,
1281 		 * since we're their parent, but WE MAY HAVE WAITED
1282 		 * FOR THEM ALREADY (in cool_heels()).
1283 		 */
1284 		if (ChildPid != WaitedChildPid) {
1285 			register int		cpid;
1286 
1287 			while (
1288 				(cpid = wait((int *)0)) != ChildPid
1289 			     && (cpid != -1 || errno != ECHILD)
1290 			)
1291 				;
1292 		}
1293 
1294 		/*
1295 		 * We can't rely on getting SIGTERM back in the wait()
1296 		 * above, because, for instance, some shells trap SIGTERM
1297 		 * and exit instead. Thus we force it.
1298 		 */
1299 		done (SIGTERM, 0);	/* Don't use Done() */
1300 		/*NOTREACHED*/
1301 	}
1302 }
1303 
1304 /**
1305  ** done() - TELL SPOOLER THIS CHILD IS DONE
1306  **/
1307 
1308 static void
1309 done(int status, int err)
1310 {
1311 	if (do_undial)
1312 		undial (1);
1313 
1314 	mputm (ChildMd, S_CHILD_DONE, key, status, err);
1315 	mdisconnect (ChildMd);
1316 
1317 	exit (0);
1318 	/*NOTREACHED*/
1319 }
1320 
1321 /**
1322  ** child_mallocfail()
1323  **/
1324 
1325 static void
1326 child_mallocfail(void)
1327 {
1328 	Done (EXEC_EXIT_NOMEM, ENOMEM);
1329 }
1330