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