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