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