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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 22 /* All Rights Reserved */ 23 24 25 /* 26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 * Copyright (c) 2016 by Delphix. All rights reserved. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 #include <stdio.h> 34 #include <string.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <sys/types.h> 38 #include <sys/zone.h> 39 #include <stdlib.h> 40 #include <libintl.h> 41 #include <sys/tsol/label_macro.h> 42 #include <bsm/devices.h> 43 #include "lp.h" 44 #include "class.h" 45 #if defined PS_FAULTED 46 #undef PS_FAULTED 47 #endif 48 #include "printers.h" 49 #include "msgs.h" 50 51 #define WHO_AM_I I_AM_LPADMIN 52 #include "oam.h" 53 54 #include "lpadmin.h" 55 56 extern void fromallclasses(); 57 58 #if !defined(PATH_MAX) 59 # define PATH_MAX 1024 60 #endif 61 #if PATH_MAX < 1024 62 # undef PATH_MAX 63 # define PATH_MAX 1024 64 #endif 65 66 extern char *label; 67 68 static void configure_printer(); 69 static char *fullpath(); 70 char *nameit(); 71 static void pack_white(char *ptr); 72 73 /** 74 ** do_printer() - CREATE OR CHANGE PRINTER 75 **/ 76 77 void do_printer () 78 { 79 int rc; 80 81 /* 82 * Set or change the printer configuration. 83 */ 84 if (strlen(modifications)) 85 configure_printer (modifications); 86 87 /* 88 * Allow/deny forms. 89 */ 90 BEGIN_CRITICAL 91 if (!oldp) 92 if (allow_form_printer(getlist(NAME_NONE, "", ","), p) == -1) { 93 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 94 done(1); 95 } 96 97 if (f_allow || f_deny) { 98 if (f_allow && allow_form_printer(f_allow, p) == -1) { 99 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 100 done(1); 101 } 102 103 if (f_deny && deny_form_printer(f_deny, p) == -1) { 104 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 105 done(1); 106 } 107 } 108 END_CRITICAL 109 110 /* Add/remove types of paper */ 111 112 BEGIN_CRITICAL 113 if (!oldp) 114 if (add_paper_to_printer(getlist(NAME_NONE, "", ","),p) == -1) { 115 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 116 done(1); 117 } 118 119 120 if (p_add && add_paper_to_printer(p_add, p) == -1) { 121 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 122 done(1); 123 } 124 125 if (p_remove && remove_paper_from_printer(p_remove, p) == -1) { 126 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 127 done(1); 128 } 129 END_CRITICAL 130 131 /* 132 * Allow/deny users. 133 */ 134 BEGIN_CRITICAL 135 if (!oldp) 136 if (allow_user_printer(getlist(NAME_ALL, "", ","), p) == -1) { 137 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 138 done(1); 139 } 140 141 if (u_allow || u_deny) { 142 if (u_allow && allow_user_printer(u_allow, p) == -1) { 143 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 144 done(1); 145 } 146 147 if (u_deny && deny_user_printer(u_deny, p) == -1) { 148 LP_ERRMSG1 (ERROR, E_ADM_ACCESSINFO, PERROR); 149 done(1); 150 } 151 } 152 END_CRITICAL 153 154 /* 155 * Tell the Spooler about the printer 156 */ 157 send_message(S_LOAD_PRINTER, p, "", ""); 158 rc = output(R_LOAD_PRINTER); 159 160 switch (rc) { 161 case MOK: 162 break; 163 164 case MNODEST: 165 case MERRDEST: 166 LP_ERRMSG (ERROR, E_ADM_ERRDEST); 167 done (1); 168 /*NOTREACHED*/ 169 170 case MNOSPACE: 171 LP_ERRMSG (WARNING, E_ADM_NOPSPACE); 172 break; 173 174 case MNOPERM: /* taken care of up front */ 175 default: 176 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 177 done (1); 178 /*NOTREACHED*/ 179 } 180 181 /* 182 * Now that the Spooler knows about the printer, 183 * we can do the balance of the changes. 184 */ 185 186 /* 187 * Mount or unmount form, print-wheel. 188 */ 189 if (M) 190 do_mount(p, (f? f : (char *)0), (S? *S : (char *)0)); 191 else if (t) do_max_trays(p); 192 193 /* 194 * Display the alert type. 195 */ 196 if (A && STREQU(A, NAME_LIST)) { 197 if (label) 198 (void) printf(gettext("Printer %s: "), label); 199 printalert (stdout, &(oldp->fault_alert), 1); 200 } 201 202 /* 203 * -A quiet. 204 */ 205 if (A && STREQU(A, NAME_QUIET)) { 206 207 send_message(S_QUIET_ALERT, p, (char *)QA_PRINTER, ""); 208 rc = output(R_QUIET_ALERT); 209 210 switch(rc) { 211 case MOK: 212 break; 213 214 case MNODEST: /* not quite, but not a lie either */ 215 case MERRDEST: 216 LP_ERRMSG1 (WARNING, E_LP_NOQUIET, p); 217 break; 218 219 case MNOPERM: /* taken care of up front */ 220 default: 221 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 222 done (1); 223 /*NOTREACHED*/ 224 } 225 } 226 227 /* 228 * Add printer p to class c 229 */ 230 if (c) { 231 CLASS *pc, 232 clsbuf; 233 234 if (STREQU(c, NAME_ANY)) 235 c = NAME_ALL; 236 237 Loop: if (!(pc = getclass(c))) { 238 if (STREQU(c, NAME_ALL)) 239 goto Done; 240 241 if (errno != ENOENT) { 242 LP_ERRMSG2 ( 243 ERROR, 244 E_LP_GETCLASS, 245 c, 246 PERROR 247 ); 248 done (1); 249 } 250 251 /* 252 * Create the class 253 */ 254 clsbuf.name = strdup(c); 255 clsbuf.members = 0; 256 if (addlist(&clsbuf.members, p) == -1) { 257 LP_ERRMSG (ERROR, E_LP_MALLOC); 258 done (1); 259 } 260 pc = &clsbuf; 261 262 } else if (searchlist(p, pc->members)) 263 LP_ERRMSG2 (WARNING, E_ADM_INCLASS, p, pc->name); 264 265 else if (addlist(&pc->members, p) == -1) { 266 LP_ERRMSG (ERROR, E_LP_MALLOC); 267 done (1); 268 } 269 270 BEGIN_CRITICAL 271 if (putclass(pc->name, pc) == -1) { 272 LP_ERRMSG2 ( 273 ERROR, 274 E_LP_PUTCLASS, 275 pc->name, 276 PERROR 277 ); 278 done(1); 279 } 280 END_CRITICAL 281 282 send_message (S_LOAD_CLASS, pc->name); 283 rc = output(R_LOAD_CLASS); 284 285 switch(rc) { 286 case MOK: 287 break; 288 289 case MNODEST: 290 case MERRDEST: 291 LP_ERRMSG (ERROR, E_ADM_ERRDEST); 292 done (1); 293 /*NOTREACHED*/ 294 295 case MNOSPACE: 296 LP_ERRMSG (WARNING, E_ADM_NOCSPACE); 297 break; 298 299 case MNOPERM: /* taken care of up front */ 300 default: 301 LP_ERRMSG1 (ERROR, E_LP_BADSTATUS, rc); 302 done (1); 303 /*NOTREACHED*/ 304 } 305 306 if (STREQU(c, NAME_ALL)) 307 goto Loop; 308 } 309 Done: 310 /* 311 * Remove printer p from class r 312 */ 313 if (r) { 314 if (STREQU(r, NAME_ALL) || STREQU(r, NAME_ANY)) 315 fromallclasses(p); 316 else 317 fromclass(p, r); 318 } 319 320 return; 321 } 322 323 /** 324 ** configure_printer() - SET OR CHANGE CONFIGURATION OF PRINTER 325 **/ 326 327 static void configure_printer (list) 328 char *list; 329 { 330 register PRINTER *prbufp; 331 332 PRINTER printer_struct; 333 334 char type; 335 char * infile_opts = NULL; 336 337 338 if (oldp) { 339 340 prbufp = oldp; 341 342 if (!T) 343 T = prbufp->printer_types; 344 345 if (!i && !e && !m) 346 /* 347 * Don't copy the original interface program 348 * again, but do keep the name of the original. 349 */ 350 ignprinter = BAD_INTERFACE; 351 else 352 ignprinter = 0; 353 354 /* 355 * If we are making this a remote printer, 356 * make sure that local-only attributes are 357 * cleared. 358 */ 359 if (s) { 360 prbufp->banner = 0; 361 prbufp->cpi.val = 0; 362 prbufp->cpi.sc = 0; 363 prbufp->device = 0; 364 prbufp->dial_info = 0; 365 prbufp->fault_rec = 0; 366 prbufp->interface = 0; 367 prbufp->lpi.val = 0; 368 prbufp->lpi.sc = 0; 369 prbufp->plen.val = 0; 370 prbufp->plen.sc = 0; 371 prbufp->login = 0; 372 prbufp->speed = 0; 373 prbufp->stty = 0; 374 prbufp->pwid.val = 0; 375 prbufp->pwid.sc = 0; 376 prbufp->fault_alert.shcmd = strdup(NAME_NONE); 377 prbufp->fault_alert.Q = 0; 378 prbufp->fault_alert.W = 0; 379 #if defined(CAN_DO_MODULES) 380 prbufp->modules = 0; 381 #endif 382 383 /* 384 * If we are making this a local printer, make 385 * sure that some local-only attributes are set. 386 * (If the user has specified these as well, their 387 * values will overwrite what we set here.) 388 */ 389 } else if (oldp->remote) { 390 prbufp->banner = BAN_ALWAYS; 391 prbufp->interface = makepath(Lp_Model, STANDARD, (char *)0); 392 prbufp->fault_alert.shcmd = nameit(NAME_MAIL); 393 394 /* 395 * Being here means "!s && oldp->remote" is true, 396 * i.e. this printer never had an interface pgm 397 * before. Thus we can safely clear the following. 398 * This is needed to let "putprinter()" copy the 399 * (default) interface program. 400 */ 401 ignprinter = 0; 402 } 403 404 } else { 405 /* 406 * The following takes care of the lion's share 407 * of the initialization of a new printer structure. 408 * However, special initialization (e.g. non-zero, 409 * or substructure members) needs to be considered 410 * for EACH NEW MEMBER added to the structure. 411 */ 412 (void)memset (&printer_struct, 0, sizeof(printer_struct)); 413 414 prbufp = &printer_struct; 415 prbufp->banner = BAN_ALWAYS; 416 prbufp->cpi.val = 0; 417 prbufp->cpi.sc = 0; 418 if (!s) 419 prbufp->interface = makepath(Lp_Model, m, (char *)0); 420 prbufp->lpi.val = 0; 421 prbufp->lpi.sc = 0; 422 prbufp->plen.val = 0; 423 prbufp->plen.sc = 0; 424 prbufp->pwid.val = 0; 425 prbufp->pwid.sc = 0; 426 if (!s && !A) 427 prbufp->fault_alert.shcmd = nameit(NAME_MAIL); 428 prbufp->fault_alert.Q = 0; 429 prbufp->fault_alert.W = 0; 430 prbufp->options = NULL; 431 } 432 433 while ((type = *list++) != '\0') switch(type) { 434 435 case 'A': 436 if (!s) { 437 if (STREQU(A, NAME_MAIL) || STREQU(A, NAME_WRITE)) 438 prbufp->fault_alert.shcmd = nameit(A); 439 else if (!STREQU(A, NAME_QUIET)) 440 prbufp->fault_alert.shcmd = A; 441 } 442 break; 443 444 case 'b': 445 if (!s) 446 prbufp->banner = banner; 447 break; 448 449 case 'c': 450 if (!s) 451 prbufp->cpi = cpi_sdn; 452 break; 453 454 case 'D': 455 prbufp->description = D; 456 break; 457 458 case 'e': 459 if (!s) { 460 prbufp->interface = makepath( 461 Lp_A_Interfaces, 462 e, 463 (char *)0 464 ); 465 } 466 break; 467 468 case 'F': 469 if (!s) 470 prbufp->fault_rec = F; 471 break; 472 473 #if defined(CAN_DO_MODULES) 474 case 'H': 475 if (!s) 476 prbufp->modules = H; 477 break; 478 #endif 479 480 case 'h': 481 if (!s) 482 prbufp->login = 0; 483 break; 484 485 case 'i': 486 if (!s) 487 prbufp->interface = fullpath(i); 488 break; 489 490 case 'I': 491 prbufp->input_types = I; 492 break; 493 494 case 'l': 495 if (!s) 496 prbufp->login = 1; 497 break; 498 499 case 'L': 500 if (!s) 501 prbufp->plen = length_sdn; 502 break; 503 504 case 'm': 505 if (!s) 506 prbufp->interface = makepath(Lp_Model, m, (char *)0); 507 break; 508 509 case 'M': 510 if (!s) 511 prbufp->lpi = lpi_sdn; 512 break; 513 514 #ifdef LP_USE_PAPI_ATTR 515 case 'n': 516 { 517 if (n_opt != NULL) 518 { 519 if (*n_opt == '/') 520 { 521 prbufp->ppd = fullpath(n_opt); 522 } 523 else 524 { 525 prbufp->ppd = 526 makepath(Lp_Model, "ppd", n_opt, (char *)0); 527 } 528 ppdopt = 1; 529 } 530 break; 531 } 532 #endif 533 534 case 'o': 535 /* 536 * The "undefined" key-value -o options 537 * 538 * Options requires special handling. It is a 539 * list whose members are to be handled 540 * individually. 541 * 542 * Need to: set new options, keep old options if not 543 * redefined, remove old options if defined as "key=". 544 * 545 * 546 * "p" is a global containing the printer name 547 */ 548 549 if (!s) { 550 551 if ((infile_opts = getpentry(p, PR_OPTIONS)) == NULL) 552 prbufp->options = o_options; 553 else { 554 prbufp->options = 555 pick_opts(infile_opts, o_options); 556 } 557 } 558 break; 559 560 case 'R': 561 if (s) { 562 prbufp->remote = s; 563 prbufp->dial_info = 0; 564 prbufp->device = 0; 565 } else 566 prbufp->remote = 0; 567 break; 568 569 case 's': 570 if (!s) { 571 /* 572 * lpadmin always defers to stty 573 */ 574 prbufp->speed = 0; 575 prbufp->stty = stty_opt; 576 } 577 break; 578 579 case 'S': 580 if (!M) 581 if (STREQU(*S, NAME_NONE)) 582 prbufp->char_sets = 0; 583 else 584 prbufp->char_sets = S; 585 break; 586 587 case 'T': 588 prbufp->printer_types = T; 589 break; 590 591 case 'U': 592 if (!s) { 593 prbufp->dial_info = U; 594 prbufp->device = 0; 595 prbufp->remote = 0; 596 } 597 break; 598 599 case 'v': 600 if (!s) { 601 prbufp->device = v; 602 prbufp->dial_info = 0; 603 prbufp->remote = 0; 604 } 605 break; 606 607 case 'w': 608 if (!s) 609 prbufp->pwid = width_sdn; 610 break; 611 612 case 'W': 613 if (!s) 614 prbufp->fault_alert.W = W; 615 break; 616 617 } 618 619 620 BEGIN_CRITICAL 621 if (putprinter(p, prbufp) == -1) { 622 if ( 623 errno == EINVAL 624 && (badprinter & BAD_INTERFACE) 625 ) 626 LP_ERRMSG1 ( 627 ERROR, 628 E_ADM_BADINTF, 629 prbufp->interface 630 ); 631 else 632 LP_ERRMSG2 ( 633 ERROR, 634 E_LP_PUTPRINTER, 635 p, 636 PERROR 637 ); 638 done(1); 639 } 640 641 if ((getzoneid() == GLOBAL_ZONEID) && system_labeled && 642 (prbufp->device != NULL)) 643 update_dev_dbs(p, prbufp->device, "ADD"); 644 645 END_CRITICAL 646 647 return; 648 } 649 650 /** 651 ** fullpath() 652 **/ 653 654 static char *fullpath (str) 655 char *str; 656 { 657 register char *cur_dir, 658 *path; 659 660 661 while (*str && *str == ' ') 662 str++; 663 if (*str == '/') 664 return (str); 665 666 if (!(cur_dir = malloc(PATH_MAX + 1))) 667 return (str); 668 669 getcwd (cur_dir, PATH_MAX); 670 path = makepath(cur_dir, str, (char *)0); 671 672 /* 673 * Here we could be nice and strip out /./ and /../ 674 * stuff, but it isn't necessary. 675 */ 676 677 return (path); 678 } 679 680 /** 681 ** nameit() - ADD USER NAME TO COMMAND 682 **/ 683 684 char *nameit (cmd) 685 char *cmd; 686 { 687 register char *nm = getname(), 688 *copy = malloc( 689 (unsigned) (strlen(cmd) + 1 + 690 strlen(nm) + 1) 691 ); 692 693 (void) strcpy (copy, cmd); 694 (void) strcat (copy, " "); 695 (void) strcat (copy, nm); 696 return (copy); 697 } 698 699 /* 700 * update_dev_dbs - ADD/REMOVE ENTRIES FOR THE PRINTER IN DEVICE 701 * ALLOCATION FILES 702 * 703 * We intentionally ignore errors, since we don't want the printer 704 * installation to be viewed as failing just because we didn't add 705 * the device_allocate entry. 706 * 707 * Input: 708 * prtname - printer name 709 * devname - device associated w/ this printer 710 * func - [ADD|REMOVE] entries in /etc/security/device_allocate 711 * and /etc/security/device_maps 712 * 713 * Return: 714 * Always 'quiet' return. Failures are ignored. 715 */ 716 void 717 update_dev_dbs(char *prtname, char *devname, char *func) 718 { 719 int fd, status; 720 pid_t pid; 721 722 pid = fork(); 723 switch (pid) { 724 case -1: 725 /* fork failed, just return quietly */ 726 return; 727 case 0: 728 /* child */ 729 /* redirect to /dev/null */ 730 (void) close(1); 731 (void) close(2); 732 fd = open("/dev/null", O_WRONLY); 733 fd = dup(fd); 734 735 if (strcmp(func, "ADD") == 0) { 736 execl("/usr/sbin/add_allocatable", "add_allocatable", 737 "-n", prtname, "-t", "lp", "-l", devname, 738 "-o", "minlabel=admin_low:maxlabel=admin_high", 739 "-a", "*", "-c", "/bin/true", NULL); 740 } else { 741 if (strcmp(func, "REMOVE") == 0) { 742 execl("/usr/sbin/remove_allocatable", 743 "remove_allocatable", "-n", prtname, NULL); 744 } 745 } 746 _exit(1); 747 /* NOT REACHED */ 748 default: 749 waitpid(pid, &status, 0); 750 return; 751 } 752 } 753 754 /* 755 * pack_white(ptr) trims off multiple occurances of white space from a NULL 756 * terminated string pointed to by "ptr". 757 */ 758 static void 759 pack_white(char *ptr) 760 { 761 char *tptr; 762 char *mptr; 763 int cnt; 764 765 if (ptr == NULL) 766 return; 767 cnt = strlen(ptr); 768 if (cnt == 0) 769 return; 770 mptr = (char *)calloc((unsigned)cnt+1, sizeof (char)); 771 if (mptr == NULL) 772 return; 773 tptr = strtok(ptr, " \t"); 774 while (tptr != NULL) { 775 (void) strcat(mptr, tptr); 776 (void) strcat(mptr, " "); 777 tptr = strtok(NULL, " \t"); 778 } 779 cnt = strlen(mptr); 780 (void) strcpy(ptr, mptr); 781 free(mptr); 782 } 783