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