xref: /openbsd/usr.sbin/lpr/lpd/recvjob.c (revision 09467b48)
1 /*	$OpenBSD: recvjob.c,v 1.26 2015/01/16 06:40:18 deraadt Exp $	*/
2 /*	$NetBSD: recvjob.c,v 1.14 2001/12/04 22:52:44 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
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  * Receive printer jobs from the network, queue them and
36  * start the printer daemon.
37  */
38 #include <sys/types.h>
39 #include <sys/mount.h>
40 #include <sys/stat.h>
41 
42 #include <unistd.h>
43 #include <signal.h>
44 #include <fcntl.h>
45 #include <dirent.h>
46 #include <syslog.h>
47 #include <stdio.h>
48 #include <errno.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <stdarg.h>
52 #include <limits.h>
53 #include "lp.h"
54 #include "lp.local.h"
55 #include "extern.h"
56 #include "pathnames.h"
57 
58 #define ack()	(void)write(STDOUT_FILENO, sp, 1);
59 
60 static char	 dfname[NAME_MAX];	/* data files */
61 static int	 minfree;       /* keep at least minfree blocks available */
62 static char	*sp = "";
63 static char	 tfname[NAME_MAX];	/* tmp copy of cf before linking */
64 
65 static int        chksize(int);
66 static void       frecverr(const char *, ...)
67 	__attribute__((__format__(__printf__, 1, 2)));
68 static int        noresponse(void);
69 static void       rcleanup(int);
70 static int        read_number(char *);
71 static int        readfile(char *, int);
72 static int        readjob(void);
73 
74 
75 void
76 recvjob(void)
77 {
78 	struct stat stb;
79 	int status;
80 
81 	/*
82 	 * Perform lookup for printer name or abbreviation
83 	 */
84 	if ((status = cgetent(&bp, printcapdb, printer)) == -2)
85 		frecverr("cannot open printer description file");
86 	else if (status == -1)
87 		frecverr("unknown printer %s", printer);
88 	else if (status == -3)
89 		fatal("potential reference loop detected in printcap file");
90 
91 	if (cgetstr(bp, "lf", &LF) == -1)
92 		LF = _PATH_CONSOLE;
93 	if (cgetstr(bp, "sd", &SD) == -1)
94 		SD = _PATH_DEFSPOOL;
95 	if (cgetstr(bp, "lo", &LO) == -1)
96 		LO = DEFLOCK;
97 
98 	(void)close(2);			/* set up log file */
99 	PRIV_START;
100 	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
101 		syslog(LOG_ERR, "%s: %m", LF);
102 		(void)open(_PATH_DEVNULL, O_WRONLY);
103 	}
104 	PRIV_END;
105 
106 	if (chdir(SD) < 0)
107 		frecverr("%s: %s: %m", printer, SD);
108 	if (stat(LO, &stb) == 0) {
109 		if (stb.st_mode & 010) {
110 			/* queue is disabled */
111 			putchar('\1');		/* return error code */
112 			exit(1);
113 		}
114 	} else if (stat(SD, &stb) < 0)
115 		frecverr("%s: %s: %m", printer, SD);
116 
117 	minfree = 2 * read_number("minfree");	/* scale KB to 512 blocks */
118 	signal(SIGTERM, rcleanup);
119 	signal(SIGPIPE, rcleanup);
120 
121 	if (readjob())
122 		printjob();
123 }
124 
125 /*
126  * Read printer jobs sent by lpd and copy them to the spooling directory.
127  * Return the number of jobs successfully transferred.
128  */
129 static int
130 readjob(void)
131 {
132 	int size, nfiles;
133 	char *cp;
134 
135 	ack();
136 	nfiles = 0;
137 	for (;;) {
138 		/*
139 		 * Read a command to tell us what to do
140 		 */
141 		cp = line;
142 		do {
143 			if ((size = read(STDOUT_FILENO, cp, 1)) != 1) {
144 				if (size < 0)
145 					frecverr("%s: Lost connection",
146 					    printer);
147 				return(nfiles);
148 			}
149 		} while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
150 		if (cp - line + 1 >= sizeof(line))
151 			frecverr("readjob overflow");
152 		*--cp = '\0';
153 		cp = line;
154 		switch (*cp++) {
155 		case '\1':	/* cleanup because data sent was bad */
156 			rcleanup(0);
157 			continue;
158 
159 		case '\2':	/* read cf file */
160 			size = 0;
161 			while (*cp >= '0' && *cp <= '9')
162 				size = size * 10 + (*cp++ - '0');
163 			if (*cp++ != ' ')
164 				break;
165 			/*
166 			 * host name has been authenticated, we use our
167 			 * view of the host name since we may be passed
168 			 * something different than what gethostbyaddr()
169 			 * returns
170 			 */
171 			strlcpy(cp + 6, from, sizeof(line) + line - cp - 6);
172 			if (strchr(cp, '/'))
173 				frecverr("readjob: %s: illegal path name", cp);
174 			strlcpy(tfname, cp, sizeof(tfname));
175 			tfname[0] = 't';
176 			if (!chksize(size)) {
177 				(void)write(STDOUT_FILENO, "\2", 1);
178 				continue;
179 			}
180 			/*
181 			 * XXX
182 			 * We blindly believe what the remote host puts
183 			 * for the path to the df file.  In general this
184 			 * is OK since we don't allow paths with '/' in
185 			 * them.  Still, it would be better to sanity
186 			 * check the cf file sent to us and make the
187 			 * df name match the cf name we used.  That way
188 			 * we avoid any possible collisions.
189 			 */
190 			if (!readfile(tfname, size)) {
191 				rcleanup(0);
192 				continue;
193 			}
194 			if (link(tfname, cp) < 0)
195 				frecverr("link %s %s: %m", tfname, cp);
196 			(void)unlink(tfname);
197 			tfname[0] = '\0';
198 			nfiles++;
199 			continue;
200 
201 		case '\3':	/* read df file */
202 			size = 0;
203 			while (*cp >= '0' && *cp <= '9')
204 				size = size * 10 + (*cp++ - '0');
205 			if (*cp++ != ' ')
206 				break;
207 			if (strchr(cp, '/'))
208 				frecverr("readjob: %s: illegal path name", cp);
209 			if (!chksize(size)) {
210 				(void)write(STDOUT_FILENO, "\2", 1);
211 				continue;
212 			}
213 			(void)strlcpy(dfname, cp, sizeof(dfname));
214 			(void)readfile(dfname, size);
215 			continue;
216 		}
217 		frecverr("protocol screwup: %s", line);
218 	}
219 }
220 
221 /*
222  * Read files send by lpd and copy them to the spooling directory.
223  */
224 static int
225 readfile(char *file, int size)
226 {
227 	char *cp;
228 	char buf[BUFSIZ];
229 	int i, j, amt;
230 	int fd, err;
231 
232 	if ((fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD)) < 0)
233 		frecverr("readfile: %s: illegal path name: %m", file);
234 	ack();
235 	err = 0;
236 	for (i = 0; i < size; i += BUFSIZ) {
237 		amt = BUFSIZ;
238 		cp = buf;
239 		if (i + amt > size)
240 			amt = size - i;
241 		do {
242 			j = read(STDOUT_FILENO, cp, amt);
243 			if (j <= 0)
244 				frecverr("Lost connection");
245 			amt -= j;
246 			cp += j;
247 		} while (amt > 0);
248 		amt = BUFSIZ;
249 		if (i + amt > size)
250 			amt = size - i;
251 		if (write(fd, buf, amt) != amt) {
252 			err++;
253 			break;
254 		}
255 	}
256 	(void)close(fd);
257 	if (err)
258 		frecverr("%s: write error", file);
259 	if (noresponse()) {		/* file sent had bad data in it */
260 		if (strchr(file, '/') == NULL)
261 			(void)unlink(file);
262 		return(0);
263 	}
264 	ack();
265 	return(1);
266 }
267 
268 static int
269 noresponse(void)
270 {
271 	char resp;
272 
273 	if (read(STDOUT_FILENO, &resp, 1) != 1)
274 		frecverr("Lost connection");
275 	if (resp == '\0')
276 		return(0);
277 	return(1);
278 }
279 
280 /*
281  * Check to see if there is enough space on the disk for size bytes.
282  * 1 == OK, 0 == Not OK.
283  */
284 static int
285 chksize(int size)
286 {
287 	int64_t spacefree;
288 	struct statfs sfb;
289 
290 	if (size <= 0)
291 		return (0);
292 	if (statfs(".", &sfb) < 0) {
293 		syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
294 		return (1);
295 	}
296 	spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
297 	size = (size + 511) / 512;
298 	if (minfree + size > spacefree)
299 		return(0);
300 	return(1);
301 }
302 
303 static int
304 read_number(char *fn)
305 {
306 	char lin[80];
307 	FILE *fp;
308 
309 	if ((fp = fopen(fn, "r")) == NULL)
310 		return (0);
311 	if (fgets(lin, sizeof(lin), fp) == NULL) {
312 		fclose(fp);
313 		return (0);
314 	}
315 	fclose(fp);
316 	return (atoi(lin));
317 }
318 
319 /*
320  * Remove all the files associated with the current job being transferred.
321  */
322 static void
323 rcleanup(int signo)
324 {
325 	int save_errno = errno;
326 
327 	if (tfname[0] && strchr(tfname, '/') == NULL)
328 		(void)unlink(tfname);
329 	if (dfname[0] && strchr(dfname, '/') == NULL) {
330 		do {
331 			do
332 				(void)unlink(dfname);
333 			while (dfname[2]-- != 'A')
334 				;
335 			dfname[2] = 'z';
336 		} while (dfname[0]-- != 'd');
337 	}
338 	dfname[0] = '\0';
339 	errno = save_errno;
340 }
341 
342 static void
343 frecverr(const char *msg, ...)
344 {
345 	extern char fromb[];
346 	va_list ap;
347 
348 	va_start(ap, msg);
349 	rcleanup(0);
350 	syslog(LOG_ERR, "%s", fromb);
351 	vsyslog(LOG_ERR, msg, ap);
352 	va_end(ap);
353 	putchar('\1');		/* return error code */
354 	exit(1);
355 }
356