xref: /openbsd/usr.sbin/lpr/lpc/lpc.c (revision d89ec533)
1 /*	$OpenBSD: lpc.c,v 1.20 2016/03/16 15:41:11 krw Exp $	*/
2 /*	$NetBSD: lpc.c,v 1.11 2001/11/14 03:01:15 enami Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <dirent.h>
35 #include <signal.h>
36 #include <syslog.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include <ctype.h>
44 #include <string.h>
45 #include <grp.h>
46 
47 #include "lp.h"
48 #include "lpc.h"
49 #include "extern.h"
50 
51 #ifndef LPR_OPER
52 #define LPR_OPER	"operator"	/* group name of lpr operators */
53 #endif
54 
55 /*
56  * lpc -- line printer control program
57  */
58 
59 #define MAX_CMDLINE	200
60 #define MAX_MARGV	20
61 int	fromatty;
62 
63 char	cmdline[MAX_CMDLINE];
64 int	margc;
65 char	*margv[MAX_MARGV];
66 
67 static void		 cmdscanner(void);
68 static struct cmd	*getcmd(char *);
69 static void		 intr(int);
70 static void		 makeargv(void);
71 static int		 ingroup(char *);
72 
73 int
74 main(int argc, char **argv)
75 {
76 	struct cmd *c;
77 
78 	effective_uid = geteuid();
79 	real_uid = getuid();
80 	effective_gid = getegid();
81 	real_gid = getgid();
82 	PRIV_END;	/* be safe */
83 
84 	openlog("lpc", 0, LOG_LPR);
85 	if (--argc > 0) {
86 		c = getcmd(*++argv);
87 		if (c == (struct cmd *)-1) {
88 			printf("?Ambiguous command\n");
89 			exit(1);
90 		}
91 		if (c == 0) {
92 			printf("?Invalid command\n");
93 			exit(1);
94 		}
95 		if (c->c_priv && real_uid && ingroup(LPR_OPER) == 0) {
96 			printf("?Privileged command\n");
97 			exit(1);
98 		}
99 		(*c->c_handler)(argc, argv);
100 		exit(0);
101 	}
102 	fromatty = isatty(fileno(stdin));
103 	signal(SIGINT, intr);
104 	for (;;)
105 		cmdscanner();
106 }
107 
108 volatile sig_atomic_t gotintr;
109 
110 static void
111 intr(int signo)
112 {
113 	if (!fromatty)
114 		_exit(0);
115 	gotintr = 1;
116 }
117 
118 /*
119  * Command parser.
120  */
121 static void
122 cmdscanner(void)
123 {
124 	struct cmd *c;
125 
126 	for (;;) {
127 		if (gotintr) {
128 			putchar('\n');
129 			gotintr = 0;
130 		}
131 		if (fromatty) {
132 			printf("lpc> ");
133 			fflush(stdout);
134 		}
135 
136 		siginterrupt(SIGINT, 1);
137 		if (fgets(cmdline, MAX_CMDLINE, stdin) == NULL) {
138 			if (errno == EINTR && gotintr) {
139 				siginterrupt(SIGINT, 0);
140 				return;
141 			}
142 			siginterrupt(SIGINT, 0);
143 			quit(0, NULL);
144 		}
145 		siginterrupt(SIGINT, 0);
146 
147 		makeargv();
148 		if (margc == 0)
149 			break;
150 		c = getcmd(margv[0]);
151 		if (c == (struct cmd *)-1) {
152 			printf("?Ambiguous command\n");
153 			continue;
154 		}
155 		if (c == 0) {
156 			printf("?Invalid command\n");
157 			continue;
158 		}
159 		if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
160 			printf("?Privileged command\n");
161 			continue;
162 		}
163 		(*c->c_handler)(margc, margv);
164 	}
165 }
166 
167 static struct cmd *
168 getcmd(char *name)
169 {
170 	char *p, *q;
171 	struct cmd *c, *found;
172 	int nmatches, longest;
173 
174 	longest = 0;
175 	nmatches = 0;
176 	found = 0;
177 	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
178 		for (q = name; *q == *p++; q++)
179 			if (*q == 0)		/* exact match? */
180 				return(c);
181 		if (!*q) {			/* the name was a prefix */
182 			if (q - name > longest) {
183 				longest = q - name;
184 				nmatches = 1;
185 				found = c;
186 			} else if (q - name == longest)
187 				nmatches++;
188 		}
189 	}
190 	if (nmatches > 1)
191 		return((struct cmd *)-1);
192 	return(found);
193 }
194 
195 /*
196  * Slice a string up into argc/argv.
197  */
198 static void
199 makeargv(void)
200 {
201 	char *cp = cmdline;
202 	char **ap = margv;
203 
204 	margc = 0;
205 	while (margc < MAX_MARGV - 1 && (*ap = strsep(&cp, " \t\n")) != NULL) {
206 		if (**ap != '\0') {
207 			ap++;
208 			margc++;
209 		}
210 	}
211 	*ap = NULL;
212 }
213 
214 #define HELPINDENT ((int)sizeof("directory"))
215 
216 /*
217  * Help command.
218  */
219 void
220 help(int argc, char **argv)
221 {
222 	struct cmd *c;
223 
224 	if (argc == 1) {
225 		int i, j, w;
226 		int columns, width = 0, lines;
227 
228 		printf("Commands may be abbreviated.  Commands are:\n\n");
229 		for (c = cmdtab; c->c_name; c++) {
230 			int len = strlen(c->c_name);
231 
232 			if (len > width)
233 				width = len;
234 		}
235 		width = (width + 8) &~ 7;
236 		columns = 80 / width;
237 		if (columns == 0)
238 			columns = 1;
239 		lines = (NCMDS + columns - 1) / columns;
240 		for (i = 0; i < lines; i++) {
241 			for (j = 0; j < columns; j++) {
242 				c = cmdtab + j * lines + i;
243 				if (c->c_name)
244 					printf("%s", c->c_name);
245 				if (c + lines >= &cmdtab[NCMDS]) {
246 					printf("\n");
247 					break;
248 				}
249 				w = strlen(c->c_name);
250 				while (w < width) {
251 					w = (w + 8) &~ 7;
252 					putchar('\t');
253 				}
254 			}
255 		}
256 		return;
257 	}
258 	while (--argc > 0) {
259 		char *arg;
260 
261 		arg = *++argv;
262 		c = getcmd(arg);
263 		if (c == (struct cmd *)-1)
264 			printf("?Ambiguous help command %s\n", arg);
265 		else if (c == NULL)
266 			printf("?Invalid help command %s\n", arg);
267 		else
268 			printf("%-*s\t%s\n", HELPINDENT,
269 				c->c_name, c->c_help);
270 	}
271 }
272 
273 /*
274  * return non-zero if the user is a member of the given group
275  */
276 static int
277 ingroup(char *grname)
278 {
279 	static struct group *gptr = NULL;
280 	static gid_t groups[NGROUPS_MAX];
281 	static int ngroups;
282 	gid_t gid;
283 	int i;
284 
285 	if (gptr == NULL) {
286 		if ((gptr = getgrnam(grname)) == NULL) {
287 			warnx("Warning: unknown group `%s'", grname);
288 			return(0);
289 		}
290 		if ((ngroups = getgroups(NGROUPS_MAX, groups)) < 0)
291 			err(1, "getgroups");
292 	}
293 	gid = gptr->gr_gid;
294 	for (i = 0; i < ngroups; i++)
295 		if (gid == groups[i])
296 			return(1);
297 	return(0);
298 }
299