GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/lpr/lpr/lpr.c Lines: 0 345 0.0 %
Date: 2017-11-07 Branches: 0 269 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: lpr.c,v 1.48 2015/02/09 23:00:14 deraadt Exp $ */
2
/*	$NetBSD: lpr.c,v 1.19 2000/10/11 20:23:52 is Exp $	*/
3
4
/*
5
 * Copyright (c) 1983, 1989, 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 * (c) UNIX System Laboratories, Inc.
8
 * All or some portions of this file are derived from material licensed
9
 * to the University of California by American Telephone and Telegraph
10
 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11
 * the permission of UNIX System Laboratories, Inc.
12
 *
13
 *
14
 * Redistribution and use in source and binary forms, with or without
15
 * modification, are permitted provided that the following conditions
16
 * are met:
17
 * 1. Redistributions of source code must retain the above copyright
18
 *    notice, this list of conditions and the following disclaimer.
19
 * 2. Redistributions in binary form must reproduce the above copyright
20
 *    notice, this list of conditions and the following disclaimer in the
21
 *    documentation and/or other materials provided with the distribution.
22
 * 3. Neither the name of the University nor the names of its contributors
23
 *    may be used to endorse or promote products derived from this software
24
 *    without specific prior written permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36
 * SUCH DAMAGE.
37
 */
38
39
/*
40
 *      lpr -- off line print
41
 *
42
 * Allows multiple printers and printers on remote machines by
43
 * using information from a printer data base.
44
 */
45
46
#include <sys/stat.h>
47
#include <sys/file.h>
48
49
#include <dirent.h>
50
#include <errno.h>
51
#include <fcntl.h>
52
#include <signal.h>
53
#include <syslog.h>
54
#include <pwd.h>
55
#include <grp.h>
56
#include <unistd.h>
57
#include <limits.h>
58
#include <stdlib.h>
59
#include <stdio.h>
60
#include <ctype.h>
61
#include <string.h>
62
#include <err.h>
63
64
#include "lp.h"
65
#include "lp.local.h"
66
#include "pathnames.h"
67
68
static char	*cfname;	/* daemon control files, linked from tf's */
69
static char	*class = host;	/* class title on header page */
70
static char	*dfname;	/* data files */
71
static char	*fonts[4];	/* troff font names */
72
static char	 format = 'f';	/* format char for printing files */
73
static int	 hdr = 1;	/* print header or not (default is yes) */
74
static int	 iflag;		/* indentation wanted */
75
static int	 inchar;	/* location to increment char in file names */
76
static int	 indent;	/* amount to indent */
77
static char	*jobname;	/* job name on header page */
78
static int	 mailflg;	/* send mail */
79
static int	 nact;		/* number of jobs to act on */
80
static int	 ncopies = 1;	/* # of copies to make */
81
static const char *person;	/* user name */
82
static int	 qflag;		/* q job, but don't exec daemon */
83
static int	 rflag;		/* remove files upon completion */
84
static int	 sflag;		/* symbolic link flag */
85
static int	 tfd;		/* control file descriptor */
86
static char	*tfname;	/* tmp copy of cf before linking */
87
static char	*title;		/* pr'ing title */
88
static char	*width;		/* width for versatec printing */
89
90
static struct stat statb;
91
92
volatile sig_atomic_t gotintr;
93
94
static void	 card(int, const char *);
95
static void	 chkprinter(char *);
96
static void	 cleanup(int);
97
static void	 copy(int, char *);
98
static char	*itoa(int);
99
static char	*linked(char *);
100
static char	*lmktemp(char *, int);
101
static void	 mktemps(void);
102
static int	 nfile(char *);
103
static int	 test(char *);
104
static __dead void usage(void);
105
106
int
107
main(int argc, char **argv)
108
{
109
	struct passwd *pw;
110
	struct group *gptr;
111
	char *arg, *cp;
112
	char buf[PATH_MAX];
113
	int i, f, ch;
114
	struct stat stb;
115
116
	/*
117
	 * Simulate setuid daemon w/ PRIV_END called.
118
	 * We don't want lpr to actually be setuid daemon since that
119
	 * requires that the lpr binary be owned by user daemon, which
120
	 * is potentially unsafe.
121
	 */
122
	if ((pw = getpwuid(DEFUID)) == NULL)
123
		errx(1, "daemon uid (%u) not in password file", DEFUID);
124
	effective_uid = pw->pw_uid;
125
	real_uid = getuid();
126
	effective_gid = pw->pw_gid;
127
	real_gid = getgid();
128
	setresgid(real_gid, real_gid, effective_gid);
129
	setresuid(real_uid, real_uid, effective_uid);
130
131
	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
132
		signal(SIGHUP, cleanup);
133
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
134
		signal(SIGINT, cleanup);
135
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
136
		signal(SIGQUIT, cleanup);
137
	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
138
		signal(SIGTERM, cleanup);
139
140
	gethostname(host, sizeof (host));
141
	openlog("lpr", 0, LOG_LPR);
142
143
	while ((ch = getopt(argc, argv,
144
	    ":#:1:2:3:4:C:J:P:T:U:cdfghi:lmnpqrstvw:")) != -1) {
145
		switch (ch) {
146
147
		case '#':		/* n copies */
148
			if (isdigit((unsigned char)*optarg)) {
149
				i = atoi(optarg);
150
				if (i > 0)
151
					ncopies = i;
152
			}
153
			break;
154
155
		case '4':		/* troff fonts */
156
		case '3':
157
		case '2':
158
		case '1':
159
			fonts[ch - '1'] = optarg;
160
			break;
161
162
		case 'C':		/* classification spec */
163
			hdr++;
164
			class = optarg;
165
			break;
166
167
		case 'J':		/* job name */
168
			hdr++;
169
			jobname = optarg;
170
			break;
171
172
		case 'P':		/* specifiy printer name */
173
			printer = optarg;
174
			break;
175
176
		case 'T':		/* pr's title line */
177
			title = optarg;
178
			break;
179
180
		case 'U':		/* user name */
181
			hdr++;
182
			person = optarg;
183
			break;
184
185
		case 'c':		/* print cifplot output */
186
		case 'd':		/* print tex output (dvi files) */
187
		case 'g':		/* print graph(1G) output */
188
		case 'l':		/* literal output */
189
		case 'n':		/* print ditroff output */
190
		case 'p':		/* print using ``pr'' */
191
		case 't':		/* print troff output (cat files) */
192
		case 'v':		/* print vplot output */
193
			format = ch;
194
			break;
195
196
		case 'f':		/* print fortran output */
197
			format = 'r';
198
			break;
199
200
		case 'h':		/* toggle want of header page */
201
			hdr = !hdr;
202
			break;
203
204
		case 'i':		/* indent output */
205
			iflag++;
206
			indent = atoi(optarg);
207
			if (indent < 0)
208
				indent = 8;
209
			break;
210
211
		case 'm':		/* send mail when done */
212
			mailflg = 1;
213
			break;
214
215
		case 'q':		/* just q job */
216
			qflag = 1;
217
			break;
218
219
		case 'r':		/* remove file when done */
220
			rflag = 1;
221
			break;
222
223
		case 's':		/* try to link files */
224
			sflag = 1;
225
			break;
226
227
		case 'w':		/* versatec page width */
228
			width = optarg;
229
			break;
230
231
		case ':':               /* catch "missing argument" error */
232
			if (optopt == 'i') {
233
				iflag++; /* -i without args is valid */
234
				indent = 8;
235
			} else
236
				usage();
237
			break;
238
239
		default:
240
			usage();
241
		}
242
	}
243
	argc -= optind;
244
	argv += optind;
245
	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
246
		printer = DEFLP;
247
	chkprinter(printer);
248
	if (SC && ncopies > 1)
249
		errx(1, "multiple copies are not allowed");
250
	if (MC > 0 && ncopies > MC)
251
		errx(1, "only %ld copies are allowed", MC);
252
	/*
253
	 * Get the identity of the person doing the lpr using the same
254
	 * algorithm as lprm.
255
	 */
256
	if (real_uid != DU || person == NULL) {
257
		if ((pw = getpwuid(real_uid)) == NULL)
258
			errx(1, "Who are you?");
259
		if ((person = strdup(pw->pw_name)) == NULL)
260
			err(1, NULL);
261
	}
262
	/*
263
	 * Check for restricted group access.
264
	 */
265
	if (RG != NULL && real_uid != DU) {
266
		if ((gptr = getgrnam(RG)) == NULL)
267
			errx(1, "Restricted group specified incorrectly");
268
		if (gptr->gr_gid != getgid()) {
269
			while (*gptr->gr_mem != NULL) {
270
				if ((strcmp(person, *gptr->gr_mem)) == 0)
271
					break;
272
				gptr->gr_mem++;
273
			}
274
			if (*gptr->gr_mem == NULL)
275
				errx(1, "Not a member of the restricted group");
276
		}
277
	}
278
	/*
279
	 * Check to make sure queuing is enabled if real_uid is not root.
280
	 */
281
	(void)snprintf(buf, sizeof(buf), "%s/%s", SD, LO);
282
	if (real_uid && stat(buf, &stb) == 0 && (stb.st_mode & 010))
283
		errx(1, "Printer queue is disabled");
284
	/*
285
	 * Initialize the control file.
286
	 */
287
	mktemps();
288
	tfd = nfile(tfname);
289
	card('H', host);
290
	card('P', person);
291
	if (hdr) {
292
		if (jobname == NULL) {
293
			if (argc == 0)
294
				jobname = "stdin";
295
			else
296
				jobname = (arg = strrchr(argv[0], '/')) ?
297
				    arg + 1 : argv[0];
298
		}
299
		card('J', jobname);
300
		card('C', class);
301
		if (!SH)
302
			card('L', person);
303
	}
304
	if (iflag)
305
		card('I', itoa(indent));
306
	if (mailflg)
307
		card('M', person);
308
	if (format == 't' || format == 'n' || format == 'd')
309
		for (i = 0; i < 4; i++)
310
			if (fonts[i] != NULL)
311
				card('1'+i, fonts[i]);
312
	if (width != NULL)
313
		card('W', width);
314
315
	/*
316
	 * Read the files and spool them.
317
	 */
318
	if (argc == 0)
319
		copy(0, " ");
320
	else while (argc--) {
321
		if (argv[0][0] == '-' && argv[0][1] == '\0') {
322
			/* use stdin */
323
			copy(0, " ");
324
			argv++;
325
			continue;
326
		}
327
		if ((f = test(arg = *argv++)) < 0)
328
			continue;	/* file unreasonable */
329
330
		if (sflag && (cp = linked(arg)) != NULL) {
331
			(void)snprintf(buf, sizeof(buf), "%d %llu",
332
			    statb.st_dev, (unsigned long long)statb.st_ino);
333
			card('S', buf);
334
			if (format == 'p')
335
				card('T', title ? title : arg);
336
			for (i = 0; i < ncopies; i++)
337
				card(format, &dfname[inchar-2]);
338
			card('U', &dfname[inchar-2]);
339
			if (f)
340
				card('U', cp);
341
			card('N', arg);
342
			dfname[inchar]++;
343
			nact++;
344
			continue;
345
		}
346
		if (sflag)
347
			warnx("%s: not linked, copying instead", arg);
348
		if ((i = safe_open(arg, O_RDONLY, 0)) < 0)
349
			warn("%s", arg);
350
		else {
351
			copy(i, arg);
352
			(void)close(i);
353
			if (f && unlink(arg) < 0)
354
				warnx("%s: not removed", arg);
355
		}
356
	}
357
358
	if (nact) {
359
		(void)close(tfd);
360
		tfname[inchar]--;
361
		/*
362
		 * Touch the control file to fix position in the queue.
363
		 */
364
		PRIV_START;
365
		if ((tfd = safe_open(tfname, O_RDWR|O_NOFOLLOW, 0)) >= 0) {
366
			char c;
367
368
			if (read(tfd, &c, 1) == 1 &&
369
			    lseek(tfd, (off_t)0, SEEK_SET) == 0 &&
370
			    write(tfd, &c, 1) != 1) {
371
				warn("%s", tfname);
372
				tfname[inchar]++;
373
				cleanup(0);
374
			}
375
			(void)close(tfd);
376
		}
377
		if (link(tfname, cfname) < 0) {
378
			warn("cannot rename %s", cfname);
379
			tfname[inchar]++;
380
			cleanup(0);
381
		}
382
		unlink(tfname);
383
		PRIV_END;
384
		if (qflag)		/* just q things up */
385
			exit(0);
386
		if (!startdaemon(printer))
387
			printf("jobs queued, but cannot start daemon.\n");
388
		exit(0);
389
	}
390
	cleanup(0);
391
	return (1);
392
	/* NOTREACHED */
393
}
394
395
/*
396
 * Create the file n and copy from file descriptor f.
397
 */
398
static void
399
copy(int f, char *n)
400
{
401
	int fd, i, nr, nc;
402
	char buf[BUFSIZ];
403
404
	if (format == 'p')
405
		card('T', title ? title : n);
406
	for (i = 0; i < ncopies; i++)
407
		card(format, &dfname[inchar-2]);
408
	card('U', &dfname[inchar-2]);
409
	card('N', n);
410
	fd = nfile(dfname);
411
	nr = nc = 0;
412
	while ((i = read(f, buf, sizeof(buf))) > 0) {
413
		if (write(fd, buf, i) != i) {
414
			warn("%s", n);
415
			break;
416
		}
417
		nc += i;
418
		if (nc >= sizeof(buf)) {
419
			nc -= sizeof(buf);
420
			nr++;
421
			if (MX > 0 && nr > MX) {
422
				warnx("%s: copy file is too large", n);
423
				break;
424
			}
425
		}
426
	}
427
	(void)close(fd);
428
	if (nc == 0 && nr == 0)
429
		warnx("%s: empty input file", f ? n : "stdin");
430
	else
431
		nact++;
432
}
433
434
/*
435
 * Try and link the file to dfname. Return a pointer to the full
436
 * path name if successful.
437
 */
438
static char *
439
linked(char *file)
440
{
441
	char *cp;
442
	static char buf[PATH_MAX];
443
	int ret;
444
445
	if (*file != '/') {
446
		if (getcwd(buf, sizeof(buf)) == NULL)
447
			return(NULL);
448
449
		while (file[0] == '.') {
450
			switch (file[1]) {
451
			case '/':
452
				file += 2;
453
				continue;
454
			case '.':
455
				if (file[2] == '/') {
456
					if ((cp = strrchr(buf, '/')) != NULL)
457
						*cp = '\0';
458
					file += 3;
459
					continue;
460
				}
461
			}
462
			break;
463
		}
464
		if (strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) ||
465
		    strlcat(buf, file, sizeof(buf)) >= sizeof(buf))
466
			return(NULL);
467
		file = buf;
468
	}
469
	PRIV_START;
470
	ret = symlink(file, dfname);
471
	PRIV_END;
472
	return(ret ? NULL : file);
473
}
474
475
/*
476
 * Put a line into the control file.
477
 */
478
static void
479
card(int c, const char *p2)
480
{
481
	char buf[BUFSIZ];
482
	char *p1 = buf;
483
	int len = 2;
484
485
	if (strlen(p2) > sizeof(buf) - 2)
486
		errx(1, "Internal error:  String longer than %ld",
487
		    (long)sizeof(buf));
488
489
	*p1++ = c;
490
	while ((c = *p2++) != '\0' && len < sizeof(buf)) {
491
		*p1++ = (c == '\n') ? ' ' : c;
492
		len++;
493
	}
494
	*p1++ = '\n';
495
	write(tfd, buf, len);
496
}
497
498
/*
499
 * Create a new file in the spool directory.
500
 */
501
static int
502
nfile(char *n)
503
{
504
	int f;
505
	int oldumask = umask(0);		/* should block signals */
506
507
	PRIV_START;
508
	f = open(n, O_WRONLY|O_EXCL|O_CREAT, FILMOD);
509
	(void)umask(oldumask);
510
	if (f < 0) {
511
		warn("%s", n);
512
		cleanup(0);
513
	}
514
	PRIV_END;
515
	if (++n[inchar] > 'z') {
516
		if (++n[inchar-2] == 't') {
517
			warnx("too many files - break up the job");
518
			cleanup(0);
519
		}
520
		n[inchar] = 'A';
521
	} else if (n[inchar] == '[')
522
		n[inchar] = 'a';
523
	return (f);
524
}
525
526
/*
527
 * Cleanup after interrupts and errors.
528
 */
529
static void
530
cleanup(int signo)
531
{
532
	int i;
533
534
	signal(SIGHUP, SIG_IGN);
535
	signal(SIGINT, SIG_IGN);
536
	signal(SIGQUIT, SIG_IGN);
537
	signal(SIGTERM, SIG_IGN);
538
	i = inchar;
539
	PRIV_START;
540
	if (tfname)
541
		do
542
			unlink(tfname);
543
		while (tfname[i]-- != 'A');
544
	if (cfname)
545
		do
546
			unlink(cfname);
547
		while (cfname[i]-- != 'A');
548
	if (dfname)
549
		do {
550
			do
551
				unlink(dfname);
552
			while (dfname[i]-- != 'A');
553
			dfname[i] = 'z';
554
		} while (dfname[i-2]-- != 'd');
555
	_exit(1);
556
}
557
558
/*
559
 * Test to see if this is a printable file.
560
 * Return -1 if it is not, 0 if its printable, and 1 if
561
 * we should remove it after printing.
562
 */
563
static int
564
test(char *file)
565
{
566
	int fd;
567
	char *cp;
568
569
	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) < 0) {
570
		warn("cannot open %s", file);
571
		goto bad;
572
	}
573
	if (fstat(fd, &statb) < 0) {
574
		warn("cannot stat %s", file);
575
		goto bad;
576
	}
577
	if (S_ISDIR(statb.st_mode)) {
578
		warnx("%s is a directory", file);
579
		goto bad;
580
	}
581
	if (!S_ISREG(statb.st_mode)) {
582
		warnx("%s is not a regular file", file);
583
		goto bad;
584
	}
585
	if (statb.st_size == 0) {
586
		warnx("%s is an empty file", file);
587
		goto bad;
588
 	}
589
	(void)close(fd);
590
	if (rflag) {
591
		if ((cp = strrchr(file, '/')) == NULL) {
592
			if (access(".", 2) == 0)
593
				return(1);
594
		} else {
595
			if (cp == file) {
596
				fd = access("/", 2);
597
			} else {
598
				*cp = '\0';
599
				fd = access(file, 2);
600
				*cp = '/';
601
			}
602
			if (fd == 0)
603
				return(1);
604
		}
605
		warnx("%s is not removable by you", file);
606
	}
607
	return(0);
608
bad:
609
	return(-1);
610
}
611
612
/*
613
 * itoa - integer to string conversion
614
 */
615
static char *
616
itoa(int i)
617
{
618
	static char b[10] = "########";
619
	char *p;
620
621
	p = &b[8];
622
	do
623
		*p-- = i%10 + '0';
624
	while (i /= 10)
625
		;
626
	return(++p);
627
}
628
629
/*
630
 * Perform lookup for printer name or abbreviation --
631
 */
632
static void
633
chkprinter(char *s)
634
{
635
	int status;
636
637
	if ((status = cgetent(&bp, printcapdb, s)) == -2)
638
		errx(1, "cannot open printer description file");
639
	else if (status == -1)
640
		errx(1, "%s: unknown printer", s);
641
	if (cgetstr(bp, "sd", &SD) == -1)
642
		SD = _PATH_DEFSPOOL;
643
	if (cgetstr(bp, "lo", &LO) == -1)
644
		LO = DEFLOCK;
645
	cgetstr(bp, "rg", &RG);
646
	if (cgetnum(bp, "mx", &MX) < 0)
647
		MX = DEFMX;
648
	if (cgetnum(bp, "mc", &MC) < 0)
649
		MC = DEFMAXCOPIES;
650
	if (cgetnum(bp, "du", &DU) < 0)
651
		DU = DEFUID;
652
	SC = (cgetcap(bp, "sc", ':') != NULL);
653
	SH = (cgetcap(bp, "sh", ':') != NULL);
654
}
655
656
/*
657
 * Make the temp files.
658
 */
659
static void
660
mktemps(void)
661
{
662
	int len, fd, n;
663
	char *cp;
664
	char buf[BUFSIZ];
665
	struct stat stb;
666
667
	if (snprintf(buf, sizeof(buf), "%s/.seq", SD) >= sizeof(buf))
668
		errc(1, ENAMETOOLONG, "%s/.seq", SD);
669
	PRIV_START;
670
	if ((fd = safe_open(buf, O_RDWR|O_CREAT|O_NOFOLLOW, 0661)) < 0)
671
		err(1, "cannot open %s", buf);
672
	if (flock(fd, LOCK_EX))
673
		err(1, "cannot lock %s", buf);
674
	PRIV_END;
675
	n = 0;
676
	if ((len = read(fd, buf, sizeof(buf))) > 0) {
677
		for (cp = buf; len--; ) {
678
			if (*cp < '0' || *cp > '9')
679
				break;
680
			n = n * 10 + (*cp++ - '0');
681
		}
682
	}
683
	do {
684
		tfname = lmktemp("tf", n);
685
		cfname = lmktemp("cf", n);
686
		dfname = lmktemp("df", n);
687
		n = (n + 1) % 1000;
688
	} while (stat(tfname, &stb) == 0 || stat(cfname, &stb) == 0 ||
689
	    stat(dfname, &stb) == 0);
690
	inchar = strlen(SD) + 3;
691
	(void)lseek(fd, (off_t)0, SEEK_SET);
692
	snprintf(buf, sizeof(buf), "%03d\n", n);
693
	(void)write(fd, buf, strlen(buf));
694
	(void)close(fd);	/* unlocks as well */
695
}
696
697
/*
698
 * Make a temp file name.
699
 */
700
static char *
701
lmktemp(char *id, int num)
702
{
703
	char *s;
704
705
	if (asprintf(&s, "%s/%sA%03d%s", SD, id, num, host) == -1)
706
		err(1, NULL);
707
708
	return(s);
709
}
710
711
static __dead void
712
usage(void)
713
{
714
	extern char *__progname;
715
716
	fprintf(stderr,
717
	    "usage: %s [-cdfghlmnpqrstv] [-#num] [-1234 font] "
718
	    "[-C class] [-i [numcols]]\n"
719
	    "\t[-J job] [-Pprinter] [-T title] [-U user] "
720
	    "[-wnum] [name ...]\n", __progname);
721
	exit(1);
722
}