lpd.c revision 1.22 1 /* $NetBSD: lpd.c,v 1.22 2000/04/10 08:09:33 mrg Exp $ */
2
3 /*
4 * Copyright (c) 1983, 1993, 1994
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. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38
39 #ifndef lint
40 __COPYRIGHT("@(#) Copyright (c) 1983, 1993, 1994\n\
41 The Regents of the University of California. All rights reserved.\n");
42 #endif /* not lint */
43
44 #ifndef lint
45 #if 0
46 static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95";
47 #else
48 __RCSID("$NetBSD: lpd.c,v 1.22 2000/04/10 08:09:33 mrg Exp $");
49 #endif
50 #endif /* not lint */
51
52 /*
53 * lpd -- line printer daemon.
54 *
55 * Listen for a connection and perform the requested operation.
56 * Operations are:
57 * \1printer\n
58 * check the queue for jobs and print any found.
59 * \2printer\n
60 * receive a job from another machine and queue it.
61 * \3printer [users ...] [jobs ...]\n
62 * return the current state of the queue (short form).
63 * \4printer [users ...] [jobs ...]\n
64 * return the current state of the queue (long form).
65 * \5printer person [users ...] [jobs ...]\n
66 * remove jobs from the queue.
67 *
68 * Strategy to maintain protected spooling area:
69 * 1. Spooling area is writable only by daemon and spooling group
70 * 2. lpr runs setuid root and setgrp spooling group; it uses
71 * root to access any file it wants (verifying things before
72 * with an access call) and group id to know how it should
73 * set up ownership of files in the spooling area.
74 * 3. Files in spooling area are owned by root, group spooling
75 * group, with mode 660.
76 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
77 * access files and printer. Users can't get to anything
78 * w/o help of lpq and lprm programs.
79 */
80
81 #include <sys/param.h>
82 #include <sys/wait.h>
83 #include <sys/types.h>
84 #include <sys/socket.h>
85 #include <sys/un.h>
86 #include <sys/stat.h>
87 #include <sys/file.h>
88 #include <netinet/in.h>
89
90 #include <err.h>
91 #include <netdb.h>
92 #include <unistd.h>
93 #include <syslog.h>
94 #include <signal.h>
95 #include <errno.h>
96 #include <fcntl.h>
97 #include <dirent.h>
98 #include <stdio.h>
99 #include <stdlib.h>
100 #include <string.h>
101 #include <ctype.h>
102 #include <arpa/inet.h>
103
104 #include "lp.h"
105 #include "lp.local.h"
106 #include "pathnames.h"
107 #include "extern.h"
108
109 /* XXX from libc/net/rcmd.c */
110 extern int __ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t,
111 const char *, const char *));
112
113 int lflag; /* log requests flag */
114 int rflag; /* allow of for remote printers */
115 int sflag; /* secure (no inet) flag */
116 int from_remote; /* from remote socket */
117
118 int main __P((int, char **));
119 static void reapchild __P((int));
120 static void mcleanup __P((int));
121 static void doit __P((void));
122 static void startup __P((void));
123 static void chkhost __P((struct sockaddr *));
124 static int ckqueue __P((char *));
125 static void usage __P((void));
126 static int *socksetup __P((int, int));
127
128 uid_t uid, euid;
129 int child_count;
130
131 int
132 main(argc, argv)
133 int argc;
134 char **argv;
135 {
136 fd_set defreadfds;
137 struct sockaddr_un un, fromunix;
138 struct sockaddr_storage frominet;
139 sigset_t nmask, omask;
140 int lfd, errs, i, f, funix, *finet;
141 int child_max = 32; /* more then enough to hose the system */
142 int options = 0;
143 struct servent *sp, serv;
144
145 euid = geteuid(); /* these shouldn't be different */
146 uid = getuid();
147 gethostname(host, sizeof(host));
148 host[sizeof(host) - 1] = '\0';
149 name = argv[0];
150
151 errs = 0;
152 while ((i = getopt(argc, argv, "dln:srw:")) != -1)
153 switch (i) {
154 case 'd':
155 options |= SO_DEBUG;
156 break;
157 case 'l':
158 lflag++;
159 break;
160 case 'n':
161 child_max = atoi(optarg);
162 if (child_max < 0 || child_max > 1024)
163 errx(1, "invalid number of children: %s",
164 optarg);
165 break;
166 case 'r':
167 rflag++;
168 break;
169 case 's':
170 sflag++;
171 break;
172 case 'w':
173 wait_time = atoi(optarg);
174 if (wait_time < 0)
175 errx(1, "wait time must be postive: %s",
176 optarg);
177 if (wait_time < 30)
178 warnx("warning: wait time less than 30 seconds");
179 break;
180 default:
181 errs++;
182 }
183 argc -= optind;
184 argv += optind;
185 if (errs)
186 usage();
187
188 switch (argc) {
189 case 1:
190 if ((i = atoi(argv[0])) == 0)
191 usage();
192 if (i < 0 || i > USHRT_MAX)
193 errx(1, "port # %d is invalid", i);
194
195 serv.s_port = htons(i);
196 sp = &serv;
197 break;
198 case 0:
199 sp = getservbyname("printer", "tcp");
200 if (sp == NULL)
201 errx(1, "printer/tcp: unknown service");
202 break;
203 default:
204 usage();
205 }
206
207 #ifndef DEBUG
208 /*
209 * Set up standard environment by detaching from the parent.
210 */
211 daemon(0, 0);
212 #endif
213
214 openlog("lpd", LOG_PID, LOG_LPR);
215 syslog(LOG_INFO, "restarted");
216 (void)umask(0);
217 lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
218 if (lfd < 0) {
219 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
220 exit(1);
221 }
222 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
223 if (errno == EWOULDBLOCK) /* active deamon present */
224 exit(0);
225 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
226 exit(1);
227 }
228 ftruncate(lfd, 0);
229 /*
230 * write process id for others to know
231 */
232 (void)snprintf(line, sizeof(line), "%u\n", getpid());
233 f = strlen(line);
234 if (write(lfd, line, f) != f) {
235 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
236 exit(1);
237 }
238 signal(SIGCHLD, reapchild);
239 /*
240 * Restart all the printers.
241 */
242 startup();
243 (void)unlink(_PATH_SOCKETNAME);
244 funix = socket(AF_LOCAL, SOCK_STREAM, 0);
245 if (funix < 0) {
246 syslog(LOG_ERR, "socket: %m");
247 exit(1);
248 }
249
250 sigemptyset(&nmask);
251 sigaddset(&nmask, SIGHUP);
252 sigaddset(&nmask, SIGINT);
253 sigaddset(&nmask, SIGQUIT);
254 sigaddset(&nmask, SIGTERM);
255 sigprocmask(SIG_BLOCK, &nmask, &omask);
256
257 (void) umask(07);
258 signal(SIGHUP, mcleanup);
259 signal(SIGINT, mcleanup);
260 signal(SIGQUIT, mcleanup);
261 signal(SIGTERM, mcleanup);
262 memset(&un, 0, sizeof(un));
263 un.sun_family = AF_LOCAL;
264 strncpy(un.sun_path, _PATH_SOCKETNAME, sizeof(un.sun_path) - 1);
265 #ifndef SUN_LEN
266 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
267 #endif
268 if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
269 syslog(LOG_ERR, "ubind: %m");
270 exit(1);
271 }
272 (void) umask(0);
273 sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
274 FD_ZERO(&defreadfds);
275 FD_SET(funix, &defreadfds);
276 listen(funix, 5);
277 if (!sflag)
278 finet = socksetup(PF_UNSPEC, options);
279 else
280 finet = NULL; /* pretend we couldn't open TCP socket. */
281
282 if (finet) {
283 for (i = 1; i <= *finet; i++) {
284 FD_SET(finet[i], &defreadfds);
285 listen(finet[i], 5);
286 }
287 }
288 /*
289 * Main loop: accept, do a request, continue.
290 */
291 memset(&frominet, 0, sizeof(frominet));
292 memset(&fromunix, 0, sizeof(fromunix));
293 for (;;) {
294 int domain, nfds, s, fromlen;
295 fd_set readfds;
296 /* "short" so it overflows in about 2 hours */
297 short sleeptime = 10;
298
299 while (child_max < child_count) {
300 syslog(LOG_WARNING,
301 "too many children, sleeping for %d seconds",
302 sleeptime);
303 sleep(sleeptime);
304 sleeptime <<= 1;
305 if (sleeptime < 0) {
306 syslog(LOG_CRIT, "sleeptime overflowed! help!");
307 sleeptime = 10;
308 }
309 }
310
311 FD_COPY(&defreadfds, &readfds);
312 nfds = select(20, &readfds, 0, 0, 0);
313 if (nfds <= 0) {
314 if (nfds < 0 && errno != EINTR)
315 syslog(LOG_WARNING, "select: %m");
316 continue;
317 }
318 if (FD_ISSET(funix, &readfds)) {
319 domain = AF_LOCAL;
320 fromlen = sizeof(fromunix);
321 s = accept(funix,
322 (struct sockaddr *)&fromunix, &fromlen);
323 } else {
324 for (i = 1; i <= *finet; i++)
325 if (FD_ISSET(finet[i], &readfds)) {
326 domain = AF_INET, fromlen = sizeof(frominet);
327 s = accept(finet[i], (struct sockaddr *)&frominet, &fromlen);
328 }
329 }
330 if (s < 0) {
331 if (errno != EINTR)
332 syslog(LOG_WARNING, "accept: %m");
333 continue;
334 }
335
336 switch (fork()) {
337 case 0:
338 signal(SIGCHLD, SIG_IGN);
339 signal(SIGHUP, SIG_IGN);
340 signal(SIGINT, SIG_IGN);
341 signal(SIGQUIT, SIG_IGN);
342 signal(SIGTERM, SIG_IGN);
343 (void)close(funix);
344 if (!sflag && finet)
345 for (i = 1; i <= *finet; i++)
346 (void)close(finet[i]);
347 dup2(s, 1);
348 (void)close(s);
349 if (domain == AF_INET) {
350 /* for both AF_INET and AF_INET6 */
351 from_remote = 1;
352 chkhost((struct sockaddr *)&frominet);
353 } else
354 from_remote = 0;
355 doit();
356 exit(0);
357 case -1:
358 syslog(LOG_WARNING, "fork: %m, sleeping for 10 seconds...");
359 sleep(10);
360 continue;
361 default:
362 child_count++;
363 }
364 (void)close(s);
365 }
366 }
367
368 static void
369 reapchild(signo)
370 int signo;
371 {
372 union wait status;
373
374 while (wait3((int *)&status, WNOHANG, 0) > 0)
375 child_count--;
376 }
377
378 static void
379 mcleanup(signo)
380 int signo;
381 {
382 if (lflag)
383 syslog(LOG_INFO, "exiting");
384 unlink(_PATH_SOCKETNAME);
385 exit(0);
386 }
387
388 /*
389 * Stuff for handling job specifications
390 */
391 char *user[MAXUSERS]; /* users to process */
392 int users; /* # of users in user array */
393 int requ[MAXREQUESTS]; /* job number of spool entries */
394 int requests; /* # of spool requests */
395 char *person; /* name of person doing lprm */
396
397 char fromb[NI_MAXHOST]; /* buffer for client's machine name */
398 char cbuf[BUFSIZ]; /* command line buffer */
399 char *cmdnames[] = {
400 "null",
401 "printjob",
402 "recvjob",
403 "displayq short",
404 "displayq long",
405 "rmjob"
406 };
407
408 static void
409 doit()
410 {
411 char *cp;
412 int n;
413
414 for (;;) {
415 cp = cbuf;
416 do {
417 if (cp >= &cbuf[sizeof(cbuf) - 1])
418 fatal("Command line too long");
419 if ((n = read(1, cp, 1)) != 1) {
420 if (n < 0)
421 fatal("Lost connection");
422 return;
423 }
424 } while (*cp++ != '\n');
425 *--cp = '\0';
426 cp = cbuf;
427 if (lflag) {
428 if (*cp >= '\1' && *cp <= '\5') {
429 syslog(LOG_INFO, "%s requests %s %s",
430 from, cmdnames[(int)*cp], cp+1);
431 setproctitle("serving %s: %s %s", from,
432 cmdnames[(int)*cp], cp+1);
433 }
434 else
435 syslog(LOG_INFO, "bad request (%d) from %s",
436 *cp, from);
437 }
438 switch (*cp++) {
439 case '\1': /* check the queue and print any jobs there */
440 printer = cp;
441 printjob();
442 break;
443 case '\2': /* receive files to be queued */
444 if (!from_remote) {
445 syslog(LOG_INFO, "illegal request (%d)", *cp);
446 exit(1);
447 }
448 printer = cp;
449 recvjob();
450 break;
451 case '\3': /* display the queue (short form) */
452 case '\4': /* display the queue (long form) */
453 printer = cp;
454 while (*cp) {
455 if (*cp != ' ') {
456 cp++;
457 continue;
458 }
459 *cp++ = '\0';
460 while (isspace(*cp))
461 cp++;
462 if (*cp == '\0')
463 break;
464 if (isdigit(*cp)) {
465 if (requests >= MAXREQUESTS)
466 fatal("Too many requests");
467 requ[requests++] = atoi(cp);
468 } else {
469 if (users >= MAXUSERS)
470 fatal("Too many users");
471 user[users++] = cp;
472 }
473 }
474 displayq(cbuf[0] - '\3');
475 exit(0);
476 case '\5': /* remove a job from the queue */
477 if (!from_remote) {
478 syslog(LOG_INFO, "illegal request (%d)", *cp);
479 exit(1);
480 }
481 printer = cp;
482 while (*cp && *cp != ' ')
483 cp++;
484 if (!*cp)
485 break;
486 *cp++ = '\0';
487 person = cp;
488 while (*cp) {
489 if (*cp != ' ') {
490 cp++;
491 continue;
492 }
493 *cp++ = '\0';
494 while (isspace(*cp))
495 cp++;
496 if (*cp == '\0')
497 break;
498 if (isdigit(*cp)) {
499 if (requests >= MAXREQUESTS)
500 fatal("Too many requests");
501 requ[requests++] = atoi(cp);
502 } else {
503 if (users >= MAXUSERS)
504 fatal("Too many users");
505 user[users++] = cp;
506 }
507 }
508 rmjob();
509 break;
510 }
511 fatal("Illegal service request");
512 }
513 }
514
515 /*
516 * Make a pass through the printcap database and start printing any
517 * files left from the last time the machine went down.
518 */
519 static void
520 startup()
521 {
522 char *buf;
523 char *cp;
524
525 /*
526 * Restart the daemons.
527 */
528 while (cgetnext(&buf, printcapdb) > 0) {
529 if (ckqueue(buf) <= 0) {
530 free(buf);
531 continue; /* no work to do for this printer */
532 }
533 for (cp = buf; *cp; cp++)
534 if (*cp == '|' || *cp == ':') {
535 *cp = '\0';
536 break;
537 }
538 if (lflag)
539 syslog(LOG_INFO, "work for %s", buf);
540 switch (fork()) {
541 case -1:
542 syslog(LOG_WARNING, "startup: cannot fork");
543 mcleanup(0);
544 case 0:
545 printer = buf;
546 setproctitle("working on printer %s", printer);
547 cgetclose();
548 printjob();
549 /* NOTREACHED */
550 default:
551 child_count++;
552 free(buf);
553 }
554 }
555 }
556
557 /*
558 * Make sure there's some work to do before forking off a child
559 */
560 static int
561 ckqueue(cap)
562 char *cap;
563 {
564 struct dirent *d;
565 DIR *dirp;
566 char *spooldir;
567
568 if (cgetstr(cap, "sd", &spooldir) == -1)
569 spooldir = _PATH_DEFSPOOL;
570 if ((dirp = opendir(spooldir)) == NULL)
571 return (-1);
572 while ((d = readdir(dirp)) != NULL) {
573 if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
574 continue; /* daemon control files only */
575 closedir(dirp);
576 return (1); /* found something */
577 }
578 closedir(dirp);
579 return (0);
580 }
581
582 #define DUMMY ":nobody::"
583
584 /*
585 * Check to see if the from host has access to the line printer.
586 */
587 static void
588 chkhost(f)
589 struct sockaddr *f;
590 {
591 struct addrinfo hints, *res, *r;
592 FILE *hostf;
593 int first = 1, good = 0;
594 char host[NI_MAXHOST], ip[NI_MAXHOST];
595 char serv[NI_MAXSERV];
596 int error;
597
598 error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
599 NI_NUMERICSERV);
600 if (error || atoi(serv) >= IPPORT_RESERVED)
601 fatal("Malformed from address");
602
603 /* Need real hostname for temporary filenames */
604 error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
605 NI_NAMEREQD);
606 if (error) {
607 error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
608 NI_NUMERICHOST);
609 if (error)
610 fatal("Host name for your address unknown");
611 else
612 fatal("Host name for your address (%s) unknown", host);
613 }
614
615 (void)strncpy(fromb, host, sizeof(fromb) - 1);
616 fromb[sizeof(fromb) - 1] = '\0';
617 from = fromb;
618
619 /* need address in stringform for comparison (no DNS lookup here) */
620 error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
621 NI_NUMERICHOST);
622 if (error)
623 fatal("Cannot print address");
624
625 /* Check for spoof, ala rlogind */
626 memset(&hints, 0, sizeof(hints));
627 hints.ai_family = PF_UNSPEC;
628 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
629 error = getaddrinfo(fromb, NULL, &hints, &res);
630 if (error) {
631 fatal("hostname for your address (%s) unknown: %s", host,
632 gai_strerror(error));
633 }
634 good = 0;
635 for (r = res; good == 0 && r; r = r->ai_next) {
636 error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
637 NULL, 0, NI_NUMERICHOST);
638 if (!error && !strcmp(host, ip))
639 good = 1;
640 }
641 if (res)
642 freeaddrinfo(res);
643 if (good == 0)
644 fatal("address for your hostname (%s) not matched", host);
645 setproctitle("serving %s", from);
646 hostf = fopen(_PATH_HOSTSEQUIV, "r");
647 again:
648 if (hostf) {
649 if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
650 (void)fclose(hostf);
651 return;
652 }
653 (void)fclose(hostf);
654 }
655 if (first == 1) {
656 first = 0;
657 hostf = fopen(_PATH_HOSTSLPD, "r");
658 goto again;
659 }
660 fatal("Your host does not have line printer access");
661 /*NOTREACHED*/
662 }
663
664 static void
665 usage()
666 {
667 extern char *__progname; /* XXX */
668
669 fprintf(stderr, "usage: %s [-dlrs] [-n maxchild] [-w maxwait] [port]\n",
670 __progname);
671 exit(1);
672 }
673
674 /* setup server socket for specified address family */
675 /* if af is PF_UNSPEC more than one socket may be returned */
676 /* the returned list is dynamically allocated, so caller needs to free it */
677 int *
678 socksetup(af, options)
679 int af, options;
680 {
681 struct addrinfo hints, *res, *r;
682 int error, maxs, *s, *socks;
683 const int on = 1;
684
685 memset(&hints, 0, sizeof(hints));
686 hints.ai_flags = AI_PASSIVE;
687 hints.ai_family = af;
688 hints.ai_socktype = SOCK_STREAM;
689 error = getaddrinfo(NULL, "printer", &hints, &res);
690 if (error) {
691 syslog(LOG_ERR, (gai_strerror(error)));
692 mcleanup(0);
693 }
694
695 /* Count max number of sockets we may open */
696 for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
697 ;
698 socks = malloc((maxs + 1) * sizeof(int));
699 if (!socks) {
700 syslog(LOG_ERR, "couldn't allocate memory for sockets");
701 mcleanup(0);
702 }
703
704 *socks = 0; /* num of sockets counter at start of array */
705 s = socks + 1;
706 for (r = res; r; r = r->ai_next) {
707 *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
708 if (*s < 0) {
709 syslog(LOG_DEBUG, "socket(): %m");
710 continue;
711 }
712 if (options & SO_DEBUG)
713 if (setsockopt(*s, SOL_SOCKET, SO_DEBUG,
714 &on, sizeof(on)) < 0) {
715 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
716 close (*s);
717 continue;
718 }
719 if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
720 syslog(LOG_DEBUG, "bind(): %m");
721 close (*s);
722 continue;
723 }
724 *socks = *socks + 1;
725 s++;
726 }
727
728 if (res)
729 freeaddrinfo(res);
730
731 if (*socks == 0) {
732 syslog(LOG_ERR, "Couldn't bind to any socket");
733 free(socks);
734 mcleanup(0);
735 }
736 return(socks);
737 }
738