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