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