lpd.c revision 1.6 1 /* $NetBSD: lpd.c,v 1.6 1995/11/15 22:49:53 pk Exp $ */
2 /*
3 * Copyright (c) 1983, 1993, 1994
4 * The Regents of the University of California. All rights reserved.
5 *
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1983, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n";
40 #endif /* not lint */
41
42 #ifndef lint
43 static char sccsid[] = "@(#)lpd.c 8.4 (Berkeley) 4/17/94";
44 #endif /* not lint */
45
46 /*
47 * lpd -- line printer daemon.
48 *
49 * Listen for a connection and perform the requested operation.
50 * Operations are:
51 * \1printer\n
52 * check the queue for jobs and print any found.
53 * \2printer\n
54 * receive a job from another machine and queue it.
55 * \3printer [users ...] [jobs ...]\n
56 * return the current state of the queue (short form).
57 * \4printer [users ...] [jobs ...]\n
58 * return the current state of the queue (long form).
59 * \5printer person [users ...] [jobs ...]\n
60 * remove jobs from the queue.
61 *
62 * Strategy to maintain protected spooling area:
63 * 1. Spooling area is writable only by daemon and spooling group
64 * 2. lpr runs setuid root and setgrp spooling group; it uses
65 * root to access any file it wants (verifying things before
66 * with an access call) and group id to know how it should
67 * set up ownership of files in the spooling area.
68 * 3. Files in spooling area are owned by root, group spooling
69 * group, with mode 660.
70 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
71 * access files and printer. Users can't get to anything
72 * w/o help of lpq and lprm programs.
73 */
74
75 #include <sys/param.h>
76 #include <sys/wait.h>
77 #include <sys/types.h>
78 #include <sys/socket.h>
79 #include <sys/un.h>
80 #include <sys/stat.h>
81 #include <netinet/in.h>
82
83 #include <netdb.h>
84 #include <unistd.h>
85 #include <syslog.h>
86 #include <signal.h>
87 #include <errno.h>
88 #include <fcntl.h>
89 #include <dirent.h>
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <string.h>
93 #include <ctype.h>
94 #include "lp.h"
95 #include "lp.local.h"
96 #include "pathnames.h"
97 #include "extern.h"
98
99 int lflag; /* log requests flag */
100 int from_remote; /* from remote socket */
101
102 static void reapchild __P((int));
103 static void mcleanup __P((int));
104 static void doit __P((void));
105 static void startup __P((void));
106 static void chkhost __P((struct sockaddr_in *));
107
108 uid_t uid, euid;
109
110 int
111 main(argc, argv)
112 int argc;
113 char **argv;
114 {
115 int f, funix, finet, options, fromlen;
116 fd_set defreadfds;
117 struct sockaddr_un un, fromunix;
118 struct sockaddr_in sin, frominet;
119 int omask, lfd;
120
121 euid = geteuid(); /* these shouldn't be different */
122 uid = getuid();
123 options = 0;
124 gethostname(host, sizeof(host));
125 name = argv[0];
126
127 while (--argc > 0) {
128 argv++;
129 if (argv[0][0] == '-')
130 switch (argv[0][1]) {
131 case 'd':
132 options |= SO_DEBUG;
133 break;
134 case 'l':
135 lflag++;
136 break;
137 }
138 }
139
140 #ifndef DEBUG
141 /*
142 * Set up standard environment by detaching from the parent.
143 */
144 daemon(0, 0);
145 #endif
146
147 openlog("lpd", LOG_PID, LOG_LPR);
148 syslog(LOG_INFO, "restarted");
149 (void) umask(0);
150 lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
151 if (lfd < 0) {
152 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
153 exit(1);
154 }
155 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
156 if (errno == EWOULDBLOCK) /* active deamon present */
157 exit(0);
158 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
159 exit(1);
160 }
161 ftruncate(lfd, 0);
162 /*
163 * write process id for others to know
164 */
165 sprintf(line, "%u\n", getpid());
166 f = strlen(line);
167 if (write(lfd, line, f) != f) {
168 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
169 exit(1);
170 }
171 signal(SIGCHLD, reapchild);
172 /*
173 * Restart all the printers.
174 */
175 startup();
176 (void) unlink(_PATH_SOCKETNAME);
177 funix = socket(AF_UNIX, SOCK_STREAM, 0);
178 if (funix < 0) {
179 syslog(LOG_ERR, "socket: %m");
180 exit(1);
181 }
182 #define mask(s) (1 << ((s) - 1))
183 omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
184 signal(SIGHUP, mcleanup);
185 signal(SIGINT, mcleanup);
186 signal(SIGQUIT, mcleanup);
187 signal(SIGTERM, mcleanup);
188 memset(&un, 0, sizeof(un));
189 un.sun_family = AF_UNIX;
190 strcpy(un.sun_path, _PATH_SOCKETNAME);
191 #ifndef SUN_LEN
192 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
193 #endif
194 if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
195 syslog(LOG_ERR, "ubind: %m");
196 exit(1);
197 }
198 sigsetmask(omask);
199 FD_ZERO(&defreadfds);
200 FD_SET(funix, &defreadfds);
201 listen(funix, 5);
202 finet = socket(AF_INET, SOCK_STREAM, 0);
203 if (finet >= 0) {
204 struct servent *sp;
205
206 if (options & SO_DEBUG)
207 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
208 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
209 mcleanup(0);
210 }
211 sp = getservbyname("printer", "tcp");
212 if (sp == NULL) {
213 syslog(LOG_ERR, "printer/tcp: unknown service");
214 mcleanup(0);
215 }
216 memset(&sin, 0, sizeof(sin));
217 sin.sin_family = AF_INET;
218 sin.sin_port = sp->s_port;
219 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
220 syslog(LOG_ERR, "bind: %m");
221 mcleanup(0);
222 }
223 FD_SET(finet, &defreadfds);
224 listen(finet, 5);
225 }
226 /*
227 * Main loop: accept, do a request, continue.
228 */
229 memset(&frominet, 0, sizeof(frominet));
230 memset(&fromunix, 0, sizeof(fromunix));
231 for (;;) {
232 int domain, nfds, s;
233 fd_set readfds;
234
235 FD_COPY(&defreadfds, &readfds);
236 nfds = select(20, &readfds, 0, 0, 0);
237 if (nfds <= 0) {
238 if (nfds < 0 && errno != EINTR)
239 syslog(LOG_WARNING, "select: %m");
240 continue;
241 }
242 if (FD_ISSET(funix, &readfds)) {
243 domain = AF_UNIX, fromlen = sizeof(fromunix);
244 s = accept(funix,
245 (struct sockaddr *)&fromunix, &fromlen);
246 } else /* if (FD_ISSET(finet, &readfds)) */ {
247 domain = AF_INET, fromlen = sizeof(frominet);
248 s = accept(finet,
249 (struct sockaddr *)&frominet, &fromlen);
250 }
251 if (s < 0) {
252 if (errno != EINTR)
253 syslog(LOG_WARNING, "accept: %m");
254 continue;
255 }
256 if (fork() == 0) {
257 signal(SIGCHLD, SIG_IGN);
258 signal(SIGHUP, SIG_IGN);
259 signal(SIGINT, SIG_IGN);
260 signal(SIGQUIT, SIG_IGN);
261 signal(SIGTERM, SIG_IGN);
262 (void) close(funix);
263 (void) close(finet);
264 dup2(s, 1);
265 (void) close(s);
266 if (domain == AF_INET) {
267 from_remote = 1;
268 chkhost(&frominet);
269 } else
270 from_remote = 0;
271 doit();
272 exit(0);
273 }
274 (void) close(s);
275 }
276 }
277
278 static void
279 reapchild(signo)
280 int signo;
281 {
282 union wait status;
283
284 while (wait3((int *)&status, WNOHANG, 0) > 0)
285 ;
286 }
287
288 static void
289 mcleanup(signo)
290 int signo;
291 {
292 if (lflag)
293 syslog(LOG_INFO, "exiting");
294 unlink(_PATH_SOCKETNAME);
295 exit(0);
296 }
297
298 /*
299 * Stuff for handling job specifications
300 */
301 char *user[MAXUSERS]; /* users to process */
302 int users; /* # of users in user array */
303 int requ[MAXREQUESTS]; /* job number of spool entries */
304 int requests; /* # of spool requests */
305 char *person; /* name of person doing lprm */
306
307 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */
308 char cbuf[BUFSIZ]; /* command line buffer */
309 char *cmdnames[] = {
310 "null",
311 "printjob",
312 "recvjob",
313 "displayq short",
314 "displayq long",
315 "rmjob"
316 };
317
318 static void
319 doit()
320 {
321 register char *cp;
322 register int n;
323
324 for (;;) {
325 cp = cbuf;
326 do {
327 if (cp >= &cbuf[sizeof(cbuf) - 1])
328 fatal("Command line too long");
329 if ((n = read(1, cp, 1)) != 1) {
330 if (n < 0)
331 fatal("Lost connection");
332 return;
333 }
334 } while (*cp++ != '\n');
335 *--cp = '\0';
336 cp = cbuf;
337 if (lflag) {
338 if (*cp >= '\1' && *cp <= '\5')
339 syslog(LOG_INFO, "%s requests %s %s",
340 from, cmdnames[*cp], cp+1);
341 else
342 syslog(LOG_INFO, "bad request (%d) from %s",
343 *cp, from);
344 }
345 switch (*cp++) {
346 case '\1': /* check the queue and print any jobs there */
347 printer = cp;
348 printjob();
349 break;
350 case '\2': /* receive files to be queued */
351 if (!from_remote) {
352 syslog(LOG_INFO, "illegal request (%d)", *cp);
353 exit(1);
354 }
355 printer = cp;
356 recvjob();
357 break;
358 case '\3': /* display the queue (short form) */
359 case '\4': /* display the queue (long form) */
360 printer = cp;
361 while (*cp) {
362 if (*cp != ' ') {
363 cp++;
364 continue;
365 }
366 *cp++ = '\0';
367 while (isspace(*cp))
368 cp++;
369 if (*cp == '\0')
370 break;
371 if (isdigit(*cp)) {
372 if (requests >= MAXREQUESTS)
373 fatal("Too many requests");
374 requ[requests++] = atoi(cp);
375 } else {
376 if (users >= MAXUSERS)
377 fatal("Too many users");
378 user[users++] = cp;
379 }
380 }
381 displayq(cbuf[0] - '\3');
382 exit(0);
383 case '\5': /* remove a job from the queue */
384 if (!from_remote) {
385 syslog(LOG_INFO, "illegal request (%d)", *cp);
386 exit(1);
387 }
388 printer = cp;
389 while (*cp && *cp != ' ')
390 cp++;
391 if (!*cp)
392 break;
393 *cp++ = '\0';
394 person = cp;
395 while (*cp) {
396 if (*cp != ' ') {
397 cp++;
398 continue;
399 }
400 *cp++ = '\0';
401 while (isspace(*cp))
402 cp++;
403 if (*cp == '\0')
404 break;
405 if (isdigit(*cp)) {
406 if (requests >= MAXREQUESTS)
407 fatal("Too many requests");
408 requ[requests++] = atoi(cp);
409 } else {
410 if (users >= MAXUSERS)
411 fatal("Too many users");
412 user[users++] = cp;
413 }
414 }
415 rmjob();
416 break;
417 }
418 fatal("Illegal service request");
419 }
420 }
421
422 /*
423 * Make a pass through the printcap database and start printing any
424 * files left from the last time the machine went down.
425 */
426 static void
427 startup()
428 {
429 char *buf;
430 register char *cp;
431 int pid;
432
433 /*
434 * Restart the daemons.
435 */
436 while (cgetnext(&buf, printcapdb) > 0) {
437 for (cp = buf; *cp; cp++)
438 if (*cp == '|' || *cp == ':') {
439 *cp = '\0';
440 break;
441 }
442 if ((pid = fork()) < 0) {
443 syslog(LOG_WARNING, "startup: cannot fork");
444 mcleanup(0);
445 }
446 if (!pid) {
447 printer = buf;
448 cgetclose();
449 printjob();
450 }
451 }
452 }
453
454 #define DUMMY ":nobody::"
455
456 /*
457 * Check to see if the from host has access to the line printer.
458 */
459 static void
460 chkhost(f)
461 struct sockaddr_in *f;
462 {
463 register struct hostent *hp;
464 register FILE *hostf;
465 int first = 1;
466 extern char *inet_ntoa();
467
468 f->sin_port = ntohs(f->sin_port);
469 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
470 fatal("Malformed from address");
471
472 /* Need real hostname for temporary filenames */
473 hp = gethostbyaddr((char *)&f->sin_addr,
474 sizeof(struct in_addr), f->sin_family);
475 if (hp == NULL)
476 fatal("Host name for your address (%s) unknown",
477 inet_ntoa(f->sin_addr));
478
479 (void) strncpy(fromb, hp->h_name, sizeof(fromb));
480 from[sizeof(fromb) - 1] = '\0';
481 from = fromb;
482
483 hostf = fopen(_PATH_HOSTSEQUIV, "r");
484 again:
485 if (hostf) {
486 if (__ivaliduser(hostf, f->sin_addr.s_addr,
487 DUMMY, DUMMY) == 0) {
488 (void) fclose(hostf);
489 return;
490 }
491 (void) fclose(hostf);
492 }
493 if (first == 1) {
494 first = 0;
495 hostf = fopen(_PATH_HOSTSLPD, "r");
496 goto again;
497 }
498 fatal("Your host does not have line printer access");
499 /*NOTREACHED*/
500 }
501
502
503
504
505
506
507
508
509
510
511
512
513