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