xref: /openbsd-src/usr.sbin/lpr/lpc/lpc.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: lpc.c,v 1.18 2009/10/27 23:59:52 deraadt 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 <sys/param.h>
35 
36 #include <dirent.h>
37 #include <signal.h>
38 #include <syslog.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <grp.h>
47 
48 #include "lp.h"
49 #include "lpc.h"
50 #include "extern.h"
51 
52 #ifndef LPR_OPER
53 #define LPR_OPER	"operator"	/* group name of lpr operators */
54 #endif
55 
56 /*
57  * lpc -- line printer control program
58  */
59 
60 #define MAX_CMDLINE	200
61 #define MAX_MARGV	20
62 int	fromatty;
63 
64 char	cmdline[MAX_CMDLINE];
65 int	margc;
66 char	*margv[MAX_MARGV];
67 
68 static void		 cmdscanner(void);
69 static struct cmd	*getcmd(char *);
70 static void		 intr(int);
71 static void		 makeargv(void);
72 static int		 ingroup(char *);
73 
74 int
75 main(int argc, char **argv)
76 {
77 	struct cmd *c;
78 
79 	effective_uid = geteuid();
80 	real_uid = getuid();
81 	effective_gid = getegid();
82 	real_gid = getgid();
83 	PRIV_END;	/* be safe */
84 
85 	openlog("lpc", 0, LOG_LPR);
86 	if (--argc > 0) {
87 		c = getcmd(*++argv);
88 		if (c == (struct cmd *)-1) {
89 			printf("?Ambiguous command\n");
90 			exit(1);
91 		}
92 		if (c == 0) {
93 			printf("?Invalid command\n");
94 			exit(1);
95 		}
96 		if (c->c_priv && real_uid && ingroup(LPR_OPER) == 0) {
97 			printf("?Privileged command\n");
98 			exit(1);
99 		}
100 		(*c->c_handler)(argc, argv);
101 		exit(0);
102 	}
103 	fromatty = isatty(fileno(stdin));
104 	signal(SIGINT, intr);
105 	for (;;)
106 		cmdscanner();
107 }
108 
109 volatile sig_atomic_t gotintr;
110 
111 static void
112 intr(int signo)
113 {
114 	if (!fromatty)
115 		_exit(0);
116 	gotintr = 1;
117 }
118 
119 /*
120  * Command parser.
121  */
122 static void
123 cmdscanner(void)
124 {
125 	struct cmd *c;
126 
127 	for (;;) {
128 		if (gotintr) {
129 			putchar('\n');
130 			gotintr = 0;
131 		}
132 		if (fromatty) {
133 			printf("lpc> ");
134 			fflush(stdout);
135 		}
136 
137 		siginterrupt(SIGINT, 1);
138 		if (fgets(cmdline, MAX_CMDLINE, stdin) == NULL) {
139 			if (errno == EINTR && gotintr) {
140 				siginterrupt(SIGINT, 0);
141 				return;
142 			}
143 			siginterrupt(SIGINT, 0);
144 			quit(0, NULL);
145 		}
146 		siginterrupt(SIGINT, 0);
147 
148 		makeargv();
149 		if (margc == 0)
150 			break;
151 		c = getcmd(margv[0]);
152 		if (c == (struct cmd *)-1) {
153 			printf("?Ambiguous command\n");
154 			continue;
155 		}
156 		if (c == 0) {
157 			printf("?Invalid command\n");
158 			continue;
159 		}
160 		if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
161 			printf("?Privileged command\n");
162 			continue;
163 		}
164 		(*c->c_handler)(margc, margv);
165 	}
166 }
167 
168 static struct cmd *
169 getcmd(char *name)
170 {
171 	char *p, *q;
172 	struct cmd *c, *found;
173 	int nmatches, longest;
174 
175 	longest = 0;
176 	nmatches = 0;
177 	found = 0;
178 	for (c = cmdtab; (p = c->c_name) != NULL; c++) {
179 		for (q = name; *q == *p++; q++)
180 			if (*q == 0)		/* exact match? */
181 				return(c);
182 		if (!*q) {			/* the name was a prefix */
183 			if (q - name > longest) {
184 				longest = q - name;
185 				nmatches = 1;
186 				found = c;
187 			} else if (q - name == longest)
188 				nmatches++;
189 		}
190 	}
191 	if (nmatches > 1)
192 		return((struct cmd *)-1);
193 	return(found);
194 }
195 
196 /*
197  * Slice a string up into argc/argv.
198  */
199 static void
200 makeargv(void)
201 {
202 	char *cp = cmdline;
203 	char **ap = margv;
204 
205 	margc = 0;
206 	while (margc < MAX_MARGV - 1 && (*ap = strsep(&cp, " \t\n")) != NULL) {
207 		if (**ap != '\0') {
208 			ap++;
209 			margc++;
210 		}
211 	}
212 	*ap = NULL;
213 }
214 
215 #define HELPINDENT ((int)sizeof("directory"))
216 
217 /*
218  * Help command.
219  */
220 void
221 help(int argc, char **argv)
222 {
223 	struct cmd *c;
224 
225 	if (argc == 1) {
226 		int i, j, w;
227 		int columns, width = 0, lines;
228 
229 		printf("Commands may be abbreviated.  Commands are:\n\n");
230 		for (c = cmdtab; c->c_name; c++) {
231 			int len = strlen(c->c_name);
232 
233 			if (len > width)
234 				width = len;
235 		}
236 		width = (width + 8) &~ 7;
237 		columns = 80 / width;
238 		if (columns == 0)
239 			columns = 1;
240 		lines = (NCMDS + columns - 1) / columns;
241 		for (i = 0; i < lines; i++) {
242 			for (j = 0; j < columns; j++) {
243 				c = cmdtab + j * lines + i;
244 				if (c->c_name)
245 					printf("%s", c->c_name);
246 				if (c + lines >= &cmdtab[NCMDS]) {
247 					printf("\n");
248 					break;
249 				}
250 				w = strlen(c->c_name);
251 				while (w < width) {
252 					w = (w + 8) &~ 7;
253 					putchar('\t');
254 				}
255 			}
256 		}
257 		return;
258 	}
259 	while (--argc > 0) {
260 		char *arg;
261 
262 		arg = *++argv;
263 		c = getcmd(arg);
264 		if (c == (struct cmd *)-1)
265 			printf("?Ambiguous help command %s\n", arg);
266 		else if (c == (struct cmd *)0)
267 			printf("?Invalid help command %s\n", arg);
268 		else
269 			printf("%-*s\t%s\n", HELPINDENT,
270 				c->c_name, c->c_help);
271 	}
272 }
273 
274 /*
275  * return non-zero if the user is a member of the given group
276  */
277 static int
278 ingroup(char *grname)
279 {
280 	static struct group *gptr = NULL;
281 	static gid_t groups[NGROUPS];
282 	static int ngroups;
283 	gid_t gid;
284 	int i;
285 
286 	if (gptr == NULL) {
287 		if ((gptr = getgrnam(grname)) == NULL) {
288 			warnx("Warning: unknown group `%s'", grname);
289 			return(0);
290 		}
291 		if ((ngroups = getgroups(NGROUPS, groups)) < 0)
292 			err(1, "getgroups");
293 	}
294 	gid = gptr->gr_gid;
295 	for (i = 0; i < ngroups; i++)
296 		if (gid == groups[i])
297 			return(1);
298 	return(0);
299 }
300