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