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