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