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
61int	fromatty;
62
63char	cmdline[MAX_CMDLINE];
64int	margc;
65char	*margv[MAX_MARGV];
66
67static void		 cmdscanner(void);
68static struct cmd	*getcmd(char *);
69static void		 intr(int);
70static void		 makeargv(void);
71static int		 ingroup(char *);
72
73int
74main(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
108volatile sig_atomic_t gotintr;
109
110static void
111intr(int signo)
112{
113	if (!fromatty)
114		_exit(0);
115	gotintr = 1;
116}
117
118/*
119 * Command parser.
120 */
121static void
122cmdscanner(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
167static struct cmd *
168getcmd(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 */
198static void
199makeargv(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 */
219void
220help(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 */
276static int
277ingroup(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