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