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