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