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
51extern	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
61extern char		*label;
62
63static void		configure_printer();
64static char		*fullpath();
65char			*nameit();
66static void		pack_white(char *ptr);
67
68/*
69 * do_printer() - CREATE OR CHANGE PRINTER
70 */
71
72void
73do_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
237Loop:		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	}
300Done:
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
316static void
317configure_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
621static char *
622fullpath(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
650char *
651nameit(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 */
682void
683update_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 */
724static void
725pack_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