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