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