1 /* 2 * Copyright (c) 1983, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the University of 17 * California, Berkeley and its contributors. 18 * 4. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 static const char copyright[] = 37 "@(#) Copyright (c) 1983, 1993, 1994\n\ 38 The Regents of the University of California. All rights reserved.\n"; 39 #endif /* not lint */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95"; 44 #endif 45 static const char rcsid[] = 46 "$FreeBSD$"; 47 #endif /* not lint */ 48 49 /* 50 * lpd -- line printer daemon. 51 * 52 * Listen for a connection and perform the requested operation. 53 * Operations are: 54 * \1printer\n 55 * check the queue for jobs and print any found. 56 * \2printer\n 57 * receive a job from another machine and queue it. 58 * \3printer [users ...] [jobs ...]\n 59 * return the current state of the queue (short form). 60 * \4printer [users ...] [jobs ...]\n 61 * return the current state of the queue (long form). 62 * \5printer person [users ...] [jobs ...]\n 63 * remove jobs from the queue. 64 * 65 * Strategy to maintain protected spooling area: 66 * 1. Spooling area is writable only by daemon and spooling group 67 * 2. lpr runs setuid root and setgrp spooling group; it uses 68 * root to access any file it wants (verifying things before 69 * with an access call) and group id to know how it should 70 * set up ownership of files in the spooling area. 71 * 3. Files in spooling area are owned by root, group spooling 72 * group, with mode 660. 73 * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 74 * access files and printer. Users can't get to anything 75 * w/o help of lpq and lprm programs. 76 */ 77 78 #include <sys/param.h> 79 #include <sys/wait.h> 80 #include <sys/types.h> 81 #include <sys/socket.h> 82 #include <sys/un.h> 83 #include <sys/stat.h> 84 #include <sys/file.h> 85 #include <netinet/in.h> 86 #include <arpa/inet.h> 87 88 #include <netdb.h> 89 #include <unistd.h> 90 #include <syslog.h> 91 #include <signal.h> 92 #include <err.h> 93 #include <errno.h> 94 #include <fcntl.h> 95 #include <dirent.h> 96 #include <stdio.h> 97 #include <stdlib.h> 98 #include <string.h> 99 #include <sysexits.h> 100 #include <ctype.h> 101 #include "lp.h" 102 #include "lp.local.h" 103 #include "pathnames.h" 104 #include "extern.h" 105 106 int lflag; /* log requests flag */ 107 int pflag; /* no incoming port flag */ 108 int from_remote; /* from remote socket */ 109 110 int main __P((int, char **)); 111 static void reapchild __P((int)); 112 static void mcleanup __P((int)); 113 static void doit __P((void)); 114 static void startup __P((void)); 115 static void chkhost __P((struct sockaddr_in *)); 116 static int ckqueue __P((struct printer *)); 117 static void usage __P((void)); 118 /* From rcmd.c: */ 119 int __ivaliduser __P((FILE *, u_long, const char *, 120 const char *)); 121 122 uid_t uid, euid; 123 124 int 125 main(argc, argv) 126 int argc; 127 char **argv; 128 { 129 int f, funix, finet, options, fromlen, i, errs; 130 fd_set defreadfds; 131 struct sockaddr_un un, fromunix; 132 struct sockaddr_in sin, frominet; 133 int lfd; 134 sigset_t omask, nmask; 135 struct servent *sp, serv; 136 137 euid = geteuid(); /* these shouldn't be different */ 138 uid = getuid(); 139 options = 0; 140 gethostname(host, sizeof(host)); 141 142 name = "lpd"; 143 144 if (euid != 0) 145 errx(EX_NOPERM,"must run as root"); 146 147 errs = 0; 148 while ((i = getopt(argc, argv, "dlp")) != -1) 149 switch (i) { 150 case 'd': 151 options |= SO_DEBUG; 152 break; 153 case 'l': 154 lflag++; 155 break; 156 case 'p': 157 pflag++; 158 break; 159 default: 160 errs++; 161 } 162 argc -= optind; 163 argv += optind; 164 if (errs) 165 usage(); 166 167 if (argc == 1) { 168 if ((i = atoi(argv[0])) == 0) 169 usage(); 170 if (i < 0 || i > USHRT_MAX) 171 errx(EX_USAGE, "port # %d is invalid", i); 172 173 serv.s_port = htons(i); 174 sp = &serv; 175 argc--; 176 } else { 177 sp = getservbyname("printer", "tcp"); 178 if (sp == NULL) 179 errx(EX_OSFILE, "printer/tcp: unknown service"); 180 } 181 182 if (argc != 0) 183 usage(); 184 185 /* 186 * We run chkprintcap right away to catch any errors and blat them 187 * to stderr while we still have it open, rather than sending them 188 * to syslog and leaving the user wondering why lpd started and 189 * then stopped. There should probably be a command-line flag to 190 * ignore errors from chkprintcap. 191 */ 192 { 193 pid_t pid; 194 int status; 195 pid = fork(); 196 if (pid < 0) { 197 err(EX_OSERR, "cannot fork"); 198 } else if (pid == 0) { /* child */ 199 execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0); 200 err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP); 201 } 202 if (waitpid(pid, &status, 0) < 0) { 203 err(EX_OSERR, "cannot wait"); 204 } 205 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 206 errx(EX_OSFILE, "%d errors in printcap file, exiting", 207 WEXITSTATUS(status)); 208 } 209 210 #ifndef DEBUG 211 /* 212 * Set up standard environment by detaching from the parent. 213 */ 214 daemon(0, 0); 215 #endif 216 217 openlog("lpd", LOG_PID, LOG_LPR); 218 syslog(LOG_INFO, "restarted"); 219 (void) umask(0); 220 /* 221 * NB: This depends on O_NONBLOCK semantics doing the right thing; 222 * i.e., applying only to the O_EXLOCK and not to the rest of the 223 * open/creation. As of 1997-12-02, this is the case for commonly- 224 * used filesystems. There are other places in this code which 225 * make the same assumption. 226 */ 227 lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 228 LOCK_FILE_MODE); 229 if (lfd < 0) { 230 if (errno == EWOULDBLOCK) /* active deamon present */ 231 exit(0); 232 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 233 exit(1); 234 } 235 fcntl(lfd, F_SETFL, 0); /* turn off non-blocking mode */ 236 ftruncate(lfd, 0); 237 /* 238 * write process id for others to know 239 */ 240 sprintf(line, "%u\n", getpid()); 241 f = strlen(line); 242 if (write(lfd, line, f) != f) { 243 syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 244 exit(1); 245 } 246 signal(SIGCHLD, reapchild); 247 /* 248 * Restart all the printers. 249 */ 250 startup(); 251 (void) unlink(_PATH_SOCKETNAME); 252 funix = socket(AF_UNIX, SOCK_STREAM, 0); 253 if (funix < 0) { 254 syslog(LOG_ERR, "socket: %m"); 255 exit(1); 256 } 257 258 sigemptyset(&nmask); 259 sigaddset(&nmask, SIGHUP); 260 sigaddset(&nmask, SIGINT); 261 sigaddset(&nmask, SIGQUIT); 262 sigaddset(&nmask, SIGTERM); 263 sigprocmask(SIG_BLOCK, &nmask, &omask); 264 265 (void) umask(07); 266 signal(SIGHUP, mcleanup); 267 signal(SIGINT, mcleanup); 268 signal(SIGQUIT, mcleanup); 269 signal(SIGTERM, mcleanup); 270 memset(&un, 0, sizeof(un)); 271 un.sun_family = AF_UNIX; 272 strcpy(un.sun_path, _PATH_SOCKETNAME); 273 #ifndef SUN_LEN 274 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 275 #endif 276 if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { 277 syslog(LOG_ERR, "ubind: %m"); 278 exit(1); 279 } 280 (void) umask(0); 281 sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); 282 FD_ZERO(&defreadfds); 283 FD_SET(funix, &defreadfds); 284 listen(funix, 5); 285 if (pflag == 0) { 286 finet = socket(AF_INET, SOCK_STREAM, 0); 287 if (finet >= 0) { 288 if (options & SO_DEBUG && 289 setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 290 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 291 mcleanup(0); 292 } 293 memset(&sin, 0, sizeof(sin)); 294 sin.sin_family = AF_INET; 295 sin.sin_port = sp->s_port; 296 if (bind(finet, (struct sockaddr *)&sin, 297 sizeof(sin)) < 0) { 298 syslog(LOG_ERR, "bind: %m"); 299 mcleanup(0); 300 } 301 FD_SET(finet, &defreadfds); 302 listen(finet, 5); 303 } 304 } 305 /* 306 * Main loop: accept, do a request, continue. 307 */ 308 memset(&frominet, 0, sizeof(frominet)); 309 memset(&fromunix, 0, sizeof(fromunix)); 310 /* 311 * XXX - should be redone for multi-protocol 312 */ 313 for (;;) { 314 int domain, nfds, s; 315 fd_set readfds; 316 317 FD_COPY(&defreadfds, &readfds); 318 nfds = select(20, &readfds, 0, 0, 0); 319 if (nfds <= 0) { 320 if (nfds < 0 && errno != EINTR) 321 syslog(LOG_WARNING, "select: %m"); 322 continue; 323 } 324 if (FD_ISSET(funix, &readfds)) { 325 domain = AF_UNIX, fromlen = sizeof(fromunix); 326 s = accept(funix, 327 (struct sockaddr *)&fromunix, &fromlen); 328 } else if (pflag == 0) /* if (FD_ISSET(finet, &readfds)) */ { 329 domain = AF_INET, fromlen = sizeof(frominet); 330 s = accept(finet, 331 (struct sockaddr *)&frominet, &fromlen); 332 if (frominet.sin_port == htons(20)) { 333 close(s); 334 continue; 335 } 336 } 337 if (s < 0) { 338 if (errno != EINTR) 339 syslog(LOG_WARNING, "accept: %m"); 340 continue; 341 } 342 if (fork() == 0) { 343 signal(SIGCHLD, SIG_IGN); 344 signal(SIGHUP, SIG_IGN); 345 signal(SIGINT, SIG_IGN); 346 signal(SIGQUIT, SIG_IGN); 347 signal(SIGTERM, SIG_IGN); 348 (void) close(funix); 349 if (pflag == 0) { 350 (void) close(finet); 351 } 352 dup2(s, 1); 353 (void) close(s); 354 if (domain == AF_INET) { 355 from_remote = 1; 356 chkhost(&frominet); 357 } else 358 from_remote = 0; 359 doit(); 360 exit(0); 361 } 362 (void) close(s); 363 } 364 } 365 366 static void 367 reapchild(signo) 368 int signo; 369 { 370 union wait status; 371 372 while (wait3((int *)&status, WNOHANG, 0) > 0) 373 ; 374 } 375 376 static void 377 mcleanup(signo) 378 int signo; 379 { 380 if (lflag) 381 syslog(LOG_INFO, "exiting"); 382 unlink(_PATH_SOCKETNAME); 383 exit(0); 384 } 385 386 /* 387 * Stuff for handling job specifications 388 */ 389 char *user[MAXUSERS]; /* users to process */ 390 int users; /* # of users in user array */ 391 int requ[MAXREQUESTS]; /* job number of spool entries */ 392 int requests; /* # of spool requests */ 393 char *person; /* name of person doing lprm */ 394 395 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ 396 char cbuf[BUFSIZ]; /* command line buffer */ 397 char *cmdnames[] = { 398 "null", 399 "printjob", 400 "recvjob", 401 "displayq short", 402 "displayq long", 403 "rmjob" 404 }; 405 406 static void 407 doit() 408 { 409 char *cp, *printer; 410 int n; 411 int status; 412 struct printer myprinter, *pp = &myprinter; 413 414 init_printer(&myprinter); 415 416 for (;;) { 417 cp = cbuf; 418 do { 419 if (cp >= &cbuf[sizeof(cbuf) - 1]) 420 fatal(0, "Command line too long"); 421 if ((n = read(1, cp, 1)) != 1) { 422 if (n < 0) 423 fatal(0, "Lost connection"); 424 return; 425 } 426 } while (*cp++ != '\n'); 427 *--cp = '\0'; 428 cp = cbuf; 429 if (lflag) { 430 if (*cp >= '\1' && *cp <= '\5') 431 syslog(LOG_INFO, "%s requests %s %s", 432 from, cmdnames[(u_char)*cp], cp+1); 433 else 434 syslog(LOG_INFO, "bad request (%d) from %s", 435 *cp, from); 436 } 437 switch (*cp++) { 438 case CMD_CHECK_QUE: /* check the queue, print any jobs there */ 439 startprinting(cp); 440 break; 441 case CMD_TAKE_THIS: /* receive files to be queued */ 442 if (!from_remote) { 443 syslog(LOG_INFO, "illegal request (%d)", *cp); 444 exit(1); 445 } 446 recvjob(cp); 447 break; 448 case CMD_SHOWQ_SHORT: /* display the queue (short form) */ 449 case CMD_SHOWQ_LONG: /* display the queue (long form) */ 450 /* XXX - this all needs to be redone. */ 451 printer = cp; 452 while (*cp) { 453 if (*cp != ' ') { 454 cp++; 455 continue; 456 } 457 *cp++ = '\0'; 458 while (isspace(*cp)) 459 cp++; 460 if (*cp == '\0') 461 break; 462 if (isdigit(*cp)) { 463 if (requests >= MAXREQUESTS) 464 fatal(0, "Too many requests"); 465 requ[requests++] = atoi(cp); 466 } else { 467 if (users >= MAXUSERS) 468 fatal(0, "Too many users"); 469 user[users++] = cp; 470 } 471 } 472 status = getprintcap(printer, pp); 473 if (status < 0) 474 fatal(pp, pcaperr(status)); 475 displayq(pp, cbuf[0] == CMD_SHOWQ_LONG); 476 exit(0); 477 case CMD_RMJOB: /* remove a job from the queue */ 478 if (!from_remote) { 479 syslog(LOG_INFO, "illegal request (%d)", *cp); 480 exit(1); 481 } 482 printer = cp; 483 while (*cp && *cp != ' ') 484 cp++; 485 if (!*cp) 486 break; 487 *cp++ = '\0'; 488 person = cp; 489 while (*cp) { 490 if (*cp != ' ') { 491 cp++; 492 continue; 493 } 494 *cp++ = '\0'; 495 while (isspace(*cp)) 496 cp++; 497 if (*cp == '\0') 498 break; 499 if (isdigit(*cp)) { 500 if (requests >= MAXREQUESTS) 501 fatal(0, "Too many requests"); 502 requ[requests++] = atoi(cp); 503 } else { 504 if (users >= MAXUSERS) 505 fatal(0, "Too many users"); 506 user[users++] = cp; 507 } 508 } 509 rmjob(printer); 510 break; 511 } 512 fatal(0, "Illegal service request"); 513 } 514 } 515 516 /* 517 * Make a pass through the printcap database and start printing any 518 * files left from the last time the machine went down. 519 */ 520 static void 521 startup() 522 { 523 int pid, status, more; 524 struct printer myprinter, *pp = &myprinter; 525 526 more = firstprinter(pp, &status); 527 if (status) 528 goto errloop; 529 while (more) { 530 if (ckqueue(pp) <= 0) { 531 goto next; 532 } 533 if (lflag) 534 syslog(LOG_INFO, "work for %s", pp->printer); 535 if ((pid = fork()) < 0) { 536 syslog(LOG_WARNING, "startup: cannot fork"); 537 mcleanup(0); 538 } 539 if (pid == 0) { 540 lastprinter(); 541 printjob(pp); 542 /* NOTREACHED */ 543 } 544 do { 545 next: 546 more = nextprinter(pp, &status); 547 errloop: 548 if (status) 549 syslog(LOG_WARNING, 550 "printcap for %s has errors, skipping", 551 pp->printer ? pp->printer : "<???>"); 552 } while (more && status); 553 } 554 } 555 556 /* 557 * Make sure there's some work to do before forking off a child 558 */ 559 static int 560 ckqueue(pp) 561 struct printer *pp; 562 { 563 register struct dirent *d; 564 DIR *dirp; 565 char *spooldir; 566 567 spooldir = pp->spool_dir; 568 if ((dirp = opendir(spooldir)) == NULL) 569 return (-1); 570 while ((d = readdir(dirp)) != NULL) { 571 if (d->d_name[0] != 'c' || d->d_name[1] != 'f') 572 continue; /* daemon control files only */ 573 closedir(dirp); 574 return (1); /* found something */ 575 } 576 closedir(dirp); 577 return (0); 578 } 579 580 #define DUMMY ":nobody::" 581 582 /* 583 * Check to see if the from host has access to the line printer. 584 */ 585 static void 586 chkhost(f) 587 struct sockaddr_in *f; 588 { 589 register struct hostent *hp; 590 register FILE *hostf; 591 int first = 1; 592 int good = 0; 593 594 /* Need real hostname for temporary filenames */ 595 hp = gethostbyaddr((char *)&f->sin_addr, 596 sizeof(struct in_addr), f->sin_family); 597 if (hp == NULL) 598 fatal(0, "Host name for your address (%s) unknown", 599 inet_ntoa(f->sin_addr)); 600 601 (void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1); 602 fromb[sizeof(fromb) - 1] = '\0'; 603 from = fromb; 604 605 /* Check for spoof, ala rlogind */ 606 hp = gethostbyname(fromb); 607 if (!hp) 608 fatal(0, "hostname for your address (%s) unknown", 609 inet_ntoa(f->sin_addr)); 610 for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) { 611 if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr, 612 sizeof(f->sin_addr))) 613 good = 1; 614 } 615 if (good == 0) 616 fatal(0, "address for your hostname (%s) not matched", 617 inet_ntoa(f->sin_addr)); 618 619 hostf = fopen(_PATH_HOSTSEQUIV, "r"); 620 again: 621 if (hostf) { 622 if (__ivaliduser(hostf, f->sin_addr.s_addr, 623 DUMMY, DUMMY) == 0) { 624 (void) fclose(hostf); 625 return; 626 } 627 (void) fclose(hostf); 628 } 629 if (first == 1) { 630 first = 0; 631 hostf = fopen(_PATH_HOSTSLPD, "r"); 632 goto again; 633 } 634 fatal(0, "Your host does not have line printer access"); 635 /*NOTREACHED*/ 636 } 637 638 static void 639 usage() 640 { 641 fprintf(stderr, "usage: lpd [-dlp] [port#]\n"); 642 exit(EX_USAGE); 643 } 644