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
main(int argc,char ** argv)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
intr(int signo)111 intr(int signo)
112 {
113 if (!fromatty)
114 _exit(0);
115 gotintr = 1;
116 }
117
118 /*
119 * Command parser.
120 */
121 static void
cmdscanner(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 *
getcmd(char * name)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
makeargv(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
help(int argc,char ** argv)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
ingroup(char * grname)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