xref: /original-bsd/usr.sbin/lpr/lpd/lpd.c (revision 1a56dd2c)
1 /*
2  * Copyright (c) 1983 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #ifndef lint
14 char copyright[] =
15 "@(#) Copyright (c) 1983 Regents of the University of California.\n\
16  All rights reserved.\n";
17 #endif /* not lint */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#)lpd.c	5.5 (Berkeley) 05/05/88";
21 #endif /* not lint */
22 
23 /*
24  * lpd -- line printer daemon.
25  *
26  * Listen for a connection and perform the requested operation.
27  * Operations are:
28  *	\1printer\n
29  *		check the queue for jobs and print any found.
30  *	\2printer\n
31  *		receive a job from another machine and queue it.
32  *	\3printer [users ...] [jobs ...]\n
33  *		return the current state of the queue (short form).
34  *	\4printer [users ...] [jobs ...]\n
35  *		return the current state of the queue (long form).
36  *	\5printer person [users ...] [jobs ...]\n
37  *		remove jobs from the queue.
38  *
39  * Strategy to maintain protected spooling area:
40  *	1. Spooling area is writable only by daemon and spooling group
41  *	2. lpr runs setuid root and setgrp spooling group; it uses
42  *	   root to access any file it wants (verifying things before
43  *	   with an access call) and group id to know how it should
44  *	   set up ownership of files in the spooling area.
45  *	3. Files in spooling area are owned by root, group spooling
46  *	   group, with mode 660.
47  *	4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
48  *	   access files and printer.  Users can't get to anything
49  *	   w/o help of lpq and lprm programs.
50  */
51 
52 #include "lp.h"
53 
54 int	lflag;				/* log requests flag */
55 
56 int	reapchild();
57 int	mcleanup();
58 
59 main(argc, argv)
60 	int argc;
61 	char **argv;
62 {
63 	int f, funix, finet, options, defreadfds, fromlen;
64 	struct sockaddr_un sun, fromunix;
65 	struct sockaddr_in sin, frominet;
66 	int omask, lfd;
67 
68 	gethostname(host, sizeof(host));
69 	name = argv[0];
70 
71 	while (--argc > 0) {
72 		argv++;
73 		if (argv[0][0] == '-')
74 			switch (argv[0][1]) {
75 			case 'd':
76 				options |= SO_DEBUG;
77 				break;
78 			case 'l':
79 				lflag++;
80 				break;
81 			}
82 	}
83 
84 #ifndef DEBUG
85 	/*
86 	 * Set up standard environment by detaching from the parent.
87 	 */
88 	if (fork())
89 		exit(0);
90 	for (f = 0; f < 5; f++)
91 		(void) close(f);
92 	(void) open("/dev/null", O_RDONLY);
93 	(void) open("/dev/null", O_WRONLY);
94 	(void) dup(1);
95 	f = open("/dev/tty", O_RDWR);
96 	if (f > 0) {
97 		ioctl(f, TIOCNOTTY, 0);
98 		(void) close(f);
99 	}
100 #endif
101 
102 	openlog("lpd", LOG_PID, LOG_LPR);
103 	(void) umask(0);
104 	lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644);
105 	if (lfd < 0) {
106 		syslog(LOG_ERR, "%s: %m", MASTERLOCK);
107 		exit(1);
108 	}
109 	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
110 		if (errno == EWOULDBLOCK)	/* active deamon present */
111 			exit(0);
112 		syslog(LOG_ERR, "%s: %m", MASTERLOCK);
113 		exit(1);
114 	}
115 	ftruncate(lfd, 0);
116 	/*
117 	 * write process id for others to know
118 	 */
119 	sprintf(line, "%u\n", getpid());
120 	f = strlen(line);
121 	if (write(lfd, line, f) != f) {
122 		syslog(LOG_ERR, "%s: %m", MASTERLOCK);
123 		exit(1);
124 	}
125 	signal(SIGCHLD, reapchild);
126 	/*
127 	 * Restart all the printers.
128 	 */
129 	startup();
130 	(void) unlink(SOCKETNAME);
131 	funix = socket(AF_UNIX, SOCK_STREAM, 0);
132 	if (funix < 0) {
133 		syslog(LOG_ERR, "socket: %m");
134 		exit(1);
135 	}
136 #define	mask(s)	(1 << ((s) - 1))
137 	omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
138 	signal(SIGHUP, mcleanup);
139 	signal(SIGINT, mcleanup);
140 	signal(SIGQUIT, mcleanup);
141 	signal(SIGTERM, mcleanup);
142 	sun.sun_family = AF_UNIX;
143 	strcpy(sun.sun_path, SOCKETNAME);
144 	if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) {
145 		syslog(LOG_ERR, "ubind: %m");
146 		exit(1);
147 	}
148 	sigsetmask(omask);
149 	defreadfds = 1 << funix;
150 	listen(funix, 5);
151 	finet = socket(AF_INET, SOCK_STREAM, 0);
152 	if (finet >= 0) {
153 		struct servent *sp;
154 
155 		if (options & SO_DEBUG)
156 			if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
157 				syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
158 				mcleanup();
159 			}
160 		sp = getservbyname("printer", "tcp");
161 		if (sp == NULL) {
162 			syslog(LOG_ERR, "printer/tcp: unknown service");
163 			mcleanup();
164 		}
165 		sin.sin_family = AF_INET;
166 		sin.sin_port = sp->s_port;
167 		if (bind(finet, &sin, sizeof(sin), 0) < 0) {
168 			syslog(LOG_ERR, "bind: %m");
169 			mcleanup();
170 		}
171 		defreadfds |= 1 << finet;
172 		listen(finet, 5);
173 	}
174 	/*
175 	 * Main loop: accept, do a request, continue.
176 	 */
177 	for (;;) {
178 		int domain, nfds, s, readfds = defreadfds;
179 
180 		nfds = select(20, &readfds, 0, 0, 0);
181 		if (nfds <= 0) {
182 			if (nfds < 0 && errno != EINTR)
183 				syslog(LOG_WARNING, "select: %m");
184 			continue;
185 		}
186 		if (readfds & (1 << funix)) {
187 			domain = AF_UNIX, fromlen = sizeof(fromunix);
188 			s = accept(funix, &fromunix, &fromlen);
189 		} else if (readfds & (1 << finet)) {
190 			domain = AF_INET, fromlen = sizeof(frominet);
191 			s = accept(finet, &frominet, &fromlen);
192 		}
193 		if (s < 0) {
194 			if (errno != EINTR)
195 				syslog(LOG_WARNING, "accept: %m");
196 			continue;
197 		}
198 		if (fork() == 0) {
199 			signal(SIGCHLD, SIG_IGN);
200 			signal(SIGHUP, SIG_IGN);
201 			signal(SIGINT, SIG_IGN);
202 			signal(SIGQUIT, SIG_IGN);
203 			signal(SIGTERM, SIG_IGN);
204 			(void) close(funix);
205 			(void) close(finet);
206 			dup2(s, 1);
207 			(void) close(s);
208 			if (domain == AF_INET)
209 				chkhost(&frominet);
210 			doit();
211 			exit(0);
212 		}
213 		(void) close(s);
214 	}
215 }
216 
217 reapchild()
218 {
219 	union wait status;
220 
221 	while (wait3(&status, WNOHANG, 0) > 0)
222 		;
223 }
224 
225 mcleanup()
226 {
227 	if (lflag)
228 		syslog(LOG_INFO, "exiting");
229 	unlink(SOCKETNAME);
230 	exit(0);
231 }
232 
233 /*
234  * Stuff for handling job specifications
235  */
236 char	*user[MAXUSERS];	/* users to process */
237 int	users;			/* # of users in user array */
238 int	requ[MAXREQUESTS];	/* job number of spool entries */
239 int	requests;		/* # of spool requests */
240 char	*person;		/* name of person doing lprm */
241 
242 char	fromb[32];	/* buffer for client's machine name */
243 char	cbuf[BUFSIZ];	/* command line buffer */
244 char	*cmdnames[] = {
245 	"null",
246 	"printjob",
247 	"recvjob",
248 	"displayq short",
249 	"displayq long",
250 	"rmjob"
251 };
252 
253 doit()
254 {
255 	register char *cp;
256 	register int n;
257 
258 	for (;;) {
259 		cp = cbuf;
260 		do {
261 			if (cp >= &cbuf[sizeof(cbuf) - 1])
262 				fatal("Command line too long");
263 			if ((n = read(1, cp, 1)) != 1) {
264 				if (n < 0)
265 					fatal("Lost connection");
266 				return;
267 			}
268 		} while (*cp++ != '\n');
269 		*--cp = '\0';
270 		cp = cbuf;
271 		if (lflag) {
272 			if (*cp >= '\1' && *cp <= '\5')
273 				syslog(LOG_INFO, "%s requests %s %s",
274 					from, cmdnames[*cp], cp+1);
275 			else
276 				syslog(LOG_INFO, "bad request (%d) from %s",
277 					*cp, from);
278 		}
279 		switch (*cp++) {
280 		case '\1':	/* check the queue and print any jobs there */
281 			printer = cp;
282 			printjob();
283 			break;
284 		case '\2':	/* receive files to be queued */
285 			printer = cp;
286 			recvjob();
287 			break;
288 		case '\3':	/* display the queue (short form) */
289 		case '\4':	/* display the queue (long form) */
290 			printer = cp;
291 			while (*cp) {
292 				if (*cp != ' ') {
293 					cp++;
294 					continue;
295 				}
296 				*cp++ = '\0';
297 				while (isspace(*cp))
298 					cp++;
299 				if (*cp == '\0')
300 					break;
301 				if (isdigit(*cp)) {
302 					if (requests >= MAXREQUESTS)
303 						fatal("Too many requests");
304 					requ[requests++] = atoi(cp);
305 				} else {
306 					if (users >= MAXUSERS)
307 						fatal("Too many users");
308 					user[users++] = cp;
309 				}
310 			}
311 			displayq(cbuf[0] - '\3');
312 			exit(0);
313 		case '\5':	/* remove a job from the queue */
314 			printer = cp;
315 			while (*cp && *cp != ' ')
316 				cp++;
317 			if (!*cp)
318 				break;
319 			*cp++ = '\0';
320 			person = cp;
321 			while (*cp) {
322 				if (*cp != ' ') {
323 					cp++;
324 					continue;
325 				}
326 				*cp++ = '\0';
327 				while (isspace(*cp))
328 					cp++;
329 				if (*cp == '\0')
330 					break;
331 				if (isdigit(*cp)) {
332 					if (requests >= MAXREQUESTS)
333 						fatal("Too many requests");
334 					requ[requests++] = atoi(cp);
335 				} else {
336 					if (users >= MAXUSERS)
337 						fatal("Too many users");
338 					user[users++] = cp;
339 				}
340 			}
341 			rmjob();
342 			break;
343 		}
344 		fatal("Illegal service request");
345 	}
346 }
347 
348 /*
349  * Make a pass through the printcap database and start printing any
350  * files left from the last time the machine went down.
351  */
352 startup()
353 {
354 	char buf[BUFSIZ];
355 	register char *cp;
356 	int pid;
357 
358 	printer = buf;
359 
360 	/*
361 	 * Restart the daemons.
362 	 */
363 	while (getprent(buf) > 0) {
364 		for (cp = buf; *cp; cp++)
365 			if (*cp == '|' || *cp == ':') {
366 				*cp = '\0';
367 				break;
368 			}
369 		if ((pid = fork()) < 0) {
370 			syslog(LOG_WARNING, "startup: cannot fork");
371 			mcleanup();
372 		}
373 		if (!pid) {
374 			endprent();
375 			printjob();
376 		}
377 	}
378 }
379 
380 #define DUMMY ":nobody::"
381 
382 /*
383  * Check to see if the from host has access to the line printer.
384  */
385 chkhost(f)
386 	struct sockaddr_in *f;
387 {
388 	register struct hostent *hp;
389 	register FILE *hostf;
390 	register char *cp, *sp;
391 	char ahost[50];
392 	int first = 1;
393 	extern char *inet_ntoa();
394 	int baselen = -1;
395 
396 	f->sin_port = ntohs(f->sin_port);
397 	if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
398 		fatal("Malformed from address");
399 	hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
400 	if (hp == 0)
401 		fatal("Host name for your address (%s) unknown",
402 			inet_ntoa(f->sin_addr));
403 
404 	strcpy(fromb, hp->h_name);
405 	from = fromb;
406 	if (!strcmp(from, host))
407 		return;
408 
409 	sp = fromb;
410 	cp = ahost;
411 	while (*sp) {
412 		if (*sp == '.') {
413 			if (baselen == -1)
414 				baselen = sp - fromb;
415 			*cp++ = *sp++;
416 		} else {
417 			*cp++ = isupper(*sp) ? tolower(*sp++) : *sp++;
418 		}
419 	}
420 	*cp = '\0';
421 	hostf = fopen("/etc/hosts.equiv", "r");
422 again:
423 	if (hostf) {
424 		if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) {
425 			(void) fclose(hostf);
426 			return;
427 		}
428 		(void) fclose(hostf);
429 	}
430 	if (first == 1) {
431 		first = 0;
432 		hostf = fopen("/etc/hosts.lpd", "r");
433 		goto again;
434 	}
435 	fatal("Your host does not have line printer access");
436 }
437