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 if (strchr (request->request->user, '@')) 565 { 566 procuid = Lp_Uid; 567 procgid = Lp_Gid; 568 } 569 else 570 { 571 procuid = request->secure->uid; 572 procgid = request->secure->gid; 573 } 574 if (printer->printer->dial_info) 575 { 576 ret = open_dialup(request->printer_type, 577 printer->printer); 578 if (ret == 0) 579 do_undial = 1; 580 } 581 else 582 { 583 ret = open_direct(request->printer_type, 584 printer->printer); 585 do_undial = 0; 586 /* this is a URI */ 587 if (is_printer_uri(printer->printer->device) == 0) 588 addenv(&envp, "DEVICE_URI", 589 printer->printer->device); 590 } 591 addenv(&envp, "DEVICE_URI", 592 printer->printer->device); 593 if (ret != 0) 594 Done (ret, errno); 595 596 if (!(request->request->outcome & RS_FILTERED)) 597 file_list = request->request->file_list; 598 599 else { 600 register int count = 0; 601 register char * num = BIGGEST_REQID_S; 602 register char * prefix; 603 604 prefix = makestr( 605 Lp_Temp, 606 "/F", 607 getreqno(request->secure->req_id), 608 "-", 609 (char *)0 610 ); 611 612 file_list = (char **)Malloc( 613 (lenlist(request->request->file_list) + 1) 614 * sizeof(char *) 615 ); 616 617 for ( 618 listp = request->request->file_list; 619 *listp; 620 listp++ 621 ) { 622 sprintf (num, "%d", count + 1); 623 file_list[count] = makestr( 624 prefix, 625 num, 626 (char *)0 627 ); 628 count++; 629 } 630 file_list[count] = 0; 631 } 632 633 #ifdef LP_USE_PAPI_ATTR 634 /* 635 * Check if the PAPI job attribute file exists, if it does 636 * pass the file's pathname to the printer interface script 637 * in an environment variable. This file is created when 638 * print jobs are submitted via the PAPI interface. 639 */ 640 snprintf(tmpName, sizeof (tmpName), "%s-%s", 641 getreqno(request->secure->req_id), LP_PAPIATTRNAME); 642 path = makepath(Lp_Temp, tmpName, (char *)0); 643 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 644 { 645 /* 646 * IPP job attribute file exists for this job so 647 * set the environment variable 648 */ 649 addenv(&envp, "ATTRPATH", path); 650 } 651 Free(path); 652 653 /* 654 * now set environment variable for the printer's PostScript 655 * Printer Description (PPD) file, this is used by the filter 656 * when forming the print data for this printer. 657 */ 658 if ((request->printer != NULL) && 659 (request->printer->printer != NULL) && 660 (request->printer->printer->name != NULL)) 661 { 662 snprintf(tmpName, sizeof (tmpName), "%s.ppd", 663 request->printer->printer->name); 664 path = makepath(ETCDIR, "ppd", tmpName, (char *)0); 665 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 666 { 667 addenv(&envp, "PPD", path); 668 } 669 Free(path); 670 } 671 #endif 672 673 if (request->printer_type) 674 addenv(&envp, "TERM", request->printer_type); 675 676 if (!(printer->printer->daisy)) { 677 register char * chset = 0; 678 register char * csp; 679 680 if ( 681 request->form 682 && request->form->form->chset 683 && request->form->form->mandatory 684 && !STREQU(NAME_ANY, request->form->form->chset) 685 ) 686 chset = request->form->form->chset; 687 688 else if ( 689 request->request->charset 690 && !STREQU(NAME_ANY, request->request->charset) 691 ) 692 chset = request->request->charset; 693 694 if (chset) { 695 csp = search_cslist( 696 chset, 697 printer->printer->char_sets 698 ); 699 700 /* 701 * The "strtok()" below wrecks the string 702 * for future use, but this is a child 703 * process where it won't be needed again. 704 */ 705 addenv (&envp, "CHARSET", 706 (csp? strtok(csp, "=") : chset) 707 ); 708 } 709 } 710 711 if (request->fast) 712 addenv(&envp, "FILTER", request->fast); 713 714 /* 715 * Add the sensitivity label to the environment for 716 * banner page and header/footer processing 717 */ 718 719 if (is_system_labeled() && request->secure->slabel != NULL) 720 addenv(&envp, "SLABEL", request->secure->slabel); 721 722 /* 723 * Add the system name to the user name (ala system!user) 724 * unless it is already there. RFS users may have trouble 725 * here, sorry! 726 */ 727 cp = strchr(request->secure->user, '@'); 728 729 allTraysWithForm(printer, request->form); 730 731 /* 732 * Fix for 4137389 733 * Remove double quotes from title string. 734 */ 735 fr_flg = 1; 736 clean_title = strdup(NB(request->request->title)); 737 if (clean_title == NULL) { 738 /* 739 * strdup failed. We're probably hosed 740 * but try setting clean_title 741 * to original title and continuing. 742 */ 743 clean_title = NB(request->request->title); 744 fr_flg = 0; 745 } else if (strcmp(clean_title, "") != 0) { 746 char *ct_p; 747 748 for (ct_p = clean_title; *ct_p != NULL; ct_p++) { 749 if (*ct_p == '"') 750 *ct_p = ' '; 751 } 752 } 753 754 av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_A_Interfaces, 755 printer->printer->name); 756 /* 757 * Read the options field of the request 758 * In case of remote lpd request 759 * the options field will have 760 * job-id-requested. This is the 761 * id sent by the client 762 */ 763 if (request->request->options != NULL) { 764 char *options = NULL, *temp = NULL; 765 options = temp = strdup(request->request->options); 766 767 /* 768 * Search for job-id-requested in 769 * options string 770 */ 771 options = strstr(options, "job-id-requested"); 772 if (options != NULL) { 773 /* 774 * Extract the ridno from the string 775 * job-id-requested=xxx 776 * In this case ridno = xxx 777 */ 778 if (STRNEQU(options, "job-id-requested=", 17)) { 779 ridno = strdup(options + 17); 780 if (ridno != NULL) 781 /* 782 * Read job-id-requested 783 * successfully 784 */ 785 setid = 0; 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 * could not read 796 * ridno from the string 797 * job-id-requested=xxx 798 */ 799 setid = 1; 800 } else 801 /* 802 * No job-id-requested in 803 * request options 804 */ 805 setid = 1; 806 807 if (temp != NULL) 808 free(temp); 809 810 } else 811 /* 812 * options field in request structure 813 * not set 814 */ 815 setid = 1; 816 817 818 /* 819 * setid = 1 means the job-id-requested attribute 820 * is not set so read the request->secure->req_id 821 */ 822 if (setid) 823 av[ac++] = arg_string(TRUSTED, "%s", 824 request->secure->req_id); 825 else { 826 /* 827 * From request->secure->req_id extract the 828 * printer-name. 829 * request->secure->req_id = <printer-name>-<req_id> 830 * The final req-id will be 831 * <printer-name>-<ridno> 832 */ 833 char *r1 = NULL, *r2 = NULL, *tmp = NULL; 834 r1 = r2 = tmp = strdup(request->secure->req_id); 835 r2 = strrchr(r1, '-'); 836 if (r2 != NULL) { 837 char *r3 = NULL; 838 int lr1 = strlen(r1); 839 int lr2 = strlen(r2); 840 r1[lr1 - lr2 + 1] = '\0'; 841 842 /* 843 * Now r1 = <printer-name>- 844 */ 845 lr1 = strlen(r1); 846 lr2 = strlen(ridno); 847 848 r3 = (char *)malloc(lr1+lr2+1); 849 if (r3 != NULL) { 850 strcpy(r3, r1); 851 strcat(r3, ridno); 852 /* 853 * Here r3 = <printer-name>-<ridno> 854 */ 855 av[ac++] = arg_string(TRUSTED, 856 "%s", r3); 857 free(r3); 858 } else 859 av[ac++] = arg_string(TRUSTED, "%s", 860 request->secure->req_id); 861 862 } else 863 av[ac++] = arg_string(TRUSTED, "%s", 864 request->secure->req_id); 865 866 if (tmp != NULL) 867 free(tmp); 868 869 if (ridno != NULL) 870 free(ridno); 871 } 872 873 av[ac++] = arg_string(UNTRUSTED, "%s", request->request->user); 874 av[ac++] = arg_string(TRUSTED, "%s", clean_title); 875 av[ac++] = arg_string(TRUSTED, "%d", request->copies); 876 877 if (fr_flg) 878 free (clean_title); 879 880 sep = ""; 881 882 /* 883 * Do the administrator defined key=value pair options 884 */ 885 886 argbuf[0] = '\0'; 887 888 if (printer->printer->options) { 889 char **tmp = printer->printer->options; 890 while(*tmp != NULL) { 891 STRLCAT(argbuf, sep, sizeof (argbuf)); 892 sep = " "; 893 STRLCAT(argbuf, *tmp++, sizeof (argbuf)); 894 } 895 } 896 897 /* 898 * Do the administrator defined ``stty'' stuff before 899 * the user's -o options, to allow the user to override. 900 */ 901 if (printer->printer->stty) { 902 STRLCAT (argbuf, sep, sizeof (argbuf)); 903 sep = " "; 904 STRLCAT (argbuf, "stty='", sizeof (argbuf)); 905 STRLCAT (argbuf, printer->printer->stty, 906 sizeof (argbuf)); 907 STRLCAT (argbuf, "'", sizeof (argbuf)); 908 } 909 910 /* 911 * Do all of the user's options except the cpi/lpi/etc. 912 * stuff, which is done separately. 913 */ 914 if (request->request->options) { 915 listp = dashos(request->request->options); 916 while (*listp) { 917 if ( 918 !STRNEQU(*listp, "cpi=", 4) 919 && !STRNEQU(*listp, "lpi=", 4) 920 && !STRNEQU(*listp, "width=", 6) 921 && !STRNEQU(*listp, "length=", 7) 922 ) { 923 STRLCAT (argbuf, sep, sizeof (argbuf)); 924 sep = " "; 925 STRLCAT (argbuf, *listp, 926 sizeof (argbuf)); 927 } 928 listp++; 929 } 930 } 931 932 /* 933 * The "pickfilter()" routine (from "validate()") 934 * stored the cpi/lpi/etc. stuff that should be 935 * used for this request. It chose form over user, 936 * and user over printer. 937 */ 938 if (request->cpi) { 939 STRLCAT (argbuf, sep, sizeof (argbuf)); 940 sep = " "; 941 STRLCAT (argbuf, "cpi=", sizeof (argbuf)); 942 STRLCAT (argbuf, request->cpi, sizeof (argbuf)); 943 } 944 if (request->lpi) { 945 STRLCAT (argbuf, sep, sizeof (argbuf)); 946 sep = " "; 947 STRLCAT (argbuf, "lpi=", sizeof (argbuf)); 948 STRLCAT (argbuf, request->lpi, sizeof (argbuf)); 949 } 950 if (request->pwid) { 951 STRLCAT (argbuf, sep, sizeof (argbuf)); 952 sep = " "; 953 STRLCAT (argbuf, "width=", sizeof (argbuf)); 954 STRLCAT (argbuf, request->pwid, sizeof (argbuf)); 955 } 956 if (request->plen) { 957 STRLCAT (argbuf, sep, sizeof (argbuf)); 958 sep = " "; 959 STRLCAT (argbuf, "length=", sizeof (argbuf)); 960 STRLCAT (argbuf, request->plen, sizeof (argbuf)); 961 } 962 963 /* 964 * Do the ``raw'' bit last, to ensure it gets 965 * done. If the user doesn't want this, then he or 966 * she can do the correct thing using -o stty= 967 * and leaving out the -r option. 968 */ 969 if (request->request->actions & ACT_RAW) { 970 STRLCAT (argbuf, sep, sizeof (argbuf)); 971 sep = " "; 972 STRLCAT (argbuf, "stty=-opost", sizeof (argbuf)); 973 } 974 975 976 /* the "options" */ 977 av[ac++] = arg_string(UNTRUSTED, "%s", argbuf); 978 979 for (listp = file_list; *listp; listp++) 980 av[ac++] = arg_string(TRUSTED, "%s", *listp); 981 982 (void)chfiles (file_list, procuid, procgid); 983 984 break; 985 986 987 case EX_SLOWF: 988 if (request->slow) 989 addenv(&envp, "FILTER", request->slow); 990 991 if (strchr (request->request->user, '@')) 992 { 993 procuid = Lp_Uid; 994 procgid = Lp_Gid; 995 } 996 else 997 { 998 procuid = request->secure->uid; 999 procgid = request->secure->gid; 1000 } 1001 cp = _alloc_files( 1002 lenlist(request->request->file_list), 1003 getreqno(request->secure->req_id), 1004 procuid, procgid); 1005 1006 av[ac++] = arg_string(TRUSTED, "%s", Lp_Slow_Filter); 1007 av[ac++] = arg_string(TRUSTED, "%s/%s", Lp_Temp, cp); 1008 for (listp = request->request->file_list; *listp; listp++) 1009 av[ac++] = arg_string(TRUSTED, "%s", *listp); 1010 1011 (void)chfiles (request->request->file_list, procuid, procgid); 1012 1013 #ifdef LP_USE_PAPI_ATTR 1014 /* 1015 * Check if the PAPI job attribute file exists, if it does 1016 * pass the file's pathname to the slow-filters in an 1017 * environment variable. Note: this file is created when 1018 * print jobs are submitted via the PAPI interface. 1019 */ 1020 snprintf(tmpName, sizeof (tmpName), "%s-%s", 1021 getreqno(request->secure->req_id), LP_PAPIATTRNAME); 1022 path = makepath(Lp_Temp, tmpName, (char *)0); 1023 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 1024 { 1025 /* 1026 * IPP job attribute file exists for this job so 1027 * set the environment variable 1028 */ 1029 addenv(&envp, "ATTRPATH", path); 1030 } 1031 Free(path); 1032 1033 1034 /* 1035 * now set environment variable for the printer's PostScript 1036 * Printer Description (PPD) file, this is used by the filter 1037 * when forming the print data for this printer. 1038 */ 1039 if ((request->printer != NULL) && 1040 (request->printer->printer != NULL) && 1041 (request->printer->printer->name != NULL)) 1042 { 1043 snprintf(tmpName, sizeof (tmpName), "%s.ppd", 1044 request->printer->printer->name); 1045 path = makepath(ETCDIR, "ppd", tmpName, (char *)0); 1046 if ((path != NULL) && (stat(path, &tmpBuf) == 0)) 1047 { 1048 addenv(&envp, "PPD", path); 1049 } 1050 Free(path); 1051 } 1052 #endif 1053 break; 1054 1055 case EX_ALERT: 1056 procuid = Lp_Uid; 1057 procgid = Lp_Gid; 1058 (void)Chown (printer->alert->msgfile, procuid, procgid); 1059 1060 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers, 1061 printer->printer->name, ALERTSHFILE); 1062 av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); 1063 1064 break; 1065 1066 case EX_PALERT: 1067 procuid = Lp_Uid; 1068 procgid = Lp_Gid; 1069 (void)Chown (pwheel->alert->msgfile, procuid, procgid); 1070 1071 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_PrintWheels, 1072 pwheel->pwheel->name, ALERTSHFILE); 1073 av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); 1074 1075 break; 1076 1077 case EX_FALERT: 1078 procuid = Lp_Uid; 1079 procgid = Lp_Gid; 1080 (void)Chown (form->alert->msgfile, procuid, procgid); 1081 1082 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms, 1083 form->form->name, ALERTSHFILE); 1084 av[ac++] = arg_string(TRUSTED, "%s", printer->alert->msgfile); 1085 1086 break; 1087 1088 case EX_FORM_MESSAGE: 1089 procuid = Lp_Uid; 1090 procgid = Lp_Gid; 1091 1092 av[ac++] = arg_string(TRUSTED, "%s/form", Lp_A_Faults); 1093 av[ac++] = arg_string(TRUSTED, "%s", form->form->name); 1094 av[ac++] = arg_string(TRUSTED, "%s", time_buf); 1095 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Forms, 1096 form->form->name, FORMMESSAGEFILE); 1097 1098 break; 1099 1100 case EX_FAULT_MESSAGE: 1101 procuid = Lp_Uid; 1102 procgid = Lp_Gid; 1103 1104 av[ac++] = arg_string(TRUSTED, "%s/printer", Lp_A_Faults); 1105 av[ac++] = arg_string(TRUSTED, "%s", printerNameToShow); 1106 av[ac++] = arg_string(TRUSTED, "%s", time_buf); 1107 av[ac++] = arg_string(TRUSTED, "%s/%s/%s", Lp_A_Printers, 1108 printerName, FAULTMESSAGEFILE); 1109 1110 break; 1111 1112 case EX_NOTIFY: 1113 if (request->request->alert) { 1114 if (strchr(request->request->user, '@')) { 1115 procuid = Lp_Uid; 1116 procgid = Lp_Gid; 1117 } else { 1118 procuid = request->secure->uid; 1119 procgid = request->secure->gid; 1120 } 1121 av[ac++] = arg_string(TRUSTED, "%s", 1122 request->request->alert); 1123 } else { 1124 char *user = strdup(request->request->user); 1125 clean_string(user); 1126 slabel = request->secure->slabel; 1127 1128 if (request->request->actions & ACT_WRITE) { 1129 av[ac++] = arg_string(TRUSTED, "%s", BINWRITE); 1130 snprintf(argbuf, sizeof (argbuf), 1131 "%s %s || %s %s", 1132 BINWRITE, user, 1133 BINMAIL, user 1134 ); 1135 av[ac++] = arg_string(TRUSTED, "/bin/sh"); 1136 av[ac++] = arg_string(TRUSTED, "-c"); 1137 av[ac++] = arg_string(TRUSTED, "%s", argbuf); 1138 } else if ((getzoneid() == GLOBAL_ZONEID) && 1139 is_system_labeled() && (slabel != NULL)) { 1140 /* 1141 * If in the global zone and the system is 1142 * labeled, mail is handled via a local 1143 * labeled zone that is the same label as 1144 * the request. 1145 */ 1146 if ((mail_zonename = 1147 get_labeled_zonename(slabel)) == 1148 (char *)-1) { 1149 /* 1150 * Cannot find labeled zone, just 1151 * return 0. 1152 */ 1153 return(0); 1154 } 1155 } 1156 if (mail_zonename == NULL) { 1157 procuid = Lp_Uid; 1158 procgid = Lp_Gid; 1159 av[ac++] = arg_string(TRUSTED, "%s", BINMAIL); 1160 av[ac++] = arg_string(UNTRUSTED, "%s", user); 1161 } else { 1162 procuid = getuid(); 1163 procgid = getgid(); 1164 av[ac++] = arg_string(TRUSTED, "%s", 1165 "/usr/sbin/zlogin"); 1166 av[ac++] = arg_string(TRUSTED, "%s", 1167 mail_zonename); 1168 av[ac++] = arg_string(TRUSTED, "%s", 1169 BINMAIL); 1170 av[ac++] = arg_string(UNTRUSTED, "%s", 1171 user); 1172 Free(mail_zonename); 1173 } 1174 1175 free(user); 1176 } 1177 break; 1178 } 1179 1180 av[ac++] = NULL; 1181 1182 Fork2 (); 1183 /* only the child returns */ 1184 1185 /* 1186 * Correctly set up the supplemental group list 1187 * for proper file access (before execl the interface program) 1188 */ 1189 1190 pwp = getpwuid(procuid); 1191 if (pwp == NULL) { 1192 note("getpwuid(%d) call failed\n", procuid); 1193 } else if (initgroups(pwp->pw_name, procgid) < 0) { 1194 note("initgroups() call failed %d\n", errno); 1195 } 1196 1197 setgid (procgid); 1198 setuid (procuid); 1199 1200 /* 1201 * The shell doesn't allow the "trap" builtin to set a trap 1202 * for a signal ignored when the shell is started. Thus, don't 1203 * turn off signals in the last child! 1204 */ 1205 1206 #ifdef DEBUG 1207 for (i = 0; av[i] != NULL; i++) 1208 note("exec(%s): av[%d] = %s", _exec_name(type), i, av[i]); 1209 for (i = 0; envp[i] != NULL; i++) 1210 note("exec(%s): envp[%d] = %s", _exec_name(type), i, envp[i]); 1211 #endif 1212 1213 execvpe(av[0], av, envp); 1214 Done (EXEC_EXIT_NEXEC, errno); 1215 /*NOTREACHED*/ 1216 return (0); 1217 } 1218 1219 /** 1220 ** addenv() - ADD A VARIABLE TO THE ENVIRONMENT 1221 **/ 1222 1223 static void 1224 addenv(char ***envp, char *name, char *value) 1225 { 1226 register char * cp; 1227 1228 if ((name == NULL) || (value == NULL)) 1229 return; 1230 1231 if ((cp = makestr(name, "=", value, (char *)0))) 1232 addlist(envp, cp); 1233 return; 1234 } 1235 1236 /** 1237 ** Fork1() - FORK FIRST CHILD, SET UP CONNECTION TO IT 1238 **/ 1239 1240 static int 1241 Fork1(EXEC *ep) 1242 { 1243 int pid; 1244 int fds[2]; 1245 1246 if (pipe(fds) == -1) { 1247 note("Failed to create pipe for child process (%s).\n", PERROR); 1248 errno = EAGAIN ; 1249 return(-1); 1250 } 1251 1252 ep->md = mconnect((char *)0, fds[0], fds[1]); 1253 1254 switch (pid = fork()) { 1255 1256 case -1: 1257 mdisconnect(ep->md); 1258 close(fds[0]); 1259 close(fds[1]); 1260 ep->md = 0; 1261 return (-1); 1262 1263 case 0: 1264 ChildMd = mconnect(NULL, fds[1], fds[1]); 1265 return (0); 1266 1267 default: 1268 mlistenadd(ep->md, POLLIN); 1269 return (pid); 1270 } 1271 } 1272 1273 /** 1274 ** Fork2() - FORK SECOND CHILD AND WAIT FOR IT 1275 **/ 1276 1277 static void 1278 Fork2(void) 1279 { 1280 switch ((ChildPid = fork())) { 1281 1282 case -1: 1283 Done (EXEC_EXIT_NFORK, errno); 1284 /*NOTREACHED*/ 1285 1286 case 0: 1287 return; 1288 1289 default: 1290 /* 1291 * Delay calling "ignore_fault_signals()" as long 1292 * as possible, to give the child a chance to exec 1293 * the interface program and turn on traps. 1294 */ 1295 1296 cool_heels (); 1297 /*NOTREACHED*/ 1298 1299 } 1300 } 1301 1302 1303 /** 1304 ** cool_heels() - WAIT FOR CHILD TO "DIE" 1305 **/ 1306 1307 static void 1308 cool_heels(void) 1309 { 1310 int status; 1311 1312 /* 1313 * At this point our only job is to wait for the child process. 1314 * If we hang out for a bit longer, that's okay. 1315 * By delaying before turning off the fault signals, 1316 * we increase the chance that the child process has completed 1317 * its exec and has turned on the fault traps. Nonetheless, 1318 * we can't guarantee a zero chance of missing a fault. 1319 * (We don't want to keep trapping the signals because the 1320 * interface program is likely to have a better way to handle 1321 * them; this process provides only rudimentary handling.) 1322 * 1323 * Note that on a very busy system, or with a very fast interface 1324 * program, the tables could be turned: Our sleep below (coupled 1325 * with a delay in the kernel scheduling us) may cause us to 1326 * detect the fault instead of the interface program. 1327 * 1328 * What we need is a way to synchronize with the child process. 1329 */ 1330 sleep (1); 1331 ignore_fault_signals (); 1332 1333 WaitedChildPid = 0; 1334 while ((WaitedChildPid = wait(&status)) != ChildPid) 1335 ; 1336 1337 if ( 1338 EXITED(status) > EXEC_EXIT_USER 1339 && EXITED(status) != EXEC_EXIT_FAULT 1340 ) 1341 Done (EXEC_EXIT_EXIT, EXITED(status)); 1342 1343 done (status, 0); /* Don't use Done() */ 1344 /*NOTREACHED*/ 1345 } 1346 1347 1348 /** 1349 ** trap_fault_signals() - TRAP SIGNALS THAT CAN OCCUR ON PRINTER FAULT 1350 ** ignore_fault_signals() - IGNORE SAME 1351 **/ 1352 1353 static void 1354 trap_fault_signals(void) 1355 { 1356 signal (SIGHUP, sigtrap); 1357 signal (SIGINT, sigtrap); 1358 signal (SIGQUIT, sigtrap); 1359 signal (SIGPIPE, sigtrap); 1360 return; 1361 } 1362 1363 static void 1364 ignore_fault_signals(void) 1365 { 1366 signal (SIGHUP, SIG_IGN); 1367 signal (SIGINT, SIG_IGN); 1368 signal (SIGQUIT, SIG_IGN); 1369 signal (SIGPIPE, SIG_IGN); 1370 return; 1371 } 1372 1373 /** 1374 ** sigtrap() - TRAP VARIOUS SIGNALS 1375 **/ 1376 1377 static void 1378 sigtrap(int sig) 1379 { 1380 signal (sig, SIG_IGN); 1381 switch (sig) { 1382 1383 case SIGHUP: 1384 Done (EXEC_EXIT_HUP, 0); 1385 /*NOTREACHED*/ 1386 1387 case SIGQUIT: 1388 case SIGINT: 1389 Done (EXEC_EXIT_INTR, 0); 1390 /*NOTREACHED*/ 1391 1392 case SIGPIPE: 1393 Done (EXEC_EXIT_PIPE, 0); 1394 /*NOTREACHED*/ 1395 1396 case SIGTERM: 1397 /* 1398 * If we were killed with SIGTERM, it should have been 1399 * via the Spooler who should have killed the entire 1400 * process group. We have to wait for the children, 1401 * since we're their parent, but WE MAY HAVE WAITED 1402 * FOR THEM ALREADY (in cool_heels()). 1403 */ 1404 if (ChildPid != WaitedChildPid) { 1405 register int cpid; 1406 1407 while ( 1408 (cpid = wait((int *)0)) != ChildPid 1409 && (cpid != -1 || errno != ECHILD) 1410 ) 1411 ; 1412 } 1413 1414 /* 1415 * We can't rely on getting SIGTERM back in the wait() 1416 * above, because, for instance, some shells trap SIGTERM 1417 * and exit instead. Thus we force it. 1418 */ 1419 done (SIGTERM, 0); /* Don't use Done() */ 1420 /*NOTREACHED*/ 1421 } 1422 } 1423 1424 /** 1425 ** done() - TELL SPOOLER THIS CHILD IS DONE 1426 **/ 1427 1428 static void 1429 done(int status, int err) 1430 { 1431 if (do_undial) 1432 undial (1); 1433 1434 mputm (ChildMd, S_CHILD_DONE, key, status, err); 1435 mdisconnect (ChildMd); 1436 1437 exit (0); 1438 /*NOTREACHED*/ 1439 } 1440 1441 /** 1442 ** child_mallocfail() 1443 **/ 1444 1445 static void 1446 child_mallocfail(void) 1447 { 1448 Done (EXEC_EXIT_NOMEM, ENOMEM); 1449 } 1450