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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <pwd.h> 32 #include <zone.h> 33 #if defined PS_FAULTED 34 #undef PS_FAULTED 35 #endif /* PS_FAULTED */ 36 #include <dial.h> 37 38 #include <stdlib.h> 39 #include "limits.h" 40 #include "stdarg.h" 41 #include "wait.h" 42 #include "dial.h" 43 #include "lpsched.h" 44 #include <syslog.h> 45 #include "tsol/label.h" 46 47 #define Done(EC,ERRNO) done(((EC) << 8),ERRNO) 48 49 #define STRLCAT(dst, src, size) \ 50 if (strlcat((dst), (src), (size)) >= (size)) { \ 51 errno = EINVAL; \ 52 return (-1); \ 53 } 54 55 static MESG * ChildMd; 56 57 static int ChildPid; 58 static int WaitedChildPid; 59 static int slot; 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 if (printer->status & PS_REMOTE) { 326 errno = EINVAL; 327 return (-1); 328 } 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 slot = ep - Exec_Table; 407 408 switch ((ep->pid = Fork1(ep))) { 409 410 case -1: 411 relock (); 412 return(-1); 413 414 case 0: 415 /* 416 * We want to be able to tell our parent how we died. 417 */ 418 lp_alloc_fail_handler = child_mallocfail; 419 break; 420 421 default: 422 switch(type) { 423 424 case EX_INTERF: 425 request->request->outcome |= RS_PRINTING; 426 break; 427 428 case EX_NOTIFY: 429 request->request->outcome |= RS_NOTIFYING; 430 break; 431 432 case EX_SLOWF: 433 request->request->outcome |= RS_FILTERING; 434 request->request->outcome &= ~RS_REFILTER; 435 break; 436 437 } 438 return(0); 439 440 } 441 442 for (i = 0; i < NSIG; i++) 443 (void)signal (i, SIG_DFL); 444 (void)signal (SIGALRM, SIG_IGN); 445 (void)signal (SIGTERM, sigtrap); 446 447 for (i = 0; i < OpenMax; i++) 448 if (i != ChildMd->writefd) 449 Close (i); 450 setpgrp(); 451 452 /* Set a default path */ 453 addenv (&envp, "PATH", "/usr/lib/lp/bin:/usr/bin:/bin:/usr/sbin:/sbin"); 454 /* copy locale related variables */ 455 addenv (&envp, "TZ", getenv("TZ")); 456 addenv (&envp, "LANG", getenv("LANG")); 457 addenv (&envp, "LC_ALL", getenv("LC_ALL")); 458 addenv (&envp, "LC_COLLATE", getenv("LC_COLLATE")); 459 addenv (&envp, "LC_CTYPE", getenv("LC_CTYPE")); 460 addenv (&envp, "LC_MESSAGES", getenv("LC_MESSAGES")); 461 addenv (&envp, "LC_MONETARY", getenv("LC_MONETARY")); 462 addenv (&envp, "LC_NUMERIC", getenv("LC_NUMERIC")); 463 addenv (&envp, "LC_TIME", getenv("LC_TIME")); 464 465 sprintf ((cp = BIGGEST_NUMBER_S), "%ld", key); 466 addenv (&envp, "SPOOLER_KEY", cp); 467 468 #if defined(DEBUG) 469 addenv (&envp, "LPDEBUG", (debug? "1" : "0")); 470 #endif 471 472 /* 473 * Open the standard input, standard output, and standard error. 474 */ 475 switch (type) { 476 477 case EX_SLOWF: 478 case EX_INTERF: 479 /* 480 * stdin: /dev/null 481 * stdout: /dev/null (EX_SLOWF), printer port (EX_INTERF) 482 * stderr: req# 483 */ 484 infile = 0; 485 outfile = 0; 486 errfile = makereqerr(request); 487 break; 488 489 case EX_NOTIFY: 490 /* 491 * stdin: req# 492 * stdout: /dev/null 493 * stderr: /dev/null 494 */ 495 infile = makereqerr(request); 496 outfile = 0; 497 errfile = 0; 498 499 break; 500 501 case EX_ALERT: 502 case EX_FALERT: 503 case EX_PALERT: 504 case EX_FAULT_MESSAGE: 505 case EX_FORM_MESSAGE: 506 /* 507 * stdin: /dev/null 508 * stdout: /dev/null 509 * stderr: /dev/null 510 */ 511 infile = 0; 512 outfile = 0; 513 errfile = 0; 514 break; 515 516 } 517 518 if (infile) { 519 if (Open(infile, O_RDONLY) == -1) 520 Done (EXEC_EXIT_NOPEN, errno); 521 } else { 522 if (Open("/dev/null", O_RDONLY) == -1) 523 Done (EXEC_EXIT_NOPEN, errno); 524 } 525 526 if (outfile) { 527 if (Open(outfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1) 528 Done (EXEC_EXIT_NOPEN, errno); 529 } else { 530 /* 531 * If EX_INTERF, this is still needed to cause the 532 * standard error channel to be #2. 533 */ 534 if (Open("/dev/null", O_WRONLY) == -1) 535 Done (EXEC_EXIT_NOPEN, errno); 536 } 537 538 if (errfile) { 539 if (Open(errfile, O_CREAT|O_TRUNC|O_WRONLY, 0600) == -1) 540 Done (EXEC_EXIT_NOPEN, errno); 541 } else { 542 if (Open("/dev/null", O_WRONLY) == -1) 543 Done (EXEC_EXIT_NOPEN, errno); 544 } 545 546 switch (type) { 547 548 case EX_INTERF: 549 /* 550 * Opening a ``port'' can be dangerous to our health: 551 * 552 * - Hangups can occur if the line is dropped. 553 * - The printer may send an interrupt. 554 * - A FIFO may be closed, generating SIGPIPE. 555 * 556 * We catch these so we can complain nicely. 557 */ 558 trap_fault_signals (); 559 560 (void)Close (1); 561 562 if (strchr (request->secure->user, '!')) 563 { 564 procuid = Lp_Uid; 565 procgid = Lp_Gid; 566 } 567 else 568 { 569 procuid = request->secure->uid; 570 procgid = request->secure->gid; 571 } 572 if (printer->printer->dial_info) 573 { 574 ret = open_dialup(request->printer_type, 575 printer->printer); 576 if (ret == 0) 577 do_undial = 1; 578 } 579 else 580 { 581 ret = open_direct(request->printer_type, 582 printer->printer); 583 do_undial = 0; 584 /* this is a URI */ 585 if (is_printer_uri(printer->printer->device) == 0) 586 addenv(&envp, "DEVICE_URI", 587 printer->printer->device); 588 } 589 addenv(&envp, "DEVICE_URI", 590 printer->printer->device); 591 if (ret != 0) 592 Done (ret, errno); 593 594 if (!(request->request->outcome & RS_FILTERED)) 595 file_list = request->request->file_list; 596 597 else { 598 register int count = 0; 599 register char * num = BIGGEST_REQID_S; 600 register char * prefix; 601 602 prefix = makestr( 603 Lp_Tmp, 604 "/", 605 (request->secure && request->secure->system ? 606 request->secure->system : Local_System), 607 "/F", 608 getreqno(request->secure->req_id), 609 "-", 610 (char *)0 611 ); 612 613 file_list = (char **)Malloc( 614 (lenlist(request->request->file_list) + 1) 615 * sizeof(char *) 616 ); 617 618 for ( 619 listp = request->request->file_list; 620 *listp; 621 listp++ 622 ) { 623 sprintf (num, "%d", count + 1); 624 file_list[count] = makestr( 625 prefix, 626 num, 627 (char *)0 628 ); 629 count++; 630 } 631 file_list[count] = 0; 632 } 633 634 #ifdef LP_USE_PAPI_ATTR 635 /* 636 * Check if the PAPI job attribute file exists, if it does 637 * pass the file's pathname to the printer interface script 638 * in an environment variable. This file is created when 639 * print jobs are submitted via the PAPI interface. 640 */ 641 snprintf(tmpName, sizeof (tmpName), "%s-%s", 642 getreqno(request->secure->req_id), LP_PAPIATTRNAME); 643 path = makepath(SPOOLDIR, "temp", tmpName, (char *)0); 644 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 645 { 646 /* 647 * IPP job attribute file exists for this job so 648 * set the environment variable 649 */ 650 syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path); 651 addenv(&envp, "ATTRPATH", path); 652 } 653 Free(path); 654 655 /* 656 * now set environment variable for the printer's PostScript 657 * Printer Description (PPD) file, this is used by the filter 658 * when forming the print data for this printer. 659 */ 660 if ((request->printer != NULL) && 661 (request->printer->printer != NULL) && 662 (request->printer->printer->name != NULL)) 663 { 664 snprintf(tmpName, sizeof (tmpName), "%s.ppd", 665 request->printer->printer->name); 666 path = makepath(ETCDIR, "ppd", tmpName, (char *)0); 667 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 668 { 669 syslog(LOG_DEBUG, 670 "exec(): Printer PPD='%s'", path); 671 addenv(&envp, "PPD", path); 672 } 673 Free(path); 674 } 675 #endif 676 677 if (request->printer_type) 678 addenv(&envp, "TERM", request->printer_type); 679 680 if (!(printer->printer->daisy)) { 681 register char * chset = 0; 682 register char * csp; 683 684 if ( 685 request->form 686 && request->form->form->chset 687 && request->form->form->mandatory 688 && !STREQU(NAME_ANY, request->form->form->chset) 689 ) 690 chset = request->form->form->chset; 691 692 else if ( 693 request->request->charset 694 && !STREQU(NAME_ANY, request->request->charset) 695 ) 696 chset = request->request->charset; 697 698 if (chset) { 699 csp = search_cslist( 700 chset, 701 printer->printer->char_sets 702 ); 703 704 /* 705 * The "strtok()" below wrecks the string 706 * for future use, but this is a child 707 * process where it won't be needed again. 708 */ 709 addenv (&envp, "CHARSET", 710 (csp? strtok(csp, "=") : chset) 711 ); 712 } 713 } 714 715 if (request->fast) 716 addenv(&envp, "FILTER", request->fast); 717 718 /* 719 */ 720 if (strcmp (request->secure->user, request->request->user)) 721 { 722 addenv (&envp, "ALIAS_USERNAME", 723 request->request->user); 724 } 725 /* 726 * Add the sensitivity label to the environment for 727 * banner page and header/footer processing 728 */ 729 730 if (is_system_labeled() && request->secure->slabel != NULL) 731 addenv(&envp, "SLABEL", request->secure->slabel); 732 733 /* 734 * Add the system name to the user name (ala system!user) 735 * unless it is already there. RFS users may have trouble 736 * here, sorry! 737 */ 738 cp = strchr(request->secure->user, '@'); 739 740 allTraysWithForm(printer, request->form); 741 742 /* 743 * Fix for 4137389 744 * Remove double quotes from title string. 745 */ 746 fr_flg = 1; 747 clean_title = strdup(NB(request->request->title)); 748 if (clean_title == NULL) { 749 /* 750 * strdup failed. We're probably hosed 751 * but try setting clean_title 752 * to original title and continuing. 753 */ 754 clean_title = NB(request->request->title); 755 fr_flg = 0; 756 } else if (strcmp(clean_title, "") != 0) { 757 char *ct_p; 758 759 for (ct_p = clean_title; *ct_p != NULL; ct_p++) { 760 if (*ct_p == '"') 761 *ct_p = ' '; 762 } 763 } 764 765 av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces, 766 printer->printer->name); 767 av[ac++] = arg_string(TRUSTED, "%s", request->secure->req_id); 768 av[ac++] = arg_string(UNTRUSTED, "%s%s%s", 769 request->secure->user, 770 (cp? "" : "@"), 771 (cp? "" : request->secure->system)); 772 av[ac++] = arg_string(TRUSTED, "%s", clean_title); 773 av[ac++] = arg_string(TRUSTED, "%d", request->copies); 774 775 if (fr_flg) 776 free (clean_title); 777 778 sep = ""; 779 780 /* 781 * Do the administrator defined key=value pair options 782 */ 783 784 argbuf[0] = '\0'; 785 786 if (printer->printer->options) { 787 char **tmp = printer->printer->options; 788 while(*tmp != NULL) { 789 STRLCAT(argbuf, sep, sizeof (argbuf)); 790 sep = " "; 791 STRLCAT(argbuf, *tmp++, sizeof (argbuf)); 792 } 793 } 794 795 /* 796 * Do the administrator defined ``stty'' stuff before 797 * the user's -o options, to allow the user to override. 798 */ 799 if (printer->printer->stty) { 800 STRLCAT (argbuf, sep, sizeof (argbuf)); 801 sep = " "; 802 STRLCAT (argbuf, "stty='", sizeof (argbuf)); 803 STRLCAT (argbuf, printer->printer->stty, 804 sizeof (argbuf)); 805 STRLCAT (argbuf, "'", sizeof (argbuf)); 806 } 807 808 /* 809 * Do all of the user's options except the cpi/lpi/etc. 810 * stuff, which is done separately. 811 */ 812 if (request->request->options) { 813 listp = dashos(request->request->options); 814 while (*listp) { 815 if ( 816 !STRNEQU(*listp, "cpi=", 4) 817 && !STRNEQU(*listp, "lpi=", 4) 818 && !STRNEQU(*listp, "width=", 6) 819 && !STRNEQU(*listp, "length=", 7) 820 ) { 821 STRLCAT (argbuf, sep, sizeof (argbuf)); 822 sep = " "; 823 STRLCAT (argbuf, *listp, 824 sizeof (argbuf)); 825 } 826 listp++; 827 } 828 } 829 830 /* 831 * The "pickfilter()" routine (from "validate()") 832 * stored the cpi/lpi/etc. stuff that should be 833 * used for this request. It chose form over user, 834 * and user over printer. 835 */ 836 if (request->cpi) { 837 STRLCAT (argbuf, sep, sizeof (argbuf)); 838 sep = " "; 839 STRLCAT (argbuf, "cpi=", sizeof (argbuf)); 840 STRLCAT (argbuf, request->cpi, sizeof (argbuf)); 841 } 842 if (request->lpi) { 843 STRLCAT (argbuf, sep, sizeof (argbuf)); 844 sep = " "; 845 STRLCAT (argbuf, "lpi=", sizeof (argbuf)); 846 STRLCAT (argbuf, request->lpi, sizeof (argbuf)); 847 } 848 if (request->pwid) { 849 STRLCAT (argbuf, sep, sizeof (argbuf)); 850 sep = " "; 851 STRLCAT (argbuf, "width=", sizeof (argbuf)); 852 STRLCAT (argbuf, request->pwid, sizeof (argbuf)); 853 } 854 if (request->plen) { 855 STRLCAT (argbuf, sep, sizeof (argbuf)); 856 sep = " "; 857 STRLCAT (argbuf, "length=", sizeof (argbuf)); 858 STRLCAT (argbuf, request->plen, sizeof (argbuf)); 859 } 860 861 /* 862 * Do the ``raw'' bit last, to ensure it gets 863 * done. If the user doesn't want this, then he or 864 * she can do the correct thing using -o stty= 865 * and leaving out the -r option. 866 */ 867 if (request->request->actions & ACT_RAW) { 868 STRLCAT (argbuf, sep, sizeof (argbuf)); 869 sep = " "; 870 STRLCAT (argbuf, "stty=-opost", sizeof (argbuf)); 871 } 872 873 874 /* the "options" */ 875 av[ac++] = arg_string(UNTRUSTED, "%s", argbuf); 876 877 for (listp = file_list; *listp; listp++) 878 av[ac++] = arg_string(TRUSTED, "%s", *listp); 879 880 (void)chfiles (file_list, procuid, procgid); 881 882 break; 883 884 885 case EX_SLOWF: 886 if (request->slow) 887 addenv(&envp, "FILTER", request->slow); 888 889 if (strchr (request->secure->user, '!')) 890 { 891 procuid = Lp_Uid; 892 procgid = Lp_Gid; 893 } 894 else 895 { 896 procuid = request->secure->uid; 897 procgid = request->secure->gid; 898 } 899 cp = _alloc_files( 900 lenlist(request->request->file_list), 901 getreqno(request->secure->req_id), 902 procuid, 903 procgid, 904 (request->secure && request->secure->system ? 905 request->secure->system : NULL ) 906 ); 907 908 av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter); 909 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_Tmp, 910 (request->secure && request->secure->system ? 911 request->secure->system : Local_System), 912 cp); 913 for (listp = request->request->file_list; *listp; listp++) 914 av[ac++] = arg_string(TRUSTED, "%s", *listp); 915 916 (void)chfiles (request->request->file_list, procuid, procgid); 917 918 #ifdef LP_USE_PAPI_ATTR 919 /* 920 * Check if the PAPI job attribute file exists, if it does 921 * pass the file's pathname to the slow-filters in an 922 * environment variable. Note: this file is created when 923 * print jobs are submitted via the PAPI interface. 924 */ 925 snprintf(tmpName, sizeof (tmpName), "%s-%s", 926 getreqno(request->secure->req_id), LP_PAPIATTRNAME); 927 path = makepath(SPOOLDIR, "temp", tmpName, (char *)0); 928 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 929 { 930 /* 931 * IPP job attribute file exists for this job so 932 * set the environment variable 933 */ 934 syslog(LOG_DEBUG, "exec(): ATTRPATH='%s'", path); 935 addenv(&envp, "ATTRPATH", path); 936 } 937 Free(path); 938 939 940 /* 941 * now set environment variable for the printer's PostScript 942 * Printer Description (PPD) file, this is used by the filter 943 * when forming the print data for this printer. 944 */ 945 if ((request->printer != NULL) && 946 (request->printer->printer != NULL) && 947 (request->printer->printer->name != NULL)) 948 { 949 snprintf(tmpName, sizeof (tmpName), "%s.ppd", 950 request->printer->printer->name); 951 path = makepath(ETCDIR, "ppd", tmpName, (char *)0); 952 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 953 { 954 syslog(LOG_DEBUG, 955 "exec(): Printer PPD='%s'", path); 956 addenv(&envp, "PPD", path); 957 } 958 Free(path); 959 } 960 #endif 961 break; 962 963 case EX_ALERT: 964 procuid = Lp_Uid; 965 procgid = Lp_Gid; 966 (void)Chown (printer->alert->msgfile, procuid, procgid); 967 968 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers, 969 printer->printer->name, ALERTSHFILE); 970 av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); 971 972 break; 973 974 case EX_PALERT: 975 procuid = Lp_Uid; 976 procgid = Lp_Gid; 977 (void)Chown (pwheel->alert->msgfile, procuid, procgid); 978 979 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels, 980 pwheel->pwheel->name, ALERTSHFILE); 981 av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); 982 983 break; 984 985 case EX_FALERT: 986 procuid = Lp_Uid; 987 procgid = Lp_Gid; 988 (void)Chown (form->alert->msgfile, procuid, procgid); 989 990 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms, 991 form->form->name, ALERTSHFILE); 992 av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); 993 994 break; 995 996 case EX_FORM_MESSAGE: 997 procuid = Lp_Uid; 998 procgid = Lp_Gid; 999 1000 av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults); 1001 av[ac++] = arg_string(TRUSTED, "%s", form->form->name); 1002 av[ac++] = arg_string(TRUSTED, "%s", time_buf); 1003 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms, 1004 form->form->name, FORMMESSAGEFILE); 1005 1006 break; 1007 1008 case EX_FAULT_MESSAGE: 1009 procuid = Lp_Uid; 1010 procgid = Lp_Gid; 1011 1012 av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults); 1013 av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow); 1014 av[ac++] = arg_string(TRUSTED, "%s", time_buf); 1015 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers, 1016 printerName, FAULTMESSAGEFILE); 1017 1018 break; 1019 1020 case EX_NOTIFY: 1021 if (request->request->alert) { 1022 if (strchr(request->secure->user, '!')) { 1023 procuid = Lp_Uid; 1024 procgid = Lp_Gid; 1025 } else { 1026 procuid = request->secure->uid; 1027 procgid = request->secure->gid; 1028 } 1029 av[ac++] = arg_string(TRUSTED, "%s", 1030 request->request->alert); 1031 } else { 1032 char *user = strdup(request->secure->user); 1033 clean_string(user); 1034 slabel = request->secure->slabel; 1035 1036 if ((request->request->actions & ACT_WRITE) && 1037 (!request->secure->system || 1038 STREQU(request->secure->system, Local_System))) { 1039 av[ac++] = arg_string(TRUSTED, "%s", BINWRITE); 1040 snprintf(argbuf, sizeof (argbuf), 1041 "%s %s || %s %s", 1042 BINWRITE, user, 1043 BINMAIL, user 1044 ); 1045 av[ac++] = arg_string(TRUSTED, "/bin/sh"); 1046 av[ac++] = arg_string(TRUSTED, "-c"); 1047 av[ac++] = arg_string(TRUSTED, "%s", argbuf); 1048 } else if ((getzoneid() == GLOBAL_ZONEID) && 1049 is_system_labeled() && (slabel != NULL)) { 1050 /* 1051 * If in the global zone and the system is 1052 * labeled, mail is handled via a local 1053 * labeled zone that is the same label as 1054 * the request. 1055 */ 1056 if ((mail_zonename = 1057 get_labeled_zonename(slabel)) == 1058 (char *)-1) { 1059 /* 1060 * Cannot find labeled zone, just 1061 * return 0. 1062 */ 1063 return(0); 1064 } 1065 } 1066 if (mail_zonename == NULL) { 1067 procuid = Lp_Uid; 1068 procgid = Lp_Gid; 1069 av[ac++] = arg_string(TRUSTED, "%s", BINMAIL); 1070 av[ac++] = arg_string(UNTRUSTED, "%s", user); 1071 } else { 1072 procuid = getuid(); 1073 procgid = getgid(); 1074 av[ac++] = arg_string(TRUSTED, "%s", 1075 "/usr/sbin/zlogin"); 1076 av[ac++] = arg_string(TRUSTED, "%s", 1077 mail_zonename); 1078 av[ac++] = arg_string(TRUSTED, "%s", 1079 BINMAIL); 1080 av[ac++] = arg_string(UNTRUSTED, "%s", 1081 user); 1082 Free(mail_zonename); 1083 } 1084 1085 free(user); 1086 } 1087 break; 1088 } 1089 1090 av[ac++] = NULL; 1091 1092 Fork2 (); 1093 /* only the child returns */ 1094 1095 /* 1096 * Correctly set up the supplemental group list 1097 * for proper file access (before execl the interface program) 1098 */ 1099 1100 pwp = getpwuid(procuid); 1101 if (pwp == NULL) { 1102 note("getpwuid(%d) call failed\n", procuid); 1103 } else if (initgroups(pwp->pw_name, procgid) < 0) { 1104 note("initgroups() call failed %d\n", errno); 1105 } 1106 1107 setgid (procgid); 1108 setuid (procuid); 1109 1110 /* 1111 * The shell doesn't allow the "trap" builtin to set a trap 1112 * for a signal ignored when the shell is started. Thus, don't 1113 * turn off signals in the last child! 1114 */ 1115 1116 for (i = 0; av[i] != NULL; i++) 1117 syslog(LOG_DEBUG, "exec: av[%d] = %s", i, av[i]); 1118 for (i = 0; envp[i] != NULL; i++) 1119 syslog(LOG_DEBUG, "exec: envp[%d] = %s", i, envp[i]); 1120 1121 execvpe(av[0], av, envp); 1122 Done (EXEC_EXIT_NEXEC, errno); 1123 /*NOTREACHED*/ 1124 return (0); 1125 } 1126 1127 /** 1128 ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT 1129 **/ 1130 1131 static void 1132 addenv(char ***envp, char *name, char *value) 1133 { 1134 register char * cp; 1135 1136 if ((name == NULL) || (value == NULL)) 1137 return; 1138 1139 if ((cp = makestr(name, "=", value, (char *)0))) 1140 addlist(envp, cp); 1141 return; 1142 } 1143 1144 /** 1145 ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT 1146 **/ 1147 1148 static int 1149 Fork1(EXEC *ep) 1150 { 1151 int pid; 1152 int fds[2]; 1153 1154 if (pipe(fds) == -1) { 1155 note("Failed to create pipe for child process (%s).\n", PERROR); 1156 errno = EAGAIN ; 1157 return(-1); 1158 } 1159 1160 ep->md = mconnect((char *)0, fds[0], fds[1]); 1161 1162 switch (pid = fork()) { 1163 1164 case -1: 1165 mdisconnect(ep->md); 1166 close(fds[0]); 1167 close(fds[1]); 1168 ep->md = 0; 1169 return (-1); 1170 1171 case 0: 1172 ChildMd = mconnect(NULL, fds[1], fds[1]); 1173 return (0); 1174 1175 default: 1176 mlistenadd(ep->md, POLLIN); 1177 return (pid); 1178 } 1179 } 1180 1181 /** 1182 ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT 1183 **/ 1184 1185 static void 1186 Fork2(void) 1187 { 1188 switch ((ChildPid = fork())) { 1189 1190 case -1: 1191 Done (EXEC_EXIT_NFORK, errno); 1192 /*NOTREACHED*/ 1193 1194 case 0: 1195 return; 1196 1197 default: 1198 /* 1199 * Delay calling "ignore_fault_signals()" as long 1200 * as possible, to give the child a chance to exec 1201 * the interface program and turn on traps. 1202 */ 1203 1204 cool_heels (); 1205 /*NOTREACHED*/ 1206 1207 } 1208 } 1209 1210 1211 /** 1212 ** cool_heels() - WAIT FOR CHILD TO "DIE" 1213 **/ 1214 1215 static void 1216 cool_heels(void) 1217 { 1218 int status; 1219 1220 /* 1221 * At this point our only job is to wait for the child process. 1222 * If we hang out for a bit longer, that's okay. 1223 * By delaying before turning off the fault signals, 1224 * we increase the chance that the child process has completed 1225 * its exec and has turned on the fault traps. Nonetheless, 1226 * we can't guarantee a zero chance of missing a fault. 1227 * (We don't want to keep trapping the signals because the 1228 * interface program is likely to have a better way to handle 1229 * them; this process provides only rudimentary handling.) 1230 * 1231 * Note that on a very busy system, or with a very fast interface 1232 * program, the tables could be turned: Our sleep below (coupled 1233 * with a delay in the kernel scheduling us) may cause us to 1234 * detect the fault instead of the interface program. 1235 * 1236 * What we need is a way to synchronize with the child process. 1237 */ 1238 sleep (1); 1239 ignore_fault_signals (); 1240 1241 WaitedChildPid = 0; 1242 while ((WaitedChildPid = wait(&status)) != ChildPid) 1243 ; 1244 1245 if ( 1246 EXITED(status) > EXEC_EXIT_USER 1247 && EXITED(status) != EXEC_EXIT_FAULT 1248 ) 1249 Done (EXEC_EXIT_EXIT, EXITED(status)); 1250 1251 done (status, 0); /* Don't use Done() */ 1252 /*NOTREACHED*/ 1253 } 1254 1255 1256 /** 1257 ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT 1258 ** ignore_fault_signals() - IGNORE SAME 1259 **/ 1260 1261 static void 1262 trap_fault_signals(void) 1263 { 1264 signal (SIGHUP, sigtrap); 1265 signal (SIGINT, sigtrap); 1266 signal (SIGQUIT, sigtrap); 1267 signal (SIGPIPE, sigtrap); 1268 return; 1269 } 1270 1271 static void 1272 ignore_fault_signals(void) 1273 { 1274 signal (SIGHUP, SIG_IGN); 1275 signal (SIGINT, SIG_IGN); 1276 signal (SIGQUIT, SIG_IGN); 1277 signal (SIGPIPE, SIG_IGN); 1278 return; 1279 } 1280 1281 /** 1282 ** sigtrap() - TRAP VARIOUS SIGNALS 1283 **/ 1284 1285 static void 1286 sigtrap(int sig) 1287 { 1288 signal (sig, SIG_IGN); 1289 switch (sig) { 1290 1291 case SIGHUP: 1292 Done (EXEC_EXIT_HUP, 0); 1293 /*NOTREACHED*/ 1294 1295 case SIGQUIT: 1296 case SIGINT: 1297 Done (EXEC_EXIT_INTR, 0); 1298 /*NOTREACHED*/ 1299 1300 case SIGPIPE: 1301 Done (EXEC_EXIT_PIPE, 0); 1302 /*NOTREACHED*/ 1303 1304 case SIGTERM: 1305 /* 1306 * If we were killed with SIGTERM, it should have been 1307 * via the Spooler who should have killed the entire 1308 * process group. We have to wait for the children, 1309 * since we're their parent, but WE MAY HAVE WAITED 1310 * FOR THEM ALREADY (in cool_heels()). 1311 */ 1312 if (ChildPid != WaitedChildPid) { 1313 register int cpid; 1314 1315 while ( 1316 (cpid = wait((int *)0)) != ChildPid 1317 && (cpid != -1 || errno != ECHILD) 1318 ) 1319 ; 1320 } 1321 1322 /* 1323 * We can't rely on getting SIGTERM back in the wait() 1324 * above, because, for instance, some shells trap SIGTERM 1325 * and exit instead. Thus we force it. 1326 */ 1327 done (SIGTERM, 0); /* Don't use Done() */ 1328 /*NOTREACHED*/ 1329 } 1330 } 1331 1332 /** 1333 ** done() - TELL SPOOLER THIS CHILD IS DONE 1334 **/ 1335 1336 static void 1337 done(int status, int err) 1338 { 1339 if (do_undial) 1340 undial (1); 1341 1342 mputm (ChildMd, S_CHILD_DONE, key, slot, status, err); 1343 mdisconnect (ChildMd); 1344 1345 exit (0); 1346 /*NOTREACHED*/ 1347 } 1348 1349 /** 1350 ** child_mallocfail() 1351 **/ 1352 1353 static void 1354 child_mallocfail(void) 1355 { 1356 Done (EXEC_EXIT_NOMEM, ENOMEM); 1357 } 1358