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
do_printer(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
configure_printer(char * list)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 *
fullpath(char * str)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 *
nameit(char * cmd)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
update_dev_dbs(char * prtname,char * devname,char * func)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
pack_white(char * ptr)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