1 /*
2 * Copyright (c) 1983, 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 * (c) UNIX System Laboratories, Inc.
5 *
6 * All or some portions of this file are derived from material licensed
7 * to the University of California by American Telephone and Telegraph
8 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
9 * the permission of UNIX System Laboratories, Inc.
10 *
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * @(#)from: lpr.c 8.4 (Berkeley) 4/28/95
37 * $FreeBSD: src/usr.sbin/lpr/lpr/lpr.c,v 1.32.2.11 2002/04/28 23:40:23 gad Exp $
38 */
39
40 /*
41 * lpr -- off line print
42 *
43 * Allows multiple printers and printers on remote machines by
44 * using information from a printer data base.
45 */
46
47 #include <sys/param.h>
48 #include <sys/stat.h>
49
50 #include <netinet/in.h> /* N_BADMAG uses ntohl() */
51
52 #include <dirent.h>
53 #include <fcntl.h>
54 #include <a.out.h>
55 #include <err.h>
56 #include <inttypes.h>
57 #include <locale.h>
58 #include <signal.h>
59 #include <syslog.h>
60 #include <pwd.h>
61 #include <grp.h>
62 #include <unistd.h>
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <ctype.h>
66 #include <string.h>
67 #include "lp.h"
68 #include "lp.local.h"
69 #include "pathnames.h"
70
71 static char *cfname; /* daemon control files, linked from tf's */
72 static char *class = local_host; /* class title on header page */
73 static char *dfname; /* data files */
74 static char *fonts[4]; /* troff font names */
75 static char format = 'f'; /* format char for printing files */
76 static int hdr = 1; /* print header or not (default is yes) */
77 static int iflag; /* indentation wanted */
78 static int inchar; /* location to increment char in file names */
79 static int indent; /* amount to indent */
80 static const char *jobname; /* job name on header page */
81 static int mailflg; /* send mail */
82 static int nact; /* number of jobs to act on */
83 static int ncopies = 1; /* # of copies to make */
84 static char *lpr_username; /* person sending the print job(s) */
85 static int qflag; /* q job, but don't exec daemon */
86 static int rflag; /* remove files upon completion */
87 static int sflag; /* symbolic link flag */
88 static int tfd; /* control file descriptor */
89 static char *tfname; /* tmp copy of cf before linking */
90 static char *title; /* pr'ing title */
91 static char *locale; /* pr'ing locale */
92 static int userid; /* user id */
93 static char *Uflag; /* user name specified with -U flag */
94 static char *width; /* width for versatec printing */
95 static char *Zflag; /* extra filter options for LPRng servers */
96
97 static struct stat statb;
98
99 static void card(int _c, const char *_p2);
100 static int checkwriteperm(const char *_file, const char *_directory);
101 static void chkprinter(const char *_ptrname, struct printer *_pp);
102 static void cleanup(int _signo);
103 static void copy(const struct printer *_pp, int _f, const char _n[]);
104 static char *itoa(int _i);
105 static const char *linked(const char *_file);
106 static char *lmktemp(const struct printer *_pp, const char *_id,
107 int _num, int len);
108 static void mktemps(const struct printer *_pp);
109 static int nfile(char *_n);
110 static int test(const char *_file);
111 static void usage(void);
112
113 uid_t uid, euid;
114
115 int
main(int argc,char * argv[])116 main(int argc, char *argv[])
117 {
118 struct passwd *pw;
119 struct group *gptr;
120 const char *arg, *cp, *printer;
121 char *p;
122 char buf[BUFSIZ];
123 int c, i, f, errs;
124 int ret, didlink;
125 struct stat stb;
126 struct stat statb1, statb2;
127 struct printer myprinter, *pp = &myprinter;
128
129 printer = NULL;
130 euid = geteuid();
131 uid = getuid();
132 seteuid(uid);
133 if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
134 signal(SIGHUP, cleanup);
135 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
136 signal(SIGINT, cleanup);
137 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
138 signal(SIGQUIT, cleanup);
139 if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
140 signal(SIGTERM, cleanup);
141
142 progname = argv[0];
143 gethostname(local_host, sizeof(local_host));
144 openlog("lpd", 0, LOG_LPR);
145
146 errs = 0;
147 while ((c = getopt(argc, argv,
148 ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:"))
149 != -1) {
150 switch (c) {
151 case '#': /* n copies */
152 i = strtol(optarg, &p, 10);
153 if (*p)
154 errx(1, "Bad argument to -#, number expected");
155 if (i > 0)
156 ncopies = i;
157 break;
158
159 case '1': /* troff fonts */
160 case '2':
161 case '3':
162 case '4':
163 fonts[optopt - '1'] = optarg;
164 break;
165
166 case 'C': /* classification spec */
167 hdr++;
168 class = optarg;
169 break;
170
171 case 'J': /* job name */
172 hdr++;
173 jobname = optarg;
174 break;
175
176 case 'P': /* specifiy printer name */
177 printer = optarg;
178 break;
179
180 case 'L': /* pr's locale */
181 locale = optarg;
182 break;
183
184 case 'T': /* pr's title line */
185 title = optarg;
186 break;
187
188 case 'U': /* user name */
189 hdr++;
190 Uflag = optarg;
191 break;
192
193 case 'Z':
194 Zflag = optarg;
195 break;
196
197 case 'c': /* print cifplot output */
198 case 'd': /* print tex output (dvi files) */
199 case 'g': /* print graph(1G) output */
200 case 'l': /* literal output */
201 case 'n': /* print ditroff output */
202 case 't': /* print troff output (cat files) */
203 case 'p': /* print using ``pr'' */
204 case 'v': /* print vplot output */
205 format = optopt;
206 break;
207
208 case 'f': /* print fortran output */
209 format = 'r';
210 break;
211
212 case 'h': /* nulifiy header page */
213 hdr = 0;
214 break;
215
216 case 'i': /* indent output */
217 iflag++;
218 if (optarg && *optarg == '-') {
219 /* default value if '-i' has no arg */
220 indent = 8;
221 /* don't let getopt(3) slup up other args */
222 optind--;
223 break;
224 }
225 indent = strtol(optarg, &p, 10);
226 if (*p)
227 errx(1, "Bad argument to -i, number expected");
228 break;
229
230 case 'm': /* send mail when done */
231 mailflg++;
232 break;
233
234 case 'q': /* just queue job */
235 qflag++;
236 break;
237
238 case 'r': /* remove file when done */
239 rflag++;
240 break;
241
242 case 's': /* try to link files */
243 sflag++;
244 break;
245
246 case 'w': /* versatec page width */
247 width = optarg;
248 break;
249
250 case ':': /* catch "missing argument" error */
251 if (optopt == 'i') {
252 iflag++; /* -i without args is valid */
253 indent = 8;
254 } else
255 errs++;
256 break;
257
258 default:
259 errs++;
260 }
261 }
262 argc -= optind;
263 argv += optind;
264 if (errs)
265 usage();
266 if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
267 printer = DEFLP;
268 chkprinter(printer, pp);
269 if (pp->no_copies && ncopies > 1)
270 errx(1, "multiple copies are not allowed");
271 if (pp->max_copies > 0 && ncopies > pp->max_copies)
272 errx(1, "only %ld copies are allowed", pp->max_copies);
273 /*
274 * Get the identity of the person doing the lpr using the same
275 * algorithm as lprm. Actually, not quite -- lprm will override
276 * the login name with "root" if the user is running as root;
277 * the daemon actually checks for the string "root" in its
278 * permission checking. Sigh.
279 */
280 userid = getuid();
281 if (Uflag) {
282 if (userid != 0 && userid != pp->daemon_user)
283 errx(1, "only privileged users may use the `-U' flag");
284 lpr_username = Uflag; /* -U person doing 'lpr' */
285 } else {
286 lpr_username = getlogin(); /* person doing 'lpr' */
287 if (userid != pp->daemon_user || lpr_username == NULL) {
288 if ((pw = getpwuid(userid)) == NULL)
289 errx(1, "Who are you?");
290 lpr_username = pw->pw_name;
291 }
292 }
293
294 /*
295 * Check for restricted group access.
296 */
297 if (pp->restrict_grp != NULL && userid != pp->daemon_user) {
298 if ((gptr = getgrnam(pp->restrict_grp)) == NULL)
299 errx(1, "Restricted group specified incorrectly");
300 if (gptr->gr_gid != getgid()) {
301 while (*gptr->gr_mem != NULL) {
302 if ((strcmp(lpr_username, *gptr->gr_mem)) == 0)
303 break;
304 gptr->gr_mem++;
305 }
306 if (*gptr->gr_mem == NULL)
307 errx(1, "Not a member of the restricted group");
308 }
309 }
310 /*
311 * Check to make sure queuing is enabled if userid is not root.
312 */
313 lock_file_name(pp, buf, sizeof buf);
314 if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS))
315 errx(1, "Printer queue is disabled");
316 /*
317 * Initialize the control file.
318 */
319 mktemps(pp);
320 tfd = nfile(tfname);
321 seteuid(euid);
322 fchown(tfd, pp->daemon_user, -1);
323 /* owned by daemon for protection */
324 seteuid(uid);
325 card('H', local_host);
326 card('P', lpr_username);
327 card('C', class);
328 if (hdr && !pp->no_header) {
329 if (jobname == NULL) {
330 if (argc == 0)
331 jobname = "stdin";
332 else
333 jobname = ((arg = strrchr(argv[0], '/'))
334 ? arg + 1 : argv[0]);
335 }
336 card('J', jobname);
337 card('L', lpr_username);
338 }
339 if (format != 'p' && Zflag != NULL)
340 card('Z', Zflag);
341 if (iflag)
342 card('I', itoa(indent));
343 if (mailflg)
344 card('M', lpr_username);
345 if (format == 't' || format == 'n' || format == 'd')
346 for (i = 0; i < 4; i++)
347 if (fonts[i] != NULL)
348 card('1'+i, fonts[i]);
349 if (width != NULL)
350 card('W', width);
351 /*
352 * XXX
353 * Our use of `Z' here is incompatible with LPRng's
354 * use. We assume that the only use of our existing
355 * `Z' card is as shown for `p' format (pr) files.
356 */
357 if (format == 'p') {
358 char *s;
359
360 if (locale)
361 card('Z', locale);
362 else if ((s = setlocale(LC_TIME, "")) != NULL)
363 card('Z', s);
364 }
365
366 /*
367 * Read the files and spool them.
368 */
369 if (argc == 0)
370 copy(pp, 0, " ");
371 else while (argc--) {
372 if (argv[0][0] == '-' && argv[0][1] == '\0') {
373 /* use stdin */
374 copy(pp, 0, " ");
375 argv++;
376 continue;
377 }
378 if ((f = test(arg = *argv++)) < 0)
379 continue; /* file unreasonable */
380
381 if (sflag && (cp = linked(arg)) != NULL) {
382 snprintf(buf, sizeof(buf), "%d %"PRId64, statb.st_dev,
383 (uint64_t)statb.st_ino);
384 card('S', buf);
385 if (format == 'p')
386 card('T', title ? title : arg);
387 for (i = 0; i < ncopies; i++)
388 card(format, &dfname[inchar-2]);
389 card('U', &dfname[inchar-2]);
390 if (f)
391 card('U', cp);
392 card('N', arg);
393 dfname[inchar]++;
394 nact++;
395 continue;
396 }
397 if (sflag)
398 printf("%s: %s: not linked, copying instead\n",
399 progname, arg);
400
401 if (f) {
402 /*
403 * The user wants the file removed after it is copied
404 * to the spool area, so see if the file can be moved
405 * instead of copy/unlink'ed. This is much faster and
406 * uses less spool space than copying the file. This
407 * can be very significant when running services like
408 * samba, pcnfs, CAP, et al.
409 */
410 seteuid(euid);
411 didlink = 0;
412 /*
413 * There are several things to check to avoid any
414 * security issues. Some of these are redundant
415 * under BSD's, but are necessary when lpr is built
416 * under some other OS's (which I do do...)
417 */
418 if (lstat(arg, &statb1) < 0)
419 goto nohardlink;
420 if (S_ISLNK(statb1.st_mode))
421 goto nohardlink;
422 if (link(arg, dfname) != 0)
423 goto nohardlink;
424 didlink = 1;
425 /*
426 * Make sure the user hasn't tried to trick us via
427 * any race conditions
428 */
429 if (lstat(dfname, &statb2) < 0)
430 goto nohardlink;
431 if (statb1.st_dev != statb2.st_dev)
432 goto nohardlink;
433 if (statb1.st_ino != statb2.st_ino)
434 goto nohardlink;
435 /*
436 * Skip if the file already had multiple hard links,
437 * because changing the owner and access-bits would
438 * change ALL versions of the file
439 */
440 if (statb2.st_nlink > 2)
441 goto nohardlink;
442 /*
443 * If we can access and remove the original file
444 * without special setuid-ness then this method is
445 * safe. Otherwise, abandon the move and fall back
446 * to the (usual) copy method.
447 */
448 seteuid(uid);
449 ret = access(dfname, R_OK);
450 if (ret == 0)
451 ret = unlink(arg);
452 seteuid(euid);
453 if (ret != 0)
454 goto nohardlink;
455 /*
456 * Unlink of user file was successful. Change the
457 * owner and permissions, add entries to the control
458 * file, and skip the file copying step.
459 */
460 chown(dfname, pp->daemon_user, getegid());
461 chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
462 seteuid(uid);
463 if (format == 'p')
464 card('T', title ? title : arg);
465 for (i = 0; i < ncopies; i++)
466 card(format, &dfname[inchar-2]);
467 card('U', &dfname[inchar-2]);
468 card('N', arg);
469 nact++;
470 continue;
471 nohardlink:
472 if (didlink)
473 unlink(dfname);
474 seteuid(uid); /* restore old uid */
475 } /* end: if (f) */
476
477 if ((i = open(arg, O_RDONLY)) < 0) {
478 printf("%s: cannot open %s\n", progname, arg);
479 } else {
480 copy(pp, i, arg);
481 close(i);
482 if (f && unlink(arg) < 0)
483 printf("%s: %s: not removed\n", progname, arg);
484 }
485 }
486
487 if (nact) {
488 close(tfd);
489 tfname[inchar]--;
490 /*
491 * Touch the control file to fix position in the queue.
492 */
493 seteuid(euid);
494 if ((tfd = open(tfname, O_RDWR)) >= 0) {
495 char touch_c;
496
497 if (read(tfd, &touch_c, 1) == 1 &&
498 lseek(tfd, (off_t)0, 0) == 0 &&
499 write(tfd, &touch_c, 1) != 1) {
500 printf("%s: cannot touch %s\n", progname,
501 tfname);
502 tfname[inchar]++;
503 cleanup(0);
504 }
505 close(tfd);
506 }
507 if (link(tfname, cfname) < 0) {
508 printf("%s: cannot rename %s\n", progname, cfname);
509 tfname[inchar]++;
510 cleanup(0);
511 }
512 unlink(tfname);
513 seteuid(uid);
514 if (qflag) /* just q things up */
515 exit(0);
516 if (!startdaemon(pp))
517 printf("jobs queued, but cannot start daemon.\n");
518 exit(0);
519 }
520 cleanup(0);
521 return (1);
522 /* NOTREACHED */
523 }
524
525 /*
526 * Create the file n and copy from file descriptor f.
527 */
528 static void
copy(const struct printer * pp,int f,const char n[])529 copy(const struct printer *pp, int f, const char n[])
530 {
531 int fd, i, nr, nc;
532 char buf[BUFSIZ];
533
534 if (format == 'p')
535 card('T', title ? title : n);
536 for (i = 0; i < ncopies; i++)
537 card(format, &dfname[inchar-2]);
538 card('U', &dfname[inchar-2]);
539 card('N', n);
540 fd = nfile(dfname);
541 nr = nc = 0;
542 while ((i = read(f, buf, BUFSIZ)) > 0) {
543 if (write(fd, buf, i) != i) {
544 printf("%s: %s: temp file write error\n", progname, n);
545 break;
546 }
547 nc += i;
548 if (nc >= BUFSIZ) {
549 nc -= BUFSIZ;
550 nr++;
551 if (pp->max_blocks > 0 && nr > pp->max_blocks) {
552 printf("%s: %s: copy file is too large\n",
553 progname, n);
554 break;
555 }
556 }
557 }
558 close(fd);
559 if (nc==0 && nr==0)
560 printf("%s: %s: empty input file\n", progname,
561 f ? n : "stdin");
562 else
563 nact++;
564 }
565
566 /*
567 * Try and link the file to dfname. Return a pointer to the full
568 * path name if successful.
569 */
570 static const char *
linked(const char * file)571 linked(const char *file)
572 {
573 char *cp;
574 static char buf[MAXPATHLEN];
575 int ret;
576
577 if (*file != '/') {
578 if (getcwd(buf, sizeof(buf)) == NULL)
579 return(NULL);
580 while (file[0] == '.') {
581 switch (file[1]) {
582 case '/':
583 file += 2;
584 continue;
585 case '.':
586 if (file[2] == '/') {
587 if ((cp = strrchr(buf, '/')) != NULL)
588 *cp = '\0';
589 file += 3;
590 continue;
591 }
592 }
593 break;
594 }
595 strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
596 strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
597 file = buf;
598 }
599 seteuid(euid);
600 ret = symlink(file, dfname);
601 seteuid(uid);
602 return(ret ? NULL : file);
603 }
604
605 /*
606 * Put a line into the control file.
607 */
608 static void
card(int c,const char * p2)609 card(int c, const char *p2)
610 {
611 char buf[BUFSIZ];
612 char *p1 = buf;
613 size_t len = 2;
614
615 *p1++ = c;
616 while ((c = *p2++) != '\0' && len < sizeof(buf)) {
617 *p1++ = (c == '\n') ? ' ' : c;
618 len++;
619 }
620 *p1++ = '\n';
621 write(tfd, buf, len);
622 }
623
624 /*
625 * Create a new file in the spool directory.
626 */
627 static int
nfile(char * n)628 nfile(char *n)
629 {
630 int f;
631 int oldumask = umask(0); /* should block signals */
632
633 seteuid(euid);
634 f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
635 umask(oldumask);
636 if (f < 0) {
637 printf("%s: cannot create %s\n", progname, n);
638 cleanup(0);
639 }
640 if (fchown(f, userid, -1) < 0) {
641 printf("%s: cannot chown %s\n", progname, n);
642 cleanup(0); /* cleanup does exit */
643 }
644 seteuid(uid);
645 if (++n[inchar] > 'z') {
646 if (++n[inchar-2] == 't') {
647 printf("too many files - break up the job\n");
648 cleanup(0);
649 }
650 n[inchar] = 'A';
651 } else if (n[inchar] == '[')
652 n[inchar] = 'a';
653 return(f);
654 }
655
656 /*
657 * Cleanup after interrupts and errors.
658 */
659 static void
cleanup(int signo __unused)660 cleanup(int signo __unused)
661 {
662 int i;
663
664 signal(SIGHUP, SIG_IGN);
665 signal(SIGINT, SIG_IGN);
666 signal(SIGQUIT, SIG_IGN);
667 signal(SIGTERM, SIG_IGN);
668 i = inchar;
669 seteuid(euid);
670 if (tfname)
671 do
672 unlink(tfname);
673 while (tfname[i]-- != 'A');
674 if (cfname)
675 do
676 unlink(cfname);
677 while (cfname[i]-- != 'A');
678 if (dfname)
679 do {
680 do
681 unlink(dfname);
682 while (dfname[i]-- != 'A');
683 dfname[i] = 'z';
684 } while (dfname[i-2]-- != 'd');
685 exit(1);
686 }
687
688 /*
689 * Test to see if this is a printable file.
690 * Return -1 if it is not, 0 if its printable, and 1 if
691 * we should remove it after printing.
692 */
693 static int
test(const char * file)694 test(const char *file)
695 {
696 struct exec execb;
697 size_t dlen;
698 int fd;
699 char *cp, *dirpath;
700
701 if (access(file, 4) < 0) {
702 printf("%s: cannot access %s\n", progname, file);
703 return(-1);
704 }
705 if (stat(file, &statb) < 0) {
706 printf("%s: cannot stat %s\n", progname, file);
707 return(-1);
708 }
709 if ((statb.st_mode & S_IFMT) == S_IFDIR) {
710 printf("%s: %s is a directory\n", progname, file);
711 return(-1);
712 }
713 if (statb.st_size == 0) {
714 printf("%s: %s is an empty file\n", progname, file);
715 return(-1);
716 }
717 if ((fd = open(file, O_RDONLY)) < 0) {
718 printf("%s: cannot open %s\n", progname, file);
719 return(-1);
720 }
721 /*
722 * XXX Shall we add a similar test for ELF?
723 */
724 if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
725 !N_BADMAG(execb)) {
726 printf("%s: %s is an executable program", progname, file);
727 goto error1;
728 }
729 close(fd);
730 if (rflag) {
731 /*
732 * aside: note that 'cp' is technically a 'const char *'
733 * (because it points into 'file'), even though strrchr
734 * returns a value of type 'char *'.
735 */
736 if ((cp = strrchr(file, '/')) == NULL) {
737 if (checkwriteperm(file,".") == 0)
738 return(1);
739 } else {
740 if (cp == file) {
741 fd = checkwriteperm(file,"/");
742 } else {
743 /* strlcpy will change the '/' to '\0' */
744 dlen = cp - file + 1;
745 dirpath = malloc(dlen);
746 strlcpy(dirpath, file, dlen);
747 fd = checkwriteperm(file, dirpath);
748 free(dirpath);
749 }
750 if (fd == 0)
751 return(1);
752 }
753 printf("%s: %s: is not removable by you\n", progname, file);
754 }
755 return(0);
756
757 error1:
758 printf(" and is unprintable\n");
759 close(fd);
760 return(-1);
761 }
762
763 static int
checkwriteperm(const char * file,const char * directory)764 checkwriteperm(const char *file, const char *directory)
765 {
766 struct stat stats;
767 if (access(directory, W_OK) == 0) {
768 stat(directory, &stats);
769 if (stats.st_mode & S_ISVTX) {
770 stat(file, &stats);
771 if(stats.st_uid == userid) {
772 return(0);
773 }
774 } else return(0);
775 }
776 return(-1);
777 }
778
779 /*
780 * itoa - integer to string conversion
781 */
782 static char *
itoa(int i)783 itoa(int i)
784 {
785 static char b[10] = "########";
786 char *p;
787
788 p = &b[8];
789 do
790 *p-- = i%10 + '0';
791 while (i /= 10);
792 return(++p);
793 }
794
795 /*
796 * Perform lookup for printer name or abbreviation --
797 */
798 static void
chkprinter(const char * ptrname,struct printer * pp)799 chkprinter(const char *ptrname, struct printer *pp)
800 {
801 int status;
802
803 init_printer(pp);
804 status = getprintcap(ptrname, pp);
805 switch(status) {
806 case PCAPERR_OSERR:
807 case PCAPERR_TCLOOP:
808 errx(1, "%s: %s", ptrname, pcaperr(status));
809 case PCAPERR_NOTFOUND:
810 errx(1, "%s: unknown printer", ptrname);
811 case PCAPERR_TCOPEN:
812 warnx("%s: unresolved tc= reference(s)", ptrname);
813 }
814 }
815
816 /*
817 * Tell the user what we wanna get.
818 */
819 static void
usage(void)820 usage(void)
821 {
822 fprintf(stderr, "%s\n",
823 "usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n"
824 "\t[-Z daemon-options] [-L locale] [-i [num]] [-1234 font]\n"
825 "\t[-w num] [-cdfghlnmprstv] [name ...]");
826 exit(1);
827 }
828
829
830 /*
831 * Make the temp files.
832 */
833 static void
mktemps(const struct printer * pp)834 mktemps(const struct printer *pp)
835 {
836 int len, fd, n;
837 char *cp;
838 char buf[BUFSIZ];
839
840 snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir);
841 seteuid(euid);
842 if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
843 printf("%s: cannot create %s\n", progname, buf);
844 exit(1);
845 }
846 if (flock(fd, LOCK_EX)) {
847 printf("%s: cannot lock %s\n", progname, buf);
848 exit(1);
849 }
850 seteuid(uid);
851 n = 0;
852 if ((len = read(fd, buf, sizeof(buf))) > 0) {
853 for (cp = buf; len--; ) {
854 if (*cp < '0' || *cp > '9')
855 break;
856 n = n * 10 + (*cp++ - '0');
857 }
858 }
859 len = strlen(pp->spool_dir) + strlen(local_host) + 8;
860 tfname = lmktemp(pp, "tf", n, len);
861 cfname = lmktemp(pp, "cf", n, len);
862 dfname = lmktemp(pp, "df", n, len);
863 inchar = strlen(pp->spool_dir) + 3;
864 n = (n + 1) % 1000;
865 lseek(fd, (off_t)0, 0);
866 snprintf(buf, sizeof(buf), "%03d\n", n);
867 write(fd, buf, strlen(buf));
868 close(fd); /* unlocks as well */
869 }
870
871 /*
872 * Make a temp file name.
873 */
874 static char *
lmktemp(const struct printer * pp,const char * id,int num,int len)875 lmktemp(const struct printer *pp, const char *id, int num, int len)
876 {
877 char *s;
878
879 if ((s = malloc(len)) == NULL)
880 errx(1, "out of memory");
881 snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num, local_host);
882 return(s);
883 }
884