xref: /freebsd/usr.sbin/lpr/lpd/lpd.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
1 /*
2  * Copyright (c) 1983, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by the University of
17  *	California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1983, 1993, 1994\n\
38 	The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40 
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)lpd.c	8.7 (Berkeley) 5/10/95";
44 #endif
45 static const char rcsid[] =
46   "$FreeBSD$";
47 #endif /* not lint */
48 
49 /*
50  * lpd -- line printer daemon.
51  *
52  * Listen for a connection and perform the requested operation.
53  * Operations are:
54  *	\1printer\n
55  *		check the queue for jobs and print any found.
56  *	\2printer\n
57  *		receive a job from another machine and queue it.
58  *	\3printer [users ...] [jobs ...]\n
59  *		return the current state of the queue (short form).
60  *	\4printer [users ...] [jobs ...]\n
61  *		return the current state of the queue (long form).
62  *	\5printer person [users ...] [jobs ...]\n
63  *		remove jobs from the queue.
64  *
65  * Strategy to maintain protected spooling area:
66  *	1. Spooling area is writable only by daemon and spooling group
67  *	2. lpr runs setuid root and setgrp spooling group; it uses
68  *	   root to access any file it wants (verifying things before
69  *	   with an access call) and group id to know how it should
70  *	   set up ownership of files in the spooling area.
71  *	3. Files in spooling area are owned by root, group spooling
72  *	   group, with mode 660.
73  *	4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
74  *	   access files and printer.  Users can't get to anything
75  *	   w/o help of lpq and lprm programs.
76  */
77 
78 #include <sys/param.h>
79 #include <sys/wait.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <sys/un.h>
83 #include <sys/stat.h>
84 #include <sys/file.h>
85 #include <netinet/in.h>
86 #include <arpa/inet.h>
87 
88 #include <netdb.h>
89 #include <unistd.h>
90 #include <syslog.h>
91 #include <signal.h>
92 #include <err.h>
93 #include <errno.h>
94 #include <fcntl.h>
95 #include <dirent.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <string.h>
99 #include <sysexits.h>
100 #include <ctype.h>
101 #include "lp.h"
102 #include "lp.local.h"
103 #include "pathnames.h"
104 #include "extern.h"
105 
106 int	lflag;				/* log requests flag */
107 int	pflag;				/* no incoming port flag */
108 int	from_remote;			/* from remote socket */
109 
110 int		  main __P((int, char **));
111 static void       reapchild __P((int));
112 static void       mcleanup __P((int));
113 static void       doit __P((void));
114 static void       startup __P((void));
115 static void       chkhost __P((struct sockaddr_in *));
116 static int	  ckqueue __P((struct printer *));
117 static void	  usage __P((void));
118 /* From rcmd.c: */
119 int		  __ivaliduser __P((FILE *, u_long, const char *,
120 				    const char *));
121 
122 uid_t	uid, euid;
123 
124 int
125 main(argc, argv)
126 	int argc;
127 	char **argv;
128 {
129 	int errs, f, funix, finet, fromlen, i, socket_debug;
130 	fd_set defreadfds;
131 	struct sockaddr_un un, fromunix;
132 	struct sockaddr_in sin, frominet;
133 	int lfd;
134 	sigset_t omask, nmask;
135 	struct servent *sp, serv;
136 
137 	euid = geteuid();	/* these shouldn't be different */
138 	uid = getuid();
139 	socket_debug = 0;
140 	gethostname(host, sizeof(host));
141 
142 	name = "lpd";
143 
144 	if (euid != 0)
145 		errx(EX_NOPERM,"must run as root");
146 
147 	errs = 0;
148 	while ((i = getopt(argc, argv, "dlp")) != -1)
149 		switch (i) {
150 		case 'd':
151 			socket_debug++;
152 			break;
153 		case 'l':
154 			lflag++;
155 			break;
156 		case 'p':
157 			pflag++;
158 			break;
159 		default:
160 			errs++;
161 		}
162 	argc -= optind;
163 	argv += optind;
164 	if (errs)
165 		usage();
166 
167 	if (argc == 1) {
168 		if ((i = atoi(argv[0])) == 0)
169 			usage();
170 		if (i < 0 || i > USHRT_MAX)
171 			errx(EX_USAGE, "port # %d is invalid", i);
172 
173 		serv.s_port = htons(i);
174 		sp = &serv;
175 		argc--;
176 	} else {
177 		sp = getservbyname("printer", "tcp");
178 		if (sp == NULL)
179 			errx(EX_OSFILE, "printer/tcp: unknown service");
180 	}
181 
182 	if (argc != 0)
183 		usage();
184 
185 	/*
186 	 * We run chkprintcap right away to catch any errors and blat them
187 	 * to stderr while we still have it open, rather than sending them
188 	 * to syslog and leaving the user wondering why lpd started and
189 	 * then stopped.  There should probably be a command-line flag to
190 	 * ignore errors from chkprintcap.
191 	 */
192 	{
193 		pid_t pid;
194 		int status;
195 		pid = fork();
196 		if (pid < 0) {
197 			err(EX_OSERR, "cannot fork");
198 		} else if (pid == 0) {	/* child */
199 			execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0);
200 			err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP);
201 		}
202 		if (waitpid(pid, &status, 0) < 0) {
203 			err(EX_OSERR, "cannot wait");
204 		}
205 		if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
206 			errx(EX_OSFILE, "%d errors in printcap file, exiting",
207 			     WEXITSTATUS(status));
208 	}
209 
210 #ifndef DEBUG
211 	/*
212 	 * Set up standard environment by detaching from the parent.
213 	 */
214 	daemon(0, 0);
215 #endif
216 
217 	openlog("lpd", LOG_PID, LOG_LPR);
218 	syslog(LOG_INFO, "lpd startup: logging=%d%s", lflag,
219 	    socket_debug ? " dbg" : "");
220 	(void) umask(0);
221 	/*
222 	 * NB: This depends on O_NONBLOCK semantics doing the right thing;
223 	 * i.e., applying only to the O_EXLOCK and not to the rest of the
224 	 * open/creation.  As of 1997-12-02, this is the case for commonly-
225 	 * used filesystems.  There are other places in this code which
226 	 * make the same assumption.
227 	 */
228 	lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
229 		   LOCK_FILE_MODE);
230 	if (lfd < 0) {
231 		if (errno == EWOULDBLOCK)	/* active deamon present */
232 			exit(0);
233 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
234 		exit(1);
235 	}
236 	fcntl(lfd, F_SETFL, 0);	/* turn off non-blocking mode */
237 	ftruncate(lfd, 0);
238 	/*
239 	 * write process id for others to know
240 	 */
241 	sprintf(line, "%u\n", getpid());
242 	f = strlen(line);
243 	if (write(lfd, line, f) != f) {
244 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
245 		exit(1);
246 	}
247 	signal(SIGCHLD, reapchild);
248 	/*
249 	 * Restart all the printers.
250 	 */
251 	startup();
252 	(void) unlink(_PATH_SOCKETNAME);
253 	funix = socket(AF_UNIX, SOCK_STREAM, 0);
254 	if (funix < 0) {
255 		syslog(LOG_ERR, "socket: %m");
256 		exit(1);
257 	}
258 
259 	sigemptyset(&nmask);
260 	sigaddset(&nmask, SIGHUP);
261 	sigaddset(&nmask, SIGINT);
262 	sigaddset(&nmask, SIGQUIT);
263 	sigaddset(&nmask, SIGTERM);
264 	sigprocmask(SIG_BLOCK, &nmask, &omask);
265 
266 	(void) umask(07);
267 	signal(SIGHUP, mcleanup);
268 	signal(SIGINT, mcleanup);
269 	signal(SIGQUIT, mcleanup);
270 	signal(SIGTERM, mcleanup);
271 	memset(&un, 0, sizeof(un));
272 	un.sun_family = AF_UNIX;
273 	strcpy(un.sun_path, _PATH_SOCKETNAME);
274 #ifndef SUN_LEN
275 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
276 #endif
277 	if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
278 		syslog(LOG_ERR, "ubind: %m");
279 		exit(1);
280 	}
281 	(void) umask(0);
282 	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
283 	FD_ZERO(&defreadfds);
284 	FD_SET(funix, &defreadfds);
285 	listen(funix, 5);
286 	finet = -1;
287 	if (pflag == 0) {
288 		finet = socket(AF_INET, SOCK_STREAM, 0);
289 		if (finet >= 0) {
290 			i = 1;
291 			if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, &i,
292 			    sizeof i) < 0) {
293 				syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
294 				mcleanup(0);
295 			}
296 			if (socket_debug &&
297 			    setsockopt(finet, SOL_SOCKET, SO_DEBUG,
298 				&socket_debug, sizeof(socket_debug)) < 0) {
299 				syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
300 				mcleanup(0);
301 			}
302 			memset(&sin, 0, sizeof(sin));
303 			sin.sin_family = AF_INET;
304 			sin.sin_port = sp->s_port;
305 			if (bind(finet, (struct sockaddr *)&sin,
306 			    sizeof(sin)) < 0) {
307 				syslog(LOG_ERR, "bind: %m");
308 				mcleanup(0);
309 			}
310 			FD_SET(finet, &defreadfds);
311 			listen(finet, 5);
312 		}
313 	}
314 	/*
315 	 * Main loop: accept, do a request, continue.
316 	 */
317 	memset(&frominet, 0, sizeof(frominet));
318 	memset(&fromunix, 0, sizeof(fromunix));
319 	if (lflag)
320 		syslog(LOG_INFO, "lpd startup: ready to accept requests");
321 	/*
322 	 * XXX - should be redone for multi-protocol
323 	 */
324 	for (;;) {
325 		int domain, nfds, s;
326 		fd_set readfds;
327 
328 		FD_COPY(&defreadfds, &readfds);
329 		nfds = select(20, &readfds, 0, 0, 0);
330 		if (nfds <= 0) {
331 			if (nfds < 0 && errno != EINTR)
332 				syslog(LOG_WARNING, "select: %m");
333 			continue;
334 		}
335 		domain = 0;			/* avoid compile-time warning */
336 		s = 0;				/* avoid compile-time warning */
337 		if (FD_ISSET(funix, &readfds)) {
338 			domain = AF_UNIX, fromlen = sizeof(fromunix);
339 			s = accept(funix,
340 			    (struct sockaddr *)&fromunix, &fromlen);
341 		} else if (pflag == 0) /* if (FD_ISSET(finet, &readfds)) */  {
342 			domain = AF_INET, fromlen = sizeof(frominet);
343 			s = accept(finet,
344 			    (struct sockaddr *)&frominet, &fromlen);
345 			if (frominet.sin_port == htons(20)) {
346 				close(s);
347 				continue;
348 			}
349 		}
350 		if (s < 0) {
351 			if (errno != EINTR)
352 				syslog(LOG_WARNING, "accept: %m");
353 			continue;
354 		}
355 		if (fork() == 0) {
356 			signal(SIGCHLD, SIG_IGN);
357 			signal(SIGHUP, SIG_IGN);
358 			signal(SIGINT, SIG_IGN);
359 			signal(SIGQUIT, SIG_IGN);
360 			signal(SIGTERM, SIG_IGN);
361 			(void) close(funix);
362 			if (pflag == 0) {
363 				(void) close(finet);
364 			}
365 			dup2(s, 1);
366 			(void) close(s);
367 			if (domain == AF_INET) {
368 				from_remote = 1;
369 				chkhost(&frominet);
370 			} else
371 				from_remote = 0;
372 			doit();
373 			exit(0);
374 		}
375 		(void) close(s);
376 	}
377 }
378 
379 static void
380 reapchild(signo)
381 	int signo;
382 {
383 	union wait status;
384 
385 	while (wait3((int *)&status, WNOHANG, 0) > 0)
386 		;
387 }
388 
389 static void
390 mcleanup(signo)
391 	int signo;
392 {
393 	/*
394 	 * XXX syslog(3) is not signal-safe.
395 	 */
396 	if (lflag) {
397 		if (signo)
398 			syslog(LOG_INFO, "exiting on signal %d", signo);
399 		else
400 			syslog(LOG_INFO, "exiting");
401 	}
402 	unlink(_PATH_SOCKETNAME);
403 	exit(0);
404 }
405 
406 /*
407  * Stuff for handling job specifications
408  */
409 char	*user[MAXUSERS];	/* users to process */
410 int	users;			/* # of users in user array */
411 int	requ[MAXREQUESTS];	/* job number of spool entries */
412 int	requests;		/* # of spool requests */
413 char	*person;		/* name of person doing lprm */
414 
415 char	fromb[MAXHOSTNAMELEN];	/* buffer for client's machine name */
416 char	cbuf[BUFSIZ];		/* command line buffer */
417 char	*cmdnames[] = {
418 	"null",
419 	"printjob",
420 	"recvjob",
421 	"displayq short",
422 	"displayq long",
423 	"rmjob"
424 };
425 
426 static void
427 doit()
428 {
429 	char *cp, *printer;
430 	int n;
431 	int status;
432 	struct printer myprinter, *pp = &myprinter;
433 
434 	init_printer(&myprinter);
435 
436 	for (;;) {
437 		cp = cbuf;
438 		do {
439 			if (cp >= &cbuf[sizeof(cbuf) - 1])
440 				fatal(0, "Command line too long");
441 			if ((n = read(1, cp, 1)) != 1) {
442 				if (n < 0)
443 					fatal(0, "Lost connection");
444 				return;
445 			}
446 		} while (*cp++ != '\n');
447 		*--cp = '\0';
448 		cp = cbuf;
449 		if (lflag) {
450 			if (*cp >= '\1' && *cp <= '\5')
451 				syslog(LOG_INFO, "%s requests %s %s",
452 					from, cmdnames[(u_char)*cp], cp+1);
453 			else
454 				syslog(LOG_INFO, "bad request (%d) from %s",
455 					*cp, from);
456 		}
457 		switch (*cp++) {
458 		case CMD_CHECK_QUE: /* check the queue, print any jobs there */
459 			startprinting(cp);
460 			break;
461 		case CMD_TAKE_THIS: /* receive files to be queued */
462 			if (!from_remote) {
463 				syslog(LOG_INFO, "illegal request (%d)", *cp);
464 				exit(1);
465 			}
466 			recvjob(cp);
467 			break;
468 		case CMD_SHOWQ_SHORT: /* display the queue (short form) */
469 		case CMD_SHOWQ_LONG: /* display the queue (long form) */
470 			/* XXX - this all needs to be redone. */
471 			printer = cp;
472 			while (*cp) {
473 				if (*cp != ' ') {
474 					cp++;
475 					continue;
476 				}
477 				*cp++ = '\0';
478 				while (isspace(*cp))
479 					cp++;
480 				if (*cp == '\0')
481 					break;
482 				if (isdigit(*cp)) {
483 					if (requests >= MAXREQUESTS)
484 						fatal(0, "Too many requests");
485 					requ[requests++] = atoi(cp);
486 				} else {
487 					if (users >= MAXUSERS)
488 						fatal(0, "Too many users");
489 					user[users++] = cp;
490 				}
491 			}
492 			status = getprintcap(printer, pp);
493 			if (status < 0)
494 				fatal(pp, pcaperr(status));
495 			displayq(pp, cbuf[0] == CMD_SHOWQ_LONG);
496 			exit(0);
497 		case CMD_RMJOB:	/* remove a job from the queue */
498 			if (!from_remote) {
499 				syslog(LOG_INFO, "illegal request (%d)", *cp);
500 				exit(1);
501 			}
502 			printer = cp;
503 			while (*cp && *cp != ' ')
504 				cp++;
505 			if (!*cp)
506 				break;
507 			*cp++ = '\0';
508 			person = cp;
509 			while (*cp) {
510 				if (*cp != ' ') {
511 					cp++;
512 					continue;
513 				}
514 				*cp++ = '\0';
515 				while (isspace(*cp))
516 					cp++;
517 				if (*cp == '\0')
518 					break;
519 				if (isdigit(*cp)) {
520 					if (requests >= MAXREQUESTS)
521 						fatal(0, "Too many requests");
522 					requ[requests++] = atoi(cp);
523 				} else {
524 					if (users >= MAXUSERS)
525 						fatal(0, "Too many users");
526 					user[users++] = cp;
527 				}
528 			}
529 			rmjob(printer);
530 			break;
531 		}
532 		fatal(0, "Illegal service request");
533 	}
534 }
535 
536 /*
537  * Make a pass through the printcap database and start printing any
538  * files left from the last time the machine went down.
539  */
540 static void
541 startup()
542 {
543 	int pid, status, more;
544 	struct printer myprinter, *pp = &myprinter;
545 
546 	more = firstprinter(pp, &status);
547 	if (status)
548 		goto errloop;
549 	while (more) {
550 		if (ckqueue(pp) <= 0) {
551 			goto next;
552 		}
553 		if (lflag)
554 			syslog(LOG_INFO, "lpd startup: work for %s",
555 			    pp->printer);
556 		if ((pid = fork()) < 0) {
557 			syslog(LOG_WARNING, "lpd startup: cannot fork for %s",
558 			    pp->printer);
559 			mcleanup(0);
560 		}
561 		if (pid == 0) {
562 			lastprinter();
563 			printjob(pp);
564 			/* NOTREACHED */
565 		}
566 		do {
567 next:
568 			more = nextprinter(pp, &status);
569 errloop:
570 			if (status)
571 				syslog(LOG_WARNING,
572 				    "lpd startup: printcap entry for %s has errors, skipping",
573 				    pp->printer ? pp->printer : "<???>");
574 		} while (more && status);
575 	}
576 }
577 
578 /*
579  * Make sure there's some work to do before forking off a child
580  */
581 static int
582 ckqueue(pp)
583 	struct printer *pp;
584 {
585 	register struct dirent *d;
586 	DIR *dirp;
587 	char *spooldir;
588 
589 	spooldir = pp->spool_dir;
590 	if ((dirp = opendir(spooldir)) == NULL)
591 		return (-1);
592 	while ((d = readdir(dirp)) != NULL) {
593 		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
594 			continue;	/* daemon control files only */
595 		closedir(dirp);
596 		return (1);		/* found something */
597 	}
598 	closedir(dirp);
599 	return (0);
600 }
601 
602 #define DUMMY ":nobody::"
603 
604 /*
605  * Check to see if the from host has access to the line printer.
606  */
607 static void
608 chkhost(f)
609 	struct sockaddr_in *f;
610 {
611 	register struct hostent *hp;
612 	register FILE *hostf;
613 	int first = 1;
614 	int good = 0;
615 
616 	/* Need real hostname for temporary filenames */
617 	hp = gethostbyaddr((char *)&f->sin_addr,
618 	    sizeof(struct in_addr), f->sin_family);
619 	if (hp == NULL)
620 		fatal(0, "Host name for your address (%s) unknown",
621 			inet_ntoa(f->sin_addr));
622 
623 	(void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1);
624 	fromb[sizeof(fromb) - 1] = '\0';
625 	from = fromb;
626 	strncpy(from_ip, inet_ntoa(f->sin_addr), MAXIPSTRLEN);
627 	from_ip[sizeof(from_ip) - 1] = '\0';
628 
629 	/* Check for spoof, ala rlogind */
630 	hp = gethostbyname(fromb);
631 	if (!hp)
632 		fatal(0, "hostname for your address (%s) unknown", from_ip);
633 	for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) {
634 		if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr,
635 		    sizeof(f->sin_addr)))
636 			good = 1;
637 	}
638 	if (good == 0)
639 		fatal(0, "address for your hostname (%s) not matched",
640 		    from_ip);
641 
642 	hostf = fopen(_PATH_HOSTSEQUIV, "r");
643 again:
644 	if (hostf) {
645 		if (__ivaliduser(hostf, f->sin_addr.s_addr,
646 		    DUMMY, DUMMY) == 0) {
647 			(void) fclose(hostf);
648 			return;
649 		}
650 		(void) fclose(hostf);
651 	}
652 	if (first == 1) {
653 		first = 0;
654 		hostf = fopen(_PATH_HOSTSLPD, "r");
655 		goto again;
656 	}
657 	fatal(0, "Your host does not have line printer access");
658 	/*NOTREACHED*/
659 }
660 
661 static void
662 usage()
663 {
664 	fprintf(stderr, "usage: lpd [-dlp] [port#]\n");
665 	exit(EX_USAGE);
666 }
667